").css({
+ position: "absolute",
+ zIndex: BI.zIndex_tip - 2,
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ opacity: 0.5
+ }).appendTo("body"));
+ $pop = BI.Widget._renderEngine.createElement("
").css({
+ position: "absolute",
+ zIndex: BI.zIndex_tip - 1,
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0
+ }).appendTo("body");
+ var close = function () {
+ messageShows[messageShows.length - 1].destroy();
+ messageShows.pop();
+ if (messageShows.length === 0) {
+ $mask.remove();
+ $mask = null;
+ }
+ };
+ var controlItems = [];
+ if (hasCancel === true) {
+ controlItems.push({
+ el: {
+ type: "bi.button",
+ text: BI.i18nText("BI-Basic_Cancel"),
+ level: "ignore",
+ handler: function () {
+ close();
+ if (BI.isFunction(callback)) {
+ callback.apply(null, [false]);
+ }
+ }
+ }
+ });
+ }
+ controlItems.push({
+ el: {
+ type: "bi.button",
+ text: BI.i18nText("BI-Basic_OK"),
+ handler: function () {
+ close();
+ if (BI.isFunction(callback)) {
+ callback.apply(null, [true]);
+ }
+ }
+ }
+ });
+ var conf = {
+ element: $pop,
+ type: "bi.center_adapt",
+ items: [
+ {
+ type: "bi.border",
+ cls: "bi-card",
+ items: {
+ north: {
+ el: {
+ type: "bi.border",
+ cls: "bi-message-title bi-background",
+ items: {
+ center: {
+ el: {
+ type: "bi.label",
+ cls: "bi-font-bold",
+ text: title || BI.i18nText("BI-Basic_Prompt"),
+ textAlign: "left",
+ hgap: 20,
+ height: 40
+ }
+ },
+ east: {
+ el: {
+ type: "bi.icon_button",
+ cls: "bi-message-close close-font",
+ // height: 50,
+ handler: function () {
+ close();
+ if (BI.isFunction(callback)) {
+ callback.apply(null, [false]);
+ }
+ }
+ },
+ width: 60
+ }
+ }
+ },
+ height: 40
+ },
+ center: {
+ el: {
+ type: "bi.label",
+ vgap: 10,
+ hgap: 20,
+ whiteSpace: "normal",
+ text: message
+ }
+ },
+ south: {
+ el: {
+ type: "bi.absolute",
+ items: [{
+ el: {
+ type: "bi.right_vertical_adapt",
+ lgap: 10,
+ items: controlItems
+ },
+ top: 0,
+ left: 20,
+ right: 20,
+ bottom: 0
+ }]
+
+ },
+ height: 44
+ }
+ },
+ width: 450,
+ height: 200
+ }
+ ]
+ };
+
+ messageShows[messageShows.length] = BI.createWidget(conf);
+ }
+ };
+}();
diff --git a/src/main/resources/com/fr/fineui/base/grid/__test__/grid.test.js b/src/main/resources/com/fr/fineui/base/grid/__test__/grid.test.js
new file mode 100644
index 0000000..d65c3d2
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/grid/__test__/grid.test.js
@@ -0,0 +1,41 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/3/20
+ */
+describe("GridTest", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("grid", function () {
+ var items = [];
+ var rowCount = 1000, columnCount = 100;
+ for (var i = 0; i < rowCount; i++) {
+ items[i] = [];
+ for (var j = 0; j < columnCount; j++) {
+ items[i][j] = {
+ type: "bi.label",
+ text: i + "-" + j
+ };
+ }
+ }
+ var grid = BI.createWidget({
+ type: "bi.grid_view",
+ width: 400,
+ height: 300,
+ estimatedRowSize: 30,
+ estimatedColumnSize: 100,
+ items: items,
+ scrollTop: 100,
+ rowHeightGetter: function () {
+ return 30;
+ },
+ columnWidthGetter: function () {
+ return 100;
+ }
+ });
+ // TODO 性能展示类控件,不知道要测啥,标记一下
+ grid.destroy();
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/base/grid/grid.js b/src/main/resources/com/fr/fineui/base/grid/grid.js
new file mode 100644
index 0000000..6096a28
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/grid/grid.js
@@ -0,0 +1,381 @@
+/**
+ * GridView
+ *
+ * Created by GUY on 2016/1/11.
+ * @class BI.GridView
+ * @extends BI.Widget
+ */
+BI.GridView = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ return BI.extend(BI.GridView.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-grid-view",
+ // width: 400, //必设
+ // height: 300, //必设
+ overflowX: true,
+ overflowY: true,
+ overscanColumnCount: 0,
+ overscanRowCount: 0,
+ rowHeightGetter: BI.emptyFn, // number类型或function类型
+ columnWidthGetter: BI.emptyFn, // number类型或function类型
+ // estimatedColumnSize: 100, //columnWidthGetter为function时必设
+ // estimatedRowSize: 30, //rowHeightGetter为function时必设
+ scrollLeft: 0,
+ scrollTop: 0,
+ items: []
+ });
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ this.renderedCells = [];
+ this.renderedKeys = [];
+ this.renderRange = {};
+ this._scrollLock = false;
+ this._debounceRelease = BI.debounce(function () {
+ self._scrollLock = false;
+ }, 1000 / 60);
+ this.container = BI._lazyCreateWidget({
+ type: "bi.absolute"
+ });
+ this.element.scroll(function () {
+ if (self._scrollLock === true) {
+ return;
+ }
+ o.scrollLeft = self.element.scrollLeft();
+ o.scrollTop = self.element.scrollTop();
+ self._calculateChildrenToRender();
+ self.fireEvent(BI.GridView.EVENT_SCROLL, {
+ scrollLeft: o.scrollLeft,
+ scrollTop: o.scrollTop
+ });
+ });
+ BI._lazyCreateWidget({
+ type: "bi.vertical",
+ element: this,
+ scrollable: o.overflowX === true && o.overflowY === true,
+ scrolly: o.overflowX === false && o.overflowY === true,
+ scrollx: o.overflowX === true && o.overflowY === false,
+ items: [this.container]
+ });
+ if (o.items.length > 0) {
+ this._calculateSizeAndPositionData();
+ this._populate();
+ }
+ },
+
+ // mounted之后绑定事件
+ mounted: function () {
+ var o = this.options;
+ if (o.scrollLeft !== 0 || o.scrollTop !== 0) {
+ this.element.scrollTop(o.scrollTop);
+ this.element.scrollLeft(o.scrollLeft);
+ }
+ },
+
+ _calculateSizeAndPositionData: function () {
+ var o = this.options;
+ this.rowCount = 0;
+ this.columnCount = 0;
+ if (BI.isNumber(o.columnCount)) {
+ this.columnCount = o.columnCount;
+ } else if (o.items.length > 0) {
+ this.columnCount = o.items[0].length;
+ }
+ if (BI.isNumber(o.rowCount)) {
+ this.rowCount = o.rowCount;
+ } else {
+ this.rowCount = o.items.length;
+ }
+ this._columnSizeAndPositionManager = new BI.ScalingCellSizeAndPositionManager(this.columnCount, o.columnWidthGetter, o.estimatedColumnSize);
+ this._rowSizeAndPositionManager = new BI.ScalingCellSizeAndPositionManager(this.rowCount, o.rowHeightGetter, o.estimatedRowSize);
+ },
+
+ _getOverscanIndices: function (cellCount, overscanCellsCount, startIndex, stopIndex) {
+ return {
+ overscanStartIndex: Math.max(0, startIndex - overscanCellsCount),
+ overscanStopIndex: Math.min(cellCount - 1, stopIndex + overscanCellsCount)
+ };
+ },
+
+ _calculateChildrenToRender: function () {
+ var self = this, o = this.options;
+
+ var width = o.width, height = o.height, scrollLeft = BI.clamp(o.scrollLeft, 0, this._getMaxScrollLeft()),
+ scrollTop = BI.clamp(o.scrollTop, 0, this._getMaxScrollTop()),
+ overscanColumnCount = o.overscanColumnCount, overscanRowCount = o.overscanRowCount;
+
+ if (height > 0 && width > 0) {
+ var visibleColumnIndices = this._columnSizeAndPositionManager.getVisibleCellRange(width, scrollLeft);
+ var visibleRowIndices = this._rowSizeAndPositionManager.getVisibleCellRange(height, scrollTop);
+
+ if (BI.isEmpty(visibleColumnIndices) || BI.isEmpty(visibleRowIndices)) {
+ return;
+ }
+ var horizontalOffsetAdjustment = this._columnSizeAndPositionManager.getOffsetAdjustment(width, scrollLeft);
+ var verticalOffsetAdjustment = this._rowSizeAndPositionManager.getOffsetAdjustment(height, scrollTop);
+
+ this._renderedColumnStartIndex = visibleColumnIndices.start;
+ this._renderedColumnStopIndex = visibleColumnIndices.stop;
+ this._renderedRowStartIndex = visibleRowIndices.start;
+ this._renderedRowStopIndex = visibleRowIndices.stop;
+
+ var overscanColumnIndices = this._getOverscanIndices(this.columnCount, overscanColumnCount, this._renderedColumnStartIndex, this._renderedColumnStopIndex);
+
+ var overscanRowIndices = this._getOverscanIndices(this.rowCount, overscanRowCount, this._renderedRowStartIndex, this._renderedRowStopIndex);
+
+ var columnStartIndex = overscanColumnIndices.overscanStartIndex;
+ var columnStopIndex = overscanColumnIndices.overscanStopIndex;
+ var rowStartIndex = overscanRowIndices.overscanStartIndex;
+ var rowStopIndex = overscanRowIndices.overscanStopIndex;
+
+ // 算区间size
+ var minRowDatum = this._rowSizeAndPositionManager.getSizeAndPositionOfCell(rowStartIndex);
+ var minColumnDatum = this._columnSizeAndPositionManager.getSizeAndPositionOfCell(columnStartIndex);
+ var maxRowDatum = this._rowSizeAndPositionManager.getSizeAndPositionOfCell(rowStopIndex);
+ var maxColumnDatum = this._columnSizeAndPositionManager.getSizeAndPositionOfCell(columnStopIndex);
+ var top = minRowDatum.offset + verticalOffsetAdjustment;
+ var left = minColumnDatum.offset + horizontalOffsetAdjustment;
+ var bottom = maxRowDatum.offset + verticalOffsetAdjustment + maxRowDatum.size;
+ var right = maxColumnDatum.offset + horizontalOffsetAdjustment + maxColumnDatum.size;
+ // 如果滚动的区间并没有超出渲染的范围
+ if (top >= this.renderRange.minY && bottom <= this.renderRange.maxY && left >= this.renderRange.minX && right <= this.renderRange.maxX) {
+ return;
+ }
+
+ var renderedCells = [], renderedKeys = {}, renderedWidgets = {};
+ var minX = this._getMaxScrollLeft(), minY = this._getMaxScrollTop(), maxX = 0, maxY = 0;
+ var count = 0;
+ for (var rowIndex = rowStartIndex; rowIndex <= rowStopIndex; rowIndex++) {
+ var rowDatum = this._rowSizeAndPositionManager.getSizeAndPositionOfCell(rowIndex);
+
+ for (var columnIndex = columnStartIndex; columnIndex <= columnStopIndex; columnIndex++) {
+ var key = rowIndex + "-" + columnIndex;
+ var columnDatum = this._columnSizeAndPositionManager.getSizeAndPositionOfCell(columnIndex);
+
+ var index = this.renderedKeys[key] && this.renderedKeys[key][2];
+ var child;
+ if (index >= 0) {
+ // if (columnDatum.size !== this.renderedCells[index]._width) {
+ // this.renderedCells[index]._width = columnDatum.size;
+ this.renderedCells[index].el.setWidth(columnDatum.size);
+ // }
+ // if (rowDatum.size !== this.renderedCells[index]._height) {
+ // this.renderedCells[index]._height = rowDatum.size;
+ this.renderedCells[index].el.setHeight(rowDatum.size);
+ // }
+ // if (this.renderedCells[index]._left !== columnDatum.offset + horizontalOffsetAdjustment) {
+ this.renderedCells[index].el.element.css("left", (columnDatum.offset + horizontalOffsetAdjustment) / BI.pixRatio + BI.pixUnit);
+ // }
+ // if (this.renderedCells[index]._top !== rowDatum.offset + verticalOffsetAdjustment) {
+ this.renderedCells[index].el.element.css("top", (rowDatum.offset + verticalOffsetAdjustment) / BI.pixRatio + BI.pixUnit);
+ // }
+ child = this.renderedCells[index].el;
+ renderedCells.push(this.renderedCells[index]);
+ } else {
+ child = BI._lazyCreateWidget(BI.extend({
+ type: "bi.label",
+ width: columnDatum.size,
+ height: rowDatum.size
+ }, o.items[rowIndex][columnIndex], {
+ cls: (o.items[rowIndex][columnIndex].cls || "") + " grid-cell" + (rowIndex === 0 ? " first-row" : "") + (columnIndex === 0 ? " first-col" : ""),
+ _rowIndex: rowIndex,
+ _columnIndex: columnIndex,
+ _left: columnDatum.offset + horizontalOffsetAdjustment,
+ _top: rowDatum.offset + verticalOffsetAdjustment
+ }), this);
+ renderedCells.push({
+ el: child,
+ left: columnDatum.offset + horizontalOffsetAdjustment,
+ top: rowDatum.offset + verticalOffsetAdjustment,
+ _left: columnDatum.offset + horizontalOffsetAdjustment,
+ _top: rowDatum.offset + verticalOffsetAdjustment
+ // _width: columnDatum.size,
+ // _height: rowDatum.size
+ });
+ }
+ minX = Math.min(minX, columnDatum.offset + horizontalOffsetAdjustment);
+ maxX = Math.max(maxX, columnDatum.offset + horizontalOffsetAdjustment + columnDatum.size);
+ minY = Math.min(minY, rowDatum.offset + verticalOffsetAdjustment);
+ maxY = Math.max(maxY, rowDatum.offset + verticalOffsetAdjustment + rowDatum.size);
+ renderedKeys[key] = [rowIndex, columnIndex, count];
+ renderedWidgets[count] = child;
+ count++;
+ }
+ }
+ // 已存在的, 需要添加的和需要删除的
+ var existSet = {}, addSet = {}, deleteArray = [];
+ BI.each(renderedKeys, function (i, key) {
+ if (self.renderedKeys[i]) {
+ existSet[i] = key;
+ } else {
+ addSet[i] = key;
+ }
+ });
+ BI.each(this.renderedKeys, function (i, key) {
+ if (existSet[i]) {
+ return;
+ }
+ if (addSet[i]) {
+ return;
+ }
+ deleteArray.push(key[2]);
+ });
+ BI.each(deleteArray, function (i, index) {
+ // 性能优化,不调用destroy方法防止触发destroy事件
+ self.renderedCells[index].el._destroy();
+ });
+ var addedItems = [];
+ BI.each(addSet, function (index, key) {
+ addedItems.push(renderedCells[key[2]]);
+ });
+ // 与listview一样, 给上下文
+ this.container.addItems(addedItems, this);
+ // 拦截父子级关系
+ this.container._children = renderedWidgets;
+ this.container.attr("items", renderedCells);
+ this.renderedCells = renderedCells;
+ this.renderedKeys = renderedKeys;
+ this.renderRange = {minX: minX, minY: minY, maxX: maxX, maxY: maxY};
+ }
+ },
+
+ _getMaxScrollLeft: function () {
+ return Math.max(0, this._getContainerWidth() - this.options.width + (this.options.overflowX ? BI.DOM.getScrollWidth() : 0));
+ },
+
+ _getMaxScrollTop: function () {
+ return Math.max(0, this._getContainerHeight() - this.options.height + (this.options.overflowY ? BI.DOM.getScrollWidth() : 0));
+ },
+
+ _getContainerWidth: function () {
+ return this.columnCount * this.options.estimatedColumnSize;
+ },
+
+ _getContainerHeight: function () {
+ return this.rowCount * this.options.estimatedRowSize;
+ },
+
+ _populate: function (items) {
+ var self = this, o = this.options;
+ this._reRange();
+ if (items && items !== this.options.items) {
+ this.options.items = items;
+ this._calculateSizeAndPositionData();
+ }
+ this.container.setWidth(this._getContainerWidth());
+ this.container.setHeight(this._getContainerHeight());
+
+ // 元素未挂载时不能设置scrollTop
+ this._debounceRelease();
+ try {
+ this.element.scrollTop(o.scrollTop);
+ this.element.scrollLeft(o.scrollLeft);
+ } catch (e) {
+ }
+ this._calculateChildrenToRender();
+ },
+
+ setScrollLeft: function (scrollLeft) {
+ if (this.options.scrollLeft === scrollLeft) {
+ return;
+ }
+ this._scrollLock = true;
+ this.options.scrollLeft = BI.clamp(scrollLeft || 0, 0, this._getMaxScrollLeft());
+ this._debounceRelease();
+ this.element.scrollLeft(this.options.scrollLeft);
+ this._calculateChildrenToRender();
+ },
+
+ setScrollTop: function (scrollTop) {
+ if (this.options.scrollTop === scrollTop) {
+ return;
+ }
+ this._scrollLock = true;
+ this.options.scrollTop = BI.clamp(scrollTop || 0, 0, this._getMaxScrollTop());
+ this._debounceRelease();
+ this.element.scrollTop(this.options.scrollTop);
+ this._calculateChildrenToRender();
+ },
+
+ setColumnCount: function (columnCount) {
+ this.options.columnCount = columnCount;
+ },
+
+ setRowCount: function (rowCount) {
+ this.options.rowCount = rowCount;
+ },
+
+ setOverflowX: function (b) {
+ var self = this;
+ if (this.options.overflowX !== !!b) {
+ this.options.overflowX = !!b;
+ BI.nextTick(function () {
+ self.element.css({overflowX: b ? "auto" : "hidden"});
+ });
+ }
+ },
+
+ setOverflowY: function (b) {
+ var self = this;
+ if (this.options.overflowY !== !!b) {
+ this.options.overflowY = !!b;
+ BI.nextTick(function () {
+ self.element.css({overflowY: b ? "auto" : "hidden"});
+ });
+ }
+ },
+
+ getScrollLeft: function () {
+ return this.options.scrollLeft;
+ },
+
+ getScrollTop: function () {
+ return this.options.scrollTop;
+ },
+
+ getMaxScrollLeft: function () {
+ return this._getMaxScrollLeft();
+ },
+
+ getMaxScrollTop: function () {
+ return this._getMaxScrollTop();
+ },
+
+ setEstimatedColumnSize: function (width) {
+ this.options.estimatedColumnSize = width;
+ },
+
+ setEstimatedRowSize: function (height) {
+ this.options.estimatedRowSize = height;
+ },
+
+ // 重新计算children
+ _reRange: function () {
+ this.renderRange = {};
+ },
+
+ _clearChildren: function () {
+ this.container._children = {};
+ this.container.attr("items", []);
+ },
+
+ restore: function () {
+ BI.each(this.renderedCells, function (i, cell) {
+ cell.el._destroy();
+ });
+ this._clearChildren();
+ this.renderedCells = [];
+ this.renderedKeys = [];
+ this.renderRange = {};
+ this._scrollLock = false;
+ },
+
+ populate: function (items) {
+ if (items && items !== this.options.items) {
+ this.restore();
+ }
+ this._populate(items);
+ }
+});
+BI.GridView.EVENT_SCROLL = "EVENT_SCROLL";
+BI.shortcut("bi.grid_view", BI.GridView);
diff --git a/src/main/resources/com/fr/fineui/base/layer/__test__/layer.popover.test.js b/src/main/resources/com/fr/fineui/base/layer/__test__/layer.popover.test.js
new file mode 100644
index 0000000..1830744
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/layer/__test__/layer.popover.test.js
@@ -0,0 +1,33 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/3/17
+ */
+
+describe("PopoverTest", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("BarPopover", function (done) {
+ var id = BI.UUID();
+ BI.Popovers.remove(id);
+ BI.Popovers.create(id, {
+ type: "bi.bar_popover",
+ size: "normal",
+ header: {
+ type: "bi.label",
+ text: "这个是header"
+ },
+ body: {
+ type: "bi.label",
+ text: "这个是body"
+ }
+ }).open(id);
+ BI.delay(function () {
+ expect(BI.Widget._renderEngine.createElement("body").find(".bi-popup-view .bi-z-index-mask").length).to.equal(1);
+ BI.Popovers.remove(id);
+ done();
+ }, 100);
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/base/layer/layer.popover.js b/src/main/resources/com/fr/fineui/base/layer/layer.popover.js
new file mode 100644
index 0000000..87bd2d4
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/layer/layer.popover.js
@@ -0,0 +1,272 @@
+/**
+ * Popover弹出层,
+ * @class BI.Popover
+ * @extends BI.Widget
+ */
+BI.Popover = BI.inherit(BI.Widget, {
+ _constant: {
+ SIZE: {
+ SMALL: "small",
+ NORMAL: "normal",
+ BIG: "big",
+ },
+ MAX_HEIGHT: 600
+ },
+
+ props: {
+ baseCls: "bi-popover bi-card bi-border-radius",
+ size: "normal", // small, normal, big
+ logic: {
+ dynamic: false,
+ },
+ header: null,
+ headerHeight: 40,
+ body: null,
+ footer: null,
+ footerHeight: 44,
+ closable: true, // BI-40839 是否显示右上角的关闭按钮
+ bodyHgap: 20,
+ bodyTgap: 10
+ },
+
+ render: function () {
+ var self = this; var o = this.options;
+ var c = this._constant;
+ this.startX = 0;
+ this.startY = 0;
+ var size = this._calculateSize();
+ this.tracker = new BI.MouseMoveTracker(function (deltaX, deltaY) {
+ var W = BI.Widget._renderEngine.createElement("body").width();
+ var H = BI.Widget._renderEngine.createElement("body").height();
+ self.startX += deltaX;
+ self.startY += deltaY;
+ self.element.css({
+ left: BI.clamp(self.startX, 0, W - self.element.width()) + "px",
+ top: BI.clamp(self.startY, 0, H - self.element.height()) + "px",
+ });
+ // BI-12134 没有什么特别好的方法
+ BI.Resizers._resize({
+ target: self.element[0],
+ });
+ }, function () {
+ self.tracker.releaseMouseMoves();
+ }, _global);
+ var items = [{
+ el: {
+ type: "bi.htape",
+ cls: "bi-message-title bi-header-background",
+ ref: function (_ref) {
+ self.dragger = _ref;
+ },
+ items: [{
+ type: "bi.absolute",
+ items: [{
+ el: BI.isPlainObject(o.header) ? BI.extend({}, o.header, {
+ extraCls: "bi-font-bold",
+ }) : {
+ type: "bi.label",
+ cls: "bi-font-bold",
+ height: o.headerHeight,
+ text: o.header,
+ title: o.header,
+ textAlign: "left",
+ },
+ left: 20,
+ top: 0,
+ right: 0,
+ bottom: 0,
+ }],
+ }, {
+ el: o.closable ? {
+ type: "bi.icon_button",
+ cls: "bi-message-close close-font",
+ height: o.headerHeight,
+ handler: function () {
+ self.close();
+ },
+ } : {
+ type: "bi.layout",
+ },
+ width: 56,
+ }],
+ height: o.headerHeight,
+ },
+ height: o.headerHeight,
+ }, o.logic.dynamic ? {
+ el: {
+ type: "bi.vertical",
+ scrolly: true,
+ cls: "popover-body",
+ ref: function () {
+ self.body = this;
+ },
+ css: {
+ "max-height": this._getSuitableBodyHeight(c.MAX_HEIGHT - o.headerHeight - (o.footer ? o.footerHeight : 0) - o.bodyTgap),
+ "min-height": this._getSuitableBodyHeight(size.height - o.headerHeight - (o.footer ? o.footerHeight : 0) - o.bodyTgap),
+ },
+ items: [{
+ el: o.body,
+ }],
+ },
+ hgap: o.bodyHgap,
+ tgap: o.bodyTgap,
+ } : {
+ el: {
+ type: "bi.absolute",
+ items: [{
+ el: o.body,
+ left: o.bodyHgap,
+ top: o.bodyTgap,
+ right: o.bodyHgap,
+ bottom: 0,
+ }],
+ },
+ }];
+ if (o.footer) {
+ items.push({
+ el: {
+ type: "bi.absolute",
+ items: [{
+ el: o.footer,
+ left: 20,
+ top: 0,
+ right: 20,
+ bottom: 0,
+ }],
+ height: o.footerHeight,
+ },
+ height: o.footerHeight,
+ });
+ }
+
+ return BI.extend({
+ type: o.logic.dynamic ? "bi.vertical" : "bi.vtape",
+ items: items,
+ width: this._getSuitableWidth(size.width),
+ }, o.logic.dynamic ? {
+ type: "bi.vertical",
+ scrolly: false,
+ } : {
+ type: "bi.vtape",
+ height: this._getSuitableHeight(size.height),
+ });
+ },
+
+ // mounted之后绑定事件
+ mounted: function () {
+ var self = this; var o = this.options;
+ this.dragger.element.mousedown(function (e) {
+ var pos = self.element.offset();
+ self.startX = pos.left;
+ self.startY = pos.top;
+ self.tracker.captureMouseMoves(e);
+ });
+ },
+
+ _getSuitableBodyHeight: function (height) {
+ var o = this.options;
+ var c = this._constant;
+ return BI.clamp(height, 0, BI.Widget._renderEngine.createElement("body")[0].clientHeight - o.headerHeight - (o.footer ? o.footerHeight : 0) - o.bodyTgap);
+ },
+
+ _getSuitableHeight: function (height) {
+ return BI.clamp(height, 0, BI.Widget._renderEngine.createElement("body")[0].clientHeight);
+ },
+
+ _getSuitableWidth: function (width) {
+ return BI.clamp(width, 0, BI.Widget._renderEngine.createElement("body").width());
+ },
+
+ _calculateSize: function () {
+ var o = this.options;
+ var size = {};
+ if (BI.isNotNull(o.size)) {
+ switch (o.size) {
+ case this._constant.SIZE.SMALL:
+ size.width = 450;
+ size.height = 200;
+ size.type = "small";
+ break;
+ case this._constant.SIZE.BIG:
+ size.width = 900;
+ size.height = 500;
+ size.type = "big";
+ break;
+ default:
+ size.width = 550;
+ size.height = 500;
+ size.type = "default";
+ }
+ }
+
+ return {
+ width: o.width || size.width,
+ height: o.height || size.height,
+ type: size.type || "default",
+ };
+ },
+
+ hide: function () {
+
+ },
+
+ open: function () {
+ this.show();
+ this.fireEvent(BI.Popover.EVENT_OPEN, arguments);
+ },
+
+ close: function () {
+ this.hide();
+ this.fireEvent(BI.Popover.EVENT_CLOSE, arguments);
+ },
+
+ setZindex: function (zindex) {
+ this.element.css({ "z-index": zindex });
+ },
+
+ destroyed: function () {},
+});
+
+BI.shortcut("bi.popover", BI.Popover);
+
+BI.BarPopover = BI.inherit(BI.Popover, {
+ _defaultConfig: function () {
+ return BI.extend(BI.BarPopover.superclass._defaultConfig.apply(this, arguments), {
+ btns: [BI.i18nText("BI-Basic_Sure"), BI.i18nText("BI-Basic_Cancel")],
+ });
+ },
+
+ beforeCreate: function () {
+ var self = this; var o = this.options;
+ o.footer || (o.footer = {
+ type: "bi.right_vertical_adapt",
+ lgap: 10,
+ items: [{
+ type: "bi.button",
+ text: this.options.btns[1],
+ value: 1,
+ level: "ignore",
+ handler: function (v) {
+ self.fireEvent(BI.Popover.EVENT_CANCEL, v);
+ self.close(v);
+ },
+ }, {
+ type: "bi.button",
+ text: this.options.btns[0],
+ warningTitle: o.warningTitle,
+ value: 0,
+ handler: function (v) {
+ self.fireEvent(BI.Popover.EVENT_CONFIRM, v);
+ self.close(v);
+ },
+ }],
+ });
+ },
+});
+
+BI.shortcut("bi.bar_popover", BI.BarPopover);
+
+BI.Popover.EVENT_CLOSE = "EVENT_CLOSE";
+BI.Popover.EVENT_OPEN = "EVENT_OPEN";
+BI.Popover.EVENT_CANCEL = "EVENT_CANCEL";
+BI.Popover.EVENT_CONFIRM = "EVENT_CONFIRM";
diff --git a/src/main/resources/com/fr/fineui/base/layer/layer.popup.js b/src/main/resources/com/fr/fineui/base/layer/layer.popup.js
new file mode 100644
index 0000000..66db79e
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/layer/layer.popup.js
@@ -0,0 +1,180 @@
+/**
+ * 下拉框弹出层, zIndex在1000w
+ * @class BI.PopupView
+ * @extends BI.Widget
+ */
+BI.PopupView = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ return BI.extend(BI.PopupView.superclass._defaultConfig.apply(this, arguments), {
+ _baseCls: "bi-popup-view",
+ maxWidth: "auto",
+ minWidth: 100,
+ // maxHeight: 200,
+ minHeight: 24,
+ lgap: 0,
+ rgap: 0,
+ tgap: 0,
+ bgap: 0,
+ vgap: 0,
+ hgap: 0,
+ innerVGap: 0,
+ innerHGap: 0,
+ direction: BI.Direction.Top, // 工具栏的方向
+ stopEvent: false, // 是否停止mousedown、mouseup事件
+ stopPropagation: false, // 是否停止mousedown、mouseup向上冒泡
+ logic: {
+ dynamic: true
+ },
+
+ tool: false, // 自定义工具栏
+ tabs: [], // 导航栏
+ buttons: [], // toolbar栏
+
+ el: {
+ type: "bi.button_group",
+ items: [],
+ chooseType: 0,
+ behaviors: {},
+ layouts: [{
+ type: "bi.vertical"
+ }]
+ }
+ });
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ var fn = function (e) {
+ e.stopPropagation();
+ }, stop = function (e) {
+ e.stopEvent();
+ return false;
+ };
+ this.element.css({
+ "z-index": BI.zIndex_popup,
+ "min-width": BI.isNumeric(o.minWidth) ? (o.minWidth / BI.pixRatio + BI.pixUnit) : o.minWidth,
+ "max-width": BI.isNumeric(o.maxWidth) ? (o.maxWidth / BI.pixRatio + BI.pixUnit) : o.maxWidth
+ }).bind({ click: fn });
+
+ this.element.bind("mousewheel", fn);
+
+ o.stopPropagation && this.element.bind({ mousedown: fn, mouseup: fn, mouseover: fn });
+ o.stopEvent && this.element.bind({ mousedown: stop, mouseup: stop, mouseover: stop });
+ this.tool = this._createTool();
+ this.tab = this._createTab();
+ this.view = this._createView();
+ this.toolbar = this._createToolBar();
+
+ this.view.on(BI.Controller.EVENT_CHANGE, function (type) {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ if (type === BI.Events.CLICK) {
+ self.fireEvent(BI.PopupView.EVENT_CHANGE);
+ }
+ });
+
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic(BI.LogicFactory.createLogicTypeByDirection(o.direction), BI.extend({}, o.logic, {
+ scrolly: false,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap,
+ vgap: o.vgap,
+ hgap: o.hgap,
+ items: BI.LogicFactory.createLogicItemsByDirection(o.direction,
+ BI.extend({
+ cls: "list-view-outer bi-card list-view-shadow"
+ }, BI.LogicFactory.createLogic(BI.LogicFactory.createLogicTypeByDirection(o.direction), BI.extend({}, o.logic, {
+ items: BI.LogicFactory.createLogicItemsByDirection(o.direction, this.tool, this.tab, this.view, this.toolbar)
+ })))
+ )
+ }))));
+ },
+
+ _createView: function () {
+ var o = this.options;
+ this.button_group = BI.createWidget(o.el, { type: "bi.button_group", value: o.value });
+ this.button_group.element.css({
+ "min-height": BI.isNumeric(o.minHeight) ? (o.minHeight / BI.pixRatio + BI.pixUnit) : o.minHeight,
+ "padding-top": o.innerVGap / BI.pixRatio + BI.pixUnit,
+ "padding-bottom": o.innerVGap / BI.pixRatio + BI.pixUnit,
+ "padding-left": o.innerHGap / BI.pixRatio + BI.pixUnit,
+ "padding-right": o.innerHGap / BI.pixRatio + BI.pixUnit,
+ });
+ return this.button_group;
+ },
+
+ _createTool: function () {
+ var o = this.options;
+ if (false === o.tool) {
+ return;
+ }
+ return BI.createWidget(o.tool);
+ },
+
+ _createTab: function () {
+ var o = this.options;
+ if (o.tabs.length === 0) {
+ return;
+ }
+ return BI.createWidget({
+ type: "bi.center",
+ cls: "list-view-tab",
+ height: 25,
+ items: o.tabs,
+ value: o.value
+ });
+ },
+
+ _createToolBar: function () {
+ var o = this.options;
+ if (o.buttons.length === 0) {
+ return;
+ }
+
+ return BI.createWidget({
+ type: "bi.center",
+ cls: "list-view-toolbar bi-high-light bi-split-top",
+ height: 24,
+ items: BI.createItems(o.buttons, {
+ once: false,
+ shadow: true,
+ isShadowShowingOnSelected: true
+ })
+ });
+ },
+
+ getView: function () {
+ return this.view;
+ },
+
+ populate: function (items) {
+ this.view.populate.apply(this.view, arguments);
+ },
+
+ resetWidth: function (w) {
+ this.options.width = w;
+ this.element.width(w);
+ },
+
+ resetHeight: function (h) {
+ var tbHeight = this.toolbar ? (this.toolbar.attr("height") || 24) : 0,
+ tabHeight = this.tab ? (this.tab.attr("height") || 24) : 0,
+ toolHeight = ((this.tool && this.tool.attr("height")) || 24) * ((this.tool && this.tool.isVisible()) ? 1 : 0);
+ var resetHeight = h - tbHeight - tabHeight - toolHeight - 2 * this.options.innerVGap;
+ this.view.resetHeight ? this.view.resetHeight(resetHeight) :
+ this.view.element.css({ "max-height": resetHeight / BI.pixRatio + BI.pixUnit });
+ },
+
+ setValue: function (selectedValues) {
+ this.tab && this.tab.setValue(selectedValues);
+ this.view.setValue(selectedValues);
+ },
+
+ getValue: function () {
+ return this.view.getValue();
+ }
+});
+BI.PopupView.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.popup_view", BI.PopupView);
diff --git a/src/main/resources/com/fr/fineui/base/layer/layer.searcher.js b/src/main/resources/com/fr/fineui/base/layer/layer.searcher.js
new file mode 100644
index 0000000..12867ed
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/layer/layer.searcher.js
@@ -0,0 +1,140 @@
+/**
+ * 搜索面板
+ *
+ * Created by GUY on 2015/9/28.
+ * @class BI.SearcherView
+ * @extends BI.Pane
+ */
+
+BI.SearcherView = BI.inherit(BI.Pane, {
+ _defaultConfig: function () {
+ var conf = BI.SearcherView.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-searcher-view bi-card",
+ tipText: BI.i18nText("BI-No_Select"),
+ chooseType: BI.Selection.Single,
+
+ matcher: {// 完全匹配的构造器
+ type: "bi.button_group",
+ behaviors: {
+ redmark: function () {
+ return true;
+ }
+ },
+ items: [],
+ layouts: [{
+ type: "bi.vertical"
+ }]
+ },
+ searcher: {
+ type: "bi.button_group",
+ behaviors: {
+ redmark: function () {
+ return true;
+ }
+ },
+ items: [],
+ layouts: [{
+ type: "bi.vertical"
+ }]
+ }
+ });
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+
+ this.matcher = BI.createWidget(o.matcher, {
+ type: "bi.button_group",
+ chooseType: o.chooseType,
+ behaviors: {
+ redmark: function () {
+ return true;
+ }
+ },
+ layouts: [{
+ type: "bi.vertical"
+ }],
+ value: o.value
+ });
+ this.matcher.on(BI.Controller.EVENT_CHANGE, function (type, val, ob) {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ if (type === BI.Events.CLICK) {
+ self.fireEvent(BI.SearcherView.EVENT_CHANGE, val, ob);
+ }
+ });
+ this.spliter = BI.createWidget({
+ type: "bi.vertical",
+ height: 1,
+ hgap: 10,
+ items: [{
+ type: "bi.layout",
+ height: 1,
+ cls: "searcher-view-spliter bi-background"
+ }]
+ });
+ this.searcher = BI.createWidget(o.searcher, {
+ type: "bi.button_group",
+ chooseType: o.chooseType,
+ behaviors: {
+ redmark: function () {
+ return true;
+ }
+ },
+ layouts: [{
+ type: "bi.vertical"
+ }],
+ value: o.value
+ });
+ this.searcher.on(BI.Controller.EVENT_CHANGE, function (type, val, ob) {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ if (type === BI.Events.CLICK) {
+ self.fireEvent(BI.SearcherView.EVENT_CHANGE, val, ob);
+ }
+ });
+
+ BI.createWidget({
+ type: "bi.vertical",
+ element: this,
+ items: [this.matcher, this.spliter, this.searcher]
+ });
+ },
+
+ startSearch: function () {
+
+ },
+
+ stopSearch: function () {
+
+ },
+
+ setValue: function (v) {
+ this.matcher.setValue(v);
+ this.searcher.setValue(v);
+ },
+
+ getValue: function () {
+ return this.matcher.getValue().concat(this.searcher.getValue());
+ },
+
+ populate: function (searchResult, matchResult, keyword) {
+ searchResult || (searchResult = []);
+ matchResult || (matchResult = []);
+ this.setTipVisible(searchResult.length + matchResult.length === 0);
+ this.spliter.setVisible(BI.isNotEmptyArray(matchResult) && BI.isNotEmptyArray(searchResult));
+ this.matcher.populate(matchResult, keyword);
+ this.searcher.populate(searchResult, keyword);
+ },
+
+ empty: function () {
+ this.searcher.empty();
+ this.matcher.empty();
+ },
+
+ hasMatched: function () {
+ return this.matcher.getAllButtons().length > 0;
+ }
+});
+BI.SearcherView.EVENT_CHANGE = "EVENT_CHANGE";
+
+BI.shortcut("bi.searcher_view", BI.SearcherView);
diff --git a/src/main/resources/com/fr/fineui/base/list/__test__/listview.test.js b/src/main/resources/com/fr/fineui/base/list/__test__/listview.test.js
new file mode 100644
index 0000000..4be8993
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/list/__test__/listview.test.js
@@ -0,0 +1,48 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/3/24
+ */
+
+// TODO 展示类控件测什么没想好标记一下
+describe("ListView && VirtualList", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("ListView初始化测试", function () {
+ var a = BI.Test.createWidget({
+ type: "bi.list_view",
+ el: {
+ type: "bi.left"
+ },
+ items: BI.map(BI.range(0, 100), function (i, item) {
+ return BI.extend({}, item, {
+ type: "bi.label",
+ width: 200,
+ height: 200,
+ text: (i + 1)
+ });
+ })
+ });
+ a.destroy();
+ });
+
+
+ /**
+ * test_author_windy
+ */
+ it("VirtualList初始化测试", function () {
+ var a = BI.Test.createWidget({
+ type: "bi.virtual_list",
+ items: BI.map(BI.range(0, 100), function (i, item) {
+ return BI.extend({}, item, {
+ type: "bi.label",
+ height: 30,
+ text: (i + 1) + "." + item.text
+ });
+ })
+ });
+ a.destroy();
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/base/list/listview.js b/src/main/resources/com/fr/fineui/base/list/listview.js
new file mode 100644
index 0000000..2708e48
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/list/listview.js
@@ -0,0 +1,119 @@
+/**
+ * 表示当前对象
+ *
+ * Created by GUY on 2017/5/23.
+ * @class BI.ListView
+ * @extends BI.Widget
+ */
+BI.ListView = BI.inherit(BI.Widget, {
+ props: function () {
+ return {
+ baseCls: "bi-list-view",
+ overscanHeight: 100,
+ blockSize: 10,
+ scrollTop: 0,
+ el: {},
+ items: []
+ };
+ },
+
+ init: function () {
+ var self = this;
+ this.renderedIndex = -1;
+ this.cache = {};
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ return {
+ type: "bi.vertical",
+ items: [BI.extend({
+ type: "bi.vertical",
+ scrolly: false,
+ ref: function () {
+ self.container = this;
+ }
+ }, o.el)],
+ element: this
+ };
+ },
+
+ // mounted之后绑定事件
+ mounted: function () {
+ var self = this, o = this.options;
+ this._populate();
+ this.element.scroll(function (e) {
+ o.scrollTop = self.element.scrollTop();
+ self._calculateBlocksToRender();
+ });
+ var lastWidth = this.element.width(),
+ lastHeight = this.element.height();
+ BI.ResizeDetector.addResizeListener(this, function () {
+ var width = self.element.width(),
+ height = self.element.height();
+ if (width !== lastWidth || height !== lastHeight) {
+ lastWidth = width;
+ lastHeight = height;
+ self._calculateBlocksToRender();
+ }
+ });
+ },
+
+ _renderMoreIf: function () {
+ var self = this, o = this.options;
+ var height = this.element.height();
+ var minContentHeight = o.scrollTop + height + o.overscanHeight;
+ var index = (this.cache[this.renderedIndex] && (this.cache[this.renderedIndex].index + o.blockSize)) || 0,
+ cnt = this.renderedIndex + 1;
+ var lastHeight;
+ var getElementHeight = function () {
+ return self.container.element.height();
+ };
+ while ((lastHeight = getElementHeight()) < minContentHeight && index < o.items.length) {
+ var items = o.items.slice(index, index + o.blockSize);
+ this.container.addItems(items, this);
+ var addedHeight = getElementHeight() - lastHeight;
+ this.cache[cnt] = {
+ index: index,
+ scrollTop: lastHeight,
+ height: addedHeight
+ };
+ this.renderedIndex = cnt;
+ cnt++;
+ index += o.blockSize;
+ }
+ },
+
+ _calculateBlocksToRender: function () {
+ var o = this.options;
+ this._renderMoreIf();
+ },
+
+ _populate: function (items) {
+ var o = this.options;
+ if (items && this.options.items !== items) {
+ this.options.items = items;
+ }
+ this._calculateBlocksToRender();
+ this.element.scrollTop(o.scrollTop);
+ },
+
+ restore: function () {
+ this.renderedIndex = -1;
+ this.container.empty();
+ this.cache = {};
+ },
+
+ populate: function (items) {
+ if (items && this.options.items !== items) {
+ this.restore();
+ }
+ this._populate(items);
+ },
+
+ destroyed: function () {
+ this.restore();
+ }
+});
+BI.shortcut("bi.list_view", BI.ListView);
+
diff --git a/src/main/resources/com/fr/fineui/base/list/virtualgrouplist.js b/src/main/resources/com/fr/fineui/base/list/virtualgrouplist.js
new file mode 100644
index 0000000..95ed647
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/list/virtualgrouplist.js
@@ -0,0 +1,158 @@
+/**
+ * 同时用于virtualGroup和virtualList特性的虚拟列表
+ *
+ * Created by GUY on 2017/5/22.
+ * @class BI.VirtualList
+ * @extends BI.Widget
+ */
+BI.VirtualGroupList = BI.inherit(BI.Widget, {
+ props: function () {
+ return {
+ baseCls: "bi-virtual-group-list",
+ overscanHeight: 100,
+ blockSize: 10,
+ scrollTop: 0,
+ rowHeight: "auto",
+ items: []
+ };
+ },
+
+ init: function () {
+ var self = this;
+ this.renderedIndex = -1;
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ return {
+ type: "bi.vertical",
+ items: [{
+ type: "bi.layout",
+ ref: function () {
+ self.topBlank = this;
+ }
+ }, {
+ type: "bi.virtual_group",
+ height: o.rowHeight * o.items.length,
+ ref: function () {
+ self.container = this;
+ },
+ layouts: [{
+ type: "bi.vertical",
+ scrolly: false
+ }]
+ }, {
+ type: "bi.layout",
+ ref: function () {
+ self.bottomBlank = this;
+ }
+ }],
+ element: this
+ };
+ },
+
+ // mounted之后绑定事件
+ mounted: function () {
+ var self = this, o = this.options;
+ this._populate();
+ this._debounceRelease = BI.debounce(function () {
+ self._scrollLock = false;
+ }, 30);
+ this.element.scroll(function (e) {
+ if (self._scrollLock === true) {
+ return;
+ }
+ self._scrollLock = true;
+ o.scrollTop = self.element.scrollTop();
+ self._debounceRelease();
+ self._calculateBlocksToRender();
+ });
+ BI.ResizeDetector.addResizeListener(this, function () {
+ self._calculateBlocksToRender();
+ });
+ },
+
+ _isAutoHeight: function () {
+ return this.options.rowHeight === "auto";
+ },
+
+ _renderMoreIf: function () {
+ var self = this, o = this.options;
+ var height = this.element.height();
+ var minContentHeight = o.scrollTop + height + o.overscanHeight;
+ var index = (this.renderedIndex + 1) * o.blockSize, cnt = this.renderedIndex + 1;
+ var lastHeight;
+ var getElementHeight = function () {
+ return self.container.element.height() + self.topBlank.element.height() + self.bottomBlank.element.height();
+ };
+ while ((lastHeight = getElementHeight()) < minContentHeight && index < o.items.length) {
+ var items = o.items.slice(index, index + o.blockSize);
+ this.container.addItems(items, this);
+ var addedHeight = getElementHeight() - lastHeight;
+ this.tree.set(cnt, addedHeight);
+ this.renderedIndex = cnt;
+ cnt++;
+ index += o.blockSize;
+ }
+ },
+
+ _calculateBlocksToRender: function () {
+ var o = this.options;
+ this._isAutoHeight() && this._renderMoreIf();
+ var height = this.element.height();
+ var minContentHeightFrom = o.scrollTop - o.overscanHeight;
+ var minContentHeightTo = o.scrollTop + height + o.overscanHeight;
+ var start = this.tree.greatestLowerBound(minContentHeightFrom);
+ var end = this.tree.leastUpperBound(minContentHeightTo);
+ var items = [];
+ var topHeight = this.tree.sumTo(Math.max(-1, start - 1));
+ this.topBlank.setHeight(topHeight);
+ if (this._isAutoHeight()) {
+ for (var i = (start < 0 ? 0 : start); i <= end && i <= this.renderedIndex; i++) {
+ var index = i * o.blockSize;
+ for (var j = index; j < index + o.blockSize && j < o.items.length; j++) {
+ items.push(o.items[j]);
+ }
+ }
+ this.bottomBlank.setHeight(this.tree.sumTo(this.renderedIndex) - this.tree.sumTo(Math.min(end, this.renderedIndex)));
+ this.container.populate(items);
+ } else {
+ for (var i = (start < 0 ? 0 : start); i <= end; i++) {
+ var index = i * o.blockSize;
+ for (var j = index; j < index + o.blockSize && j < o.items.length; j++) {
+ items.push(o.items[j]);
+ }
+ }
+ this.container.element.height(o.rowHeight * o.items.length - topHeight);
+ this.container.populate(items);
+ }
+ },
+
+ _populate: function (items) {
+ var o = this.options;
+ if (items && this.options.items !== items) {
+ this.options.items = items;
+ }
+ this.tree = BI.PrefixIntervalTree.uniform(Math.ceil(o.items.length / o.blockSize), this._isAutoHeight() ? 0 : o.rowHeight * o.blockSize);
+
+ this._calculateBlocksToRender();
+ try {
+ this.element.scrollTop(o.scrollTop);
+ } catch (e) {
+ }
+ },
+
+ restore: function () {
+ this.renderedIndex = -1;
+ this.options.scrollTop = 0;
+ // 依赖于cache的占位元素也要初始化
+ this.topBlank.setHeight(0);
+ this.bottomBlank.setHeight(0);
+ },
+
+ populate: function (items) {
+ this._populate(items);
+ }
+});
+BI.shortcut("bi.virtual_group_list", BI.VirtualGroupList);
+
diff --git a/src/main/resources/com/fr/fineui/base/list/virtuallist.js b/src/main/resources/com/fr/fineui/base/list/virtuallist.js
new file mode 100644
index 0000000..7f90c76
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/list/virtuallist.js
@@ -0,0 +1,195 @@
+/**
+ * 虚拟列表
+ *
+ * Created by GUY on 2017/5/22.
+ * @class BI.VirtualList
+ * @extends BI.Widget
+ */
+BI.VirtualList = BI.inherit(BI.Widget, {
+ props: function () {
+ return {
+ baseCls: "bi-virtual-list",
+ overscanHeight: 100,
+ blockSize: 10,
+ scrollTop: 0,
+ items: []
+ };
+ },
+
+ init: function () {
+ var self = this;
+ this.renderedIndex = -1;
+ this.cache = {};
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ return {
+ type: "bi.vertical",
+ items: [{
+ type: "bi.layout",
+ ref: function () {
+ self.topBlank = this;
+ }
+ }, {
+ type: "bi.vertical",
+ scrolly: false,
+ ref: function () {
+ self.container = this;
+ }
+ }, {
+ type: "bi.layout",
+ ref: function () {
+ self.bottomBlank = this;
+ }
+ }],
+ element: this
+ };
+ },
+
+ // mounted之后绑定事件
+ mounted: function () {
+ var self = this, o = this.options;
+ this._populate();
+ this.element.scroll(function (e) {
+ o.scrollTop = self.element.scrollTop();
+ self._calculateBlocksToRender();
+ });
+ BI.ResizeDetector.addResizeListener(this, function () {
+ self._calculateBlocksToRender();
+ });
+ },
+
+ _renderMoreIf: function () {
+ var self = this, o = this.options;
+ var height = this.element.height();
+ var minContentHeight = o.scrollTop + height + o.overscanHeight;
+ var index = (this.renderedIndex + 1) * o.blockSize, cnt = this.renderedIndex + 1;
+ var lastHeight;
+ var getElementHeight = function () {
+ return self.container.element.height() + self.topBlank.element.height() + self.bottomBlank.element.height();
+ };
+ while ((lastHeight = getElementHeight()) < minContentHeight && index < o.items.length) {
+ var items = o.items.slice(index, index + o.blockSize);
+ this.container.addItems(items, this);
+ var addedHeight = getElementHeight() - lastHeight;
+ this.tree.set(cnt, addedHeight);
+ this.renderedIndex = cnt;
+ cnt++;
+ index += o.blockSize;
+ }
+ },
+
+ _calculateBlocksToRender: function () {
+ var o = this.options;
+ this._renderMoreIf();
+ var height = this.element.height();
+ var minContentHeightFrom = o.scrollTop - o.overscanHeight;
+ var minContentHeightTo = o.scrollTop + height + o.overscanHeight;
+ var start = this.tree.greatestLowerBound(minContentHeightFrom);
+ var end = this.tree.leastUpperBound(minContentHeightTo);
+ var needDestroyed = [], needMount = [];
+ for (var i = 0; i < start; i++) {
+ var index = i * o.blockSize;
+ if (!this.cache[i]) {
+ this.cache[i] = {};
+ }
+ if (!this.cache[i].destroyed) {
+ for (var j = index; j < index + o.blockSize && j < o.items.length; j++) {
+ needDestroyed.push(this.container._children[j]);
+ this.container._children[j] = null;
+ }
+ this.cache[i].destroyed = true;
+ }
+ }
+ for (var i = end + 1; i <= this.renderedIndex; i++) {
+ var index = i * o.blockSize;
+ if (!this.cache[i]) {
+ this.cache[i] = {};
+ }
+ if (!this.cache[i].destroyed) {
+ for (var j = index; j < index + o.blockSize && j < o.items.length; j++) {
+ needDestroyed.push(this.container._children[j]);
+ this.container._children[j] = null;
+ }
+ this.cache[i].destroyed = true;
+ }
+ }
+ var firstFragment = BI.Widget._renderEngine.createFragment(),
+ lastFragment = BI.Widget._renderEngine.createFragment();
+ var currentFragment = firstFragment;
+ for (var i = (start < 0 ? 0 : start); i <= end && i <= this.renderedIndex; i++) {
+ var index = i * o.blockSize;
+ if (!this.cache[i]) {
+ this.cache[i] = {};
+ }
+ if (!this.cache[i].destroyed) {
+ currentFragment = lastFragment;
+ }
+ if (this.cache[i].destroyed === true) {
+ for (var j = index; j < index + o.blockSize && j < o.items.length; j++) {
+ var w = this.container._addElement(j, o.items[j], this);
+ needMount.push(w);
+ currentFragment.appendChild(w.element[0]);
+ }
+ this.cache[i].destroyed = false;
+ }
+ }
+ this.container.element.prepend(firstFragment);
+ this.container.element.append(lastFragment);
+ this.topBlank.setHeight(this.tree.sumTo(Math.max(-1, start - 1)));
+ this.bottomBlank.setHeight(this.tree.sumTo(this.renderedIndex) - this.tree.sumTo(Math.min(end, this.renderedIndex)));
+ BI.each(needMount, function (i, child) {
+ child && child._mount();
+ });
+ BI.each(needDestroyed, function (i, child) {
+ child && child._destroy();
+ });
+ },
+
+ _populate: function (items) {
+ var o = this.options;
+ if (items && this.options.items !== items) {
+ this.options.items = items;
+ }
+ this.tree = BI.PrefixIntervalTree.empty(Math.ceil(o.items.length / o.blockSize));
+
+ this._calculateBlocksToRender();
+ try {
+ this.element.scrollTop(o.scrollTop);
+ } catch (e) {
+ }
+ },
+
+ _clearChildren: function () {
+ BI.each(this.container._children, function (i, cell) {
+ cell && cell._destroy();
+ });
+ this.container._children = {};
+ this.container.attr("items", []);
+ },
+
+ restore: function () {
+ this.renderedIndex = -1;
+ this._clearChildren();
+ this.cache = {};
+ this.options.scrollTop = 0;
+ // 依赖于cache的占位元素也要初始化
+ this.topBlank.setHeight(0);
+ this.bottomBlank.setHeight(0);
+ },
+
+ populate: function (items) {
+ if (items && this.options.items !== items) {
+ this.restore();
+ }
+ this._populate(items);
+ },
+
+ destroyed: function () {
+ this.cache = {};
+ this.renderedIndex = -1;
+ }
+});
+BI.shortcut("bi.virtual_list", BI.VirtualList);
+
diff --git a/src/main/resources/com/fr/fineui/base/pager/pager.js b/src/main/resources/com/fr/fineui/base/pager/pager.js
new file mode 100644
index 0000000..5967271
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/pager/pager.js
@@ -0,0 +1,289 @@
+/**
+ * 分页控件
+ *
+ * Created by GUY on 2015/8/31.
+ * @class BI.Pager
+ * @extends BI.Widget
+ */
+BI.Pager = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ return BI.extend(BI.Pager.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-pager",
+ behaviors: {},
+ layouts: [{
+ type: "bi.horizontal",
+ hgap: 10,
+ vgap: 0
+ }],
+
+ dynamicShow: true, // 是否动态显示上一页、下一页、首页、尾页, 若为false,则指对其设置使能状态
+ // dynamicShow为false时以下两个有用
+ dynamicShowFirstLast: false, // 是否动态显示首页、尾页
+ dynamicShowPrevNext: false, // 是否动态显示上一页、下一页
+ pages: false, // 总页数
+ curr: function () {
+ return 1;
+ }, // 初始化当前页
+ groups: 0, // 连续显示分页数
+ jump: BI.emptyFn, // 分页的回调函数
+
+ first: false, // 是否显示首页
+ last: false, // 是否显示尾页
+ prev: "上一页",
+ next: "下一页",
+
+ firstPage: 1,
+ lastPage: function () { // 在万不得已时才会调用这个函数获取最后一页的页码, 主要作用于setValue方法
+ return 1;
+ },
+ hasPrev: BI.emptyFn, // pages不可用时有效
+ hasNext: BI.emptyFn // pages不可用时有效
+ });
+ },
+
+ render: function () {
+ var self = this;
+ this.currPage = BI.result(this.options, "curr");
+ // 翻页太灵敏
+ // this._lock = false;
+ // this._debouce = BI.debounce(function () {
+ // self._lock = false;
+ // }, 300);
+ this._populate();
+ },
+
+ _populate: function () {
+ var self = this, o = this.options, view = [], dict = {};
+ this.empty();
+ var pages = BI.result(o, "pages");
+ var curr = BI.result(this, "currPage");
+ var groups = BI.result(o, "groups");
+ var first = BI.result(o, "first");
+ var last = BI.result(o, "last");
+ var prev = BI.result(o, "prev");
+ var next = BI.result(o, "next");
+
+ if (pages === false) {
+ groups = 0;
+ first = false;
+ last = false;
+ } else {
+ groups > pages && (groups = pages);
+ }
+
+ // 计算当前组
+ dict.index = Math.ceil((curr + ((groups > 1 && groups !== pages) ? 1 : 0)) / (groups === 0 ? 1 : groups));
+
+ // 当前页非首页,则输出上一页
+ if (((!o.dynamicShow && !o.dynamicShowPrevNext) || curr > 1) && prev !== false) {
+ if (BI.isKey(prev)) {
+ view.push({
+ text: prev,
+ value: "prev",
+ disabled: pages === false ? o.hasPrev(curr) === false : !(curr > 1 && prev !== false)
+ });
+ } else {
+ view.push(BI.extend({
+ disabled: pages === false ? o.hasPrev(curr) === false : !(curr > 1 && prev !== false)
+ }, prev));
+ }
+ }
+
+ // 当前组非首组,则输出首页
+ if (((!o.dynamicShow && !o.dynamicShowFirstLast) || (dict.index > 1 && groups !== 0)) && first) {
+ view.push({
+ text: first,
+ value: "first",
+ disabled: !(dict.index > 1 && groups !== 0)
+ });
+ if (dict.index > 1 && groups !== 0) {
+ view.push({
+ type: "bi.label",
+ cls: "page-ellipsis",
+ text: "\u2026"
+ });
+ }
+ }
+
+ // 输出当前页组
+ dict.poor = Math.floor((groups - 1) / 2);
+ dict.start = dict.index > 1 ? curr - dict.poor : 1;
+ dict.end = dict.index > 1 ? (function () {
+ var max = curr + (groups - dict.poor - 1);
+ return max > pages ? pages : max;
+ }()) : groups;
+ if (dict.end - dict.start < groups - 1) { // 最后一组状态
+ dict.start = dict.end - groups + 1;
+ }
+ var s = dict.start, e = dict.end;
+ if (first && last && (dict.index > 1 && groups !== 0) && (pages > groups && dict.end < pages && groups !== 0)) {
+ s++;
+ e--;
+ }
+ for (; s <= e; s++) {
+ if (s === curr) {
+ view.push({
+ text: s,
+ value: s,
+ selected: true
+ });
+ } else {
+ view.push({
+ text: s,
+ value: s
+ });
+ }
+ }
+
+ // 总页数大于连续分页数,且当前组最大页小于总页,输出尾页
+ if (((!o.dynamicShow && !o.dynamicShowFirstLast) || (pages > groups && dict.end < pages && groups !== 0)) && last) {
+ if (pages > groups && dict.end < pages && groups !== 0) {
+ view.push({
+ type: "bi.label",
+ cls: "page-ellipsis",
+ text: "\u2026"
+ });
+ }
+ view.push({
+ text: last,
+ value: "last",
+ disabled: !(pages > groups && dict.end < pages && groups !== 0)
+ });
+ }
+
+ // 当前页不为尾页时,输出下一页
+ dict.flow = !prev && groups === 0;
+ if (((!o.dynamicShow && !o.dynamicShowPrevNext) && next) || (curr !== pages && next || dict.flow)) {
+ view.push((function () {
+ if (BI.isKey(next)) {
+ if (pages === false) {
+ return {text: next, value: "next", disabled: o.hasNext(curr) === false};
+ }
+ return (dict.flow && curr === pages)
+ ?
+ {text: next, value: "next", disabled: true}
+ :
+ {text: next, value: "next", disabled: !(curr !== pages && next || dict.flow)};
+ }
+ return BI.extend({
+ disabled: pages === false ? o.hasNext(curr) === false : !(curr !== pages && next || dict.flow)
+ }, next);
+
+ }()));
+ }
+
+ this.button_group = BI.createWidget({
+ type: "bi.button_group",
+ element: this,
+ items: BI.createItems(view, {
+ cls: "bi-list-item-select bi-border-radius",
+ height: 23,
+ hgap: 10,
+ stopPropagation: true
+ }),
+ behaviors: o.behaviors,
+ layouts: o.layouts
+ });
+ this.button_group.on(BI.Controller.EVENT_CHANGE, function (type, value, obj) {
+ // if (self._lock === true) {
+ // return;
+ // }
+ // self._lock = true;
+ // self._debouce();
+ if (type === BI.Events.CLICK) {
+ var v = self.button_group.getValue()[0];
+ switch (v) {
+ case "first":
+ self.currPage = 1;
+ break;
+ case "last":
+ self.currPage = pages;
+ break;
+ case "prev":
+ self.currPage--;
+ break;
+ case "next":
+ self.currPage++;
+ break;
+ default:
+ self.currPage = v;
+ break;
+ }
+ o.jump.apply(self, [{
+ pages: pages,
+ curr: self.currPage
+ }]);
+ self._populate();
+ self.fireEvent(BI.Pager.EVENT_CHANGE, obj);
+ }
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ this.fireEvent(BI.Pager.EVENT_AFTER_POPULATE);
+ },
+
+ getCurrentPage: function () {
+ return this.currPage;
+ },
+
+ setAllPages: function (pages) {
+ this.options.pages = pages;
+ },
+
+ hasPrev: function (v) {
+ v || (v = 1);
+ var o = this.options;
+ var pages = this.options.pages;
+ return pages === false ? o.hasPrev(v) : v > 1;
+ },
+
+ hasNext: function (v) {
+ v || (v = 1);
+ var o = this.options;
+ var pages = this.options.pages;
+ return pages === false ? o.hasNext(v) : v < pages;
+ },
+
+ setValue: function (v) {
+ var o = this.options;
+ v = v || 0;
+ v = v < 1 ? 1 : v;
+ if (o.pages === false) {
+ var lastPage = BI.result(o, "lastPage"), firstPage = 1;
+ this.currPage = v > lastPage ? lastPage : ((firstPage = BI.result(o, "firstPage")), (v < firstPage ? firstPage : v));
+ } else {
+ v = v > o.pages ? o.pages : v;
+ this.currPage = v;
+ }
+ this._populate();
+ },
+
+ getValue: function () {
+ var val = this.button_group.getValue()[0];
+ switch (val) {
+ case "prev":
+ return -1;
+ case "next":
+ return 1;
+ case "first":
+ return BI.MIN;
+ case "last":
+ return BI.MAX;
+ default :
+ return val;
+ }
+ },
+
+ attr: function (key, value) {
+ BI.Pager.superclass.attr.apply(this, arguments);
+ if (key === "curr") {
+ this.currPage = BI.result(this.options, "curr");
+ }
+ },
+
+ populate: function () {
+ this._populate();
+ }
+});
+BI.Pager.EVENT_CHANGE = "EVENT_CHANGE";
+BI.Pager.EVENT_AFTER_POPULATE = "EVENT_AFTER_POPULATE";
+BI.shortcut("bi.pager", BI.Pager);
diff --git a/src/main/resources/com/fr/fineui/base/single/0.single.js b/src/main/resources/com/fr/fineui/base/single/0.single.js
new file mode 100644
index 0000000..070935e
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/0.single.js
@@ -0,0 +1,207 @@
+/**
+ * guy
+ * 这仅仅只是一个超类, 所有简单控件的基类
+ * 1、类的控制,
+ * 2、title的控制
+ * 3、文字超过边界显示3个点
+ * 4、cursor默认pointor
+ * @class BI.Single
+ * @extends BI.Widget
+ * @abstract
+ */
+BI.Single = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ var conf = BI.Single.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ readonly: false,
+ title: null,
+ warningTitle: null,
+ tipType: null, // success或warning
+ belowMouse: false // title是否跟随鼠标
+ });
+ },
+
+ _showToolTip: function (e, opt) {
+ opt || (opt = {});
+ var self = this, o = this.options;
+ var type = this.getTipType() || (this.isEnabled() ? "success" : "warning");
+ var title = type === "success" ? this.getTitle() : (this.getWarningTitle() || this.getTitle());
+ if (BI.isKey(title)) {
+ BI.Tooltips.show(e, this.getName(), title, type, this, opt);
+ if (o.action) {
+ BI.Actions.runAction(o.action, "hover", o, this);
+ }
+ BI.Actions.runGlobalAction("hover", o, this);
+ }
+ },
+
+ _hideTooltip: function () {
+ var self = this;
+ var tooltip = BI.Tooltips.get(this.getName());
+ if (BI.isNotNull(tooltip)) {
+ tooltip.element.fadeOut(200, function () {
+ BI.Tooltips.remove(self.getName());
+ });
+ }
+ },
+
+ _init: function () {
+ BI.Single.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ if (BI.isKey(o.title) || BI.isKey(o.warningTitle)
+ || BI.isFunction(o.title) || BI.isFunction(o.warningTitle)) {
+ this.enableHover({
+ belowMouse: o.belowMouse,
+ container: o.container
+ });
+ }
+ },
+
+ _clearTimeOut: function () {
+ if (BI.isNotNull(this.showTimeout)) {
+ clearTimeout(this.showTimeout);
+ this.showTimeout = null;
+ }
+ if (BI.isNotNull(this.hideTimeout)) {
+ clearTimeout(this.hideTimeout);
+ this.hideTimeout = null;
+ }
+ },
+
+ enableHover: function (opt) {
+ opt || (opt = {});
+ var self = this;
+ if (!this._hoverBinded) {
+ this.element.on("mouseenter.title" + this.getName(), function (e) {
+ self._e = e;
+ if (self.getTipType() === "warning" || (BI.isKey(self.getWarningTitle()) && !self.isEnabled())) {
+ self.showTimeout = BI.delay(function () {
+ if (BI.isNotNull(self.showTimeout)) {
+ self._showToolTip(self._e || e, opt);
+ }
+ }, 200);
+ } else if (self.getTipType() === "success" || self.isEnabled()) {
+ self.showTimeout = BI.delay(function () {
+ if (BI.isNotNull(self.showTimeout)) {
+ self._showToolTip(self._e || e, opt);
+ }
+ }, 500);
+ }
+ });
+ this.element.on("mousemove.title" + this.getName(), function (e) {
+ self._e = e;
+ if (BI.isNotNull(self.showTimeout)) {
+ clearTimeout(self.showTimeout);
+ self.showTimeout = null;
+ }
+ if (BI.isNull(self.hideTimeout)) {
+ self.hideTimeout = BI.delay(function () {
+ if (BI.isNotNull(self.hideTimeout)) {
+ self._hideTooltip();
+ }
+ }, 500);
+ }
+
+ self.showTimeout = BI.delay(function () {
+ // DEC-5321 IE下如果回调已经进入事件队列,clearTimeout将不会起作用
+ if (BI.isNotNull(self.showTimeout)) {
+ if (BI.isNotNull(self.hideTimeout)) {
+ clearTimeout(self.hideTimeout);
+ self.hideTimeout = null;
+ }
+ // CHART-10611 在拖拽的情况下, 鼠标拖拽着元素离开了拖拽元素的容器,但是子元素在dom结构上仍然属于容器
+ // 这样会认为鼠标仍然在容器中, 500ms内放开的话,会在容器之外显示鼠标停留处显示容器的title
+ if (self.element.__isMouseInBounds__(self._e || e)) {
+ self._showToolTip(self._e || e, opt);
+ }
+ }
+ }, 500);
+
+ });
+ this.element.on("mouseleave.title" + this.getName(), function (e) {
+ self._e = null;
+ self._clearTimeOut();
+ self._hideTooltip();
+ });
+ this._hoverBinded = true;
+ }
+ },
+
+ disabledHover: function () {
+ // 取消hover事件
+ this._clearTimeOut();
+ this._hideTooltip();
+ this.element.unbind("mouseenter.title" + this.getName())
+ .unbind("mousemove.title" + this.getName())
+ .unbind("mouseleave.title" + this.getName());
+ this._hoverBinded = false;
+ },
+
+ // opt: {container: '', belowMouse: false}
+ setTitle: function (title, opt) {
+ this.options.title = title;
+ if (BI.isKey(title) || BI.isFunction(title)) {
+ this.enableHover(opt);
+ } else {
+ this.disabledHover();
+ }
+ },
+
+ setWarningTitle: function (title, opt) {
+ this.options.warningTitle = title;
+ if (BI.isKey(title) || BI.isFunction(title)) {
+ this.enableHover(opt);
+ } else {
+ this.disabledHover();
+ }
+ },
+
+ setTipType: function (type) {
+ this.options.tipType = type;
+ },
+
+ getTipType: function () {
+ return this.options.tipType;
+ },
+
+ isReadOnly: function () {
+ return !!this.options.readonly;
+ },
+
+ getTitle: function () {
+ var title = this.options.title;
+ if (BI.isFunction(title)) {
+ return title();
+ }
+ return title;
+ },
+
+ getWarningTitle: function () {
+ var title = this.options.warningTitle;
+ if (BI.isFunction(title)) {
+ return title();
+ }
+ return title;
+ },
+
+ setValue: function (val) {
+ if (!this.options.readonly) {
+ this.options.value = val;
+ this.options.setValue && this.options.setValue(val);
+ }
+ },
+
+ getValue: function () {
+ return this.options.value;
+ },
+
+ __d: function () {
+ BI.Single.superclass.__d.call(this);
+ if (BI.isNotNull(this.showTimeout)) {
+ clearTimeout(this.showTimeout);
+ this.showTimeout = null;
+ }
+ BI.Tooltips.remove(this.getName());
+ }
+});
+BI.shortcut("bi.single", BI.Single);
diff --git a/src/main/resources/com/fr/fineui/base/single/1.text.js b/src/main/resources/com/fr/fineui/base/single/1.text.js
new file mode 100644
index 0000000..cc6081e
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/1.text.js
@@ -0,0 +1,170 @@
+/**
+ * guy 表示一行数据,通过position来定位位置的数据
+ * @class BI.Text
+ * @extends BI.Single
+ */
+!(function () {
+ BI.Text = BI.inherit(BI.Single, {
+
+ props: {
+ baseCls: "bi-text",
+ textAlign: "left",
+ whiteSpace: "normal",
+ lineHeight: null,
+ handler: null, // 如果传入handler,表示处理文字的点击事件,不是区域的
+ hgap: 0,
+ vgap: 0,
+ lgap: 0,
+ rgap: 0,
+ tgap: 0,
+ bgap: 0,
+ py: "",
+ highLight: false
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ if (o.hgap + o.lgap > 0) {
+ this.element.css({
+ "padding-left": (o.hgap + o.lgap) / BI.pixRatio + BI.pixUnit
+ });
+ }
+ if (o.hgap + o.rgap > 0) {
+ this.element.css({
+ "padding-right": (o.hgap + o.rgap) / BI.pixRatio + BI.pixUnit
+ });
+ }
+ if (o.vgap + o.tgap > 0) {
+ this.element.css({
+ "padding-top": (o.vgap + o.tgap) / BI.pixRatio + BI.pixUnit
+ });
+ }
+ if (o.vgap + o.bgap > 0) {
+ this.element.css({
+ "padding-bottom": (o.vgap + o.bgap) / BI.pixRatio + BI.pixUnit
+ });
+ }
+ if (BI.isWidthOrHeight(o.height)) {
+ this.element.css({lineHeight: BI.isNumber(o.height) ? (o.height / BI.pixRatio + BI.pixUnit) : o.height});
+ }
+ if (BI.isWidthOrHeight(o.lineHeight)) {
+ this.element.css({lineHeight: BI.isNumber(o.lineHeight) ? (o.lineHeight / BI.pixRatio + BI.pixUnit) : o.lineHeight});
+ }
+ if (BI.isWidthOrHeight(o.maxWidth)) {
+ this.element.css({maxWidth: BI.isNumber(o.maxWidth) ? (o.maxWidth / BI.pixRatio + BI.pixUnit) : o.maxWidth});
+ }
+ this.element.css({
+ textAlign: o.textAlign,
+ whiteSpace: this._getTextWrap(),
+ textOverflow: o.whiteSpace === "nowrap" ? "ellipsis" : "",
+ overflow: o.whiteSpace === "nowrap" ? "" : (BI.isWidthOrHeight(o.height) ? "auto" : "")
+ });
+ if (o.handler && o.handler !== BI.emptyFn) {
+ this.text = BI.createWidget({
+ type: "bi.layout",
+ tagName: "span"
+ });
+ this.text.element.click(function (e) {
+ o.handler.call(self, self.getValue(), self, e);
+ });
+ BI.createWidget({
+ type: "bi.default",
+ element: this,
+ items: [this.text]
+ });
+ } else {
+ this.text = this;
+ }
+
+ var text = this._getShowText();
+ // 只要不是undefined就可以显示text值,否则显示value
+ if (!BI.isUndefined(text)) {
+ this.setText(text);
+ } else if (BI.isKey(o.value)) {
+ this.setText(o.value);
+ }
+ if (BI.isKey(o.keyword)) {
+ this.doRedMark(o.keyword);
+ }
+ if (o.highLight) {
+ this.doHighLight();
+ }
+ },
+
+ _getTextWrap: function () {
+ var o = this.options;
+ switch (o.whiteSpace) {
+ case "nowrap":
+ return "pre";
+ case "normal":
+ return "pre-wrap";
+ default:
+ return o.whiteSpace;
+ }
+ },
+
+ _getShowText: function () {
+ var o = this.options;
+ var text = BI.isFunction(o.text) ? o.text() : o.text;
+ return BI.isKey(text) ? BI.Text.formatText(text + "") : text;
+ },
+
+ _doRedMark: function (keyword) {
+ var o = this.options;
+ // render之后做的doRedMark,这个时候虽然标红了,但是之后text mounted执行的时候并没有keyword
+ o.keyword = keyword;
+ this.text.element.__textKeywordMarked__(this._getShowText(), keyword, o.py);
+ },
+
+ doRedMark: function (keyword) {
+ if (BI.isKey(keyword)) {
+ this._doRedMark(keyword);
+ }
+ },
+
+ unRedMark: function () {
+ var o = this.options;
+ o.keyword = "";
+ this.text.element.__textKeywordMarked__(this._getShowText(), "", o.py);
+ },
+
+ doHighLight: function () {
+ this.text.element.addClass("bi-high-light");
+ },
+
+ unHighLight: function () {
+ this.text.element.removeClass("bi-high-light");
+ },
+
+ setValue: function (text) {
+ BI.Text.superclass.setValue.apply(this, arguments);
+ if (!this.isReadOnly()) {
+ this.setText(text);
+ }
+ },
+
+ setStyle: function (css) {
+ this.text.element.css(css);
+ },
+
+ setText: function (text) {
+ BI.Text.superclass.setText.apply(this, arguments);
+ this.options.text = text;
+ this._doRedMark(this.options.keyword);
+ }
+ });
+ var formatters = [];
+ BI.Text.addTextFormatter = function (formatter) {
+ formatters.push(formatter);
+ };
+ BI.Text.formatText = function (text) {
+ if (formatters.length > 0) {
+ for (var i = 0, len = formatters.length; i < len; i++) {
+ text = formatters[i](text);
+ }
+ }
+ return text;
+ };
+ BI.shortcut("bi.text", BI.Text);
+}());
+
diff --git a/src/main/resources/com/fr/fineui/base/single/__test__/text.test.js b/src/main/resources/com/fr/fineui/base/single/__test__/text.test.js
new file mode 100644
index 0000000..9549b2d
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/__test__/text.test.js
@@ -0,0 +1,195 @@
+/**
+ * Created by windy on 2018/01/23.
+ */
+describe("TextTest", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("setText", function () {
+ var text = BI.Test.createWidget({
+ type: "bi.text"
+ });
+ text.setText("AAA");
+ expect(text.element.text()).to.equal("AAA");
+ text.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("setStyle", function () {
+ var text = BI.Test.createWidget({
+ type: "bi.text"
+ });
+ text.setStyle({"color": "red"});
+ expect(text.element.getStyle("color")).to.equal("rgb(255, 0, 0)");
+ text.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("高亮doHighlight", function () {
+ var text = BI.Test.createWidget({
+ type: "bi.text",
+ text: "AAA",
+ highLight: true
+ });
+ expect(text.element.getStyle("color")).to.equal("rgb(54, 133, 242)");
+ text.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("标红doRedMark", function () {
+ var text = BI.Test.createWidget({
+ type: "bi.text",
+ text: "我是要标红的A",
+ keyword: "A"
+ });
+ expect(text.element.children(".bi-keyword-red-mark").length).to.not.equal(0);
+ text.destroy();
+ });
+
+
+ /**
+ * test_author_windy
+ */
+ it("取消高亮undoHighlight", function () {
+ var text = BI.Test.createWidget({
+ type: "bi.text",
+ text: "AAA",
+ highLight: true
+ });
+ text.unHighLight();
+ expect(text.element.getStyle("color")).to.not.equal("rgb(54, 133, 242)");
+ text.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("取消标红undoRedMark", function () {
+ var text = BI.Test.createWidget({
+ type: "bi.text",
+ text: "我是要标红的A",
+ keyword: "A"
+ });
+ text.unRedMark();
+ expect(text.element.children(".bi-keyword-red-mark").length).to.equal(0);
+ text.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("setValue", function () {
+ var text = BI.Test.createWidget({
+ type: "bi.text",
+ value: "AAA",
+ });
+ text.setValue("value");
+ expect(text.element.text()).to.equal("value");
+ text.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("gap测试", function () {
+ var text = BI.Test.createWidget({
+ type: "bi.text",
+ text: "我是要标红的A",
+ vgap: 10,
+ hgap: 10
+ });
+ expect(text.element.css("padding")).to.equal("10px");
+ text.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("空格测试", function () {
+ var text = BI.Test.createWidget({
+ type: "bi.text",
+ text: "我是要标红的 A",
+ });
+ expect(text.element.text()).to.equal("我是要标红的 A");
+ text.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("lineHeight和height", function () {
+ var text = BI.Test.createWidget({
+ type: "bi.text",
+ text: "我是A",
+ lineHeight: 12,
+ height: 24
+ });
+ expect(text.element.css("height")).to.equal("24px");
+ expect(text.element.css("line-height")).to.equal("12px");
+ text.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("handler", function (done) {
+ var text = BI.Test.createWidget({
+ type: "bi.text",
+ text: "我是A",
+ handler: function () {
+ text.setText("handler");
+ }
+ });
+ BI.nextTick(function () {
+ text.text.element.click();
+ expect(text.text.element.text()).to.equal("handler");
+ text.destroy();
+ done();
+ });
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("text的value属性", function () {
+ var text = BI.Test.createWidget({
+ type: "bi.text",
+ text: "",
+ value: "aaaa"
+ });
+ expect(text.element.text()).to.equal("");
+ text.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("text的value属性1", function () {
+ var text = BI.Test.createWidget({
+ type: "bi.text",
+ value: "aaaa"
+ });
+ expect(text.element.text()).to.equal("aaaa");
+ text.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("text的value属性2", function () {
+ var text = BI.Test.createWidget({
+ type: "bi.text",
+ text: null,
+ value: "aaaa"
+ });
+ expect(text.element.text()).to.equal("");
+ text.destroy();
+ });
+});
diff --git a/src/main/resources/com/fr/fineui/base/single/a/__test__/a.test.js b/src/main/resources/com/fr/fineui/base/single/a/__test__/a.test.js
new file mode 100644
index 0000000..6a78187
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/a/__test__/a.test.js
@@ -0,0 +1,32 @@
+/**
+ * Created by windy on 2018/01/23.
+ */
+describe("ALinkTest", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("A初始化测试", function () {
+ var a = BI.Test.createWidget({
+ type: "bi.a",
+ text: "CCC"
+ });
+ expect(a.element.is('a')).to.equal(true);
+ a.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("A的el测试", function () {
+ var a = BI.Test.createWidget({
+ type: "bi.a",
+ text: "DDD",
+ el: {
+ type: "bi.label"
+ }
+ });
+ expect(a.element.is('a') && a.element.hasClass("bi-label")).to.equal(true);
+ a.destroy();
+ });
+});
diff --git a/src/main/resources/com/fr/fineui/base/single/a/a.js b/src/main/resources/com/fr/fineui/base/single/a/a.js
new file mode 100644
index 0000000..2bab1ea
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/a/a.js
@@ -0,0 +1,33 @@
+/**
+ * 超链接
+ *
+ * Created by GUY on 2015/9/9.
+ * @class BI.A
+ * @extends BI.Text
+ * @abstract
+ */
+BI.A = BI.inherit(BI.Text, {
+ _defaultConfig: function () {
+ var conf = BI.A.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-a display-block",
+ href: "",
+ target: "_blank",
+ el: null,
+ tagName: "a"
+ });
+ },
+
+ render: function () {
+ var o = this.options;
+ BI.A.superclass.render.apply(this, arguments);
+ this.element.attr({href: o.href, target: o.target});
+ if (o.el) {
+ BI.createWidget(o.el, {
+ element: this
+ });
+ }
+ }
+});
+
+BI.shortcut("bi.a", BI.A);
diff --git a/src/main/resources/com/fr/fineui/base/single/bar/bar.loading.js b/src/main/resources/com/fr/fineui/base/single/bar/bar.loading.js
new file mode 100644
index 0000000..27b54cd
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/bar/bar.loading.js
@@ -0,0 +1,80 @@
+/**
+ * guy
+ * 加载条
+ * @type {*|void|Object}
+ */
+BI.LoadingBar = BI.inherit(BI.Single, {
+ _defaultConfig: function () {
+ var conf = BI.LoadingBar.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend( conf, {
+ baseCls: (conf.baseCls || "") + " bi-loading-bar bi-tips",
+ height: 30,
+ handler: BI.emptyFn
+ });
+ },
+
+ render: function () {
+ var self = this;
+ this.loaded = BI.createWidget({
+ type: "bi.text_button",
+ cls: "loading-text bi-list-item-simple",
+ text: BI.i18nText("BI-Load_More"),
+ width: 120,
+ handler: this.options.handler
+ });
+ this.loaded.on(BI.Controller.EVENT_CHANGE, function (type) {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+
+ this.loading = BI.createWidget({
+ type: "bi.layout",
+ width: this.options.height,
+ height: this.options.height,
+ cls: "loading-background cursor-default"
+ });
+ var loaded = BI.createWidget({
+ type: "bi.center_adapt",
+ items: [this.loaded]
+ });
+ var loading = BI.createWidget({
+ type: "bi.center_adapt",
+ items: [this.loading]
+ });
+ this.cardLayout = BI.createWidget({
+ type: "bi.card",
+ element: this,
+ items: [{
+ el: loaded,
+ cardName: "loaded"
+ }, {
+ el: loading,
+ cardName: "loading"
+ }]
+ });
+ this.invisible();
+ },
+
+ _reset: function () {
+ this.visible();
+ this.loaded.setText(BI.i18nText("BI-Load_More"));
+ this.loaded.enable();
+ },
+
+ setLoaded: function () {
+ this._reset();
+ this.cardLayout.showCardByName("loaded");
+ },
+
+ setEnd: function () {
+ this.setLoaded();
+ this.loaded.setText(BI.i18nText("BI-No_More_Data"));
+ this.loaded.disable();
+ },
+
+ setLoading: function () {
+ this._reset();
+ this.cardLayout.showCardByName("loading");
+ }
+});
+
+BI.shortcut("bi.loading_bar", BI.LoadingBar);
diff --git a/src/main/resources/com/fr/fineui/base/single/button/button.basic.js b/src/main/resources/com/fr/fineui/base/single/button/button.basic.js
new file mode 100644
index 0000000..4a5d68a
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/button.basic.js
@@ -0,0 +1,412 @@
+/**
+ * guy
+ * @class BI.BasicButton
+ * @extends BI.Single
+ *
+ * 一般的button父级
+ */
+BI.BasicButton = BI.inherit(BI.Single, {
+ _defaultConfig: function () {
+ var conf = BI.BasicButton.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ _baseCls: (conf._baseCls || "") + " bi-basic-button" + (conf.invalid ? "" : " cursor-pointer") + ((BI.isIE() && BI.getIEVersion() < 10) ? " hack" : ""),
+ value: "",
+ stopEvent: false,
+ stopPropagation: false,
+ selected: false,
+ once: false, // 点击一次选中有效,再点无效
+ forceSelected: false, // 点击即选中, 选中了就不会被取消,与once的区别是forceSelected不影响事件的触发
+ forceNotSelected: false, // 无论怎么点击都不会被选中
+ disableSelected: false, // 使能选中
+
+ shadow: false,
+ isShadowShowingOnSelected: false, // 选中状态下是否显示阴影
+ trigger: null,
+ handler: BI.emptyFn,
+ bubble: null
+ });
+ },
+ _init: function () {
+ BI.BasicButton.superclass._init.apply(this, arguments);
+ var opts = this.options;
+ if (opts.selected === true) {
+ BI.nextTick(BI.bind(function () {
+ this.setSelected(opts.selected);
+ }, this));
+ }
+ BI.nextTick(BI.bind(this.bindEvent, this));
+
+ if (opts.shadow) {
+ this._createShadow();
+ }
+ if (opts.level) {
+ this.element.addClass("button-" + opts.level);
+ }
+ },
+
+ _createShadow: function () {
+ var self = this, o = this.options;
+
+ var assertMask = function () {
+ if (!self.$mask) {
+ self.$mask = BI.createWidget(BI.isObject(o.shadow) ? o.shadow : {}, {
+ type: "bi.layout",
+ cls: "bi-button-mask"
+ });
+ self.$mask.invisible();
+ BI.createWidget({
+ type: "bi.absolute",
+ element: self,
+ items: [{
+ el: self.$mask,
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0
+ }]
+ });
+ }
+ };
+
+ this.element.mouseup(function () {
+ if (!self._hover && !o.isShadowShowingOnSelected) {
+ assertMask();
+ self.$mask.invisible();
+ }
+ });
+ this.element.on("mouseenter." + this.getName(), function (e) {
+ if (self.element.__isMouseInBounds__(e)) {
+ if (self.isEnabled() && !self._hover && (o.isShadowShowingOnSelected || !self.isSelected())) {
+ assertMask();
+ self.$mask.visible();
+ }
+ }
+ });
+ this.element.on("mousemove." + this.getName(), function (e) {
+ if (!self.element.__isMouseInBounds__(e)) {
+ if (self.isEnabled() && !self._hover) {
+ assertMask();
+ self.$mask.invisible();
+ }
+ }
+ });
+ this.element.on("mouseleave." + this.getName(), function () {
+ if (self.isEnabled() && !self._hover) {
+ assertMask();
+ self.$mask.invisible();
+ }
+ });
+ },
+
+ bindEvent: function () {
+ var self = this;
+ var o = this.options, hand = this.handle();
+ if (!hand) {
+ return;
+ }
+ hand = hand.element;
+ var triggerArr = (o.trigger || "").split(",");
+ BI.each(triggerArr, function (idx, trigger) {
+ switch (trigger) {
+ case "mouseup":
+ var mouseDown = false;
+ hand.mousedown(function () {
+ mouseDown = true;
+ });
+ hand.mouseup(function (e) {
+ if (mouseDown === true) {
+ clk(e);
+ }
+ mouseDown = false;
+ ev(e);
+ });
+ break;
+ case "mousedown":
+ var mouseDown = false;
+ var selected = false;
+ hand.mousedown(function (e) {
+ // if (e.button === 0) {
+ BI.Widget._renderEngine.createElement(document).bind("mouseup." + self.getName(), function (e) {
+ // if (e.button === 0) {
+ if (BI.DOM.isExist(self) && !hand.__isMouseInBounds__(e) && mouseDown === true && !selected) {
+ // self.setSelected(!self.isSelected());
+ self._trigger();
+ }
+ mouseDown = false;
+ BI.Widget._renderEngine.createElement(document).unbind("mouseup." + self.getName());
+ // }
+ });
+ if (mouseDown === true) {
+ return;
+ }
+ if (self.isSelected()) {
+ selected = true;
+ } else {
+ clk(e);
+ }
+ mouseDown = true;
+ ev(e);
+ // }
+ });
+ hand.mouseup(function (e) {
+ // if (e.button === 0) {
+ if (BI.DOM.isExist(self) && mouseDown === true && selected === true) {
+ clk(e);
+ }
+ mouseDown = false;
+ selected = false;
+ BI.Widget._renderEngine.createElement(document).unbind("mouseup." + self.getName());
+ // }
+ });
+ break;
+ case "dblclick":
+ hand.dblclick(clk);
+ break;
+ case "lclick":
+ var mouseDown = false;
+ var interval;
+ hand.mousedown(function (e) {
+ BI.Widget._renderEngine.createElement(document).bind("mouseup." + self.getName(), function (e) {
+ interval && clearInterval(interval);
+ interval = null;
+ mouseDown = false;
+ BI.Widget._renderEngine.createElement(document).unbind("mouseup." + self.getName());
+ });
+ if (mouseDown === true) {
+ return;
+ }
+ if (!self.isEnabled() || (self.isOnce() && self.isSelected())) {
+ return;
+ }
+ interval = setInterval(function () {
+ if (self.isEnabled()) {
+ self.doClick();
+ }
+ }, 180);
+ mouseDown = true;
+ ev(e);
+ });
+ break;
+ default:
+ if (o.stopEvent || o.stopPropagation) {
+ hand.mousedown(function (e) {
+ ev(e);
+ });
+ }
+ hand.click(clk);
+ break;
+ }
+ });
+
+ // 之后的300ms点击无效
+ var onClick = BI.debounce(this._doClick, BI.EVENT_RESPONSE_TIME, {
+ "leading": true,
+ "trailing": false
+ });
+
+ function ev (e) {
+ if (o.stopEvent) {
+ e.stopEvent();
+ }
+ if (o.stopPropagation) {
+ e.stopPropagation();
+ }
+ }
+
+ function clk (e) {
+ ev(e);
+ if (!self.isEnabled() || (self.isOnce() && self.isSelected())) {
+ return;
+ }
+ if (BI.isKey(o.bubble) || BI.isFunction(o.bubble)) {
+ if (BI.isNull(self.combo)) {
+ var popup;
+ BI.createWidget({
+ type: "bi.absolute",
+ element: self,
+ items: [{
+ el: {
+ type: "bi.bubble_combo",
+ trigger: "",
+ // bubble的提示不需要一直存在在界面上
+ destroyWhenHide: true,
+ ref: function () {
+ self.combo = this;
+ },
+ el: {
+ type: "bi.layout",
+ height: "100%"
+ },
+ popup: {
+ type: "bi.text_bubble_bar_popup_view",
+ text: getBubble(),
+ ref: function () {
+ popup = this;
+ },
+ listeners: [{
+ eventName: BI.BubblePopupBarView.EVENT_CLICK_TOOLBAR_BUTTON,
+ action: function (v) {
+ self.combo.hideView();
+ if (v) {
+ onClick.apply(self, arguments);
+ }
+ }
+ }]
+ },
+ listeners: [{
+ eventName: BI.BubbleCombo.EVENT_BEFORE_POPUPVIEW,
+ action: function () {
+ popup.populate(getBubble());
+ }
+ }]
+ },
+ left: 0,
+ right: 0,
+ bottom: 0,
+ top: 0
+ }]
+ });
+ }
+ if (self.combo.isViewVisible()) {
+ self.combo.hideView();
+ } else {
+ self.combo.showView();
+ }
+ return;
+ }
+ onClick.apply(self, arguments);
+ }
+
+ function getBubble () {
+ var bubble = self.options.bubble;
+ if (BI.isFunction(bubble)) {
+ return bubble();
+ }
+ return bubble;
+ }
+ },
+
+ _trigger: function (e) {
+ var o = this.options;
+ if (!this.isEnabled()) {
+ return;
+ }
+ if (!this.isDisableSelected()) {
+ this.isForceSelected() ? this.setSelected(true) :
+ (this.isForceNotSelected() ? this.setSelected(false) :
+ this.setSelected(!this.isSelected()));
+ }
+ if (this.isValid()) {
+ var v = this.getValue();
+ o.handler.call(this, v, this, e);
+ this.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.CLICK, v, this, e);
+ this.fireEvent(BI.BasicButton.EVENT_CHANGE, v, this);
+ if (o.action) {
+ BI.Actions.runAction(o.action, "click", o, this);
+ }
+ BI.Actions.runGlobalAction("click", o, this);
+ }
+ },
+
+ _doClick: function (e) {
+ if (this.isValid()) {
+ this.beforeClick(e);
+ }
+ this._trigger(e);
+ if (this.isValid()) {
+ this.doClick(e);
+ }
+ },
+
+ beforeClick: function () {
+
+ },
+
+ doClick: function () {
+
+ },
+
+ handle: function () {
+ return this;
+ },
+
+ hover: function () {
+ this._hover = true;
+ this.handle().element.addClass("hover");
+ if (this.options.shadow) {
+ this.$mask && this.$mask.setVisible(true);
+ }
+ },
+
+ dishover: function () {
+ this._hover = false;
+ this.handle().element.removeClass("hover");
+ if (this.options.shadow) {
+ this.$mask && this.$mask.setVisible(false);
+ }
+ },
+
+ setSelected: function (b) {
+ var o = this.options;
+ o.selected = b;
+ if (b) {
+ this.handle().element.addClass("active");
+ } else {
+ this.handle().element.removeClass("active");
+ }
+ if (o.shadow && !o.isShadowShowingOnSelected) {
+ this.$mask && this.$mask.setVisible(false);
+ }
+ this.options.setSelected && this.options.setSelected.call(this, b);
+ },
+
+ isSelected: function () {
+ return this.options.selected;
+ },
+
+ isOnce: function () {
+ return this.options.once;
+ },
+
+ isForceSelected: function () {
+ return this.options.forceSelected;
+ },
+
+ isForceNotSelected: function () {
+ return this.options.forceNotSelected;
+ },
+
+ isDisableSelected: function () {
+ return this.options.disableSelected;
+ },
+
+ setText: function (text) {
+ this.options.text = text;
+ this.options.setText && this.options.setText.call(this, text);
+ },
+
+ getText: function () {
+ return this.options.text;
+ },
+
+ _setEnable: function (enable) {
+ BI.BasicButton.superclass._setEnable.apply(this, arguments);
+ if (enable === true) {
+ this.element.removeClass("base-disabled disabled");
+ } else if (enable === false) {
+ this.element.addClass("base-disabled disabled");
+ }
+ if (!enable) {
+ if (this.options.shadow) {
+ this.$mask && this.$mask.setVisible(false);
+ }
+ }
+ },
+
+ empty: function () {
+ BI.Widget._renderEngine.createElement(document).unbind("mouseup." + this.getName());
+ BI.BasicButton.superclass.empty.apply(this, arguments);
+ }
+});
+BI.BasicButton.EVENT_CHANGE = "BasicButton.EVENT_CHANGE";
+BI.shortcut("bi.basic_button", BI.BasicButton);
diff --git a/src/main/resources/com/fr/fineui/base/single/button/button.node.js b/src/main/resources/com/fr/fineui/base/single/button/button.node.js
new file mode 100644
index 0000000..b8fda22
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/button.node.js
@@ -0,0 +1,57 @@
+/**
+ * 表示一个可以展开的节点, 不仅有选中状态而且有展开状态
+ *
+ * Created by GUY on 2015/9/9.
+ * @class BI.NodeButton
+ * @extends BI.BasicButton
+ * @abstract
+ */
+BI.NodeButton = BI.inherit(BI.BasicButton, {
+ _defaultConfig: function () {
+ var conf = BI.NodeButton.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend( conf, {
+ _baseCls: (conf._baseCls || "") + " bi-node",
+ open: false
+ });
+ },
+
+ _init: function () {
+ BI.NodeButton.superclass._init.apply(this, arguments);
+ var self = this;
+ BI.nextTick(function () {
+ self.setOpened(self.isOpened());
+ });
+ },
+
+ doClick: function () {
+ BI.NodeButton.superclass.doClick.apply(this, arguments);
+ this.setOpened(!this.isOpened());
+ },
+
+ isOnce: function () {
+ return false;
+ },
+
+ isOpened: function () {
+ return !!this.options.open;
+ },
+
+ setOpened: function (b) {
+ this.options.open = !!b;
+ },
+
+ triggerCollapse: function () {
+ if(this.isOpened()) {
+ this.setOpened(false);
+ this.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.COLLAPSE, this.getValue(), this);
+ }
+ },
+
+ triggerExpand: function () {
+ if(!this.isOpened()) {
+ this.setOpened(true);
+ this.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.EXPAND, this.getValue(), this);
+ }
+ }
+});
+BI.shortcut("bi.node_button", BI.NodeButton);
diff --git a/src/main/resources/com/fr/fineui/base/single/button/buttons/__test__/button.test.js b/src/main/resources/com/fr/fineui/base/single/button/buttons/__test__/button.test.js
new file mode 100644
index 0000000..2ef4e0c
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/buttons/__test__/button.test.js
@@ -0,0 +1,117 @@
+/**
+ * Created by windy on 2018/01/23.
+ */
+describe("ButtonTest", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("Click点击触发事件", function (done) {
+ var button = BI.Test.createWidget({
+ type: "bi.button",
+ text: "CCC",
+ handler: function () {
+ this.setText("click");
+ }
+ });
+ BI.nextTick(function () {
+ button.element.click();
+ expect(button.element.children(".bi-text").text()).to.equal("click");
+ button.destroy();
+ done();
+ });
+
+ });
+
+
+ /**
+ * test_author_windy
+ */
+ it("MouseDown触发事件", function (done) {
+ var button = BI.Test.createWidget({
+ type: "bi.button",
+ text: "CCC",
+ trigger: "mousedown",
+ handler: function () {
+ this.setText("click");
+ }
+ });
+ BI.nextTick(function () {
+ button.element.mousedown();
+ expect(button.element.children(".bi-text").text()).to.equal("click");
+ button.destroy();
+ done();
+ });
+
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("MouseUp触发事件", function (done) {
+ var button = BI.Test.createWidget({
+ type: "bi.button",
+ text: "CCC",
+ trigger: "mouseup",
+ handler: function () {
+ this.setText("click");
+ }
+ });
+ BI.nextTick(function () {
+ button.element.mousedown();
+ button.element.mouseup();
+ expect(button.element.children(".bi-text").text()).to.equal("click");
+ button.destroy();
+ done();
+ });
+
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("doubleClick触发事件", function (done) {
+ var button = BI.Test.createWidget({
+ type: "bi.button",
+ text: "CCC",
+ trigger: "dblclick",
+ handler: function () {
+ this.setText("click");
+ }
+ });
+ BI.nextTick(function () {
+ button.element.dblclick();
+ expect(button.element.children(".bi-text").text()).to.equal("click");
+ button.destroy();
+ done();
+ });
+
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("LongClick触发事件", function (done) {
+ var clickNum = 0;
+ var button = BI.Test.createWidget({
+ type: "bi.button",
+ text: "CCC",
+ trigger: "lclick",
+ listeners: [{
+ eventName: BI.Button.EVENT_CHANGE,
+ action: function () {
+ clickNum++;
+ }
+ }]
+ });
+ BI.nextTick(function () {
+ button.element.mousedown();
+ BI.delay(function () {
+ expect(clickNum).to.equal(2);
+ button.destroy();
+ done();
+ }, 360);
+ });
+
+ });
+});
diff --git a/src/main/resources/com/fr/fineui/base/single/button/buttons/button.icon.js b/src/main/resources/com/fr/fineui/base/single/button/buttons/button.icon.js
new file mode 100644
index 0000000..5eb104e
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/buttons/button.icon.js
@@ -0,0 +1,51 @@
+/**
+ * @class BI.IconButton
+ * @extends BI.BasicButton
+ * 图标的button
+ */
+BI.IconButton = BI.inherit(BI.BasicButton, {
+ _defaultConfig: function () {
+ var conf = BI.IconButton.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ _baseCls: (conf._baseCls || "") + " bi-icon-button horizon-center",
+ iconWidth: null,
+ iconHeight: null
+ });
+ },
+
+ render: function () {
+ var o = this.options;
+ this.element.css({
+ textAlign: "center"
+ });
+ this.icon = BI.createWidget({
+ type: "bi.icon",
+ width: o.iconWidth,
+ height: o.iconHeight
+ });
+ if (BI.isNumber(o.height) && o.height > 0 && BI.isNull(o.iconWidth) && BI.isNull(o.iconHeight)) {
+ this.element.css("lineHeight", o.height / BI.pixRatio + BI.pixUnit);
+ BI.createWidget({
+ type: "bi.default",
+ element: this,
+ items: [this.icon]
+ });
+ } else {
+ this.element.css("lineHeight", "1");
+ BI.createWidget({
+ element: this,
+ type: "bi.center_adapt",
+ items: [this.icon]
+ });
+ }
+ },
+
+ doClick: function () {
+ BI.IconButton.superclass.doClick.apply(this, arguments);
+ if (this.isValid()) {
+ this.fireEvent(BI.IconButton.EVENT_CHANGE, this);
+ }
+ }
+});
+BI.IconButton.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.icon_button", BI.IconButton);
diff --git a/src/main/resources/com/fr/fineui/base/single/button/buttons/button.image.js b/src/main/resources/com/fr/fineui/base/single/button/buttons/button.image.js
new file mode 100644
index 0000000..58be435
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/buttons/button.image.js
@@ -0,0 +1,86 @@
+/**
+ * 图片的button
+ *
+ * Created by GUY on 2016/1/27.
+ * @class BI.ImageButton
+ * @extends BI.BasicButton
+ */
+BI.ImageButton = BI.inherit(BI.BasicButton, {
+ _defaultConfig: function () {
+ var conf = BI.ImageButton.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-image-button",
+ src: "",
+ iconWidth: "100%",
+ iconHeight: "100%"
+ });
+ },
+
+ render: function () {
+ var o = this.options;
+ this.image = BI.createWidget({
+ type: "bi.img",
+ width: o.iconWidth,
+ height: o.iconHeight,
+ src: o.src
+ });
+ if (BI.isNumber(o.iconWidth) || BI.isNumber(o.iconHeight)) {
+ BI.createWidget({
+ type: "bi.center_adapt",
+ element: this,
+ items: [this.image]
+ });
+ } else {
+ BI.createWidget({
+ type: "bi.adaptive",
+ element: this,
+ items: [this.image],
+ scrollable: false
+ });
+ }
+ },
+
+ setWidth: function (w) {
+ BI.ImageButton.superclass.setWidth.apply(this, arguments);
+ this.options.width = w;
+ },
+
+ setHeight: function (h) {
+ BI.ImageButton.superclass.setHeight.apply(this, arguments);
+ this.options.height = h;
+ },
+
+ setImageWidth: function (w) {
+ this.image.setWidth(w);
+ },
+
+ setImageHeight: function (h) {
+ this.image.setHeight(h);
+ },
+
+ getImageWidth: function () {
+ return this.image.element.width();
+ },
+
+ getImageHeight: function () {
+ return this.image.element.height();
+ },
+
+ setSrc: function (src) {
+ this.options.src = src;
+ this.image.setSrc(src);
+ },
+
+ getSrc: function () {
+ return this.image.getSrc();
+ },
+
+ doClick: function () {
+ BI.ImageButton.superclass.doClick.apply(this, arguments);
+ if (this.isValid()) {
+ this.fireEvent(BI.ImageButton.EVENT_CHANGE, this);
+ }
+ }
+});
+BI.ImageButton.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.image_button", BI.ImageButton);
diff --git a/src/main/resources/com/fr/fineui/base/single/button/buttons/button.js b/src/main/resources/com/fr/fineui/base/single/button/buttons/button.js
new file mode 100644
index 0000000..b5dc71c
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/buttons/button.js
@@ -0,0 +1,157 @@
+/**
+ * 文字类型的按钮
+ * @class BI.Button
+ * @extends BI.BasicButton
+ *
+ * @cfg {JSON} options 配置属性
+ * @cfg {'common'/'success'/'warning'/'ignore'} [options.level='common'] 按钮类型,用不同颜色强调不同的场景
+ */
+BI.Button = BI.inherit(BI.BasicButton, {
+
+ _const: {
+ iconWidth: 18
+ },
+
+ _defaultConfig: function (props) {
+ var conf = BI.Button.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-button" + ((BI.isIE() && BI.isIE9Below()) ? " hack" : ""),
+ minWidth: (props.block === true || props.clear === true) ? 0 : 80,
+ height: 24,
+ shadow: props.clear !== true,
+ isShadowShowingOnSelected: true,
+ readonly: true,
+ iconCls: "",
+ level: "common",
+ block: false, // 是否块状显示,即不显示边框,没有最小宽度的限制
+ clear: false, // 是否去掉边框和背景
+ ghost: false, // 是否幽灵显示, 即正常状态无背景
+ textAlign: "center",
+ whiteSpace: "nowrap",
+ textWidth: null,
+ textHeight: null,
+ hgap: props.clear ? 0 : 10,
+ vgap: 0,
+ tgap: 0,
+ bgap: 0,
+ lgap: 0,
+ rgap: 0
+ });
+ },
+
+ render: function () {
+ var o = this.options, self = this;
+
+ // 由于button默认情况下有个边框,所以要主动算行高
+ var lineHeight, textHeight = o.textHeight;
+ if (BI.isNumber(o.height)) {
+ if (o.clear || o.block) {
+ lineHeight = o.height;
+ } else {
+ lineHeight = o.height - 2;
+ }
+ }
+ if (!textHeight) {
+ if (o.whiteSpace === "nowrap") {
+ textHeight = lineHeight;
+ }
+ }
+ if (BI.isKey(o.iconCls)) {
+ this.icon = BI.createWidget({
+ type: "bi.icon_label",
+ cls: o.iconCls,
+ width: this._const.iconWidth,
+ height: o.height,
+ lineHeight: lineHeight,
+ iconWidth: o.iconWidth,
+ iconHeight: o.iconHeight
+ });
+ this.text = BI.createWidget({
+ type: "bi.label",
+ text: o.text,
+ textWidth: BI.isNotNull(o.textWidth) ? o.textWidth - this._const.iconWidth : null,
+ textHeight: textHeight,
+ height: o.height,
+ value: o.value
+ });
+ BI.createWidget({
+ type: "bi.center_adapt",
+ element: this,
+ hgap: o.hgap,
+ vgap: o.vgap,
+ items: [{
+ type: "bi.horizontal",
+ columnSize: ["", "fill"],
+ items: [this.icon, this.text]
+ }]
+ });
+ } else {
+ this.text = BI.createWidget({
+ type: "bi.label",
+ height: o.height,
+ textAlign: o.textAlign,
+ whiteSpace: o.whiteSpace,
+ textWidth: o.textWidth,
+ textHeight: textHeight,
+ hgap: o.hgap,
+ vgap: o.vgap,
+ tgap: o.tgap,
+ bgap: o.bgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ element: this,
+ text: o.text,
+ value: o.value
+ });
+ }
+ if (o.block === true) {
+ this.element.addClass("block");
+ }
+ if (o.clear === true) {
+ this.element.addClass("clear");
+ }
+ if (o.ghost === true) {
+ this.element.addClass("ghost");
+ }
+ if (o.minWidth > 0) {
+ this.element.css({"min-width": o.minWidth / BI.pixRatio + BI.pixUnit});
+ }
+ },
+
+ doClick: function () {
+ BI.Button.superclass.doClick.apply(this, arguments);
+ if (this.isValid()) {
+ this.fireEvent(BI.Button.EVENT_CHANGE, this);
+ }
+ },
+
+ setText: function (text) {
+ BI.Button.superclass.setText.apply(this, arguments);
+ this.text.setText(text);
+ },
+
+ setValue: function (text) {
+ BI.Button.superclass.setValue.apply(this, arguments);
+ if (!this.isReadOnly()) {
+ this.text.setValue(text);
+ }
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doHighLight: function () {
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ }
+});
+BI.shortcut("bi.button", BI.Button);
+BI.Button.EVENT_CHANGE = "EVENT_CHANGE";
diff --git a/src/main/resources/com/fr/fineui/base/single/button/buttons/button.text.js b/src/main/resources/com/fr/fineui/base/single/button/buttons/button.text.js
new file mode 100644
index 0000000..bbb3e0e
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/buttons/button.text.js
@@ -0,0 +1,89 @@
+/**
+ * guy
+ * 可以点击的一行文字
+ * @class BI.TextButton
+ * @extends BI.BasicButton
+ * 文字button
+ */
+BI.TextButton = BI.inherit(BI.BasicButton, {
+ _defaultConfig: function () {
+ var conf = BI.TextButton.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-text-button",
+ textAlign: "center",
+ whiteSpace: "nowrap",
+ textWidth: null,
+ textHeight: null,
+ hgap: 0,
+ lgap: 0,
+ rgap: 0,
+ vgap: 0,
+ py: ""
+ });
+ },
+
+ render: function () {
+ var o = this.options;
+ this.text = BI.createWidget({
+ type: "bi.label",
+ element: this,
+ textAlign: o.textAlign,
+ whiteSpace: o.whiteSpace,
+ textWidth: o.textWidth,
+ textHeight: o.textHeight,
+ width: o.width,
+ height: o.height,
+ hgap: o.hgap,
+ vgap: o.vgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ text: o.text,
+ value: o.value,
+ py: o.py,
+ keyword: o.keyword
+ });
+ },
+
+ doClick: function () {
+ BI.TextButton.superclass.doClick.apply(this, arguments);
+ if (this.isValid()) {
+ this.fireEvent(BI.TextButton.EVENT_CHANGE, this.getValue(), this);
+ }
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doHighLight: function () {
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ },
+
+ setText: function (text) {
+ BI.TextButton.superclass.setText.apply(this, arguments);
+ text = BI.isArray(text) ? text.join(",") : text;
+ this.text.setText(text);
+ },
+
+ setStyle: function (style) {
+ this.text.setStyle(style);
+ },
+
+ setValue: function (text) {
+ BI.TextButton.superclass.setValue.apply(this, arguments);
+ if (!this.isReadOnly()) {
+ text = BI.isArray(text) ? text.join(",") : text;
+ this.text.setValue(text);
+ }
+ }
+});
+BI.TextButton.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.text_button", BI.TextButton);
diff --git a/src/main/resources/com/fr/fineui/base/single/button/listitem/blankiconicontextitem.js b/src/main/resources/com/fr/fineui/base/single/button/listitem/blankiconicontextitem.js
new file mode 100644
index 0000000..9e8301a
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/listitem/blankiconicontextitem.js
@@ -0,0 +1,119 @@
+/**
+ * 带有一个占位
+ *
+ * Created by GUY on 2015/9/11.
+ * @class BI.BlankIconIconTextItem
+ * @extends BI.BasicButton
+ */
+BI.BlankIconIconTextItem = BI.inherit(BI.BasicButton, {
+
+ _defaultConfig: function () {
+ var conf = BI.BlankIconIconTextItem.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-blank-icon-text-item",
+ logic: {
+ dynamic: false
+ },
+ iconCls1: "",
+ iconCls2: "",
+ blankWidth: 0,
+ iconHeight: null,
+ iconWidth: null,
+ textHgap: 0,
+ textVgap: 0,
+ textLgap: 0,
+ textRgap: 0
+ });
+ },
+
+ render: function () {
+ var o = this.options, c = this._const;
+ var blank = BI.createWidget({
+ type: "bi.layout",
+ width: o.blankWidth,
+ height: o.height
+ });
+ this.text = BI.createWidget({
+ type: "bi.label",
+ cls: "list-item-text",
+ textAlign: "left",
+ hgap: o.textHgap,
+ vgap: o.textVgap,
+ lgap: o.textLgap,
+ rgap: o.textRgap,
+ text: o.text,
+ value: o.value,
+ keyword: o.keyword,
+ height: o.height
+ });
+ this.icon1 = BI.createWidget({
+ type: "bi.icon_button",
+ cls: o.iconCls1,
+ forceNotSelected: true,
+ width: o.height,
+ height: o.height
+ });
+ this.icon2 = BI.createWidget({
+ type: "bi.icon_button",
+ cls: o.iconCls2,
+ forceNotSelected: true,
+ width: o.height,
+ height: o.height
+ });
+
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic("horizontal", BI.extend(o.logic, {
+ items: BI.LogicFactory.createLogicItemsByDirection("left", blank, this.icon1, this.icon2, this.text)
+ }))));
+ },
+
+ doClick: function () {
+ BI.BlankIconIconTextItem.superclass.doClick.apply(this, arguments);
+ if (this.isValid()) {
+ this.fireEvent(BI.BlankIconIconTextItem.EVENT_CHANGE, this.getValue(), this);
+ }
+ },
+
+ setSelected: function (b) {
+ BI.BlankIconIconTextItem.superclass.setSelected.apply(this, arguments);
+ this.icon1.setSelected(b);
+ this.icon2.setSelected(b);
+ },
+
+ setValue: function () {
+ if (!this.isReadOnly()) {
+ this.text.setValue.apply(this.text, arguments);
+ }
+ },
+
+ getValue: function () {
+ return this.text.getValue();
+ },
+
+ setText: function () {
+ this.text.setText.apply(this.text, arguments);
+ },
+
+ getText: function () {
+ return this.text.getText();
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doHighLight: function () {
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ }
+});
+BI.BlankIconIconTextItem.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.blank_icon_icon_text_item", BI.BlankIconIconTextItem);
diff --git a/src/main/resources/com/fr/fineui/base/single/button/listitem/blankicontexticonitem.js b/src/main/resources/com/fr/fineui/base/single/button/listitem/blankicontexticonitem.js
new file mode 100644
index 0000000..99d6ad5
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/listitem/blankicontexticonitem.js
@@ -0,0 +1,126 @@
+/**
+ * guy
+ * 一个占位符和两个icon和一行数 组成的一行listitem
+ *
+ * Created by GUY on 2015/9/15.
+ * @class BI.BlankIconTextIconItem
+ * @extends BI.BasicButton
+ */
+BI.BlankIconTextIconItem = BI.inherit(BI.BasicButton, {
+
+ _defaultConfig: function () {
+ var conf = BI.BlankIconTextIconItem.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-blank-icon-text-icon-item",
+ logic: {
+ dynamic: false
+ },
+ iconCls1: "",
+ iconCls2: "",
+ blankWidth: 0,
+ iconHeight: null,
+ iconWidth: null,
+ textHgap: 0,
+ textVgap: 0,
+ textLgap: 0,
+ textRgap: 0
+ });
+ },
+
+ render: function () {
+ var o = this.options, c = this._const;
+ this.text = BI.createWidget({
+ type: "bi.label",
+ textAlign: "left",
+ hgap: o.textHgap,
+ vgap: o.textVgap,
+ lgap: o.textLgap,
+ rgap: o.textRgap,
+ text: o.text,
+ value: o.value,
+ keyword: o.keyword,
+ height: o.height
+ });
+
+ var icon1 = BI.createWidget({
+ type: "bi.icon_label",
+ cls: o.iconCls1,
+ width: o.height,
+ height: o.height,
+ iconWidth: o.iconWidth,
+ iconHeight: o.iconHeight
+ });
+ BI.createWidget({
+ type: "bi.absolute",
+ element: this,
+ items: [{
+ el: {
+ type: "bi.icon_label",
+ cls: o.iconCls2,
+ width: o.height,
+ height: o.height,
+ iconWidth: o.iconWidth,
+ iconHeight: o.iconHeight
+ },
+ top: 0,
+ bottom: 0,
+ right: 0
+ }]
+ });
+
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic("horizontal", BI.extend(o.logic, {
+ items: BI.LogicFactory.createLogicItemsByDirection("left", {
+ type: "bi.layout",
+ width: o.blankWidth
+ }, icon1, this.text, {
+ type: "bi.layout",
+ width: o.height
+ })
+ }))));
+ },
+
+ doClick: function () {
+ BI.BlankIconTextIconItem.superclass.doClick.apply(this, arguments);
+ if (this.isValid()) {
+ this.fireEvent(BI.BlankIconTextIconItem.EVENT_CHANGE, this.getValue(), this);
+ }
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doHighLight: function () {
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ },
+
+ setValue: function () {
+ if (!this.isReadOnly()) {
+ this.text.setValue.apply(this.text, arguments);
+ }
+ },
+
+ getValue: function () {
+ return this.text.getValue();
+ },
+
+ setText: function () {
+ this.text.setText.apply(this.text, arguments);
+ },
+
+ getText: function () {
+ return this.text.getText();
+ }
+});
+BI.BlankIconTextIconItem.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.blank_icon_text_icon_item", BI.BlankIconTextIconItem);
diff --git a/src/main/resources/com/fr/fineui/base/single/button/listitem/blankicontextitem.js b/src/main/resources/com/fr/fineui/base/single/button/listitem/blankicontextitem.js
new file mode 100644
index 0000000..d647637
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/listitem/blankicontextitem.js
@@ -0,0 +1,105 @@
+/**
+ * 带有一个占位
+ *
+ * Created by GUY on 2015/9/11.
+ * @class BI.BlankIconTextItem
+ * @extends BI.BasicButton
+ */
+BI.BlankIconTextItem = BI.inherit(BI.BasicButton, {
+
+ _defaultConfig: function () {
+ var conf = BI.BlankIconTextItem.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-blank-icon-text-item",
+ logic: {
+ dynamic: false
+ },
+ blankWidth: 0,
+ iconHeight: null,
+ iconWidth: null,
+ iconCls: "",
+ textHgap: 0,
+ textVgap: 0,
+ textLgap: 0,
+ textRgap: 0
+ });
+ },
+
+ render: function () {
+ var o = this.options, c = this._const;
+ var blank = BI.createWidget({
+ type: "bi.layout",
+ width: o.blankWidth
+ });
+ this.text = BI.createWidget({
+ type: "bi.label",
+ cls: "list-item-text",
+ textAlign: "left",
+ hgap: o.textHgap,
+ vgap: o.textVgap,
+ lgap: o.textLgap,
+ rgap: o.textRgap,
+ text: o.text,
+ value: o.value,
+ keyword: o.keyword,
+ height: o.height
+ });
+ this.icon = BI.createWidget({
+ type: "bi.icon_label",
+ cls: o.iconCls,
+ width: o.height,
+ height: o.height,
+ iconWidth: o.iconWidth,
+ iconHeight: o.iconHeight
+ });
+
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic("horizontal", BI.extend(o.logic, {
+ items: BI.LogicFactory.createLogicItemsByDirection("left", blank, this.icon, this.text)
+ }))));
+ },
+
+ doClick: function () {
+ BI.BlankIconTextItem.superclass.doClick.apply(this, arguments);
+ if (this.isValid()) {
+ this.fireEvent(BI.BlankIconTextItem.EVENT_CHANGE, this.getValue(), this);
+ }
+ },
+
+ setValue: function () {
+ if (!this.isReadOnly()) {
+ this.text.setValue.apply(this.text, arguments);
+ }
+ },
+
+ getValue: function () {
+ return this.text.getValue();
+ },
+
+ setText: function () {
+ this.text.setText.apply(this.text, arguments);
+ },
+
+ getText: function () {
+ return this.text.getText();
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doHighLight: function () {
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ }
+});
+BI.BlankIconTextItem.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.blank_icon_text_item", BI.BlankIconTextItem);
diff --git a/src/main/resources/com/fr/fineui/base/single/button/listitem/icontexticonitem.js b/src/main/resources/com/fr/fineui/base/single/button/listitem/icontexticonitem.js
new file mode 100644
index 0000000..3cae823
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/listitem/icontexticonitem.js
@@ -0,0 +1,123 @@
+/**
+ * guy
+ * 两个icon和一行数 组成的一行listitem
+ *
+ * Created by GUY on 2015/9/9.
+ * @class BI.IconTextIconItem
+ * @extends BI.BasicButton
+ */
+BI.IconTextIconItem = BI.inherit(BI.BasicButton, {
+
+ _defaultConfig: function () {
+ var conf = BI.IconTextIconItem.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-icon-text-icon-item",
+ logic: {
+ dynamic: false
+ },
+ iconCls1: "",
+ iconCls2: "",
+ iconHeight: null,
+ iconWidth: null,
+ textHgap: 0,
+ textVgap: 0,
+ textLgap: 0,
+ textRgap: 0
+ });
+ },
+
+ render: function () {
+ var o = this.options, c = this._const;
+ this.text = BI.createWidget({
+ type: "bi.label",
+ textAlign: "left",
+ hgap: o.textHgap,
+ vgap: o.textVgap,
+ lgap: o.textLgap,
+ rgap: o.textRgap,
+ text: o.text,
+ value: o.value,
+ keyword: o.keyword,
+ height: o.height
+ });
+
+ var icon1 = BI.createWidget({
+ type: "bi.icon_label",
+ cls: o.iconCls1,
+ width: o.leftIconWrapperWidth,
+ height: o.height,
+ iconWidth: o.iconWidth,
+ iconHeight: o.iconHeight
+ });
+ var blank = BI.createWidget({
+ type: "bi.layout",
+ width: o.height
+ });
+ BI.createWidget({
+ type: "bi.absolute",
+ element: this,
+ items: [{
+ el: {
+ type: "bi.icon_label",
+ cls: o.iconCls2,
+ width: o.rightIconWrapperWidth,
+ height: o.height,
+ iconWidth: o.iconWidth,
+ iconHeight: o.iconHeight
+ },
+ top: 0,
+ bottom: 0,
+ right: 0
+ }]
+ });
+
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic("horizontal", BI.extend(o.logic, {
+ items: BI.LogicFactory.createLogicItemsByDirection("left", icon1, this.text, blank)
+ }))));
+ },
+
+ doClick: function () {
+ BI.IconTextIconItem.superclass.doClick.apply(this, arguments);
+ if (this.isValid()) {
+ this.fireEvent(BI.IconTextIconItem.EVENT_CHANGE, this.getValue(), this);
+ }
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doHighLight: function () {
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ },
+
+ setValue: function () {
+ if (!this.isReadOnly()) {
+ this.text.setValue.apply(this.text, arguments);
+ }
+ },
+
+ getValue: function () {
+ return this.text.getValue();
+ },
+
+ setText: function () {
+ this.text.setText.apply(this.text, arguments);
+ },
+
+ getText: function () {
+ return this.text.getText();
+ }
+});
+BI.IconTextIconItem.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.icon_text_icon_item", BI.IconTextIconItem);
diff --git a/src/main/resources/com/fr/fineui/base/single/button/listitem/icontextitem.js b/src/main/resources/com/fr/fineui/base/single/button/listitem/icontextitem.js
new file mode 100644
index 0000000..648b983
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/listitem/icontextitem.js
@@ -0,0 +1,102 @@
+/**
+ * guy
+ *
+ * Created by GUY on 2015/9/9.
+ * @class BI.IconTextItem
+ * @extends BI.BasicButton
+ */
+BI.IconTextItem = BI.inherit(BI.BasicButton, {
+
+ _defaultConfig: function () {
+ var conf = BI.IconTextItem.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-icon-text-item",
+ direction: BI.Direction.Left,
+ logic: {
+ dynamic: false
+ },
+ iconWrapperWidth: null,
+ iconHeight: null,
+ iconWidth: null,
+ iconCls: "",
+ textHgap: 0,
+ textVgap: 0,
+ textLgap: 0,
+ textRgap: 0
+ });
+ },
+
+ render: function () {
+ var o = this.options, c = this._const;
+ this.text = BI.createWidget({
+ type: "bi.label",
+ cls: "list-item-text",
+ textAlign: "left",
+ hgap: o.textHgap,
+ vgap: o.textVgap,
+ lgap: o.textLgap,
+ rgap: o.textRgap,
+ text: o.text,
+ value: o.value,
+ keyword: o.keyword,
+ height: o.height
+ });
+ this.icon = BI.createWidget({
+ type: "bi.icon_label",
+ cls: o.iconCls,
+ width: o.iconWrapperWidth || o.height,
+ height: o.height,
+ iconWidth: o.iconWidth,
+ iconHeight: o.iconHeight
+ });
+
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic(BI.LogicFactory.createLogicTypeByDirection(o.direction), BI.extend(o.logic, {
+ items: BI.LogicFactory.createLogicItemsByDirection(o.direction, this.icon, this.text)
+ }))));
+ },
+
+ setValue: function () {
+ if (!this.isReadOnly()) {
+ this.text.setValue.apply(this.text, arguments);
+ }
+ },
+
+ getValue: function () {
+ return this.text.getValue();
+ },
+
+ setText: function () {
+ this.text.setText.apply(this.text, arguments);
+ },
+
+ getText: function () {
+ return this.text.getText();
+ },
+
+ doClick: function () {
+ BI.IconTextItem.superclass.doClick.apply(this, arguments);
+ if (this.isValid()) {
+ this.fireEvent(BI.IconTextItem.EVENT_CHANGE, this.getValue(), this);
+ }
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doHighLight: function () {
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ }
+});
+BI.IconTextItem.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.icon_text_item", BI.IconTextItem);
diff --git a/src/main/resources/com/fr/fineui/base/single/button/listitem/texticonitem.js b/src/main/resources/com/fr/fineui/base/single/button/listitem/texticonitem.js
new file mode 100644
index 0000000..7eb4680
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/listitem/texticonitem.js
@@ -0,0 +1,101 @@
+/**
+ *
+ * 图标的button
+ *
+ * Created by GUY on 2015/9/9.
+ * @class BI.TextIconItem
+ * @extends BI.BasicButton
+ */
+BI.TextIconItem = BI.inherit(BI.BasicButton, {
+
+ _defaultConfig: function () {
+ var conf = BI.TextIconItem.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-text-icon-item",
+ logic: {
+ dynamic: false
+ },
+ iconHeight: null,
+ iconWidth: null,
+ iconCls: "",
+ textHgap: 0,
+ textVgap: 0,
+ textLgap: 0,
+ textRgap: 0
+ });
+ },
+
+ render: function () {
+ var o = this.options, c = this._const;
+ this.text = BI.createWidget({
+ type: "bi.label",
+ cls: "list-item-text",
+ textAlign: "left",
+ hgap: o.textHgap,
+ vgap: o.textVgap,
+ lgap: o.textLgap,
+ rgap: o.textRgap,
+ text: o.text,
+ value: o.value,
+ keyword: o.keyword,
+ height: o.height
+ });
+ this.icon = BI.createWidget({
+ type: "bi.icon_label",
+ cls: o.iconCls,
+ width: o.height,
+ height: o.height,
+ iconWidth: o.iconWidth,
+ iconHeight: o.iconHeight
+ });
+
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic("horizontal", BI.extend(o.logic, {
+ items: BI.LogicFactory.createLogicItemsByDirection("left", this.text, this.icon)
+ }))));
+ },
+
+ doClick: function () {
+ BI.TextIconItem.superclass.doClick.apply(this, arguments);
+ if (this.isValid()) {
+ this.fireEvent(BI.TextIconItem.EVENT_CHANGE, this.getValue(), this);
+ }
+ },
+
+ setValue: function () {
+ if (!this.isReadOnly()) {
+ this.text.setValue.apply(this.text, arguments);
+ }
+ },
+
+ getValue: function () {
+ return this.text.getValue();
+ },
+
+ setText: function () {
+ this.text.setText.apply(this.text, arguments);
+ },
+
+ getText: function () {
+ return this.text.getText();
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doHighLight: function () {
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ }
+});
+BI.TextIconItem.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.text_icon_item", BI.TextIconItem);
diff --git a/src/main/resources/com/fr/fineui/base/single/button/listitem/textitem.js b/src/main/resources/com/fr/fineui/base/single/button/listitem/textitem.js
new file mode 100644
index 0000000..ffbcc1e
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/listitem/textitem.js
@@ -0,0 +1,86 @@
+/**
+ * guy
+ * 一个button和一行数 组成的一行listitem
+ *
+ * Created by GUY on 2015/9/9.
+ * @class BI.TextItem
+ * @extends BI.BasicButton
+ */
+BI.TextItem = BI.inherit(BI.BasicButton, {
+
+ _defaultConfig: function () {
+ var conf = BI.TextItem.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-text-item",
+ textAlign: "left",
+ whiteSpace: "nowrap",
+ textHgap: 0,
+ textVgap: 0,
+ textLgap: 0,
+ textRgap: 0
+ });
+ },
+
+ render: function () {
+ var o = this.options;
+ this.text = BI.createWidget({
+ type: "bi.label",
+ element: this,
+ textAlign: o.textAlign,
+ whiteSpace: o.whiteSpace,
+ textHeight: o.whiteSpace == "nowrap" ? o.height : o.textHeight,
+ height: o.height,
+ hgap: o.textHgap,
+ vgap: o.textVgap,
+ lgap: o.textLgap,
+ rgap: o.textRgap,
+ text: o.text,
+ value: o.value,
+ keyword: o.keyword,
+ py: o.py
+ });
+ },
+
+ doClick: function () {
+ BI.TextItem.superclass.doClick.apply(this, arguments);
+ if (this.isValid()) {
+ this.fireEvent(BI.TextItem.EVENT_CHANGE, this.getValue(), this);
+ }
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doHighLight: function () {
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ },
+
+ setValue: function () {
+ if (!this.isReadOnly()) {
+ this.text.setValue.apply(this.text, arguments);
+ }
+ },
+
+ getValue: function () {
+ return this.text.getValue();
+ },
+
+ setText: function () {
+ this.text.setText.apply(this.text, arguments);
+ },
+
+ getText: function () {
+ return this.text.getText();
+ }
+});
+BI.TextItem.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.text_item", BI.TextItem);
diff --git a/src/main/resources/com/fr/fineui/base/single/button/node/__test__/icontexticonnode.test.js b/src/main/resources/com/fr/fineui/base/single/button/node/__test__/icontexticonnode.test.js
new file mode 100644
index 0000000..ae2a8fb
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/node/__test__/icontexticonnode.test.js
@@ -0,0 +1,104 @@
+/**
+ * @author Kobi
+ * @date 2020/4/21
+ */
+
+describe("IconTextIconNodeTest", function () {
+
+ /**
+ * test_author_kobi
+ */
+ it("setText", function () {
+ var iconTextIconNode = BI.Test.createWidget({
+ type: "bi.icon_text_icon_node"
+ });
+ iconTextIconNode.setText("AAA");
+ expect(iconTextIconNode.element.find(".bi-text").text()).to.equal("AAA");
+ iconTextIconNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("getText", function () {
+ var iconTextIconNode = BI.Test.createWidget({
+ type: "bi.icon_text_icon_node",
+ text: "AAA",
+ });
+ expect(iconTextIconNode.getText()).to.equal("AAA");
+ iconTextIconNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("setValue", function () {
+ var iconTextIconNode = BI.Test.createWidget({
+ type: "bi.icon_text_icon_node"
+ });
+ iconTextIconNode.setValue("AAA");
+ expect(iconTextIconNode.element.find(".bi-text").text()).to.equal("AAA");
+ iconTextIconNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("readonly下的setValue", function () {
+ var iconTextIconNode = BI.Test.createWidget({
+ type: "bi.icon_text_icon_node",
+ value: "AAA",
+ readonly: true
+ });
+ iconTextIconNode.setValue("BBB");
+ expect(iconTextIconNode.element.find(".bi-text").text()).to.equal("AAA");
+ iconTextIconNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("getValue", function () {
+ var iconTextIconNode = BI.Test.createWidget({
+ type: "bi.icon_text_icon_node",
+ value: "AAA"
+ });
+ expect(iconTextIconNode.getValue()).to.equal("AAA");
+ iconTextIconNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("doRedMark和unRedMark", function () {
+ var iconTextIconNode = BI.Test.createWidget({
+ type: "bi.icon_text_icon_node",
+ text: "要标红的AAA",
+ });
+ iconTextIconNode.doRedMark("AAA");
+ expect(iconTextIconNode.element.find(".bi-keyword-red-mark").length).to.not.equal(0);
+ iconTextIconNode.unRedMark();
+ expect(iconTextIconNode.element.find(".bi-keyword-red-mark").length).to.equal(0);
+ iconTextIconNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("Click点击触发事件", function (done) {
+ var iconTextIconNode = BI.Test.createWidget({
+ type: "bi.icon_text_icon_node",
+ text: "AAA",
+ handler: function () {
+ this.setText("click");
+ }
+ });
+ BI.nextTick(function () {
+ iconTextIconNode.element.click();
+ expect(iconTextIconNode.element.find(".bi-text").text()).to.equal("click");
+ iconTextIconNode.destroy();
+ done();
+ });
+ });
+
+});
diff --git a/src/main/resources/com/fr/fineui/base/single/button/node/__test__/icontextnode.test.js b/src/main/resources/com/fr/fineui/base/single/button/node/__test__/icontextnode.test.js
new file mode 100644
index 0000000..2319e23
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/node/__test__/icontextnode.test.js
@@ -0,0 +1,104 @@
+/**
+ * @author Kobi
+ * @date 2020/4/21
+ */
+
+describe("IconTextNodeTest", function () {
+
+ /**
+ * test_author_kobi
+ */
+ it("setText", function () {
+ var iconTextNode = BI.Test.createWidget({
+ type: "bi.icon_text_node"
+ });
+ iconTextNode.setText("AAA");
+ expect(iconTextNode.element.find(".bi-text").text()).to.equal("AAA");
+ iconTextNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("getText", function () {
+ var iconTextNode = BI.Test.createWidget({
+ type: "bi.icon_text_node",
+ text: "AAA",
+ });
+ expect(iconTextNode.getText()).to.equal("AAA");
+ iconTextNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("setValue", function () {
+ var iconTextNode = BI.Test.createWidget({
+ type: "bi.icon_text_node"
+ });
+ iconTextNode.setValue("AAA");
+ expect(iconTextNode.element.find(".bi-text").text()).to.equal("AAA");
+ iconTextNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("readonly下的setValue", function () {
+ var iconTextNode = BI.Test.createWidget({
+ type: "bi.icon_text_node",
+ value: "AAA",
+ readonly: true
+ });
+ iconTextNode.setValue("BBB");
+ expect(iconTextNode.element.find(".bi-text").text()).to.equal("AAA");
+ iconTextNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("getValue", function () {
+ var iconTextNode = BI.Test.createWidget({
+ type: "bi.icon_text_node",
+ value: "AAA"
+ });
+ expect(iconTextNode.getValue()).to.equal("AAA");
+ iconTextNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("doRedMark和unRedMark", function () {
+ var iconTextNode = BI.Test.createWidget({
+ type: "bi.icon_text_node",
+ text: "要标红的AAA",
+ });
+ iconTextNode.doRedMark("AAA");
+ expect(iconTextNode.element.find(".bi-keyword-red-mark").length).to.not.equal(0);
+ iconTextNode.unRedMark();
+ expect(iconTextNode.element.find(".bi-keyword-red-mark").length).to.equal(0);
+ iconTextNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("Click点击触发事件", function (done) {
+ var iconTextNode = BI.Test.createWidget({
+ type: "bi.icon_text_node",
+ text: "AAA",
+ handler: function () {
+ this.setText("click");
+ }
+ });
+ BI.nextTick(function () {
+ iconTextNode.element.click();
+ expect(iconTextNode.element.find(".bi-text").text()).to.equal("click");
+ iconTextNode.destroy();
+ done();
+ });
+ });
+
+});
diff --git a/src/main/resources/com/fr/fineui/base/single/button/node/__test__/texticonnode.test.js b/src/main/resources/com/fr/fineui/base/single/button/node/__test__/texticonnode.test.js
new file mode 100644
index 0000000..9eb4ec2
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/node/__test__/texticonnode.test.js
@@ -0,0 +1,104 @@
+/**
+ * @author Kobi
+ * @date 2020/4/21
+ */
+
+describe("TextIconNodeTest", function () {
+
+ /**
+ * test_author_kobi
+ */
+ it("setText", function () {
+ var textIconNode = BI.Test.createWidget({
+ type: "bi.text_icon_node"
+ });
+ textIconNode.setText("AAA");
+ expect(textIconNode.element.find(".bi-text").text()).to.equal("AAA");
+ textIconNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("getText", function () {
+ var textIconNode = BI.Test.createWidget({
+ type: "bi.text_icon_node",
+ text: "AAA",
+ });
+ expect(textIconNode.getText()).to.equal("AAA");
+ textIconNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("setValue", function () {
+ var textIconNode = BI.Test.createWidget({
+ type: "bi.text_icon_node"
+ });
+ textIconNode.setValue("AAA");
+ expect(textIconNode.element.find(".bi-text").text()).to.equal("AAA");
+ textIconNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("readonly下的setValue", function () {
+ var textIconNode = BI.Test.createWidget({
+ type: "bi.text_icon_node",
+ value: "AAA",
+ readonly: true
+ });
+ textIconNode.setValue("BBB");
+ expect(textIconNode.element.find(".bi-text").text()).to.equal("AAA");
+ textIconNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("getValue", function () {
+ var textIconNode = BI.Test.createWidget({
+ type: "bi.text_icon_node",
+ value: "AAA"
+ });
+ expect(textIconNode.getValue()).to.equal("AAA");
+ textIconNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("doRedMark和unRedMark", function () {
+ var textIconNode = BI.Test.createWidget({
+ type: "bi.text_icon_node",
+ text: "要标红的AAA",
+ });
+ textIconNode.doRedMark("AAA");
+ expect(textIconNode.element.find(".bi-keyword-red-mark").length).to.not.equal(0);
+ textIconNode.unRedMark();
+ expect(textIconNode.element.find(".bi-keyword-red-mark").length).to.equal(0);
+ textIconNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("Click点击触发事件", function (done) {
+ var textIconNode = BI.Test.createWidget({
+ type: "bi.text_icon_node",
+ text: "AAA",
+ handler: function () {
+ this.setText("click");
+ }
+ });
+ BI.nextTick(function () {
+ textIconNode.element.click();
+ expect(textIconNode.element.find(".bi-text").text()).to.equal("click");
+ textIconNode.destroy();
+ done();
+ });
+ });
+
+});
diff --git a/src/main/resources/com/fr/fineui/base/single/button/node/__test__/textnode.test.js b/src/main/resources/com/fr/fineui/base/single/button/node/__test__/textnode.test.js
new file mode 100644
index 0000000..5674ae2
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/node/__test__/textnode.test.js
@@ -0,0 +1,106 @@
+/**
+ * @author Kobi
+ * @date 2020/4/21
+ */
+
+describe("TextNodeTest", function () {
+
+ /**
+ * test_author_kobi
+ */
+ it("setText", function () {
+ var textNode = BI.Test.createWidget({
+ type: "bi.text_node"
+ });
+ textNode.setText("AAA");
+ expect(textNode.element.children(".bi-text").text()).to.equal("AAA");
+ textNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("getText", function () {
+ var textNode = BI.Test.createWidget({
+ type: "bi.text_node",
+ text: "AAA",
+ whiteSpace: "normal"
+ });
+ expect(textNode.getText()).to.equal("AAA");
+ textNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("setValue", function () {
+ var textNode = BI.Test.createWidget({
+ type: "bi.text_node"
+ });
+ textNode.setValue("AAA");
+ expect(textNode.element.children(".bi-text").text()).to.equal("AAA");
+ textNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("readonly下的setValue", function () {
+ var textNode = BI.Test.createWidget({
+ type: "bi.text_node",
+ value: "AAA",
+ readonly: true
+ });
+ textNode.setValue("BBB");
+ expect(textNode.element.children(".bi-text").text()).to.equal("AAA");
+ textNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("getValue", function () {
+ var textNode = BI.Test.createWidget({
+ type: "bi.text_node",
+ value: "AAA"
+ });
+ expect(textNode.getValue()).to.equal("AAA");
+ textNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("doRedMark和unRedMark", function () {
+ var textNode = BI.Test.createWidget({
+ type: "bi.text_node",
+ text: "要标红的AAA",
+ });
+ textNode.doRedMark("AAA");
+ expect(textNode.element.find(".bi-keyword-red-mark").length).to.not.equal(0);
+ textNode.unRedMark();
+ expect(textNode.element.find(".bi-keyword-red-mark").length).to.equal(0);
+ textNode.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ */
+ it("Click点击触发事件", function (done) {
+ var textNode = BI.Test.createWidget({
+ type: "bi.text_node",
+ text: "AAA",
+ handler: function () {
+ this.setText("click");
+ }
+ });
+ BI.nextTick(function () {
+ textNode.element.click();
+ expect(textNode.element.children(".bi-text").text()).to.equal("click");
+ textNode.destroy();
+ done();
+ });
+ });
+
+
+});
diff --git a/src/main/resources/com/fr/fineui/base/single/button/node/icontexticonnode.js b/src/main/resources/com/fr/fineui/base/single/button/node/icontexticonnode.js
new file mode 100644
index 0000000..0187042
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/node/icontexticonnode.js
@@ -0,0 +1,113 @@
+/**
+ * guy
+ * Created by GUY on 2015/9/9.
+ * @class BI.IconTextIconNode
+ * @extends BI.NodeButton
+ */
+BI.IconTextIconNode = BI.inherit(BI.NodeButton, {
+
+ _defaultConfig: function () {
+ var conf = BI.IconTextIconNode.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-icon-text-icon-node",
+ logic: {
+ dynamic: false
+ },
+ iconCls1: "close-ha-font",
+ iconCls2: "close-ha-font",
+ iconHeight: null,
+ iconWidth: null,
+ textHgap: 0,
+ textVgap: 0,
+ textLgap: 0,
+ textRgap: 0
+ });
+ },
+
+ render: function () {
+ var o = this.options, c = this._const;
+ this.text = BI.createWidget({
+ type: "bi.label",
+ textAlign: "left",
+ hgap: o.textHgap,
+ vgap: o.textVgap,
+ lgap: o.textLgap,
+ rgap: o.textRgap,
+ text: o.text,
+ value: o.value,
+ keyword: o.keyword,
+ height: o.height
+ });
+
+ var icon1 = BI.createWidget({
+ type: "bi.icon_label",
+ cls: o.iconCls1,
+ width: o.height,
+ height: o.height,
+ iconWidth: o.iconWidth,
+ iconHeight: o.iconHeight
+ });
+ var blank = BI.createWidget({
+ type: "bi.layout",
+ width: o.height,
+ height: o.height
+ });
+ BI.createWidget({
+ type: "bi.absolute",
+ element: this,
+ items: [{
+ el: {
+ type: "bi.icon_label",
+ cls: o.iconCls2,
+ width: o.height,
+ iconWidth: o.iconWidth,
+ iconHeight: o.iconHeight
+ },
+ top: 0,
+ bottom: 0,
+ right: 0
+ }]
+ });
+
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic("horizontal", BI.extend(o.logic, {
+ items: BI.LogicFactory.createLogicItemsByDirection("left", icon1, this.text, blank)
+ }))));
+ },
+
+ doClick: function () {
+ BI.IconTextIconNode.superclass.doClick.apply(this, arguments);
+ if (this.isValid()) {
+ this.fireEvent(BI.IconTextIconNode.EVENT_CHANGE, this.getValue(), this);
+ }
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ setValue: function () {
+ if (!this.isReadOnly()) {
+ this.text.setValue.apply(this.text, arguments);
+ }
+ },
+
+ getValue: function () {
+ return this.text.getValue();
+ },
+
+ setText: function () {
+ this.text.setText.apply(this.text, arguments);
+ },
+
+ getText: function () {
+ return this.text.getText();
+ }
+});
+BI.IconTextIconNode.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.icon_text_icon_node", BI.IconTextIconNode);
diff --git a/src/main/resources/com/fr/fineui/base/single/button/node/icontextnode.js b/src/main/resources/com/fr/fineui/base/single/button/node/icontextnode.js
new file mode 100644
index 0000000..35e5757
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/node/icontextnode.js
@@ -0,0 +1,90 @@
+/**
+ * guy
+ * Created by GUY on 2015/9/9.
+ * @class BI.IconTextNode
+ * @extends BI.NodeButton
+ */
+BI.IconTextNode = BI.inherit(BI.NodeButton, {
+
+ _defaultConfig: function () {
+ var conf = BI.IconTextNode.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-icon-text-node",
+ logic: {
+ dynamic: false
+ },
+ cls: "close-ha-font",
+ iconHeight: null,
+ iconWidth: null,
+ textHgap: 0,
+ textVgap: 0,
+ textLgap: 0,
+ textRgap: 0
+ });
+ },
+
+ render: function () {
+ var o = this.options, c = this._const;
+ this.text = BI.createWidget({
+ type: "bi.label",
+ cls: "list-item-text",
+ textAlign: "left",
+ hgap: o.textHgap,
+ vgap: o.textVgap,
+ lgap: o.textLgap,
+ rgap: o.textRgap,
+ text: o.text,
+ value: o.value,
+ keyword: o.keyword,
+ height: o.height
+ });
+ this.icon = BI.createWidget({
+ type: "bi.icon_label",
+ width: o.height,
+ height: o.height,
+ iconWidth: o.iconWidth,
+ iconHeight: o.iconHeight
+ });
+
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic("horizontal", BI.extend(o.logic, {
+ items: BI.LogicFactory.createLogicItemsByDirection("left", this.icon, this.text)
+ }))));
+ },
+
+ setValue: function () {
+ if (!this.isReadOnly()) {
+ this.text.setValue.apply(this.text, arguments);
+ }
+ },
+
+ getValue: function () {
+ return this.text.getValue();
+ },
+
+ setText: function () {
+ this.text.setText.apply(this.text, arguments);
+ },
+
+ getText: function () {
+ return this.text.getText();
+ },
+
+ doClick: function () {
+ BI.IconTextNode.superclass.doClick.apply(this, arguments);
+ if (this.isValid()) {
+ this.fireEvent(BI.IconTextNode.EVENT_CHANGE, this.getValue(), this);
+ }
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ }
+});
+BI.IconTextNode.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.icon_text_node", BI.IconTextNode);
diff --git a/src/main/resources/com/fr/fineui/base/single/button/node/texticonnode.js b/src/main/resources/com/fr/fineui/base/single/button/node/texticonnode.js
new file mode 100644
index 0000000..2231c4c
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/node/texticonnode.js
@@ -0,0 +1,89 @@
+/**
+ * Created by GUY on 2015/9/9.
+ * @class BI.TextIconNode
+ * @extends BI.NodeButton
+ */
+BI.TextIconNode = BI.inherit(BI.NodeButton, {
+
+ _defaultConfig: function () {
+ var conf = BI.TextIconNode.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-text-icon-node",
+ logic: {
+ dynamic: false
+ },
+ cls: "close-ha-font",
+ iconHeight: null,
+ iconWidth: null,
+ textHgap: 0,
+ textVgap: 0,
+ textLgap: 0,
+ textRgap: 0
+ });
+ },
+
+ render: function () {
+ var o = this.options, c = this._const;
+ this.text = BI.createWidget({
+ type: "bi.label",
+ cls: "list-item-text",
+ textAlign: "left",
+ hgap: o.textHgap,
+ vgap: o.textVgap,
+ lgap: o.textLgap,
+ rgap: o.textRgap,
+ text: o.text,
+ value: o.value,
+ keyword: o.keyword,
+ height: o.height
+ });
+ this.icon = BI.createWidget({
+ type: "bi.icon_label",
+ width: o.height,
+ height: o.height,
+ iconWidth: o.iconWidth,
+ iconHeight: o.iconHeight
+ });
+
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic("horizontal", BI.extend(o.logic, {
+ items: BI.LogicFactory.createLogicItemsByDirection("left", this.text, this.icon)
+ }))));
+ },
+
+ doClick: function () {
+ BI.TextIconNode.superclass.doClick.apply(this, arguments);
+ if (this.isValid()) {
+ this.fireEvent(BI.TextIconNode.EVENT_CHANGE, this.getValue(), this);
+ }
+ },
+
+ setValue: function () {
+ if (!this.isReadOnly()) {
+ this.text.setValue.apply(this.text, arguments);
+ }
+ },
+
+ getValue: function () {
+ return this.text.getValue();
+ },
+
+ setText: function () {
+ this.text.setText.apply(this.text, arguments);
+ },
+
+ getText: function () {
+ return this.text.getText();
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ }
+});
+BI.TextIconNode.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.text_icon_node", BI.TextIconNode);
diff --git a/src/main/resources/com/fr/fineui/base/single/button/node/textnode.js b/src/main/resources/com/fr/fineui/base/single/button/node/textnode.js
new file mode 100644
index 0000000..7497445
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/button/node/textnode.js
@@ -0,0 +1,77 @@
+/**
+ * guy
+ *
+ * Created by GUY on 2015/9/9.
+ * @class BI.TextNode
+ * @extends BI.NodeButton
+ */
+BI.TextNode = BI.inherit(BI.NodeButton, {
+
+ _defaultConfig: function () {
+ var conf = BI.TextNode.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-text-node",
+ textAlign: "left",
+ whiteSpace: "nowrap",
+ textHgap: 0,
+ textVgap: 0,
+ textLgap: 0,
+ textRgap: 0
+ });
+ },
+
+ render: function () {
+ var o = this.options;
+ this.text = BI.createWidget({
+ type: "bi.label",
+ element: this,
+ textAlign: o.textAlign,
+ whiteSpace: o.whiteSpace,
+ textHeight: o.whiteSpace == "nowrap" ? o.height : o.textHeight,
+ height: o.height,
+ hgap: o.textHgap,
+ vgap: o.textVgap,
+ lgap: o.textLgap,
+ rgap: o.textRgap,
+ text: o.text,
+ value: o.value,
+ keyword: o.keyword,
+ py: o.py
+ });
+ },
+
+ doClick: function () {
+ BI.TextNode.superclass.doClick.apply(this, arguments);
+ if (this.isValid()) {
+ this.fireEvent(BI.TextNode.EVENT_CHANGE, this.getValue(), this);
+ }
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ setValue: function () {
+ if (!this.isReadOnly()) {
+ this.text.setValue.apply(this.text, arguments);
+ }
+ },
+
+ getValue: function () {
+ return this.text.getValue();
+ },
+
+ setText: function () {
+ this.text.setText.apply(this.text, arguments);
+ },
+
+ getText: function () {
+ return this.text.getText();
+ }
+});
+BI.TextNode.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.text_node", BI.TextNode);
diff --git a/src/main/resources/com/fr/fineui/base/single/editor/editor.js b/src/main/resources/com/fr/fineui/base/single/editor/editor.js
new file mode 100644
index 0000000..3191e4a
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/editor/editor.js
@@ -0,0 +1,370 @@
+/**
+ * Created by GUY on 2015/4/15.
+ * @class BI.Editor
+ * @extends BI.Single
+ */
+BI.Editor = BI.inherit(BI.Single, {
+ _defaultConfig: function () {
+ var conf = BI.Editor.superclass._defaultConfig.apply(this, arguments);
+
+ return BI.extend(conf, {
+ baseCls: "bi-editor bi-focus-shadow",
+ hgap: 4,
+ vgap: 2,
+ lgap: 0,
+ rgap: 0,
+ tgap: 0,
+ bgap: 0,
+ // title,warningTitle这两个属性没用
+ tipType: "warning",
+ inputType: "text",
+ validationChecker: BI.emptyFn,
+ quitChecker: BI.emptyFn,
+ allowBlank: false,
+ watermark: "",
+ errorText: "",
+ });
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ // 密码输入框设置autocomplete="new-password"的情况下Firefox和chrome不会自动填充密码
+ var autocomplete = o.autocomplete ? " autocomplete=" + o.autocomplete : "";
+ this.editor = this.addWidget(BI.createWidget({
+ type: "bi.input",
+ element: "
",
+ root: true,
+ value: o.value,
+ watermark: o.watermark,
+ validationChecker: o.validationChecker,
+ quitChecker: o.quitChecker,
+ allowBlank: o.allowBlank
+ }));
+ this.editor.element.css({
+ width: "100%",
+ height: "100%",
+ border: "none",
+ outline: "none",
+ padding: "0",
+ margin: "0"
+ });
+
+ var items = [{
+ el: {
+ type: "bi.absolute",
+ ref: function (_ref) {
+ self.contentWrapper = _ref;
+ },
+ items: [{
+ el: this.editor,
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0
+ }]
+ },
+ left: o.hgap + o.lgap,
+ right: o.hgap + o.rgap,
+ top: o.vgap + o.tgap,
+ bottom: o.vgap + o.bgap
+ }];
+
+ BI.createWidget({
+ type: "bi.absolute",
+ element: this,
+ items: items
+ });
+
+ this.setWaterMark(this.options.watermark);
+
+ this.editor.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ this.editor.on(BI.Input.EVENT_FOCUS, function () {
+ self._checkError();
+ self.element.addClass("bi-editor-focus");
+ self.fireEvent(BI.Editor.EVENT_FOCUS, arguments);
+ });
+ this.editor.on(BI.Input.EVENT_BLUR, function () {
+ self._setErrorVisible(false);
+ self.element.removeClass("bi-editor-focus");
+ self.fireEvent(BI.Editor.EVENT_BLUR, arguments);
+ });
+ this.editor.on(BI.Input.EVENT_CLICK, function () {
+ self.fireEvent(BI.Editor.EVENT_CLICK, arguments);
+ });
+ this.editor.on(BI.Input.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Editor.EVENT_CHANGE, arguments);
+ });
+ this.editor.on(BI.Input.EVENT_KEY_DOWN, function (v) {
+ self.fireEvent(BI.Editor.EVENT_KEY_DOWN, arguments);
+ });
+ this.editor.on(BI.Input.EVENT_QUICK_DOWN, function (e) {
+ // tab键就不要隐藏了
+ if (e.keyCode !== BI.KeyCode.TAB && self.watermark) {
+ self.watermark.invisible();
+ }
+ });
+
+ this.editor.on(BI.Input.EVENT_VALID, function () {
+ self._checkWaterMark();
+ self._setErrorVisible(false);
+ self.fireEvent(BI.Editor.EVENT_VALID, arguments);
+ });
+ this.editor.on(BI.Input.EVENT_ERROR, function () {
+ self._checkWaterMark();
+ self.fireEvent(BI.Editor.EVENT_ERROR, arguments);
+ self._setErrorVisible(self.isEditing());
+ });
+ this.editor.on(BI.Input.EVENT_RESTRICT, function () {
+ self._checkWaterMark();
+ var tip = self._setErrorVisible(true);
+ tip && tip.element.fadeOut(100, function () {
+ tip.element.fadeIn(100);
+ });
+ self.fireEvent(BI.Editor.EVENT_RESTRICT, arguments);
+ });
+ this.editor.on(BI.Input.EVENT_EMPTY, function () {
+ self._checkWaterMark();
+ self.fireEvent(BI.Editor.EVENT_EMPTY, arguments);
+ });
+ this.editor.on(BI.Input.EVENT_ENTER, function () {
+ self.fireEvent(BI.Editor.EVENT_ENTER, arguments);
+ });
+ this.editor.on(BI.Input.EVENT_SPACE, function () {
+ self.fireEvent(BI.Editor.EVENT_SPACE, arguments);
+ });
+ this.editor.on(BI.Input.EVENT_BACKSPACE, function () {
+ self.fireEvent(BI.Editor.EVENT_BACKSPACE, arguments);
+ });
+ this.editor.on(BI.Input.EVENT_REMOVE, function () {
+ self.fireEvent(BI.Editor.EVENT_REMOVE, arguments);
+ });
+ this.editor.on(BI.Input.EVENT_START, function () {
+ self.fireEvent(BI.Editor.EVENT_START, arguments);
+ });
+ this.editor.on(BI.Input.EVENT_PAUSE, function () {
+ self.fireEvent(BI.Editor.EVENT_PAUSE, arguments);
+ });
+ this.editor.on(BI.Input.EVENT_STOP, function () {
+ self.fireEvent(BI.Editor.EVENT_STOP, arguments);
+ });
+ this.editor.on(BI.Input.EVENT_CONFIRM, function () {
+ self.fireEvent(BI.Editor.EVENT_CONFIRM, arguments);
+ });
+ this.editor.on(BI.Input.EVENT_CHANGE_CONFIRM, function () {
+ self.fireEvent(BI.Editor.EVENT_CHANGE_CONFIRM, arguments);
+ });
+ this.element.click(function (e) {
+ e.stopPropagation();
+ return false;
+ });
+ if (BI.isKey(this.options.value) || BI.isEmptyString(this.options.value)) {
+ this._checkError();
+ this._checkWaterMark();
+ } else {
+ this._checkWaterMark();
+ }
+ },
+
+ _checkToolTip: function () {
+ var o = this.options;
+ var errorText = o.errorText;
+ if (BI.isFunction(errorText)) {
+ errorText = errorText(this.editor.getValue());
+ }
+ if (BI.isKey(errorText)) {
+ if (!this.isEnabled() || this.isValid() || BI.Bubbles.has(this.getName())) {
+ this.setTitle("");
+ } else {
+ this.setTitle(errorText);
+ }
+ }
+ },
+
+ _assertWaterMark: function () {
+ var self = this, o = this.options;
+ if (BI.isNull(this.watermark)) {
+ this.watermark = BI.createWidget({
+ type: "bi.label",
+ cls: "bi-water-mark",
+ text: this.options.watermark,
+ height: o.height - 2 * o.vgap - o.tgap,
+ hgap: 2,
+ whiteSpace: "nowrap",
+ textAlign: "left"
+ });
+ this.watermark.element.bind({
+ mousedown: function (e) {
+ if (self.isEnabled()) {
+ self.editor.isEditing() || self.editor.focus();
+ } else {
+ self.editor.isEditing() && self.editor.blur();
+ }
+ e.stopEvent();
+ }
+ });
+ this.watermark.element.bind("click", function (e) {
+ if (self.isEnabled()) {
+ self.editor.isEditing() || self.editor.focus();
+ } else {
+ self.editor.isEditing() && self.editor.blur();
+ }
+ e.stopEvent();
+ });
+ }
+ },
+
+ _checkError: function () {
+ this._setErrorVisible(this.isEnabled() && !this.isValid());
+ this._checkToolTip();
+ },
+
+ _checkWaterMark: function () {
+ var o = this.options;
+ if (!this.disabledWaterMark && this.editor.getValue() === "" && BI.isKey(o.watermark)) {
+ this.watermark && this.watermark.visible();
+ } else {
+ this.watermark && this.watermark.invisible();
+ }
+ },
+
+ setErrorText: function (text) {
+ this.options.errorText = text;
+ },
+
+ getErrorText: function () {
+ return this.options.errorText;
+ },
+
+ setWaterMark: function (v) {
+ if (!BI.isKey(v)) {
+ return;
+ }
+
+ this.options.watermark = v;
+
+ if (BI.isNull(this.watermark)) {
+ this._assertWaterMark();
+ BI.createWidget({
+ type: "bi.absolute",
+ element: this.contentWrapper,
+ items: [{
+ el: this.watermark,
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0,
+ }],
+ });
+ }
+ this.watermark.setText(v);
+ },
+
+ _setErrorVisible: function (b) {
+ var o = this.options;
+ var errorText = o.errorText;
+ if (BI.isFunction(errorText)) {
+ errorText = errorText(BI.trim(this.editor.getValue()));
+ }
+ if (!this.disabledError && BI.isKey(errorText)) {
+ BI.Bubbles[b ? "show" : "hide"](this.getName(), errorText, this, {
+ adjustYOffset: 2
+ });
+ this._checkToolTip();
+ }
+ },
+
+ disableError: function () {
+ this.disabledError = true;
+ this._checkError();
+ },
+
+ enableError: function () {
+ this.disabledError = false;
+ this._checkError();
+ },
+
+ disableWaterMark: function () {
+ this.disabledWaterMark = true;
+ this._checkWaterMark();
+ },
+
+ enableWaterMark: function () {
+ this.disabledWaterMark = false;
+ this._checkWaterMark();
+ },
+
+ focus: function () {
+ this.element.addClass("text-editor-focus");
+ this.editor.focus();
+ },
+
+ blur: function () {
+ this.element.removeClass("text-editor-focus");
+ this.editor.blur();
+ },
+
+ selectAll: function () {
+ this.editor.selectAll();
+ },
+
+ onKeyDown: function (k) {
+ this.editor.onKeyDown(k);
+ },
+
+ setValue: function (v) {
+ BI.Editor.superclass.setValue.apply(this, arguments);
+ this.editor.setValue(v);
+ this._checkError();
+ this._checkWaterMark();
+ },
+
+ getLastValidValue: function () {
+ return this.editor.getLastValidValue();
+ },
+
+ getLastChangedValue: function () {
+ return this.editor.getLastChangedValue();
+ },
+
+ getValue: function () {
+ if (!this.isValid()) {
+ return BI.trim(this.editor.getLastValidValue());
+ }
+ return BI.trim(this.editor.getValue());
+ },
+
+ isEditing: function () {
+ return this.editor.isEditing();
+ },
+
+ isValid: function () {
+ return this.editor.isValid();
+ },
+
+ destroyed: function () {
+ BI.Bubbles.remove(this.getName());
+ }
+});
+BI.Editor.EVENT_CHANGE = "EVENT_CHANGE";
+BI.Editor.EVENT_FOCUS = "EVENT_FOCUS";
+BI.Editor.EVENT_BLUR = "EVENT_BLUR";
+BI.Editor.EVENT_CLICK = "EVENT_CLICK";
+BI.Editor.EVENT_KEY_DOWN = "EVENT_KEY_DOWN";
+BI.Editor.EVENT_SPACE = "EVENT_SPACE";
+BI.Editor.EVENT_BACKSPACE = "EVENT_BACKSPACE";
+
+BI.Editor.EVENT_START = "EVENT_START";
+BI.Editor.EVENT_PAUSE = "EVENT_PAUSE";
+BI.Editor.EVENT_STOP = "EVENT_STOP";
+BI.Editor.EVENT_CONFIRM = "EVENT_CONFIRM";
+BI.Editor.EVENT_CHANGE_CONFIRM = "EVENT_CHANGE_CONFIRM";
+BI.Editor.EVENT_VALID = "EVENT_VALID";
+BI.Editor.EVENT_ERROR = "EVENT_ERROR";
+BI.Editor.EVENT_ENTER = "EVENT_ENTER";
+BI.Editor.EVENT_RESTRICT = "EVENT_RESTRICT";
+BI.Editor.EVENT_REMOVE = "EVENT_REMOVE";
+BI.Editor.EVENT_EMPTY = "EVENT_EMPTY";
+
+BI.shortcut("bi.editor", BI.Editor);
diff --git a/src/main/resources/com/fr/fineui/base/single/editor/editor.multifile.js b/src/main/resources/com/fr/fineui/base/single/editor/editor.multifile.js
new file mode 100644
index 0000000..d917a47
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/editor/editor.multifile.js
@@ -0,0 +1,99 @@
+/**
+ * 多文件
+ *
+ * Created by GUY on 2016/4/13.
+ * @class BI.MultifileEditor
+ * @extends BI.Single
+ * @abstract
+ */
+BI.MultifileEditor = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ var conf = BI.MultifileEditor.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-multifile-editor",
+ multiple: false,
+ maxSize: -1, // 1024 * 1024
+ accept: "",
+ url: ""
+ });
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ this.file = BI.createWidget({
+ type: "bi.file",
+ cls: "multifile-editor",
+ width: "100%",
+ height: "100%",
+ name: o.name,
+ url: o.url,
+ multiple: o.multiple,
+ accept: o.accept,
+ maxSize: o.maxSize,
+ maxLength: o.maxLength,
+ title: o.title
+ });
+ this.file.on(BI.File.EVENT_CHANGE, function () {
+ self.fireEvent(BI.MultifileEditor.EVENT_CHANGE, arguments);
+ });
+ this.file.on(BI.File.EVENT_UPLOADSTART, function () {
+ self.fireEvent(BI.MultifileEditor.EVENT_UPLOADSTART, arguments);
+ });
+ this.file.on(BI.File.EVENT_ERROR, function () {
+ self.fireEvent(BI.MultifileEditor.EVENT_ERROR, arguments);
+ });
+ this.file.on(BI.File.EVENT_PROGRESS, function () {
+ self.fireEvent(BI.MultifileEditor.EVENT_PROGRESS, arguments);
+ });
+ this.file.on(BI.File.EVENT_UPLOADED, function () {
+ self.fireEvent(BI.MultifileEditor.EVENT_UPLOADED, arguments);
+ });
+
+ BI.createWidget({
+ type: "bi.absolute",
+ element: this,
+ items: [{
+ el: {
+ type: "bi.adaptive",
+ scrollable: false,
+ items: [this.file]
+ },
+ top: 0,
+ right: 0,
+ left: 0,
+ bottom: 0
+ }]
+ });
+ },
+
+ _reset: function () {
+ this.file.reset();
+ },
+
+ setMaxFileLength: function (v) {
+ this.file.setMaxFileLength(v);
+ },
+
+ select: function () {
+ this.file.select();
+ },
+
+ getValue: function () {
+ return this.file.getValue();
+ },
+
+ upload: function () {
+ this._reset();
+ this.file.upload();
+ },
+
+ reset: function () {
+ this._reset();
+ }
+});
+BI.MultifileEditor.EVENT_CHANGE = "EVENT_CHANGE";
+BI.MultifileEditor.EVENT_UPLOADSTART = "EVENT_UPLOADSTART";
+BI.MultifileEditor.EVENT_ERROR = "EVENT_ERROR";
+BI.MultifileEditor.EVENT_PROGRESS = "EVENT_PROGRESS";
+BI.MultifileEditor.EVENT_UPLOADED = "EVENT_UPLOADED";
+BI.shortcut("bi.multifile_editor", BI.MultifileEditor);
diff --git a/src/main/resources/com/fr/fineui/base/single/editor/editor.textarea.js b/src/main/resources/com/fr/fineui/base/single/editor/editor.textarea.js
new file mode 100644
index 0000000..177f33b
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/editor/editor.textarea.js
@@ -0,0 +1,252 @@
+/**
+ *
+ * Created by GUY on 2016/1/18.
+ * @class BI.TextAreaEditor
+ * @extends BI.Single
+ */
+BI.TextAreaEditor = BI.inherit(BI.Single, {
+ _defaultConfig: function () {
+ return BI.extend(BI.TextAreaEditor.superclass._defaultConfig.apply(), {
+ baseCls: "bi-textarea-editor",
+ value: "",
+ errorText: "",
+ adjustYOffset: 2,
+ adjustXOffset: 0,
+ offsetStyle: "left",
+ validationChecker: function () {
+ return true;
+ },
+ scrolly: true,
+ });
+ },
+
+ render: function () {
+ var o = this.options, self = this;
+ this.content = BI.createWidget({
+ type: "bi.layout",
+ tagName: "textarea",
+ width: "100%",
+ height: "100%",
+ cls: "bi-textarea textarea-editor-content display-block",
+ css: o.scrolly ? null : {
+ overflowY: "hidden",
+ },
+ });
+ this.content.element.css({ resize: "none" });
+ BI.createWidget({
+ type: "bi.absolute",
+ element: this,
+ items: [{
+ el: {
+ type: "bi.adaptive",
+ items: [this.content]
+ },
+ left: 4,
+ right: 4,
+ top: 2,
+ bottom: 2
+ }]
+ });
+
+ this.content.element.on("input propertychange", function (e) {
+ self._checkError();
+ self._checkWaterMark();
+ self.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.CHANGE, self.getValue(), self);
+ self.fireEvent(BI.TextAreaEditor.EVENT_CHANGE);
+ if (BI.isEmptyString(self.getValue())) {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.EMPTY, self.getValue(), self);
+ }
+ });
+
+ this.content.element.focus(function () {
+ self._checkError();
+ self._focus();
+ self.fireEvent(BI.TextAreaEditor.EVENT_FOCUS);
+ BI.Widget._renderEngine.createElement(document).bind("mousedown." + self.getName(), function (e) {
+ if (BI.DOM.isExist(self) && !self.element.__isMouseInBounds__(e)) {
+ BI.Widget._renderEngine.createElement(document).unbind("mousedown." + self.getName());
+ self.content.element.blur();
+ }
+ });
+ });
+ this.content.element.blur(function () {
+ self._setErrorVisible(false);
+ self._blur();
+ if (!self._isError()) {
+ self.fireEvent(BI.TextAreaEditor.EVENT_CONFIRM);
+ }
+ self.fireEvent(BI.TextAreaEditor.EVENT_BLUR);
+ BI.Widget._renderEngine.createElement(document).unbind("mousedown." + self.getName());
+ });
+ this.content.element.keydown(function () {
+ // 水印快速消失
+ self._checkWaterMark();
+ });
+ this.content.element.keyup(function (e) {
+ self.fireEvent(BI.TextAreaEditor.EVENT_KEY_DOWN, e.keyCode);
+ });
+ this.content.element.click(function (e) {
+ e.stopPropagation();
+ });
+ if (BI.isKey(o.value)) {
+ this.setValue(o.value);
+ }
+ if (BI.isNotNull(o.style)) {
+ this.setStyle(o.style);
+ }
+ this._checkWaterMark();
+ },
+
+ _checkWaterMark: function () {
+ var self = this, o = this.options;
+ var val = this.getValue();
+ if (BI.isNotEmptyString(val)) {
+ this.watermark && this.watermark.destroy();
+ this.watermark = null;
+ } else {
+ if (BI.isNotEmptyString(o.watermark)) {
+ if (!this.watermark) {
+ this.watermark = BI.createWidget({
+ type: "bi.label",
+ cls: "bi-water-mark textarea-watermark",
+ textAlign: "left",
+ whiteSpace: o.scrolly ? "normal" : "nowrap",
+ title: o.watermark,
+ text: o.watermark,
+ invalid: o.invalid,
+ disabled: o.disabled,
+ hgap: 6,
+ vgap: o.height > 24 ? 4 : 2,
+ height: o.height > 24 ? "" : o.height,
+ });
+ this.watermark.element.bind({
+ mousedown: function (e) {
+ if (self.isEnabled()) {
+ self.focus();
+ } else {
+ self.blur();
+ }
+ e.stopEvent();
+ },
+ click: function (e) {
+ e.stopPropagation();
+ },
+ });
+ BI.createWidget({
+ type: "bi.absolute",
+ element: this,
+ items: [{
+ el: this.watermark,
+ left: 0,
+ top: 0,
+ right: 0
+ }]
+ });
+ } else {
+ this.watermark.setText(o.watermark);
+ this.watermark.setValid(!o.invalid);
+ this.watermark.setEnable(!o.disabled);
+ }
+ }
+ }
+ },
+
+ _isError: function () {
+ return this.isEnabled() && !this.options.validationChecker(this.getValue());
+ },
+
+ _checkError: function () {
+ this._setErrorVisible(this._isError());
+ },
+
+ _focus: function () {
+ this.content.element.addClass("textarea-editor-focus");
+ this._checkWaterMark();
+ if (BI.isEmptyString(this.getValue())) {
+ this.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.EMPTY, this.getValue(), this);
+ }
+ },
+
+ _blur: function () {
+ this.content.element.removeClass("textarea-editor-focus");
+ this._checkWaterMark();
+ },
+
+ _setErrorVisible: function (b) {
+ var o = this.options;
+ var errorText = o.errorText;
+ if (BI.isFunction(errorText)) {
+ errorText = errorText(BI.trim(this.getValue()));
+ }
+ if (!this.disabledError && BI.isKey(errorText)) {
+ BI.Bubbles[b ? "show" : "hide"](this.getName(), errorText, this, {
+ adjustYOffset: o.adjustYOffset,
+ adjustXOffset: o.adjustXOffset,
+ offsetStyle: o.offsetStyle,
+ });
+ }
+ },
+
+ _defaultState: function () {
+ if (BI.isEmptyString(this.getValue())) {
+ this.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.EMPTY, this.getValue(), this);
+ this.fireEvent(BI.TextAreaEditor.EVENT_EMPTY);
+ }
+ },
+
+ focus: function () {
+ this._focus();
+ this.content.element.focus();
+ },
+
+ blur: function () {
+ this._blur();
+ this.content.element.blur();
+ },
+
+ getValue: function () {
+ return this.content.element.val();
+ },
+
+ setValue: function (value) {
+ this.content.element.val(value);
+ this._checkError();
+ this._checkWaterMark();
+ this._defaultState();
+ },
+
+ setStyle: function (style) {
+ this.style = style;
+ this.element.css(style);
+ this.content.element.css(BI.extend({}, style, {
+ color: style.color || BI.DOM.getContrastColor(BI.DOM.isRGBColor(style.backgroundColor) ? BI.DOM.rgb2hex(style.backgroundColor) : style.backgroundColor)
+ }));
+ },
+
+ getStyle: function () {
+ return this.style;
+ },
+
+ setWatermark: function (v) {
+ this.options.watermark = v;
+ this._checkWaterMark();
+ },
+
+ _setValid: function (b) {
+ BI.TextAreaEditor.superclass._setValid.apply(this, arguments);
+ // this.content.setValid(b);
+ // this.watermark && this.watermark.setValid(b);
+ },
+
+ _setEnable: function (b) {
+ BI.TextAreaEditor.superclass._setEnable.apply(this, [b]);
+ this.content && (this.content.element[0].disabled = !b);
+ }
+});
+BI.TextAreaEditor.EVENT_CHANGE = "EVENT_CHANGE";
+BI.TextAreaEditor.EVENT_BLUR = "EVENT_BLUR";
+BI.TextAreaEditor.EVENT_FOCUS = "EVENT_FOCUS";
+BI.TextAreaEditor.EVENT_CONFIRM = "EVENT_CONFIRM";
+BI.TextAreaEditor.EVENT_EMPTY = "EVENT_EMPTY";
+BI.TextAreaEditor.EVENT_KEY_DOWN = "EVENT_KEY_DOWN";
+BI.shortcut("bi.textarea_editor", BI.TextAreaEditor);
diff --git a/src/main/resources/com/fr/fineui/base/single/html/__test__/html.test.js b/src/main/resources/com/fr/fineui/base/single/html/__test__/html.test.js
new file mode 100644
index 0000000..2d599ec
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/html/__test__/html.test.js
@@ -0,0 +1,42 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/3/10
+ */
+
+describe("HtmlTest", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("html_h1", function () {
+ var a = BI.Test.createWidget({
+ type: "bi.html",
+ text: "
在bi.html标签中使用html原生标签 "
+ });
+ expect(a.element.find("h1").length).to.equal(1);
+ a.destroy();
+ });
+
+
+ /**
+ * test_author_windy
+ */
+ it("html测试属性方法", function () {
+ var a = BI.Test.createWidget({
+ type: "bi.html",
+ text: "
在bi.html标签中使用html原生标签 ",
+ height: 200,
+ width: 200,
+ value: "1",
+ highLight: true,
+ hgap: 10,
+ vgap: 10,
+ handler: BI.emptyFn
+ });
+ a.setValue("DDDDD");
+ a.setStyle({"background-color": "red"});
+ expect(a.text.element.css("background-color")).to.equal("rgb(255, 0, 0)");
+ a.destroy();
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/base/single/html/html.js b/src/main/resources/com/fr/fineui/base/single/html/html.js
new file mode 100644
index 0000000..af88bac
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/html/html.js
@@ -0,0 +1,114 @@
+/**
+ * guy 表示一行数据,通过position来定位位置的数据
+ * @class BI.Html
+ * @extends BI.Single
+ */
+BI.Html = BI.inherit(BI.Single, {
+
+ props: {
+ baseCls: "bi-html",
+ textAlign: "left",
+ whiteSpace: "normal",
+ lineHeight: null,
+ handler: null, // 如果传入handler,表示处理文字的点击事件,不是区域的
+ hgap: 0,
+ vgap: 0,
+ lgap: 0,
+ rgap: 0,
+ tgap: 0,
+ bgap: 0,
+ text: "",
+ highLight: false,
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ if (o.hgap + o.lgap > 0) {
+ this.element.css({
+ "padding-left": (o.hgap + o.lgap) / BI.pixRatio + BI.pixUnit
+ });
+ }
+ if (o.hgap + o.rgap > 0) {
+ this.element.css({
+ "padding-right": (o.hgap + o.rgap) / BI.pixRatio + BI.pixUnit
+ });
+ }
+ if (o.vgap + o.tgap > 0) {
+ this.element.css({
+ "padding-top": (o.vgap + o.tgap) / BI.pixRatio + BI.pixUnit
+ });
+ }
+ if (o.vgap + o.bgap > 0) {
+ this.element.css({
+ "padding-bottom": (o.vgap + o.bgap) / BI.pixRatio + BI.pixUnit
+ });
+ }
+ if (BI.isNumber(o.height)) {
+ this.element.css({lineHeight: o.height / BI.pixRatio + BI.pixUnit});
+ }
+ if (BI.isNumber(o.lineHeight)) {
+ this.element.css({lineHeight: o.lineHeight / BI.pixRatio + BI.pixUnit});
+ }
+ if (BI.isWidthOrHeight(o.maxWidth)) {
+ this.element.css({maxWidth: o.maxWidth});
+ }
+ this.element.css({
+ textAlign: o.textAlign,
+ whiteSpace: o.whiteSpace,
+ textOverflow: o.whiteSpace === 'nowrap' ? "ellipsis" : "",
+ overflow: o.whiteSpace === "nowrap" ? "" : "auto"
+ });
+ if (o.handler) {
+ this.text = BI.createWidget({
+ type: "bi.layout",
+ tagName: "span"
+ });
+ this.text.element.click(function () {
+ o.handler(self.getValue());
+ });
+ BI.createWidget({
+ type: "bi.default",
+ element: this,
+ items: [this.text]
+ });
+ } else {
+ this.text = this;
+ }
+
+ if (BI.isKey(o.text)) {
+ this.setText(o.text);
+ } else if (BI.isKey(o.value)) {
+ this.setText(o.value);
+ }
+ if (o.highLight) {
+ this.doHighLight();
+ }
+ },
+
+ doHighLight: function () {
+ this.text.element.addClass("bi-high-light");
+ },
+
+ unHighLight: function () {
+ this.text.element.removeClass("bi-high-light");
+ },
+
+ setValue: function (text) {
+ BI.Html.superclass.setValue.apply(this, arguments);
+ if (!this.isReadOnly()) {
+ this.setText(text);
+ }
+ },
+
+ setStyle: function (css) {
+ this.text.element.css(css);
+ },
+
+ setText: function (text) {
+ BI.Html.superclass.setText.apply(this, arguments);
+ this.options.text = text;
+ this.text.element.html(text);
+ }
+});
+
+BI.shortcut("bi.html", BI.Html);
diff --git a/src/main/resources/com/fr/fineui/base/single/icon/icon.js b/src/main/resources/com/fr/fineui/base/single/icon/icon.js
new file mode 100644
index 0000000..112ea53
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/icon/icon.js
@@ -0,0 +1,21 @@
+/**
+ * guy 图标
+ * @class BI.Icon
+ * @extends BI.Single
+ */
+BI.Icon = BI.inherit(BI.Single, {
+ _defaultConfig: function () {
+ var conf = BI.Icon.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ tagName: "i",
+ baseCls: (conf.baseCls || "") + " x-icon b-font horizon-center display-block"
+ });
+ },
+
+ render: function () {
+ if (BI.isIE9Below && BI.isIE9Below()) {
+ this.element.addClass("hack");
+ }
+ }
+});
+BI.shortcut("bi.icon", BI.Icon);
diff --git a/src/main/resources/com/fr/fineui/base/single/iframe/__test__/iframe.test.js b/src/main/resources/com/fr/fineui/base/single/iframe/__test__/iframe.test.js
new file mode 100644
index 0000000..8e99a48
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/iframe/__test__/iframe.test.js
@@ -0,0 +1,23 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/3/17
+ */
+
+describe("IframeTest", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("directionPager", function () {
+ var a = BI.Test.createWidget({
+ type: "bi.iframe"
+ });
+ a.setSrc("http://www.baidu.com");
+ a.setName("testIFrame");
+ expect(a.element.attr("src"), "http://www.baidu.com");
+ expect(a.getSrc(), "http://www.baidu.com");
+ expect(a.getName(), "testIFrame");
+ a.destroy();
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/base/single/iframe/iframe.js b/src/main/resources/com/fr/fineui/base/single/iframe/iframe.js
new file mode 100644
index 0000000..a7137fa
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/iframe/iframe.js
@@ -0,0 +1,57 @@
+/**
+ * @class BI.Iframe
+ * @extends BI.Single
+ * @abstract
+ * Created by GameJian on 2016/3/2.
+ */
+BI.Iframe = BI.inherit(BI.Single, {
+ _defaultConfig: function (config) {
+ var conf = BI.Iframe.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ tagName: "iframe",
+ baseCls: (conf.baseCls || "") + " bi-iframe",
+ src: "",
+ name: "",
+ attributes: {},
+ width: "100%",
+ height: "100%"
+ });
+ },
+
+ render: function () {
+ var self = this;
+ this.element.on("load", function () {
+ self.fireEvent("EVENT_LOADED");
+ });
+ },
+
+ _initProps: function () {
+ BI.Iframe.superclass._initProps.apply(this, arguments);
+ var o = this.options;
+ this.options.attributes = BI.extend({
+ frameborder: 0,
+ src: o.src,
+ name: o.name
+ }, this.options.attributes);
+ },
+
+ setSrc: function (src) {
+ this.options.src = src;
+ this.element.attr("src", src);
+ },
+
+ getSrc: function () {
+ return this.options.src;
+ },
+
+ setName: function (name) {
+ this.options.name = name;
+ this.element.attr("name", name);
+ },
+
+ getName: function () {
+ return this.options.name;
+ }
+});
+
+BI.shortcut("bi.iframe", BI.Iframe);
diff --git a/src/main/resources/com/fr/fineui/base/single/img/__test__/img.test.js b/src/main/resources/com/fr/fineui/base/single/img/__test__/img.test.js
new file mode 100644
index 0000000..d16ca92
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/img/__test__/img.test.js
@@ -0,0 +1,23 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/3/17
+ */
+
+describe("ImgTest", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("img", function () {
+ var a = BI.Test.createWidget({
+ type: "bi.img",
+ iconWidth: 36,
+ iconHeight: 36
+ });
+ a.setSrc("test.png");
+ expect(a.element.attr("src")).to.equal("test.png");
+ expect(a.getSrc()).to.equal("test.png");
+ a.destroy();
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/base/single/img/img.js b/src/main/resources/com/fr/fineui/base/single/img/img.js
new file mode 100644
index 0000000..aa90aaf
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/img/img.js
@@ -0,0 +1,40 @@
+/**
+ * ͼƬ
+ *
+ * Created by GUY on 2016/1/26.
+ * @class BI.Img
+ * @extends BI.Single
+ * @abstract
+ */
+BI.Img = BI.inherit(BI.Single, {
+ _defaultConfig: function (config) {
+ var conf = BI.Img.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ tagName: "img",
+ baseCls: (conf.baseCls || "") + " bi-img display-block",
+ src: "",
+ attributes: config.src ? { src: config.src } : {},
+ width: "100%",
+ height: "100%"
+ });
+ },
+
+ _initProps: function () {
+ BI.Img.superclass._initProps.apply(this, arguments);
+ var o = this.options;
+ this.options.attributes = BI.extend({
+ src: o.src
+ }, this.options.attributes);
+ },
+
+ setSrc: function (src) {
+ this.options.src = src;
+ this.element.attr("src", src);
+ },
+
+ getSrc: function () {
+ return this.options.src;
+ }
+});
+
+BI.shortcut("bi.img", BI.Img);
diff --git a/src/main/resources/com/fr/fineui/base/single/input/checkbox/checkbox.image.js b/src/main/resources/com/fr/fineui/base/single/input/checkbox/checkbox.image.js
new file mode 100644
index 0000000..dc17035
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/input/checkbox/checkbox.image.js
@@ -0,0 +1,22 @@
+/**
+ * guy
+ * @extends BI.Single
+ * @type {*|void|Object}
+ */
+BI.ImageCheckbox = BI.inherit(BI.IconButton, {
+ _defaultConfig: function () {
+ var conf = BI.ImageCheckbox.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-image-checkbox check-box-icon",
+ selected: false,
+ handler: BI.emptyFn,
+ width: 16,
+ height: 16,
+ iconWidth: 16,
+ iconHeight: 16
+ });
+ }
+});
+BI.ImageCheckbox.EVENT_CHANGE = BI.IconButton.EVENT_CHANGE;
+
+BI.shortcut("bi.image_checkbox", BI.ImageCheckbox);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/base/single/input/checkbox/checkbox.js b/src/main/resources/com/fr/fineui/base/single/input/checkbox/checkbox.js
new file mode 100644
index 0000000..4a72f43
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/input/checkbox/checkbox.js
@@ -0,0 +1,61 @@
+/**
+ * guy
+ * @extends BI.Single
+ * @type {*|void|Object}
+ */
+BI.Checkbox = BI.inherit(BI.BasicButton, {
+
+ props: {
+ baseCls: "bi-checkbox",
+ selected: false,
+ handler: BI.emptyFn,
+ width: 14,
+ height: 14,
+ iconWidth: 14,
+ iconHeight: 14
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ return {
+ type: "bi.center_adapt",
+ items: [{
+ type: "bi.default",
+ ref: function (_ref) {
+ self.checkbox = _ref;
+ },
+ cls: "checkbox-content",
+ width: o.iconWidth,
+ height: o.iconHeight
+ }]
+ };
+ },
+
+ _setEnable: function (enable) {
+ BI.Checkbox.superclass._setEnable.apply(this, arguments);
+ if (enable === true) {
+ this.checkbox.element.removeClass("base-disabled disabled");
+ } else {
+ this.checkbox.element.addClass("base-disabled disabled");
+ }
+ },
+
+ doClick: function () {
+ BI.Checkbox.superclass.doClick.apply(this, arguments);
+ if(this.isValid()) {
+ this.fireEvent(BI.Checkbox.EVENT_CHANGE);
+ }
+ },
+
+ setSelected: function (b) {
+ BI.Checkbox.superclass.setSelected.apply(this, arguments);
+ if (b) {
+ this.checkbox.element.addClass("bi-high-light-background");
+ } else {
+ this.checkbox.element.removeClass("bi-high-light-background");
+ }
+ }
+});
+BI.Checkbox.EVENT_CHANGE = "EVENT_CHANGE";
+
+BI.shortcut("bi.checkbox", BI.Checkbox);
diff --git a/src/main/resources/com/fr/fineui/base/single/input/file.js b/src/main/resources/com/fr/fineui/base/single/input/file.js
new file mode 100644
index 0000000..bf4e207
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/input/file.js
@@ -0,0 +1,697 @@
+/**
+ * 文件
+ *
+ * Created by GUY on 2016/1/27.
+ * @class BI.File
+ * @extends BI.Single
+ * @abstract
+ */
+(function (document) {
+
+ /**
+ * @description normalize input.files. create if not present, add item method if not present
+ * @param Object generated wrap object
+ * @return Object the wrap object itself
+ */
+ var F = (function (item) {
+ return function (input) {
+ var files = input.files || [input];
+ if (!files.item) {
+ files.item = item;
+ }
+ return files;
+ };
+ })(function (i) {
+ return this[i];
+ });
+
+ var event = {
+
+ /**
+ * @description add an event via addEventListener or attachEvent
+ * @param DOMElement the element to add event
+ * @param String event name without "on" (e.g. "mouseover")
+ * @param Function the callback to associate as event
+ * @return Object noswfupload.event
+ */
+ add: document.addEventListener ?
+ function (node, name, callback) {
+ node.addEventListener(name, callback, false);
+ return this;
+ } :
+ function (node, name, callback) {
+ node.attachEvent("on" + name, callback);
+ return this;
+ },
+
+ /**
+ * @description remove an event via removeEventListener or detachEvent
+ * @param DOMElement the element to remove event
+ * @param String event name without "on" (e.g. "mouseover")
+ * @param Function the callback associated as event
+ * @return Object noswfupload.event
+ */
+ del: document.removeEventListener ?
+ function (node, name, callback) {
+ node.removeEventListener(name, callback, false);
+ return this;
+ } :
+ function (node, name, callback) {
+ node.detachEvent("on" + name, callback);
+ return this;
+ },
+
+ /**
+ * @description to block event propagation and prevent event default
+ * @param void generated event or undefined
+ * @return Boolean false
+ */
+ stop: function (e) {
+ if (!e) {
+ if (self.event) {
+ event.returnValue = !(event.cancelBubble = true);
+ }
+ } else {
+ e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true;
+ e.preventDefault ? e.preventDefault() : e.returnValue = false;
+ }
+
+ return false;
+ }
+ };
+
+ var sendFile = (function (toString) {
+ var multipart = function (boundary, name, file) {
+ return "--".concat(
+ boundary, CRLF,
+ "Content-Disposition: form-data; name=\"", name, "\"; filename=\"", _global.encodeURIComponent(file.fileName), "\"", CRLF,
+ "Content-Type: application/octet-stream", CRLF,
+ CRLF,
+ file.getAsBinary(), CRLF,
+ "--", boundary, "--", CRLF
+ );
+ },
+ isFunction = function (Function) {
+ return toString.call(Function) === "[object Function]";
+ },
+ split = "onabort.onerror.onloadstart.onprogress".split("."),
+ length = split.length,
+ CRLF = "\r\n",
+ xhr = this.XMLHttpRequest ? new XMLHttpRequest : new ActiveXObject("Microsoft.XMLHTTP"),
+ sendFile;
+
+ // FireFox 3+, Safari 4 beta (Chrome 2 beta file is buggy and will not work)
+ if (xhr.upload || xhr.sendAsBinary) {
+ sendFile = function (handler, maxSize, width, height) {
+ var current = handler.current;
+ if (-1 < maxSize && maxSize < handler.file.fileSize) {
+ if (isFunction(handler.onerror)) {
+ handler.onerror();
+ }
+ return;
+ }
+ for (var
+ xhr = new XMLHttpRequest,
+ upload = xhr.upload || {
+ addEventListener: function (event, callback) {
+ this["on" + event] = callback;
+ }
+ },
+ i = 0;
+ i < length;
+ i++
+ ) {
+ upload.addEventListener(
+ split[i].substring(2),
+ (function (event) {
+ return function (rpe) {
+ if (isFunction(handler[event])) {
+ handler[event](rpe, xhr);
+ }
+ };
+ })(split[i]),
+ false
+ );
+ }
+ upload.addEventListener(
+ "load",
+ function (rpe) {
+ if (handler.onreadystatechange === false) {
+ if (isFunction(handler.onload)) {
+ handler.onload(rpe, xhr);
+ }
+ } else {
+ setTimeout(function () {
+ if (xhr.readyState === 4) {
+ if (isFunction(handler.onload)) {
+ handler.onload(rpe, xhr);
+ }
+ } else {
+ setTimeout(arguments.callee, 15);
+ }
+ }, 15);
+ }
+ },
+ false
+ );
+ xhr.open("post", BI.appendQuery(handler.url, {
+ filename: _global.encodeURIComponent(handler.file.fileName),
+ }), true);
+ if (!xhr.upload) {
+ var rpe = { loaded: 0, total: handler.file.fileSize || handler.file.size, simulation: true };
+ rpe.interval = setInterval(function () {
+ rpe.loaded += 1024 / 4;
+ if (rpe.total <= rpe.loaded) {
+ rpe.loaded = rpe.total;
+ }
+ upload.onprogress(rpe);
+ }, 100);
+ xhr.onabort = function () {
+ upload.onabort({});
+ };
+ xhr.onerror = function () {
+ upload.onerror({});
+ };
+ xhr.onreadystatechange = function () {
+ switch (xhr.readyState) {
+ case 2:
+ case 3:
+ if (rpe.total <= rpe.loaded) {
+ rpe.loaded = rpe.total;
+ }
+ upload.onprogress(rpe);
+ break;
+ case 4:
+ clearInterval(rpe.interval);
+ rpe.interval = 0;
+ rpe.loaded = rpe.total;
+ upload.onprogress(rpe);
+ if (199 < xhr.status && xhr.status < 400) {
+ upload["onload"]({});
+ var attachO = BI.jsonDecode(xhr.responseText);
+ attachO.filename = handler.file.fileName;
+ if (handler.file.type.indexOf("image") != -1) {
+ attachO.attach_type = "image";
+ }
+ handler.attach_array[current] = attachO;
+ } else {
+ upload["onerror"]({});
+ }
+ break;
+ }
+ };
+ upload.onloadstart(rpe);
+ } else {
+ xhr.onreadystatechange = function () {
+ switch (xhr.readyState) {
+ case 4:
+ var attachO = BI.jsonDecode(xhr.responseText);
+ if (handler.file.type.indexOf("image") != -1) {
+ attachO.attach_type = "image";
+ }
+ attachO.filename = handler.file.fileName;
+ if (handler.maxLength == 1) {
+ handler.attach_array[0] = attachO;
+ // handler.attach_array.push(attachO);
+ } else {
+ handler.attach_array[current] = attachO;
+ }
+ break;
+ }
+ };
+ if (isFunction(upload.onloadstart)) {
+ upload.onloadstart();
+ }
+ }
+ var boundary = "AjaxUploadBoundary" + (new Date).getTime();
+ xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
+ if (handler.file.getAsBinary) {
+ xhr[xhr.sendAsBinary ? "sendAsBinary" : "send"](multipart(boundary, handler.name, handler.file));
+ } else {
+ xhr.setRequestHeader("Content-Type", "multipart/form-data");
+ // xhr.setRequestHeader("X-Name", handler.name);
+ // xhr.setRequestHeader("X-File-Name", handler.file.fileName);
+ var form = new FormData();
+ form.append("FileData", handler.file);
+ xhr.send(form);
+ }
+ return handler;
+ };
+ }
+ // Internet Explorer, Opera, others
+ else {
+ sendFile = function (handler, maxSize, width, height) {
+ var current = handler.current;
+ var url = handler.url.concat(-1 === handler.url.indexOf("?") ? "?" : "&", "AjaxUploadFrame=true"),
+ rpe = {
+ loaded: 1, total: 100, simulation: true, interval: setInterval(function () {
+ if (rpe.loaded < rpe.total) {
+ ++rpe.loaded;
+ }
+ if (isFunction(handler.onprogress)) {
+ handler.onprogress(rpe, {});
+ }
+ }, 100)
+ },
+ onload = function () {
+ iframe.onreadystatechange = iframe.onload = iframe.onerror = null;
+ form.parentNode.removeChild(form);
+ form = null;
+ clearInterval(rpe.interval);
+ // rpe.loaded = rpe.total;
+ try {
+ var responseText = (iframe.contentWindow.document || iframe.contentWindow.contentDocument).body.innerHTML;
+ var attachO = BI.jsonDecode(responseText);
+ if (handler.file.type.indexOf("image") != -1) {
+ attachO.attach_type = "image";
+ }
+
+ // attachO.fileSize = responseText.length;
+ try {
+ // decodeURIComponent特殊字符可能有问题, catch一下,保证能正常上传
+ attachO.filename = _global.decodeURIComponent(handler.file.fileName);
+ } catch (e) {
+ attachO.filename = handler.file.fileName;
+ }
+ if (handler.maxLength == 1) {
+ handler.attach_array[0] = attachO;
+ } else {
+ handler.attach_array[current] = attachO;
+ }
+ } catch (e) {
+ if (isFunction(handler.onerror)) {
+ handler.onerror(rpe, event || _global.event);
+ }
+ }
+ if (isFunction(handler.onload)) {
+ handler.onload(rpe, { responseText: responseText });
+ }
+ },
+ target = ["AjaxUpload", (new Date).getTime(), String(Math.random()).substring(2)].join("_");
+ try { // IE < 8 does not accept enctype attribute ...
+ var form = document.createElement("
"),
+ iframe = handler.iframe || (handler.iframe = document.createElement("
"));
+ } catch (e) {
+ var form = document.createElement("form"),
+ iframe = handler.iframe || (handler.iframe = document.createElement("iframe"));
+ form.setAttribute("enctype", "multipart/form-data");
+ iframe.setAttribute("name", iframe.id = target);
+ iframe.setAttribute("src", url);
+ }
+ iframe.style.position = "absolute";
+ iframe.style.left = iframe.style.top = "-10000px";
+ iframe.onload = onload;
+ iframe.onerror = function (event) {
+ if (isFunction(handler.onerror)) {
+ handler.onerror(rpe, event || _global.event);
+ }
+ };
+ iframe.onreadystatechange = function () {
+ if (/loaded|complete/i.test(iframe.readyState)) {
+ onload();
+
+ // wei : todo,将附件信息放到handler.attach
+ } else if (isFunction(handler.onloadprogress)) {
+ if (rpe.loaded < rpe.total) {
+ ++rpe.loaded;
+ }
+ handler.onloadprogress(rpe, {
+ readyState: {
+ loading: 2,
+ interactive: 3,
+ loaded: 4,
+ complete: 4
+ }[iframe.readyState] || 1
+ });
+ }
+ };
+ form.setAttribute("action", handler.url + "&filename=" + _global.encodeURIComponent(handler.file.fileName));
+ form.setAttribute("target", iframe.id);
+ form.setAttribute("method", "post");
+ form.appendChild(handler.file);
+ form.style.display = "none";
+ if (isFunction(handler.onloadstart)) {
+ handler.onloadstart(rpe, {});
+ }
+ with (document.body || document.documentElement) {
+ appendChild(iframe);
+ appendChild(form);
+ form.submit();
+ }
+
+ return handler;
+ };
+ }
+ xhr = null;
+ return sendFile;
+ })(Object.prototype.toString);
+
+ var sendFiles = function (handler, maxSize, width, height) {
+
+ var length = handler.files.length,
+ i = 0,
+ onload = handler.onload,
+ onloadstart = handler.onloadstart;
+ handler.current = 0;
+ handler.total = 0;
+ handler.sent = 0;
+ while (handler.current < length) {
+ handler.total += (handler.files[handler.current].fileSize || handler.files[handler.current].size);
+ handler.current++;
+ }
+ handler.current = 0;
+ if (length && handler.files[0].fileSize !== -1) {
+ handler.file = handler.files[handler.current];
+
+ sendFile(handler, maxSize, width, height).onload = function (rpe, xhr) {
+ handler.onloadstart = null;
+ handler.sent += (handler.files[handler.current].fileSize || handler.files[handler.current].size);
+ if (++handler.current < length) {
+ handler.file = handler.files[handler.current];
+ sendFile(handler, maxSize, width, height).onload = arguments.callee;
+ } else if (onload) {
+ handler.onloadstart = onloadstart;
+ handler.onload = onload;
+ handler.onload(rpe, xhr);
+ }
+ };
+ } else if (length) {
+ handler.total = length * 100;
+ handler.file = handler.files[handler.current];
+ sendFile(handler, maxSize, width, height).onload = function (rpe, xhr) {
+ var callee = arguments.callee;
+ handler.onloadstart = null;
+ handler.sent += 100;
+ if (++handler.current < length) {
+ if (/\b(chrome|safari)\b/i.test(navigator.userAgent)) {
+ handler.iframe.parentNode.removeChild(handler.iframe);
+ handler.iframe = null;
+ }
+ setTimeout(function () {
+ handler.file = handler.files[handler.current];
+ sendFile(handler, maxSize, width, height).onload = callee;
+ }, 15);
+ } else if (onload) {
+ setTimeout(function () {
+ handler.iframe.parentNode.removeChild(handler.iframe);
+ handler.iframe = null;
+ handler.onloadstart = onloadstart;
+ handler.onload = onload;
+ handler.onload(rpe, xhr);
+ }, 15);
+ }
+ };
+ }
+ return handler;
+ };
+
+ var r1 = /\.([^.]+)$/; // .png
+ var r2 = /\/([^/]+)$/; // image/png
+
+ /**
+ * 校验文件类型是否合法,同时兼容旧版形式
+ * @param fileName
+ * @param fileType
+ * @returns {boolean}
+ */
+ var fileTypeValidate = function (fileName, fileType) {
+ if (!fileType) {
+ return true;
+ }
+ var mimes = fileType.split(",");
+ if (mimes[0] === fileType) {
+ mimes = (fileType + "").split(";");
+ }
+ return BI.some(mimes, function (index, mime) {
+ var matches;
+ if (matches = mime.match(r1)) {
+ return fileName.toLowerCase().indexOf(matches[1]) > -1;
+ }
+ if (matches = mime.match(r2)) {
+ return matches[1] === "*" ? true : fileName.toLowerCase().indexOf(matches[1]) > -1;
+ }
+ });
+ };
+
+ BI.File = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ var conf = BI.File.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-file display-block",
+ tagName: "input",
+ attributes: {
+ type: "file"
+ },
+ name: "",
+ url: "",
+ multiple: true,
+ accept: "", // .png,.pdf,image/jpg,image/* 等
+ maxSize: -1, // 1024 * 1024
+ maxLength: -1 // 无限制, 与multiple配合使用
+ });
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ if (o.multiple === true) {
+ this.element.attr("multiple", "multiple");
+ }
+ this.element.attr("name", o.name || this.getName());
+ this.element.attr("title", o.title || "");
+ this.element.attr("accept", o.accept);
+ },
+
+ created: function () {
+ var self = this, o = this.options;
+ // create the noswfupload.wrap Object
+ // wrap.maxSize 文件大小限制
+ // wrap.maxLength 文件个数限制
+ var _wrap = this.wrap = this._wrap(this.element[0], o.maxSize);
+ // fileType could contain whatever text but filter checks *.{extension}
+ // if present
+
+ // handlers
+
+ _wrap.onloadstart = function (rpe, xhr) {
+ // BI.Msg.toast("loadstart");
+ self.fireEvent(BI.File.EVENT_UPLOADSTART, arguments);
+ };
+
+ _wrap.onprogress = function (rpe, xhr) {
+ // BI.Msg.toast("onprogress");
+ // percent for each bar
+
+ // fileSize is -1 only if browser does not support file info access
+ // this if splits recent browsers from others
+ if (this.file.fileSize !== -1) {
+ // simulation property indicates when the progress event is fake
+ if (rpe.simulation) {
+
+ } else {
+
+ }
+ } else {
+ // if fileSIze is -1 browser is using an iframe because it does
+ // not support
+ // files sent via Ajax (XMLHttpRequest)
+ // We can still show some information
+ }
+ self.fireEvent(BI.File.EVENT_PROGRESS, {
+ file: this.file,
+ total: rpe.total,
+ loaded: rpe.loaded,
+ simulation: rpe.simulation
+ });
+ };
+
+ // generated if there is something wrong during upload
+ _wrap.onerror = function () {
+ // just inform the user something was wrong
+ self.fireEvent(BI.File.EVENT_ERROR);
+ };
+
+ // generated when every file has been sent (one or more, it does not
+ // matter)
+ _wrap.onload = function (rpe, xhr) {
+ var self_ = this;
+ // just show everything is fine ...
+ // ... and after a second reset the component
+ setTimeout(function () {
+ self_.clean(); // remove files from list
+ self_.hide(); // hide progress bars and enable input file
+ // enable again the submit button/element
+ }, 100);
+ if (200 > xhr.status || xhr.status > 399) {
+ BI.Msg.toast(BI.i18nText("BI-Upload_File_Error"), { level: "error" });
+ self.fireEvent(BI.File.EVENT_ERROR);
+ return;
+ }
+ var error = BI.some(_wrap.attach_array, function (index, attach) {
+ if (attach.errorCode) {
+ BI.Msg.toast(BI.i18nText(attach.errorMsg), { level: "error" });
+ self.fireEvent(BI.File.EVENT_ERROR, attach);
+ return true;
+ }
+ });
+ !error && self.fireEvent(BI.File.EVENT_UPLOADED);
+ };
+ _wrap.url = o.url;
+ _wrap.fileType = o.accept; // 文件类型限制
+ _wrap.attach_array = [];
+ _wrap.attach_names = [];
+ _wrap.attachNum = 0;
+ },
+
+ _events: function (wrap) {
+ var self = this, o = this.options;
+ event.add(wrap.dom.input, "change", function () {
+ event.del(wrap.dom.input, "change", arguments.callee);
+ var input = wrap.dom.input.cloneNode(true);
+ var files = F(wrap.dom.input);
+ if (o.maxLength !== -1 && o.maxLength < files.length) {
+ self.fireEvent(BI.File.EVENT_ERROR, {
+ errorType: 2
+ });
+ } else {
+ for (var i = 0; i < files.length; i++) {
+ var item = files.item(i);
+ var tempFile = item.value || item.name;
+ var value = item.fileName || (item.fileName = tempFile.split("\\").pop()),
+ ext = -1 !== value.indexOf(".") ? value.split(".").pop().toLowerCase() : "unknown",
+ size = item.fileSize || item.size;
+ var validateFileType = fileTypeValidate(value, wrap.fileType);
+ if (!validateFileType) {
+ // 文件类型不支持
+ BI.Msg.toast(BI.i18nText("BI-Upload_File_Type_Error"), { level: "error" });
+ self.fireEvent(BI.File.EVENT_ERROR, {
+ errorType: 0,
+ file: item
+ });
+ } else if (wrap.maxSize !== -1 && size && wrap.maxSize < size) {
+ // 文件大小不支持
+ BI.Msg.toast(BI.i18nText("BI-Upload_File_Size_Error"), { level: "error" });
+ self.fireEvent(BI.File.EVENT_ERROR, {
+ errorType: 1,
+ file: item
+ });
+ } else {
+ wrap.files.unshift(item);
+ // BI.Msg.toast(value);
+ }
+ }
+ }
+ wrap.files.length > 0 && self.fireEvent(BI.File.EVENT_CHANGE, {
+ files: wrap.files
+ });
+ input.value = "";
+ wrap.dom.input.parentNode.replaceChild(input, wrap.dom.input);
+ wrap.dom.input = input;
+ event.add(wrap.dom.input, "change", arguments.callee);
+ });
+ return wrap;
+ },
+
+ _wrap: function () {
+ var self = this, o = this.options;
+ // be sure input accept multiple files
+ var input = this.element[0];
+ if (o.multiple === true) {
+ this.element.attr("multiple", "multiple");
+ }
+ input.value = "";
+
+ // wrap Object
+ return this._events({
+
+ // DOM namespace
+ dom: {
+ input: input, // input file
+ disabled: false // internal use, checks input file state
+ },
+ name: input.name, // name to send for each file ($_FILES[{name}] in the server)
+ // maxSize is the maximum amount of bytes for each file
+ maxSize: o.maxSize ? o.maxSize >> 0 : -1,
+ maxLength: o.maxLength,
+ files: [], // file list
+
+ // remove every file from the noswfupload component
+ clean: function () {
+ this.files = [];
+ },
+
+ // upload one file a time (which make progress possible rather than all files in one shot)
+ // the handler is an object injected into the wrap one, could be the wrap itself or
+ // something like {onload:function(){alert("OK")},onerror:function(){alert("Error")}, etc ...}
+ upload: function (handler) {
+ if (handler) {
+ for (var key in handler) {
+ this[key] = handler[key];
+ }
+ }
+ sendFiles(this, this.maxSize);
+ return this;
+ },
+
+ // hide progress bar (total + current) and enable files selection
+ hide: function () {
+ if (this.dom.disabled) {
+ this.dom.disabled = false;
+ this.dom.input.removeAttribute("disabled");
+ }
+ },
+
+ // show progress bar and disable file selection (used during upload)
+ // total and current are pixels used to style bars
+ // totalProp and currentProp are properties to change, "height" by default
+ show: function (total, current, totalProp, currentProp) {
+ if (!this.dom.disabled) {
+ this.dom.disabled = true;
+ this.dom.input.setAttribute("disabled", "disabled");
+ }
+ }
+ });
+ },
+
+ setMaxFileLength: function(v) {
+ this.options.maxLength = v;
+ if (this.wrap) {
+ this.wrap.maxLength = v;
+ }
+ },
+
+ select: function () {
+ this.wrap && BI.Widget._renderEngine.createElement(this.wrap.dom.input).click();
+ },
+
+ upload: function (handler) {
+ this.wrap && this.wrap.upload(handler);
+ },
+
+ getValue: function () {
+ return this.wrap ? this.wrap.attach_array : [];
+ },
+
+ reset: function () {
+ if (this.wrap) {
+ this.wrap.attach_array = [];
+ this.wrap.attach_names = [];
+ this.wrap.attachNum = 0;
+ }
+ },
+
+ _setEnable: function (enable) {
+ BI.File.superclass._setEnable.apply(this, arguments);
+ if (enable === true) {
+ this.element.attr("disabled", "disabled");
+ } else {
+ this.element.removeAttr("disabled");
+ }
+ }
+ });
+ BI.File.EVENT_CHANGE = "EVENT_CHANGE";
+ BI.File.EVENT_UPLOADSTART = "EVENT_UPLOADSTART";
+ BI.File.EVENT_ERROR = "EVENT_ERROR";
+ BI.File.EVENT_PROGRESS = "EVENT_PROGRESS";
+ BI.File.EVENT_UPLOADED = "EVENT_UPLOADED";
+ BI.shortcut("bi.file", BI.File);
+})(_global.document || {});
diff --git a/src/main/resources/com/fr/fineui/base/single/input/input.js b/src/main/resources/com/fr/fineui/base/single/input/input.js
new file mode 100644
index 0000000..837d6d2
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/input/input.js
@@ -0,0 +1,315 @@
+/**
+ * guy
+ * @class BI.Input 一个button和一行数 组成的一行listitem
+ * @extends BI.Single
+ * @type {*|void|Object}
+ */
+BI.Input = BI.inherit(BI.Single, {
+ _defaultConfig: function () {
+ var conf = BI.Input.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-input display-block overflow-dot",
+ tagName: "input",
+ validationChecker: BI.emptyFn,
+ quitChecker: BI.emptyFn, // 按确定键能否退出编辑
+ allowBlank: false
+ });
+ },
+
+ render: function () {
+ var self = this;
+ var ctrlKey = false;
+ var keyCode = null;
+ var inputEventValid = false;
+ var _keydown = BI.debounce(function (keyCode) {
+ self.onKeyDown(keyCode, ctrlKey);
+ self._keydown_ = false;
+ }, 300);
+ var _clk = BI.debounce(BI.bind(this._click, this), BI.EVENT_RESPONSE_TIME, {
+ "leading": true,
+ "trailing": false
+ });
+ this._focusDebounce = BI.debounce(BI.bind(this._focus, this), BI.EVENT_RESPONSE_TIME, {
+ "leading": true,
+ "trailing": false
+ });
+ this._blurDebounce = BI.debounce(BI.bind(this._blur, this), BI.EVENT_RESPONSE_TIME, {
+ "leading": true,
+ "trailing": false
+ });
+ this.element
+ .keydown(function (e) {
+ inputEventValid = false;
+ ctrlKey = e.ctrlKey || e.metaKey; // mac的cmd支持一下
+ keyCode = e.keyCode;
+ self.fireEvent(BI.Input.EVENT_QUICK_DOWN, arguments);
+ })
+ .keyup(function (e) {
+ keyCode = null;
+ if (!(inputEventValid && e.keyCode === BI.KeyCode.ENTER)) {
+ self._keydown_ = true;
+ _keydown(e.keyCode);
+ }
+ })
+ .on("input propertychange", function (e) {
+ // 输入内容全选并直接删光,如果按键没放开就失去焦点不会触发keyup,被focusout覆盖了
+ // 其中propertychange在元素属性发生改变的时候就会触发 是为了兼容IE8
+ // 通过keyCode判断会漏掉输入法点击输入(右键粘贴暂缓)
+ var originalEvent = e.originalEvent;
+ if (BI.isNull(originalEvent.propertyName) || originalEvent.propertyName === "value") {
+ inputEventValid = true;
+ self._keydown_ = true;
+ _keydown(keyCode);
+ keyCode = null;
+ }
+ })
+ .click(function (e) {
+ e.stopPropagation();
+ _clk();
+ })
+ .mousedown(function (e) {
+ self.element.val(self.element.val());
+ })
+ .focus(function (e) { // 可以不用冒泡
+ self._focusDebounce();
+ })
+ .blur(function (e) {
+ // DEC-14919 IE11在浏览器重新获得焦点之后会先触发focusout再触发focus,要保持先获得焦点再失去焦点的顺序不变,因此采用blur
+ self._blurDebounce();
+ });
+ if (BI.isKey(this.options.value) || BI.isEmptyString(this.options.value)) {
+ this.setValue(this.options.value);
+ }
+ },
+
+ _focus: function () {
+ this.element.addClass("bi-input-focus");
+ this._checkValidationOnValueChange();
+ this._isEditing = true;
+ if (this.getValue() == "") {
+ this.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.EMPTY, this.getValue(), this);
+ this.fireEvent(BI.Input.EVENT_EMPTY);
+ }
+ this.fireEvent(BI.Input.EVENT_FOCUS);
+ },
+
+ _blur: function () {
+ var self = this;
+ if (self._keydown_ === true) {
+ BI.delay(blur, 300);
+ } else {
+ blur();
+ }
+
+ function blur () {
+ if (!self.isValid() && self.options.quitChecker.apply(self, [BI.trim(self.getValue())]) !== false) {
+ self.element.val(self._lastValidValue ? self._lastValidValue : "");
+ self._checkValidationOnValueChange();
+ self._defaultState();
+ }
+ self.element.removeClass("bi-input-focus");
+ self._isEditing = false;
+ self._start = false;
+ if (self.isValid()) {
+ var lastValidValue = self._lastValidValue;
+ self._lastValidValue = self.getValue();
+ self.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.CONFIRM, self.getValue(), self);
+ self.fireEvent(BI.Input.EVENT_CONFIRM);
+ if (self._lastValidValue !== lastValidValue) {
+ self.fireEvent(BI.Input.EVENT_CHANGE_CONFIRM);
+ }
+ }
+ self.fireEvent(BI.Input.EVENT_BLUR);
+ }
+ },
+
+ _click: function () {
+ if (this._isEditing !== true) {
+ this.selectAll();
+ this.fireEvent(BI.Input.EVENT_CLICK);
+ }
+ },
+
+ onClick: function () {
+ this._click();
+ },
+
+ onKeyDown: function (keyCode, ctrlKey) {
+ if (!this.isValid() || BI.trim(this._lastChangedValue) !== BI.trim(this.getValue())) {
+ this._checkValidationOnValueChange();
+ }
+ if (this.isValid() && BI.trim(this.getValue()) !== "") {
+ if (BI.trim(this.getValue()) !== this._lastValue && (!this._start || this._lastValue == null || this._lastValue === "")
+ || (this._pause === true && !/(\s|\u00A0)$/.test(this.getValue()))) {
+ this._start = true;
+ this._pause = false;
+ this.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.STARTEDIT, this.getValue(), this);
+ this.fireEvent(BI.Input.EVENT_START);
+ }
+ }
+ if (keyCode == BI.KeyCode.ENTER) {
+ if (this.isValid() || this.options.quitChecker.apply(this, [BI.trim(this.getValue())]) !== false) {
+ this.blur();
+ this.fireEvent(BI.Input.EVENT_ENTER);
+ } else {
+ this.fireEvent(BI.Input.EVENT_RESTRICT);
+ }
+ }
+ if (keyCode == BI.KeyCode.SPACE) {
+ this.fireEvent(BI.Input.EVENT_SPACE);
+ }
+ if (keyCode == BI.KeyCode.BACKSPACE && this._lastValue == "") {
+ this.fireEvent(BI.Input.EVENT_REMOVE);
+ }
+ if (keyCode == BI.KeyCode.BACKSPACE || keyCode == BI.KeyCode.DELETE) {
+ this.fireEvent(BI.Input.EVENT_BACKSPACE);
+ }
+ this.fireEvent(BI.Input.EVENT_KEY_DOWN, arguments);
+
+ // _valueChange中会更新_lastValue, 这边缓存用以后续STOP事件服务
+ var lastValue = this._lastValue;
+ if(BI.trim(this.getValue()) !== BI.trim(this._lastValue || "")){
+ this._valueChange();
+ }
+ if (BI.isEndWithBlank(this.getValue())) {
+ this._pause = true;
+ this.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.PAUSE, "", this);
+ this.fireEvent(BI.Input.EVENT_PAUSE);
+ this._defaultState();
+ } else if ((keyCode === BI.KeyCode.BACKSPACE || keyCode === BI.KeyCode.DELETE) &&
+ BI.trim(this.getValue()) === "" && (lastValue !== null && BI.trim(lastValue) !== "")) {
+ this.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.STOPEDIT, this.getValue(), this);
+ this.fireEvent(BI.Input.EVENT_STOP);
+ }
+ },
+
+ // 初始状态
+ _defaultState: function () {
+ if (this.getValue() == "") {
+ this.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.EMPTY, this.getValue(), this);
+ this.fireEvent(BI.Input.EVENT_EMPTY);
+ }
+ this._lastValue = this.getValue();
+ this._lastSubmitValue = null;
+ },
+
+ _valueChange: function () {
+ if (this.isValid() && BI.trim(this.getValue()) !== this._lastSubmitValue) {
+ this.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.CHANGE, this.getValue(), this);
+ this.fireEvent(BI.Input.EVENT_CHANGE);
+ this._lastSubmitValue = BI.trim(this.getValue());
+ }
+ if (this.getValue() == "") {
+ this.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.EMPTY, this.getValue(), this);
+ this.fireEvent(BI.Input.EVENT_EMPTY);
+ }
+ this._lastValue = this.getValue();
+ },
+
+ _checkValidationOnValueChange: function () {
+ var o = this.options;
+ var v = this.getValue();
+ this.setValid(
+ (o.allowBlank === true && BI.trim(v) == "") || (
+ BI.isNotEmptyString(BI.trim(v)) && o.validationChecker.apply(this, [BI.trim(v)]) !== false
+ )
+ );
+ },
+
+ focus: function () {
+ if (!this.element.is(":visible")) {
+ throw new Error("input输入框在不可见下不能focus");
+ }
+ if (!this._isEditing === true) {
+ this.element.focus();
+ this.selectAll();
+ }
+ },
+
+ blur: function () {
+ if (!this.element.is(":visible")) {
+ throw new Error("input输入框在不可见下不能blur");
+ }
+ if (this._isEditing === true) {
+ this.element.blur();
+ this._blurDebounce();
+ }
+ },
+
+ selectAll: function () {
+ if (!this.element.is(":visible")) {
+ throw new Error("input输入框在不可见下不能select");
+ }
+ this.element.select();
+ this._isEditing = true;
+ },
+
+ setValue: function (textValue) {
+ this.element.val(textValue);
+ BI.nextTick(BI.bind(function () {
+ this._checkValidationOnValueChange();
+ this._defaultState();
+ if (this.isValid()) {
+ this._lastValidValue = this._lastSubmitValue = this.getValue();
+ }
+ }, this));
+ },
+
+ getValue: function () {
+ return this.element.val() || "";
+ },
+
+ isEditing: function () {
+ return this._isEditing;
+ },
+
+ getLastValidValue: function () {
+ return this._lastValidValue;
+ },
+
+ getLastChangedValue: function () {
+ return this._lastChangedValue;
+ },
+
+ _setValid: function () {
+ BI.Input.superclass._setValid.apply(this, arguments);
+ if (this.isValid()) {
+ this._lastChangedValue = this.getValue();
+ this.element.removeClass("bi-input-error");
+ this.fireEvent(BI.Input.EVENT_VALID, BI.trim(this.getValue()), this);
+ } else {
+ if (this._lastChangedValue === this.getValue()) {
+ this._lastChangedValue = null;
+ }
+ this.element.addClass("bi-input-error");
+ this.fireEvent(BI.Input.EVENT_ERROR, BI.trim(this.getValue()), this);
+ }
+ },
+
+ _setEnable: function (b) {
+ BI.Input.superclass._setEnable.apply(this, [b]);
+ this.element[0].disabled = !b;
+ }
+});
+BI.Input.EVENT_CHANGE = "EVENT_CHANGE";
+
+BI.Input.EVENT_FOCUS = "EVENT_FOCUS";
+BI.Input.EVENT_CLICK = "EVENT_CLICK";
+BI.Input.EVENT_BLUR = "EVENT_BLUR";
+BI.Input.EVENT_KEY_DOWN = "EVENT_KEY_DOWN";
+BI.Input.EVENT_QUICK_DOWN = "EVENT_QUICK_DOWN";
+BI.Input.EVENT_SPACE = "EVENT_SPACE";
+BI.Input.EVENT_BACKSPACE = "EVENT_BACKSPACE";
+
+BI.Input.EVENT_START = "EVENT_START";
+BI.Input.EVENT_PAUSE = "EVENT_PAUSE";
+BI.Input.EVENT_STOP = "EVENT_STOP";
+BI.Input.EVENT_CHANGE_CONFIRM = "EVENT_CHANGE_CONFIRM";
+BI.Input.EVENT_CONFIRM = "EVENT_CONFIRM";
+BI.Input.EVENT_REMOVE = "EVENT_REMOVE";
+BI.Input.EVENT_EMPTY = "EVENT_EMPTY";
+BI.Input.EVENT_VALID = "EVENT_VALID";
+BI.Input.EVENT_ERROR = "EVENT_ERROR";
+BI.Input.EVENT_ENTER = "EVENT_ENTER";
+BI.Input.EVENT_RESTRICT = "EVENT_RESTRICT";
+BI.shortcut("bi.input", BI.Input);
diff --git a/src/main/resources/com/fr/fineui/base/single/input/radio/radio.image.js b/src/main/resources/com/fr/fineui/base/single/input/radio/radio.image.js
new file mode 100644
index 0000000..e362592
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/input/radio/radio.image.js
@@ -0,0 +1,29 @@
+/**
+ * guy
+ * @extends BI.Single
+ * @type {*|void|Object}
+ */
+BI.ImageRadio = BI.inherit(BI.IconButton, {
+ _defaultConfig: function () {
+ var conf = BI.ImageRadio.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-radio radio-icon",
+ selected: false,
+ handler: BI.emptyFn,
+ width: 16,
+ height: 16,
+ iconWidth: 16,
+ iconHeight: 16
+ });
+ },
+
+ doClick: function () {
+ BI.ImageRadio.superclass.doClick.apply(this, arguments);
+ if(this.isValid()) {
+ this.fireEvent(BI.ImageRadio.EVENT_CHANGE);
+ }
+ }
+});
+BI.ImageRadio.EVENT_CHANGE = BI.IconButton.EVENT_CHANGE;
+
+BI.shortcut("bi.image_radio", BI.ImageRadio);
diff --git a/src/main/resources/com/fr/fineui/base/single/input/radio/radio.js b/src/main/resources/com/fr/fineui/base/single/input/radio/radio.js
new file mode 100644
index 0000000..61096af
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/input/radio/radio.js
@@ -0,0 +1,62 @@
+/**
+ * guy
+ * @extends BI.Single
+ * @type {*|void|Object}
+ */
+BI.Radio = BI.inherit(BI.BasicButton, {
+
+ props: {
+ baseCls: "bi-radio",
+ selected: false,
+ handler: BI.emptyFn,
+ width: 14,
+ height: 14,
+ iconWidth: 14,
+ iconHeight: 14
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ return {
+ type: "bi.center_adapt",
+ element: this.element,
+ items: [{
+ type: "bi.layout",
+ cls: "radio-content",
+ ref: function (_ref) {
+ self.radio = _ref;
+ },
+ width: o.iconWidth,
+ height: o.iconHeight
+ }]
+ };
+ },
+
+ _setEnable: function (enable) {
+ BI.Radio.superclass._setEnable.apply(this, arguments);
+ if (enable === true) {
+ this.radio.element.removeClass("base-disabled disabled");
+ } else {
+ this.radio.element.addClass("base-disabled disabled");
+ }
+ },
+
+ doClick: function () {
+ BI.Radio.superclass.doClick.apply(this, arguments);
+ if(this.isValid()) {
+ this.fireEvent(BI.Radio.EVENT_CHANGE);
+ }
+ },
+
+ setSelected: function (b) {
+ BI.Radio.superclass.setSelected.apply(this, arguments);
+ if (b) {
+ this.radio.element.addClass("bi-high-light-background");
+ } else {
+ this.radio.element.removeClass("bi-high-light-background");
+ }
+ }
+});
+BI.Radio.EVENT_CHANGE = "EVENT_CHANGE";
+
+BI.shortcut("bi.radio", BI.Radio);
diff --git a/src/main/resources/com/fr/fineui/base/single/label/abstract.label.js b/src/main/resources/com/fr/fineui/base/single/label/abstract.label.js
new file mode 100644
index 0000000..8bbfbee
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/label/abstract.label.js
@@ -0,0 +1,377 @@
+/**
+ * Created by dailer on 2019/6/19.
+ */
+!(function () {
+ BI.AbstractLabel = BI.inherit(BI.Single, {
+
+ _defaultConfig: function (props) {
+ var conf = BI.AbstractLabel.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ textAlign: "center",
+ whiteSpace: "nowrap", // normal or nowrap
+ textWidth: null,
+ textHeight: null,
+ hgap: 0,
+ vgap: 0,
+ lgap: 0,
+ rgap: 0,
+ tgap: 0,
+ bgap: 0,
+ highLight: false,
+ handler: null
+ });
+ },
+
+ _createJson: function () {
+ var o = this.options;
+ return {
+ type: "bi.text",
+ textAlign: o.textAlign,
+ whiteSpace: o.whiteSpace,
+ lineHeight: o.textHeight,
+ maxWidth: "100%",
+ text: o.text,
+ value: o.value,
+ py: o.py,
+ keyword: o.keyword,
+ highLight: o.highLight,
+ handler: o.handler
+ };
+ },
+
+ render: function () {
+ if (this.options.textAlign === "center") {
+ this._createCenterEl();
+ } else {
+ this._createNotCenterEl();
+ }
+ },
+
+ _createCenterEl: function () {
+ var o = this.options;
+ var json = this._createJson();
+ json.textAlign = "left";
+ if (BI.isNumber(o.width) && o.width > 0) {
+ if (BI.isNumber(o.textWidth) && o.textWidth > 0) {
+ json.maxWidth = o.textWidth;
+ if (BI.isNumber(o.height) && o.height > 0) { // 1.1
+ BI.createWidget({
+ type: "bi.center_adapt",
+ height: o.height,
+ columnSize: ["auto"], // important! 让文字在flex布局下shrink为1
+ scrollable: o.whiteSpace === "normal",
+ element: this,
+ items: [
+ {
+ el: (this.text = BI.createWidget(json))
+ }
+ ]
+ });
+ return;
+ }
+ BI.createWidget({ // 1.2
+ type: "bi.center_adapt",
+ columnSize: ["auto"], // important! 让文字在flex布局下shrink为1
+ scrollable: o.whiteSpace === "normal",
+ element: this,
+ items: [
+ {
+ el: (this.text = BI.createWidget(json))
+ }
+ ]
+ });
+ return;
+ }
+ if (o.whiteSpace === "normal") { // 1.3
+ BI.extend(json, {
+ hgap: o.hgap,
+ vgap: o.vgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap
+ });
+ this.text = BI.createWidget(json);
+ BI.createWidget({
+ type: "bi.center_adapt",
+ columnSize: ["auto"], // important! 让文字在flex布局下shrink为1
+ scrollable: o.whiteSpace === "normal",
+ element: this,
+ items: [this.text]
+ });
+ return;
+ }
+ if (BI.isNumber(o.height) && o.height > 0) { // 1.4
+ this.element.css({
+ "line-height": o.height / BI.pixRatio + BI.pixUnit
+ });
+ json.textAlign = o.textAlign;
+ this.text = BI.createWidget(BI.extend(json, {
+ element: this,
+ hgap: o.hgap,
+ vgap: o.vgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap
+ }));
+ return;
+ }
+ BI.extend(json, { // 1.5
+ hgap: o.hgap,
+ vgap: o.vgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap,
+ maxWidth: "100%"
+ });
+ this.text = BI.createWidget(json);
+ BI.createWidget({
+ type: "bi.center_adapt",
+ columnSize: ["auto"], // important! 让文字在flex布局下shrink为1
+ scrollable: o.whiteSpace === "normal",
+ element: this,
+ items: [this.text]
+ });
+ return;
+ }
+ if (BI.isNumber(o.textWidth) && o.textWidth > 0) { // 1.6
+ json.maxWidth = o.textWidth;
+ BI.createWidget({
+ type: "bi.center_adapt",
+ columnSize: ["auto"], // important! 让文字在flex布局下shrink为1
+ scrollable: o.whiteSpace === "normal",
+ element: this,
+ items: [
+ {
+ el: (this.text = BI.createWidget(json))
+ }
+ ]
+ });
+ return;
+ }
+ if (o.whiteSpace === "normal") { // 1.7
+ BI.extend(json, {
+ hgap: o.hgap,
+ vgap: o.vgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap
+ });
+ this.text = BI.createWidget(json);
+ BI.createWidget({
+ type: "bi.center_adapt",
+ columnSize: ["auto"], // important! 让文字在flex布局下shrink为1
+ scrollable: true,
+ element: this,
+ items: [this.text]
+ });
+ return;
+ }
+ if (BI.isNumber(o.height) && o.height > 0) { // 1.8
+ this.element.css({
+ "line-height": o.height / BI.pixRatio + BI.pixUnit
+ });
+ json.textAlign = o.textAlign;
+ this.text = BI.createWidget(BI.extend(json, {
+ element: this,
+ hgap: o.hgap,
+ vgap: o.vgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap
+ }));
+ return;
+ }
+ this.text = BI.createWidget(BI.extend(json, {
+ hgap: o.hgap,
+ vgap: o.vgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap
+ }));
+ BI.createWidget({
+ type: "bi.center_adapt",
+ columnSize: ["auto"], // important! 让文字在flex布局下shrink为1
+ element: this,
+ items: [this.text]
+ });
+ },
+
+ _createNotCenterEl: function () {
+ var o = this.options;
+ var adaptLayout = "bi.vertical_adapt";
+ var json = this._createJson();
+ if (BI.isNumber(o.width) && o.width > 0) {
+ if (BI.isNumber(o.textWidth) && o.textWidth > 0) {
+ json.maxWidth = o.textWidth;
+ if (BI.isNumber(o.height) && o.height > 0) { // 2.1
+ BI.createWidget({
+ type: adaptLayout,
+ horizontalAlign: o.textAlign,
+ columnSize: ["auto"], // important! 让文字在flex布局下shrink为1
+ height: o.height,
+ scrollable: o.whiteSpace === "normal",
+ element: this,
+ items: [
+ {
+ el: (this.text = BI.createWidget(json))
+ }
+ ]
+ });
+ return;
+ }
+ BI.createWidget({ // 2.2
+ type: adaptLayout,
+ horizontalAlign: o.textAlign,
+ columnSize: ["auto"], // important! 让文字在flex布局下shrink为1
+ scrollable: o.whiteSpace === "normal",
+ hgap: o.hgap,
+ vgap: o.vgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap,
+ element: this,
+ items: [
+ {
+ el: (this.text = BI.createWidget(json))
+ }
+ ]
+ });
+ return;
+ }
+ if (BI.isNumber(o.height) && o.height > 0) { // 2.3
+ if (o.whiteSpace !== "normal") {
+ this.element.css({
+ "line-height": (o.height - (o.vgap * 2)) / BI.pixRatio + BI.pixUnit
+ });
+ }
+ this.text = BI.createWidget(BI.extend(json, {
+ element: this,
+ hgap: o.hgap,
+ vgap: o.vgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap
+ }));
+ return;
+ }
+ json.maxWidth = o.width - 2 * o.hgap - o.lgap - o.rgap;
+ BI.createWidget({ // 2.4
+ type: adaptLayout,
+ horizontalAlign: o.textAlign,
+ columnSize: ["auto"], // important! 让文字在flex布局下shrink为1
+ scrollable: o.whiteSpace === "normal",
+ hgap: o.hgap,
+ vgap: o.vgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap,
+ element: this,
+ items: [{
+ el: (this.text = BI.createWidget(json))
+ }]
+ });
+ return;
+ }
+ if (BI.isNumber(o.textWidth) && o.textWidth > 0) {
+ json.maxWidth = o.textWidth;
+ BI.createWidget({ // 2.5
+ type: adaptLayout,
+ horizontalAlign: o.textAlign,
+ columnSize: ["auto"], // important! 让文字在flex布局下shrink为1
+ scrollable: o.whiteSpace === "normal",
+ hgap: o.hgap,
+ vgap: o.vgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap,
+ element: this,
+ items: [
+ {
+ el: (this.text = BI.createWidget(json))
+ }
+ ]
+ });
+ return;
+ }
+ if (BI.isNumber(o.height) && o.height > 0) {
+ if (o.whiteSpace !== "normal") {
+ this.element.css({
+ "line-height": (o.height - (o.vgap * 2)) / BI.pixRatio + BI.pixUnit
+ });
+ }
+ this.text = BI.createWidget(BI.extend(json, { // 2.6
+ element: this,
+ hgap: o.hgap,
+ vgap: o.vgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap
+ }));
+ return;
+ }
+ this.text = BI.createWidget(BI.extend(json, {
+ hgap: o.hgap,
+ vgap: o.vgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap
+ }));
+ BI.createWidget({
+ type: adaptLayout,
+ horizontalAlign: o.textAlign,
+ columnSize: ["auto"], // important! 让文字在flex布局下shrink为1
+ element: this,
+ scrollable: o.whiteSpace === "normal",
+ items: [this.text]
+ });
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doHighLight: function () {
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ },
+
+ setText: function (v) {
+ this.options.text = v;
+ this.text.setText(v);
+ },
+
+ getText: function () {
+ return this.options.text;
+ },
+
+ setStyle: function (css) {
+ this.text.setStyle(css);
+ },
+
+ setValue: function (v) {
+ BI.AbstractLabel.superclass.setValue.apply(this, arguments);
+ if (!this.isReadOnly()) {
+ this.text.setValue(v);
+ }
+ }
+ });
+}());
diff --git a/src/main/resources/com/fr/fineui/base/single/label/html.label.js b/src/main/resources/com/fr/fineui/base/single/label/html.label.js
new file mode 100644
index 0000000..2ebe781
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/label/html.label.js
@@ -0,0 +1,25 @@
+/**
+ * Created by GUY on 2015/6/26.
+ */
+
+BI.HtmlLabel = BI.inherit(BI.AbstractLabel, {
+
+ props: {
+ baseCls: "bi-html-label"
+ },
+
+ _createJson: function () {
+ var o = this.options;
+ return {
+ type: "bi.html",
+ textAlign: o.textAlign,
+ whiteSpace: o.whiteSpace,
+ lineHeight: o.textHeight,
+ text: o.text,
+ value: o.value,
+ handler: o.handler
+ };
+ }
+});
+
+BI.shortcut("bi.html_label", BI.HtmlLabel);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/base/single/label/icon.label.js b/src/main/resources/com/fr/fineui/base/single/label/icon.label.js
new file mode 100644
index 0000000..6575564
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/label/icon.label.js
@@ -0,0 +1,42 @@
+/**
+ * @class BI.IconButton
+ * @extends BI.BasicButton
+ * 图标标签
+ */
+BI.IconLabel = BI.inherit(BI.Single, {
+
+ props: {
+ baseCls: "bi-icon-label horizon-center",
+ iconWidth: null,
+ iconHeight: null,
+ lineHeight: null,
+ },
+
+ render: function () {
+ var o = this.options;
+ this.element.css({
+ textAlign: "center"
+ });
+ this.icon = BI.createWidget({
+ type: "bi.icon",
+ width: o.iconWidth,
+ height: o.iconHeight
+ });
+ if (BI.isNumber(o.height) && o.height > 0 && BI.isNull(o.iconWidth) && BI.isNull(o.iconHeight)) {
+ this.element.css("lineHeight", (o.lineHeight || o.height) / BI.pixRatio + BI.pixUnit);
+ BI.createWidget({
+ type: "bi.default",
+ element: this,
+ items: [this.icon]
+ });
+ } else {
+ this.element.css("lineHeight", "1");
+ BI.createWidget({
+ element: this,
+ type: "bi.center_adapt",
+ items: [this.icon]
+ });
+ }
+ }
+});
+BI.shortcut("bi.icon_label", BI.IconLabel);
diff --git a/src/main/resources/com/fr/fineui/base/single/label/label.js b/src/main/resources/com/fr/fineui/base/single/label/label.js
new file mode 100644
index 0000000..3712908
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/label/label.js
@@ -0,0 +1,22 @@
+/**
+ * Created by GUY on 2015/6/26.
+ */
+
+BI.Label = BI.inherit(BI.AbstractLabel, {
+
+ props: {
+ baseCls: "bi-label",
+ py: "",
+ keyword: ""
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ }
+});
+
+BI.shortcut("bi.label", BI.Label);
diff --git a/src/main/resources/com/fr/fineui/base/single/link/__test__/link.test.js b/src/main/resources/com/fr/fineui/base/single/link/__test__/link.test.js
new file mode 100644
index 0000000..e425cb4
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/link/__test__/link.test.js
@@ -0,0 +1,19 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/3/17
+ */
+
+describe("LinkTest", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("link", function () {
+ var a = BI.Test.createWidget({
+ type: "bi.link"
+ });
+ expect(a.element.is('a')).to.equal(true);
+ a.destroy();
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/base/single/link/link.js b/src/main/resources/com/fr/fineui/base/single/link/link.js
new file mode 100644
index 0000000..6808935
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/link/link.js
@@ -0,0 +1,34 @@
+/**
+ * guy a元素
+ * @class BI.Link
+ * @extends BI.Text
+ */
+BI.Link = BI.inherit(BI.Label, {
+ _defaultConfig: function () {
+ var conf = BI.Link.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-link display-block",
+ tagName: "a",
+ href: "",
+ target: "_blank"
+ });
+ },
+
+ _createJson: function () {
+ var o = this.options;
+ return {
+ type: "bi.a",
+ textAlign: o.textAlign,
+ whiteSpace: o.whiteSpace,
+ lineHeight: o.textHeight,
+ text: o.text,
+ keyword: o.keyword,
+ value: o.value,
+ py: o.py,
+ href: o.href,
+ target: o.target
+ };
+ }
+});
+
+BI.shortcut("bi.link", BI.Link);
diff --git a/src/main/resources/com/fr/fineui/base/single/text.pure.js b/src/main/resources/com/fr/fineui/base/single/text.pure.js
new file mode 100644
index 0000000..eb7f6e3
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/text.pure.js
@@ -0,0 +1,43 @@
+/**
+ * 没有html标签的纯文本
+ */
+!(function () {
+ BI.PureText = BI.inherit(BI.Widget, {
+
+ props: {
+ tagName: null
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ var text = this._getShowText();
+ if (BI.isKey(text)) {
+ this.setText(text);
+ } else if (BI.isKey(o.value)) {
+ this.setText(o.value);
+ }
+ },
+
+ _getShowText: function () {
+ var o = this.options;
+ var text = BI.isFunction(o.text) ? o.text() : o.text;
+ text = BI.isKey(text) ? text : o.value;
+ if (!BI.isKey(text)) {
+ return "";
+ }
+ return BI.Text.formatText(text + "");
+ },
+
+ setValue: function (value) {
+ this.options.value = value;
+ this.setText(value);
+ },
+
+ setText: function (text) {
+ this.options.text = BI.isNotNull(text) ? text : "";
+ this.element.__textKeywordMarked__(this._getShowText());
+ }
+ });
+ BI.shortcut("bi.pure_text", BI.PureText);
+}());
+
diff --git a/src/main/resources/com/fr/fineui/base/single/tip/0.tip.js b/src/main/resources/com/fr/fineui/base/single/tip/0.tip.js
new file mode 100644
index 0000000..c792b91
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/tip/0.tip.js
@@ -0,0 +1,22 @@
+/**
+ * guy
+ * tip提示
+ * zIndex在10亿级别
+ * @class BI.Tip
+ * @extends BI.Single
+ * @abstract
+ */
+BI.Tip = BI.inherit(BI.Single, {
+ _defaultConfig: function () {
+ var conf = BI.Tip.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ _baseCls: (conf._baseCls || "") + " bi-tip",
+ zIndex: BI.zIndex_tip
+ });
+ },
+
+ _init: function () {
+ BI.Tip.superclass._init.apply(this, arguments);
+ this.element.css({zIndex: this.options.zIndex});
+ }
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/base/single/tip/tip.toast.js b/src/main/resources/com/fr/fineui/base/single/tip/tip.toast.js
new file mode 100644
index 0000000..afd444f
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/tip/tip.toast.js
@@ -0,0 +1,107 @@
+/**
+ * toast提示
+ *
+ * Created by GUY on 2015/9/7.
+ * @class BI.Toast
+ * @extends BI.Tip
+ */
+BI.Toast = BI.inherit(BI.Tip, {
+ _const: {
+ minWidth: 200,
+ hgap: 10
+ },
+
+ _defaultConfig: function () {
+ return BI.extend(BI.Toast.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "bi-toast",
+ text: "",
+ level: "success" // success或warning
+ });
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ this.element.css({
+ minWidth: this._const.minWidth / BI.pixRatio + BI.pixUnit
+ });
+ this.element.addClass("toast-" + o.level);
+ var fn = function (e) {
+ e.stopPropagation();
+ e.stopEvent();
+ return false;
+ };
+ this.element.bind({
+ click: fn,
+ mousedown: fn,
+ mouseup: fn,
+ mouseover: fn,
+ mouseenter: fn,
+ mouseleave: fn,
+ mousemove: fn
+ });
+ var cls = "close-font";
+ switch (o.level) {
+ case "success":
+ cls = "toast-success-font";
+ break;
+ case "error":
+ cls = "toast-error-font";
+ break;
+ case "warning":
+ cls = "toast-warning-font";
+ break;
+ case "normal":
+ default:
+ cls = "toast-message-font";
+ break;
+ }
+
+ var items = [{
+ type: "bi.icon_label",
+ cls: cls + " toast-icon",
+ width: 36
+ }, {
+ el: {
+ type: "bi.label",
+ whiteSpace: "normal",
+ text: o.text,
+ textHeight: 16,
+ textAlign: "left"
+ },
+ rgap: o.autoClose ? this._const.hgap : 0
+ }];
+
+ var columnSize = [36, "fill"];
+
+ if (o.autoClose === false) {
+ items.push({
+ type: "bi.icon_button",
+ cls: "close-font toast-icon",
+ handler: function () {
+ self.destroy();
+ },
+ width: 36
+ });
+ columnSize.push(36);
+ }
+
+ this.text = BI.createWidget({
+ type: "bi.horizontal",
+ horizontalAlign: BI.HorizontalAlign.Stretch,
+ element: this,
+ items: items,
+ vgap: 7,
+ columnSize: columnSize
+ });
+ },
+
+ setText: function (text) {
+ this.text.setText(text);
+ },
+
+ beforeDestroy: function () {
+ this.fireEvent(BI.Toast.EVENT_DESTORY);
+ }
+});
+BI.Toast.EVENT_DESTORY = "EVENT_DESTORY";
+BI.shortcut("bi.toast", BI.Toast);
diff --git a/src/main/resources/com/fr/fineui/base/single/tip/tip.tooltip.js b/src/main/resources/com/fr/fineui/base/single/tip/tip.tooltip.js
new file mode 100644
index 0000000..ca206a8
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/tip/tip.tooltip.js
@@ -0,0 +1,84 @@
+/**
+ * title提示
+ *
+ * Created by GUY on 2015/9/7.
+ * @class BI.Tooltip
+ * @extends BI.Tip
+ */
+BI.Tooltip = BI.inherit(BI.Tip, {
+ _const: {
+ hgap: 5,
+ vgap: 3
+ },
+
+ _defaultConfig: function () {
+ return BI.extend(BI.Tooltip.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "bi-tooltip",
+ text: "",
+ level: "success", // success或warning
+ stopEvent: false,
+ stopPropagation: false
+ });
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ this.element.addClass("tooltip-" + o.level);
+ var fn = function (e) {
+ o.stopPropagation && e.stopPropagation();
+ o.stopEvent && e.stopEvent();
+ };
+ this.element.bind({
+ click: fn,
+ mousedown: fn,
+ mouseup: fn,
+ mouseover: fn,
+ mouseenter: fn,
+ mouseleave: fn,
+ mousemove: fn
+ });
+
+ var texts = (o.text + "").split("\n");
+ if (texts.length > 1) {
+ BI.createWidget({
+ type: "bi.vertical",
+ element: this,
+ hgap: this._const.hgap,
+ items: BI.map(texts, function (i, text) {
+ return {
+ type: "bi.label",
+ textAlign: "left",
+ whiteSpace: "normal",
+ text: text,
+ textHeight: 18
+ };
+ })
+ });
+ } else {
+ this.text = BI.createWidget({
+ type: "bi.label",
+ element: this,
+ textAlign: "left",
+ whiteSpace: "normal",
+ text: o.text,
+ textHeight: 18,
+ hgap: this._const.hgap
+ });
+ }
+ },
+
+ setWidth: function (width) {
+ this.element.width(width - 2 * this._const.hgap);
+ },
+
+ setText: function (text) {
+ this.text && this.text.setText(text);
+ },
+
+ setLevel: function (level) {
+ this.element.removeClass("tooltip-success").removeClass("tooltip-warning");
+ this.element.addClass("tooltip-" + level);
+ }
+});
+
+BI.shortcut("bi.tooltip", BI.Tooltip);
diff --git a/src/main/resources/com/fr/fineui/base/single/trigger/trigger.js b/src/main/resources/com/fr/fineui/base/single/trigger/trigger.js
new file mode 100644
index 0000000..9e97d22
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/single/trigger/trigger.js
@@ -0,0 +1,23 @@
+/**
+ * 下拉
+ * @class BI.Trigger
+ * @extends BI.Single
+ * @abstract
+ */
+BI.Trigger = BI.inherit(BI.Single, {
+ _defaultConfig: function () {
+ var conf = BI.Trigger.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ _baseCls: (conf._baseCls || "") + " bi-trigger cursor-pointer",
+ height: 24
+ });
+ },
+
+ setKey: function () {
+
+ },
+
+ getKey: function () {
+
+ },
+});
diff --git a/src/main/resources/com/fr/fineui/base/tree/customtree.js b/src/main/resources/com/fr/fineui/base/tree/customtree.js
new file mode 100644
index 0000000..a72edba
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/base/tree/customtree.js
@@ -0,0 +1,148 @@
+/**
+ *
+ * 自定义树
+ *
+ * Created by GUY on 2015/9/7.
+ * @class BI.CustomTree
+ * @extends BI.Single
+ */
+BI.CustomTree = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ return BI.extend(BI.CustomTree.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-custom-tree",
+ expander: {
+ el: {},
+ popup: {
+ type: "bi.custom_tree"
+ }
+ },
+
+ items: [],
+ itemsCreator: BI.emptyFn,
+
+ el: {
+ type: "bi.button_tree",
+ chooseType: 0,
+ layouts: [{
+ type: "bi.vertical"
+ }]
+ }
+ });
+ },
+
+ _init: function () {
+ BI.CustomTree.superclass._init.apply(this, arguments);
+ this.initTree(this.options.items);
+ },
+
+ _formatItems: function (nodes) {
+ var self = this, o = this.options;
+ nodes = BI.Tree.transformToTreeFormat(nodes);
+
+ var items = [];
+ BI.each(nodes, function (i, node) {
+ if (BI.isNotEmptyArray(node.children) || node.isParent === true) {
+ var item = BI.extend({
+ type: "bi.expander",
+ el: {
+ value: node.value
+ },
+ popup: {type: "bi.custom_tree"}
+ }, BI.deepClone(o.expander), {
+ id: node.id,
+ pId: node.pId
+ });
+ var el = BI.stripEL(node);
+ if (!BI.isWidget(el)) {
+ el = BI.clone(el);
+ delete el.children;
+ BI.extend(item.el, el);
+ } else {
+ item.el = el;
+ }
+ item.popup.expander = BI.deepClone(o.expander);
+ item.items = item.popup.items = node.children;
+ item.itemsCreator = item.popup.itemsCreator = function (op) {
+ if (BI.isNotNull(op.node)) {// 从子节点传过来的itemsCreator直接向上传递
+ return o.itemsCreator.apply(self, arguments);
+ }
+ var args = Array.prototype.slice.call(arguments, 0);
+ args[0].node = node;
+ return o.itemsCreator.apply(self, args);
+ };
+ BI.isNull(item.popup.el) && (item.popup.el = BI.deepClone(o.el));
+ items.push(item);
+ } else {
+ items.push(node);
+ }
+ });
+ return items;
+ },
+
+ // 构造树结构,
+ initTree: function (nodes) {
+ var self = this, o = this.options;
+ this.tree = BI.createWidget(o.el, {
+ element: this,
+ items: this._formatItems(nodes),
+ itemsCreator: function (op, callback) {
+ o.itemsCreator.apply(this, [op, function (items) {
+ var args = Array.prototype.slice.call(arguments, 0);
+ args[0] = self._formatItems(items);
+ callback.apply(null, args);
+ }]);
+ },
+ value: o.value
+ });
+ this.tree.on(BI.Controller.EVENT_CHANGE, function (type, val, obj) {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ if (type === BI.Events.CLICK) {
+ self.fireEvent(BI.CustomTree.EVENT_CHANGE, val, obj);
+ }
+ });
+ },
+
+ // 生成树方法
+ stroke: function (nodes) {
+ this.populate.apply(this, arguments);
+ },
+
+ populate: function (nodes) {
+ var args = Array.prototype.slice.call(arguments, 0);
+ if (arguments.length > 0) {
+ args[0] = this._formatItems(nodes);
+ }
+ this.tree.populate.apply(this.tree, args);
+ },
+
+ setValue: function (v) {
+ this.tree && this.tree.setValue(v);
+ },
+
+ getValue: function () {
+ return this.tree ? this.tree.getValue() : [];
+ },
+
+ getAllButtons: function () {
+ return this.tree ? this.tree.getAllButtons() : [];
+ },
+
+ getAllLeaves: function () {
+ return this.tree ? this.tree.getAllLeaves() : [];
+ },
+
+ getNodeById: function (id) {
+ return this.tree && this.tree.getNodeById(id);
+ },
+
+ getNodeByValue: function (id) {
+ return this.tree && this.tree.getNodeByValue(id);
+ },
+
+ empty: function () {
+ this.tree.empty();
+ }
+});
+BI.CustomTree.EVENT_CHANGE = "EVENT_CHANGE";
+
+BI.shortcut("bi.custom_tree", BI.CustomTree);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/button/icon/icon.change.js b/src/main/resources/com/fr/fineui/case/button/icon/icon.change.js
new file mode 100644
index 0000000..8790008
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/icon/icon.change.js
@@ -0,0 +1,83 @@
+/**
+ * 可以改变图标的button
+ *
+ * Created by GUY on 2016/2/2.
+ *
+ * @class BI.IconChangeButton
+ * @extends BI.Single
+ */
+BI.IconChangeButton = BI.inherit(BI.Single, {
+ _defaultConfig: function () {
+ var conf = BI.IconChangeButton.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: "bi-icon-change-button",
+ iconCls: "",
+ iconWidth: null,
+ iconHeight: null,
+
+ stopEvent: false,
+ stopPropagation: false,
+ selected: false,
+ once: false, // 点击一次选中有效,再点无效
+ forceSelected: false, // 点击即选中, 选中了就不会被取消
+ forceNotSelected: false, // 无论怎么点击都不会被选中
+ disableSelected: false, // 使能选中
+
+ shadow: false,
+ isShadowShowingOnSelected: false, // 选中状态下是否显示阴影
+ trigger: null,
+ handler: BI.emptyFn
+ });
+ },
+
+ _init: function () {
+ BI.IconChangeButton.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.button = BI.createWidget({
+ type: "bi.icon_button",
+ element: this,
+ cls: o.iconCls,
+ height: o.height,
+ iconWidth: o.iconWidth,
+ iconHeight: o.iconHeight,
+
+ stopEvent: o.stopEvent,
+ stopPropagation: o.stopPropagation,
+ selected: o.selected,
+ once: o.once,
+ forceSelected: o.forceSelected,
+ forceNotSelected: o.forceNotSelected,
+ disableSelected: o.disableSelected,
+
+ shadow: o.shadow,
+ isShadowShowingOnSelected: o.isShadowShowingOnSelected,
+ trigger: o.trigger,
+ handler: o.handler
+ });
+
+ this.button.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ this.button.on(BI.IconButton.EVENT_CHANGE, function () {
+ self.fireEvent(BI.IconChangeButton.EVENT_CHANGE, arguments);
+ });
+ },
+
+ isSelected: function () {
+ return this.button.isSelected();
+ },
+
+ setSelected: function (b) {
+ this.button.setSelected(b);
+ },
+
+ setIcon: function (cls) {
+ var o = this.options;
+ if (o.iconCls !== cls) {
+ this.element.removeClass(o.iconCls).addClass(cls);
+ o.iconCls = cls;
+ }
+ }
+});
+BI.IconChangeButton.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.icon_change_button", BI.IconChangeButton);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/button/icon/icon.trigger.js b/src/main/resources/com/fr/fineui/case/button/icon/icon.trigger.js
new file mode 100644
index 0000000..daa76b1
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/icon/icon.trigger.js
@@ -0,0 +1,19 @@
+/**
+ * 统一的trigger图标按钮
+ *
+ * Created by GUY on 2015/9/16.
+ * @class BI.TriggerIconButton
+ * @extends BI.IconButton
+ */
+BI.TriggerIconButton = BI.inherit(BI.IconButton, {
+
+ _defaultConfig: function () {
+ var conf = BI.TriggerIconButton.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-trigger-icon-button overflow-hidden",
+ extraCls: "pull-down-font"
+ });
+ }
+});
+BI.TriggerIconButton.EVENT_CHANGE = BI.IconButton.EVENT_CHANGE;
+BI.shortcut("bi.trigger_icon_button", BI.TriggerIconButton);
diff --git a/src/main/resources/com/fr/fineui/case/button/icon/iconhalf/icon.half.image.js b/src/main/resources/com/fr/fineui/case/button/icon/iconhalf/icon.half.image.js
new file mode 100644
index 0000000..a9e9d00
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/icon/iconhalf/icon.half.image.js
@@ -0,0 +1,21 @@
+/**
+ * guy
+ * @extends BI.Single
+ * @type {*|void|Object}
+ */
+BI.HalfIconButton = BI.inherit(BI.IconButton, {
+ _defaultConfig: function () {
+ var conf = BI.HalfIconButton.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ extraCls: "bi-half-icon-button check-half-select-icon",
+ height: 16,
+ width: 16,
+ iconWidth: 16,
+ iconHeight: 16,
+ selected: false
+ });
+ }
+});
+BI.HalfIconButton.EVENT_CHANGE = BI.IconButton.EVENT_CHANGE;
+
+BI.shortcut("bi.half_icon_button", BI.HalfIconButton);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/button/icon/iconhalf/icon.half.js b/src/main/resources/com/fr/fineui/case/button/icon/iconhalf/icon.half.js
new file mode 100644
index 0000000..6a46b16
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/icon/iconhalf/icon.half.js
@@ -0,0 +1,26 @@
+/**
+ * guy
+ * @extends BI.Single
+ * @type {*|void|Object}
+ */
+BI.HalfButton = BI.inherit(BI.BasicButton, {
+ _defaultConfig: function () {
+ var conf = BI.HalfIconButton.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ extraCls: "bi-half-button bi-high-light-border",
+ height: 14,
+ width: 14,
+ selected: false
+ });
+ },
+
+ doClick: function () {
+ BI.HalfButton.superclass.doClick.apply(this, arguments);
+ if(this.isValid()) {
+ this.fireEvent(BI.HalfButton.EVENT_CHANGE);
+ }
+ }
+});
+BI.HalfButton.EVENT_CHANGE = "EVENT_CHANGE";
+
+BI.shortcut("bi.half_button", BI.HalfButton);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/button/item.multiselect.js b/src/main/resources/com/fr/fineui/case/button/item.multiselect.js
new file mode 100644
index 0000000..9653634
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/item.multiselect.js
@@ -0,0 +1,80 @@
+/**
+ * guy
+ * 复选框item
+ * @type {*|void|Object}
+ */
+BI.MultiSelectItem = BI.inherit(BI.BasicButton, {
+ _defaultConfig: function () {
+ return BI.extend(BI.MultiSelectItem.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "bi-multi-select-item",
+ height: 24,
+ logic: {
+ dynamic: false
+ },
+ iconWrapperWidth: 26,
+ textHgap: 0,
+ textLgap: 0,
+ textRgap: 0
+ });
+ },
+ _init: function () {
+ BI.MultiSelectItem.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.checkbox = BI.createWidget({
+ type: "bi.checkbox"
+ });
+ this.text = BI.createWidget({
+ type: "bi.label",
+ cls: "list-item-text",
+ textAlign: "left",
+ whiteSpace: "nowrap",
+ textHeight: o.height,
+ height: o.height,
+ hgap: o.hgap,
+ rgap: o.rgap,
+ lgap: o.textLgap,
+ text: o.text,
+ keyword: o.keyword,
+ value: o.value,
+ py: o.py
+ });
+ this.checkbox.on(BI.Controller.EVENT_CHANGE, function (type) {
+ if (type === BI.Events.CLICK) {
+ self.setSelected(self.isSelected());
+ }
+ });
+
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic("horizontal", BI.extend(o.logic, {
+ items: BI.LogicFactory.createLogicItemsByDirection("left", {
+ type: "bi.center_adapt",
+ items: [this.checkbox],
+ width: o.iconWrapperWidth
+ }, this.text)
+ }))));
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doClick: function () {
+ BI.MultiSelectItem.superclass.doClick.apply(this, arguments);
+ this.checkbox.setSelected(this.isSelected());
+ if (this.isValid()) {
+ this.fireEvent(BI.MultiSelectItem.EVENT_CHANGE, this.getValue(), this);
+ }
+ },
+
+ setSelected: function (v) {
+ BI.MultiSelectItem.superclass.setSelected.apply(this, arguments);
+ this.checkbox.setSelected(v);
+ }
+});
+BI.MultiSelectItem.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.multi_select_item", BI.MultiSelectItem);
diff --git a/src/main/resources/com/fr/fineui/case/button/item.singleselect.icontext.js b/src/main/resources/com/fr/fineui/case/button/item.singleselect.icontext.js
new file mode 100644
index 0000000..1a70d4c
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/item.singleselect.icontext.js
@@ -0,0 +1,59 @@
+/**
+ * Created by GUY on 2016/2/2.
+ *
+ * @class BI.SingleSelectIconTextItem
+ * @extends BI.BasicButton
+ */
+BI.SingleSelectIconTextItem = BI.inherit(BI.Single, {
+ _defaultConfig: function () {
+ return BI.extend(BI.SingleSelectIconTextItem.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "bi-single-select-icon-text-item bi-list-item-active",
+ iconCls: "",
+ height: 24
+ });
+ },
+ _init: function () {
+ BI.SingleSelectIconTextItem.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.text = BI.createWidget({
+ type: "bi.icon_text_item",
+ element: this,
+ cls: o.iconCls,
+ once: o.once,
+ iconWrapperWidth: o.iconWrapperWidth,
+ selected: o.selected,
+ height: o.height,
+ iconHeight: o.iconHeight,
+ iconWidth: o.iconWidth,
+ text: o.text,
+ keyword: o.keyword,
+ value: o.value,
+ py: o.py
+ });
+ this.text.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ },
+
+ isSelected: function () {
+ return this.text.isSelected();
+ },
+
+ setSelected: function (b) {
+ this.text.setSelected(b);
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doClick: function () {
+ BI.SingleSelectIconTextItem.superclass.doClick.apply(this, arguments);
+ }
+});
+
+BI.shortcut("bi.single_select_icon_text_item", BI.SingleSelectIconTextItem);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/button/item.singleselect.js b/src/main/resources/com/fr/fineui/case/button/item.singleselect.js
new file mode 100644
index 0000000..910b579
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/item.singleselect.js
@@ -0,0 +1,51 @@
+BI.SingleSelectItem = BI.inherit(BI.BasicButton, {
+ _defaultConfig: function () {
+ return BI.extend(BI.SingleSelectItem.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "bi-single-select-item bi-list-item-active",
+ hgap: 10,
+ height: 24,
+ textAlign: "left"
+ });
+ },
+ _init: function () {
+ BI.SingleSelectItem.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.text = BI.createWidget({
+ type: "bi.label",
+ element: this,
+ textAlign: o.textAlign,
+ whiteSpace: "nowrap",
+ textHeight: o.height,
+ height: o.height,
+ hgap: o.hgap,
+ text: o.text,
+ keyword: o.keyword,
+ value: o.value,
+ title: o.title || o.text,
+ warningTitle: o.warningTitle,
+ py: o.py
+ });
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doClick: function () {
+ BI.SingleSelectItem.superclass.doClick.apply(this, arguments);
+ if (this.isValid()) {
+ this.fireEvent(BI.SingleSelectItem.EVENT_CHANGE, this.isSelected(), this);
+ }
+ },
+
+ setSelected: function (v) {
+ BI.SingleSelectItem.superclass.setSelected.apply(this, arguments);
+ }
+});
+
+BI.SingleSelectItem.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.single_select_item", BI.SingleSelectItem);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/button/item.singleselect.radio.js b/src/main/resources/com/fr/fineui/case/button/item.singleselect.radio.js
new file mode 100644
index 0000000..0c6a55d
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/item.singleselect.radio.js
@@ -0,0 +1,78 @@
+/**
+ * guy
+ * 单选框item
+ * @type {*|void|Object}
+ */
+BI.SingleSelectRadioItem = BI.inherit(BI.BasicButton, {
+ _defaultConfig: function () {
+ return BI.extend(BI.SingleSelectRadioItem.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "bi-single-select-radio-item",
+ logic: {
+ dynamic: false
+ },
+ height: 24,
+ iconWrapperWidth: 16,
+ hgap: 10,
+ textHgap: 0,
+ textLgap: 0,
+ textRgap: 0
+ });
+ },
+ _init: function () {
+ BI.SingleSelectRadioItem.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.radio = BI.createWidget({
+ type: "bi.radio"
+ });
+ this.text = BI.createWidget({
+ type: "bi.label",
+ cls: "list-item-text",
+ textAlign: "left",
+ whiteSpace: "nowrap",
+ textHeight: o.height,
+ height: o.height,
+ hgap: o.hgap,
+ rgap: o.textRgap,
+ lgap: o.textLgap,
+ text: o.text,
+ keyword: o.keyword,
+ value: o.value,
+ py: o.py
+ });
+
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic("horizontal", BI.extend(o.logic, {
+ items: BI.LogicFactory.createLogicItemsByDirection("left", {
+ type: "bi.center_adapt",
+ items: [this.radio],
+ width: o.iconWrapperWidth
+ }, this.text)
+ }))));
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doClick: function () {
+ BI.SingleSelectRadioItem.superclass.doClick.apply(this, arguments);
+ this.radio.setSelected(this.isSelected());
+ if (this.isValid()) {
+ this.fireEvent(BI.SingleSelectRadioItem.EVENT_CHANGE, this.isSelected(), this);
+ }
+ },
+
+ setSelected: function (v) {
+ BI.SingleSelectRadioItem.superclass.setSelected.apply(this, arguments);
+ this.radio.setSelected(v);
+
+ }
+});
+
+BI.SingleSelectRadioItem.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.single_select_radio_item", BI.SingleSelectRadioItem);
diff --git a/src/main/resources/com/fr/fineui/case/button/node/__test__/node.arrow.test.js b/src/main/resources/com/fr/fineui/case/button/node/__test__/node.arrow.test.js
new file mode 100644
index 0000000..4b2a08e
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/node/__test__/node.arrow.test.js
@@ -0,0 +1,79 @@
+/**
+ * @author Kobi
+ * @date 2020/5/12
+ */
+
+describe("test node.arrow", function () {
+
+ /**
+ * test_author_kobi
+ **/
+ it("doRedMark 和 unRedMark", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.arrow_group_node",
+ text: "要标红的AAA",
+ });
+ widget.doRedMark("AAA");
+ expect(widget.element.find(".bi-keyword-red-mark").length).to.not.equal(0);
+ widget.unRedMark();
+ expect(widget.element.find(".bi-keyword-red-mark").length).to.equal(0);
+ widget.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("doClick", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.arrow_group_node",
+ text: "AAA",
+ });
+ BI.nextTick(function () {
+ widget.element.click();
+ expect(widget.element.find(".expander-down-font").length).to.not.equal(0);
+ BI.delay(function () {
+ BI.nextTick(function () {
+ widget.checkbox.element.click();
+ expect(widget.element.find(".expander-right-font").length).to.not.equal(0);
+ widget.destroy();
+ done();
+ });
+ }, 300);
+ });
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("点击图标", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.arrow_group_node",
+ text: "AAA",
+ });
+ BI.nextTick(function () {
+ widget.checkbox.element.click();
+ expect(widget.element.find(".expander-down-font").length).to.not.equal(0);
+ BI.delay(function () {
+ BI.nextTick(function () {
+ widget.checkbox.element.click();
+ expect(widget.element.find(".expander-right-font").length).to.not.equal(0);
+ widget.destroy();
+ done();
+ });
+ }, 300);
+ });
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("setText", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.arrow_group_node",
+ text: "AAA",
+ });
+ widget.setText("BBB");
+ expect(widget.element.find(".bi-text").text()).to.equal("BBB");
+ widget.destroy();
+ });
+});
diff --git a/src/main/resources/com/fr/fineui/case/button/node/__test__/node.first.plus.test.js b/src/main/resources/com/fr/fineui/case/button/node/__test__/node.first.plus.test.js
new file mode 100644
index 0000000..28912a1
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/node/__test__/node.first.plus.test.js
@@ -0,0 +1,66 @@
+/**
+ * @author Kobi
+ * @date 2020/5/12
+ */
+
+describe("test node.first.plus", function () {
+
+ /**
+ * test_author_kobi
+ **/
+ it("doRedMark 和 unRedMark", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.first_plus_group_node",
+ text: "要标红的AAA",
+ });
+ widget.doRedMark("AAA");
+ expect(widget.element.find(".bi-keyword-red-mark").length).to.not.equal(0);
+ widget.unRedMark();
+ expect(widget.element.find(".bi-keyword-red-mark").length).to.equal(0);
+ widget.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("doClick", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.first_plus_group_node",
+ text: "AAA",
+ });
+ BI.nextTick(function () {
+ widget.element.click();
+ expect(widget.element.find(".tree-expand-icon-type2").length).to.not.equal(0);
+ BI.delay(function () {
+ BI.nextTick(function () {
+ widget.element.click();
+ expect(widget.element.find(".tree-expand-icon-type2").length).to.equal(0);
+ widget.destroy();
+ done();
+ });
+ }, 300);
+ });
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("点击图标", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.first_plus_group_node",
+ text: "AAA",
+ });
+ BI.nextTick(function () {
+ widget.checkbox.element.click();
+ expect(widget.element.find(".tree-expand-icon-type2").length).to.not.equal(0);
+ BI.delay(function () {
+ BI.nextTick(function () {
+ widget.checkbox.element.click();
+ expect(widget.element.find(".tree-expand-icon-type2").length).to.equal(0);
+ widget.destroy();
+ done();
+ });
+ }, 300);
+ });
+ });
+});
diff --git a/src/main/resources/com/fr/fineui/case/button/node/__test__/node.icon.arrow.test.js b/src/main/resources/com/fr/fineui/case/button/node/__test__/node.icon.arrow.test.js
new file mode 100644
index 0000000..d5bd74d
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/node/__test__/node.icon.arrow.test.js
@@ -0,0 +1,66 @@
+/**
+ * @author Kobi
+ * @date 2020/5/12
+ */
+
+describe("test node.icon.arrow", function () {
+
+ /**
+ * test_author_kobi
+ **/
+ it("doRedMark 和 unRedMark", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.icon_arrow_node",
+ text: "要标红的AAA",
+ });
+ widget.doRedMark("AAA");
+ expect(widget.element.find(".bi-keyword-red-mark").length).to.not.equal(0);
+ widget.unRedMark();
+ expect(widget.element.find(".bi-keyword-red-mark").length).to.equal(0);
+ widget.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("doClick", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.icon_arrow_node",
+ text: "AAA",
+ });
+ BI.nextTick(function () {
+ widget.element.click();
+ expect(widget.element.find(".expander-down-font").length).to.not.equal(0);
+ BI.delay(function () {
+ BI.nextTick(function () {
+ widget.checkbox.element.click();
+ expect(widget.element.find(".expander-right-font").length).to.not.equal(0);
+ widget.destroy();
+ done();
+ });
+ }, 300);
+ });
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("点击图标", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.icon_arrow_node",
+ text: "AAA",
+ });
+ BI.nextTick(function () {
+ widget.checkbox.element.click();
+ expect(widget.element.find(".expander-down-font").length).to.not.equal(0);
+ BI.delay(function () {
+ BI.nextTick(function () {
+ widget.checkbox.element.click();
+ expect(widget.element.find(".expander-right-font").length).to.not.equal(0);
+ widget.destroy();
+ done();
+ });
+ }, 300);
+ });
+ });
+});
diff --git a/src/main/resources/com/fr/fineui/case/button/node/__test__/node.last.plus.test.js b/src/main/resources/com/fr/fineui/case/button/node/__test__/node.last.plus.test.js
new file mode 100644
index 0000000..53fab71
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/node/__test__/node.last.plus.test.js
@@ -0,0 +1,66 @@
+/**
+ * @author Kobi
+ * @date 2020/5/12
+ */
+
+describe("test node.last.plus", function () {
+
+ /**
+ * test_author_kobi
+ **/
+ it("doRedMark 和 unRedMark", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.last_plus_group_node",
+ text: "要标红的AAA",
+ });
+ widget.doRedMark("AAA");
+ expect(widget.element.find(".bi-keyword-red-mark").length).to.not.equal(0);
+ widget.unRedMark();
+ expect(widget.element.find(".bi-keyword-red-mark").length).to.equal(0);
+ widget.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("doClick", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.last_plus_group_node",
+ text: "AAA",
+ });
+ BI.nextTick(function () {
+ widget.element.click();
+ expect(widget.element.find(".tree-expand-icon-type4").length).to.not.equal(0);
+ BI.delay(function () {
+ BI.nextTick(function () {
+ widget.element.click();
+ expect(widget.element.find(".tree-expand-icon-type4").length).to.equal(0);
+ widget.destroy();
+ done();
+ });
+ }, 300);
+ });
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("点击图标", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.last_plus_group_node",
+ text: "AAA",
+ });
+ BI.nextTick(function () {
+ widget.checkbox.element.click();
+ expect(widget.element.find(".tree-expand-icon-type4").length).to.not.equal(0);
+ BI.delay(function () {
+ BI.nextTick(function () {
+ widget.checkbox.element.click();
+ expect(widget.element.find(".tree-expand-icon-type4").length).to.equal(0);
+ widget.destroy();
+ done();
+ });
+ }, 300);
+ });
+ });
+});
diff --git a/src/main/resources/com/fr/fineui/case/button/node/__test__/node.mid.plus.test.js b/src/main/resources/com/fr/fineui/case/button/node/__test__/node.mid.plus.test.js
new file mode 100644
index 0000000..d5c899c
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/node/__test__/node.mid.plus.test.js
@@ -0,0 +1,66 @@
+/**
+ * @author Kobi
+ * @date 2020/5/12
+ */
+
+describe("test node.mid.plus", function () {
+
+ /**
+ * test_author_kobi
+ **/
+ it("doRedMark 和 unRedMark", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.mid_plus_group_node",
+ text: "要标红的AAA",
+ });
+ widget.doRedMark("AAA");
+ expect(widget.element.find(".bi-keyword-red-mark").length).to.not.equal(0);
+ widget.unRedMark();
+ expect(widget.element.find(".bi-keyword-red-mark").length).to.equal(0);
+ widget.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("doClick", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.mid_plus_group_node",
+ text: "AAA",
+ });
+ BI.nextTick(function () {
+ widget.element.click();
+ expect(widget.element.find(".tree-expand-icon-type3").length).to.not.equal(0);
+ BI.delay(function () {
+ BI.nextTick(function () {
+ widget.element.click();
+ expect(widget.element.find(".tree-expand-icon-type3").length).to.equal(0);
+ widget.destroy();
+ done();
+ });
+ }, 300);
+ });
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("点击图标", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.mid_plus_group_node",
+ text: "AAA",
+ });
+ BI.nextTick(function () {
+ widget.checkbox.element.click();
+ expect(widget.element.find(".tree-expand-icon-type3").length).to.not.equal(0);
+ BI.delay(function () {
+ BI.nextTick(function () {
+ widget.checkbox.element.click();
+ expect(widget.element.find(".tree-expand-icon-type3").length).to.equal(0);
+ widget.destroy();
+ done();
+ });
+ }, 300);
+ });
+ });
+});
diff --git a/src/main/resources/com/fr/fineui/case/button/node/__test__/node.multilayer.icon.arrow.test.js b/src/main/resources/com/fr/fineui/case/button/node/__test__/node.multilayer.icon.arrow.test.js
new file mode 100644
index 0000000..c060e54
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/node/__test__/node.multilayer.icon.arrow.test.js
@@ -0,0 +1,73 @@
+/**
+ * @author Kobi
+ * @date 2020/5/12
+ */
+
+describe("test node.multilayer.icon.arrow", function () {
+
+ /**
+ * test_author_kobi
+ **/
+ it("doRedMark 和 unRedMark", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.multilayer_icon_arrow_node",
+ text: "要标红的AAA",
+ layer: 3,
+ });
+ expect(widget.isOnce()).to.equal(true);
+ widget.doRedMark("AAA");
+ expect(widget.element.find(".bi-keyword-red-mark").length).to.not.equal(0);
+ widget.unRedMark();
+ expect(widget.element.find(".bi-keyword-red-mark").length).to.equal(0);
+ widget.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("isSelected 和 setSelected", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.multilayer_icon_arrow_node",
+ text: "AAA",
+ layer: 3,
+ });
+ widget.setSelected(true);
+ expect(widget.element.find(".active").length).to.not.equal(0);
+ expect(widget.isSelected()).to.equal(true);
+ widget.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("doClick", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.multilayer_icon_arrow_node",
+ text: "AAA",
+ layer: 3,
+ });
+ BI.nextTick(function () {
+ widget.element.click();
+ expect(widget.isSelected()).to.equal(true);
+ widget.destroy();
+ done();
+ });
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("点击图标", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.multilayer_icon_arrow_node",
+ text: "AAA",
+ layer: 3,
+ });
+ BI.nextTick(function () {
+ widget.node.element.click();
+ expect(widget.element.find(".expander-down-font").length).to.not.equal(0);
+ widget.destroy();
+ done();
+ });
+ });
+});
diff --git a/src/main/resources/com/fr/fineui/case/button/node/__test__/node.plus.test.js b/src/main/resources/com/fr/fineui/case/button/node/__test__/node.plus.test.js
new file mode 100644
index 0000000..aebcf46
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/node/__test__/node.plus.test.js
@@ -0,0 +1,66 @@
+/**
+ * @author Kobi
+ * @date 2020/5/12
+ */
+
+describe("test node.plus", function () {
+
+ /**
+ * test_author_kobi
+ **/
+ it("doRedMark 和 unRedMark", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.plus_group_node",
+ text: "要标红的AAA",
+ });
+ widget.doRedMark("AAA");
+ expect(widget.element.find(".bi-keyword-red-mark").length).to.not.equal(0);
+ widget.unRedMark();
+ expect(widget.element.find(".bi-keyword-red-mark").length).to.equal(0);
+ widget.destroy();
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("doClick", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.plus_group_node",
+ text: "AAA",
+ });
+ BI.nextTick(function () {
+ widget.element.click();
+ expect(widget.element.find(".tree-expand-icon-type1").length).to.not.equal(0);
+ BI.delay(function () {
+ BI.nextTick(function () {
+ widget.element.click();
+ expect(widget.element.find(".tree-expand-icon-type1").length).to.equal(0);
+ widget.destroy();
+ done();
+ });
+ }, 300);
+ });
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("点击图标", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.plus_group_node",
+ text: "AAA",
+ });
+ BI.nextTick(function () {
+ widget.checkbox.element.click();
+ expect(widget.element.find(".tree-expand-icon-type1").length).to.not.equal(0);
+ BI.delay(function () {
+ BI.nextTick(function () {
+ widget.checkbox.element.click();
+ expect(widget.element.find(".tree-expand-icon-type1").length).to.equal(0);
+ widget.destroy();
+ done();
+ });
+ }, 300);
+ });
+ });
+});
diff --git a/src/main/resources/com/fr/fineui/case/button/node/node.arrow.js b/src/main/resources/com/fr/fineui/case/button/node/node.arrow.js
new file mode 100644
index 0000000..38fe40f
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/node/node.arrow.js
@@ -0,0 +1,82 @@
+/**
+ * Created by roy on 15/10/16.
+ */
+BI.ArrowNode = BI.inherit(BI.NodeButton, {
+ _defaultConfig: function () {
+ var conf = BI.ArrowNode.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-arrow-group-node bi-list-item",
+ logic: {
+ dynamic: false
+ },
+ id: "",
+ pId: "",
+ open: false,
+ height: 24,
+ iconWrapperWidth: 16
+ });
+ },
+ _init: function () {
+ var self = this, o = this.options;
+ BI.ArrowNode.superclass._init.apply(this, arguments);
+ this.checkbox = BI.createWidget({
+ type: "bi.arrow_group_node_checkbox"
+ });
+
+ this.text = BI.createWidget({
+ type: "bi.label",
+ textAlign: "left",
+ whiteSpace: "nowrap",
+ textHeight: o.height,
+ height: o.height,
+ hgap: o.hgap,
+ text: o.text,
+ value: o.value,
+ py: o.py,
+ keyword: o.keyword
+ });
+
+ this.checkbox.on(BI.Controller.EVENT_CHANGE, function (type) {
+ if (type === BI.Events.CLICK) {
+ self.setSelected(self.isSelected());
+ }
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+
+ var type = BI.LogicFactory.createLogicTypeByDirection(BI.Direction.Left);
+ var items = BI.LogicFactory.createLogicItemsByDirection(BI.Direction.Left, {
+ width: o.iconWrapperWidth,
+ el: this.checkbox
+ }, this.text);
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic(type, BI.extend(o.logic, {
+ items: items
+ }))));
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doClick: function () {
+ BI.ArrowNode.superclass.doClick.apply(this, arguments);
+ this.checkbox.setSelected(this.isOpened());
+ },
+
+ setText: function (text) {
+ BI.ArrowNode.superclass.setText.apply(this, arguments);
+ this.text.setText(text);
+ },
+
+ setOpened: function (v) {
+ BI.ArrowNode.superclass.setOpened.apply(this, arguments);
+ this.checkbox.setSelected(v);
+ }
+});
+
+BI.shortcut("bi.arrow_group_node", BI.ArrowNode);
diff --git a/src/main/resources/com/fr/fineui/case/button/node/node.first.plus.js b/src/main/resources/com/fr/fineui/case/button/node/node.first.plus.js
new file mode 100644
index 0000000..7969867
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/node/node.first.plus.js
@@ -0,0 +1,82 @@
+/**
+ * 加号表示的组节点
+ * Created by GUY on 2015/9/6.
+ * @class BI.FirstPlusGroupNode
+ * @extends BI.NodeButton
+ */
+BI.FirstPlusGroupNode = BI.inherit(BI.NodeButton, {
+ _defaultConfig: function () {
+ var conf = BI.FirstPlusGroupNode.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-first-plus-group-node bi-list-item",
+ logic: {
+ dynamic: false
+ },
+ id: "",
+ pId: "",
+ open: false,
+ height: 24
+ });
+ },
+ _init: function () {
+ BI.FirstPlusGroupNode.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.checkbox = BI.createWidget({
+ type: "bi.first_tree_node_checkbox",
+ stopPropagation: true
+ });
+ this.text = BI.createWidget({
+ type: "bi.label",
+ textAlign: "left",
+ whiteSpace: "nowrap",
+ textHeight: o.height,
+ height: o.height,
+ hgap: o.hgap,
+ text: o.text,
+ value: o.value,
+ py: o.py,
+ keyword: o.keyword
+ });
+ this.checkbox.on(BI.Controller.EVENT_CHANGE, function (type) {
+ if (type === BI.Events.CLICK) {
+ if (this.isSelected()) {
+ self.triggerExpand();
+ } else {
+ self.triggerCollapse();
+ }
+ }
+ });
+ var type = BI.LogicFactory.createLogicTypeByDirection(BI.Direction.Left);
+ var items = BI.LogicFactory.createLogicItemsByDirection(BI.Direction.Left, {
+ width: 24,
+ el: this.checkbox
+ }, this.text);
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic(type, BI.extend(o.logic, {
+ items: items
+ }))));
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doClick: function () {
+ BI.FirstPlusGroupNode.superclass.doClick.apply(this, arguments);
+ this.checkbox.setSelected(this.isSelected());
+ },
+
+ setOpened: function (v) {
+ BI.FirstPlusGroupNode.superclass.setOpened.apply(this, arguments);
+ if (BI.isNotNull(this.checkbox)) {
+ this.checkbox.setSelected(v);
+ }
+ }
+});
+
+BI.shortcut("bi.first_plus_group_node", BI.FirstPlusGroupNode);
diff --git a/src/main/resources/com/fr/fineui/case/button/node/node.icon.arrow.js b/src/main/resources/com/fr/fineui/case/button/node/node.icon.arrow.js
new file mode 100644
index 0000000..1cfa529
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/node/node.icon.arrow.js
@@ -0,0 +1,102 @@
+/**
+ * Created by User on 2016/3/31.
+ */
+/**
+ * > + icon + 文本
+ * @class BI.IconArrowNode
+ * @extends BI.NodeButton
+ */
+BI.IconArrowNode = BI.inherit(BI.NodeButton, {
+ _defaultConfig: function () {
+ var conf = BI.IconArrowNode.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-icon-arrow-node bi-list-item",
+ logic: {
+ dynamic: false
+ },
+ id: "",
+ pId: "",
+ open: false,
+ height: 24,
+ iconHeight: 12,
+ iconWidth: 12,
+ iconCls: "",
+ iconWrapperWidth: 16
+ });
+ },
+ _init: function () {
+ BI.IconArrowNode.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.checkbox = BI.createWidget({
+ type: "bi.arrow_group_node_checkbox",
+ width: 24,
+ stopPropagation: true
+ });
+
+ var icon = BI.createWidget({
+ type: "bi.icon_label",
+ width: 24,
+ cls: o.iconCls,
+ iconWidth: o.iconWidth,
+ iconHeight: o.iconHeight
+ });
+
+ this.text = BI.createWidget({
+ type: "bi.label",
+ textAlign: "left",
+ whiteSpace: "nowrap",
+ textHeight: o.height,
+ height: o.height,
+ hgap: o.hgap,
+ text: o.text,
+ value: o.value,
+ py: o.py,
+ keyword: o.keyword
+ });
+ this.checkbox.on(BI.Controller.EVENT_CHANGE, function (type) {
+ if (type === BI.Events.CLICK) {
+ if (this.isSelected()) {
+ self.triggerExpand();
+ } else {
+ self.triggerCollapse();
+ }
+ }
+ });
+ var type = BI.LogicFactory.createLogicTypeByDirection(BI.Direction.Left);
+ var items = BI.LogicFactory.createLogicItemsByDirection(BI.Direction.Left, {
+ width: o.iconWrapperWidth,
+ el: this.checkbox
+ }, {
+ width: 16,
+ el: icon
+ }, this.text);
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic(type, BI.extend(o.logic, {
+ items: items,
+ rgap: 5
+ }))));
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doClick: function () {
+ BI.IconArrowNode.superclass.doClick.apply(this, arguments);
+ this.checkbox.setSelected(this.isSelected());
+ },
+
+ setOpened: function (v) {
+ BI.IconArrowNode.superclass.setOpened.apply(this, arguments);
+ if (BI.isNotNull(this.checkbox)) {
+ this.checkbox.setSelected(v);
+ }
+ }
+});
+
+BI.shortcut("bi.icon_arrow_node", BI.IconArrowNode);
diff --git a/src/main/resources/com/fr/fineui/case/button/node/node.last.plus.js b/src/main/resources/com/fr/fineui/case/button/node/node.last.plus.js
new file mode 100644
index 0000000..acc72d6
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/node/node.last.plus.js
@@ -0,0 +1,82 @@
+/**
+ * 加号表示的组节点
+ * Created by GUY on 2015/9/6.
+ * @class BI.LastPlusGroupNode
+ * @extends BI.NodeButton
+ */
+BI.LastPlusGroupNode = BI.inherit(BI.NodeButton, {
+ _defaultConfig: function () {
+ var conf = BI.LastPlusGroupNode.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-last-plus-group-node bi-list-item",
+ logic: {
+ dynamic: false
+ },
+ id: "",
+ pId: "",
+ open: false,
+ height: 24
+ });
+ },
+ _init: function () {
+ BI.LastPlusGroupNode.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.checkbox = BI.createWidget({
+ type: "bi.last_tree_node_checkbox",
+ stopPropagation: true
+ });
+ this.text = BI.createWidget({
+ type: "bi.label",
+ textAlign: "left",
+ whiteSpace: "nowrap",
+ textHeight: o.height,
+ height: o.height,
+ hgap: o.hgap,
+ text: o.text,
+ value: o.value,
+ py: o.py,
+ keyword: o.keyword
+ });
+ this.checkbox.on(BI.Controller.EVENT_CHANGE, function (type) {
+ if(type === BI.Events.CLICK) {
+ if (this.isSelected()) {
+ self.triggerExpand();
+ } else {
+ self.triggerCollapse();
+ }
+ }
+ });
+ var type = BI.LogicFactory.createLogicTypeByDirection(BI.Direction.Left);
+ var items = BI.LogicFactory.createLogicItemsByDirection(BI.Direction.Left, {
+ width: 24,
+ el: this.checkbox
+ }, this.text);
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic(type, BI.extend(o.logic, {
+ items: items
+ }))));
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doClick: function () {
+ BI.LastPlusGroupNode.superclass.doClick.apply(this, arguments);
+ this.checkbox.setSelected(this.isSelected());
+ },
+
+ setOpened: function (v) {
+ BI.LastPlusGroupNode.superclass.setOpened.apply(this, arguments);
+ if (BI.isNotNull(this.checkbox)) {
+ this.checkbox.setSelected(v);
+ }
+ }
+});
+
+BI.shortcut("bi.last_plus_group_node", BI.LastPlusGroupNode);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/button/node/node.mid.plus.js b/src/main/resources/com/fr/fineui/case/button/node/node.mid.plus.js
new file mode 100644
index 0000000..4615ce7
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/node/node.mid.plus.js
@@ -0,0 +1,82 @@
+/**
+ * 加号表示的组节点
+ * Created by GUY on 2015/9/6.
+ * @class BI.MidPlusGroupNode
+ * @extends BI.NodeButton
+ */
+BI.MidPlusGroupNode = BI.inherit(BI.NodeButton, {
+ _defaultConfig: function () {
+ var conf = BI.MidPlusGroupNode.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-mid-plus-group-node bi-list-item",
+ logic: {
+ dynamic: false
+ },
+ id: "",
+ pId: "",
+ open: false,
+ height: 24
+ });
+ },
+ _init: function () {
+ BI.MidPlusGroupNode.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.checkbox = BI.createWidget({
+ type: "bi.mid_tree_node_checkbox",
+ stopPropagation: true
+ });
+ this.text = BI.createWidget({
+ type: "bi.label",
+ textAlign: "left",
+ whiteSpace: "nowrap",
+ textHeight: o.height,
+ height: o.height,
+ hgap: o.hgap,
+ text: o.text,
+ value: o.value,
+ py: o.py,
+ keyword: o.keyword
+ });
+ this.checkbox.on(BI.Controller.EVENT_CHANGE, function (type) {
+ if (type === BI.Events.CLICK) {
+ if (this.isSelected()) {
+ self.triggerExpand();
+ } else {
+ self.triggerCollapse();
+ }
+ }
+ });
+ var type = BI.LogicFactory.createLogicTypeByDirection(BI.Direction.Left);
+ var items = BI.LogicFactory.createLogicItemsByDirection(BI.Direction.Left, {
+ width: 24,
+ el: this.checkbox
+ }, this.text);
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic(type, BI.extend(o.logic, {
+ items: items
+ }))));
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doClick: function () {
+ BI.MidPlusGroupNode.superclass.doClick.apply(this, arguments);
+ this.checkbox.setSelected(this.isSelected());
+ },
+
+ setOpened: function (v) {
+ BI.MidPlusGroupNode.superclass.setOpened.apply(this, arguments);
+ if (BI.isNotNull(this.checkbox)) {
+ this.checkbox.setSelected(v);
+ }
+ }
+});
+
+BI.shortcut("bi.mid_plus_group_node", BI.MidPlusGroupNode);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/button/node/node.multilayer.icon.arrow.js b/src/main/resources/com/fr/fineui/case/button/node/node.multilayer.icon.arrow.js
new file mode 100644
index 0000000..ffa9c50
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/node/node.multilayer.icon.arrow.js
@@ -0,0 +1,89 @@
+BI.MultiLayerIconArrowNode = BI.inherit(BI.NodeButton, {
+ _defaultConfig: function () {
+ var conf = BI.MultiLayerIconArrowNode.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ extraCls: "bi-multilayer-icon-arrow-node bi-list-item",
+ layer: 0, // 第几层级
+ id: "",
+ pId: "",
+ open: false,
+ height: 24,
+ iconHeight: 16,
+ iconWidth: 16,
+ iconCls: ""
+ });
+ },
+ _init: function () {
+ BI.MultiLayerIconArrowNode.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.node = BI.createWidget({
+ type: "bi.icon_arrow_node",
+ iconCls: o.iconCls,
+ cls: "bi-list-item-none",
+ id: o.id,
+ pId: o.pId,
+ open: o.open,
+ height: o.height,
+ iconHeight: o.iconHeight,
+ iconWidth: o.iconWidth,
+ hgap: o.hgap,
+ text: o.text,
+ value: o.value,
+ py: o.py,
+ keyword: o.keyword
+ });
+ this.node.on(BI.Controller.EVENT_CHANGE, function (type) {
+ self.setSelected(self.isSelected());
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+
+ var items = [];
+ BI.count(0, o.layer, function () {
+ items.push({
+ type: "bi.layout",
+ width: 15,
+ height: o.height
+ });
+ });
+ items.push(this.node);
+ BI.createWidget({
+ type: "bi.horizontal_adapt",
+ element: this,
+ columnSize: BI.makeArray(o.layer, 15),
+ items: items
+ });
+ },
+
+ isOnce: function () {
+ return true;
+ },
+
+ doRedMark: function () {
+ this.node.doRedMark.apply(this.node, arguments);
+ },
+
+ unRedMark: function () {
+ this.node.unRedMark.apply(this.node, arguments);
+ },
+
+ isSelected: function () {
+ return this.node.isSelected();
+ },
+
+ setSelected: function (b) {
+ BI.MultiLayerIconArrowNode.superclass.setSelected.apply(this, arguments);
+ this.node.setSelected(b);
+ },
+
+ doClick: function () {
+ BI.NodeButton.superclass.doClick.apply(this, arguments);
+ this.node.setSelected(this.isSelected());
+ },
+
+ setOpened: function (v) {
+ BI.MultiLayerIconArrowNode.superclass.setOpened.apply(this, arguments);
+ this.node.setOpened(v);
+ }
+});
+
+BI.shortcut("bi.multilayer_icon_arrow_node", BI.MultiLayerIconArrowNode);
diff --git a/src/main/resources/com/fr/fineui/case/button/node/node.plus.js b/src/main/resources/com/fr/fineui/case/button/node/node.plus.js
new file mode 100644
index 0000000..16b78b8
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/node/node.plus.js
@@ -0,0 +1,78 @@
+/**
+ * 加号表示的组节点
+ * Created by GUY on 2015/9/6.
+ * @class BI.PlusGroupNode
+ * @extends BI.NodeButton
+ */
+BI.PlusGroupNode = BI.inherit(BI.NodeButton, {
+ _defaultConfig: function () {
+ var conf = BI.PlusGroupNode.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-plus-group-node bi-list-item",
+ logic: {
+ dynamic: false
+ },
+ id: "",
+ pId: "",
+ open: false,
+ height: 24
+ });
+ },
+ _init: function () {
+ BI.PlusGroupNode.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.checkbox = BI.createWidget({
+ type: "bi.tree_node_checkbox"
+ });
+ this.text = BI.createWidget({
+ type: "bi.label",
+ textAlign: "left",
+ whiteSpace: "nowrap",
+ textHeight: o.height,
+ height: o.height,
+ hgap: o.hgap,
+ text: o.text,
+ value: o.value,
+ keyword: o.keyword,
+ py: o.py
+ });
+ this.checkbox.on(BI.Controller.EVENT_CHANGE, function (type) {
+ if (type === BI.Events.CLICK) {
+ self.setSelected(self.isSelected());
+ }
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ var type = BI.LogicFactory.createLogicTypeByDirection(BI.Direction.Left);
+ var items = BI.LogicFactory.createLogicItemsByDirection(BI.Direction.Left, {
+ width: 24,
+ el: this.checkbox
+ }, this.text);
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic(type, BI.extend(o.logic, {
+ items: items
+ }))));
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doClick: function () {
+ BI.PlusGroupNode.superclass.doClick.apply(this, arguments);
+ this.checkbox.setSelected(this.isSelected());
+ },
+
+ setOpened: function (v) {
+ BI.PlusGroupNode.superclass.setOpened.apply(this, arguments);
+ if (this.checkbox) {
+ this.checkbox.setSelected(v);
+ }
+ }
+});
+
+BI.shortcut("bi.plus_group_node", BI.PlusGroupNode);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/button/switch.js b/src/main/resources/com/fr/fineui/case/button/switch.js
new file mode 100644
index 0000000..5ae4609
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/switch.js
@@ -0,0 +1,47 @@
+/**
+ * Created by Windy on 2018/2/1.
+ */
+BI.Switch = BI.inherit(BI.BasicButton, {
+
+ props: {
+ extraCls: "bi-switch",
+ height: 22,
+ width: 44,
+ logic: {
+ dynamic: false
+ }
+ },
+
+ render: function () {
+ var self = this;
+ return {
+ type: "bi.absolute",
+ ref: function () {
+ self.layout = this;
+ },
+ items: [{
+ el: {
+ type: "bi.text_button",
+ cls: "circle-button bi-card"
+ },
+ width: 18,
+ height: 18,
+ top: 2,
+ left: this.options.selected ? 24 : 2
+ }]
+ };
+ },
+
+ setSelected: function (v) {
+ BI.Switch.superclass.setSelected.apply(this, arguments);
+ this.layout.attr("items")[0].left = v ? 24 : 2;
+ this.layout.resize();
+ },
+
+ doClick: function () {
+ BI.Switch.superclass.doClick.apply(this, arguments);
+ this.fireEvent(BI.Switch.EVENT_CHANGE);
+ }
+});
+BI.Switch.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.switch", BI.Switch);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/button/treeitem/__test__/item.treeleaf.test.js b/src/main/resources/com/fr/fineui/case/button/treeitem/__test__/item.treeleaf.test.js
new file mode 100644
index 0000000..57b2e66
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/treeitem/__test__/item.treeleaf.test.js
@@ -0,0 +1,240 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/5/8
+ */
+describe("leafTest", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("标红与高亮", function () {
+ var textNode = BI.Test.createWidget({
+ type: "bi.first_tree_leaf_item",
+ id: "1",
+ pId: "-1",
+ layer: 0,
+ height: 24,
+ text: "ABC",
+ keyword: "B"
+ });
+ textNode.doRedMark("C");
+ expect(textNode.element.find(".bi-keyword-red-mark").length).to.not.equal(0);
+ textNode.unRedMark("C");
+ expect(textNode.element.find(".bi-keyword-red-mark").length).to.equal(0);
+ textNode.doHighLight("C");
+ expect(textNode.element.find(".bi-high-light").length).to.not.equal(0);
+ textNode.unHighLight("C");
+ expect(textNode.element.find(".bi-high-light").length).to.equal(0);
+ textNode.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("function", function () {
+ var textNode = BI.Test.createWidget({
+ type: "bi.first_tree_leaf_item",
+ id: "1",
+ pId: "-1"
+ });
+ expect(textNode.getId()).to.equal("1");
+ expect(textNode.getPId()).to.equal("-1");
+ textNode.destroy();
+ });
+
+
+ /**
+ * test_author_windy
+ */
+ it("标红与高亮1", function () {
+ var textNode = BI.Test.createWidget({
+ type: "bi.last_tree_leaf_item",
+ id: "1",
+ pId: "-1",
+ layer: 0,
+ height: 24,
+ text: "ABC",
+ keyword: "B"
+ });
+ textNode.doRedMark("C");
+ expect(textNode.element.find(".bi-keyword-red-mark").length).to.not.equal(0);
+ textNode.unRedMark("C");
+ expect(textNode.element.find(".bi-keyword-red-mark").length).to.equal(0);
+ textNode.doHighLight("C");
+ expect(textNode.element.find(".bi-high-light").length).to.not.equal(0);
+ textNode.unHighLight("C");
+ expect(textNode.element.find(".bi-high-light").length).to.equal(0);
+ textNode.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("function1", function () {
+ var textNode = BI.Test.createWidget({
+ type: "bi.last_tree_leaf_item",
+ id: "1",
+ pId: "-1"
+ });
+ expect(textNode.getId()).to.equal("1");
+ expect(textNode.getPId()).to.equal("-1");
+ textNode.destroy();
+ });
+
+
+ /**
+ * test_author_windy
+ */
+ it("标红与高亮12", function () {
+ var textNode = BI.Test.createWidget({
+ type: "bi.mid_tree_leaf_item",
+ id: "1",
+ pId: "-1",
+ layer: 0,
+ height: 24,
+ text: "ABC",
+ keyword: "B"
+ });
+ textNode.doRedMark("C");
+ expect(textNode.element.find(".bi-keyword-red-mark").length).to.not.equal(0);
+ textNode.unRedMark("C");
+ expect(textNode.element.find(".bi-keyword-red-mark").length).to.equal(0);
+ textNode.doHighLight("C");
+ expect(textNode.element.find(".bi-high-light").length).to.not.equal(0);
+ textNode.unHighLight("C");
+ expect(textNode.element.find(".bi-high-light").length).to.equal(0);
+ textNode.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("function12", function () {
+ var textNode = BI.Test.createWidget({
+ type: "bi.mid_tree_leaf_item",
+ id: "1",
+ pId: "-1"
+ });
+ expect(textNode.getId()).to.equal("1");
+ expect(textNode.getPId()).to.equal("-1");
+ textNode.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("标红与高亮123", function () {
+ var textNode = BI.Test.createWidget({
+ type: "bi.icon_tree_leaf_item",
+ id: "1",
+ pId: "-1",
+ layer: 0,
+ height: 24,
+ text: "ABC",
+ keyword: "B"
+ });
+ textNode.doRedMark("C");
+ expect(textNode.element.find(".bi-keyword-red-mark").length).to.not.equal(0);
+ textNode.unRedMark("C");
+ expect(textNode.element.find(".bi-keyword-red-mark").length).to.equal(0);
+ textNode.doHighLight("C");
+ expect(textNode.element.find(".bi-high-light").length).to.not.equal(0);
+ textNode.unHighLight("C");
+ expect(textNode.element.find(".bi-high-light").length).to.equal(0);
+ textNode.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("function123", function () {
+ var textNode = BI.Test.createWidget({
+ type: "bi.icon_tree_leaf_item",
+ id: "1",
+ pId: "-1"
+ });
+ expect(textNode.getId()).to.equal("1");
+ expect(textNode.getPId()).to.equal("-1");
+ textNode.destroy();
+ });
+
+
+ /**
+ * test_author_windy
+ */
+ it("标红与高亮1234", function () {
+ var textNode = BI.Test.createWidget({
+ type: "bi.multilayer_icon_tree_leaf_item",
+ id: "1",
+ pId: "-1",
+ layer: 0,
+ height: 24,
+ text: "ABC",
+ keyword: "B"
+ });
+ textNode.doRedMark("C");
+ expect(textNode.element.find(".bi-keyword-red-mark").length).to.not.equal(0);
+ textNode.unRedMark("C");
+ expect(textNode.element.find(".bi-keyword-red-mark").length).to.equal(0);
+ textNode.doHighLight("C");
+ expect(textNode.element.find(".bi-high-light").length).to.not.equal(0);
+ textNode.unHighLight("C");
+ expect(textNode.element.find(".bi-high-light").length).to.equal(0);
+ textNode.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("function1234", function () {
+ var textNode = BI.Test.createWidget({
+ type: "bi.multilayer_icon_tree_leaf_item",
+ id: "1",
+ pId: "-1"
+ });
+ expect(textNode.getId()).to.equal("1");
+ expect(textNode.getPId()).to.equal("-1");
+ textNode.destroy();
+ });
+
+
+ /**
+ * test_author_windy
+ */
+ it("标红与高亮12345", function () {
+ var textNode = BI.Test.createWidget({
+ type: "bi.tree_text_leaf_item",
+ id: "1",
+ pId: "-1",
+ layer: 0,
+ height: 24,
+ text: "ABC",
+ keyword: "B"
+ });
+ textNode.doRedMark("C");
+ expect(textNode.element.find(".bi-keyword-red-mark").length).to.not.equal(0);
+ textNode.unRedMark("C");
+ expect(textNode.element.find(".bi-keyword-red-mark").length).to.equal(0);
+ textNode.doHighLight("C");
+ expect(textNode.element.find(".bi-high-light").length).to.not.equal(0);
+ textNode.unHighLight("C");
+ expect(textNode.element.find(".bi-high-light").length).to.equal(0);
+ textNode.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("function12345", function () {
+ var textNode = BI.Test.createWidget({
+ type: "bi.tree_text_leaf_item",
+ id: "1",
+ pId: "-1"
+ });
+ expect(textNode.getId()).to.equal("1");
+ expect(textNode.getPId()).to.equal("-1");
+ textNode.destroy();
+ });
+
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/button/treeitem/item.first.treeleaf.js b/src/main/resources/com/fr/fineui/case/button/treeitem/item.first.treeleaf.js
new file mode 100644
index 0000000..afaa2f6
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/treeitem/item.first.treeleaf.js
@@ -0,0 +1,81 @@
+BI.FirstTreeLeafItem = BI.inherit(BI.BasicButton, {
+ _defaultConfig: function () {
+ return BI.extend(BI.FirstTreeLeafItem.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "bi-first-tree-leaf-item bi-list-item-active",
+ logic: {
+ dynamic: false
+ },
+ id: "",
+ pId: "",
+ layer: 0,
+ height: 24
+ });
+ },
+ _init: function () {
+ BI.FirstTreeLeafItem.superclass._init.apply(this, arguments);
+ var o = this.options;
+ this.text = BI.createWidget({
+ type: "bi.label",
+ textAlign: "left",
+ whiteSpace: "nowrap",
+ textHeight: o.height,
+ height: o.height,
+ hgap: o.hgap,
+ text: o.text,
+ value: o.value,
+ py: o.py,
+ keyword: o.keyword
+ });
+ var type = BI.LogicFactory.createLogicTypeByDirection(BI.Direction.Left);
+ var items = BI.LogicFactory.createLogicItemsByDirection(BI.Direction.Left, ((o.layer === 0) ? "" : {
+ width: 12,
+ el: {
+ type: "bi.layout",
+ cls: (o.pNode && o.pNode.isLastNode) ? "" : "base-line-conn-background",
+ width: 12,
+ height: o.height
+ }
+ }), {
+ width: 24,
+ el: {
+ type: "bi.layout",
+ cls: "first-line-conn-background",
+ width: 24,
+ height: o.height
+ }
+ }, {
+ el: this.text
+ });
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic(type, BI.extend(o.logic, {
+ items: items
+ }))));
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doHighLight: function () {
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ },
+
+ getId: function () {
+ return this.options.id;
+ },
+
+ getPId: function () {
+ return this.options.pId;
+ }
+});
+
+BI.shortcut("bi.first_tree_leaf_item", BI.FirstTreeLeafItem);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/button/treeitem/item.icon.treeleaf.js b/src/main/resources/com/fr/fineui/case/button/treeitem/item.icon.treeleaf.js
new file mode 100644
index 0000000..92fd5e3
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/treeitem/item.icon.treeleaf.js
@@ -0,0 +1,82 @@
+BI.IconTreeLeafItem = BI.inherit(BI.BasicButton, {
+ _defaultConfig: function () {
+ return BI.extend(BI.IconTreeLeafItem.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "bi-icon-tree-leaf-item bi-list-item-active",
+ logic: {
+ dynamic: false
+ },
+ height: 24,
+ iconWidth: 16,
+ iconHeight: 16,
+ iconCls: ""
+ });
+ },
+
+ _init: function () {
+ BI.IconTreeLeafItem.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+
+ var icon = BI.createWidget({
+ type: "bi.center_adapt",
+ width: 24,
+ cls: o.iconCls,
+ items: [{
+ type: "bi.icon",
+ width: o.iconWidth,
+ height: o.iconHeight
+ }]
+ });
+
+ this.text = BI.createWidget({
+ type: "bi.label",
+ textAlign: "left",
+ whiteSpace: "nowrap",
+ textHeight: o.height,
+ height: o.height,
+ hgap: o.hgap,
+ text: o.text,
+ value: o.value,
+ py: o.py,
+ keyword: o.keyword
+ });
+ var type = BI.LogicFactory.createLogicTypeByDirection(BI.Direction.Left);
+ var items = BI.LogicFactory.createLogicItemsByDirection(BI.Direction.Left, {
+ width: 16,
+ el: icon
+ }, {
+ el: this.text
+ });
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic(type, BI.extend(o.logic, {
+ items: items,
+ hgap: 5
+ }))));
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doHighLight: function () {
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ },
+
+ getId: function () {
+ return this.options.id;
+ },
+
+ getPId: function () {
+ return this.options.pId;
+ }
+});
+
+BI.shortcut("bi.icon_tree_leaf_item", BI.IconTreeLeafItem);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/button/treeitem/item.last.treeleaf.js b/src/main/resources/com/fr/fineui/case/button/treeitem/item.last.treeleaf.js
new file mode 100644
index 0000000..8b30892
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/treeitem/item.last.treeleaf.js
@@ -0,0 +1,81 @@
+BI.LastTreeLeafItem = BI.inherit(BI.BasicButton, {
+ _defaultConfig: function () {
+ return BI.extend(BI.LastTreeLeafItem.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "bi-last-tree-leaf-item bi-list-item-active",
+ logic: {
+ dynamic: false
+ },
+ id: "",
+ pId: "",
+ layer: 0,
+ height: 24
+ });
+ },
+ _init: function () {
+ BI.LastTreeLeafItem.superclass._init.apply(this, arguments);
+ var o = this.options;
+ this.text = BI.createWidget({
+ type: "bi.label",
+ textAlign: "left",
+ whiteSpace: "nowrap",
+ textHeight: o.height,
+ height: o.height,
+ hgap: o.hgap,
+ text: o.text,
+ value: o.value,
+ py: o.py,
+ keyword: o.keyword
+ });
+ var type = BI.LogicFactory.createLogicTypeByDirection(BI.Direction.Left);
+ var items = BI.LogicFactory.createLogicItemsByDirection(BI.Direction.Left, ((o.layer === 0) ? "" : {
+ width: 12,
+ el: {
+ type: "bi.layout",
+ cls: (o.pNode && o.pNode.isLastNode) ? "" : "base-line-conn-background",
+ width: 12,
+ height: o.height
+ }
+ }), {
+ width: 24,
+ el: {
+ type: "bi.layout",
+ cls: "last-line-conn-background",
+ width: 24,
+ height: o.height
+ }
+ }, {
+ el: this.text
+ });
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic(type, BI.extend(o.logic, {
+ items: items
+ }))));
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doHighLight: function () {
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ },
+
+ getId: function () {
+ return this.options.id;
+ },
+
+ getPId: function () {
+ return this.options.pId;
+ }
+});
+
+BI.shortcut("bi.last_tree_leaf_item", BI.LastTreeLeafItem);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/button/treeitem/item.mid.treeleaf.js b/src/main/resources/com/fr/fineui/case/button/treeitem/item.mid.treeleaf.js
new file mode 100644
index 0000000..a3a7a6c
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/treeitem/item.mid.treeleaf.js
@@ -0,0 +1,81 @@
+BI.MidTreeLeafItem = BI.inherit(BI.BasicButton, {
+ _defaultConfig: function () {
+ return BI.extend(BI.MidTreeLeafItem.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "bi-mid-tree-leaf-item bi-list-item-active",
+ logic: {
+ dynamic: false
+ },
+ id: "",
+ pId: "",
+ layer: 0,
+ height: 24
+ });
+ },
+ _init: function () {
+ BI.MidTreeLeafItem.superclass._init.apply(this, arguments);
+ var o = this.options;
+ this.text = BI.createWidget({
+ type: "bi.label",
+ textAlign: "left",
+ whiteSpace: "nowrap",
+ textHeight: o.height,
+ height: o.height,
+ hgap: o.hgap,
+ text: o.text,
+ value: o.value,
+ py: o.py,
+ keyword: o.keyword
+ });
+ var type = BI.LogicFactory.createLogicTypeByDirection(BI.Direction.Left);
+ var items = BI.LogicFactory.createLogicItemsByDirection(BI.Direction.Left, ((o.layer === 0) ? "" : {
+ width: 12,
+ el: {
+ type: "bi.layout",
+ cls: (o.pNode && o.pNode.isLastNode) ? "" : "base-line-conn-background",
+ width: 12,
+ height: o.height
+ }
+ }), {
+ width: 24,
+ el: {
+ type: "bi.layout",
+ cls: "mid-line-conn-background",
+ width: 24,
+ height: o.height
+ }
+ }, {
+ el: this.text
+ });
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic(type, BI.extend(o.logic, {
+ items: items
+ }))));
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doHighLight: function () {
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ },
+
+ getId: function () {
+ return this.options.id;
+ },
+
+ getPId: function () {
+ return this.options.pId;
+ }
+});
+
+BI.shortcut("bi.mid_tree_leaf_item", BI.MidTreeLeafItem);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/button/treeitem/item.multilayer.icon.treeleaf.js b/src/main/resources/com/fr/fineui/case/button/treeitem/item.multilayer.icon.treeleaf.js
new file mode 100644
index 0000000..363a348
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/treeitem/item.multilayer.icon.treeleaf.js
@@ -0,0 +1,98 @@
+/**
+ * @class BI.MultiLayerIconTreeLeafItem
+ * @extends BI.BasicButton
+ */
+BI.MultiLayerIconTreeLeafItem = BI.inherit(BI.BasicButton, {
+ _defaultConfig: function () {
+ return BI.extend(BI.MultiLayerIconTreeLeafItem.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "bi-multilayer-icon-tree-leaf-item bi-list-item-active",
+ layer: 0,
+ height: 24,
+ iconCls: "",
+ iconHeight: 16,
+ iconWidth: 16
+ });
+ },
+ _init: function () {
+ BI.MultiLayerIconTreeLeafItem.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.item = BI.createWidget({
+ type: "bi.icon_tree_leaf_item",
+ cls: "bi-list-item-none",
+ iconCls: o.iconCls,
+ id: o.id,
+ pId: o.pId,
+ isFront: true,
+ height: o.height,
+ hgap: o.hgap,
+ text: o.text,
+ value: o.value,
+ py: o.py,
+ keyword: o.keyword,
+ iconWidth: o.iconWidth,
+ iconHeight: o.iconHeight
+ });
+ this.item.on(BI.Controller.EVENT_CHANGE, function (type) {
+ if (type === BI.Events.CLICK) {// 本身实现click功能
+ return;
+ }
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+
+ var items = [];
+ BI.count(0, o.layer, function () {
+ items.push({
+ type: "bi.layout",
+ width: 15,
+ height: o.height
+ });
+ });
+ items.push(this.item);
+ BI.createWidget({
+ type: "bi.horizontal_adapt",
+ element: this,
+ columnSize: BI.makeArray(o.layer, 15),
+ items: items
+ });
+ },
+
+ doRedMark: function () {
+ this.item.doRedMark.apply(this.item, arguments);
+ },
+
+ unRedMark: function () {
+ this.item.unRedMark.apply(this.item, arguments);
+ },
+
+ doHighLight: function () {
+ this.item.doHighLight.apply(this.item, arguments);
+ },
+
+ unHighLight: function () {
+ this.item.unHighLight.apply(this.item, arguments);
+ },
+
+ getId: function () {
+ return this.options.id;
+ },
+
+ getPId: function () {
+ return this.options.pId;
+ },
+
+ doClick: function () {
+ BI.MultiLayerIconTreeLeafItem.superclass.doClick.apply(this, arguments);
+ this.item.setSelected(this.isSelected());
+ },
+
+ setSelected: function (v) {
+ BI.MultiLayerIconTreeLeafItem.superclass.setSelected.apply(this, arguments);
+ this.item.setSelected(v);
+ },
+
+ getValue: function () {
+ return this.options.value;
+ }
+});
+
+BI.shortcut("bi.multilayer_icon_tree_leaf_item", BI.MultiLayerIconTreeLeafItem);
diff --git a/src/main/resources/com/fr/fineui/case/button/treeitem/item.root.treeleaf.js b/src/main/resources/com/fr/fineui/case/button/treeitem/item.root.treeleaf.js
new file mode 100644
index 0000000..8e1a9a0
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/treeitem/item.root.treeleaf.js
@@ -0,0 +1,74 @@
+BI.RootTreeLeafItem = BI.inherit(BI.BasicButton, {
+ props: {
+ baseCls: "bi-root-tree-leaf-item bi-list-item-active",
+ logic: {
+ dynamic: false
+ },
+ id: "",
+ pId: "",
+ layer: 0,
+ height: 24
+ },
+
+ render: function () {
+ var self = this;
+ var o = this.options;
+ var text = {
+ type: "bi.label",
+ ref: function (_ref) {
+ self.text = _ref;
+ },
+ textAlign: "left",
+ whiteSpace: "nowrap",
+ textHeight: o.height,
+ height: o.height,
+ hgap: o.hgap,
+ text: o.text,
+ value: o.value,
+ py: o.py,
+ keyword: o.keyword
+ };
+
+ var type = BI.LogicFactory.createLogicTypeByDirection(BI.Direction.Left);
+ var items = BI.LogicFactory.createLogicItemsByDirection(BI.Direction.Left, {
+ width: 24,
+ el: {
+ type: "bi.layout",
+ width: 24,
+ height: o.height
+ }
+ }, {
+ el: text
+ });
+
+ return BI.LogicFactory.createLogic(type, BI.extend(o.logic, {
+ items: items
+ }));
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doHighLight: function () {
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ },
+
+ getId: function () {
+ return this.options.id;
+ },
+
+ getPId: function () {
+ return this.options.pId;
+ }
+});
+
+BI.shortcut("bi.root_tree_leaf_item", BI.RootTreeLeafItem);
diff --git a/src/main/resources/com/fr/fineui/case/button/treeitem/item.treetextleaf.js b/src/main/resources/com/fr/fineui/case/button/treeitem/item.treetextleaf.js
new file mode 100644
index 0000000..872663f
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/button/treeitem/item.treetextleaf.js
@@ -0,0 +1,70 @@
+/**
+ * 树叶子节点
+ * Created by GUY on 2015/9/6.
+ * @class BI.TreeTextLeafItem
+ * @extends BI.BasicButton
+ */
+BI.TreeTextLeafItem = BI.inherit(BI.BasicButton, {
+ _defaultConfig: function () {
+ return BI.extend(BI.TreeTextLeafItem.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "bi-tree-text-leaf-item bi-list-item-active",
+ id: "",
+ pId: "",
+ height: 24,
+ hgap: 0,
+ lgap: 0,
+ rgap: 0
+ });
+ },
+ _init: function () {
+ BI.TreeTextLeafItem.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.text = BI.createWidget({
+ type: "bi.label",
+ textAlign: "left",
+ whiteSpace: "nowrap",
+ textHeight: o.height,
+ height: o.height,
+ hgap: o.hgap,
+ lgap: o.lgap,
+ rgap: o.hgap,
+ text: o.text,
+ value: o.value,
+ py: o.py,
+ keyword: o.keyword
+ });
+ BI.createWidget({
+ type: "bi.htape",
+ element: this,
+ items: [{
+ el: this.text
+ }]
+ });
+ },
+
+ doRedMark: function () {
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doHighLight: function () {
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ },
+
+ getId: function () {
+ return this.options.id;
+ },
+
+ getPId: function () {
+ return this.options.pId;
+ }
+});
+
+BI.shortcut("bi.tree_text_leaf_item", BI.TreeTextLeafItem);
diff --git a/src/main/resources/com/fr/fineui/case/calendar/calendar.date.item.js b/src/main/resources/com/fr/fineui/case/calendar/calendar.date.item.js
new file mode 100644
index 0000000..b8ee105
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/calendar/calendar.date.item.js
@@ -0,0 +1,53 @@
+/**
+ * 专门为calendar的视觉加的button,作为私有button,不能配置任何属性,也不要用这个玩意
+ */
+BI.CalendarDateItem = BI.inherit(BI.BasicButton, {
+
+ render: function () {
+ var self = this, o = this.options;
+ return {
+ type: "bi.absolute",
+ items: [{
+ el: {
+ type: "bi.text_item",
+ cls: "bi-list-item-select",
+ textAlign: "center",
+ whiteSpace: "normal",
+ text: o.text,
+ value: o.value,
+ ref: function () {
+ self.text = this;
+ }
+ },
+ left: o.lgap,
+ right: o.rgap,
+ top: 0,
+ bottom: 0
+ }]
+ };
+ },
+
+ doHighLight: function () {
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ },
+
+ setValue: function () {
+ if (!this.isReadOnly()) {
+ this.text.setValue.apply(this.text, arguments);
+ }
+ },
+
+ setSelected: function (b) {
+ BI.CalendarDateItem.superclass.setSelected.apply(this, arguments);
+ this.text.setSelected(b);
+ },
+
+ getValue: function () {
+ return this.text.getValue();
+ }
+});
+BI.shortcut("bi.calendar_date_item", BI.CalendarDateItem);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/calendar/calendar.js b/src/main/resources/com/fr/fineui/case/calendar/calendar.js
new file mode 100644
index 0000000..fb762da
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/calendar/calendar.js
@@ -0,0 +1,234 @@
+/**
+ * Created by GUY on 2015/8/28.
+ * @class BI.Calendar
+ * @extends BI.Widget
+ */
+BI.Calendar = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ var conf = BI.Calendar.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: "bi-calendar",
+ logic: {
+ dynamic: false
+ },
+ min: "1900-01-01", // 最小日期
+ max: "2099-12-31", // 最大日期
+ year: 2015,
+ month: 8,
+ day: 25
+ });
+ },
+
+ _dateCreator: function (Y, M, D) {
+ var self = this, o = this.options, log = {}, De = BI.getDate();
+ var mins = o.min.match(/\d+/g);
+ var maxs = o.max.match(/\d+/g);
+
+ De.setFullYear(Y, M, D);
+ log.ymd = [De.getFullYear(), De.getMonth(), De.getDate()];
+
+ var MD = BI.Date._MD.slice(0);
+ MD[1] = BI.isLeapYear(log.ymd[0]) ? 29 : 28;
+
+ // 日期所在月第一天
+ De.setFullYear(log.ymd[0], log.ymd[1], 1);
+ // 是周几
+ log.FDay = De.getDay();
+
+ // 当前BI.StartOfWeek与周日对齐后的FDay是周几
+ var offSetFDay = (7 - BI.StartOfWeek + log.FDay) % 7;
+
+ // 当前月页第一天是几号
+ log.PDay = MD[M === 0 ? 11 : M - 1] - offSetFDay + 1;
+ log.NDay = 1;
+
+ var items = [];
+ BI.each(BI.range(42), function (i) {
+ var td = {}, YY = log.ymd[0], MM = log.ymd[1] + 1, DD;
+ // 上个月的日期
+ if (i < offSetFDay) {
+ td.lastMonth = true;
+ DD = i + log.PDay;
+ // 上一年
+ MM === 1 && (YY -= 1);
+ MM = MM === 1 ? 12 : MM - 1;
+ } else if (i >= offSetFDay && i < offSetFDay + MD[log.ymd[1]]) {
+ DD = i - offSetFDay + 1;
+ if (i - offSetFDay + 1 === log.ymd[2]) {
+ td.currentDay = true;
+ }
+ } else {
+ td.nextMonth = true;
+ DD = log.NDay++;
+ MM === 12 && (YY += 1);
+ MM = MM === 12 ? 1 : MM + 1;
+ }
+ if (BI.checkDateVoid(YY, MM, DD, mins, maxs)[0]) {
+ td.disabled = true;
+ }
+ td.text = DD;
+ items.push(td);
+ });
+ return items;
+ },
+
+ _init: function () {
+ BI.Calendar.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ var items = BI.map(this._getWeekLabel(), function (i, value) {
+ return {
+ type: "bi.label",
+ height: BI.SIZE_CONSANTS.LIST_ITEM_HEIGHT,
+ text: value
+ };
+ });
+ var title = BI.createWidget({
+ type: "bi.button_group",
+ height: 44,
+ items: items,
+ layouts: [{
+ type: "bi.center",
+ hgap: 5,
+ vgap: 10
+ }]
+ });
+
+ this.days = BI.createWidget({
+ type: "bi.button_group",
+ items: BI.createItems(this._getItems(), {}),
+ layouts: [BI.LogicFactory.createLogic("table", BI.extend({}, o.logic, {
+ columns: 7,
+ rows: 6,
+ columnSize: [1 / 7, 1 / 7, 1 / 7, 1 / 7, 1 / 7, 1 / 7, 1 / 7],
+ rowSize: BI.SIZE_CONSANTS.LIST_ITEM_HEIGHT,
+ vgap: 10
+ }))]
+ });
+ this.days.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ BI.createWidget(BI.extend({
+ element: this
+
+ }, BI.LogicFactory.createLogic("vertical", BI.extend({}, o.logic, {
+ items: BI.LogicFactory.createLogicItemsByDirection("top", title, this.days)
+ }))));
+ },
+
+ _getWeekLabel: function () {
+ return BI.map(BI.range(0, 7), function (idx, v) {
+ return BI.Date._SDN[(v + BI.StartOfWeek) % 7];
+ });
+ },
+
+ isFrontDate: function () {
+ var o = this.options, c = this._const;
+ var Y = o.year, M = o.month, De = BI.getDate(), day = De.getDay();
+ Y = Y | 0;
+ De.setFullYear(Y, M, 1);
+ var newDate = BI.getOffsetDate(De, -1 * (day + 1));
+ return !!BI.checkDateVoid(newDate.getFullYear(), newDate.getMonth(), newDate.getDate(), o.min, o.max)[0];
+ },
+
+ isFinalDate: function () {
+ var o = this.options, c = this._const;
+ var Y = o.year, M = o.month, De = BI.getDate(), day = De.getDay();
+ Y = Y | 0;
+ De.setFullYear(Y, M, 1);
+ var newDate = BI.getOffsetDate(De, 42 - day);
+ return !!BI.checkDateVoid(newDate.getFullYear(), newDate.getMonth(), newDate.getDate(), o.min, o.max)[0];
+ },
+
+ _getItems: function () {
+ var o = this.options;
+ var days = this._dateCreator(o.year, o.month - 1, o.day);
+ var items = [];
+ items.push(days.slice(0, 7));
+ items.push(days.slice(7, 14));
+ items.push(days.slice(14, 21));
+ items.push(days.slice(21, 28));
+ items.push(days.slice(28, 35));
+ items.push(days.slice(35, 42));
+
+ return BI.map(items, function (i, item) {
+ return BI.map(item, function (j, td) {
+ var month = td.lastMonth ? o.month - 1 : (td.nextMonth ? o.month + 1 : o.month);
+ return BI.extend(td, {
+ type: "bi.calendar_date_item",
+ textAlign: "center",
+ whiteSpace: "normal",
+ once: false,
+ forceSelected: true,
+ height: BI.SIZE_CONSANTS.LIST_ITEM_HEIGHT,
+ value: o.year + "-" + month + "-" + td.text,
+ disabled: td.lastMonth || td.nextMonth || td.disabled,
+ lgap: 5,
+ rgap: 5
+ // selected: td.currentDay
+ });
+ });
+ });
+ },
+
+ _populate: function() {
+ this.days.populate(this._getItems());
+ },
+
+ setMinDate: function (minDate) {
+ var o = this.options;
+ if (BI.isNotEmptyString(o.min)) {
+ o.min = minDate;
+ this._populate();
+ }
+ },
+
+ setMaxDate: function (maxDate) {
+ var o = this.options;
+ if (BI.isNotEmptyString(o.max)) {
+ o.max = maxDate;
+ this._populate();
+ }
+ },
+
+ setValue: function (ob) {
+ this.days.setValue([ob.year + "-" + ob.month + "-" + ob.day]);
+ },
+
+ getValue: function () {
+ var date = this.days.getValue()[0].match(/\d+/g);
+ return {
+ year: date[0] | 0,
+ month: date[1] | 0,
+ day: date[2] | 0
+ };
+ }
+});
+
+BI.extend(BI.Calendar, {
+ getPageByDateJSON: function (json) {
+ var year = BI.getDate().getFullYear();
+ var month = BI.getDate().getMonth();
+ var page = (json.year - year) * 12;
+ page += json.month - 1 - month;
+ return page;
+ },
+ getDateJSONByPage: function (v) {
+ var months = BI.getDate().getMonth();
+ var page = v;
+
+ // 对当前page做偏移,使到当前年初
+ page = page + months;
+
+ var year = BI.parseInt(page / 12);
+ if(page < 0 && page % 12 !== 0) {
+ year--;
+ }
+ var month = page >= 0 ? (page % 12) : ((12 + page % 12) % 12);
+ return {
+ year: BI.getDate().getFullYear() + year,
+ month: month + 1
+ };
+ }
+});
+
+BI.shortcut("bi.calendar", BI.Calendar);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/calendar/calendar.year.js b/src/main/resources/com/fr/fineui/case/calendar/calendar.year.js
new file mode 100644
index 0000000..7de7811
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/calendar/calendar.year.js
@@ -0,0 +1,171 @@
+/**
+ * Created by GUY on 2015/8/28.
+ * @class BI.YearCalendar
+ * @extends BI.Widget
+ */
+BI.YearCalendar = BI.inherit(BI.Widget, {
+
+ _defaultConfig: function () {
+ var conf = BI.YearCalendar.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: "bi-year-calendar",
+ behaviors: {},
+ logic: {
+ dynamic: false
+ },
+ min: "1900-01-01", // 最小日期
+ max: "2099-12-31", // 最大日期
+ year: null
+ });
+ },
+
+ _yearCreator: function (Y) {
+ var o = this.options;
+ Y = Y | 0;
+ var start = BI.YearCalendar.getStartYear(Y);
+ var items = [];
+ // 对于年控件来说,只要传入的minDate和maxDate的year区间包含v就是合法的
+ var startDate = BI.parseDateTime(o.min, "%Y-%X-%d");
+ var endDate = BI.parseDateTime(o.max, "%Y-%X-%d");
+ BI.each(BI.range(BI.YearCalendar.INTERVAL), function (i) {
+ var td = {};
+ if (BI.checkDateVoid(start + i, 1, 1, BI.print(BI.getDate(startDate.getFullYear(), 0, 1), "%Y-%X-%d"), BI.print(BI.getDate(endDate.getFullYear(), 0, 1), "%Y-%X-%d"))[0]) {
+ td.disabled = true;
+ }
+ td.text = start + i;
+ items.push(td);
+ });
+ return items;
+ },
+
+ _init: function () {
+ BI.YearCalendar.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.currentYear = BI.getDate().getFullYear();
+
+ this.years = BI.createWidget({
+ type: "bi.button_group",
+ behaviors: o.behaviors,
+ items: BI.createItems(this._getItems(), {}),
+ layouts: [BI.LogicFactory.createLogic("table", BI.extend({}, o.logic, {
+ columns: 2,
+ rows: 6,
+ columnSize: [1 / 2, 1 / 2],
+ rowSize: BI.SIZE_CONSANTS.LIST_ITEM_HEIGHT,
+ })), {
+ type: "bi.center_adapt",
+ vgap: 1
+ }]
+ });
+ this.years.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ BI.createWidget(BI.extend({
+ element: this
+
+ }, BI.LogicFactory.createLogic("vertical", BI.extend({}, o.logic, {
+ items: BI.LogicFactory.createLogicItemsByDirection("top", this.years)
+ }))));
+ },
+
+ isFrontYear: function () {
+ var o = this.options;
+ var Y = o.year;
+ Y = Y | 0;
+ return !!BI.checkDateVoid(BI.YearCalendar.getStartYear(Y) - 1, 1, 1, o.min, o.max)[0];
+ },
+
+ isFinalYear: function () {
+ var o = this.options, c = this._const;
+ var Y = o.year;
+ Y = Y | 0;
+ return !!BI.checkDateVoid(BI.YearCalendar.getEndYear(Y) + 1, 1, 1, o.min, o.max)[0];
+ },
+
+ _getItems: function () {
+ var o = this.options;
+ var years = this._yearCreator(o.year || this.currentYear);
+
+ // 纵向排列年
+ var len = years.length, tyears = BI.makeArray(len, "");
+ var map = [0, 6, 1, 7, 2, 8, 3, 9, 4, 10, 5, 11];
+ BI.each(years, function (i, y) {
+ tyears[i] = years[map[i]];
+ });
+
+ var items = [];
+ items.push(tyears.slice(0, 2));
+ items.push(tyears.slice(2, 4));
+ items.push(tyears.slice(4, 6));
+ items.push(tyears.slice(6, 8));
+ items.push(tyears.slice(8, 10));
+ items.push(tyears.slice(10, 12));
+
+ return BI.map(items, function (i, item) {
+ return BI.map(item, function (j, td) {
+ return BI.extend(td, {
+ type: "bi.text_item",
+ cls: "bi-list-item-select",
+ textAlign: "center",
+ whiteSpace: "normal",
+ once: false,
+ forceSelected: true,
+ height: BI.SIZE_CONSANTS.LIST_ITEM_HEIGHT,
+ width: 45,
+ value: td.text,
+ disabled: td.disabled
+ });
+ });
+ });
+ },
+
+ _populate: function () {
+ this.years.populate(this._getItems());
+ },
+
+ setMinDate: function (minDate) {
+ var o = this.options;
+ if (BI.isNotEmptyString(o.min)) {
+ o.min = minDate;
+ this._populate();
+ }
+ },
+
+ setMaxDate: function (maxDate) {
+ var o = this.options;
+ if (BI.isNotEmptyString(this.options.max)) {
+ o.max = maxDate;
+ this._populate();
+ }
+ },
+
+ setValue: function (val) {
+ this.years.setValue([val]);
+ },
+
+ getValue: function () {
+ return this.years.getValue()[0];
+ }
+});
+// 类方法
+BI.extend(BI.YearCalendar, {
+ INTERVAL: 12,
+
+ // 获取显示的第一年
+ getStartYear: function (year) {
+ var cur = BI.getDate().getFullYear();
+ return year - ((year - cur + 3) % BI.YearCalendar.INTERVAL + 12) % BI.YearCalendar.INTERVAL;
+ },
+
+ getEndYear: function (year) {
+ return BI.YearCalendar.getStartYear(year) + BI.YearCalendar.INTERVAL - 1;
+ },
+
+ getPageByYear: function (year) {
+ var cur = BI.getDate().getFullYear();
+ year = BI.YearCalendar.getStartYear(year);
+ return (year - cur + 3) / BI.YearCalendar.INTERVAL;
+ }
+});
+
+BI.shortcut("bi.year_calendar", BI.YearCalendar);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/checkbox/check.arrownode.js b/src/main/resources/com/fr/fineui/case/checkbox/check.arrownode.js
new file mode 100644
index 0000000..ebcb784
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/checkbox/check.arrownode.js
@@ -0,0 +1,21 @@
+/**
+ * Created by roy on 15/10/16.
+ * 右与下箭头切换的树节点
+ */
+BI.ArrowTreeGroupNodeCheckbox = BI.inherit(BI.IconButton, {
+ _defaultConfig: function () {
+ return BI.extend(BI.ArrowTreeGroupNodeCheckbox.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "bi-arrow-group-node-checkbox"
+ });
+ },
+
+ setSelected: function (v) {
+ BI.ArrowTreeGroupNodeCheckbox.superclass.setSelected.apply(this, arguments);
+ if(v) {
+ this.element.removeClass("expander-right-font").addClass("expander-down-font");
+ } else {
+ this.element.removeClass("expander-down-font").addClass("expander-right-font");
+ }
+ }
+});
+BI.shortcut("bi.arrow_group_node_checkbox", BI.ArrowTreeGroupNodeCheckbox);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/checkbox/check.checkingmarknode.js b/src/main/resources/com/fr/fineui/case/checkbox/check.checkingmarknode.js
new file mode 100644
index 0000000..d801828
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/checkbox/check.checkingmarknode.js
@@ -0,0 +1,26 @@
+/**
+ * 十字型的树节点
+ * @class BI.CheckingMarkNode
+ * @extends BI.IconButton
+ */
+BI.CheckingMarkNode = BI.inherit(BI.IconButton, {
+ _defaultConfig: function () {
+ return BI.extend( BI.CheckingMarkNode.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "check-mark-font"
+ });
+ },
+ _init: function () {
+ BI.CheckingMarkNode.superclass._init.apply(this, arguments);
+ this.setSelected(this.options.selected);
+
+ },
+ setSelected: function (v) {
+ BI.CheckingMarkNode.superclass.setSelected.apply(this, arguments);
+ if(v === true) {
+ this.element.addClass("check-mark-font");
+ } else {
+ this.element.removeClass("check-mark-font");
+ }
+ }
+});
+BI.shortcut("bi.checking_mark_node", BI.CheckingMarkNode);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/checkbox/check.first.treenode.js b/src/main/resources/com/fr/fineui/case/checkbox/check.first.treenode.js
new file mode 100644
index 0000000..572908d
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/checkbox/check.first.treenode.js
@@ -0,0 +1,24 @@
+/**
+ * 十字型的树节点
+ * @class BI.FirstTreeNodeCheckbox
+ * @extends BI.IconButton
+ */
+BI.FirstTreeNodeCheckbox = BI.inherit(BI.IconButton, {
+ _defaultConfig: function () {
+ return BI.extend( BI.FirstTreeNodeCheckbox.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "tree-collapse-icon-type2",
+ iconWidth: 24,
+ iconHeight: 24
+ });
+ },
+
+ setSelected: function (v) {
+ BI.FirstTreeNodeCheckbox.superclass.setSelected.apply(this, arguments);
+ if(v === true) {
+ this.element.addClass("tree-expand-icon-type2");
+ } else {
+ this.element.removeClass("tree-expand-icon-type2");
+ }
+ }
+});
+BI.shortcut("bi.first_tree_node_checkbox", BI.FirstTreeNodeCheckbox);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/checkbox/check.last.treenode.js b/src/main/resources/com/fr/fineui/case/checkbox/check.last.treenode.js
new file mode 100644
index 0000000..e0a9f62
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/checkbox/check.last.treenode.js
@@ -0,0 +1,24 @@
+/**
+ * 十字型的树节点
+ * @class BI.LastTreeNodeCheckbox
+ * @extends BI.IconButton
+ */
+BI.LastTreeNodeCheckbox = BI.inherit(BI.IconButton, {
+ _defaultConfig: function () {
+ return BI.extend(BI.LastTreeNodeCheckbox.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "tree-collapse-icon-type4",
+ iconWidth: 24,
+ iconHeight: 24
+ });
+ },
+
+ setSelected: function (v) {
+ BI.LastTreeNodeCheckbox.superclass.setSelected.apply(this, arguments);
+ if (v === true) {
+ this.element.addClass("tree-expand-icon-type4");
+ } else {
+ this.element.removeClass("tree-expand-icon-type4");
+ }
+ }
+});
+BI.shortcut("bi.last_tree_node_checkbox", BI.LastTreeNodeCheckbox);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/checkbox/check.mid.treenode.js b/src/main/resources/com/fr/fineui/case/checkbox/check.mid.treenode.js
new file mode 100644
index 0000000..7679b46
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/checkbox/check.mid.treenode.js
@@ -0,0 +1,24 @@
+/**
+ * 十字型的树节点
+ * @class BI.MidTreeNodeCheckbox
+ * @extends BI.IconButton
+ */
+BI.MidTreeNodeCheckbox = BI.inherit(BI.IconButton, {
+ _defaultConfig: function () {
+ return BI.extend( BI.MidTreeNodeCheckbox.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "tree-collapse-icon-type3",
+ iconWidth: 24,
+ iconHeight: 24
+ });
+ },
+
+ setSelected: function (v) {
+ BI.MidTreeNodeCheckbox.superclass.setSelected.apply(this, arguments);
+ if(v === true) {
+ this.element.addClass("tree-expand-icon-type3");
+ } else {
+ this.element.removeClass("tree-expand-icon-type3");
+ }
+ }
+});
+BI.shortcut("bi.mid_tree_node_checkbox", BI.MidTreeNodeCheckbox);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/checkbox/check.treenode.js b/src/main/resources/com/fr/fineui/case/checkbox/check.treenode.js
new file mode 100644
index 0000000..b5642aa
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/checkbox/check.treenode.js
@@ -0,0 +1,24 @@
+/**
+ * 十字型的树节点
+ * @class BI.TreeNodeCheckbox
+ * @extends BI.IconButton
+ */
+BI.TreeNodeCheckbox = BI.inherit(BI.IconButton, {
+ _defaultConfig: function () {
+ return BI.extend( BI.TreeNodeCheckbox.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "tree-collapse-icon-type1",
+ iconWidth: 24,
+ iconHeight: 24
+ });
+ },
+
+ setSelected: function (v) {
+ BI.TreeNodeCheckbox.superclass.setSelected.apply(this, arguments);
+ if(v) {
+ this.element.addClass("tree-expand-icon-type1");
+ } else {
+ this.element.removeClass("tree-expand-icon-type1");
+ }
+ }
+});
+BI.shortcut("bi.tree_node_checkbox", BI.TreeNodeCheckbox);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/colorchooser/__test__/colorchooser.test.js b/src/main/resources/com/fr/fineui/case/colorchooser/__test__/colorchooser.test.js
new file mode 100644
index 0000000..0377c20
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/colorchooser/__test__/colorchooser.test.js
@@ -0,0 +1,52 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/3/3
+ */
+
+describe("color_chooser_test", function () {
+
+ /**
+ * test_author_windy
+ **/
+ it("setValue", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.color_chooser",
+ height: 24
+ });
+ widget.setValue("#69821b");
+ expect(widget.getValue()).to.equal("#69821b");
+ widget.destroy();
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("点选选值", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.color_chooser",
+ height: 24
+ });
+ widget.element.find(".bi-color-chooser-trigger").click();
+ BI.delay(function () {
+ // 等300ms, button有debounce
+ widget.element.find(".bi-color-picker .bi-color-picker-button:nth-child(3)").click();
+ expect(widget.getValue()).to.equal("#e5e5e5");
+ widget.destroy();
+ done();
+ }, 300);
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("默认值", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.color_chooser",
+ height: 24,
+ value: "#69821b"
+ });
+ expect(widget.getValue()).to.equal("#69821b");
+ widget.destroy();
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.custom.js b/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.custom.js
new file mode 100644
index 0000000..49091a1
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.custom.js
@@ -0,0 +1,69 @@
+/**
+ * 自定义选色
+ *
+ * Created by GUY on 2015/11/17.
+ * @class BI.CustomColorChooser
+ * @extends BI.Widget
+ */
+BI.CustomColorChooser = BI.inherit(BI.Widget, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.CustomColorChooser.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-custom-color-chooser",
+ width: 292,
+ height: 265
+ });
+ },
+
+ _init: function () {
+ BI.CustomColorChooser.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.editor = BI.createWidget(o.editor, {
+ type: "bi.simple_hex_color_picker_editor"
+ });
+ this.editor.on(BI.ColorPickerEditor.EVENT_CHANGE, function () {
+ self.setValue(this.getValue());
+ });
+ this.farbtastic = BI.createWidget({
+ type: "bi.farbtastic"
+ });
+ this.farbtastic.on(BI.Farbtastic.EVENT_CHANGE, function () {
+ self.setValue(this.getValue());
+ });
+
+ BI.createWidget({
+ type: "bi.vtape",
+ element: this,
+ items: [{
+ type: "bi.absolute",
+ items: [{
+ el: this.editor,
+ left: 10,
+ top: 0,
+ right: 10
+ }],
+ height: 50
+ }, {
+ type: "bi.absolute",
+ items: [{
+ el: this.farbtastic,
+ left: 46,
+ right: 46,
+ top: 7
+ }],
+ height: 215
+ }]
+ });
+ },
+
+ setValue: function (color) {
+ this.editor.setValue(color);
+ this.farbtastic.setValue(color);
+ },
+
+ getValue: function () {
+ return this.editor.getValue();
+ }
+});
+BI.CustomColorChooser.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.custom_color_chooser", BI.CustomColorChooser);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.js b/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.js
new file mode 100644
index 0000000..698afa7
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.js
@@ -0,0 +1,107 @@
+/**
+ * 选色控件
+ *
+ * Created by GUY on 2015/11/17.
+ * @class BI.ColorChooser
+ * @extends BI.Widget
+ */
+BI.ColorChooser = BI.inherit(BI.Widget, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.ColorChooser.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-color-chooser",
+ value: "",
+ height: 24,
+ el: {},
+ });
+ },
+
+ _init: function () {
+ var self = this, o = this.options;
+ BI.ColorChooser.superclass._init.apply(this, arguments);
+ o.value = (o.value || "").toLowerCase();
+ this.combo = BI.createWidget({
+ type: "bi.combo",
+ element: this,
+ container: o.container,
+ adjustLength: 1,
+ destroyWhenHide: o.destroyWhenHide,
+ isNeedAdjustWidth: false,
+ isNeedAdjustHeight: false,
+ el: BI.extend({
+ type: o.width <= 24 ? "bi.color_chooser_trigger" : "bi.long_color_chooser_trigger",
+ ref: function (_ref) {
+ self.trigger = _ref;
+ },
+ width: o.el.type ? o.width : o.width - 2,
+ height: o.el.type ? o.height : o.height - 2
+ }, o.el),
+ popup: {
+ el: BI.extend({
+ type: "bi.hex_color_chooser_popup",
+ recommendColorsGetter: o.recommendColorsGetter,
+ ref: function (_ref) {
+ self.colorPicker = _ref;
+ },
+ listeners: [{
+ eventName: BI.ColorChooserPopup.EVENT_VALUE_CHANGE,
+ action: function () {
+ fn();
+ if (!self._isRGBColor(self.colorPicker.getValue())) {
+ self.combo.hideView();
+ }
+ }
+ }, {
+ eventName: BI.ColorChooserPopup.EVENT_CHANGE,
+ action: function () {
+ fn();
+ self.combo.hideView();
+ }
+ }]
+ }, o.popup),
+ value: o.value,
+ width: 300
+ },
+ value: o.value
+ });
+
+ var fn = function () {
+ var color = self.colorPicker.getValue();
+ self.trigger.setValue(color);
+ };
+
+ this.combo.on(BI.Combo.EVENT_BEFORE_HIDEVIEW, function () {
+ self.fireEvent(BI.ColorChooser.EVENT_CHANGE, arguments);
+ });
+ this.combo.on(BI.Combo.EVENT_AFTER_POPUPVIEW, function () {
+ self.fireEvent(BI.ColorChooser.EVENT_AFTER_POPUPVIEW, arguments);
+ });
+ },
+
+ _isRGBColor: function (color) {
+ return BI.isNotEmptyString(color) && color !== "transparent";
+ },
+
+ isViewVisible: function () {
+ return this.combo.isViewVisible();
+ },
+
+ hideView: function () {
+ this.combo.hideView();
+ },
+
+ showView: function () {
+ this.combo.showView();
+ },
+
+ setValue: function (color) {
+ this.combo.setValue((color || "").toLowerCase());
+ },
+
+ getValue: function () {
+ return this.combo.getValue();
+ }
+});
+BI.ColorChooser.EVENT_CHANGE = "EVENT_CHANGE";
+BI.ColorChooser.EVENT_AFTER_POPUPVIEW = "EVENT_AFTER_POPUPVIEW";
+BI.shortcut("bi.color_chooser", BI.ColorChooser);
diff --git a/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.popup.hex.js b/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.popup.hex.js
new file mode 100644
index 0000000..347dd04
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.popup.hex.js
@@ -0,0 +1,287 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/11/10
+ */
+BI.HexColorChooserPopup = BI.inherit(BI.Widget, {
+
+ props: {
+ baseCls: "bi-color-chooser-popup",
+ width: 300,
+ recommendColorsGetter: BI.emptyFn, // 推荐色获取接口
+ simple: false // 简单模式, popup中没有自动和透明
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ var hasRecommendColors = BI.isNotNull(o.recommendColorsGetter());
+ return [{
+ el: {
+ type: 'bi.vertical',
+ items: [{
+ el: {
+ type: "bi.vertical",
+ hgap: 15,
+ items: [BI.extend({
+ type: o.simple ? "bi.simple_hex_color_picker_editor" : "bi.hex_color_picker_editor",
+ value: o.value,
+ height: o.simple ? 36 : 70,
+ listeners: [{
+ eventName: BI.ColorPickerEditor.EVENT_CHANGE,
+ action: function () {
+ self.setValue(this.getValue());
+ self._dealStoreColors();
+ self.fireEvent(BI.ColorChooserPopup.EVENT_VALUE_CHANGE, arguments);
+ }
+ }],
+ ref: function (_ref) {
+ self.colorEditor = _ref;
+ }
+ }, o.editor), {
+ el: {
+ type: "bi.hex_color_picker",
+ cls: "bi-border-bottom bi-border-right",
+ items: [this._digestStoreColors(this._getStoreColors())],
+ height: 22,
+ value: o.value,
+ listeners: [{
+ eventName: BI.ColorPicker.EVENT_CHANGE,
+ action: function () {
+ self.setValue(this.getValue()[0]);
+ self._dealStoreColors();
+ self.fireEvent(BI.ColorChooserPopup.EVENT_CHANGE, arguments);
+ }
+ }],
+ ref: function (_ref) {
+ self.storeColors = _ref;
+ }
+ },
+ tgap: 10,
+ height: 22
+ }, {
+ el: hasRecommendColors ? {
+ type: 'bi.vertical',
+ items: [{
+ type: 'bi.label',
+ text: BI.i18nText('BI-Basic_Recommend_Color'),
+ textAlign: 'left',
+ height: 24,
+ }, {
+ type: "bi.hex_color_picker",
+ cls: "bi-border-bottom bi-border-right",
+ items: [this._digestStoreColors(o.recommendColorsGetter())],
+ height: 22,
+ value: o.value,
+ listeners: [{
+ eventName: BI.ColorPicker.EVENT_CHANGE,
+ action: function () {
+ self.setValue(this.getValue()[0]);
+ self._dealStoreColors();
+ self.fireEvent(BI.ColorChooserPopup.EVENT_CHANGE, arguments);
+ }
+ }],
+ ref: function (_ref) {
+ self.recommendColors = _ref;
+ }
+ }]
+ } : { type: 'bi.layout' },
+ tgap: hasRecommendColors ? 10 : 0,
+ height: hasRecommendColors ? 47 : 0
+ }, {
+ el: {
+ type: 'bi.layout',
+ cls: 'bi-border-top',
+ },
+ vgap: 10,
+ height: 1
+ }, {
+ type: 'bi.absolute',
+ items: [{
+ el: {
+ type: "bi.hex_color_picker",
+ space: true,
+ value: o.value,
+ listeners: [{
+ eventName: BI.ColorPicker.EVENT_CHANGE,
+ action: function () {
+ self.setValue(this.getValue()[0]);
+ self._dealStoreColors();
+ self.fireEvent(BI.ColorChooserPopup.EVENT_CHANGE, arguments);
+ }
+ }],
+ ref: function (_ref) {
+ self.colorPicker = _ref;
+ },
+ },
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 1,
+ }],
+ height: 80,
+ }]
+ }
+ }, {
+ el: {
+ type: "bi.combo",
+ cls: "bi-border-top",
+ container: null,
+ direction: "right,top",
+ isNeedAdjustHeight: false,
+ el: {
+ type: "bi.text_item",
+ cls: "color-chooser-popup-more bi-list-item",
+ textAlign: "center",
+ height: 24,
+ textLgap: 10,
+ text: BI.i18nText("BI-Basic_More") + "..."
+ },
+ popup: {
+ type: "bi.popup_panel",
+ buttons: [BI.i18nText("BI-Basic_Cancel"), BI.i18nText("BI-Basic_Save")],
+ title: BI.i18nText("BI-Custom_Color"),
+ el: {
+ type: "bi.custom_color_chooser",
+ editor: o.editor,
+ ref: function (_ref) {
+ self.customColorChooser = _ref;
+ }
+ },
+ stopPropagation: false,
+ bgap: -1,
+ rgap: 1,
+ lgap: 1,
+ minWidth: 227,
+ listeners: [{
+ eventName: BI.PopupPanel.EVENT_CLICK_TOOLBAR_BUTTON,
+ action: function (index) {
+ switch (index) {
+ case 0:
+ self.more.hideView();
+ break;
+ case 1:
+ var color = self.customColorChooser.getValue();
+ // farbtastic选择器没有透明和自动选项,点击保存不应该设置透明
+ if (BI.isNotEmptyString(color)) {
+ self.setValue(color);
+ self._dealStoreColors();
+ }
+ self.more.hideView();
+ self.fireEvent(BI.ColorChooserPopup.EVENT_CHANGE, arguments);
+ break;
+ }
+ }
+ }]
+ },
+ listeners: [{
+ eventName: BI.Combo.EVENT_AFTER_POPUPVIEW,
+ action: function () {
+ self.customColorChooser.setValue(self.getValue());
+ }
+ }],
+ ref: function (_ref) {
+ self.more = _ref;
+ }
+ },
+ tgap: 10,
+ height: 24
+ }]
+ },
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0
+ }, {
+ type: "bi.absolute",
+ items: [{
+ el: {
+ type: "bi.layout",
+ cls: "disable-mask",
+ invisible: !o.disabled,
+ ref: function () {
+ self.mask = this;
+ }
+ },
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0
+ }]
+ }];
+ },
+
+ // 这里就实现的不好了,setValue里面有个editor,editor的setValue会检测错误然后出bubble提示
+ mounted: function () {
+ var self = this;
+ var o = this.options;
+ if (BI.isNotNull(o.value)) {
+ this.setValue(o.value);
+ }
+ },
+
+ _setEnable: function (enable) {
+ BI.ColorChooserPopup.superclass._setEnable.apply(this, arguments);
+ this.mask.setVisible(!enable);
+ },
+
+ _dealStoreColors: function () {
+ var color = this.getValue();
+ var colors = this._getStoreColors();
+ var que = new BI.Queue(12);
+ que.fromArray(colors);
+ que.remove(color);
+ que.unshift(color);
+ var array = que.toArray();
+ BI.Cache.setItem("colors", BI.array2String(array));
+ this.setStoreColors(array);
+ },
+
+ _digestStoreColors: function (colors) {
+ var items = BI.map(colors.slice(0, 12), function (i, color) {
+ return {
+ value: color
+ };
+ });
+ BI.count(colors.length, 12, function (i) {
+ items.push({
+ value: "empty",
+ disabled: true
+ });
+ });
+ return items;
+ },
+
+ _getStoreColors: function() {
+ var self = this, o = this.options;
+ var colorsArray = BI.string2Array(BI.Cache.getItem("colors") || "");
+ return BI.filter(colorsArray, function (idx, color) {
+ return o.simple ? self._isRGBColor(color) : true;
+ });
+ },
+
+ _isRGBColor: function (color) {
+ return BI.isNotEmptyString(color) && color !== "transparent";
+ },
+
+ setStoreColors: function (colors) {
+ if (BI.isArray(colors)) {
+ this.storeColors.populate([this._digestStoreColors(colors)]);
+ // BI-66973 选中颜色的同时选中历史
+ this.storeColors.setValue(this.getValue());
+ }
+ },
+
+ setValue: function (color) {
+ this.colorEditor.setValue(color);
+ this.colorPicker.setValue(color);
+ this.storeColors.setValue(color);
+ this.recommendColors && this.recommendColors.setValue(color);
+ },
+
+ getValue: function () {
+ return this.colorEditor.getValue();
+ }
+});
+BI.HexColorChooserPopup.EVENT_VALUE_CHANGE = "EVENT_VALUE_CHANGE";
+BI.HexColorChooserPopup.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.hex_color_chooser_popup", BI.HexColorChooserPopup);
diff --git a/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.popup.hex.simple.js b/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.popup.hex.simple.js
new file mode 100644
index 0000000..628ad6f
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.popup.hex.simple.js
@@ -0,0 +1,50 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/11/10
+ */
+BI.SimpleHexColorChooserPopup = BI.inherit(BI.Widget, {
+
+ props: {
+ baseCls: "bi-color-chooser-popup",
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ return {
+ type: "bi.hex_color_chooser_popup",
+ recommendColorsGetter: o.recommendColorsGetter,
+ value: o.value,
+ simple: true, // 是否有自动
+ listeners: [{
+ eventName: BI.ColorChooserPopup.EVENT_CHANGE,
+ action: function () {
+ self.fireEvent(BI.SimpleColorChooserPopup.EVENT_CHANGE, arguments);
+ }
+ }, {
+ eventName: BI.ColorChooserPopup.EVENT_VALUE_CHANGE,
+ action: function () {
+ self.fireEvent(BI.SimpleColorChooserPopup.EVENT_VALUE_CHANGE, arguments);
+ }
+ }],
+ ref: function (_ref) {
+ self.popup = _ref;
+ }
+ }
+ },
+
+ setStoreColors: function (colors) {
+ this.popup.setStoreColors(colors);
+ },
+
+ setValue: function (color) {
+ this.popup.setValue(color);
+ },
+
+ getValue: function () {
+ return this.popup.getValue();
+ }
+});
+BI.SimpleHexColorChooserPopup.EVENT_VALUE_CHANGE = "EVENT_VALUE_CHANGE";
+BI.SimpleHexColorChooserPopup.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.simple_hex_color_chooser_popup", BI.SimpleHexColorChooserPopup);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.popup.js b/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.popup.js
new file mode 100644
index 0000000..c73fd91
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.popup.js
@@ -0,0 +1,237 @@
+/**
+ * 选色控件
+ *
+ * Created by GUY on 2015/11/17.
+ * @class BI.ColorChooserPopup
+ * @extends BI.Widget
+ */
+BI.ColorChooserPopup = BI.inherit(BI.Widget, {
+
+ props: {
+ baseCls: "bi-color-chooser-popup",
+ width: 230,
+ height: 145,
+ simple: false // 简单模式, popup中没有自动和透明
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ this.colorEditor = BI.createWidget(o.editor, {
+ type: o.simple ? "bi.simple_color_picker_editor" : "bi.color_picker_editor",
+ value: o.value,
+ cls: "bi-header-background bi-border-bottom",
+ height: 30
+ });
+
+ this.colorEditor.on(BI.ColorPickerEditor.EVENT_CHANGE, function () {
+ self.setValue(this.getValue());
+ self._dealStoreColors();
+ self.fireEvent(BI.ColorChooserPopup.EVENT_VALUE_CHANGE, arguments);
+ });
+
+ this.storeColors = BI.createWidget({
+ type: "bi.color_picker",
+ cls: "bi-border-bottom bi-border-right",
+ items: [this._digestStoreColors(this._getStoreColors())],
+ width: 210,
+ height: 24,
+ value: o.value
+ });
+ this.storeColors.on(BI.ColorPicker.EVENT_CHANGE, function () {
+ self.setValue(this.getValue()[0]);
+ self._dealStoreColors();
+ self.fireEvent(BI.ColorChooserPopup.EVENT_CHANGE, arguments);
+ });
+
+ this.colorPicker = BI.createWidget({
+ type: "bi.color_picker",
+ width: 210,
+ height: 50,
+ value: o.value
+ });
+
+ this.colorPicker.on(BI.ColorPicker.EVENT_CHANGE, function () {
+ self.setValue(this.getValue()[0]);
+ self._dealStoreColors();
+ self.fireEvent(BI.ColorChooserPopup.EVENT_CHANGE, arguments);
+ });
+
+ this.customColorChooser = BI.createWidget({
+ type: "bi.custom_color_chooser",
+ editor: o.editor
+ });
+
+ var panel = BI.createWidget({
+ type: "bi.popup_panel",
+ buttons: [BI.i18nText("BI-Basic_Cancel"), BI.i18nText("BI-Basic_Save")],
+ title: BI.i18nText("BI-Custom_Color"),
+ el: this.customColorChooser,
+ stopPropagation: false,
+ bgap: -1,
+ rgap: 1,
+ lgap: 1,
+ minWidth: 227
+ });
+
+ this.more = BI.createWidget({
+ type: "bi.combo",
+ cls: "bi-border-top",
+ container: null,
+ direction: "right,top",
+ isNeedAdjustHeight: false,
+ el: {
+ type: "bi.text_item",
+ cls: "color-chooser-popup-more bi-list-item",
+ textAlign: "center",
+ height: 24,
+ textLgap: 10,
+ text: BI.i18nText("BI-Basic_More") + "..."
+ },
+ popup: panel
+ });
+
+ this.more.on(BI.Combo.EVENT_AFTER_POPUPVIEW, function () {
+ self.customColorChooser.setValue(self.getValue());
+ });
+ panel.on(BI.PopupPanel.EVENT_CLICK_TOOLBAR_BUTTON, function (index) {
+ switch (index) {
+ case 0:
+ self.more.hideView();
+ break;
+ case 1:
+ self.setValue(self.customColorChooser.getValue());
+ self._dealStoreColors();
+ self.more.hideView();
+ self.fireEvent(BI.ColorChooserPopup.EVENT_CHANGE, arguments);
+ break;
+ }
+ });
+
+ return {
+ type: "bi.absolute",
+ items: [{
+ el: {
+ type: "bi.vtape",
+ items: [this.colorEditor, {
+ el: {
+ type: "bi.absolute",
+ items: [{
+ el: this.storeColors,
+ left: 10,
+ right: 10,
+ top: 5
+ }]
+ },
+ height: 29
+ }, {
+ el: {
+ type: "bi.absolute",
+ items: [{
+ el: this.colorPicker,
+ left: 10,
+ right: 10,
+ top: 5,
+ bottom: 5
+ }]
+ },
+ height: 60
+ }, {
+ el: this.more,
+ height: 24
+ }]
+ },
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0
+ }, {
+ el: {
+ type: "bi.layout",
+ cls: "disable-mask",
+ invisible: !o.disabled,
+ ref: function () {
+ self.mask = this;
+ }
+ },
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0
+ }]
+ };
+ },
+
+ // 这里就实现的不好了,setValue里面有个editor,editor的setValue会检测错误然后出bubble提示
+ mounted: function () {
+ var self = this;
+ var o = this.options;
+ if (BI.isNotNull(o.value)) {
+ this.setValue(o.value);
+ }
+ },
+
+ _setEnable: function (enable) {
+ BI.ColorChooserPopup.superclass._setEnable.apply(this, arguments);
+ this.mask.setVisible(!enable);
+ },
+
+ _dealStoreColors: function () {
+ var color = this.getValue();
+ var colors = this._getStoreColors();
+ var que = new BI.Queue(8);
+ que.fromArray(colors);
+ que.remove(color);
+ que.unshift(color);
+ var array = que.toArray();
+ BI.Cache.setItem("colors", BI.array2String(array));
+ this.setStoreColors(array);
+ },
+
+ _digestStoreColors: function (colors) {
+ var items = BI.map(colors, function (i, color) {
+ return {
+ value: color
+ };
+ });
+ BI.count(colors.length, 8, function (i) {
+ items.push({
+ value: "",
+ disabled: true
+ });
+ });
+ return items;
+ },
+
+ _getStoreColors: function() {
+ var self = this, o = this.options;
+ var colorsArray = BI.string2Array(BI.Cache.getItem("colors") || "");
+ return BI.filter(colorsArray, function (idx, color) {
+ return o.simple ? self._isRGBColor(color) : true;
+ });
+ },
+
+ _isRGBColor: function (color) {
+ return BI.isNotEmptyString(color) && color !== "transparent";
+ },
+
+ setStoreColors: function (colors) {
+ if (BI.isArray(colors)) {
+ this.storeColors.populate([this._digestStoreColors(colors)]);
+ // BI-66973 选中颜色的同时选中历史
+ this.storeColors.setValue(this.getValue());
+ }
+ },
+
+ setValue: function (color) {
+ this.colorEditor.setValue(color);
+ this.colorPicker.setValue(color);
+ this.storeColors.setValue(color);
+ },
+
+ getValue: function () {
+ return this.colorEditor.getValue();
+ }
+});
+BI.ColorChooserPopup.EVENT_VALUE_CHANGE = "EVENT_VALUE_CHANGE";
+BI.ColorChooserPopup.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.color_chooser_popup", BI.ColorChooserPopup);
diff --git a/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.popup.simple.js b/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.popup.simple.js
new file mode 100644
index 0000000..54b34ce
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.popup.simple.js
@@ -0,0 +1,47 @@
+/**
+ * 选色控件
+ *
+ * Created by GUY on 2015/11/17.
+ * @class BI.SimpleColorChooserPopup
+ * @extends BI.Widget
+ */
+BI.SimpleColorChooserPopup = BI.inherit(BI.Widget, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.SimpleColorChooserPopup.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-color-chooser-popup"
+ });
+ },
+
+ _init: function () {
+ BI.SimpleColorChooserPopup.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.popup = BI.createWidget({
+ type: o.hex ? "bi.hex_color_chooser_popup" : "bi.color_chooser_popup",
+ value: o.value,
+ element: this,
+ simple: true // 是否有自动
+ });
+ this.popup.on(BI.ColorChooserPopup.EVENT_CHANGE, function () {
+ self.fireEvent(BI.SimpleColorChooserPopup.EVENT_CHANGE, arguments);
+ });
+ this.popup.on(BI.ColorChooserPopup.EVENT_VALUE_CHANGE, function () {
+ self.fireEvent(BI.SimpleColorChooserPopup.EVENT_VALUE_CHANGE, arguments);
+ });
+ },
+
+ setStoreColors: function (colors) {
+ this.popup.setStoreColors(colors);
+ },
+
+ setValue: function (color) {
+ this.popup.setValue(color);
+ },
+
+ getValue: function () {
+ return this.popup.getValue();
+ }
+});
+BI.SimpleColorChooserPopup.EVENT_VALUE_CHANGE = "EVENT_VALUE_CHANGE";
+BI.SimpleColorChooserPopup.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.simple_color_chooser_popup", BI.SimpleColorChooserPopup);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.simple.js b/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.simple.js
new file mode 100644
index 0000000..b360efb
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.simple.js
@@ -0,0 +1,64 @@
+/**
+ * 简单选色控件,没有自动和透明
+ *
+ * Created by GUY on 2015/11/17.
+ * @class BI.SimpleColorChooser
+ * @extends BI.Widget
+ */
+BI.SimpleColorChooser = BI.inherit(BI.Widget, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.SimpleColorChooser.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-simple-color-chooser",
+ value: "#ffffff"
+ });
+ },
+
+ _init: function () {
+ BI.SimpleColorChooser.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+
+ this.combo = BI.createWidget({
+ type: "bi.color_chooser",
+ element: this,
+ container: o.container,
+ value: o.value,
+ width: o.width,
+ height: o.height,
+ destroyWhenHide: o.destroyWhenHide,
+ popup: {
+ type: "bi.simple_hex_color_chooser_popup",
+ recommendColorsGetter: o.recommendColorsGetter,
+ }
+ });
+ this.combo.on(BI.ColorChooser.EVENT_CHANGE, function () {
+ self.fireEvent(BI.SimpleColorChooser.EVENT_CHANGE, arguments);
+ });
+ this.combo.on(BI.ColorChooser.EVENT_AFTER_POPUPVIEW, function () {
+ self.fireEvent(BI.SimpleColorChooser.EVENT_AFTER_POPUPVIEW, arguments);
+ });
+ },
+
+ isViewVisible: function () {
+ return this.combo.isViewVisible();
+ },
+
+ hideView: function () {
+ this.combo.hideView();
+ },
+
+ showView: function () {
+ this.combo.showView();
+ },
+
+ setValue: function (color) {
+ this.combo.setValue(color);
+ },
+
+ getValue: function () {
+ return this.combo.getValue();
+ }
+});
+BI.SimpleColorChooser.EVENT_CHANGE = "EVENT_CHANGE";
+BI.SimpleColorChooser.EVENT_AFTER_POPUPVIEW = "EVENT_AFTER_POPUPVIEW";
+BI.shortcut("bi.simple_color_chooser", BI.SimpleColorChooser);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.trigger.js b/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.trigger.js
new file mode 100644
index 0000000..8efe4fb
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.trigger.js
@@ -0,0 +1,65 @@
+/**
+ * 选色控件
+ *
+ * Created by GUY on 2015/11/17.
+ * @class BI.ColorChooserTrigger
+ * @extends BI.Trigger
+ */
+BI.ColorChooserTrigger = BI.inherit(BI.Trigger, {
+
+ _defaultConfig: function () {
+ var conf = BI.ColorChooserTrigger.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-color-chooser-trigger bi-border bi-focus-shadow",
+ height: 22
+ });
+ },
+
+ _init: function () {
+ BI.ColorChooserTrigger.superclass._init.apply(this, arguments);
+ this.colorContainer = BI.createWidget({
+ type: "bi.layout",
+ cls: "color-chooser-trigger-content" + (BI.isIE9Below && BI.isIE9Below() ? " hack" : "")
+ });
+
+ var down = BI.createWidget({
+ type: "bi.icon_button",
+ disableSelected: true,
+ cls: "icon-combo-down-icon trigger-triangle-font icon-size-12",
+ width: 12,
+ height: 8
+ });
+
+ BI.createWidget({
+ type: "bi.absolute",
+ element: this,
+ items: [{
+ el: this.colorContainer,
+ left: 2,
+ right: 2,
+ top: 2,
+ bottom: 2
+ }, {
+ el: down,
+ right: -1,
+ bottom: 1
+ }]
+ });
+ if (BI.isNotNull(this.options.value)) {
+ this.setValue(this.options.value);
+ }
+ },
+
+ setValue: function (color) {
+ BI.ColorChooserTrigger.superclass.setValue.apply(this, arguments);
+ if (color === "") {
+ this.colorContainer.element.css("background-color", "").removeClass("trans-color-background").addClass("auto-color-background");
+ } else if (color === "transparent") {
+ this.colorContainer.element.css("background-color", "").removeClass("auto-color-background").addClass("trans-color-background");
+ } else {
+ this.colorContainer.element.css({"background-color": color}).removeClass("auto-color-background").removeClass("trans-color-background");
+ }
+ }
+});
+BI.ColorChooserTrigger.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.color_chooser_trigger", BI.ColorChooserTrigger);
diff --git a/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.trigger.long.js b/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.trigger.long.js
new file mode 100644
index 0000000..eb15f75
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/colorchooser/colorchooser.trigger.long.js
@@ -0,0 +1,98 @@
+/**
+ * 选色控件
+ *
+ * Created by GUY on 2015/11/17.
+ * @class BI.LongColorChooserTrigger
+ * @extends BI.Trigger
+ */
+BI.LongColorChooserTrigger = BI.inherit(BI.Trigger, {
+
+ _defaultConfig: function () {
+ var conf = BI.LongColorChooserTrigger.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-color-chooser-trigger bi-border bi-focus-shadow",
+ height: 24
+ });
+ },
+
+ _init: function () {
+ BI.LongColorChooserTrigger.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.colorContainer = BI.createWidget({
+ type: "bi.htape",
+ cls: "color-chooser-trigger-content",
+ items: [{
+ type: "bi.icon_change_button",
+ ref: function (_ref) {
+ self.changeIcon = _ref;
+ },
+ disableSelected: true,
+ iconCls: "auto-color-icon",
+ width: 24,
+ iconWidth: 16,
+ iconHeight: 16
+ }, {
+ el: {
+ type: "bi.label",
+ ref: function (_ref) {
+ self.label = _ref;
+ },
+ textAlign: "left",
+ hgap: 5,
+ height: 18,
+ text: BI.i18nText("BI-Basic_Auto")
+ }
+ }]
+ });
+
+ var down = BI.createWidget({
+ type: "bi.icon_button",
+ disableSelected: true,
+ cls: "icon-combo-down-icon trigger-triangle-font icon-size-12",
+ width: 12,
+ height: 8
+ });
+
+ BI.createWidget({
+ type: "bi.absolute",
+ element: this,
+ items: [{
+ el: this.colorContainer,
+ left: 2,
+ right: 2,
+ top: 2,
+ bottom: 2
+ }, {
+ el: down,
+ right: 3,
+ bottom: 3
+ }]
+ });
+ if (this.options.value) {
+ this.setValue(this.options.value);
+ }
+ },
+
+ setValue: function (color) {
+ BI.LongColorChooserTrigger.superclass.setValue.apply(this, arguments);
+ if (color === "") {
+ this.colorContainer.element.css("background-color", "");
+ this.changeIcon.setVisible(true);
+ this.label.setVisible(true);
+ this.changeIcon.setIcon("auto-color-icon");
+ this.label.setText(BI.i18nText("BI-Basic_Auto"));
+ } else if (color === "transparent") {
+ this.colorContainer.element.css("background-color", "");
+ this.changeIcon.setVisible(true);
+ this.label.setVisible(true);
+ this.changeIcon.setIcon("trans-color-icon");
+ this.label.setText(BI.i18nText("BI-Transparent_Color"));
+ } else {
+ this.colorContainer.element.css({"background-color": color});
+ this.changeIcon.setVisible(false);
+ this.label.setVisible(false);
+ }
+ }
+});
+BI.LongColorChooserTrigger.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.long_color_chooser_trigger", BI.LongColorChooserTrigger);
diff --git a/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/button/button.colorpicker.js b/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/button/button.colorpicker.js
new file mode 100644
index 0000000..d3ebf53
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/button/button.colorpicker.js
@@ -0,0 +1,66 @@
+/**
+ * 简单选色控件按钮
+ *
+ * Created by GUY on 2015/11/16.
+ * @class BI.ColorPickerButton
+ * @extends BI.BasicButton
+ */
+BI.ColorPickerButton = BI.inherit(BI.BasicButton, {
+
+ _defaultConfig: function () {
+ var conf = BI.ColorPickerButton.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-color-picker-button bi-background bi-border-top bi-border-left"
+ });
+ },
+
+ _init: function () {
+ BI.ColorPickerButton.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ if (BI.isNotNull(o.value)) {
+ if (o.value === '') {
+ this.element.addClass("auto-color-no-square-normal-background");
+ } else if (o.value === "transparent") {
+ this.element.addClass("trans-color-background");
+ } else {
+ this.element.css("background-color", o.value);
+ }
+ var name = this.getName();
+ this.element.hover(function () {
+ self._createMask();
+ if (self.isEnabled()) {
+ BI.Maskers.show(name);
+ }
+ }, function () {
+ if (!self.isSelected()) {
+ BI.Maskers.hide(name);
+ }
+ });
+ }
+ },
+
+ _createMask: function () {
+ var o = this.options, name = this.getName();
+ if (this.isEnabled() && !BI.Maskers.has(name)) {
+ var w = BI.Maskers.make(name, this, {
+ offset: {
+ left: -1,
+ top: -1,
+ right: -1,
+ bottom: -1
+ }
+ });
+ w.element.addClass("color-picker-button-mask").css("background-color", o.value);
+ }
+ },
+
+ setSelected: function (b) {
+ BI.ColorPickerButton.superclass.setSelected.apply(this, arguments);
+ if (b) {
+ this._createMask();
+ }
+ BI.Maskers[b ? "show" : "hide"](this.getName());
+ }
+});
+BI.ColorPickerButton.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.color_picker_button", BI.ColorPickerButton);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/button/button.colorshow.js b/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/button/button.colorshow.js
new file mode 100644
index 0000000..740a5d0
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/button/button.colorshow.js
@@ -0,0 +1,43 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2021/7/28
+ */
+BI.ColorChooserShowButton = BI.inherit(BI.BasicButton, {
+
+ props: {
+ baseCls: 'bi-color-chooser-show-button bi-border bi-list-item-effect',
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ return {
+ type: 'bi.htape',
+ items: [{
+ el: {
+ type: "bi.icon_label",
+ ref: function (_ref) {
+ self.icon = _ref;
+ },
+ iconWidth: 16,
+ iconHeight: 16,
+ },
+ hgap: 20,
+ width: 16,
+ }, {
+ type: 'bi.label',
+ textAlign: 'left',
+ text: o.text,
+ }]
+ }
+ },
+
+ doClick: function () {
+ BI.ColorChooserShowButton.superclass.doClick.apply(this, arguments);
+ if (this.isValid()) {
+ this.fireEvent(BI.ColorChooserShowButton.EVENT_CHANGE, this);
+ }
+ },
+});
+BI.ColorChooserShowButton.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.color_picker_show_button", BI.ColorChooserShowButton);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/colorpicker.hex.js b/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/colorpicker.hex.js
new file mode 100644
index 0000000..fad7c5b
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/colorpicker.hex.js
@@ -0,0 +1,170 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2021/7/28
+ */
+BI.HexColorPicker = BI.inherit(BI.Widget, {
+
+ props: {
+ baseCls: "bi-hex-color-picker",
+ items: null,
+ },
+
+ _items: [
+ [{
+ value: "#808080"
+ }, {
+ value: "#ffffff"
+ }, {
+ value: "#ffebe5"
+ }, {
+ value: "#ffddba"
+ }, {
+ value: "#ffeebb"
+ }, {
+ value: "#d4e9bf"
+ }, {
+ value: "#c7e1e1"
+ }, {
+ value: "#bfe3f0"
+ }, {
+ value: "#ccd6eb"
+ }],
+ [{
+ value: "#616161"
+ }, {
+ value: "#f2f2f2"
+ }, {
+ value: "#ffd6cc"
+ }, {
+ value: "#ffb87a"
+ }, {
+ value: "#ffdf91"
+ }, {
+ value: "#b7d2b6"
+ }, {
+ value: "#a3d2c9"
+ }, {
+ value: "#8ab6d6"
+ }, {
+ value: "#bcbce0"
+ }],
+ [{
+ value: "#404040"
+ }, {
+ value: "#dedede"
+ }, {
+ value: "#ffab9b"
+ }, {
+ value: "#eb8a3a"
+ }, {
+ value: "#ffc947"
+ }, {
+ value: "#8aa964"
+ }, {
+ value: "#5eaaa0"
+ }, {
+ value: "#2978b5"
+ }, {
+ value: "#8f8faa"
+ }],
+ [{
+ value: "#202020"
+ }, {
+ value: "#bfbfbf"
+ }, {
+ value: "#df7461"
+ }, {
+ value: "#cf7536"
+ }, {
+ value: "#e6b63b"
+ }, {
+ value: "#5b8a72"
+ }, {
+ value: "#3b9aa3"
+ }, {
+ value: "#336291"
+ }, {
+ value: "#58568f"
+ }],
+ [{
+ value: "#000000"
+ }, {
+ value: "#a1a1a1"
+ }, {
+ value: "#b55140"
+ }, {
+ value: "#a6713c"
+ }, {
+ value: "#ad975f"
+ }, {
+ value: "#5f7d6e"
+ }, {
+ value: "#3b7480"
+ }, {
+ value: "#425d78"
+ }, {
+ value: "#62608a"
+ }]
+ ],
+
+ render: function () {
+ var self = this, o = this.options;
+ this.colors = BI.createWidget();
+
+ return {
+ type: "bi.button_group",
+ items: this._digest(o.items || this._items),
+ layouts: [{
+ type: "bi.grid",
+ }],
+ value: o.value,
+ listeners: [{
+ eventName: BI.ButtonGroup.EVENT_CHANGE,
+ action: function () {
+ self.fireEvent(BI.HexColorPicker.EVENT_CHANGE, arguments);
+ }
+ }],
+ ref: function (_ref) {
+ self.colors = _ref;
+ }
+ };
+ },
+
+ _digest: function (items) {
+ var o = this.options;
+ var blocks = [];
+ BI.each(items, function (idx, row) {
+ var bRow = [];
+ BI.each(row, function (idx, item) {
+ bRow.push(BI.extend({
+ type: "bi.color_picker_button",
+ once: false,
+ cls: o.space ? 'bi-border-right' : '',
+ }, item));
+ if (o.space && idx < row.length - 1) {
+ bRow.push({ type: 'bi.layout' });
+ }
+ });
+ blocks.push(bRow);
+ });
+
+ return blocks;
+ },
+
+ populate: function (items) {
+ var args = [].slice.call(arguments);
+ args[0] = this._digest(items);
+ this.colors.populate.apply(this.colors, args);
+ },
+
+ setValue: function (color) {
+ this.colors.setValue(color);
+ },
+
+ getValue: function () {
+ return this.colors.getValue();
+ }
+});
+BI.HexColorPicker.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.hex_color_picker", BI.HexColorPicker);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/colorpicker.js b/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/colorpicker.js
new file mode 100644
index 0000000..54a8274
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/colorpicker.js
@@ -0,0 +1,190 @@
+/**
+ * 简单选色控件
+ *
+ * Created by GUY on 2015/11/16.
+ * @class BI.ColorPicker
+ * @extends BI.Widget
+ */
+BI.ColorPicker = BI.inherit(BI.Widget, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.ColorPicker.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-color-picker",
+ items: null
+ });
+ },
+
+ _items: [
+ [{
+ value: "#ffffff"
+ }, {
+ value: "#f2f2f2"
+ }, {
+ value: "#e5e5e5"
+ }, {
+ value: "#d9d9d9"
+ }, {
+ value: "#cccccc"
+ }, {
+ value: "#bfbfbf"
+ }, {
+ value: "#b2b2b2"
+ }, {
+ value: "#a6a6a6"
+ }, {
+ value: "#999999"
+ }, {
+ value: "#8c8c8c"
+ }, {
+ value: "#808080"
+ }, {
+ value: "#737373"
+ }, {
+ value: "#666666"
+ }, {
+ value: "#4d4d4d"
+ }, {
+ value: "#333333"
+ }, {
+ value: "#000000"
+ }],
+ [{
+ value: "#d8b5a6"
+ }, {
+ value: "#ff9e9a"
+ }, {
+ value: "#ffc17d"
+ }, {
+ value: "#f5e56b"
+ }, {
+ value: "#d8e698"
+ }, {
+ value: "#e0ebaf"
+ }, {
+ value: "#c3d825"
+ }, {
+ value: "#bbe2e7"
+ }, {
+ value: "#85d3cd"
+ }, {
+ value: "#bde1e6"
+ }, {
+ value: "#a0d8ef"
+ }, {
+ value: "#89c3eb"
+ }, {
+ value: "#bbc8e6"
+ }, {
+ value: "#bbbcde"
+ }, {
+ value: "#d6b4cc"
+ }, {
+ value: "#fbc0d3"
+ }],
+ [{
+ value: "#bb9581"
+ }, {
+ value: "#f37d79"
+ }, {
+ value: "#fba74f"
+ }, {
+ value: "#ffdb4f"
+ }, {
+ value: "#c7dc68"
+ }, {
+ value: "#b0ca71"
+ }, {
+ value: "#99ab4e"
+ }, {
+ value: "#84b9cb"
+ }, {
+ value: "#00a3af"
+ }, {
+ value: "#2ca9e1"
+ }, {
+ value: "#0095d9"
+ }, {
+ value: "#4c6cb3"
+ }, {
+ value: "#8491c3"
+ }, {
+ value: "#a59aca"
+ }, {
+ value: "#cc7eb1"
+ }, {
+ value: "#e89bb4"
+ }],
+ [{
+ value: "#9d775f"
+ }, {
+ value: "#dd4b4b"
+ }, {
+ value: "#ef8b07"
+ }, {
+ value: "#fcc800"
+ }, {
+ value: "#aacf53"
+ }, {
+ value: "#82ae46"
+ }, {
+ value: "#69821b"
+ }, {
+ value: "#59b9c6"
+ }, {
+ value: "#2a83a2"
+ }, {
+ value: "#007bbb"
+ }, {
+ value: "#19448e"
+ }, {
+ value: "#274a78"
+ }, {
+ value: "#4a488e"
+ }, {
+ value: "#7058a3"
+ }, {
+ value: "#884898"
+ }, {
+ value: "#d47596"
+ }]
+ ],
+
+ _init: function () {
+ BI.ColorPicker.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.colors = BI.createWidget({
+ type: "bi.button_group",
+ element: this,
+ items: BI.createItems(o.items || this._items, {
+ type: "bi.color_picker_button",
+ once: false
+ }),
+ layouts: [{
+ type: "bi.grid"
+ }],
+ value: o.value
+ });
+ this.colors.on(BI.ButtonGroup.EVENT_CHANGE, function () {
+ self.fireEvent(BI.ColorPicker.EVENT_CHANGE, arguments);
+ });
+ },
+
+ populate: function (items) {
+ var args = [].slice.call(arguments);
+ args[0] = BI.createItems(items, {
+ type: "bi.color_picker_button",
+ once: false
+ });
+ this.colors.populate.apply(this.colors, args);
+ },
+
+ setValue: function (color) {
+ this.colors.setValue(color);
+ },
+
+ getValue: function () {
+ return this.colors.getValue();
+ }
+});
+BI.ColorPicker.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.color_picker", BI.ColorPicker);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/editor.colorpicker.hex.js b/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/editor.colorpicker.hex.js
new file mode 100644
index 0000000..6aa07af
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/editor.colorpicker.hex.js
@@ -0,0 +1,307 @@
+/**
+ * 简单选色控件
+ *
+ * Created by GUY on 2015/11/16.
+ * @class BI.ColorPickerEditor
+ * @extends BI.Widget
+ */
+BI.HexColorPickerEditor = BI.inherit(BI.Widget, {
+
+ constants: {
+ RGB_WIDTH: 32,
+ HEX_WIDTH: 70,
+ HEX_PREFIX_POSITION: 1
+ },
+
+ props: {
+ baseCls: "bi-color-picker-editor",
+ height: 30
+ },
+
+ render: function () {
+ var self = this, o = this.options, c = this.constants;
+ this.storeValue = {};
+ var RGB = BI.createItems([{text: "R"}, {text: "G"}, {text: "B"}], {
+ type: "bi.label",
+ cls: "color-picker-editor-label",
+ height: 20
+ });
+
+ var checker = function (v) {
+ return BI.isNumeric(v) && (v | 0) >= 0 && (v | 0) <= 255;
+ };
+ var Ws = BI.map(BI.range(0, 3), function () {
+ return {
+ type: "bi.small_text_editor",
+ cls: "color-picker-editor-input",
+ validationChecker: checker,
+ errorText: BI.i18nText("BI-Color_Picker_Error_Text"),
+ allowBlank: true,
+ value: 255,
+ width: c.RGB_WIDTH,
+ height: 24,
+ listeners: [{
+ eventName: BI.TextEditor.EVENT_CHANGE,
+ action: function () {
+ self._checkEditors();
+ if (checker(self.storeValue.r) && checker(self.storeValue.g) && checker(self.storeValue.b)) {
+ self.colorShow.element.css("background-color", self.getValue());
+ self.fireEvent(BI.ColorPickerEditor.EVENT_CHANGE);
+ }
+ }
+ }]
+ };
+ });
+
+ return {
+ type: "bi.absolute",
+ items: [{
+ el: {
+ type: "bi.vertical",
+ tgap: 10,
+ items: [{
+ type: 'bi.vertical_adapt',
+ columnSize: [0.5, 'fill'],
+ height: 24,
+ items: [{
+ type: "bi.color_picker_show_button",
+ cls: "trans-color-icon",
+ height: 22,
+ title: BI.i18nText("BI-Transparent_Color"),
+ text: BI.i18nText("BI-Transparent_Color"),
+ listeners: [{
+ eventName: BI.ColorChooserShowButton.EVENT_CHANGE,
+ action: function () {
+ if (this.isSelected()) {
+ self.lastColor = self.getValue();
+ self.setValue("transparent");
+ } else {
+ if (self.lastColor === "transparent") {
+ self.lastColor = "";
+ }
+ self.setValue(self.lastColor || "#ffffff");
+ }
+ if ((self.R.isValid() && self.G.isValid() && self.B.isValid()) ||
+ self._isEmptyRGB()) {
+ self.colorShow.element.css("background-color", self.getValue());
+ self.fireEvent(BI.ColorPickerEditor.EVENT_CHANGE);
+ }
+ }
+ }],
+ ref: function (_ref) {
+ self.transparent = _ref;
+ }
+ }, {
+ el: {
+ type: "bi.color_picker_show_button",
+ cls: "auto-color-icon",
+ height: 22,
+ title: BI.i18nText("BI-Basic_Auto"),
+ text: BI.i18nText("BI-Basic_Auto"),
+ listeners: [{
+ eventName: BI.ColorChooserShowButton.EVENT_CHANGE,
+ action: function () {
+ if (this.isSelected()) {
+ self.lastColor = self.getValue();
+ self.setValue("");
+ } else {
+ self.setValue(self.lastColor || "#ffffff");
+ }
+ if ((self.R.isValid() && self.G.isValid() && self.B.isValid()) || self._isEmptyRGB()) {
+ self.colorShow.element.css("background-color", self.getValue());
+ self.fireEvent(BI.ColorPickerEditor.EVENT_CHANGE);
+ }
+ }
+ }],
+ ref: function (_ref) {
+ self.none = _ref;
+ }
+ },
+ lgap: 10,
+ }]
+ }, {
+ el: {
+ type: "bi.vertical_adapt",
+ columnSize: [22, 10, 'fill', 12, c.RGB_WIDTH, 12, c.RGB_WIDTH, 12, c.RGB_WIDTH],
+
+ rgap: 5,
+ items: [{
+ el: {
+ type: "bi.layout",
+ cls: "color-picker-editor-display bi-card bi-border",
+ height: 22,
+ width: 22,
+ ref: function (_ref) {
+ self.colorShow = _ref;
+ }
+ },
+ width: 18
+ }, {
+ type: "bi.label",
+ text: "#",
+ width: 10
+ }, {
+ type: "bi.small_text_editor",
+ ref: function (_ref) {
+ self.hexEditor = _ref;
+ },
+ cls: "color-picker-editor-input",
+ validationChecker: this._hexChecker,
+ allowBlank: true,
+ errorText: BI.i18nText("BI-Color_Picker_Error_Text_Hex"),
+ width: c.HEX_WIDTH,
+ height: 24,
+ listeners: [{
+ eventName: "EVENT_CHANGE",
+ action: function () {
+ self._checkHexEditor();
+ if (checker(self.storeValue.r) && checker(self.storeValue.g) && checker(self.storeValue.b)) {
+ self.colorShow.element.css("background-color", self.getValue());
+ self.fireEvent(BI.ColorPickerEditor.EVENT_CHANGE);
+ }
+
+ }
+ }]
+ }, RGB[0], {
+ el: BI.extend(Ws[0], {
+ ref: function (_ref) {
+ self.R = _ref
+ }
+ }),
+ width: c.RGB_WIDTH
+ }, RGB[1], {
+ el: BI.extend(Ws[1], {
+ ref: function (_ref) {
+ self.G = _ref
+ }
+ }),
+ width: c.RGB_WIDTH
+ }, RGB[2], {
+ el: BI.extend(Ws[2], {
+ ref: function (_ref) {
+ self.B = _ref
+ }
+ }),
+ rgap: -5,
+ width: c.RGB_WIDTH
+ }]
+ }
+ }]
+ },
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0
+ }]
+ };
+ },
+
+ _hexChecker: function (v) {
+ return /^[0-9a-fA-F]{6}$/.test(v);
+ },
+
+ _checkEditors: function () {
+ if(BI.isEmptyString(this.R.getValue())) {
+ this.R.setValue(0);
+ }
+ if(BI.isEmptyString(this.G.getValue())) {
+ this.G.setValue(0);
+ }
+ if(BI.isEmptyString(this.B.getValue())) {
+ this.B.setValue(0);
+ }
+ this.storeValue = {
+ r: this.R.getValue() || 0,
+ g: this.G.getValue() || 0,
+ b: this.B.getValue() || 0
+ };
+ this.hexEditor.setValue(this.getValue().slice(this.constants.HEX_PREFIX_POSITION));
+ },
+
+ _isEmptyRGB: function () {
+ return BI.isEmptyString(this.storeValue.r) && BI.isEmptyString(this.storeValue.g) && BI.isEmptyString(this.storeValue.b);
+ },
+
+ _checkHexEditor: function () {
+ if (BI.isEmptyString(this.hexEditor.getValue())) {
+ this.hexEditor.setValue("000000");
+ }
+ var json = BI.DOM.rgb2json(BI.DOM.hex2rgb("#" + this.hexEditor.getValue()));
+ this.storeValue = {
+ r: json.r || 0,
+ g: json.g || 0,
+ b: json.b || 0,
+ };
+ this.R.setValue(this.storeValue.r);
+ this.G.setValue(this.storeValue.g);
+ this.B.setValue(this.storeValue.b);
+ },
+
+ _showPreColor: function (color) {
+ if (color === "") {
+ this.colorShow.element.css("background-color", "").removeClass("trans-color-background").addClass("auto-color-square-normal-background");
+ } else if (color === "transparent") {
+ this.colorShow.element.css("background-color", "").removeClass("auto-color-square-normal-background").addClass("trans-color-background");
+ } else {
+ this.colorShow.element.css({"background-color": color}).removeClass("auto-color-square-normal-background").removeClass("trans-color-background");
+ }
+ },
+
+ _setEnable: function (enable) {
+ BI.ColorPickerEditor.superclass._setEnable.apply(this, arguments);
+ if (enable === true) {
+ this.element.removeClass("base-disabled disabled");
+ } else if (enable === false) {
+ this.element.addClass("base-disabled disabled");
+ }
+ },
+
+ setValue: function (color) {
+ if (color === "transparent") {
+ this.transparent.setSelected(true);
+ this.none.setSelected(false);
+ this._showPreColor("transparent");
+ this.R.setValue("");
+ this.G.setValue("");
+ this.B.setValue("");
+ this.hexEditor.setValue("");
+ this.storeValue = {
+ r: "",
+ g: "",
+ b: ""
+ };
+ return;
+ }
+ if (!color) {
+ color = "";
+ this.none.setSelected(true);
+ } else {
+ this.none.setSelected(false);
+ }
+ this.transparent.setSelected(false);
+ this._showPreColor(color);
+ var json = BI.DOM.rgb2json(BI.DOM.hex2rgb(color));
+ this.storeValue = {
+ r: BI.isNull(json.r) ? "" : json.r,
+ g: BI.isNull(json.g) ? "" : json.g,
+ b: BI.isNull(json.b) ? "" : json.b
+ };
+ this.R.setValue(this.storeValue.r);
+ this.G.setValue(this.storeValue.g);
+ this.B.setValue(this.storeValue.b);
+ this.hexEditor.setValue(color.slice(this.constants.HEX_PREFIX_POSITION));
+ },
+
+ getValue: function () {
+ if (this._isEmptyRGB() && this.transparent.isSelected()) {
+ return "transparent";
+ }
+ return BI.DOM.rgb2hex(BI.DOM.json2rgb({
+ r: this.storeValue.r,
+ g: this.storeValue.g,
+ b: this.storeValue.b
+ }));
+ }
+});
+BI.HexColorPickerEditor.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.hex_color_picker_editor", BI.HexColorPickerEditor);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/editor.colorpicker.hex.simple.js b/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/editor.colorpicker.hex.simple.js
new file mode 100644
index 0000000..9e9c33e
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/editor.colorpicker.hex.simple.js
@@ -0,0 +1,177 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/11/10
+ */
+BI.SimpleHexColorPickerEditor = BI.inherit(BI.Widget, {
+
+ constants: {
+ RGB_WIDTH: 32,
+ HEX_WIDTH: 70,
+ HEX_PREFIX_POSITION: 1
+ },
+
+ props: {
+ baseCls: "bi-color-picker-editor",
+ height: 36
+ },
+
+ render: function () {
+ var self = this, o = this.options, c = this.constants;
+
+ var RGB = BI.createItems([{text: "R"}, {text: "G"}, {text: "B"}], {
+ type: "bi.label",
+ cls: "color-picker-editor-label",
+ height: 20
+ });
+
+ var checker = function (v) {
+ return BI.isNumeric(v) && (v | 0) >= 0 && (v | 0) <= 255;
+ };
+ var Ws = BI.map(BI.range(0, 3), function () {
+ return {
+ type: "bi.small_text_editor",
+ cls: "color-picker-editor-input",
+ validationChecker: checker,
+ errorText: BI.i18nText("BI-Color_Picker_Error_Text"),
+ allowBlank: true,
+ value: 255,
+ width: c.RGB_WIDTH,
+ height: 24,
+ listeners: [{
+ eventName: BI.TextEditor.EVENT_CHANGE,
+ action: function () {
+ self._checkEditors();
+ if (self.R.isValid() && self.G.isValid() && self.B.isValid()) {
+ self.colorShow.element.css("background-color", self.getValue());
+ self.fireEvent(BI.SimpleColorPickerEditor.EVENT_CHANGE);
+ }
+ }
+ }]
+ }
+ });
+
+ return {
+ type: "bi.vertical",
+ tgap: 10,
+ items: [{
+ el: {
+ type: "bi.vertical_adapt",
+ rgap: 5,
+ columnSize: [22, 10, 'fill', 12, c.RGB_WIDTH, 12, c.RGB_WIDTH, 12, c.RGB_WIDTH],
+ items: [{
+ el: {
+ type: "bi.layout",
+ cls: "color-picker-editor-display bi-card bi-border",
+ height: 22,
+ width: 22,
+ ref: function (_ref) {
+ self.colorShow = _ref;
+ }
+ },
+ width: 18,
+ }, {
+ type: "bi.label",
+ text: "#",
+ width: 10
+ }, {
+ type: "bi.small_text_editor",
+ ref: function (_ref) {
+ self.hexEditor = _ref;
+ },
+ cls: "color-picker-editor-input",
+ validationChecker: this._hexChecker,
+ allowBlank: true,
+ errorText: BI.i18nText("BI-Color_Picker_Error_Text_Hex"),
+ width: c.HEX_WIDTH,
+ height: 24,
+ listeners: [{
+ eventName: "EVENT_CHANGE",
+ action: function () {
+ self._checkHexEditor();
+ if (checker(self.storeValue.r) && checker(self.storeValue.g) && checker(self.storeValue.b)) {
+ self.colorShow.element.css("background-color", self.getValue());
+ self.fireEvent(BI.ColorPickerEditor.EVENT_CHANGE);
+ }
+ }
+ }]
+ }, RGB[0], {
+ el: BI.extend(Ws[0], {
+ ref: function (_ref) {
+ self.R = _ref
+ }
+ }),
+ width: c.RGB_WIDTH
+ }, RGB[1], {
+ el: BI.extend(Ws[1], {
+ ref: function (_ref) {
+ self.G = _ref
+ }
+ }),
+ width: c.RGB_WIDTH
+ }, RGB[2], {
+ el: BI.extend(Ws[2], {
+ ref: function (_ref) {
+ self.B = _ref
+ }
+ }),
+ rgap: -5,
+ width: c.RGB_WIDTH
+ }]
+ }
+ }]
+
+ }
+ },
+
+ _hexChecker: function (v) {
+ return /^[0-9a-fA-F]{6}$/.test(v);
+ },
+
+ _checkEditors: function () {
+ if(BI.isEmptyString(this.R.getValue())) {
+ this.R.setValue(0);
+ }
+ if(BI.isEmptyString(this.G.getValue())) {
+ this.G.setValue(0);
+ }
+ if(BI.isEmptyString(this.B.getValue())) {
+ this.B.setValue(0);
+ }
+ this.hexEditor.setValue(this.getValue().slice(this.constants.HEX_PREFIX_POSITION));
+ },
+
+ _checkHexEditor: function () {
+ if (BI.isEmptyString(this.hexEditor.getValue())) {
+ this.hexEditor.setValue("000000");
+ }
+ var json = BI.DOM.rgb2json(BI.DOM.hex2rgb("#" + this.hexEditor.getValue()));
+ this.storeValue = {
+ r: json.r || 0,
+ g: json.g || 0,
+ b: json.b || 0,
+ };
+ this.R.setValue(this.storeValue.r);
+ this.G.setValue(this.storeValue.g);
+ this.B.setValue(this.storeValue.b);
+ },
+
+ setValue: function (color) {
+ this.colorShow.element.css({"background-color": color});
+ var json = BI.DOM.rgb2json(BI.DOM.hex2rgb(color));
+ this.R.setValue(BI.isNull(json.r) ? "" : json.r);
+ this.G.setValue(BI.isNull(json.g) ? "" : json.g);
+ this.B.setValue(BI.isNull(json.b) ? "" : json.b);
+ this.hexEditor.setValue(BI.isEmptyObject(json) ? "" : color.slice(this.constants.HEX_PREFIX_POSITION));
+ },
+
+ getValue: function () {
+ return BI.DOM.rgb2hex(BI.DOM.json2rgb({
+ r: this.R.getValue(),
+ g: this.G.getValue(),
+ b: this.B.getValue()
+ }));
+ }
+});
+BI.SimpleHexColorPickerEditor.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.simple_hex_color_picker_editor", BI.SimpleHexColorPickerEditor);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/editor.colorpicker.js b/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/editor.colorpicker.js
new file mode 100644
index 0000000..4be3375
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/editor.colorpicker.js
@@ -0,0 +1,244 @@
+/**
+ * 简单选色控件
+ *
+ * Created by GUY on 2015/11/16.
+ * @class BI.ColorPickerEditor
+ * @extends BI.Widget
+ */
+BI.ColorPickerEditor = BI.inherit(BI.Widget, {
+
+ constants: {
+ RGB_WIDTH: 32
+ },
+
+ _defaultConfig: function () {
+ return BI.extend(BI.ColorPickerEditor.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-color-picker-editor",
+ // width: 200,
+ height: 30
+ });
+ },
+
+ _init: function () {
+ BI.ColorPickerEditor.superclass._init.apply(this, arguments);
+ var self = this, o = this.options, c = this.constants;
+ this.storeValue = {};
+ this.colorShow = BI.createWidget({
+ type: "bi.layout",
+ cls: "color-picker-editor-display bi-card bi-border",
+ height: 16,
+ width: 16
+ });
+ var RGB = BI.createWidgets(BI.createItems([{text: "R"}, {text: "G"}, {text: "B"}], {
+ type: "bi.label",
+ cls: "color-picker-editor-label",
+ width: 20,
+ height: 20
+ }));
+
+ var checker = function (v) {
+ return BI.isNumeric(v) && (v | 0) >= 0 && (v | 0) <= 255;
+ };
+ var Ws = BI.createWidgets([{}, {}, {}], {
+ type: "bi.small_text_editor",
+ cls: "color-picker-editor-input",
+ validationChecker: checker,
+ errorText: BI.i18nText("BI-Color_Picker_Error_Text"),
+ allowBlank: true,
+ value: 255,
+ width: c.RGB_WIDTH,
+ height: 20
+ });
+ BI.each(Ws, function (i, w) {
+ w.on(BI.TextEditor.EVENT_CHANGE, function () {
+ self._checkEditors();
+ if (checker(self.storeValue.r) && checker(self.storeValue.g) && checker(self.storeValue.b)) {
+ self.colorShow.element.css("background-color", self.getValue());
+ self.fireEvent(BI.ColorPickerEditor.EVENT_CHANGE);
+ }
+ });
+ });
+ this.R = Ws[0];
+ this.G = Ws[1];
+ this.B = Ws[2];
+
+ this.none = BI.createWidget({
+ type: "bi.icon_button",
+ cls: "auto-color-icon",
+ width: 16,
+ height: 16,
+ iconWidth: 16,
+ iconHeight: 16,
+ title: BI.i18nText("BI-Basic_Auto")
+ });
+ this.none.on(BI.IconButton.EVENT_CHANGE, function () {
+ if (this.isSelected()) {
+ self.lastColor = self.getValue();
+ self.setValue("");
+ } else {
+ self.setValue(self.lastColor || "#ffffff");
+ }
+ if ((self.R.isValid() && self.G.isValid() && self.B.isValid()) || self._isEmptyRGB()) {
+ self.colorShow.element.css("background-color", self.getValue());
+ self.fireEvent(BI.ColorPickerEditor.EVENT_CHANGE);
+ }
+ });
+
+ this.transparent = BI.createWidget({
+ type: "bi.icon_button",
+ cls: "trans-color-icon",
+ width: 16,
+ height: 16,
+ iconWidth: 16,
+ iconHeight: 16,
+ title: BI.i18nText("BI-Transparent_Color")
+ });
+ this.transparent.on(BI.IconButton.EVENT_CHANGE, function () {
+ if (this.isSelected()) {
+ self.lastColor = self.getValue();
+ self.setValue("transparent");
+ } else {
+ if (self.lastColor === "transparent") {
+ self.lastColor = "";
+ }
+ self.setValue(self.lastColor || "#ffffff");
+ }
+ if ((self.R.isValid() && self.G.isValid() && self.B.isValid()) ||
+ self._isEmptyRGB()) {
+ self.colorShow.element.css("background-color", self.getValue());
+ self.fireEvent(BI.ColorPickerEditor.EVENT_CHANGE);
+ }
+ });
+
+ BI.createWidget({
+ type: "bi.absolute",
+ element: this,
+ items: [{
+ el: {
+ type: "bi.vertical_adapt",
+ items: [{
+ el: this.colorShow,
+ width: 16
+ }, {
+ el: RGB[0],
+ width: 20
+ }, {
+ el: this.R,
+ width: c.RGB_WIDTH
+ }, {
+ el: RGB[1],
+ width: 20
+ }, {
+ el: this.G,
+ width: c.RGB_WIDTH
+ }, {
+ el: RGB[2],
+ width: 20
+ }, {
+ el: this.B,
+ width: c.RGB_WIDTH
+ }, {
+ el: this.transparent,
+ width: 16,
+ lgap: 5
+ }, {
+ el: this.none,
+ width: 16,
+ lgap: 5
+ }]
+ },
+ left: 10,
+ right: 10,
+ top: 0,
+ bottom: 0
+ }]
+ });
+ },
+
+ _checkEditors: function () {
+ if(BI.isEmptyString(this.R.getValue())) {
+ this.R.setValue(0);
+ }
+ if(BI.isEmptyString(this.G.getValue())) {
+ this.G.setValue(0);
+ }
+ if(BI.isEmptyString(this.B.getValue())) {
+ this.B.setValue(0);
+ }
+ this.storeValue = {
+ r: this.R.getValue() || 0,
+ g: this.G.getValue() || 0,
+ b: this.B.getValue() || 0
+ };
+ },
+
+ _isEmptyRGB: function () {
+ return BI.isEmptyString(this.storeValue.r) && BI.isEmptyString(this.storeValue.g) && BI.isEmptyString(this.storeValue.b);
+ },
+
+ _showPreColor: function (color) {
+ if (color === "") {
+ this.colorShow.element.css("background-color", "").removeClass("trans-color-background").addClass("auto-color-normal-background");
+ } else if (color === "transparent") {
+ this.colorShow.element.css("background-color", "").removeClass("auto-color-normal-background").addClass("trans-color-background");
+ } else {
+ this.colorShow.element.css({"background-color": color}).removeClass("auto-color-normal-background").removeClass("trans-color-background");
+ }
+ },
+
+ _setEnable: function (enable) {
+ BI.ColorPickerEditor.superclass._setEnable.apply(this, arguments);
+ if (enable === true) {
+ this.element.removeClass("base-disabled disabled");
+ } else if (enable === false) {
+ this.element.addClass("base-disabled disabled");
+ }
+ },
+
+ setValue: function (color) {
+ if (color === "transparent") {
+ this.transparent.setSelected(true);
+ this.none.setSelected(false);
+ this._showPreColor("transparent");
+ this.R.setValue("");
+ this.G.setValue("");
+ this.B.setValue("");
+ this.storeValue = {
+ r: "",
+ g: "",
+ b: ""
+ };
+ return;
+ }
+ if (!color) {
+ color = "";
+ this.none.setSelected(true);
+ } else {
+ this.none.setSelected(false);
+ }
+ this.transparent.setSelected(false);
+ this._showPreColor(color);
+ var json = BI.DOM.rgb2json(BI.DOM.hex2rgb(color));
+ this.storeValue = {
+ r: BI.isNull(json.r) ? "" : json.r,
+ g: BI.isNull(json.g) ? "" : json.g,
+ b: BI.isNull(json.b) ? "" : json.b
+ };
+ this.R.setValue(this.storeValue.r);
+ this.G.setValue(this.storeValue.g);
+ this.B.setValue(this.storeValue.b);
+ },
+
+ getValue: function () {
+ if (this._isEmptyRGB() && this.transparent.isSelected()) {
+ return "transparent";
+ }
+ return BI.DOM.rgb2hex(BI.DOM.json2rgb({
+ r: this.storeValue.r,
+ g: this.storeValue.g,
+ b: this.storeValue.b
+ }));
+ }
+});
+BI.ColorPickerEditor.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.color_picker_editor", BI.ColorPickerEditor);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/editor.colorpicker.simple.js b/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/editor.colorpicker.simple.js
new file mode 100644
index 0000000..32943ec
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/colorchooser/colorpicker/editor.colorpicker.simple.js
@@ -0,0 +1,123 @@
+/**
+ * 简单选色控件
+ *
+ * Created by GUY on 2015/11/16.
+ * @class BI.SimpleColorPickerEditor
+ * @extends BI.Widget
+ */
+BI.SimpleColorPickerEditor = BI.inherit(BI.Widget, {
+
+ constants: {
+ RGB_WIDTH: 32
+ },
+
+ _defaultConfig: function () {
+ return BI.extend(BI.SimpleColorPickerEditor.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-color-picker-editor",
+ // width: 200,
+ height: 30
+ });
+ },
+
+ _init: function () {
+ BI.SimpleColorPickerEditor.superclass._init.apply(this, arguments);
+ var self = this, o = this.options, c = this.constants;
+ this.colorShow = BI.createWidget({
+ type: "bi.layout",
+ cls: "color-picker-editor-display bi-card bi-border",
+ height: 16,
+ width: 16
+ });
+ var RGB = BI.createWidgets(BI.createItems([{text: "R"}, {text: "G"}, {text: "B"}], {
+ type: "bi.label",
+ cls: "color-picker-editor-label",
+ width: 20,
+ height: 20
+ }));
+
+ var checker = function (v) {
+ return BI.isNumeric(v) && (v | 0) >= 0 && (v | 0) <= 255;
+ };
+ var Ws = BI.createWidgets([{}, {}, {}], {
+ type: "bi.small_text_editor",
+ cls: "color-picker-editor-input",
+ validationChecker: checker,
+ errorText: BI.i18nText("BI-Color_Picker_Error_Text"),
+ allowBlank: true,
+ value: 255,
+ width: c.RGB_WIDTH,
+ height: 20
+ });
+ BI.each(Ws, function (i, w) {
+ w.on(BI.TextEditor.EVENT_CHANGE, function () {
+ self._checkEditors();
+ if (self.R.isValid() && self.G.isValid() && self.B.isValid()) {
+ self.colorShow.element.css("background-color", self.getValue());
+ self.fireEvent(BI.SimpleColorPickerEditor.EVENT_CHANGE);
+ }
+ });
+ });
+ this.R = Ws[0];
+ this.G = Ws[1];
+ this.B = Ws[2];
+
+ BI.createWidget({
+ type: "bi.vertical_adapt",
+ element: this,
+ items: [{
+ el: this.colorShow,
+ width: 16,
+ lgap: 20,
+ rgap: 15
+ }, {
+ el: RGB[0],
+ width: 20
+ }, {
+ el: this.R,
+ width: c.RGB_WIDTH
+ }, {
+ el: RGB[1],
+ width: 20
+ }, {
+ el: this.G,
+ width: c.RGB_WIDTH
+ }, {
+ el: RGB[2],
+ width: 20
+ }, {
+ el: this.B,
+ width: c.RGB_WIDTH
+ }]
+ });
+ },
+
+ _checkEditors: function () {
+ if(BI.isEmptyString(this.R.getValue())) {
+ this.R.setValue(0);
+ }
+ if(BI.isEmptyString(this.G.getValue())) {
+ this.G.setValue(0);
+ }
+ if(BI.isEmptyString(this.B.getValue())) {
+ this.B.setValue(0);
+ }
+ },
+
+ setValue: function (color) {
+ this.colorShow.element.css({"background-color": color});
+ var json = BI.DOM.rgb2json(BI.DOM.hex2rgb(color));
+ this.R.setValue(BI.isNull(json.r) ? "" : json.r);
+ this.G.setValue(BI.isNull(json.g) ? "" : json.g);
+ this.B.setValue(BI.isNull(json.b) ? "" : json.b);
+ },
+
+ getValue: function () {
+ return BI.DOM.rgb2hex(BI.DOM.json2rgb({
+ r: this.R.getValue(),
+ g: this.G.getValue(),
+ b: this.B.getValue()
+ }));
+ }
+});
+BI.SimpleColorPickerEditor.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.simple_color_picker_editor", BI.SimpleColorPickerEditor);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/colorchooser/farbtastic/__test__/farbtastic.test.js b/src/main/resources/com/fr/fineui/case/colorchooser/farbtastic/__test__/farbtastic.test.js
new file mode 100644
index 0000000..b3703d9
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/colorchooser/farbtastic/__test__/farbtastic.test.js
@@ -0,0 +1,21 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/3/20
+ */
+describe("FarbtasticTest", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("Farbtastic", function () {
+ var a = BI.Test.createWidget({
+ type: "bi.farbtastic",
+ height: 200,
+ width: 200
+ });
+ a.setValue("#d56c6c");
+ expect(a.getValue()).to.equal("#d56c6c");
+ a.destroy();
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/colorchooser/farbtastic/farbtastic.js b/src/main/resources/com/fr/fineui/case/colorchooser/farbtastic/farbtastic.js
new file mode 100644
index 0000000..58eb563
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/colorchooser/farbtastic/farbtastic.js
@@ -0,0 +1,279 @@
+BI.Farbtastic = BI.inherit(BI.BasicButton, {
+
+ constants: {
+ RADIUS: 84,
+ SQUARE: 100,
+ WIDTH: 194
+ },
+
+ props: {
+ baseCls: "bi-farbtastic",
+ width: 195,
+ height: 195,
+ stopPropagation: true,
+ value: "#000000"
+ },
+
+ render: function () {
+ var self = this;
+ return {
+ type: "bi.absolute",
+ items: [{
+ el: {
+ type: "bi.layout",
+ cls: "",
+ ref: function (_ref) {
+ self.colorWrapper = _ref;
+ }
+ },
+ top: 47,
+ left: 47,
+ width: 101,
+ height: 101
+ }, {
+ el: {
+ type: "bi.layout",
+ cls: "wheel",
+ ref: function (_ref) {
+ self.wheel = _ref;
+ }
+ },
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0
+ }, {
+ el: {
+ type: "bi.layout",
+ cls: "overlay",
+ ref: function (_ref) {
+ self.overlay = _ref;
+ }
+ },
+ top: 47,
+ left: 47,
+ width: 101,
+ height: 101
+ }, {
+ el: {
+ type: "bi.layout",
+ cls: "marker",
+ ref: function (_ref) {
+ self.hMarker = _ref;
+ },
+ scrollable: false,
+ width: 17,
+ height: 17
+ }
+ }, {
+ el: {
+ type: "bi.layout",
+ cls: "marker",
+ ref: function (_ref) {
+ self.slMarker = _ref;
+ },
+ scrollable: false,
+ width: 17,
+ height: 17
+ }
+ }]
+ };
+ },
+
+ created: function () {
+ var o = this.options;
+ if (BI.isKey(o.value)) {
+ this.setValue(o.value);
+ }
+ },
+
+ _unpack: function (color) {
+ if (color.length === 7) {
+ return [parseInt("0x" + color.substring(1, 3)) / 255,
+ parseInt("0x" + color.substring(3, 5)) / 255,
+ parseInt("0x" + color.substring(5, 7)) / 255];
+ } else if (color.length === 4) {
+ return [parseInt("0x" + color.substring(1, 2)) / 15,
+ parseInt("0x" + color.substring(2, 3)) / 15,
+ parseInt("0x" + color.substring(3, 4)) / 15];
+ }
+ },
+
+ _pack: function (rgb) {
+ var r = Math.round(rgb[0] * 255);
+ var g = Math.round(rgb[1] * 255);
+ var b = Math.round(rgb[2] * 255);
+ return "#" + (r < 16 ? "0" : "") + r.toString(16) +
+ (g < 16 ? "0" : "") + g.toString(16) +
+ (b < 16 ? "0" : "") + b.toString(16);
+ },
+
+ _setColor: function (color) {
+ var unpack = this._unpack(color);
+ if (this.value !== color && unpack) {
+ this.value = color;
+ this.rgb = unpack;
+ this.hsl = this._RGBToHSL(this.rgb);
+ this._updateDisplay();
+ }
+ },
+
+ _setHSL: function (hsl) {
+ this.hsl = hsl;
+ this.rgb = this._HSLToRGB(hsl);
+ this.value = this._pack(this.rgb);
+ this._updateDisplay();
+ return this;
+ },
+
+ _HSLToRGB: function (hsl) {
+ var m1, m2, r, g, b;
+ var h = hsl[0], s = hsl[1], l = hsl[2];
+ m2 = (l <= 0.5) ? l * (s + 1) : l + s - l * s;
+ m1 = l * 2 - m2;
+ return [this._hueToRGB(m1, m2, h + 0.33333),
+ this._hueToRGB(m1, m2, h),
+ this._hueToRGB(m1, m2, h - 0.33333)];
+ },
+
+ _hueToRGB: function (m1, m2, h) {
+ h = (h < 0) ? h + 1 : ((h > 1) ? h - 1 : h);
+ if (h * 6 < 1) return m1 + (m2 - m1) * h * 6;
+ if (h * 2 < 1) return m2;
+ if (h * 3 < 2) return m1 + (m2 - m1) * (0.66666 - h) * 6;
+ return m1;
+ },
+
+ _RGBToHSL: function (rgb) {
+ var min, max, delta, h, s, l;
+ var r = rgb[0], g = rgb[1], b = rgb[2];
+ min = Math.min(r, Math.min(g, b));
+ max = Math.max(r, Math.max(g, b));
+ delta = max - min;
+ l = (min + max) / 2;
+ s = 0;
+ if (l > 0 && l < 1) {
+ s = delta / (l < 0.5 ? (2 * l) : (2 - 2 * l));
+ }
+ h = 0;
+ if (delta > 0) {
+ if (max == r && max != g) h += (g - b) / delta;
+ if (max == g && max != b) h += (2 + (b - r) / delta);
+ if (max == b && max != r) h += (4 + (r - g) / delta);
+ h /= 6;
+ }
+ return [h, s, l];
+ },
+
+ _updateDisplay: function () {
+ var angle = this.hsl[0] * 6.28;
+ this.hMarker.element.css({
+ left: Math.round(Math.sin(angle) * this.constants.RADIUS + this.constants.WIDTH / 2) + "px",
+ top: Math.round(-Math.cos(angle) * this.constants.RADIUS + this.constants.WIDTH / 2) + "px"
+ });
+
+ this.slMarker.element.css({
+ left: Math.round(this.constants.SQUARE * (.5 - this.hsl[1]) + this.constants.WIDTH / 2) + "px",
+ top: Math.round(this.constants.SQUARE * (.5 - this.hsl[2]) + this.constants.WIDTH / 2) + "px"
+ });
+
+ // Saturation/Luminance gradient
+ this.colorWrapper.element.css("backgroundColor", this._pack(this._HSLToRGB([this.hsl[0], 1, 0.5])));
+ },
+
+ _absolutePosition: function (el) {
+ var r = {x: el.offsetLeft, y: el.offsetTop};
+ // Resolve relative to offsetParent
+ if (el.offsetParent) {
+ var tmp = this._absolutePosition(el.offsetParent);
+ r.x += tmp.x;
+ r.y += tmp.y;
+ }
+ return r;
+ },
+
+ _widgetCoords: function (event) {
+ var x, y;
+ var el = event.target || event.srcElement;
+ var reference = this.wheel.element[0];
+
+ if (typeof event.offsetX !== "undefined") {
+ // Use offset coordinates and find common offsetParent
+ var pos = {x: event.offsetX, y: event.offsetY};
+
+ // Send the coordinates upwards through the offsetParent chain.
+ var e = el;
+ while (e) {
+ e.mouseX = pos.x;
+ e.mouseY = pos.y;
+ pos.x += e.offsetLeft;
+ pos.y += e.offsetTop;
+ e = e.offsetParent;
+ }
+
+ // Look for the coordinates starting from the wheel widget.
+ var e = reference;
+ var offset = {x: 0, y: 0};
+ while (e) {
+ if (typeof e.mouseX !== "undefined") {
+ x = e.mouseX - offset.x;
+ y = e.mouseY - offset.y;
+ break;
+ }
+ offset.x += e.offsetLeft;
+ offset.y += e.offsetTop;
+ e = e.offsetParent;
+ }
+
+ // Reset stored coordinates
+ e = el;
+ while (e) {
+ e.mouseX = undefined;
+ e.mouseY = undefined;
+ e = e.offsetParent;
+ }
+ } else {
+ // Use absolute coordinates
+ var pos = this._absolutePosition(reference);
+ x = (event.pageX || 0) - pos.x;
+ y = (event.pageY || 0) - pos.y;
+ }
+ // Subtract distance to middle
+ return {x: x - this.constants.WIDTH / 2, y: y - this.constants.WIDTH / 2};
+ },
+
+ _doMouseMove: function (event) {
+ var pos = this._widgetCoords(event);
+
+ // Set new HSL parameters
+ if (this.circleDrag) {
+ var hue = Math.atan2(pos.x, -pos.y) / 6.28;
+ if (hue < 0) hue += 1;
+ this._setHSL([hue, this.hsl[1], this.hsl[2]]);
+ } else {
+ var sat = Math.max(0, Math.min(1, -(pos.x / this.constants.SQUARE) + .5));
+ var lum = Math.max(0, Math.min(1, -(pos.y / this.constants.SQUARE) + .5));
+ this._setHSL([this.hsl[0], sat, lum]);
+ }
+ this.fireEvent(BI.Farbtastic.EVENT_CHANGE, this.getValue(), this);
+ },
+
+ doClick: function (event) {
+ var pos = this._widgetCoords(event);
+ this.circleDrag = Math.max(Math.abs(pos.x), Math.abs(pos.y)) * 2 > this.constants.SQUARE;
+
+ // Process
+ this._doMouseMove(event);
+ return false;
+ },
+
+ setValue: function (color) {
+ this._setColor(color);
+ },
+
+ getValue: function () {
+ return this.value;
+ }
+});
+BI.Farbtastic.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.farbtastic", BI.Farbtastic);
diff --git a/src/main/resources/com/fr/fineui/case/combo/bubblecombo/__test__/combo.bubble.test.js b/src/main/resources/com/fr/fineui/case/combo/bubblecombo/__test__/combo.bubble.test.js
new file mode 100644
index 0000000..f179f9a
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/bubblecombo/__test__/combo.bubble.test.js
@@ -0,0 +1,132 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/3/2
+ */
+
+describe("bubble_combo", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("测试弹出收起", function (done) {
+ var bubbleCombo = BI.Test.createWidget({
+ type: "bi.bubble_combo",
+ el: {
+ type: "bi.button",
+ text: "测试",
+ height: 24
+ },
+ popup: {
+ el: {
+ type: "bi.button_group",
+ items: BI.makeArray(100, {
+ type: "bi.text_item",
+ height: 24,
+ text: "item"
+ }),
+ layouts: [{
+ type: "bi.vertical"
+ }]
+ },
+ maxHeight: 200
+ }
+ });
+ BI.nextTick(function () {
+ bubbleCombo.element.find(".bi-button").click();
+ expect(bubbleCombo.element.find(".bi-bubble-popup-view").css("display")).to.equal("block");
+ bubbleCombo.destroy();
+ done();
+ })
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("测试弹出收起", function (done) {
+ var bubbleCombo = BI.Test.createWidget({
+ type: "bi.bubble_combo",
+ el: {
+ type: "bi.button",
+ text: "测试",
+ height: 24
+ },
+ popup: {
+ el: {
+ type: "bi.button_group",
+ items: BI.makeArray(100, {
+ type: "bi.text_item",
+ height: 24,
+ text: "item"
+ }),
+ layouts: [{
+ type: "bi.vertical"
+ }]
+ },
+ maxHeight: 200
+ }
+ });
+ BI.nextTick(function () {
+ bubbleCombo.element.find(".bi-button").click();
+ expect(bubbleCombo.element.find(".bi-bubble-popup-view").css("display")).to.equal("block");
+ bubbleCombo.destroy();
+ done();
+ })
+ });
+
+
+ /**
+ * test_author_windy
+ */
+ it("bubble_bar_popup_view", function (done) {
+ var bubbleCombo = BI.Test.createWidget({
+ type: "bi.bubble_combo",
+ el: {
+ type: "bi.button",
+ text: "测试",
+ height: 24
+ },
+ popup: {
+ type: "bi.bubble_bar_popup_view",
+ el: {
+ type: "bi.vertical",
+ height: 40
+ }
+ }
+ });
+ BI.nextTick(function () {
+ bubbleCombo.element.find(".bi-button").click();
+ expect(bubbleCombo.element.find(".bi-text:contains(确定)").length).to.equal(1);
+ bubbleCombo.destroy();
+ done();
+ })
+ });
+
+
+ /**
+ * test_author_windy
+ */
+ it("text_bubble_bar_popup_view", function (done) {
+ var bubbleCombo = BI.Test.createWidget({
+ type: "bi.bubble_combo",
+ el: {
+ type: "bi.button",
+ text: "测试",
+ height: 24
+ },
+ popup: {
+ type: "bi.text_bubble_bar_popup_view",
+ el: {
+ type: "bi.vertical",
+ height: 40
+ }
+ }
+ });
+ BI.nextTick(function () {
+ bubbleCombo.element.find(".bi-button").click();
+ expect(bubbleCombo.element.find(".bi-text:contains(确定)").length).to.equal(1);
+ bubbleCombo.destroy();
+ done();
+ })
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/combo/bubblecombo/combo.bubble.js b/src/main/resources/com/fr/fineui/case/combo/bubblecombo/combo.bubble.js
new file mode 100644
index 0000000..8a0af89
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/bubblecombo/combo.bubble.js
@@ -0,0 +1,222 @@
+/**
+ * Created by GUY on 2017/2/8.
+ *
+ * @class BI.BubbleCombo
+ * @extends BI.Widget
+ */
+BI.BubbleCombo = BI.inherit(BI.Widget, {
+ _const: {
+ TRIANGLE_LENGTH: 9
+ },
+ _defaultConfig: function () {
+ return BI.extend(BI.BubbleCombo.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-bubble-combo",
+ trigger: "click",
+ toggle: true,
+ direction: "bottom,left", // top||bottom||left||right||top,left||top,right||bottom,left||bottom,right
+ isDefaultInit: false,
+ destroyWhenHide: false,
+ hideWhenBlur: true,
+ isNeedAdjustHeight: true, // 是否需要高度调整
+ isNeedAdjustWidth: true,
+ stopPropagation: false,
+ adjustLength: 0, // 调整的距离
+ // adjustXOffset: 0,
+ // adjustYOffset: 10,
+ hideChecker: BI.emptyFn,
+ offsetStyle: "left", // left,right,center
+ el: {},
+ popup: {}
+ });
+ },
+ _init: function () {
+ BI.BubbleCombo.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.combo = BI.createWidget({
+ type: "bi.combo",
+ element: this,
+ trigger: o.trigger,
+ toggle: o.toggle,
+ logic: o.logic,
+ container: o.container,
+ direction: o.direction,
+ isDefaultInit: o.isDefaultInit,
+ hideWhenBlur: o.hideWhenBlur,
+ destroyWhenHide: o.destroyWhenHide,
+ hideWhenAnotherComboOpen: o.hideWhenAnotherComboOpen,
+ isNeedAdjustHeight: o.isNeedAdjustHeight,
+ isNeedAdjustWidth: o.isNeedAdjustWidth,
+ adjustLength: this._getAdjustLength(),
+ stopPropagation: o.stopPropagation,
+ adjustXOffset: 0,
+ adjustYOffset: 0,
+ hideChecker: o.hideChecker,
+ offsetStyle: o.offsetStyle,
+ el: o.el,
+ popup: BI.extend({
+ type: "bi.bubble_popup_view"
+ }, o.popup)
+ });
+ this.combo.on(BI.Combo.EVENT_TRIGGER_CHANGE, function () {
+ self.fireEvent(BI.BubbleCombo.EVENT_TRIGGER_CHANGE, arguments);
+ });
+ this.combo.on(BI.Combo.EVENT_CHANGE, function () {
+ self.fireEvent(BI.BubbleCombo.EVENT_CHANGE, arguments);
+ });
+ this.combo.on(BI.Combo.EVENT_EXPAND, function () {
+ self.fireEvent(BI.BubbleCombo.EVENT_EXPAND, arguments);
+ });
+ this.combo.on(BI.Combo.EVENT_COLLAPSE, function () {
+ self.fireEvent(BI.BubbleCombo.EVENT_COLLAPSE, arguments);
+ });
+ this.combo.on(BI.Combo.EVENT_AFTER_INIT, function () {
+ self.fireEvent(BI.BubbleCombo.EVENT_AFTER_INIT, arguments);
+ });
+ this.combo.on(BI.Combo.EVENT_BEFORE_POPUPVIEW, function () {
+ self.fireEvent(BI.BubbleCombo.EVENT_BEFORE_POPUPVIEW, arguments);
+ });
+ this.combo.on(BI.Combo.EVENT_AFTER_POPUPVIEW, function () {
+ self._showTriangle();
+ self.fireEvent(BI.BubbleCombo.EVENT_AFTER_POPUPVIEW, arguments);
+ });
+ this.combo.on(BI.Combo.EVENT_BEFORE_HIDEVIEW, function () {
+ self._hideTriangle();
+ self.fireEvent(BI.BubbleCombo.EVENT_BEFORE_HIDEVIEW, arguments);
+ });
+ this.combo.on(BI.Combo.EVENT_AFTER_HIDEVIEW, function () {
+ self.fireEvent(BI.BubbleCombo.EVENT_AFTER_HIDEVIEW, arguments);
+ });
+ },
+
+ _getAdjustLength: function () {
+ return this._const.TRIANGLE_LENGTH + this.options.adjustLength;
+ },
+
+ _createTriangle: function (direction) {
+ var pos = {}, op = {};
+ var adjustLength = this.options.adjustLength;
+ var offset = this.element.offset();
+ var left = offset.left, right = offset.left + this.element.outerWidth();
+ var top = offset.top, bottom = offset.top + this.element.outerHeight();
+ switch (direction) {
+ case "left":
+ pos = {
+ top: top,
+ height: this.element.outerHeight(),
+ left: left - adjustLength - this._const.TRIANGLE_LENGTH
+ };
+ op = {width: this._const.TRIANGLE_LENGTH};
+ break;
+ case "right":
+ pos = {
+ top: top,
+ height: this.element.outerHeight(),
+ left: right + adjustLength
+ };
+ op = {width: this._const.TRIANGLE_LENGTH};
+ break;
+ case "top":
+ pos = {
+ left: left,
+ width: this.element.outerWidth(),
+ top: top - adjustLength - this._const.TRIANGLE_LENGTH
+ };
+ op = {height: this._const.TRIANGLE_LENGTH};
+ break;
+ case "bottom":
+ pos = {
+ left: left,
+ width: this.element.outerWidth(),
+ top: bottom + adjustLength
+ };
+ op = {height: this._const.TRIANGLE_LENGTH};
+ break;
+ default:
+ break;
+ }
+ this.triangle && this.triangle.destroy();
+ this.triangle = BI.createWidget(op, {
+ type: "bi.center_adapt",
+ cls: "button-combo-triangle-wrapper",
+ items: [{
+ type: "bi.layout",
+ cls: "bubble-combo-triangle-" + direction
+ }]
+ });
+ pos.el = this.triangle;
+ BI.createWidget({
+ type: "bi.absolute",
+ element: this,
+ items: [pos]
+ });
+ },
+
+ _createLeftTriangle: function () {
+ this._createTriangle("left");
+ },
+
+ _createRightTriangle: function () {
+ this._createTriangle("right");
+ },
+
+ _createTopTriangle: function () {
+ this._createTriangle("top");
+ },
+
+ _createBottomTriangle: function () {
+ this._createTriangle("bottom");
+ },
+
+ _showTriangle: function () {
+ var pos = this.combo.getPopupPosition();
+ switch (pos.dir) {
+ case "left,top":
+ case "left,bottom":
+ this._createLeftTriangle();
+ break;
+ case "right,top":
+ case "right,bottom":
+ this._createRightTriangle();
+ break;
+ case "top,left":
+ case "top,right":
+ this._createTopTriangle();
+ break;
+ case "bottom,left":
+ case "bottom,right":
+ this._createBottomTriangle();
+ break;
+ }
+ },
+
+ _hideTriangle: function () {
+ this.triangle && this.triangle.destroy();
+ this.triangle = null;
+ },
+
+ hideView: function () {
+ this._hideTriangle();
+ this.combo && this.combo.hideView();
+ },
+
+ showView: function () {
+ this.combo && this.combo.showView();
+ },
+
+ isViewVisible: function () {
+ return this.combo.isViewVisible();
+ }
+});
+
+BI.BubbleCombo.EVENT_TRIGGER_CHANGE = "EVENT_TRIGGER_CHANGE";
+BI.BubbleCombo.EVENT_CHANGE = "EVENT_CHANGE";
+BI.BubbleCombo.EVENT_EXPAND = "EVENT_EXPAND";
+BI.BubbleCombo.EVENT_COLLAPSE = "EVENT_COLLAPSE";
+BI.BubbleCombo.EVENT_AFTER_INIT = "EVENT_AFTER_INIT";
+
+
+BI.BubbleCombo.EVENT_BEFORE_POPUPVIEW = "EVENT_BEFORE_POPUPVIEW";
+BI.BubbleCombo.EVENT_AFTER_POPUPVIEW = "EVENT_AFTER_POPUPVIEW";
+BI.BubbleCombo.EVENT_BEFORE_HIDEVIEW = "EVENT_BEFORE_HIDEVIEW";
+BI.BubbleCombo.EVENT_AFTER_HIDEVIEW = "EVENT_AFTER_HIDEVIEW";
+BI.shortcut("bi.bubble_combo", BI.BubbleCombo);
diff --git a/src/main/resources/com/fr/fineui/case/combo/bubblecombo/popup.bubble.js b/src/main/resources/com/fr/fineui/case/combo/bubblecombo/popup.bubble.js
new file mode 100644
index 0000000..2fa9999
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/bubblecombo/popup.bubble.js
@@ -0,0 +1,161 @@
+/**
+ * Created by GUY on 2017/2/8.
+ *
+ * @class BI.BubblePopupView
+ * @extends BI.PopupView
+ */
+BI.BubblePopupView = BI.inherit(BI.PopupView, {
+ _defaultConfig: function () {
+ var config = BI.BubblePopupView.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(config, {
+ baseCls: config.baseCls + " bi-bubble-popup-view",
+ minWidth: 220,
+ maxWidth: 300,
+ minHeight: 90
+ });
+ }
+});
+
+BI.shortcut("bi.bubble_popup_view", BI.BubblePopupView);
+
+/**
+ * Created by GUY on 2017/2/8.
+ *
+ * @class BI.BubblePopupBarView
+ * @extends BI.BubblePopupView
+ */
+BI.BubblePopupBarView = BI.inherit(BI.BubblePopupView, {
+ _defaultConfig: function () {
+ return BI.extend(BI.BubblePopupBarView.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "bi-bubble-bar-popup-view",
+ buttons: [{
+ value: false,
+ text: BI.i18nText("BI-Basic_Cancel"),
+ ghost: true
+ }, {
+ text: BI.i18nText(BI.i18nText("BI-Basic_Sure")),
+ value: true
+ }]
+ });
+ },
+ _init: function () {
+ BI.BubblePopupBarView.superclass._init.apply(this, arguments);
+ },
+ _createToolBar: function () {
+ var o = this.options, self = this;
+
+ var items = [];
+ BI.each(o.buttons, function (i, buttonOpt) {
+ if (BI.isWidget(buttonOpt)) {
+ items.push(buttonOpt);
+ } else {
+ items.push(BI.extend({
+ type: "bi.button",
+ height: 24,
+ handler: function (v) {
+ self.fireEvent(BI.BubblePopupBarView.EVENT_CLICK_TOOLBAR_BUTTON, v);
+ }
+ }, buttonOpt));
+ }
+ });
+ return BI.createWidget({
+ type: "bi.center",
+ height: 44,
+ rgap: 15,
+ items: [{
+ type: "bi.right_vertical_adapt",
+ lgap: 10,
+ items: items
+ }]
+ });
+ },
+
+ _createView: function () {
+ var o = this.options;
+
+ var button = BI.createWidget({
+ type: "bi.button_group",
+ items: [o.el],
+ layouts: [{
+ type: "bi.vertical",
+ cls: "bar-popup-container",
+ hgap: 15,
+ tgap: 10
+ }]
+ });
+
+ button.element.css("min-height", o.minHeight - 44);
+
+ return button;
+ }
+});
+BI.BubblePopupBarView.EVENT_CLICK_TOOLBAR_BUTTON = "EVENT_CLICK_TOOLBAR_BUTTON";
+BI.shortcut("bi.bubble_bar_popup_view", BI.BubblePopupBarView);
+
+/**
+ * Created by Windy on 2018/2/2.
+ *
+ * @class BI.TextBubblePopupBarView
+ * @extends BI.BubblePopupView
+ */
+BI.TextBubblePopupBarView = BI.inherit(BI.Widget, {
+
+ props: function () {
+ return {
+ baseCls: "bi-text-bubble-bar-popup-view",
+ text: "",
+ buttons: [{
+ level: "ignore",
+ value: false,
+ stopPropagation: true,
+ text: BI.i18nText("BI-Basic_Cancel")
+ }, {
+ value: true,
+ stopPropagation: true,
+ text: BI.i18nText("BI-Basic_Sure")
+ }]
+ };
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ var buttons = BI.map(o.buttons, function (index, buttonOpt) {
+ if (BI.isWidget(buttonOpt)) {
+ return buttonOpt;
+ }
+ return BI.extend({
+ type: "bi.button",
+ height: 24,
+ handler: function (v) {
+ self.fireEvent(BI.TextBubblePopupBarView.EVENT_CHANGE, v);
+ }
+ }, buttonOpt);
+
+ });
+ return {
+ type: "bi.bubble_bar_popup_view",
+ minWidth: o.minWidth,
+ maxWidth: o.maxWidth,
+ minHeight: o.minHeight,
+ ref: function () {
+ self.popup = this;
+ },
+ el: {
+ type: "bi.label",
+ text: o.text,
+ whiteSpace: "normal",
+ textAlign: "left",
+ ref: function () {
+ self.text = this;
+ }
+ },
+ buttons: buttons
+ };
+ },
+
+ populate: function (v) {
+ this.text.setText(v || this.options.text);
+ }
+});
+BI.TextBubblePopupBarView.EVENT_CHANGE = "EVENT_CLICK_TOOLBAR_BUTTON";
+BI.shortcut("bi.text_bubble_bar_popup_view", BI.TextBubblePopupBarView);
diff --git a/src/main/resources/com/fr/fineui/case/combo/editoriconcheckcombo/__test__/combo.editiconcheck.test.js b/src/main/resources/com/fr/fineui/case/combo/editoriconcheckcombo/__test__/combo.editiconcheck.test.js
new file mode 100644
index 0000000..72a0b9a
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/editoriconcheckcombo/__test__/combo.editiconcheck.test.js
@@ -0,0 +1,50 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/3/2
+ */
+
+describe("edit_icon_check_combo", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("测试弹出收起", function (done) {
+ var combo = BI.Test.createWidget({
+ type: "bi.editor_icon_check_combo",
+ watermark: "默认值",
+ width: 200,
+ height: 24,
+ value: 2,
+ items: [{
+ text: "MVC-1",
+ value: "1"
+ }, {
+ text: "MVC-2",
+ value: "2"
+ }, {
+ text: "MVC-3",
+ value: "3"
+ }]
+ });
+ BI.nextTick(function () {
+ combo.element.find(".bi-editor-trigger").click();
+ combo.element.find(".bi-text-icon-popup .bi-single-select-item").click();
+ expect(combo.getValue()[0]).to.equal("3");
+ combo.populate([{
+ text: "MVC-1",
+ value: "4"
+ }, {
+ text: "MVC-2",
+ value: "5"
+ }, {
+ text: "MVC-3",
+ value: "6"
+ }]);
+ combo.setValue("4");
+ expect(combo.getValue()[0]).to.equal("4");
+ combo.destroy();
+ done();
+ })
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/combo/editoriconcheckcombo/combo.editiconcheck.js b/src/main/resources/com/fr/fineui/case/combo/editoriconcheckcombo/combo.editiconcheck.js
new file mode 100644
index 0000000..28c57a2
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/editoriconcheckcombo/combo.editiconcheck.js
@@ -0,0 +1,96 @@
+/**
+ * Created by Young's on 2016/4/28.
+ */
+BI.EditorIconCheckCombo = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ return BI.extend(BI.EditorIconCheckCombo.superclass._defaultConfig.apply(this, arguments), {
+ baseClass: "bi-check-editor-combo",
+ width: 100,
+ height: 24,
+ chooseType: BI.ButtonGroup.CHOOSE_TYPE_SINGLE,
+ validationChecker: BI.emptyFn,
+ quitChecker: BI.emptyFn,
+ allowBlank: true,
+ watermark: "",
+ errorText: ""
+ });
+ },
+
+ _init: function () {
+ BI.EditorIconCheckCombo.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.trigger = BI.createWidget({
+ type: "bi.editor_trigger",
+ items: o.items,
+ height: o.height,
+ validationChecker: o.validationChecker,
+ quitChecker: o.quitChecker,
+ allowBlank: o.allowBlank,
+ watermark: o.watermark,
+ errorText: o.errorText,
+ value: o.value
+ });
+ this.trigger.on(BI.EditorTrigger.EVENT_CHANGE, function () {
+ self.popup.setValue(this.getValue());
+ self.fireEvent(BI.EditorIconCheckCombo.EVENT_CHANGE, arguments);
+ });
+ this.trigger.on(BI.EditorTrigger.EVENT_FOCUS, function () {
+ self.fireEvent(BI.EditorIconCheckCombo.EVENT_FOCUS, arguments);
+ });
+ this.trigger.on(BI.EditorTrigger.EVENT_EMPTY, function () {
+ self.fireEvent(BI.EditorIconCheckCombo.EVENT_EMPTY, arguments);
+ });
+ this.trigger.on(BI.EditorTrigger.EVENT_VALID, function () {
+ self.fireEvent(BI.EditorIconCheckCombo.EVENT_VALID, arguments);
+ });
+ this.trigger.on(BI.EditorTrigger.EVENT_ERROR, function () {
+ self.fireEvent(BI.EditorIconCheckCombo.EVENT_ERROR, arguments);
+ });
+
+ this.popup = BI.createWidget({
+ type: "bi.text_value_check_combo_popup",
+ chooseType: o.chooseType,
+ items: o.items,
+ value: o.value
+ });
+ this.popup.on(BI.TextValueCheckComboPopup.EVENT_CHANGE, function () {
+ self.setValue(self.popup.getValue());
+ self.editorIconCheckCombo.hideView();
+ self.fireEvent(BI.EditorIconCheckCombo.EVENT_CHANGE);
+ });
+ this.popup.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ this.editorIconCheckCombo = BI.createWidget({
+ type: "bi.combo",
+ container: o.container,
+ direction: o.direction,
+ element: this,
+ adjustLength: 2,
+ el: this.trigger,
+ popup: {
+ el: this.popup,
+ maxHeight: 300
+ }
+ });
+ },
+
+ setValue: function (v) {
+ this.editorIconCheckCombo.setValue(v);
+ },
+
+ getValue: function () {
+ return this.trigger.getValue();
+ },
+
+ populate: function (items) {
+ this.options.items = items;
+ this.editorIconCheckCombo.populate(items);
+ }
+});
+BI.EditorIconCheckCombo.EVENT_CHANGE = "EVENT_CHANGE";
+BI.EditorIconCheckCombo.EVENT_FOCUS = "EVENT_FOCUS";
+BI.EditorIconCheckCombo.EVENT_EMPTY = "EVENT_EMPTY";
+BI.EditorIconCheckCombo.EVENT_VALID = "EVENT_VALID";
+BI.EditorIconCheckCombo.EVENT_ERROR = "EVENT_ERROR";
+BI.shortcut("bi.editor_icon_check_combo", BI.EditorIconCheckCombo);
diff --git a/src/main/resources/com/fr/fineui/case/combo/iconcombo/combo.icon.js b/src/main/resources/com/fr/fineui/case/combo/iconcombo/combo.icon.js
new file mode 100644
index 0000000..409e1a0
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/iconcombo/combo.icon.js
@@ -0,0 +1,99 @@
+/**
+ * Created by GUY on 2016/2/2.
+ *
+ * @class BI.IconCombo
+ * @extend BI.Widget
+ */
+BI.IconCombo = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ return BI.extend(BI.IconCombo.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-icon-combo",
+ width: 24,
+ height: 24,
+ el: {},
+ popup: {},
+ minWidth: 100,
+ maxWidth: "auto",
+ maxHeight: 300,
+ direction: "bottom",
+ adjustLength: 3, // 调整的距离
+ adjustXOffset: 0,
+ adjustYOffset: 0,
+ offsetStyle: "left",
+ chooseType: BI.ButtonGroup.CHOOSE_TYPE_SINGLE
+ });
+ },
+
+ _init: function () {
+ BI.IconCombo.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.trigger = BI.createWidget(o.el, {
+ type: "bi.icon_combo_trigger",
+ iconCls: o.iconCls,
+ title: o.title,
+ items: o.items,
+ width: o.width,
+ height: o.height,
+ iconWidth: o.iconWidth,
+ iconHeight: o.iconHeight,
+ value: o.value
+ });
+ this.popup = BI.createWidget(o.popup, {
+ type: "bi.icon_combo_popup",
+ chooseType: o.chooseType,
+ items: o.items,
+ value: o.value
+ });
+ this.popup.on(BI.IconComboPopup.EVENT_CHANGE, function () {
+ self.setValue(self.popup.getValue());
+ self.iconCombo.hideView();
+ self.fireEvent(BI.IconCombo.EVENT_CHANGE);
+ });
+ this.popup.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ this.iconCombo = BI.createWidget({
+ type: "bi.combo",
+ element: this,
+ direction: o.direction,
+ trigger: o.trigger,
+ container: o.container,
+ adjustLength: o.adjustLength,
+ adjustXOffset: o.adjustXOffset,
+ adjustYOffset: o.adjustYOffset,
+ offsetStyle: o.offsetStyle,
+ el: this.trigger,
+ popup: {
+ el: this.popup,
+ maxWidth: o.maxWidth,
+ maxHeight: o.maxHeight,
+ minWidth: o.minWidth
+ }
+ });
+ },
+
+ showView: function () {
+ this.iconCombo.showView();
+ },
+
+ hideView: function () {
+ this.iconCombo.hideView();
+ },
+
+ setValue: function (v) {
+ this.trigger.setValue(v);
+ this.popup.setValue(v);
+ },
+
+ getValue: function () {
+ var value = this.popup.getValue();
+ return BI.isNull(value) ? [] : (BI.isArray(value) ? value : [value]);
+ },
+
+ populate: function (items) {
+ this.options.items = items;
+ this.iconCombo.populate(items);
+ }
+});
+BI.IconCombo.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.icon_combo", BI.IconCombo);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/combo/iconcombo/popup.iconcombo.js b/src/main/resources/com/fr/fineui/case/combo/iconcombo/popup.iconcombo.js
new file mode 100644
index 0000000..0c6067b
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/iconcombo/popup.iconcombo.js
@@ -0,0 +1,65 @@
+/**
+ * Created by GUY on 2016/2/2.
+ *
+ * @class BI.IconComboPopup
+ * @extend BI.Pane
+ */
+BI.IconComboPopup = BI.inherit(BI.Pane, {
+ _defaultConfig: function () {
+ return BI.extend(BI.IconComboPopup.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi.icon-combo-popup",
+ chooseType: BI.ButtonGroup.CHOOSE_TYPE_SINGLE
+ });
+ },
+
+ _init: function () {
+ BI.IconComboPopup.superclass._init.apply(this, arguments);
+ var o = this.options, self = this;
+ this.popup = BI.createWidget({
+ type: "bi.button_group",
+ items: BI.createItems(o.items, {
+ type: "bi.single_select_icon_text_item",
+ height: BI.SIZE_CONSANTS.LIST_ITEM_HEIGHT,
+ }),
+ chooseType: o.chooseType,
+ layouts: [{
+ type: "bi.vertical"
+ }],
+ value: o.value
+ });
+
+ this.popup.on(BI.Controller.EVENT_CHANGE, function (type, val, obj) {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ if (type === BI.Events.CLICK) {
+ self.fireEvent(BI.IconComboPopup.EVENT_CHANGE, val, obj);
+ }
+ });
+
+ BI.createWidget({
+ type: "bi.vertical",
+ element: this,
+ vgap: 5,
+ items: [this.popup]
+ });
+ },
+
+ populate: function (items) {
+ BI.IconComboPopup.superclass.populate.apply(this, arguments);
+ items = BI.createItems(items, {
+ type: "bi.single_select_icon_text_item",
+ height: BI.SIZE_CONSANTS.LIST_ITEM_HEIGHT,
+ });
+ this.popup.populate(items);
+ },
+
+ getValue: function () {
+ return this.popup.getValue();
+ },
+
+ setValue: function (v) {
+ this.popup.setValue(v);
+ }
+
+});
+BI.IconComboPopup.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.icon_combo_popup", BI.IconComboPopup);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/combo/iconcombo/trigger.iconcombo.js b/src/main/resources/com/fr/fineui/case/combo/iconcombo/trigger.iconcombo.js
new file mode 100644
index 0000000..2378465
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/iconcombo/trigger.iconcombo.js
@@ -0,0 +1,102 @@
+/**
+ * Created by GUY on 2016/2/2.
+ *
+ * @class BI.IconComboTrigger
+ * @extend BI.Widget
+ */
+BI.IconComboTrigger = BI.inherit(BI.Trigger, {
+ _defaultConfig: function () {
+ return BI.extend(BI.IconComboTrigger.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "bi-icon-combo-trigger",
+ el: {},
+ items: [],
+ iconCls: "",
+ width: 24,
+ height: 24,
+ isShowDown: true,
+ value: ""
+ });
+ },
+
+ _init: function () {
+ BI.IconComboTrigger.superclass._init.apply(this, arguments);
+ var o = this.options, self = this;
+ var iconCls = "";
+ if(BI.isKey(o.value)){
+ iconCls = this._digest(o.value, o.items);
+ }
+ this.button = BI.createWidget(o.el, {
+ type: "bi.icon_change_button",
+ cls: "icon-combo-trigger-icon",
+ iconCls: iconCls,
+ disableSelected: true,
+ width: o.isShowDown ? o.width - 12 : o.width,
+ height: o.height,
+ iconWidth: o.iconWidth,
+ iconHeight: o.iconHeight,
+ selected: BI.isNotEmptyString(iconCls)
+ });
+ this.down = BI.createWidget({
+ type: "bi.icon_button",
+ disableSelected: true,
+ cls: "icon-combo-down-icon trigger-triangle-font font-size-12",
+ width: 12,
+ height: 8,
+ selected: BI.isNotEmptyString(iconCls)
+ });
+ this.down.setVisible(o.isShowDown);
+ BI.createWidget({
+ type: "bi.absolute",
+ element: this,
+ items: [{
+ el: this.button,
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0
+ }, {
+ el: this.down,
+ right: 3,
+ bottom: 0
+ }]
+ });
+ },
+
+ _digest: function (v, items) {
+ var iconCls = "";
+ v = BI.isArray(v) ? v[0] : v;
+ BI.any(items, function (i, item) {
+ if (v === item.value) {
+ iconCls = item.iconCls;
+ return true;
+ }
+ });
+ return iconCls;
+ },
+
+ populate: function (items) {
+ var o = this.options;
+ this.options.items = items || [];
+ this.button.setIcon(o.iconCls);
+ this.button.setSelected(false);
+ this.down.setSelected(false);
+ },
+
+ setValue: function (v) {
+ BI.IconComboTrigger.superclass.setValue.apply(this, arguments);
+ var o = this.options;
+ var iconCls = this._digest(v, this.options.items);
+ v = BI.isArray(v) ? v[0] : v;
+ if (BI.isNotEmptyString(iconCls)) {
+ this.button.setIcon(iconCls);
+ this.button.setSelected(true);
+ this.down.setSelected(true);
+ } else {
+ this.button.setIcon(o.iconCls);
+ this.button.setSelected(false);
+ this.down.setSelected(false);
+ }
+ }
+});
+BI.IconComboTrigger.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.icon_combo_trigger", BI.IconComboTrigger);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/combo/icontextvaluecombo/__test__/combo.icontextvalue.test.js b/src/main/resources/com/fr/fineui/case/combo/icontextvaluecombo/__test__/combo.icontextvalue.test.js
new file mode 100644
index 0000000..e3450ae
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/icontextvaluecombo/__test__/combo.icontextvalue.test.js
@@ -0,0 +1,65 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/3/2
+ */
+
+describe("icontextvaluecombo", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("测试setValue", function () {
+ var combo = BI.Test.createWidget({
+ type: "bi.icon_text_value_combo",
+ text: "默认值",
+ value: 22,
+ width: 300,
+ items: [{
+ text: "MVC-1",
+ iconCls: "date-font",
+ value: 1
+ }, {
+ text: "MVC-2",
+ iconCls: "search-font",
+ value: 2
+ }, {
+ text: "MVC-3",
+ iconCls: "pull-right-font",
+ value: 3
+ }]
+ });
+ combo.setValue(2);
+ expect(combo.getValue()[0]).to.equal(2);
+ combo.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("测试populate", function () {
+ var combo = BI.Test.createWidget({
+ type: "bi.text_value_combo",
+ text: "默认值",
+ value: 22,
+ width: 300
+ });
+ combo.populate([{
+ text: "MVC-1",
+ iconCls: "date-font",
+ value: 1
+ }, {
+ text: "MVC-2",
+ iconCls: "search-font",
+ value: 2
+ }, {
+ text: "MVC-3",
+ iconCls: "pull-right-font",
+ value: 3
+ }]);
+ combo.setValue(2);
+ expect(combo.getValue()[0]).to.equal(2);
+ combo.destroy();
+ });
+});
+
diff --git a/src/main/resources/com/fr/fineui/case/combo/icontextvaluecombo/combo.icontextvalue.js b/src/main/resources/com/fr/fineui/case/combo/icontextvaluecombo/combo.icontextvalue.js
new file mode 100644
index 0000000..1181387
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/icontextvaluecombo/combo.icontextvalue.js
@@ -0,0 +1,105 @@
+/**
+ * Created by Windy on 2017/12/12.
+ * combo : icon + text + icon, popup : icon + text
+ */
+BI.IconTextValueCombo = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ return BI.extend(BI.IconTextValueCombo.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-icon-text-value-combo bi-border bi-border-radius",
+ height: 24,
+ iconHeight: null,
+ iconWidth: null,
+ value: "",
+ });
+ },
+
+ _init: function () {
+ var self = this, o = this.options;
+ o.height -= 2;
+ BI.isNumeric(o.width) && (o.width -= 2);
+ BI.IconTextValueCombo.superclass._init.apply(this, arguments);
+ this.trigger = BI.createWidget({
+ type: "bi.select_icon_text_trigger",
+ cls: "icon-text-value-trigger",
+ items: o.items,
+ height: o.height,
+ text: o.text,
+ iconCls: o.iconCls,
+ value: o.value,
+ iconHeight: o.iconHeight,
+ iconWidth: o.iconWidth,
+ iconWrapperWidth: o.iconWrapperWidth,
+ title: o.title,
+ warningTitle: o.warningTitle
+ });
+ this.popup = BI.createWidget({
+ type: "bi.icon_text_value_combo_popup",
+ items: o.items,
+ value: o.value,
+ iconHeight: o.iconHeight,
+ iconWidth: o.iconWidth,
+ iconWrapperWidth: o.iconWrapperWidth
+ });
+ this.popup.on(BI.IconTextValueComboPopup.EVENT_CHANGE, function () {
+ self.setValue(self.popup.getValue());
+ self.textIconCombo.hideView();
+ self.fireEvent(BI.IconTextValueCombo.EVENT_CHANGE, arguments);
+ });
+ this.popup.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ this.textIconCombo = BI.createWidget({
+ type: "bi.combo",
+ element: this,
+ container: o.container,
+ direction: o.direction,
+ adjustLength: 2,
+ el: this.trigger,
+ popup: {
+ el: this.popup,
+ maxHeight: 240,
+ minHeight: 25
+ }
+ });
+ if (BI.isKey(o.value)) {
+ this.setValue(o.value);
+ }
+ },
+
+ _checkError: function (v) {
+ if(BI.isNull(v) || BI.isEmptyArray(v) || BI.isEmptyString(v)) {
+ this.trigger.options.tipType = "success";
+ this.element.removeClass("combo-error");
+ } else {
+ v = BI.isArray(v) ? v : [v];
+ var result = BI.find(this.options.items, function (idx, item) {
+ return BI.contains(v, item.value);
+ });
+ if (BI.isNull(result)) {
+ this.trigger.options.tipType = "warning";
+ this.element.removeClass("combo-error").addClass("combo-error");
+ } else {
+ this.trigger.options.tipType = "success";
+ this.element.removeClass("combo-error");
+ }
+ }
+ },
+
+ setValue: function (v) {
+ this.trigger.setValue(v);
+ this.popup.setValue(v);
+ this._checkError(v);
+ },
+
+ getValue: function () {
+ var value = this.popup.getValue();
+ return BI.isNull(value) ? [] : (BI.isArray(value) ? value : [value]);
+ },
+
+ populate: function (items) {
+ this.options.items = items;
+ this.textIconCombo.populate(items);
+ }
+});
+BI.IconTextValueCombo.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.icon_text_value_combo", BI.IconTextValueCombo);
diff --git a/src/main/resources/com/fr/fineui/case/combo/icontextvaluecombo/popup.icontextvalue.js b/src/main/resources/com/fr/fineui/case/combo/icontextvaluecombo/popup.icontextvalue.js
new file mode 100644
index 0000000..993bceb
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/icontextvaluecombo/popup.icontextvalue.js
@@ -0,0 +1,76 @@
+/**
+ * Created by Windy on 2017/12/12.
+ */
+BI.IconTextValueComboPopup = BI.inherit(BI.Pane, {
+ _defaultConfig: function () {
+ return BI.extend(BI.IconTextValueComboPopup.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-icon-text-icon-popup",
+ behaviors: {
+ redmark: function () {
+ return true;
+ }
+ }
+ });
+ },
+
+ _init: function () {
+ BI.IconTextValueComboPopup.superclass._init.apply(this, arguments);
+ var o = this.options, self = this;
+ this.popup = BI.createWidget({
+ type: "bi.button_group",
+ items: BI.createItems(o.items, {
+ type: "bi.single_select_icon_text_item",
+ height: BI.SIZE_CONSANTS.LIST_ITEM_HEIGHT,
+ iconHeight: o.iconHeight,
+ iconWidth: o.iconWidth,
+ iconWrapperWidth: o.iconWrapperWidth
+ }),
+ chooseType: BI.ButtonGroup.CHOOSE_TYPE_SINGLE,
+ layouts: [{
+ type: "bi.vertical"
+ }],
+ behaviors: o.behaviors,
+ value: o.value
+ });
+
+ this.popup.on(BI.Controller.EVENT_CHANGE, function (type, val, obj) {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ if (type === BI.Events.CLICK) {
+ self.fireEvent(BI.IconTextValueComboPopup.EVENT_CHANGE, val, obj);
+ }
+ });
+
+ this.check();
+
+ BI.createWidget({
+ type: "bi.vertical",
+ element: this,
+ vgap: 5,
+ items: [this.popup]
+ });
+ },
+
+ populate: function (items, keyword) {
+ BI.IconTextValueComboPopup.superclass.populate.apply(this, arguments);
+ var o = this.options;
+ items = BI.createItems(items, {
+ type: "bi.single_select_icon_text_item",
+ height: BI.SIZE_CONSANTS.LIST_ITEM_HEIGHT,
+ iconWrapperWidth: o.iconWrapperWidth,
+ iconHeight: o.iconHeight,
+ iconWidth: o.iconWidth,
+ });
+ this.popup.populate(items, keyword);
+ },
+
+ getValue: function () {
+ return this.popup.getValue();
+ },
+
+ setValue: function (v) {
+ this.popup.setValue(v);
+ }
+
+});
+BI.IconTextValueComboPopup.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.icon_text_value_combo_popup", BI.IconTextValueComboPopup);
diff --git a/src/main/resources/com/fr/fineui/case/combo/searchtextvaluecombo/__test__/combo.searchtextvaluecombo.test.js b/src/main/resources/com/fr/fineui/case/combo/searchtextvaluecombo/__test__/combo.searchtextvaluecombo.test.js
new file mode 100644
index 0000000..0b66b13
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/searchtextvaluecombo/__test__/combo.searchtextvaluecombo.test.js
@@ -0,0 +1,93 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/3/3
+ */
+
+describe("search_text_value_combo", function () {
+
+ var items;
+ before(function () {
+ items = BI.map(BI.makeArray(100, null), function(idx, v) {
+ return {
+ text: idx,
+ value: idx,
+ title: idx
+ };
+ });
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("setValue", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.search_text_value_combo",
+ width: 220,
+ height: 24,
+ items: items
+ });
+ widget.setValue(2);
+ expect(widget.getValue()[0]).to.equal(2);
+ widget.destroy();
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("getValue", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.search_text_value_combo",
+ width: 220,
+ items: items,
+ value: 2
+ });
+ expect(widget.getValue()[0]).to.equal(2);
+ widget.destroy();
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("点选选值", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.search_text_value_combo",
+ width: 220,
+ items: items
+ });
+ widget.element.find(".bi-search-text-value-trigger").click();
+ // 为什么要delay 300呢,因为按钮有debounce
+ BI.delay(function () {
+ widget.element.find(".bi-single-select-item:contains(10)").click();
+ expect(widget.getValue()[0]).to.equal(10);
+ widget.destroy();
+ done();
+ }, 300);
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("搜索选值", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.search_text_value_combo",
+ width: 220,
+ items: items
+ });
+ BI.nextTick(function () {
+ widget.element.find(".bi-search-text-value-trigger .tip-text-style").click();
+ // 这边为啥要加呢,因为input的setValue中有nextTick
+ BI.nextTick(function () {
+ BI.Test.triggerKeyDown(widget.element.find(".bi-search-text-value-trigger .bi-input"), "2", 50, function () {
+ BI.nextTick(function () {
+ widget.element.find(".bi-search-text-value-popup .bi-single-select-item")[0].click();
+ expect(widget.getValue()[0]).to.deep.equal(2);
+ widget.destroy();
+ done();
+ });
+ });
+ });
+ });
+ });
+});
+
diff --git a/src/main/resources/com/fr/fineui/case/combo/searchtextvaluecombo/combo.searchtextvalue.js b/src/main/resources/com/fr/fineui/case/combo/searchtextvaluecombo/combo.searchtextvalue.js
new file mode 100644
index 0000000..05553d0
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/searchtextvaluecombo/combo.searchtextvalue.js
@@ -0,0 +1,163 @@
+/**
+ * Created by Windy on 2018/2/2.
+ */
+BI.SearchTextValueCombo = BI.inherit(BI.Widget, {
+
+ props: {
+ baseCls: "bi-search-text-value-combo",
+ height: 24,
+ text: "",
+ defaultText: "",
+ items: [],
+ tipType: "",
+ warningTitle: "",
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ return {
+ type: "bi.absolute",
+ items: [{
+ el: {
+ type: "bi.combo",
+ cls: "bi-border bi-focus-shadow",
+ container: o.container,
+ adjustLength: 2,
+ toggle: false,
+ ref: function () {
+ self.combo = this;
+ },
+ el: {
+ type: "bi.search_text_value_trigger",
+ cls: "search-text-value-trigger",
+ watermark: o.watermark,
+ ref: function () {
+ self.trigger = this;
+ },
+ items: o.items,
+ height: o.height - 2,
+ text: o.text,
+ defaultText: o.defaultText,
+ value: o.value,
+ tipType: o.tipType,
+ warningTitle: o.warningTitle,
+ title: o.title,
+ listeners: [{
+ eventName: BI.SearchTextValueTrigger.EVENT_CHANGE,
+ action: function () {
+ self.setValue(this.getValue());
+ self.combo.hideView();
+ self.fireEvent(BI.SearchTextValueCombo.EVENT_CHANGE);
+ }
+ }]
+ },
+ popup: {
+ el: {
+ type: "bi.text_value_combo_popup",
+ chooseType: BI.ButtonGroup.CHOOSE_TYPE_SINGLE,
+ value: o.value,
+ items: o.items,
+ ref: function () {
+ self.popup = this;
+ self.trigger.getSearcher().setAdapter(self.popup);
+ },
+ listeners: [{
+ eventName: BI.TextValueComboPopup.EVENT_CHANGE,
+ action: function () {
+ self.setValue(this.getValue());
+ self.combo.hideView();
+ self.fireEvent(BI.SearchTextValueCombo.EVENT_CHANGE);
+ }
+ }]
+ },
+ value: o.value,
+ maxHeight: 252,
+ minHeight: 25
+ },
+ listeners: [{
+ eventName: BI.Combo.EVENT_AFTER_HIDEVIEW,
+ action: function () {
+ self.trigger.stopEditing();
+ }
+ }, {
+ eventName: BI.Combo.EVENT_BEFORE_POPUPVIEW,
+ action: function () {
+ self.fireEvent(BI.SearchTextValueCombo.EVENT_BEFORE_POPUPVIEW);
+ }
+ }],
+ hideChecker: function (e) {
+ return self.triggerBtn.element.find(e.target).length === 0;
+ }
+ },
+ left: 0,
+ right: 0,
+ bottom: 0,
+ top: 0
+ }, {
+ el: {
+ type: "bi.trigger_icon_button",
+ cls: "trigger-icon-button",
+ ref: function () {
+ self.triggerBtn = this;
+ },
+ width: o.height,
+ height: o.height,
+ handler: function () {
+ if (self.combo.isViewVisible()) {
+ self.combo.hideView();
+ } else {
+ self.combo.showView();
+ }
+ }
+ },
+ right: 0,
+ bottom: 0,
+ top: 0
+ }]
+ };
+ },
+
+ created: function () {
+ var o = this.options;
+ if (BI.isKey(o.value)) {
+ this._checkError(o.value);
+ }
+ },
+
+ _checkError: function (v) {
+ if (BI.isNull(v) || BI.isEmptyArray(v) || BI.isEmptyString(v)) {
+ this.trigger.options.tipType = "success";
+ this.element.removeClass("combo-error");
+ } else {
+ v = BI.isArray(v) ? v : [v];
+ var result = BI.find(this.options.items, function (idx, item) {
+ return BI.contains(v, item.value);
+ });
+ if (BI.isNull(result)) {
+ this.element.removeClass("combo-error").addClass("combo-error");
+ this.trigger.attr("tipType", "warning");
+ } else {
+ this.element.removeClass("combo-error");
+ this.trigger.attr("tipType", "success");
+ }
+ }
+ },
+
+ populate: function (items) {
+ this.options.items = items;
+ this.combo.populate(items);
+ },
+
+ setValue: function (v) {
+ this.combo.setValue(v);
+ this._checkError(v);
+ },
+
+ getValue: function () {
+ var value = this.combo.getValue();
+ return BI.isNull(value) ? [] : (BI.isArray(value) ? value : [value]);
+ }
+});
+BI.SearchTextValueCombo.EVENT_CHANGE = "EVENT_CHANGE";
+BI.SearchTextValueCombo.EVENT_BEFORE_POPUPVIEW = "EVENT_BEFORE_POPUPVIEW";
+BI.shortcut("bi.search_text_value_combo", BI.SearchTextValueCombo);
diff --git a/src/main/resources/com/fr/fineui/case/combo/searchtextvaluecombo/popup.searchtextvalue.js b/src/main/resources/com/fr/fineui/case/combo/searchtextvaluecombo/popup.searchtextvalue.js
new file mode 100644
index 0000000..0118cf8
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/searchtextvaluecombo/popup.searchtextvalue.js
@@ -0,0 +1,73 @@
+/**
+ * Created by Windy on 2018/2/5.
+ */
+BI.SearchTextValueComboPopup = BI.inherit(BI.Pane, {
+
+ props: {
+ baseCls: "bi-search-text-value-popup"
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ return {
+ type: "bi.vertical",
+ vgap: 5,
+ items: [{
+ type: "bi.button_group",
+ ref: function () {
+ self.popup = this;
+ },
+ items: BI.createItems(o.items, {
+ type: "bi.single_select_item",
+ textAlign: o.textAlign,
+ height: 24
+ }),
+ chooseType: BI.ButtonGroup.CHOOSE_TYPE_SINGLE,
+ layouts: [{
+ type: "bi.vertical"
+ }],
+ behaviors: {
+ redmark: function () {
+ return true;
+ }
+ },
+ value: o.value,
+ listeners: [{
+ eventName: BI.Controller.EVENT_CHANGE,
+ action: function (type, val, obj) {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ if (type === BI.Events.CLICK) {
+ self.fireEvent(BI.SearchTextValueComboPopup.EVENT_CHANGE, val, obj);
+ }
+ }
+ }]
+ }]
+ };
+ },
+
+ // mounted之后做check
+ mounted: function() {
+ this.check();
+ },
+
+ populate: function (find, match, keyword) {
+ var items = BI.concat(find, match);
+ BI.SearchTextValueComboPopup.superclass.populate.apply(this, items);
+ items = BI.createItems(items, {
+ type: "bi.single_select_item",
+ height: 24
+ });
+ this.popup.populate(items, keyword);
+ },
+
+ getValue: function () {
+ return this.popup.getValue();
+ },
+
+ setValue: function (v) {
+ this.popup.setValue(v);
+ }
+
+});
+BI.SearchTextValueComboPopup.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.search_text_value_combo_popup", BI.SearchTextValueComboPopup);
diff --git a/src/main/resources/com/fr/fineui/case/combo/searchtextvaluecombo/trigger.searchtextvalue.js b/src/main/resources/com/fr/fineui/case/combo/searchtextvaluecombo/trigger.searchtextvalue.js
new file mode 100644
index 0000000..7e84a44
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/searchtextvaluecombo/trigger.searchtextvalue.js
@@ -0,0 +1,114 @@
+/**
+ * Created by Windy on 2018/2/2.
+ */
+BI.SearchTextValueTrigger = BI.inherit(BI.Trigger, {
+
+ props: function () {
+ return {
+ extraCls: "bi-search-text-value-trigger",
+ height: 24,
+ watermark: BI.i18nText("BI-Basic_Search")
+ };
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ return {
+ type: "bi.htape",
+ items: [
+ {
+ el: {
+ type: "bi.searcher",
+ ref: function () {
+ self.searcher = this;
+ },
+ isAutoSearch: false,
+ el: {
+ type: "bi.state_editor",
+ ref: function () {
+ self.editor = this;
+ },
+ watermark: o.watermark,
+ defaultText: o.defaultText,
+ text: this._digest(o.value, o.items),
+ value: o.value,
+ height: o.height,
+ tipText: ""
+ },
+ popup: {
+ type: "bi.search_text_value_combo_popup",
+ cls: "bi-card",
+ chooseType: BI.ButtonGroup.CHOOSE_TYPE_SINGLE,
+ tipText: BI.i18nText("BI-No_Select"),
+ },
+ onSearch: function (obj, callback) {
+ var keyword = obj.keyword;
+ var finding = BI.Func.getSearchResult(o.items, keyword);
+ var matched = finding.match, find = finding.find;
+ callback(matched, find);
+ },
+ listeners: [{
+ eventName: BI.Searcher.EVENT_CHANGE,
+ action: function () {
+ self.fireEvent(BI.SearchTextValueTrigger.EVENT_CHANGE);
+ }
+ }]
+ }
+ }, {
+ el: {
+ type: "bi.layout",
+ width: 24
+ },
+ width: 24
+ }
+ ]
+ };
+ },
+
+ _setState: function (v) {
+ this.editor.setState(v);
+ },
+
+ _digest: function (vals, items) {
+ var o = this.options;
+ vals = BI.isArray(vals) ? vals : [vals];
+ var result = [];
+ var formatItems = BI.Tree.transformToArrayFormat(items);
+ BI.each(formatItems, function (i, item) {
+ if (BI.deepContains(vals, item.value) && !BI.contains(result, item.text || item.value)) {
+ result.push(item.text || item.value);
+ }
+ });
+
+ if (result.length > 0) {
+ return result.join(",");
+ } else {
+ return BI.isFunction(o.text) ? o.text() : o.text;
+ }
+ },
+
+ stopEditing: function () {
+ this.searcher.stopSearch();
+ },
+
+ getSearcher: function () {
+ return this.searcher;
+ },
+
+ populate: function (items) {
+ this.options.items = items;
+ },
+
+ setValue: function (vals) {
+ this._setState(this._digest(vals, this.options.items));
+ },
+
+ getValue: function () {
+ return this.searcher.getValue();
+ }
+});
+BI.SearchTextValueTrigger.EVENT_SEARCHING = "EVENT_SEARCHING";
+BI.SearchTextValueTrigger.EVENT_STOP = "EVENT_STOP";
+BI.SearchTextValueTrigger.EVENT_START = "EVENT_START";
+BI.SearchTextValueTrigger.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.search_text_value_trigger", BI.SearchTextValueTrigger);
diff --git a/src/main/resources/com/fr/fineui/case/combo/textvaluecheckcombo/__test__/combo.textvaluecheck.test.js b/src/main/resources/com/fr/fineui/case/combo/textvaluecheckcombo/__test__/combo.textvaluecheck.test.js
new file mode 100644
index 0000000..30b6714
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/textvaluecheckcombo/__test__/combo.textvaluecheck.test.js
@@ -0,0 +1,64 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/3/5
+ */
+
+describe("text_value_check_combo", function () {
+
+ var items = BI.map(BI.makeArray(11, null), function(idx, v) {
+ return {
+ text: idx,
+ value: idx,
+ title: idx
+ };
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("setValue", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.text_value_check_combo",
+ width: 220,
+ height: 24,
+ items: items
+ });
+ widget.setValue(2);
+ expect(widget.getValue()[0]).to.equal(2);
+ widget.destroy();
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("getValue", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.text_value_check_combo",
+ width: 220,
+ items: items,
+ value: 2
+ });
+ expect(widget.getValue()[0]).to.equal(2);
+ widget.destroy();
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("点选选值", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.text_value_check_combo",
+ width: 220,
+ items: items
+ });
+ widget.element.find(".bi-select-text-trigger").click();
+ // 为什么要delay 300呢,因为按钮有debounce
+ BI.delay(function () {
+ widget.element.find(".bi-single-select-item:contains(10)").click();
+ expect(widget.getValue()[0]).to.equal(10);
+ widget.destroy();
+ done();
+ }, 300);
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/combo/textvaluecheckcombo/combo.textvaluecheck.js b/src/main/resources/com/fr/fineui/case/combo/textvaluecheckcombo/combo.textvaluecheck.js
new file mode 100644
index 0000000..530cc2a
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/textvaluecheckcombo/combo.textvaluecheck.js
@@ -0,0 +1,86 @@
+/**
+ * @class BI.TextValueCheckCombo
+ * @extend BI.Widget
+ * combo : text + icon, popup : check + text
+ */
+BI.TextValueCheckCombo = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ return BI.extend(BI.TextValueCheckCombo.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-text-value-check-combo bi-border",
+ width: 100,
+ height: 24,
+ chooseType: BI.ButtonGroup.CHOOSE_TYPE_SINGLE,
+ value: "",
+ });
+ },
+
+ _init: function () {
+ var self = this, o = this.options;
+ o.height -= 2;
+ BI.isNumeric(o.width) && (o.width -= 2);
+ BI.TextValueCheckCombo.superclass._init.apply(this, arguments);
+ this.trigger = BI.createWidget({
+ type: "bi.select_text_trigger",
+ cls: "text-value-trigger",
+ items: o.items,
+ height: o.height,
+ text: o.text,
+ value: o.value
+ });
+ this.popup = BI.createWidget({
+ type: "bi.text_value_check_combo_popup",
+ chooseType: o.chooseType,
+ items: o.items,
+ value: o.value
+ });
+ this.popup.on(BI.TextValueCheckComboPopup.EVENT_CHANGE, function () {
+ self.setValue(self.popup.getValue());
+ self.textIconCheckCombo.hideView();
+ self.fireEvent(BI.TextValueCheckCombo.EVENT_CHANGE);
+ });
+ this.popup.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ this.textIconCheckCombo = BI.createWidget({
+ type: "bi.combo",
+ container: o.container,
+ direction: o.direction,
+ element: this,
+ adjustLength: 2,
+ el: this.trigger,
+ popup: {
+ el: this.popup,
+ maxHeight: 300
+ }
+ });
+
+ if (BI.isKey(o.value)) {
+ this.setValue(o.value);
+ }
+ },
+
+ setTitle: function (title) {
+ this.trigger.setTitle(title);
+ },
+
+ setValue: function (v) {
+ this.trigger.setValue(v);
+ this.popup.setValue(v);
+ },
+
+ setWarningTitle: function (title) {
+ this.trigger.setWarningTitle(title);
+ },
+
+ getValue: function () {
+ var value = this.popup.getValue();
+ return BI.isNull(value) ? [] : (BI.isArray(value) ? value : [value]);
+ },
+
+ populate: function (items) {
+ this.options.items = items;
+ this.textIconCheckCombo.populate(items);
+ }
+});
+BI.TextValueCheckCombo.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.text_value_check_combo", BI.TextValueCheckCombo);
diff --git a/src/main/resources/com/fr/fineui/case/combo/textvaluecheckcombo/popup.textvaluecheck.js b/src/main/resources/com/fr/fineui/case/combo/textvaluecheckcombo/popup.textvaluecheck.js
new file mode 100644
index 0000000..f5678d8
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/textvaluecheckcombo/popup.textvaluecheck.js
@@ -0,0 +1,62 @@
+BI.TextValueCheckComboPopup = BI.inherit(BI.Pane, {
+ _defaultConfig: function () {
+ return BI.extend(BI.TextValueCheckComboPopup.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-text-icon-popup",
+ chooseType: BI.ButtonGroup.CHOOSE_TYPE_SINGLE
+ });
+ },
+
+ _init: function () {
+ BI.TextValueCheckComboPopup.superclass._init.apply(this, arguments);
+ var o = this.options, self = this;
+ this.popup = BI.createWidget({
+ type: "bi.button_group",
+ items: this._formatItems(o.items),
+ chooseType: o.chooseType,
+ layouts: [{
+ type: "bi.vertical"
+ }],
+ value: o.value
+ });
+
+ this.popup.on(BI.Controller.EVENT_CHANGE, function (type, val, obj) {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ if (type === BI.Events.CLICK) {
+ self.fireEvent(BI.TextValueCheckComboPopup.EVENT_CHANGE, val, obj);
+ }
+ });
+
+ BI.createWidget({
+ type: "bi.vertical",
+ element: this,
+ vgap: 5,
+ items: [this.popup]
+ });
+ },
+
+ _formatItems: function (items) {
+ return BI.map(items, function (i, item) {
+ return BI.extend({
+ type: "bi.single_select_item",
+ cls: "bi-list-item",
+ height: 24
+ }, item);
+ });
+ },
+
+ populate: function (items) {
+ BI.TextValueCheckComboPopup.superclass.populate.apply(this, arguments);
+ this.popup.populate(this._formatItems(items));
+ },
+
+ getValue: function () {
+ return this.popup.getValue();
+ },
+
+ setValue: function (v) {
+ this.popup.setValue(v);
+ }
+
+});
+BI.TextValueCheckComboPopup.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.text_value_check_combo_popup", BI.TextValueCheckComboPopup);
diff --git a/src/main/resources/com/fr/fineui/case/combo/textvaluecombo/__test__/combo.textvalue.test.js b/src/main/resources/com/fr/fineui/case/combo/textvaluecombo/__test__/combo.textvalue.test.js
new file mode 100644
index 0000000..5a90598
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/textvaluecombo/__test__/combo.textvalue.test.js
@@ -0,0 +1,120 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/3/2
+ */
+
+describe("textvaluecombo", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("测试setValue", function () {
+ var combo = BI.Test.createWidget({
+ type: "bi.text_value_combo",
+ text: "默认值",
+ value: 22,
+ width: 300,
+ items: [{
+ text: "MVC-1",
+ iconCls: "date-font",
+ value: 1
+ }, {
+ text: "MVC-2",
+ iconCls: "search-font",
+ value: 2
+ }, {
+ text: "MVC-3",
+ iconCls: "pull-right-font",
+ value: 3
+ }]
+ });
+ combo.setValue(2);
+ expect(combo.getValue()[0]).to.equal(2);
+ combo.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("测试populate", function () {
+ var combo = BI.Test.createWidget({
+ type: "bi.text_value_combo",
+ text: "默认值",
+ value: 22,
+ width: 300
+ });
+ combo.populate([{
+ text: "MVC-1",
+ iconCls: "date-font",
+ value: 1
+ }, {
+ text: "MVC-2",
+ iconCls: "search-font",
+ value: 2
+ }, {
+ text: "MVC-3",
+ iconCls: "pull-right-font",
+ value: 3
+ }]);
+ combo.setValue(2);
+ expect(combo.getValue()[0]).to.equal(2);
+ combo.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("测试small_setValue", function () {
+ var combo = BI.Test.createWidget({
+ type: "bi.small_text_value_combo",
+ text: "默认值",
+ value: 22,
+ width: 300,
+ items: [{
+ text: "MVC-1",
+ iconCls: "date-font",
+ value: 1
+ }, {
+ text: "MVC-2",
+ iconCls: "search-font",
+ value: 2
+ }, {
+ text: "MVC-3",
+ iconCls: "pull-right-font",
+ value: 3
+ }]
+ });
+ combo.setValue(2);
+ expect(combo.getValue()[0]).to.equal(2);
+ combo.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("测试small_populate", function () {
+ var combo = BI.Test.createWidget({
+ type: "bi.small_text_value_combo",
+ text: "默认值",
+ value: 22,
+ width: 300
+ });
+ combo.populate([{
+ text: "MVC-1",
+ iconCls: "date-font",
+ value: 1
+ }, {
+ text: "MVC-2",
+ iconCls: "search-font",
+ value: 2
+ }, {
+ text: "MVC-3",
+ iconCls: "pull-right-font",
+ value: 3
+ }]);
+ combo.setValue(2);
+ expect(combo.getValue()[0]).to.equal(2);
+ combo.destroy();
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/combo/textvaluecombo/combo.textvalue.js b/src/main/resources/com/fr/fineui/case/combo/textvaluecombo/combo.textvalue.js
new file mode 100644
index 0000000..b0242a6
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/textvaluecombo/combo.textvalue.js
@@ -0,0 +1,100 @@
+/**
+ * @class BI.TextValueCombo
+ * @extend BI.Widget
+ * combo : text + icon, popup : text
+ * 参见场景dashboard布局方式选择
+ */
+BI.TextValueCombo = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ return BI.extend(BI.TextValueCombo.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-text-value-combo bi-border",
+ height: 24,
+ chooseType: BI.ButtonGroup.CHOOSE_TYPE_SINGLE,
+ text: "",
+ value: "",
+ });
+ },
+
+ _init: function () {
+ var self = this, o = this.options;
+ o.height -= 2;
+ BI.isNumeric(o.width) && (o.width -= 2);
+ BI.TextValueCombo.superclass._init.apply(this, arguments);
+ this.trigger = BI.createWidget({
+ type: "bi.select_text_trigger",
+ cls: "text-value-trigger",
+ items: o.items,
+ height: o.height,
+ text: o.text,
+ value: o.value,
+ warningTitle: o.warningTitle
+ });
+ this.popup = BI.createWidget({
+ type: "bi.text_value_combo_popup",
+ chooseType: o.chooseType,
+ value: o.value,
+ items: o.items
+ });
+ this.popup.on(BI.TextValueComboPopup.EVENT_CHANGE, function () {
+ self.setValue(self.popup.getValue());
+ self.textIconCombo.hideView();
+ self.fireEvent(BI.TextValueCombo.EVENT_CHANGE, arguments);
+ });
+ this.popup.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ this.textIconCombo = BI.createWidget({
+ type: "bi.combo",
+ container: o.container,
+ direction: o.direction,
+ element: this,
+ adjustLength: 2,
+ el: this.trigger,
+ popup: {
+ el: this.popup,
+ maxHeight: 240,
+ minHeight: 25
+ }
+ });
+ if(BI.isKey(o.value)) {
+ this._checkError(o.value);
+ }
+ },
+
+ _checkError: function (v) {
+ if(BI.isNull(v) || BI.isEmptyArray(v) || BI.isEmptyString(v)) {
+ this.trigger.options.tipType = "success";
+ this.element.removeClass("combo-error");
+ } else {
+ v = BI.isArray(v) ? v : [v];
+ var result = BI.find(this.options.items, function (idx, item) {
+ return BI.contains(v, item.value);
+ });
+ if (BI.isNull(result)) {
+ this.trigger.setTipType("warning");
+ this.element.removeClass("combo-error").addClass("combo-error");
+ } else {
+ this.trigger.setTipType("success");
+ this.element.removeClass("combo-error");
+ }
+ }
+ },
+
+ setValue: function (v) {
+ this.trigger.setValue(v);
+ this.popup.setValue(v);
+ this._checkError(v);
+ },
+
+ getValue: function () {
+ var value = this.popup.getValue();
+ return BI.isNull(value) ? [] : (BI.isArray(value) ? value : [value]);
+ },
+
+ populate: function (items) {
+ this.options.items = items;
+ this.textIconCombo.populate(items);
+ }
+});
+BI.TextValueCombo.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.text_value_combo", BI.TextValueCombo);
diff --git a/src/main/resources/com/fr/fineui/case/combo/textvaluecombo/combo.textvaluesmall.js b/src/main/resources/com/fr/fineui/case/combo/textvaluecombo/combo.textvaluesmall.js
new file mode 100644
index 0000000..70e5517
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/textvaluecombo/combo.textvaluesmall.js
@@ -0,0 +1,73 @@
+/**
+ * @class BI.SmallTextValueCombo
+ * @extend BI.Widget
+ * combo : text + icon, popup : text
+ * 参见场景dashboard布局方式选择
+ */
+BI.SmallTextValueCombo = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ return BI.extend(BI.SmallTextValueCombo.superclass._defaultConfig.apply(this, arguments), {
+ width: 100,
+ height: 20,
+ chooseType: BI.ButtonGroup.CHOOSE_TYPE_SINGLE,
+ el: {},
+ text: ""
+ });
+ },
+
+ _init: function () {
+ BI.SmallTextValueCombo.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.trigger = BI.createWidget(o.el, {
+ type: "bi.small_select_text_trigger",
+ items: o.items,
+ height: o.height,
+ text: o.text
+ });
+ this.popup = BI.createWidget({
+ type: "bi.text_value_combo_popup",
+ chooseType: o.chooseType,
+ items: o.items
+ });
+ this.popup.on(BI.TextValueComboPopup.EVENT_CHANGE, function () {
+ self.setValue(self.popup.getValue());
+ self.SmallTextValueCombo.hideView();
+ self.fireEvent(BI.SmallTextValueCombo.EVENT_CHANGE);
+ });
+ this.popup.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ this.SmallTextValueCombo = BI.createWidget({
+ type: "bi.combo",
+ element: this,
+ container: o.container,
+ adjustLength: 2,
+ el: this.trigger,
+ popup: {
+ el: this.popup,
+ maxHeight: 240,
+ minHeight: 25
+ }
+ });
+
+ if(BI.isKey(o.value)){
+ this.setValue(o.value);
+ }
+ },
+
+ setValue: function (v) {
+ this.trigger.setValue(v);
+ this.popup.setValue(v);
+ },
+
+ getValue: function () {
+ return this.popup.getValue();
+ },
+
+ populate: function (items) {
+ this.options.items = items;
+ this.SmallTextValueCombo.populate(items);
+ }
+});
+BI.SmallTextValueCombo.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.small_text_value_combo", BI.SmallTextValueCombo);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/combo/textvaluecombo/popup.textvalue.js b/src/main/resources/com/fr/fineui/case/combo/textvaluecombo/popup.textvalue.js
new file mode 100644
index 0000000..5b3c0a8
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/combo/textvaluecombo/popup.textvalue.js
@@ -0,0 +1,61 @@
+BI.TextValueComboPopup = BI.inherit(BI.Pane, {
+ _defaultConfig: function () {
+ return BI.extend(BI.TextValueComboPopup.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-text-icon-popup",
+ chooseType: BI.ButtonGroup.CHOOSE_TYPE_SINGLE
+ });
+ },
+
+ _init: function () {
+ BI.TextValueComboPopup.superclass._init.apply(this, arguments);
+ var o = this.options, self = this;
+ this.popup = BI.createWidget({
+ type: "bi.button_group",
+ items: BI.createItems(o.items, {
+ type: "bi.single_select_item",
+ textAlign: o.textAlign,
+ height: BI.SIZE_CONSANTS.LIST_ITEM_HEIGHT,
+ }),
+ chooseType: o.chooseType,
+ layouts: [{
+ type: "bi.vertical"
+ }],
+ value: o.value
+ });
+
+ this.popup.on(BI.Controller.EVENT_CHANGE, function (type, val, obj) {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ if (type === BI.Events.CLICK) {
+ self.fireEvent(BI.TextValueComboPopup.EVENT_CHANGE, val, obj);
+ }
+ });
+ this.check();
+
+ BI.createWidget({
+ type: "bi.vertical",
+ element: this,
+ vgap: 5,
+ items: [this.popup]
+ });
+ },
+
+ populate: function (items) {
+ BI.TextValueComboPopup.superclass.populate.apply(this, arguments);
+ items = BI.createItems(items, {
+ type: "bi.single_select_item",
+ height: BI.SIZE_CONSANTS.LIST_ITEM_HEIGHT,
+ });
+ this.popup.populate(items);
+ },
+
+ getValue: function () {
+ return this.popup.getValue();
+ },
+
+ setValue: function (v) {
+ this.popup.setValue(v);
+ }
+
+});
+BI.TextValueComboPopup.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.text_value_combo_popup", BI.TextValueComboPopup);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/editor/__test__/editor.clear.test.js b/src/main/resources/com/fr/fineui/case/editor/__test__/editor.clear.test.js
new file mode 100644
index 0000000..7181a76
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/editor/__test__/editor.clear.test.js
@@ -0,0 +1,110 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/5/13
+ */
+describe("clear_editor", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("clear-editor", function (done) {
+ var editor = BI.Test.createWidget({
+ type: "bi.clear_editor",
+ width: 300,
+ value: "12345",
+ watermark: "添加合法性判断",
+ errorText: "长度必须大于4",
+ validationChecker: function () {
+ return this.getValue().length > 4;
+ }
+ });
+ editor.setWaterMark("AAAAA");
+ expect(editor.element.find(".bi-water-mark").text()).to.equal("AAAAA");
+ expect(editor.element.find(".bi-input").val()).to.equal("12345");
+ editor.focus();
+ BI.Test.triggerKeyDown(editor.element.find(".bi-input"), "8", 56, function () {
+ expect(editor.element.find(".bi-bubble .bubble-text:first-child").text()).to.equal("长度必须大于4");
+ editor.destroy();
+ done();
+ })
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("clear", function (done) {
+ var editor = BI.Test.createWidget({
+ type: "bi.clear_editor",
+ width: 300,
+ value: "12345",
+ watermark: "添加合法性判断",
+ errorText: "长度必须大于4",
+ validationChecker: function () {
+ return this.getValue().length > 4;
+ }
+ });
+ BI.nextTick(function () {
+ editor.element.find(".search-close-h-font").click();
+ expect(editor.element.find(".bi-input").val()).to.equal("");
+ editor.destroy();
+ done();
+ });
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("setValue", function () {
+ var editor = BI.Test.createWidget({
+ type: "bi.clear_editor",
+ width: 300,
+ watermark: "添加合法性判断",
+ errorText: "长度必须大于4",
+ validationChecker: function () {
+ return this.getValue().length > 4;
+ }
+ });
+ editor.setValue("12345");
+ expect(editor.element.find(".bi-input").val()).to.equal("12345");
+ editor.destroy();
+ });
+
+
+ /**
+ * test_author_windy
+ */
+ it("getValue", function () {
+ var editor = BI.Test.createWidget({
+ type: "bi.clear_editor",
+ width: 300,
+ value: "12346",
+ watermark: "添加合法性判断",
+ errorText: "长度必须大于4",
+ validationChecker: function () {
+ return this.getValue().length > 4;
+ }
+ });
+ expect(editor.getValue()).to.equal("12346");
+ editor.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("getValue1", function () {
+ var editor = BI.Test.createWidget({
+ type: "bi.clear_editor",
+ width: 300,
+ value: "12346 7890",
+ watermark: "添加合法性判断",
+ errorText: "长度必须大于4",
+ validationChecker: function () {
+ return this.getValue().length > 4;
+ }
+ });
+ expect(editor.getValue()).to.equal("12346 7890");
+ editor.destroy();
+ });
+
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/editor/__test__/editor.sign.test.js b/src/main/resources/com/fr/fineui/case/editor/__test__/editor.sign.test.js
new file mode 100644
index 0000000..d741800
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/editor/__test__/editor.sign.test.js
@@ -0,0 +1,70 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/5/13
+ */
+describe("sign_editor", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("sign-editor", function (done) {
+ var editor = BI.Test.createWidget({
+ type: "bi.sign_editor",
+ width: 300,
+ value: "12345",
+ watermark: "添加合法性判断",
+ errorText: "长度必须大于4",
+ validationChecker: function () {
+ return this.getValue().length > 4;
+ }
+ });
+ editor.setWaterMark("AAAAA");
+ expect(editor.element.find(".bi-water-mark").text()).to.equal("AAAAA");
+ expect(editor.element.find(".bi-input").val()).to.equal("12345");
+ editor.focus();
+ BI.Test.triggerKeyDown(editor.element.find(".bi-input"), "8", 56, function () {
+ expect(editor.element.find(".bi-bubble .bubble-text:first-child").text()).to.equal("长度必须大于4");
+ editor.destroy();
+ done();
+ })
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("setValue", function () {
+ var editor = BI.Test.createWidget({
+ type: "bi.sign_editor",
+ width: 300,
+ watermark: "添加合法性判断",
+ errorText: "长度必须大于4",
+ validationChecker: function () {
+ return this.getValue().length > 4;
+ }
+ });
+ editor.setValue("12345");
+ expect(editor.element.find(".bi-input").val()).to.equal("12345");
+ editor.destroy();
+ });
+
+
+ /**
+ * test_author_windy
+ */
+ it("getValue", function () {
+ var editor = BI.Test.createWidget({
+ type: "bi.sign_editor",
+ width: 300,
+ value: "12346",
+ watermark: "添加合法性判断",
+ errorText: "长度必须大于4",
+ validationChecker: function () {
+ return this.getValue().length > 4;
+ }
+ });
+ expect(editor.getValue()).to.equal("12346");
+ editor.destroy();
+ });
+
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/editor/__test__/editor.state.simple.test.js b/src/main/resources/com/fr/fineui/case/editor/__test__/editor.state.simple.test.js
new file mode 100644
index 0000000..9839153
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/editor/__test__/editor.state.simple.test.js
@@ -0,0 +1,70 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/5/13
+ */
+describe("simple_state_editor", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("state-editor", function (done) {
+ var editor = BI.Test.createWidget({
+ type: "bi.simple_state_editor",
+ width: 300,
+ value: "12345",
+ watermark: "添加合法性判断",
+ errorText: "长度必须大于4",
+ validationChecker: function () {
+ return this.getValue().length > 4;
+ }
+ });
+ editor.setWaterMark("AAAAA");
+ expect(editor.element.find(".bi-water-mark").text()).to.equal("AAAAA");
+ expect(editor.element.find(".bi-input").val()).to.equal("12345");
+ editor.focus();
+ BI.Test.triggerKeyDown(editor.element.find(".bi-input"), "8", 56, function () {
+ expect(editor.element.find(".bi-bubble .bubble-text:first-child").text()).to.equal("长度必须大于4");
+ editor.destroy();
+ done();
+ })
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("setValue", function () {
+ var editor = BI.Test.createWidget({
+ type: "bi.simple_state_editor",
+ width: 300,
+ watermark: "添加合法性判断",
+ errorText: "长度必须大于4",
+ validationChecker: function () {
+ return this.getValue().length > 4;
+ }
+ });
+ editor.setValue("12345");
+ expect(editor.element.find(".bi-input").val()).to.equal("12345");
+ editor.destroy();
+ });
+
+
+ /**
+ * test_author_windy
+ */
+ it("getValue", function () {
+ var editor = BI.Test.createWidget({
+ type: "bi.simple_state_editor",
+ width: 300,
+ value: "12346",
+ watermark: "添加合法性判断",
+ errorText: "长度必须大于4",
+ validationChecker: function () {
+ return this.getValue().length > 4;
+ }
+ });
+ expect(editor.getValue()).to.equal("12346");
+ editor.destroy();
+ });
+
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/editor/__test__/editor.state.test.js b/src/main/resources/com/fr/fineui/case/editor/__test__/editor.state.test.js
new file mode 100644
index 0000000..63466fd
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/editor/__test__/editor.state.test.js
@@ -0,0 +1,70 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/5/13
+ */
+describe("state_editor", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("state-editor", function (done) {
+ var editor = BI.Test.createWidget({
+ type: "bi.state_editor",
+ width: 300,
+ value: "12345",
+ watermark: "添加合法性判断",
+ errorText: "长度必须大于4",
+ validationChecker: function () {
+ return this.getValue().length > 4;
+ }
+ });
+ editor.setWaterMark("AAAAA");
+ expect(editor.element.find(".bi-water-mark").text()).to.equal("AAAAA");
+ expect(editor.element.find(".bi-input").val()).to.equal("12345");
+ editor.focus();
+ BI.Test.triggerKeyDown(editor.element.find(".bi-input"), "8", 56, function () {
+ expect(editor.element.find(".bi-bubble .bubble-text:first-child").text()).to.equal("长度必须大于4");
+ editor.destroy();
+ done();
+ })
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("setValue", function () {
+ var editor = BI.Test.createWidget({
+ type: "bi.state_editor",
+ width: 300,
+ watermark: "添加合法性判断",
+ errorText: "长度必须大于4",
+ validationChecker: function () {
+ return this.getValue().length > 4;
+ }
+ });
+ editor.setValue("12345");
+ expect(editor.element.find(".bi-input").val()).to.equal("12345");
+ editor.destroy();
+ });
+
+
+ /**
+ * test_author_windy
+ */
+ it("getValue", function () {
+ var editor = BI.Test.createWidget({
+ type: "bi.state_editor",
+ width: 300,
+ value: "12346",
+ watermark: "添加合法性判断",
+ errorText: "长度必须大于4",
+ validationChecker: function () {
+ return this.getValue().length > 4;
+ }
+ });
+ expect(editor.getValue()).to.equal("12346");
+ editor.destroy();
+ });
+
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/editor/editor.clear.js b/src/main/resources/com/fr/fineui/case/editor/editor.clear.js
new file mode 100644
index 0000000..372d31b
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/editor/editor.clear.js
@@ -0,0 +1,182 @@
+/**
+ * 有清楚按钮的文本框
+ * Created by GUY on 2015/9/29.
+ * @class BI.SmallTextEditor
+ * @extends BI.SearchEditor
+ */
+BI.ClearEditor = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ var conf = BI.ClearEditor.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: "bi-clear-editor",
+ height: 24,
+ errorText: "",
+ watermark: "",
+ validationChecker: BI.emptyFn,
+ quitChecker: BI.emptyFn
+ });
+ },
+ _init: function () {
+ BI.ClearEditor.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.editor = BI.createWidget({
+ type: "bi.editor",
+ height: o.height,
+ watermark: o.watermark,
+ allowBlank: true,
+ errorText: o.errorText,
+ validationChecker: o.validationChecker,
+ quitChecker: o.quitChecker,
+ value: o.value
+ });
+ this.clear = BI.createWidget({
+ type: "bi.icon_button",
+ stopEvent: true,
+ cls: "search-close-h-font"
+ });
+ this.clear.on(BI.IconButton.EVENT_CHANGE, function () {
+ self.setValue("");
+ self.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.STOPEDIT);
+ self.fireEvent(BI.ClearEditor.EVENT_CLEAR);
+ });
+ BI.createWidget({
+ element: this,
+ type: "bi.htape",
+ items: [
+ {
+ el: this.editor
+ },
+ {
+ el: this.clear,
+ width: 24
+ }]
+ });
+ this.editor.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+
+ this.editor.on(BI.Editor.EVENT_FOCUS, function () {
+ self.fireEvent(BI.ClearEditor.EVENT_FOCUS);
+ });
+ this.editor.on(BI.Editor.EVENT_BLUR, function () {
+ self.fireEvent(BI.ClearEditor.EVENT_BLUR);
+ });
+ this.editor.on(BI.Editor.EVENT_CLICK, function () {
+ self.fireEvent(BI.ClearEditor.EVENT_CLICK);
+ });
+ this.editor.on(BI.Editor.EVENT_CHANGE, function () {
+ self._checkClear();
+ self.fireEvent(BI.ClearEditor.EVENT_CHANGE);
+ });
+ this.editor.on(BI.Editor.EVENT_KEY_DOWN, function (v) {
+ self.fireEvent(BI.ClearEditor.EVENT_KEY_DOWN, v);
+ });
+ this.editor.on(BI.Editor.EVENT_SPACE, function () {
+ self.fireEvent(BI.ClearEditor.EVENT_SPACE);
+ });
+ this.editor.on(BI.Editor.EVENT_BACKSPACE, function () {
+ self.fireEvent(BI.ClearEditor.EVENT_BACKSPACE);
+ });
+
+
+ this.editor.on(BI.Editor.EVENT_VALID, function () {
+ self.fireEvent(BI.ClearEditor.EVENT_VALID);
+ });
+ this.editor.on(BI.Editor.EVENT_ERROR, function () {
+ self.fireEvent(BI.ClearEditor.EVENT_ERROR);
+ });
+ this.editor.on(BI.Editor.EVENT_ENTER, function () {
+ self.fireEvent(BI.ClearEditor.EVENT_ENTER);
+ });
+ this.editor.on(BI.Editor.EVENT_RESTRICT, function () {
+ self.fireEvent(BI.ClearEditor.EVENT_RESTRICT);
+ });
+ this.editor.on(BI.Editor.EVENT_EMPTY, function () {
+ self._checkClear();
+ self.fireEvent(BI.ClearEditor.EVENT_EMPTY);
+ });
+ this.editor.on(BI.Editor.EVENT_REMOVE, function () {
+ self.fireEvent(BI.ClearEditor.EVENT_REMOVE);
+ });
+ this.editor.on(BI.Editor.EVENT_CONFIRM, function () {
+ self.fireEvent(BI.ClearEditor.EVENT_CONFIRM);
+ });
+ this.editor.on(BI.Editor.EVENT_CHANGE_CONFIRM, function () {
+ self.fireEvent(BI.ClearEditor.EVENT_CHANGE_CONFIRM);
+ });
+ this.editor.on(BI.Editor.EVENT_START, function () {
+ self.fireEvent(BI.ClearEditor.EVENT_START);
+ });
+ this.editor.on(BI.Editor.EVENT_PAUSE, function () {
+ self.fireEvent(BI.ClearEditor.EVENT_PAUSE);
+ });
+ this.editor.on(BI.Editor.EVENT_STOP, function () {
+ self.fireEvent(BI.ClearEditor.EVENT_STOP);
+ });
+
+ if (BI.isKey(o.value)) {
+ this.clear.visible();
+ } else {
+ this.clear.invisible();
+ }
+ },
+
+ _checkClear: function () {
+ if (!this.getValue()) {
+ this.clear.invisible();
+ } else {
+ this.clear.visible();
+ }
+ },
+
+ setWaterMark: function (v) {
+ this.options.watermark = v;
+ this.editor.setWaterMark(v);
+ },
+
+ focus: function () {
+ this.editor.focus();
+ },
+
+ blur: function () {
+ this.editor.blur();
+ },
+
+ getValue: function () {
+ if (this.isValid()) {
+ return this.editor.getValue();
+ }
+ },
+
+ setValue: function (v) {
+ this.editor.setValue(v);
+ if (BI.isKey(v)) {
+ this.clear.visible();
+ }
+ },
+
+ isValid: function () {
+ return this.editor.isValid();
+ }
+});
+BI.ClearEditor.EVENT_CHANGE = "EVENT_CHANGE";
+BI.ClearEditor.EVENT_FOCUS = "EVENT_FOCUS";
+BI.ClearEditor.EVENT_BLUR = "EVENT_BLUR";
+BI.ClearEditor.EVENT_CLICK = "EVENT_CLICK";
+BI.ClearEditor.EVENT_KEY_DOWN = "EVENT_KEY_DOWN";
+BI.ClearEditor.EVENT_SPACE = "EVENT_SPACE";
+BI.ClearEditor.EVENT_BACKSPACE = "EVENT_BACKSPACE";
+BI.ClearEditor.EVENT_CLEAR = "EVENT_CLEAR";
+
+BI.ClearEditor.EVENT_START = "EVENT_START";
+BI.ClearEditor.EVENT_PAUSE = "EVENT_PAUSE";
+BI.ClearEditor.EVENT_STOP = "EVENT_STOP";
+BI.ClearEditor.EVENT_CONFIRM = "EVENT_CONFIRM";
+BI.ClearEditor.EVENT_CHANGE_CONFIRM = "EVENT_CHANGE_CONFIRM";
+BI.ClearEditor.EVENT_VALID = "EVENT_VALID";
+BI.ClearEditor.EVENT_ERROR = "EVENT_ERROR";
+BI.ClearEditor.EVENT_ENTER = "EVENT_ENTER";
+BI.ClearEditor.EVENT_RESTRICT = "EVENT_RESTRICT";
+BI.ClearEditor.EVENT_REMOVE = "EVENT_REMOVE";
+BI.ClearEditor.EVENT_EMPTY = "EVENT_EMPTY";
+BI.shortcut("bi.clear_editor", BI.ClearEditor);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/editor/editor.shelter.js b/src/main/resources/com/fr/fineui/case/editor/editor.shelter.js
new file mode 100644
index 0000000..562cbe3
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/editor/editor.shelter.js
@@ -0,0 +1,276 @@
+/**
+ * 带标记的文本框
+ * Created by GUY on 2016/1/25.
+ * @class BI.ShelterEditor
+ * @extends BI.Widget
+ */
+BI.ShelterEditor = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ var conf = BI.ShelterEditor.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-shelter-editor",
+ hgap: 4,
+ vgap: 2,
+ lgap: 0,
+ rgap: 0,
+ tgap: 0,
+ bgap: 0,
+ validationChecker: BI.emptyFn,
+ quitChecker: BI.emptyFn,
+ allowBlank: true,
+ watermark: "",
+ errorText: "",
+ height: 24,
+ textAlign: "left"
+ });
+ },
+
+ _init: function () {
+ BI.ShelterEditor.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.editor = BI.createWidget({
+ type: "bi.editor",
+ height: o.height,
+ hgap: o.hgap,
+ vgap: o.vgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap,
+ value: o.value,
+ validationChecker: o.validationChecker,
+ quitChecker: o.quitChecker,
+ allowBlank: o.allowBlank,
+ watermark: o.watermark,
+ errorText: o.errorText
+ });
+ this.text = BI.createWidget({
+ type: "bi.text_button",
+ cls: "shelter-editor-text",
+ title: o.title,
+ warningTitle: o.warningTitle,
+ tipType: o.tipType,
+ textAlign: o.textAlign,
+ height: o.height,
+ hgap: o.hgap + 2
+ });
+ BI.createWidget({
+ type: "bi.absolute",
+ element: this,
+ items: [{
+ el: this.text,
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0
+ }]
+ });
+ this.text.on(BI.Controller.EVENT_CHANGE, function () {
+ arguments[2] = self;
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ this.text.on(BI.TextButton.EVENT_CHANGE, function () {
+ self.fireEvent(BI.ShelterEditor.EVENT_CLICK_LABEL);
+ });
+ this.editor.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_FOCUS, function () {
+ self.fireEvent(BI.ShelterEditor.EVENT_FOCUS, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_BLUR, function () {
+ self.fireEvent(BI.ShelterEditor.EVENT_BLUR, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_CLICK, function () {
+ self.fireEvent(BI.ShelterEditor.EVENT_CLICK, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_CHANGE, function () {
+ self.fireEvent(BI.ShelterEditor.EVENT_CHANGE, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_KEY_DOWN, function (v) {
+ self.fireEvent(BI.ShelterEditor.EVENT_KEY_DOWN, arguments);
+ });
+
+ this.editor.on(BI.Editor.EVENT_VALID, function () {
+ self.fireEvent(BI.ShelterEditor.EVENT_VALID, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_CONFIRM, function () {
+ self._showHint();
+ self._checkText();
+ self.fireEvent(BI.ShelterEditor.EVENT_CONFIRM, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_CHANGE_CONFIRM, function () {
+ self._showHint();
+ self._checkText();
+ self.fireEvent(BI.ShelterEditor.EVENT_CHANGE_CONFIRM, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_START, function () {
+ self.fireEvent(BI.ShelterEditor.EVENT_START, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_PAUSE, function () {
+ self.fireEvent(BI.ShelterEditor.EVENT_PAUSE, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_STOP, function () {
+ self.fireEvent(BI.ShelterEditor.EVENT_STOP, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_SPACE, function () {
+ self.fireEvent(BI.ShelterEditor.EVENT_SPACE, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_ERROR, function () {
+ self._checkText();
+ self.fireEvent(BI.ShelterEditor.EVENT_ERROR, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_ENTER, function () {
+ self.fireEvent(BI.ShelterEditor.EVENT_ENTER, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_RESTRICT, function () {
+ self.fireEvent(BI.ShelterEditor.EVENT_RESTRICT, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_EMPTY, function () {
+ self.fireEvent(BI.ShelterEditor.EVENT_EMPTY, arguments);
+ });
+ BI.createWidget({
+ type: "bi.vertical",
+ scrolly: false,
+ element: this,
+ items: [this.editor]
+ });
+ this._showHint();
+ self._checkText();
+ },
+
+ _checkText: function () {
+ var o = this.options;
+ if (this.editor.getValue() === "") {
+ this.text.setValue(o.watermark || "");
+ this.text.element.addClass("bi-water-mark");
+ } else {
+ this.text.setValue(this.editor.getValue());
+ this.text.element.removeClass("bi-water-mark");
+ }
+ BI.isKey(o.keyword) && this.text.doRedMark(o.keyword);
+ },
+
+ _showInput: function () {
+ this.editor.visible();
+ this.text.invisible();
+ },
+
+ _showHint: function () {
+ this.editor.invisible();
+ this.text.visible();
+ },
+
+ setWaterMark: function (v) {
+ this.options.watermark = v;
+ this.editor.setWaterMark(v);
+ },
+
+ setTitle: function (title) {
+ this.text.setTitle(title);
+ },
+
+ setWarningTitle: function (title) {
+ this.text.setWarningTitle(title);
+ },
+
+ focus: function () {
+ this._showInput();
+ this.editor.focus();
+ },
+
+ blur: function () {
+ this.editor.blur();
+ this._showHint();
+ this._checkText();
+ },
+
+ doRedMark: function () {
+ if (this.editor.getValue() === "" && BI.isKey(this.options.watermark)) {
+ return;
+ }
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doHighLight: function () {
+ if (this.editor.getValue() === "" && BI.isKey(this.options.watermark)) {
+ return;
+ }
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ },
+
+ isValid: function () {
+ return this.editor.isValid();
+ },
+
+ setErrorText: function (text) {
+ this.editor.setErrorText(text);
+ },
+
+ getErrorText: function () {
+ return this.editor.getErrorText();
+ },
+
+ isEditing: function () {
+ return this.editor.isEditing();
+ },
+
+ getLastValidValue: function () {
+ return this.editor.getLastValidValue();
+ },
+
+ getLastChangedValue: function () {
+ return this.editor.getLastChangedValue();
+ },
+
+ setTextStyle: function (style) {
+ this.text.setStyle(style);
+ },
+
+ setValue: function (k) {
+ var o = this.options;
+ this.editor.setValue(k);
+ this._checkText();
+ },
+
+ getValue: function () {
+ return this.editor.getValue();
+ },
+
+ getState: function () {
+ return this.text.getValue();
+ },
+
+ setState: function (v) {
+ this._showHint();
+ this.text.setValue(v);
+ }
+});
+BI.ShelterEditor.EVENT_CHANGE = "EVENT_CHANGE";
+BI.ShelterEditor.EVENT_FOCUS = "EVENT_FOCUS";
+BI.ShelterEditor.EVENT_BLUR = "EVENT_BLUR";
+BI.ShelterEditor.EVENT_CLICK = "EVENT_CLICK";
+BI.ShelterEditor.EVENT_KEY_DOWN = "EVENT_KEY_DOWN";
+BI.ShelterEditor.EVENT_CLICK_LABEL = "EVENT_CLICK_LABEL";
+
+BI.ShelterEditor.EVENT_START = "EVENT_START";
+BI.ShelterEditor.EVENT_PAUSE = "EVENT_PAUSE";
+BI.ShelterEditor.EVENT_STOP = "EVENT_STOP";
+BI.ShelterEditor.EVENT_CONFIRM = "EVENT_CONFIRM";
+BI.ShelterEditor.EVENT_CHANGE_CONFIRM = "EVENT_CHANGE_CONFIRM";
+BI.ShelterEditor.EVENT_VALID = "EVENT_VALID";
+BI.ShelterEditor.EVENT_ERROR = "EVENT_ERROR";
+BI.ShelterEditor.EVENT_ENTER = "EVENT_ENTER";
+BI.ShelterEditor.EVENT_RESTRICT = "EVENT_RESTRICT";
+BI.ShelterEditor.EVENT_SPACE = "EVENT_SPACE";
+BI.ShelterEditor.EVENT_EMPTY = "EVENT_EMPTY";
+
+BI.shortcut("bi.shelter_editor", BI.ShelterEditor);
diff --git a/src/main/resources/com/fr/fineui/case/editor/editor.sign.js b/src/main/resources/com/fr/fineui/case/editor/editor.sign.js
new file mode 100644
index 0000000..35ed78e
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/editor/editor.sign.js
@@ -0,0 +1,276 @@
+/**
+ * 带标记的文本框
+ * Created by GUY on 2015/8/28.
+ * @class BI.SignEditor
+ * @extends BI.Widget
+ */
+BI.SignEditor = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ var conf = BI.SignEditor.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-sign-editor",
+ hgap: 4,
+ vgap: 2,
+ lgap: 0,
+ rgap: 0,
+ tgap: 0,
+ bgap: 0,
+ validationChecker: BI.emptyFn,
+ quitChecker: BI.emptyFn,
+ allowBlank: true,
+ watermark: "",
+ errorText: "",
+ textAlign: "left",
+ height: 24
+ });
+ },
+
+ _init: function () {
+ BI.SignEditor.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.editor = BI.createWidget({
+ type: "bi.editor",
+ height: o.height,
+ hgap: o.hgap,
+ vgap: o.vgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap,
+ value: o.value,
+ validationChecker: o.validationChecker,
+ quitChecker: o.quitChecker,
+ allowBlank: o.allowBlank,
+ watermark: o.watermark,
+ errorText: o.errorText
+ });
+ this.text = BI.createWidget({
+ type: "bi.text_button",
+ cls: "sign-editor-text",
+ title: o.title,
+ warningTitle: o.warningTitle,
+ tipType: o.tipType,
+ textAlign: o.textAlign,
+ height: o.height,
+ hgap: o.hgap + 2,
+ handler: function () {
+ self._showInput();
+ self.editor.focus();
+ self.editor.selectAll();
+ }
+ });
+ this.text.on(BI.TextButton.EVENT_CHANGE, function () {
+ BI.nextTick(function () {
+ self.fireEvent(BI.SignEditor.EVENT_CLICK_LABEL);
+ });
+ });
+ BI.createWidget({
+ type: "bi.absolute",
+ element: this,
+ items: [{
+ el: this.text,
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0
+ }]
+ });
+ this.editor.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_FOCUS, function () {
+ self.fireEvent(BI.SignEditor.EVENT_FOCUS, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_BLUR, function () {
+ self.fireEvent(BI.SignEditor.EVENT_BLUR, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_CLICK, function () {
+ self.fireEvent(BI.SignEditor.EVENT_CLICK, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_CHANGE, function () {
+ self.fireEvent(BI.SignEditor.EVENT_CHANGE, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_KEY_DOWN, function (v) {
+ self.fireEvent(BI.SignEditor.EVENT_KEY_DOWN, arguments);
+ });
+
+ this.editor.on(BI.Editor.EVENT_VALID, function () {
+ self.fireEvent(BI.SignEditor.EVENT_VALID, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_CONFIRM, function () {
+ self._showHint();
+ self._checkText();
+ self.fireEvent(BI.SignEditor.EVENT_CONFIRM, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_CHANGE_CONFIRM, function () {
+ self._showHint();
+ self._checkText();
+ self.fireEvent(BI.SignEditor.EVENT_CHANGE_CONFIRM, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_START, function () {
+ self.fireEvent(BI.SignEditor.EVENT_START, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_PAUSE, function () {
+ self.fireEvent(BI.SignEditor.EVENT_PAUSE, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_STOP, function () {
+ self.fireEvent(BI.SignEditor.EVENT_STOP, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_SPACE, function () {
+ self.fireEvent(BI.SignEditor.EVENT_SPACE, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_ERROR, function () {
+ self._checkText();
+ self.fireEvent(BI.SignEditor.EVENT_ERROR, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_ENTER, function () {
+ self.fireEvent(BI.SignEditor.EVENT_ENTER, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_RESTRICT, function () {
+ self.fireEvent(BI.SignEditor.EVENT_RESTRICT, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_EMPTY, function () {
+ self.fireEvent(BI.SignEditor.EVENT_EMPTY, arguments);
+ });
+ BI.createWidget({
+ type: "bi.vertical",
+ scrolly: false,
+ element: this,
+ items: [this.editor]
+ });
+ this._showHint();
+ self._checkText();
+ },
+
+ _checkText: function () {
+ var o = this.options;
+ BI.nextTick(BI.bind(function () {
+ if (this.editor.getValue() === "") {
+ this.text.setValue(o.watermark || "");
+ this.text.element.addClass("bi-water-mark");
+ } else {
+ this.text.setValue(this.editor.getValue());
+ this.text.element.removeClass("bi-water-mark");
+ BI.isKey(o.keyword) && this.text.doRedMark(o.keyword);
+ }
+ }, this));
+ },
+
+ _showInput: function () {
+ this.editor.visible();
+ this.text.invisible();
+ },
+
+ _showHint: function () {
+ this.editor.invisible();
+ this.text.visible();
+ },
+
+ setTitle: function (title) {
+ this.text.setTitle(title);
+ },
+
+ setWarningTitle: function (title) {
+ this.text.setWarningTitle(title);
+ },
+
+ setWaterMark: function (v) {
+ this.options.watermark = v;
+ this.editor.setWaterMark(v);
+ },
+
+ focus: function () {
+ this._showInput();
+ this.editor.focus();
+ },
+
+ blur: function () {
+ this.editor.blur();
+ this._showHint();
+ this._checkText();
+ },
+
+ doRedMark: function () {
+ if (this.editor.getValue() === "" && BI.isKey(this.options.watermark)) {
+ return;
+ }
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doHighLight: function () {
+ if (this.editor.getValue() === "" && BI.isKey(this.options.watermark)) {
+ return;
+ }
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ },
+
+ isValid: function () {
+ return this.editor.isValid();
+ },
+
+ setErrorText: function (text) {
+ this.editor.setErrorText(text);
+ },
+
+ getErrorText: function () {
+ return this.editor.getErrorText();
+ },
+
+ isEditing: function () {
+ return this.editor.isEditing();
+ },
+
+ getLastValidValue: function () {
+ return this.editor.getLastValidValue();
+ },
+
+ getLastChangedValue: function () {
+ return this.editor.getLastChangedValue();
+ },
+
+ setValue: function (k) {
+ this.editor.setValue(k);
+ this._checkText();
+ },
+
+ getValue: function () {
+ return this.editor.getValue();
+ },
+
+ getState: function () {
+ return this.text.getValue();
+ },
+
+ setState: function (v) {
+ this._showHint();
+ this.text.setValue(v);
+ }
+});
+BI.SignEditor.EVENT_CHANGE = "EVENT_CHANGE";
+BI.SignEditor.EVENT_FOCUS = "EVENT_FOCUS";
+BI.SignEditor.EVENT_BLUR = "EVENT_BLUR";
+BI.SignEditor.EVENT_CLICK = "EVENT_CLICK";
+BI.SignEditor.EVENT_KEY_DOWN = "EVENT_KEY_DOWN";
+BI.SignEditor.EVENT_CLICK_LABEL = "EVENT_CLICK_LABEL";
+
+BI.SignEditor.EVENT_START = "EVENT_START";
+BI.SignEditor.EVENT_PAUSE = "EVENT_PAUSE";
+BI.SignEditor.EVENT_STOP = "EVENT_STOP";
+BI.SignEditor.EVENT_CONFIRM = "EVENT_CONFIRM";
+BI.SignEditor.EVENT_CHANGE_CONFIRM = "EVENT_CHANGE_CONFIRM";
+BI.SignEditor.EVENT_VALID = "EVENT_VALID";
+BI.SignEditor.EVENT_ERROR = "EVENT_ERROR";
+BI.SignEditor.EVENT_ENTER = "EVENT_ENTER";
+BI.SignEditor.EVENT_RESTRICT = "EVENT_RESTRICT";
+BI.SignEditor.EVENT_SPACE = "EVENT_SPACE";
+BI.SignEditor.EVENT_EMPTY = "EVENT_EMPTY";
+
+BI.shortcut("bi.sign_editor", BI.SignEditor);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/editor/editor.state.js b/src/main/resources/com/fr/fineui/case/editor/editor.state.js
new file mode 100644
index 0000000..dcd8e91
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/editor/editor.state.js
@@ -0,0 +1,311 @@
+/**
+ * guy
+ * 记录状态的输入框
+ * @class BI.StateEditor
+ * @extends BI.Single
+ */
+BI.StateEditor = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ var conf = BI.StateEditor.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-state-editor",
+ hgap: 4,
+ vgap: 2,
+ lgap: 0,
+ rgap: 0,
+ tgap: 0,
+ bgap: 0,
+ validationChecker: BI.emptyFn,
+ quitChecker: BI.emptyFn,
+ allowBlank: true,
+ watermark: "",
+ errorText: "",
+ height: 24,
+ defaultText: "", // 默认显示值,默认显示值与显示值的区别是默认显示值标记灰色
+ text: BI.i18nText("BI-Basic_Unrestricted"), // 显示值
+ el: {}
+ });
+ },
+
+ _init: function () {
+ BI.StateEditor.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.editor = BI.createWidget(o.el, {
+ type: "bi.editor",
+ height: o.height,
+ hgap: o.hgap,
+ vgap: o.vgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap,
+ value: o.value,
+ validationChecker: o.validationChecker,
+ quitChecker: o.quitChecker,
+ allowBlank: o.allowBlank,
+ watermark: o.watermark,
+ errorText: o.errorText
+ });
+ this.text = BI.createWidget({
+ type: "bi.text_button",
+ cls: "bi-water-mark tip-text-style",
+ textAlign: "left",
+ height: o.height,
+ text: o.text,
+ hgap: o.hgap + 2,
+ handler: function () {
+ self._showInput();
+ self.editor.focus();
+ self.editor.setValue("");
+ },
+ title: BI.isNotNull(o.tipText) ? o.tipText : function () {
+ var title = "";
+ if (BI.isString(self.stateValue)) {
+ title = self.stateValue;
+ }
+ if (BI.isArray(self.stateValue) && self.stateValue.length === 1) {
+ title = self.stateValue[0];
+ }
+ return title;
+ },
+ warningTitle: o.warningTitle,
+ tipType: o.tipType
+ });
+ this.text.on(BI.TextButton.EVENT_CHANGE, function () {
+ BI.nextTick(function () {
+ self.fireEvent(BI.StateEditor.EVENT_CLICK_LABEL);
+ });
+ });
+ BI.createWidget({
+ type: "bi.absolute",
+ element: this,
+ items: [{
+ el: this.text,
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0
+ }]
+ });
+ this.editor.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_FOCUS, function () {
+ self.fireEvent(BI.StateEditor.EVENT_FOCUS, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_BLUR, function () {
+ self.fireEvent(BI.StateEditor.EVENT_BLUR, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_CLICK, function () {
+ self.fireEvent(BI.StateEditor.EVENT_CLICK, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_CHANGE, function () {
+ self.fireEvent(BI.StateEditor.EVENT_CHANGE, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_KEY_DOWN, function (v) {
+ self.fireEvent(BI.StateEditor.EVENT_KEY_DOWN, arguments);
+ });
+
+ this.editor.on(BI.Editor.EVENT_VALID, function () {
+ self.fireEvent(BI.StateEditor.EVENT_VALID, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_CONFIRM, function () {
+ self._showHint();
+ self.fireEvent(BI.StateEditor.EVENT_CONFIRM, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_CHANGE_CONFIRM, function () {
+ self._showHint();
+ self.fireEvent(BI.StateEditor.EVENT_CHANGE_CONFIRM, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_START, function () {
+ self.fireEvent(BI.StateEditor.EVENT_START, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_PAUSE, function () {
+ self.fireEvent(BI.StateEditor.EVENT_PAUSE, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_STOP, function () {
+ self.fireEvent(BI.StateEditor.EVENT_STOP, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_SPACE, function () {
+ self.fireEvent(BI.StateEditor.EVENT_SPACE, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_ERROR, function () {
+ self.fireEvent(BI.StateEditor.EVENT_ERROR, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_ENTER, function () {
+ self.fireEvent(BI.StateEditor.EVENT_ENTER, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_RESTRICT, function () {
+ self.fireEvent(BI.StateEditor.EVENT_RESTRICT, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_EMPTY, function () {
+ self.fireEvent(BI.StateEditor.EVENT_EMPTY, arguments);
+ });
+ BI.createWidget({
+ type: "bi.vertical",
+ scrolly: false,
+ element: this,
+ items: [this.editor]
+ });
+ this._showHint();
+ if (BI.isNotNull(o.text)) {
+ this.setState(o.text);
+ }
+ },
+
+ setWaterMark: function (v) {
+ this.options.watermark = v;
+ this.editor.setWaterMark(v);
+ },
+
+ doRedMark: function () {
+ if (this.editor.getValue() === "" && BI.isKey(this.options.watermark)) {
+ return;
+ }
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doHighLight: function () {
+ if (this.editor.getValue() === "" && BI.isKey(this.options.watermark)) {
+ return;
+ }
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ },
+
+ focus: function () {
+ if (this.options.disabled === false) {
+ this._showInput();
+ this.editor.focus();
+ }
+ },
+
+ blur: function () {
+ this.editor.blur();
+ this._showHint();
+ },
+
+ _showInput: function () {
+ this.editor.visible();
+ this.text.invisible();
+ },
+
+ _showHint: function () {
+ this.editor.invisible();
+ this.text.visible();
+ },
+
+ _setText: function (v) {
+ this.text.setText(v);
+ this.text.setTitle(v);
+ },
+
+ isValid: function () {
+ return this.editor.isValid();
+ },
+
+ setErrorText: function (text) {
+ this.editor.setErrorText(text);
+ },
+
+ getErrorText: function () {
+ return this.editor.getErrorText();
+ },
+
+ isEditing: function () {
+ return this.editor.isEditing();
+ },
+
+ getLastValidValue: function () {
+ return this.editor.getLastValidValue();
+ },
+
+ getLastChangedValue: function () {
+ return this.editor.getLastChangedValue();
+ },
+
+ setValue: function (k) {
+ this.editor.setValue(k);
+ },
+
+ getValue: function () {
+ return this.editor.getValue();
+ },
+
+ getState: function () {
+ return this.editor.getValue().match(/[^\s]+/g);
+ },
+
+ setState: function (v) {
+ var o = this.options;
+ var defaultText = BI.isFunction(o.defaultText) ? o.defaultText() : o.defaultText;
+ BI.StateEditor.superclass.setValue.apply(this, arguments);
+ this.stateValue = v;
+ if (BI.isNumber(v)) {
+ if (v === BI.Selection.All) {
+ this._setText(BI.i18nText("BI-Select_All"));
+ this.text.element.removeClass("bi-water-mark");
+ } else if (v === BI.Selection.Multi) {
+ this._setText(BI.i18nText("BI-Select_Part"));
+ this.text.element.removeClass("bi-water-mark");
+ } else {
+ this._setText(BI.isKey(defaultText) ? defaultText : o.text);
+ BI.isKey(defaultText) ? this.text.element.addClass("bi-water-mark") : this.text.element.removeClass("bi-water-mark");
+ }
+ return;
+ }
+ if (BI.isString(v)) {
+ this._setText(v);
+ // 配置了defaultText才判断标灰,其他情况不标灰
+ (BI.isKey(defaultText) && defaultText === v) ? this.text.element.addClass("bi-water-mark") : this.text.element.removeClass("bi-water-mark");
+ return;
+ }
+ if (BI.isArray(v)) {
+ if (BI.isEmpty(v)) {
+ this._setText(BI.isKey(defaultText) ? defaultText : o.text);
+ BI.isKey(defaultText) ? this.text.element.addClass("bi-water-mark") : this.text.element.removeClass("bi-water-mark");
+ } else if (v.length === 1) {
+ this._setText(v[0]);
+ this.text.element.removeClass("bi-water-mark");
+ } else {
+ this._setText(BI.i18nText("BI-Select_Part"));
+ this.text.element.removeClass("bi-water-mark");
+ }
+ }
+ },
+
+ setTipType: function (v) {
+ this.text.options.tipType = v;
+ },
+
+ getText: function () {
+ return this.text.getText();
+ }
+});
+BI.StateEditor.EVENT_CHANGE = "EVENT_CHANGE";
+BI.StateEditor.EVENT_FOCUS = "EVENT_FOCUS";
+BI.StateEditor.EVENT_BLUR = "EVENT_BLUR";
+BI.StateEditor.EVENT_CLICK = "EVENT_CLICK";
+BI.StateEditor.EVENT_KEY_DOWN = "EVENT_KEY_DOWN";
+BI.StateEditor.EVENT_CLICK_LABEL = "EVENT_CLICK_LABEL";
+
+BI.StateEditor.EVENT_START = "EVENT_START";
+BI.StateEditor.EVENT_PAUSE = "EVENT_PAUSE";
+BI.StateEditor.EVENT_STOP = "EVENT_STOP";
+BI.StateEditor.EVENT_CONFIRM = "EVENT_CONFIRM";
+BI.StateEditor.EVENT_CHANGE_CONFIRM = "EVENT_CHANGE_CONFIRM";
+BI.StateEditor.EVENT_VALID = "EVENT_VALID";
+BI.StateEditor.EVENT_ERROR = "EVENT_ERROR";
+BI.StateEditor.EVENT_ENTER = "EVENT_ENTER";
+BI.StateEditor.EVENT_RESTRICT = "EVENT_RESTRICT";
+BI.StateEditor.EVENT_SPACE = "EVENT_SPACE";
+BI.StateEditor.EVENT_EMPTY = "EVENT_EMPTY";
+
+BI.shortcut("bi.state_editor", BI.StateEditor);
diff --git a/src/main/resources/com/fr/fineui/case/editor/editor.state.simple.js b/src/main/resources/com/fr/fineui/case/editor/editor.state.simple.js
new file mode 100644
index 0000000..6fab8d6
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/editor/editor.state.simple.js
@@ -0,0 +1,282 @@
+/**
+ * 无限制-已选择状态输入框
+ * Created by GUY on 2016/5/18.
+ * @class BI.SimpleStateEditor
+ * @extends BI.Single
+ */
+BI.SimpleStateEditor = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ var conf = BI.SimpleStateEditor.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-simple-state-editor",
+ hgap: 4,
+ vgap: 2,
+ lgap: 0,
+ rgap: 0,
+ tgap: 0,
+ bgap: 0,
+ validationChecker: BI.emptyFn,
+ quitChecker: BI.emptyFn,
+ mouseOut: false,
+ allowBlank: true,
+ watermark: "",
+ errorText: "",
+ height: 24,
+ text: BI.i18nText("BI-Basic_Unrestricted")
+ });
+ },
+
+ _init: function () {
+ BI.SimpleStateEditor.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.editor = BI.createWidget({
+ type: "bi.editor",
+ height: o.height,
+ hgap: o.hgap,
+ vgap: o.vgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap,
+ value: o.value,
+ validationChecker: o.validationChecker,
+ quitChecker: o.quitChecker,
+ allowBlank: o.allowBlank,
+ watermark: o.watermark,
+ errorText: o.errorText
+ });
+ this.text = BI.createWidget({
+ type: "bi.text_button",
+ cls: "bi-water-mark",
+ textAlign: "left",
+ text: o.text,
+ height: o.height,
+ hgap: o.hgap + 2,
+ handler: function () {
+ self._showInput();
+ self.editor.focus();
+ self.editor.setValue("");
+ }
+ });
+ this.text.on(BI.TextButton.EVENT_CHANGE, function () {
+ BI.nextTick(function () {
+ self.fireEvent(BI.SimpleStateEditor.EVENT_CLICK_LABEL);
+ });
+ });
+ BI.createWidget({
+ type: "bi.absolute",
+ element: this,
+ items: [{
+ el: this.text,
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0
+ }]
+ });
+ this.editor.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_FOCUS, function () {
+ self.fireEvent(BI.SimpleStateEditor.EVENT_FOCUS, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_BLUR, function () {
+ self.fireEvent(BI.SimpleStateEditor.EVENT_BLUR, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_CLICK, function () {
+ self.fireEvent(BI.SimpleStateEditor.EVENT_CLICK, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_CHANGE, function () {
+ self.fireEvent(BI.SimpleStateEditor.EVENT_CHANGE, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_KEY_DOWN, function (v) {
+ self.fireEvent(BI.SimpleStateEditor.EVENT_KEY_DOWN, arguments);
+ });
+
+ this.editor.on(BI.Editor.EVENT_VALID, function () {
+ self.fireEvent(BI.SimpleStateEditor.EVENT_VALID, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_CONFIRM, function () {
+ self._showHint();
+ self.fireEvent(BI.SimpleStateEditor.EVENT_CONFIRM, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_CHANGE_CONFIRM, function () {
+ self._showHint();
+ self.fireEvent(BI.SimpleStateEditor.EVENT_CHANGE_CONFIRM, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_START, function () {
+ self.fireEvent(BI.SimpleStateEditor.EVENT_START, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_PAUSE, function () {
+ self.fireEvent(BI.SimpleStateEditor.EVENT_PAUSE, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_STOP, function () {
+ self.fireEvent(BI.SimpleStateEditor.EVENT_STOP, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_SPACE, function () {
+ self.fireEvent(BI.SimpleStateEditor.EVENT_SPACE, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_ERROR, function () {
+ self.fireEvent(BI.SimpleStateEditor.EVENT_ERROR, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_ENTER, function () {
+ self.fireEvent(BI.SimpleStateEditor.EVENT_ENTER, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_RESTRICT, function () {
+ self.fireEvent(BI.SimpleStateEditor.EVENT_RESTRICT, arguments);
+ });
+ this.editor.on(BI.Editor.EVENT_EMPTY, function () {
+ self.fireEvent(BI.SimpleStateEditor.EVENT_EMPTY, arguments);
+ });
+ BI.createWidget({
+ type: "bi.vertical",
+ scrolly: false,
+ element: this,
+ items: [this.editor]
+ });
+ this._showHint();
+ if(BI.isNotNull(o.text)){
+ this.setState(o.text);
+ }
+ },
+
+ setWaterMark: function (v) {
+ this.options.watermark = v;
+ this.editor.setWaterMark(v);
+ },
+
+ doRedMark: function () {
+ if (this.editor.getValue() === "" && BI.isKey(this.options.watermark)) {
+ return;
+ }
+ this.text.doRedMark.apply(this.text, arguments);
+ },
+
+ unRedMark: function () {
+ this.text.unRedMark.apply(this.text, arguments);
+ },
+
+ doHighLight: function () {
+ if (this.editor.getValue() === "" && BI.isKey(this.options.watermark)) {
+ return;
+ }
+ this.text.doHighLight.apply(this.text, arguments);
+ },
+
+ unHighLight: function () {
+ this.text.unHighLight.apply(this.text, arguments);
+ },
+
+ focus: function () {
+ this._showInput();
+ this.editor.focus();
+ },
+
+ blur: function () {
+ this.editor.blur();
+ this._showHint();
+ },
+
+ _showInput: function () {
+ this.editor.visible();
+ this.text.invisible();
+ },
+
+ _showHint: function () {
+ this.editor.invisible();
+ this.text.visible();
+ },
+
+ _setText: function (v) {
+ this.text.setText(v);
+ this.text.setTitle(v);
+ },
+
+ isValid: function () {
+ return this.editor.isValid();
+ },
+
+ setErrorText: function (text) {
+ this.editor.setErrorText(text);
+ },
+
+ getErrorText: function () {
+ return this.editor.getErrorText();
+ },
+
+ isEditing: function () {
+ return this.editor.isEditing();
+ },
+
+ getLastValidValue: function () {
+ return this.editor.getLastValidValue();
+ },
+
+ getLastChangedValue: function () {
+ return this.editor.getLastChangedValue();
+ },
+
+ setValue: function (k) {
+ this.editor.setValue(k);
+ },
+
+ getValue: function () {
+ return this.editor.getValue();
+ },
+
+ getState: function () {
+ return this.editor.getValue().match(/[^\s]+/g);
+ },
+
+ setState: function (v) {
+ var o = this.options;
+ BI.SimpleStateEditor.superclass.setValue.apply(this, arguments);
+ if (BI.isNumber(v)) {
+ if (v === BI.Selection.All) {
+ this._setText(BI.i18nText("BI-Already_Selected"));
+ this.text.element.removeClass("bi-water-mark");
+ } else if (v === BI.Selection.Multi) {
+ this._setText(BI.i18nText("BI-Already_Selected"));
+ this.text.element.removeClass("bi-water-mark");
+ } else {
+ this._setText(o.text);
+ this.text.element.addClass("bi-water-mark");
+ }
+ return;
+ }
+ if (!BI.isArray(v) || v.length === 1) {
+ this._setText(v);
+ this.text.element.removeClass("bi-water-mark");
+ } else if (BI.isEmpty(v)) {
+ this._setText(o.text);
+ this.text.element.addClass("bi-water-mark");
+ } else {
+ this._setText(BI.i18nText("BI-Already_Selected"));
+ this.text.element.removeClass("bi-water-mark");
+ }
+ },
+
+ getText: function () {
+ return this.text.getText();
+ }
+});
+BI.SimpleStateEditor.EVENT_CHANGE = "EVENT_CHANGE";
+BI.SimpleStateEditor.EVENT_FOCUS = "EVENT_FOCUS";
+BI.SimpleStateEditor.EVENT_BLUR = "EVENT_BLUR";
+BI.SimpleStateEditor.EVENT_CLICK = "EVENT_CLICK";
+BI.SimpleStateEditor.EVENT_KEY_DOWN = "EVENT_KEY_DOWN";
+BI.SimpleStateEditor.EVENT_CLICK_LABEL = "EVENT_CLICK_LABEL";
+
+BI.SimpleStateEditor.EVENT_START = "EVENT_START";
+BI.SimpleStateEditor.EVENT_PAUSE = "EVENT_PAUSE";
+BI.SimpleStateEditor.EVENT_STOP = "EVENT_STOP";
+BI.SimpleStateEditor.EVENT_CONFIRM = "EVENT_CONFIRM";
+BI.SimpleStateEditor.EVENT_CHANGE_CONFIRM = "EVENT_CHANGE_CONFIRM";
+BI.SimpleStateEditor.EVENT_VALID = "EVENT_VALID";
+BI.SimpleStateEditor.EVENT_ERROR = "EVENT_ERROR";
+BI.SimpleStateEditor.EVENT_ENTER = "EVENT_ENTER";
+BI.SimpleStateEditor.EVENT_RESTRICT = "EVENT_RESTRICT";
+BI.SimpleStateEditor.EVENT_SPACE = "EVENT_SPACE";
+BI.SimpleStateEditor.EVENT_EMPTY = "EVENT_EMPTY";
+
+BI.shortcut("bi.simple_state_editor", BI.SimpleStateEditor);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/layer/layer.multipopup.js b/src/main/resources/com/fr/fineui/case/layer/layer.multipopup.js
new file mode 100644
index 0000000..3979c69
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/layer/layer.multipopup.js
@@ -0,0 +1,60 @@
+/**
+ * 下拉框弹出层的多选版本,toolbar带有若干按钮, zIndex在1000w
+ * @class BI.MultiPopupView
+ * @extends BI.Widget
+ */
+
+BI.MultiPopupView = BI.inherit(BI.PopupView, {
+
+ _defaultConfig: function () {
+ var conf = BI.MultiPopupView.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ _baseCls: (conf._baseCls || "") + " bi-multi-list-view",
+ buttons: [BI.i18nText("BI-Basic_Sure")]
+ });
+ },
+
+ _createToolBar: function () {
+ var o = this.options, self = this;
+ if (o.buttons.length === 0) {
+ return;
+ }
+
+ var text = []; // 构造[{text:content},……]
+ BI.each(o.buttons, function (idx, item) {
+ text.push({
+ text: item,
+ value: idx
+ });
+ });
+
+ this.buttongroup = BI.createWidget({
+ type: "bi.button_group",
+ cls: "list-view-toolbar bi-high-light bi-split-top",
+ height: BI.SIZE_CONSANTS.LIST_ITEM_HEIGHT,
+ items: BI.createItems(text, {
+ type: "bi.text_button",
+ once: false,
+ shadow: true,
+ isShadowShowingOnSelected: true
+ }),
+ layouts: [{
+ type: "bi.center",
+ hgap: 0,
+ vgap: 0
+ }]
+ });
+
+ this.buttongroup.on(BI.ButtonGroup.EVENT_CHANGE, function (value, obj) {
+ self.fireEvent(BI.MultiPopupView.EVENT_CLICK_TOOLBAR_BUTTON, value, obj);
+ });
+
+ return this.buttongroup;
+ }
+
+});
+
+BI.MultiPopupView.EVENT_CHANGE = "EVENT_CHANGE";
+BI.MultiPopupView.EVENT_CLICK_TOOLBAR_BUTTON = "EVENT_CLICK_TOOLBAR_BUTTON";
+
+BI.shortcut("bi.multi_popup_view", BI.MultiPopupView);
diff --git a/src/main/resources/com/fr/fineui/case/layer/layer.panel.js b/src/main/resources/com/fr/fineui/case/layer/layer.panel.js
new file mode 100644
index 0000000..c136cc0
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/layer/layer.panel.js
@@ -0,0 +1,53 @@
+/**
+ * 可以理解为MultiPopupView和Panel两个面板的结合体
+ * @class BI.PopupPanel
+ * @extends BI.MultiPopupView
+ */
+
+BI.PopupPanel = BI.inherit(BI.MultiPopupView, {
+
+ _defaultConfig: function () {
+ var conf = BI.PopupPanel.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-popup-panel",
+ title: ""
+ });
+ },
+
+ _createTool: function () {
+ var self = this, o = this.options;
+ var close = BI.createWidget({
+ type: "bi.icon_button",
+ cls: "close-h-font",
+ width: 25,
+ height: 25
+ });
+ close.on(BI.IconButton.EVENT_CHANGE, function () {
+ self.setVisible(false);
+ self.fireEvent(BI.PopupPanel.EVENT_CLOSE);
+ });
+ return BI.createWidget({
+ type: "bi.htape",
+ cls: "popup-panel-title bi-header-background",
+ height: 25,
+ items: [{
+ el: {
+ type: "bi.label",
+ textAlign: "left",
+ text: o.title,
+ height: 25,
+ lgap: 10
+ }
+ }, {
+ el: close,
+ width: 25
+ }]
+ });
+ }
+});
+
+BI.PopupPanel.EVENT_CHANGE = "EVENT_CHANGE";
+BI.PopupPanel.EVENT_CLOSE = "EVENT_CLOSE";
+BI.PopupPanel.EVENT_CLICK_TOOLBAR_BUTTON = "EVENT_CLICK_TOOLBAR_BUTTON";
+
+BI.shortcut("bi.popup_panel", BI.PopupPanel);
diff --git a/src/main/resources/com/fr/fineui/case/layer/pane.list.js b/src/main/resources/com/fr/fineui/case/layer/pane.list.js
new file mode 100644
index 0000000..efeb8c8
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/layer/pane.list.js
@@ -0,0 +1,186 @@
+/**
+ * list面板
+ *
+ * Created by GUY on 2015/10/30.
+ * @class BI.ListPane
+ * @extends BI.Pane
+ */
+BI.ListPane = BI.inherit(BI.Pane, {
+
+ _defaultConfig: function () {
+ var conf = BI.ListPane.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-list-pane",
+ logic: {
+ dynamic: true
+ },
+ lgap: 0,
+ rgap: 0,
+ tgap: 0,
+ bgap: 0,
+ vgap: 0,
+ hgap: 0,
+ items: [],
+ itemsCreator: BI.emptyFn,
+ hasNext: BI.emptyFn,
+ onLoaded: BI.emptyFn,
+ el: {
+ type: "bi.button_group"
+ }
+ });
+ },
+ _init: function () {
+ BI.ListPane.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+
+ this.button_group = BI.createWidget(o.el, {
+ type: "bi.button_group",
+ chooseType: BI.ButtonGroup.CHOOSE_TYPE_SINGLE,
+ behaviors: {},
+ items: o.items,
+ itemsCreator: function (op, calback) {
+ if (op.times === 1) {
+ self.empty();
+ BI.nextTick(function () {
+ self.loading();
+ });
+ }
+ o.itemsCreator(op, function () {
+ calback.apply(self, arguments);
+ op.times === 1 && BI.nextTick(function () {
+ self.loaded();
+ // callback可能在loading之前执行, check保证显示正确
+ self.check();
+ });
+ });
+ },
+ hasNext: o.hasNext,
+ layouts: [{
+ type: "bi.vertical"
+ }]
+ });
+
+ this.button_group.on(BI.Controller.EVENT_CHANGE, function (type, value, obj) {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ if (type === BI.Events.CLICK) {
+ self.fireEvent(BI.ListPane.EVENT_CHANGE, value, obj);
+ }
+ });
+ this.check();
+
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic(BI.LogicFactory.createLogicTypeByDirection(BI.Direction.Top), BI.extend({
+ scrolly: true,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap,
+ vgap: o.vgap,
+ hgap: o.hgap
+ }, o.logic, {
+ items: BI.LogicFactory.createLogicItemsByDirection(BI.Direction.Top, this.button_group)
+ }))));
+ },
+
+ hasPrev: function () {
+ return this.button_group.hasPrev && this.button_group.hasPrev();
+ },
+
+ hasNext: function () {
+ return this.button_group.hasNext && this.button_group.hasNext();
+ },
+
+ prependItems: function (items) {
+ this.options.items = items.concat(this.options.items);
+ this.button_group.prependItems.apply(this.button_group, arguments);
+ this.check();
+ },
+
+ addItems: function (items) {
+ this.options.items = this.options.items.concat(items);
+ this.button_group.addItems.apply(this.button_group, arguments);
+ this.check();
+ },
+
+ removeItemAt: function (indexes) {
+ indexes = BI.isNull(indexes) ? [] : indexes;
+ BI.removeAt(this.options.items, indexes);
+ this.button_group.removeItemAt.apply(this.button_group, arguments);
+ this.check();
+ },
+
+ populate: function (items) {
+ var self = this, o = this.options;
+ if (arguments.length === 0 && (BI.isFunction(this.button_group.attr("itemsCreator")))) {// 接管loader的populate方法
+ this.button_group.attr("itemsCreator").apply(this, [{times: 1}, function () {
+ if (arguments.length === 0) {
+ throw new Error("参数不能为空");
+ }
+ self.populate.apply(self, arguments);
+ }]);
+ return;
+ }
+
+ var context = BI.get(arguments, [2], {});
+ var tipText = context.tipText || '';
+ if (BI.isNotEmptyString(tipText)) {
+ BI.ListPane.superclass.populate.apply(this, []);
+ this.setTipText(tipText);
+ } else {
+ BI.ListPane.superclass.populate.apply(this, arguments);
+ this.button_group.populate.apply(this.button_group, arguments);
+ BI.isEmptyArray(BI.get(arguments, [0], [])) && this.setTipText(o.tipText);
+ }
+ },
+
+ empty: function () {
+ this.button_group.empty();
+ },
+
+ setNotSelectedValue: function () {
+ this.button_group.setNotSelectedValue.apply(this.button_group, arguments);
+ },
+
+ getNotSelectedValue: function () {
+ return this.button_group.getNotSelectedValue();
+ },
+
+ setValue: function () {
+ this.button_group.setValue.apply(this.button_group, arguments);
+ },
+
+ getValue: function () {
+ return this.button_group.getValue.apply(this.button_group, arguments);
+ },
+
+ getAllButtons: function () {
+ return this.button_group.getAllButtons();
+ },
+
+ getAllLeaves: function () {
+ return this.button_group.getAllLeaves();
+ },
+
+ getSelectedButtons: function () {
+ return this.button_group.getSelectedButtons();
+ },
+
+ getNotSelectedButtons: function () {
+ return this.button_group.getNotSelectedButtons();
+ },
+
+ getIndexByValue: function (value) {
+ return this.button_group.getIndexByValue(value);
+ },
+
+ getNodeById: function (id) {
+ return this.button_group.getNodeById(id);
+ },
+
+ getNodeByValue: function (value) {
+ return this.button_group.getNodeByValue(value);
+ }
+});
+BI.ListPane.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.list_pane", BI.ListPane);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/layer/panel.js b/src/main/resources/com/fr/fineui/case/layer/panel.js
new file mode 100644
index 0000000..d24b7b5
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/layer/panel.js
@@ -0,0 +1,79 @@
+/**
+ * 带有标题栏的pane
+ * @class BI.Panel
+ * @extends BI.Widget
+ */
+BI.Panel = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ return BI.extend(BI.Panel.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-panel bi-border",
+ title: "",
+ titleButtons: [],
+ el: {},
+ logic: {
+ dynamic: false
+ }
+ });
+ },
+
+ _init: function () {
+ BI.Panel.superclass._init.apply(this, arguments);
+ var o = this.options;
+
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic("vertical", BI.extend(o.logic, {
+ items: BI.LogicFactory.createLogicItemsByDirection("top", this._createTitle()
+ , this.options.el)
+ }))));
+ },
+
+ _createTitle: function () {
+ var self = this, o = this.options;
+ this.text = BI.createWidget({
+ type: "bi.label",
+ cls: "panel-title-text",
+ text: o.title,
+ height: 30
+ });
+
+ this.button_group = BI.createWidget({
+ type: "bi.button_group",
+ items: o.titleButtons,
+ layouts: [{
+ type: "bi.center_adapt",
+ lgap: 10
+ }]
+ });
+
+ this.button_group.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+
+ this.button_group.on(BI.ButtonGroup.EVENT_CHANGE, function (value, obj) {
+ self.fireEvent(BI.Panel.EVENT_CHANGE, value, obj);
+ });
+
+ return {
+ el: {
+ type: "bi.left_right_vertical_adapt",
+ cls: "panel-title bi-header-background bi-border-bottom",
+ height: 29,
+ items: {
+ left: [this.text],
+ right: [this.button_group]
+ },
+ lhgap: 10,
+ rhgap: 10
+ },
+ height: 29
+ };
+ },
+
+ setTitle: function (title) {
+ this.text.setValue(title);
+ }
+});
+BI.Panel.EVENT_CHANGE = "EVENT_CHANGE";
+
+BI.shortcut("bi.panel", BI.Panel);
diff --git a/src/main/resources/com/fr/fineui/case/linersegment/button.linear.segment.js b/src/main/resources/com/fr/fineui/case/linersegment/button.linear.segment.js
new file mode 100644
index 0000000..be84038
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/linersegment/button.linear.segment.js
@@ -0,0 +1,54 @@
+BI.LinearSegmentButton = BI.inherit(BI.BasicButton, {
+
+ props: {
+ extraCls: "bi-line-segment-button bi-list-item-effect",
+ once: true,
+ readonly: true,
+ hgap: 10,
+ height: 25
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+
+ return [{
+ type: "bi.label",
+ text: o.text,
+ height: o.height,
+ value: o.value,
+ hgap: o.hgap,
+ ref: function () {
+ self.text = this;
+ }
+ }, {
+ type: "bi.absolute",
+ items: [{
+ el: {
+ type: "bi.layout",
+ cls: "line-segment-button-line",
+ height: 2,
+ ref: function () {
+ self.line = this;
+ }
+ },
+ left: 0,
+ right: 0,
+ bottom: 0
+ }]
+ }];
+ },
+
+ setSelected: function (v) {
+ BI.LinearSegmentButton.superclass.setSelected.apply(this, arguments);
+ if (v) {
+ this.line.element.addClass("bi-high-light-background");
+ } else {
+ this.line.element.removeClass("bi-high-light-background");
+ }
+ },
+
+ setText: function (text) {
+ this.text.setText(text);
+ }
+});
+BI.shortcut("bi.linear_segment_button", BI.LinearSegmentButton);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/linersegment/linear.segment.js b/src/main/resources/com/fr/fineui/case/linersegment/linear.segment.js
new file mode 100644
index 0000000..f0060dc
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/linersegment/linear.segment.js
@@ -0,0 +1,51 @@
+BI.LinearSegment = BI.inherit(BI.Widget, {
+
+ props: {
+ baseCls: "bi-linear-segment",
+ items: [],
+ layouts: [{
+ type: "bi.center"
+ }],
+ height: 29
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ return {
+ type: "bi.button_group",
+ items: BI.createItems(o.items, {
+ type: "bi.linear_segment_button",
+ height: o.height - 1
+ }),
+ layouts: o.layouts,
+ listeners: [{
+ eventName: "__EVENT_CHANGE__",
+ action: function () {
+ self.fireEvent("__EVENT_CHANGE__", arguments);
+ }
+ }, {
+ eventName: "EVENT_CHANGE",
+ action: function () {
+ self.fireEvent("EVENT_CHANGE");
+ }
+ }],
+ ref: function () {
+ self.buttonGroup = this;
+ }
+ };
+ },
+
+ setValue: function (v) {
+ this.buttonGroup.setValue(v);
+ },
+
+ setEnabledValue: function (v) {
+ this.buttonGroup.setEnabledValue(v);
+ },
+
+
+ getValue: function () {
+ return this.buttonGroup.getValue();
+ }
+});
+BI.shortcut("bi.linear_segment", BI.LinearSegment);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/list/list.select.js b/src/main/resources/com/fr/fineui/case/list/list.select.js
new file mode 100644
index 0000000..12f71e3
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/list/list.select.js
@@ -0,0 +1,219 @@
+/**
+ * 选择列表
+ *
+ * Created by GUY on 2015/11/1.
+ * @class BI.SelectList
+ * @extends BI.Widget
+ */
+BI.SelectList = BI.inherit(BI.Widget, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.SelectList.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-select-list",
+ direction: BI.Direction.Top, // toolbar的位置
+ logic: {
+ dynamic: true
+ },
+ items: [],
+ itemsCreator: BI.emptyFn,
+ hasNext: BI.emptyFn,
+ onLoaded: BI.emptyFn,
+ toolbar: {
+ type: "bi.multi_select_bar",
+ iconWrapperWidth: 36
+ },
+ el: {
+ type: "bi.list_pane"
+ }
+ });
+ },
+ _init: function () {
+ BI.SelectList.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+
+ // 全选
+ this.toolbar = BI.createWidget(o.toolbar);
+ this.allSelected = false;
+ this.toolbar.on(BI.Controller.EVENT_CHANGE, function (type, value, obj) {
+ self.allSelected = this.isSelected();
+ if (type === BI.Events.CLICK) {
+ self.setAllSelected(self.allSelected);
+ self.fireEvent(BI.SelectList.EVENT_CHANGE, value, obj);
+ }
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+
+ this.list = BI.createWidget(o.el, {
+ type: "bi.list_pane",
+ items: o.items,
+ itemsCreator: function (op, callback) {
+ op.times === 1 && self.toolbar.setVisible(false);
+ o.itemsCreator(op, function (items, keywords, context) {
+ callback.apply(self, arguments);
+ if (op.times === 1) {
+ var tipText = BI.get(context, 'tipText', '');
+ var visible = BI.isEmptyString(tipText) && items && items.length > 0;
+ self.toolbar.setVisible(visible);
+ self.toolbar.setEnable(self.isEnabled() && visible);
+ }
+ self._checkAllSelected();
+ });
+ },
+ onLoaded: o.onLoaded,
+ hasNext: o.hasNext
+ });
+
+ this.list.on(BI.Controller.EVENT_CHANGE, function (type, value, obj) {
+ if (type === BI.Events.CLICK) {
+ self._checkAllSelected();
+ self.fireEvent(BI.SelectList.EVENT_CHANGE, value, obj);
+ }
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+
+ BI.createWidget(BI.extend({
+ element: this
+ }, BI.LogicFactory.createLogic(BI.LogicFactory.createLogicTypeByDirection(o.direction), BI.extend({
+ scrolly: true
+ }, o.logic, {
+ items: BI.LogicFactory.createLogicItemsByDirection(o.direction, this.toolbar, this.list)
+ }))));
+
+ if (o.items.length <= 0) {
+ this.toolbar.setVisible(false);
+ this.toolbar.setEnable(false);
+ }
+ if(BI.isNotNull(o.value)){
+ this.setValue(o.value);
+ }
+ },
+
+ _checkAllSelected: function () {
+ var selectLength = this.list.getValue().length;
+ var notSelectLength = this.getAllLeaves().length - selectLength;
+ var hasNext = this.list.hasNext();
+ var isAlreadyAllSelected = this.toolbar.isSelected();
+ var isHalf = selectLength > 0 && (notSelectLength > 0 || (!isAlreadyAllSelected && hasNext));
+ isHalf = isHalf || (notSelectLength > 0 && hasNext && isAlreadyAllSelected);
+ this.toolbar.setHalfSelected(isHalf);
+ !isHalf && this.toolbar.setSelected(selectLength > 0 && notSelectLength <= 0 && (!hasNext || isAlreadyAllSelected));
+ },
+
+ setAllSelected: function (v) {
+ BI.each(this.getAllButtons(), function (i, btn) {
+ (btn.setSelected || btn.setAllSelected).apply(btn, [v]);
+ });
+ this.allSelected = !!v;
+ this.toolbar.setSelected(v);
+ this.toolbar.setHalfSelected(false);
+ },
+
+ setToolBarVisible: function (b) {
+ this.toolbar.setVisible(b);
+ },
+
+ isAllSelected: function () {
+ return this.allSelected;
+ // return this.toolbar.isSelected();
+ },
+
+ hasPrev: function () {
+ return this.list.hasPrev();
+ },
+
+ hasNext: function () {
+ return this.list.hasNext();
+ },
+
+ prependItems: function (items) {
+ this.list.prependItems.apply(this.list, arguments);
+ },
+
+ addItems: function (items) {
+ this.list.addItems.apply(this.list, arguments);
+ },
+
+ setValue: function (data) {
+ var selectAll = data.type === BI.ButtonGroup.CHOOSE_TYPE_ALL;
+ this.setAllSelected(selectAll);
+ this.list[selectAll ? "setNotSelectedValue" : "setValue"](data.value);
+ this._checkAllSelected();
+ },
+
+ getValue: function () {
+ if (this.isAllSelected() === false) {
+ return {
+ type: BI.ButtonGroup.CHOOSE_TYPE_MULTI,
+ value: this.list.getValue(),
+ assist: this.list.getNotSelectedValue()
+ };
+ }
+ return {
+ type: BI.ButtonGroup.CHOOSE_TYPE_ALL,
+ value: this.list.getNotSelectedValue(),
+ assist: this.list.getValue()
+ };
+
+ },
+
+ empty: function () {
+ this.list.empty();
+ },
+
+ populate: function (items) {
+ this.toolbar.setVisible(!BI.isEmptyArray(items));
+ this.toolbar.setEnable(this.isEnabled() && !BI.isEmptyArray(items));
+ this.list.populate.apply(this.list, arguments);
+ this._checkAllSelected();
+ },
+
+ _setEnable: function (enable) {
+ BI.SelectList.superclass._setEnable.apply(this, arguments);
+ this.toolbar.setEnable(enable);
+ },
+
+ resetHeight: function (h) {
+ var toolHeight = ( this.toolbar.element.outerHeight() || 25) * ( this.toolbar.isVisible() ? 1 : 0);
+ this.list.resetHeight ? this.list.resetHeight(h - toolHeight) :
+ this.list.element.css({"max-height": (h - toolHeight) / BI.pixRatio + BI.pixUnit});
+ },
+
+ setNotSelectedValue: function () {
+ this.list.setNotSelectedValue.apply(this.list, arguments);
+ this._checkAllSelected();
+ },
+
+ getNotSelectedValue: function () {
+ return this.list.getNotSelectedValue();
+ },
+
+ getAllButtons: function () {
+ return this.list.getAllButtons();
+ },
+
+ getAllLeaves: function () {
+ return this.list.getAllLeaves();
+ },
+
+ getSelectedButtons: function () {
+ return this.list.getSelectedButtons();
+ },
+
+ getNotSelectedButtons: function () {
+ return this.list.getNotSelectedButtons();
+ },
+
+ getIndexByValue: function (value) {
+ return this.list.getIndexByValue(value);
+ },
+
+ getNodeById: function (id) {
+ return this.list.getNodeById(id);
+ },
+
+ getNodeByValue: function (value) {
+ return this.list.getNodeByValue(value);
+ }
+});
+BI.SelectList.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.select_list", BI.SelectList);
diff --git a/src/main/resources/com/fr/fineui/case/loader/__test__/loader.lazy.test.js b/src/main/resources/com/fr/fineui/case/loader/__test__/loader.lazy.test.js
new file mode 100644
index 0000000..8b0ae15
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/loader/__test__/loader.lazy.test.js
@@ -0,0 +1,52 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/4/3
+ */
+
+describe("lazy_loader", function () {
+
+ /**
+ * test_author_windy
+ **/
+ it("setValue", function (done) {
+ var items = BI.map(BI.range(0, 100), function (idx, v) {
+ return {
+ type: "bi.single_select_item",
+ text: v,
+ value: v
+ }
+ });
+ var widget = BI.Test.createWidget({
+ type: "bi.lazy_loader",
+ el: {
+ layouts: [{
+ type: "bi.left",
+ hgap: 5
+ }]
+ },
+ items: items
+ });
+ BI.nextTick(function () {
+ expect(widget.getAllButtons().length).to.equal(100);
+ widget.addItems([{
+ type: "bi.single_select_item",
+ text: 102,
+ value: 102
+ }, {
+ type: "bi.single_select_item",
+ text: 103,
+ value: 103
+ }]);
+ expect(widget.getAllLeaves().length).to.equal(102);
+ widget.setValue(102);
+ expect(widget.getValue()[0]).to.equal(102);
+ expect(widget.getSelectedButtons().length).to.equal(1);
+ widget.setNotSelectedValue(102);
+ expect(widget.getNotSelectedValue()[0]).to.equal(102);
+ expect(widget.getNotSelectedButtons().length).to.equal(1);
+ widget.destroy();
+ done();
+ });
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/loader/__test__/loader.list.test.js b/src/main/resources/com/fr/fineui/case/loader/__test__/loader.list.test.js
new file mode 100644
index 0000000..cad5f51
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/loader/__test__/loader.list.test.js
@@ -0,0 +1,52 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/4/3
+ */
+
+describe("list_loader", function () {
+
+ /**
+ * test_author_windy
+ **/
+ it("setValue", function (done) {
+ var items = BI.map(BI.range(0, 100), function (idx, v) {
+ return {
+ type: "bi.single_select_item",
+ text: v,
+ value: v
+ }
+ });
+ var widget = BI.Test.createWidget({
+ type: "bi.list_loader",
+ el: {
+ layouts: [{
+ type: "bi.left",
+ hgap: 5
+ }]
+ },
+ items: items
+ });
+ BI.nextTick(function () {
+ expect(widget.getAllButtons().length).to.equal(100);
+ widget.addItems([{
+ type: "bi.single_select_item",
+ text: 102,
+ value: 102
+ }, {
+ type: "bi.single_select_item",
+ text: 103,
+ value: 103
+ }]);
+ expect(widget.getAllLeaves().length).to.equal(102);
+ widget.setValue(102);
+ expect(widget.getValue()[0]).to.equal(102);
+ expect(widget.getSelectedButtons().length).to.equal(1);
+ widget.setNotSelectedValue(102);
+ expect(widget.getNotSelectedValue()[0]).to.equal(102);
+ expect(widget.getNotSelectedButtons().length).to.equal(1);
+ widget.destroy();
+ done();
+ });
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/loader/loader.lazy.js b/src/main/resources/com/fr/fineui/case/loader/loader.lazy.js
new file mode 100644
index 0000000..3399400
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/loader/loader.lazy.js
@@ -0,0 +1,103 @@
+/**
+ * Created by roy on 15/11/6.
+ */
+BI.LazyLoader = BI.inherit(BI.Widget, {
+ _const: {
+ PAGE: 100
+ },
+ _defaultConfig: function () {
+ return BI.extend(BI.LazyLoader.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-lazy-loader",
+ el: {},
+ items: []
+ });
+ },
+
+ _init: function () {
+ var self = this, o = this.options;
+ BI.LazyLoader.superclass._init.apply(this, arguments);
+ var all = o.items.length;
+ this.loader = BI.createWidget({
+ type: "bi.loader",
+ element: this,
+ // 下面是button_group的属性
+ el: o.el,
+
+ itemsCreator: function (options, populate) {
+ populate(self._getNextItems(options));
+ },
+ hasNext: function (option) {
+ return option.count < all;
+ }
+ });
+
+ this.loader.on(BI.Loader.EVENT_CHANGE, function (obj) {
+ self.fireEvent(BI.LazyLoader.EVENT_CHANGE, obj);
+ });
+ },
+ _getNextItems: function (options) {
+ var self = this, o = this.options;
+ var lastNum = o.items.length - this._const.PAGE * (options.times - 1);
+ var lastItems = BI.takeRight(o.items, lastNum);
+ var nextItems = BI.take(lastItems, this._const.PAGE);
+ return nextItems;
+ },
+
+ populate: function (items) {
+ this.loader.populate(items);
+ },
+
+ addItems: function (items) {
+ this.loader.addItems(items);
+ },
+
+ empty: function () {
+ this.loader.empty();
+ },
+
+ setNotSelectedValue: function () {
+ this.loader.setNotSelectedValue.apply(this.loader, arguments);
+ },
+
+ getNotSelectedValue: function () {
+ return this.loader.getNotSelectedValue();
+ },
+
+ setValue: function () {
+ this.loader.setValue.apply(this.loader, arguments);
+ },
+
+ getValue: function () {
+ return this.loader.getValue.apply(this.loader, arguments);
+ },
+
+ getAllButtons: function () {
+ return this.loader.getAllButtons();
+ },
+
+ getAllLeaves: function () {
+ return this.loader.getAllLeaves();
+ },
+
+ getSelectedButtons: function () {
+ return this.loader.getSelectedButtons();
+ },
+
+ getNotSelectedButtons: function () {
+ return this.loader.getNotSelectedButtons();
+ },
+
+ getIndexByValue: function (value) {
+ return this.loader.getIndexByValue(value);
+ },
+
+ getNodeById: function (id) {
+ return this.loader.getNodeById(id);
+ },
+
+ getNodeByValue: function (value) {
+ return this.loader.getNodeByValue(value);
+ }
+});
+BI.LazyLoader.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.lazy_loader", BI.LazyLoader);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/loader/loader.list.js b/src/main/resources/com/fr/fineui/case/loader/loader.list.js
new file mode 100644
index 0000000..aeb3ad9
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/loader/loader.list.js
@@ -0,0 +1,196 @@
+/**
+ * 恶心的加载控件, 为解决排序问题引入的控件
+ *
+ * Created by GUY on 2015/11/12.
+ * @class BI.ListLoader
+ * @extends BI.Widget
+ */
+BI.ListLoader = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ return BI.extend(BI.ListLoader.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-list-loader",
+
+ isDefaultInit: true, // 是否默认初始化数据
+
+ // 下面是button_group的属性
+ el: {
+ type: "bi.button_group"
+ },
+
+ items: [],
+ itemsCreator: BI.emptyFn,
+ onLoaded: BI.emptyFn,
+
+ // 下面是分页信息
+ count: false,
+ next: {},
+ hasNext: BI.emptyFn
+ });
+ },
+
+ _nextLoad: function () {
+ var self = this, o = this.options;
+ this.next.setLoading();
+ o.itemsCreator.apply(this, [{times: ++this.times}, function () {
+ self.next.setLoaded();
+ self.addItems.apply(self, arguments);
+ }]);
+ },
+
+ _init: function () {
+ BI.ListLoader.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ if (o.itemsCreator === false) {
+ o.next = false;
+ }
+
+ this.button_group = BI.createWidget(o.el, {
+ type: "bi.button_group",
+ element: this,
+ chooseType: 0,
+ items: o.items,
+ behaviors: {},
+ layouts: [{
+ type: "bi.vertical"
+ }]
+ });
+ this.button_group.on(BI.Controller.EVENT_CHANGE, function (type, value, obj) {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ if (type === BI.Events.CLICK) {
+ self.fireEvent(BI.ListLoader.EVENT_CHANGE, obj);
+ }
+ });
+
+ if (o.next !== false) {
+ this.next = BI.createWidget(BI.extend({
+ type: "bi.loading_bar"
+ }, o.next));
+ this.next.on(BI.Controller.EVENT_CHANGE, function (type) {
+ if (type === BI.Events.CLICK) {
+ self._nextLoad();
+ }
+ });
+ }
+
+ BI.createWidget({
+ type: "bi.vertical",
+ element: this,
+ items: [this.next]
+ });
+
+ o.isDefaultInit && BI.isEmpty(o.items) && BI.nextTick(BI.bind(function () {
+ this.populate();
+ }, this));
+ if (BI.isNotEmptyArray(o.items)) {
+ this.populate(o.items);
+ }
+ },
+
+ hasNext: function () {
+ var o = this.options;
+ if (BI.isNumber(o.count)) {
+ return this.count < o.count;
+ }
+ return !!o.hasNext.apply(this, [{
+ times: this.times,
+ count: this.count
+ }]);
+ },
+
+ addItems: function (items) {
+ this.count += items.length;
+ if (BI.isObject(this.next)) {
+ this.options.items = this.options.items.concat(items);
+ if (this.hasNext()) {
+ this.next.setLoaded();
+ } else {
+ this.next.setEnd();
+ }
+ }
+ this.button_group.addItems.apply(this.button_group, arguments);
+ this.next.element.appendTo(this.element);
+ },
+
+ populate: function (items) {
+ var self = this, o = this.options;
+ if (arguments.length === 0 && (BI.isFunction(o.itemsCreator))) {
+ o.itemsCreator.apply(this, [{times: 1}, function () {
+ if (arguments.length === 0) {
+ throw new Error("参数不能为空");
+ }
+ self.populate.apply(self, arguments);
+ o.onLoaded();
+ }]);
+ return;
+ }
+ this.options.items = items;
+ this.times = 1;
+ this.count = 0;
+ this.count += items.length;
+ if (BI.isObject(this.next)) {
+ if (this.hasNext()) {
+ this.next.setLoaded();
+ } else {
+ this.next.invisible();
+ }
+ }
+ BI.DOM.hang([this.next]);
+ this.button_group.populate.apply(this.button_group, arguments);
+ this.next.element.appendTo(this.element);
+ },
+
+ empty: function () {
+ BI.DOM.hang([this.next]);
+ this.button_group.empty();
+ this.next.element.appendTo(this.element);
+ BI.each([this.next], function (i, ob) {
+ ob && ob.setVisible(false);
+ });
+ },
+
+ setNotSelectedValue: function () {
+ this.button_group.setNotSelectedValue.apply(this.button_group, arguments);
+ },
+
+ getNotSelectedValue: function () {
+ return this.button_group.getNotSelectedValue();
+ },
+
+ setValue: function () {
+ this.button_group.setValue.apply(this.button_group, arguments);
+ },
+
+ getValue: function () {
+ return this.button_group.getValue.apply(this.button_group, arguments);
+ },
+
+ getAllButtons: function () {
+ return this.button_group.getAllButtons();
+ },
+
+ getAllLeaves: function () {
+ return this.button_group.getAllLeaves();
+ },
+
+ getSelectedButtons: function () {
+ return this.button_group.getSelectedButtons();
+ },
+
+ getNotSelectedButtons: function () {
+ return this.button_group.getNotSelectedButtons();
+ },
+
+ getIndexByValue: function (value) {
+ return this.button_group.getIndexByValue(value);
+ },
+
+ getNodeById: function (id) {
+ return this.button_group.getNodeById(id);
+ },
+
+ getNodeByValue: function (value) {
+ return this.button_group.getNodeByValue(value);
+ }
+});
+BI.ListLoader.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.list_loader", BI.ListLoader);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/loader/sort.list.js b/src/main/resources/com/fr/fineui/case/loader/sort.list.js
new file mode 100644
index 0000000..c4606a7
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/loader/sort.list.js
@@ -0,0 +1,176 @@
+/**
+ * Created by GUY on 2016/4/29.
+ *
+ * @class BI.SortList
+ * @extends BI.Widget
+ */
+BI.SortList = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ return BI.extend(BI.SortList.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-sort-list",
+
+ isDefaultInit: true, // 是否默认初始化数据
+
+ // 下面是button_group的属性
+ el: {
+ type: "bi.button_group"
+ },
+
+ items: [],
+ itemsCreator: BI.emptyFn,
+ onLoaded: BI.emptyFn,
+
+ // 下面是分页信息
+ count: false,
+ next: {},
+ hasNext: BI.emptyFn
+
+ // containment: this.element,
+ // connectWith: ".bi-sort-list",
+ });
+ },
+
+ _init: function () {
+ BI.SortList.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.loader = BI.createWidget({
+ type: "bi.list_loader",
+ element: this,
+ isDefaultInit: o.isDefaultInit,
+ el: o.el,
+ items: this._formatItems(o.items),
+ itemsCreator: function (op, callback) {
+ o.itemsCreator(op, function (items) {
+ callback(self._formatItems(items));
+ });
+ },
+ onLoaded: o.onLoaded,
+ count: o.count,
+ next: o.next,
+ hasNext: o.hasNext
+ });
+ this.loader.on(BI.Controller.EVENT_CHANGE, function (type, value, obj) {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ if (type === BI.Events.CLICK) {
+ self.fireEvent(BI.SortList.EVENT_CHANGE, value, obj);
+ }
+ });
+
+ this.loader.element.sortable({
+ containment: o.containment || this.element,
+ connectWith: o.connectWith || ".bi-sort-list",
+ items: ".sort-item",
+ cursor: o.cursor || "drag",
+ tolerance: o.tolerance || "intersect",
+ placeholder: {
+ element: function ($currentItem) {
+ var holder = BI.createWidget({
+ type: "bi.layout",
+ cls: "bi-sortable-holder",
+ height: $currentItem.outerHeight()
+ });
+ holder.element.css({
+ "margin-left": $currentItem.css("margin-left"),
+ "margin-right": $currentItem.css("margin-right"),
+ "margin-top": $currentItem.css("margin-top"),
+ "margin-bottom": $currentItem.css("margin-bottom"),
+ margin: $currentItem.css("margin")
+ });
+ return holder.element;
+ },
+ update: function () {
+
+ }
+ },
+ start: function (event, ui) {
+
+ },
+ stop: function (event, ui) {
+ self.fireEvent(BI.SortList.EVENT_CHANGE);
+ },
+ over: function (event, ui) {
+
+ }
+ });
+ },
+
+ _formatItems: function (items) {
+ BI.each(items, function (i, item) {
+ item = BI.stripEL(item);
+ item.cls = item.cls ? item.cls + " sort-item" : "sort-item";
+ item.attributes = {
+ sorted: item.value
+ };
+ });
+ return items;
+ },
+
+ hasNext: function () {
+ return this.loader.hasNext();
+ },
+
+ addItems: function (items) {
+ this.loader.addItems(items);
+ },
+
+ populate: function (items) {
+ if (items) {
+ arguments[0] = this._formatItems(items);
+ }
+ this.loader.populate.apply(this.loader, arguments);
+ },
+
+ empty: function () {
+ this.loader.empty();
+ },
+
+ setNotSelectedValue: function () {
+ this.loader.setNotSelectedValue.apply(this.loader, arguments);
+ },
+
+ getNotSelectedValue: function () {
+ return this.loader.getNotSelectedValue();
+ },
+
+ setValue: function () {
+ this.loader.setValue.apply(this.loader, arguments);
+ },
+
+ getValue: function () {
+ return this.loader.getValue();
+ },
+
+ getAllButtons: function () {
+ return this.loader.getAllButtons();
+ },
+
+ getAllLeaves: function () {
+ return this.loader.getAllLeaves();
+ },
+
+ getSelectedButtons: function () {
+ return this.loader.getSelectedButtons();
+ },
+
+ getNotSelectedButtons: function () {
+ return this.loader.getNotSelectedButtons();
+ },
+
+ getIndexByValue: function (value) {
+ return this.loader.getIndexByValue(value);
+ },
+
+ getNodeById: function (id) {
+ return this.loader.getNodeById(id);
+ },
+
+ getNodeByValue: function (value) {
+ return this.loader.getNodeByValue(value);
+ },
+
+ getSortedValues: function () {
+ return this.loader.element.sortable("toArray", {attribute: "sorted"});
+ }
+});
+BI.SortList.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.sort_list", BI.SortList);
diff --git a/src/main/resources/com/fr/fineui/case/pager/__test__/pager.test.js b/src/main/resources/com/fr/fineui/case/pager/__test__/pager.test.js
new file mode 100644
index 0000000..a090410
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/pager/__test__/pager.test.js
@@ -0,0 +1,97 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/3/17
+ */
+
+describe("PagerTest", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("allCountPager", function () {
+ var a = BI.Test.createWidget({
+ type: "bi.all_count_pager",
+ pages: 3,
+ curr: 1,
+ count: 1000
+ });
+ a.setCount(888);
+ expect(a.element.find(".row-count").text()).to.equal("888");
+ a.setAllPages(777);
+ a.setValue(4);
+ expect(a.element.find(".bi-input").val()).to.equal("4");
+ expect(a.getCurrentPage()).to.equal(4);
+ expect(a.hasPrev()).to.equal(false);
+ expect(a.hasNext()).to.equal(true);
+ a.populate();
+ expect(a.element.find(".bi-input").val()).to.equal("4");
+ a.setPagerVisible(false);
+ expect(a.element.find(".bi-pager").css("display")).to.equal("none");
+ a.destroy();
+ });
+
+
+ /**
+ * test_author_windy
+ */
+ it("directionPager", function () {
+ var a = BI.Test.createWidget({
+ type: "bi.direction_pager",
+ horizontal: {
+ pages: false, // 总页数
+ curr: 1, // 初始化当前页, pages为数字时可用
+
+ hasPrev: function (v) {
+ return v > 1;
+ },
+ hasNext: function () {
+ return true;
+ },
+ firstPage: 1
+ },
+ vertical: {
+ pages: false, // 总页数
+ curr: 1, // 初始化当前页, pages为数字时可用
+
+ hasPrev: function (v) {
+ return v > 1;
+ },
+ hasNext: function () {
+ return true;
+ },
+ firstPage: 1
+ }
+ });
+ a.populate();
+ a.setVPage(2);
+ expect(a.getVPage()).to.equal(1);
+ a.setHPage(2);
+ expect(a.getHPage()).to.equal(1);
+ expect(a.hasVNext()).to.equal(true);
+ expect(a.hasHNext()).to.equal(true);
+ expect(a.hasVPrev()).to.equal(false);
+ expect(a.hasHPrev()).to.equal(false);
+ a.setHPagerVisible(false)
+ a.setVPagerVisible(false)
+ expect(a.element.find(".bi-pager").css("display")).to.equal("none");
+ a.clear();
+ a.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("detail_Pager", function () {
+ var a = BI.Test.createWidget({
+ type: "bi.detail_pager",
+ pages: 100
+ });
+ a.setAllPages(200);
+ a.populate();
+ a.setValue(200);
+ expect(a.hasPrev(200)).to.equal(true);
+ expect(a.hasNext(200)).to.equal(false);
+ a.destroy();
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/pager/pager.all.count.js b/src/main/resources/com/fr/fineui/case/pager/pager.all.count.js
new file mode 100644
index 0000000..8e03d57
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/pager/pager.all.count.js
@@ -0,0 +1,234 @@
+/**
+ * 有总页数和总行数的分页控件
+ * Created by Young's on 2016/10/13.
+ */
+BI.AllCountPager = BI.inherit(BI.Widget, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.AllCountPager.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "bi-all-count-pager",
+ pagerDirection: "vertical", // 翻页按钮方向,可选值:vertical/horizontal
+ height: 24,
+ pages: 1, // 必选项
+ curr: 1, // 初始化当前页, pages为数字时可用,
+ count: 1, // 总行数
+ rowInfoObject: null,
+ showRowCount: true,
+ showRowInfo: true,
+ });
+ },
+ _init: function () {
+ BI.AllCountPager.superclass._init.apply(this, arguments);
+ var self = this, o = this.options, pagerIconCls = this._getPagerIconCls();
+ this.editor = BI.createWidget({
+ type: "bi.small_text_editor",
+ cls: "pager-editor bi-border-radius",
+ validationChecker: function (v) {
+ return (o.pages === 0 && v === "0") || BI.isPositiveInteger(v);
+ },
+ hgap: 4,
+ vgap: 0,
+ value: o.curr,
+ errorText: BI.i18nText("BI-Please_Input_Positive_Integer"),
+ width: 40,
+ height: 24,
+ invisible: o.pages <= 1
+ });
+
+ this.pager = BI.createWidget({
+ type: "bi.pager",
+ width: 58,
+ layouts: [{
+ type: "bi.horizontal",
+ lgap: 5
+ }],
+
+ dynamicShow: false,
+ pages: o.pages,
+ curr: o.curr,
+ groups: 0,
+
+ first: false,
+ last: false,
+ prev: {
+ type: "bi.icon_button",
+ value: "prev",
+ title: BI.i18nText("BI-Previous_Page"),
+ warningTitle: BI.i18nText("BI-Current_Is_First_Page"),
+ height: 22,
+ width: 22,
+ cls: "bi-border bi-border-radius all-pager-prev bi-list-item-select2 " + pagerIconCls.preCls
+ },
+ next: {
+ type: "bi.icon_button",
+ value: "next",
+ title: BI.i18nText("BI-Next_Page"),
+ warningTitle: BI.i18nText("BI-Current_Is_Last_Page"),
+ height: 22,
+ width: 22,
+ cls: "bi-border bi-border-radius all-pager-next bi-list-item-select2 " + pagerIconCls.nextCls
+ },
+
+ hasPrev: o.hasPrev,
+ hasNext: o.hasNext,
+ firstPage: o.firstPage,
+ lastPage: o.lastPage,
+ invisible: o.pages <= 1
+ });
+
+ this.editor.on(BI.TextEditor.EVENT_CONFIRM, function () {
+ self.pager.setValue(BI.parseInt(self.editor.getValue()));
+ self.fireEvent(BI.AllCountPager.EVENT_CHANGE);
+ });
+ this.pager.on(BI.Pager.EVENT_CHANGE, function () {
+ self.fireEvent(BI.AllCountPager.EVENT_CHANGE);
+ });
+ this.pager.on(BI.Pager.EVENT_AFTER_POPULATE, function () {
+ self.editor.setValue(self.pager.getCurrentPage());
+ });
+
+ this.allPages = BI.createWidget({
+ type: "bi.label",
+ title: o.pages,
+ height: o.height,
+ text: "/" + o.pages,
+ lgap: 5,
+ invisible: o.pages <= 1
+ });
+
+ BI.createWidget(o.showRowCount ? {
+ type: "bi.vertical_adapt",
+ element: this,
+ scrollx: false,
+ columnSize: ["fill", ""],
+ horizontalAlign: BI.HorizontalAlign.Right,
+ items: [
+ this._getRowCountObject(),
+ this.editor, this.allPages, this.pager
+ ],
+ } : {
+ type: "bi.vertical_adapt",
+ element: this,
+ items: [this.editor, this.allPages, this.pager]
+ });
+ },
+
+ _getPagerIconCls: function () {
+ var o = this.options;
+ switch (o.pagerDirection) {
+ case "horizontal":
+ return {
+ preCls: "row-pre-page-h-font ",
+ nextCls: "row-next-page-h-font "
+ };
+ case "vertical":
+ default:
+ return {
+ preCls: "column-pre-page-h-font ",
+ nextCls: "column-next-page-h-font "
+ };
+ }
+ },
+
+ _getRowCountObject: function() {
+ var self = this, o = this.options;
+
+ return {
+ type: "bi.left",
+ height: o.height,
+ scrollable: false,
+ ref: function (_ref) {
+ self.rowCountObject = _ref;
+ },
+ items: [{
+ type: "bi.label",
+ height: o.height,
+ text: BI.i18nText("BI-Basic_Total"),
+ ref: function (_ref) {
+ self.prevText = _ref;
+ }
+ }, {
+ el: {
+ type: "bi.label",
+ ref: function (_ref) {
+ self.rowCount = _ref;
+ },
+ cls: "row-count",
+ height: o.height,
+ text: o.count,
+ title: o.count
+ },
+ hgap: 5,
+ }, {
+ type: "bi.label",
+ height: o.height,
+ text: BI.i18nText("BI-Tiao_Data"),
+ textAlign: "left"
+ }, BI.isNotEmptyObject(o.rowInfoObject) ? o.rowInfoObject : null]
+ };
+ },
+
+ setAllPages: function (v) {
+ this.allPages.setText("/" + v);
+ this.allPages.setTitle(v);
+ this.options.pages = v;
+ this.pager.setAllPages(v);
+ this.editor.setEnable(v >= 1);
+ this.setPagerVisible(v > 1);
+ },
+
+ setShowRowInfo: function (b) {
+ this.options.showRowInfo = b;
+ this.rowCountObject.setVisible(b);
+ },
+
+ setValue: function (v) {
+ this.pager.setValue(v);
+ },
+
+ setVPage: function (v) {
+ this.pager.setValue(v);
+ },
+
+ setCount: function (count) {
+ if (this.options.showRowCount) {
+ this.rowCount.setText(count);
+ this.rowCount.setTitle(count);
+ }
+ },
+
+ setCountPrevText: function (text) {
+ if (this.options.showRowCount) {
+ this.prevText.setText(text);
+ }
+ },
+
+ getCurrentPage: function () {
+ return this.pager.getCurrentPage();
+ },
+
+ hasPrev: function () {
+ return this.pager.hasPrev();
+ },
+
+ hasNext: function () {
+ return this.pager.hasNext();
+ },
+
+ isShowPager: function () {
+ return this.options.showRowInfo || this.options.pages > 1;
+ },
+
+ setPagerVisible: function (b) {
+ this.editor.setVisible(b);
+ this.allPages.setVisible(b);
+ this.pager.setVisible(b);
+ },
+
+ populate: function () {
+ this.pager.populate();
+ this.setPagerVisible(this.options.pages > 1);
+ }
+});
+BI.AllCountPager.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.all_count_pager", BI.AllCountPager);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/pager/pager.direction.js b/src/main/resources/com/fr/fineui/case/pager/pager.direction.js
new file mode 100644
index 0000000..dcc0ab9
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/pager/pager.direction.js
@@ -0,0 +1,276 @@
+/**
+ * 显示页码的分页控件
+ *
+ * Created by GUY on 2016/6/30.
+ * @class BI.DirectionPager
+ * @extends BI.Widget
+ */
+BI.DirectionPager = BI.inherit(BI.Widget, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.DirectionPager.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-direction-pager",
+ height: 24,
+ horizontal: {
+ pages: false, // 总页数
+ curr: 1, // 初始化当前页, pages为数字时可用
+
+ hasPrev: BI.emptyFn,
+ hasNext: BI.emptyFn,
+ firstPage: 1,
+ lastPage: BI.emptyFn
+ },
+ vertical: {
+ pages: false, // 总页数
+ curr: 1, // 初始化当前页, pages为数字时可用
+
+ hasPrev: BI.emptyFn,
+ hasNext: BI.emptyFn,
+ firstPage: 1,
+ lastPage: BI.emptyFn
+ }
+ });
+ },
+ _init: function () {
+ BI.DirectionPager.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ var v = o.vertical, h = o.horizontal;
+ this._createVPager();
+ this._createHPager();
+ this.layout = BI.createWidget({
+ type: "bi.absolute",
+ scrollable: false,
+ element: this,
+ items: [{
+ el: this.vpager,
+ top: 0,
+ right: 86
+ }, {
+ el: this.vlabel,
+ top: 0,
+ right: 110
+ }, {
+ el: this.hpager,
+ top: 0,
+ right: 0
+ }, {
+ el: this.hlabel,
+ top: 0,
+ right: 24
+ }]
+ });
+ },
+
+ _createVPager: function () {
+ var self = this, o = this.options;
+ var v = o.vertical;
+ this.vlabel = BI.createWidget({
+ type: "bi.label",
+ width: 24,
+ height: 24,
+ value: v.curr,
+ title: v.curr,
+ invisible: true
+ });
+ this.vpager = BI.createWidget({
+ type: "bi.pager",
+ width: 72,
+ layouts: [{
+ type: "bi.horizontal",
+ scrollx: false,
+ rgap: 24
+ }],
+ invisible: true,
+
+ dynamicShow: false,
+ pages: v.pages,
+ curr: v.curr,
+ groups: 0,
+
+ first: false,
+ last: false,
+ prev: {
+ type: "bi.icon_button",
+ value: "prev",
+ title: BI.i18nText("BI-Up_Page"),
+ warningTitle: BI.i18nText("BI-Current_Is_First_Page"),
+ height: 22,
+ width: 22,
+ cls: "bi-border bi-border-radius direction-pager-prev column-pre-page-h-font bi-list-item-select2"
+ },
+ next: {
+ type: "bi.icon_button",
+ value: "next",
+ title: BI.i18nText("BI-Down_Page"),
+ warningTitle: BI.i18nText("BI-Current_Is_Last_Page"),
+ height: 22,
+ width: 22,
+ cls: "bi-border bi-border-radius direction-pager-next column-next-page-h-font bi-list-item-select2"
+ },
+
+ hasPrev: v.hasPrev,
+ hasNext: v.hasNext,
+ firstPage: v.firstPage,
+ lastPage: v.lastPage
+ });
+
+ this.vpager.on(BI.Pager.EVENT_CHANGE, function () {
+ self.fireEvent(BI.DirectionPager.EVENT_CHANGE);
+ });
+ this.vpager.on(BI.Pager.EVENT_AFTER_POPULATE, function () {
+ self.vlabel.setValue(this.getCurrentPage());
+ self.vlabel.setTitle(this.getCurrentPage());
+ });
+ },
+
+ _createHPager: function () {
+ var self = this, o = this.options;
+ var h = o.horizontal;
+ this.hlabel = BI.createWidget({
+ type: "bi.label",
+ width: 24,
+ height: 24,
+ value: h.curr,
+ title: h.curr,
+ invisible: true
+ });
+ this.hpager = BI.createWidget({
+ type: "bi.pager",
+ width: 72,
+ layouts: [{
+ type: "bi.horizontal",
+ scrollx: false,
+ rgap: 24
+ }],
+ invisible: true,
+
+ dynamicShow: false,
+ pages: h.pages,
+ curr: h.curr,
+ groups: 0,
+
+ first: false,
+ last: false,
+ prev: {
+ type: "bi.icon_button",
+ value: "prev",
+ title: BI.i18nText("BI-Left_Page"),
+ warningTitle: BI.i18nText("BI-Current_Is_First_Page"),
+ height: 22,
+ width: 22,
+ cls: "bi-border bi-border-radius direction-pager-prev row-pre-page-h-font bi-list-item-select2"
+ },
+ next: {
+ type: "bi.icon_button",
+ value: "next",
+ title: BI.i18nText("BI-Right_Page"),
+ warningTitle: BI.i18nText("BI-Current_Is_Last_Page"),
+ height: 22,
+ width: 22,
+ cls: "bi-border bi-border-radius direction-pager-next row-next-page-h-font bi-list-item-select2"
+ },
+
+ hasPrev: h.hasPrev,
+ hasNext: h.hasNext,
+ firstPage: h.firstPage,
+ lastPage: h.lastPage
+ });
+
+ this.hpager.on(BI.Pager.EVENT_CHANGE, function () {
+ self.fireEvent(BI.DirectionPager.EVENT_CHANGE);
+ });
+ this.hpager.on(BI.Pager.EVENT_AFTER_POPULATE, function () {
+ self.hlabel.setValue(this.getCurrentPage());
+ self.hlabel.setTitle(this.getCurrentPage());
+ });
+ },
+
+ getVPage: function () {
+ return this.vpager.getCurrentPage();
+ },
+
+ getHPage: function () {
+ return this.hpager.getCurrentPage();
+ },
+
+ setVPage: function (v) {
+ this.vpager.setValue(v);
+ this.vlabel.setValue(v);
+ this.vlabel.setTitle(v);
+ },
+
+ setHPage: function (v) {
+ this.hpager.setValue(v);
+ this.hlabel.setValue(v);
+ this.hlabel.setTitle(v);
+ },
+
+ hasVNext: function () {
+ return this.vpager.hasNext();
+ },
+
+ hasHNext: function () {
+ return this.hpager.hasNext();
+ },
+
+ hasVPrev: function () {
+ return this.vpager.hasPrev();
+ },
+
+ hasHPrev: function () {
+ return this.hpager.hasPrev();
+ },
+
+ setHPagerVisible: function (b) {
+ this.hpager.setVisible(b);
+ this.hlabel.setVisible(b);
+ },
+
+ setVPagerVisible: function (b) {
+ this.vpager.setVisible(b);
+ this.vlabel.setVisible(b);
+ },
+
+ populate: function () {
+ this.vpager.populate();
+ this.hpager.populate();
+ var vShow = false, hShow = false;
+ if (!this.hasHNext() && !this.hasHPrev()) {
+ this.setHPagerVisible(false);
+ } else {
+ this.setHPagerVisible(true);
+ hShow = true;
+ }
+ if (!this.hasVNext() && !this.hasVPrev()) {
+ this.setVPagerVisible(false);
+ } else {
+ this.setVPagerVisible(true);
+ vShow = true;
+ }
+ this.setVisible(hShow || vShow);
+ var num = [86, 110, 0, 24];
+ var items = this.layout.attr("items");
+
+ if (vShow === true && hShow === true) {
+ items[0].right = num[0];
+ items[1].right = num[1];
+ items[2].right = num[2];
+ items[3].right = num[3];
+ } else if (vShow === true) {
+ items[0].right = num[2];
+ items[1].right = num[3];
+ } else if (hShow === true) {
+ items[2].right = num[2];
+ items[3].right = num[3];
+ }
+ this.layout.attr("items", items);
+ this.layout.resize();
+ },
+
+ clear: function () {
+ this.vpager.attr("curr", 1);
+ this.hpager.attr("curr", 1);
+ }
+});
+BI.DirectionPager.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.direction_pager", BI.DirectionPager);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/pager/pager.js b/src/main/resources/com/fr/fineui/case/pager/pager.js
new file mode 100644
index 0000000..cc057fd
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/pager/pager.js
@@ -0,0 +1,288 @@
+/**
+ * 分页控件
+ *
+ * Created by GUY on 2015/8/31.
+ * @class BI.DetailPager
+ * @extends BI.Widget
+ */
+BI.DetailPager = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ return BI.extend(BI.DetailPager.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-detail-pager",
+ behaviors: {},
+ layouts: [{
+ type: "bi.horizontal",
+ hgap: 10,
+ vgap: 0
+ }],
+
+ dynamicShow: true, // 是否动态显示上一页、下一页、首页、尾页, 若为false,则指对其设置使能状态
+ // dynamicShow为false时以下两个有用
+ dynamicShowFirstLast: false, // 是否动态显示首页、尾页
+ dynamicShowPrevNext: false, // 是否动态显示上一页、下一页
+ pages: false, // 总页数
+ curr: function () {
+ return 1;
+ }, // 初始化当前页
+ groups: 0, // 连续显示分页数
+ jump: BI.emptyFn, // 分页的回调函数
+
+ first: false, // 是否显示首页
+ last: false, // 是否显示尾页
+ prev: "上一页",
+ next: "下一页",
+
+ firstPage: 1,
+ lastPage: function () { // 在万不得已时才会调用这个函数获取最后一页的页码, 主要作用于setValue方法
+ return 1;
+ },
+ hasPrev: BI.emptyFn, // pages不可用时有效
+ hasNext: BI.emptyFn // pages不可用时有效
+ });
+ },
+ _init: function () {
+ BI.DetailPager.superclass._init.apply(this, arguments);
+ var self = this;
+ this.currPage = BI.result(this.options, "curr");
+ // 翻页太灵敏
+ this._lock = false;
+ this._debouce = BI.debounce(function () {
+ self._lock = false;
+ }, 300);
+ this._populate();
+ },
+
+ _populate: function () {
+ var self = this, o = this.options, view = [], dict = {};
+ this.empty();
+ var pages = BI.result(o, "pages");
+ var curr = BI.result(this, "currPage");
+ var groups = BI.result(o, "groups");
+ var first = BI.result(o, "first");
+ var last = BI.result(o, "last");
+ var prev = BI.result(o, "prev");
+ var next = BI.result(o, "next");
+
+ if (pages === false) {
+ groups = 0;
+ first = false;
+ last = false;
+ } else {
+ groups > pages && (groups = pages);
+ }
+
+ // 计算当前组
+ dict.index = Math.ceil((curr + ((groups > 1 && groups !== pages) ? 1 : 0)) / (groups === 0 ? 1 : groups));
+
+ // 当前页非首页,则输出上一页
+ if (((!o.dynamicShow && !o.dynamicShowPrevNext) || curr > 1) && prev !== false) {
+ if (BI.isKey(prev)) {
+ view.push({
+ text: prev,
+ value: "prev",
+ disabled: pages === false ? o.hasPrev(curr) === false : !(curr > 1 && prev !== false)
+ });
+ } else {
+ view.push(BI.extend({
+ disabled: pages === false ? o.hasPrev(curr) === false : !(curr > 1 && prev !== false)
+ }, prev));
+ }
+ }
+
+ // 当前组非首组,则输出首页
+ if (((!o.dynamicShow && !o.dynamicShowFirstLast) || (dict.index > 1 && groups !== 0)) && first) {
+ view.push({
+ text: first,
+ value: "first",
+ disabled: !(dict.index > 1 && groups !== 0)
+ });
+ if (dict.index > 1 && groups !== 0) {
+ view.push({
+ type: "bi.label",
+ cls: "page-ellipsis",
+ text: "\u2026"
+ });
+ }
+ }
+
+ // 输出当前页组
+ dict.poor = Math.floor((groups - 1) / 2);
+ dict.start = dict.index > 1 ? curr - dict.poor : 1;
+ dict.end = dict.index > 1 ? (function () {
+ var max = curr + (groups - dict.poor - 1);
+ return max > pages ? pages : max;
+ }()) : groups;
+ if (dict.end - dict.start < groups - 1) { // 最后一组状态
+ dict.start = dict.end - groups + 1;
+ }
+ var s = dict.start, e = dict.end;
+ if (first && last && (dict.index > 1 && groups !== 0) && (pages > groups && dict.end < pages && groups !== 0)) {
+ s++;
+ e--;
+ }
+ for (; s <= e; s++) {
+ if (s === curr) {
+ view.push({
+ text: s,
+ value: s,
+ selected: true
+ });
+ } else {
+ view.push({
+ text: s,
+ value: s
+ });
+ }
+ }
+
+ // 总页数大于连续分页数,且当前组最大页小于总页,输出尾页
+ if (((!o.dynamicShow && !o.dynamicShowFirstLast) || (pages > groups && dict.end < pages && groups !== 0)) && last) {
+ if (pages > groups && dict.end < pages && groups !== 0) {
+ view.push({
+ type: "bi.label",
+ cls: "page-ellipsis",
+ text: "\u2026"
+ });
+ }
+ view.push({
+ text: last,
+ value: "last",
+ disabled: !(pages > groups && dict.end < pages && groups !== 0)
+ });
+ }
+
+ // 当前页不为尾页时,输出下一页
+ dict.flow = !prev && groups === 0;
+ if (((!o.dynamicShow && !o.dynamicShowPrevNext) && next) || (curr !== pages && next || dict.flow)) {
+ view.push((function () {
+ if (BI.isKey(next)) {
+ if (pages === false) {
+ return {text: next, value: "next", disabled: o.hasNext(curr) === false};
+ }
+ return (dict.flow && curr === pages)
+ ?
+ {text: next, value: "next", disabled: true}
+ :
+ {text: next, value: "next", disabled: !(curr !== pages && next || dict.flow)};
+ }
+ return BI.extend({
+ disabled: pages === false ? o.hasNext(curr) === false : !(curr !== pages && next || dict.flow)
+ }, next);
+
+ }()));
+ }
+
+ this.button_group = BI.createWidget({
+ type: "bi.button_group",
+ element: this,
+ items: BI.createItems(view, {
+ cls: "page-item bi-border bi-list-item-active",
+ height: 23,
+ hgap: 10
+ }),
+ behaviors: o.behaviors,
+ layouts: o.layouts
+ });
+ this.button_group.on(BI.Controller.EVENT_CHANGE, function (type, value, obj) {
+ if (self._lock === true) {
+ return;
+ }
+ self._lock = true;
+ self._debouce();
+ if (type === BI.Events.CLICK) {
+ var v = self.button_group.getValue()[0];
+ switch (v) {
+ case "first":
+ self.currPage = 1;
+ break;
+ case "last":
+ self.currPage = pages;
+ break;
+ case "prev":
+ self.currPage--;
+ break;
+ case "next":
+ self.currPage++;
+ break;
+ default:
+ self.currPage = v;
+ break;
+ }
+ o.jump.apply(self, [{
+ pages: pages,
+ curr: self.currPage
+ }]);
+ self._populate();
+ self.fireEvent(BI.DetailPager.EVENT_CHANGE, obj);
+ }
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ this.fireEvent(BI.DetailPager.EVENT_AFTER_POPULATE);
+ },
+
+ getCurrentPage: function () {
+ return this.currPage;
+ },
+
+ setAllPages: function (pages) {
+ this.options.pages = pages;
+ },
+
+ hasPrev: function (v) {
+ v || (v = 1);
+ var o = this.options;
+ var pages = this.options.pages;
+ return pages === false ? o.hasPrev(v) : v > 1;
+ },
+
+ hasNext: function (v) {
+ v || (v = 1);
+ var o = this.options;
+ var pages = this.options.pages;
+ return pages === false ? o.hasNext(v) : v < pages;
+ },
+
+ setValue: function (v) {
+ var o = this.options;
+ v = v || 0;
+ v = v < 1 ? 1 : v;
+ if (o.pages === false) {
+ var lastPage = BI.result(o, "lastPage"), firstPage = 1;
+ this.currPage = v > lastPage ? lastPage : ((firstPage = BI.result(o, "firstPage")), (v < firstPage ? firstPage : v));
+ } else {
+ v = v > o.pages ? o.pages : v;
+ this.currPage = v;
+ }
+ this._populate();
+ },
+
+ getValue: function () {
+ var val = this.button_group.getValue()[0];
+ switch (val) {
+ case "prev":
+ return -1;
+ case "next":
+ return 1;
+ case "first":
+ return BI.MIN;
+ case "last":
+ return BI.MAX;
+ default :
+ return val;
+ }
+ },
+
+ attr: function (key, value) {
+ BI.DetailPager.superclass.attr.apply(this, arguments);
+ if (key === "curr") {
+ this.currPage = BI.result(this.options, "curr");
+ }
+ },
+
+ populate: function () {
+ this._populate();
+ }
+});
+BI.DetailPager.EVENT_CHANGE = "EVENT_CHANGE";
+BI.DetailPager.EVENT_AFTER_POPULATE = "EVENT_AFTER_POPULATE";
+BI.shortcut("bi.detail_pager", BI.DetailPager);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/segment/__test__/segment.test.js b/src/main/resources/com/fr/fineui/case/segment/__test__/segment.test.js
new file mode 100644
index 0000000..0b45ab5
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/segment/__test__/segment.test.js
@@ -0,0 +1,33 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/3/17
+ */
+describe("SegmentTest", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("segment", function () {
+ var a = BI.Test.createWidget({
+ type: "bi.segment",
+ items: [{
+ text: "1",
+ value: 1
+ }, {
+ text: "2",
+ value: 2
+ }, {
+ text: "3",
+ value: 3
+ }]
+ });
+ a.setValue(2);
+ expect(a.getValue()[0]).to.equal(2);
+ a.setEnable(false);
+ a.setEnabledValue(3);
+ a.setValue(3);
+ expect(a.getValue()[0]).to.equal(3);
+ a.destroy();
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/segment/button.segment.js b/src/main/resources/com/fr/fineui/case/segment/button.segment.js
new file mode 100644
index 0000000..0e3ac34
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/segment/button.segment.js
@@ -0,0 +1,46 @@
+/**
+ * 分段控件使用的button
+ *
+ * Created by GUY on 2015/9/7.
+ * @class BI.SegmentButton
+ * @extends BI.BasicButton
+ */
+BI.SegmentButton = BI.inherit(BI.BasicButton, {
+
+ _defaultConfig: function () {
+ var conf = BI.SegmentButton.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-segment-button bi-list-item-select",
+ shadow: true,
+ readonly: true,
+ hgap: 5
+ });
+ },
+
+ _init: function () {
+ BI.SegmentButton.superclass._init.apply(this, arguments);
+ var opts = this.options, self = this;
+ // if (BI.isNumber(opts.height) && BI.isNull(opts.lineHeight)) {
+ // this.element.css({lineHeight : (opts.height - 2) + 'px'});
+ // }
+ this.text = BI.createWidget({
+ type: "bi.label",
+ element: this,
+ textHeight: opts.height,
+ whiteSpace: opts.whiteSpace,
+ text: opts.text,
+ value: opts.value,
+ hgap: opts.hgap
+ });
+ },
+
+ setSelected: function () {
+ BI.SegmentButton.superclass.setSelected.apply(this, arguments);
+ },
+
+ setText: function (text) {
+ BI.SegmentButton.superclass.setText.apply(this, arguments);
+ this.text.setText(text);
+ }
+});
+BI.shortcut("bi.segment_button", BI.SegmentButton);
diff --git a/src/main/resources/com/fr/fineui/case/segment/segment.js b/src/main/resources/com/fr/fineui/case/segment/segment.js
new file mode 100644
index 0000000..789d8ce
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/segment/segment.js
@@ -0,0 +1,64 @@
+/**
+ * 单选按钮组
+ *
+ * Created by GUY on 2015/9/7.
+ * @class BI.Segment
+ * @extends BI.Widget
+ */
+BI.Segment = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ return BI.extend(BI.Segment.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-segment",
+ items: [],
+ height: 24
+ });
+ },
+ _init: function () {
+ BI.Segment.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.buttonGroup = BI.createWidget({
+ element: this,
+ type: "bi.button_group",
+ value: o.value,
+ items: BI.createItems(o.items, {
+ type: "bi.segment_button",
+ height: o.height - 2,
+ whiteSpace: o.whiteSpace
+ }),
+ layout: [
+ {
+ type: "bi.center"
+ }
+ ]
+ });
+ this.buttonGroup.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ this.buttonGroup.on(BI.ButtonGroup.EVENT_CHANGE, function (value, obj) {
+ self.fireEvent(BI.Segment.EVENT_CHANGE, value, obj);
+ });
+ },
+
+ _setEnable: function (enable) {
+ BI.Segment.superclass._setEnable.apply(this, arguments);
+ if (enable === true) {
+ this.element.removeClass("base-disabled disabled");
+ } else if (enable === false) {
+ this.element.addClass("base-disabled disabled");
+ }
+ },
+
+ setValue: function (v) {
+ this.buttonGroup.setValue(v);
+ },
+
+ setEnabledValue: function (v) {
+ this.buttonGroup.setEnabledValue(v);
+ },
+
+ getValue: function () {
+ return this.buttonGroup.getValue();
+ }
+});
+BI.Segment.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.segment", BI.Segment);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/toolbar/toolbar.multiselect.js b/src/main/resources/com/fr/fineui/case/toolbar/toolbar.multiselect.js
new file mode 100644
index 0000000..f18677b
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/toolbar/toolbar.multiselect.js
@@ -0,0 +1,145 @@
+/**
+ * guy
+ * 复选导航条
+ * Created by GUY on 2015/8/25.
+ * @class BI.MultiSelectBar
+ * @extends BI.BasicButton
+ */
+BI.MultiSelectBar = BI.inherit(BI.BasicButton, {
+ _defaultConfig: function () {
+ return BI.extend(BI.MultiSelectBar.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "bi-multi-select-bar",
+ height: 25,
+ text: BI.i18nText("BI-Select_All"),
+ isAllCheckedBySelectedValue: BI.emptyFn,
+ // 手动控制选中
+ disableSelected: true,
+ isHalfCheckedBySelectedValue: function (selectedValues) {
+ return selectedValues.length > 0;
+ },
+ halfSelected: false,
+ iconWrapperWidth: 26,
+ iconWidth: 14,
+ iconHeight: 14,
+ });
+ },
+ _init: function () {
+ BI.MultiSelectBar.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ var isSelect = o.selected === true;
+ var isHalfSelect = !o.selected && o.halfSelected;
+ this.checkbox = BI.createWidget({
+ type: "bi.checkbox",
+ stopPropagation: true,
+ handler: function () {
+ self.setSelected(self.isSelected());
+ },
+ selected: isSelect,
+ invisible: isHalfSelect,
+ iconWidth: o.iconWidth,
+ iconHeight: o.iconHeight
+ });
+ this.half = BI.createWidget({
+ type: "bi.half_icon_button",
+ stopPropagation: true,
+ handler: function () {
+ self.setSelected(true);
+ },
+ invisible: isSelect || !isHalfSelect,
+ iconWidth: o.iconWidth,
+ iconHeight: o.iconHeight
+ });
+ this.checkbox.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.CLICK, self.isSelected(), self);
+ });
+ this.checkbox.on(BI.Checkbox.EVENT_CHANGE, function () {
+ self.fireEvent(BI.MultiSelectBar.EVENT_CHANGE, self.isSelected(), self);
+ });
+ this.half.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.CLICK, self.isSelected(), self);
+ });
+ this.half.on(BI.HalfIconButton.EVENT_CHANGE, function () {
+ self.fireEvent(BI.MultiSelectBar.EVENT_CHANGE, self.isSelected(), self);
+ });
+ this.text = BI.createWidget({
+ type: "bi.label",
+ textAlign: "left",
+ whiteSpace: "nowrap",
+ textHeight: o.height,
+ height: o.height,
+ hgap: o.hgap,
+ text: o.text,
+ keyword: o.keyword,
+ value: o.value,
+ py: o.py
+ });
+ BI.createWidget({
+ type: "bi.htape",
+ element: this,
+ items: [{
+ width: o.iconWrapperWidth,
+ el: {
+ type: "bi.center_adapt",
+ items: [this.checkbox, this.half]
+ }
+ }, {
+ el: this.text
+ }]
+ });
+ },
+
+ _setSelected: function (v) {
+ this.checkbox.setSelected(!!v);
+ },
+
+ // 自己手动控制选中
+ beforeClick: function () {
+ var isHalf = this.isHalfSelected(), isSelected = this.isSelected();
+ if (isHalf === true) {
+ this.setSelected(true);
+ } else {
+ this.setSelected(!isSelected);
+ }
+ },
+
+ setSelected: function (v) {
+ this.checkbox.setSelected(v);
+ this.setHalfSelected(false);
+ },
+
+ setHalfSelected: function (b) {
+ this.halfSelected = !!b;
+ if (b === true) {
+ this.checkbox.setSelected(false);
+ this.half.visible();
+ this.checkbox.invisible();
+ } else {
+ this.half.invisible();
+ this.checkbox.visible();
+ }
+ },
+
+ isHalfSelected: function () {
+ return !this.isSelected() && !!this.halfSelected;
+ },
+
+ isSelected: function () {
+ return this.checkbox.isSelected();
+ },
+
+ setValue: function (selectedValues) {
+ BI.MultiSelectBar.superclass.setValue.apply(this, arguments);
+ var isAllChecked = this.options.isAllCheckedBySelectedValue.apply(this, arguments);
+ this.setSelected(isAllChecked);
+ !isAllChecked && this.setHalfSelected(this.options.isHalfCheckedBySelectedValue.apply(this, arguments));
+ },
+
+ doClick: function () {
+ BI.MultiSelectBar.superclass.doClick.apply(this, arguments);
+ if(this.isValid()) {
+ this.fireEvent(BI.MultiSelectBar.EVENT_CHANGE, this.isSelected(), this);
+ }
+ }
+});
+BI.MultiSelectBar.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.multi_select_bar", BI.MultiSelectBar);
diff --git a/src/main/resources/com/fr/fineui/case/tree/tree.level.js b/src/main/resources/com/fr/fineui/case/tree/tree.level.js
new file mode 100644
index 0000000..eb6d509
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/tree/tree.level.js
@@ -0,0 +1,136 @@
+/**
+ * guy
+ * 二级树
+ * @class BI.LevelTree
+ * @extends BI.Single
+ */
+BI.LevelTree = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ return BI.extend(BI.LevelTree.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-level-tree",
+ el: {
+ chooseType: 0
+ },
+ expander: {},
+ items: [],
+ value: ""
+ });
+ },
+
+ _init: function () {
+ BI.LevelTree.superclass._init.apply(this, arguments);
+
+ this.initTree(this.options.items);
+ },
+
+ _formatItems: function (nodes, layer, pNode) {
+ var self = this;
+ BI.each(nodes, function (i, node) {
+ var extend = { layer: layer };
+ if (!BI.isKey(node.id)) {
+ node.id = BI.UUID();
+ }
+ extend.pNode = pNode;
+ if (node.isParent === true || node.parent === true || BI.isNotEmptyArray(node.children)) {
+ extend.type = "bi.mid_plus_group_node";
+ if (i === nodes.length - 1) {
+ extend.type = "bi.last_plus_group_node";
+ extend.isLastNode = true;
+ }
+ if (i === 0 && !pNode) {
+ extend.type = "bi.first_plus_group_node";
+ }
+ if (i === 0 && i === nodes.length - 1) { // 根
+ extend.type = "bi.plus_group_node";
+ }
+ BI.defaults(node, extend);
+ self._formatItems(node.children, layer + 1, node);
+ } else {
+ extend.type = "bi.mid_tree_leaf_item";
+ if (i === 0 && !pNode) {
+ extend.type = "bi.first_tree_leaf_item";
+ }
+ if (i === nodes.length - 1) {
+ extend.type = "bi.last_tree_leaf_item";
+ }
+ BI.defaults(node, extend);
+ }
+ });
+ return nodes;
+ },
+
+ _assertId: function (sNodes) {
+ BI.each(sNodes, function (i, node) {
+ if (!BI.isKey(node.id)) {
+ node.id = BI.UUID();
+ }
+ });
+ },
+
+ // 构造树结构,
+ initTree: function (nodes) {
+ var self = this, o = this.options;
+ this.empty();
+ this._assertId(nodes);
+ this.tree = BI.createWidget({
+ type: "bi.custom_tree",
+ element: this,
+ expander: BI.extend({
+ el: {},
+ popup: {
+ type: "bi.custom_tree"
+ }
+ }, o.expander),
+
+ items: this._formatItems(BI.Tree.transformToTreeFormat(nodes), 0),
+ value: o.value,
+
+ el: BI.extend({
+ type: "bi.button_tree",
+ chooseType: 0,
+ layouts: [{
+ type: "bi.vertical"
+ }]
+ }, o.el)
+ });
+ this.tree.on(BI.Controller.EVENT_CHANGE, function (type, value, ob) {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ if (type === BI.Events.CLICK) {
+ self.fireEvent(BI.LevelTree.EVENT_CHANGE, value, ob);
+ }
+ });
+ },
+
+ // 生成树方法
+ stroke: function (nodes) {
+ this.tree.stroke.apply(this.tree, arguments);
+ },
+
+ populate: function (items, keyword) {
+ items = this._formatItems(BI.Tree.transformToTreeFormat(items), 0);
+ this.tree.populate(items, keyword);
+ },
+
+ setValue: function (v) {
+ this.tree.setValue(v);
+ },
+
+ getValue: function () {
+ return this.tree.getValue();
+ },
+
+ getAllLeaves: function () {
+ return this.tree.getAllLeaves();
+ },
+
+ getNodeById: function (id) {
+ return this.tree.getNodeById(id);
+ },
+
+ getNodeByValue: function (id) {
+ return this.tree.getNodeByValue(id);
+ }
+});
+BI.LevelTree.EVENT_CHANGE = "EVENT_CHANGE";
+
+BI.shortcut("bi.level_tree", BI.LevelTree);
diff --git a/src/main/resources/com/fr/fineui/case/tree/treeexpander/tree.expander.js b/src/main/resources/com/fr/fineui/case/tree/treeexpander/tree.expander.js
new file mode 100644
index 0000000..88f5dbf
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/tree/treeexpander/tree.expander.js
@@ -0,0 +1,77 @@
+!(function () {
+ var Widget = BI.inherit(BI.Widget, {
+ props: {
+ baseCls: "bi-tree-expander",
+ layer: 0, // 第几层级
+ isLastNode: false, // 是不是最后一个
+ isFirstNode: false, // 是不是第一个
+ selectable: false,
+ },
+
+ render: function () {
+
+ var self = this;
+ var o = this.options;
+
+ this.trigger = BI.createWidget(o.el, {
+ forceNotSelected: !o.selectable,
+ });
+ this.trigger.on(BI.Controller.EVENT_CHANGE, function (type) {
+ o.selectable && self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+
+ return {
+ type: "bi.expander",
+ ref: function (_ref) {
+ self.expander = _ref;
+ },
+ trigger: o.selectable ? "" : "click",
+ el: this.trigger,
+ isDefaultInit: o.isDefaultInit,
+ popup: {
+ type: "bi.tree_expander.popup",
+ layer: o.layer || o.el.layer,
+ isLastNode: o.isLastNode || o.el.isLastNode,
+ isFirstNode: o.isFirstNode || o.el.isFirstNode,
+ el: o.popup,
+ },
+ value: o.value,
+ listeners: [
+ {
+ eventName: BI.Controller.EVENT_CHANGE,
+ action: function (type) {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ },
+ },
+ ],
+ };
+ },
+
+ setValue: function (v) {
+ if (BI.contains(v, this.trigger.getValue())) {
+ this.trigger.setSelected(true);
+ this.expander.setValue([]);
+ } else {
+ this.trigger.setSelected(false);
+ this.expander.setValue(v);
+ }
+ },
+
+ getValue: function () {
+ if (this.trigger.isSelected()) {
+ return [this.trigger.getValue()];
+ }
+ return this.expander.getValue();
+ },
+
+ populate: function (items) {
+ this.expander.populate(items);
+ },
+
+ getAllLeaves: function () {
+ return this.expander && this.expander.getAllLeaves();
+ }
+ });
+
+ BI.shortcut("bi.tree_expander", Widget);
+}());
diff --git a/src/main/resources/com/fr/fineui/case/tree/treeexpander/tree.expander.popup.js b/src/main/resources/com/fr/fineui/case/tree/treeexpander/tree.expander.popup.js
new file mode 100644
index 0000000..6deae09
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/tree/treeexpander/tree.expander.popup.js
@@ -0,0 +1,53 @@
+!(function () {
+ var Widget = BI.inherit(BI.Widget, {
+ props: {
+ baseCls: "bi-tree-expander-popup",
+ layer: 0, // 第几层级
+ el: {},
+ isLastNode: false,
+ },
+
+ render: function () {
+
+ var self = this;
+ var o = this.options;
+
+ this.popupView = BI.createWidget(BI.extend(o.el, {
+ value: o.value
+ }), this);
+
+ this.popupView.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ this.popupView.element.css("margin-left", -12 * o.layer);
+ this.element.css("margin-left", 12 * o.layer);
+
+ return {
+ type: "bi.vertical",
+ cls: !o.isLastNode ? "line" : "",
+ scrolly: null,
+ items: [
+ this.popupView,
+ ],
+ };
+ },
+
+ setValue: function (v) {
+ this.popupView.setValue(v);
+ },
+
+ getValue: function () {
+ return this.popupview.getValue();
+ },
+
+ populate: function (items) {
+ this.popupview.populate(items);
+ },
+
+ getAllLeaves: function () {
+ return this.popupView && this.popupView.getAllLeaves();
+ }
+ });
+
+ BI.shortcut("bi.tree_expander.popup", Widget);
+}());
diff --git a/src/main/resources/com/fr/fineui/case/trigger/trigger.editor.js b/src/main/resources/com/fr/fineui/case/trigger/trigger.editor.js
new file mode 100644
index 0000000..814f1b8
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/trigger/trigger.editor.js
@@ -0,0 +1,96 @@
+/**
+ * 文本输入框trigger
+ *
+ * Created by GUY on 2015/9/15.
+ * @class BI.EditorTrigger
+ * @extends BI.Trigger
+ */
+BI.EditorTrigger = BI.inherit(BI.Trigger, {
+ _const: {
+ hgap: 4
+ },
+
+ _defaultConfig: function () {
+ var conf = BI.EditorTrigger.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-editor-trigger bi-border bi-border-radius",
+ height: 24,
+ validationChecker: BI.emptyFn,
+ quitChecker: BI.emptyFn,
+ allowBlank: false,
+ watermark: "",
+ errorText: ""
+ });
+ },
+
+ _init: function () {
+ this.options.height -= 2;
+ BI.EditorTrigger.superclass._init.apply(this, arguments);
+ var self = this, o = this.options, c = this._const;
+ this.editor = BI.createWidget({
+ type: "bi.sign_editor",
+ height: o.height,
+ value: o.value,
+ validationChecker: o.validationChecker,
+ quitChecker: o.quitChecker,
+ allowBlank: o.allowBlank,
+ watermark: o.watermark,
+ errorText: o.errorText,
+ title: function () {
+ return self.getValue();
+ }
+ });
+ this.editor.on(BI.Controller.EVENT_CHANGE, function () {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ this.editor.on(BI.SignEditor.EVENT_CHANGE, function () {
+ self.fireEvent(BI.EditorTrigger.EVENT_CHANGE, arguments);
+ });
+ this.editor.on(BI.SignEditor.EVENT_FOCUS, function () {
+ self.fireEvent(BI.EditorTrigger.EVENT_FOCUS, arguments);
+ });
+ this.editor.on(BI.SignEditor.EVENT_EMPTY, function () {
+ self.fireEvent(BI.EditorTrigger.EVENT_EMPTY, arguments);
+ });
+ this.editor.on(BI.SignEditor.EVENT_VALID, function () {
+ self.fireEvent(BI.EditorTrigger.EVENT_VALID, arguments);
+ });
+ this.editor.on(BI.SignEditor.EVENT_ERROR, function () {
+ self.fireEvent(BI.EditorTrigger.EVENT_ERROR, arguments);
+ });
+
+ BI.createWidget({
+ element: this,
+ type: "bi.htape",
+ items: [
+ {
+ el: this.editor
+ }, {
+ el: {
+ type: "bi.trigger_icon_button",
+ width: o.triggerWidth || o.height
+ },
+ width: o.triggerWidth || o.height
+ }
+ ]
+ });
+ },
+
+ getValue: function () {
+ return this.editor.getValue();
+ },
+
+ setValue: function (value) {
+ this.editor.setValue(value);
+ },
+
+ setText: function (text) {
+ this.editor.setState(text);
+ }
+});
+BI.EditorTrigger.EVENT_CHANGE = "EVENT_CHANGE";
+BI.EditorTrigger.EVENT_FOCUS = "EVENT_FOCUS";
+BI.EditorTrigger.EVENT_EMPTY = "EVENT_EMPTY";
+BI.EditorTrigger.EVENT_VALID = "EVENT_VALID";
+BI.EditorTrigger.EVENT_ERROR = "EVENT_ERROR";
+BI.shortcut("bi.editor_trigger", BI.EditorTrigger);
diff --git a/src/main/resources/com/fr/fineui/case/trigger/trigger.icon.js b/src/main/resources/com/fr/fineui/case/trigger/trigger.icon.js
new file mode 100644
index 0000000..2da61d4
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/trigger/trigger.icon.js
@@ -0,0 +1,30 @@
+/**
+ * 图标按钮trigger
+ *
+ * Created by GUY on 2015/10/8.
+ * @class BI.IconTrigger
+ * @extends BI.Trigger
+ */
+BI.IconTrigger = BI.inherit(BI.Trigger, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.IconTrigger.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-icon-trigger",
+ extraCls: "pull-down-font",
+ el: {},
+ height: 24
+ });
+ },
+ _init: function () {
+ var o = this.options;
+ BI.IconTrigger.superclass._init.apply(this, arguments);
+ this.iconButton = BI.createWidget(o.el, {
+ type: "bi.trigger_icon_button",
+ element: this,
+ width: o.width,
+ height: o.height,
+ extraCls: o.extraCls
+ });
+ }
+});
+BI.shortcut("bi.icon_trigger", BI.IconTrigger);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/trigger/trigger.icon.text.js b/src/main/resources/com/fr/fineui/case/trigger/trigger.icon.text.js
new file mode 100644
index 0000000..eac94ab
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/trigger/trigger.icon.text.js
@@ -0,0 +1,105 @@
+/**
+ * 文字trigger
+ *
+ * Created by GUY on 2015/9/15.
+ * @class BI.IconTextTrigger
+ * @extends BI.Trigger
+ */
+BI.IconTextTrigger = BI.inherit(BI.Trigger, {
+ _const: {
+ hgap: 4
+ },
+
+ _defaultConfig: function () {
+ var conf = BI.IconTextTrigger.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-text-trigger",
+ height: 24,
+ iconHeight: null,
+ iconWidth: null,
+ textCls: ""
+ });
+ },
+
+ _init: function () {
+ BI.IconTextTrigger.superclass._init.apply(this, arguments);
+ var self = this, o = this.options, c = this._const;
+ this.text = BI.createWidget({
+ type: "bi.label",
+ cls: "select-text-label" + (BI.isKey(o.textCls) ? (" " + o.textCls) : ""),
+ textAlign: "left",
+ height: o.height,
+ text: o.text
+ });
+ this.trigerButton = BI.createWidget({
+ type: "bi.trigger_icon_button",
+ width: o.triggerWidth || o.height
+ });
+
+ BI.createWidget({
+ element: this,
+ type: "bi.htape",
+ ref: function (_ref) {
+ self.wrapper = _ref;
+ },
+ items: [{
+ el: {
+ type: "bi.icon_change_button",
+ cls: "icon-combo-trigger-icon",
+ iconCls: o.iconCls,
+ ref: function (_ref) {
+ self.icon = _ref;
+ },
+ iconHeight: o.iconHeight,
+ iconWidth: o.iconWidth,
+ disableSelected: true
+ },
+ width: BI.isEmptyString(o.iconCls) ? 0 : (o.iconWrapperWidth || o.height)
+ },
+ {
+ el: this.text,
+ lgap: BI.isEmptyString(o.iconCls) ? 5 : 0
+ }, {
+ el: this.trigerButton,
+ width: o.triggerWidth || o.height
+ }
+ ]
+ });
+ },
+
+ setValue: function (value) {
+ this.text.setValue(value);
+ },
+
+ setIcon: function (iconCls) {
+ var o = this.options;
+ this.icon.setIcon(iconCls);
+ var iconItem = this.wrapper.attr("items")[0];
+ var textItem = this.wrapper.attr("items")[1];
+ if(BI.isNull(iconCls) || BI.isEmptyString(iconCls)) {
+ if(iconItem.width !== 0) {
+ iconItem.width = 0;
+ textItem.lgap = 5;
+ this.wrapper.resize();
+ }
+ }else{
+ if(iconItem.width !== (o.iconWrapperWidth || o.height)) {
+ iconItem.width = (o.iconWrapperWidth || o.height);
+ textItem.lgap = 0;
+ this.wrapper.resize();
+ }
+ }
+ },
+
+ setTextCls: function(cls) {
+ var o = this.options;
+ var oldCls = o.textCls;
+ o.textCls = cls;
+ this.text.element.removeClass(oldCls).addClass(cls);
+ },
+
+ setText: function (text) {
+ this.text.setText(text);
+ }
+});
+BI.shortcut("bi.icon_text_trigger", BI.IconTextTrigger);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/trigger/trigger.icon.text.select.js b/src/main/resources/com/fr/fineui/case/trigger/trigger.icon.text.select.js
new file mode 100644
index 0000000..a980b05
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/trigger/trigger.icon.text.select.js
@@ -0,0 +1,74 @@
+/**
+ * Created by Windy on 2017/12/12.
+ */
+BI.SelectIconTextTrigger = BI.inherit(BI.Trigger, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.SelectIconTextTrigger.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-select-text-trigger",
+ height: 24,
+ iconHeight: null,
+ iconWidth: null,
+ iconCls: ""
+ });
+ },
+
+ _init: function () {
+ BI.SelectIconTextTrigger.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ var obj = this._digist(o.value, o.items);
+ this.trigger = BI.createWidget({
+ type: "bi.icon_text_trigger",
+ element: this,
+ text: obj.text,
+ textCls: obj.textCls,
+ iconCls: obj.iconCls,
+ height: o.height,
+ iconHeight: o.iconHeight,
+ iconWidth: o.iconWidth,
+ iconWrapperWidth: o.iconWrapperWidth
+ });
+ },
+
+ _digist: function (vals, items) {
+ var o = this.options;
+ vals = BI.isArray(vals) ? vals : [vals];
+ var result;
+ var formatItems = BI.Tree.transformToArrayFormat(items);
+ BI.any(formatItems, function (i, item) {
+ if (BI.deepContains(vals, item.value)) {
+ result = {
+ text: item.text || item.value,
+ iconCls: item.iconCls
+ };
+ return true;
+ }
+ });
+
+ if (BI.isNotNull(result)) {
+ return {
+ text: result.text,
+ textCls: "",
+ iconCls: result.iconCls
+ };
+ } else {
+ return {
+ text: BI.isFunction(o.text) ? o.text() : o.text,
+ textCls: "bi-water-mark",
+ iconCls: o.iconCls
+ };
+ }
+ },
+
+ setValue: function (vals) {
+ var obj = this._digist(vals, this.options.items);
+ this.trigger.setText(obj.text);
+ this.trigger.setIcon(obj.iconCls);
+ this.trigger.setTextCls(obj.textCls);
+ },
+
+ populate: function (items) {
+ this.options.items = items;
+ }
+});
+BI.shortcut("bi.select_icon_text_trigger", BI.SelectIconTextTrigger);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/trigger/trigger.text.js b/src/main/resources/com/fr/fineui/case/trigger/trigger.text.js
new file mode 100644
index 0000000..8fc69d5
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/trigger/trigger.text.js
@@ -0,0 +1,77 @@
+/**
+ * 文字trigger
+ *
+ * Created by GUY on 2015/9/15.
+ * @class BI.TextTrigger
+ * @extends BI.Trigger
+ */
+BI.TextTrigger = BI.inherit(BI.Trigger, {
+ _const: {
+ hgap: 6
+ },
+
+ _defaultConfig: function () {
+ var conf = BI.TextTrigger.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-text-trigger",
+ height: 24,
+ textCls: ""
+ });
+ },
+
+ _init: function () {
+ BI.TextTrigger.superclass._init.apply(this, arguments);
+ var self = this, o = this.options, c = this._const;
+ this.text = BI.createWidget({
+ type: "bi.label",
+ cls: "select-text-label" + (BI.isKey(o.textCls) ? (" " + o.textCls) : ""),
+ textAlign: "left",
+ height: o.height,
+ text: o.text,
+ title: function () {
+ return self.text.getText();
+ },
+ tipType: o.tipType,
+ warningTitle: o.warningTitle,
+ hgap: c.hgap,
+ readonly: o.readonly
+ });
+ this.trigerButton = BI.createWidget({
+ type: "bi.trigger_icon_button",
+ width: o.triggerWidth || o.height
+ });
+
+ BI.createWidget({
+ element: this,
+ type: "bi.htape",
+ items: [
+ {
+ el: this.text
+ }, {
+ el: this.trigerButton,
+ width: o.triggerWidth || o.height
+ }
+ ]
+ });
+ },
+
+ getTextor: function() {
+ return this.text;
+ },
+
+ setTextCls: function(cls) {
+ var o = this.options;
+ var oldCls = o.textCls;
+ o.textCls = cls;
+ this.text.element.removeClass(oldCls).addClass(cls);
+ },
+
+ setText: function (text) {
+ this.text.setText(text);
+ },
+
+ setTipType: function (v) {
+ this.text.options.tipType = v;
+ }
+});
+BI.shortcut("bi.text_trigger", BI.TextTrigger);
diff --git a/src/main/resources/com/fr/fineui/case/trigger/trigger.text.select.js b/src/main/resources/com/fr/fineui/case/trigger/trigger.text.select.js
new file mode 100644
index 0000000..03481d1
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/trigger/trigger.text.select.js
@@ -0,0 +1,75 @@
+/**
+ * 选择字段trigger
+ *
+ * Created by GUY on 2015/9/15.
+ * @class BI.SelectTextTrigger
+ * @extends BI.Trigger
+ */
+BI.SelectTextTrigger = BI.inherit(BI.Trigger, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.SelectTextTrigger.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-select-text-trigger",
+ height: 24
+ });
+ },
+
+ _init: function () {
+ BI.SelectTextTrigger.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ var obj = this._digest(o.value, o.items);
+ this.trigger = BI.createWidget({
+ type: "bi.text_trigger",
+ element: this,
+ height: o.height,
+ readonly: o.readonly,
+ text: obj.text,
+ textCls: obj.textCls,
+ tipType: o.tipType,
+ warningTitle: o.warningTitle
+ });
+ },
+
+ _digest: function(vals, items){
+ var o = this.options;
+ vals = BI.isArray(vals) ? vals : [vals];
+ var result = [];
+ var formatItems = BI.Tree.transformToArrayFormat(items);
+ BI.each(formatItems, function (i, item) {
+ if (BI.deepContains(vals, item.value) && !BI.contains(result, item.text || item.value)) {
+ result.push(item.text || item.value);
+ }
+ });
+
+ if (result.length > 0) {
+ return {
+ textCls: "",
+ text: result.join(",")
+ }
+ } else {
+ return {
+ textCls: "bi-water-mark",
+ text: BI.isFunction(o.text) ? o.text() : o.text
+ }
+ }
+ },
+
+ setValue: function (vals) {
+ var formatValue = this._digest(vals, this.options.items);
+ this.trigger.setTextCls(formatValue.textCls);
+ this.trigger.setText(formatValue.text);
+ },
+
+ setTipType: function (v) {
+ this.trigger.setTipType(v);
+ },
+
+ getTextor: function() {
+ return this.trigger.getTextor();
+ },
+
+ populate: function (items) {
+ this.options.items = items;
+ }
+});
+BI.shortcut("bi.select_text_trigger", BI.SelectTextTrigger);
diff --git a/src/main/resources/com/fr/fineui/case/trigger/trigger.text.select.small.js b/src/main/resources/com/fr/fineui/case/trigger/trigger.text.select.small.js
new file mode 100644
index 0000000..215a04f
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/trigger/trigger.text.select.small.js
@@ -0,0 +1,64 @@
+/**
+ * 选择字段trigger小一号的
+ *
+ * @class BI.SmallSelectTextTrigger
+ * @extends BI.Trigger
+ */
+BI.SmallSelectTextTrigger = BI.inherit(BI.Trigger, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.SmallSelectTextTrigger.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-small-select-text-trigger bi-border",
+ height: 20
+ });
+ },
+
+ _init: function () {
+ this.options.height -= 2;
+ BI.SmallSelectTextTrigger.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ var obj = this._digest(o.value, o.items);
+ this.trigger = BI.createWidget({
+ type: "bi.small_text_trigger",
+ element: this,
+ height: o.height,
+ text: obj.text,
+ cls: obj.cls
+ });
+ },
+
+ _digest: function(vals, items){
+ var o = this.options;
+ vals = BI.isArray(vals) ? vals : [vals];
+ var result = [];
+ var formatItems = BI.Tree.transformToArrayFormat(items);
+ BI.each(formatItems, function (i, item) {
+ if (BI.deepContains(vals, item.value) && !BI.contains(result, item.text || item.value)) {
+ result.push(item.text || item.value);
+ }
+ });
+
+ if (result.length > 0) {
+ return {
+ cls: "",
+ text: result.join(",")
+ }
+ } else {
+ return {
+ cls: "bi-water-mark",
+ text: o.text
+ }
+ }
+ },
+
+ setValue: function (vals) {
+ var formatValue = this._digest(vals, this.options.items);
+ this.trigger.element.removeClass("bi-water-mark").addClass(formatValue.cls);
+ this.trigger.setText(formatValue.text);
+ },
+
+ populate: function (items) {
+ this.options.items = items;
+ }
+});
+BI.shortcut("bi.small_select_text_trigger", BI.SmallSelectTextTrigger);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/trigger/trigger.text.small.js b/src/main/resources/com/fr/fineui/case/trigger/trigger.text.small.js
new file mode 100644
index 0000000..1ddc390
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/trigger/trigger.text.small.js
@@ -0,0 +1,57 @@
+/**
+ * 文字trigger(右边小三角小一号的) ==
+ *
+ * @class BI.SmallTextTrigger
+ * @extends BI.Trigger
+ */
+BI.SmallTextTrigger = BI.inherit(BI.Trigger, {
+ _const: {
+ hgap: 6
+ },
+
+ _defaultConfig: function () {
+ var conf = BI.SmallTextTrigger.superclass._defaultConfig.apply(this, arguments);
+ return BI.extend(conf, {
+ baseCls: (conf.baseCls || "") + " bi-text-trigger",
+ height: 20
+ });
+ },
+
+ _init: function () {
+ BI.SmallTextTrigger.superclass._init.apply(this, arguments);
+ var self = this, o = this.options, c = this._const;
+ this.text = BI.createWidget({
+ type: "bi.label",
+ textAlign: "left",
+ height: o.height,
+ text: o.text,
+ hgap: c.hgap
+ });
+ this.trigerButton = BI.createWidget({
+ type: "bi.trigger_icon_button",
+ width: o.triggerWidth || o.height
+ });
+
+ BI.createWidget({
+ element: this,
+ type: "bi.htape",
+ items: [
+ {
+ el: this.text
+ }, {
+ el: this.trigerButton,
+ width: o.triggerWidth || o.height
+ }
+ ]
+ });
+ },
+
+ setValue: function (value) {
+ this.text.setValue(value);
+ },
+
+ setText: function (text) {
+ this.text.setText(text);
+ }
+});
+BI.shortcut("bi.small_text_trigger", BI.SmallTextTrigger);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/ztree/0.treeview.js b/src/main/resources/com/fr/fineui/case/ztree/0.treeview.js
new file mode 100644
index 0000000..f67c929
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/ztree/0.treeview.js
@@ -0,0 +1,550 @@
+/**
+ * guy
+ * 异步树
+ * @class BI.TreeView
+ * @extends BI.Pane
+ */
+BI.TreeView = BI.inherit(BI.Pane, {
+ _defaultConfig: function () {
+ return BI.extend(BI.TreeView.superclass._defaultConfig.apply(this, arguments), {
+ _baseCls: "bi-tree",
+ paras: {
+ selectedValues: {}
+ },
+ itemsCreator: BI.emptyFn,
+ showLine: true
+ });
+ },
+ _init: function () {
+ BI.TreeView.superclass._init.apply(this, arguments);
+ var o = this.options;
+ this._stop = false;
+
+ this._createTree();
+ this.tip = BI.createWidget({
+ type: "bi.loading_bar",
+ invisible: true,
+ handler: BI.bind(this._loadMore, this)
+ });
+ BI.createWidget({
+ type: "bi.vertical",
+ scrollable: true,
+ scrolly: false,
+ element: this,
+ items: [this.tip]
+ });
+ if (BI.isNotNull(o.value)) {
+ this.setSelectedValue(o.value);
+ }
+ if (BI.isIE9Below && BI.isIE9Below()) {
+ this.element.addClass("hack");
+ }
+ },
+
+ _createTree: function () {
+ this.id = "bi-tree" + BI.UUID();
+ if (this.nodes) {
+ this.nodes.destroy();
+ }
+ if (this.tree) {
+ this.tree.destroy();
+ }
+ this.tree = BI.createWidget({
+ type: "bi.layout",
+ element: "
"
+ });
+ BI.createWidget({
+ type: "bi.default",
+ element: this.element,
+ items: [this.tree]
+ });
+ },
+
+ // 选择节点触发方法
+ _selectTreeNode: function (treeId, treeNode) {
+ this.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.CLICK, treeNode, this);
+ this.fireEvent(BI.TreeView.EVENT_CHANGE, treeNode, this);
+ },
+
+ // 配置属性
+ _configSetting: function () {
+ var paras = this.options.paras;
+ var self = this;
+ var o = this.options;
+ var setting = {
+ async: {
+ enable: true,
+ url: getUrl,
+ autoParam: ["id", "name"], // 节点展开异步请求自动提交id和name
+ otherParam: BI.cjkEncodeDO(paras) // 静态参数
+ },
+ check: {
+ enable: true
+ },
+ data: {
+ key: {
+ title: "title",
+ name: "text" // 节点的name属性替换成text
+ },
+ simpleData: {
+ enable: true // 可以穿id,pid属性的对象数组
+ }
+ },
+ view: {
+ showIcon: false,
+ expandSpeed: "",
+ nameIsHTML: true, // 节点可以用html标签代替
+ dblClickExpand: false,
+ showLine: o.showLine
+ },
+ callback: {
+ beforeExpand: beforeExpand,
+ onAsyncSuccess: onAsyncSuccess,
+ onAsyncError: onAsyncError,
+ beforeCheck: beforeCheck,
+ onCheck: onCheck,
+ onExpand: onExpand,
+ onCollapse: onCollapse,
+ onClick: onClick
+ }
+ };
+ var className = "dark", perTime = 100;
+
+ function onClick (event, treeId, treeNode) {
+ // 当前点击节点的状态是半选,且为true_part, 则将其改为false_part,使得点击半选后切换到的是全选
+ var checked = treeNode.checked;
+ var status = treeNode.getCheckStatus();
+ if (status.half === true && status.checked === true) {
+ checked = false;
+ }
+ // 更新此node的check状态, 影响父子关联,并调用beforeCheck和onCheck回调
+ self.nodes.checkNode(treeNode, !checked, true, true);
+ }
+
+ function getUrl (treeId, treeNode) {
+ var parentNode = self._getParentValues(treeNode);
+ treeNode.times = treeNode.times || 1;
+ var param = "id=" + treeNode.id
+ + "×=" + (treeNode.times++)
+ + "&parentValues= " + _global.encodeURIComponent(BI.jsonEncode(parentNode))
+ + "&checkState=" + _global.encodeURIComponent(BI.jsonEncode(treeNode.getCheckStatus()));
+
+ return "&" + param;
+ }
+
+ function beforeExpand (treeId, treeNode) {
+ if (!treeNode.isAjaxing) {
+ if (!treeNode.children) {
+ treeNode.times = 1;
+ ajaxGetNodes(treeNode, "refresh");
+ }
+ return true;
+ }
+ BI.Msg.toast("Please Wait。", "warning"); // 不展开节点,也不触发onExpand事件
+ return false;
+
+ }
+
+ function onAsyncSuccess (event, treeId, treeNode, msg) {
+ treeNode.halfCheck = false;
+ if (!msg || msg.length === 0 || /^[\s,\S]*<\/html>$/gi.test(msg) || self._stop) {
+ return;
+ }
+ var zTree = self.nodes;
+ var totalCount = treeNode.count || 0;
+
+ // 尝试去获取下一组节点,若获取值为空数组,表示获取完成
+ // TODO by GUY
+ if (treeNode.children.length > totalCount) {
+ treeNode.count = treeNode.children.length;
+ BI.delay(function () {
+ ajaxGetNodes(treeNode);
+ }, perTime);
+ } else {
+ // treeNode.icon = "";
+ zTree.updateNode(treeNode);
+ zTree.selectNode(treeNode.children[0]);
+ // className = (className === "dark" ? "":"dark");
+ }
+ }
+
+ function onAsyncError (event, treeId, treeNode, XMLHttpRequest, textStatus, errorThrown) {
+ var zTree = self.nodes;
+ BI.Msg.toast("Error!", "warning");
+ // treeNode.icon = "";
+ // zTree.updateNode(treeNode);
+ }
+
+ function ajaxGetNodes (treeNode, reloadType) {
+ var zTree = self.nodes;
+ if (reloadType == "refresh") {
+ zTree.updateNode(treeNode); // 刷新一下当前节点,如果treeNode.xxx被改了的话
+ }
+ zTree.reAsyncChildNodes(treeNode, reloadType, true); // 强制加载子节点,reloadType === refresh为先清空再加载,否则为追加到现有子节点之后
+ }
+
+ function beforeCheck (treeId, treeNode) {
+ // 下面主动修改了node的halfCheck属性, 节点属性的判断依赖halfCheck,改之前就获取一下
+ var status = treeNode.getCheckStatus();
+ treeNode.halfCheck = false;
+ if (treeNode.checked === true) {
+ // 将展开的节点halfCheck设为false,解决展开节点存在halfCheck=true的情况 guy
+ // 所有的半选状态都需要取消halfCheck=true的情况
+ function track (children) {
+ BI.each(children, function (i, ch) {
+ if (ch.halfCheck === true) {
+ ch.halfCheck = false;
+ track(ch.children);
+ }
+ });
+ }
+
+ track(treeNode.children);
+ var treeObj = self.nodes;
+ var nodes = treeObj.getSelectedNodes();
+ BI.$.each(nodes, function (index, node) {
+ node.halfCheck = false;
+ });
+ }
+ // 当前点击节点的状态是半选,且为true_part, 则将其改为false_part,使得点击半选后切换到的是全选
+ if (status.half === true && status.checked === true) {
+ treeNode.checked = false;
+ }
+ }
+
+ function onCheck (event, treeId, treeNode) {
+ self._selectTreeNode(treeId, treeNode);
+ }
+
+ function onExpand (event, treeId, treeNode) {
+ treeNode.halfCheck = false;
+ }
+
+ function onCollapse (event, treeId, treeNode) {
+ }
+
+ return setting;
+ },
+
+ _getParentValues: function (treeNode) {
+ if (!treeNode.getParentNode()) {
+ return [];
+ }
+ var parentNode = treeNode.getParentNode();
+ var result = this._getParentValues(parentNode);
+ result = result.concat([this._getNodeValue(parentNode)]);
+ return result;
+ },
+
+ _getNodeValue: function (node) {
+ // 去除标红
+ return BI.isUndefined(node.value) ? BI.replaceAll(node.text.replace(/<[^>]+>/g, ""), " ", " ") : node.value;
+ },
+
+ // 获取半选框值
+ _getHalfSelectedValues: function (map, node) {
+ var self = this;
+ var checkState = node.getCheckStatus();
+ // 将未选的去掉
+ if (checkState.checked === false && checkState.half === false) {
+ return;
+ }
+ // 如果节点已展开,并且是半选
+ if (BI.isNotEmptyArray(node.children) && checkState.half === true) {
+ var children = node.children;
+ BI.each(children, function (i, ch) {
+ self._getHalfSelectedValues(map, ch);
+ });
+ return;
+ }
+ var parent = node.parentValues || self._getParentValues(node);
+ var path = parent.concat(this._getNodeValue(node));
+ // 当前节点是全选的,因为上面的判断已经排除了不选和半选
+ if (BI.isNotEmptyArray(node.children) || checkState.half === false) {
+ this._buildTree(map, path);
+ return;
+ }
+ // 剩下的就是半选不展开的节点,因为不知道里面是什么情况,所以借助selectedValues(这个是完整的选中情况)
+ var storeValues = BI.deepClone(this.options.paras.selectedValues);
+ var treeNode = this._getTree(storeValues, path);
+ this._addTreeNode(map, parent, this._getNodeValue(node), treeNode);
+ },
+
+ // 获取的是以values最后一个节点为根的子树
+ _getTree: function (map, values) {
+ var cur = map;
+ BI.any(values, function (i, value) {
+ if (cur[value] == null) {
+ return true;
+ }
+ cur = cur[value];
+ });
+ return cur;
+ },
+
+ // 以values为path一路向里补充map, 并在末尾节点添加key: value节点
+ _addTreeNode: function (map, values, key, value) {
+ var cur = map;
+ BI.each(values, function (i, value) {
+ if (cur[value] == null) {
+ cur[value] = {};
+ }
+ cur = cur[value];
+ });
+ cur[key] = value;
+ },
+
+ // 构造树节点
+ _buildTree: function (map, values) {
+ var cur = map;
+ BI.each(values, function (i, value) {
+ if (cur[value] == null) {
+ cur[value] = {};
+ }
+ cur = cur[value];
+ });
+ },
+
+ // 获取选中的值
+ _getSelectedValues: function () {
+ var self = this;
+ var hashMap = {};
+ var rootNoots = this.nodes.getNodes();
+ track(rootNoots); // 可以看到这个方法没有递归调用,所以在_getHalfSelectedValues中需要关心全选的节点
+ function track (nodes) {
+ BI.each(nodes, function (i, node) {
+ var checkState = node.getCheckStatus();
+ if (checkState.checked === true || checkState.half === true) {
+ if (checkState.half === true) {
+ self._getHalfSelectedValues(hashMap, node);
+ } else {
+ var parentValues = node.parentValues || self._getParentValues(node);
+ var values = parentValues.concat([self._getNodeValue(node)]);
+ self._buildTree(hashMap, values);
+ }
+ }
+ });
+ }
+
+ return hashMap;
+ },
+
+ // 处理节点
+ _dealWidthNodes: function (nodes) {
+ var self = this, o = this.options;
+ var ns = BI.Tree.arrayFormat(nodes);
+ BI.each(ns, function (i, n) {
+ n.isParent = n.isParent || n.parent;
+ n.value = BI.isUndefined(n.value) ? n.text : n.value;
+ n.text = BI.isUndefined(n.text) ? n.value : n.text;
+ if (n.text === null) {
+ n.text = "";
+ }
+ if (BI.isNull(n.title)) {
+ n.title = n.text;
+ }
+ // 处理标红
+ if (BI.isNotNull(n.text)) {
+ if (BI.isKey(o.paras.keyword)) {
+ n.text = BI.$("
").__textKeywordMarked__(BI.Text.formatText(n.text + ""), o.paras.keyword, n.py).html();
+ } else {
+ n.text = BI.htmlEncode(BI.Text.formatText(n.text + ""));
+ }
+ }
+ });
+ return nodes;
+ },
+
+ _loadMore: function () {
+ var self = this, o = this.options;
+ this.tip.setLoading();
+ var op = BI.extend({}, o.paras, {
+ times: ++this.times
+ });
+ o.itemsCreator(op, function (res) {
+ if (self._stop === true) {
+ return;
+ }
+ var hasNext = !!res.hasNext, nodes = res.items || [];
+
+ if (!hasNext) {
+ self.tip.setEnd();
+ } else {
+ self.tip.setLoaded();
+ }
+ if (nodes.length > 0) {
+ self.nodes.addNodes(null, self._dealWidthNodes(nodes));
+ }
+ });
+ },
+
+ // 生成树内部方法
+ _initTree: function (setting) {
+ var self = this, o = this.options;
+ self.fireEvent(BI.Events.INIT);
+ this.times = 1;
+ var tree = this.tree;
+ tree.empty();
+ this.loading();
+ this.tip.setVisible(false);
+ var callback = function (nodes) {
+ if (self._stop === true) {
+ return;
+ }
+ self.nodes = BI.$.fn.zTree.init(tree.element, setting, nodes);
+ };
+ var op = BI.extend({}, o.paras, {
+ times: 1
+ });
+
+ o.itemsCreator(op, function (res) {
+ if (self._stop === true) {
+ return;
+ }
+ var hasNext = !!res.hasNext, nodes = res.items || [];
+ if (nodes.length > 0) {
+ callback(self._dealWidthNodes(nodes));
+ }
+ self.setTipVisible(nodes.length <= 0);
+ self.loaded();
+ if (!hasNext) {
+ self.tip.invisible();
+ } else {
+ self.tip.setLoaded();
+ }
+ op.times === 1 && self.fireEvent(BI.Events.AFTERINIT);
+ });
+ },
+
+ // 构造树结构,
+ initTree: function (nodes, setting) {
+ var setting = setting || {
+ async: {
+ enable: false
+ },
+ check: {
+ enable: false
+ },
+ data: {
+ key: {
+ title: "title",
+ name: "text"
+ },
+ simpleData: {
+ enable: true
+ }
+ },
+ view: {
+ showIcon: false,
+ expandSpeed: "",
+ nameIsHTML: true
+ },
+ callback: {}
+ };
+ this.nodes = BI.$.fn.zTree.init(this.tree.element, setting, nodes);
+ },
+
+ start: function () {
+ this._stop = false;
+ },
+
+ stop: function () {
+ this._stop = true;
+ },
+
+ // 生成树方法
+ stroke: function (config) {
+ delete this.options.keyword;
+ BI.extend(this.options.paras, config);
+ var setting = this._configSetting();
+ this._createTree();
+ this.start();
+ this._initTree(setting);
+ },
+
+ populate: function () {
+ this.stroke.apply(this, arguments);
+ },
+
+ hasChecked: function () {
+ var treeObj = this.nodes;
+ return treeObj.getCheckedNodes(true).length > 0;
+ },
+
+ checkAll: function (checked) {
+ function setNode (children) {
+ BI.each(children, function (i, child) {
+ child.halfCheck = false;
+ setNode(child.children);
+ });
+ }
+
+ if (!this.nodes) {
+ return;
+ }
+
+ BI.each(this.nodes.getNodes(), function (i, node) {
+ node.halfCheck = false;
+ setNode(node.children);
+ });
+ this.nodes.checkAllNodes(checked);
+ },
+
+ expandAll: function (flag) {
+ this.nodes && this.nodes.expandAll(flag);
+ },
+
+ // 设置树节点的状态
+ setValue: function (value, param) {
+ this.checkAll(false);
+ this.updateValue(value, param);
+ this.refresh();
+ },
+
+ setSelectedValue: function (value) {
+ this.options.paras.selectedValues = BI.deepClone(value || {});
+ },
+
+ updateValue: function (values, param) {
+ if (!this.nodes) {
+ return;
+ }
+ param || (param = "value");
+ var treeObj = this.nodes;
+ BI.each(values, function (v, op) {
+ var nodes = treeObj.getNodesByParam(param, v, null);
+ BI.each(nodes, function (j, node) {
+ BI.extend(node, {checked: true}, op);
+ treeObj.updateNode(node);
+ });
+ });
+ },
+
+ refresh: function () {
+ this.nodes && this.nodes.refresh();
+ },
+
+ getValue: function () {
+ if (!this.nodes) {
+ return null;
+ }
+ return this._getSelectedValues();
+ },
+
+ destroyed: function () {
+ this.stop();
+ this.nodes && this.nodes.destroy();
+ }
+});
+BI.extend(BI.TreeView, {
+ REQ_TYPE_INIT_DATA: 1,
+ REQ_TYPE_ADJUST_DATA: 2,
+ REQ_TYPE_SELECT_DATA: 3,
+ REQ_TYPE_GET_SELECTED_DATA: 4
+});
+
+BI.TreeView.EVENT_CHANGE = "EVENT_CHANGE";
+BI.TreeView.EVENT_INIT = BI.Events.INIT;
+BI.TreeView.EVENT_AFTERINIT = BI.Events.AFTERINIT;
+
+BI.shortcut("bi.tree_view", BI.TreeView);
diff --git a/src/main/resources/com/fr/fineui/case/ztree/1.asynctree.js b/src/main/resources/com/fr/fineui/case/ztree/1.asynctree.js
new file mode 100644
index 0000000..9bff9ee
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/ztree/1.asynctree.js
@@ -0,0 +1,248 @@
+/**
+ * guy
+ * 同步树
+ * @class BI.AsyncTree
+ * @extends BI.TreeView
+ */
+BI.AsyncTree = BI.inherit(BI.TreeView, {
+ _defaultConfig: function () {
+ return BI.extend(BI.AsyncTree.superclass._defaultConfig.apply(this, arguments), {});
+ },
+ _init: function () {
+ BI.AsyncTree.superclass._init.apply(this, arguments);
+ var self = this;
+ this.service = new BI.TreeRenderPageService({
+ subNodeListGetter: function (tId) {
+ // 获取待检测的子节点列表, ztree并没有获取节点列表dom的API, 此处使用BI.$获取
+ return BI.$("#" + self.id + " #" + tId + "_ul");
+ }
+ });
+ },
+
+ // 配置属性
+ _configSetting: function () {
+ var o = this.options;
+ var paras = this.options.paras;
+ var self = this;
+ var setting = {
+ async: {
+ enable: false, // 很明显这棵树把异步请求关掉了,所有的异步请求都是手动控制的
+ otherParam: BI.cjkEncodeDO(paras)
+ },
+ check: {
+ enable: true
+ },
+ data: {
+ key: {
+ title: "title",
+ name: "text"
+ },
+ simpleData: {
+ enable: true
+ }
+ },
+ view: {
+ showIcon: false,
+ expandSpeed: "",
+ nameIsHTML: true,
+ dblClickExpand: false,
+ showLine: o.showLine
+ },
+ callback: {
+ beforeCheck: beforeCheck,
+ onCheck: onCheck,
+ beforeExpand: beforeExpand,
+ onExpand: onExpand,
+ onCollapse: onCollapse,
+ onClick: onClick
+ }
+ };
+
+ function onClick (event, treeId, treeNode) {
+ var zTree = BI.$.fn.zTree.getZTreeObj(treeId);
+ // 当前点击节点的状态是半选,且为true_part, 则将其改为false_part,使得点击半选后切换到的是全选
+ var checked = treeNode.checked;
+ var status = treeNode.getCheckStatus();
+ if (status.half === true && status.checked === true) {
+ checked = false;
+ }
+ zTree.checkNode(treeNode, !checked, true, true);
+ }
+
+ function beforeCheck (treeId, treeNode) {
+ // 下面主动修改了node的halfCheck属性, 节点属性的判断依赖halfCheck,改之前就获取一下
+ var status = treeNode.getCheckStatus();
+ treeNode.halfCheck = false;
+ if (treeNode.checked === true) {
+ // 将展开的节点halfCheck设为false,解决展开节点存在halfCheck=true的情况 guy
+ // 所有的半选状态都需要取消halfCheck=true的情况
+ function track (children) {
+ BI.each(children, function (i, ch) {
+ ch.halfCheck = false;
+ track(ch.children);
+ });
+ }
+
+ track(treeNode.children);
+
+ var treeObj = BI.$.fn.zTree.getZTreeObj(treeId);
+ var nodes = treeObj.getSelectedNodes();
+ BI.each(nodes, function (index, node) {
+ node.halfCheck = false;
+ });
+ }
+ // 当前点击节点的状态是半选,且为true_part, 则将其改为false_part,使得点击半选后切换到的是全选
+ if (status.half === true && status.checked === true) {
+ treeNode.checked = false;
+ }
+ }
+
+ function beforeExpand (treeId, treeNode) {
+ self._beforeExpandNode(treeId, treeNode);
+ }
+
+ function onCheck (event, treeId, treeNode) {
+ self._selectTreeNode(treeId, treeNode);
+ }
+
+ function onExpand (event, treeId, treeNode) {
+ treeNode.halfCheck = false;
+ }
+
+ function onCollapse (event, treeId, treeNode) {
+ treeNode.halfCheck = false;
+ }
+
+ return setting;
+ },
+
+ // 用来更新this.options.paras.selectedValues, 和ztree内部无关
+ _selectTreeNode: function (treeId, treeNode) {
+ var self = this, o = this.options;
+ var parentValues = BI.deepClone(treeNode.parentValues || self._getParentValues(treeNode));
+ var name = this._getNodeValue(treeNode);
+ // var values = parentValues.concat([name]);
+ if (treeNode.checked === true) {
+ this._addTreeNode(this.options.paras.selectedValues, parentValues, name, {});
+ } else {
+ var tNode = treeNode;
+ var pNode = this._getTree(this.options.paras.selectedValues, parentValues);
+ if (BI.isNotNull(pNode[name])) {
+ delete pNode[name];
+ }
+ while (tNode != null && BI.isEmpty(pNode)) {
+ parentValues = parentValues.slice(0, parentValues.length - 1);
+ tNode = tNode.getParentNode();
+ if (tNode != null) {
+ pNode = this._getTree(this.options.paras.selectedValues, parentValues);
+ name = this._getNodeValue(tNode);
+ delete pNode[name];
+ }
+ }
+ this.options.paras.selectedValues = this._getJoinValue();
+ }
+ BI.AsyncTree.superclass._selectTreeNode.apply(self, arguments);
+ },
+
+ // 展开节点
+ _beforeExpandNode: function (treeId, treeNode) {
+ var self = this, o = this.options;
+ var complete = function (d) {
+ var nodes = d.items || [];
+ if (nodes.length > 0) {
+ callback(self._dealWidthNodes(nodes), !!d.hasNext);
+ }
+ };
+
+ function callback(nodes, hasNext) {
+ self.nodes.addNodes(treeNode, nodes);
+ if (hasNext) {
+ self.service.pushNodeList(treeNode.tId, getNodes);
+ } else {
+ self.service.removeNodeList(treeNode.tId);
+ }
+
+ }
+
+ function getNodes(options) {
+ // console.log(times);
+ options = options || {};
+ var parentValues = treeNode.parentValues || self._getParentValues(treeNode);
+ var op = BI.extend({}, o.paras, {
+ id: treeNode.id,
+ times: options.times,
+ parentValues: parentValues.concat(self._getNodeValue(treeNode)),
+ checkState: treeNode.getCheckStatus()
+ }, options);
+ o.itemsCreator(op, complete);
+ }
+
+ // 展开节点会将halfCheck置为false以开启自动计算半选, 所以第一次展开节点的时候需要在置为false之前获取配置
+ var checkState = treeNode.getCheckStatus();
+ if (!treeNode.children) {
+ setTimeout(function () {
+ getNodes({
+ times: 1,
+ checkState: checkState
+ });
+ }, 17);
+ }
+ },
+
+ // a,b 两棵树
+ // a->b b->a 做两次校验, 构造一个校验后的map
+ // e.g. 以a为基准,如果b没有此节点,则在map中添加。 如b有,且是全选的, 则在map中构造全选(为什么不添加a的值呢? 因为这次是取并集), 如果b中也有和a一样的存值,就递归
+ _join: function (valueA, valueB) {
+ var self = this;
+ var map = {};
+ track([], valueA, valueB);
+ track([], valueB, valueA);
+
+ function track (parent, node, compare) {
+ BI.each(node, function (n, item) {
+ if (BI.isNull(compare[n])) {
+ self._addTreeNode(map, parent, n, item);
+ } else if (BI.isEmpty(compare[n])) {
+ self._addTreeNode(map, parent, n, item);
+ } else {
+ track(parent.concat([n]), node[n], compare[n]);
+ }
+ });
+ }
+
+ return map;
+ },
+
+ hasChecked: function () {
+ return !BI.isEmpty(this.options.paras.selectedValues) || BI.AsyncTree.superclass.hasChecked.apply(this, arguments);
+ },
+
+ _getJoinValue: function () {
+ if (!this.nodes) {
+ return this.options.paras.selectedValues || {};
+ }
+ var checkedValues = this._getSelectedValues();
+ if (BI.isEmpty(checkedValues)) {
+ return BI.deepClone(this.options.paras.selectedValues);
+ }
+ if (BI.isEmpty(this.options.paras.selectedValues)) {
+ return checkedValues;
+ }
+ return this._join(checkedValues, this.options.paras.selectedValues);
+ },
+
+ getValue: function () {
+ return this._getJoinValue();
+ },
+
+ // 生成树方法
+ stroke: function (config) {
+ delete this.options.keyword;
+ this.service.clear();
+ BI.extend(this.options.paras, config);
+ var setting = this._configSetting();
+ this._initTree(setting);
+ }
+});
+
+BI.shortcut("bi.async_tree", BI.AsyncTree);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/ztree/jquery.ztree.core-3.5.js b/src/main/resources/com/fr/fineui/case/ztree/jquery.ztree.core-3.5.js
new file mode 100644
index 0000000..d0f2f46
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/ztree/jquery.ztree.core-3.5.js
@@ -0,0 +1,1715 @@
+/*
+ * JQuery zTree core v3.5.18
+ * http://zTree.me/
+ *
+ * Copyright (c) 2010 Hunter.z
+ *
+ * Licensed same as jquery - MIT License
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * email: hunter.z@263.net
+ * Date: 2015-06-18
+ */
+(function($){
+ var settings = {}, roots = {}, caches = {},
+ //default consts of core
+ _consts = {
+ className: {
+ BUTTON: "button",
+ LEVEL: "level",
+ ICO_LOADING: "ico_loading",
+ SWITCH: "switch"
+ },
+ event: {
+ NODECREATED: "ztree_nodeCreated",
+ CLICK: "ztree_click",
+ EXPAND: "ztree_expand",
+ COLLAPSE: "ztree_collapse",
+ ASYNC_SUCCESS: "ztree_async_success",
+ ASYNC_ERROR: "ztree_async_error",
+ REMOVE: "ztree_remove",
+ SELECTED: "ztree_selected",
+ UNSELECTED: "ztree_unselected"
+ },
+ id: {
+ A: "_a",
+ ICON: "_ico",
+ SPAN: "_span",
+ SWITCH: "_switch",
+ UL: "_ul"
+ },
+ line: {
+ ROOT: "root",
+ ROOTS: "roots",
+ CENTER: "center",
+ BOTTOM: "bottom",
+ NOLINE: "noline",
+ LINE: "line"
+ },
+ folder: {
+ OPEN: "open",
+ CLOSE: "close",
+ DOCU: "docu"
+ },
+ node: {
+ CURSELECTED: "curSelectedNode"
+ }
+ },
+ //default setting of core
+ _setting = {
+ treeId: "",
+ treeObj: null,
+ view: {
+ addDiyDom: null,
+ autoCancelSelected: true,
+ dblClickExpand: true,
+ expandSpeed: "fast",
+ fontCss: {},
+ nameIsHTML: false,
+ selectedMulti: true,
+ showIcon: true,
+ showLine: true,
+ showTitle: true,
+ txtSelectedEnable: false
+ },
+ data: {
+ key: {
+ children: "children",
+ name: "name",
+ title: "",
+ url: "url"
+ },
+ simpleData: {
+ enable: false,
+ idKey: "id",
+ pIdKey: "pId",
+ rootPId: null
+ },
+ keep: {
+ parent: false,
+ leaf: false
+ }
+ },
+ async: {
+ enable: false,
+ contentType: "application/x-www-form-urlencoded",
+ type: "post",
+ dataType: "text",
+ url: "",
+ autoParam: [],
+ otherParam: [],
+ dataFilter: null
+ },
+ callback: {
+ beforeAsync:null,
+ beforeClick:null,
+ beforeDblClick:null,
+ beforeRightClick:null,
+ beforeMouseDown:null,
+ beforeMouseUp:null,
+ beforeExpand:null,
+ beforeCollapse:null,
+ beforeRemove:null,
+
+ onAsyncError:null,
+ onAsyncSuccess:null,
+ onNodeCreated:null,
+ onClick:null,
+ onDblClick:null,
+ onRightClick:null,
+ onMouseDown:null,
+ onMouseUp:null,
+ onExpand:null,
+ onCollapse:null,
+ onRemove:null
+ }
+ },
+ //default root of core
+ //zTree use root to save full data
+ _initRoot = function (setting) {
+ var r = data.getRoot(setting);
+ if (!r) {
+ r = {};
+ data.setRoot(setting, r);
+ }
+ r[setting.data.key.children] = [];
+ r.expandTriggerFlag = false;
+ r.curSelectedList = [];
+ r.noSelection = true;
+ r.createdNodes = [];
+ r.zId = 0;
+ r._ver = (new Date()).getTime();
+ },
+ //default cache of core
+ _initCache = function(setting) {
+ var c = data.getCache(setting);
+ if (!c) {
+ c = {};
+ data.setCache(setting, c);
+ }
+ c.nodes = [];
+ c.doms = [];
+ },
+ //default bindEvent of core
+ _bindEvent = function(setting) {
+ var o = setting.treeObj,
+ c = consts.event;
+ o.bind(c.NODECREATED, function (event, treeId, node) {
+ tools.apply(setting.callback.onNodeCreated, [event, treeId, node]);
+ });
+
+ o.bind(c.CLICK, function (event, srcEvent, treeId, node, clickFlag) {
+ tools.apply(setting.callback.onClick, [srcEvent, treeId, node, clickFlag]);
+ });
+
+ o.bind(c.EXPAND, function (event, treeId, node) {
+ tools.apply(setting.callback.onExpand, [event, treeId, node]);
+ });
+
+ o.bind(c.COLLAPSE, function (event, treeId, node) {
+ tools.apply(setting.callback.onCollapse, [event, treeId, node]);
+ });
+
+ o.bind(c.ASYNC_SUCCESS, function (event, treeId, node, msg) {
+ tools.apply(setting.callback.onAsyncSuccess, [event, treeId, node, msg]);
+ });
+
+ o.bind(c.ASYNC_ERROR, function (event, treeId, node, XMLHttpRequest, textStatus, errorThrown) {
+ tools.apply(setting.callback.onAsyncError, [event, treeId, node, XMLHttpRequest, textStatus, errorThrown]);
+ });
+
+ o.bind(c.REMOVE, function (event, treeId, treeNode) {
+ tools.apply(setting.callback.onRemove, [event, treeId, treeNode]);
+ });
+
+ o.bind(c.SELECTED, function (event, srcEvent, treeId, node) {
+ tools.apply(setting.callback.onSelected, [srcEvent, treeId, node]);
+ });
+ o.bind(c.UNSELECTED, function (event, srcEvent, treeId, node) {
+ tools.apply(setting.callback.onUnSelected, [srcEvent, treeId, node]);
+ });
+ },
+ _unbindEvent = function(setting) {
+ var o = setting.treeObj,
+ c = consts.event;
+ o.unbind(c.NODECREATED)
+ .unbind(c.CLICK)
+ .unbind(c.EXPAND)
+ .unbind(c.COLLAPSE)
+ .unbind(c.ASYNC_SUCCESS)
+ .unbind(c.ASYNC_ERROR)
+ .unbind(c.REMOVE)
+ .unbind(c.SELECTED)
+ .unbind(c.UNSELECTED);
+ },
+ //default event proxy of core
+ _eventProxy = function(event) {
+ var target = event.target,
+ setting = data.getSetting(event.data.treeId),
+ tId = "", node = null,
+ nodeEventType = "", treeEventType = "",
+ nodeEventCallback = null, treeEventCallback = null,
+ tmp = null;
+
+ if (tools.eqs(event.type, "mousedown")) {
+ treeEventType = "mousedown";
+ } else if (tools.eqs(event.type, "mouseup")) {
+ treeEventType = "mouseup";
+ } else if (tools.eqs(event.type, "contextmenu")) {
+ treeEventType = "contextmenu";
+ } else if (tools.eqs(event.type, "click")) {
+ if (tools.eqs(target.tagName, "span") && target.getAttribute("treeNode"+ consts.id.SWITCH) !== null) {
+ tId = tools.getNodeMainDom(target).id;
+ nodeEventType = "switchNode";
+ } else {
+ tmp = tools.getMDom(setting, target, [{tagName:"a", attrName:"treeNode"+consts.id.A}]);
+ if (tmp) {
+ tId = tools.getNodeMainDom(tmp).id;
+ nodeEventType = "clickNode";
+ }
+ }
+ } else if (tools.eqs(event.type, "dblclick")) {
+ treeEventType = "dblclick";
+ tmp = tools.getMDom(setting, target, [{tagName:"a", attrName:"treeNode"+consts.id.A}]);
+ if (tmp) {
+ tId = tools.getNodeMainDom(tmp).id;
+ nodeEventType = "switchNode";
+ }
+ }
+ if (treeEventType.length > 0 && tId.length == 0) {
+ tmp = tools.getMDom(setting, target, [{tagName:"a", attrName:"treeNode"+consts.id.A}]);
+ if (tmp) {tId = tools.getNodeMainDom(tmp).id;}
+ }
+ // event to node
+ if (tId.length>0) {
+ node = data.getNodeCache(setting, tId);
+ switch (nodeEventType) {
+ case "switchNode" :
+ if (!node.isParent) {
+ nodeEventType = "";
+ } else if (tools.eqs(event.type, "click")
+ || (tools.eqs(event.type, "dblclick") && tools.apply(setting.view.dblClickExpand, [setting.treeId, node], setting.view.dblClickExpand))) {
+ nodeEventCallback = handler.onSwitchNode;
+ } else {
+ nodeEventType = "";
+ }
+ break;
+ case "clickNode" :
+ nodeEventCallback = handler.onClickNode;
+ break;
+ }
+ }
+ // event to zTree
+ switch (treeEventType) {
+ case "mousedown" :
+ treeEventCallback = handler.onZTreeMousedown;
+ break;
+ case "mouseup" :
+ treeEventCallback = handler.onZTreeMouseup;
+ break;
+ case "dblclick" :
+ treeEventCallback = handler.onZTreeDblclick;
+ break;
+ case "contextmenu" :
+ treeEventCallback = handler.onZTreeContextmenu;
+ break;
+ }
+ var proxyResult = {
+ stop: false,
+ node: node,
+ nodeEventType: nodeEventType,
+ nodeEventCallback: nodeEventCallback,
+ treeEventType: treeEventType,
+ treeEventCallback: treeEventCallback
+ };
+ return proxyResult
+ },
+ //default init node of core
+ _initNode = function(setting, level, n, parentNode, isFirstNode, isLastNode, openFlag) {
+ if (!n) return;
+ var r = data.getRoot(setting),
+ childKey = setting.data.key.children;
+ n.level = level;
+ n.tId = setting.treeId + "_" + (++r.zId);
+ n.parentTId = parentNode ? parentNode.tId : null;
+ n.open = (typeof n.open == "string") ? tools.eqs(n.open, "true") : !!n.open;
+ if (n[childKey] && n[childKey].length > 0) {
+ n.isParent = true;
+ n.zAsync = true;
+ } else {
+ n.isParent = (typeof n.isParent == "string") ? tools.eqs(n.isParent, "true") : !!n.isParent;
+ n.open = (n.isParent && !setting.async.enable) ? n.open : false;
+ n.zAsync = !n.isParent;
+ }
+ n.isFirstNode = isFirstNode;
+ n.isLastNode = isLastNode;
+ n.getParentNode = function() {return data.getNodeCache(setting, n.parentTId);};
+ n.getPreNode = function() {return data.getPreNode(setting, n);};
+ n.getNextNode = function() {return data.getNextNode(setting, n);};
+ n.isAjaxing = false;
+ data.fixPIdKeyValue(setting, n);
+ },
+ _init = {
+ bind: [_bindEvent],
+ unbind: [_unbindEvent],
+ caches: [_initCache],
+ nodes: [_initNode],
+ proxys: [_eventProxy],
+ roots: [_initRoot],
+ beforeA: [],
+ afterA: [],
+ innerBeforeA: [],
+ innerAfterA: [],
+ zTreeTools: []
+ },
+ //method of operate data
+ data = {
+ addNodeCache: function(setting, node) {
+ data.getCache(setting).nodes[data.getNodeCacheId(node.tId)] = node;
+ },
+ getNodeCacheId: function(tId) {
+ return tId.substring(tId.lastIndexOf("_")+1);
+ },
+ addAfterA: function(afterA) {
+ _init.afterA.push(afterA);
+ },
+ addBeforeA: function(beforeA) {
+ _init.beforeA.push(beforeA);
+ },
+ addInnerAfterA: function(innerAfterA) {
+ _init.innerAfterA.push(innerAfterA);
+ },
+ addInnerBeforeA: function(innerBeforeA) {
+ _init.innerBeforeA.push(innerBeforeA);
+ },
+ addInitBind: function(bindEvent) {
+ _init.bind.push(bindEvent);
+ },
+ addInitUnBind: function(unbindEvent) {
+ _init.unbind.push(unbindEvent);
+ },
+ addInitCache: function(initCache) {
+ _init.caches.push(initCache);
+ },
+ addInitNode: function(initNode) {
+ _init.nodes.push(initNode);
+ },
+ addInitProxy: function(initProxy, isFirst) {
+ if (!!isFirst) {
+ _init.proxys.splice(0,0,initProxy);
+ } else {
+ _init.proxys.push(initProxy);
+ }
+ },
+ addInitRoot: function(initRoot) {
+ _init.roots.push(initRoot);
+ },
+ addNodesData: function(setting, parentNode, nodes) {
+ var childKey = setting.data.key.children;
+ if (!parentNode[childKey]) parentNode[childKey] = [];
+ if (parentNode[childKey].length > 0) {
+ parentNode[childKey][parentNode[childKey].length - 1].isLastNode = false;
+ view.setNodeLineIcos(setting, parentNode[childKey][parentNode[childKey].length - 1]);
+ }
+ parentNode.isParent = true;
+ parentNode[childKey] = parentNode[childKey].concat(nodes);
+ },
+ addSelectedNode: function(setting, node) {
+ var root = data.getRoot(setting);
+ if (!data.isSelectedNode(setting, node)) {
+ root.curSelectedList.push(node);
+ }
+ },
+ addCreatedNode: function(setting, node) {
+ if (!!setting.callback.onNodeCreated || !!setting.view.addDiyDom) {
+ var root = data.getRoot(setting);
+ root.createdNodes.push(node);
+ }
+ },
+ addZTreeTools: function(zTreeTools) {
+ _init.zTreeTools.push(zTreeTools);
+ },
+ exSetting: function(s) {
+ $.extend(true, _setting, s);
+ },
+ fixPIdKeyValue: function(setting, node) {
+ if (setting.data.simpleData.enable) {
+ node[setting.data.simpleData.pIdKey] = node.parentTId ? node.getParentNode()[setting.data.simpleData.idKey] : setting.data.simpleData.rootPId;
+ }
+ },
+ getAfterA: function(setting, node, array) {
+ for (var i=0, j=_init.afterA.length; i
-1) {
+ result.push(nodes[i]);
+ }
+ result = result.concat(data.getNodesByParamFuzzy(setting, nodes[i][childKey], key, value));
+ }
+ return result;
+ },
+ getNodesByFilter: function(setting, nodes, filter, isSingle, invokeParam) {
+ if (!nodes) return (isSingle ? null : []);
+ var childKey = setting.data.key.children,
+ result = isSingle ? null : [];
+ for (var i = 0, l = nodes.length; i < l; i++) {
+ if (tools.apply(filter, [nodes[i], invokeParam], false)) {
+ if (isSingle) {return nodes[i];}
+ result.push(nodes[i]);
+ }
+ var tmpResult = data.getNodesByFilter(setting, nodes[i][childKey], filter, isSingle, invokeParam);
+ if (isSingle && !!tmpResult) {return tmpResult;}
+ result = isSingle ? tmpResult : result.concat(tmpResult);
+ }
+ return result;
+ },
+ getPreNode: function(setting, node) {
+ if (!node) return null;
+ var childKey = setting.data.key.children,
+ p = node.parentTId ? node.getParentNode() : data.getRoot(setting);
+ for (var i=0, l=p[childKey].length; i 0)));
+ },
+ clone: function (obj){
+ if (obj === null) return null;
+ var o = tools.isArray(obj) ? [] : {};
+ for(var i in obj){
+ o[i] = (obj[i] instanceof Date) ? new Date(obj[i].getTime()) : (typeof obj[i] === "object" ? arguments.callee(obj[i]) : obj[i]);
+ }
+ return o;
+ },
+ eqs: function(str1, str2) {
+ return str1.toLowerCase() === str2.toLowerCase();
+ },
+ isArray: function(arr) {
+ return Object.prototype.toString.apply(arr) === "[object Array]";
+ },
+ $: function(node, exp, setting) {
+ if (!!exp && typeof exp != "string") {
+ setting = exp;
+ exp = "";
+ }
+ if (typeof node == "string") {
+ return $(node, setting ? setting.treeObj.get(0).ownerDocument : null);
+ } else {
+ return $("#" + node.tId + exp, setting ? setting.treeObj : null);
+ }
+ },
+ getMDom: function (setting, curDom, targetExpr) {
+ if (!curDom) return null;
+ while (curDom && curDom.id !== setting.treeId) {
+ for (var i=0, l=targetExpr.length; curDom.tagName && i 0 );
+ },
+ uCanDo: function(setting, e) {
+ return true;
+ }
+ },
+ //method of operate ztree dom
+ view = {
+ addNodes: function(setting, parentNode, newNodes, isSilent) {
+ if (setting.data.keep.leaf && parentNode && !parentNode.isParent) {
+ return;
+ }
+ if (!tools.isArray(newNodes)) {
+ newNodes = [newNodes];
+ }
+ if (setting.data.simpleData.enable) {
+ newNodes = data.transformTozTreeFormat(setting, newNodes);
+ }
+ if (parentNode) {
+ var target_switchObj = $$(parentNode, consts.id.SWITCH, setting),
+ target_icoObj = $$(parentNode, consts.id.ICON, setting),
+ target_ulObj = $$(parentNode, consts.id.UL, setting);
+
+ if (!parentNode.open) {
+ view.replaceSwitchClass(parentNode, target_switchObj, consts.folder.CLOSE);
+ view.replaceIcoClass(parentNode, target_icoObj, consts.folder.CLOSE);
+ parentNode.open = false;
+ target_ulObj.css({
+ "display": "none"
+ });
+ }
+
+ data.addNodesData(setting, parentNode, newNodes);
+ view.createNodes(setting, parentNode.level + 1, newNodes, parentNode);
+ if (!isSilent) {
+ view.expandCollapseParentNode(setting, parentNode, true);
+ }
+ } else {
+ data.addNodesData(setting, data.getRoot(setting), newNodes);
+ view.createNodes(setting, 0, newNodes, null);
+ }
+ },
+ appendNodes: function(setting, level, nodes, parentNode, initFlag, openFlag) {
+ if (!nodes) return [];
+ var html = [],
+ childKey = setting.data.key.children;
+ for (var i = 0, l = nodes.length; i < l; i++) {
+ var node = nodes[i];
+ if (initFlag) {
+ var tmpPNode = (parentNode) ? parentNode: data.getRoot(setting),
+ tmpPChild = tmpPNode[childKey],
+ isFirstNode = ((tmpPChild.length == nodes.length) && (i == 0)),
+ isLastNode = (i == (nodes.length - 1));
+ data.initNode(setting, level, node, parentNode, isFirstNode, isLastNode, openFlag);
+ data.addNodeCache(setting, node);
+ }
+
+ var childHtml = [];
+ if (node[childKey] && node[childKey].length > 0) {
+ //make child html first, because checkType
+ childHtml = view.appendNodes(setting, level + 1, node[childKey], node, initFlag, openFlag && node.open);
+ }
+ if (openFlag) {
+
+ view.makeDOMNodeMainBefore(html, setting, node);
+ view.makeDOMNodeLine(html, setting, node);
+ data.getBeforeA(setting, node, html);
+ view.makeDOMNodeNameBefore(html, setting, node);
+ data.getInnerBeforeA(setting, node, html);
+ view.makeDOMNodeIcon(html, setting, node);
+ data.getInnerAfterA(setting, node, html);
+ view.makeDOMNodeNameAfter(html, setting, node);
+ data.getAfterA(setting, node, html);
+ if (node.isParent && node.open) {
+ view.makeUlHtml(setting, node, html, childHtml.join(''));
+ }
+ view.makeDOMNodeMainAfter(html, setting, node);
+ data.addCreatedNode(setting, node);
+ }
+ }
+ return html;
+ },
+ appendParentULDom: function(setting, node) {
+ var html = [],
+ nObj = $$(node, setting);
+ if (!nObj.get(0) && !!node.parentTId) {
+ view.appendParentULDom(setting, node.getParentNode());
+ nObj = $$(node, setting);
+ }
+ var ulObj = $$(node, consts.id.UL, setting);
+ if (ulObj.get(0)) {
+ ulObj.remove();
+ }
+ var childKey = setting.data.key.children,
+ childHtml = view.appendNodes(setting, node.level+1, node[childKey], node, false, true);
+ view.makeUlHtml(setting, node, html, childHtml.join(''));
+ nObj.append(html.join(''));
+ },
+ asyncNode: function(setting, node, isSilent, callback) {
+ var i, l;
+ if (node && !node.isParent) {
+ tools.apply(callback);
+ return false;
+ } else if (node && node.isAjaxing) {
+ return false;
+ } else if (tools.apply(setting.callback.beforeAsync, [setting.treeId, node], true) == false) {
+ tools.apply(callback);
+ return false;
+ }
+ if (node) {
+ node.isAjaxing = true;
+ var icoObj = $$(node, consts.id.ICON, setting);
+ icoObj.attr({"style":"", "class":consts.className.BUTTON + " " + consts.className.ICO_LOADING});
+ }
+
+ var tmpParam = {};
+ for (i = 0, l = setting.async.autoParam.length; node && i < l; i++) {
+ var pKey = setting.async.autoParam[i].split("="), spKey = pKey;
+ if (pKey.length>1) {
+ spKey = pKey[1];
+ pKey = pKey[0];
+ }
+ tmpParam[spKey] = node[pKey];
+ }
+ if (tools.isArray(setting.async.otherParam)) {
+ for (i = 0, l = setting.async.otherParam.length; i < l; i += 2) {
+ tmpParam[setting.async.otherParam[i]] = setting.async.otherParam[i + 1];
+ }
+ } else {
+ for (var p in setting.async.otherParam) {
+ tmpParam[p] = setting.async.otherParam[p];
+ }
+ }
+
+ var _tmpV = data.getRoot(setting)._ver;
+ $.ajax({
+ contentType: setting.async.contentType,
+ cache: false,
+ type: setting.async.type,
+ url: tools.apply(setting.async.url, [setting.treeId, node], setting.async.url),
+ data: tmpParam,
+ dataType: setting.async.dataType,
+ success: function(msg) {
+ if (_tmpV != data.getRoot(setting)._ver) {
+ return;
+ }
+ var newNodes = [];
+ try {
+ if (!msg || msg.length == 0) {
+ newNodes = [];
+ } else if (typeof msg == "string") {
+ newNodes = eval("(" + msg + ")");
+ } else {
+ newNodes = msg;
+ }
+ } catch(err) {
+ newNodes = msg;
+ }
+
+ if (node) {
+ node.isAjaxing = null;
+ node.zAsync = true;
+ }
+ view.setNodeLineIcos(setting, node);
+ if (newNodes && newNodes !== "") {
+ newNodes = tools.apply(setting.async.dataFilter, [setting.treeId, node, newNodes], newNodes);
+ view.addNodes(setting, node, !!newNodes ? tools.clone(newNodes) : [], !!isSilent);
+ } else {
+ view.addNodes(setting, node, [], !!isSilent);
+ }
+ setting.treeObj.trigger(consts.event.ASYNC_SUCCESS, [setting.treeId, node, msg]);
+ tools.apply(callback);
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ if (_tmpV != data.getRoot(setting)._ver) {
+ return;
+ }
+ if (node) node.isAjaxing = null;
+ view.setNodeLineIcos(setting, node);
+ setting.treeObj.trigger(consts.event.ASYNC_ERROR, [setting.treeId, node, XMLHttpRequest, textStatus, errorThrown]);
+ }
+ });
+ return true;
+ },
+ cancelPreSelectedNode: function (setting, node, excludeNode) {
+ var list = data.getRoot(setting).curSelectedList,
+ i, n;
+ for (i=list.length-1; i>=0; i--) {
+ n = list[i];
+ if (node === n || (!node && (!excludeNode || excludeNode !== n))) {
+ $$(n, consts.id.A, setting).removeClass(consts.node.CURSELECTED);
+ if (node) {
+ data.removeSelectedNode(setting, node);
+ setting.treeObj.trigger(consts.event.UNSELECTED, [event, setting.treeId, n]);
+ break;
+ } else {
+ list.splice(i, 1);
+ setting.treeObj.trigger(consts.event.UNSELECTED, [event, setting.treeId, n]);
+ }
+ }
+ }
+ },
+ createNodeCallback: function(setting) {
+ if (!!setting.callback.onNodeCreated || !!setting.view.addDiyDom) {
+ var root = data.getRoot(setting);
+ while (root.createdNodes.length>0) {
+ var node = root.createdNodes.shift();
+ tools.apply(setting.view.addDiyDom, [setting.treeId, node]);
+ if (!!setting.callback.onNodeCreated) {
+ setting.treeObj.trigger(consts.event.NODECREATED, [setting.treeId, node]);
+ }
+ }
+ }
+ },
+ createNodes: function(setting, level, nodes, parentNode) {
+ if (!nodes || nodes.length == 0) return;
+ var root = data.getRoot(setting),
+ childKey = setting.data.key.children,
+ openFlag = !parentNode || parentNode.open || !!$$(parentNode[childKey][0], setting).get(0);
+ root.createdNodes = [];
+ var zTreeHtml = view.appendNodes(setting, level, nodes, parentNode, true, openFlag);
+ if (!parentNode) {
+ setting.treeObj.append(zTreeHtml.join(''));
+ } else {
+ var ulObj = $$(parentNode, consts.id.UL, setting);
+ if (ulObj.get(0)) {
+ ulObj.append(zTreeHtml.join(''));
+ }
+ }
+ view.createNodeCallback(setting);
+ },
+ destroy: function(setting) {
+ if (!setting) return;
+ data.initCache(setting);
+ data.initRoot(setting);
+ event.unbindTree(setting);
+ event.unbindEvent(setting);
+ setting.treeObj.empty();
+ delete settings[setting.treeId];
+ },
+ expandCollapseNode: function(setting, node, expandFlag, animateFlag, callback) {
+ var root = data.getRoot(setting),
+ childKey = setting.data.key.children;
+ if (!node) {
+ tools.apply(callback, []);
+ return;
+ }
+ if (root.expandTriggerFlag) {
+ var _callback = callback;
+ callback = function(){
+ if (_callback) _callback();
+ if (node.open) {
+ setting.treeObj.trigger(consts.event.EXPAND, [setting.treeId, node]);
+ } else {
+ setting.treeObj.trigger(consts.event.COLLAPSE, [setting.treeId, node]);
+ }
+ };
+ root.expandTriggerFlag = false;
+ }
+ if (!node.open && node.isParent && ((!$$(node, consts.id.UL, setting).get(0)) || (node[childKey] && node[childKey].length>0 && !$$(node[childKey][0], setting).get(0)))) {
+ view.appendParentULDom(setting, node);
+ view.createNodeCallback(setting);
+ }
+ if (node.open == expandFlag) {
+ tools.apply(callback, []);
+ return;
+ }
+ var ulObj = $$(node, consts.id.UL, setting),
+ switchObj = $$(node, consts.id.SWITCH, setting),
+ icoObj = $$(node, consts.id.ICON, setting);
+
+ if (node.isParent) {
+ node.open = !node.open;
+ if (node.iconOpen && node.iconClose) {
+ icoObj.attr("style", view.makeNodeIcoStyle(setting, node));
+ }
+
+ if (node.open) {
+ view.replaceSwitchClass(node, switchObj, consts.folder.OPEN);
+ view.replaceIcoClass(node, icoObj, consts.folder.OPEN);
+ if (animateFlag == false || setting.view.expandSpeed == "") {
+ ulObj.show();
+ tools.apply(callback, []);
+ } else {
+ if (node[childKey] && node[childKey].length > 0) {
+ ulObj.slideDown(setting.view.expandSpeed, callback);
+ } else {
+ ulObj.show();
+ tools.apply(callback, []);
+ }
+ }
+ } else {
+ view.replaceSwitchClass(node, switchObj, consts.folder.CLOSE);
+ view.replaceIcoClass(node, icoObj, consts.folder.CLOSE);
+ if (animateFlag == false || setting.view.expandSpeed == "" || !(node[childKey] && node[childKey].length > 0)) {
+ ulObj.hide();
+ tools.apply(callback, []);
+ } else {
+ ulObj.slideUp(setting.view.expandSpeed, callback);
+ }
+ }
+ } else {
+ tools.apply(callback, []);
+ }
+ },
+ expandCollapseParentNode: function(setting, node, expandFlag, animateFlag, callback) {
+ if (!node) return;
+ if (!node.parentTId) {
+ view.expandCollapseNode(setting, node, expandFlag, animateFlag, callback);
+ return;
+ } else {
+ view.expandCollapseNode(setting, node, expandFlag, animateFlag);
+ }
+ if (node.parentTId) {
+ view.expandCollapseParentNode(setting, node.getParentNode(), expandFlag, animateFlag, callback);
+ }
+ },
+ expandCollapseSonNode: function(setting, node, expandFlag, animateFlag, callback) {
+ var root = data.getRoot(setting),
+ childKey = setting.data.key.children,
+ treeNodes = (node) ? node[childKey]: root[childKey],
+ selfAnimateSign = (node) ? false : animateFlag,
+ expandTriggerFlag = data.getRoot(setting).expandTriggerFlag;
+ data.getRoot(setting).expandTriggerFlag = false;
+ if (treeNodes) {
+ for (var i = 0, l = treeNodes.length; i < l; i++) {
+ if (treeNodes[i]) view.expandCollapseSonNode(setting, treeNodes[i], expandFlag, selfAnimateSign);
+ }
+ }
+ data.getRoot(setting).expandTriggerFlag = expandTriggerFlag;
+ view.expandCollapseNode(setting, node, expandFlag, animateFlag, callback );
+ },
+ isSelectedNode: function (setting, node) {
+ if (!node) {
+ return false;
+ }
+ var list = data.getRoot(setting).curSelectedList,
+ i;
+ for (i=list.length-1; i>=0; i--) {
+ if (node === list[i]) {
+ return true;
+ }
+ }
+ return false;
+ },
+ makeDOMNodeIcon: function(html, setting, node) {
+ var nameStr = data.getNodeName(setting, node),
+ name = setting.view.nameIsHTML ? nameStr : nameStr.replace(/&/g,'&').replace(//g,'>');
+ html.push("",name," ");
+ },
+ makeDOMNodeLine: function(html, setting, node) {
+ html.push(" ");
+ },
+ makeDOMNodeMainAfter: function(html, setting, node) {
+ html.push("");
+ },
+ makeDOMNodeMainBefore: function(html, setting, node) {
+ html.push("");
+ },
+ makeDOMNodeNameAfter: function(html, setting, node) {
+ html.push("");
+ },
+ makeDOMNodeNameBefore: function(html, setting, node) {
+ var title = data.getNodeTitle(setting, node),
+ url = view.makeNodeUrl(setting, node),
+ fontcss = view.makeNodeFontCss(setting, node),
+ fontStyle = [];
+ for (var f in fontcss) {
+ fontStyle.push(f, ":", fontcss[f], ";");
+ }
+ html.push(" 0) ? "href='" + url + "'" : ""), " target='",view.makeNodeTarget(node),"' style='", fontStyle.join(''),
+ "'");
+ if (tools.apply(setting.view.showTitle, [setting.treeId, node], setting.view.showTitle) && title) {html.push("title='", title.replace(/'/g,"'").replace(//g,'>'),"'");}
+ html.push(">");
+ },
+ makeNodeFontCss: function(setting, node) {
+ var fontCss = tools.apply(setting.view.fontCss, [setting.treeId, node], setting.view.fontCss);
+ return (fontCss && ((typeof fontCss) != "function")) ? fontCss : {};
+ },
+ makeNodeIcoClass: function(setting, node) {
+ var icoCss = ["ico"];
+ if (!node.isAjaxing) {
+ icoCss[0] = (node.iconSkin ? node.iconSkin + "_" : "") + icoCss[0];
+ if (node.isParent) {
+ icoCss.push(node.open ? consts.folder.OPEN : consts.folder.CLOSE);
+ } else {
+ icoCss.push(consts.folder.DOCU);
+ }
+ }
+ return consts.className.BUTTON + " " + icoCss.join('_');
+ },
+ makeNodeIcoStyle: function(setting, node) {
+ var icoStyle = [];
+ if (!node.isAjaxing) {
+ var icon = (node.isParent && node.iconOpen && node.iconClose) ? (node.open ? node.iconOpen : node.iconClose) : node.icon;
+ if (icon) icoStyle.push("background:url(", icon, ") 0 0 no-repeat;");
+ if (setting.view.showIcon == false || !tools.apply(setting.view.showIcon, [setting.treeId, node], true)) {
+ icoStyle.push("width:0px;height:0px;");
+ }
+ }
+ return icoStyle.join('');
+ },
+ makeNodeLineClass: function(setting, node) {
+ var lineClass = [];
+ if (setting.view.showLine) {
+ if (node.level == 0 && node.isFirstNode && node.isLastNode) {
+ lineClass.push(consts.line.ROOT);
+ } else if (node.level == 0 && node.isFirstNode) {
+ lineClass.push(consts.line.ROOTS);
+ } else if (node.isLastNode) {
+ lineClass.push(consts.line.BOTTOM);
+ } else {
+ lineClass.push(consts.line.CENTER);
+ }
+ } else {
+ lineClass.push(consts.line.NOLINE);
+ }
+ if (node.isParent) {
+ lineClass.push(node.open ? consts.folder.OPEN : consts.folder.CLOSE);
+ } else {
+ lineClass.push(consts.folder.DOCU);
+ }
+ return view.makeNodeLineClassEx(node) + lineClass.join('_');
+ },
+ makeNodeLineClassEx: function(node) {
+ return consts.className.BUTTON + " " + consts.className.LEVEL + node.level + " " + consts.className.SWITCH + " ";
+ },
+ makeNodeTarget: function(node) {
+ return (node.target || "_blank");
+ },
+ makeNodeUrl: function(setting, node) {
+ var urlKey = setting.data.key.url;
+ return node[urlKey] ? node[urlKey] : null;
+ },
+ makeUlHtml: function(setting, node, html, content) {
+ html.push("");
+ html.push(content);
+ html.push(" ");
+ },
+ makeUlLineClass: function(setting, node) {
+ return ((setting.view.showLine && !node.isLastNode) ? consts.line.LINE : "");
+ },
+ removeChildNodes: function(setting, node) {
+ if (!node) return;
+ var childKey = setting.data.key.children,
+ nodes = node[childKey];
+ if (!nodes) return;
+
+ for (var i = 0, l = nodes.length; i < l; i++) {
+ data.removeNodeCache(setting, nodes[i]);
+ }
+ data.removeSelectedNode(setting);
+ delete node[childKey];
+
+ if (!setting.data.keep.parent) {
+ node.isParent = false;
+ node.open = false;
+ var tmp_switchObj = $$(node, consts.id.SWITCH, setting),
+ tmp_icoObj = $$(node, consts.id.ICON, setting);
+ view.replaceSwitchClass(node, tmp_switchObj, consts.folder.DOCU);
+ view.replaceIcoClass(node, tmp_icoObj, consts.folder.DOCU);
+ $$(node, consts.id.UL, setting).remove();
+ } else {
+ $$(node, consts.id.UL, setting).empty();
+ }
+ },
+ setFirstNode: function(setting, parentNode) {
+ var childKey = setting.data.key.children, childLength = parentNode[childKey].length;
+ if ( childLength > 0) {
+ parentNode[childKey][0].isFirstNode = true;
+ }
+ },
+ setLastNode: function(setting, parentNode) {
+ var childKey = setting.data.key.children, childLength = parentNode[childKey].length;
+ if ( childLength > 0) {
+ parentNode[childKey][childLength - 1].isLastNode = true;
+ }
+ },
+ removeNode: function(setting, node) {
+ var root = data.getRoot(setting),
+ childKey = setting.data.key.children,
+ parentNode = (node.parentTId) ? node.getParentNode() : root;
+
+ node.isFirstNode = false;
+ node.isLastNode = false;
+ node.getPreNode = function() {return null;};
+ node.getNextNode = function() {return null;};
+
+ if (!data.getNodeCache(setting, node.tId)) {
+ return;
+ }
+
+ $$(node, setting).remove();
+ data.removeNodeCache(setting, node);
+ data.removeSelectedNode(setting, node);
+
+ for (var i = 0, l = parentNode[childKey].length; i < l; i++) {
+ if (parentNode[childKey][i].tId == node.tId) {
+ parentNode[childKey].splice(i, 1);
+ break;
+ }
+ }
+ view.setFirstNode(setting, parentNode);
+ view.setLastNode(setting, parentNode);
+
+ var tmp_ulObj,tmp_switchObj,tmp_icoObj,
+ childLength = parentNode[childKey].length;
+
+ //repair nodes old parent
+ if (!setting.data.keep.parent && childLength == 0) {
+ //old parentNode has no child nodes
+ parentNode.isParent = false;
+ parentNode.open = false;
+ tmp_ulObj = $$(parentNode, consts.id.UL, setting);
+ tmp_switchObj = $$(parentNode, consts.id.SWITCH, setting);
+ tmp_icoObj = $$(parentNode, consts.id.ICON, setting);
+ view.replaceSwitchClass(parentNode, tmp_switchObj, consts.folder.DOCU);
+ view.replaceIcoClass(parentNode, tmp_icoObj, consts.folder.DOCU);
+ tmp_ulObj.css("display", "none");
+
+ } else if (setting.view.showLine && childLength > 0) {
+ //old parentNode has child nodes
+ var newLast = parentNode[childKey][childLength - 1];
+ tmp_ulObj = $$(newLast, consts.id.UL, setting);
+ tmp_switchObj = $$(newLast, consts.id.SWITCH, setting);
+ tmp_icoObj = $$(newLast, consts.id.ICON, setting);
+ if (parentNode == root) {
+ if (parentNode[childKey].length == 1) {
+ //node was root, and ztree has only one root after move node
+ view.replaceSwitchClass(newLast, tmp_switchObj, consts.line.ROOT);
+ } else {
+ var tmp_first_switchObj = $$(parentNode[childKey][0], consts.id.SWITCH, setting);
+ view.replaceSwitchClass(parentNode[childKey][0], tmp_first_switchObj, consts.line.ROOTS);
+ view.replaceSwitchClass(newLast, tmp_switchObj, consts.line.BOTTOM);
+ }
+ } else {
+ view.replaceSwitchClass(newLast, tmp_switchObj, consts.line.BOTTOM);
+ }
+ tmp_ulObj.removeClass(consts.line.LINE);
+ }
+ },
+ replaceIcoClass: function(node, obj, newName) {
+ if (!obj || node.isAjaxing) return;
+ var tmpName = obj.attr("class");
+ if (tmpName == undefined) return;
+ var tmpList = tmpName.split("_");
+ switch (newName) {
+ case consts.folder.OPEN:
+ case consts.folder.CLOSE:
+ case consts.folder.DOCU:
+ tmpList[tmpList.length-1] = newName;
+ break;
+ }
+ obj.attr("class", tmpList.join("_"));
+ },
+ replaceSwitchClass: function(node, obj, newName) {
+ if (!obj) return;
+ var tmpName = obj.attr("class");
+ if (tmpName == undefined) return;
+ var tmpList = tmpName.split("_");
+ switch (newName) {
+ case consts.line.ROOT:
+ case consts.line.ROOTS:
+ case consts.line.CENTER:
+ case consts.line.BOTTOM:
+ case consts.line.NOLINE:
+ tmpList[0] = view.makeNodeLineClassEx(node) + newName;
+ break;
+ case consts.folder.OPEN:
+ case consts.folder.CLOSE:
+ case consts.folder.DOCU:
+ tmpList[1] = newName;
+ break;
+ }
+ obj.attr("class", tmpList.join("_"));
+ if (newName !== consts.folder.DOCU) {
+ obj.removeAttr("disabled");
+ } else {
+ obj.attr("disabled", "disabled");
+ }
+ },
+ selectNode: function(setting, node, addFlag) {
+ if (!addFlag) {
+ view.cancelPreSelectedNode(setting, null, node);
+ }
+ $$(node, consts.id.A, setting).addClass(consts.node.CURSELECTED);
+ data.addSelectedNode(setting, node);
+ setting.treeObj.trigger(consts.event.SELECTED, [event, setting.treeId, node]);
+ },
+ setNodeFontCss: function(setting, treeNode) {
+ var aObj = $$(treeNode, consts.id.A, setting),
+ fontCss = view.makeNodeFontCss(setting, treeNode);
+ if (fontCss) {
+ aObj.css(fontCss);
+ }
+ },
+ setNodeLineIcos: function(setting, node) {
+ if (!node) return;
+ var switchObj = $$(node, consts.id.SWITCH, setting),
+ ulObj = $$(node, consts.id.UL, setting),
+ icoObj = $$(node, consts.id.ICON, setting),
+ ulLine = view.makeUlLineClass(setting, node);
+ if (ulLine.length==0) {
+ ulObj.removeClass(consts.line.LINE);
+ } else {
+ ulObj.addClass(ulLine);
+ }
+ switchObj.attr("class", view.makeNodeLineClass(setting, node));
+ if (node.isParent) {
+ switchObj.removeAttr("disabled");
+ } else {
+ switchObj.attr("disabled", "disabled");
+ }
+ icoObj.removeAttr("style");
+ icoObj.attr("style", view.makeNodeIcoStyle(setting, node));
+ icoObj.attr("class", view.makeNodeIcoClass(setting, node));
+ },
+ setNodeName: function(setting, node) {
+ var title = data.getNodeTitle(setting, node),
+ nObj = $$(node, consts.id.SPAN, setting);
+ nObj.empty();
+ if (setting.view.nameIsHTML) {
+ nObj.html(data.getNodeName(setting, node));
+ } else {
+ nObj.text(data.getNodeName(setting, node));
+ }
+ if (tools.apply(setting.view.showTitle, [setting.treeId, node], setting.view.showTitle)) {
+ var aObj = $$(node, consts.id.A, setting);
+ aObj.attr("title", !title ? "" : title);
+ }
+ },
+ setNodeTarget: function(setting, node) {
+ var aObj = $$(node, consts.id.A, setting);
+ aObj.attr("target", view.makeNodeTarget(node));
+ },
+ setNodeUrl: function(setting, node) {
+ var aObj = $$(node, consts.id.A, setting),
+ url = view.makeNodeUrl(setting, node);
+ if (url == null || url.length == 0) {
+ aObj.removeAttr("href");
+ } else {
+ aObj.attr("href", url);
+ }
+ },
+ switchNode: function(setting, node) {
+ if (node.open || !tools.canAsync(setting, node)) {
+ view.expandCollapseNode(setting, node, !node.open);
+ } else if (setting.async.enable) {
+ if (!view.asyncNode(setting, node)) {
+ view.expandCollapseNode(setting, node, !node.open);
+ return;
+ }
+ } else if (node) {
+ view.expandCollapseNode(setting, node, !node.open);
+ }
+ }
+ };
+ // zTree defind
+ $.fn.zTree = {
+ consts : _consts,
+ _z : {
+ tools: tools,
+ view: view,
+ event: event,
+ data: data
+ },
+ getZTreeObj: function(treeId) {
+ var o = data.getZTreeTools(treeId);
+ return o ? o : null;
+ },
+ destroy: function(treeId) {
+ if (!!treeId && treeId.length > 0) {
+ view.destroy(data.getSetting(treeId));
+ } else {
+ for(var s in settings) {
+ view.destroy(settings[s]);
+ }
+ }
+ },
+ init: function(obj, zSetting, zNodes) {
+ var setting = tools.clone(_setting);
+ $.extend(true, setting, zSetting);
+ setting.treeId = obj.attr("id");
+ setting.treeObj = obj;
+ setting.treeObj.empty();
+ settings[setting.treeId] = setting;
+ //For some older browser,(e.g., ie6)
+ if(typeof document.body.style.maxHeight === "undefined") {
+ setting.view.expandSpeed = "";
+ }
+ data.initRoot(setting);
+ var root = data.getRoot(setting),
+ childKey = setting.data.key.children;
+ zNodes = zNodes ? tools.clone(tools.isArray(zNodes)? zNodes : [zNodes]) : [];
+ if (setting.data.simpleData.enable) {
+ root[childKey] = data.transformTozTreeFormat(setting, zNodes);
+ } else {
+ root[childKey] = zNodes;
+ }
+
+ data.initCache(setting);
+ event.unbindTree(setting);
+ event.bindTree(setting);
+ event.unbindEvent(setting);
+ event.bindEvent(setting);
+
+ var zTreeTools = {
+ setting : setting,
+ addNodes : function(parentNode, newNodes, isSilent) {
+ if (!newNodes) return null;
+ if (!parentNode) parentNode = null;
+ if (parentNode && !parentNode.isParent && setting.data.keep.leaf) return null;
+ var xNewNodes = tools.clone(tools.isArray(newNodes)? newNodes: [newNodes]);
+ function addCallback() {
+ view.addNodes(setting, parentNode, xNewNodes, (isSilent==true));
+ }
+
+ if (tools.canAsync(setting, parentNode)) {
+ view.asyncNode(setting, parentNode, isSilent, addCallback);
+ } else {
+ addCallback();
+ }
+ return xNewNodes;
+ },
+ cancelSelectedNode : function(node) {
+ view.cancelPreSelectedNode(setting, node);
+ },
+ destroy : function() {
+ view.destroy(setting);
+ },
+ expandAll : function(expandFlag) {
+ expandFlag = !!expandFlag;
+ view.expandCollapseSonNode(setting, null, expandFlag, true);
+ return expandFlag;
+ },
+ expandNode : function(node, expandFlag, sonSign, focus, callbackFlag) {
+ if (!node || !node.isParent) return null;
+ if (expandFlag !== true && expandFlag !== false) {
+ expandFlag = !node.open;
+ }
+ callbackFlag = !!callbackFlag;
+
+ if (callbackFlag && expandFlag && (tools.apply(setting.callback.beforeExpand, [setting.treeId, node], true) == false)) {
+ return null;
+ } else if (callbackFlag && !expandFlag && (tools.apply(setting.callback.beforeCollapse, [setting.treeId, node], true) == false)) {
+ return null;
+ }
+ if (expandFlag && node.parentTId) {
+ view.expandCollapseParentNode(setting, node.getParentNode(), expandFlag, false);
+ }
+ if (expandFlag === node.open && !sonSign) {
+ return null;
+ }
+
+ data.getRoot(setting).expandTriggerFlag = callbackFlag;
+ if (!tools.canAsync(setting, node) && sonSign) {
+ view.expandCollapseSonNode(setting, node, expandFlag, true, function() {
+ if (focus !== false) {try{$$(node, setting).focus().blur();}catch(e){}}
+ });
+ } else {
+ node.open = !expandFlag;
+ view.switchNode(this.setting, node);
+ if (focus !== false) {try{$$(node, setting).focus().blur();}catch(e){}}
+ }
+ return expandFlag;
+ },
+ getNodes : function() {
+ return data.getNodes(setting);
+ },
+ getNodeByParam : function(key, value, parentNode) {
+ if (!key) return null;
+ return data.getNodeByParam(setting, parentNode?parentNode[setting.data.key.children]:data.getNodes(setting), key, value);
+ },
+ getNodeByTId : function(tId) {
+ return data.getNodeCache(setting, tId);
+ },
+ getNodesByParam : function(key, value, parentNode) {
+ if (!key) return null;
+ return data.getNodesByParam(setting, parentNode?parentNode[setting.data.key.children]:data.getNodes(setting), key, value);
+ },
+ getNodesByParamFuzzy : function(key, value, parentNode) {
+ if (!key) return null;
+ return data.getNodesByParamFuzzy(setting, parentNode?parentNode[setting.data.key.children]:data.getNodes(setting), key, value);
+ },
+ getNodesByFilter: function(filter, isSingle, parentNode, invokeParam) {
+ isSingle = !!isSingle;
+ if (!filter || (typeof filter != "function")) return (isSingle ? null : []);
+ return data.getNodesByFilter(setting, parentNode?parentNode[setting.data.key.children]:data.getNodes(setting), filter, isSingle, invokeParam);
+ },
+ getNodeIndex : function(node) {
+ if (!node) return null;
+ var childKey = setting.data.key.children,
+ parentNode = (node.parentTId) ? node.getParentNode() : data.getRoot(setting);
+ for (var i=0, l = parentNode[childKey].length; i < l; i++) {
+ if (parentNode[childKey][i] == node) return i;
+ }
+ return -1;
+ },
+ getSelectedNodes : function() {
+ var r = [], list = data.getRoot(setting).curSelectedList;
+ for (var i=0, l=list.length; i 0) {
+ view.createNodes(setting, 0, root[childKey]);
+ } else if (setting.async.enable && setting.async.url && setting.async.url !== '') {
+ view.asyncNode(setting);
+ }
+ return zTreeTools;
+ }
+ };
+
+ var zt = $.fn.zTree,
+ $$ = tools.$,
+ consts = zt.consts;
+})(BI.jQuery);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/ztree/jquery.ztree.excheck-3.5.js b/src/main/resources/com/fr/fineui/case/ztree/jquery.ztree.excheck-3.5.js
new file mode 100644
index 0000000..ea1e9d2
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/ztree/jquery.ztree.excheck-3.5.js
@@ -0,0 +1,646 @@
+/*
+ * JQuery zTree excheck v3.5.18
+ * http://zTree.me/
+ *
+ * Copyright (c) 2010 Hunter.z
+ *
+ * Licensed same as jquery - MIT License
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * email: hunter.z@263.net
+ * Date: 2015-06-18
+ */
+(function($){
+ //default consts of excheck
+ var _consts = {
+ event: {
+ CHECK: "ztree_check"
+ },
+ id: {
+ CHECK: "_check"
+ },
+ checkbox: {
+ STYLE: "checkbox",
+ DEFAULT: "chk",
+ DISABLED: "disable",
+ FALSE: "false",
+ TRUE: "true",
+ FULL: "full",
+ PART: "part",
+ FOCUS: "focus"
+ },
+ radio: {
+ STYLE: "radio",
+ TYPE_ALL: "all",
+ TYPE_LEVEL: "level"
+ }
+ },
+ //default setting of excheck
+ _setting = {
+ check: {
+ enable: false,
+ autoCheckTrigger: false,
+ chkStyle: _consts.checkbox.STYLE,
+ nocheckInherit: false,
+ chkDisabledInherit: false,
+ radioType: _consts.radio.TYPE_LEVEL,
+ chkboxType: {
+ "Y": "ps",
+ "N": "ps"
+ }
+ },
+ data: {
+ key: {
+ checked: "checked"
+ }
+ },
+ callback: {
+ beforeCheck:null,
+ onCheck:null
+ }
+ },
+ //default root of excheck
+ _initRoot = function (setting) {
+ var r = data.getRoot(setting);
+ r.radioCheckedList = [];
+ },
+ //default cache of excheck
+ _initCache = function(treeId) {},
+ //default bind event of excheck
+ _bindEvent = function(setting) {
+ var o = setting.treeObj,
+ c = consts.event;
+ o.bind(c.CHECK, function (event, srcEvent, treeId, node) {
+ event.srcEvent = srcEvent;
+ tools.apply(setting.callback.onCheck, [event, treeId, node]);
+ });
+ },
+ _unbindEvent = function(setting) {
+ var o = setting.treeObj,
+ c = consts.event;
+ o.unbind(c.CHECK);
+ },
+ //default event proxy of excheck
+ _eventProxy = function(e) {
+ var target = e.target,
+ setting = data.getSetting(e.data.treeId),
+ tId = "", node = null,
+ nodeEventType = "", treeEventType = "",
+ nodeEventCallback = null, treeEventCallback = null;
+
+ if (tools.eqs(e.type, "mouseover")) {
+ if (setting.check.enable && tools.eqs(target.tagName, "span") && target.getAttribute("treeNode"+ consts.id.CHECK) !== null) {
+ tId = tools.getNodeMainDom(target).id;
+ nodeEventType = "mouseoverCheck";
+ }
+ } else if (tools.eqs(e.type, "mouseout")) {
+ if (setting.check.enable && tools.eqs(target.tagName, "span") && target.getAttribute("treeNode"+ consts.id.CHECK) !== null) {
+ tId = tools.getNodeMainDom(target).id;
+ nodeEventType = "mouseoutCheck";
+ }
+ } else if (tools.eqs(e.type, "click")) {
+ if (setting.check.enable && tools.eqs(target.tagName, "span") && target.getAttribute("treeNode"+ consts.id.CHECK) !== null) {
+ tId = tools.getNodeMainDom(target).id;
+ nodeEventType = "checkNode";
+ }
+ }
+ if (tId.length>0) {
+ node = data.getNodeCache(setting, tId);
+ switch (nodeEventType) {
+ case "checkNode" :
+ nodeEventCallback = _handler.onCheckNode;
+ break;
+ case "mouseoverCheck" :
+ nodeEventCallback = _handler.onMouseoverCheck;
+ break;
+ case "mouseoutCheck" :
+ nodeEventCallback = _handler.onMouseoutCheck;
+ break;
+ }
+ }
+ var proxyResult = {
+ stop: nodeEventType === "checkNode",
+ node: node,
+ nodeEventType: nodeEventType,
+ nodeEventCallback: nodeEventCallback,
+ treeEventType: treeEventType,
+ treeEventCallback: treeEventCallback
+ };
+ return proxyResult
+ },
+ //default init node of excheck
+ _initNode = function(setting, level, n, parentNode, isFirstNode, isLastNode, openFlag) {
+ if (!n) return;
+ var checkedKey = setting.data.key.checked;
+ if (typeof n[checkedKey] == "string") n[checkedKey] = tools.eqs(n[checkedKey], "true");
+ n[checkedKey] = !!n[checkedKey];
+ n.checkedOld = n[checkedKey];
+ if (typeof n.nocheck == "string") n.nocheck = tools.eqs(n.nocheck, "true");
+ n.nocheck = !!n.nocheck || (setting.check.nocheckInherit && parentNode && !!parentNode.nocheck);
+ if (typeof n.chkDisabled == "string") n.chkDisabled = tools.eqs(n.chkDisabled, "true");
+ n.chkDisabled = !!n.chkDisabled || (setting.check.chkDisabledInherit && parentNode && !!parentNode.chkDisabled);
+ if (typeof n.halfCheck == "string") n.halfCheck = tools.eqs(n.halfCheck, "true");
+ n.halfCheck = !!n.halfCheck;
+ n.check_Child_State = -1;
+ n.check_Focus = false;
+ n.getCheckStatus = function() {return data.getCheckStatus(setting, n);};
+
+ if (setting.check.chkStyle == consts.radio.STYLE && setting.check.radioType == consts.radio.TYPE_ALL && n[checkedKey] ) {
+ var r = data.getRoot(setting);
+ r.radioCheckedList.push(n);
+ }
+ },
+ //add dom for check
+ _beforeA = function(setting, node, html) {
+ var checkedKey = setting.data.key.checked;
+ if (setting.check.enable) {
+ data.makeChkFlag(setting, node);
+ html.push(" ");
+ }
+ },
+ //update zTreeObj, add method of check
+ _zTreeTools = function(setting, zTreeTools) {
+ zTreeTools.checkNode = function(node, checked, checkTypeFlag, callbackFlag) {
+ var checkedKey = this.setting.data.key.checked;
+ if (node.chkDisabled === true) return;
+ if (checked !== true && checked !== false) {
+ checked = !node[checkedKey];
+ }
+ callbackFlag = !!callbackFlag;
+
+ if (node[checkedKey] === checked && !checkTypeFlag) {
+ return;
+ } else if (callbackFlag && tools.apply(this.setting.callback.beforeCheck, [this.setting.treeId, node], true) == false) {
+ return;
+ }
+ if (tools.uCanDo(this.setting) && this.setting.check.enable && node.nocheck !== true) {
+ node[checkedKey] = checked;
+ var checkObj = $$(node, consts.id.CHECK, this.setting);
+ if (checkTypeFlag || this.setting.check.chkStyle === consts.radio.STYLE) view.checkNodeRelation(this.setting, node);
+ view.setChkClass(this.setting, checkObj, node);
+ view.repairParentChkClassWithSelf(this.setting, node);
+ if (callbackFlag) {
+ this.setting.treeObj.trigger(consts.event.CHECK, [null, this.setting.treeId, node]);
+ }
+ }
+ }
+
+ zTreeTools.checkAllNodes = function(checked) {
+ view.repairAllChk(this.setting, !!checked);
+ }
+
+ zTreeTools.getCheckedNodes = function(checked) {
+ var childKey = this.setting.data.key.children;
+ checked = (checked !== false);
+ return data.getTreeCheckedNodes(this.setting, data.getRoot(this.setting)[childKey], checked);
+ }
+
+ zTreeTools.getChangeCheckedNodes = function() {
+ var childKey = this.setting.data.key.children;
+ return data.getTreeChangeCheckedNodes(this.setting, data.getRoot(this.setting)[childKey]);
+ }
+
+ zTreeTools.setChkDisabled = function(node, disabled, inheritParent, inheritChildren) {
+ disabled = !!disabled;
+ inheritParent = !!inheritParent;
+ inheritChildren = !!inheritChildren;
+ view.repairSonChkDisabled(this.setting, node, disabled, inheritChildren);
+ view.repairParentChkDisabled(this.setting, node.getParentNode(), disabled, inheritParent);
+ }
+
+ var _updateNode = zTreeTools.updateNode;
+ zTreeTools.updateNode = function(node, checkTypeFlag) {
+ if (_updateNode) _updateNode.apply(zTreeTools, arguments);
+ if (!node || !this.setting.check.enable) return;
+ var nObj = $$(node, this.setting);
+ if (nObj.get(0) && tools.uCanDo(this.setting)) {
+ var checkObj = $$(node, consts.id.CHECK, this.setting);
+ if (checkTypeFlag == true || this.setting.check.chkStyle === consts.radio.STYLE) view.checkNodeRelation(this.setting, node);
+ view.setChkClass(this.setting, checkObj, node);
+ view.repairParentChkClassWithSelf(this.setting, node);
+ }
+ }
+ },
+ //method of operate data
+ _data = {
+ getRadioCheckedList: function(setting) {
+ var checkedList = data.getRoot(setting).radioCheckedList;
+ for (var i=0, j=checkedList.length; i -1 && node.check_Child_State < 2) : (node.check_Child_State > 0)))
+ };
+ return r;
+ },
+ getTreeCheckedNodes: function(setting, nodes, checked, results) {
+ if (!nodes) return [];
+ var childKey = setting.data.key.children,
+ checkedKey = setting.data.key.checked,
+ onlyOne = (checked && setting.check.chkStyle == consts.radio.STYLE && setting.check.radioType == consts.radio.TYPE_ALL);
+ results = !results ? [] : results;
+ for (var i = 0, l = nodes.length; i < l; i++) {
+ if (nodes[i].nocheck !== true && nodes[i].chkDisabled !== true && nodes[i][checkedKey] == checked) {
+ results.push(nodes[i]);
+ if(onlyOne) {
+ break;
+ }
+ }
+ data.getTreeCheckedNodes(setting, nodes[i][childKey], checked, results);
+ if(onlyOne && results.length > 0) {
+ break;
+ }
+ }
+ return results;
+ },
+ getTreeChangeCheckedNodes: function(setting, nodes, results) {
+ if (!nodes) return [];
+ var childKey = setting.data.key.children,
+ checkedKey = setting.data.key.checked;
+ results = !results ? [] : results;
+ for (var i = 0, l = nodes.length; i < l; i++) {
+ if (nodes[i].nocheck !== true && nodes[i].chkDisabled !== true && nodes[i][checkedKey] != nodes[i].checkedOld) {
+ results.push(nodes[i]);
+ }
+ data.getTreeChangeCheckedNodes(setting, nodes[i][childKey], results);
+ }
+ return results;
+ },
+ makeChkFlag: function(setting, node) {
+ if (!node) return;
+ var childKey = setting.data.key.children,
+ checkedKey = setting.data.key.checked,
+ chkFlag = -1;
+ if (node[childKey]) {
+ for (var i = 0, l = node[childKey].length; i < l; i++) {
+ var cNode = node[childKey][i];
+ var tmp = -1;
+ if (setting.check.chkStyle == consts.radio.STYLE) {
+ if (cNode.nocheck === true || cNode.chkDisabled === true) {
+ tmp = cNode.check_Child_State;
+ } else if (cNode.halfCheck === true) {
+ tmp = 2;
+ } else if (cNode[checkedKey]) {
+ tmp = 2;
+ } else {
+ tmp = cNode.check_Child_State > 0 ? 2:0;
+ }
+ if (tmp == 2) {
+ chkFlag = 2; break;
+ } else if (tmp == 0){
+ chkFlag = 0;
+ }
+ } else if (setting.check.chkStyle == consts.checkbox.STYLE) {
+ if (cNode.nocheck === true || cNode.chkDisabled === true) {
+ tmp = cNode.check_Child_State;
+ } else if (cNode.halfCheck === true) {
+ tmp = 1;
+ } else if (cNode[checkedKey] ) {
+ tmp = (cNode.check_Child_State === -1 || cNode.check_Child_State === 2) ? 2 : 1;
+ } else {
+ tmp = (cNode.check_Child_State > 0) ? 1 : 0;
+ }
+ if (tmp === 1) {
+ chkFlag = 1; break;
+ } else if (tmp === 2 && chkFlag > -1 && i > 0 && tmp !== chkFlag) {
+ chkFlag = 1; break;
+ } else if (chkFlag === 2 && tmp > -1 && tmp < 2) {
+ chkFlag = 1; break;
+ } else if (tmp > -1) {
+ chkFlag = tmp;
+ }
+ }
+ }
+ }
+ node.check_Child_State = chkFlag;
+ }
+ },
+ //method of event proxy
+ _event = {
+
+ },
+ //method of event handler
+ _handler = {
+ onCheckNode: function (event, node) {
+ if (node.chkDisabled === true) return false;
+ var setting = data.getSetting(event.data.treeId),
+ checkedKey = setting.data.key.checked;
+ if (tools.apply(setting.callback.beforeCheck, [setting.treeId, node], true) == false) return true;
+ node[checkedKey] = !node[checkedKey];
+ view.checkNodeRelation(setting, node);
+ var checkObj = $$(node, consts.id.CHECK, setting);
+ view.setChkClass(setting, checkObj, node);
+ view.repairParentChkClassWithSelf(setting, node);
+ setting.treeObj.trigger(consts.event.CHECK, [event, setting.treeId, node]);
+ return true;
+ },
+ onMouseoverCheck: function(event, node) {
+ if (node.chkDisabled === true) return false;
+ var setting = data.getSetting(event.data.treeId),
+ checkObj = $$(node, consts.id.CHECK, setting);
+ node.check_Focus = true;
+ view.setChkClass(setting, checkObj, node);
+ return true;
+ },
+ onMouseoutCheck: function(event, node) {
+ if (node.chkDisabled === true) return false;
+ var setting = data.getSetting(event.data.treeId),
+ checkObj = $$(node, consts.id.CHECK, setting);
+ node.check_Focus = false;
+ view.setChkClass(setting, checkObj, node);
+ return true;
+ }
+ },
+ //method of tools for zTree
+ _tools = {
+
+ },
+ //method of operate ztree dom
+ _view = {
+ checkNodeRelation: function(setting, node) {
+ var pNode, i, l,
+ childKey = setting.data.key.children,
+ checkedKey = setting.data.key.checked,
+ r = consts.radio;
+ if (setting.check.chkStyle == r.STYLE) {
+ var checkedList = data.getRadioCheckedList(setting);
+ if (node[checkedKey]) {
+ if (setting.check.radioType == r.TYPE_ALL) {
+ for (i = checkedList.length-1; i >= 0; i--) {
+ pNode = checkedList[i];
+ if (pNode[checkedKey] && pNode != node) {
+ pNode[checkedKey] = false;
+ checkedList.splice(i, 1);
+
+ view.setChkClass(setting, $$(pNode, consts.id.CHECK, setting), pNode);
+ if (pNode.parentTId != node.parentTId) {
+ view.repairParentChkClassWithSelf(setting, pNode);
+ }
+ }
+ }
+ checkedList.push(node);
+ } else {
+ var parentNode = (node.parentTId) ? node.getParentNode() : data.getRoot(setting);
+ for (i = 0, l = parentNode[childKey].length; i < l; i++) {
+ pNode = parentNode[childKey][i];
+ if (pNode[checkedKey] && pNode != node) {
+ pNode[checkedKey] = false;
+ view.setChkClass(setting, $$(pNode, consts.id.CHECK, setting), pNode);
+ }
+ }
+ }
+ } else if (setting.check.radioType == r.TYPE_ALL) {
+ for (i = 0, l = checkedList.length; i < l; i++) {
+ if (node == checkedList[i]) {
+ checkedList.splice(i, 1);
+ break;
+ }
+ }
+ }
+
+ } else {
+ if (node[checkedKey] && (!node[childKey] || node[childKey].length==0 || setting.check.chkboxType.Y.indexOf("s") > -1)) {
+ view.setSonNodeCheckBox(setting, node, true);
+ }
+ if (!node[checkedKey] && (!node[childKey] || node[childKey].length==0 || setting.check.chkboxType.N.indexOf("s") > -1)) {
+ view.setSonNodeCheckBox(setting, node, false);
+ }
+ if (node[checkedKey] && setting.check.chkboxType.Y.indexOf("p") > -1) {
+ view.setParentNodeCheckBox(setting, node, true);
+ }
+ if (!node[checkedKey] && setting.check.chkboxType.N.indexOf("p") > -1) {
+ view.setParentNodeCheckBox(setting, node, false);
+ }
+ }
+ },
+ makeChkClass: function(setting, node) {
+ var checkedKey = setting.data.key.checked,
+ c = consts.checkbox, r = consts.radio,
+ checkboxType = setting.check.chkboxType;
+ var notEffectByOtherNode = (checkboxType.Y === "" && checkboxType.N === "");
+ fullStyle = "";
+ if (node.chkDisabled === true) {
+ fullStyle = c.DISABLED;
+ } else if (node.halfCheck) {
+ fullStyle = c.PART;
+ } else if (setting.check.chkStyle == r.STYLE) {
+ fullStyle = (node.check_Child_State < 1)? c.FULL:c.PART;
+ } else {
+ fullStyle = node[checkedKey] ? ((node.check_Child_State === 2 || node.check_Child_State === -1) || notEffectByOtherNode ? c.FULL:c.PART) : ((node.check_Child_State < 1 || notEffectByOtherNode)? c.FULL:c.PART);
+ }
+ var chkName = setting.check.chkStyle + "_" + (node[checkedKey] ? c.TRUE : c.FALSE) + "_" + fullStyle;
+ chkName = (node.check_Focus && node.chkDisabled !== true) ? chkName + "_" + c.FOCUS : chkName;
+ var chClass = consts.className.BUTTON + " " + c.DEFAULT + " " + chkName;
+ switch (chkName) {
+ case 'checkbox_true_part':
+ case 'checkbox_true_part_focus':
+ chClass += ' bi-half-button bi-high-light-border';
+ break;
+ case 'checkbox_true_full':
+ case 'checkbox_true_full_focus':
+ chClass += ' bi-checkbox checkbox-content bi-high-light-background active';
+ break;
+ case 'checkbox_false_full':
+ case 'checkbox_false_full_focus':
+ default:
+ chClass += ' bi-checkbox checkbox-content';
+ break;
+ }
+ return chClass;
+ },
+ repairAllChk: function(setting, checked) {
+ if (setting.check.enable && setting.check.chkStyle === consts.checkbox.STYLE) {
+ var checkedKey = setting.data.key.checked,
+ childKey = setting.data.key.children,
+ root = data.getRoot(setting);
+ for (var i = 0, l = root[childKey].length; i 0) {
+ view.repairParentChkClass(setting, node[childKey][0]);
+ } else {
+ view.repairParentChkClass(setting, node);
+ }
+ },
+ repairSonChkDisabled: function(setting, node, chkDisabled, inherit) {
+ if (!node) return;
+ var childKey = setting.data.key.children;
+ if (node.chkDisabled != chkDisabled) {
+ node.chkDisabled = chkDisabled;
+ }
+ view.repairChkClass(setting, node);
+ if (node[childKey] && inherit) {
+ for (var i = 0, l = node[childKey].length; i < l; i++) {
+ var sNode = node[childKey][i];
+ view.repairSonChkDisabled(setting, sNode, chkDisabled, inherit);
+ }
+ }
+ },
+ repairParentChkDisabled: function(setting, node, chkDisabled, inherit) {
+ if (!node) return;
+ if (node.chkDisabled != chkDisabled && inherit) {
+ node.chkDisabled = chkDisabled;
+ }
+ view.repairChkClass(setting, node);
+ view.repairParentChkDisabled(setting, node.getParentNode(), chkDisabled, inherit);
+ },
+ setChkClass: function(setting, obj, node) {
+ if (!obj) return;
+ if (node.nocheck === true) {
+ obj.hide();
+ } else {
+ obj.show();
+ }
+ obj.attr('class', view.makeChkClass(setting, node));
+ },
+ setParentNodeCheckBox: function(setting, node, value, srcNode) {
+ var childKey = setting.data.key.children,
+ checkedKey = setting.data.key.checked,
+ checkObj = $$(node, consts.id.CHECK, setting);
+ if (!srcNode) srcNode = node;
+ data.makeChkFlag(setting, node);
+ if (node.nocheck !== true && node.chkDisabled !== true) {
+ node[checkedKey] = value;
+ view.setChkClass(setting, checkObj, node);
+ if (setting.check.autoCheckTrigger && node != srcNode) {
+ setting.treeObj.trigger(consts.event.CHECK, [null, setting.treeId, node]);
+ }
+ }
+ if (node.parentTId) {
+ var pSign = true;
+ if (!value) {
+ var pNodes = node.getParentNode()[childKey];
+ for (var i = 0, l = pNodes.length; i < l; i++) {
+ if ((pNodes[i].nocheck !== true && pNodes[i].chkDisabled !== true && pNodes[i][checkedKey])
+ || ((pNodes[i].nocheck === true || pNodes[i].chkDisabled === true) && pNodes[i].check_Child_State > 0)) {
+ pSign = false;
+ break;
+ }
+ }
+ }
+ if (pSign) {
+ view.setParentNodeCheckBox(setting, node.getParentNode(), value, srcNode);
+ }
+ }
+ },
+ setSonNodeCheckBox: function(setting, node, value, srcNode) {
+ if (!node) return;
+ var childKey = setting.data.key.children,
+ checkedKey = setting.data.key.checked,
+ checkObj = $$(node, consts.id.CHECK, setting);
+ if (!srcNode) srcNode = node;
+
+ var hasDisable = false;
+ if (node[childKey]) {
+ for (var i = 0, l = node[childKey].length; i < l && node.chkDisabled !== true; i++) {
+ var sNode = node[childKey][i];
+ view.setSonNodeCheckBox(setting, sNode, value, srcNode);
+ if (sNode.chkDisabled === true) hasDisable = true;
+ }
+ }
+
+ if (node != data.getRoot(setting) && node.chkDisabled !== true) {
+ if (hasDisable && node.nocheck !== true) {
+ data.makeChkFlag(setting, node);
+ }
+ if (node.nocheck !== true && node.chkDisabled !== true) {
+ node[checkedKey] = value;
+ if (!hasDisable) node.check_Child_State = (node[childKey] && node[childKey].length > 0) ? (value ? 2 : 0) : -1;
+ } else {
+ node.check_Child_State = -1;
+ }
+ view.setChkClass(setting, checkObj, node);
+ if (setting.check.autoCheckTrigger && node != srcNode && node.nocheck !== true && node.chkDisabled !== true) {
+ setting.treeObj.trigger(consts.event.CHECK, [null, setting.treeId, node]);
+ }
+ }
+
+ }
+ },
+
+ _z = {
+ tools: _tools,
+ view: _view,
+ event: _event,
+ data: _data
+ };
+ $.extend(true, $.fn.zTree.consts, _consts);
+ $.extend(true, $.fn.zTree._z, _z);
+
+ var zt = $.fn.zTree,
+ tools = zt._z.tools,
+ consts = zt.consts,
+ view = zt._z.view,
+ data = zt._z.data,
+ event = zt._z.event,
+ $$ = tools.$;
+
+ data.exSetting(_setting);
+ data.addInitBind(_bindEvent);
+ data.addInitUnBind(_unbindEvent);
+ data.addInitCache(_initCache);
+ data.addInitNode(_initNode);
+ data.addInitProxy(_eventProxy, true);
+ data.addInitRoot(_initRoot);
+ data.addBeforeA(_beforeA);
+ data.addZTreeTools(_zTreeTools);
+
+ var _createNodes = view.createNodes;
+ view.createNodes = function(setting, level, nodes, parentNode) {
+ if (_createNodes) _createNodes.apply(view, arguments);
+ if (!nodes) return;
+ view.repairParentChkClassWithSelf(setting, parentNode);
+ }
+ var _removeNode = view.removeNode;
+ view.removeNode = function(setting, node) {
+ var parentNode = node.getParentNode();
+ if (_removeNode) _removeNode.apply(view, arguments);
+ if (!node || !parentNode) return;
+ view.repairChkClass(setting, parentNode);
+ view.repairParentChkClass(setting, parentNode);
+ }
+
+ var _appendNodes = view.appendNodes;
+ view.appendNodes = function(setting, level, nodes, parentNode, initFlag, openFlag) {
+ var html = "";
+ if (_appendNodes) {
+ html = _appendNodes.apply(view, arguments);
+ }
+ if (parentNode) {
+ data.makeChkFlag(setting, parentNode);
+ }
+ return html;
+ }
+})(BI.jQuery);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/ztree/list/0.listtreeview.js b/src/main/resources/com/fr/fineui/case/ztree/list/0.listtreeview.js
new file mode 100644
index 0000000..dd9a1f8
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/ztree/list/0.listtreeview.js
@@ -0,0 +1,118 @@
+/**
+ * author: windy
+ * 继承自treeView, 此树的父子节点的勾选状态互不影响, 此树不会有半选节点
+ * 返回value格式为[["A"], ["A", "a"]]表示勾选了A且勾选了a
+ * @class BI.ListTreeView
+ * @extends BI.TreeView
+ */
+BI.ListTreeView = BI.inherit(BI.TreeView, {
+
+ _constants: {
+ SPLIT: "<|>"
+ },
+
+ _defaultConfig: function () {
+ return BI.extend(BI.ListTreeView.superclass._defaultConfig.apply(this, arguments), {
+ value: {}
+ });
+ },
+ _init: function () {
+ BI.ListTreeView.superclass._init.apply(this, arguments);
+ var o = this.options;
+ if(BI.isNotNull(o.value)) {
+ this.setSelectedValue(o.value);
+ }
+ },
+
+ // 配置属性
+ _configSetting: function () {
+ var paras = this.options.paras;
+ var self = this;
+ var setting = {
+ async: {
+ enable: false
+ },
+ check: {
+ enable: true,
+ chkboxType: {Y: "", N: ""}
+ },
+ data: {
+ key: {
+ title: "title",
+ name: "text"
+ },
+ simpleData: {
+ enable: true
+ }
+ },
+ view: {
+ showIcon: false,
+ expandSpeed: "",
+ nameIsHTML: true,
+ dblClickExpand: false
+ },
+ callback: {
+ onCheck: onCheck,
+ onClick: onClick
+ }
+ };
+
+ function onClick (event, treeId, treeNode) {
+ var zTree = BI.$.fn.zTree.getZTreeObj(treeId);
+ var checked = treeNode.checked;
+ self._checkValue(treeNode, !checked);
+ zTree.checkNode(treeNode, !checked, true, true);
+ }
+
+ function onCheck (event, treeId, treeNode) {
+ self._selectTreeNode(treeId, treeNode);
+ }
+
+ return setting;
+ },
+
+ _selectTreeNode: function (treeId, treeNode) {
+ this._checkValue(treeNode, treeNode.checked);
+ BI.ListTreeView.superclass._selectTreeNode.apply(this, arguments);
+ },
+
+ _transArrayToMap: function (treeArrays) {
+ var self = this;
+ var map = {};
+ BI.each(treeArrays, function (idx, array) {
+ var key = array.join(self._constants.SPLIT);
+ map[key] = true;
+ });
+ return map;
+ },
+
+ _transMapToArray: function (treeMap) {
+ var self = this;
+ var array = [];
+ BI.each(treeMap, function (key) {
+ var item = key.split(self._constants.SPLIT);
+ array.push(item);
+ });
+ return array;
+ },
+
+ _checkValue: function (treeNode, checked) {
+ var key = BI.concat(this._getParentValues(treeNode), this._getNodeValue(treeNode)).join(this._constants.SPLIT);
+ if(checked) {
+ this.storeValue[key] = true;
+ } else {
+ delete this.storeValue[key];
+ }
+ },
+
+ setSelectedValue: function (value) {
+ this.options.paras.selectedValues = value || [];
+ this.storeValue = this._transArrayToMap(value);
+ },
+
+ getValue: function () {
+ return this._transMapToArray(this.storeValue);
+ }
+});
+
+BI.shortcut("bi.list_tree_view", BI.ListTreeView);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/ztree/list/1.listasynctree.js b/src/main/resources/com/fr/fineui/case/ztree/list/1.listasynctree.js
new file mode 100644
index 0000000..7e1f25b
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/ztree/list/1.listasynctree.js
@@ -0,0 +1,123 @@
+/**
+ * author: windy
+ * 继承自treeView, 此树的父子节点的勾选状态互不影响, 此树不会有半选节点
+ * 返回value格式为["A", ["A", "a"]]表示勾选了A且勾选了a
+ * @class BI.ListListAsyncTree
+ * @extends BI.TreeView
+ */
+BI.ListAsyncTree = BI.inherit(BI.ListTreeView, {
+ _defaultConfig: function () {
+ return BI.extend(BI.ListAsyncTree.superclass._defaultConfig.apply(this, arguments), {});
+ },
+ _init: function () {
+ BI.ListAsyncTree.superclass._init.apply(this, arguments);
+ },
+
+ // 配置属性
+ _configSetting: function () {
+ var paras = this.options.paras;
+ var self = this;
+ var setting = {
+ async: {
+ enable: false, // 很明显这棵树把异步请求关掉了,所有的异步请求都是手动控制的
+ otherParam: BI.cjkEncodeDO(paras)
+ },
+ check: {
+ enable: true,
+ chkboxType: {Y: "", N: ""}
+ },
+ data: {
+ key: {
+ title: "title",
+ name: "text"
+ },
+ simpleData: {
+ enable: true
+ }
+ },
+ view: {
+ showIcon: false,
+ expandSpeed: "",
+ nameIsHTML: true,
+ dblClickExpand: false
+ },
+ callback: {
+ onCheck: onCheck,
+ beforeExpand: beforeExpand,
+ beforeCheck: beforeCheck,
+ onClick: onClick
+ }
+ };
+
+ function beforeCheck (treeId, treeNode) {
+ treeNode.half = false;
+ }
+
+ function onClick (event, treeId, treeNode) {
+ var zTree = BI.$.fn.zTree.getZTreeObj(treeId);
+ var checked = treeNode.checked;
+ self._checkValue(treeNode, !checked);
+ zTree.checkNode(treeNode, !checked, true, true);
+ }
+
+ function beforeExpand (treeId, treeNode) {
+ self._beforeExpandNode(treeId, treeNode);
+ }
+
+ function onCheck (event, treeId, treeNode) {
+ self._selectTreeNode(treeId, treeNode);
+ }
+
+ return setting;
+ },
+
+ // 展开节点
+ _beforeExpandNode: function (treeId, treeNode) {
+ var self = this, o = this.options;
+ var parentValues = treeNode.parentValues || self._getParentValues(treeNode);
+ var op = BI.extend({}, o.paras, {
+ id: treeNode.id,
+ times: 1,
+ parentValues: parentValues.concat(this._getNodeValue(treeNode))
+ });
+ var complete = function (d) {
+ var nodes = d.items || [];
+ if (nodes.length > 0) {
+ callback(self._dealWidthNodes(nodes), !!d.hasNext);
+ }
+ };
+ var times = 1;
+
+ function callback (nodes, hasNext) {
+ self.nodes.addNodes(treeNode, nodes);
+ // 展开节点是没有分页的
+ if (hasNext === true) {
+ BI.delay(function () {
+ times++;
+ op.times = times;
+ o.itemsCreator(op, complete);
+ }, 100);
+ }
+ }
+
+ if (!treeNode.children) {
+ setTimeout(function () {
+ o.itemsCreator(op, complete);
+ }, 17);
+ }
+ },
+
+ hasChecked: function () {
+ return !BI.isEmpty(this.options.paras.selectedValues) || BI.ListAsyncTree.superclass.hasChecked.apply(this, arguments);
+ },
+
+ // 生成树方法
+ stroke: function (config) {
+ delete this.options.keyword;
+ BI.extend(this.options.paras, config);
+ var setting = this._configSetting();
+ this._initTree(setting);
+ }
+});
+
+BI.shortcut("bi.list_async_tree", BI.ListAsyncTree);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/ztree/list/listparttree.js b/src/main/resources/com/fr/fineui/case/ztree/list/listparttree.js
new file mode 100644
index 0000000..cc44621
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/ztree/list/listparttree.js
@@ -0,0 +1,92 @@
+/**
+ * guy
+ * 局部树,两个请求树, 第一个请求构造树,第二个请求获取节点
+ * @class BI.ListPartTree
+ * @extends BI.AsyncTree
+ */
+BI.ListPartTree = BI.inherit(BI.ListAsyncTree, {
+ _defaultConfig: function () {
+ return BI.extend(BI.ListPartTree.superclass._defaultConfig.apply(this, arguments), {});
+ },
+
+ _init: function () {
+ BI.ListPartTree.superclass._init.apply(this, arguments);
+ },
+
+ _loadMore: function () {
+ var self = this, o = this.options;
+ var op = BI.extend({}, o.paras, {
+ type: BI.TreeView.REQ_TYPE_INIT_DATA,
+ times: ++this.times
+ });
+ this.tip.setLoading();
+ o.itemsCreator(op, function (d) {
+ var hasNext = !!d.hasNext, nodes = d.items || [];
+ o.paras.lastSearchValue = d.lastSearchValue;
+ if (self._stop === true) {
+ return;
+ }
+ if (!hasNext) {
+ self.tip.setEnd();
+ } else {
+ self.tip.setLoaded();
+ }
+ if (nodes.length > 0) {
+ self.nodes.addNodes(null, self._dealWidthNodes(nodes));
+ }
+ });
+ },
+
+ _initTree: function (setting, keyword) {
+ var self = this, o = this.options;
+ this.times = 1;
+ var tree = this.tree;
+ tree.empty();
+ self.tip.setVisible(false);
+ this.loading();
+ var op = BI.extend({}, o.paras, {
+ type: BI.TreeView.REQ_TYPE_INIT_DATA,
+ times: this.times
+ });
+ var complete = function (d) {
+ if (self._stop === true || keyword != o.paras.keyword) {
+ return;
+ }
+ var hasNext = !!d.hasNext, nodes = d.items || [];
+ o.paras.lastSearchValue = d.lastSearchValue;
+ // 没有请求到数据也要初始化空树, 如果不初始化, 树就是上一次构造的树, 节点信息都是过期的
+ callback(nodes.length > 0 ? self._dealWidthNodes(nodes) : []);
+ self.setTipVisible(nodes.length <= 0);
+ self.loaded();
+ if (!hasNext) {
+ self.tip.invisible();
+ } else {
+ self.tip.setLoaded();
+ }
+ self.fireEvent(BI.Events.AFTERINIT);
+ };
+
+ function callback (nodes) {
+ if (self._stop === true) {
+ return;
+ }
+ self.nodes = BI.$.fn.zTree.init(tree.element, setting, nodes);
+ }
+
+ BI.delay(function () {
+ o.itemsCreator(op, complete);
+ }, 100);
+ },
+
+ // 生成树方法
+ stroke: function (config) {
+ var o = this.options;
+ delete o.paras.keyword;
+ BI.extend(o.paras, config);
+ delete o.paras.lastSearchValue;
+ var setting = this._configSetting();
+ this._initTree(setting, o.paras.keyword);
+ }
+});
+
+BI.shortcut("bi.list_part_tree", BI.ListPartTree);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/ztree/parttree.js b/src/main/resources/com/fr/fineui/case/ztree/parttree.js
new file mode 100644
index 0000000..974196b
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/ztree/parttree.js
@@ -0,0 +1,200 @@
+/**
+ * guy
+ * 局部树,两个请求树, 第一个请求构造树,第二个请求获取节点
+ * @class BI.PartTree
+ * @extends BI.AsyncTree
+ */
+BI.PartTree = BI.inherit(BI.AsyncTree, {
+ _defaultConfig: function () {
+ return BI.extend(BI.PartTree.superclass._defaultConfig.apply(this, arguments), {});
+ },
+
+ _loadMore: function () {
+ var self = this, o = this.options;
+ var op = BI.extend({}, o.paras, {
+ type: BI.TreeView.REQ_TYPE_INIT_DATA,
+ times: ++this.times
+ });
+ this.tip.setLoading();
+ o.itemsCreator(op, function (d) {
+ var hasNext = !!d.hasNext, nodes = d.items || [];
+ o.paras.lastSearchValue = d.lastSearchValue;
+ if (self._stop === true) {
+ return;
+ }
+ if (!hasNext) {
+ self.tip.setEnd();
+ } else {
+ self.tip.setLoaded();
+ }
+ if (nodes.length > 0) {
+ self.nodes.addNodes(null, self._dealWidthNodes(nodes));
+ }
+ });
+ },
+
+ _selectTreeNode: function (treeId, treeNode) {
+ var self = this, o = this.options;
+ var parentValues = BI.deepClone(treeNode.parentValues || self._getParentValues(treeNode));
+ var name = this._getNodeValue(treeNode);
+ if (treeNode.checked === true) {
+ this.options.paras.selectedValues = this._getUnionValue();
+ // this._buildTree(self.options.paras.selectedValues, BI.concat(parentValues, name));
+ o.itemsCreator(BI.extend({}, o.paras, {
+ type: BI.TreeView.REQ_TYPE_ADJUST_DATA,
+ curSelectedValue: name,
+ parentValues: parentValues
+ }), function (res) {
+ self.options.paras.selectedValues = res;
+ BI.AsyncTree.superclass._selectTreeNode.apply(self, arguments);
+ });
+ } else {
+ // 如果选中的值中不存在该值不处理
+ // 因为反正是不选中,没必要管
+ var t = this.options.paras.selectedValues;
+ var p = parentValues.concat(name);
+ for (var i = 0, len = p.length; i < len; i++) {
+ t = t[p[i]];
+ if (t == null) {
+ return;
+ }
+ // 选中中国-江苏, 搜索南京,取消勾选
+ if (BI.isEmpty(t)) {
+ break;
+ }
+ }
+ o.itemsCreator(BI.extend({}, o.paras, {
+ type: BI.TreeView.REQ_TYPE_SELECT_DATA,
+ notSelectedValue: name,
+ parentValues: parentValues
+ }), function (new_values) {
+ self.options.paras.selectedValues = new_values;
+ BI.AsyncTree.superclass._selectTreeNode.apply(self, arguments);
+ });
+ }
+ },
+
+ _getSelectedValues: function () {
+ var self = this;
+ var hashMap = {};
+ var rootNoots = this.nodes.getNodes();
+ track(rootNoots);
+
+ function track (nodes) {
+ BI.each(nodes, function (i, node) {
+ var checkState = node.getCheckStatus();
+ if (checkState.checked === false) {
+ return true;
+ }
+ var parentValues = node.parentValues || self._getParentValues(node);
+ // 把文字中的html去掉,其实就是把文字颜色去掉
+ var values = parentValues.concat([self._getNodeValue(node)]);
+ self._buildTree(hashMap, values);
+ // if(checkState.checked === true && checkState.half === false && nodes[i].flag === true){
+ // continue;
+ // }
+ if (BI.isNotEmptyArray(node.children)) {
+ track(node.children);
+ return true;
+ }
+ if (checkState.half === true) {
+ self._getHalfSelectedValues(hashMap, node);
+ }
+ });
+ }
+
+ return hashMap;
+ },
+
+ _initTree: function (setting, keyword) {
+ var self = this, o = this.options;
+ this.times = 1;
+ var tree = this.tree;
+ tree.empty();
+ self.tip.setVisible(false);
+ this.loading();
+ var op = BI.extend({}, o.paras, {
+ type: BI.TreeView.REQ_TYPE_INIT_DATA,
+ times: this.times
+ });
+ var complete = function (d) {
+ if (self._stop === true || keyword != o.paras.keyword) {
+ return;
+ }
+ var hasNext = !!d.hasNext, nodes = d.items || [];
+ o.paras.lastSearchValue = d.lastSearchValue;
+ // 没有请求到数据也要初始化空树, 如果不初始化, 树就是上一次构造的树, 节点信息都是过期的
+ callback(nodes.length > 0 ? self._dealWidthNodes(nodes) : []);
+ self.setTipVisible(nodes.length <= 0);
+ self.loaded();
+ if (!hasNext) {
+ self.tip.invisible();
+ } else {
+ self.tip.setLoaded();
+ }
+ self.fireEvent(BI.Events.AFTERINIT);
+ };
+
+ function callback (nodes) {
+ if (self._stop === true) {
+ return;
+ }
+ self.nodes = BI.$.fn.zTree.init(tree.element, setting, nodes);
+ }
+
+ BI.delay(function () {
+ o.itemsCreator(op, complete);
+ }, 100);
+ },
+
+ getValue: function () {
+ return BI.deepClone(this.options.paras.selectedValues || {});
+ },
+
+ _getUnionValue: function () {
+ if (!this.nodes) {
+ return {};
+ }
+ var checkedValues = this._getSelectedValues();
+ if (BI.isEmpty(checkedValues)) {
+ return BI.deepClone(this.options.paras.selectedValues);
+ }
+ if (BI.isEmpty(this.options.paras.selectedValues)) {
+ return checkedValues;
+ }
+ return this._union(checkedValues, this.options.paras.selectedValues);
+ },
+
+ _union: function (valueA, valueB) {
+ var self = this;
+ var map = {};
+ track([], valueA, valueB);
+ track([], valueB, valueA);
+
+ function track (parent, node, compare) {
+ BI.each(node, function (n, item) {
+ if (BI.isNull(compare[n])) {
+ self._addTreeNode(map, parent, n, item);
+ } else if (BI.isEmpty(compare[n])) {
+ self._addTreeNode(map, parent, n, {});
+ } else {
+ track(parent.concat([n]), node[n], compare[n]);
+ }
+ });
+ }
+
+ return map;
+ },
+
+ // 生成树方法
+ stroke: function (config) {
+ var o = this.options;
+ delete o.paras.keyword;
+ BI.extend(o.paras, config);
+ delete o.paras.lastSearchValue;
+ var setting = this._configSetting();
+ this._initTree(setting, o.paras.keyword);
+ }
+});
+
+BI.shortcut("bi.part_tree", BI.PartTree);
diff --git a/src/main/resources/com/fr/fineui/case/ztree/tree.display.js b/src/main/resources/com/fr/fineui/case/ztree/tree.display.js
new file mode 100644
index 0000000..4c01356
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/ztree/tree.display.js
@@ -0,0 +1,66 @@
+/**
+ * guy
+ * 异步树
+ * @class BI.DisplayTree
+ * @extends BI.TreeView
+ */
+BI.DisplayTree = BI.inherit(BI.TreeView, {
+ _defaultConfig: function () {
+ return BI.extend(BI.DisplayTree.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "bi-display-tree"
+ });
+ },
+
+ // 配置属性
+ _configSetting: function () {
+ var setting = {
+ view: {
+ selectedMulti: false,
+ dblClickExpand: false,
+ showIcon: false,
+ nameIsHTML: true,
+ showTitle: false
+ },
+ data: {
+ key: {
+ title: "title",
+ name: "text"
+ },
+ simpleData: {
+ enable: true
+ }
+ },
+ callback: {
+ beforeCollapse: beforeCollapse
+ }
+ };
+
+ function beforeCollapse(treeId, treeNode) {
+ return false;
+ }
+
+ return setting;
+ },
+
+ _dealWidthNodes: function (nodes) {
+ nodes = BI.DisplayTree.superclass._dealWidthNodes.apply(this, arguments);
+ var self = this, o = this.options;
+ BI.each(nodes, function (i, node) {
+ node.isParent = node.isParent || node.parent;
+ if (node.text == null) {
+ if (node.count > 0) {
+ node.text = node.value + "(" + BI.i18nText("BI-Basic_Altogether") + node.count + BI.i18nText("BI-Basic_Count") + ")";
+ }
+ }
+ });
+ return nodes;
+ },
+
+ initTree: function (nodes, setting) {
+ var setting = setting || this._configSetting();
+ this.nodes = BI.$.fn.zTree.init(this.tree.element, setting, nodes);
+ }
+});
+BI.DisplayTree.EVENT_CHANGE = "EVENT_CHANGE";
+
+BI.shortcut("bi.display_tree", BI.DisplayTree);
diff --git a/src/main/resources/com/fr/fineui/case/ztree/tree.list.display.js b/src/main/resources/com/fr/fineui/case/ztree/tree.list.display.js
new file mode 100644
index 0000000..60ce557
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/ztree/tree.list.display.js
@@ -0,0 +1,78 @@
+/**
+ * guy
+ * 异步树
+ * @class BI.ListListDisplayTree
+ * @extends BI.TreeView
+ */
+BI.ListDisplayTree = BI.inherit(BI.ListTreeView, {
+ _defaultConfig: function () {
+ return BI.extend(BI.ListDisplayTree.superclass._defaultConfig.apply(this, arguments), {
+ extraCls: "bi-list-display-tree"
+ });
+ },
+ _init: function () {
+ BI.ListDisplayTree.superclass._init.apply(this, arguments);
+ },
+
+ // 配置属性
+ _configSetting: function () {
+ var setting = {
+ view: {
+ selectedMulti: false,
+ dblClickExpand: false,
+ showIcon: false,
+ nameIsHTML: true,
+ showTitle: false,
+ fontCss: getFont
+ },
+ data: {
+ key: {
+ title: "title",
+ name: "text"
+ },
+ simpleData: {
+ enable: true
+ }
+ },
+ callback: {
+ beforeCollapse: beforeCollapse
+ }
+ };
+
+ function beforeCollapse(treeId, treeNode) {
+ return false;
+ }
+
+ function getFont(treeId, node) {
+ return node.isLeaf ? {} : {color: "#999999"};
+ }
+
+ return setting;
+ },
+
+ _dealWidthNodes: function (nodes) {
+ nodes = BI.ListDisplayTree.superclass._dealWidthNodes.apply(this, arguments);
+ var self = this, o = this.options;
+ BI.each(nodes, function (i, node) {
+ node.isParent = node.isParent || node.parent;
+ if (node.text == null) {
+ if (node.count > 0) {
+ node.text = node.value + "(" + BI.i18nText("BI-Basic_Altogether") + node.count + BI.i18nText("BI-Basic_Count") + ")";
+ }
+ }
+ });
+ return nodes;
+ },
+
+ initTree: function (nodes, setting) {
+ var setting = setting || this._configSetting();
+ this.nodes = BI.$.fn.zTree.init(this.tree.element, setting, nodes);
+ },
+
+ destroy: function () {
+ BI.ListDisplayTree.superclass.destroy.apply(this, arguments);
+ }
+});
+BI.ListDisplayTree.EVENT_CHANGE = "EVENT_CHANGE";
+
+BI.shortcut("bi.list_display_tree", BI.ListDisplayTree);
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/ztree/tree.simple.js b/src/main/resources/com/fr/fineui/case/ztree/tree.simple.js
new file mode 100644
index 0000000..690a445
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/ztree/tree.simple.js
@@ -0,0 +1,127 @@
+/**
+ * 简单的多选树
+ *
+ * Created by GUY on 2016/2/16.
+ * @class BI.SimpleTreeView
+ * @extends BI.Widget
+ */
+BI.SimpleTreeView = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ return BI.extend(BI.SimpleTreeView.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-simple-tree",
+ itemsCreator: BI.emptyFn,
+ items: null
+ });
+ },
+ _init: function () {
+ BI.SimpleTreeView.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.structure = new BI.Tree();
+ this.tree = BI.createWidget({
+ type: "bi.tree_view",
+ element: this,
+ itemsCreator: function (op, callback) {
+ var fn = function (items) {
+ callback({
+ items: items
+ });
+ self.structure.initTree(BI.Tree.transformToTreeFormat(items));
+ };
+ if (BI.isNotNull(o.items)) {
+ fn(o.items);
+ } else {
+ o.itemsCreator(op, fn);
+ }
+ }
+ });
+ this.tree.on(BI.TreeView.EVENT_CHANGE, function () {
+ self.fireEvent(BI.SimpleTreeView.EVENT_CHANGE, arguments);
+ });
+ if (BI.isNotEmptyArray(o.items)) {
+ this.populate();
+ }
+ if (BI.isNotNull(o.value)) {
+ this.setValue(o.value);
+ }
+ },
+
+ populate: function (items, keyword) {
+ if (items) {
+ this.options.items = items;
+ }
+ this.tree.stroke({
+ keyword: keyword
+ });
+ },
+
+ _digest: function (v) {
+ v || (v = []);
+ var self = this, map = {};
+ var selected = [];
+ BI.each(v, function (i, val) {
+ var node = self.structure.search(val, "value");
+ if (node) {
+ var p = node;
+ p = p.getParent();
+ if (p) {
+ if (!map[p.value]) {
+ map[p.value] = 0;
+ }
+ map[p.value]++;
+ }
+
+ while (p && p.getChildrenLength() <= map[p.value]) {
+ selected.push(p.value);
+ p = p.getParent();
+ if (p) {
+ if (!map[p.value]) {
+ map[p.value] = 0;
+ }
+ map[p.value]++;
+ }
+ }
+ }
+ });
+ return BI.makeObject(v.concat(selected));
+ },
+
+ setValue: function (v) {
+ this.tree.setValue(this._digest(v));
+ },
+
+ _getValue: function () {
+ var self = this, result = [], val = this.tree.getValue();
+ var track = function (nodes) {
+ BI.each(nodes, function (key, node) {
+ if (BI.isEmpty(node)) {
+ result.push(key);
+ } else {
+ track(node);
+ }
+ });
+ };
+ track(val);
+ return result;
+ },
+
+ empty: function () {
+ this.tree.empty();
+ },
+
+ getValue: function () {
+ var self = this, result = [], val = this._getValue();
+ BI.each(val, function (i, key) {
+ var target = self.structure.search(key, "value");
+ if (target) {
+ self.structure._traverse(target, function (node) {
+ if (node.isLeaf()) {
+ result.push(node.value);
+ }
+ });
+ }
+ });
+ return result;
+ }
+});
+BI.SimpleTreeView.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.simple_tree", BI.SimpleTreeView);
diff --git a/src/main/resources/com/fr/fineui/case/ztree/treerender.scroll.service.js b/src/main/resources/com/fr/fineui/case/ztree/treerender.scroll.service.js
new file mode 100644
index 0000000..a2d019c
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/ztree/treerender.scroll.service.js
@@ -0,0 +1,124 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/1/8
+ * 提供节点滚动加载方式
+ */
+
+!(function () {
+ BI.TreeRenderScrollService = BI.inherit(BI.OB, {
+ _init: function () {
+ this.nodeLists = {};
+
+ this.id = this.options.id;
+ // renderService是否已经注册了滚动
+ this.hasBinded = false;
+
+ this.container = this.options.container;
+ },
+
+ _getNodeListBounds: function (tId) {
+ var nodeList = this.options.subNodeListGetter(tId)[0];
+ return {
+ top: nodeList.offsetTop,
+ left: nodeList.offsetLeft,
+ width: nodeList.offsetWidth,
+ height: nodeList.offsetHeight
+ }
+ },
+
+ _getTreeContainerBounds: function () {
+ var nodeList = this.container[0];
+ if (BI.isNotNull(nodeList)) {
+ return {
+ top: nodeList.offsetTop + nodeList.scrollTop,
+ left: nodeList.offsetLeft + nodeList.scrollLeft,
+ width: nodeList.offsetWidth,
+ height: nodeList.offsetHeight
+ };
+ }
+ return {};
+ },
+
+ _canNodePopulate: function (tId) {
+ if (this.nodeLists[tId].locked) {
+ return false;
+ }
+ // 获取ul, 即子节点列表的bounds
+ // 是否需要继续加载,只需要看子节点列表的下边界与container是否无交集
+ var bounds = this._getNodeListBounds(tId);
+ var containerBounds = this._getTreeContainerBounds(tId);
+ // ul底部是不是漏出来了
+ if (bounds.top + bounds.height < containerBounds.top + containerBounds.height) {
+ return true;
+ }
+ return false;
+ },
+
+ _isNodeInVisible: function (tId) {
+ var nodeList = this.options.subNodeListGetter(tId);
+ return nodeList.length === 0 || nodeList.css("display") === "none";
+ },
+
+ pushNodeList: function (tId, populate) {
+ var self = this;
+ if (!BI.has(this.nodeLists, tId)) {
+ this.nodeLists[tId] = {
+ populate: BI.debounce(populate, 0),
+ options: {
+ times: 1
+ },
+ // 在上一次请求返回前, 通过滚动再次触发加载的时候, 不应该认为是下一次分页, 需要等待上次请求返回
+ // 以保证顺序和请求次数的完整
+ locked: false
+ };
+ } else {
+ this.nodeLists[tId].locked = false;
+ }
+ if(!this.hasBinded) {
+ // console.log("绑定事件");
+ this.hasBinded = true;
+ this.container && this.container.on("scroll", BI.debounce(function () {
+ self.refreshAllNodes();
+ }, 30));
+ }
+ },
+
+ refreshAllNodes: function () {
+ var self = this;
+ BI.each(this.nodeLists, function (tId) {
+ // 不展开的节点就不看了
+ !self._isNodeInVisible(tId) && self.refreshNodes(tId);
+ });
+ },
+
+ refreshNodes: function (tId) {
+ if (this._canNodePopulate(tId)) {
+ var nodeList = this.nodeLists[tId];
+ nodeList.options.times++;
+ nodeList.locked = true;
+ nodeList.populate({
+ times: nodeList.options.times
+ });
+ }
+ },
+
+ removeNodeList: function (tId) {
+ delete this.nodeLists[tId];
+ if (BI.size(this.nodeLists) === 0) {
+ this.clear();
+ }
+ },
+
+ clear: function () {
+ var self = this;
+ BI.each(this.nodeLists, function (tId) {
+ self.removeNodeList(tId);
+ });
+ this.nodeLists = {};
+ // console.log("解绑事件");
+ this.container.off("scroll");
+ this.hasBinded = false;
+ }
+ });
+})();
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/case/ztree/treetrender.page.service.js b/src/main/resources/com/fr/fineui/case/ztree/treetrender.page.service.js
new file mode 100644
index 0000000..e1d7edb
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/case/ztree/treetrender.page.service.js
@@ -0,0 +1,76 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/1/8
+ * 提供节点分页加载方式
+ */
+
+!(function () {
+ BI.TreeRenderPageService = BI.inherit(BI.OB, {
+ _init: function () {
+ this.nodeLists = {};
+ },
+
+ _getLoadingBar: function(tId) {
+ var self = this;
+ var tip = BI.createWidget({
+ type: "bi.loading_bar",
+ height: 25,
+ handler: function () {
+ self.refreshNodes(tId);
+ }
+ });
+ tip.setLoaded();
+ return tip;
+ },
+
+ pushNodeList: function (tId, populate) {
+ var self = this, o = this.options;
+ var tip = this._getLoadingBar(tId);
+ if (!BI.has(this.nodeLists, tId)) {
+ this.nodeLists[tId] = {
+ populate: BI.debounce(populate, 0),
+ options: {
+ times: 1
+ },
+ loadWidget: tip
+ };
+ } else {
+ this.nodeLists[tId].loadWidget.destroy();
+ this.nodeLists[tId].loadWidget = tip;
+ }
+ BI.createWidget({
+ type: "bi.vertical",
+ element: o.subNodeListGetter(tId),
+ items: [tip]
+ });
+ },
+
+ refreshNodes: function (tId) {
+ var nodeList = this.nodeLists[tId];
+ nodeList.options.times++;
+ nodeList.loadWidget.setLoading();
+ nodeList.populate({
+ times: nodeList.options.times
+ });
+ },
+
+ removeNodeList: function (tId) {
+ this.nodeLists[tId] && this.nodeLists[tId].loadWidget.destroy();
+ this.nodeLists[tId] && (this.nodeLists[tId].loadWidget = null);
+ delete this.nodeLists[tId];
+ if (BI.size(this.nodeLists) === 0) {
+ this.clear();
+ }
+ },
+
+ clear: function () {
+ var self = this;
+ BI.each(this.nodeLists, function (tId) {
+ self.removeNodeList(tId);
+ });
+ this.nodeLists = {};
+ }
+ });
+
+})();
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/component/allvaluechooser/__test__/combo.allvaluechooser.test.js b/src/main/resources/com/fr/fineui/component/allvaluechooser/__test__/combo.allvaluechooser.test.js
new file mode 100644
index 0000000..6c4030c
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/allvaluechooser/__test__/combo.allvaluechooser.test.js
@@ -0,0 +1,67 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/3/4
+ */
+
+describe("all_value_chooser_combo", function () {
+
+ var items = BI.map(BI.makeArray(1000, null), function(idx, v) {
+ return {
+ text: idx,
+ value: idx,
+ title: idx
+ };
+ });
+
+ var itemSelectorGetter = function (array) {
+ return BI.map(array, function (idx, num) {
+ return ".bi-multi-select-popup-view .bi-loader .bi-button-group .bi-multi-select-item:nth-child(" + num + ")";
+ });
+ };
+
+ var searchItemSelectorGetter = function (array) {
+ return BI.map(array, function (idx, num) {
+ return ".bi-multi-select-search-pane .bi-loader .bi-button-group .bi-multi-select-item:nth-child(" + num + ")";
+ });
+ };
+
+ /**
+ * test_author_windy
+ **/
+ it("setValue", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.all_value_chooser_combo",
+ width: 220,
+ itemsCreator: function (op, callback) {
+ callback(items);
+ }
+ });
+ widget.setValue([1, 2]);
+ expect(widget.getValue()).to.deep.equal([1, 2]);
+ widget.destroy();
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("点选选值", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.all_value_chooser_combo",
+ width: 220,
+ itemsCreator: function (op, callback) {
+ callback(items);
+ }
+ });
+ widget.element.find(".bi-multi-select-trigger").click();
+ // 为什么要delay 300呢,因为按钮有debounce
+ BI.delay(function () {
+ // 点选1、2、3
+ BI.each(itemSelectorGetter([1,2,3]), function (idx, selector) {
+ widget.element.find(selector).click();
+ });
+ expect(widget.getValue()).to.deep.equal([0, 1, 2]);
+ done();
+ }, 300);
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/component/allvaluechooser/abstract.allvaluechooser.js b/src/main/resources/com/fr/fineui/component/allvaluechooser/abstract.allvaluechooser.js
new file mode 100644
index 0000000..1f30e8d
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/allvaluechooser/abstract.allvaluechooser.js
@@ -0,0 +1,101 @@
+/**
+ * 简单的复选下拉框控件, 适用于数据量少的情况, 与valuechooser的区别是allvaluechooser setValue和getValue返回的是所有值
+ * 封装了字段处理逻辑
+ *
+ * Created by GUY on 2015/10/29.
+ * @class BI.AbstractAllValueChooser
+ * @extends BI.Widget
+ */
+BI.AbstractAllValueChooser = BI.inherit(BI.Widget, {
+
+ _const: {
+ perPage: 100
+ },
+
+ _defaultConfig: function () {
+ return BI.extend(BI.AbstractAllValueChooser.superclass._defaultConfig.apply(this, arguments), {
+ width: 200,
+ height: 30,
+ items: null,
+ itemsCreator: BI.emptyFn,
+ cache: true
+ });
+ },
+
+ _valueFormatter: function (v) {
+ var text = v;
+ if (BI.isNotNull(this.items)) {
+ BI.some(this.items, function (i, item) {
+ // 把value都换成字符串
+ // 需要考虑到value也可能是数字
+ if (item.value === v || item.value + "" === v) {
+ text = item.text;
+ return true;
+ }
+ });
+ }
+ return text;
+ },
+
+ _itemsCreator: function (options, callback) {
+ var self = this, o = this.options;
+ if (!o.cache || !this.items) {
+ o.itemsCreator({}, function (items) {
+ self.items = items;
+ call(items);
+ });
+ } else {
+ call(this.items);
+ }
+ function call (items) {
+ var keywords = (options.keywords || []).slice();
+ if (options.keyword) {
+ keywords.push(options.keyword);
+ }
+ var resultItems = items;
+ if(BI.isNotEmptyArray(keywords)) {
+ resultItems = [];
+ BI.each(keywords, function (i, kw) {
+ var search = BI.Func.getSearchResult(items, kw);
+ resultItems = resultItems.concat(search.match).concat(search.find);
+ });
+ resultItems = BI.uniq(resultItems);
+ }
+ if (options.selectedValues) {// 过滤
+ var filter = BI.makeObject(options.selectedValues, true);
+ resultItems = BI.filter(resultItems, function (i, ob) {
+ return !filter[ob.value];
+ });
+ }
+ if (options.type === BI.MultiSelectCombo.REQ_GET_ALL_DATA) {
+ callback({
+ items: resultItems
+ });
+ return;
+ }
+ if (options.type === BI.MultiSelectCombo.REQ_GET_DATA_LENGTH) {
+ callback({count: resultItems.length});
+ return;
+ }
+ callback({
+ items: resultItems,
+ hasNext: false
+ });
+ }
+ },
+
+ _assertValue: function (v) {
+ v = v || {};
+ var value = v;
+ if (BI.isNotNull(this.items)) {
+ var isAllSelect = BI.difference(BI.map(this.items, "value"), v.value).length === 0;
+ if (isAllSelect) {
+ value = {
+ type: BI.Selection.All,
+ value: [],
+ };
+ }
+ }
+ return value;
+ },
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/component/allvaluechooser/combo.allvaluechooser.js b/src/main/resources/com/fr/fineui/component/allvaluechooser/combo.allvaluechooser.js
new file mode 100644
index 0000000..ac0f03b
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/allvaluechooser/combo.allvaluechooser.js
@@ -0,0 +1,76 @@
+/**
+ * 简单的复选下拉框控件, 适用于数据量少的情况, 与valuechooser的区别是allvaluechooser setValue和getValue返回的是所有值
+ * 封装了字段处理逻辑
+ *
+ * Created by GUY on 2015/10/29.
+ * @class BI.AllValueChooserCombo
+ * @extends BI.AbstractAllValueChooser
+ */
+BI.AllValueChooserCombo = BI.inherit(BI.AbstractAllValueChooser, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.AllValueChooserCombo.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-all-value-chooser-combo",
+ width: 200,
+ height: 24,
+ items: null,
+ itemsCreator: BI.emptyFn,
+ cache: true
+ });
+ },
+
+ _init: function () {
+ BI.AllValueChooserCombo.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ if (BI.isNotNull(o.items)) {
+ this.items = o.items;
+ }
+ this.combo = BI.createWidget({
+ type: "bi.multi_select_combo",
+ text: o.text,
+ element: this,
+ itemsCreator: BI.bind(this._itemsCreator, this),
+ valueFormatter: BI.bind(this._valueFormatter, this),
+ width: o.width,
+ height: o.height,
+ value: this._assertValue({
+ type: BI.Selection.Multi,
+ value: o.value || []
+ })
+ });
+
+ this.combo.on(BI.MultiSelectCombo.EVENT_CONFIRM, function () {
+ self.fireEvent(BI.AllValueChooserCombo.EVENT_CONFIRM);
+ });
+ },
+
+ setValue: function (v) {
+ this.combo.setValue(this._assertValue({
+ type: BI.Selection.Multi,
+ value: v || []
+ }));
+ },
+
+ getValue: function () {
+ return this.getAllValue();
+ },
+
+ getAllValue: function () {
+ var val = this.combo.getValue() || {};
+ if (val.type === BI.Selection.Multi) {
+ return val.value || [];
+ }
+
+ return BI.difference(BI.map(this.items, "value"), val.value || []);
+ },
+
+ populate: function (items) {
+ // 直接用combo的populate不会作用到AbstractValueChooser上
+ if (BI.isNotNull(items)) {
+ this.items = items;
+ }
+ this.combo.populate();
+ }
+});
+BI.AllValueChooserCombo.EVENT_CONFIRM = "EVENT_CONFIRM";
+BI.shortcut("bi.all_value_chooser_combo", BI.AllValueChooserCombo);
diff --git a/src/main/resources/com/fr/fineui/component/allvaluechooser/pane.allvaluechooser.js b/src/main/resources/com/fr/fineui/component/allvaluechooser/pane.allvaluechooser.js
new file mode 100644
index 0000000..93ad36c
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/allvaluechooser/pane.allvaluechooser.js
@@ -0,0 +1,68 @@
+/**
+ * 简单的复选面板, 适用于数据量少的情况, 与valuechooser的区别是allvaluechooser setValue和getValue返回的是所有值
+ * 封装了字段处理逻辑
+ *
+ * Created by GUY on 2015/10/29.
+ * @class BI.AllValueChooserPane
+ * @extends BI.AbstractAllValueChooser
+ */
+BI.AllValueChooserPane = BI.inherit(BI.AbstractAllValueChooser, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.AllValueChooserPane.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-all-value-chooser-pane",
+ width: 200,
+ height: 30,
+ items: null,
+ itemsCreator: BI.emptyFn,
+ cache: true
+ });
+ },
+
+ _init: function () {
+ BI.AllValueChooserPane.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.list = BI.createWidget({
+ type: "bi.multi_select_list",
+ element: this,
+ itemsCreator: BI.bind(this._itemsCreator, this),
+ valueFormatter: BI.bind(this._valueFormatter, this),
+ width: o.width,
+ height: o.height
+ });
+
+ this.list.on(BI.MultiSelectList.EVENT_CHANGE, function () {
+ self.fireEvent(BI.AllValueChooserPane.EVENT_CHANGE);
+ });
+
+ if (BI.isNotNull(o.items)) {
+ this.items = o.items;
+ this.list.populate();
+ }
+ },
+
+ setValue: function (v) {
+ this.list.setValue({
+ type: BI.Selection.Multi,
+ value: v || []
+ });
+ },
+
+ getValue: function () {
+ var val = this.list.getValue() || {};
+ if (val.type === BI.Selection.All) {
+ return val.assist;
+ }
+ return val.value || [];
+ },
+
+ populate: function (items) {
+ // 直接用combo的populate不会作用到AbstractValueChooser上
+ if (BI.isNotNull(items)) {
+ this.items = items;
+ }
+ this.list.populate();
+ }
+});
+BI.AllValueChooserPane.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.all_value_chooser_pane", BI.AllValueChooserPane);
diff --git a/src/main/resources/com/fr/fineui/component/allvaluemultitextvaluecombo/__test__/allvalue.multitextvalue.combo.test.js b/src/main/resources/com/fr/fineui/component/allvaluemultitextvaluecombo/__test__/allvalue.multitextvalue.combo.test.js
new file mode 100644
index 0000000..f4e86de
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/allvaluemultitextvaluecombo/__test__/allvalue.multitextvalue.combo.test.js
@@ -0,0 +1,30 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/3/4
+ */
+
+describe("all_value_multi_text_value_combo", function () {
+
+ var items = BI.map(BI.makeArray(1000, null), function(idx, v) {
+ return {
+ text: idx,
+ value: idx,
+ title: idx
+ };
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("setValue", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.all_value_multi_text_value_combo",
+ width: 220,
+ items: items
+ });
+ widget.setValue([1, 2]);
+ expect(widget.getValue()).to.deep.equal([1, 2]);
+ widget.destroy();
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/component/allvaluemultitextvaluecombo/allvalue.multitextvalue.combo.js b/src/main/resources/com/fr/fineui/component/allvaluemultitextvaluecombo/allvalue.multitextvalue.combo.js
new file mode 100644
index 0000000..213d56f
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/allvaluemultitextvaluecombo/allvalue.multitextvalue.combo.js
@@ -0,0 +1,65 @@
+BI.AllValueMultiTextValueCombo = BI.inherit(BI.Widget, {
+
+ props: {
+ baseCls: "bi-all-value-multi-text-value-combo",
+ width: 200,
+ height: 24,
+ items: []
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ var value = this._digestValue(o.value);
+ return {
+ type: "bi.search_multi_text_value_combo",
+ text: o.text,
+ height: o.height,
+ items: o.items,
+ value: value,
+ numOfPage: 100,
+ valueFormatter: o.valueFormatter,
+ warningTitle: o.warningTitle,
+ listeners: [{
+ eventName: BI.SearchMultiTextValueCombo.EVENT_CONFIRM,
+ action: function () {
+ self.fireEvent(BI.AllValueMultiTextValueCombo.EVENT_CONFIRM);
+ }
+ }],
+ ref: function () {
+ self.combo = this;
+ }
+ };
+ },
+
+ setValue: function (v) {
+ var value = this._digestValue(v);
+ this.combo.setValue(value);
+ },
+
+ getValue: function () {
+ var obj = this.combo.getValue() || {};
+ obj.value = obj.value || [];
+ if(obj.type === BI.Selection.All) {
+ var values = [];
+ BI.each(this.options.items, function (idx, item) {
+ !BI.contains(obj.value, item.value) && values.push(item.value);
+ });
+ return values;
+ }
+ return obj.value || [];
+ },
+
+ populate: function (items) {
+ this.options.items = items;
+ this.combo.populate.apply(this.combo, arguments);
+ },
+
+ _digestValue: function (v) {
+ return {
+ type: BI.Selection.Multi,
+ value: v || []
+ };
+ }
+});
+BI.AllValueMultiTextValueCombo.EVENT_CONFIRM = "EVENT_CONFIRM";
+BI.shortcut("bi.all_value_multi_text_value_combo", BI.AllValueMultiTextValueCombo);
diff --git a/src/main/resources/com/fr/fineui/component/treevaluechooser/__test__/combo.listtreevaluechooser.test.js b/src/main/resources/com/fr/fineui/component/treevaluechooser/__test__/combo.listtreevaluechooser.test.js
new file mode 100644
index 0000000..afd9576
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/treevaluechooser/__test__/combo.listtreevaluechooser.test.js
@@ -0,0 +1,95 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2019/9/25
+ */
+// describe("list_tree_value_chooser_insert_combo", function () {
+//
+// var items = [{pId: "0", id: "0_0", text: "中国", value: "", open: true}, {
+// pId: "0_0",
+// id: "0_0_0",
+// text: "安徽省( 共1个 )",
+// value: "安徽省",
+// open: true
+// }, {pId: "0_0_0", id: "0_0_0_0", text: "芜湖市", value: "芜湖市", open: true}, {
+// pId: "0_0",
+// id: "0_0_1",
+// text: "北京市( 共6个 )",
+// value: "北京市",
+// open: true
+// }, {pId: "0_0_1", id: "0_0_1_0", text: "北京市区", value: "北京市区", open: true}, {
+// pId: "0_0_1",
+// id: "0_0_1_1",
+// text: "朝阳区",
+// value: "朝阳区",
+// open: true
+// }, {pId: "0_0_1", id: "0_0_1_2", text: "东城区", value: "东城区", open: true}, {
+// pId: "0_0_1",
+// id: "0_0_1_3",
+// text: "海淀区4内",
+// value: "海淀区4内",
+// open: true
+// }, {pId: "0_0_1", id: "0_0_1_4", text: "海淀区4外", value: "海淀区4外", open: true}, {
+// pId: "0_0_1",
+// id: "0_0_1_5",
+// text: "石景山区",
+// value: "石景山区",
+// open: true
+// }, {pId: "0_0", id: "0_0_2", text: "福建省( 共2个 )", value: "福建省", open: true}, {
+// pId: "0_0_2",
+// id: "0_0_2_0",
+// text: "莆田市",
+// value: "莆田市",
+// open: true
+// }, {pId: "0_0_2", id: "0_0_2_1", text: "泉州市", value: "泉州市", open: true}, {
+// pId: "0_0",
+// id: "0_0_3",
+// text: "甘肃省( 共1个 )",
+// value: "甘肃省",
+// open: true
+// }, {pId: "0_0_3", id: "0_0_3_0", text: "兰州市", value: "兰州市", open: true}, {
+// pId: "0_0",
+// id: "0_0_4",
+// text: "广东省( 共5个 )",
+// value: "广东省",
+// open: true
+// }, {pId: "0_0_4", id: "0_0_4_0", text: "东莞市", value: "东莞市", open: true}, {
+// pId: "0_0_4",
+// id: "0_0_4_1",
+// text: "广州市",
+// value: "广州市",
+// open: true
+// }];
+//
+// /**
+// * test_author_windy
+// **/
+// it("setValue", function () {
+// var widget = BI.Test.createWidget({
+// type: "bi.list_tree_value_chooser_insert_combo",
+// width: 220,
+// itemsCreator: function (op, callback) {
+// callback(items);
+// }
+// });
+// widget.setValue([["中国", "北京市", "朝阳区"]]);
+// expect(widget.getValue()).to.deep.equal([["中国", "北京市", "朝阳区"]]);
+// widget.destroy();
+// });
+//
+// /**
+// * test_author_windy
+// **/
+// it("getValue", function () {
+// var widget = BI.Test.createWidget({
+// type: "bi.list_tree_value_chooser_insert_combo",
+// width: 220,
+// itemsCreator: function (op, callback) {
+// callback(items);
+// },
+// value: [["中国", "北京市", "朝阳区"]]
+// });
+// expect(widget.getValue()).to.deep.equal([["中国", "北京市", "朝阳区"]]);
+// widget.destroy();
+// });
+// });
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/component/treevaluechooser/__test__/combo.treevaluechooser.insert.test.js b/src/main/resources/com/fr/fineui/component/treevaluechooser/__test__/combo.treevaluechooser.insert.test.js
new file mode 100644
index 0000000..ecfd2f5
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/treevaluechooser/__test__/combo.treevaluechooser.insert.test.js
@@ -0,0 +1,132 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2019/9/25
+ */
+
+// describe("tree_value_chooser_insert_combo", function () {
+//
+// var items = [{pId: "0", id: "0_0", text: "中国", value: "", open: true}, {
+// pId: "0_0",
+// id: "0_0_0",
+// text: "安徽省( 共1个 )",
+// value: "安徽省",
+// open: true
+// }, {pId: "0_0_0", id: "0_0_0_0", text: "芜湖市", value: "芜湖市", open: true}, {
+// pId: "0_0",
+// id: "0_0_1",
+// text: "北京市( 共6个 )",
+// value: "北京市",
+// open: true
+// }, {pId: "0_0_1", id: "0_0_1_0", text: "北京市区", value: "北京市区", open: true}, {
+// pId: "0_0_1",
+// id: "0_0_1_1",
+// text: "朝阳区",
+// value: "朝阳区",
+// open: true
+// }, {pId: "0_0_1", id: "0_0_1_2", text: "东城区", value: "东城区", open: true}, {
+// pId: "0_0_1",
+// id: "0_0_1_3",
+// text: "海淀区4内",
+// value: "海淀区4内",
+// open: true
+// }, {pId: "0_0_1", id: "0_0_1_4", text: "海淀区4外", value: "海淀区4外", open: true}, {
+// pId: "0_0_1",
+// id: "0_0_1_5",
+// text: "石景山区",
+// value: "石景山区",
+// open: true
+// }, {pId: "0_0", id: "0_0_2", text: "福建省( 共2个 )", value: "福建省", open: true}, {
+// pId: "0_0_2",
+// id: "0_0_2_0",
+// text: "莆田市",
+// value: "莆田市",
+// open: true
+// }, {pId: "0_0_2", id: "0_0_2_1", text: "泉州市", value: "泉州市", open: true}, {
+// pId: "0_0",
+// id: "0_0_3",
+// text: "甘肃省( 共1个 )",
+// value: "甘肃省",
+// open: true
+// }, {pId: "0_0_3", id: "0_0_3_0", text: "兰州市", value: "兰州市", open: true}, {
+// pId: "0_0",
+// id: "0_0_4",
+// text: "广东省( 共5个 )",
+// value: "广东省",
+// open: true
+// }, {pId: "0_0_4", id: "0_0_4_0", text: "东莞市", value: "东莞市", open: true}, {
+// pId: "0_0_4",
+// id: "0_0_4_1",
+// text: "广州市",
+// value: "广州市",
+// open: true
+// }];
+//
+// var itemSelectorGetter = function (array) {
+// return BI.map(array, function (idx, num) {
+// return ".bi-multi-select-popup-view .bi-loader .bi-button-group .bi-multi-select-item:nth-child(" + num + ")";
+// });
+// };
+//
+// var searchItemSelectorGetter = function (array) {
+// return BI.map(array, function (idx, num) {
+// return ".bi-multi-select-search-pane .bi-loader .bi-button-group .bi-multi-select-item:nth-child(" + num + ")";
+// });
+// };
+//
+// /**
+// * test_author_windy
+// **/
+// it("setValue", function () {
+// var widget = BI.Test.createWidget({
+// type: "bi.tree_value_chooser_insert_combo",
+// width: 220,
+// itemsCreator: function (op, callback) {
+// callback(items);
+// }
+// });
+// widget.setValue({
+// "中国": {
+// "北京市": {
+// "朝阳区": {}
+// }
+// }
+// });
+// expect(widget.getValue()).to.deep.equal({
+// "中国": {
+// "北京市": {
+// "朝阳区": {}
+// }
+// }
+// });
+// widget.destroy();
+// });
+//
+// /**
+// * test_author_windy
+// **/
+// it("getValue", function () {
+// var widget = BI.Test.createWidget({
+// type: "bi.tree_value_chooser_insert_combo",
+// width: 220,
+// itemsCreator: function (op, callback) {
+// callback(items);
+// },
+// value: {
+// "中国": {
+// "北京市": {
+// "朝阳区": {}
+// }
+// }
+// }
+// });
+// expect(widget.getValue()).to.deep.equal({
+// "中国": {
+// "北京市": {
+// "朝阳区": {}
+// }
+// }
+// });
+// widget.destroy();
+// });
+// });
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/component/treevaluechooser/__test__/combo.treevaluechooser.test.js b/src/main/resources/com/fr/fineui/component/treevaluechooser/__test__/combo.treevaluechooser.test.js
new file mode 100644
index 0000000..c1941c4
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/treevaluechooser/__test__/combo.treevaluechooser.test.js
@@ -0,0 +1,132 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2019/9/25
+ */
+
+// describe("tree_value_chooser_combo", function () {
+//
+// var items = [{pId: "0", id: "0_0", text: "中国", value: "", open: true}, {
+// pId: "0_0",
+// id: "0_0_0",
+// text: "安徽省( 共1个 )",
+// value: "安徽省",
+// open: true
+// }, {pId: "0_0_0", id: "0_0_0_0", text: "芜湖市", value: "芜湖市", open: true}, {
+// pId: "0_0",
+// id: "0_0_1",
+// text: "北京市( 共6个 )",
+// value: "北京市",
+// open: true
+// }, {pId: "0_0_1", id: "0_0_1_0", text: "北京市区", value: "北京市区", open: true}, {
+// pId: "0_0_1",
+// id: "0_0_1_1",
+// text: "朝阳区",
+// value: "朝阳区",
+// open: true
+// }, {pId: "0_0_1", id: "0_0_1_2", text: "东城区", value: "东城区", open: true}, {
+// pId: "0_0_1",
+// id: "0_0_1_3",
+// text: "海淀区4内",
+// value: "海淀区4内",
+// open: true
+// }, {pId: "0_0_1", id: "0_0_1_4", text: "海淀区4外", value: "海淀区4外", open: true}, {
+// pId: "0_0_1",
+// id: "0_0_1_5",
+// text: "石景山区",
+// value: "石景山区",
+// open: true
+// }, {pId: "0_0", id: "0_0_2", text: "福建省( 共2个 )", value: "福建省", open: true}, {
+// pId: "0_0_2",
+// id: "0_0_2_0",
+// text: "莆田市",
+// value: "莆田市",
+// open: true
+// }, {pId: "0_0_2", id: "0_0_2_1", text: "泉州市", value: "泉州市", open: true}, {
+// pId: "0_0",
+// id: "0_0_3",
+// text: "甘肃省( 共1个 )",
+// value: "甘肃省",
+// open: true
+// }, {pId: "0_0_3", id: "0_0_3_0", text: "兰州市", value: "兰州市", open: true}, {
+// pId: "0_0",
+// id: "0_0_4",
+// text: "广东省( 共5个 )",
+// value: "广东省",
+// open: true
+// }, {pId: "0_0_4", id: "0_0_4_0", text: "东莞市", value: "东莞市", open: true}, {
+// pId: "0_0_4",
+// id: "0_0_4_1",
+// text: "广州市",
+// value: "广州市",
+// open: true
+// }];
+//
+// var itemSelectorGetter = function (array) {
+// return BI.map(array, function (idx, num) {
+// return ".bi-multi-select-popup-view .bi-loader .bi-button-group .bi-multi-select-item:nth-child(" + num + ")";
+// });
+// };
+//
+// var searchItemSelectorGetter = function (array) {
+// return BI.map(array, function (idx, num) {
+// return ".bi-multi-select-search-pane .bi-loader .bi-button-group .bi-multi-select-item:nth-child(" + num + ")";
+// });
+// };
+//
+// /**
+// * test_author_windy
+// **/
+// it("setValue", function () {
+// var widget = BI.Test.createWidget({
+// type: "bi.tree_value_chooser_combo",
+// width: 220,
+// itemsCreator: function (op, callback) {
+// callback(items);
+// }
+// });
+// widget.setValue({
+// "中国": {
+// "北京市": {
+// "朝阳区": {}
+// }
+// }
+// });
+// expect(widget.getValue()).to.deep.equal({
+// "中国": {
+// "北京市": {
+// "朝阳区": {}
+// }
+// }
+// });
+// widget.destroy();
+// });
+//
+// /**
+// * test_author_windy
+// **/
+// it("getValue", function () {
+// var widget = BI.Test.createWidget({
+// type: "bi.tree_value_chooser_combo",
+// width: 220,
+// itemsCreator: function (op, callback) {
+// callback(items);
+// },
+// value: {
+// "中国": {
+// "北京市": {
+// "朝阳区": {}
+// }
+// }
+// }
+// });
+// expect(widget.getValue()).to.deep.equal({
+// "中国": {
+// "北京市": {
+// "朝阳区": {}
+// }
+// }
+// });
+// widget.destroy();
+// });
+// });
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/component/treevaluechooser/__test__/pane.treevaluechooser.test.js b/src/main/resources/com/fr/fineui/component/treevaluechooser/__test__/pane.treevaluechooser.test.js
new file mode 100644
index 0000000..47775e4
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/treevaluechooser/__test__/pane.treevaluechooser.test.js
@@ -0,0 +1,140 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2019/9/25
+ */
+
+// describe("tree_value_chooser_pane", function () {
+//
+// var items = [{pId: "0", id: "0_0", text: "中国", value: "", open: true}, {
+// pId: "0_0",
+// id: "0_0_0",
+// text: "安徽省( 共1个 )",
+// value: "安徽省",
+// open: true
+// }, {pId: "0_0_0", id: "0_0_0_0", text: "芜湖市", value: "芜湖市", open: true}, {
+// pId: "0_0",
+// id: "0_0_1",
+// text: "北京市( 共6个 )",
+// value: "北京市",
+// open: true
+// }, {pId: "0_0_1", id: "0_0_1_0", text: "北京市区", value: "北京市区", open: true}, {
+// pId: "0_0_1",
+// id: "0_0_1_1",
+// text: "朝阳区",
+// value: "朝阳区",
+// open: true
+// }, {pId: "0_0_1", id: "0_0_1_2", text: "东城区", value: "东城区", open: true}, {
+// pId: "0_0_1",
+// id: "0_0_1_3",
+// text: "海淀区4内",
+// value: "海淀区4内",
+// open: true
+// }, {pId: "0_0_1", id: "0_0_1_4", text: "海淀区4外", value: "海淀区4外", open: true}, {
+// pId: "0_0_1",
+// id: "0_0_1_5",
+// text: "石景山区",
+// value: "石景山区",
+// open: true
+// }, {pId: "0_0", id: "0_0_2", text: "福建省( 共2个 )", value: "福建省", open: true}, {
+// pId: "0_0_2",
+// id: "0_0_2_0",
+// text: "莆田市",
+// value: "莆田市",
+// open: true
+// }, {pId: "0_0_2", id: "0_0_2_1", text: "泉州市", value: "泉州市", open: true}, {
+// pId: "0_0",
+// id: "0_0_3",
+// text: "甘肃省( 共1个 )",
+// value: "甘肃省",
+// open: true
+// }, {pId: "0_0_3", id: "0_0_3_0", text: "兰州市", value: "兰州市", open: true}, {
+// pId: "0_0",
+// id: "0_0_4",
+// text: "广东省( 共5个 )",
+// value: "广东省",
+// open: true
+// }, {pId: "0_0_4", id: "0_0_4_0", text: "东莞市", value: "东莞市", open: true}, {
+// pId: "0_0_4",
+// id: "0_0_4_1",
+// text: "广州市",
+// value: "广州市",
+// open: true
+// }];
+//
+// var itemSelectorGetter = function (array) {
+// return BI.map(array, function (idx, num) {
+// return ".bi-multi-select-popup-view .bi-loader .bi-button-group .bi-multi-select-item:nth-child(" + num + ")";
+// });
+// };
+//
+// var searchItemSelectorGetter = function (array) {
+// return BI.map(array, function (idx, num) {
+// return ".bi-multi-select-search-pane .bi-loader .bi-button-group .bi-multi-select-item:nth-child(" + num + ")";
+// });
+// };
+//
+// /**
+// * test_author_windy
+// **/
+// it("setValue", function () {
+// var widget = BI.Test.createWidget({
+// type: "bi.tree_value_chooser_pane",
+// width: 220,
+// items: items,
+// // itemsCreator: function (op, callback) {
+// // callback(items);
+// // }
+// });
+// widget.setSelectedValue({
+// "中国": {
+// "北京市": {
+// "朝阳区": {}
+// }
+// }
+// });
+// expect(widget.getValue()).to.deep.equal({
+// "中国": {
+// "北京市": {
+// "朝阳区": {}
+// }
+// }
+// });
+// widget.destroy();
+// });
+//
+// /**
+// * test_author_windy
+// **/
+// it("getValue", function () {
+// var widget = BI.Test.createWidget({
+// type: "bi.tree_value_chooser_pane",
+// width: 220,
+// itemsCreator: function (op, callback) {
+// callback(items);
+// },
+// value: {
+// "中国": {
+// "北京市": {
+// "朝阳区": {}
+// }
+// }
+// }
+// });
+// widget.setSelectedValue({
+// "中国": {
+// "北京市": {
+// "朝阳区": {}
+// }
+// }
+// });
+// expect(widget.getValue()).to.deep.equal({
+// "中国": {
+// "北京市": {
+// "朝阳区": {}
+// }
+// }
+// });
+// widget.destroy();
+// });
+// });
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/component/treevaluechooser/abstract.treevaluechooser.js b/src/main/resources/com/fr/fineui/component/treevaluechooser/abstract.treevaluechooser.js
new file mode 100644
index 0000000..cf00c98
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/treevaluechooser/abstract.treevaluechooser.js
@@ -0,0 +1,854 @@
+BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, {
+
+ _const: {
+ perPage: 100
+ },
+
+ _defaultConfig: function () {
+ return BI.extend(BI.AbstractTreeValueChooser.superclass._defaultConfig.apply(this, arguments), {
+ items: null,
+ itemsCreator: BI.emptyFn,
+ open: false
+ });
+ },
+
+ _valueFormatter: function (v) {
+ var text = v;
+ if (BI.isNotNull(this.items)) {
+ BI.some(this.items, function (i, item) {
+ if (item.value === v || item.value + "" === v) {
+ text = item.text;
+ return true;
+ }
+ });
+ }
+ return text;
+ },
+
+ _initData: function (items) {
+ this.items = items;
+ var nodes = BI.Tree.treeFormat(items);
+ this.tree = new BI.Tree();
+ this.tree.initTree(nodes);
+ },
+
+ _itemsCreator: function (options, callback) {
+ var self = this, o = this.options;
+ if (!this.items) {
+ o.itemsCreator({}, function (items) {
+ self._initData(items);
+ call();
+ });
+ } else {
+ call();
+ }
+
+ function call() {
+ switch (options.type) {
+ case BI.TreeView.REQ_TYPE_INIT_DATA:
+ self._reqInitTreeNode(options, callback);
+ break;
+ case BI.TreeView.REQ_TYPE_ADJUST_DATA:
+ self._reqAdjustTreeNode(options, callback);
+ break;
+ case BI.TreeView.REQ_TYPE_SELECT_DATA:
+ self._reqSelectedTreeNode(options, callback);
+ break;
+ case BI.TreeView.REQ_TYPE_GET_SELECTED_DATA:
+ self._reqDisplayTreeNode(options, callback);
+ break;
+ default :
+ self._reqTreeNode(options, callback);
+ break;
+ }
+ }
+ },
+
+ _reqDisplayTreeNode: function (op, callback) {
+ var self = this;
+ var result = [];
+ var selectedValues = op.selectedValues;
+
+ if (selectedValues == null || BI.isEmpty(selectedValues)) {
+ callback({});
+ return;
+ }
+
+ doCheck([], this.tree.getRoot(), selectedValues);
+
+ callback({
+ items: result
+ });
+
+ function doCheck(parentValues, node, selected) {
+ if (selected == null || BI.isEmpty(selected)) {
+ BI.each(node.getChildren(), function (i, child) {
+ var newParents = BI.clone(parentValues);
+ newParents.push(child.value);
+ var llen = self._getChildCount(newParents);
+ createOneJson(child, node.id, llen);
+ doCheck(newParents, child, {});
+ });
+ return;
+ }
+ BI.each(selected, function (k) {
+ var node = self._getTreeNode(parentValues, k);
+ // 找不到就是新增值
+ if(BI.isNull(node)) {
+ createOneJson({
+ id: BI.UUID(),
+ text: k,
+ value: k
+ }, BI.UUID(), 0);
+ } else {
+ var newParents = BI.clone(parentValues);
+ newParents.push(node.value);
+ createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents));
+ doCheck(newParents, node, selected[k]);
+ }
+ });
+ }
+
+ function getCount(jo, parentValues) {
+ if (jo == null) {
+ return 0;
+ }
+ if (BI.isEmpty(jo)) {
+ return self._getChildCount(parentValues);
+ }
+
+ return BI.size(jo);
+ }
+
+ function createOneJson(node, pId, llen) {
+ result.push({
+ id: node.id,
+ pId: pId,
+ text: node.text + (llen > 0 ? ("(" + BI.i18nText("BI-Basic_Altogether") + llen + BI.i18nText("BI-Basic_Count") + ")") : ""),
+ value: node.value,
+ open: true
+ });
+ }
+ },
+
+ _reqSelectedTreeNode: function (op, callback) {
+ var self = this;
+ var selectedValues = BI.deepClone(op.selectedValues);
+ var notSelectedValue = op.notSelectedValue || {};
+ var keyword = op.keyword || "";
+ var parentValues = op.parentValues || [];
+
+ if (selectedValues == null || BI.isEmpty(selectedValues)) {
+ callback({});
+ return;
+ }
+
+ dealWithSelectedValues(selectedValues);
+ callback(selectedValues);
+
+
+ function dealWithSelectedValues(selectedValues) {
+ var p = parentValues.concat(notSelectedValue);
+ // 存储的值中存在这个值就把它删掉
+ // 例如选中了中国-江苏-南京, 取消中国或江苏或南京
+ // p长度不大于selectedValues的情况才可能找到,这样可以直接删除selectedValues的节点
+ if (canFindKey(selectedValues, p)) {
+ // 如果搜索的值在父亲链中
+ if (isSearchValueInParent(p)) {
+ // 例如选中了 中国-江苏, 搜索江苏, 取消江苏(干掉了江苏)
+ self._deleteNode(selectedValues, p);
+ } else {
+ var searched = [];
+ // 要找到所有以notSelectedValue为叶子节点的链路
+ var find = search(parentValues, notSelectedValue, [], searched);
+ if (find && BI.isNotEmptyArray(searched)) {
+ BI.each(searched, function (i, arr) {
+ var node = self._getNode(selectedValues, arr);
+ if (node) {
+ // 例如选中了 中国-江苏, 搜索江苏, 取消中国(实际上只想删除中国-江苏,因为搜的是江苏)
+ // 例如选中了 中国-江苏-南京,搜索南京,取消中国(实际上只想删除中国-江苏-南京,因为搜的是南京)
+ self._deleteNode(selectedValues, arr);
+ } else {
+ // 例如选中了 中国-江苏,搜索南京,取消中国(实际上只想删除中国-江苏-南京,因为搜的是南京)
+ expandSelectedValue(selectedValues, arr, BI.last(arr));
+ }
+ });
+ }
+ }
+ }
+
+ // 存储的值中不存在这个值,但父亲节点是全选的情况
+ // 例如选中了中国-江苏,取消南京
+ // important 选中了中国-江苏,取消了江苏,但是搜索的是南京
+ if (isChild(selectedValues, p)) {
+ var result = [], find = false;
+ // 如果parentValues中有匹配的值,说明搜索结果不在当前值下
+ if (isSearchValueInParent(p)) {
+ find = true;
+ } else {
+ // 从当前值开始搜
+ find = search(parentValues, notSelectedValue, result);
+ p = parentValues;
+ }
+
+ if (find === true) {
+ // 去掉点击的节点之后的结果集
+ expandSelectedValue(selectedValues, p, notSelectedValue);
+ // 添加去掉搜索的结果集
+ if (result.length > 0) {
+ BI.each(result, function (i, strs) {
+ self._buildTree(selectedValues, strs);
+ });
+ }
+ }
+ }
+
+ }
+
+ function expandSelectedValue(selectedValues, parents, notSelectedValue) {
+ var next = selectedValues;
+ var childrenCount = [];
+ var path = [];
+ // 去掉点击的节点之后的结果集
+ BI.some(parents, function (i, v) {
+ var t = next[v];
+ if (t == null) {
+ if (i === 0) {
+ return true;
+ }
+ if (BI.isEmpty(next)) {
+ var split = parents.slice(0, i);
+ var expanded = self._getChildren(split);
+ path.push(split);
+ childrenCount.push(expanded.length);
+ // 如果只有一个值且取消的就是这个值
+ if (i === parents.length - 1 && expanded.length === 1 && expanded[0].value === notSelectedValue) {
+ for (var j = childrenCount.length - 1; j >= 0; j--) {
+ if (childrenCount[j] === 1) {
+ self._deleteNode(selectedValues, path[j]);
+ } else {
+ break;
+ }
+ }
+ } else {
+ BI.each(expanded, function (m, child) {
+ if (i === parents.length - 1 && child.value === notSelectedValue) {
+ return true;
+ }
+ next[child.value] = {};
+ });
+ }
+ next = next[v];
+ } else {
+ return true;
+ // next = {};
+ // next[v] = {};
+ }
+ } else {
+ next = t;
+ }
+ });
+ }
+
+ function search(parents, current, result, searched) {
+ var newParents = BI.clone(parents);
+ newParents.push(current);
+ if (self._isMatch(parents, current, keyword)) {
+ searched && searched.push(newParents);
+ return true;
+ }
+
+ var children = self._getChildren(newParents);
+
+ var notSearch = [];
+ var can = false;
+
+ BI.each(children, function (i, child) {
+ if (search(newParents, child.value, result, searched)) {
+ can = true;
+ } else {
+ notSearch.push(child.value);
+ }
+ });
+ if (can === true) {
+ BI.each(notSearch, function (i, v) {
+ var next = BI.clone(newParents);
+ next.push(v);
+ result.push(next);
+ });
+ }
+ return can;
+ }
+
+ function isSearchValueInParent(parentValues) {
+ for (var i = 0, len = parentValues.length; i < len; i++) {
+ if (self._isMatch(parentValues.slice(0, i), parentValues[i], keyword)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function canFindKey(selectedValues, parents) {
+ var t = selectedValues;
+ for (var i = 0; i < parents.length; i++) {
+ var v = parents[i];
+ t = t[v];
+ if (t == null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ function isChild(selectedValues, parents) {
+ var t = selectedValues;
+ for (var i = 0; i < parents.length; i++) {
+ var v = parents[i];
+ if (!BI.has(t, v)) {
+ return false;
+ }
+ t = t[v];
+ if (BI.isEmpty(t)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ },
+
+ _reqAdjustTreeNode: function (op, callback) {
+ var self = this;
+ var result = [];
+ var selectedValues = op.selectedValues;
+ if (selectedValues == null || BI.isEmpty(selectedValues)) {
+ callback({});
+ return;
+ }
+ BI.each(selectedValues, function (k, v) {
+ result.push([k]);
+ });
+
+ dealWithSelectedValues(selectedValues, []);
+
+ var jo = {};
+ BI.each(result, function (i, strs) {
+ self._buildTree(jo, strs);
+ });
+ callback(jo);
+
+ function dealWithSelectedValues(selected, parents) {
+ if (selected == null || BI.isEmpty(selected)) {
+ return true;
+ }
+ var can = true;
+ BI.each(selected, function (k, v) {
+ var p = BI.clone(parents);
+ p.push(k);
+ if (!dealWithSelectedValues(selected[k], p)) {
+ BI.each(selected[k], function (nk, nv) {
+ var t = BI.clone(p);
+ t.push(nk);
+ result.push(t);
+ });
+ can = false;
+ }
+ });
+ return can && isAllSelected(selected, parents);
+ }
+
+ function isAllSelected(selected, parents) {
+ return BI.isEmpty(selected) || self._getChildCount(parents) === BI.size(selected);
+ }
+ },
+
+ _reqInitTreeNode: function (op, callback) {
+ var self = this;
+ var result = [];
+ var keyword = op.keyword || "";
+ var selectedValues = op.selectedValues;
+ var lastSearchValue = op.lastSearchValue || ""; // 一次请求100个,但是搜索是拿全部的,lastSearchValue是上一次遍历到的节点索引
+ var output = search();
+ BI.nextTick(function () {
+ callback({
+ hasNext: output.length > self._const.perPage,
+ items: result,
+ lastSearchValue: BI.last(output)
+ });
+ });
+
+ function search() {
+ var children = self._getChildren([]);
+ var start = children.length;
+ if (lastSearchValue !== "") {
+ for (var j = 0, len = start; j < len; j++) {
+ if (children[j].value === lastSearchValue) {
+ start = j + 1;
+ break;
+ }
+ }
+ } else {
+ start = 0;
+ }
+ var output = [];
+ for (var i = start, len = children.length; i < len; i++) {
+ if (output.length < self._const.perPage) {
+ var find = nodeSearch(1, [], children[i].value, false, result);
+ } else if (output.length === self._const.perPage) {
+ var find = nodeSearch(1, [], children[i].value, false, []);
+ }
+ if (find[0] === true) {
+ output.push(children[i].value);
+ }
+ if (output.length > self._const.perPage) {
+ break;
+ }
+ }
+
+ // 深层嵌套的比较麻烦,这边先实现的是在根节点添加
+ if (op.times === 1) {
+ var nodes = self._getAddedValueNode([], selectedValues);
+ result = BI.concat(BI.filter(nodes, function (idx, node) {
+ var find = BI.Func.getSearchResult([node.text || node.value], keyword);
+ return find.find.length > 0 || find.match.length > 0;
+ }), result);
+ }
+ return output;
+ }
+
+ function nodeSearch(deep, parentValues, current, isAllSelect, result) {
+ if (self._isMatch(parentValues, current, keyword)) {
+ var checked = isAllSelect || isSelected(parentValues, current);
+ createOneJson(parentValues, current, false, checked, !isAllSelect && isHalf(parentValues, current), true, result);
+ return [true, checked];
+ }
+ var newParents = BI.clone(parentValues);
+ newParents.push(current);
+ var children = self._getChildren(newParents);
+
+ var can = false, checked = false;
+
+ var isCurAllSelected = isAllSelect || isAllSelected(parentValues, current);
+ BI.each(children, function (i, child) {
+ var state = nodeSearch(deep + 1, newParents, child.value, isCurAllSelected, result);
+ // 当前节点的子节点是否选中,并不确定全选还是半选
+ if (state[1] === true) {
+ checked = true;
+ }
+ // 当前节点的子节点要不要加入到结果集中
+ if (state[0] === true) {
+ can = true;
+ }
+ });
+ // 子节点匹配, 补充父节点
+ if (can === true) {
+ checked = isCurAllSelected || (isSelected(parentValues, current) && checked);
+ createOneJson(parentValues, current, true, checked, false, false, result);
+ }
+ return [can, checked];
+ }
+
+ function createOneJson(parentValues, value, isOpen, checked, half, flag, result) {
+ var node = self._getTreeNode(parentValues, value);
+ result.push({
+ id: node.id,
+ pId: node.pId,
+ text: node.text,
+ value: node.value,
+ title: node.title,
+ isParent: node.getChildrenLength() > 0,
+ open: isOpen,
+ checked: checked,
+ halfCheck: half,
+ flag: flag
+ });
+ }
+
+ function isHalf(parentValues, value) {
+ var find = findSelectedObj(parentValues);
+ if (find == null) {
+ return null;
+ }
+ return BI.any(find, function (v, ob) {
+ if (v === value) {
+ if (ob != null && !BI.isEmpty(ob)) {
+ return true;
+ }
+ }
+ });
+ }
+
+ function isAllSelected(parentValues, value) {
+ var find = findSelectedObj(parentValues);
+ if (find == null) {
+ return null;
+ }
+ return BI.any(find, function (v, ob) {
+ if (v === value) {
+ if (ob != null && BI.isEmpty(ob)) {
+ return true;
+ }
+ }
+ });
+ }
+
+ function isSelected(parentValues, value) {
+ var find = findSelectedObj(parentValues);
+ if (find == null) {
+ return false;
+ }
+ return BI.any(find, function (v) {
+ if (v === value) {
+ return true;
+ }
+ });
+ }
+
+ function findSelectedObj(parentValues) {
+ var find = selectedValues;
+ if (find == null) {
+ return null;
+ }
+ BI.every(parentValues, function (i, v) {
+ find = find[v];
+ if (find == null) {
+ return false;
+ }
+ return true;
+ });
+ return find;
+ }
+ },
+
+ _reqTreeNode: function (op, callback) {
+ var self = this, o = this.options;
+ var result = [];
+ var times = op.times;
+ var checkState = op.checkState || {};
+ var parentValues = op.parentValues || [];
+ var selectedValues = op.selectedValues || {};
+ var valueMap = {};
+ // if (judgeState(parentValues, selectedValues, checkState)) {
+ valueMap = dealWithSelectedValue(parentValues, selectedValues);
+ // }
+ var nodes = this._getChildren(parentValues);
+ for (var i = (times - 1) * this._const.perPage; nodes[i] && i < times * this._const.perPage; i++) {
+ var state = getCheckState(nodes[i].value, parentValues, valueMap, checkState);
+ result.push({
+ id: nodes[i].id,
+ pId: nodes[i].pId,
+ value: nodes[i].value,
+ text: nodes[i].text,
+ times: 1,
+ isParent: nodes[i].getChildrenLength() > 0,
+ checked: state[0],
+ halfCheck: state[1],
+ open: o.open
+ });
+ }
+ // 如果指定节点全部打开
+ if (o.open) {
+ var allNodes = [];
+ // 获取所有节点
+ BI.each(nodes, function (idx, node) {
+ allNodes = BI.concat(allNodes, self._getAllChildren(parentValues.concat([node.value])));
+ });
+ BI.each(allNodes, function (idx, node) {
+ var valueMap = dealWithSelectedValue(node.parentValues, selectedValues);
+ // REPORT-24409 fix: 设置节点全部展开,添加的节点没有给状态
+ var parentCheckState = {};
+ var find = BI.find(result, function (idx, pNode) {
+ return pNode.id === node.pId;
+ });
+ if (find) {
+ parentCheckState.checked = find.halfCheck ? false : find.checked;
+ parentCheckState.half = find.halfCheck;
+ }
+ var state = getCheckState(node.value, node.parentValues, valueMap, parentCheckState);
+ result.push({
+ id: node.id,
+ pId: node.pId,
+ value: node.value,
+ text: node.text,
+ times: 1,
+ isParent: node.getChildrenLength() > 0,
+ checked: state[0],
+ halfCheck: state[1],
+ open: self.options.open
+ });
+ });
+ }
+ // 深层嵌套的比较麻烦,这边先实现的是在根节点添加
+ if (parentValues.length === 0 && times === 1) {
+ result = BI.concat(self._getAddedValueNode(parentValues, selectedValues), result);
+ }
+ BI.nextTick(function () {
+ callback({
+ items: result,
+ hasNext: nodes.length > times * self._const.perPage
+ });
+ });
+
+ function judgeState(parentValues, selected_value, checkState) {
+ var checked = checkState.checked, half = checkState.half;
+ if (parentValues.length > 0 && !checked) {
+ return false;
+ }
+ return (parentValues.length === 0 || (checked && half) && !BI.isEmpty(selected_value));
+ }
+
+ function dealWithSelectedValue(parentValues, selectedValues) {
+ var valueMap = {}, parents = (parentValues || []).slice(0);
+ BI.each(parentValues, function (i, v) {
+ parents.push(v);
+
+ selectedValues = selectedValues[v] || {};
+ });
+ BI.each(selectedValues, function (value, obj) {
+ var currentParents = BI.concat(parents, value);
+
+ if (BI.isNull(obj)) {
+ valueMap[value] = [0, 0];
+ return;
+ }
+ if (BI.isEmpty(obj)) {
+ valueMap[value] = [2, 0];
+ return;
+ }
+ var nextNames = {};
+ BI.each(obj, function (t, o) {
+ if (BI.isNull(o) || BI.isEmpty(o)) {
+ nextNames[t] = true;
+ } else {
+ isAllSelected(o, BI.concat(currentParents, [t])) && (nextNames[t] = true);
+ }
+ });
+ // valueMap的数组第一个参数为不选: 0, 半选: 1, 全选:2, 第二个参数为改节点下选中的子节点个数(子节点全选或者不存在)
+ valueMap[value] = [1, BI.size(nextNames)];
+ });
+ return valueMap;
+ }
+
+ function isAllSelected(selected, parents) {
+ if (BI.isEmpty(selected)) {
+ return true;
+ }
+
+ if (self._getChildCount(parents) !== BI.size(selected)) {
+ return false;
+ }
+
+ return BI.every(selected, function (value) {
+ return isAllSelected(selected[value], BI.concat(parents, value));
+ });
+ }
+
+ function getCheckState(current, parentValues, valueMap, checkState) {
+ // 节点本身的checked和half优先级最高
+ var checked = checkState.checked, half = checkState.half;
+ var tempCheck = false, halfCheck = false;
+ if (BI.has(valueMap, current)) {
+ // 可能是半选
+ if (valueMap[current][0] === 1) {
+ var values = BI.clone(parentValues);
+ values.push(current);
+ var childCount = self._getChildCount(values);
+ if (childCount > 0 && childCount !== valueMap[current][1]) {
+ halfCheck = true;
+ }
+ } else if (valueMap[current][0] === 2) {
+ tempCheck = true;
+ }
+ }
+ var check;
+ // 展开的节点checked为false 且没有明确得出当前子节点是半选或者全选, 则check状态取决于valueMap
+ if (!checked && !halfCheck && !tempCheck) {
+ check = BI.has(valueMap, current);
+ } else {
+ // 不是上面那种情况就先看在节点没有带有明确半选的时候,通过节点自身的checked和valueMap的状态能都得到选中信息
+ check = ((tempCheck || checked) && !half) || BI.has(valueMap, current);
+ }
+ return [check, halfCheck];
+ }
+ },
+
+ _getAddedValueNode: function (parentValues, selectedValues) {
+ var nodes = this._getChildren(parentValues);
+ return BI.map(BI.difference(BI.keys(selectedValues), BI.map(nodes, "value")), function (idx, v) {
+ return {
+ id: BI.UUID(),
+ pId: nodes.length > 0 ? nodes[0].pId : BI.UUID(),
+ value: v,
+ text: v,
+ times: 1,
+ isParent: false,
+ checked: true,
+ halfCheck: false
+ };
+ });
+ },
+
+ _getNode: function (selectedValues, parentValues) {
+ var pNode = selectedValues;
+ for (var i = 0, len = parentValues.length; i < len; i++) {
+ if (pNode == null) {
+ return null;
+ }
+ pNode = pNode[parentValues[i]];
+ }
+ return pNode;
+ },
+
+ _deleteNode: function (selectedValues, values) {
+ var name = values[values.length - 1];
+ var p = values.slice(0, values.length - 1);
+ var pNode = this._getNode(selectedValues, p);
+ if (pNode != null && pNode[name]) {
+ delete pNode[name];
+ // 递归删掉空父节点
+ while (p.length > 0 && BI.isEmpty(pNode)) {
+ name = p[p.length - 1];
+ p = p.slice(0, p.length - 1);
+ pNode = this._getNode(selectedValues, p);
+ if (pNode != null) {
+ delete pNode[name];
+ }
+ }
+ }
+ },
+
+ _buildTree: function (jo, values) {
+ var t = jo;
+ BI.each(values, function (i, v) {
+ if (!BI.has(t, v)) {
+ t[v] = {};
+ }
+ t = t[v];
+ });
+ },
+
+ _isMatch: function (parentValues, value, keyword) {
+ var o = this.options;
+ var node = this._getTreeNode(parentValues, value);
+ if (!node) {
+ return false;
+ }
+ var find = BI.Func.getSearchResult([node.text || node.value], keyword);
+ if(o.allowSearchValue && node.value) {
+ var valueFind = BI.Func.getSearchResult([node.value], keyword);
+ return valueFind.find.length > 0 || valueFind.match.length > 0 ||
+ find.find.length > 0 || find.match.length > 0;
+ }
+ return find.find.length > 0 || find.match.length > 0;
+ },
+
+ _getTreeNode: function (parentValues, v) {
+ var self = this;
+ var findParentNode;
+ var index = 0;
+ var currentParent = this.tree.getRoot();
+ this.tree.traverse(function (node) {
+ if (self.tree.isRoot(node)) {
+ return;
+ }
+ if (index > parentValues.length) {
+ return false;
+ }
+
+ /**
+ * 一个树结构。要找root_1_3的子节点
+ * {root: { 1: {1: {}, 2: {}, 3: {}}, 3: {1: {}, 2: {}} } }
+ * 当遍历到root_1节点时,index++,而下一个节点root_3时,符合下面的if逻辑,这样找到的节点就是root_3节点了,需要加步判断是否是root_1的子节点
+ */
+ if (index === parentValues.length && node.value === v) {
+ if (node.getParent() !== currentParent) {
+ return;
+ }
+
+ findParentNode = node;
+
+ return false;
+ }
+ if (node.value === parentValues[index] && node.getParent() === currentParent) {
+ index++;
+ currentParent = node;
+
+ return;
+ }
+
+ return true;
+ });
+
+ return findParentNode;
+ },
+
+ _getChildren: function (parentValues) {
+ if (parentValues.length > 0) {
+ var value = BI.last(parentValues);
+ var parent = this._getTreeNode(parentValues.slice(0, parentValues.length - 1), value);
+ } else {
+ var parent = this.tree.getRoot();
+ }
+
+ return parent ? parent.getChildren() : [];
+ },
+
+ _getAllChildren: function(parentValues) {
+ var children = this._getChildren(parentValues);
+ var nodes = [].concat(children);
+ BI.each(nodes, function (idx, node) {
+ node.parentValues = parentValues;
+ });
+ var queue = BI.map(children, function (idx, node) {
+ return {
+ parentValues: parentValues,
+ value: node.value
+ };
+ });
+ while (BI.isNotEmptyArray(queue)) {
+ var node = queue.shift();
+ var pValues = (node.parentValues).concat(node.value);
+ var childNodes = this._getChildren(pValues);
+ BI.each(childNodes, function (idx, node) {
+ node.parentValues = pValues;
+ });
+ queue = queue.concat(childNodes);
+ nodes = nodes.concat(childNodes);
+ }
+ return nodes;
+ },
+
+ _getChildCount: function (parentValues) {
+ return this._getChildren(parentValues).length;
+ },
+
+ buildCompleteTree: function (selectedValues) {
+ var self = this;
+ var result = {};
+
+ if (selectedValues !== null && !BI.isEmpty(selectedValues)) {
+ fill([], this.tree.getRoot(), selectedValues, result);
+ }
+
+ return result;
+
+ function fill(parentValues, node, selected, r) {
+ if (selected === null || BI.isEmpty(selected)) {
+ BI.each(node.getChildren(), function (i, child) {
+ var newParents = BI.clone(parentValues);
+ newParents.push(child.value);
+ r[child.value] = {};
+ fill(newParents, child, null, r[child.value]);
+ });
+ return;
+ }
+ BI.each(selected, function (k) {
+ var node = self._getTreeNode(parentValues, k);
+ var newParents = BI.clone(parentValues);
+ newParents.push(node.value);
+ r[k] = {};
+ fill(newParents, node, selected[k], r[k]);
+ });
+ }
+ },
+});
diff --git a/src/main/resources/com/fr/fineui/component/treevaluechooser/abstract.treevaluechooser.list.js b/src/main/resources/com/fr/fineui/component/treevaluechooser/abstract.treevaluechooser.list.js
new file mode 100644
index 0000000..048a656
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/treevaluechooser/abstract.treevaluechooser.list.js
@@ -0,0 +1,287 @@
+BI.AbstractListTreeValueChooser = BI.inherit(BI.AbstractTreeValueChooser, {
+
+ _reqDisplayTreeNode: function (op, callback) {
+ var self = this;
+ var result = {};
+ var selectedValues = op.selectedValues;
+
+ if (selectedValues == null || BI.isEmpty(selectedValues)) {
+ callback({});
+ return;
+ }
+
+ doCheck([], this.tree.getRoot(), selectedValues);
+
+ callback({
+ items: BI.values(result)
+ });
+
+ function doCheck(parentValues, node, selected) {
+ BI.each(selected, function (idx, path) {
+ BI.each(path, function (id, value) {
+ var nodeValue = value;
+ var node = self._getTreeNode(path.slice(0, id), nodeValue);
+ // 找不到就是新增值
+ if (BI.isNull(node)) {
+ createOneJson({
+ id: BI.UUID(),
+ text: nodeValue,
+ value: nodeValue,
+ isLeaf: true
+ }, BI.UUID());
+ } else {
+ if(!BI.has(result, node.id)) {
+ createOneJson(node, node.parent && node.parent.id);
+ }
+ result[node.id].isLeaf !== true && (result[node.id].isLeaf = id === path.length - 1);
+ }
+ });
+ });
+ }
+
+ function createOneJson(node, pId) {
+ result[node.id] = {
+ id: node.id,
+ pId: pId,
+ text: node.text,
+ value: node.value,
+ open: true,
+ isLeaf: node.isLeaf
+ };
+ }
+ },
+
+ _reqInitTreeNode: function (op, callback) {
+ var self = this;
+ var result = [];
+ var keyword = op.keyword || "";
+ var selectedValues = op.selectedValues;
+ var lastSearchValue = op.lastSearchValue || ""; // 一次请求100个,但是搜索是拿全部的,lastSearchValue是上一次遍历到的节点索引
+ var output = search();
+ BI.nextTick(function () {
+ callback({
+ hasNext: output.length > self._const.perPage,
+ items: result,
+ lastSearchValue: BI.last(output)
+ });
+ });
+
+ function search() {
+ var children = self._getChildren([]);
+ var start = children.length;
+ if (lastSearchValue !== "") {
+ for (var j = 0, len = start; j < len; j++) {
+ if (children[j].value === lastSearchValue) {
+ start = j + 1;
+ break;
+ }
+ }
+ } else {
+ start = 0;
+ }
+ var output = [];
+ for (var i = start, len = children.length; i < len; i++) {
+ if (output.length < self._const.perPage) {
+ var find = nodeSearch(1, [], children[i].value, result);
+ } else if (output.length === self._const.perPage) {
+ var find = nodeSearch(1, [], children[i].value, []);
+ }
+ if (find[0] === true) {
+ output.push(children[i].value);
+ }
+ if (output.length > self._const.perPage) {
+ break;
+ }
+ }
+
+ // 深层嵌套的比较麻烦,这边先实现的是在根节点添加
+ if (op.times === 1) {
+ var nodes = self._getAddedValueNode([], selectedValues);
+ result = BI.concat(BI.filter(nodes, function (idx, node) {
+ var find = BI.Func.getSearchResult([node.text || node.value], keyword);
+ return find.find.length > 0 || find.match.length > 0;
+ }), result);
+ }
+ return output;
+ }
+
+ function nodeSearch(deep, parentValues, current, result) {
+ if (self._isMatch(parentValues, current, keyword)) {
+ var checked = isSelected(parentValues, current);
+ createOneJson(parentValues, current, false, checked, true, result);
+ return [true, checked];
+ }
+ var newParents = BI.clone(parentValues);
+ newParents.push(current);
+ var children = self._getChildren(newParents);
+
+ var can = false, checked = false;
+
+ BI.each(children, function (i, child) {
+ var state = nodeSearch(deep + 1, newParents, child.value, result);
+ if (state[1] === true) {
+ checked = true;
+ }
+ if (state[0] === true) {
+ can = true;
+ }
+ });
+ if (can === true) {
+ checked = isSelected(parentValues, current);
+ createOneJson(parentValues, current, true, checked, false, result);
+ }
+ return [can, checked];
+ }
+
+ function createOneJson(parentValues, value, isOpen, checked, flag, result) {
+ var node = self._getTreeNode(parentValues, value);
+ result.push({
+ id: node.id,
+ pId: node.pId,
+ text: node.text,
+ value: node.value,
+ title: node.title,
+ isParent: node.getChildrenLength() > 0,
+ open: isOpen,
+ checked: checked,
+ halfCheck: false,
+ flag: flag
+ });
+ }
+
+ function isHalf(parentValues, value) {
+ var find = findSelectedObj(parentValues);
+ if (find == null) {
+ return null;
+ }
+ return BI.any(find, function (v, ob) {
+ if (v === value) {
+ if (ob != null && !BI.isEmpty(ob)) {
+ return true;
+ }
+ }
+ });
+ }
+
+ function isAllSelected(parentValues, value) {
+ var find = findSelectedObj(parentValues);
+ if (find == null) {
+ return null;
+ }
+ return BI.any(find, function (v, ob) {
+ if (v === value) {
+ if (ob != null && BI.isEmpty(ob)) {
+ return true;
+ }
+ }
+ });
+ }
+
+ function isSelected(parentValues, value) {
+ return BI.any(selectedValues, function (idx, array) {
+ return BI.isEqual(parentValues, array.slice(0, parentValues.length)) && BI.last(array) === value;
+ });
+ }
+
+ function findSelectedObj(parentValues) {
+ var find = selectedValues;
+ if (find == null) {
+ return null;
+ }
+ BI.every(parentValues, function (i, v) {
+ find = find[v];
+ if (find == null) {
+ return false;
+ }
+ return true;
+ });
+ return find;
+ }
+ },
+
+ _reqTreeNode: function (op, callback) {
+ var self = this, o = this.options;
+ var result = [];
+ var times = op.times;
+ var parentValues = op.parentValues || [];
+ var selectedValues = op.selectedValues || [];
+ var valueMap = dealWithSelectedValue(parentValues, selectedValues);
+ var nodes = this._getChildren(parentValues);
+ for (var i = (times - 1) * this._const.perPage; nodes[i] && i < times * this._const.perPage; i++) {
+ var checked = BI.has(valueMap, nodes[i].value);
+ result.push({
+ id: nodes[i].id,
+ pId: nodes[i].pId,
+ value: nodes[i].value,
+ text: nodes[i].text,
+ times: 1,
+ isParent: nodes[i].getChildrenLength() > 0,
+ checked: checked,
+ halfCheck: false,
+ open: o.open
+ });
+ }
+ // 如果指定节点全部打开
+ if (o.open) {
+ var allNodes = [];
+ // 获取所有节点
+ BI.each(nodes, function (idx, node) {
+ allNodes = BI.concat(allNodes, self._getAllChildren(parentValues.concat([node.value])));
+ });
+ BI.each(allNodes, function (idx, node) {
+ var valueMap = dealWithSelectedValue(node.parentValues, selectedValues);
+ var checked = BI.has(valueMap, node.value);
+ result.push({
+ id: node.id,
+ pId: node.pId,
+ value: node.value,
+ text: node.text,
+ times: 1,
+ isParent: node.getChildrenLength() > 0,
+ checked: checked,
+ halfCheck: false,
+ open: o.open
+ });
+ });
+ }
+ // 深层嵌套的比较麻烦,这边先实现的是在根节点添加
+ if (parentValues.length === 0 && times === 1) {
+ result = BI.concat(self._getAddedValueNode(parentValues, selectedValues), result);
+ }
+ BI.nextTick(function () {
+ callback({
+ items: result,
+ hasNext: nodes.length > times * self._const.perPage
+ });
+ });
+
+ function dealWithSelectedValue(parentValues, selectedValues) {
+ var valueMap = {};
+ BI.each(selectedValues, function (idx, v) {
+ if (BI.isEqual(parentValues, v.slice(0, parentValues.length))) {
+ valueMap[BI.last(v)] = [2, 0];
+ }
+ });
+ return valueMap;
+ }
+ },
+
+ _getAddedValueNode: function (parentValues, selectedValues) {
+ var nodes = this._getChildren(parentValues);
+ var values = BI.flatten(BI.filter(selectedValues, function (idx, array) {
+ return array.length === 1;
+ }));
+ return BI.map(BI.difference(values, BI.map(nodes, "value")), function (idx, v) {
+ return {
+ id: BI.UUID(),
+ pId: nodes.length > 0 ? nodes[0].pId : BI.UUID(),
+ value: v,
+ text: v,
+ times: 1,
+ isParent: false,
+ checked: true,
+ halfCheck: false
+ };
+ });
+ }
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/component/treevaluechooser/combo.listtreevaluechooser.js b/src/main/resources/com/fr/fineui/component/treevaluechooser/combo.listtreevaluechooser.js
new file mode 100644
index 0000000..97a682a
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/treevaluechooser/combo.listtreevaluechooser.js
@@ -0,0 +1,114 @@
+/**
+ * 简单的复选下拉树控件, 适用于数据量少的情况, 可以自增值
+ *
+ * Created by GUY on 2015/10/29.
+ * @class BI.ListTreeValueChooserInsertCombo
+ * @extends BI.Widget
+ */
+BI.ListTreeValueChooserInsertCombo = BI.inherit(BI.AbstractListTreeValueChooser, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.ListTreeValueChooserInsertCombo.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-list-tree-value-chooser-insert-combo",
+ width: 200,
+ height: 24,
+ items: null,
+ itemsCreator: BI.emptyFn,
+ isNeedAdjustWidth: true,
+ });
+ },
+
+ _init: function () {
+ BI.ListTreeValueChooserInsertCombo.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ if (BI.isNotNull(o.items)) {
+ this._initData(o.items);
+ }
+ this.combo = BI.createWidget({
+ type: "bi.multi_tree_list_combo",
+ isNeedAdjustWidth: o.isNeedAdjustWidth,
+ element: this,
+ text: o.text,
+ value: o.value,
+ watermark: o.watermark,
+ allowInsertValue: o.allowInsertValue,
+ allowEdit: o.allowEdit,
+ itemsCreator: BI.bind(this._itemsCreator, this),
+ valueFormatter: BI.bind(this._valueFormatter, this),
+ width: o.width,
+ height: o.height,
+ listeners: [{
+ eventName: BI.MultiTreeListCombo.EVENT_FOCUS,
+ action: function () {
+ self.fireEvent(BI.ListTreeValueChooserInsertCombo.EVENT_FOCUS);
+ }
+ }, {
+ eventName: BI.MultiTreeListCombo.EVENT_BLUR,
+ action: function () {
+ self.fireEvent(BI.ListTreeValueChooserInsertCombo.EVENT_BLUR);
+ }
+ }, {
+ eventName: BI.MultiTreeListCombo.EVENT_STOP,
+ action: function () {
+ self.fireEvent(BI.ListTreeValueChooserInsertCombo.EVENT_STOP);
+ }
+ }, {
+ eventName: BI.MultiTreeListCombo.EVENT_CLICK_ITEM,
+ action: function (v) {
+ self.fireEvent(BI.ListTreeValueChooserInsertCombo.EVENT_CLICK_ITEM, v);
+ }
+ }, {
+ eventName: BI.MultiTreeListCombo.EVENT_SEARCHING,
+ action: function () {
+ self.fireEvent(BI.ListTreeValueChooserInsertCombo.EVENT_SEARCHING);
+ }
+ }, {
+ eventName: BI.MultiTreeListCombo.EVENT_CONFIRM,
+ action: function () {
+ self.fireEvent(BI.ListTreeValueChooserInsertCombo.EVENT_CONFIRM);
+ }
+ }, {
+ eventName: BI.MultiTreeCombo.EVENT_BEFORE_POPUPVIEW,
+ action: function () {
+ self.fireEvent(BI.TreeValueChooserInsertCombo.EVENT_BEFORE_POPUPVIEW);
+ }
+ }]
+ });
+ },
+
+ showView: function () {
+ this.combo.showView();
+ },
+
+ hideView: function () {
+ this.combo.hideView();
+ },
+
+ getSearcher: function () {
+ return this.combo.getSearcher();
+ },
+
+ setValue: function (v) {
+ this.combo.setValue(v);
+ },
+
+ getValue: function () {
+ return this.combo.getValue();
+ },
+
+ populate: function (items) {
+ if (BI.isNotNull(items)) {
+ this._initData(items);
+ }
+ this.combo.populate();
+ }
+});
+
+BI.ListTreeValueChooserInsertCombo.EVENT_FOCUS = "EVENT_FOCUS";
+BI.ListTreeValueChooserInsertCombo.EVENT_BLUR = "EVENT_BLUR";
+BI.ListTreeValueChooserInsertCombo.EVENT_STOP = "EVENT_STOP";
+BI.ListTreeValueChooserInsertCombo.EVENT_CLICK_ITEM = "EVENT_CLICK_ITEM";
+BI.ListTreeValueChooserInsertCombo.EVENT_SEARCHING = "EVENT_SEARCHING";
+BI.ListTreeValueChooserInsertCombo.EVENT_CONFIRM = "EVENT_CONFIRM";
+BI.ListTreeValueChooserInsertCombo.EVENT_BEFORE_POPUPVIEW = "EVENT_BEFORE_POPUPVIEW";
+BI.shortcut("bi.list_tree_value_chooser_insert_combo", BI.ListTreeValueChooserInsertCombo);
diff --git a/src/main/resources/com/fr/fineui/component/treevaluechooser/combo.treevaluechooser.insert.js b/src/main/resources/com/fr/fineui/component/treevaluechooser/combo.treevaluechooser.insert.js
new file mode 100644
index 0000000..07c4ad9
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/treevaluechooser/combo.treevaluechooser.insert.js
@@ -0,0 +1,113 @@
+/**
+ * 简单的复选下拉树控件, 适用于数据量少的情况, 可以自增值
+ *
+ * Created by GUY on 2015/10/29.
+ * @class BI.TreeValueChooserInsertCombo
+ * @extends BI.Widget
+ */
+BI.TreeValueChooserInsertCombo = BI.inherit(BI.AbstractTreeValueChooser, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.TreeValueChooserInsertCombo.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-tree-value-chooser-insert-combo",
+ width: 200,
+ height: 24,
+ items: null,
+ itemsCreator: BI.emptyFn,
+ isNeedAdjustWidth: true
+ });
+ },
+
+ _init: function () {
+ BI.TreeValueChooserInsertCombo.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ if (BI.isNotNull(o.items)) {
+ this._initData(o.items);
+ }
+ this.combo = BI.createWidget({
+ type: "bi.multi_tree_insert_combo",
+ isNeedAdjustWidth: o.isNeedAdjustWidth,
+ allowEdit: o.allowEdit,
+ text: o.text,
+ value: o.value,
+ watermark: o.watermark,
+ element: this,
+ itemsCreator: BI.bind(this._itemsCreator, this),
+ valueFormatter: BI.bind(this._valueFormatter, this),
+ width: o.width,
+ height: o.height,
+ listeners: [{
+ eventName: BI.MultiTreeInsertCombo.EVENT_FOCUS,
+ action: function () {
+ self.fireEvent(BI.TreeValueChooserCombo.EVENT_FOCUS);
+ }
+ }, {
+ eventName: BI.MultiTreeInsertCombo.EVENT_BLUR,
+ action: function () {
+ self.fireEvent(BI.TreeValueChooserCombo.EVENT_BLUR);
+ }
+ }, {
+ eventName: BI.MultiTreeInsertCombo.EVENT_STOP,
+ action: function () {
+ self.fireEvent(BI.TreeValueChooserInsertCombo.EVENT_STOP);
+ }
+ }, {
+ eventName: BI.MultiTreeInsertCombo.EVENT_CLICK_ITEM,
+ action: function (v) {
+ self.fireEvent(BI.TreeValueChooserInsertCombo.EVENT_CLICK_ITEM, v);
+ }
+ }, {
+ eventName: BI.MultiTreeInsertCombo.EVENT_SEARCHING,
+ action: function () {
+ self.fireEvent(BI.TreeValueChooserInsertCombo.EVENT_SEARCHING);
+ }
+ }, {
+ eventName: BI.MultiTreeInsertCombo.EVENT_CONFIRM,
+ action: function () {
+ self.fireEvent(BI.TreeValueChooserInsertCombo.EVENT_CONFIRM);
+ }
+ }, {
+ eventName: BI.MultiTreeCombo.EVENT_BEFORE_POPUPVIEW,
+ action: function () {
+ self.fireEvent(BI.TreeValueChooserInsertCombo.EVENT_BEFORE_POPUPVIEW);
+ }
+ }]
+ });
+ },
+
+ showView: function () {
+ this.combo.showView();
+ },
+
+ hideView: function () {
+ this.combo.hideView();
+ },
+
+ getSearcher: function () {
+ return this.combo.getSearcher();
+ },
+
+ setValue: function (v) {
+ this.combo.setValue(v);
+ },
+
+ getValue: function () {
+ return this.combo.getValue();
+ },
+
+ populate: function (items) {
+ if (BI.isNotNull(items)) {
+ this._initData(items);
+ }
+ this.combo.populate();
+ }
+});
+
+BI.TreeValueChooserInsertCombo.EVENT_FOCUS = "EVENT_FOCUS";
+BI.TreeValueChooserInsertCombo.EVENT_BLUR = "EVENT_BLUR";
+BI.TreeValueChooserInsertCombo.EVENT_STOP = "EVENT_STOP";
+BI.TreeValueChooserInsertCombo.EVENT_CLICK_ITEM = "EVENT_CLICK_ITEM";
+BI.TreeValueChooserInsertCombo.EVENT_SEARCHING = "EVENT_SEARCHING";
+BI.TreeValueChooserInsertCombo.EVENT_CONFIRM = "EVENT_CONFIRM";
+BI.TreeValueChooserInsertCombo.EVENT_BEFORE_POPUPVIEW = "EVENT_BEFORE_POPUPVIEW";
+BI.shortcut("bi.tree_value_chooser_insert_combo", BI.TreeValueChooserInsertCombo);
diff --git a/src/main/resources/com/fr/fineui/component/treevaluechooser/combo.treevaluechooser.js b/src/main/resources/com/fr/fineui/component/treevaluechooser/combo.treevaluechooser.js
new file mode 100644
index 0000000..452dce5
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/treevaluechooser/combo.treevaluechooser.js
@@ -0,0 +1,117 @@
+/**
+ * 简单的复选下拉树控件, 适用于数据量少的情况
+ *
+ * Created by GUY on 2015/10/29.
+ * @class BI.TreeValueChooserCombo
+ * @extends BI.Widget
+ */
+BI.TreeValueChooserCombo = BI.inherit(BI.AbstractTreeValueChooser, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.TreeValueChooserCombo.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-tree-value-chooser-combo",
+ width: 200,
+ height: 24,
+ items: null,
+ itemsCreator: BI.emptyFn,
+ isNeedAdjustWidth: true
+ });
+ },
+
+ _init: function () {
+ BI.TreeValueChooserCombo.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ if (BI.isNotNull(o.items)) {
+ this._initData(o.items);
+ }
+ this.combo = BI.createWidget({
+ type: "bi.multi_tree_combo",
+ text: o.text,
+ allowEdit: o.allowEdit,
+ value: o.value,
+ watermark: o.watermark,
+ element: this,
+ itemsCreator: BI.bind(this._itemsCreator, this),
+ valueFormatter: BI.bind(this._valueFormatter, this),
+ width: o.width,
+ height: o.height,
+ isNeedAdjustWidth: o.isNeedAdjustWidth,
+ listeners: [{
+ eventName: BI.MultiTreeCombo.EVENT_FOCUS,
+ action: function () {
+ self.fireEvent(BI.TreeValueChooserCombo.EVENT_FOCUS);
+ }
+ }, {
+ eventName: BI.MultiTreeCombo.EVENT_BLUR,
+ action: function () {
+ self.fireEvent(BI.TreeValueChooserCombo.EVENT_BLUR);
+ }
+ }, {
+ eventName: BI.MultiTreeCombo.EVENT_STOP,
+ action: function () {
+ self.fireEvent(BI.TreeValueChooserCombo.EVENT_STOP);
+ }
+ }, {
+ eventName: BI.MultiTreeCombo.EVENT_CLICK_ITEM,
+ action: function (v) {
+ self.fireEvent(BI.TreeValueChooserCombo.EVENT_CLICK_ITEM, v);
+ }
+ }, {
+ eventName: BI.MultiTreeCombo.EVENT_SEARCHING,
+ action: function () {
+ self.fireEvent(BI.TreeValueChooserCombo.EVENT_SEARCHING);
+ }
+ }, {
+ eventName: BI.MultiTreeCombo.EVENT_CONFIRM,
+ action: function () {
+ self.fireEvent(BI.TreeValueChooserCombo.EVENT_CONFIRM);
+ }
+ }, {
+ eventName: BI.MultiTreeCombo.EVENT_BEFORE_POPUPVIEW,
+ action: function () {
+ self.fireEvent(BI.TreeValueChooserCombo.EVENT_BEFORE_POPUPVIEW);
+ }
+ }]
+ });
+ },
+
+ showView: function () {
+ this.combo.showView();
+ },
+
+ hideView: function () {
+ this.combo.hideView();
+ },
+
+ getSearcher: function () {
+ return this.combo.getSearcher();
+ },
+
+ setValue: function (v) {
+ this.combo.setValue(v);
+ },
+
+ getValue: function () {
+ return this.combo.getValue();
+ },
+
+ getAllValue: function() {
+ return this.buildCompleteTree(this.combo.getValue());
+ },
+
+ populate: function (items) {
+ if (BI.isNotNull(items)) {
+ this._initData(items);
+ }
+ this.combo.populate();
+ }
+});
+
+BI.TreeValueChooserCombo.EVENT_BEFORE_POPUPVIEW = "EVENT_BEFORE_POPUPVIEW";
+BI.TreeValueChooserCombo.EVENT_CONFIRM = "EVENT_CONFIRM";
+BI.TreeValueChooserCombo.EVENT_FOCUS = "EVENT_FOCUS";
+BI.TreeValueChooserCombo.EVENT_BLUR = "EVENT_BLUR";
+BI.TreeValueChooserCombo.EVENT_STOP = "EVENT_STOP";
+BI.TreeValueChooserCombo.EVENT_CLICK_ITEM = "EVENT_CLICK_ITEM";
+BI.TreeValueChooserCombo.EVENT_SEARCHING = "EVENT_SEARCHING";
+BI.shortcut("bi.tree_value_chooser_combo", BI.TreeValueChooserCombo);
diff --git a/src/main/resources/com/fr/fineui/component/treevaluechooser/pane.treevaluechooser.js b/src/main/resources/com/fr/fineui/component/treevaluechooser/pane.treevaluechooser.js
new file mode 100644
index 0000000..e6fc4f0
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/treevaluechooser/pane.treevaluechooser.js
@@ -0,0 +1,62 @@
+/**
+ * 简单的树面板, 适用于数据量少的情况
+ *
+ * Created by GUY on 2015/10/29.
+ * @class BI.TreeValueChooserPane
+ * @extends BI.AbstractTreeValueChooser
+ */
+BI.TreeValueChooserPane = BI.inherit(BI.AbstractTreeValueChooser, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.TreeValueChooserPane.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-tree-value-chooser-pane",
+ items: null,
+ itemsCreator: BI.emptyFn,
+ showLine: true
+ });
+ },
+
+ _init: function () {
+ BI.TreeValueChooserPane.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.pane = BI.createWidget({
+ type: o.hideSearch ? "bi.multi_select_tree_popup" : "bi.multi_select_tree",
+ element: this,
+ showLine: o.showLine,
+ itemsCreator: BI.bind(this._itemsCreator, this)
+ });
+
+ this.pane.on(BI.MultiSelectTree.EVENT_CHANGE, function () {
+ self.fireEvent(BI.TreeValueChooserPane.EVENT_CHANGE);
+ });
+ if (BI.isNotNull(o.items)) {
+ this._initData(o.items);
+ this.pane.populate();
+ }
+ },
+
+ setSelectedValue: function (v) {
+ this.pane.setSelectedValue(v);
+ },
+
+ setValue: function (v) {
+ this.pane.setValue(v);
+ },
+
+ getValue: function () {
+ return this.pane.getValue();
+ },
+
+ getAllValue: function() {
+ return this.buildCompleteTree(this.pane.getValue());
+ },
+
+ populate: function (items) {
+ if (BI.isNotNull(items)) {
+ this._initData(items);
+ }
+ this.pane.populate();
+ }
+});
+BI.TreeValueChooserPane.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.tree_value_chooser_pane", BI.TreeValueChooserPane);
diff --git a/src/main/resources/com/fr/fineui/component/valuechooser/__test__/combo.valuechooser.insert.test.js b/src/main/resources/com/fr/fineui/component/valuechooser/__test__/combo.valuechooser.insert.test.js
new file mode 100644
index 0000000..b57d60c
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/valuechooser/__test__/combo.valuechooser.insert.test.js
@@ -0,0 +1,238 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2019/9/23
+ */
+describe("value_chooser_insert_combo", function () {
+
+ var items = BI.map(BI.makeArray(1000, null), function(idx, v) {
+ return {
+ text: idx,
+ value: idx,
+ title: idx
+ };
+ });
+
+ var itemSelectorGetter = function (array) {
+ return BI.map(array, function (idx, num) {
+ return ".bi-multi-select-popup-view .bi-loader .bi-button-group .bi-multi-select-item:nth-child(" + num + ")";
+ });
+ };
+
+ var searchItemSelectorGetter = function (array) {
+ return BI.map(array, function (idx, num) {
+ return ".bi-multi-select-search-pane .bi-loader .bi-button-group .bi-multi-select-item:nth-child(" + num + ")";
+ });
+ };
+
+ /**
+ * test_author_windy
+ **/
+ it("setValue", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.value_chooser_insert_combo",
+ width: 220,
+ itemsCreator: function (op, callback) {
+ callback(items);
+ }
+ });
+ widget.setValue({
+ type: 1,
+ value: [1, 2]
+ });
+ expect(widget.getValue()).to.deep.equal({
+ type: 1,
+ value: [1, 2]
+ });
+ widget.destroy();
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("getValue", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.value_chooser_insert_combo",
+ width: 220,
+ itemsCreator: function (op, callback) {
+ callback(items);
+ },
+ value: {
+ type: 2,
+ value: [1, 2, 3]
+ }
+ });
+ expect(widget.getValue()).to.deep.equal({
+ type: 2,
+ value: [1, 2, 3]
+ });
+ widget.destroy();
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("点选选值", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.value_chooser_insert_combo",
+ width: 220,
+ itemsCreator: function (op, callback) {
+ callback(items);
+ }
+ });
+ widget.element.find(".bi-multi-select-trigger").click();
+ // 为什么要delay 300呢,因为按钮有debounce
+ BI.delay(function () {
+ // 点选1、2、3
+ BI.each(itemSelectorGetter([1,2,3]), function (idx, selector) {
+ widget.element.find(selector).click();
+ });
+ // 点全选
+ widget.element.find(".bi-multi-select-popup-view .bi-label:contains(全选)").click();
+ // 取消勾选1、2、3
+ BI.delay(function () {
+ BI.each(itemSelectorGetter([1,2,3]), function (idx, selector) {
+ widget.element.find(selector).click();
+ });
+ expect(widget.getValue()).to.deep.equal({
+ type: 2,
+ value: [0, 1, 2]
+ });
+ widget.destroy();
+ done();
+ }, 300);
+ }, 300);
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("搜索选值", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.value_chooser_insert_combo",
+ width: 220,
+ itemsCreator: function (op, callback) {
+ callback(items);
+ }
+ });
+ BI.nextTick(function () {
+ widget.element.find(".bi-multi-select-trigger .tip-text-style").click();
+ // 这边为啥要加呢,因为input的setValue中有nextTick
+ BI.nextTick(function () {
+ BI.Test.triggerKeyDown(widget.element.find(".bi-multi-select-trigger .bi-input"), "2", 50, function () {
+ BI.nextTick(function () {
+ BI.each(searchItemSelectorGetter([1,2]), function (idx, selector) {
+ widget.element.find(selector).click();
+ });
+ expect(widget.getValue()).to.deep.equal({
+ type: 1,
+ value: [2, 12]
+ });
+ widget.destroy();
+ done();
+ });
+ });
+ });
+ });
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("新增值", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.value_chooser_insert_combo",
+ width: 220,
+ itemsCreator: function (op, callback) {
+ callback(items);
+ }
+ });
+ BI.nextTick(function () {
+ widget.element.find(".bi-multi-select-trigger .tip-text-style").click();
+ // 这边为啥要加呢,因为input的setValue中有nextTick
+ BI.nextTick(function () {
+ BI.Test.triggerKeyDown(widget.element.find(".bi-multi-select-trigger .bi-input"), "z", 50, function () {
+ BI.nextTick(function () {
+ widget.element.find(".bi-text-button:contains(+点击新增\"z\")").click();
+ expect(widget.getValue()).to.deep.equal({
+ type: 1,
+ value: ["z"]
+ });
+ widget.destroy();
+ done();
+ });
+ });
+ });
+ });
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("查看已选", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.value_chooser_insert_combo",
+ width: 220,
+ itemsCreator: function (op, callback) {
+ callback(items);
+ },
+ value: {
+ type: 1,
+ value: [1, 2]
+ }
+ });
+ BI.nextTick(function () {
+ widget.element.find(".bi-multi-select-check-selected-button").click();
+ BI.delay(function () {
+ expect(widget.element.find(".display-list-item").length).to.equal(2);
+ widget.destroy();
+ done();
+ }, 300);
+ });
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("BI-64399", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.value_chooser_insert_combo",
+ width: 220,
+ itemsCreator: function (op, callback) {
+ callback([{
+ text: "A",
+ value: "A",
+ title: "A"
+ }, {
+ text: "AA",
+ value: "AA",
+ title: "AA"
+ }, {
+ text: "a1",
+ value: "a1",
+ title: "a1"
+ }]);
+ }
+ });
+
+ widget.element.find(".bi-multi-select-trigger").click();
+ // 为什么要delay 300呢,因为按钮有debounce
+ BI.delay(function () {
+ // 点选a1
+ BI.each(itemSelectorGetter([3]), function (idx, selector) {
+ widget.element.find(selector).click();
+ });
+ widget.element.find(".bi-multi-select-trigger .tip-text-style").click();
+ // 这边为啥要加呢,因为input的setValue中有nextTick
+ BI.nextTick(function () {
+ BI.Test.triggerKeyDown(widget.element.find(".bi-multi-select-trigger .bi-input"), "A", 65, function () {
+ BI.nextTick(function () {
+ expect(widget.element.find(".bi-multi-select-search-pane .multi-select-toolbar").css("display")).to.equal("block");
+ widget.destroy();
+ done();
+ });
+ });
+ });
+ }, 300);
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/component/valuechooser/__test__/combo.valuechooser.test.js b/src/main/resources/com/fr/fineui/component/valuechooser/__test__/combo.valuechooser.test.js
new file mode 100644
index 0000000..bf21ed0
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/valuechooser/__test__/combo.valuechooser.test.js
@@ -0,0 +1,163 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2019/9/23
+ */
+describe("value_chooser_combo", function () {
+
+ var items = BI.map(BI.makeArray(1000, null), function(idx, v) {
+ return {
+ text: idx,
+ value: idx,
+ title: idx
+ };
+ });
+
+ var itemSelectorGetter = function (array) {
+ return BI.map(array, function (idx, num) {
+ return ".bi-multi-select-popup-view .bi-loader .bi-button-group .bi-multi-select-item:nth-child(" + num + ")";
+ });
+ };
+
+ var searchItemSelectorGetter = function (array) {
+ return BI.map(array, function (idx, num) {
+ return ".bi-multi-select-search-pane .bi-loader .bi-button-group .bi-multi-select-item:nth-child(" + num + ")";
+ });
+ };
+
+ /**
+ * test_author_windy
+ **/
+ it("setValue", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.value_chooser_combo",
+ width: 220,
+ itemsCreator: function (op, callback) {
+ callback(items);
+ }
+ });
+ widget.setValue({
+ type: 1,
+ value: [1, 2]
+ });
+ expect(widget.getValue()).to.deep.equal({
+ type: 1,
+ value: [1, 2]
+ });
+ widget.destroy();
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("getValue", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.value_chooser_combo",
+ width: 220,
+ itemsCreator: function (op, callback) {
+ callback(items);
+ },
+ value: {
+ type: 2,
+ value: [1, 2, 3]
+ }
+ });
+ expect(widget.getValue()).to.deep.equal({
+ type: 2,
+ value: [1, 2, 3]
+ });
+ widget.destroy();
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("点选选值", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.value_chooser_combo",
+ width: 220,
+ itemsCreator: function (op, callback) {
+ callback(items);
+ }
+ });
+ widget.element.find(".bi-multi-select-trigger").click();
+ // 为什么要delay 300呢,因为按钮有debounce
+ BI.delay(function () {
+ // 点选1、2、3
+ BI.each(itemSelectorGetter([1,2,3]), function (idx, selector) {
+ widget.element.find(selector).click();
+ });
+ // 点全选
+ widget.element.find(".bi-multi-select-popup-view .bi-label:contains(全选)").click();
+ // 取消勾选1、2、3
+ BI.delay(function () {
+ BI.each(itemSelectorGetter([1,2,3]), function (idx, selector) {
+ widget.element.find(selector).click();
+ });
+ expect(widget.getValue()).to.deep.equal({
+ type: 2,
+ value: [0, 1, 2]
+ });
+ widget.destroy();
+ done();
+ }, 300);
+ }, 300);
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("搜索选值", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.value_chooser_combo",
+ width: 220,
+ itemsCreator: function (op, callback) {
+ callback(items);
+ }
+ });
+ BI.nextTick(function () {
+ widget.element.find(".bi-multi-select-trigger .tip-text-style").click();
+ // 这边为啥要加呢,因为input的setValue中有nextTick
+ BI.nextTick(function () {
+ BI.Test.triggerKeyDown(widget.element.find(".bi-multi-select-trigger .bi-input"), "2", 50, function () {
+ BI.nextTick(function () {
+ BI.each(searchItemSelectorGetter([1,2]), function (idx, selector) {
+ widget.element.find(selector).click();
+ });
+ expect(widget.getValue()).to.deep.equal({
+ type: 1,
+ value: [2, 12]
+ });
+ widget.destroy();
+ done();
+ });
+ });
+ });
+ });
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("查看已选", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.value_chooser_combo",
+ width: 220,
+ itemsCreator: function (op, callback) {
+ callback(items);
+ },
+ value: {
+ type: 1,
+ value: [1, 2]
+ }
+ });
+ BI.nextTick(function () {
+ widget.element.find(".bi-multi-select-check-selected-button").click();
+ BI.delay(function () {
+ expect(widget.element.find(".display-list-item").length).to.equal(2);
+ widget.destroy();
+ done();
+ }, 300);
+ });
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/component/valuechooser/__test__/pane.valuechooser.test.js b/src/main/resources/com/fr/fineui/component/valuechooser/__test__/pane.valuechooser.test.js
new file mode 100644
index 0000000..76e9390
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/valuechooser/__test__/pane.valuechooser.test.js
@@ -0,0 +1,111 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2019/9/23
+ */
+
+describe("value_chooser_pane", function () {
+
+ var items = BI.map(BI.makeArray(1000, null), function(idx, v) {
+ return {
+ text: idx,
+ value: idx,
+ title: idx
+ };
+ });
+
+ var itemSelectorGetter = function (array) {
+ return BI.map(array, function (idx, num) {
+ return ".popup-multi-select-list .bi-loader .bi-button-group .bi-multi-select-item:nth-child(" + num + ")";
+ });
+ };
+
+ var searchItemSelectorGetter = function (array) {
+ return BI.map(array, function (idx, num) {
+ return ".bi-multi-select-search-pane .bi-loader .bi-button-group .bi-multi-select-item:nth-child(" + num + ")";
+ });
+ };
+
+ /**
+ * test_author_windy
+ **/
+ it("setValue", function () {
+ var widget = BI.Test.createWidget({
+ type: "bi.value_chooser_pane",
+ width: 220,
+ itemsCreator: function (op, callback) {
+ callback(items);
+ }
+ });
+ widget.setValue({
+ type: 1,
+ value: [1, 2]
+ });
+ expect(widget.getValue()).to.deep.equal({
+ type: 1,
+ value: [1, 2]
+ });
+ widget.destroy();
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("点选选值", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.value_chooser_pane",
+ width: 220,
+ items: items
+ });
+ BI.nextTick(function () {
+ // 点选1、2、3
+ BI.each(itemSelectorGetter([1,2,3]), function (idx, selector) {
+ widget.element.find(selector).click();
+ });
+ // 点全选
+ widget.element.find(".popup-multi-select-list .bi-label:contains(全选)").click();
+ // 取消勾选1、2、3
+ BI.delay(function () {
+ BI.each(itemSelectorGetter([1,2,3]), function (idx, selector) {
+ widget.element.find(selector).click();
+ });
+ expect(widget.getValue()).to.deep.equal({
+ type: 2,
+ value: [0, 1, 2]
+ });
+ widget.destroy();
+ done();
+ }, 300);
+ });
+ });
+
+ /**
+ * test_author_windy
+ **/
+ it("搜索选值", function (done) {
+ var widget = BI.Test.createWidget({
+ type: "bi.value_chooser_pane",
+ width: 220,
+ itemsCreator: function (op, callback) {
+ callback(items);
+ }
+ });
+ widget.element.find(".bi-water-mark").click();
+ // 这边为啥要加呢,因为input的setValue中有nextTick
+ BI.nextTick(function () {
+ BI.Test.triggerKeyDown(widget.element.find(".bi-input"), "2", 50, function () {
+ BI.nextTick(function () {
+ BI.each(searchItemSelectorGetter([1,2]), function (idx, selector) {
+ widget.element.find(selector).click();
+ });
+ expect(widget.getValue()).to.deep.equal({
+ type: 1,
+ value: [2, 12]
+ });
+ widget.destroy();
+ done();
+ });
+ });
+ });
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/component/valuechooser/abstract.valuechooser.js b/src/main/resources/com/fr/fineui/component/valuechooser/abstract.valuechooser.js
new file mode 100644
index 0000000..bd1ab30
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/valuechooser/abstract.valuechooser.js
@@ -0,0 +1,107 @@
+/**
+ * 简单的复选下拉框控件, 适用于数据量少的情况
+ * 封装了字段处理逻辑
+ *
+ * Created by GUY on 2015/10/29.
+ * @class BI.AbstractValueChooser
+ * @extends BI.Widget
+ */
+BI.AbstractValueChooser = BI.inherit(BI.Widget, {
+
+ _const: {
+ perPage: 100
+ },
+
+ _defaultConfig: function () {
+ return BI.extend(BI.AbstractValueChooser.superclass._defaultConfig.apply(this, arguments), {
+ items: null,
+ itemsCreator: BI.emptyFn,
+ cache: true
+ });
+ },
+
+ _valueFormatter: function (v) {
+ var text = v;
+ if (BI.isNotNull(this.items)) {
+ BI.some(this.items, function (i, item) {
+ // 把value都换成字符串
+ if (item.value === v || item.value + "" === v) {
+ text = item.text;
+ return true;
+ }
+ });
+ }
+ return text;
+ },
+
+ _getItemsByTimes: function (items, times) {
+ var res = [];
+ for (var i = (times - 1) * this._const.perPage; items[i] && i < times * this._const.perPage; i++) {
+ res.push(items[i]);
+ }
+ return res;
+ },
+
+ _hasNextByTimes: function (items, times) {
+ return times * this._const.perPage < items.length;
+ },
+
+ _itemsCreator: function (options, callback) {
+ var self = this, o = this.options;
+ if (!o.cache || !this.items) {
+ o.itemsCreator({}, function (items) {
+ self.items = items;
+ call(items);
+ });
+ } else {
+ call(this.items);
+ }
+ function call (items) {
+ var keywords = (options.keywords || []).slice();
+ var resultItems = items;
+ if(BI.isNotEmptyArray(keywords)) {
+ resultItems = [];
+ BI.each(keywords, function (i, kw) {
+ var search = BI.Func.getSearchResult(items, kw);
+ resultItems = resultItems.concat(search.match).concat(search.find);
+ });
+ resultItems = BI.uniq(resultItems);
+ }
+ if (options.selectedValues) {// 过滤
+ var filter = BI.makeObject(options.selectedValues, true);
+ resultItems = BI.filter(resultItems, function (i, ob) {
+ return !filter[ob.value];
+ });
+ }
+ if (options.type === BI.MultiSelectCombo.REQ_GET_ALL_DATA) {
+ callback({
+ items: resultItems
+ });
+ return;
+ }
+ if (options.type === BI.MultiSelectCombo.REQ_GET_DATA_LENGTH) {
+ callback({count: resultItems.length});
+ return;
+ }
+ callback({
+ items: self._getItemsByTimes(resultItems, options.times),
+ hasNext: self._hasNextByTimes(resultItems, options.times)
+ });
+ }
+ },
+
+ _assertValue: function (v) {
+ v = v || {};
+ var value = v;
+ if (v.type === BI.Selection.Multi && BI.isNotNull(this.items)) {
+ var isAllSelect = BI.difference(BI.map(this.items, "value"), v.value).length === 0;
+ if (isAllSelect) {
+ value = {
+ type: BI.Selection.All,
+ value: [],
+ };
+ }
+ }
+ return value;
+ },
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/component/valuechooser/combo.valuechooser.insert.js b/src/main/resources/com/fr/fineui/component/valuechooser/combo.valuechooser.insert.js
new file mode 100644
index 0000000..cc64d15
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/valuechooser/combo.valuechooser.insert.js
@@ -0,0 +1,104 @@
+/**
+ * 简单的复选下拉框控件, 适用于数据量少的情况
+ * 封装了字段处理逻辑
+ */
+BI.ValueChooserInsertCombo = BI.inherit(BI.AbstractValueChooser, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.ValueChooserInsertCombo.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-value-chooser-insert-combo",
+ width: 200,
+ height: 24,
+ items: null,
+ itemsCreator: BI.emptyFn,
+ cache: true
+ });
+ },
+
+ _init: function () {
+ BI.ValueChooserInsertCombo.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ if (BI.isNotNull(o.items)) {
+ this.items = o.items;
+ }
+ this.combo = BI.createWidget({
+ type: "bi.multi_select_insert_combo",
+ element: this,
+ allowEdit: o.allowEdit,
+ text: o.text,
+ value: this._assertValue(o.value),
+ itemsCreator: BI.bind(this._itemsCreator, this),
+ valueFormatter: BI.bind(this._valueFormatter, this),
+ width: o.width,
+ height: o.height,
+ listeners: [{
+ eventName: BI.MultiSelectCombo.EVENT_FOCUS,
+ action: function () {
+ self.fireEvent(BI.ValueChooserInsertCombo.EVENT_FOCUS);
+ }
+ }, {
+ eventName: BI.MultiSelectCombo.EVENT_BLUR,
+ action: function () {
+ self.fireEvent(BI.ValueChooserInsertCombo.EVENT_BLUR);
+ }
+ }, {
+ eventName: BI.MultiSelectCombo.EVENT_STOP,
+ action: function () {
+ self.fireEvent(BI.ValueChooserInsertCombo.EVENT_STOP);
+ }
+ }, {
+ eventName: BI.MultiSelectCombo.EVENT_CLICK_ITEM,
+ action: function () {
+ self.fireEvent(BI.ValueChooserInsertCombo.EVENT_CLICK_ITEM);
+ }
+ }, {
+ eventName: BI.MultiSelectCombo.EVENT_SEARCHING,
+ action: function () {
+ self.fireEvent(BI.ValueChooserInsertCombo.EVENT_SEARCHING);
+ }
+ }, {
+ eventName: BI.MultiSelectCombo.EVENT_CONFIRM,
+ action: function () {
+ self.fireEvent(BI.ValueChooserInsertCombo.EVENT_CONFIRM);
+ }
+ }]
+ });
+ },
+
+ setValue: function (v) {
+ this.combo.setValue(this._assertValue(v));
+ },
+
+ getValue: function () {
+ var val = this.combo.getValue() || {};
+ return {
+ type: val.type,
+ value: val.value
+ };
+ },
+
+ getAllValue: function() {
+ var val = this.combo.getValue() || {};
+ if (val.type === BI.Selection.Multi) {
+ return val.value || [];
+ }
+
+ return BI.difference(BI.map(this.items, "value"), val.value || []);
+ },
+
+ populate: function (items) {
+ // 直接用combo的populate不会作用到AbstractValueChooser上
+ if (BI.isNotNull(items)) {
+ this.items = items;
+ }
+ this.combo.populate();
+ }
+});
+
+BI.ValueChooserInsertCombo.EVENT_BLUR = "EVENT_BLUR";
+BI.ValueChooserInsertCombo.EVENT_FOCUS = "EVENT_FOCUS";
+BI.ValueChooserInsertCombo.EVENT_STOP = "EVENT_STOP";
+BI.ValueChooserInsertCombo.EVENT_SEARCHING = "EVENT_SEARCHING";
+BI.ValueChooserInsertCombo.EVENT_CLICK_ITEM = "EVENT_CLICK_ITEM";
+BI.ValueChooserInsertCombo.EVENT_CONFIRM = "EVENT_CONFIRM";
+BI.shortcut("bi.value_chooser_insert_combo", BI.ValueChooserInsertCombo);
diff --git a/src/main/resources/com/fr/fineui/component/valuechooser/combo.valuechooser.js b/src/main/resources/com/fr/fineui/component/valuechooser/combo.valuechooser.js
new file mode 100644
index 0000000..9f1a05f
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/valuechooser/combo.valuechooser.js
@@ -0,0 +1,108 @@
+/**
+ * 简单的复选下拉框控件, 适用于数据量少的情况
+ * 封装了字段处理逻辑
+ *
+ * Created by GUY on 2015/10/29.
+ * @class BI.ValueChooserCombo
+ * @extends BI.Widget
+ */
+BI.ValueChooserCombo = BI.inherit(BI.AbstractValueChooser, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.ValueChooserCombo.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-value-chooser-combo",
+ width: 200,
+ height: 24,
+ items: null,
+ itemsCreator: BI.emptyFn,
+ cache: true
+ });
+ },
+
+ _init: function () {
+ BI.ValueChooserCombo.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ if (BI.isNotNull(o.items)) {
+ this.items = o.items;
+ }
+ this.combo = BI.createWidget({
+ type: "bi.multi_select_combo",
+ element: this,
+ allowEdit: o.allowEdit,
+ text: o.text,
+ value: this._assertValue(o.value),
+ itemsCreator: BI.bind(this._itemsCreator, this),
+ valueFormatter: BI.bind(this._valueFormatter, this),
+ width: o.width,
+ height: o.height,
+ listeners: [{
+ eventName: BI.MultiSelectCombo.EVENT_FOCUS,
+ action: function () {
+ self.fireEvent(BI.ValueChooserCombo.EVENT_FOCUS);
+ }
+ }, {
+ eventName: BI.MultiSelectCombo.EVENT_BLUR,
+ action: function () {
+ self.fireEvent(BI.ValueChooserCombo.EVENT_BLUR);
+ }
+ }, {
+ eventName: BI.MultiSelectCombo.EVENT_STOP,
+ action: function () {
+ self.fireEvent(BI.ValueChooserCombo.EVENT_STOP);
+ }
+ }, {
+ eventName: BI.MultiSelectCombo.EVENT_CLICK_ITEM,
+ action: function () {
+ self.fireEvent(BI.ValueChooserCombo.EVENT_CLICK_ITEM);
+ }
+ }, {
+ eventName: BI.MultiSelectCombo.EVENT_SEARCHING,
+ action: function () {
+ self.fireEvent(BI.ValueChooserCombo.EVENT_SEARCHING);
+ }
+ }, {
+ eventName: BI.MultiSelectCombo.EVENT_CONFIRM,
+ action: function () {
+ self.fireEvent(BI.ValueChooserCombo.EVENT_CONFIRM);
+ }
+ }]
+ });
+ },
+
+ setValue: function (v) {
+ this.combo.setValue(this._assertValue(v));
+ },
+
+ getValue: function () {
+ var val = this.combo.getValue() || {};
+ return {
+ type: val.type,
+ value: val.value
+ };
+ },
+
+ getAllValue: function() {
+ var val = this.combo.getValue() || {};
+ if (val.type === BI.Selection.Multi) {
+ return val.value || [];
+ }
+
+ return BI.difference(BI.map(this.items, "value"), val.value || []);
+ },
+
+ populate: function (items) {
+ // 直接用combo的populate不会作用到AbstractValueChooser上
+ if (BI.isNotNull(items)) {
+ this.items = items;
+ }
+ this.combo.populate();
+ }
+});
+
+BI.ValueChooserCombo.EVENT_BLUR = "EVENT_BLUR";
+BI.ValueChooserCombo.EVENT_FOCUS = "EVENT_FOCUS";
+BI.ValueChooserCombo.EVENT_STOP = "EVENT_STOP";
+BI.ValueChooserCombo.EVENT_SEARCHING = "EVENT_SEARCHING";
+BI.ValueChooserCombo.EVENT_CLICK_ITEM = "EVENT_CLICK_ITEM";
+BI.ValueChooserCombo.EVENT_CONFIRM = "EVENT_CONFIRM";
+BI.shortcut("bi.value_chooser_combo", BI.ValueChooserCombo);
diff --git a/src/main/resources/com/fr/fineui/component/valuechooser/combo.valuechooser.nobar.js b/src/main/resources/com/fr/fineui/component/valuechooser/combo.valuechooser.nobar.js
new file mode 100644
index 0000000..3fc381d
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/valuechooser/combo.valuechooser.nobar.js
@@ -0,0 +1,96 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/12/31
+ */
+BI.ValueChooserNoBarCombo = BI.inherit(BI.AbstractValueChooser, {
+
+ props: {
+ baseCls: "bi-value-chooser-combo",
+ width: 200,
+ height: 24,
+ items: null,
+ itemsCreator: BI.emptyFn,
+ cache: true
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ if (BI.isNotNull(o.items)) {
+ this.items = o.items;
+ }
+
+ return {
+ type: "bi.multi_select_no_bar_combo",
+ allowEdit: o.allowEdit,
+ text: o.text,
+ value: this._assertValue(o.value),
+ itemsCreator: BI.bind(this._itemsCreator, this),
+ valueFormatter: BI.bind(this._valueFormatter, this),
+ width: o.width,
+ height: o.height,
+ ref: function(_ref) {
+ self.combo = _ref;
+ },
+ listeners: [{
+ eventName: BI.MultiSelectCombo.EVENT_FOCUS,
+ action: function () {
+ self.fireEvent(BI.ValueChooserNoBarCombo.EVENT_FOCUS);
+ }
+ }, {
+ eventName: BI.MultiSelectCombo.EVENT_BLUR,
+ action: function () {
+ self.fireEvent(BI.ValueChooserNoBarCombo.EVENT_BLUR);
+ }
+ }, {
+ eventName: BI.MultiSelectCombo.EVENT_STOP,
+ action: function () {
+ self.fireEvent(BI.ValueChooserNoBarCombo.EVENT_STOP);
+ }
+ }, {
+ eventName: BI.MultiSelectCombo.EVENT_CLICK_ITEM,
+ action: function () {
+ self.fireEvent(BI.ValueChooserNoBarCombo.EVENT_CLICK_ITEM);
+ }
+ }, {
+ eventName: BI.MultiSelectCombo.EVENT_SEARCHING,
+ action: function () {
+ self.fireEvent(BI.ValueChooserNoBarCombo.EVENT_SEARCHING);
+ }
+ }, {
+ eventName: BI.MultiSelectCombo.EVENT_CONFIRM,
+ action: function () {
+ self.fireEvent(BI.ValueChooserNoBarCombo.EVENT_CONFIRM);
+ }
+ }]
+ }
+ },
+
+ setValue: function (v) {
+ this.combo.setValue(v);
+ },
+
+ getValue: function () {
+ return this.combo.getValue();
+ },
+
+ getAllValue: function() {
+ return this.getValue();
+ },
+
+ populate: function (items) {
+ // 直接用combo的populate不会作用到AbstractValueChooser上
+ if (BI.isNotNull(items)) {
+ this.items = items;
+ }
+ this.combo.populate();
+ }
+});
+
+BI.ValueChooserNoBarCombo.EVENT_BLUR = "EVENT_BLUR";
+BI.ValueChooserNoBarCombo.EVENT_FOCUS = "EVENT_FOCUS";
+BI.ValueChooserNoBarCombo.EVENT_STOP = "EVENT_STOP";
+BI.ValueChooserNoBarCombo.EVENT_SEARCHING = "EVENT_SEARCHING";
+BI.ValueChooserNoBarCombo.EVENT_CLICK_ITEM = "EVENT_CLICK_ITEM";
+BI.ValueChooserNoBarCombo.EVENT_CONFIRM = "EVENT_CONFIRM";
+BI.shortcut("bi.value_chooser_no_bar_combo", BI.ValueChooserNoBarCombo);
diff --git a/src/main/resources/com/fr/fineui/component/valuechooser/pane.valuechooser.js b/src/main/resources/com/fr/fineui/component/valuechooser/pane.valuechooser.js
new file mode 100644
index 0000000..d483f26
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/component/valuechooser/pane.valuechooser.js
@@ -0,0 +1,70 @@
+/**
+ * 简单的复选面板, 适用于数据量少的情况
+ * 封装了字段处理逻辑
+ *
+ * Created by GUY on 2015/10/29.
+ * @class BI.ValueChooserPane
+ * @extends BI.Widget
+ */
+BI.ValueChooserPane = BI.inherit(BI.AbstractValueChooser, {
+
+ _defaultConfig: function () {
+ return BI.extend(BI.ValueChooserPane.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-value-chooser-pane",
+ items: null,
+ itemsCreator: BI.emptyFn,
+ cache: true
+ });
+ },
+
+ _init: function () {
+ BI.ValueChooserPane.superclass._init.apply(this, arguments);
+ var self = this, o = this.options;
+ this.list = BI.createWidget({
+ type: "bi.multi_select_list",
+ element: this,
+ value: o.value,
+ itemsCreator: BI.bind(this._itemsCreator, this),
+ valueFormatter: BI.bind(this._valueFormatter, this)
+ });
+
+ this.list.on(BI.MultiSelectList.EVENT_CHANGE, function () {
+ self.fireEvent(BI.ValueChooserPane.EVENT_CHANGE);
+ });
+ if (BI.isNotNull(o.items)) {
+ this.items = o.items;
+ this.list.populate();
+ }
+ },
+
+ setValue: function (v) {
+ this.list.setValue(v);
+ },
+
+ getValue: function () {
+ var val = this.list.getValue() || {};
+ return {
+ type: val.type,
+ value: val.value
+ };
+ },
+
+ getAllValue: function() {
+ var val = this.combo.getValue() || {};
+ if (val.type === BI.Selection.Multi) {
+ return val.value || [];
+ }
+
+ return BI.difference(BI.map(this.items, "value"), val.value || []);
+ },
+
+ populate: function (items) {
+ // 直接用combo的populate不会作用到AbstractValueChooser上
+ if (BI.isNotNull(items)) {
+ this.items = items;
+ }
+ this.list.populate();
+ }
+});
+BI.ValueChooserPane.EVENT_CHANGE = "EVENT_CHANGE";
+BI.shortcut("bi.value_chooser_pane", BI.ValueChooserPane);
diff --git a/src/main/resources/com/fr/fineui/core/0.foundation.js b/src/main/resources/com/fr/fineui/core/0.foundation.js
new file mode 100644
index 0000000..067178c
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/0.foundation.js
@@ -0,0 +1,22 @@
+/**
+ * Created by richie on 15/7/8.
+ */
+/**
+ * 初始化BI对象
+ */
+_global = undefined;
+if (typeof window !== "undefined") {
+ _global = window;
+} else if (typeof global !== "undefined") {
+ _global = global;
+} else if (typeof self !== "undefined") {
+ _global = self;
+} else {
+ _global = this;
+}
+if (_global.BI == null) {
+ _global.BI = {prepares: []};
+}
+if(_global.BI.prepares == null) {
+ _global.BI.prepares = [];
+}
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/core/1.lodash.js b/src/main/resources/com/fr/fineui/core/1.lodash.js
new file mode 100644
index 0000000..cab97c0
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/1.lodash.js
@@ -0,0 +1,10275 @@
+/**
+ * @license
+ * Lodash (Custom Build)
+ * Build: `lodash core plus="debounce,throttle,get,set,findIndex,findLastIndex,findKey,findLastKey,isArrayLike,invert,invertBy,uniq,uniqBy,omit,omitBy,zip,unzip,rest,range,random,reject,intersection,drop,countBy,union,zipObject,initial,cloneDeep,clamp,isPlainObject,take,takeRight,without,difference,defaultsDeep,trim,merge,groupBy,uniqBy,before,after,unescape"`
+ * Copyright JS Foundation and other contributors
+ * Released under MIT license
+ * Based on Underscore.js 1.8.3
+ * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ */
+;(function() {
+
+ /** Used as a safe reference for `undefined` in pre-ES5 environments. */
+ var undefined;
+
+ /** Used as the semantic version number. */
+ var VERSION = '4.17.5';
+
+ /** Used as the size to enable large array optimizations. */
+ var LARGE_ARRAY_SIZE = 200;
+
+ /** Error message constants. */
+ var FUNC_ERROR_TEXT = 'Expected a function';
+
+ /** Used to stand-in for `undefined` hash values. */
+ var HASH_UNDEFINED = '__lodash_hash_undefined__';
+
+ /** Used as the maximum memoize cache size. */
+ var MAX_MEMOIZE_SIZE = 500;
+
+ /** Used as the internal argument placeholder. */
+ var PLACEHOLDER = '__lodash_placeholder__';
+
+ /** Used to compose bitmasks for cloning. */
+ var CLONE_DEEP_FLAG = 1,
+ CLONE_FLAT_FLAG = 2,
+ CLONE_SYMBOLS_FLAG = 4;
+
+ /** Used to compose bitmasks for value comparisons. */
+ var COMPARE_PARTIAL_FLAG = 1,
+ COMPARE_UNORDERED_FLAG = 2;
+
+ /** Used to compose bitmasks for function metadata. */
+ var WRAP_BIND_FLAG = 1,
+ WRAP_BIND_KEY_FLAG = 2,
+ WRAP_CURRY_BOUND_FLAG = 4,
+ WRAP_CURRY_FLAG = 8,
+ WRAP_CURRY_RIGHT_FLAG = 16,
+ WRAP_PARTIAL_FLAG = 32,
+ WRAP_PARTIAL_RIGHT_FLAG = 64,
+ WRAP_ARY_FLAG = 128,
+ WRAP_REARG_FLAG = 256,
+ WRAP_FLIP_FLAG = 512;
+
+ /** Used to detect hot functions by number of calls within a span of milliseconds. */
+ var HOT_COUNT = 800,
+ HOT_SPAN = 16;
+
+ /** Used to indicate the type of lazy iteratees. */
+ var LAZY_FILTER_FLAG = 1,
+ LAZY_MAP_FLAG = 2,
+ LAZY_WHILE_FLAG = 3;
+
+ /** Used as references for various `Number` constants. */
+ var INFINITY = 1 / 0,
+ MAX_SAFE_INTEGER = 9007199254740991,
+ MAX_INTEGER = 1.7976931348623157e+308,
+ NAN = 0 / 0;
+
+ /** Used as references for the maximum length and index of an array. */
+ var MAX_ARRAY_LENGTH = 4294967295;
+
+ /** Used to associate wrap methods with their bit flags. */
+ var wrapFlags = [
+ ['ary', WRAP_ARY_FLAG],
+ ['bind', WRAP_BIND_FLAG],
+ ['bindKey', WRAP_BIND_KEY_FLAG],
+ ['curry', WRAP_CURRY_FLAG],
+ ['curryRight', WRAP_CURRY_RIGHT_FLAG],
+ ['flip', WRAP_FLIP_FLAG],
+ ['partial', WRAP_PARTIAL_FLAG],
+ ['partialRight', WRAP_PARTIAL_RIGHT_FLAG],
+ ['rearg', WRAP_REARG_FLAG]
+ ];
+
+ /** `Object#toString` result references. */
+ var argsTag = '[object Arguments]',
+ arrayTag = '[object Array]',
+ asyncTag = '[object AsyncFunction]',
+ boolTag = '[object Boolean]',
+ dateTag = '[object Date]',
+ errorTag = '[object Error]',
+ funcTag = '[object Function]',
+ genTag = '[object GeneratorFunction]',
+ mapTag = '[object Map]',
+ numberTag = '[object Number]',
+ nullTag = '[object Null]',
+ objectTag = '[object Object]',
+ promiseTag = '[object Promise]',
+ proxyTag = '[object Proxy]',
+ regexpTag = '[object RegExp]',
+ setTag = '[object Set]',
+ stringTag = '[object String]',
+ symbolTag = '[object Symbol]',
+ undefinedTag = '[object Undefined]',
+ weakMapTag = '[object WeakMap]';
+
+ var arrayBufferTag = '[object ArrayBuffer]',
+ dataViewTag = '[object DataView]',
+ float32Tag = '[object Float32Array]',
+ float64Tag = '[object Float64Array]',
+ int8Tag = '[object Int8Array]',
+ int16Tag = '[object Int16Array]',
+ int32Tag = '[object Int32Array]',
+ uint8Tag = '[object Uint8Array]',
+ uint8ClampedTag = '[object Uint8ClampedArray]',
+ uint16Tag = '[object Uint16Array]',
+ uint32Tag = '[object Uint32Array]';
+
+ /** Used to match HTML entities and HTML characters. */
+ var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g,
+ reUnescapedHtml = /[&<>"']/g,
+ reHasEscapedHtml = RegExp(reEscapedHtml.source),
+ reHasUnescapedHtml = RegExp(reUnescapedHtml.source);
+
+ /** Used to match property names within property paths. */
+ var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
+ reIsPlainProp = /^\w*$/,
+ rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;
+
+ /**
+ * Used to match `RegExp`
+ * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
+ */
+ var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
+
+ /** Used to match leading and trailing whitespace. */
+ var reTrim = /^\s+|\s+$/g;
+
+ /** Used to match wrap detail comments. */
+ var reWrapComment = /\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,
+ reWrapDetails = /\{\n\/\* \[wrapped with (.+)\] \*/,
+ reSplitDetails = /,? & /;
+
+ /** Used to match backslashes in property paths. */
+ var reEscapeChar = /\\(\\)?/g;
+
+ /** Used to match `RegExp` flags from their coerced string values. */
+ var reFlags = /\w*$/;
+
+ /** Used to detect bad signed hexadecimal string values. */
+ var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
+
+ /** Used to detect binary string values. */
+ var reIsBinary = /^0b[01]+$/i;
+
+ /** Used to detect host constructors (Safari). */
+ var reIsHostCtor = /^\[object .+?Constructor\]$/;
+
+ /** Used to detect octal string values. */
+ var reIsOctal = /^0o[0-7]+$/i;
+
+ /** Used to detect unsigned integer values. */
+ var reIsUint = /^(?:0|[1-9]\d*)$/;
+
+ /** Used to compose unicode character classes. */
+ var rsAstralRange = '\\ud800-\\udfff',
+ rsComboMarksRange = '\\u0300-\\u036f',
+ reComboHalfMarksRange = '\\ufe20-\\ufe2f',
+ rsComboSymbolsRange = '\\u20d0-\\u20ff',
+ rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange,
+ rsVarRange = '\\ufe0e\\ufe0f';
+
+ /** Used to compose unicode capture groups. */
+ var rsAstral = '[' + rsAstralRange + ']',
+ rsCombo = '[' + rsComboRange + ']',
+ rsFitz = '\\ud83c[\\udffb-\\udfff]',
+ rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',
+ rsNonAstral = '[^' + rsAstralRange + ']',
+ rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}',
+ rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]',
+ rsZWJ = '\\u200d';
+
+ /** Used to compose unicode regexes. */
+ var reOptMod = rsModifier + '?',
+ rsOptVar = '[' + rsVarRange + ']?',
+ rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',
+ rsSeq = rsOptVar + reOptMod + rsOptJoin,
+ rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';
+
+ /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */
+ var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');
+
+ /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */
+ var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']');
+
+ /** Used to identify `toStringTag` values of typed arrays. */
+ var typedArrayTags = {};
+ typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
+ typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
+ typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
+ typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
+ typedArrayTags[uint32Tag] = true;
+ typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
+ typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
+ typedArrayTags[dataViewTag] = typedArrayTags[dateTag] =
+ typedArrayTags[errorTag] = typedArrayTags[funcTag] =
+ typedArrayTags[mapTag] = typedArrayTags[numberTag] =
+ typedArrayTags[objectTag] = typedArrayTags[regexpTag] =
+ typedArrayTags[setTag] = typedArrayTags[stringTag] =
+ typedArrayTags[weakMapTag] = false;
+
+ /** Used to identify `toStringTag` values supported by `_.clone`. */
+ var cloneableTags = {};
+ cloneableTags[argsTag] = cloneableTags[arrayTag] =
+ cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =
+ cloneableTags[boolTag] = cloneableTags[dateTag] =
+ cloneableTags[float32Tag] = cloneableTags[float64Tag] =
+ cloneableTags[int8Tag] = cloneableTags[int16Tag] =
+ cloneableTags[int32Tag] = cloneableTags[mapTag] =
+ cloneableTags[numberTag] = cloneableTags[objectTag] =
+ cloneableTags[regexpTag] = cloneableTags[setTag] =
+ cloneableTags[stringTag] = cloneableTags[symbolTag] =
+ cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
+ cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
+ cloneableTags[errorTag] = cloneableTags[funcTag] =
+ cloneableTags[weakMapTag] = false;
+
+ /** Used to map characters to HTML entities. */
+ var htmlEscapes = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": '''
+ };
+
+ /** Used to map HTML entities to characters. */
+ var htmlUnescapes = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ ''': "'"
+ };
+
+ /** Built-in method references without a dependency on `root`. */
+ var freeParseFloat = parseFloat,
+ freeParseInt = parseInt;
+
+ /** Detect free variable `global` from Node.js. */
+ var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
+
+ /** Detect free variable `self`. */
+ var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
+
+ /** Used as a reference to the global object. */
+ var root = freeGlobal || freeSelf || Function('return this')();
+
+ /** Detect free variable `exports`. */
+ var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
+
+ /** Detect free variable `module`. */
+ var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
+
+ /** Detect the popular CommonJS extension `module.exports`. */
+ var moduleExports = freeModule && freeModule.exports === freeExports;
+
+ /** Detect free variable `process` from Node.js. */
+ var freeProcess = moduleExports && freeGlobal.process;
+
+ /** Used to access faster Node.js helpers. */
+ var nodeUtil = (function() {
+ try {
+ return freeProcess && freeProcess.binding && freeProcess.binding('util');
+ } catch (e) {}
+ }());
+
+ /* Node.js helper references. */
+ var nodeIsDate = nodeUtil && nodeUtil.isDate,
+ nodeIsMap = nodeUtil && nodeUtil.isMap,
+ nodeIsRegExp = nodeUtil && nodeUtil.isRegExp,
+ nodeIsSet = nodeUtil && nodeUtil.isSet,
+ nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * A faster alternative to `Function#apply`, this function invokes `func`
+ * with the `this` binding of `thisArg` and the arguments of `args`.
+ *
+ * @private
+ * @param {Function} func The function to invoke.
+ * @param {*} thisArg The `this` binding of `func`.
+ * @param {Array} args The arguments to invoke `func` with.
+ * @returns {*} Returns the result of `func`.
+ */
+ function apply(func, thisArg, args) {
+ switch (args.length) {
+ case 0: return func.call(thisArg);
+ case 1: return func.call(thisArg, args[0]);
+ case 2: return func.call(thisArg, args[0], args[1]);
+ case 3: return func.call(thisArg, args[0], args[1], args[2]);
+ }
+ return func.apply(thisArg, args);
+ }
+
+ /**
+ * A specialized version of `baseAggregator` for arrays.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} setter The function to set `accumulator` values.
+ * @param {Function} iteratee The iteratee to transform keys.
+ * @param {Object} accumulator The initial aggregated object.
+ * @returns {Function} Returns `accumulator`.
+ */
+ function arrayAggregator(array, setter, iteratee, accumulator) {
+ var index = -1,
+ length = array == null ? 0 : array.length;
+
+ while (++index < length) {
+ var value = array[index];
+ setter(accumulator, value, iteratee(value), array);
+ }
+ return accumulator;
+ }
+
+ /**
+ * A specialized version of `_.forEach` for arrays without support for
+ * iteratee shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns `array`.
+ */
+ function arrayEach(array, iteratee) {
+ var index = -1,
+ length = array == null ? 0 : array.length;
+
+ while (++index < length) {
+ if (iteratee(array[index], index, array) === false) {
+ break;
+ }
+ }
+ return array;
+ }
+
+ /**
+ * A specialized version of `_.every` for arrays without support for
+ * iteratee shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {boolean} Returns `true` if all elements pass the predicate check,
+ * else `false`.
+ */
+ function arrayEvery(array, predicate) {
+ var index = -1,
+ length = array == null ? 0 : array.length;
+
+ while (++index < length) {
+ if (!predicate(array[index], index, array)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * A specialized version of `_.filter` for arrays without support for
+ * iteratee shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {Array} Returns the new filtered array.
+ */
+ function arrayFilter(array, predicate) {
+ var index = -1,
+ length = array == null ? 0 : array.length,
+ resIndex = 0,
+ result = [];
+
+ while (++index < length) {
+ var value = array[index];
+ if (predicate(value, index, array)) {
+ result[resIndex++] = value;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * A specialized version of `_.includes` for arrays without support for
+ * specifying an index to search from.
+ *
+ * @private
+ * @param {Array} [array] The array to inspect.
+ * @param {*} target The value to search for.
+ * @returns {boolean} Returns `true` if `target` is found, else `false`.
+ */
+ function arrayIncludes(array, value) {
+ var length = array == null ? 0 : array.length;
+ return !!length && baseIndexOf(array, value, 0) > -1;
+ }
+
+ /**
+ * This function is like `arrayIncludes` except that it accepts a comparator.
+ *
+ * @private
+ * @param {Array} [array] The array to inspect.
+ * @param {*} target The value to search for.
+ * @param {Function} comparator The comparator invoked per element.
+ * @returns {boolean} Returns `true` if `target` is found, else `false`.
+ */
+ function arrayIncludesWith(array, value, comparator) {
+ var index = -1,
+ length = array == null ? 0 : array.length;
+
+ while (++index < length) {
+ if (comparator(value, array[index])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * A specialized version of `_.map` for arrays without support for iteratee
+ * shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns the new mapped array.
+ */
+ function arrayMap(array, iteratee) {
+ var index = -1,
+ length = array == null ? 0 : array.length,
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = iteratee(array[index], index, array);
+ }
+ return result;
+ }
+
+ /**
+ * Appends the elements of `values` to `array`.
+ *
+ * @private
+ * @param {Array} array The array to modify.
+ * @param {Array} values The values to append.
+ * @returns {Array} Returns `array`.
+ */
+ function arrayPush(array, values) {
+ var index = -1,
+ length = values.length,
+ offset = array.length;
+
+ while (++index < length) {
+ array[offset + index] = values[index];
+ }
+ return array;
+ }
+
+ /**
+ * A specialized version of `_.reduce` for arrays without support for
+ * iteratee shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {*} [accumulator] The initial value.
+ * @param {boolean} [initAccum] Specify using the first element of `array` as
+ * the initial value.
+ * @returns {*} Returns the accumulated value.
+ */
+ function arrayReduce(array, iteratee, accumulator, initAccum) {
+ var index = -1,
+ length = array == null ? 0 : array.length;
+
+ if (initAccum && length) {
+ accumulator = array[++index];
+ }
+ while (++index < length) {
+ accumulator = iteratee(accumulator, array[index], index, array);
+ }
+ return accumulator;
+ }
+
+ /**
+ * A specialized version of `_.some` for arrays without support for iteratee
+ * shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {boolean} Returns `true` if any element passes the predicate check,
+ * else `false`.
+ */
+ function arraySome(array, predicate) {
+ var index = -1,
+ length = array == null ? 0 : array.length;
+
+ while (++index < length) {
+ if (predicate(array[index], index, array)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gets the size of an ASCII `string`.
+ *
+ * @private
+ * @param {string} string The string inspect.
+ * @returns {number} Returns the string size.
+ */
+ var asciiSize = baseProperty('length');
+
+ /**
+ * Converts an ASCII `string` to an array.
+ *
+ * @private
+ * @param {string} string The string to convert.
+ * @returns {Array} Returns the converted array.
+ */
+ function asciiToArray(string) {
+ return string.split('');
+ }
+
+ /**
+ * The base implementation of methods like `_.findKey` and `_.findLastKey`,
+ * without support for iteratee shorthands, which iterates over `collection`
+ * using `eachFunc`.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to inspect.
+ * @param {Function} predicate The function invoked per iteration.
+ * @param {Function} eachFunc The function to iterate over `collection`.
+ * @returns {*} Returns the found element or its key, else `undefined`.
+ */
+ function baseFindKey(collection, predicate, eachFunc) {
+ var result;
+ eachFunc(collection, function(value, key, collection) {
+ if (predicate(value, key, collection)) {
+ result = key;
+ return false;
+ }
+ });
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.findIndex` and `_.findLastIndex` without
+ * support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {Function} predicate The function invoked per iteration.
+ * @param {number} fromIndex The index to search from.
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+ function baseFindIndex(array, predicate, fromIndex, fromRight) {
+ var length = array.length,
+ index = fromIndex + (fromRight ? 1 : -1);
+
+ while ((fromRight ? index-- : ++index < length)) {
+ if (predicate(array[index], index, array)) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to search for.
+ * @param {number} fromIndex The index to search from.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+ function baseIndexOf(array, value, fromIndex) {
+ return value === value
+ ? strictIndexOf(array, value, fromIndex)
+ : baseFindIndex(array, baseIsNaN, fromIndex);
+ }
+
+ /**
+ * The base implementation of `_.isNaN` without support for number objects.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
+ */
+ function baseIsNaN(value) {
+ return value !== value;
+ }
+
+ /**
+ * The base implementation of `_.property` without support for deep paths.
+ *
+ * @private
+ * @param {string} key The key of the property to get.
+ * @returns {Function} Returns the new accessor function.
+ */
+ function baseProperty(key) {
+ return function(object) {
+ return object == null ? undefined : object[key];
+ };
+ }
+
+ /**
+ * The base implementation of `_.propertyOf` without support for deep paths.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @returns {Function} Returns the new accessor function.
+ */
+ function basePropertyOf(object) {
+ return function(key) {
+ return object == null ? undefined : object[key];
+ };
+ }
+
+ /**
+ * The base implementation of `_.reduce` and `_.reduceRight`, without support
+ * for iteratee shorthands, which iterates over `collection` using `eachFunc`.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {*} accumulator The initial value.
+ * @param {boolean} initAccum Specify using the first or last element of
+ * `collection` as the initial value.
+ * @param {Function} eachFunc The function to iterate over `collection`.
+ * @returns {*} Returns the accumulated value.
+ */
+ function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) {
+ eachFunc(collection, function(value, index, collection) {
+ accumulator = initAccum
+ ? (initAccum = false, value)
+ : iteratee(accumulator, value, index, collection);
+ });
+ return accumulator;
+ }
+
+ /**
+ * The base implementation of `_.sortBy` which uses `comparer` to define the
+ * sort order of `array` and replaces criteria objects with their corresponding
+ * values.
+ *
+ * @private
+ * @param {Array} array The array to sort.
+ * @param {Function} comparer The function to define sort order.
+ * @returns {Array} Returns `array`.
+ */
+ function baseSortBy(array, comparer) {
+ var length = array.length;
+
+ array.sort(comparer);
+ while (length--) {
+ array[length] = array[length].value;
+ }
+ return array;
+ }
+
+ /**
+ * The base implementation of `_.times` without support for iteratee shorthands
+ * or max array length checks.
+ *
+ * @private
+ * @param {number} n The number of times to invoke `iteratee`.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns the array of results.
+ */
+ function baseTimes(n, iteratee) {
+ var index = -1,
+ result = Array(n);
+
+ while (++index < n) {
+ result[index] = iteratee(index);
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.unary` without support for storing metadata.
+ *
+ * @private
+ * @param {Function} func The function to cap arguments for.
+ * @returns {Function} Returns the new capped function.
+ */
+ function baseUnary(func) {
+ return function(value) {
+ return func(value);
+ };
+ }
+
+ /**
+ * The base implementation of `_.values` and `_.valuesIn` which creates an
+ * array of `object` property values corresponding to the property names
+ * of `props`.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @param {Array} props The property names to get values for.
+ * @returns {Object} Returns the array of property values.
+ */
+ function baseValues(object, props) {
+ return arrayMap(props, function(key) {
+ return object[key];
+ });
+ }
+
+ /**
+ * Checks if a `cache` value for `key` exists.
+ *
+ * @private
+ * @param {Object} cache The cache to query.
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+ function cacheHas(cache, key) {
+ return cache.has(key);
+ }
+
+ /**
+ * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol
+ * that is not found in the character symbols.
+ *
+ * @private
+ * @param {Array} strSymbols The string symbols to inspect.
+ * @param {Array} chrSymbols The character symbols to find.
+ * @returns {number} Returns the index of the first unmatched string symbol.
+ */
+ function charsStartIndex(strSymbols, chrSymbols) {
+ var index = -1,
+ length = strSymbols.length;
+
+ while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
+ return index;
+ }
+
+ /**
+ * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol
+ * that is not found in the character symbols.
+ *
+ * @private
+ * @param {Array} strSymbols The string symbols to inspect.
+ * @param {Array} chrSymbols The character symbols to find.
+ * @returns {number} Returns the index of the last unmatched string symbol.
+ */
+ function charsEndIndex(strSymbols, chrSymbols) {
+ var index = strSymbols.length;
+
+ while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
+ return index;
+ }
+
+ /**
+ * Gets the number of `placeholder` occurrences in `array`.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} placeholder The placeholder to search for.
+ * @returns {number} Returns the placeholder count.
+ */
+ function countHolders(array, placeholder) {
+ var length = array.length,
+ result = 0;
+
+ while (length--) {
+ if (array[length] === placeholder) {
+ ++result;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Used by `_.escape` to convert characters to HTML entities.
+ *
+ * @private
+ * @param {string} chr The matched character to escape.
+ * @returns {string} Returns the escaped character.
+ */
+ var escapeHtmlChar = basePropertyOf(htmlEscapes);
+
+ /**
+ * Gets the value at `key` of `object`.
+ *
+ * @private
+ * @param {Object} [object] The object to query.
+ * @param {string} key The key of the property to get.
+ * @returns {*} Returns the property value.
+ */
+ function getValue(object, key) {
+ return object == null ? undefined : object[key];
+ }
+
+ /**
+ * Checks if `string` contains Unicode symbols.
+ *
+ * @private
+ * @param {string} string The string to inspect.
+ * @returns {boolean} Returns `true` if a symbol is found, else `false`.
+ */
+ function hasUnicode(string) {
+ return reHasUnicode.test(string);
+ }
+
+ /**
+ * Converts `iterator` to an array.
+ *
+ * @private
+ * @param {Object} iterator The iterator to convert.
+ * @returns {Array} Returns the converted array.
+ */
+ function iteratorToArray(iterator) {
+ var data,
+ result = [];
+
+ while (!(data = iterator.next()).done) {
+ result.push(data.value);
+ }
+ return result;
+ }
+
+ /**
+ * Converts `map` to its key-value pairs.
+ *
+ * @private
+ * @param {Object} map The map to convert.
+ * @returns {Array} Returns the key-value pairs.
+ */
+ function mapToArray(map) {
+ var index = -1,
+ result = Array(map.size);
+
+ map.forEach(function(value, key) {
+ result[++index] = [key, value];
+ });
+ return result;
+ }
+
+ /**
+ * Creates a unary function that invokes `func` with its argument transformed.
+ *
+ * @private
+ * @param {Function} func The function to wrap.
+ * @param {Function} transform The argument transform.
+ * @returns {Function} Returns the new function.
+ */
+ function overArg(func, transform) {
+ return function(arg) {
+ return func(transform(arg));
+ };
+ }
+
+ /**
+ * Replaces all `placeholder` elements in `array` with an internal placeholder
+ * and returns an array of their indexes.
+ *
+ * @private
+ * @param {Array} array The array to modify.
+ * @param {*} placeholder The placeholder to replace.
+ * @returns {Array} Returns the new array of placeholder indexes.
+ */
+ function replaceHolders(array, placeholder) {
+ var index = -1,
+ length = array.length,
+ resIndex = 0,
+ result = [];
+
+ while (++index < length) {
+ var value = array[index];
+ if (value === placeholder || value === PLACEHOLDER) {
+ array[index] = PLACEHOLDER;
+ result[resIndex++] = index;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Gets the value at `key`, unless `key` is "__proto__".
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @param {string} key The key of the property to get.
+ * @returns {*} Returns the property value.
+ */
+ function safeGet(object, key) {
+ return key == '__proto__'
+ ? undefined
+ : object[key];
+ }
+
+ /**
+ * Converts `set` to an array of its values.
+ *
+ * @private
+ * @param {Object} set The set to convert.
+ * @returns {Array} Returns the values.
+ */
+ function setToArray(set) {
+ var index = -1,
+ result = Array(set.size);
+
+ set.forEach(function(value) {
+ result[++index] = value;
+ });
+ return result;
+ }
+
+ /**
+ * A specialized version of `_.indexOf` which performs strict equality
+ * comparisons of values, i.e. `===`.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to search for.
+ * @param {number} fromIndex The index to search from.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+ function strictIndexOf(array, value, fromIndex) {
+ var index = fromIndex - 1,
+ length = array.length;
+
+ while (++index < length) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Gets the number of symbols in `string`.
+ *
+ * @private
+ * @param {string} string The string to inspect.
+ * @returns {number} Returns the string size.
+ */
+ function stringSize(string) {
+ return hasUnicode(string)
+ ? unicodeSize(string)
+ : asciiSize(string);
+ }
+
+ /**
+ * Converts `string` to an array.
+ *
+ * @private
+ * @param {string} string The string to convert.
+ * @returns {Array} Returns the converted array.
+ */
+ function stringToArray(string) {
+ return hasUnicode(string)
+ ? unicodeToArray(string)
+ : asciiToArray(string);
+ }
+
+ /**
+ * Used by `_.unescape` to convert HTML entities to characters.
+ *
+ * @private
+ * @param {string} chr The matched character to unescape.
+ * @returns {string} Returns the unescaped character.
+ */
+ var unescapeHtmlChar = basePropertyOf(htmlUnescapes);
+
+ /**
+ * Gets the size of a Unicode `string`.
+ *
+ * @private
+ * @param {string} string The string inspect.
+ * @returns {number} Returns the string size.
+ */
+ function unicodeSize(string) {
+ var result = reUnicode.lastIndex = 0;
+ while (reUnicode.test(string)) {
+ ++result;
+ }
+ return result;
+ }
+
+ /**
+ * Converts a Unicode `string` to an array.
+ *
+ * @private
+ * @param {string} string The string to convert.
+ * @returns {Array} Returns the converted array.
+ */
+ function unicodeToArray(string) {
+ return string.match(reUnicode) || [];
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /** Used for built-in method references. */
+ var arrayProto = Array.prototype,
+ funcProto = Function.prototype,
+ objectProto = Object.prototype;
+
+ /** Used to detect overreaching core-js shims. */
+ var coreJsData = root['__core-js_shared__'];
+
+ /** Used to resolve the decompiled source of functions. */
+ var funcToString = funcProto.toString;
+
+ /** Used to check objects for own properties. */
+ var hasOwnProperty = objectProto.hasOwnProperty;
+
+ /** Used to generate unique IDs. */
+ var idCounter = 0;
+
+ /** Used to detect methods masquerading as native. */
+ var maskSrcKey = (function() {
+ var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');
+ return uid ? ('Symbol(src)_1.' + uid) : '';
+ }());
+
+ /**
+ * Used to resolve the
+ * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
+ * of values.
+ */
+ var nativeObjectToString = objectProto.toString;
+
+ /** Used to infer the `Object` constructor. */
+ var objectCtorString = funcToString.call(Object);
+
+ /** Used to restore the original `_` reference in `_.noConflict`. */
+ var oldDash = root._;
+
+ /** Used to detect if a method is native. */
+ var reIsNative = RegExp('^' +
+ funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&')
+ .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
+ );
+
+ /** Built-in value references. */
+ var Buffer = moduleExports ? root.Buffer : undefined,
+ Symbol = root.Symbol,
+ Uint8Array = root.Uint8Array,
+ allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined,
+ getPrototype = overArg(Object.getPrototypeOf, Object),
+ objectCreate = Object.create,
+ propertyIsEnumerable = objectProto.propertyIsEnumerable,
+ splice = arrayProto.splice,
+ spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined,
+ symIterator = Symbol ? Symbol.iterator : undefined,
+ symToStringTag = Symbol ? Symbol.toStringTag : undefined;
+
+ var defineProperty = (function() {
+ try {
+ var func = getNative(Object, 'defineProperty');
+ func({}, '', {});
+ return func;
+ } catch (e) {}
+ }());
+
+ /* Built-in method references for those with the same name as other `lodash` methods. */
+ var nativeCeil = Math.ceil,
+ nativeFloor = Math.floor,
+ nativeGetSymbols = Object.getOwnPropertySymbols,
+ nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined,
+ nativeIsFinite = root.isFinite,
+ nativeKeys = overArg(Object.keys, Object),
+ nativeMax = Math.max,
+ nativeMin = Math.min,
+ nativeNow = Date.now,
+ nativeRandom = Math.random,
+ nativeReverse = arrayProto.reverse;
+
+ /* Built-in method references that are verified to be native. */
+ var DataView = getNative(root, 'DataView'),
+ Map = getNative(root, 'Map'),
+ Promise = getNative(root, 'Promise'),
+ Set = getNative(root, 'Set'),
+ WeakMap = getNative(root, 'WeakMap'),
+ nativeCreate = getNative(Object, 'create');
+
+ /** Used to store function metadata. */
+ var metaMap = WeakMap && new WeakMap;
+
+ /** Used to lookup unminified function names. */
+ var realNames = {};
+
+ /** Used to detect maps, sets, and weakmaps. */
+ var dataViewCtorString = toSource(DataView),
+ mapCtorString = toSource(Map),
+ promiseCtorString = toSource(Promise),
+ setCtorString = toSource(Set),
+ weakMapCtorString = toSource(WeakMap);
+
+ /** Used to convert symbols to primitives and strings. */
+ var symbolProto = Symbol ? Symbol.prototype : undefined,
+ symbolValueOf = symbolProto ? symbolProto.valueOf : undefined,
+ symbolToString = symbolProto ? symbolProto.toString : undefined;
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates a `lodash` object which wraps `value` to enable implicit method
+ * chain sequences. Methods that operate on and return arrays, collections,
+ * and functions can be chained together. Methods that retrieve a single value
+ * or may return a primitive value will automatically end the chain sequence
+ * and return the unwrapped value. Otherwise, the value must be unwrapped
+ * with `_#value`.
+ *
+ * Explicit chain sequences, which must be unwrapped with `_#value`, may be
+ * enabled using `_.chain`.
+ *
+ * The execution of chained methods is lazy, that is, it's deferred until
+ * `_#value` is implicitly or explicitly called.
+ *
+ * Lazy evaluation allows several methods to support shortcut fusion.
+ * Shortcut fusion is an optimization to merge iteratee calls; this avoids
+ * the creation of intermediate arrays and can greatly reduce the number of
+ * iteratee executions. Sections of a chain sequence qualify for shortcut
+ * fusion if the section is applied to an array and iteratees accept only
+ * one argument. The heuristic for whether a section qualifies for shortcut
+ * fusion is subject to change.
+ *
+ * Chaining is supported in custom builds as long as the `_#value` method is
+ * directly or indirectly included in the build.
+ *
+ * In addition to lodash methods, wrappers have `Array` and `String` methods.
+ *
+ * The wrapper `Array` methods are:
+ * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift`
+ *
+ * The wrapper `String` methods are:
+ * `replace` and `split`
+ *
+ * The wrapper methods that support shortcut fusion are:
+ * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`,
+ * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`,
+ * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray`
+ *
+ * The chainable wrapper methods are:
+ * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`,
+ * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`,
+ * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`,
+ * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`,
+ * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`,
+ * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`,
+ * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`,
+ * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`,
+ * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`,
+ * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`,
+ * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,
+ * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`,
+ * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`,
+ * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`,
+ * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`,
+ * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`,
+ * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`,
+ * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`,
+ * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`,
+ * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`,
+ * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`,
+ * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`,
+ * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`,
+ * `zipObject`, `zipObjectDeep`, and `zipWith`
+ *
+ * The wrapper methods that are **not** chainable by default are:
+ * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`,
+ * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`,
+ * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`,
+ * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`,
+ * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`,
+ * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`,
+ * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`,
+ * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`,
+ * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`,
+ * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`,
+ * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`,
+ * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`,
+ * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`,
+ * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`,
+ * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`,
+ * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`,
+ * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`,
+ * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`,
+ * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`,
+ * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`,
+ * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`,
+ * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`,
+ * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`,
+ * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`,
+ * `upperFirst`, `value`, and `words`
+ *
+ * @name _
+ * @constructor
+ * @category Seq
+ * @param {*} value The value to wrap in a `lodash` instance.
+ * @returns {Object} Returns the new `lodash` wrapper instance.
+ * @example
+ *
+ * function square(n) {
+ * return n * n;
+ * }
+ *
+ * var wrapped = _([1, 2, 3]);
+ *
+ * // Returns an unwrapped value.
+ * wrapped.reduce(_.add);
+ * // => 6
+ *
+ * // Returns a wrapped value.
+ * var squares = wrapped.map(square);
+ *
+ * _.isArray(squares);
+ * // => false
+ *
+ * _.isArray(squares.value());
+ * // => true
+ */
+ function lodash(value) {
+ if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {
+ if (value instanceof LodashWrapper) {
+ return value;
+ }
+ if (hasOwnProperty.call(value, '__wrapped__')) {
+ return wrapperClone(value);
+ }
+ }
+ return new LodashWrapper(value);
+ }
+
+ /**
+ * The base implementation of `_.create` without support for assigning
+ * properties to the created object.
+ *
+ * @private
+ * @param {Object} proto The object to inherit from.
+ * @returns {Object} Returns the new object.
+ */
+ var baseCreate = (function() {
+ function object() {}
+ return function(proto) {
+ if (!isObject(proto)) {
+ return {};
+ }
+ if (objectCreate) {
+ return objectCreate(proto);
+ }
+ object.prototype = proto;
+ var result = new object;
+ object.prototype = undefined;
+ return result;
+ };
+ }());
+
+ /**
+ * The function whose prototype chain sequence wrappers inherit from.
+ *
+ * @private
+ */
+ function baseLodash() {
+ // No operation performed.
+ }
+
+ /**
+ * The base constructor for creating `lodash` wrapper objects.
+ *
+ * @private
+ * @param {*} value The value to wrap.
+ * @param {boolean} [chainAll] Enable explicit method chain sequences.
+ */
+ function LodashWrapper(value, chainAll) {
+ this.__wrapped__ = value;
+ this.__actions__ = [];
+ this.__chain__ = !!chainAll;
+ this.__index__ = 0;
+ this.__values__ = undefined;
+ }
+
+ // Ensure wrappers are instances of `baseLodash`.
+ lodash.prototype = baseLodash.prototype;
+ lodash.prototype.constructor = lodash;
+
+ LodashWrapper.prototype = baseCreate(baseLodash.prototype);
+ LodashWrapper.prototype.constructor = LodashWrapper;
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.
+ *
+ * @private
+ * @constructor
+ * @param {*} value The value to wrap.
+ */
+ function LazyWrapper(value) {
+ this.__wrapped__ = value;
+ this.__actions__ = [];
+ this.__dir__ = 1;
+ this.__filtered__ = false;
+ this.__iteratees__ = [];
+ this.__takeCount__ = MAX_ARRAY_LENGTH;
+ this.__views__ = [];
+ }
+
+ /**
+ * Creates a clone of the lazy wrapper object.
+ *
+ * @private
+ * @name clone
+ * @memberOf LazyWrapper
+ * @returns {Object} Returns the cloned `LazyWrapper` object.
+ */
+ function lazyClone() {
+ var result = new LazyWrapper(this.__wrapped__);
+ result.__actions__ = copyArray(this.__actions__);
+ result.__dir__ = this.__dir__;
+ result.__filtered__ = this.__filtered__;
+ result.__iteratees__ = copyArray(this.__iteratees__);
+ result.__takeCount__ = this.__takeCount__;
+ result.__views__ = copyArray(this.__views__);
+ return result;
+ }
+
+ /**
+ * Reverses the direction of lazy iteration.
+ *
+ * @private
+ * @name reverse
+ * @memberOf LazyWrapper
+ * @returns {Object} Returns the new reversed `LazyWrapper` object.
+ */
+ function lazyReverse() {
+ if (this.__filtered__) {
+ var result = new LazyWrapper(this);
+ result.__dir__ = -1;
+ result.__filtered__ = true;
+ } else {
+ result = this.clone();
+ result.__dir__ *= -1;
+ }
+ return result;
+ }
+
+ /**
+ * Extracts the unwrapped value from its lazy wrapper.
+ *
+ * @private
+ * @name value
+ * @memberOf LazyWrapper
+ * @returns {*} Returns the unwrapped value.
+ */
+ function lazyValue() {
+ var array = this.__wrapped__.value(),
+ dir = this.__dir__,
+ isArr = isArray(array),
+ isRight = dir < 0,
+ arrLength = isArr ? array.length : 0,
+ view = getView(0, arrLength, this.__views__),
+ start = view.start,
+ end = view.end,
+ length = end - start,
+ index = isRight ? end : (start - 1),
+ iteratees = this.__iteratees__,
+ iterLength = iteratees.length,
+ resIndex = 0,
+ takeCount = nativeMin(length, this.__takeCount__);
+
+ if (!isArr || (!isRight && arrLength == length && takeCount == length)) {
+ return baseWrapperValue(array, this.__actions__);
+ }
+ var result = [];
+
+ outer:
+ while (length-- && resIndex < takeCount) {
+ index += dir;
+
+ var iterIndex = -1,
+ value = array[index];
+
+ while (++iterIndex < iterLength) {
+ var data = iteratees[iterIndex],
+ iteratee = data.iteratee,
+ type = data.type,
+ computed = iteratee(value);
+
+ if (type == LAZY_MAP_FLAG) {
+ value = computed;
+ } else if (!computed) {
+ if (type == LAZY_FILTER_FLAG) {
+ continue outer;
+ } else {
+ break outer;
+ }
+ }
+ }
+ result[resIndex++] = value;
+ }
+ return result;
+ }
+
+ // Ensure `LazyWrapper` is an instance of `baseLodash`.
+ LazyWrapper.prototype = baseCreate(baseLodash.prototype);
+ LazyWrapper.prototype.constructor = LazyWrapper;
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates a hash object.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+ function Hash(entries) {
+ var index = -1,
+ length = entries == null ? 0 : entries.length;
+
+ this.clear();
+ while (++index < length) {
+ var entry = entries[index];
+ this.set(entry[0], entry[1]);
+ }
+ }
+
+ /**
+ * Removes all key-value entries from the hash.
+ *
+ * @private
+ * @name clear
+ * @memberOf Hash
+ */
+ function hashClear() {
+ this.__data__ = nativeCreate ? nativeCreate(null) : {};
+ this.size = 0;
+ }
+
+ /**
+ * Removes `key` and its value from the hash.
+ *
+ * @private
+ * @name delete
+ * @memberOf Hash
+ * @param {Object} hash The hash to modify.
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+ function hashDelete(key) {
+ var result = this.has(key) && delete this.__data__[key];
+ this.size -= result ? 1 : 0;
+ return result;
+ }
+
+ /**
+ * Gets the hash value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf Hash
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+ function hashGet(key) {
+ var data = this.__data__;
+ if (nativeCreate) {
+ var result = data[key];
+ return result === HASH_UNDEFINED ? undefined : result;
+ }
+ return hasOwnProperty.call(data, key) ? data[key] : undefined;
+ }
+
+ /**
+ * Checks if a hash value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf Hash
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+ function hashHas(key) {
+ var data = this.__data__;
+ return nativeCreate ? (data[key] !== undefined) : hasOwnProperty.call(data, key);
+ }
+
+ /**
+ * Sets the hash `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf Hash
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the hash instance.
+ */
+ function hashSet(key, value) {
+ var data = this.__data__;
+ this.size += this.has(key) ? 0 : 1;
+ data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
+ return this;
+ }
+
+ // Add methods to `Hash`.
+ Hash.prototype.clear = hashClear;
+ Hash.prototype['delete'] = hashDelete;
+ Hash.prototype.get = hashGet;
+ Hash.prototype.has = hashHas;
+ Hash.prototype.set = hashSet;
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates an list cache object.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+ function ListCache(entries) {
+ var index = -1,
+ length = entries == null ? 0 : entries.length;
+
+ this.clear();
+ while (++index < length) {
+ var entry = entries[index];
+ this.set(entry[0], entry[1]);
+ }
+ }
+
+ /**
+ * Removes all key-value entries from the list cache.
+ *
+ * @private
+ * @name clear
+ * @memberOf ListCache
+ */
+ function listCacheClear() {
+ this.__data__ = [];
+ this.size = 0;
+ }
+
+ /**
+ * Removes `key` and its value from the list cache.
+ *
+ * @private
+ * @name delete
+ * @memberOf ListCache
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+ function listCacheDelete(key) {
+ var data = this.__data__,
+ index = assocIndexOf(data, key);
+
+ if (index < 0) {
+ return false;
+ }
+ var lastIndex = data.length - 1;
+ if (index == lastIndex) {
+ data.pop();
+ } else {
+ splice.call(data, index, 1);
+ }
+ --this.size;
+ return true;
+ }
+
+ /**
+ * Gets the list cache value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf ListCache
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+ function listCacheGet(key) {
+ var data = this.__data__,
+ index = assocIndexOf(data, key);
+
+ return index < 0 ? undefined : data[index][1];
+ }
+
+ /**
+ * Checks if a list cache value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf ListCache
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+ function listCacheHas(key) {
+ return assocIndexOf(this.__data__, key) > -1;
+ }
+
+ /**
+ * Sets the list cache `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf ListCache
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the list cache instance.
+ */
+ function listCacheSet(key, value) {
+ var data = this.__data__,
+ index = assocIndexOf(data, key);
+
+ if (index < 0) {
+ ++this.size;
+ data.push([key, value]);
+ } else {
+ data[index][1] = value;
+ }
+ return this;
+ }
+
+ // Add methods to `ListCache`.
+ ListCache.prototype.clear = listCacheClear;
+ ListCache.prototype['delete'] = listCacheDelete;
+ ListCache.prototype.get = listCacheGet;
+ ListCache.prototype.has = listCacheHas;
+ ListCache.prototype.set = listCacheSet;
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates a map cache object to store key-value pairs.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+ function MapCache(entries) {
+ var index = -1,
+ length = entries == null ? 0 : entries.length;
+
+ this.clear();
+ while (++index < length) {
+ var entry = entries[index];
+ this.set(entry[0], entry[1]);
+ }
+ }
+
+ /**
+ * Removes all key-value entries from the map.
+ *
+ * @private
+ * @name clear
+ * @memberOf MapCache
+ */
+ function mapCacheClear() {
+ this.size = 0;
+ this.__data__ = {
+ 'hash': new Hash,
+ 'map': new (Map || ListCache),
+ 'string': new Hash
+ };
+ }
+
+ /**
+ * Removes `key` and its value from the map.
+ *
+ * @private
+ * @name delete
+ * @memberOf MapCache
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+ function mapCacheDelete(key) {
+ var result = getMapData(this, key)['delete'](key);
+ this.size -= result ? 1 : 0;
+ return result;
+ }
+
+ /**
+ * Gets the map value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf MapCache
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+ function mapCacheGet(key) {
+ return getMapData(this, key).get(key);
+ }
+
+ /**
+ * Checks if a map value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf MapCache
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+ function mapCacheHas(key) {
+ return getMapData(this, key).has(key);
+ }
+
+ /**
+ * Sets the map `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf MapCache
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the map cache instance.
+ */
+ function mapCacheSet(key, value) {
+ var data = getMapData(this, key),
+ size = data.size;
+
+ data.set(key, value);
+ this.size += data.size == size ? 0 : 1;
+ return this;
+ }
+
+ // Add methods to `MapCache`.
+ MapCache.prototype.clear = mapCacheClear;
+ MapCache.prototype['delete'] = mapCacheDelete;
+ MapCache.prototype.get = mapCacheGet;
+ MapCache.prototype.has = mapCacheHas;
+ MapCache.prototype.set = mapCacheSet;
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ *
+ * Creates an array cache object to store unique values.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [values] The values to cache.
+ */
+ function SetCache(values) {
+ var index = -1,
+ length = values == null ? 0 : values.length;
+
+ this.__data__ = new MapCache;
+ while (++index < length) {
+ this.add(values[index]);
+ }
+ }
+
+ /**
+ * Adds `value` to the array cache.
+ *
+ * @private
+ * @name add
+ * @memberOf SetCache
+ * @alias push
+ * @param {*} value The value to cache.
+ * @returns {Object} Returns the cache instance.
+ */
+ function setCacheAdd(value) {
+ this.__data__.set(value, HASH_UNDEFINED);
+ return this;
+ }
+
+ /**
+ * Checks if `value` is in the array cache.
+ *
+ * @private
+ * @name has
+ * @memberOf SetCache
+ * @param {*} value The value to search for.
+ * @returns {number} Returns `true` if `value` is found, else `false`.
+ */
+ function setCacheHas(value) {
+ return this.__data__.has(value);
+ }
+
+ // Add methods to `SetCache`.
+ SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;
+ SetCache.prototype.has = setCacheHas;
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates a stack cache object to store key-value pairs.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+ function Stack(entries) {
+ var data = this.__data__ = new ListCache(entries);
+ this.size = data.size;
+ }
+
+ /**
+ * Removes all key-value entries from the stack.
+ *
+ * @private
+ * @name clear
+ * @memberOf Stack
+ */
+ function stackClear() {
+ this.__data__ = new ListCache;
+ this.size = 0;
+ }
+
+ /**
+ * Removes `key` and its value from the stack.
+ *
+ * @private
+ * @name delete
+ * @memberOf Stack
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+ function stackDelete(key) {
+ var data = this.__data__,
+ result = data['delete'](key);
+
+ this.size = data.size;
+ return result;
+ }
+
+ /**
+ * Gets the stack value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf Stack
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+ function stackGet(key) {
+ return this.__data__.get(key);
+ }
+
+ /**
+ * Checks if a stack value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf Stack
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+ function stackHas(key) {
+ return this.__data__.has(key);
+ }
+
+ /**
+ * Sets the stack `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf Stack
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the stack cache instance.
+ */
+ function stackSet(key, value) {
+ var data = this.__data__;
+ if (data instanceof ListCache) {
+ var pairs = data.__data__;
+ if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {
+ pairs.push([key, value]);
+ this.size = ++data.size;
+ return this;
+ }
+ data = this.__data__ = new MapCache(pairs);
+ }
+ data.set(key, value);
+ this.size = data.size;
+ return this;
+ }
+
+ // Add methods to `Stack`.
+ Stack.prototype.clear = stackClear;
+ Stack.prototype['delete'] = stackDelete;
+ Stack.prototype.get = stackGet;
+ Stack.prototype.has = stackHas;
+ Stack.prototype.set = stackSet;
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates an array of the enumerable property names of the array-like `value`.
+ *
+ * @private
+ * @param {*} value The value to query.
+ * @param {boolean} inherited Specify returning inherited property names.
+ * @returns {Array} Returns the array of property names.
+ */
+ function arrayLikeKeys(value, inherited) {
+ var isArr = isArray(value),
+ isArg = !isArr && isArguments(value),
+ isBuff = !isArr && !isArg && isBuffer(value),
+ isType = !isArr && !isArg && !isBuff && isTypedArray(value),
+ skipIndexes = isArr || isArg || isBuff || isType,
+ result = skipIndexes ? baseTimes(value.length, String) : [],
+ length = result.length;
+
+ for (var key in value) {
+ if ((inherited || hasOwnProperty.call(value, key)) &&
+ !(skipIndexes && (
+ // Safari 9 has enumerable `arguments.length` in strict mode.
+ key == 'length' ||
+ // Node.js 0.10 has enumerable non-index properties on buffers.
+ (isBuff && (key == 'offset' || key == 'parent')) ||
+ // PhantomJS 2 has enumerable non-index properties on typed arrays.
+ (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) ||
+ // Skip index properties.
+ isIndex(key, length)
+ ))) {
+ result.push(key);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This function is like `assignValue` except that it doesn't assign
+ * `undefined` values.
+ *
+ * @private
+ * @param {Object} object The object to modify.
+ * @param {string} key The key of the property to assign.
+ * @param {*} value The value to assign.
+ */
+ function assignMergeValue(object, key, value) {
+ if ((value !== undefined && !eq(object[key], value)) ||
+ (value === undefined && !(key in object))) {
+ baseAssignValue(object, key, value);
+ }
+ }
+
+ /**
+ * Assigns `value` to `key` of `object` if the existing value is not equivalent
+ * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons.
+ *
+ * @private
+ * @param {Object} object The object to modify.
+ * @param {string} key The key of the property to assign.
+ * @param {*} value The value to assign.
+ */
+ function assignValue(object, key, value) {
+ var objValue = object[key];
+ if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||
+ (value === undefined && !(key in object))) {
+ baseAssignValue(object, key, value);
+ }
+ }
+
+ /**
+ * Gets the index at which the `key` is found in `array` of key-value pairs.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} key The key to search for.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+ function assocIndexOf(array, key) {
+ var length = array.length;
+ while (length--) {
+ if (eq(array[length][0], key)) {
+ return length;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Aggregates elements of `collection` on `accumulator` with keys transformed
+ * by `iteratee` and values set by `setter`.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} setter The function to set `accumulator` values.
+ * @param {Function} iteratee The iteratee to transform keys.
+ * @param {Object} accumulator The initial aggregated object.
+ * @returns {Function} Returns `accumulator`.
+ */
+ function baseAggregator(collection, setter, iteratee, accumulator) {
+ baseEach(collection, function(value, key, collection) {
+ setter(accumulator, value, iteratee(value), collection);
+ });
+ return accumulator;
+ }
+
+ /**
+ * The base implementation of `_.assign` without support for multiple sources
+ * or `customizer` functions.
+ *
+ * @private
+ * @param {Object} object The destination object.
+ * @param {Object} source The source object.
+ * @returns {Object} Returns `object`.
+ */
+ function baseAssign(object, source) {
+ return object && copyObject(source, keys(source), object);
+ }
+
+ /**
+ * The base implementation of `_.assignIn` without support for multiple sources
+ * or `customizer` functions.
+ *
+ * @private
+ * @param {Object} object The destination object.
+ * @param {Object} source The source object.
+ * @returns {Object} Returns `object`.
+ */
+ function baseAssignIn(object, source) {
+ return object && copyObject(source, keysIn(source), object);
+ }
+
+ /**
+ * The base implementation of `assignValue` and `assignMergeValue` without
+ * value checks.
+ *
+ * @private
+ * @param {Object} object The object to modify.
+ * @param {string} key The key of the property to assign.
+ * @param {*} value The value to assign.
+ */
+ function baseAssignValue(object, key, value) {
+ if (key == '__proto__' && defineProperty) {
+ defineProperty(object, key, {
+ 'configurable': true,
+ 'enumerable': true,
+ 'value': value,
+ 'writable': true
+ });
+ } else {
+ object[key] = value;
+ }
+ }
+
+ /**
+ * The base implementation of `_.at` without support for individual paths.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {string[]} paths The property paths to pick.
+ * @returns {Array} Returns the picked elements.
+ */
+ function baseAt(object, paths) {
+ var index = -1,
+ length = paths.length,
+ result = Array(length),
+ skip = object == null;
+
+ while (++index < length) {
+ result[index] = skip ? undefined : get(object, paths[index]);
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.clamp` which doesn't coerce arguments.
+ *
+ * @private
+ * @param {number} number The number to clamp.
+ * @param {number} [lower] The lower bound.
+ * @param {number} upper The upper bound.
+ * @returns {number} Returns the clamped number.
+ */
+ function baseClamp(number, lower, upper) {
+ if (number === number) {
+ if (upper !== undefined) {
+ number = number <= upper ? number : upper;
+ }
+ if (lower !== undefined) {
+ number = number >= lower ? number : lower;
+ }
+ }
+ return number;
+ }
+
+ /**
+ * The base implementation of `_.clone` and `_.cloneDeep` which tracks
+ * traversed objects.
+ *
+ * @private
+ * @param {*} value The value to clone.
+ * @param {boolean} bitmask The bitmask flags.
+ * 1 - Deep clone
+ * 2 - Flatten inherited properties
+ * 4 - Clone symbols
+ * @param {Function} [customizer] The function to customize cloning.
+ * @param {string} [key] The key of `value`.
+ * @param {Object} [object] The parent object of `value`.
+ * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
+ * @returns {*} Returns the cloned value.
+ */
+ function baseClone(value, bitmask, customizer, key, object, stack) {
+ var result,
+ isDeep = bitmask & CLONE_DEEP_FLAG,
+ isFlat = bitmask & CLONE_FLAT_FLAG,
+ isFull = bitmask & CLONE_SYMBOLS_FLAG;
+
+ if (customizer) {
+ result = object ? customizer(value, key, object, stack) : customizer(value);
+ }
+ if (result !== undefined) {
+ return result;
+ }
+ if (!isObject(value)) {
+ return value;
+ }
+ var isArr = isArray(value);
+ if (isArr) {
+ result = initCloneArray(value);
+ if (!isDeep) {
+ return copyArray(value, result);
+ }
+ } else {
+ var tag = getTag(value),
+ isFunc = tag == funcTag || tag == genTag;
+
+ if (isBuffer(value)) {
+ return cloneBuffer(value, isDeep);
+ }
+ if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
+ result = (isFlat || isFunc) ? {} : initCloneObject(value);
+ if (!isDeep) {
+ return isFlat
+ ? copySymbolsIn(value, baseAssignIn(result, value))
+ : copySymbols(value, baseAssign(result, value));
+ }
+ } else {
+ if (!cloneableTags[tag]) {
+ return object ? value : {};
+ }
+ result = initCloneByTag(value, tag, isDeep);
+ }
+ }
+ // Check for circular references and return its corresponding clone.
+ stack || (stack = new Stack);
+ var stacked = stack.get(value);
+ if (stacked) {
+ return stacked;
+ }
+ stack.set(value, result);
+
+ if (isSet(value)) {
+ value.forEach(function(subValue) {
+ result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack));
+ });
+
+ return result;
+ }
+
+ if (isMap(value)) {
+ value.forEach(function(subValue, key) {
+ result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack));
+ });
+
+ return result;
+ }
+
+ var keysFunc = isFull
+ ? (isFlat ? getAllKeysIn : getAllKeys)
+ : (isFlat ? keysIn : keys);
+
+ var props = isArr ? undefined : keysFunc(value);
+ arrayEach(props || value, function(subValue, key) {
+ if (props) {
+ key = subValue;
+ subValue = value[key];
+ }
+ // Recursively populate clone (susceptible to call stack limits).
+ assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack));
+ });
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.delay` and `_.defer` which accepts `args`
+ * to provide to `func`.
+ *
+ * @private
+ * @param {Function} func The function to delay.
+ * @param {number} wait The number of milliseconds to delay invocation.
+ * @param {Array} args The arguments to provide to `func`.
+ * @returns {number|Object} Returns the timer id or timeout object.
+ */
+ function baseDelay(func, wait, args) {
+ if (typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ return setTimeout(function() { func.apply(undefined, args); }, wait);
+ }
+
+ /**
+ * The base implementation of methods like `_.difference` without support
+ * for excluding multiple arrays or iteratee shorthands.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {Array} values The values to exclude.
+ * @param {Function} [iteratee] The iteratee invoked per element.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new array of filtered values.
+ */
+ function baseDifference(array, values, iteratee, comparator) {
+ var index = -1,
+ includes = arrayIncludes,
+ isCommon = true,
+ length = array.length,
+ result = [],
+ valuesLength = values.length;
+
+ if (!length) {
+ return result;
+ }
+ if (iteratee) {
+ values = arrayMap(values, baseUnary(iteratee));
+ }
+ if (comparator) {
+ includes = arrayIncludesWith;
+ isCommon = false;
+ }
+ else if (values.length >= LARGE_ARRAY_SIZE) {
+ includes = cacheHas;
+ isCommon = false;
+ values = new SetCache(values);
+ }
+ outer:
+ while (++index < length) {
+ var value = array[index],
+ computed = iteratee == null ? value : iteratee(value);
+
+ value = (comparator || value !== 0) ? value : 0;
+ if (isCommon && computed === computed) {
+ var valuesIndex = valuesLength;
+ while (valuesIndex--) {
+ if (values[valuesIndex] === computed) {
+ continue outer;
+ }
+ }
+ result.push(value);
+ }
+ else if (!includes(values, computed, comparator)) {
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.forEach` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array|Object} Returns `collection`.
+ */
+ var baseEach = createBaseEach(baseForOwn);
+
+ /**
+ * The base implementation of `_.every` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {boolean} Returns `true` if all elements pass the predicate check,
+ * else `false`
+ */
+ function baseEvery(collection, predicate) {
+ var result = true;
+ baseEach(collection, function(value, index, collection) {
+ result = !!predicate(value, index, collection);
+ return result;
+ });
+ return result;
+ }
+
+ /**
+ * The base implementation of methods like `_.max` and `_.min` which accepts a
+ * `comparator` to determine the extremum value.
+ *
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} iteratee The iteratee invoked per iteration.
+ * @param {Function} comparator The comparator used to compare values.
+ * @returns {*} Returns the extremum value.
+ */
+ function baseExtremum(array, iteratee, comparator) {
+ var index = -1,
+ length = array.length;
+
+ while (++index < length) {
+ var value = array[index],
+ current = iteratee(value);
+
+ if (current != null && (computed === undefined
+ ? (current === current && !isSymbol(current))
+ : comparator(current, computed)
+ )) {
+ var computed = current,
+ result = value;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.filter` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {Array} Returns the new filtered array.
+ */
+ function baseFilter(collection, predicate) {
+ var result = [];
+ baseEach(collection, function(value, index, collection) {
+ if (predicate(value, index, collection)) {
+ result.push(value);
+ }
+ });
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.flatten` with support for restricting flattening.
+ *
+ * @private
+ * @param {Array} array The array to flatten.
+ * @param {number} depth The maximum recursion depth.
+ * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.
+ * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.
+ * @param {Array} [result=[]] The initial result value.
+ * @returns {Array} Returns the new flattened array.
+ */
+ function baseFlatten(array, depth, predicate, isStrict, result) {
+ var index = -1,
+ length = array.length;
+
+ predicate || (predicate = isFlattenable);
+ result || (result = []);
+
+ while (++index < length) {
+ var value = array[index];
+ if (depth > 0 && predicate(value)) {
+ if (depth > 1) {
+ // Recursively flatten arrays (susceptible to call stack limits).
+ baseFlatten(value, depth - 1, predicate, isStrict, result);
+ } else {
+ arrayPush(result, value);
+ }
+ } else if (!isStrict) {
+ result[result.length] = value;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `baseForOwn` which iterates over `object`
+ * properties returned by `keysFunc` and invokes `iteratee` for each property.
+ * Iteratee functions may exit iteration early by explicitly returning `false`.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {Function} keysFunc The function to get the keys of `object`.
+ * @returns {Object} Returns `object`.
+ */
+ var baseFor = createBaseFor();
+
+ /**
+ * This function is like `baseFor` except that it iterates over properties
+ * in the opposite order.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {Function} keysFunc The function to get the keys of `object`.
+ * @returns {Object} Returns `object`.
+ */
+ var baseForRight = createBaseFor(true);
+
+ /**
+ * The base implementation of `_.forOwn` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Object} Returns `object`.
+ */
+ function baseForOwn(object, iteratee) {
+ return object && baseFor(object, iteratee, keys);
+ }
+
+ /**
+ * The base implementation of `_.forOwnRight` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Object} Returns `object`.
+ */
+ function baseForOwnRight(object, iteratee) {
+ return object && baseForRight(object, iteratee, keys);
+ }
+
+ /**
+ * The base implementation of `_.functions` which creates an array of
+ * `object` function property names filtered from `props`.
+ *
+ * @private
+ * @param {Object} object The object to inspect.
+ * @param {Array} props The property names to filter.
+ * @returns {Array} Returns the function names.
+ */
+ function baseFunctions(object, props) {
+ return arrayFilter(props, function(key) {
+ return isFunction(object[key]);
+ });
+ }
+
+ /**
+ * The base implementation of `_.get` without support for default values.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path of the property to get.
+ * @returns {*} Returns the resolved value.
+ */
+ function baseGet(object, path) {
+ path = castPath(path, object);
+
+ var index = 0,
+ length = path.length;
+
+ while (object != null && index < length) {
+ object = object[toKey(path[index++])];
+ }
+ return (index && index == length) ? object : undefined;
+ }
+
+ /**
+ * The base implementation of `getAllKeys` and `getAllKeysIn` which uses
+ * `keysFunc` and `symbolsFunc` to get the enumerable property names and
+ * symbols of `object`.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @param {Function} keysFunc The function to get the keys of `object`.
+ * @param {Function} symbolsFunc The function to get the symbols of `object`.
+ * @returns {Array} Returns the array of property names and symbols.
+ */
+ function baseGetAllKeys(object, keysFunc, symbolsFunc) {
+ var result = keysFunc(object);
+ return isArray(object) ? result : arrayPush(result, symbolsFunc(object));
+ }
+
+ /**
+ * The base implementation of `getTag` without fallbacks for buggy environments.
+ *
+ * @private
+ * @param {*} value The value to query.
+ * @returns {string} Returns the `toStringTag`.
+ */
+ function baseGetTag(value) {
+ if (value == null) {
+ return value === undefined ? undefinedTag : nullTag;
+ }
+ return (symToStringTag && symToStringTag in Object(value))
+ ? getRawTag(value)
+ : objectToString(value);
+ }
+
+ /**
+ * The base implementation of `_.gt` which doesn't coerce arguments.
+ *
+ * @private
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @returns {boolean} Returns `true` if `value` is greater than `other`,
+ * else `false`.
+ */
+ function baseGt(value, other) {
+ return value > other;
+ }
+
+ /**
+ * The base implementation of `_.has` without support for deep paths.
+ *
+ * @private
+ * @param {Object} [object] The object to query.
+ * @param {Array|string} key The key to check.
+ * @returns {boolean} Returns `true` if `key` exists, else `false`.
+ */
+ function baseHas(object, key) {
+ return object != null && hasOwnProperty.call(object, key);
+ }
+
+ /**
+ * The base implementation of `_.hasIn` without support for deep paths.
+ *
+ * @private
+ * @param {Object} [object] The object to query.
+ * @param {Array|string} key The key to check.
+ * @returns {boolean} Returns `true` if `key` exists, else `false`.
+ */
+ function baseHasIn(object, key) {
+ return object != null && key in Object(object);
+ }
+
+ /**
+ * The base implementation of methods like `_.intersection`, without support
+ * for iteratee shorthands, that accepts an array of arrays to inspect.
+ *
+ * @private
+ * @param {Array} arrays The arrays to inspect.
+ * @param {Function} [iteratee] The iteratee invoked per element.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new array of shared values.
+ */
+ function baseIntersection(arrays, iteratee, comparator) {
+ var includes = comparator ? arrayIncludesWith : arrayIncludes,
+ length = arrays[0].length,
+ othLength = arrays.length,
+ othIndex = othLength,
+ caches = Array(othLength),
+ maxLength = Infinity,
+ result = [];
+
+ while (othIndex--) {
+ var array = arrays[othIndex];
+ if (othIndex && iteratee) {
+ array = arrayMap(array, baseUnary(iteratee));
+ }
+ maxLength = nativeMin(array.length, maxLength);
+ caches[othIndex] = !comparator && (iteratee || (length >= 120 && array.length >= 120))
+ ? new SetCache(othIndex && array)
+ : undefined;
+ }
+ array = arrays[0];
+
+ var index = -1,
+ seen = caches[0];
+
+ outer:
+ while (++index < length && result.length < maxLength) {
+ var value = array[index],
+ computed = iteratee ? iteratee(value) : value;
+
+ value = (comparator || value !== 0) ? value : 0;
+ if (!(seen
+ ? cacheHas(seen, computed)
+ : includes(result, computed, comparator)
+ )) {
+ othIndex = othLength;
+ while (--othIndex) {
+ var cache = caches[othIndex];
+ if (!(cache
+ ? cacheHas(cache, computed)
+ : includes(arrays[othIndex], computed, comparator))
+ ) {
+ continue outer;
+ }
+ }
+ if (seen) {
+ seen.push(computed);
+ }
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.invert` and `_.invertBy` which inverts
+ * `object` with values transformed by `iteratee` and set by `setter`.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} setter The function to set `accumulator` values.
+ * @param {Function} iteratee The iteratee to transform values.
+ * @param {Object} accumulator The initial inverted object.
+ * @returns {Function} Returns `accumulator`.
+ */
+ function baseInverter(object, setter, iteratee, accumulator) {
+ baseForOwn(object, function(value, key, object) {
+ setter(accumulator, iteratee(value), key, object);
+ });
+ return accumulator;
+ }
+
+ /**
+ * The base implementation of `_.invoke` without support for individual
+ * method arguments.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path of the method to invoke.
+ * @param {Array} args The arguments to invoke the method with.
+ * @returns {*} Returns the result of the invoked method.
+ */
+ function baseInvoke(object, path, args) {
+ path = castPath(path, object);
+ object = parent(object, path);
+ var func = object == null ? object : object[toKey(last(path))];
+ return func == null ? undefined : apply(func, object, args);
+ }
+
+ /**
+ * The base implementation of `_.isArguments`.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an `arguments` object,
+ */
+ function baseIsArguments(value) {
+ return isObjectLike(value) && baseGetTag(value) == argsTag;
+ }
+
+ /**
+ * The base implementation of `_.isDate` without Node.js optimizations.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a date object, else `false`.
+ */
+ function baseIsDate(value) {
+ return isObjectLike(value) && baseGetTag(value) == dateTag;
+ }
+
+ /**
+ * The base implementation of `_.isEqual` which supports partial comparisons
+ * and tracks traversed objects.
+ *
+ * @private
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @param {boolean} bitmask The bitmask flags.
+ * 1 - Unordered comparison
+ * 2 - Partial comparison
+ * @param {Function} [customizer] The function to customize comparisons.
+ * @param {Object} [stack] Tracks traversed `value` and `other` objects.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ */
+ function baseIsEqual(value, other, bitmask, customizer, stack) {
+ if (value === other) {
+ return true;
+ }
+ if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) {
+ return value !== value && other !== other;
+ }
+ return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack);
+ }
+
+ /**
+ * A specialized version of `baseIsEqual` for arrays and objects which performs
+ * deep comparisons and tracks traversed objects enabling objects with circular
+ * references to be compared.
+ *
+ * @private
+ * @param {Object} object The object to compare.
+ * @param {Object} other The other object to compare.
+ * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
+ * @param {Function} customizer The function to customize comparisons.
+ * @param {Function} equalFunc The function to determine equivalents of values.
+ * @param {Object} [stack] Tracks traversed `object` and `other` objects.
+ * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+ */
+ function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) {
+ var objIsArr = isArray(object),
+ othIsArr = isArray(other),
+ objTag = objIsArr ? arrayTag : getTag(object),
+ othTag = othIsArr ? arrayTag : getTag(other);
+
+ objTag = objTag == argsTag ? objectTag : objTag;
+ othTag = othTag == argsTag ? objectTag : othTag;
+
+ var objIsObj = objTag == objectTag,
+ othIsObj = othTag == objectTag,
+ isSameTag = objTag == othTag;
+
+ if (isSameTag && isBuffer(object)) {
+ if (!isBuffer(other)) {
+ return false;
+ }
+ objIsArr = true;
+ objIsObj = false;
+ }
+ if (isSameTag && !objIsObj) {
+ stack || (stack = new Stack);
+ return (objIsArr || isTypedArray(object))
+ ? equalArrays(object, other, bitmask, customizer, equalFunc, stack)
+ : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack);
+ }
+ if (!(bitmask & COMPARE_PARTIAL_FLAG)) {
+ var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
+ othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
+
+ if (objIsWrapped || othIsWrapped) {
+ var objUnwrapped = objIsWrapped ? object.value() : object,
+ othUnwrapped = othIsWrapped ? other.value() : other;
+
+ stack || (stack = new Stack);
+ return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack);
+ }
+ }
+ if (!isSameTag) {
+ return false;
+ }
+ stack || (stack = new Stack);
+ return equalObjects(object, other, bitmask, customizer, equalFunc, stack);
+ }
+
+ /**
+ * The base implementation of `_.isMap` without Node.js optimizations.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a map, else `false`.
+ */
+ function baseIsMap(value) {
+ return isObjectLike(value) && getTag(value) == mapTag;
+ }
+
+ /**
+ * The base implementation of `_.isMatch` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Object} object The object to inspect.
+ * @param {Object} source The object of property values to match.
+ * @param {Array} matchData The property names, values, and compare flags to match.
+ * @param {Function} [customizer] The function to customize comparisons.
+ * @returns {boolean} Returns `true` if `object` is a match, else `false`.
+ */
+ function baseIsMatch(object, source, matchData, customizer) {
+ var index = matchData.length,
+ length = index,
+ noCustomizer = !customizer;
+
+ if (object == null) {
+ return !length;
+ }
+ object = Object(object);
+ while (index--) {
+ var data = matchData[index];
+ if ((noCustomizer && data[2])
+ ? data[1] !== object[data[0]]
+ : !(data[0] in object)
+ ) {
+ return false;
+ }
+ }
+ while (++index < length) {
+ data = matchData[index];
+ var key = data[0],
+ objValue = object[key],
+ srcValue = data[1];
+
+ if (noCustomizer && data[2]) {
+ if (objValue === undefined && !(key in object)) {
+ return false;
+ }
+ } else {
+ var stack = new Stack;
+ if (customizer) {
+ var result = customizer(objValue, srcValue, key, object, source, stack);
+ }
+ if (!(result === undefined
+ ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack)
+ : result
+ )) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * The base implementation of `_.isNative` without bad shim checks.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a native function,
+ * else `false`.
+ */
+ function baseIsNative(value) {
+ if (!isObject(value) || isMasked(value)) {
+ return false;
+ }
+ var pattern = isFunction(value) ? reIsNative : reIsHostCtor;
+ return pattern.test(toSource(value));
+ }
+
+ /**
+ * The base implementation of `_.isRegExp` without Node.js optimizations.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.
+ */
+ function baseIsRegExp(value) {
+ return isObjectLike(value) && baseGetTag(value) == regexpTag;
+ }
+
+ /**
+ * The base implementation of `_.isSet` without Node.js optimizations.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a set, else `false`.
+ */
+ function baseIsSet(value) {
+ return isObjectLike(value) && getTag(value) == setTag;
+ }
+
+ /**
+ * The base implementation of `_.isTypedArray` without Node.js optimizations.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
+ */
+ function baseIsTypedArray(value) {
+ return isObjectLike(value) &&
+ isLength(value.length) && !!typedArrayTags[baseGetTag(value)];
+ }
+
+ /**
+ * The base implementation of `_.iteratee`.
+ *
+ * @private
+ * @param {*} [value=_.identity] The value to convert to an iteratee.
+ * @returns {Function} Returns the iteratee.
+ */
+ function baseIteratee(value) {
+ // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.
+ // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.
+ if (typeof value == 'function') {
+ return value;
+ }
+ if (value == null) {
+ return identity;
+ }
+ if (typeof value == 'object') {
+ return isArray(value)
+ ? baseMatchesProperty(value[0], value[1])
+ : baseMatches(value);
+ }
+ return property(value);
+ }
+
+ /**
+ * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of property names.
+ */
+ function baseKeys(object) {
+ if (!isPrototype(object)) {
+ return nativeKeys(object);
+ }
+ var result = [];
+ for (var key in Object(object)) {
+ if (hasOwnProperty.call(object, key) && key != 'constructor') {
+ result.push(key);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of property names.
+ */
+ function baseKeysIn(object) {
+ if (!isObject(object)) {
+ return nativeKeysIn(object);
+ }
+ var isProto = isPrototype(object),
+ result = [];
+
+ for (var key in object) {
+ if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
+ result.push(key);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.lt` which doesn't coerce arguments.
+ *
+ * @private
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @returns {boolean} Returns `true` if `value` is less than `other`,
+ * else `false`.
+ */
+ function baseLt(value, other) {
+ return value < other;
+ }
+
+ /**
+ * The base implementation of `_.map` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns the new mapped array.
+ */
+ function baseMap(collection, iteratee) {
+ var index = -1,
+ result = isArrayLike(collection) ? Array(collection.length) : [];
+
+ baseEach(collection, function(value, key, collection) {
+ result[++index] = iteratee(value, key, collection);
+ });
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.matches` which doesn't clone `source`.
+ *
+ * @private
+ * @param {Object} source The object of property values to match.
+ * @returns {Function} Returns the new spec function.
+ */
+ function baseMatches(source) {
+ var matchData = getMatchData(source);
+ if (matchData.length == 1 && matchData[0][2]) {
+ return matchesStrictComparable(matchData[0][0], matchData[0][1]);
+ }
+ return function(object) {
+ return object === source || baseIsMatch(object, source, matchData);
+ };
+ }
+
+ /**
+ * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`.
+ *
+ * @private
+ * @param {string} path The path of the property to get.
+ * @param {*} srcValue The value to match.
+ * @returns {Function} Returns the new spec function.
+ */
+ function baseMatchesProperty(path, srcValue) {
+ if (isKey(path) && isStrictComparable(srcValue)) {
+ return matchesStrictComparable(toKey(path), srcValue);
+ }
+ return function(object) {
+ var objValue = get(object, path);
+ return (objValue === undefined && objValue === srcValue)
+ ? hasIn(object, path)
+ : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG);
+ };
+ }
+
+ /**
+ * The base implementation of `_.merge` without support for multiple sources.
+ *
+ * @private
+ * @param {Object} object The destination object.
+ * @param {Object} source The source object.
+ * @param {number} srcIndex The index of `source`.
+ * @param {Function} [customizer] The function to customize merged values.
+ * @param {Object} [stack] Tracks traversed source values and their merged
+ * counterparts.
+ */
+ function baseMerge(object, source, srcIndex, customizer, stack) {
+ if (object === source) {
+ return;
+ }
+ baseFor(source, function(srcValue, key) {
+ if (isObject(srcValue)) {
+ stack || (stack = new Stack);
+ baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack);
+ }
+ else {
+ var newValue = customizer
+ ? customizer(safeGet(object, key), srcValue, (key + ''), object, source, stack)
+ : undefined;
+
+ if (newValue === undefined) {
+ newValue = srcValue;
+ }
+ assignMergeValue(object, key, newValue);
+ }
+ }, keysIn);
+ }
+
+ /**
+ * A specialized version of `baseMerge` for arrays and objects which performs
+ * deep merges and tracks traversed objects enabling objects with circular
+ * references to be merged.
+ *
+ * @private
+ * @param {Object} object The destination object.
+ * @param {Object} source The source object.
+ * @param {string} key The key of the value to merge.
+ * @param {number} srcIndex The index of `source`.
+ * @param {Function} mergeFunc The function to merge values.
+ * @param {Function} [customizer] The function to customize assigned values.
+ * @param {Object} [stack] Tracks traversed source values and their merged
+ * counterparts.
+ */
+ function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) {
+ var objValue = safeGet(object, key),
+ srcValue = safeGet(source, key),
+ stacked = stack.get(srcValue);
+
+ if (stacked) {
+ assignMergeValue(object, key, stacked);
+ return;
+ }
+ var newValue = customizer
+ ? customizer(objValue, srcValue, (key + ''), object, source, stack)
+ : undefined;
+
+ var isCommon = newValue === undefined;
+
+ if (isCommon) {
+ var isArr = isArray(srcValue),
+ isBuff = !isArr && isBuffer(srcValue),
+ isTyped = !isArr && !isBuff && isTypedArray(srcValue);
+
+ newValue = srcValue;
+ if (isArr || isBuff || isTyped) {
+ if (isArray(objValue)) {
+ newValue = objValue;
+ }
+ else if (isArrayLikeObject(objValue)) {
+ newValue = copyArray(objValue);
+ }
+ else if (isBuff) {
+ isCommon = false;
+ newValue = cloneBuffer(srcValue, true);
+ }
+ else if (isTyped) {
+ isCommon = false;
+ newValue = cloneTypedArray(srcValue, true);
+ }
+ else {
+ newValue = [];
+ }
+ }
+ else if (isPlainObject(srcValue) || isArguments(srcValue)) {
+ newValue = objValue;
+ if (isArguments(objValue)) {
+ newValue = toPlainObject(objValue);
+ }
+ else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) {
+ newValue = initCloneObject(srcValue);
+ }
+ }
+ else {
+ isCommon = false;
+ }
+ }
+ if (isCommon) {
+ // Recursively merge objects and arrays (susceptible to call stack limits).
+ stack.set(srcValue, newValue);
+ mergeFunc(newValue, srcValue, srcIndex, customizer, stack);
+ stack['delete'](srcValue);
+ }
+ assignMergeValue(object, key, newValue);
+ }
+
+ /**
+ * The base implementation of `_.orderBy` without param guards.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by.
+ * @param {string[]} orders The sort orders of `iteratees`.
+ * @returns {Array} Returns the new sorted array.
+ */
+ function baseOrderBy(collection, iteratees, orders) {
+ var index = -1;
+ iteratees = arrayMap(iteratees.length ? iteratees : [identity], baseUnary(baseIteratee));
+
+ var result = baseMap(collection, function(value, key, collection) {
+ var criteria = arrayMap(iteratees, function(iteratee) {
+ return iteratee(value);
+ });
+ return { 'criteria': criteria, 'index': ++index, 'value': value };
+ });
+
+ return baseSortBy(result, function(object, other) {
+ return compareMultiple(object, other, orders);
+ });
+ }
+
+ /**
+ * The base implementation of `_.pick` without support for individual
+ * property identifiers.
+ *
+ * @private
+ * @param {Object} object The source object.
+ * @param {string[]} paths The property paths to pick.
+ * @returns {Object} Returns the new object.
+ */
+ function basePick(object, paths) {
+ return basePickBy(object, paths, function(value, path) {
+ return hasIn(object, path);
+ });
+ }
+
+ /**
+ * The base implementation of `_.pickBy` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Object} object The source object.
+ * @param {string[]} paths The property paths to pick.
+ * @param {Function} predicate The function invoked per property.
+ * @returns {Object} Returns the new object.
+ */
+ function basePickBy(object, paths, predicate) {
+ var index = -1,
+ length = paths.length,
+ result = {};
+
+ while (++index < length) {
+ var path = paths[index],
+ value = baseGet(object, path);
+
+ if (predicate(value, path)) {
+ baseSet(result, castPath(path, object), value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * A specialized version of `baseProperty` which supports deep paths.
+ *
+ * @private
+ * @param {Array|string} path The path of the property to get.
+ * @returns {Function} Returns the new accessor function.
+ */
+ function basePropertyDeep(path) {
+ return function(object) {
+ return baseGet(object, path);
+ };
+ }
+
+ /**
+ * The base implementation of `_.random` without support for returning
+ * floating-point numbers.
+ *
+ * @private
+ * @param {number} lower The lower bound.
+ * @param {number} upper The upper bound.
+ * @returns {number} Returns the random number.
+ */
+ function baseRandom(lower, upper) {
+ return lower + nativeFloor(nativeRandom() * (upper - lower + 1));
+ }
+
+ /**
+ * The base implementation of `_.range` and `_.rangeRight` which doesn't
+ * coerce arguments.
+ *
+ * @private
+ * @param {number} start The start of the range.
+ * @param {number} end The end of the range.
+ * @param {number} step The value to increment or decrement by.
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {Array} Returns the range of numbers.
+ */
+ function baseRange(start, end, step, fromRight) {
+ var index = -1,
+ length = nativeMax(nativeCeil((end - start) / (step || 1)), 0),
+ result = Array(length);
+
+ while (length--) {
+ result[fromRight ? length : ++index] = start;
+ start += step;
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.rest` which doesn't validate or coerce arguments.
+ *
+ * @private
+ * @param {Function} func The function to apply a rest parameter to.
+ * @param {number} [start=func.length-1] The start position of the rest parameter.
+ * @returns {Function} Returns the new function.
+ */
+ function baseRest(func, start) {
+ return setToString(overRest(func, start, identity), func + '');
+ }
+
+ /**
+ * The base implementation of `_.set`.
+ *
+ * @private
+ * @param {Object} object The object to modify.
+ * @param {Array|string} path The path of the property to set.
+ * @param {*} value The value to set.
+ * @param {Function} [customizer] The function to customize path creation.
+ * @returns {Object} Returns `object`.
+ */
+ function baseSet(object, path, value, customizer) {
+ if (!isObject(object)) {
+ return object;
+ }
+ path = castPath(path, object);
+
+ var index = -1,
+ length = path.length,
+ lastIndex = length - 1,
+ nested = object;
+
+ while (nested != null && ++index < length) {
+ var key = toKey(path[index]),
+ newValue = value;
+
+ if (index != lastIndex) {
+ var objValue = nested[key];
+ newValue = customizer ? customizer(objValue, key, nested) : undefined;
+ if (newValue === undefined) {
+ newValue = isObject(objValue)
+ ? objValue
+ : (isIndex(path[index + 1]) ? [] : {});
+ }
+ }
+ assignValue(nested, key, newValue);
+ nested = nested[key];
+ }
+ return object;
+ }
+
+ /**
+ * The base implementation of `setData` without support for hot loop shorting.
+ *
+ * @private
+ * @param {Function} func The function to associate metadata with.
+ * @param {*} data The metadata.
+ * @returns {Function} Returns `func`.
+ */
+ var baseSetData = !metaMap ? identity : function(func, data) {
+ metaMap.set(func, data);
+ return func;
+ };
+
+ /**
+ * The base implementation of `setToString` without support for hot loop shorting.
+ *
+ * @private
+ * @param {Function} func The function to modify.
+ * @param {Function} string The `toString` result.
+ * @returns {Function} Returns `func`.
+ */
+ var baseSetToString = !defineProperty ? identity : function(func, string) {
+ return defineProperty(func, 'toString', {
+ 'configurable': true,
+ 'enumerable': false,
+ 'value': constant(string),
+ 'writable': true
+ });
+ };
+
+ /**
+ * The base implementation of `_.slice` without an iteratee call guard.
+ *
+ * @private
+ * @param {Array} array The array to slice.
+ * @param {number} [start=0] The start position.
+ * @param {number} [end=array.length] The end position.
+ * @returns {Array} Returns the slice of `array`.
+ */
+ function baseSlice(array, start, end) {
+ var index = -1,
+ length = array.length;
+
+ if (start < 0) {
+ start = -start > length ? 0 : (length + start);
+ }
+ end = end > length ? length : end;
+ if (end < 0) {
+ end += length;
+ }
+ length = start > end ? 0 : ((end - start) >>> 0);
+ start >>>= 0;
+
+ var result = Array(length);
+ while (++index < length) {
+ result[index] = array[index + start];
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.some` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {boolean} Returns `true` if any element passes the predicate check,
+ * else `false`.
+ */
+ function baseSome(collection, predicate) {
+ var result;
+
+ baseEach(collection, function(value, index, collection) {
+ result = predicate(value, index, collection);
+ return !result;
+ });
+ return !!result;
+ }
+
+ /**
+ * The base implementation of `_.toString` which doesn't convert nullish
+ * values to empty strings.
+ *
+ * @private
+ * @param {*} value The value to process.
+ * @returns {string} Returns the string.
+ */
+ function baseToString(value) {
+ // Exit early for strings to avoid a performance hit in some environments.
+ if (typeof value == 'string') {
+ return value;
+ }
+ if (isArray(value)) {
+ // Recursively convert values (susceptible to call stack limits).
+ return arrayMap(value, baseToString) + '';
+ }
+ if (isSymbol(value)) {
+ return symbolToString ? symbolToString.call(value) : '';
+ }
+ var result = (value + '');
+ return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
+ }
+
+ /**
+ * The base implementation of `_.uniqBy` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {Function} [iteratee] The iteratee invoked per element.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new duplicate free array.
+ */
+ function baseUniq(array, iteratee, comparator) {
+ var index = -1,
+ includes = arrayIncludes,
+ length = array.length,
+ isCommon = true,
+ result = [],
+ seen = result;
+
+ if (comparator) {
+ isCommon = false;
+ includes = arrayIncludesWith;
+ }
+ else if (length >= LARGE_ARRAY_SIZE) {
+ var set = iteratee ? null : createSet(array);
+ if (set) {
+ return setToArray(set);
+ }
+ isCommon = false;
+ includes = cacheHas;
+ seen = new SetCache;
+ }
+ else {
+ seen = iteratee ? [] : result;
+ }
+ outer:
+ while (++index < length) {
+ var value = array[index],
+ computed = iteratee ? iteratee(value) : value;
+
+ value = (comparator || value !== 0) ? value : 0;
+ if (isCommon && computed === computed) {
+ var seenIndex = seen.length;
+ while (seenIndex--) {
+ if (seen[seenIndex] === computed) {
+ continue outer;
+ }
+ }
+ if (iteratee) {
+ seen.push(computed);
+ }
+ result.push(value);
+ }
+ else if (!includes(seen, computed, comparator)) {
+ if (seen !== result) {
+ seen.push(computed);
+ }
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The base implementation of `_.unset`.
+ *
+ * @private
+ * @param {Object} object The object to modify.
+ * @param {Array|string} path The property path to unset.
+ * @returns {boolean} Returns `true` if the property is deleted, else `false`.
+ */
+ function baseUnset(object, path) {
+ path = castPath(path, object);
+ object = parent(object, path);
+ return object == null || delete object[toKey(last(path))];
+ }
+
+ /**
+ * The base implementation of `wrapperValue` which returns the result of
+ * performing a sequence of actions on the unwrapped `value`, where each
+ * successive action is supplied the return value of the previous.
+ *
+ * @private
+ * @param {*} value The unwrapped value.
+ * @param {Array} actions Actions to perform to resolve the unwrapped value.
+ * @returns {*} Returns the resolved value.
+ */
+ function baseWrapperValue(value, actions) {
+ var result = value;
+ if (result instanceof LazyWrapper) {
+ result = result.value();
+ }
+ return arrayReduce(actions, function(result, action) {
+ return action.func.apply(action.thisArg, arrayPush([result], action.args));
+ }, result);
+ }
+
+ /**
+ * This base implementation of `_.zipObject` which assigns values using `assignFunc`.
+ *
+ * @private
+ * @param {Array} props The property identifiers.
+ * @param {Array} values The property values.
+ * @param {Function} assignFunc The function to assign values.
+ * @returns {Object} Returns the new object.
+ */
+ function baseZipObject(props, values, assignFunc) {
+ var index = -1,
+ length = props.length,
+ valsLength = values.length,
+ result = {};
+
+ while (++index < length) {
+ var value = index < valsLength ? values[index] : undefined;
+ assignFunc(result, props[index], value);
+ }
+ return result;
+ }
+
+ /**
+ * Casts `value` to an empty array if it's not an array like object.
+ *
+ * @private
+ * @param {*} value The value to inspect.
+ * @returns {Array|Object} Returns the cast array-like object.
+ */
+ function castArrayLikeObject(value) {
+ return isArrayLikeObject(value) ? value : [];
+ }
+
+ /**
+ * Casts `value` to a path array if it's not one.
+ *
+ * @private
+ * @param {*} value The value to inspect.
+ * @param {Object} [object] The object to query keys on.
+ * @returns {Array} Returns the cast property path array.
+ */
+ function castPath(value, object) {
+ if (isArray(value)) {
+ return value;
+ }
+ return isKey(value, object) ? [value] : stringToPath(toString(value));
+ }
+
+ /**
+ * Casts `array` to a slice if it's needed.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {number} start The start position.
+ * @param {number} [end=array.length] The end position.
+ * @returns {Array} Returns the cast slice.
+ */
+ function castSlice(array, start, end) {
+ var length = array.length;
+ end = end === undefined ? length : end;
+ return (!start && end >= length) ? array : baseSlice(array, start, end);
+ }
+
+ /**
+ * Creates a clone of `buffer`.
+ *
+ * @private
+ * @param {Buffer} buffer The buffer to clone.
+ * @param {boolean} [isDeep] Specify a deep clone.
+ * @returns {Buffer} Returns the cloned buffer.
+ */
+ function cloneBuffer(buffer, isDeep) {
+ if (isDeep) {
+ return buffer.slice();
+ }
+ var length = buffer.length,
+ result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length);
+
+ buffer.copy(result);
+ return result;
+ }
+
+ /**
+ * Creates a clone of `arrayBuffer`.
+ *
+ * @private
+ * @param {ArrayBuffer} arrayBuffer The array buffer to clone.
+ * @returns {ArrayBuffer} Returns the cloned array buffer.
+ */
+ function cloneArrayBuffer(arrayBuffer) {
+ var result = new arrayBuffer.constructor(arrayBuffer.byteLength);
+ new Uint8Array(result).set(new Uint8Array(arrayBuffer));
+ return result;
+ }
+
+ /**
+ * Creates a clone of `dataView`.
+ *
+ * @private
+ * @param {Object} dataView The data view to clone.
+ * @param {boolean} [isDeep] Specify a deep clone.
+ * @returns {Object} Returns the cloned data view.
+ */
+ function cloneDataView(dataView, isDeep) {
+ var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;
+ return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);
+ }
+
+ /**
+ * Creates a clone of `regexp`.
+ *
+ * @private
+ * @param {Object} regexp The regexp to clone.
+ * @returns {Object} Returns the cloned regexp.
+ */
+ function cloneRegExp(regexp) {
+ var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));
+ result.lastIndex = regexp.lastIndex;
+ return result;
+ }
+
+ /**
+ * Creates a clone of the `symbol` object.
+ *
+ * @private
+ * @param {Object} symbol The symbol object to clone.
+ * @returns {Object} Returns the cloned symbol object.
+ */
+ function cloneSymbol(symbol) {
+ return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};
+ }
+
+ /**
+ * Creates a clone of `typedArray`.
+ *
+ * @private
+ * @param {Object} typedArray The typed array to clone.
+ * @param {boolean} [isDeep] Specify a deep clone.
+ * @returns {Object} Returns the cloned typed array.
+ */
+ function cloneTypedArray(typedArray, isDeep) {
+ var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;
+ return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);
+ }
+
+ /**
+ * Compares values to sort them in ascending order.
+ *
+ * @private
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @returns {number} Returns the sort order indicator for `value`.
+ */
+ function compareAscending(value, other) {
+ if (value !== other) {
+ var valIsDefined = value !== undefined,
+ valIsNull = value === null,
+ valIsReflexive = value === value,
+ valIsSymbol = isSymbol(value);
+
+ var othIsDefined = other !== undefined,
+ othIsNull = other === null,
+ othIsReflexive = other === other,
+ othIsSymbol = isSymbol(other);
+
+ if ((!othIsNull && !othIsSymbol && !valIsSymbol && value > other) ||
+ (valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol) ||
+ (valIsNull && othIsDefined && othIsReflexive) ||
+ (!valIsDefined && othIsReflexive) ||
+ !valIsReflexive) {
+ return 1;
+ }
+ if ((!valIsNull && !valIsSymbol && !othIsSymbol && value < other) ||
+ (othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol) ||
+ (othIsNull && valIsDefined && valIsReflexive) ||
+ (!othIsDefined && valIsReflexive) ||
+ !othIsReflexive) {
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Used by `_.orderBy` to compare multiple properties of a value to another
+ * and stable sort them.
+ *
+ * If `orders` is unspecified, all values are sorted in ascending order. Otherwise,
+ * specify an order of "desc" for descending or "asc" for ascending sort order
+ * of corresponding values.
+ *
+ * @private
+ * @param {Object} object The object to compare.
+ * @param {Object} other The other object to compare.
+ * @param {boolean[]|string[]} orders The order to sort by for each property.
+ * @returns {number} Returns the sort order indicator for `object`.
+ */
+ function compareMultiple(object, other, orders) {
+ var index = -1,
+ objCriteria = object.criteria,
+ othCriteria = other.criteria,
+ length = objCriteria.length,
+ ordersLength = orders.length;
+
+ while (++index < length) {
+ var result = compareAscending(objCriteria[index], othCriteria[index]);
+ if (result) {
+ if (index >= ordersLength) {
+ return result;
+ }
+ var order = orders[index];
+ return result * (order == 'desc' ? -1 : 1);
+ }
+ }
+ // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
+ // that causes it, under certain circumstances, to provide the same value for
+ // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247
+ // for more details.
+ //
+ // This also ensures a stable sort in V8 and other engines.
+ // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details.
+ return object.index - other.index;
+ }
+
+ /**
+ * Creates an array that is the composition of partially applied arguments,
+ * placeholders, and provided arguments into a single array of arguments.
+ *
+ * @private
+ * @param {Array} args The provided arguments.
+ * @param {Array} partials The arguments to prepend to those provided.
+ * @param {Array} holders The `partials` placeholder indexes.
+ * @params {boolean} [isCurried] Specify composing for a curried function.
+ * @returns {Array} Returns the new array of composed arguments.
+ */
+ function composeArgs(args, partials, holders, isCurried) {
+ var argsIndex = -1,
+ argsLength = args.length,
+ holdersLength = holders.length,
+ leftIndex = -1,
+ leftLength = partials.length,
+ rangeLength = nativeMax(argsLength - holdersLength, 0),
+ result = Array(leftLength + rangeLength),
+ isUncurried = !isCurried;
+
+ while (++leftIndex < leftLength) {
+ result[leftIndex] = partials[leftIndex];
+ }
+ while (++argsIndex < holdersLength) {
+ if (isUncurried || argsIndex < argsLength) {
+ result[holders[argsIndex]] = args[argsIndex];
+ }
+ }
+ while (rangeLength--) {
+ result[leftIndex++] = args[argsIndex++];
+ }
+ return result;
+ }
+
+ /**
+ * This function is like `composeArgs` except that the arguments composition
+ * is tailored for `_.partialRight`.
+ *
+ * @private
+ * @param {Array} args The provided arguments.
+ * @param {Array} partials The arguments to append to those provided.
+ * @param {Array} holders The `partials` placeholder indexes.
+ * @params {boolean} [isCurried] Specify composing for a curried function.
+ * @returns {Array} Returns the new array of composed arguments.
+ */
+ function composeArgsRight(args, partials, holders, isCurried) {
+ var argsIndex = -1,
+ argsLength = args.length,
+ holdersIndex = -1,
+ holdersLength = holders.length,
+ rightIndex = -1,
+ rightLength = partials.length,
+ rangeLength = nativeMax(argsLength - holdersLength, 0),
+ result = Array(rangeLength + rightLength),
+ isUncurried = !isCurried;
+
+ while (++argsIndex < rangeLength) {
+ result[argsIndex] = args[argsIndex];
+ }
+ var offset = argsIndex;
+ while (++rightIndex < rightLength) {
+ result[offset + rightIndex] = partials[rightIndex];
+ }
+ while (++holdersIndex < holdersLength) {
+ if (isUncurried || argsIndex < argsLength) {
+ result[offset + holders[holdersIndex]] = args[argsIndex++];
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Copies the values of `source` to `array`.
+ *
+ * @private
+ * @param {Array} source The array to copy values from.
+ * @param {Array} [array=[]] The array to copy values to.
+ * @returns {Array} Returns `array`.
+ */
+ function copyArray(source, array) {
+ var index = -1,
+ length = source.length;
+
+ array || (array = Array(length));
+ while (++index < length) {
+ array[index] = source[index];
+ }
+ return array;
+ }
+
+ /**
+ * Copies properties of `source` to `object`.
+ *
+ * @private
+ * @param {Object} source The object to copy properties from.
+ * @param {Array} props The property identifiers to copy.
+ * @param {Object} [object={}] The object to copy properties to.
+ * @param {Function} [customizer] The function to customize copied values.
+ * @returns {Object} Returns `object`.
+ */
+ function copyObject(source, props, object, customizer) {
+ var isNew = !object;
+ object || (object = {});
+
+ var index = -1,
+ length = props.length;
+
+ while (++index < length) {
+ var key = props[index];
+
+ var newValue = customizer
+ ? customizer(object[key], source[key], key, object, source)
+ : undefined;
+
+ if (newValue === undefined) {
+ newValue = source[key];
+ }
+ if (isNew) {
+ baseAssignValue(object, key, newValue);
+ } else {
+ assignValue(object, key, newValue);
+ }
+ }
+ return object;
+ }
+
+ /**
+ * Copies own symbols of `source` to `object`.
+ *
+ * @private
+ * @param {Object} source The object to copy symbols from.
+ * @param {Object} [object={}] The object to copy symbols to.
+ * @returns {Object} Returns `object`.
+ */
+ function copySymbols(source, object) {
+ return copyObject(source, getSymbols(source), object);
+ }
+
+ /**
+ * Copies own and inherited symbols of `source` to `object`.
+ *
+ * @private
+ * @param {Object} source The object to copy symbols from.
+ * @param {Object} [object={}] The object to copy symbols to.
+ * @returns {Object} Returns `object`.
+ */
+ function copySymbolsIn(source, object) {
+ return copyObject(source, getSymbolsIn(source), object);
+ }
+
+ /**
+ * Creates a function like `_.groupBy`.
+ *
+ * @private
+ * @param {Function} setter The function to set accumulator values.
+ * @param {Function} [initializer] The accumulator object initializer.
+ * @returns {Function} Returns the new aggregator function.
+ */
+ function createAggregator(setter, initializer) {
+ return function(collection, iteratee) {
+ var func = isArray(collection) ? arrayAggregator : baseAggregator,
+ accumulator = initializer ? initializer() : {};
+
+ return func(collection, setter, baseIteratee(iteratee, 2), accumulator);
+ };
+ }
+
+ /**
+ * Creates a function like `_.assign`.
+ *
+ * @private
+ * @param {Function} assigner The function to assign values.
+ * @returns {Function} Returns the new assigner function.
+ */
+ function createAssigner(assigner) {
+ return baseRest(function(object, sources) {
+ var index = -1,
+ length = sources.length,
+ customizer = length > 1 ? sources[length - 1] : undefined,
+ guard = length > 2 ? sources[2] : undefined;
+
+ customizer = (assigner.length > 3 && typeof customizer == 'function')
+ ? (length--, customizer)
+ : undefined;
+
+ if (guard && isIterateeCall(sources[0], sources[1], guard)) {
+ customizer = length < 3 ? undefined : customizer;
+ length = 1;
+ }
+ object = Object(object);
+ while (++index < length) {
+ var source = sources[index];
+ if (source) {
+ assigner(object, source, index, customizer);
+ }
+ }
+ return object;
+ });
+ }
+
+ /**
+ * Creates a `baseEach` or `baseEachRight` function.
+ *
+ * @private
+ * @param {Function} eachFunc The function to iterate over a collection.
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {Function} Returns the new base function.
+ */
+ function createBaseEach(eachFunc, fromRight) {
+ return function(collection, iteratee) {
+ if (collection == null) {
+ return collection;
+ }
+ if (!isArrayLike(collection)) {
+ return eachFunc(collection, iteratee);
+ }
+ var length = collection.length,
+ index = fromRight ? length : -1,
+ iterable = Object(collection);
+
+ while ((fromRight ? index-- : ++index < length)) {
+ if (iteratee(iterable[index], index, iterable) === false) {
+ break;
+ }
+ }
+ return collection;
+ };
+ }
+
+ /**
+ * Creates a base function for methods like `_.forIn` and `_.forOwn`.
+ *
+ * @private
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {Function} Returns the new base function.
+ */
+ function createBaseFor(fromRight) {
+ return function(object, iteratee, keysFunc) {
+ var index = -1,
+ iterable = Object(object),
+ props = keysFunc(object),
+ length = props.length;
+
+ while (length--) {
+ var key = props[fromRight ? length : ++index];
+ if (iteratee(iterable[key], key, iterable) === false) {
+ break;
+ }
+ }
+ return object;
+ };
+ }
+
+ /**
+ * Creates a function that wraps `func` to invoke it with the optional `this`
+ * binding of `thisArg`.
+ *
+ * @private
+ * @param {Function} func The function to wrap.
+ * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @returns {Function} Returns the new wrapped function.
+ */
+ function createBind(func, bitmask, thisArg) {
+ var isBind = bitmask & WRAP_BIND_FLAG,
+ Ctor = createCtor(func);
+
+ function wrapper() {
+ var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
+ return fn.apply(isBind ? thisArg : this, arguments);
+ }
+ return wrapper;
+ }
+
+ /**
+ * Creates a function that produces an instance of `Ctor` regardless of
+ * whether it was invoked as part of a `new` expression or by `call` or `apply`.
+ *
+ * @private
+ * @param {Function} Ctor The constructor to wrap.
+ * @returns {Function} Returns the new wrapped function.
+ */
+ function createCtor(Ctor) {
+ return function() {
+ // Use a `switch` statement to work with class constructors. See
+ // http://ecma-international.org/ecma-262/7.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist
+ // for more details.
+ var args = arguments;
+ switch (args.length) {
+ case 0: return new Ctor;
+ case 1: return new Ctor(args[0]);
+ case 2: return new Ctor(args[0], args[1]);
+ case 3: return new Ctor(args[0], args[1], args[2]);
+ case 4: return new Ctor(args[0], args[1], args[2], args[3]);
+ case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);
+ case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]);
+ case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
+ }
+ var thisBinding = baseCreate(Ctor.prototype),
+ result = Ctor.apply(thisBinding, args);
+
+ // Mimic the constructor's `return` behavior.
+ // See https://es5.github.io/#x13.2.2 for more details.
+ return isObject(result) ? result : thisBinding;
+ };
+ }
+
+ /**
+ * Creates a function that wraps `func` to enable currying.
+ *
+ * @private
+ * @param {Function} func The function to wrap.
+ * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+ * @param {number} arity The arity of `func`.
+ * @returns {Function} Returns the new wrapped function.
+ */
+ function createCurry(func, bitmask, arity) {
+ var Ctor = createCtor(func);
+
+ function wrapper() {
+ var length = arguments.length,
+ args = Array(length),
+ index = length,
+ placeholder = getHolder(wrapper);
+
+ while (index--) {
+ args[index] = arguments[index];
+ }
+ var holders = (length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder)
+ ? []
+ : replaceHolders(args, placeholder);
+
+ length -= holders.length;
+ if (length < arity) {
+ return createRecurry(
+ func, bitmask, createHybrid, wrapper.placeholder, undefined,
+ args, holders, undefined, undefined, arity - length);
+ }
+ var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
+ return apply(fn, this, args);
+ }
+ return wrapper;
+ }
+
+ /**
+ * Creates a `_.find` or `_.findLast` function.
+ *
+ * @private
+ * @param {Function} findIndexFunc The function to find the collection index.
+ * @returns {Function} Returns the new find function.
+ */
+ function createFind(findIndexFunc) {
+ return function(collection, predicate, fromIndex) {
+ var iterable = Object(collection);
+ if (!isArrayLike(collection)) {
+ var iteratee = baseIteratee(predicate, 3);
+ collection = keys(collection);
+ predicate = function(key) { return iteratee(iterable[key], key, iterable); };
+ }
+ var index = findIndexFunc(collection, predicate, fromIndex);
+ return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined;
+ };
+ }
+
+ /**
+ * Creates a function that wraps `func` to invoke it with optional `this`
+ * binding of `thisArg`, partial application, and currying.
+ *
+ * @private
+ * @param {Function|string} func The function or method name to wrap.
+ * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {Array} [partials] The arguments to prepend to those provided to
+ * the new function.
+ * @param {Array} [holders] The `partials` placeholder indexes.
+ * @param {Array} [partialsRight] The arguments to append to those provided
+ * to the new function.
+ * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
+ * @param {Array} [argPos] The argument positions of the new function.
+ * @param {number} [ary] The arity cap of `func`.
+ * @param {number} [arity] The arity of `func`.
+ * @returns {Function} Returns the new wrapped function.
+ */
+ function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
+ var isAry = bitmask & WRAP_ARY_FLAG,
+ isBind = bitmask & WRAP_BIND_FLAG,
+ isBindKey = bitmask & WRAP_BIND_KEY_FLAG,
+ isCurried = bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG),
+ isFlip = bitmask & WRAP_FLIP_FLAG,
+ Ctor = isBindKey ? undefined : createCtor(func);
+
+ function wrapper() {
+ var length = arguments.length,
+ args = Array(length),
+ index = length;
+
+ while (index--) {
+ args[index] = arguments[index];
+ }
+ if (isCurried) {
+ var placeholder = getHolder(wrapper),
+ holdersCount = countHolders(args, placeholder);
+ }
+ if (partials) {
+ args = composeArgs(args, partials, holders, isCurried);
+ }
+ if (partialsRight) {
+ args = composeArgsRight(args, partialsRight, holdersRight, isCurried);
+ }
+ length -= holdersCount;
+ if (isCurried && length < arity) {
+ var newHolders = replaceHolders(args, placeholder);
+ return createRecurry(
+ func, bitmask, createHybrid, wrapper.placeholder, thisArg,
+ args, newHolders, argPos, ary, arity - length
+ );
+ }
+ var thisBinding = isBind ? thisArg : this,
+ fn = isBindKey ? thisBinding[func] : func;
+
+ length = args.length;
+ if (argPos) {
+ args = reorder(args, argPos);
+ } else if (isFlip && length > 1) {
+ args.reverse();
+ }
+ if (isAry && ary < length) {
+ args.length = ary;
+ }
+ if (this && this !== root && this instanceof wrapper) {
+ fn = Ctor || createCtor(fn);
+ }
+ return fn.apply(thisBinding, args);
+ }
+ return wrapper;
+ }
+
+ /**
+ * Creates a function like `_.invertBy`.
+ *
+ * @private
+ * @param {Function} setter The function to set accumulator values.
+ * @param {Function} toIteratee The function to resolve iteratees.
+ * @returns {Function} Returns the new inverter function.
+ */
+ function createInverter(setter, toIteratee) {
+ return function(object, iteratee) {
+ return baseInverter(object, setter, toIteratee(iteratee), {});
+ };
+ }
+
+ /**
+ * Creates a function that wraps `func` to invoke it with the `this` binding
+ * of `thisArg` and `partials` prepended to the arguments it receives.
+ *
+ * @private
+ * @param {Function} func The function to wrap.
+ * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+ * @param {*} thisArg The `this` binding of `func`.
+ * @param {Array} partials The arguments to prepend to those provided to
+ * the new function.
+ * @returns {Function} Returns the new wrapped function.
+ */
+ function createPartial(func, bitmask, thisArg, partials) {
+ var isBind = bitmask & WRAP_BIND_FLAG,
+ Ctor = createCtor(func);
+
+ function wrapper() {
+ var argsIndex = -1,
+ argsLength = arguments.length,
+ leftIndex = -1,
+ leftLength = partials.length,
+ args = Array(leftLength + argsLength),
+ fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
+
+ while (++leftIndex < leftLength) {
+ args[leftIndex] = partials[leftIndex];
+ }
+ while (argsLength--) {
+ args[leftIndex++] = arguments[++argsIndex];
+ }
+ return apply(fn, isBind ? thisArg : this, args);
+ }
+ return wrapper;
+ }
+
+ /**
+ * Creates a `_.range` or `_.rangeRight` function.
+ *
+ * @private
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {Function} Returns the new range function.
+ */
+ function createRange(fromRight) {
+ return function(start, end, step) {
+ if (step && typeof step != 'number' && isIterateeCall(start, end, step)) {
+ end = step = undefined;
+ }
+ // Ensure the sign of `-0` is preserved.
+ start = toFinite(start);
+ if (end === undefined) {
+ end = start;
+ start = 0;
+ } else {
+ end = toFinite(end);
+ }
+ step = step === undefined ? (start < end ? 1 : -1) : toFinite(step);
+ return baseRange(start, end, step, fromRight);
+ };
+ }
+
+ /**
+ * Creates a function that wraps `func` to continue currying.
+ *
+ * @private
+ * @param {Function} func The function to wrap.
+ * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+ * @param {Function} wrapFunc The function to create the `func` wrapper.
+ * @param {*} placeholder The placeholder value.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {Array} [partials] The arguments to prepend to those provided to
+ * the new function.
+ * @param {Array} [holders] The `partials` placeholder indexes.
+ * @param {Array} [argPos] The argument positions of the new function.
+ * @param {number} [ary] The arity cap of `func`.
+ * @param {number} [arity] The arity of `func`.
+ * @returns {Function} Returns the new wrapped function.
+ */
+ function createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) {
+ var isCurry = bitmask & WRAP_CURRY_FLAG,
+ newHolders = isCurry ? holders : undefined,
+ newHoldersRight = isCurry ? undefined : holders,
+ newPartials = isCurry ? partials : undefined,
+ newPartialsRight = isCurry ? undefined : partials;
+
+ bitmask |= (isCurry ? WRAP_PARTIAL_FLAG : WRAP_PARTIAL_RIGHT_FLAG);
+ bitmask &= ~(isCurry ? WRAP_PARTIAL_RIGHT_FLAG : WRAP_PARTIAL_FLAG);
+
+ if (!(bitmask & WRAP_CURRY_BOUND_FLAG)) {
+ bitmask &= ~(WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG);
+ }
+ var newData = [
+ func, bitmask, thisArg, newPartials, newHolders, newPartialsRight,
+ newHoldersRight, argPos, ary, arity
+ ];
+
+ var result = wrapFunc.apply(undefined, newData);
+ if (isLaziable(func)) {
+ setData(result, newData);
+ }
+ result.placeholder = placeholder;
+ return setWrapToString(result, func, bitmask);
+ }
+
+ /**
+ * Creates a set object of `values`.
+ *
+ * @private
+ * @param {Array} values The values to add to the set.
+ * @returns {Object} Returns the new set.
+ */
+ var createSet = !(Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) ? noop : function(values) {
+ return new Set(values);
+ };
+
+ /**
+ * Creates a function that either curries or invokes `func` with optional
+ * `this` binding and partially applied arguments.
+ *
+ * @private
+ * @param {Function|string} func The function or method name to wrap.
+ * @param {number} bitmask The bitmask flags.
+ * 1 - `_.bind`
+ * 2 - `_.bindKey`
+ * 4 - `_.curry` or `_.curryRight` of a bound function
+ * 8 - `_.curry`
+ * 16 - `_.curryRight`
+ * 32 - `_.partial`
+ * 64 - `_.partialRight`
+ * 128 - `_.rearg`
+ * 256 - `_.ary`
+ * 512 - `_.flip`
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {Array} [partials] The arguments to be partially applied.
+ * @param {Array} [holders] The `partials` placeholder indexes.
+ * @param {Array} [argPos] The argument positions of the new function.
+ * @param {number} [ary] The arity cap of `func`.
+ * @param {number} [arity] The arity of `func`.
+ * @returns {Function} Returns the new wrapped function.
+ */
+ function createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
+ var isBindKey = bitmask & WRAP_BIND_KEY_FLAG;
+ if (!isBindKey && typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ var length = partials ? partials.length : 0;
+ if (!length) {
+ bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG);
+ partials = holders = undefined;
+ }
+ ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0);
+ arity = arity === undefined ? arity : toInteger(arity);
+ length -= holders ? holders.length : 0;
+
+ if (bitmask & WRAP_PARTIAL_RIGHT_FLAG) {
+ var partialsRight = partials,
+ holdersRight = holders;
+
+ partials = holders = undefined;
+ }
+ var data = isBindKey ? undefined : getData(func);
+
+ var newData = [
+ func, bitmask, thisArg, partials, holders, partialsRight, holdersRight,
+ argPos, ary, arity
+ ];
+
+ if (data) {
+ mergeData(newData, data);
+ }
+ func = newData[0];
+ bitmask = newData[1];
+ thisArg = newData[2];
+ partials = newData[3];
+ holders = newData[4];
+ arity = newData[9] = newData[9] === undefined
+ ? (isBindKey ? 0 : func.length)
+ : nativeMax(newData[9] - length, 0);
+
+ if (!arity && bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG)) {
+ bitmask &= ~(WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG);
+ }
+ if (!bitmask || bitmask == WRAP_BIND_FLAG) {
+ var result = createBind(func, bitmask, thisArg);
+ } else if (bitmask == WRAP_CURRY_FLAG || bitmask == WRAP_CURRY_RIGHT_FLAG) {
+ result = createCurry(func, bitmask, arity);
+ } else if ((bitmask == WRAP_PARTIAL_FLAG || bitmask == (WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG)) && !holders.length) {
+ result = createPartial(func, bitmask, thisArg, partials);
+ } else {
+ result = createHybrid.apply(undefined, newData);
+ }
+ var setter = data ? baseSetData : setData;
+ return setWrapToString(setter(result, newData), func, bitmask);
+ }
+
+ /**
+ * Used by `_.defaultsDeep` to customize its `_.merge` use to merge source
+ * objects into destination objects that are passed thru.
+ *
+ * @private
+ * @param {*} objValue The destination value.
+ * @param {*} srcValue The source value.
+ * @param {string} key The key of the property to merge.
+ * @param {Object} object The parent object of `objValue`.
+ * @param {Object} source The parent object of `srcValue`.
+ * @param {Object} [stack] Tracks traversed source values and their merged
+ * counterparts.
+ * @returns {*} Returns the value to assign.
+ */
+ function customDefaultsMerge(objValue, srcValue, key, object, source, stack) {
+ if (isObject(objValue) && isObject(srcValue)) {
+ // Recursively merge objects and arrays (susceptible to call stack limits).
+ stack.set(srcValue, objValue);
+ baseMerge(objValue, srcValue, undefined, customDefaultsMerge, stack);
+ stack['delete'](srcValue);
+ }
+ return objValue;
+ }
+
+ /**
+ * Used by `_.omit` to customize its `_.cloneDeep` use to only clone plain
+ * objects.
+ *
+ * @private
+ * @param {*} value The value to inspect.
+ * @param {string} key The key of the property to inspect.
+ * @returns {*} Returns the uncloned value or `undefined` to defer cloning to `_.cloneDeep`.
+ */
+ function customOmitClone(value) {
+ return isPlainObject(value) ? undefined : value;
+ }
+
+ /**
+ * A specialized version of `baseIsEqualDeep` for arrays with support for
+ * partial deep comparisons.
+ *
+ * @private
+ * @param {Array} array The array to compare.
+ * @param {Array} other The other array to compare.
+ * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
+ * @param {Function} customizer The function to customize comparisons.
+ * @param {Function} equalFunc The function to determine equivalents of values.
+ * @param {Object} stack Tracks traversed `array` and `other` objects.
+ * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
+ */
+ function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
+ var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
+ arrLength = array.length,
+ othLength = other.length;
+
+ if (arrLength != othLength && !(isPartial && othLength > arrLength)) {
+ return false;
+ }
+ // Assume cyclic values are equal.
+ var stacked = stack.get(array);
+ if (stacked && stack.get(other)) {
+ return stacked == other;
+ }
+ var index = -1,
+ result = true,
+ seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined;
+
+ stack.set(array, other);
+ stack.set(other, array);
+
+ // Ignore non-index properties.
+ while (++index < arrLength) {
+ var arrValue = array[index],
+ othValue = other[index];
+
+ if (customizer) {
+ var compared = isPartial
+ ? customizer(othValue, arrValue, index, other, array, stack)
+ : customizer(arrValue, othValue, index, array, other, stack);
+ }
+ if (compared !== undefined) {
+ if (compared) {
+ continue;
+ }
+ result = false;
+ break;
+ }
+ // Recursively compare arrays (susceptible to call stack limits).
+ if (seen) {
+ if (!arraySome(other, function(othValue, othIndex) {
+ if (!cacheHas(seen, othIndex) &&
+ (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {
+ return seen.push(othIndex);
+ }
+ })) {
+ result = false;
+ break;
+ }
+ } else if (!(
+ arrValue === othValue ||
+ equalFunc(arrValue, othValue, bitmask, customizer, stack)
+ )) {
+ result = false;
+ break;
+ }
+ }
+ stack['delete'](array);
+ stack['delete'](other);
+ return result;
+ }
+
+ /**
+ * A specialized version of `baseIsEqualDeep` for comparing objects of
+ * the same `toStringTag`.
+ *
+ * **Note:** This function only supports comparing values with tags of
+ * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
+ *
+ * @private
+ * @param {Object} object The object to compare.
+ * @param {Object} other The other object to compare.
+ * @param {string} tag The `toStringTag` of the objects to compare.
+ * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
+ * @param {Function} customizer The function to customize comparisons.
+ * @param {Function} equalFunc The function to determine equivalents of values.
+ * @param {Object} stack Tracks traversed `object` and `other` objects.
+ * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+ */
+ function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) {
+ switch (tag) {
+ case dataViewTag:
+ if ((object.byteLength != other.byteLength) ||
+ (object.byteOffset != other.byteOffset)) {
+ return false;
+ }
+ object = object.buffer;
+ other = other.buffer;
+
+ case arrayBufferTag:
+ if ((object.byteLength != other.byteLength) ||
+ !equalFunc(new Uint8Array(object), new Uint8Array(other))) {
+ return false;
+ }
+ return true;
+
+ case boolTag:
+ case dateTag:
+ case numberTag:
+ // Coerce booleans to `1` or `0` and dates to milliseconds.
+ // Invalid dates are coerced to `NaN`.
+ return eq(+object, +other);
+
+ case errorTag:
+ return object.name == other.name && object.message == other.message;
+
+ case regexpTag:
+ case stringTag:
+ // Coerce regexes to strings and treat strings, primitives and objects,
+ // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring
+ // for more details.
+ return object == (other + '');
+
+ case mapTag:
+ var convert = mapToArray;
+
+ case setTag:
+ var isPartial = bitmask & COMPARE_PARTIAL_FLAG;
+ convert || (convert = setToArray);
+
+ if (object.size != other.size && !isPartial) {
+ return false;
+ }
+ // Assume cyclic values are equal.
+ var stacked = stack.get(object);
+ if (stacked) {
+ return stacked == other;
+ }
+ bitmask |= COMPARE_UNORDERED_FLAG;
+
+ // Recursively compare objects (susceptible to call stack limits).
+ stack.set(object, other);
+ var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack);
+ stack['delete'](object);
+ return result;
+
+ case symbolTag:
+ if (symbolValueOf) {
+ return symbolValueOf.call(object) == symbolValueOf.call(other);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * A specialized version of `baseIsEqualDeep` for objects with support for
+ * partial deep comparisons.
+ *
+ * @private
+ * @param {Object} object The object to compare.
+ * @param {Object} other The other object to compare.
+ * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
+ * @param {Function} customizer The function to customize comparisons.
+ * @param {Function} equalFunc The function to determine equivalents of values.
+ * @param {Object} stack Tracks traversed `object` and `other` objects.
+ * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+ */
+ function equalObjects(object, other, bitmask, customizer, equalFunc, stack) {
+ var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
+ objProps = getAllKeys(object),
+ objLength = objProps.length,
+ othProps = getAllKeys(other),
+ othLength = othProps.length;
+
+ if (objLength != othLength && !isPartial) {
+ return false;
+ }
+ var index = objLength;
+ while (index--) {
+ var key = objProps[index];
+ if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) {
+ return false;
+ }
+ }
+ // Assume cyclic values are equal.
+ var stacked = stack.get(object);
+ if (stacked && stack.get(other)) {
+ return stacked == other;
+ }
+ var result = true;
+ stack.set(object, other);
+ stack.set(other, object);
+
+ var skipCtor = isPartial;
+ while (++index < objLength) {
+ key = objProps[index];
+ var objValue = object[key],
+ othValue = other[key];
+
+ if (customizer) {
+ var compared = isPartial
+ ? customizer(othValue, objValue, key, other, object, stack)
+ : customizer(objValue, othValue, key, object, other, stack);
+ }
+ // Recursively compare objects (susceptible to call stack limits).
+ if (!(compared === undefined
+ ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack))
+ : compared
+ )) {
+ result = false;
+ break;
+ }
+ skipCtor || (skipCtor = key == 'constructor');
+ }
+ if (result && !skipCtor) {
+ var objCtor = object.constructor,
+ othCtor = other.constructor;
+
+ // Non `Object` object instances with different constructors are not equal.
+ if (objCtor != othCtor &&
+ ('constructor' in object && 'constructor' in other) &&
+ !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
+ typeof othCtor == 'function' && othCtor instanceof othCtor)) {
+ result = false;
+ }
+ }
+ stack['delete'](object);
+ stack['delete'](other);
+ return result;
+ }
+
+ /**
+ * A specialized version of `baseRest` which flattens the rest array.
+ *
+ * @private
+ * @param {Function} func The function to apply a rest parameter to.
+ * @returns {Function} Returns the new function.
+ */
+ function flatRest(func) {
+ return setToString(overRest(func, undefined, flatten), func + '');
+ }
+
+ /**
+ * Creates an array of own enumerable property names and symbols of `object`.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of property names and symbols.
+ */
+ function getAllKeys(object) {
+ return baseGetAllKeys(object, keys, getSymbols);
+ }
+
+ /**
+ * Creates an array of own and inherited enumerable property names and
+ * symbols of `object`.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of property names and symbols.
+ */
+ function getAllKeysIn(object) {
+ return baseGetAllKeys(object, keysIn, getSymbolsIn);
+ }
+
+ /**
+ * Gets metadata for `func`.
+ *
+ * @private
+ * @param {Function} func The function to query.
+ * @returns {*} Returns the metadata for `func`.
+ */
+ var getData = !metaMap ? noop : function(func) {
+ return metaMap.get(func);
+ };
+
+ /**
+ * Gets the name of `func`.
+ *
+ * @private
+ * @param {Function} func The function to query.
+ * @returns {string} Returns the function name.
+ */
+ function getFuncName(func) {
+ var result = (func.name + ''),
+ array = realNames[result],
+ length = hasOwnProperty.call(realNames, result) ? array.length : 0;
+
+ while (length--) {
+ var data = array[length],
+ otherFunc = data.func;
+ if (otherFunc == null || otherFunc == func) {
+ return data.name;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Gets the argument placeholder value for `func`.
+ *
+ * @private
+ * @param {Function} func The function to inspect.
+ * @returns {*} Returns the placeholder value.
+ */
+ function getHolder(func) {
+ var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func;
+ return object.placeholder;
+ }
+
+ /**
+ * Gets the data for `map`.
+ *
+ * @private
+ * @param {Object} map The map to query.
+ * @param {string} key The reference key.
+ * @returns {*} Returns the map data.
+ */
+ function getMapData(map, key) {
+ var data = map.__data__;
+ return isKeyable(key)
+ ? data[typeof key == 'string' ? 'string' : 'hash']
+ : data.map;
+ }
+
+ /**
+ * Gets the property names, values, and compare flags of `object`.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the match data of `object`.
+ */
+ function getMatchData(object) {
+ var result = keys(object),
+ length = result.length;
+
+ while (length--) {
+ var key = result[length],
+ value = object[key];
+
+ result[length] = [key, value, isStrictComparable(value)];
+ }
+ return result;
+ }
+
+ /**
+ * Gets the native function at `key` of `object`.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @param {string} key The key of the method to get.
+ * @returns {*} Returns the function if it's native, else `undefined`.
+ */
+ function getNative(object, key) {
+ var value = getValue(object, key);
+ return baseIsNative(value) ? value : undefined;
+ }
+
+ /**
+ * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
+ *
+ * @private
+ * @param {*} value The value to query.
+ * @returns {string} Returns the raw `toStringTag`.
+ */
+ function getRawTag(value) {
+ var isOwn = hasOwnProperty.call(value, symToStringTag),
+ tag = value[symToStringTag];
+
+ try {
+ value[symToStringTag] = undefined;
+ var unmasked = true;
+ } catch (e) {}
+
+ var result = nativeObjectToString.call(value);
+ if (unmasked) {
+ if (isOwn) {
+ value[symToStringTag] = tag;
+ } else {
+ delete value[symToStringTag];
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Creates an array of the own enumerable symbols of `object`.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of symbols.
+ */
+ var getSymbols = !nativeGetSymbols ? stubArray : function(object) {
+ if (object == null) {
+ return [];
+ }
+ object = Object(object);
+ return arrayFilter(nativeGetSymbols(object), function(symbol) {
+ return propertyIsEnumerable.call(object, symbol);
+ });
+ };
+
+ /**
+ * Creates an array of the own and inherited enumerable symbols of `object`.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of symbols.
+ */
+ var getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) {
+ var result = [];
+ while (object) {
+ arrayPush(result, getSymbols(object));
+ object = getPrototype(object);
+ }
+ return result;
+ };
+
+ /**
+ * Gets the `toStringTag` of `value`.
+ *
+ * @private
+ * @param {*} value The value to query.
+ * @returns {string} Returns the `toStringTag`.
+ */
+ var getTag = baseGetTag;
+
+ // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.
+ if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||
+ (Map && getTag(new Map) != mapTag) ||
+ (Promise && getTag(Promise.resolve()) != promiseTag) ||
+ (Set && getTag(new Set) != setTag) ||
+ (WeakMap && getTag(new WeakMap) != weakMapTag)) {
+ getTag = function(value) {
+ var result = baseGetTag(value),
+ Ctor = result == objectTag ? value.constructor : undefined,
+ ctorString = Ctor ? toSource(Ctor) : '';
+
+ if (ctorString) {
+ switch (ctorString) {
+ case dataViewCtorString: return dataViewTag;
+ case mapCtorString: return mapTag;
+ case promiseCtorString: return promiseTag;
+ case setCtorString: return setTag;
+ case weakMapCtorString: return weakMapTag;
+ }
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Gets the view, applying any `transforms` to the `start` and `end` positions.
+ *
+ * @private
+ * @param {number} start The start of the view.
+ * @param {number} end The end of the view.
+ * @param {Array} transforms The transformations to apply to the view.
+ * @returns {Object} Returns an object containing the `start` and `end`
+ * positions of the view.
+ */
+ function getView(start, end, transforms) {
+ var index = -1,
+ length = transforms.length;
+
+ while (++index < length) {
+ var data = transforms[index],
+ size = data.size;
+
+ switch (data.type) {
+ case 'drop': start += size; break;
+ case 'dropRight': end -= size; break;
+ case 'take': end = nativeMin(end, start + size); break;
+ case 'takeRight': start = nativeMax(start, end - size); break;
+ }
+ }
+ return { 'start': start, 'end': end };
+ }
+
+ /**
+ * Extracts wrapper details from the `source` body comment.
+ *
+ * @private
+ * @param {string} source The source to inspect.
+ * @returns {Array} Returns the wrapper details.
+ */
+ function getWrapDetails(source) {
+ var match = source.match(reWrapDetails);
+ return match ? match[1].split(reSplitDetails) : [];
+ }
+
+ /**
+ * Checks if `path` exists on `object`.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path to check.
+ * @param {Function} hasFunc The function to check properties.
+ * @returns {boolean} Returns `true` if `path` exists, else `false`.
+ */
+ function hasPath(object, path, hasFunc) {
+ path = castPath(path, object);
+
+ var index = -1,
+ length = path.length,
+ result = false;
+
+ while (++index < length) {
+ var key = toKey(path[index]);
+ if (!(result = object != null && hasFunc(object, key))) {
+ break;
+ }
+ object = object[key];
+ }
+ if (result || ++index != length) {
+ return result;
+ }
+ length = object == null ? 0 : object.length;
+ return !!length && isLength(length) && isIndex(key, length) &&
+ (isArray(object) || isArguments(object));
+ }
+
+ /**
+ * Initializes an array clone.
+ *
+ * @private
+ * @param {Array} array The array to clone.
+ * @returns {Array} Returns the initialized clone.
+ */
+ function initCloneArray(array) {
+ var length = array.length,
+ result = new array.constructor(length);
+
+ // Add properties assigned by `RegExp#exec`.
+ if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
+ result.index = array.index;
+ result.input = array.input;
+ }
+ return result;
+ }
+
+ /**
+ * Initializes an object clone.
+ *
+ * @private
+ * @param {Object} object The object to clone.
+ * @returns {Object} Returns the initialized clone.
+ */
+ function initCloneObject(object) {
+ return (typeof object.constructor == 'function' && !isPrototype(object))
+ ? baseCreate(getPrototype(object))
+ : {};
+ }
+
+ /**
+ * Initializes an object clone based on its `toStringTag`.
+ *
+ * **Note:** This function only supports cloning values with tags of
+ * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`.
+ *
+ * @private
+ * @param {Object} object The object to clone.
+ * @param {string} tag The `toStringTag` of the object to clone.
+ * @param {boolean} [isDeep] Specify a deep clone.
+ * @returns {Object} Returns the initialized clone.
+ */
+ function initCloneByTag(object, tag, isDeep) {
+ var Ctor = object.constructor;
+ switch (tag) {
+ case arrayBufferTag:
+ return cloneArrayBuffer(object);
+
+ case boolTag:
+ case dateTag:
+ return new Ctor(+object);
+
+ case dataViewTag:
+ return cloneDataView(object, isDeep);
+
+ case float32Tag: case float64Tag:
+ case int8Tag: case int16Tag: case int32Tag:
+ case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
+ return cloneTypedArray(object, isDeep);
+
+ case mapTag:
+ return new Ctor;
+
+ case numberTag:
+ case stringTag:
+ return new Ctor(object);
+
+ case regexpTag:
+ return cloneRegExp(object);
+
+ case setTag:
+ return new Ctor;
+
+ case symbolTag:
+ return cloneSymbol(object);
+ }
+ }
+
+ /**
+ * Inserts wrapper `details` in a comment at the top of the `source` body.
+ *
+ * @private
+ * @param {string} source The source to modify.
+ * @returns {Array} details The details to insert.
+ * @returns {string} Returns the modified source.
+ */
+ function insertWrapDetails(source, details) {
+ var length = details.length;
+ if (!length) {
+ return source;
+ }
+ var lastIndex = length - 1;
+ details[lastIndex] = (length > 1 ? '& ' : '') + details[lastIndex];
+ details = details.join(length > 2 ? ', ' : ' ');
+ return source.replace(reWrapComment, '{\n/* [wrapped with ' + details + '] */\n');
+ }
+
+ /**
+ * Checks if `value` is a flattenable `arguments` object or array.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
+ */
+ function isFlattenable(value) {
+ return isArray(value) || isArguments(value) ||
+ !!(spreadableSymbol && value && value[spreadableSymbol]);
+ }
+
+ /**
+ * Checks if `value` is a valid array-like index.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
+ * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
+ */
+ function isIndex(value, length) {
+ var type = typeof value;
+ length = length == null ? MAX_SAFE_INTEGER : length;
+
+ return !!length &&
+ (type == 'number' ||
+ (type != 'symbol' && reIsUint.test(value))) &&
+ (value > -1 && value % 1 == 0 && value < length);
+ }
+
+ /**
+ * Checks if the given arguments are from an iteratee call.
+ *
+ * @private
+ * @param {*} value The potential iteratee value argument.
+ * @param {*} index The potential iteratee index or key argument.
+ * @param {*} object The potential iteratee object argument.
+ * @returns {boolean} Returns `true` if the arguments are from an iteratee call,
+ * else `false`.
+ */
+ function isIterateeCall(value, index, object) {
+ if (!isObject(object)) {
+ return false;
+ }
+ var type = typeof index;
+ if (type == 'number'
+ ? (isArrayLike(object) && isIndex(index, object.length))
+ : (type == 'string' && index in object)
+ ) {
+ return eq(object[index], value);
+ }
+ return false;
+ }
+
+ /**
+ * Checks if `value` is a property name and not a property path.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @param {Object} [object] The object to query keys on.
+ * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
+ */
+ function isKey(value, object) {
+ if (isArray(value)) {
+ return false;
+ }
+ var type = typeof value;
+ if (type == 'number' || type == 'symbol' || type == 'boolean' ||
+ value == null || isSymbol(value)) {
+ return true;
+ }
+ return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
+ (object != null && value in Object(object));
+ }
+
+ /**
+ * Checks if `value` is suitable for use as unique object key.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
+ */
+ function isKeyable(value) {
+ var type = typeof value;
+ return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
+ ? (value !== '__proto__')
+ : (value === null);
+ }
+
+ /**
+ * Checks if `func` has a lazy counterpart.
+ *
+ * @private
+ * @param {Function} func The function to check.
+ * @returns {boolean} Returns `true` if `func` has a lazy counterpart,
+ * else `false`.
+ */
+ function isLaziable(func) {
+ var funcName = getFuncName(func),
+ other = lodash[funcName];
+
+ if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) {
+ return false;
+ }
+ if (func === other) {
+ return true;
+ }
+ var data = getData(other);
+ return !!data && func === data[0];
+ }
+
+ /**
+ * Checks if `func` has its source masked.
+ *
+ * @private
+ * @param {Function} func The function to check.
+ * @returns {boolean} Returns `true` if `func` is masked, else `false`.
+ */
+ function isMasked(func) {
+ return !!maskSrcKey && (maskSrcKey in func);
+ }
+
+ /**
+ * Checks if `value` is likely a prototype object.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
+ */
+ function isPrototype(value) {
+ var Ctor = value && value.constructor,
+ proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;
+
+ return value === proto;
+ }
+
+ /**
+ * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` if suitable for strict
+ * equality comparisons, else `false`.
+ */
+ function isStrictComparable(value) {
+ return value === value && !isObject(value);
+ }
+
+ /**
+ * A specialized version of `matchesProperty` for source values suitable
+ * for strict equality comparisons, i.e. `===`.
+ *
+ * @private
+ * @param {string} key The key of the property to get.
+ * @param {*} srcValue The value to match.
+ * @returns {Function} Returns the new spec function.
+ */
+ function matchesStrictComparable(key, srcValue) {
+ return function(object) {
+ if (object == null) {
+ return false;
+ }
+ return object[key] === srcValue &&
+ (srcValue !== undefined || (key in Object(object)));
+ };
+ }
+
+ /**
+ * A specialized version of `_.memoize` which clears the memoized function's
+ * cache when it exceeds `MAX_MEMOIZE_SIZE`.
+ *
+ * @private
+ * @param {Function} func The function to have its output memoized.
+ * @returns {Function} Returns the new memoized function.
+ */
+ function memoizeCapped(func) {
+ var result = memoize(func, function(key) {
+ if (cache.size === MAX_MEMOIZE_SIZE) {
+ cache.clear();
+ }
+ return key;
+ });
+
+ var cache = result.cache;
+ return result;
+ }
+
+ /**
+ * Merges the function metadata of `source` into `data`.
+ *
+ * Merging metadata reduces the number of wrappers used to invoke a function.
+ * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`
+ * may be applied regardless of execution order. Methods like `_.ary` and
+ * `_.rearg` modify function arguments, making the order in which they are
+ * executed important, preventing the merging of metadata. However, we make
+ * an exception for a safe combined case where curried functions have `_.ary`
+ * and or `_.rearg` applied.
+ *
+ * @private
+ * @param {Array} data The destination metadata.
+ * @param {Array} source The source metadata.
+ * @returns {Array} Returns `data`.
+ */
+ function mergeData(data, source) {
+ var bitmask = data[1],
+ srcBitmask = source[1],
+ newBitmask = bitmask | srcBitmask,
+ isCommon = newBitmask < (WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG | WRAP_ARY_FLAG);
+
+ var isCombo =
+ ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_CURRY_FLAG)) ||
+ ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_REARG_FLAG) && (data[7].length <= source[8])) ||
+ ((srcBitmask == (WRAP_ARY_FLAG | WRAP_REARG_FLAG)) && (source[7].length <= source[8]) && (bitmask == WRAP_CURRY_FLAG));
+
+ // Exit early if metadata can't be merged.
+ if (!(isCommon || isCombo)) {
+ return data;
+ }
+ // Use source `thisArg` if available.
+ if (srcBitmask & WRAP_BIND_FLAG) {
+ data[2] = source[2];
+ // Set when currying a bound function.
+ newBitmask |= bitmask & WRAP_BIND_FLAG ? 0 : WRAP_CURRY_BOUND_FLAG;
+ }
+ // Compose partial arguments.
+ var value = source[3];
+ if (value) {
+ var partials = data[3];
+ data[3] = partials ? composeArgs(partials, value, source[4]) : value;
+ data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4];
+ }
+ // Compose partial right arguments.
+ value = source[5];
+ if (value) {
+ partials = data[5];
+ data[5] = partials ? composeArgsRight(partials, value, source[6]) : value;
+ data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6];
+ }
+ // Use source `argPos` if available.
+ value = source[7];
+ if (value) {
+ data[7] = value;
+ }
+ // Use source `ary` if it's smaller.
+ if (srcBitmask & WRAP_ARY_FLAG) {
+ data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);
+ }
+ // Use source `arity` if one is not provided.
+ if (data[9] == null) {
+ data[9] = source[9];
+ }
+ // Use source `func` and merge bitmasks.
+ data[0] = source[0];
+ data[1] = newBitmask;
+
+ return data;
+ }
+
+ /**
+ * This function is like
+ * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
+ * except that it includes inherited enumerable properties.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of property names.
+ */
+ function nativeKeysIn(object) {
+ var result = [];
+ if (object != null) {
+ for (var key in Object(object)) {
+ result.push(key);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Converts `value` to a string using `Object.prototype.toString`.
+ *
+ * @private
+ * @param {*} value The value to convert.
+ * @returns {string} Returns the converted string.
+ */
+ function objectToString(value) {
+ return nativeObjectToString.call(value);
+ }
+
+ /**
+ * A specialized version of `baseRest` which transforms the rest array.
+ *
+ * @private
+ * @param {Function} func The function to apply a rest parameter to.
+ * @param {number} [start=func.length-1] The start position of the rest parameter.
+ * @param {Function} transform The rest array transform.
+ * @returns {Function} Returns the new function.
+ */
+ function overRest(func, start, transform) {
+ start = nativeMax(start === undefined ? (func.length - 1) : start, 0);
+ return function() {
+ var args = arguments,
+ index = -1,
+ length = nativeMax(args.length - start, 0),
+ array = Array(length);
+
+ while (++index < length) {
+ array[index] = args[start + index];
+ }
+ index = -1;
+ var otherArgs = Array(start + 1);
+ while (++index < start) {
+ otherArgs[index] = args[index];
+ }
+ otherArgs[start] = transform(array);
+ return apply(func, this, otherArgs);
+ };
+ }
+
+ /**
+ * Gets the parent value at `path` of `object`.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @param {Array} path The path to get the parent value of.
+ * @returns {*} Returns the parent value.
+ */
+ function parent(object, path) {
+ return path.length < 2 ? object : baseGet(object, baseSlice(path, 0, -1));
+ }
+
+ /**
+ * Reorder `array` according to the specified indexes where the element at
+ * the first index is assigned as the first element, the element at
+ * the second index is assigned as the second element, and so on.
+ *
+ * @private
+ * @param {Array} array The array to reorder.
+ * @param {Array} indexes The arranged array indexes.
+ * @returns {Array} Returns `array`.
+ */
+ function reorder(array, indexes) {
+ var arrLength = array.length,
+ length = nativeMin(indexes.length, arrLength),
+ oldArray = copyArray(array);
+
+ while (length--) {
+ var index = indexes[length];
+ array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
+ }
+ return array;
+ }
+
+ /**
+ * Sets metadata for `func`.
+ *
+ * **Note:** If this function becomes hot, i.e. is invoked a lot in a short
+ * period of time, it will trip its breaker and transition to an identity
+ * function to avoid garbage collection pauses in V8. See
+ * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070)
+ * for more details.
+ *
+ * @private
+ * @param {Function} func The function to associate metadata with.
+ * @param {*} data The metadata.
+ * @returns {Function} Returns `func`.
+ */
+ var setData = shortOut(baseSetData);
+
+ /**
+ * Sets the `toString` method of `func` to return `string`.
+ *
+ * @private
+ * @param {Function} func The function to modify.
+ * @param {Function} string The `toString` result.
+ * @returns {Function} Returns `func`.
+ */
+ var setToString = shortOut(baseSetToString);
+
+ /**
+ * Sets the `toString` method of `wrapper` to mimic the source of `reference`
+ * with wrapper details in a comment at the top of the source body.
+ *
+ * @private
+ * @param {Function} wrapper The function to modify.
+ * @param {Function} reference The reference function.
+ * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+ * @returns {Function} Returns `wrapper`.
+ */
+ function setWrapToString(wrapper, reference, bitmask) {
+ var source = (reference + '');
+ return setToString(wrapper, insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask)));
+ }
+
+ /**
+ * Creates a function that'll short out and invoke `identity` instead
+ * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN`
+ * milliseconds.
+ *
+ * @private
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new shortable function.
+ */
+ function shortOut(func) {
+ var count = 0,
+ lastCalled = 0;
+
+ return function() {
+ var stamp = nativeNow(),
+ remaining = HOT_SPAN - (stamp - lastCalled);
+
+ lastCalled = stamp;
+ if (remaining > 0) {
+ if (++count >= HOT_COUNT) {
+ return arguments[0];
+ }
+ } else {
+ count = 0;
+ }
+ return func.apply(undefined, arguments);
+ };
+ }
+
+ /**
+ * Converts `string` to a property path array.
+ *
+ * @private
+ * @param {string} string The string to convert.
+ * @returns {Array} Returns the property path array.
+ */
+ var stringToPath = memoizeCapped(function(string) {
+ var result = [];
+ if (string.charCodeAt(0) === 46 /* . */) {
+ result.push('');
+ }
+ string.replace(rePropName, function(match, number, quote, subString) {
+ result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match));
+ });
+ return result;
+ });
+
+ /**
+ * Converts `value` to a string key if it's not a string or symbol.
+ *
+ * @private
+ * @param {*} value The value to inspect.
+ * @returns {string|symbol} Returns the key.
+ */
+ function toKey(value) {
+ if (typeof value == 'string' || isSymbol(value)) {
+ return value;
+ }
+ var result = (value + '');
+ return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
+ }
+
+ /**
+ * Converts `func` to its source code.
+ *
+ * @private
+ * @param {Function} func The function to convert.
+ * @returns {string} Returns the source code.
+ */
+ function toSource(func) {
+ if (func != null) {
+ try {
+ return funcToString.call(func);
+ } catch (e) {}
+ try {
+ return (func + '');
+ } catch (e) {}
+ }
+ return '';
+ }
+
+ /**
+ * Updates wrapper `details` based on `bitmask` flags.
+ *
+ * @private
+ * @returns {Array} details The details to modify.
+ * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+ * @returns {Array} Returns `details`.
+ */
+ function updateWrapDetails(details, bitmask) {
+ arrayEach(wrapFlags, function(pair) {
+ var value = '_.' + pair[0];
+ if ((bitmask & pair[1]) && !arrayIncludes(details, value)) {
+ details.push(value);
+ }
+ });
+ return details.sort();
+ }
+
+ /**
+ * Creates a clone of `wrapper`.
+ *
+ * @private
+ * @param {Object} wrapper The wrapper to clone.
+ * @returns {Object} Returns the cloned wrapper.
+ */
+ function wrapperClone(wrapper) {
+ if (wrapper instanceof LazyWrapper) {
+ return wrapper.clone();
+ }
+ var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__);
+ result.__actions__ = copyArray(wrapper.__actions__);
+ result.__index__ = wrapper.__index__;
+ result.__values__ = wrapper.__values__;
+ return result;
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates an array with all falsey values removed. The values `false`, `null`,
+ * `0`, `""`, `undefined`, and `NaN` are falsey.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to compact.
+ * @returns {Array} Returns the new array of filtered values.
+ * @example
+ *
+ * _.compact([0, 1, false, 2, '', 3]);
+ * // => [1, 2, 3]
+ */
+ function compact(array) {
+ var index = -1,
+ length = array == null ? 0 : array.length,
+ resIndex = 0,
+ result = [];
+
+ while (++index < length) {
+ var value = array[index];
+ if (value) {
+ result[resIndex++] = value;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Creates a new array concatenating `array` with any additional arrays
+ * and/or values.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to concatenate.
+ * @param {...*} [values] The values to concatenate.
+ * @returns {Array} Returns the new concatenated array.
+ * @example
+ *
+ * var array = [1];
+ * var other = _.concat(array, 2, [3], [[4]]);
+ *
+ * console.log(other);
+ * // => [1, 2, 3, [4]]
+ *
+ * console.log(array);
+ * // => [1]
+ */
+ function concat() {
+ var length = arguments.length;
+ if (!length) {
+ return [];
+ }
+ var args = Array(length - 1),
+ array = arguments[0],
+ index = length;
+
+ while (index--) {
+ args[index - 1] = arguments[index];
+ }
+ return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1));
+ }
+
+ /**
+ * Creates an array of `array` values not included in the other given arrays
+ * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons. The order and references of result values are
+ * determined by the first array.
+ *
+ * **Note:** Unlike `_.pullAll`, this method returns a new array.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {...Array} [values] The values to exclude.
+ * @returns {Array} Returns the new array of filtered values.
+ * @see _.without, _.xor
+ * @example
+ *
+ * _.difference([2, 1], [2, 3]);
+ * // => [1]
+ */
+ var difference = baseRest(function(array, values) {
+ return isArrayLikeObject(array)
+ ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))
+ : [];
+ });
+
+ /**
+ * Creates a slice of `array` with `n` elements dropped from the beginning.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.5.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {number} [n=1] The number of elements to drop.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.drop([1, 2, 3]);
+ * // => [2, 3]
+ *
+ * _.drop([1, 2, 3], 2);
+ * // => [3]
+ *
+ * _.drop([1, 2, 3], 5);
+ * // => []
+ *
+ * _.drop([1, 2, 3], 0);
+ * // => [1, 2, 3]
+ */
+ function drop(array, n, guard) {
+ var length = array == null ? 0 : array.length;
+ if (!length) {
+ return [];
+ }
+ n = (guard || n === undefined) ? 1 : toInteger(n);
+ return baseSlice(array, n < 0 ? 0 : n, length);
+ }
+
+ /**
+ * This method is like `_.find` except that it returns the index of the first
+ * element `predicate` returns truthy for instead of the element itself.
+ *
+ * @static
+ * @memberOf _
+ * @since 1.1.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {number} Returns the index of the found element, else `-1`.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'active': false },
+ * { 'user': 'fred', 'active': false },
+ * { 'user': 'pebbles', 'active': true }
+ * ];
+ *
+ * _.findIndex(users, function(o) { return o.user == 'barney'; });
+ * // => 0
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.findIndex(users, { 'user': 'fred', 'active': false });
+ * // => 1
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.findIndex(users, ['active', false]);
+ * // => 0
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.findIndex(users, 'active');
+ * // => 2
+ */
+ function findIndex(array, predicate, fromIndex) {
+ var length = array == null ? 0 : array.length;
+ if (!length) {
+ return -1;
+ }
+ var index = fromIndex == null ? 0 : toInteger(fromIndex);
+ if (index < 0) {
+ index = nativeMax(length + index, 0);
+ }
+ return baseFindIndex(array, baseIteratee(predicate, 3), index);
+ }
+
+ /**
+ * This method is like `_.findIndex` except that it iterates over elements
+ * of `collection` from right to left.
+ *
+ * @static
+ * @memberOf _
+ * @since 2.0.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @param {number} [fromIndex=array.length-1] The index to search from.
+ * @returns {number} Returns the index of the found element, else `-1`.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'active': true },
+ * { 'user': 'fred', 'active': false },
+ * { 'user': 'pebbles', 'active': false }
+ * ];
+ *
+ * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; });
+ * // => 2
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.findLastIndex(users, { 'user': 'barney', 'active': true });
+ * // => 0
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.findLastIndex(users, ['active', false]);
+ * // => 2
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.findLastIndex(users, 'active');
+ * // => 0
+ */
+ function findLastIndex(array, predicate, fromIndex) {
+ var length = array == null ? 0 : array.length;
+ if (!length) {
+ return -1;
+ }
+ var index = length - 1;
+ if (fromIndex !== undefined) {
+ index = toInteger(fromIndex);
+ index = fromIndex < 0
+ ? nativeMax(length + index, 0)
+ : nativeMin(index, length - 1);
+ }
+ return baseFindIndex(array, baseIteratee(predicate, 3), index, true);
+ }
+
+ /**
+ * Flattens `array` a single level deep.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to flatten.
+ * @returns {Array} Returns the new flattened array.
+ * @example
+ *
+ * _.flatten([1, [2, [3, [4]], 5]]);
+ * // => [1, 2, [3, [4]], 5]
+ */
+ function flatten(array) {
+ var length = array == null ? 0 : array.length;
+ return length ? baseFlatten(array, 1) : [];
+ }
+
+ /**
+ * Recursively flattens `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Array
+ * @param {Array} array The array to flatten.
+ * @returns {Array} Returns the new flattened array.
+ * @example
+ *
+ * _.flattenDeep([1, [2, [3, [4]], 5]]);
+ * // => [1, 2, 3, 4, 5]
+ */
+ function flattenDeep(array) {
+ var length = array == null ? 0 : array.length;
+ return length ? baseFlatten(array, INFINITY) : [];
+ }
+
+ /**
+ * Gets the first element of `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @alias first
+ * @category Array
+ * @param {Array} array The array to query.
+ * @returns {*} Returns the first element of `array`.
+ * @example
+ *
+ * _.head([1, 2, 3]);
+ * // => 1
+ *
+ * _.head([]);
+ * // => undefined
+ */
+ function head(array) {
+ return (array && array.length) ? array[0] : undefined;
+ }
+
+ /**
+ * Gets the index at which the first occurrence of `value` is found in `array`
+ * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons. If `fromIndex` is negative, it's used as the
+ * offset from the end of `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to search for.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ * @example
+ *
+ * _.indexOf([1, 2, 1, 2], 2);
+ * // => 1
+ *
+ * // Search from the `fromIndex`.
+ * _.indexOf([1, 2, 1, 2], 2, 2);
+ * // => 3
+ */
+ function indexOf(array, value, fromIndex) {
+ var length = array == null ? 0 : array.length;
+ if (!length) {
+ return -1;
+ }
+ var index = fromIndex == null ? 0 : toInteger(fromIndex);
+ if (index < 0) {
+ index = nativeMax(length + index, 0);
+ }
+ return baseIndexOf(array, value, index);
+ }
+
+ /**
+ * Gets all but the last element of `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.initial([1, 2, 3]);
+ * // => [1, 2]
+ */
+ function initial(array) {
+ var length = array == null ? 0 : array.length;
+ return length ? baseSlice(array, 0, -1) : [];
+ }
+
+ /**
+ * Creates an array of unique values that are included in all given arrays
+ * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons. The order and references of result values are
+ * determined by the first array.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @returns {Array} Returns the new array of intersecting values.
+ * @example
+ *
+ * _.intersection([2, 1], [2, 3]);
+ * // => [2]
+ */
+ var intersection = baseRest(function(arrays) {
+ var mapped = arrayMap(arrays, castArrayLikeObject);
+ return (mapped.length && mapped[0] === arrays[0])
+ ? baseIntersection(mapped)
+ : [];
+ });
+
+ /**
+ * Gets the last element of `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @returns {*} Returns the last element of `array`.
+ * @example
+ *
+ * _.last([1, 2, 3]);
+ * // => 3
+ */
+ function last(array) {
+ var length = array == null ? 0 : array.length;
+ return length ? array[length - 1] : undefined;
+ }
+
+ /**
+ * Reverses `array` so that the first element becomes the last, the second
+ * element becomes the second to last, and so on.
+ *
+ * **Note:** This method mutates `array` and is based on
+ * [`Array#reverse`](https://mdn.io/Array/reverse).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to modify.
+ * @returns {Array} Returns `array`.
+ * @example
+ *
+ * var array = [1, 2, 3];
+ *
+ * _.reverse(array);
+ * // => [3, 2, 1]
+ *
+ * console.log(array);
+ * // => [3, 2, 1]
+ */
+ function reverse(array) {
+ return array == null ? array : nativeReverse.call(array);
+ }
+
+ /**
+ * Creates a slice of `array` from `start` up to, but not including, `end`.
+ *
+ * **Note:** This method is used instead of
+ * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are
+ * returned.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Array
+ * @param {Array} array The array to slice.
+ * @param {number} [start=0] The start position.
+ * @param {number} [end=array.length] The end position.
+ * @returns {Array} Returns the slice of `array`.
+ */
+ function slice(array, start, end) {
+ var length = array == null ? 0 : array.length;
+ if (!length) {
+ return [];
+ }
+ if (end && typeof end != 'number' && isIterateeCall(array, start, end)) {
+ start = 0;
+ end = length;
+ }
+ else {
+ start = start == null ? 0 : toInteger(start);
+ end = end === undefined ? length : toInteger(end);
+ }
+ return baseSlice(array, start, end);
+ }
+
+ /**
+ * Creates a slice of `array` with `n` elements taken from the beginning.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {number} [n=1] The number of elements to take.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.take([1, 2, 3]);
+ * // => [1]
+ *
+ * _.take([1, 2, 3], 2);
+ * // => [1, 2]
+ *
+ * _.take([1, 2, 3], 5);
+ * // => [1, 2, 3]
+ *
+ * _.take([1, 2, 3], 0);
+ * // => []
+ */
+ function take(array, n, guard) {
+ if (!(array && array.length)) {
+ return [];
+ }
+ n = (guard || n === undefined) ? 1 : toInteger(n);
+ return baseSlice(array, 0, n < 0 ? 0 : n);
+ }
+
+ /**
+ * Creates a slice of `array` with `n` elements taken from the end.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {number} [n=1] The number of elements to take.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.takeRight([1, 2, 3]);
+ * // => [3]
+ *
+ * _.takeRight([1, 2, 3], 2);
+ * // => [2, 3]
+ *
+ * _.takeRight([1, 2, 3], 5);
+ * // => [1, 2, 3]
+ *
+ * _.takeRight([1, 2, 3], 0);
+ * // => []
+ */
+ function takeRight(array, n, guard) {
+ var length = array == null ? 0 : array.length;
+ if (!length) {
+ return [];
+ }
+ n = (guard || n === undefined) ? 1 : toInteger(n);
+ n = length - n;
+ return baseSlice(array, n < 0 ? 0 : n, length);
+ }
+
+ /**
+ * Creates an array of unique values, in order, from all given arrays using
+ * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @returns {Array} Returns the new array of combined values.
+ * @example
+ *
+ * _.union([2], [1, 2]);
+ * // => [2, 1]
+ */
+ var union = baseRest(function(arrays) {
+ return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true));
+ });
+
+ /**
+ * Creates a duplicate-free version of an array, using
+ * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons, in which only the first occurrence of each element
+ * is kept. The order of result values is determined by the order they occur
+ * in the array.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @returns {Array} Returns the new duplicate free array.
+ * @example
+ *
+ * _.uniq([2, 1, 2]);
+ * // => [2, 1]
+ */
+ function uniq(array) {
+ return (array && array.length) ? baseUniq(array) : [];
+ }
+
+ /**
+ * This method is like `_.uniq` except that it accepts `iteratee` which is
+ * invoked for each element in `array` to generate the criterion by which
+ * uniqueness is computed. The order of result values is determined by the
+ * order they occur in the array. The iteratee is invoked with one argument:
+ * (value).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {Array} Returns the new duplicate free array.
+ * @example
+ *
+ * _.uniqBy([2.1, 1.2, 2.3], Math.floor);
+ * // => [2.1, 1.2]
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
+ * // => [{ 'x': 1 }, { 'x': 2 }]
+ */
+ function uniqBy(array, iteratee) {
+ return (array && array.length) ? baseUniq(array, baseIteratee(iteratee, 2)) : [];
+ }
+
+ /**
+ * This method is like `_.zip` except that it accepts an array of grouped
+ * elements and creates an array regrouping the elements to their pre-zip
+ * configuration.
+ *
+ * @static
+ * @memberOf _
+ * @since 1.2.0
+ * @category Array
+ * @param {Array} array The array of grouped elements to process.
+ * @returns {Array} Returns the new array of regrouped elements.
+ * @example
+ *
+ * var zipped = _.zip(['a', 'b'], [1, 2], [true, false]);
+ * // => [['a', 1, true], ['b', 2, false]]
+ *
+ * _.unzip(zipped);
+ * // => [['a', 'b'], [1, 2], [true, false]]
+ */
+ function unzip(array) {
+ if (!(array && array.length)) {
+ return [];
+ }
+ var length = 0;
+ array = arrayFilter(array, function(group) {
+ if (isArrayLikeObject(group)) {
+ length = nativeMax(group.length, length);
+ return true;
+ }
+ });
+ return baseTimes(length, function(index) {
+ return arrayMap(array, baseProperty(index));
+ });
+ }
+
+ /**
+ * Creates an array excluding all given values using
+ * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons.
+ *
+ * **Note:** Unlike `_.pull`, this method returns a new array.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {...*} [values] The values to exclude.
+ * @returns {Array} Returns the new array of filtered values.
+ * @see _.difference, _.xor
+ * @example
+ *
+ * _.without([2, 1, 2, 3], 1, 2);
+ * // => [3]
+ */
+ var without = baseRest(function(array, values) {
+ return isArrayLikeObject(array)
+ ? baseDifference(array, values)
+ : [];
+ });
+
+ /**
+ * Creates an array of grouped elements, the first of which contains the
+ * first elements of the given arrays, the second of which contains the
+ * second elements of the given arrays, and so on.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {...Array} [arrays] The arrays to process.
+ * @returns {Array} Returns the new array of grouped elements.
+ * @example
+ *
+ * _.zip(['a', 'b'], [1, 2], [true, false]);
+ * // => [['a', 1, true], ['b', 2, false]]
+ */
+ var zip = baseRest(unzip);
+
+ /**
+ * This method is like `_.fromPairs` except that it accepts two arrays,
+ * one of property identifiers and one of corresponding values.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.4.0
+ * @category Array
+ * @param {Array} [props=[]] The property identifiers.
+ * @param {Array} [values=[]] The property values.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * _.zipObject(['a', 'b'], [1, 2]);
+ * // => { 'a': 1, 'b': 2 }
+ */
+ function zipObject(props, values) {
+ return baseZipObject(props || [], values || [], assignValue);
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates a `lodash` wrapper instance that wraps `value` with explicit method
+ * chain sequences enabled. The result of such sequences must be unwrapped
+ * with `_#value`.
+ *
+ * @static
+ * @memberOf _
+ * @since 1.3.0
+ * @category Seq
+ * @param {*} value The value to wrap.
+ * @returns {Object} Returns the new `lodash` wrapper instance.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36 },
+ * { 'user': 'fred', 'age': 40 },
+ * { 'user': 'pebbles', 'age': 1 }
+ * ];
+ *
+ * var youngest = _
+ * .chain(users)
+ * .sortBy('age')
+ * .map(function(o) {
+ * return o.user + ' is ' + o.age;
+ * })
+ * .head()
+ * .value();
+ * // => 'pebbles is 1'
+ */
+ function chain(value) {
+ var result = lodash(value);
+ result.__chain__ = true;
+ return result;
+ }
+
+ /**
+ * This method invokes `interceptor` and returns `value`. The interceptor
+ * is invoked with one argument; (value). The purpose of this method is to
+ * "tap into" a method chain sequence in order to modify intermediate results.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Seq
+ * @param {*} value The value to provide to `interceptor`.
+ * @param {Function} interceptor The function to invoke.
+ * @returns {*} Returns `value`.
+ * @example
+ *
+ * _([1, 2, 3])
+ * .tap(function(array) {
+ * // Mutate input array.
+ * array.pop();
+ * })
+ * .reverse()
+ * .value();
+ * // => [2, 1]
+ */
+ function tap(value, interceptor) {
+ interceptor(value);
+ return value;
+ }
+
+ /**
+ * This method is like `_.tap` except that it returns the result of `interceptor`.
+ * The purpose of this method is to "pass thru" values replacing intermediate
+ * results in a method chain sequence.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Seq
+ * @param {*} value The value to provide to `interceptor`.
+ * @param {Function} interceptor The function to invoke.
+ * @returns {*} Returns the result of `interceptor`.
+ * @example
+ *
+ * _(' abc ')
+ * .chain()
+ * .trim()
+ * .thru(function(value) {
+ * return [value];
+ * })
+ * .value();
+ * // => ['abc']
+ */
+ function thru(value, interceptor) {
+ return interceptor(value);
+ }
+
+ /**
+ * This method is the wrapper version of `_.at`.
+ *
+ * @name at
+ * @memberOf _
+ * @since 1.0.0
+ * @category Seq
+ * @param {...(string|string[])} [paths] The property paths to pick.
+ * @returns {Object} Returns the new `lodash` wrapper instance.
+ * @example
+ *
+ * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
+ *
+ * _(object).at(['a[0].b.c', 'a[1]']).value();
+ * // => [3, 4]
+ */
+ var wrapperAt = flatRest(function(paths) {
+ var length = paths.length,
+ start = length ? paths[0] : 0,
+ value = this.__wrapped__,
+ interceptor = function(object) { return baseAt(object, paths); };
+
+ if (length > 1 || this.__actions__.length ||
+ !(value instanceof LazyWrapper) || !isIndex(start)) {
+ return this.thru(interceptor);
+ }
+ value = value.slice(start, +start + (length ? 1 : 0));
+ value.__actions__.push({
+ 'func': thru,
+ 'args': [interceptor],
+ 'thisArg': undefined
+ });
+ return new LodashWrapper(value, this.__chain__).thru(function(array) {
+ if (length && !array.length) {
+ array.push(undefined);
+ }
+ return array;
+ });
+ });
+
+ /**
+ * Creates a `lodash` wrapper instance with explicit method chain sequences enabled.
+ *
+ * @name chain
+ * @memberOf _
+ * @since 0.1.0
+ * @category Seq
+ * @returns {Object} Returns the new `lodash` wrapper instance.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36 },
+ * { 'user': 'fred', 'age': 40 }
+ * ];
+ *
+ * // A sequence without explicit chaining.
+ * _(users).head();
+ * // => { 'user': 'barney', 'age': 36 }
+ *
+ * // A sequence with explicit chaining.
+ * _(users)
+ * .chain()
+ * .head()
+ * .pick('user')
+ * .value();
+ * // => { 'user': 'barney' }
+ */
+ function wrapperChain() {
+ return chain(this);
+ }
+
+ /**
+ * Executes the chain sequence and returns the wrapped result.
+ *
+ * @name commit
+ * @memberOf _
+ * @since 3.2.0
+ * @category Seq
+ * @returns {Object} Returns the new `lodash` wrapper instance.
+ * @example
+ *
+ * var array = [1, 2];
+ * var wrapped = _(array).push(3);
+ *
+ * console.log(array);
+ * // => [1, 2]
+ *
+ * wrapped = wrapped.commit();
+ * console.log(array);
+ * // => [1, 2, 3]
+ *
+ * wrapped.last();
+ * // => 3
+ *
+ * console.log(array);
+ * // => [1, 2, 3]
+ */
+ function wrapperCommit() {
+ return new LodashWrapper(this.value(), this.__chain__);
+ }
+
+ /**
+ * Gets the next value on a wrapped object following the
+ * [iterator protocol](https://mdn.io/iteration_protocols#iterator).
+ *
+ * @name next
+ * @memberOf _
+ * @since 4.0.0
+ * @category Seq
+ * @returns {Object} Returns the next iterator value.
+ * @example
+ *
+ * var wrapped = _([1, 2]);
+ *
+ * wrapped.next();
+ * // => { 'done': false, 'value': 1 }
+ *
+ * wrapped.next();
+ * // => { 'done': false, 'value': 2 }
+ *
+ * wrapped.next();
+ * // => { 'done': true, 'value': undefined }
+ */
+ function wrapperNext() {
+ if (this.__values__ === undefined) {
+ this.__values__ = toArray(this.value());
+ }
+ var done = this.__index__ >= this.__values__.length,
+ value = done ? undefined : this.__values__[this.__index__++];
+
+ return { 'done': done, 'value': value };
+ }
+
+ /**
+ * Enables the wrapper to be iterable.
+ *
+ * @name Symbol.iterator
+ * @memberOf _
+ * @since 4.0.0
+ * @category Seq
+ * @returns {Object} Returns the wrapper object.
+ * @example
+ *
+ * var wrapped = _([1, 2]);
+ *
+ * wrapped[Symbol.iterator]() === wrapped;
+ * // => true
+ *
+ * Array.from(wrapped);
+ * // => [1, 2]
+ */
+ function wrapperToIterator() {
+ return this;
+ }
+
+ /**
+ * Creates a clone of the chain sequence planting `value` as the wrapped value.
+ *
+ * @name plant
+ * @memberOf _
+ * @since 3.2.0
+ * @category Seq
+ * @param {*} value The value to plant.
+ * @returns {Object} Returns the new `lodash` wrapper instance.
+ * @example
+ *
+ * function square(n) {
+ * return n * n;
+ * }
+ *
+ * var wrapped = _([1, 2]).map(square);
+ * var other = wrapped.plant([3, 4]);
+ *
+ * other.value();
+ * // => [9, 16]
+ *
+ * wrapped.value();
+ * // => [1, 4]
+ */
+ function wrapperPlant(value) {
+ var result,
+ parent = this;
+
+ while (parent instanceof baseLodash) {
+ var clone = wrapperClone(parent);
+ clone.__index__ = 0;
+ clone.__values__ = undefined;
+ if (result) {
+ previous.__wrapped__ = clone;
+ } else {
+ result = clone;
+ }
+ var previous = clone;
+ parent = parent.__wrapped__;
+ }
+ previous.__wrapped__ = value;
+ return result;
+ }
+
+ /**
+ * This method is the wrapper version of `_.reverse`.
+ *
+ * **Note:** This method mutates the wrapped array.
+ *
+ * @name reverse
+ * @memberOf _
+ * @since 0.1.0
+ * @category Seq
+ * @returns {Object} Returns the new `lodash` wrapper instance.
+ * @example
+ *
+ * var array = [1, 2, 3];
+ *
+ * _(array).reverse().value()
+ * // => [3, 2, 1]
+ *
+ * console.log(array);
+ * // => [3, 2, 1]
+ */
+ function wrapperReverse() {
+ var value = this.__wrapped__;
+ if (value instanceof LazyWrapper) {
+ var wrapped = value;
+ if (this.__actions__.length) {
+ wrapped = new LazyWrapper(this);
+ }
+ wrapped = wrapped.reverse();
+ wrapped.__actions__.push({
+ 'func': thru,
+ 'args': [reverse],
+ 'thisArg': undefined
+ });
+ return new LodashWrapper(wrapped, this.__chain__);
+ }
+ return this.thru(reverse);
+ }
+
+ /**
+ * Executes the chain sequence to resolve the unwrapped value.
+ *
+ * @name value
+ * @memberOf _
+ * @since 0.1.0
+ * @alias toJSON, valueOf
+ * @category Seq
+ * @returns {*} Returns the resolved unwrapped value.
+ * @example
+ *
+ * _([1, 2, 3]).value();
+ * // => [1, 2, 3]
+ */
+ function wrapperValue() {
+ return baseWrapperValue(this.__wrapped__, this.__actions__);
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of `collection` thru `iteratee`. The corresponding value of
+ * each key is the number of times the key was returned by `iteratee`. The
+ * iteratee is invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @since 0.5.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * _.countBy([6.1, 4.2, 6.3], Math.floor);
+ * // => { '4': 1, '6': 2 }
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.countBy(['one', 'two', 'three'], 'length');
+ * // => { '3': 2, '5': 1 }
+ */
+ var countBy = createAggregator(function(result, value, key) {
+ if (hasOwnProperty.call(result, key)) {
+ ++result[key];
+ } else {
+ baseAssignValue(result, key, 1);
+ }
+ });
+
+ /**
+ * Checks if `predicate` returns truthy for **all** elements of `collection`.
+ * Iteration is stopped once `predicate` returns falsey. The predicate is
+ * invoked with three arguments: (value, index|key, collection).
+ *
+ * **Note:** This method returns `true` for
+ * [empty collections](https://en.wikipedia.org/wiki/Empty_set) because
+ * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of
+ * elements of empty collections.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {boolean} Returns `true` if all elements pass the predicate check,
+ * else `false`.
+ * @example
+ *
+ * _.every([true, 1, null, 'yes'], Boolean);
+ * // => false
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': false },
+ * { 'user': 'fred', 'age': 40, 'active': false }
+ * ];
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.every(users, { 'user': 'barney', 'active': false });
+ * // => false
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.every(users, ['active', false]);
+ * // => true
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.every(users, 'active');
+ * // => false
+ */
+ function every(collection, predicate, guard) {
+ var func = isArray(collection) ? arrayEvery : baseEvery;
+ if (guard && isIterateeCall(collection, predicate, guard)) {
+ predicate = undefined;
+ }
+ return func(collection, baseIteratee(predicate, 3));
+ }
+
+ /**
+ * Iterates over elements of `collection`, returning an array of all elements
+ * `predicate` returns truthy for. The predicate is invoked with three
+ * arguments: (value, index|key, collection).
+ *
+ * **Note:** Unlike `_.remove`, this method returns a new array.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the new filtered array.
+ * @see _.reject
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': true },
+ * { 'user': 'fred', 'age': 40, 'active': false }
+ * ];
+ *
+ * _.filter(users, function(o) { return !o.active; });
+ * // => objects for ['fred']
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.filter(users, { 'age': 36, 'active': true });
+ * // => objects for ['barney']
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.filter(users, ['active', false]);
+ * // => objects for ['fred']
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.filter(users, 'active');
+ * // => objects for ['barney']
+ */
+ function filter(collection, predicate) {
+ var func = isArray(collection) ? arrayFilter : baseFilter;
+ return func(collection, baseIteratee(predicate, 3));
+ }
+
+ /**
+ * Iterates over elements of `collection`, returning the first element
+ * `predicate` returns truthy for. The predicate is invoked with three
+ * arguments: (value, index|key, collection).
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to inspect.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {*} Returns the matched element, else `undefined`.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': true },
+ * { 'user': 'fred', 'age': 40, 'active': false },
+ * { 'user': 'pebbles', 'age': 1, 'active': true }
+ * ];
+ *
+ * _.find(users, function(o) { return o.age < 40; });
+ * // => object for 'barney'
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.find(users, { 'age': 1, 'active': true });
+ * // => object for 'pebbles'
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.find(users, ['active', false]);
+ * // => object for 'fred'
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.find(users, 'active');
+ * // => object for 'barney'
+ */
+ var find = createFind(findIndex);
+
+ /**
+ * Iterates over elements of `collection` and invokes `iteratee` for each element.
+ * The iteratee is invoked with three arguments: (value, index|key, collection).
+ * Iteratee functions may exit iteration early by explicitly returning `false`.
+ *
+ * **Note:** As with other "Collections" methods, objects with a "length"
+ * property are iterated like arrays. To avoid this behavior use `_.forIn`
+ * or `_.forOwn` for object iteration.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @alias each
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @returns {Array|Object} Returns `collection`.
+ * @see _.forEachRight
+ * @example
+ *
+ * _.forEach([1, 2], function(value) {
+ * console.log(value);
+ * });
+ * // => Logs `1` then `2`.
+ *
+ * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) {
+ * console.log(key);
+ * });
+ * // => Logs 'a' then 'b' (iteration order is not guaranteed).
+ */
+ function forEach(collection, iteratee) {
+ var func = isArray(collection) ? arrayEach : baseEach;
+ return func(collection, baseIteratee(iteratee, 3));
+ }
+
+ /**
+ * Creates an object composed of keys generated from the results of running
+ * each element of `collection` thru `iteratee`. The order of grouped values
+ * is determined by the order they occur in `collection`. The corresponding
+ * value of each key is an array of elements responsible for generating the
+ * key. The iteratee is invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
+ * @returns {Object} Returns the composed aggregate object.
+ * @example
+ *
+ * _.groupBy([6.1, 4.2, 6.3], Math.floor);
+ * // => { '4': [4.2], '6': [6.1, 6.3] }
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.groupBy(['one', 'two', 'three'], 'length');
+ * // => { '3': ['one', 'two'], '5': ['three'] }
+ */
+ var groupBy = createAggregator(function(result, value, key) {
+ if (hasOwnProperty.call(result, key)) {
+ result[key].push(value);
+ } else {
+ baseAssignValue(result, key, [value]);
+ }
+ });
+
+ /**
+ * Creates an array of values by running each element in `collection` thru
+ * `iteratee`. The iteratee is invoked with three arguments:
+ * (value, index|key, collection).
+ *
+ * Many lodash methods are guarded to work as iteratees for methods like
+ * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
+ *
+ * The guarded methods are:
+ * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`,
+ * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`,
+ * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`,
+ * `template`, `trim`, `trimEnd`, `trimStart`, and `words`
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the new mapped array.
+ * @example
+ *
+ * function square(n) {
+ * return n * n;
+ * }
+ *
+ * _.map([4, 8], square);
+ * // => [16, 64]
+ *
+ * _.map({ 'a': 4, 'b': 8 }, square);
+ * // => [16, 64] (iteration order is not guaranteed)
+ *
+ * var users = [
+ * { 'user': 'barney' },
+ * { 'user': 'fred' }
+ * ];
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.map(users, 'user');
+ * // => ['barney', 'fred']
+ */
+ function map(collection, iteratee) {
+ var func = isArray(collection) ? arrayMap : baseMap;
+ return func(collection, baseIteratee(iteratee, 3));
+ }
+
+ /**
+ * Reduces `collection` to a value which is the accumulated result of running
+ * each element in `collection` thru `iteratee`, where each successive
+ * invocation is supplied the return value of the previous. If `accumulator`
+ * is not given, the first element of `collection` is used as the initial
+ * value. The iteratee is invoked with four arguments:
+ * (accumulator, value, index|key, collection).
+ *
+ * Many lodash methods are guarded to work as iteratees for methods like
+ * `_.reduce`, `_.reduceRight`, and `_.transform`.
+ *
+ * The guarded methods are:
+ * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`,
+ * and `sortBy`
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @param {*} [accumulator] The initial value.
+ * @returns {*} Returns the accumulated value.
+ * @see _.reduceRight
+ * @example
+ *
+ * _.reduce([1, 2], function(sum, n) {
+ * return sum + n;
+ * }, 0);
+ * // => 3
+ *
+ * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {
+ * (result[value] || (result[value] = [])).push(key);
+ * return result;
+ * }, {});
+ * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed)
+ */
+ function reduce(collection, iteratee, accumulator) {
+ var func = isArray(collection) ? arrayReduce : baseReduce,
+ initAccum = arguments.length < 3;
+
+ return func(collection, baseIteratee(iteratee, 4), accumulator, initAccum, baseEach);
+ }
+
+ /**
+ * The opposite of `_.filter`; this method returns the elements of `collection`
+ * that `predicate` does **not** return truthy for.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the new filtered array.
+ * @see _.filter
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': false },
+ * { 'user': 'fred', 'age': 40, 'active': true }
+ * ];
+ *
+ * _.reject(users, function(o) { return !o.active; });
+ * // => objects for ['fred']
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.reject(users, { 'age': 40, 'active': true });
+ * // => objects for ['barney']
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.reject(users, ['active', false]);
+ * // => objects for ['fred']
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.reject(users, 'active');
+ * // => objects for ['barney']
+ */
+ function reject(collection, predicate) {
+ var func = isArray(collection) ? arrayFilter : baseFilter;
+ return func(collection, negate(baseIteratee(predicate, 3)));
+ }
+
+ /**
+ * Gets the size of `collection` by returning its length for array-like
+ * values or the number of own enumerable string keyed properties for objects.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to inspect.
+ * @returns {number} Returns the collection size.
+ * @example
+ *
+ * _.size([1, 2, 3]);
+ * // => 3
+ *
+ * _.size({ 'a': 1, 'b': 2 });
+ * // => 2
+ *
+ * _.size('pebbles');
+ * // => 7
+ */
+ function size(collection) {
+ if (collection == null) {
+ return 0;
+ }
+ if (isArrayLike(collection)) {
+ return isString(collection) ? stringSize(collection) : collection.length;
+ }
+ var tag = getTag(collection);
+ if (tag == mapTag || tag == setTag) {
+ return collection.size;
+ }
+ return baseKeys(collection).length;
+ }
+
+ /**
+ * Checks if `predicate` returns truthy for **any** element of `collection`.
+ * Iteration is stopped once `predicate` returns truthy. The predicate is
+ * invoked with three arguments: (value, index|key, collection).
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {boolean} Returns `true` if any element passes the predicate check,
+ * else `false`.
+ * @example
+ *
+ * _.some([null, 0, 'yes', false], Boolean);
+ * // => true
+ *
+ * var users = [
+ * { 'user': 'barney', 'active': true },
+ * { 'user': 'fred', 'active': false }
+ * ];
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.some(users, { 'user': 'barney', 'active': false });
+ * // => false
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.some(users, ['active', false]);
+ * // => true
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.some(users, 'active');
+ * // => true
+ */
+ function some(collection, predicate, guard) {
+ var func = isArray(collection) ? arraySome : baseSome;
+ if (guard && isIterateeCall(collection, predicate, guard)) {
+ predicate = undefined;
+ }
+ return func(collection, baseIteratee(predicate, 3));
+ }
+
+ /**
+ * Creates an array of elements, sorted in ascending order by the results of
+ * running each element in a collection thru each iteratee. This method
+ * performs a stable sort, that is, it preserves the original sort order of
+ * equal elements. The iteratees are invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {...(Function|Function[])} [iteratees=[_.identity]]
+ * The iteratees to sort by.
+ * @returns {Array} Returns the new sorted array.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'fred', 'age': 48 },
+ * { 'user': 'barney', 'age': 36 },
+ * { 'user': 'fred', 'age': 40 },
+ * { 'user': 'barney', 'age': 34 }
+ * ];
+ *
+ * _.sortBy(users, [function(o) { return o.user; }]);
+ * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
+ *
+ * _.sortBy(users, ['user', 'age']);
+ * // => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]]
+ */
+ var sortBy = baseRest(function(collection, iteratees) {
+ if (collection == null) {
+ return [];
+ }
+ var length = iteratees.length;
+ if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) {
+ iteratees = [];
+ } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) {
+ iteratees = [iteratees[0]];
+ }
+ return baseOrderBy(collection, baseFlatten(iteratees, 1), []);
+ });
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Gets the timestamp of the number of milliseconds that have elapsed since
+ * the Unix epoch (1 January 1970 00:00:00 UTC).
+ *
+ * @static
+ * @memberOf _
+ * @since 2.4.0
+ * @category Date
+ * @returns {number} Returns the timestamp.
+ * @example
+ *
+ * _.defer(function(stamp) {
+ * console.log(_.now() - stamp);
+ * }, _.now());
+ * // => Logs the number of milliseconds it took for the deferred invocation.
+ */
+ var now = function() {
+ return root.Date.now();
+ };
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * The opposite of `_.before`; this method creates a function that invokes
+ * `func` once it's called `n` or more times.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {number} n The number of calls before `func` is invoked.
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * var saves = ['profile', 'settings'];
+ *
+ * var done = _.after(saves.length, function() {
+ * console.log('done saving!');
+ * });
+ *
+ * _.forEach(saves, function(type) {
+ * asyncSave({ 'type': type, 'complete': done });
+ * });
+ * // => Logs 'done saving!' after the two async saves have completed.
+ */
+ function after(n, func) {
+ if (typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ n = toInteger(n);
+ return function() {
+ if (--n < 1) {
+ return func.apply(this, arguments);
+ }
+ };
+ }
+
+ /**
+ * Creates a function that invokes `func`, with the `this` binding and arguments
+ * of the created function, while it's called less than `n` times. Subsequent
+ * calls to the created function return the result of the last `func` invocation.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Function
+ * @param {number} n The number of calls at which `func` is no longer invoked.
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * jQuery(element).on('click', _.before(5, addContactToList));
+ * // => Allows adding up to 4 contacts to the list.
+ */
+ function before(n, func) {
+ var result;
+ if (typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ n = toInteger(n);
+ return function() {
+ if (--n > 0) {
+ result = func.apply(this, arguments);
+ }
+ if (n <= 1) {
+ func = undefined;
+ }
+ return result;
+ };
+ }
+
+ /**
+ * Creates a function that invokes `func` with the `this` binding of `thisArg`
+ * and `partials` prepended to the arguments it receives.
+ *
+ * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
+ * may be used as a placeholder for partially applied arguments.
+ *
+ * **Note:** Unlike native `Function#bind`, this method doesn't set the "length"
+ * property of bound functions.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to bind.
+ * @param {*} thisArg The `this` binding of `func`.
+ * @param {...*} [partials] The arguments to be partially applied.
+ * @returns {Function} Returns the new bound function.
+ * @example
+ *
+ * function greet(greeting, punctuation) {
+ * return greeting + ' ' + this.user + punctuation;
+ * }
+ *
+ * var object = { 'user': 'fred' };
+ *
+ * var bound = _.bind(greet, object, 'hi');
+ * bound('!');
+ * // => 'hi fred!'
+ *
+ * // Bound with placeholders.
+ * var bound = _.bind(greet, object, _, '!');
+ * bound('hi');
+ * // => 'hi fred!'
+ */
+ var bind = baseRest(function(func, thisArg, partials) {
+ var bitmask = WRAP_BIND_FLAG;
+ if (partials.length) {
+ var holders = replaceHolders(partials, getHolder(bind));
+ bitmask |= WRAP_PARTIAL_FLAG;
+ }
+ return createWrap(func, bitmask, thisArg, partials, holders);
+ });
+
+ /**
+ * Creates a debounced function that delays invoking `func` until after `wait`
+ * milliseconds have elapsed since the last time the debounced function was
+ * invoked. The debounced function comes with a `cancel` method to cancel
+ * delayed `func` invocations and a `flush` method to immediately invoke them.
+ * Provide `options` to indicate whether `func` should be invoked on the
+ * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
+ * with the last arguments provided to the debounced function. Subsequent
+ * calls to the debounced function return the result of the last `func`
+ * invocation.
+ *
+ * **Note:** If `leading` and `trailing` options are `true`, `func` is
+ * invoked on the trailing edge of the timeout only if the debounced function
+ * is invoked more than once during the `wait` timeout.
+ *
+ * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
+ * until to the next tick, similar to `setTimeout` with a timeout of `0`.
+ *
+ * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
+ * for details over the differences between `_.debounce` and `_.throttle`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to debounce.
+ * @param {number} [wait=0] The number of milliseconds to delay.
+ * @param {Object} [options={}] The options object.
+ * @param {boolean} [options.leading=false]
+ * Specify invoking on the leading edge of the timeout.
+ * @param {number} [options.maxWait]
+ * The maximum time `func` is allowed to be delayed before it's invoked.
+ * @param {boolean} [options.trailing=true]
+ * Specify invoking on the trailing edge of the timeout.
+ * @returns {Function} Returns the new debounced function.
+ * @example
+ *
+ * // Avoid costly calculations while the window size is in flux.
+ * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
+ *
+ * // Invoke `sendMail` when clicked, debouncing subsequent calls.
+ * jQuery(element).on('click', _.debounce(sendMail, 300, {
+ * 'leading': true,
+ * 'trailing': false
+ * }));
+ *
+ * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
+ * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
+ * var source = new EventSource('/stream');
+ * jQuery(source).on('message', debounced);
+ *
+ * // Cancel the trailing debounced invocation.
+ * jQuery(window).on('popstate', debounced.cancel);
+ */
+ function debounce(func, wait, options) {
+ var lastArgs,
+ lastThis,
+ maxWait,
+ result,
+ timerId,
+ lastCallTime,
+ lastInvokeTime = 0,
+ leading = false,
+ maxing = false,
+ trailing = true;
+
+ if (typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ wait = toNumber(wait) || 0;
+ if (isObject(options)) {
+ leading = !!options.leading;
+ maxing = 'maxWait' in options;
+ maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
+ trailing = 'trailing' in options ? !!options.trailing : trailing;
+ }
+
+ function invokeFunc(time) {
+ var args = lastArgs,
+ thisArg = lastThis;
+
+ lastArgs = lastThis = undefined;
+ lastInvokeTime = time;
+ result = func.apply(thisArg, args);
+ return result;
+ }
+
+ function leadingEdge(time) {
+ // Reset any `maxWait` timer.
+ lastInvokeTime = time;
+ // Start the timer for the trailing edge.
+ timerId = setTimeout(timerExpired, wait);
+ // Invoke the leading edge.
+ return leading ? invokeFunc(time) : result;
+ }
+
+ function remainingWait(time) {
+ var timeSinceLastCall = time - lastCallTime,
+ timeSinceLastInvoke = time - lastInvokeTime,
+ timeWaiting = wait - timeSinceLastCall;
+
+ return maxing
+ ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)
+ : timeWaiting;
+ }
+
+ function shouldInvoke(time) {
+ var timeSinceLastCall = time - lastCallTime,
+ timeSinceLastInvoke = time - lastInvokeTime;
+
+ // Either this is the first call, activity has stopped and we're at the
+ // trailing edge, the system time has gone backwards and we're treating
+ // it as the trailing edge, or we've hit the `maxWait` limit.
+ return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
+ (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
+ }
+
+ function timerExpired() {
+ var time = now();
+ if (shouldInvoke(time)) {
+ return trailingEdge(time);
+ }
+ // Restart the timer.
+ timerId = setTimeout(timerExpired, remainingWait(time));
+ }
+
+ function trailingEdge(time) {
+ timerId = undefined;
+
+ // Only invoke if we have `lastArgs` which means `func` has been
+ // debounced at least once.
+ if (trailing && lastArgs) {
+ return invokeFunc(time);
+ }
+ lastArgs = lastThis = undefined;
+ return result;
+ }
+
+ function cancel() {
+ if (timerId !== undefined) {
+ clearTimeout(timerId);
+ }
+ lastInvokeTime = 0;
+ lastArgs = lastCallTime = lastThis = timerId = undefined;
+ }
+
+ function flush() {
+ return timerId === undefined ? result : trailingEdge(now());
+ }
+
+ function debounced() {
+ var time = now(),
+ isInvoking = shouldInvoke(time);
+
+ lastArgs = arguments;
+ lastThis = this;
+ lastCallTime = time;
+
+ if (isInvoking) {
+ if (timerId === undefined) {
+ return leadingEdge(lastCallTime);
+ }
+ if (maxing) {
+ // Handle invocations in a tight loop.
+ timerId = setTimeout(timerExpired, wait);
+ return invokeFunc(lastCallTime);
+ }
+ }
+ if (timerId === undefined) {
+ timerId = setTimeout(timerExpired, wait);
+ }
+ return result;
+ }
+ debounced.cancel = cancel;
+ debounced.flush = flush;
+ return debounced;
+ }
+
+ /**
+ * Defers invoking the `func` until the current call stack has cleared. Any
+ * additional arguments are provided to `func` when it's invoked.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to defer.
+ * @param {...*} [args] The arguments to invoke `func` with.
+ * @returns {number} Returns the timer id.
+ * @example
+ *
+ * _.defer(function(text) {
+ * console.log(text);
+ * }, 'deferred');
+ * // => Logs 'deferred' after one millisecond.
+ */
+ var defer = baseRest(function(func, args) {
+ return baseDelay(func, 1, args);
+ });
+
+ /**
+ * Invokes `func` after `wait` milliseconds. Any additional arguments are
+ * provided to `func` when it's invoked.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to delay.
+ * @param {number} wait The number of milliseconds to delay invocation.
+ * @param {...*} [args] The arguments to invoke `func` with.
+ * @returns {number} Returns the timer id.
+ * @example
+ *
+ * _.delay(function(text) {
+ * console.log(text);
+ * }, 1000, 'later');
+ * // => Logs 'later' after one second.
+ */
+ var delay = baseRest(function(func, wait, args) {
+ return baseDelay(func, toNumber(wait) || 0, args);
+ });
+
+ /**
+ * Creates a function that memoizes the result of `func`. If `resolver` is
+ * provided, it determines the cache key for storing the result based on the
+ * arguments provided to the memoized function. By default, the first argument
+ * provided to the memoized function is used as the map cache key. The `func`
+ * is invoked with the `this` binding of the memoized function.
+ *
+ * **Note:** The cache is exposed as the `cache` property on the memoized
+ * function. Its creation may be customized by replacing the `_.memoize.Cache`
+ * constructor with one whose instances implement the
+ * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)
+ * method interface of `clear`, `delete`, `get`, `has`, and `set`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to have its output memoized.
+ * @param {Function} [resolver] The function to resolve the cache key.
+ * @returns {Function} Returns the new memoized function.
+ * @example
+ *
+ * var object = { 'a': 1, 'b': 2 };
+ * var other = { 'c': 3, 'd': 4 };
+ *
+ * var values = _.memoize(_.values);
+ * values(object);
+ * // => [1, 2]
+ *
+ * values(other);
+ * // => [3, 4]
+ *
+ * object.a = 2;
+ * values(object);
+ * // => [1, 2]
+ *
+ * // Modify the result cache.
+ * values.cache.set(object, ['a', 'b']);
+ * values(object);
+ * // => ['a', 'b']
+ *
+ * // Replace `_.memoize.Cache`.
+ * _.memoize.Cache = WeakMap;
+ */
+ function memoize(func, resolver) {
+ if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ var memoized = function() {
+ var args = arguments,
+ key = resolver ? resolver.apply(this, args) : args[0],
+ cache = memoized.cache;
+
+ if (cache.has(key)) {
+ return cache.get(key);
+ }
+ var result = func.apply(this, args);
+ memoized.cache = cache.set(key, result) || cache;
+ return result;
+ };
+ memoized.cache = new (memoize.Cache || MapCache);
+ return memoized;
+ }
+
+ // Expose `MapCache`.
+ memoize.Cache = MapCache;
+
+ /**
+ * Creates a function that negates the result of the predicate `func`. The
+ * `func` predicate is invoked with the `this` binding and arguments of the
+ * created function.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Function
+ * @param {Function} predicate The predicate to negate.
+ * @returns {Function} Returns the new negated function.
+ * @example
+ *
+ * function isEven(n) {
+ * return n % 2 == 0;
+ * }
+ *
+ * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven));
+ * // => [1, 3, 5]
+ */
+ function negate(predicate) {
+ if (typeof predicate != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ return function() {
+ var args = arguments;
+ switch (args.length) {
+ case 0: return !predicate.call(this);
+ case 1: return !predicate.call(this, args[0]);
+ case 2: return !predicate.call(this, args[0], args[1]);
+ case 3: return !predicate.call(this, args[0], args[1], args[2]);
+ }
+ return !predicate.apply(this, args);
+ };
+ }
+
+ /**
+ * Creates a function that is restricted to invoking `func` once. Repeat calls
+ * to the function return the value of the first invocation. The `func` is
+ * invoked with the `this` binding and arguments of the created function.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
+ * @example
+ *
+ * var initialize = _.once(createApplication);
+ * initialize();
+ * initialize();
+ * // => `createApplication` is invoked once
+ */
+ function once(func) {
+ return before(2, func);
+ }
+
+ /**
+ * Creates a function that invokes `func` with the `this` binding of the
+ * created function and arguments from `start` and beyond provided as
+ * an array.
+ *
+ * **Note:** This method is based on the
+ * [rest parameter](https://mdn.io/rest_parameters).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Function
+ * @param {Function} func The function to apply a rest parameter to.
+ * @param {number} [start=func.length-1] The start position of the rest parameter.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var say = _.rest(function(what, names) {
+ * return what + ' ' + _.initial(names).join(', ') +
+ * (_.size(names) > 1 ? ', & ' : '') + _.last(names);
+ * });
+ *
+ * say('hello', 'fred', 'barney', 'pebbles');
+ * // => 'hello fred, barney, & pebbles'
+ */
+ function rest(func, start) {
+ if (typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ start = start === undefined ? start : toInteger(start);
+ return baseRest(func, start);
+ }
+
+ /**
+ * Creates a throttled function that only invokes `func` at most once per
+ * every `wait` milliseconds. The throttled function comes with a `cancel`
+ * method to cancel delayed `func` invocations and a `flush` method to
+ * immediately invoke them. Provide `options` to indicate whether `func`
+ * should be invoked on the leading and/or trailing edge of the `wait`
+ * timeout. The `func` is invoked with the last arguments provided to the
+ * throttled function. Subsequent calls to the throttled function return the
+ * result of the last `func` invocation.
+ *
+ * **Note:** If `leading` and `trailing` options are `true`, `func` is
+ * invoked on the trailing edge of the timeout only if the throttled function
+ * is invoked more than once during the `wait` timeout.
+ *
+ * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
+ * until to the next tick, similar to `setTimeout` with a timeout of `0`.
+ *
+ * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
+ * for details over the differences between `_.throttle` and `_.debounce`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to throttle.
+ * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
+ * @param {Object} [options={}] The options object.
+ * @param {boolean} [options.leading=true]
+ * Specify invoking on the leading edge of the timeout.
+ * @param {boolean} [options.trailing=true]
+ * Specify invoking on the trailing edge of the timeout.
+ * @returns {Function} Returns the new throttled function.
+ * @example
+ *
+ * // Avoid excessively updating the position while scrolling.
+ * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
+ *
+ * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
+ * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
+ * jQuery(element).on('click', throttled);
+ *
+ * // Cancel the trailing throttled invocation.
+ * jQuery(window).on('popstate', throttled.cancel);
+ */
+ function throttle(func, wait, options) {
+ var leading = true,
+ trailing = true;
+
+ if (typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ if (isObject(options)) {
+ leading = 'leading' in options ? !!options.leading : leading;
+ trailing = 'trailing' in options ? !!options.trailing : trailing;
+ }
+ return debounce(func, wait, {
+ 'leading': leading,
+ 'maxWait': wait,
+ 'trailing': trailing
+ });
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates a shallow clone of `value`.
+ *
+ * **Note:** This method is loosely based on the
+ * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm)
+ * and supports cloning arrays, array buffers, booleans, date objects, maps,
+ * numbers, `Object` objects, regexes, sets, strings, symbols, and typed
+ * arrays. The own enumerable properties of `arguments` objects are cloned
+ * as plain objects. An empty object is returned for uncloneable values such
+ * as error objects, functions, DOM nodes, and WeakMaps.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to clone.
+ * @returns {*} Returns the cloned value.
+ * @see _.cloneDeep
+ * @example
+ *
+ * var objects = [{ 'a': 1 }, { 'b': 2 }];
+ *
+ * var shallow = _.clone(objects);
+ * console.log(shallow[0] === objects[0]);
+ * // => true
+ */
+ function clone(value) {
+ return baseClone(value, CLONE_SYMBOLS_FLAG);
+ }
+
+ /**
+ * This method is like `_.clone` except that it recursively clones `value`.
+ *
+ * @static
+ * @memberOf _
+ * @since 1.0.0
+ * @category Lang
+ * @param {*} value The value to recursively clone.
+ * @returns {*} Returns the deep cloned value.
+ * @see _.clone
+ * @example
+ *
+ * var objects = [{ 'a': 1 }, { 'b': 2 }];
+ *
+ * var deep = _.cloneDeep(objects);
+ * console.log(deep[0] === objects[0]);
+ * // => false
+ */
+ function cloneDeep(value) {
+ return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG);
+ }
+
+ /**
+ * Performs a
+ * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * comparison between two values to determine if they are equivalent.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ * @example
+ *
+ * var object = { 'a': 1 };
+ * var other = { 'a': 1 };
+ *
+ * _.eq(object, object);
+ * // => true
+ *
+ * _.eq(object, other);
+ * // => false
+ *
+ * _.eq('a', 'a');
+ * // => true
+ *
+ * _.eq('a', Object('a'));
+ * // => false
+ *
+ * _.eq(NaN, NaN);
+ * // => true
+ */
+ function eq(value, other) {
+ return value === other || (value !== value && other !== other);
+ }
+
+ /**
+ * Checks if `value` is likely an `arguments` object.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an `arguments` object,
+ * else `false`.
+ * @example
+ *
+ * _.isArguments(function() { return arguments; }());
+ * // => true
+ *
+ * _.isArguments([1, 2, 3]);
+ * // => false
+ */
+ var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) {
+ return isObjectLike(value) && hasOwnProperty.call(value, 'callee') &&
+ !propertyIsEnumerable.call(value, 'callee');
+ };
+
+ /**
+ * Checks if `value` is classified as an `Array` object.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an array, else `false`.
+ * @example
+ *
+ * _.isArray([1, 2, 3]);
+ * // => true
+ *
+ * _.isArray(document.body.children);
+ * // => false
+ *
+ * _.isArray('abc');
+ * // => false
+ *
+ * _.isArray(_.noop);
+ * // => false
+ */
+ var isArray = Array.isArray;
+
+ /**
+ * Checks if `value` is array-like. A value is considered array-like if it's
+ * not a function and has a `value.length` that's an integer greater than or
+ * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
+ * @example
+ *
+ * _.isArrayLike([1, 2, 3]);
+ * // => true
+ *
+ * _.isArrayLike(document.body.children);
+ * // => true
+ *
+ * _.isArrayLike('abc');
+ * // => true
+ *
+ * _.isArrayLike(_.noop);
+ * // => false
+ */
+ function isArrayLike(value) {
+ return value != null && isLength(value.length) && !isFunction(value);
+ }
+
+ /**
+ * This method is like `_.isArrayLike` except that it also checks if `value`
+ * is an object.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an array-like object,
+ * else `false`.
+ * @example
+ *
+ * _.isArrayLikeObject([1, 2, 3]);
+ * // => true
+ *
+ * _.isArrayLikeObject(document.body.children);
+ * // => true
+ *
+ * _.isArrayLikeObject('abc');
+ * // => false
+ *
+ * _.isArrayLikeObject(_.noop);
+ * // => false
+ */
+ function isArrayLikeObject(value) {
+ return isObjectLike(value) && isArrayLike(value);
+ }
+
+ /**
+ * Checks if `value` is classified as a boolean primitive or object.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a boolean, else `false`.
+ * @example
+ *
+ * _.isBoolean(false);
+ * // => true
+ *
+ * _.isBoolean(null);
+ * // => false
+ */
+ function isBoolean(value) {
+ return value === true || value === false ||
+ (isObjectLike(value) && baseGetTag(value) == boolTag);
+ }
+
+ /**
+ * Checks if `value` is a buffer.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.3.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
+ * @example
+ *
+ * _.isBuffer(new Buffer(2));
+ * // => true
+ *
+ * _.isBuffer(new Uint8Array(2));
+ * // => false
+ */
+ var isBuffer = nativeIsBuffer || stubFalse;
+
+ /**
+ * Checks if `value` is classified as a `Date` object.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a date object, else `false`.
+ * @example
+ *
+ * _.isDate(new Date);
+ * // => true
+ *
+ * _.isDate('Mon April 23 2012');
+ * // => false
+ */
+ var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate;
+
+ /**
+ * Checks if `value` is an empty object, collection, map, or set.
+ *
+ * Objects are considered empty if they have no own enumerable string keyed
+ * properties.
+ *
+ * Array-like values such as `arguments` objects, arrays, buffers, strings, or
+ * jQuery-like collections are considered empty if they have a `length` of `0`.
+ * Similarly, maps and sets are considered empty if they have a `size` of `0`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is empty, else `false`.
+ * @example
+ *
+ * _.isEmpty(null);
+ * // => true
+ *
+ * _.isEmpty(true);
+ * // => true
+ *
+ * _.isEmpty(1);
+ * // => true
+ *
+ * _.isEmpty([1, 2, 3]);
+ * // => false
+ *
+ * _.isEmpty({ 'a': 1 });
+ * // => false
+ */
+ function isEmpty(value) {
+ if (value == null) {
+ return true;
+ }
+ if (isArrayLike(value) &&
+ (isArray(value) || typeof value == 'string' || typeof value.splice == 'function' ||
+ isBuffer(value) || isTypedArray(value) || isArguments(value))) {
+ return !value.length;
+ }
+ var tag = getTag(value);
+ if (tag == mapTag || tag == setTag) {
+ return !value.size;
+ }
+ if (isPrototype(value)) {
+ return !baseKeys(value).length;
+ }
+ for (var key in value) {
+ if (hasOwnProperty.call(value, key)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Performs a deep comparison between two values to determine if they are
+ * equivalent.
+ *
+ * **Note:** This method supports comparing arrays, array buffers, booleans,
+ * date objects, error objects, maps, numbers, `Object` objects, regexes,
+ * sets, strings, symbols, and typed arrays. `Object` objects are compared
+ * by their own, not inherited, enumerable properties. Functions and DOM
+ * nodes are compared by strict equality, i.e. `===`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ * @example
+ *
+ * var object = { 'a': 1 };
+ * var other = { 'a': 1 };
+ *
+ * _.isEqual(object, other);
+ * // => true
+ *
+ * object === other;
+ * // => false
+ */
+ function isEqual(value, other) {
+ return baseIsEqual(value, other);
+ }
+
+ /**
+ * Checks if `value` is a finite primitive number.
+ *
+ * **Note:** This method is based on
+ * [`Number.isFinite`](https://mdn.io/Number/isFinite).
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a finite number, else `false`.
+ * @example
+ *
+ * _.isFinite(3);
+ * // => true
+ *
+ * _.isFinite(Number.MIN_VALUE);
+ * // => true
+ *
+ * _.isFinite(Infinity);
+ * // => false
+ *
+ * _.isFinite('3');
+ * // => false
+ */
+ function isFinite(value) {
+ return typeof value == 'number' && nativeIsFinite(value);
+ }
+
+ /**
+ * Checks if `value` is classified as a `Function` object.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a function, else `false`.
+ * @example
+ *
+ * _.isFunction(_);
+ * // => true
+ *
+ * _.isFunction(/abc/);
+ * // => false
+ */
+ function isFunction(value) {
+ if (!isObject(value)) {
+ return false;
+ }
+ // The use of `Object#toString` avoids issues with the `typeof` operator
+ // in Safari 9 which returns 'object' for typed arrays and other constructors.
+ var tag = baseGetTag(value);
+ return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
+ }
+
+ /**
+ * Checks if `value` is a valid array-like length.
+ *
+ * **Note:** This method is loosely based on
+ * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
+ * @example
+ *
+ * _.isLength(3);
+ * // => true
+ *
+ * _.isLength(Number.MIN_VALUE);
+ * // => false
+ *
+ * _.isLength(Infinity);
+ * // => false
+ *
+ * _.isLength('3');
+ * // => false
+ */
+ function isLength(value) {
+ return typeof value == 'number' &&
+ value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
+ }
+
+ /**
+ * Checks if `value` is the
+ * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
+ * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an object, else `false`.
+ * @example
+ *
+ * _.isObject({});
+ * // => true
+ *
+ * _.isObject([1, 2, 3]);
+ * // => true
+ *
+ * _.isObject(_.noop);
+ * // => true
+ *
+ * _.isObject(null);
+ * // => false
+ */
+ function isObject(value) {
+ var type = typeof value;
+ return value != null && (type == 'object' || type == 'function');
+ }
+
+ /**
+ * Checks if `value` is object-like. A value is object-like if it's not `null`
+ * and has a `typeof` result of "object".
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
+ * @example
+ *
+ * _.isObjectLike({});
+ * // => true
+ *
+ * _.isObjectLike([1, 2, 3]);
+ * // => true
+ *
+ * _.isObjectLike(_.noop);
+ * // => false
+ *
+ * _.isObjectLike(null);
+ * // => false
+ */
+ function isObjectLike(value) {
+ return value != null && typeof value == 'object';
+ }
+
+ /**
+ * Checks if `value` is classified as a `Map` object.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.3.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a map, else `false`.
+ * @example
+ *
+ * _.isMap(new Map);
+ * // => true
+ *
+ * _.isMap(new WeakMap);
+ * // => false
+ */
+ var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap;
+
+ /**
+ * Checks if `value` is `NaN`.
+ *
+ * **Note:** This method is based on
+ * [`Number.isNaN`](https://mdn.io/Number/isNaN) and is not the same as
+ * global [`isNaN`](https://mdn.io/isNaN) which returns `true` for
+ * `undefined` and other non-number values.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
+ * @example
+ *
+ * _.isNaN(NaN);
+ * // => true
+ *
+ * _.isNaN(new Number(NaN));
+ * // => true
+ *
+ * isNaN(undefined);
+ * // => true
+ *
+ * _.isNaN(undefined);
+ * // => false
+ */
+ function isNaN(value) {
+ // An `NaN` primitive is the only value that is not equal to itself.
+ // Perform the `toStringTag` check first to avoid errors with some
+ // ActiveX objects in IE.
+ return isNumber(value) && value != +value;
+ }
+
+ /**
+ * Checks if `value` is `null`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is `null`, else `false`.
+ * @example
+ *
+ * _.isNull(null);
+ * // => true
+ *
+ * _.isNull(void 0);
+ * // => false
+ */
+ function isNull(value) {
+ return value === null;
+ }
+
+ /**
+ * Checks if `value` is classified as a `Number` primitive or object.
+ *
+ * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are
+ * classified as numbers, use the `_.isFinite` method.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a number, else `false`.
+ * @example
+ *
+ * _.isNumber(3);
+ * // => true
+ *
+ * _.isNumber(Number.MIN_VALUE);
+ * // => true
+ *
+ * _.isNumber(Infinity);
+ * // => true
+ *
+ * _.isNumber('3');
+ * // => false
+ */
+ function isNumber(value) {
+ return typeof value == 'number' ||
+ (isObjectLike(value) && baseGetTag(value) == numberTag);
+ }
+
+ /**
+ * Checks if `value` is a plain object, that is, an object created by the
+ * `Object` constructor or one with a `[[Prototype]]` of `null`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.8.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+ * @example
+ *
+ * function Foo() {
+ * this.a = 1;
+ * }
+ *
+ * _.isPlainObject(new Foo);
+ * // => false
+ *
+ * _.isPlainObject([1, 2, 3]);
+ * // => false
+ *
+ * _.isPlainObject({ 'x': 0, 'y': 0 });
+ * // => true
+ *
+ * _.isPlainObject(Object.create(null));
+ * // => true
+ */
+ function isPlainObject(value) {
+ if (!isObjectLike(value) || baseGetTag(value) != objectTag) {
+ return false;
+ }
+ var proto = getPrototype(value);
+ if (proto === null) {
+ return true;
+ }
+ var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor;
+ return typeof Ctor == 'function' && Ctor instanceof Ctor &&
+ funcToString.call(Ctor) == objectCtorString;
+ }
+
+ /**
+ * Checks if `value` is classified as a `RegExp` object.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.
+ * @example
+ *
+ * _.isRegExp(/abc/);
+ * // => true
+ *
+ * _.isRegExp('/abc/');
+ * // => false
+ */
+ var isRegExp = nodeIsRegExp ? baseUnary(nodeIsRegExp) : baseIsRegExp;
+
+ /**
+ * Checks if `value` is classified as a `Set` object.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.3.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a set, else `false`.
+ * @example
+ *
+ * _.isSet(new Set);
+ * // => true
+ *
+ * _.isSet(new WeakSet);
+ * // => false
+ */
+ var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet;
+
+ /**
+ * Checks if `value` is classified as a `String` primitive or object.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a string, else `false`.
+ * @example
+ *
+ * _.isString('abc');
+ * // => true
+ *
+ * _.isString(1);
+ * // => false
+ */
+ function isString(value) {
+ return typeof value == 'string' ||
+ (!isArray(value) && isObjectLike(value) && baseGetTag(value) == stringTag);
+ }
+
+ /**
+ * Checks if `value` is classified as a `Symbol` primitive or object.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
+ * @example
+ *
+ * _.isSymbol(Symbol.iterator);
+ * // => true
+ *
+ * _.isSymbol('abc');
+ * // => false
+ */
+ function isSymbol(value) {
+ return typeof value == 'symbol' ||
+ (isObjectLike(value) && baseGetTag(value) == symbolTag);
+ }
+
+ /**
+ * Checks if `value` is classified as a typed array.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
+ * @example
+ *
+ * _.isTypedArray(new Uint8Array);
+ * // => true
+ *
+ * _.isTypedArray([]);
+ * // => false
+ */
+ var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;
+
+ /**
+ * Checks if `value` is `undefined`.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
+ * @example
+ *
+ * _.isUndefined(void 0);
+ * // => true
+ *
+ * _.isUndefined(null);
+ * // => false
+ */
+ function isUndefined(value) {
+ return value === undefined;
+ }
+
+ /**
+ * Converts `value` to an array.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to convert.
+ * @returns {Array} Returns the converted array.
+ * @example
+ *
+ * _.toArray({ 'a': 1, 'b': 2 });
+ * // => [1, 2]
+ *
+ * _.toArray('abc');
+ * // => ['a', 'b', 'c']
+ *
+ * _.toArray(1);
+ * // => []
+ *
+ * _.toArray(null);
+ * // => []
+ */
+ function toArray(value) {
+ if (!value) {
+ return [];
+ }
+ if (isArrayLike(value)) {
+ return isString(value) ? stringToArray(value) : copyArray(value);
+ }
+ if (symIterator && value[symIterator]) {
+ return iteratorToArray(value[symIterator]());
+ }
+ var tag = getTag(value),
+ func = tag == mapTag ? mapToArray : (tag == setTag ? setToArray : values);
+
+ return func(value);
+ }
+
+ /**
+ * Converts `value` to a finite number.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.12.0
+ * @category Lang
+ * @param {*} value The value to convert.
+ * @returns {number} Returns the converted number.
+ * @example
+ *
+ * _.toFinite(3.2);
+ * // => 3.2
+ *
+ * _.toFinite(Number.MIN_VALUE);
+ * // => 5e-324
+ *
+ * _.toFinite(Infinity);
+ * // => 1.7976931348623157e+308
+ *
+ * _.toFinite('3.2');
+ * // => 3.2
+ */
+ function toFinite(value) {
+ if (!value) {
+ return value === 0 ? value : 0;
+ }
+ value = toNumber(value);
+ if (value === INFINITY || value === -INFINITY) {
+ var sign = (value < 0 ? -1 : 1);
+ return sign * MAX_INTEGER;
+ }
+ return value === value ? value : 0;
+ }
+
+ /**
+ * Converts `value` to an integer.
+ *
+ * **Note:** This method is loosely based on
+ * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to convert.
+ * @returns {number} Returns the converted integer.
+ * @example
+ *
+ * _.toInteger(3.2);
+ * // => 3
+ *
+ * _.toInteger(Number.MIN_VALUE);
+ * // => 0
+ *
+ * _.toInteger(Infinity);
+ * // => 1.7976931348623157e+308
+ *
+ * _.toInteger('3.2');
+ * // => 3
+ */
+ function toInteger(value) {
+ var result = toFinite(value),
+ remainder = result % 1;
+
+ return result === result ? (remainder ? result - remainder : result) : 0;
+ }
+
+ /**
+ * Converts `value` to a number.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to process.
+ * @returns {number} Returns the number.
+ * @example
+ *
+ * _.toNumber(3.2);
+ * // => 3.2
+ *
+ * _.toNumber(Number.MIN_VALUE);
+ * // => 5e-324
+ *
+ * _.toNumber(Infinity);
+ * // => Infinity
+ *
+ * _.toNumber('3.2');
+ * // => 3.2
+ */
+ function toNumber(value) {
+ if (typeof value == 'number') {
+ return value;
+ }
+ if (isSymbol(value)) {
+ return NAN;
+ }
+ if (isObject(value)) {
+ var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
+ value = isObject(other) ? (other + '') : other;
+ }
+ if (typeof value != 'string') {
+ return value === 0 ? value : +value;
+ }
+ value = value.replace(reTrim, '');
+ var isBinary = reIsBinary.test(value);
+ return (isBinary || reIsOctal.test(value))
+ ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
+ : (reIsBadHex.test(value) ? NAN : +value);
+ }
+
+ /**
+ * Converts `value` to a plain object flattening inherited enumerable string
+ * keyed properties of `value` to own properties of the plain object.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Lang
+ * @param {*} value The value to convert.
+ * @returns {Object} Returns the converted plain object.
+ * @example
+ *
+ * function Foo() {
+ * this.b = 2;
+ * }
+ *
+ * Foo.prototype.c = 3;
+ *
+ * _.assign({ 'a': 1 }, new Foo);
+ * // => { 'a': 1, 'b': 2 }
+ *
+ * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
+ * // => { 'a': 1, 'b': 2, 'c': 3 }
+ */
+ function toPlainObject(value) {
+ return copyObject(value, keysIn(value));
+ }
+
+ /**
+ * Converts `value` to a string. An empty string is returned for `null`
+ * and `undefined` values. The sign of `-0` is preserved.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to convert.
+ * @returns {string} Returns the converted string.
+ * @example
+ *
+ * _.toString(null);
+ * // => ''
+ *
+ * _.toString(-0);
+ * // => '-0'
+ *
+ * _.toString([1, 2, 3]);
+ * // => '1,2,3'
+ */
+ function toString(value) {
+ return value == null ? '' : baseToString(value);
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * This method is like `_.assign` except that it iterates over own and
+ * inherited source properties.
+ *
+ * **Note:** This method mutates `object`.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @alias extend
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} [sources] The source objects.
+ * @returns {Object} Returns `object`.
+ * @see _.assign
+ * @example
+ *
+ * function Foo() {
+ * this.a = 1;
+ * }
+ *
+ * function Bar() {
+ * this.c = 3;
+ * }
+ *
+ * Foo.prototype.b = 2;
+ * Bar.prototype.d = 4;
+ *
+ * _.assignIn({ 'a': 0 }, new Foo, new Bar);
+ * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }
+ */
+ var assignIn = createAssigner(function(object, source) {
+ copyObject(source, keysIn(source), object);
+ });
+
+ /**
+ * Creates an object that inherits from the `prototype` object. If a
+ * `properties` object is given, its own enumerable string keyed properties
+ * are assigned to the created object.
+ *
+ * @static
+ * @memberOf _
+ * @since 2.3.0
+ * @category Object
+ * @param {Object} prototype The object to inherit from.
+ * @param {Object} [properties] The properties to assign to the object.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * function Circle() {
+ * Shape.call(this);
+ * }
+ *
+ * Circle.prototype = _.create(Shape.prototype, {
+ * 'constructor': Circle
+ * });
+ *
+ * var circle = new Circle;
+ * circle instanceof Circle;
+ * // => true
+ *
+ * circle instanceof Shape;
+ * // => true
+ */
+ function create(prototype, properties) {
+ var result = baseCreate(prototype);
+ return properties == null ? result : baseAssign(result, properties);
+ }
+
+ /**
+ * Assigns own and inherited enumerable string keyed properties of source
+ * objects to the destination object for all destination properties that
+ * resolve to `undefined`. Source objects are applied from left to right.
+ * Once a property is set, additional values of the same property are ignored.
+ *
+ * **Note:** This method mutates `object`.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} [sources] The source objects.
+ * @returns {Object} Returns `object`.
+ * @see _.defaultsDeep
+ * @example
+ *
+ * _.defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });
+ * // => { 'a': 1, 'b': 2 }
+ */
+ var defaults = baseRest(function(object, sources) {
+ object = Object(object);
+
+ var index = -1;
+ var length = sources.length;
+ var guard = length > 2 ? sources[2] : undefined;
+
+ if (guard && isIterateeCall(sources[0], sources[1], guard)) {
+ length = 1;
+ }
+
+ while (++index < length) {
+ var source = sources[index];
+ var props = keysIn(source);
+ var propsIndex = -1;
+ var propsLength = props.length;
+
+ while (++propsIndex < propsLength) {
+ var key = props[propsIndex];
+ var value = object[key];
+
+ if (value === undefined ||
+ (eq(value, objectProto[key]) && !hasOwnProperty.call(object, key))) {
+ object[key] = source[key];
+ }
+ }
+ }
+
+ return object;
+ });
+
+ /**
+ * This method is like `_.defaults` except that it recursively assigns
+ * default properties.
+ *
+ * **Note:** This method mutates `object`.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.10.0
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} [sources] The source objects.
+ * @returns {Object} Returns `object`.
+ * @see _.defaults
+ * @example
+ *
+ * _.defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } });
+ * // => { 'a': { 'b': 2, 'c': 3 } }
+ */
+ var defaultsDeep = baseRest(function(args) {
+ args.push(undefined, customDefaultsMerge);
+ return apply(mergeWith, undefined, args);
+ });
+
+ /**
+ * This method is like `_.find` except that it returns the key of the first
+ * element `predicate` returns truthy for instead of the element itself.
+ *
+ * @static
+ * @memberOf _
+ * @since 1.1.0
+ * @category Object
+ * @param {Object} object The object to inspect.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {string|undefined} Returns the key of the matched element,
+ * else `undefined`.
+ * @example
+ *
+ * var users = {
+ * 'barney': { 'age': 36, 'active': true },
+ * 'fred': { 'age': 40, 'active': false },
+ * 'pebbles': { 'age': 1, 'active': true }
+ * };
+ *
+ * _.findKey(users, function(o) { return o.age < 40; });
+ * // => 'barney' (iteration order is not guaranteed)
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.findKey(users, { 'age': 1, 'active': true });
+ * // => 'pebbles'
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.findKey(users, ['active', false]);
+ * // => 'fred'
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.findKey(users, 'active');
+ * // => 'barney'
+ */
+ function findKey(object, predicate) {
+ return baseFindKey(object, baseIteratee(predicate, 3), baseForOwn);
+ }
+
+ /**
+ * This method is like `_.findKey` except that it iterates over elements of
+ * a collection in the opposite order.
+ *
+ * @static
+ * @memberOf _
+ * @since 2.0.0
+ * @category Object
+ * @param {Object} object The object to inspect.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {string|undefined} Returns the key of the matched element,
+ * else `undefined`.
+ * @example
+ *
+ * var users = {
+ * 'barney': { 'age': 36, 'active': true },
+ * 'fred': { 'age': 40, 'active': false },
+ * 'pebbles': { 'age': 1, 'active': true }
+ * };
+ *
+ * _.findLastKey(users, function(o) { return o.age < 40; });
+ * // => returns 'pebbles' assuming `_.findKey` returns 'barney'
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.findLastKey(users, { 'age': 36, 'active': true });
+ * // => 'barney'
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.findLastKey(users, ['active', false]);
+ * // => 'fred'
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.findLastKey(users, 'active');
+ * // => 'pebbles'
+ */
+ function findLastKey(object, predicate) {
+ return baseFindKey(object, baseIteratee(predicate, 3), baseForOwnRight);
+ }
+
+ /**
+ * Gets the value at `path` of `object`. If the resolved value is
+ * `undefined`, the `defaultValue` is returned in its place.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.7.0
+ * @category Object
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path of the property to get.
+ * @param {*} [defaultValue] The value returned for `undefined` resolved values.
+ * @returns {*} Returns the resolved value.
+ * @example
+ *
+ * var object = { 'a': [{ 'b': { 'c': 3 } }] };
+ *
+ * _.get(object, 'a[0].b.c');
+ * // => 3
+ *
+ * _.get(object, ['a', '0', 'b', 'c']);
+ * // => 3
+ *
+ * _.get(object, 'a.b.c', 'default');
+ * // => 'default'
+ */
+ function get(object, path, defaultValue) {
+ var result = object == null ? undefined : baseGet(object, path);
+ return result === undefined ? defaultValue : result;
+ }
+
+ /**
+ * Checks if `path` is a direct property of `object`.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path to check.
+ * @returns {boolean} Returns `true` if `path` exists, else `false`.
+ * @example
+ *
+ * var object = { 'a': { 'b': 2 } };
+ * var other = _.create({ 'a': _.create({ 'b': 2 }) });
+ *
+ * _.has(object, 'a');
+ * // => true
+ *
+ * _.has(object, 'a.b');
+ * // => true
+ *
+ * _.has(object, ['a', 'b']);
+ * // => true
+ *
+ * _.has(other, 'a');
+ * // => false
+ */
+ function has(object, path) {
+ return object != null && hasPath(object, path, baseHas);
+ }
+
+ /**
+ * Checks if `path` is a direct or inherited property of `object`.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Object
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path to check.
+ * @returns {boolean} Returns `true` if `path` exists, else `false`.
+ * @example
+ *
+ * var object = _.create({ 'a': _.create({ 'b': 2 }) });
+ *
+ * _.hasIn(object, 'a');
+ * // => true
+ *
+ * _.hasIn(object, 'a.b');
+ * // => true
+ *
+ * _.hasIn(object, ['a', 'b']);
+ * // => true
+ *
+ * _.hasIn(object, 'b');
+ * // => false
+ */
+ function hasIn(object, path) {
+ return object != null && hasPath(object, path, baseHasIn);
+ }
+
+ /**
+ * Creates an object composed of the inverted keys and values of `object`.
+ * If `object` contains duplicate values, subsequent values overwrite
+ * property assignments of previous values.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.7.0
+ * @category Object
+ * @param {Object} object The object to invert.
+ * @returns {Object} Returns the new inverted object.
+ * @example
+ *
+ * var object = { 'a': 1, 'b': 2, 'c': 1 };
+ *
+ * _.invert(object);
+ * // => { '1': 'c', '2': 'b' }
+ */
+ var invert = createInverter(function(result, value, key) {
+ if (value != null &&
+ typeof value.toString != 'function') {
+ value = nativeObjectToString.call(value);
+ }
+
+ result[value] = key;
+ }, constant(identity));
+
+ /**
+ * This method is like `_.invert` except that the inverted object is generated
+ * from the results of running each element of `object` thru `iteratee`. The
+ * corresponding inverted value of each inverted key is an array of keys
+ * responsible for generating the inverted value. The iteratee is invoked
+ * with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.1.0
+ * @category Object
+ * @param {Object} object The object to invert.
+ * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {Object} Returns the new inverted object.
+ * @example
+ *
+ * var object = { 'a': 1, 'b': 2, 'c': 1 };
+ *
+ * _.invertBy(object);
+ * // => { '1': ['a', 'c'], '2': ['b'] }
+ *
+ * _.invertBy(object, function(value) {
+ * return 'group' + value;
+ * });
+ * // => { 'group1': ['a', 'c'], 'group2': ['b'] }
+ */
+ var invertBy = createInverter(function(result, value, key) {
+ if (value != null &&
+ typeof value.toString != 'function') {
+ value = nativeObjectToString.call(value);
+ }
+
+ if (hasOwnProperty.call(result, value)) {
+ result[value].push(key);
+ } else {
+ result[value] = [key];
+ }
+ }, baseIteratee);
+
+ /**
+ * Creates an array of the own enumerable property names of `object`.
+ *
+ * **Note:** Non-object values are coerced to objects. See the
+ * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
+ * for more details.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of property names.
+ * @example
+ *
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
+ *
+ * Foo.prototype.c = 3;
+ *
+ * _.keys(new Foo);
+ * // => ['a', 'b'] (iteration order is not guaranteed)
+ *
+ * _.keys('hi');
+ * // => ['0', '1']
+ */
+ function keys(object) {
+ return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);
+ }
+
+ /**
+ * Creates an array of the own and inherited enumerable property names of `object`.
+ *
+ * **Note:** Non-object values are coerced to objects.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Object
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of property names.
+ * @example
+ *
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
+ *
+ * Foo.prototype.c = 3;
+ *
+ * _.keysIn(new Foo);
+ * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
+ */
+ function keysIn(object) {
+ return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object);
+ }
+
+ /**
+ * This method is like `_.assign` except that it recursively merges own and
+ * inherited enumerable string keyed properties of source objects into the
+ * destination object. Source properties that resolve to `undefined` are
+ * skipped if a destination value exists. Array and plain object properties
+ * are merged recursively. Other objects and value types are overridden by
+ * assignment. Source objects are applied from left to right. Subsequent
+ * sources overwrite property assignments of previous sources.
+ *
+ * **Note:** This method mutates `object`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.5.0
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} [sources] The source objects.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * var object = {
+ * 'a': [{ 'b': 2 }, { 'd': 4 }]
+ * };
+ *
+ * var other = {
+ * 'a': [{ 'c': 3 }, { 'e': 5 }]
+ * };
+ *
+ * _.merge(object, other);
+ * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
+ */
+ var merge = createAssigner(function(object, source, srcIndex) {
+ baseMerge(object, source, srcIndex);
+ });
+
+ /**
+ * This method is like `_.merge` except that it accepts `customizer` which
+ * is invoked to produce the merged values of the destination and source
+ * properties. If `customizer` returns `undefined`, merging is handled by the
+ * method instead. The `customizer` is invoked with six arguments:
+ * (objValue, srcValue, key, object, source, stack).
+ *
+ * **Note:** This method mutates `object`.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} sources The source objects.
+ * @param {Function} customizer The function to customize assigned values.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * function customizer(objValue, srcValue) {
+ * if (_.isArray(objValue)) {
+ * return objValue.concat(srcValue);
+ * }
+ * }
+ *
+ * var object = { 'a': [1], 'b': [2] };
+ * var other = { 'a': [3], 'b': [4] };
+ *
+ * _.mergeWith(object, other, customizer);
+ * // => { 'a': [1, 3], 'b': [2, 4] }
+ */
+ var mergeWith = createAssigner(function(object, source, srcIndex, customizer) {
+ baseMerge(object, source, srcIndex, customizer);
+ });
+
+ /**
+ * The opposite of `_.pick`; this method creates an object composed of the
+ * own and inherited enumerable property paths of `object` that are not omitted.
+ *
+ * **Note:** This method is considerably slower than `_.pick`.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The source object.
+ * @param {...(string|string[])} [paths] The property paths to omit.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * var object = { 'a': 1, 'b': '2', 'c': 3 };
+ *
+ * _.omit(object, ['a', 'c']);
+ * // => { 'b': '2' }
+ */
+ var omit = flatRest(function(object, paths) {
+ var result = {};
+ if (object == null) {
+ return result;
+ }
+ var isDeep = false;
+ paths = arrayMap(paths, function(path) {
+ path = castPath(path, object);
+ isDeep || (isDeep = path.length > 1);
+ return path;
+ });
+ copyObject(object, getAllKeysIn(object), result);
+ if (isDeep) {
+ result = baseClone(result, CLONE_DEEP_FLAG | CLONE_FLAT_FLAG | CLONE_SYMBOLS_FLAG, customOmitClone);
+ }
+ var length = paths.length;
+ while (length--) {
+ baseUnset(result, paths[length]);
+ }
+ return result;
+ });
+
+ /**
+ * The opposite of `_.pickBy`; this method creates an object composed of
+ * the own and inherited enumerable string keyed properties of `object` that
+ * `predicate` doesn't return truthy for. The predicate is invoked with two
+ * arguments: (value, key).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Object
+ * @param {Object} object The source object.
+ * @param {Function} [predicate=_.identity] The function invoked per property.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * var object = { 'a': 1, 'b': '2', 'c': 3 };
+ *
+ * _.omitBy(object, _.isNumber);
+ * // => { 'b': '2' }
+ */
+ function omitBy(object, predicate) {
+ return pickBy(object, negate(baseIteratee(predicate)));
+ }
+
+ /**
+ * Creates an object composed of the picked `object` properties.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The source object.
+ * @param {...(string|string[])} [paths] The property paths to pick.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * var object = { 'a': 1, 'b': '2', 'c': 3 };
+ *
+ * _.pick(object, ['a', 'c']);
+ * // => { 'a': 1, 'c': 3 }
+ */
+ var pick = flatRest(function(object, paths) {
+ return object == null ? {} : basePick(object, paths);
+ });
+
+ /**
+ * Creates an object composed of the `object` properties `predicate` returns
+ * truthy for. The predicate is invoked with two arguments: (value, key).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Object
+ * @param {Object} object The source object.
+ * @param {Function} [predicate=_.identity] The function invoked per property.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * var object = { 'a': 1, 'b': '2', 'c': 3 };
+ *
+ * _.pickBy(object, _.isNumber);
+ * // => { 'a': 1, 'c': 3 }
+ */
+ function pickBy(object, predicate) {
+ if (object == null) {
+ return {};
+ }
+ var props = arrayMap(getAllKeysIn(object), function(prop) {
+ return [prop];
+ });
+ predicate = baseIteratee(predicate);
+ return basePickBy(object, props, function(value, path) {
+ return predicate(value, path[0]);
+ });
+ }
+
+ /**
+ * This method is like `_.get` except that if the resolved value is a
+ * function it's invoked with the `this` binding of its parent object and
+ * its result is returned.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path of the property to resolve.
+ * @param {*} [defaultValue] The value returned for `undefined` resolved values.
+ * @returns {*} Returns the resolved value.
+ * @example
+ *
+ * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] };
+ *
+ * _.result(object, 'a[0].b.c1');
+ * // => 3
+ *
+ * _.result(object, 'a[0].b.c2');
+ * // => 4
+ *
+ * _.result(object, 'a[0].b.c3', 'default');
+ * // => 'default'
+ *
+ * _.result(object, 'a[0].b.c3', _.constant('default'));
+ * // => 'default'
+ */
+ function result(object, path, defaultValue) {
+ path = castPath(path, object);
+
+ var index = -1,
+ length = path.length;
+
+ // Ensure the loop is entered when path is empty.
+ if (!length) {
+ length = 1;
+ object = undefined;
+ }
+ while (++index < length) {
+ var value = object == null ? undefined : object[toKey(path[index])];
+ if (value === undefined) {
+ index = length;
+ value = defaultValue;
+ }
+ object = isFunction(value) ? value.call(object) : value;
+ }
+ return object;
+ }
+
+ /**
+ * Sets the value at `path` of `object`. If a portion of `path` doesn't exist,
+ * it's created. Arrays are created for missing index properties while objects
+ * are created for all other missing properties. Use `_.setWith` to customize
+ * `path` creation.
+ *
+ * **Note:** This method mutates `object`.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.7.0
+ * @category Object
+ * @param {Object} object The object to modify.
+ * @param {Array|string} path The path of the property to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * var object = { 'a': [{ 'b': { 'c': 3 } }] };
+ *
+ * _.set(object, 'a[0].b.c', 4);
+ * console.log(object.a[0].b.c);
+ * // => 4
+ *
+ * _.set(object, ['x', '0', 'y', 'z'], 5);
+ * console.log(object.x[0].y.z);
+ * // => 5
+ */
+ function set(object, path, value) {
+ return object == null ? object : baseSet(object, path, value);
+ }
+
+ /**
+ * Creates an array of the own enumerable string keyed property values of `object`.
+ *
+ * **Note:** Non-object values are coerced to objects.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of property values.
+ * @example
+ *
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
+ *
+ * Foo.prototype.c = 3;
+ *
+ * _.values(new Foo);
+ * // => [1, 2] (iteration order is not guaranteed)
+ *
+ * _.values('hi');
+ * // => ['h', 'i']
+ */
+ function values(object) {
+ return object == null ? [] : baseValues(object, keys(object));
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Clamps `number` within the inclusive `lower` and `upper` bounds.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Number
+ * @param {number} number The number to clamp.
+ * @param {number} [lower] The lower bound.
+ * @param {number} upper The upper bound.
+ * @returns {number} Returns the clamped number.
+ * @example
+ *
+ * _.clamp(-10, -5, 5);
+ * // => -5
+ *
+ * _.clamp(10, -5, 5);
+ * // => 5
+ */
+ function clamp(number, lower, upper) {
+ if (upper === undefined) {
+ upper = lower;
+ lower = undefined;
+ }
+ if (upper !== undefined) {
+ upper = toNumber(upper);
+ upper = upper === upper ? upper : 0;
+ }
+ if (lower !== undefined) {
+ lower = toNumber(lower);
+ lower = lower === lower ? lower : 0;
+ }
+ return baseClamp(toNumber(number), lower, upper);
+ }
+
+ /**
+ * Produces a random number between the inclusive `lower` and `upper` bounds.
+ * If only one argument is provided a number between `0` and the given number
+ * is returned. If `floating` is `true`, or either `lower` or `upper` are
+ * floats, a floating-point number is returned instead of an integer.
+ *
+ * **Note:** JavaScript follows the IEEE-754 standard for resolving
+ * floating-point values which can produce unexpected results.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.7.0
+ * @category Number
+ * @param {number} [lower=0] The lower bound.
+ * @param {number} [upper=1] The upper bound.
+ * @param {boolean} [floating] Specify returning a floating-point number.
+ * @returns {number} Returns the random number.
+ * @example
+ *
+ * _.random(0, 5);
+ * // => an integer between 0 and 5
+ *
+ * _.random(5);
+ * // => also an integer between 0 and 5
+ *
+ * _.random(5, true);
+ * // => a floating-point number between 0 and 5
+ *
+ * _.random(1.2, 5.2);
+ * // => a floating-point number between 1.2 and 5.2
+ */
+ function random(lower, upper, floating) {
+ if (floating && typeof floating != 'boolean' && isIterateeCall(lower, upper, floating)) {
+ upper = floating = undefined;
+ }
+ if (floating === undefined) {
+ if (typeof upper == 'boolean') {
+ floating = upper;
+ upper = undefined;
+ }
+ else if (typeof lower == 'boolean') {
+ floating = lower;
+ lower = undefined;
+ }
+ }
+ if (lower === undefined && upper === undefined) {
+ lower = 0;
+ upper = 1;
+ }
+ else {
+ lower = toFinite(lower);
+ if (upper === undefined) {
+ upper = lower;
+ lower = 0;
+ } else {
+ upper = toFinite(upper);
+ }
+ }
+ if (lower > upper) {
+ var temp = lower;
+ lower = upper;
+ upper = temp;
+ }
+ if (floating || lower % 1 || upper % 1) {
+ var rand = nativeRandom();
+ return nativeMin(lower + (rand * (upper - lower + freeParseFloat('1e-' + ((rand + '').length - 1)))), upper);
+ }
+ return baseRandom(lower, upper);
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Converts the characters "&", "<", ">", '"', and "'" in `string` to their
+ * corresponding HTML entities.
+ *
+ * **Note:** No other characters are escaped. To escape additional
+ * characters use a third-party library like [_he_](https://mths.be/he).
+ *
+ * Though the ">" character is escaped for symmetry, characters like
+ * ">" and "/" don't need escaping in HTML and have no special meaning
+ * unless they're part of a tag or unquoted attribute value. See
+ * [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands)
+ * (under "semi-related fun fact") for more details.
+ *
+ * When working with HTML you should always
+ * [quote attribute values](http://wonko.com/post/html-escaping) to reduce
+ * XSS vectors.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category String
+ * @param {string} [string=''] The string to escape.
+ * @returns {string} Returns the escaped string.
+ * @example
+ *
+ * _.escape('fred, barney, & pebbles');
+ * // => 'fred, barney, & pebbles'
+ */
+ function escape(string) {
+ string = toString(string);
+ return (string && reHasUnescapedHtml.test(string))
+ ? string.replace(reUnescapedHtml, escapeHtmlChar)
+ : string;
+ }
+
+ /**
+ * Removes leading and trailing whitespace or specified characters from `string`.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category String
+ * @param {string} [string=''] The string to trim.
+ * @param {string} [chars=whitespace] The characters to trim.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {string} Returns the trimmed string.
+ * @example
+ *
+ * _.trim(' abc ');
+ * // => 'abc'
+ *
+ * _.trim('-_-abc-_-', '_-');
+ * // => 'abc'
+ *
+ * _.map([' foo ', ' bar '], _.trim);
+ * // => ['foo', 'bar']
+ */
+ function trim(string, chars, guard) {
+ string = toString(string);
+ if (string && (guard || chars === undefined)) {
+ return string.replace(reTrim, '');
+ }
+ if (!string || !(chars = baseToString(chars))) {
+ return string;
+ }
+ var strSymbols = stringToArray(string),
+ chrSymbols = stringToArray(chars),
+ start = charsStartIndex(strSymbols, chrSymbols),
+ end = charsEndIndex(strSymbols, chrSymbols) + 1;
+
+ return castSlice(strSymbols, start, end).join('');
+ }
+
+ /**
+ * The inverse of `_.escape`; this method converts the HTML entities
+ * `&`, `<`, `>`, `"`, and `'` in `string` to
+ * their corresponding characters.
+ *
+ * **Note:** No other HTML entities are unescaped. To unescape additional
+ * HTML entities use a third-party library like [_he_](https://mths.be/he).
+ *
+ * @static
+ * @memberOf _
+ * @since 0.6.0
+ * @category String
+ * @param {string} [string=''] The string to unescape.
+ * @returns {string} Returns the unescaped string.
+ * @example
+ *
+ * _.unescape('fred, barney, & pebbles');
+ * // => 'fred, barney, & pebbles'
+ */
+ function unescape(string) {
+ string = toString(string);
+ return (string && reHasEscapedHtml.test(string))
+ ? string.replace(reEscapedHtml, unescapeHtmlChar)
+ : string;
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates a function that returns `value`.
+ *
+ * @static
+ * @memberOf _
+ * @since 2.4.0
+ * @category Util
+ * @param {*} value The value to return from the new function.
+ * @returns {Function} Returns the new constant function.
+ * @example
+ *
+ * var objects = _.times(2, _.constant({ 'a': 1 }));
+ *
+ * console.log(objects);
+ * // => [{ 'a': 1 }, { 'a': 1 }]
+ *
+ * console.log(objects[0] === objects[1]);
+ * // => true
+ */
+ function constant(value) {
+ return function() {
+ return value;
+ };
+ }
+
+ /**
+ * This method returns the first argument it receives.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category Util
+ * @param {*} value Any value.
+ * @returns {*} Returns `value`.
+ * @example
+ *
+ * var object = { 'a': 1 };
+ *
+ * console.log(_.identity(object) === object);
+ * // => true
+ */
+ function identity(value) {
+ return value;
+ }
+
+ /**
+ * Creates a function that invokes `func` with the arguments of the created
+ * function. If `func` is a property name, the created function returns the
+ * property value for a given element. If `func` is an array or object, the
+ * created function returns `true` for elements that contain the equivalent
+ * source properties, otherwise it returns `false`.
+ *
+ * @static
+ * @since 4.0.0
+ * @memberOf _
+ * @category Util
+ * @param {*} [func=_.identity] The value to convert to a callback.
+ * @returns {Function} Returns the callback.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': true },
+ * { 'user': 'fred', 'age': 40, 'active': false }
+ * ];
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.filter(users, _.iteratee({ 'user': 'barney', 'active': true }));
+ * // => [{ 'user': 'barney', 'age': 36, 'active': true }]
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.filter(users, _.iteratee(['user', 'fred']));
+ * // => [{ 'user': 'fred', 'age': 40 }]
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.map(users, _.iteratee('user'));
+ * // => ['barney', 'fred']
+ *
+ * // Create custom iteratee shorthands.
+ * _.iteratee = _.wrap(_.iteratee, function(iteratee, func) {
+ * return !_.isRegExp(func) ? iteratee(func) : function(string) {
+ * return func.test(string);
+ * };
+ * });
+ *
+ * _.filter(['abc', 'def'], /ef/);
+ * // => ['def']
+ */
+ function iteratee(func) {
+ return baseIteratee(typeof func == 'function' ? func : baseClone(func, CLONE_DEEP_FLAG));
+ }
+
+ /**
+ * Creates a function that performs a partial deep comparison between a given
+ * object and `source`, returning `true` if the given object has equivalent
+ * property values, else `false`.
+ *
+ * **Note:** The created function is equivalent to `_.isMatch` with `source`
+ * partially applied.
+ *
+ * Partial comparisons will match empty array and empty object `source`
+ * values against any array or object value, respectively. See `_.isEqual`
+ * for a list of supported value comparisons.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Util
+ * @param {Object} source The object of property values to match.
+ * @returns {Function} Returns the new spec function.
+ * @example
+ *
+ * var objects = [
+ * { 'a': 1, 'b': 2, 'c': 3 },
+ * { 'a': 4, 'b': 5, 'c': 6 }
+ * ];
+ *
+ * _.filter(objects, _.matches({ 'a': 4, 'c': 6 }));
+ * // => [{ 'a': 4, 'b': 5, 'c': 6 }]
+ */
+ function matches(source) {
+ return baseMatches(baseClone(source, CLONE_DEEP_FLAG));
+ }
+
+ /**
+ * Adds all own enumerable string keyed function properties of a source
+ * object to the destination object. If `object` is a function, then methods
+ * are added to its prototype as well.
+ *
+ * **Note:** Use `_.runInContext` to create a pristine `lodash` function to
+ * avoid conflicts caused by modifying the original.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category Util
+ * @param {Function|Object} [object=lodash] The destination object.
+ * @param {Object} source The object of functions to add.
+ * @param {Object} [options={}] The options object.
+ * @param {boolean} [options.chain=true] Specify whether mixins are chainable.
+ * @returns {Function|Object} Returns `object`.
+ * @example
+ *
+ * function vowels(string) {
+ * return _.filter(string, function(v) {
+ * return /[aeiou]/i.test(v);
+ * });
+ * }
+ *
+ * _.mixin({ 'vowels': vowels });
+ * _.vowels('fred');
+ * // => ['e']
+ *
+ * _('fred').vowels().value();
+ * // => ['e']
+ *
+ * _.mixin({ 'vowels': vowels }, { 'chain': false });
+ * _('fred').vowels();
+ * // => ['e']
+ */
+ function mixin(object, source, options) {
+ var props = keys(source),
+ methodNames = baseFunctions(source, props);
+
+ if (options == null &&
+ !(isObject(source) && (methodNames.length || !props.length))) {
+ options = source;
+ source = object;
+ object = this;
+ methodNames = baseFunctions(source, keys(source));
+ }
+ var chain = !(isObject(options) && 'chain' in options) || !!options.chain,
+ isFunc = isFunction(object);
+
+ arrayEach(methodNames, function(methodName) {
+ var func = source[methodName];
+ object[methodName] = func;
+ if (isFunc) {
+ object.prototype[methodName] = function() {
+ var chainAll = this.__chain__;
+ if (chain || chainAll) {
+ var result = object(this.__wrapped__),
+ actions = result.__actions__ = copyArray(this.__actions__);
+
+ actions.push({ 'func': func, 'args': arguments, 'thisArg': object });
+ result.__chain__ = chainAll;
+ return result;
+ }
+ return func.apply(object, arrayPush([this.value()], arguments));
+ };
+ }
+ });
+
+ return object;
+ }
+
+ /**
+ * Reverts the `_` variable to its previous value and returns a reference to
+ * the `lodash` function.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category Util
+ * @returns {Function} Returns the `lodash` function.
+ * @example
+ *
+ * var lodash = _.noConflict();
+ */
+ function noConflict() {
+ if (root._ === this) {
+ root._ = oldDash;
+ }
+ return this;
+ }
+
+ /**
+ * This method returns `undefined`.
+ *
+ * @static
+ * @memberOf _
+ * @since 2.3.0
+ * @category Util
+ * @example
+ *
+ * _.times(2, _.noop);
+ * // => [undefined, undefined]
+ */
+ function noop() {
+ // No operation performed.
+ }
+
+ /**
+ * Creates a function that returns the value at `path` of a given object.
+ *
+ * @static
+ * @memberOf _
+ * @since 2.4.0
+ * @category Util
+ * @param {Array|string} path The path of the property to get.
+ * @returns {Function} Returns the new accessor function.
+ * @example
+ *
+ * var objects = [
+ * { 'a': { 'b': 2 } },
+ * { 'a': { 'b': 1 } }
+ * ];
+ *
+ * _.map(objects, _.property('a.b'));
+ * // => [2, 1]
+ *
+ * _.map(_.sortBy(objects, _.property(['a', 'b'])), 'a.b');
+ * // => [1, 2]
+ */
+ function property(path) {
+ return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path);
+ }
+
+ /**
+ * Creates an array of numbers (positive and/or negative) progressing from
+ * `start` up to, but not including, `end`. A step of `-1` is used if a negative
+ * `start` is specified without an `end` or `step`. If `end` is not specified,
+ * it's set to `start` with `start` then set to `0`.
+ *
+ * **Note:** JavaScript follows the IEEE-754 standard for resolving
+ * floating-point values which can produce unexpected results.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category Util
+ * @param {number} [start=0] The start of the range.
+ * @param {number} end The end of the range.
+ * @param {number} [step=1] The value to increment or decrement by.
+ * @returns {Array} Returns the range of numbers.
+ * @see _.inRange, _.rangeRight
+ * @example
+ *
+ * _.range(4);
+ * // => [0, 1, 2, 3]
+ *
+ * _.range(-4);
+ * // => [0, -1, -2, -3]
+ *
+ * _.range(1, 5);
+ * // => [1, 2, 3, 4]
+ *
+ * _.range(0, 20, 5);
+ * // => [0, 5, 10, 15]
+ *
+ * _.range(0, -4, -1);
+ * // => [0, -1, -2, -3]
+ *
+ * _.range(1, 4, 0);
+ * // => [1, 1, 1]
+ *
+ * _.range(0);
+ * // => []
+ */
+ var range = createRange();
+
+ /**
+ * This method returns a new empty array.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.13.0
+ * @category Util
+ * @returns {Array} Returns the new empty array.
+ * @example
+ *
+ * var arrays = _.times(2, _.stubArray);
+ *
+ * console.log(arrays);
+ * // => [[], []]
+ *
+ * console.log(arrays[0] === arrays[1]);
+ * // => false
+ */
+ function stubArray() {
+ return [];
+ }
+
+ /**
+ * This method returns `false`.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.13.0
+ * @category Util
+ * @returns {boolean} Returns `false`.
+ * @example
+ *
+ * _.times(2, _.stubFalse);
+ * // => [false, false]
+ */
+ function stubFalse() {
+ return false;
+ }
+
+ /**
+ * Generates a unique ID. If `prefix` is given, the ID is appended to it.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category Util
+ * @param {string} [prefix=''] The value to prefix the ID with.
+ * @returns {string} Returns the unique ID.
+ * @example
+ *
+ * _.uniqueId('contact_');
+ * // => 'contact_104'
+ *
+ * _.uniqueId();
+ * // => '105'
+ */
+ function uniqueId(prefix) {
+ var id = ++idCounter;
+ return toString(prefix) + id;
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Computes the maximum value of `array`. If `array` is empty or falsey,
+ * `undefined` is returned.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category Math
+ * @param {Array} array The array to iterate over.
+ * @returns {*} Returns the maximum value.
+ * @example
+ *
+ * _.max([4, 2, 8, 6]);
+ * // => 8
+ *
+ * _.max([]);
+ * // => undefined
+ */
+ function max(array) {
+ return (array && array.length)
+ ? baseExtremum(array, identity, baseGt)
+ : undefined;
+ }
+
+ /**
+ * Computes the minimum value of `array`. If `array` is empty or falsey,
+ * `undefined` is returned.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category Math
+ * @param {Array} array The array to iterate over.
+ * @returns {*} Returns the minimum value.
+ * @example
+ *
+ * _.min([4, 2, 8, 6]);
+ * // => 2
+ *
+ * _.min([]);
+ * // => undefined
+ */
+ function min(array) {
+ return (array && array.length)
+ ? baseExtremum(array, identity, baseLt)
+ : undefined;
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ // Add methods that return wrapped values in chain sequences.
+ lodash.after = after;
+ lodash.assignIn = assignIn;
+ lodash.before = before;
+ lodash.bind = bind;
+ lodash.chain = chain;
+ lodash.compact = compact;
+ lodash.concat = concat;
+ lodash.countBy = countBy;
+ lodash.create = create;
+ lodash.debounce = debounce;
+ lodash.defaults = defaults;
+ lodash.defaultsDeep = defaultsDeep;
+ lodash.defer = defer;
+ lodash.delay = delay;
+ lodash.difference = difference;
+ lodash.drop = drop;
+ lodash.filter = filter;
+ lodash.flatten = flatten;
+ lodash.flattenDeep = flattenDeep;
+ lodash.groupBy = groupBy;
+ lodash.initial = initial;
+ lodash.intersection = intersection;
+ lodash.invert = invert;
+ lodash.invertBy = invertBy;
+ lodash.iteratee = iteratee;
+ lodash.keys = keys;
+ lodash.map = map;
+ lodash.matches = matches;
+ lodash.merge = merge;
+ lodash.mixin = mixin;
+ lodash.negate = negate;
+ lodash.omit = omit;
+ lodash.omitBy = omitBy;
+ lodash.once = once;
+ lodash.pick = pick;
+ lodash.range = range;
+ lodash.reject = reject;
+ lodash.rest = rest;
+ lodash.set = set;
+ lodash.slice = slice;
+ lodash.sortBy = sortBy;
+ lodash.take = take;
+ lodash.takeRight = takeRight;
+ lodash.tap = tap;
+ lodash.throttle = throttle;
+ lodash.thru = thru;
+ lodash.toArray = toArray;
+ lodash.union = union;
+ lodash.uniq = uniq;
+ lodash.uniqBy = uniqBy;
+ lodash.unzip = unzip;
+ lodash.values = values;
+ lodash.without = without;
+ lodash.zip = zip;
+ lodash.zipObject = zipObject;
+
+ // Add aliases.
+ lodash.extend = assignIn;
+
+ // Add methods to `lodash.prototype`.
+ mixin(lodash, lodash);
+
+ /*------------------------------------------------------------------------*/
+
+ // Add methods that return unwrapped values in chain sequences.
+ lodash.clamp = clamp;
+ lodash.clone = clone;
+ lodash.cloneDeep = cloneDeep;
+ lodash.escape = escape;
+ lodash.every = every;
+ lodash.find = find;
+ lodash.findIndex = findIndex;
+ lodash.findKey = findKey;
+ lodash.findLastIndex = findLastIndex;
+ lodash.findLastKey = findLastKey;
+ lodash.forEach = forEach;
+ lodash.get = get;
+ lodash.has = has;
+ lodash.head = head;
+ lodash.identity = identity;
+ lodash.indexOf = indexOf;
+ lodash.isArguments = isArguments;
+ lodash.isArray = isArray;
+ lodash.isArrayLike = isArrayLike;
+ lodash.isBoolean = isBoolean;
+ lodash.isDate = isDate;
+ lodash.isEmpty = isEmpty;
+ lodash.isEqual = isEqual;
+ lodash.isFinite = isFinite;
+ lodash.isFunction = isFunction;
+ lodash.isNaN = isNaN;
+ lodash.isNull = isNull;
+ lodash.isNumber = isNumber;
+ lodash.isObject = isObject;
+ lodash.isPlainObject = isPlainObject;
+ lodash.isRegExp = isRegExp;
+ lodash.isString = isString;
+ lodash.isUndefined = isUndefined;
+ lodash.last = last;
+ lodash.max = max;
+ lodash.min = min;
+ lodash.noConflict = noConflict;
+ lodash.noop = noop;
+ lodash.random = random;
+ lodash.reduce = reduce;
+ lodash.result = result;
+ lodash.size = size;
+ lodash.some = some;
+ lodash.trim = trim;
+ lodash.unescape = unescape;
+ lodash.uniqueId = uniqueId;
+
+ // Add aliases.
+ lodash.each = forEach;
+ lodash.first = head;
+
+ mixin(lodash, (function() {
+ var source = {};
+ baseForOwn(lodash, function(func, methodName) {
+ if (!hasOwnProperty.call(lodash.prototype, methodName)) {
+ source[methodName] = func;
+ }
+ });
+ return source;
+ }()), { 'chain': false });
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * The semantic version number.
+ *
+ * @static
+ * @memberOf _
+ * @type {string}
+ */
+ lodash.VERSION = VERSION;
+
+ // Add `LazyWrapper` methods for `_.drop` and `_.take` variants.
+ arrayEach(['drop', 'take'], function(methodName, index) {
+ LazyWrapper.prototype[methodName] = function(n) {
+ n = n === undefined ? 1 : nativeMax(toInteger(n), 0);
+
+ var result = (this.__filtered__ && !index)
+ ? new LazyWrapper(this)
+ : this.clone();
+
+ if (result.__filtered__) {
+ result.__takeCount__ = nativeMin(n, result.__takeCount__);
+ } else {
+ result.__views__.push({
+ 'size': nativeMin(n, MAX_ARRAY_LENGTH),
+ 'type': methodName + (result.__dir__ < 0 ? 'Right' : '')
+ });
+ }
+ return result;
+ };
+
+ LazyWrapper.prototype[methodName + 'Right'] = function(n) {
+ return this.reverse()[methodName](n).reverse();
+ };
+ });
+
+ // Add `LazyWrapper` methods that accept an `iteratee` value.
+ arrayEach(['filter', 'map', 'takeWhile'], function(methodName, index) {
+ var type = index + 1,
+ isFilter = type == LAZY_FILTER_FLAG || type == LAZY_WHILE_FLAG;
+
+ LazyWrapper.prototype[methodName] = function(iteratee) {
+ var result = this.clone();
+ result.__iteratees__.push({
+ 'iteratee': getIteratee(iteratee, 3),
+ 'type': type
+ });
+ result.__filtered__ = result.__filtered__ || isFilter;
+ return result;
+ };
+ });
+
+ // Add `LazyWrapper` methods for `_.head` and `_.last`.
+ arrayEach(['head', 'last'], function(methodName, index) {
+ var takeName = 'take' + (index ? 'Right' : '');
+
+ LazyWrapper.prototype[methodName] = function() {
+ return this[takeName](1).value()[0];
+ };
+ });
+
+ // Add `LazyWrapper` methods for `_.initial` and `_.tail`.
+ arrayEach(['initial', 'tail'], function(methodName, index) {
+ var dropName = 'drop' + (index ? '' : 'Right');
+
+ LazyWrapper.prototype[methodName] = function() {
+ return this.__filtered__ ? new LazyWrapper(this) : this[dropName](1);
+ };
+ });
+
+ LazyWrapper.prototype.compact = function() {
+ return this.filter(identity);
+ };
+
+ LazyWrapper.prototype.find = function(predicate) {
+ return this.filter(predicate).head();
+ };
+
+ LazyWrapper.prototype.findLast = function(predicate) {
+ return this.reverse().find(predicate);
+ };
+
+ LazyWrapper.prototype.invokeMap = baseRest(function(path, args) {
+ if (typeof path == 'function') {
+ return new LazyWrapper(this);
+ }
+ return this.map(function(value) {
+ return baseInvoke(value, path, args);
+ });
+ });
+
+ LazyWrapper.prototype.reject = function(predicate) {
+ return this.filter(negate(getIteratee(predicate)));
+ };
+
+ LazyWrapper.prototype.slice = function(start, end) {
+ start = toInteger(start);
+
+ var result = this;
+ if (result.__filtered__ && (start > 0 || end < 0)) {
+ return new LazyWrapper(result);
+ }
+ if (start < 0) {
+ result = result.takeRight(-start);
+ } else if (start) {
+ result = result.drop(start);
+ }
+ if (end !== undefined) {
+ end = toInteger(end);
+ result = end < 0 ? result.dropRight(-end) : result.take(end - start);
+ }
+ return result;
+ };
+
+ LazyWrapper.prototype.takeRightWhile = function(predicate) {
+ return this.reverse().takeWhile(predicate).reverse();
+ };
+
+ LazyWrapper.prototype.toArray = function() {
+ return this.take(MAX_ARRAY_LENGTH);
+ };
+
+ // Add `LazyWrapper` methods to `lodash.prototype`.
+ baseForOwn(LazyWrapper.prototype, function(func, methodName) {
+ var checkIteratee = /^(?:filter|find|map|reject)|While$/.test(methodName),
+ isTaker = /^(?:head|last)$/.test(methodName),
+ lodashFunc = lodash[isTaker ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName],
+ retUnwrapped = isTaker || /^find/.test(methodName);
+
+ if (!lodashFunc) {
+ return;
+ }
+ lodash.prototype[methodName] = function() {
+ var value = this.__wrapped__,
+ args = isTaker ? [1] : arguments,
+ isLazy = value instanceof LazyWrapper,
+ iteratee = args[0],
+ useLazy = isLazy || isArray(value);
+
+ var interceptor = function(value) {
+ var result = lodashFunc.apply(lodash, arrayPush([value], args));
+ return (isTaker && chainAll) ? result[0] : result;
+ };
+
+ if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) {
+ // Avoid lazy use if the iteratee has a "length" value other than `1`.
+ isLazy = useLazy = false;
+ }
+ var chainAll = this.__chain__,
+ isHybrid = !!this.__actions__.length,
+ isUnwrapped = retUnwrapped && !chainAll,
+ onlyLazy = isLazy && !isHybrid;
+
+ if (!retUnwrapped && useLazy) {
+ value = onlyLazy ? value : new LazyWrapper(this);
+ var result = func.apply(value, args);
+ result.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined });
+ return new LodashWrapper(result, chainAll);
+ }
+ if (isUnwrapped && onlyLazy) {
+ return func.apply(this, args);
+ }
+ result = this.thru(interceptor);
+ return isUnwrapped ? (isTaker ? result.value()[0] : result.value()) : result;
+ };
+ });
+
+ // Add `Array` methods to `lodash.prototype`.
+ arrayEach(['pop', 'push', 'shift', 'sort', 'splice', 'unshift'], function(methodName) {
+ var func = arrayProto[methodName],
+ chainName = /^(?:push|sort|unshift)$/.test(methodName) ? 'tap' : 'thru',
+ retUnwrapped = /^(?:pop|shift)$/.test(methodName);
+
+ lodash.prototype[methodName] = function() {
+ var args = arguments;
+ if (retUnwrapped && !this.__chain__) {
+ var value = this.value();
+ return func.apply(isArray(value) ? value : [], args);
+ }
+ return this[chainName](function(value) {
+ return func.apply(isArray(value) ? value : [], args);
+ });
+ };
+ });
+
+ // Map minified method names to their real names.
+ baseForOwn(LazyWrapper.prototype, function(func, methodName) {
+ var lodashFunc = lodash[methodName];
+ if (lodashFunc) {
+ var key = (lodashFunc.name + ''),
+ names = realNames[key] || (realNames[key] = []);
+
+ names.push({ 'name': methodName, 'func': lodashFunc });
+ }
+ });
+
+ realNames[createHybrid(undefined, WRAP_BIND_KEY_FLAG).name] = [{
+ 'name': 'wrapper',
+ 'func': undefined
+ }];
+
+ // Add methods to `LazyWrapper`.
+ LazyWrapper.prototype.clone = lazyClone;
+ LazyWrapper.prototype.reverse = lazyReverse;
+ LazyWrapper.prototype.value = lazyValue;
+
+ // Add lazy aliases.
+ lodash.prototype.first = lodash.prototype.head;
+
+ if (symIterator) {
+ lodash.prototype[symIterator] = wrapperToIterator;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ // Some AMD build optimizers, like r.js, check for condition patterns like:
+ if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
+ // Expose Lodash on the global object to prevent errors when Lodash is
+ // loaded by a script tag in the presence of an AMD loader.
+ // See http://requirejs.org/docs/errors.html#mismatch for more details.
+ // Use `_.noConflict` to remove Lodash from the global object.
+ root._ = lodash;
+
+ // Define as an anonymous module so, through path mapping, it can be
+ // referenced as the "underscore" module.
+ define(function() {
+ return lodash;
+ });
+ }
+ // Check for `exports` after `define` in case a build optimizer adds it.
+ else if (freeModule) {
+ // Export for Node.js.
+ (freeModule.exports = lodash)._ = lodash;
+ // Export for CommonJS support.
+ freeExports._ = lodash;
+ }
+ else {
+ // Export to the global object.
+ root._ = lodash;
+ }
+}.call(this));
diff --git a/src/main/resources/com/fr/fineui/core/2.base.js b/src/main/resources/com/fr/fineui/core/2.base.js
new file mode 100644
index 0000000..16a4a9f
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/2.base.js
@@ -0,0 +1,1304 @@
+/**
+ * 基本函数
+ * Create By GUY 2014\11\17
+ *
+ */
+_global = undefined;
+if (typeof window !== "undefined") {
+ _global = window;
+} else if (typeof global !== "undefined") {
+ _global = global;
+} else if (typeof self !== "undefined") {
+ _global = self;
+} else {
+ _global = this;
+}
+if (!_global.BI) {
+ _global.BI = {};
+}
+
+!(function (undefined) {
+ var traverse = function (func, context) {
+ return function (value, key, obj) {
+ return func.call(context, key, value, obj);
+ };
+ };
+ var _apply = function (name) {
+ return function () {
+ return _[name].apply(_, arguments);
+ };
+ };
+ var _applyFunc = function (name) {
+ return function () {
+ var args = Array.prototype.slice.call(arguments, 0);
+ args[1] = _.isFunction(args[1]) ? traverse(args[1], args[2]) : args[1];
+ return _[name].apply(_, args);
+ };
+ };
+
+ // Utility
+ _.extend(BI, {
+ assert: function (v, is) {
+ if (this.isFunction(is)) {
+ if (!is(v)) {
+ throw new Error(v + " error");
+ } else {
+ return true;
+ }
+ }
+ if (!this.isArray(is)) {
+ is = [is];
+ }
+ if (!this.deepContains(is, v)) {
+ throw new Error(v + " error");
+ }
+ return true;
+ },
+
+ warn: function (message) {
+ console.warn(message);
+ },
+
+ UUID: function () {
+ var f = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"];
+ var str = "";
+ for (var i = 0; i < 16; i++) {
+ var r = parseInt(f.length * Math.random(), 10);
+ str += f[r];
+ }
+ return str;
+ },
+
+ isWidget: function (widget) {
+ return widget instanceof BI.Widget;
+ },
+
+ createWidgets: function (items, options, context) {
+ if (!BI.isArray(items)) {
+ throw new Error("items必须是数组", items);
+ }
+ if (BI.isWidget(options)) {
+ context = options;
+ options = {};
+ } else {
+ options || (options = {});
+ }
+ return BI.map(BI.flatten(items), function (i, item) {
+ return BI.createWidget(item, BI.deepClone(options), context);
+ });
+ },
+
+ createItems: function (data, innerAttr, outerAttr) {
+ innerAttr = BI.isArray(innerAttr) ? innerAttr : BI.makeArray(BI.flatten(data).length, innerAttr || {});
+ outerAttr = BI.isArray(outerAttr) ? outerAttr : BI.makeArray(BI.flatten(data).length, outerAttr || {});
+ return BI.map(data, function (i, item) {
+ if (BI.isArray(item)) {
+ return BI.createItems(item, innerAttr, outerAttr);
+ }
+ if (item instanceof BI.Widget) {
+ return BI.extend({}, innerAttr.shift(), outerAttr.shift(), {
+ type: null,
+ el: item
+ });
+ }
+ if (innerAttr[0] instanceof BI.Widget) {
+ outerAttr.shift();
+ return BI.extend({}, item, {
+ el: innerAttr.shift()
+ });
+ }
+ if (item.el instanceof BI.Widget) {
+ innerAttr.shift();
+ return BI.extend({}, outerAttr.shift(), { type: null }, item);
+ }
+ if (item.el) {
+ return BI.extend({}, outerAttr.shift(), item, {
+ el: BI.extend({}, innerAttr.shift(), item.el)
+ });
+ }
+ return BI.extend({}, outerAttr.shift(), {
+ el: BI.extend({}, innerAttr.shift(), item)
+ });
+ });
+ },
+
+ // 用容器包装items
+ packageItems: function (items, layouts) {
+ for (var i = layouts.length - 1; i >= 0; i--) {
+ items = BI.map(items, function (k, it) {
+ return BI.extend({}, layouts[i], {
+ items: [
+ BI.extend({}, layouts[i].el, {
+ el: it
+ })
+ ]
+ });
+ });
+ }
+ return items;
+ },
+
+ formatEL: function (obj) {
+ if (obj && !obj.type && obj.el) {
+ return obj;
+ }
+ return {
+ el: obj
+ };
+ },
+
+ // 剥开EL
+ stripEL: function (obj) {
+ return obj.type && obj || obj.el || obj;
+ },
+
+ trans2Element: function (widgets) {
+ return BI.map(widgets, function (i, wi) {
+ return wi.element;
+ });
+ }
+ });
+
+ // 集合相关方法
+ _.each(["where", "findWhere", "invoke", "pluck", "shuffle", "sample", "toArray", "size"], function (name) {
+ BI[name] = _apply(name);
+ });
+ _.each(["get", "set", "each", "map", "reduce", "reduceRight", "find", "filter", "reject", "every", "all", "some", "any", "max", "min",
+ "sortBy", "groupBy", "indexBy", "countBy", "partition", "clamp"], function (name) {
+ if (name === "any") {
+ BI[name] = _applyFunc("some");
+ } else {
+ BI[name] = _applyFunc(name);
+ }
+ });
+ _.extend(BI, {
+ // 数数
+ count: function (from, to, predicate) {
+ var t;
+ if (predicate) {
+ for (t = from; t < to; t++) {
+ predicate(t);
+ }
+ }
+ return to - from;
+ },
+
+ // 倒数
+ inverse: function (from, to, predicate) {
+ return BI.count(to, from, predicate);
+ },
+
+ firstKey: function (obj) {
+ var res = undefined;
+ BI.any(obj, function (key, value) {
+ res = key;
+ return true;
+ });
+ return res;
+ },
+
+ lastKey: function (obj) {
+ var res = undefined;
+ BI.each(obj, function (key, value) {
+ res = key;
+ return true;
+ });
+ return res;
+ },
+
+ firstObject: function (obj) {
+ var res = undefined;
+ BI.any(obj, function (key, value) {
+ res = value;
+ return true;
+ });
+ return res;
+ },
+
+ lastObject: function (obj) {
+ var res = undefined;
+ BI.each(obj, function (key, value) {
+ res = value;
+ return true;
+ });
+ return res;
+ },
+
+ concat: function (obj1, obj2) {
+ if (BI.isKey(obj1)) {
+ return BI.map([].slice.apply(arguments), function (idx, v) {
+ return v;
+ }).join("");
+ }
+ if (BI.isArray(obj1)) {
+ return _.concat.apply([], arguments);
+ }
+ if (BI.isObject(obj1)) {
+ return _.extend.apply({}, arguments);
+ }
+ },
+
+ backEach: function (obj, predicate, context) {
+ predicate = BI.iteratee(predicate, context);
+ for (var index = obj.length - 1; index >= 0; index--) {
+ predicate(index, obj[index], obj);
+ }
+ return false;
+ },
+
+ backAny: function (obj, predicate, context) {
+ predicate = BI.iteratee(predicate, context);
+ for (var index = obj.length - 1; index >= 0; index--) {
+ if (predicate(index, obj[index], obj)) {
+ return true;
+ }
+ }
+ return false;
+ },
+
+ backEvery: function (obj, predicate, context) {
+ predicate = BI.iteratee(predicate, context);
+ for (var index = obj.length - 1; index >= 0; index--) {
+ if (!predicate(index, obj[index], obj)) {
+ return false;
+ }
+ }
+ return true;
+ },
+
+ backFindKey: function (obj, predicate, context) {
+ predicate = BI.iteratee(predicate, context);
+ var keys = _.keys(obj), key;
+ for (var i = keys.length - 1; i >= 0; i--) {
+ key = keys[i];
+ if (predicate(obj[key], key, obj)) {
+ return key;
+ }
+ }
+ },
+
+ backFind: function (obj, predicate, context) {
+ var key;
+ if (BI.isArray(obj)) {
+ key = BI.findLastIndex(obj, predicate, context);
+ } else {
+ key = BI.backFindKey(obj, predicate, context);
+ }
+ if (key !== void 0 && key !== -1) {
+ return obj[key];
+ }
+ },
+
+ remove: function (obj, target, context) {
+ var isFunction = BI.isFunction(target);
+ target = isFunction || BI.isArray(target) ? target : [target];
+ var i;
+ if (BI.isArray(obj)) {
+ for (i = 0; i < obj.length; i++) {
+ if ((isFunction && target.apply(context, [i, obj[i]]) === true) || (!isFunction && BI.contains(target, obj[i]))) {
+ obj.splice(i--, 1);
+ }
+ }
+ } else {
+ BI.each(obj, function (i, v) {
+ if ((isFunction && target.apply(context, [i, obj[i]]) === true) || (!isFunction && BI.contains(target, obj[i]))) {
+ delete obj[i];
+ }
+ });
+ }
+ },
+
+ removeAt: function (obj, index) {
+ index = BI.isArray(index) ? index : [index];
+ var isArray = BI.isArray(obj), i;
+ for (i = 0; i < index.length; i++) {
+ if (isArray) {
+ obj[index[i]] = "$deleteIndex";
+ } else {
+ delete obj[index[i]];
+ }
+ }
+ if (isArray) {
+ BI.remove(obj, "$deleteIndex");
+ }
+ },
+
+ string2Array: function (str) {
+ return str.split("&-&");
+ },
+
+ array2String: function (array) {
+ return array.join("&-&");
+ },
+
+ abc2Int: function (str) {
+ var idx = 0, start = "A", str = str.toUpperCase();
+ for (var i = 0, len = str.length; i < len; ++i) {
+ idx = str.charAt(i).charCodeAt(0) - start.charCodeAt(0) + 26 * idx + 1;
+ if (idx > (2147483646 - str.charAt(i).charCodeAt(0) + start.charCodeAt(0)) / 26) {
+ return 0;
+ }
+ }
+ return idx;
+ },
+
+ int2Abc: function (num) {
+ var DIGITS = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
+ var idx = num, str = "";
+ if (num === 0) {
+ return "";
+ }
+ while (idx !== 0) {
+ var t = idx % 26;
+ if (t === 0) {
+ t = 26;
+ }
+ str = DIGITS[t - 1] + str;
+ idx = (idx - t) / 26;
+ }
+ return str;
+ }
+ });
+
+ // 数组相关的方法
+ _.each(["first", "initial", "last", "rest", "compact", "flatten", "without", "union", "intersection",
+ "difference", "zip", "unzip", "object", "indexOf", "lastIndexOf", "sortedIndex", "range", "take", "takeRight", "uniqBy"], function (name) {
+ BI[name] = _apply(name);
+ });
+ _.each(["findIndex", "findLastIndex"], function (name) {
+ BI[name] = _applyFunc(name);
+ });
+ _.extend(BI, {
+ // 构建一个长度为length的数组
+ makeArray: function (length, value) {
+ var res = [];
+ for (var i = 0; i < length; i++) {
+ if (BI.isNull(value)) {
+ res.push(i);
+ } else {
+ res.push(BI.deepClone(value));
+ }
+ }
+ return res;
+ },
+
+ makeObject: function (array, value) {
+ var map = {};
+ for (var i = 0; i < array.length; i++) {
+ if (BI.isNull(value)) {
+ map[array[i]] = array[i];
+ } else if (BI.isFunction(value)) {
+ map[array[i]] = value(i, array[i]);
+ } else {
+ map[array[i]] = BI.deepClone(value);
+ }
+ }
+ return map;
+ },
+
+ makeArrayByArray: function (array, value) {
+ var res = [];
+ if (!array) {
+ return res;
+ }
+ for (var i = 0, len = array.length; i < len; i++) {
+ if (BI.isArray(array[i])) {
+ res.push(arguments.callee(array[i], value));
+ } else {
+ res.push(BI.deepClone(value));
+ }
+ }
+ return res;
+ },
+
+ uniq: function (array, isSorted, iteratee, context) {
+ if (array == null) {
+ return [];
+ }
+ if (!_.isBoolean(isSorted)) {
+ context = iteratee;
+ iteratee = isSorted;
+ isSorted = false;
+ }
+ iteratee && (iteratee = traverse(iteratee, context));
+ return _.uniq.call(_, array, isSorted, iteratee, context);
+ }
+ });
+
+ // 对象相关方法
+ _.each(["keys", "allKeys", "values", "pairs", "invert", "create", "functions", "extend", "extendOwn",
+ "defaults", "clone", "property", "propertyOf", "matcher", "isEqual", "isMatch", "isEmpty",
+ "isElement", "isNumber", "isString", "isArray", "isObject", "isPlainObject", "isArguments", "isFunction", "isFinite",
+ "isBoolean", "isDate", "isRegExp", "isError", "isNaN", "isUndefined", "zipObject", "cloneDeep"], function (name) {
+ BI[name] = _apply(name);
+ });
+ _.each(["mapObject", "findKey", "pick", "omit", "tap"], function (name) {
+ BI[name] = _applyFunc(name);
+ });
+ _.extend(BI, {
+
+ inherit: function (sp, overrides) {
+ var sb = function () {
+ return sp.apply(this, arguments);
+ };
+ var F = function () {
+ }, spp = sp.prototype;
+ F.prototype = spp;
+ sb.prototype = new F();
+ sb.superclass = spp;
+ _.extend(sb.prototype, overrides, {
+ superclass: sp
+ });
+ return sb;
+ },
+
+ init: function () {
+ // 先把准备环境准备好
+ while (BI.prepares && BI.prepares.length > 0) {
+ BI.prepares.shift()();
+ }
+ while (_global.___fineuiExposedFunction && _global.___fineuiExposedFunction.length > 0) {
+ _global.___fineuiExposedFunction.shift()();
+ }
+ BI.initialized = true;
+ },
+
+ has: function (obj, keys) {
+ if (BI.isArray(keys)) {
+ if (keys.length === 0) {
+ return false;
+ }
+ return BI.every(keys, function (i, key) {
+ return _.has(obj, key);
+ });
+ }
+ return _.has.apply(_, arguments);
+ },
+
+ freeze: function (value) {
+ // 在ES5中,如果这个方法的参数不是一个对象(一个原始值),那么它会导致 TypeError
+ // 在ES2015中,非对象参数将被视为要被冻结的普通对象,并被简单地返回
+ if (Object.freeze && BI.isObject(value)) {
+ return Object.freeze(value);
+ }
+ return value;
+ },
+
+ // 数字和字符串可以作为key
+ isKey: function (key) {
+ return BI.isNumber(key) || (BI.isString(key) && key.length > 0);
+ },
+
+ // 忽略大小写的等于
+ isCapitalEqual: function (a, b) {
+ a = BI.isNull(a) ? a : ("" + a).toLowerCase();
+ b = BI.isNull(b) ? b : ("" + b).toLowerCase();
+ return BI.isEqual(a, b);
+ },
+
+ isWidthOrHeight: function (w) {
+ if (typeof w === "number") {
+ return w >= 0;
+ } else if (typeof w === "string") {
+ return /^\d{1,3}(\.\d)?%$/.test(w) || w === "auto" || /^\d+px$/.test(w) || /^calc/.test(w);
+ }
+ },
+
+ isNotNull: function (obj) {
+ return !BI.isNull(obj);
+ },
+
+ isNull: function (obj) {
+ return typeof obj === "undefined" || obj === null;
+ },
+
+ isEmptyArray: function (arr) {
+ return BI.isArray(arr) && BI.isEmpty(arr);
+ },
+
+ isNotEmptyArray: function (arr) {
+ return BI.isArray(arr) && !BI.isEmpty(arr);
+ },
+
+ isEmptyObject: function (obj) {
+ return BI.isEqual(obj, {});
+ },
+
+ isNotEmptyObject: function (obj) {
+ return BI.isPlainObject(obj) && !BI.isEmptyObject(obj);
+ },
+
+ isEmptyString: function (obj) {
+ return BI.isString(obj) && obj.length === 0;
+ },
+
+ isNotEmptyString: function (obj) {
+ return BI.isString(obj) && !BI.isEmptyString(obj);
+ },
+
+ isWindow: function (obj) {
+ return obj != null && obj == obj.window;
+ }
+ });
+
+ // deep方法
+ _.extend(BI, {
+ deepClone: _.cloneDeep,
+ deepExtend: _.merge,
+
+ isDeepMatch: function (object, attrs) {
+ var keys = BI.keys(attrs), length = keys.length;
+ if (object == null) {
+ return !length;
+ }
+ var obj = Object(object);
+ for (var i = 0; i < length; i++) {
+ var key = keys[i];
+ if (!BI.isEqual(attrs[key], obj[key]) || !(key in obj)) {
+ return false;
+ }
+ }
+ return true;
+ },
+
+ contains: function (obj, target, fromIndex) {
+ if (!_.isArrayLike(obj)) obj = _.values(obj);
+ return _.indexOf(obj, target, typeof fromIndex === "number" && fromIndex) >= 0;
+ },
+
+ deepContains: function (obj, copy) {
+ if (BI.isObject(copy)) {
+ return BI.any(obj, function (i, v) {
+ if (BI.isEqual(v, copy)) {
+ return true;
+ }
+ });
+ }
+ return BI.contains(obj, copy);
+ },
+
+ deepIndexOf: function (obj, target) {
+ for (var i = 0; i < obj.length; i++) {
+ if (BI.isEqual(target, obj[i])) {
+ return i;
+ }
+ }
+ return -1;
+ },
+
+ deepRemove: function (obj, target) {
+ var done = false;
+ var i;
+ if (BI.isArray(obj)) {
+ for (i = 0; i < obj.length; i++) {
+ if (BI.isEqual(target, obj[i])) {
+ obj.splice(i--, 1);
+ done = true;
+ }
+ }
+ } else {
+ BI.each(obj, function (i, v) {
+ if (BI.isEqual(target, obj[i])) {
+ delete obj[i];
+ done = true;
+ }
+ });
+ }
+ return done;
+ },
+
+ deepWithout: function (obj, target) {
+ if (BI.isArray(obj)) {
+ var result = [];
+ for (var i = 0; i < obj.length; i++) {
+ if (!BI.isEqual(target, obj[i])) {
+ result.push(obj[i]);
+ }
+ }
+ return result;
+ }
+ var result = {};
+ BI.each(obj, function (i, v) {
+ if (!BI.isEqual(target, obj[i])) {
+ result[i] = v;
+ }
+ });
+ return result;
+
+ },
+
+ deepUnique: function (array) {
+ var result = [];
+ BI.each(array, function (i, item) {
+ if (!BI.deepContains(result, item)) {
+ result.push(item);
+ }
+ });
+ return result;
+ },
+
+ // 比较两个对象得出不一样的key值
+ deepDiff: function (object, other) {
+ object || (object = {});
+ other || (other = {});
+ var result = [];
+ var used = [];
+ for (var b in object) {
+ if (this.has(object, b)) {
+ if (!this.isEqual(object[b], other[b])) {
+ result.push(b);
+ }
+ used.push(b);
+ }
+ }
+ for (var b in other) {
+ if (this.has(other, b) && !BI.contains(used, b)) {
+ result.push(b);
+ }
+ }
+ return result;
+ }
+ });
+
+ // 通用方法
+ _.each(["uniqueId", "result", "chain", "iteratee", "escape", "unescape", "before", "after"], function (name) {
+ BI[name] = function () {
+ return _[name].apply(_, arguments);
+ };
+ });
+
+ // 事件相关方法
+ _.each(["bind", "once", "partial", "debounce", "throttle", "delay", "defer", "wrap"], function (name) {
+ BI[name] = function () {
+ return _[name].apply(_, arguments);
+ };
+ });
+
+ _.extend(BI, {
+ nextTick: (function () {
+ var callbacks = [];
+ var pending = false;
+ var timerFunc = void 0;
+
+ function nextTickHandler() {
+ pending = false;
+ var copies = callbacks.slice(0);
+ callbacks.length = 0;
+ for (var i = 0; i < copies.length; i++) {
+ copies[i]();
+ }
+ }
+
+ if (typeof Promise !== "undefined") {
+ var p = Promise.resolve();
+ timerFunc = function timerFunc() {
+ p.then(nextTickHandler);
+ };
+ } else if (typeof MutationObserver !== "undefined") {
+ var counter = 1;
+ var observer = new MutationObserver(nextTickHandler);
+ var textNode = document.createTextNode(String(counter));
+ observer.observe(textNode, {
+ characterData: true
+ });
+ timerFunc = function timerFunc() {
+ counter = (counter + 1) % 2;
+ textNode.data = String(counter);
+ };
+ } else if (typeof setImmediate !== "undefined") {
+ timerFunc = function timerFunc() {
+ setImmediate(nextTickHandler);
+ };
+ } else {
+ // Fallback to setTimeout.
+ timerFunc = function timerFunc() {
+ setTimeout(nextTickHandler, 0);
+ };
+ }
+
+ return function queueNextTick(cb) {
+ var _resolve = void 0;
+ var args = [].slice.call(arguments, 1);
+ callbacks.push(function () {
+ if (cb) {
+ try {
+ cb.apply(null, args);
+ } catch (e) {
+ console.error(e);
+ }
+ } else if (_resolve) {
+ _resolve.apply(null, args);
+ }
+ });
+ if (!pending) {
+ pending = true;
+ timerFunc();
+ }
+ // $flow-disable-line
+ if (!cb && typeof Promise !== 'undefined') {
+ return new Promise(function (resolve, reject) {
+ _resolve = resolve;
+ });
+ }
+ };
+ })()
+ });
+
+ // 数字相关方法
+ _.each(["random"], function (name) {
+ BI[name] = _apply(name);
+ });
+ _.extend(BI, {
+ getTime: function () {
+ if (_global.performance && _global.performance.now) {
+ return _global.performance.now();
+ }
+ if (_global.performance && _global.performance.webkitNow) {
+ return _global.performance.webkitNow();
+ }
+ if (Date.now) {
+ return Date.now();
+ }
+ return BI.getDate().getTime();
+
+
+ },
+
+ parseInt: function (number) {
+ var radix = 10;
+ if (/^0x/g.test(number)) {
+ radix = 16;
+ }
+ try {
+ return parseInt(number, radix);
+ } catch (e) {
+ throw new Error(number + "parse int error");
+ return NaN;
+ }
+ },
+
+ parseSafeInt: function (value) {
+ var MAX_SAFE_INTEGER = 9007199254740991;
+ return value
+ ? this.clamp(this.parseInt(value), -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER)
+ : (value === 0 ? value : 0);
+ },
+
+ parseFloat: function (number) {
+ try {
+ return parseFloat(number);
+ } catch (e) {
+ throw new Error(number + "parse float error");
+ return NaN;
+ }
+ },
+
+ isNaturalNumber: function (number) {
+ if (/^\d+$/.test(number)) {
+ return true;
+ }
+ return false;
+ },
+
+ isPositiveInteger: function (number) {
+ if (/^\+?[1-9][0-9]*$/.test(number)) {
+ return true;
+ }
+ return false;
+ },
+
+ isNegativeInteger: function (number) {
+ if (/^\-[1-9][0-9]*$/.test(number)) {
+ return true;
+ }
+ return false;
+ },
+
+ isInteger: function (number) {
+ if (/^\-?\d+$/.test(number)) {
+ return true;
+ }
+ return false;
+ },
+
+ isNumeric: function (number) {
+ return !isNaN(parseFloat(number)) && isFinite(number);
+ },
+
+ isFloat: function (number) {
+ if (/^([+-]?)\d*\.\d+$/.test(number)) {
+ return true;
+ }
+ return false;
+ },
+
+ isOdd: function (number) {
+ if (!BI.isInteger(number)) {
+ return false;
+ }
+ return (number & 1) === 1;
+ },
+
+ isEven: function (number) {
+ if (!BI.isInteger(number)) {
+ return false;
+ }
+ return (number & 1) === 0;
+ },
+
+ sum: function (array, iteratee, context) {
+ var sum = 0;
+ BI.each(array, function (i, item) {
+ if (iteratee) {
+ sum += Number(iteratee.apply(context, [i, item]));
+ } else {
+ sum += Number(item);
+ }
+ });
+ return sum;
+ },
+
+ average: function (array, iteratee, context) {
+ var sum = BI.sum(array, iteratee, context);
+ return sum / array.length;
+ }
+ });
+
+ // 字符串相关方法
+ _.extend(BI, {
+ trim: function () {
+ return _.trim.apply(_, arguments);
+ },
+
+ toUpperCase: function (string) {
+ return (string + "").toLocaleUpperCase();
+ },
+
+ toLowerCase: function (string) {
+ return (string + "").toLocaleLowerCase();
+ },
+
+ isEndWithBlank: function (string) {
+ return /(\s|\u00A0)$/.test(string);
+ },
+
+ isLiteral: function (exp) {
+ var literalValueRE = /^\s?(true|false|-?[\d\.]+|'[^']*'|"[^"]*")\s?$/;
+ return literalValueRE.test(exp);
+ },
+
+ stripQuotes: function (str) {
+ var a = str.charCodeAt(0);
+ var b = str.charCodeAt(str.length - 1);
+ return a === b && (a === 0x22 || a === 0x27)
+ ? str.slice(1, -1)
+ : str;
+ },
+
+ // background-color => backgroundColor
+ camelize: function (str) {
+ return str.replace(/-(.)/g, function (_, character) {
+ return character.toUpperCase();
+ });
+ },
+
+ // backgroundColor => background-color
+ hyphenate: function (str) {
+ return str.replace(/([A-Z])/g, "-$1").toLowerCase();
+ },
+
+ isNotEmptyString: function (str) {
+ return BI.isString(str) && !BI.isEmpty(str);
+ },
+
+ isEmptyString: function (str) {
+ return BI.isString(str) && BI.isEmpty(str);
+ },
+
+ /**
+ * 通用加密方法
+ */
+ encrypt: function (type, text, key) {
+ switch (type) {
+ case BI.CRYPT_TYPE.AES:
+ default:
+ return BI.aesEncrypt(text, key);
+ }
+ },
+
+ /**
+ * 通用解密方法
+ * @param type 解密方式
+ * @param text 文本
+ * @param key 种子
+ * @return {*}
+ */
+ decrypt: function (type, text, key) {
+ switch (type) {
+ case BI.CRYPT_TYPE.AES:
+ default:
+ return BI.aesDecrypt(text, key);
+ }
+ },
+
+ /**
+ * 对字符串中的'和\做编码处理
+ * @static
+ * @param {String} string 要做编码处理的字符串
+ * @return {String} 编码后的字符串
+ */
+ escape: function (string) {
+ return string.replace(/('|\\)/g, "\\$1");
+ },
+
+ /**
+ * 让字符串通过指定字符做补齐的函数
+ *
+ * var s = BI.leftPad('123', 5, '0');//s的值为:'00123'
+ *
+ * @static
+ * @param {String} val 原始值
+ * @param {Number} size 总共需要的位数
+ * @param {String} ch 用于补齐的字符
+ * @return {String} 补齐后的字符串
+ */
+ leftPad: function (val, size, ch) {
+ var result = String(val);
+ if (!ch) {
+ ch = " ";
+ }
+ while (result.length < size) {
+ result = ch + result;
+ }
+ return result.toString();
+ },
+
+ /**
+ * 对字符串做替换的函数
+ *
+ * var cls = 'my-class', text = 'Some text';
+ * var res = BI.format('{1}
', cls, text);
+ * //res的值为:'Some text
';
+ *
+ * @static
+ * @param {String} format 要做替换的字符串,替换字符串1,替换字符串2...
+ * @return {String} 做了替换后的字符串
+ */
+ format: function (format) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ return format.replace(/\{(\d+)\}/g, function (m, i) {
+ return args[i];
+ });
+ }
+ });
+
+ // 日期相关方法
+ _.extend(BI, {
+ /**
+ * 是否是闰年
+ * @param year
+ * @returns {boolean}
+ */
+ isLeapYear: function (year) {
+ return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
+ },
+
+ /**
+ * 检测是否在有效期
+ *
+ * @param YY 年
+ * @param MM 月
+ * @param DD 日
+ * @param minDate '1900-01-01'
+ * @param maxDate '2099-12-31'
+ * @returns {Array} 若无效返回无效状态
+ */
+ checkDateVoid: function (YY, MM, DD, minDate, maxDate) {
+ var back = [];
+ YY = YY | 0;
+ MM = MM | 0;
+ DD = DD | 0;
+ minDate = BI.isString(minDate) ? minDate.match(/\d+/g) : minDate;
+ maxDate = BI.isString(maxDate) ? maxDate.match(/\d+/g) : maxDate;
+ if (YY < minDate[0]) {
+ back = ["y"];
+ } else if (YY > maxDate[0]) {
+ back = ["y", 1];
+ } else if (YY >= minDate[0] && YY <= maxDate[0]) {
+ if (YY == minDate[0]) {
+ if (MM < minDate[1]) {
+ back = ["m"];
+ } else if (MM == minDate[1]) {
+ if (DD < minDate[2]) {
+ back = ["d"];
+ }
+ }
+ }
+ if (YY == maxDate[0]) {
+ if (MM > maxDate[1]) {
+ back = ["m", 1];
+ } else if (MM == maxDate[1]) {
+ if (DD > maxDate[2]) {
+ back = ["d", 1];
+ }
+ }
+ }
+ }
+ return back;
+ },
+
+ checkDateLegal: function (str) {
+ var ar = str.match(/\d+/g);
+ var YY = ar[0] | 0, MM = ar[1] | 0, DD = ar[2] | 0;
+ if (ar.length <= 1) {
+ return true;
+ }
+ if (ar.length <= 2) {
+ return MM >= 1 && MM <= 12;
+ }
+ var MD = BI.Date._MD.slice(0);
+ MD[1] = BI.isLeapYear(YY) ? 29 : 28;
+ return MM >= 1 && MM <= 12 && DD <= MD[MM - 1];
+ },
+
+ parseDateTime: function (str, fmt) {
+ var today = BI.getDate();
+ var y = 0;
+ var m = 0;
+ var d = 1;
+ // wei : 对于fmt为‘YYYYMM’或者‘YYYYMMdd’的格式,str的值为类似'201111'的形式,因为年月之间没有分隔符,所以正则表达式分割无效,导致bug7376。
+ var a = str.split(/\W+/);
+ if (fmt.toLowerCase() == "%y%x" || fmt.toLowerCase() == "%y%x%d") {
+ var yearlength = 4;
+ var otherlength = 2;
+ a[0] = str.substring(0, yearlength);
+ a[1] = str.substring(yearlength, yearlength + otherlength);
+ a[2] = str.substring(yearlength + otherlength, yearlength + otherlength * 2);
+ }
+ var b = fmt.match(/%./g);
+ var i = 0, j = 0;
+ var hr = 0;
+ var min = 0;
+ var sec = 0;
+ for (i = 0; i < a.length; ++i) {
+ switch (b[i]) {
+ case "%d":
+ case "%e":
+ d = parseInt(a[i], 10);
+ break;
+
+ case "%X":
+ m = parseInt(a[i], 10) - 1;
+ break;
+ case "%x":
+ m = parseInt(a[i], 10) - 1;
+ break;
+
+ case "%Y":
+ case "%y":
+ y = parseInt(a[i], 10);
+ (y < 100) && (y += (y > 29) ? 1900 : 2000);
+ break;
+
+ case "%b":
+ case "%B":
+ for (j = 0; j < 12; ++j) {
+ if (BI.Date._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) {
+ m = j;
+ break;
+ }
+ }
+ break;
+
+ case "%H":
+ case "%I":
+ case "%k":
+ case "%l":
+ hr = parseInt(a[i], 10);
+ break;
+
+ case "%P":
+ case "%p":
+ if (/pm/i.test(a[i]) && hr < 12) {
+ hr += 12;
+ } else if (/am/i.test(a[i]) && hr >= 12) {
+ hr -= 12;
+ }
+ break;
+ case "%Q":
+ case "%q":
+ m = (parseInt(a[i], 10) - 1) * 3;
+ break;
+ case "%M":
+ min = parseInt(a[i], 10);
+ break;
+ case "%S":
+ sec = parseInt(a[i], 10);
+ break;
+ }
+ }
+ // if (!a[i]) {
+ // continue;
+ // }
+ if (isNaN(y)) {
+ y = today.getFullYear();
+ }
+ if (isNaN(m)) {
+ m = today.getMonth();
+ }
+ if (isNaN(d)) {
+ d = today.getDate();
+ }
+ if (isNaN(hr)) {
+ hr = today.getHours();
+ }
+ if (isNaN(min)) {
+ min = today.getMinutes();
+ }
+ if (isNaN(sec)) {
+ sec = today.getSeconds();
+ }
+ if (y != 0) {
+ return BI.getDate(y, m, d, hr, min, sec);
+ }
+ y = 0;
+ m = -1;
+ d = 0;
+ for (i = 0; i < a.length; ++i) {
+ if (a[i].search(/[a-zA-Z]+/) != -1) {
+ var t = -1;
+ for (j = 0; j < 12; ++j) {
+ if (BI.Date._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) {
+ t = j;
+ break;
+ }
+ }
+ if (t != -1) {
+ if (m != -1) {
+ d = m + 1;
+ }
+ m = t;
+ }
+ } else if (parseInt(a[i], 10) <= 12 && m == -1) {
+ m = a[i] - 1;
+ } else if (parseInt(a[i], 10) > 31 && y == 0) {
+ y = parseInt(a[i], 10);
+ (y < 100) && (y += (y > 29) ? 1900 : 2000);
+ } else if (d == 0) {
+ d = a[i];
+ }
+ }
+ if (y == 0) {
+ y = today.getFullYear();
+ }
+ if (m === -1) {
+ m = today.getMonth();
+ }
+ if (m != -1 && d != 0) {
+ return BI.getDate(y, m, d, hr, min, sec);
+ }
+ return today;
+ },
+
+ getDate: function () {
+ var length = arguments.length;
+ var args = arguments;
+ var dt;
+ switch (length) {
+ // new Date()
+ case 0:
+ dt = new Date();
+ break;
+ // new Date(long)
+ case 1:
+ dt = new Date(args[0]);
+ break;
+ // new Date(year, month)
+ case 2:
+ dt = new Date(args[0], args[1]);
+ break;
+ // new Date(year, month, day)
+ case 3:
+ dt = new Date(args[0], args[1], args[2]);
+ break;
+ // new Date(year, month, day, hour)
+ case 4:
+ dt = new Date(args[0], args[1], args[2], args[3]);
+ break;
+ // new Date(year, month, day, hour, minute)
+ case 5:
+ dt = new Date(args[0], args[1], args[2], args[3], args[4]);
+ break;
+ // new Date(year, month, day, hour, minute, second)
+ case 6:
+ dt = new Date(args[0], args[1], args[2], args[3], args[4], args[5]);
+ break;
+ // new Date(year, month, day, hour, minute, second, millisecond)
+ case 7:
+ dt = new Date(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
+ break;
+ default:
+ dt = new Date();
+ break;
+ }
+ if (BI.isNotNull(BI.timeZone) && (arguments.length === 0 || (arguments.length === 1 && BI.isNumber(arguments[0])))) {
+ var localTime = dt.getTime();
+ // BI-33791 1901年以前的东8区标准是GMT+0805, 统一无论是什么时间,都以整的0800这样的为基准
+ var localOffset = dt.getTimezoneOffset() * 60000; // 获得当地时间偏移的毫秒数
+ var utc = localTime + localOffset; // utc即GMT时间标准时区
+ return new Date(utc + BI.timeZone);// + Pool.timeZone.offset);
+ }
+ return dt;
+
+ },
+
+ getTime: function () {
+ var length = arguments.length;
+ var args = arguments;
+ var dt;
+ switch (length) {
+ // new Date()
+ case 0:
+ dt = new Date();
+ break;
+ // new Date(long)
+ case 1:
+ dt = new Date(args[0]);
+ break;
+ // new Date(year, month)
+ case 2:
+ dt = new Date(args[0], args[1]);
+ break;
+ // new Date(year, month, day)
+ case 3:
+ dt = new Date(args[0], args[1], args[2]);
+ break;
+ // new Date(year, month, day, hour)
+ case 4:
+ dt = new Date(args[0], args[1], args[2], args[3]);
+ break;
+ // new Date(year, month, day, hour, minute)
+ case 5:
+ dt = new Date(args[0], args[1], args[2], args[3], args[4]);
+ break;
+ // new Date(year, month, day, hour, minute, second)
+ case 6:
+ dt = new Date(args[0], args[1], args[2], args[3], args[4], args[5]);
+ break;
+ // new Date(year, month, day, hour, minute, second, millisecond)
+ case 7:
+ dt = new Date(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
+ break;
+ default:
+ dt = new Date();
+ break;
+ }
+ if (BI.isNotNull(BI.timeZone)) {
+ // BI-33791 1901年以前的东8区标准是GMT+0805, 统一无论是什么时间,都以整的0800这样的为基准
+ return dt.getTime() - BI.timeZone - new Date().getTimezoneOffset() * 60000;
+ }
+ return dt.getTime();
+
+ }
+ });
+})();
diff --git a/src/main/resources/com/fr/fineui/core/3.ob.js b/src/main/resources/com/fr/fineui/core/3.ob.js
new file mode 100644
index 0000000..9eab5d3
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/3.ob.js
@@ -0,0 +1,200 @@
+!(function () {
+ function extend () {
+ var target = arguments[0] || {}, length = arguments.length, i = 1, options, name, src, copy;
+ for (; i < length; i++) {
+ // Only deal with non-null/undefined values
+ if ((options = arguments[i]) != null) {
+ // Extend the base object
+ for (name in options) {
+ src = target[name];
+ copy = options[name];
+
+ // Prevent never-ending loop
+ if (target === copy) {
+ continue;
+ }
+
+ if (copy !== undefined) {
+ target[name] = copy;
+ }
+ }
+ }
+ }
+ return target;
+ }
+
+ /**
+ * 客户端观察者,主要处理事件的添加、删除、执行等
+ * @class BI.OB
+ * @abstract
+ */
+ var OB = function (config) {
+ this._constructor(config);
+ };
+ _.extend(OB.prototype, {
+ props: {},
+ init: null,
+ destroyed: null,
+
+ _constructor: function (config) {
+ this._initProps(config);
+ this._init();
+ this._initRef();
+ },
+
+ _defaultConfig: function (config) {
+ return {};
+ },
+
+ _initProps: function (config) {
+ var props = this.props;
+ if (BI.isFunction(this.props)) {
+ props = this.props(config);
+ }
+ this.options = extend(this._defaultConfig(config), props, config);
+ },
+
+ _init: function () {
+ this._initListeners();
+ this.init && this.init();
+ },
+
+ _initListeners: function () {
+ var self = this;
+ if (this.options.listeners != null) {
+ _.each(this.options.listeners, function (lis) {
+ (lis.target ? lis.target : self)[lis.once ? "once" : "on"]
+ (lis.eventName, _.bind(lis.action, self));
+ });
+ delete this.options.listeners;
+ }
+ },
+
+ // 获得一个当前对象的引用
+ _initRef: function () {
+ if (this.options.__ref) {
+ this.options.__ref.call(this, this);
+ }
+ if (this.options.ref) {
+ this.options.ref.call(this, this);
+ }
+ },
+
+ //释放当前对象
+ _purgeRef: function () {
+ if (this.options.__ref) {
+ this.options.__ref.call(null);
+ this.options.__ref = null;
+ }
+ if (this.options.ref) {
+ this.options.ref.call(null);
+ this.options.ref = null;
+ }
+ },
+
+ _getEvents: function () {
+ if (!_.isObject(this.events)) {
+ this.events = {};
+ }
+ return this.events;
+ },
+
+ /**
+ * 给观察者绑定一个事件
+ * @param {String} eventName 事件的名字
+ * @param {Function} fn 事件对应的执行函数
+ */
+ on: function (eventName, fn) {
+ var self = this;
+ eventName = eventName.toLowerCase();
+ var fns = this._getEvents()[eventName];
+ if (!_.isArray(fns)) {
+ fns = [];
+ this._getEvents()[eventName] = fns;
+ }
+ fns.push(fn);
+
+ return function () {
+ self.un(eventName, fn);
+ };
+ },
+
+ /**
+ * 给观察者绑定一个只执行一次的事件
+ * @param {String} eventName 事件的名字
+ * @param {Function} fn 事件对应的执行函数
+ */
+ once: function (eventName, fn) {
+ var proxy = function () {
+ fn.apply(this, arguments);
+ this.un(eventName, proxy);
+ };
+ this.on(eventName, proxy);
+ },
+ /**
+ * 解除观察者绑定的指定事件
+ * @param {String} eventName 要解除绑定事件的名字
+ * @param {Function} fn 事件对应的执行函数,该参数是可选的,没有该参数时,将解除绑定所有同名字的事件
+ */
+ un: function (eventName, fn) {
+ eventName = eventName.toLowerCase();
+
+ /* alex:如果fn是null,就是把eventName上面所有方法都un掉*/
+ if (fn == null) {
+ delete this._getEvents()[eventName];
+ } else {
+ var fns = this._getEvents()[eventName];
+ if (_.isArray(fns)) {
+ var newFns = [];
+ _.each(fns, function (ifn) {
+ if (ifn != fn) {
+ newFns.push(ifn);
+ }
+ });
+ this._getEvents()[eventName] = newFns;
+ }
+ }
+ },
+ /**
+ * 清除观察者的所有事件绑定
+ */
+ purgeListeners: function () {
+ /* alex:清空events*/
+ this.events = {};
+ },
+ /**
+ * 触发绑定过的事件
+ *
+ * @param {String} eventName 要触发的事件的名字
+ * @returns {Boolean} 如果事件函数返回false,则返回false并中断其他同名事件的执行,否则执行所有的同名事件并返回true
+ */
+ fireEvent: function () {
+ var eventName = arguments[0].toLowerCase();
+ var fns = this._getEvents()[eventName];
+ if (BI.isArray(fns)) {
+ if (BI.isArguments(arguments[1])) {
+ for (var i = 0; i < fns.length; i++) {
+ if (fns[i].apply(this, arguments[1]) === false) {
+ return false;
+ }
+ }
+ } else {
+ var args = Array.prototype.slice.call(arguments, 1);
+ for (var i = 0; i < fns.length; i++) {
+ if (fns[i].apply(this, args) === false) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ },
+
+ destroy: function () {
+ this.destroyed && this.destroyed();
+ this._purgeRef();
+ this.purgeListeners();
+ }
+ });
+ BI.OB = BI.OB || OB;
+})();
diff --git a/src/main/resources/com/fr/fineui/core/4.widget.js b/src/main/resources/com/fr/fineui/core/4.widget.js
new file mode 100644
index 0000000..c3d1e0b
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/4.widget.js
@@ -0,0 +1,873 @@
+/**
+ * Widget超类
+ * @class BI.Widget
+ * @extends BI.OB
+ *
+ * @cfg {JSON} options 配置属性
+ */
+
+!(function () {
+ function callLifeHook (self, life) {
+ var hooks = [], hook;
+ hook = self[life];
+ if (hook) {
+ hooks = hooks.concat(BI.isArray(hook) ? hook : [hook]);
+ }
+ hook = self.options[life];
+ if (hook) {
+ hooks = hooks.concat(BI.isArray(hook) ? hook : [hook]);
+ }
+ BI.each(hooks, function (i, hook) {
+ hook.call(self);
+ });
+ }
+
+ BI.Widget = BI.Widget || BI.inherit(BI.OB, {
+ _defaultConfig: function () {
+ return BI.extend(BI.Widget.superclass._defaultConfig.apply(this), {
+ root: false,
+ tagName: "div",
+ attributes: null,
+ data: null,
+ key: null,
+
+ tag: null,
+ disabled: false,
+ invisible: false,
+ invalid: false,
+ baseCls: "",
+ extraCls: "",
+ cls: "",
+ css: null
+
+ // vdom: false
+ });
+ },
+
+ _constructor: function () {
+
+ },
+
+ // 覆盖父类的_constructor方法,widget不走ob的生命周期
+ _constructed: function () {
+ if (this.setup) {
+ pushTarget(this);
+ var delegate = this.setup(this.options);
+ if (BI.isPlainObject(delegate)) {
+ // 如果setup返回一个json,即对外暴露的方法
+ BI.extend(this, delegate);
+ } else {
+ this.render = delegate;
+ }
+ popTarget();
+ }
+ },
+
+ _lazyConstructor: function () {
+ if (!this.__constructed) {
+ this.__constructed = true;
+ this._init();
+ this._initRef();
+ }
+ },
+
+ // 生命周期函数
+ beforeInit: null,
+
+ beforeRender: null,
+
+ beforeCreate: null,
+
+ created: null,
+
+ render: null,
+
+ beforeMount: null,
+
+ mounted: null,
+
+ shouldUpdate: null,
+
+ update: null,
+
+ beforeUpdate: null,
+
+ updated: null,
+
+ beforeDestroy: null,
+
+ destroyed: null,
+
+ _init: function () {
+ BI.Widget.superclass._init.apply(this, arguments);
+ this._initElementWidth();
+ this._initElementHeight();
+ this._initVisual();
+ this._initState();
+ this._initRender();
+ },
+
+ _initRender: function () {
+ var self = this;
+ var initCallbackCalled = false;
+ var renderCallbackCalled = false;
+
+ function init () {
+ // 加个保险
+ if (initCallbackCalled === true) {
+ _global.console && console.error("组件: 请检查beforeInit内部的写法,callback只能执行一次");
+ return;
+ }
+ initCallbackCalled = true;
+
+ function render () {
+ // 加个保险
+ if (renderCallbackCalled === true) {
+ _global.console && console.error("组件: 请检查beforeRender内部的写法,callback只能执行一次");
+ return;
+ }
+ renderCallbackCalled = true;
+ self._render();
+ self.__afterRender();
+ }
+
+ if (self.options.beforeRender || self.beforeRender) {
+ self.__async = true;
+ (self.options.beforeRender || self.beforeRender).call(self, render);
+ } else {
+ self._render();
+ self.__afterRender();
+ }
+ }
+
+ if (this.options.beforeInit || this.beforeInit) {
+ this.__asking = true;
+ (this.options.beforeInit || this.beforeInit).call(this, init);
+ } else {
+ init();
+ }
+ },
+
+ __afterRender: function () {
+ pushTarget(this);
+ var async = this.__async;
+ this.__async = false;
+ if (async && this._isMounted) {
+ callLifeHook(this, "beforeMount");
+ this._mount();
+ callLifeHook(this, "mounted");
+ this.fireEvent(BI.Events.MOUNT);
+ } else {
+ this._mount();
+ }
+ popTarget();
+ },
+
+ _render: function () {
+ this.__asking = false;
+ pushTarget(this);
+ callLifeHook(this, "beforeCreate");
+ this._initElement();
+ this._initEffects();
+ callLifeHook(this, "created");
+ popTarget();
+ },
+
+ _initCurrent: function () {
+ var self = this, o = this.options;
+ if (o._baseCls || o.baseCls || o.extraCls) {
+ this.element.addClass((o._baseCls || "") + " " + (o.baseCls || "") + " " + (o.extraCls || ""));
+ }
+ if (o.cls) {
+ if (BI.isFunction(o.cls)) {
+ var cls = this.__watch(o.cls, function (context, newValue) {
+ self.element.removeClass(cls).addClass(cls = newValue);
+ });
+ this.element.addClass(cls);
+ } else {
+ this.element.addClass(o.cls);
+ }
+ }
+ // if (o.key != null) {
+ // this.element.attr("key", o.key);
+ // }
+ if (o.attributes) {
+ this.element.attr(o.attributes);
+ }
+ if (o.data) {
+ this.element.data(o.data);
+ }
+ if (o.css) {
+ if (BI.isFunction(o.css)) {
+ var css = this.__watch(o.css, function (context, newValue) {
+ for (var k in css) {
+ if (!newValue[k]) {
+ newValue[k] = "";
+ }
+ }
+ self.element.css(css = newValue);
+ }, {
+ deep: true
+ });
+ this.element.css(css);
+ } else {
+ this.element.css(o.css);
+ }
+ }
+ },
+
+ __watch: function (getter, handler, options) {
+ var self = this;
+ if (_global.Fix) {
+ this._watchers = this._watchers || [];
+ var watcher = new Fix.Watcher(null, function () {
+ return getter.call(self, self);
+ }, (handler && function (v) {
+ handler.call(self, self, v);
+ }) || BI.emptyFn, options);
+ this._watchers.push(watcher);
+ return watcher.value;
+ } else {
+ return getter();
+ }
+ },
+
+ /**
+ * 初始化根节点
+ * @private
+ */
+ _initRoot: function () {
+ var o = this.options;
+ this.widgetName = o.widgetName || BI.uniqueId("widget");
+ this._isRoot = o.root;
+ this._children = {};
+ if (BI.isWidget(o.element)) {
+ this.element = this.options.element.element;
+ this._parent = o.element;
+ this._parent.addWidget(this.widgetName, this);
+ } else if (o.element) {
+ this.element = BI.Widget._renderEngine.createElement(this);
+ this._isRoot = true;
+ } else {
+ this.element = BI.Widget._renderEngine.createElement(this);
+ }
+ this.element._isWidget = true;
+ // var widgets = this.element.data("__widgets") || [];
+ // widgets.push(this);
+ // this.element.data("__widgets", widgets);
+ this._initCurrent();
+ },
+
+ _initElementWidth: function () {
+ var o = this.options;
+ if (BI.isWidthOrHeight(o.width)) {
+ this.element.css("width", BI.isNumber(o.width) ? o.width / BI.pixRatio + BI.pixUnit : o.width);
+ }
+ },
+
+ _initElementHeight: function () {
+ var o = this.options;
+ if (BI.isWidthOrHeight(o.height)) {
+ this.element.css("height", BI.isNumber(o.height) ? o.height / BI.pixRatio + BI.pixUnit : o.height);
+ }
+ },
+
+ _initVisual: function () {
+ var o = this.options;
+ if (o.invisible) {
+ // 用display属性做显示和隐藏,否则jquery会在显示时将display设为block会覆盖掉display:flex属性
+ this.element.css("display", "none");
+ }
+ },
+
+ _initEffects: function () {
+ var self = this, o = this.options;
+ if (o.disabled || o.invalid) {
+ if (this.options.disabled) {
+ this.setEnable(false);
+ }
+ if (this.options.invalid) {
+ this.setValid(false);
+ }
+ }
+ if (o.effect) {
+ if (BI.isArray(o.effect)) {
+ if (BI.isArray(o.effect[0])) {
+ BI.each(o.effect, function (i, effect) {
+ self.__watch(effect[0], effect[1], {
+ deep: true
+ });
+ });
+ } else {
+ self.__watch(o.effect[0], o.effect[1], {
+ deep: true
+ });
+ }
+ } else {
+ this.__watch(o.effect);
+ }
+ }
+ },
+
+ _initState: function () {
+ this._isMounted = false;
+ },
+
+ _initElement: function () {
+ var self = this;
+ this.__isMounting = true;
+ var render = BI.isFunction(this.options.render) ? this.options.render : this.render;
+ var els = render && render.call(this);
+ els = BI.Plugin.getRender(this.options.type, els);
+ if (BI.isPlainObject(els)) {
+ els = [els];
+ }
+ if (BI.isArray(els)) {
+ BI.each(els, function (i, el) {
+ if (el) {
+ BI._lazyCreateWidget(el, {
+ element: self
+ });
+ }
+ });
+ }
+ },
+
+ _setParent: function (parent) {
+ this._parent = parent;
+ },
+
+ /**
+ *
+ * @param force 是否强制挂载子节点
+ * @param deep 子节点是否也是按照当前force处理
+ * @param lifeHook 生命周期钩子触不触发,默认触发
+ * @param predicate 递归每个widget的回调
+ * @param layer 组件层级
+ * @returns {boolean}
+ * @private
+ */
+ _mount: function (force, deep, lifeHook, predicate, layer) {
+ var self = this;
+ if (!force && (this._isMounted || !this.isVisible() || this.__asking === true || !(this._isRoot === true || (this._parent && this._parent._isMounted === true)))) {
+ return false;
+ }
+ layer = layer || 0;
+ lifeHook !== false && !this.__async && callLifeHook(this, "beforeMount");
+ this._isMounted = true;
+ this.__isMounting = false;
+ for (var key in this._children) {
+ var child = this._children[key];
+ !self.isEnabled() && child._setEnable(false);
+ !self.isValid() && child._setValid(false);
+ child._mount && child._mount(deep ? force : false, deep, lifeHook, predicate, layer + 1);
+ }
+ this._mountChildren && this._mountChildren();
+ if (layer === 0) {
+ // mounted里面会执行scrollTo之类的方法,如果放宏任务里会闪
+ // setTimeout(function () {
+ self.__afterMount(lifeHook, predicate);
+ // }, 0);
+ }
+ return true;
+ },
+
+ __afterMount: function (lifeHook, predicate) {
+ if (this._isMounted) {
+ for (var key in this._children) {
+ var child = this._children[key];
+ child.__afterMount && child.__afterMount(lifeHook, predicate);
+ }
+ if (lifeHook !== false && !this.__async) {
+ callLifeHook(this, "mounted");
+ this.fireEvent(BI.Events.MOUNT);
+ }
+ predicate && predicate(this);
+ }
+ },
+
+ _mountChildren: null,
+
+ _update: function (nextProps, shouldUpdate) {
+ callLifeHook(this, "beforeUpdate");
+ if (shouldUpdate) {
+ var res = this.update && this.update(nextProps, shouldUpdate);
+ }
+ callLifeHook(this, "updated");
+ return res;
+ },
+
+ isMounted: function () {
+ return this._isMounted;
+ },
+
+ setWidth: function (w) {
+ this.options.width = w;
+ this._initElementWidth();
+ },
+
+ setHeight: function (h) {
+ this.options.height = h;
+ this._initElementHeight();
+ },
+
+ _setEnable: function (enable) {
+ if (enable === true) {
+ this.options.disabled = false;
+ } else if (enable === false) {
+ this.options.disabled = true;
+ }
+ // 递归将所有子组件使能
+ BI.each(this._children, function (i, child) {
+ !child._manualSetEnable && child._setEnable && child._setEnable(enable);
+ });
+ },
+
+ _setValid: function (valid) {
+ if (valid === true) {
+ this.options.invalid = false;
+ } else if (valid === false) {
+ this.options.invalid = true;
+ }
+ // 递归将所有子组件使有效
+ BI.each(this._children, function (i, child) {
+ !child._manualSetValid && child._setValid && child._setValid(valid);
+ });
+ },
+
+ _setVisible: function (visible) {
+ if (visible === true) {
+ this.options.invisible = false;
+ } else if (visible === false) {
+ this.options.invisible = true;
+ }
+ },
+
+ setEnable: function (enable) {
+ this._manualSetEnable = true;
+ this._setEnable(enable);
+ if (enable === true) {
+ this.element.removeClass("base-disabled disabled");
+ } else if (enable === false) {
+ this.element.addClass("base-disabled disabled");
+ }
+ },
+
+ setVisible: function (visible) {
+ this._setVisible(visible);
+ if (visible === true) {
+ // 用this.element.show()会把display属性改成block
+ this.element.css("display", "");
+ this._mount();
+ } else if (visible === false) {
+ this.element.css("display", "none");
+ }
+ this.fireEvent(BI.Events.VIEW, visible);
+ },
+
+ setValid: function (valid) {
+ this._manualSetValid = true;
+ this._setValid(valid);
+ if (valid === true) {
+ this.element.removeClass("base-invalid invalid");
+ } else if (valid === false) {
+ this.element.addClass("base-invalid invalid");
+ }
+ },
+
+ doBehavior: function () {
+ var args = arguments;
+ // 递归将所有子组件使有效
+ BI.each(this._children, function (i, child) {
+ child.doBehavior && child.doBehavior.apply(child, args);
+ });
+ },
+
+ getWidth: function () {
+ return this.options.width;
+ },
+
+ getHeight: function () {
+ return this.options.height;
+ },
+
+ isValid: function () {
+ return !this.options.invalid;
+ },
+
+ addWidget: function (name, widget) {
+ var self = this;
+ if (name instanceof BI.Widget) {
+ widget = name;
+ name = widget.getName();
+ }
+ if (BI.isKey(name)) {
+ name = name + "";
+ }
+ name = name || widget.getName() || BI.uniqueId("widget");
+ if (this._children[name]) {
+ throw new Error("组件:组件名已存在,不能进行添加");
+ }
+ widget._setParent && widget._setParent(this);
+ widget.on(BI.Events.DESTROY, function () {
+ BI.remove(self._children, this);
+ });
+ return (this._children[name] = widget);
+ },
+
+ getWidgetByName: function (name) {
+ if (!BI.isKey(name) || name === this.getName()) {
+ return this;
+ }
+ name = name + "";
+ var widget = void 0, other = {};
+ BI.any(this._children, function (i, wi) {
+ if (i === name) {
+ widget = wi;
+ return true;
+ }
+ other[i] = wi;
+ });
+ if (!widget) {
+ BI.any(other, function (i, wi) {
+ return (widget = wi.getWidgetByName(i));
+ });
+ }
+ return widget;
+ },
+
+ removeWidget: function (nameOrWidget) {
+ if (BI.isWidget(nameOrWidget)) {
+ BI.remove(this._children, nameOrWidget);
+ } else {
+ delete this._children[nameOrWidget];
+ }
+ },
+
+ hasWidget: function (name) {
+ return this._children[name] != null;
+ },
+
+ getName: function () {
+ return this.widgetName;
+ },
+
+ setTag: function (tag) {
+ this.options.tag = tag;
+ },
+
+ getTag: function () {
+ return this.options.tag;
+ },
+
+ attr: function (key, value) {
+ var self = this;
+ if (BI.isPlainObject(key)) {
+ BI.each(key, function (k, v) {
+ self.attr(k, v);
+ });
+ return;
+ }
+ if (BI.isNotNull(value)) {
+ return this.options[key] = value;
+ }
+ return this.options[key];
+ },
+
+ css: function (name, value) {
+ return this.element.css(name, value);
+ },
+
+ getText: function () {
+
+ },
+
+ setText: function (text) {
+
+ },
+
+ getValue: function () {
+
+ },
+
+ setValue: function (value) {
+
+ },
+
+ isEnabled: function () {
+ return !this.options.disabled;
+ },
+
+ isVisible: function () {
+ return !this.options.invisible;
+ },
+
+ disable: function () {
+ this.setEnable(false);
+ },
+
+ enable: function () {
+ this.setEnable(true);
+ },
+
+ valid: function () {
+ this.setValid(true);
+ },
+
+ invalid: function () {
+ this.setValid(false);
+ },
+
+ invisible: function () {
+ this.setVisible(false);
+ },
+
+ visible: function () {
+ this.setVisible(true);
+ },
+
+ __d: function () {
+ callLifeHook(this, "beforeDestroy");
+ this.beforeDestroy = null;
+ BI.each(this._children, function (i, widget) {
+ widget && widget._unMount && widget._unMount();
+ });
+ this._children = {};
+ this._parent = null;
+ this._isMounted = false;
+ callLifeHook(this, "destroyed");
+ this.destroyed = null;
+ },
+
+ _unMount: function () {
+ this.__d();
+ this.fireEvent(BI.Events.UNMOUNT);
+ this.purgeListeners();
+ },
+
+ _empty: function () {
+ BI.each(this._children, function (i, widget) {
+ widget && widget._unMount && widget._unMount();
+ });
+ this._children = {};
+ this.element.empty();
+ },
+
+ isolate: function () {
+ if (this._parent) {
+ this._parent.removeWidget(this);
+ }
+ BI.DOM.hang([this]);
+ },
+
+ empty: function () {
+ this._empty();
+ },
+
+ // 默认的reset方法就是干掉重来
+ reset: function () {
+ // 还在异步状态的不需要执行reset
+ if (this.__async === true || this.__asking === true) {
+ return;
+ }
+ // if (this.options.vdom) {
+ // var vnode = this._renderVNode();
+ // BI.patchVNode(this.vnode, vnode);
+ // this.vnode = vnode;
+ // return;
+ // }
+ // this._isMounted = false;
+ // this.purgeListeners();
+ this._empty();
+ this.element.unbind();
+ this._initCurrent();
+ this._init();
+ this._mount();
+ // this._initRef();
+ },
+
+ _destroy: function () {
+ this.__d();
+ this.element.destroy();
+ this.purgeListeners();
+ },
+
+ destroy: function () {
+ this.__d();
+ this.element.destroy();
+ this.fireEvent(BI.Events.UNMOUNT);
+ this.fireEvent(BI.Events.DESTROY);
+ this._purgeRef();
+ this.purgeListeners();
+ }
+ });
+ var context = null, current = null;
+ var contextStack = [], currentStack = [];
+
+ BI.Widget.pushContext = function (_context) {
+ if (context) contextStack.push(context);
+ BI.Widget.context = context = _context;
+ };
+
+ BI.Widget.popContext = function () {
+ BI.Widget.context = context = contextStack.pop();
+ };
+
+ function pushTarget (_current) {
+ if (current) currentStack.push(current);
+ BI.Widget.current = current = _current;
+ }
+
+ function popTarget () {
+ BI.Widget.current = current = currentStack.pop();
+ }
+
+ BI.useStore = function (_store) {
+ if (current && current.store) {
+ return current.store;
+ }
+ if (current && current.$storeDelegate) {
+ return current.$storeDelegate;
+ }
+ if (current) {
+ var currentStore = current._store;
+ var delegate = {}, origin;
+ if (_global.Proxy) {
+ var proxy = new Proxy(delegate, {
+ get: function (target, key) {
+ return Reflect.get(origin, key);
+ },
+ set: function (target, key, value) {
+ return Reflect.set(origin, key, value);
+ }
+ });
+ current._store = function () {
+ origin = (_store || currentStore).apply(this, arguments);
+ delegate.$delegate = origin;
+ return origin;
+ };
+ return current.$storeDelegate = proxy;
+ }
+ current._store = function () {
+ var st = (_store || currentStore).apply(this, arguments);
+ BI.extend(delegate, st);
+ return st;
+ };
+ return current.$storeDelegate = delegate;
+ }
+ };
+
+ BI.watch = function (watch, handler) {
+ if (BI.Widget.current) {
+ BI.Widget.current.$watchDelayCallbacks || (BI.Widget.current.$watchDelayCallbacks = []);
+ BI.Widget.current.$watchDelayCallbacks.push([watch, handler]);
+ }
+ };
+
+ BI.onBeforeMount = function (beforeMount) {
+ if (current) {
+ if (current.__isMounting) {
+ beforeMount();
+ return;
+ }
+ if (!current.beforeMount) {
+ current.beforeMount = [];
+ } else if (!BI.isArray(current.beforeMount)) {
+ current.beforeMount = [current.beforeMount];
+ }
+ current.beforeMount.push(beforeMount);
+ }
+ };
+ BI.onMounted = function (mounted) {
+ if (current) {
+ if (current._isMounted && !this.__async) {
+ mounted();
+ return;
+ }
+ if (!current.mounted) {
+ current.mounted = [];
+ } else if (!BI.isArray(current.mounted)) {
+ current.mounted = [current.mounted];
+ }
+ current.mounted.push(mounted);
+ }
+ };
+ BI.onBeforeUnmount = function (beforeDestroy) {
+ if (current) {
+ if (!current.beforeDestroy) {
+ current.beforeDestroy = [];
+ } else if (!BI.isArray(current.beforeDestroy)) {
+ current.beforeDestroy = [current.beforeDestroy];
+ }
+ current.beforeDestroy.push(beforeDestroy);
+ }
+ };
+ BI.onUnmounted = function (destroyed) {
+ if (current) {
+ if (!current.destroyed) {
+ current.destroyed = [];
+ } else if (!BI.isArray(current.destroyed)) {
+ current.destroyed = [current.destroyed];
+ }
+ current.destroyed.push(destroyed);
+ }
+ };
+
+ BI.Widget.registerRenderEngine = function (engine) {
+ BI.Widget._renderEngine = engine;
+ };
+ BI.Widget.registerRenderEngine({
+ createElement: function (widget) {
+ if (BI.isWidget(widget)) {
+ var o = widget.options;
+ if (o.element) {
+ return BI.$(o.element);
+ }
+ if (o.tagName) {
+ return BI.$(document.createElement(o.tagName));
+ }
+ return BI.$(document.createDocumentFragment());
+ }
+ return BI.$(widget);
+ },
+ createFragment: function () {
+ return document.createDocumentFragment();
+ }
+ });
+
+ BI.mount = function (widget, container, predicate, hydrate) {
+ if (hydrate === true) {
+ // 将widget的element元素都挂载好,并建立相互关系
+ widget.element.data("__widgets", [widget]);
+ var res = widget._mount(true, false, false, function (w) {
+ BI.each(w._children, function (i, child) {
+ var ws = child.element.data("__widgets");
+ if (!ws) {
+ ws = [];
+ }
+ ws.push(child);
+ child.element.data("__widgets", ws);
+ });
+ predicate && predicate.apply(this, arguments);
+ });
+ // 将新的dom树属性(事件等)patch到已存在的dom上
+ var c = BI.Widget._renderEngine.createElement;
+ BI.DOM.patchProps(widget.element, c(c(container).children()[0]));
+
+ var triggerLifeHook = function (w) {
+ w.beforeMount && w.beforeMount();
+ w.mounted && w.mounted();
+ BI.each(w._children, function (i, child) {
+ triggerLifeHook(child);
+ });
+ };
+ // 最后触发组件树生命周期函数
+ triggerLifeHook(widget);
+ return res;
+ }
+ if (container) {
+ BI.Widget._renderEngine.createElement(container).append(widget.element);
+ }
+ return widget._mount(true, false, false, predicate);
+ };
+})();
diff --git a/src/main/resources/com/fr/fineui/core/5.shortcut.js b/src/main/resources/com/fr/fineui/core/5.shortcut.js
new file mode 100644
index 0000000..95e4882
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/5.shortcut.js
@@ -0,0 +1,98 @@
+(function () {
+ var kv = {};
+ BI.shortcut = BI.component = BI.shortcut || function (xtype, cls) {
+ if (kv[xtype] != null) {
+ _global.console && console.error("组件: [" + xtype + "] 已经注册过了");
+ }
+ if (cls) {
+ cls["xtype"] = xtype;
+ }
+ kv[xtype] = cls;
+ };
+
+ // 根据配置属性生成widget
+ var createWidget = function (config, context, lazy) {
+ var cls = kv[config.type];
+
+ if (!cls) {
+ throw new Error("组件: [" + config.type + "] 未定义");
+ }
+ var pushed = false;
+ var widget = new cls();
+ widget._context = BI.Widget.context || context;
+ if (!BI.Widget.context && context) {
+ pushed = true;
+ BI.Widget.pushContext(context);
+ }
+ widget._initProps(config);
+ widget._initRoot();
+ widget._constructed();
+ // if (!lazy || config.element || config.root) {
+ widget._lazyConstructor();
+ // }
+ pushed && BI.Widget.popContext();
+ return widget;
+ };
+
+ BI.createWidget = BI.createWidget || function (item, options, context, lazy) {
+ item || (item = {});
+ if (BI.isWidget(options)) {
+ context = options;
+ options = {};
+ } else {
+ options || (options = {});
+ }
+
+ var el, w;
+ if (item.type || options.type) {
+ el = BI.extend({}, options, item);
+ } else if (item.el && (item.el.type || options.type)) {
+ el = BI.extend({}, options, item.el);
+ }
+
+ if (el) {
+ BI.runConfigFunction(el.type);
+ }
+
+ // 先把准备环境准备好
+ BI.init();
+
+ if (BI.isEmpty(item) && BI.isEmpty(options)) {
+ return BI.createWidget({
+ type: "bi.layout"
+ });
+ }
+ if (BI.isWidget(item)) {
+ return item;
+ }
+ if (el) {
+ w = BI.Plugin.getWidget(el.type, el);
+ if (w.type === el.type) {
+ if (BI.Plugin.hasObject(el.type)) {
+ w.listeners = (w.listeners || []).concat([{
+ eventName: BI.Events.MOUNT,
+ action: function () {
+ BI.Plugin.getObject(el.type, this);
+ }
+ }]);
+ }
+ return createWidget(w, context, lazy);
+ }
+ return BI.createWidget(w, options, context, lazy);
+ }
+ if (BI.isWidget(item.el)) {
+ return item.el;
+ }
+ throw new Error("组件:无法根据item创建组件", item);
+ };
+
+ BI._lazyCreateWidget = BI._lazyCreateWidget || function (item, options, context) {
+ return BI.createWidget(item, options, context, true);
+ };
+
+ BI.createElement = BI.createElement || function () {
+ var widget = BI.createWidget.apply(this, arguments);
+ return widget.element;
+ };
+
+})();
diff --git a/src/main/resources/com/fr/fineui/core/6.inject.js b/src/main/resources/com/fr/fineui/core/6.inject.js
new file mode 100644
index 0000000..b60093e
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/6.inject.js
@@ -0,0 +1,447 @@
+(function () {
+ var moduleInjection = {}, moduleInjectionMap = {
+ components: {},
+ constants: {},
+ stores: {},
+ services: {},
+ models: {},
+ providers: {}
+ };
+ BI.module = BI.module || function (xtype, cls) {
+ if (moduleInjection[xtype] != null) {
+ _global.console && console.error("module: [" + xtype + "] 已经注册过了");
+ }
+ if (BI.isFunction(cls)) {
+ cls = cls();
+ }
+ for (var k in moduleInjectionMap) {
+ if (cls[k]) {
+ for (var key in cls[k]) {
+ if (!moduleInjectionMap[k]) {
+ continue;
+ }
+ if (!moduleInjectionMap[k][key]) {
+ moduleInjectionMap[k][key] = [];
+ }
+ moduleInjectionMap[k][key].push({
+ version: cls[k][key],
+ moduleId: xtype
+ });
+ }
+ }
+ }
+ moduleInjection[xtype] = cls;
+ };
+
+ var constantInjection = {};
+ BI.constant = BI.constant || function (xtype, cls) {
+ if (constantInjection[xtype] != null) {
+ _global.console && console.error("constant: [" + xtype + "]已经注册过了");
+ }
+ constantInjection[xtype] = cls;
+ };
+
+ var modelInjection = {};
+ BI.model = BI.model || function (xtype, cls) {
+ if (modelInjection[xtype] != null) {
+ _global.console && console.error("model: [" + xtype + "] 已经注册过了");
+ }
+ modelInjection[xtype] = cls;
+ };
+
+ var storeInjection = {};
+ BI.store = BI.store || function (xtype, cls) {
+ if (storeInjection[xtype] != null) {
+ _global.console && console.error("store: [" + xtype + "] 已经注册过了");
+ }
+ storeInjection[xtype] = cls;
+ };
+
+ var serviceInjection = {};
+ BI.service = BI.service || function (xtype, cls) {
+ if (serviceInjection[xtype] != null) {
+ _global.console && console.error("service: [" + xtype + "] 已经注册过了");
+ }
+ serviceInjection[xtype] = cls;
+ };
+
+ var providerInjection = {};
+ BI.provider = BI.provider || function (xtype, cls) {
+ if (providerInjection[xtype] != null) {
+ _global.console && console.error("provider: [" + xtype + "] 已经注册过了");
+ }
+ providerInjection[xtype] = cls;
+ };
+
+ var configFunctions = {};
+ var runConfigFunction = BI.runConfigFunction = function (type) {
+ if (!type || !configFunctions[type]) {
+ return false;
+ }
+ var queue = configFunctions[type];
+ delete configFunctions[type];
+
+ var dependencies = BI.Providers.getProvider("bi.provider.system").getDependencies();
+ var modules = moduleInjectionMap.components[type]
+ || moduleInjectionMap.constants[type]
+ || moduleInjectionMap.services[type]
+ || moduleInjectionMap.stores[type]
+ || moduleInjectionMap.models[type]
+ || moduleInjectionMap.providers[type];
+ for (var i = 0; i < queue.length; i++) {
+ var conf = queue[i];
+ var version = conf.opt.version;
+ var fn = conf.fn;
+ if (modules && version) {
+ var findVersion = false;
+ for (var j = 0; j < modules.length; j++) {
+ var module = modules[j];
+ if (module && dependencies[module.moduleId] && module.version === version) {
+ var minVersion = dependencies[module.moduleId].minVersion,
+ maxVersion = dependencies[module.moduleId].maxVersion;
+ if (minVersion && (moduleInjection[module.moduleId].version || version) < minVersion) {
+ findVersion = true;
+ break;
+ }
+ if (maxVersion && (moduleInjection[module.moduleId].version || version) > maxVersion) {
+ findVersion = true;
+ break;
+ }
+ }
+ }
+ if (findVersion === true) {
+ _global.console && console.error("moduleId: [" + module.moduleId + "] 接口: [" + type + "] 接口版本: [" + version + "] 已过期,版本要求为:", dependencies[module.moduleId], "=>", moduleInjection[module.moduleId]);
+ continue;
+ }
+ }
+ if (constantInjection[type]) {
+ constantInjection[type] = fn(constantInjection[type]);
+ continue;
+ }
+ if (providerInjection[type]) {
+ if (!providers[type]) {
+ providers[type] = new providerInjection[type]();
+ }
+ if (providerInstance[type]) {
+ delete providerInstance[type];
+ }
+ fn(providers[type]);
+ continue;
+ }
+ BI.Plugin.configWidget(type, fn, conf.opt);
+ }
+ };
+ BI.config = BI.config || function (type, configFn, opt) {
+ opt = opt || {};
+
+ // 系统配置直接执行
+ if ("bi.provider.system" === type) {
+ if (!providers[type]) {
+ providers[type] = new providerInjection[type]();
+ }
+ // 如果config被重新配置的话,需要删除掉之前的实例
+ if (providerInstance[type]) {
+ delete providerInstance[type];
+ }
+ return configFn(providers[type]);
+ }
+
+ if (!configFunctions[type]) {
+ configFunctions[type] = [];
+ }
+ configFunctions[type].push({
+ fn: configFn,
+ opt: opt
+ });
+
+ // // 初始化过或者系统配置需要立刻执行
+ // if (BI.initialized || "bi.provider.system" === type) {
+ // if (constantInjection[type]) {
+ // return (constantInjection[type] = configFn(constantInjection[type]));
+ // }
+ // if (providerInjection[type]) {
+ // if (!providers[type]) {
+ // providers[type] = new providerInjection[type]();
+ // }
+ // // 如果config被重新配置的话,需要删除掉之前的实例
+ // if (providerInstance[type]) {
+ // delete providerInstance[type];
+ // }
+ // return configFn(providers[type]);
+ // }
+ // return BI.Plugin.configWidget(type, configFn, opt);
+ // }
+ // if (!configFunctions[type]) {
+ // configFunctions[type] = [];
+ // BI.prepares.push(function () {
+ // var queue = configFunctions[type];
+ // var dependencies = BI.Providers.getProvider("bi.provider.system").getDependencies();
+ // var modules = moduleInjectionMap.components[type]
+ // || moduleInjectionMap.constants[type]
+ // || moduleInjectionMap.services[type]
+ // || moduleInjectionMap.stores[type]
+ // || moduleInjectionMap.models[type]
+ // || moduleInjectionMap.providers[type];
+ // for (var i = 0; i < queue.length; i++) {
+ // var conf = queue[i];
+ // var version = conf.opt.version;
+ // var fn = conf.fn;
+ // if (modules && version) {
+ // var findVersion = false;
+ // for (var j = 0; j < modules.length; j++) {
+ // var module = modules[i];
+ // if (module && dependencies[module.moduleId] && module.version === version) {
+ // var minVersion = dependencies[module.moduleId].minVersion,
+ // maxVersion = dependencies[module.moduleId].maxVersion;
+ // if (minVersion && (moduleInjection[module.moduleId].version || version) < minVersion) {
+ // findVersion = true;
+ // break;
+ // }
+ // if (maxVersion && (moduleInjection[module.moduleId].version || version) > maxVersion) {
+ // findVersion = true;
+ // break;
+ // }
+ // }
+ // }
+ // if (findVersion === true) {
+ // _global.console && console.error("moduleId: [" + module.moduleId + "] 接口: [" + type + "] 接口版本: [" + version + "] 已过期,版本要求为:", dependencies[module.moduleId], "=>", moduleInjection[module.moduleId]);
+ // continue;
+ // }
+ // }
+ // if (constantInjection[type]) {
+ // constantInjection[type] = fn(constantInjection[type]);
+ // continue;
+ // }
+ // if (providerInjection[type]) {
+ // if (!providers[type]) {
+ // providers[type] = new providerInjection[type]();
+ // }
+ // if (providerInstance[type]) {
+ // delete providerInstance[type];
+ // }
+ // fn(providers[type]);
+ // continue;
+ // }
+ // BI.Plugin.configWidget(type, fn);
+ // }
+ // configFunctions[type] = null;
+ // });
+ // }
+ // configFunctions[type].push({
+ // fn: configFn,
+ // opt: opt
+ // });
+ };
+
+ BI.getReference = BI.getReference || function (type, fn) {
+ return BI.Plugin.registerObject(type, fn);
+ };
+
+ var actions = {};
+ var globalAction = [];
+ BI.action = BI.action || function (type, actionFn) {
+ if (BI.isFunction(type)) {
+ globalAction.push(type);
+ return function () {
+ BI.remove(globalAction, function (idx) {
+ return globalAction.indexOf(actionFn) === idx;
+ });
+ };
+ }
+ if (!actions[type]) {
+ actions[type] = [];
+ }
+ actions[type].push(actionFn);
+ return function () {
+ BI.remove(actions[type], function (idx) {
+ return actions[type].indexOf(actionFn) === idx;
+ });
+ if (actions[type].length === 0) {
+ delete actions[type];
+ }
+ };
+ };
+
+ var points = {};
+ BI.point = BI.point || function (type, action, pointFn, after) {
+ if (!points[type]) {
+ points[type] = {};
+ }
+ if (!points[type][action]) {
+ points[type][action] = {};
+ }
+ if (!points[type][action][after ? "after" : "before"]) {
+ points[type][action][after ? "after" : "before"] = [];
+ }
+ points[type][action][after ? "after" : "before"].push(pointFn);
+ };
+
+ BI.Modules = BI.Modules || {
+ getModule: function (type) {
+ if (!moduleInjection[type]) {
+ _global.console && console.error("module: [" + type + "] 未定义");
+ }
+ return moduleInjection[type];
+ },
+ getAllModules: function () {
+ return moduleInjection;
+ }
+ };
+
+ BI.Constants = BI.Constants || {
+ getConstant: function (type) {
+ if (BI.isNull(constantInjection[type])) {
+ _global.console && console.error("constant: [" + type + "] 未定义");
+ }
+ runConfigFunction(type);
+ return constantInjection[type];
+ }
+ };
+
+ var callPoint = function (inst, types) {
+ types = BI.isArray(types) ? types : [types];
+ BI.each(types, function (idx, type) {
+ if (points[type]) {
+ for (var action in points[type]) {
+ var bfns = points[type][action].before;
+ if (bfns) {
+ BI.aspect.before(inst, action, function (bfns) {
+ return function () {
+ for (var i = 0, len = bfns.length; i < len; i++) {
+ try {
+ bfns[i].apply(inst, arguments);
+ } catch (e) {
+ _global.console && console.error(e);
+ }
+ }
+ };
+ }(bfns));
+ }
+ var afns = points[type][action].after;
+ if (afns) {
+ BI.aspect.after(inst, action, function (afns) {
+ return function () {
+ for (var i = 0, len = afns.length; i < len; i++) {
+ try {
+ afns[i].apply(inst, arguments);
+ } catch (e) {
+ _global.console && console.error(e);
+ }
+ }
+ };
+ }(afns));
+ }
+ }
+ }
+ });
+ };
+
+ BI.Models = BI.Models || {
+ getModel: function (type, config) {
+ if (!modelInjection[type]) {
+ _global.console && console.error("model: [" + type + "] 未定义");
+ }
+ runConfigFunction(type);
+ var inst = new modelInjection[type](config);
+ inst._constructor && inst._constructor(config);
+ inst.mixins && callPoint(inst, inst.mixins);
+ callPoint(inst, type);
+ return inst;
+ }
+ };
+
+ var stores = {};
+
+ BI.Stores = BI.Stores || {
+ getStore: function (type, config) {
+ if (!storeInjection[type]) {
+ _global.console && console.error("store: [" + type + "] 未定义");
+ }
+ if (stores[type]) {
+ return stores[type];
+ }
+ var inst = stores[type] = new storeInjection[type](config);
+ inst._constructor && inst._constructor(config, function () {
+ delete stores[type];
+ });
+ callPoint(inst, type);
+ return inst;
+ }
+ };
+
+ var services = {};
+
+ BI.Services = BI.Services || {
+ getService: function (type, config) {
+ if (!serviceInjection[type]) {
+ _global.console && console.error("service: [" + type + "] 未定义");
+ }
+ if (services[type]) {
+ return services[type];
+ }
+ services[type] = new serviceInjection[type](config);
+ callPoint(services[type], type);
+ return services[type];
+ }
+ };
+
+ var providers = {},
+ providerInstance = {};
+
+ BI.Providers = BI.Providers || {
+ getProvider: function (type, config) {
+ if (!providerInjection[type]) {
+ _global.console && console.error("provider: [" + type + "] 未定义");
+ }
+ runConfigFunction(type);
+ if (!providers[type]) {
+ providers[type] = new providerInjection[type]();
+ }
+ if (!providerInstance[type]) {
+ providerInstance[type] = new (providers[type].$get())(config);
+ }
+ return providerInstance[type];
+ }
+ };
+
+ BI.Actions = BI.Actions || {
+ runAction: function (type, event, config) {
+ BI.each(actions[type], function (i, act) {
+ try {
+ act(event, config);
+ } catch (e) {
+ _global.console && console.error(e);
+ }
+ });
+ },
+ runGlobalAction: function () {
+ var args = [].slice.call(arguments);
+ BI.each(globalAction, function (i, act) {
+ try {
+ act.apply(null, args);
+ } catch (e) {
+ _global.console && console.error(e);
+ }
+ });
+ }
+ };
+
+ BI.getResource = BI.getResource || function (type, config) {
+ if (BI.isNotNull(constantInjection[type])) {
+ return BI.Constants.getConstant(type);
+ }
+ if (modelInjection[type]) {
+ return BI.Models.getModel(type, config);
+ }
+ if (storeInjection[type]) {
+ return BI.Stores.getStore(type, config);
+ }
+ if (serviceInjection[type]) {
+ return BI.Services.getService(type, config);
+ }
+ if (providerInjection[type]) {
+ return BI.Providers.getProvider(type, config);
+ }
+ throw new Error("未知类型: [" + type + "] 未定义");
+ };
+})();
diff --git a/src/main/resources/com/fr/fineui/core/7.plugin.js b/src/main/resources/com/fr/fineui/core/7.plugin.js
new file mode 100644
index 0000000..50109a4
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/7.plugin.js
@@ -0,0 +1,123 @@
+BI.Plugin = BI.Plugin || {};
+!(function () {
+ var _WidgetsPlugin = {};
+ var _ObjectPlugin = {};
+ var _ConfigPlugin = {};
+ var _ConfigRenderPlugin = {};
+ var _GlobalWidgetConfigFns = [];
+ var __GlobalObjectConfigFns = [];
+ BI.defaults(BI.Plugin, {
+
+ getWidget: function (type, options) {
+ if (_GlobalWidgetConfigFns.length > 0) {
+ var fns = _GlobalWidgetConfigFns.slice(0);
+ for (var i = fns.length - 1; i >= 0; i--) {
+ fns[i](type, options);
+ }
+ }
+
+ var res;
+ if (_ConfigPlugin[type]) {
+ for (var i = _ConfigPlugin[type].length - 1; i >= 0; i--) {
+ if (res = _ConfigPlugin[type][i](options)) {
+ options = res;
+ }
+ }
+ }
+ // Deprecated
+ if (_WidgetsPlugin[type]) {
+ for (var i = _WidgetsPlugin[type].length - 1; i >= 0; i--) {
+ if (res = _WidgetsPlugin[type][i](options)) {
+ return res;
+ }
+ }
+ }
+ return options;
+ },
+
+ config: function (widgetConfigFn, objectConfigFn) {
+ _GlobalWidgetConfigFns = _GlobalWidgetConfigFns.concat(_.isArray(widgetConfigFn) ? widgetConfigFn : [widgetConfigFn]);
+ __GlobalObjectConfigFns = __GlobalObjectConfigFns.concat(_.isArray(objectConfigFn) ? objectConfigFn : [objectConfigFn]);
+ },
+
+ configWidget: function (type, fn, opt) {
+ // opt.single: true 最后一次注册有效
+ if (!_ConfigPlugin[type] || (opt && opt.single)) {
+ _ConfigPlugin[type] = [];
+ }
+ _ConfigPlugin[type].push(fn);
+ },
+
+ getRender: function (type, rendered) {
+ var res;
+ if (_ConfigRenderPlugin[type]) {
+ for (var i = _ConfigRenderPlugin[type].length - 1; i >= 0; i--) {
+ if (res = _ConfigRenderPlugin[type][i](rendered)) {
+ rendered = res;
+ }
+ }
+ }
+ return rendered;
+ },
+
+ configRender: function (type, fn) {
+ if (!_ConfigRenderPlugin[type]) {
+ _ConfigRenderPlugin[type] = [];
+ }
+ _ConfigRenderPlugin[type].push(fn);
+ },
+
+ // Deprecated
+ registerWidget: function (type, fn) {
+ if (!_WidgetsPlugin[type]) {
+ _WidgetsPlugin[type] = [];
+ }
+ if (_WidgetsPlugin[type].length > 0) {
+ console.log("组件已经注册过了!");
+ }
+ _WidgetsPlugin[type].push(fn);
+ },
+
+ // Deprecated
+ relieveWidget: function (type) {
+ delete _WidgetsPlugin[type];
+ },
+
+ getObject: function (type, object) {
+ if (__GlobalObjectConfigFns.length > 0) {
+ var fns = __GlobalObjectConfigFns.slice(0);
+ for (var i = fns.length - 1; i >= 0; i--) {
+ fns[i](type, object);
+ }
+ }
+
+ if (_ObjectPlugin[type]) {
+ var res;
+ for (var i = 0, len = _ObjectPlugin[type].length; i < len; i++) {
+ if (res = _ObjectPlugin[type][i](object)) {
+ object = res;
+ }
+ }
+ }
+ return res || object;
+ },
+
+ hasObject: function (type) {
+ return __GlobalObjectConfigFns.length > 0 || !!_ObjectPlugin[type];
+ },
+
+ registerObject: function (type, fn) {
+ if (!_ObjectPlugin[type]) {
+ _ObjectPlugin[type] = [];
+ }
+ if (_ObjectPlugin[type].length > 0) {
+ console.log("对象已经注册过了!");
+ }
+ _ObjectPlugin[type].push(fn);
+ },
+
+ relieveObject: function (type) {
+ delete _ObjectPlugin[type];
+ }
+ });
+})();
diff --git a/src/main/resources/com/fr/fineui/core/8.popper.js b/src/main/resources/com/fr/fineui/core/8.popper.js
new file mode 100644
index 0000000..a3708db
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/8.popper.js
@@ -0,0 +1,1908 @@
+/**
+ * @popperjs/core v2.9.2 - MIT License
+ */
+
+(function (global, factory) {
+ factory(BI.Popper = {});
+}(this, (function (exports) { 'use strict';
+
+ function getBoundingClientRect(element) {
+ var rect = element.getBoundingClientRect();
+ return {
+ width: rect.width,
+ height: rect.height,
+ top: rect.top,
+ right: rect.right,
+ bottom: rect.bottom,
+ left: rect.left,
+ x: rect.left,
+ y: rect.top
+ };
+ }
+
+ function getWindow(node) {
+ if (node == null) {
+ return window;
+ }
+
+ if (node.toString() !== '[object Window]') {
+ var ownerDocument = node.ownerDocument;
+ return ownerDocument ? ownerDocument.defaultView || window : window;
+ }
+
+ return node;
+ }
+
+ function getWindowScroll(node) {
+ var win = getWindow(node);
+ var scrollLeft = win.pageXOffset;
+ var scrollTop = win.pageYOffset;
+ return {
+ scrollLeft: scrollLeft,
+ scrollTop: scrollTop
+ };
+ }
+
+ function isElement(node) {
+ var OwnElement = getWindow(node).Element;
+ return node instanceof OwnElement || node instanceof Element;
+ }
+
+ function isHTMLElement(node) {
+ var OwnElement = getWindow(node).HTMLElement;
+ return node instanceof OwnElement || node instanceof HTMLElement;
+ }
+
+ function isShadowRoot(node) {
+ // IE 11 has no ShadowRoot
+ if (typeof ShadowRoot === 'undefined') {
+ return false;
+ }
+
+ var OwnElement = getWindow(node).ShadowRoot;
+ return node instanceof OwnElement || node instanceof ShadowRoot;
+ }
+
+ function getHTMLElementScroll(element) {
+ return {
+ scrollLeft: element.scrollLeft,
+ scrollTop: element.scrollTop
+ };
+ }
+
+ function getNodeScroll(node) {
+ if (node === getWindow(node) || !isHTMLElement(node)) {
+ return getWindowScroll(node);
+ } else {
+ return getHTMLElementScroll(node);
+ }
+ }
+
+ function getNodeName(element) {
+ return element ? (element.nodeName || '').toLowerCase() : null;
+ }
+
+ function getDocumentElement(element) {
+ // $FlowFixMe[incompatible-return]: assume body is always available
+ return ((isElement(element) ? element.ownerDocument : // $FlowFixMe[prop-missing]
+ element.document) || window.document).documentElement;
+ }
+
+ function getWindowScrollBarX(element) {
+ // If has a CSS width greater than the viewport, then this will be
+ // incorrect for RTL.
+ // Popper 1 is broken in this case and never had a bug report so let's assume
+ // it's not an issue. I don't think anyone ever specifies width on
+ // anyway.
+ // Browsers where the left scrollbar doesn't cause an issue report `0` for
+ // this (e.g. Edge 2019, IE11, Safari)
+ return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft;
+ }
+
+ function getComputedStyle(element) {
+ return getWindow(element).getComputedStyle(element);
+ }
+
+ function isScrollParent(element) {
+ // Firefox wants us to check `-x` and `-y` variations as well
+ var _getComputedStyle = getComputedStyle(element),
+ overflow = _getComputedStyle.overflow,
+ overflowX = _getComputedStyle.overflowX,
+ overflowY = _getComputedStyle.overflowY;
+
+ return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX);
+ }
+
+ // Composite means it takes into account transforms as well as layout.
+
+ function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) {
+ if (isFixed === void 0) {
+ isFixed = false;
+ }
+
+ var documentElement = getDocumentElement(offsetParent);
+ var rect = getBoundingClientRect(elementOrVirtualElement);
+ var isOffsetParentAnElement = isHTMLElement(offsetParent);
+ var scroll = {
+ scrollLeft: 0,
+ scrollTop: 0
+ };
+ var offsets = {
+ x: 0,
+ y: 0
+ };
+
+ if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {
+ if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078
+ isScrollParent(documentElement)) {
+ scroll = getNodeScroll(offsetParent);
+ }
+
+ if (isHTMLElement(offsetParent)) {
+ offsets = getBoundingClientRect(offsetParent);
+ offsets.x += offsetParent.clientLeft;
+ offsets.y += offsetParent.clientTop;
+ } else if (documentElement) {
+ offsets.x = getWindowScrollBarX(documentElement);
+ }
+ }
+
+ return {
+ x: rect.left + scroll.scrollLeft - offsets.x,
+ y: rect.top + scroll.scrollTop - offsets.y,
+ width: rect.width,
+ height: rect.height
+ };
+ }
+
+ // means it doesn't take into account transforms.
+
+ function getLayoutRect(element) {
+ var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed.
+ // Fixes https://github.com/popperjs/popper-core/issues/1223
+
+ var width = element.offsetWidth;
+ var height = element.offsetHeight;
+
+ if (Math.abs(clientRect.width - width) <= 1) {
+ width = clientRect.width;
+ }
+
+ if (Math.abs(clientRect.height - height) <= 1) {
+ height = clientRect.height;
+ }
+
+ return {
+ x: element.offsetLeft,
+ y: element.offsetTop,
+ width: width,
+ height: height
+ };
+ }
+
+ function getParentNode(element) {
+ if (getNodeName(element) === 'html') {
+ return element;
+ }
+
+ return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle
+ // $FlowFixMe[incompatible-return]
+ // $FlowFixMe[prop-missing]
+ element.assignedSlot || // step into the shadow DOM of the parent of a slotted node
+ element.parentNode || ( // DOM Element detected
+ isShadowRoot(element) ? element.host : null) || // ShadowRoot detected
+ // $FlowFixMe[incompatible-call]: HTMLElement is a Node
+ getDocumentElement(element) // fallback
+
+ );
+ }
+
+ function getScrollParent(node) {
+ if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) {
+ // $FlowFixMe[incompatible-return]: assume body is always available
+ return node.ownerDocument.body;
+ }
+
+ if (isHTMLElement(node) && isScrollParent(node)) {
+ return node;
+ }
+
+ return getScrollParent(getParentNode(node));
+ }
+
+ /*
+ given a DOM element, return the list of all scroll parents, up the list of ancesors
+ until we get to the top window object. This list is what we attach scroll listeners
+ to, because if any of these parent elements scroll, we'll need to re-calculate the
+ reference element's position.
+ */
+
+ function listScrollParents(element, list) {
+ var _element$ownerDocumen;
+
+ if (list === void 0) {
+ list = [];
+ }
+
+ var scrollParent = getScrollParent(element);
+ var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body);
+ var win = getWindow(scrollParent);
+ var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent;
+ var updatedList = list.concat(target);
+ return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here
+ updatedList.concat(listScrollParents(getParentNode(target)));
+ }
+
+ function isTableElement(element) {
+ return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0;
+ }
+
+ function getTrueOffsetParent(element) {
+ if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837
+ getComputedStyle(element).position === 'fixed') {
+ return null;
+ }
+
+ return element.offsetParent;
+ } // `.offsetParent` reports `null` for fixed elements, while absolute elements
+ // return the containing block
+
+
+ function getContainingBlock(element) {
+ var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') !== -1;
+ var isIE = navigator.userAgent.indexOf('Trident') !== -1;
+
+ if (isIE && isHTMLElement(element)) {
+ // In IE 9, 10 and 11 fixed elements containing block is always established by the viewport
+ var elementCss = getComputedStyle(element);
+
+ if (elementCss.position === 'fixed') {
+ return null;
+ }
+ }
+
+ var currentNode = getParentNode(element);
+
+ while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) {
+ var css = getComputedStyle(currentNode); // This is non-exhaustive but covers the most common CSS properties that
+ // create a containing block.
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
+
+ if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') {
+ return currentNode;
+ } else {
+ currentNode = currentNode.parentNode;
+ }
+ }
+
+ return null;
+ } // Gets the closest ancestor positioned element. Handles some edge cases,
+ // such as table ancestors and cross browser bugs.
+
+
+ function getOffsetParent(element) {
+ var window = getWindow(element);
+ var offsetParent = getTrueOffsetParent(element);
+
+ while (offsetParent && isTableElement(offsetParent) && getComputedStyle(offsetParent).position === 'static') {
+ offsetParent = getTrueOffsetParent(offsetParent);
+ }
+
+ if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle(offsetParent).position === 'static')) {
+ return window;
+ }
+
+ return offsetParent || getContainingBlock(element) || window;
+ }
+
+ var top = 'top';
+ var bottom = 'bottom';
+ var right = 'right';
+ var left = 'left';
+ var auto = 'auto';
+ var basePlacements = [top, bottom, right, left];
+ var start = 'start';
+ var end = 'end';
+ var clippingParents = 'clippingParents';
+ var viewport = 'viewport';
+ var popper = 'popper';
+ var reference = 'reference';
+ var variationPlacements = /*#__PURE__*/basePlacements.reduce(function (acc, placement) {
+ return acc.concat([placement + "-" + start, placement + "-" + end]);
+ }, []);
+ var placements = /*#__PURE__*/[].concat(basePlacements, [auto]).reduce(function (acc, placement) {
+ return acc.concat([placement, placement + "-" + start, placement + "-" + end]);
+ }, []); // modifiers that need to read the DOM
+
+ var beforeRead = 'beforeRead';
+ var read = 'read';
+ var afterRead = 'afterRead'; // pure-logic modifiers
+
+ var beforeMain = 'beforeMain';
+ var main = 'main';
+ var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state)
+
+ var beforeWrite = 'beforeWrite';
+ var write = 'write';
+ var afterWrite = 'afterWrite';
+ var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite];
+
+ function order(modifiers) {
+ var map = new Map();
+ var visited = new Set();
+ var result = [];
+ modifiers.forEach(function (modifier) {
+ map.set(modifier.name, modifier);
+ }); // On visiting object, check for its dependencies and visit them recursively
+
+ function sort(modifier) {
+ visited.add(modifier.name);
+ var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []);
+ requires.forEach(function (dep) {
+ if (!visited.has(dep)) {
+ var depModifier = map.get(dep);
+
+ if (depModifier) {
+ sort(depModifier);
+ }
+ }
+ });
+ result.push(modifier);
+ }
+
+ modifiers.forEach(function (modifier) {
+ if (!visited.has(modifier.name)) {
+ // check for visited object
+ sort(modifier);
+ }
+ });
+ return result;
+ }
+
+ function orderModifiers(modifiers) {
+ // order based on dependencies
+ var orderedModifiers = order(modifiers); // order based on phase
+
+ return modifierPhases.reduce(function (acc, phase) {
+ return acc.concat(orderedModifiers.filter(function (modifier) {
+ return modifier.phase === phase;
+ }));
+ }, []);
+ }
+
+ function debounce(fn) {
+ var pending;
+ return function () {
+ if (!pending) {
+ pending = new Promise(function (resolve) {
+ Promise.resolve().then(function () {
+ pending = undefined;
+ resolve(fn());
+ });
+ });
+ }
+
+ return pending;
+ };
+ }
+
+ function format(str) {
+ for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+ args[_key - 1] = arguments[_key];
+ }
+
+ return [].concat(args).reduce(function (p, c) {
+ return p.replace(/%s/, c);
+ }, str);
+ }
+
+ var INVALID_MODIFIER_ERROR = 'Popper: modifier "%s" provided an invalid %s property, expected %s but got %s';
+ var MISSING_DEPENDENCY_ERROR = 'Popper: modifier "%s" requires "%s", but "%s" modifier is not available';
+ var VALID_PROPERTIES = ['name', 'enabled', 'phase', 'fn', 'effect', 'requires', 'options'];
+ function validateModifiers(modifiers) {
+ modifiers.forEach(function (modifier) {
+ Object.keys(modifier).forEach(function (key) {
+ switch (key) {
+ case 'name':
+ if (typeof modifier.name !== 'string') {
+ console.error(format(INVALID_MODIFIER_ERROR, String(modifier.name), '"name"', '"string"', "\"" + String(modifier.name) + "\""));
+ }
+
+ break;
+
+ case 'enabled':
+ if (typeof modifier.enabled !== 'boolean') {
+ console.error(format(INVALID_MODIFIER_ERROR, modifier.name, '"enabled"', '"boolean"', "\"" + String(modifier.enabled) + "\""));
+ }
+
+ case 'phase':
+ if (modifierPhases.indexOf(modifier.phase) < 0) {
+ console.error(format(INVALID_MODIFIER_ERROR, modifier.name, '"phase"', "either " + modifierPhases.join(', '), "\"" + String(modifier.phase) + "\""));
+ }
+
+ break;
+
+ case 'fn':
+ if (typeof modifier.fn !== 'function') {
+ console.error(format(INVALID_MODIFIER_ERROR, modifier.name, '"fn"', '"function"', "\"" + String(modifier.fn) + "\""));
+ }
+
+ break;
+
+ case 'effect':
+ if (typeof modifier.effect !== 'function') {
+ console.error(format(INVALID_MODIFIER_ERROR, modifier.name, '"effect"', '"function"', "\"" + String(modifier.fn) + "\""));
+ }
+
+ break;
+
+ case 'requires':
+ if (!Array.isArray(modifier.requires)) {
+ console.error(format(INVALID_MODIFIER_ERROR, modifier.name, '"requires"', '"array"', "\"" + String(modifier.requires) + "\""));
+ }
+
+ break;
+
+ case 'requiresIfExists':
+ if (!Array.isArray(modifier.requiresIfExists)) {
+ console.error(format(INVALID_MODIFIER_ERROR, modifier.name, '"requiresIfExists"', '"array"', "\"" + String(modifier.requiresIfExists) + "\""));
+ }
+
+ break;
+
+ case 'options':
+ case 'data':
+ break;
+
+ default:
+ console.error("PopperJS: an invalid property has been provided to the \"" + modifier.name + "\" modifier, valid properties are " + VALID_PROPERTIES.map(function (s) {
+ return "\"" + s + "\"";
+ }).join(', ') + "; but \"" + key + "\" was provided.");
+ }
+
+ modifier.requires && modifier.requires.forEach(function (requirement) {
+ if (modifiers.find(function (mod) {
+ return mod.name === requirement;
+ }) == null) {
+ console.error(format(MISSING_DEPENDENCY_ERROR, String(modifier.name), requirement, requirement));
+ }
+ });
+ });
+ });
+ }
+
+ function uniqueBy(arr, fn) {
+ var identifiers = new Set();
+ return arr.filter(function (item) {
+ var identifier = fn(item);
+
+ if (!identifiers.has(identifier)) {
+ identifiers.add(identifier);
+ return true;
+ }
+ });
+ }
+
+ function getBasePlacement(placement) {
+ return placement.split('-')[0];
+ }
+
+ function mergeByName(modifiers) {
+ var merged = modifiers.reduce(function (merged, current) {
+ var existing = merged[current.name];
+ merged[current.name] = existing ? Object.assign({}, existing, current, {
+ options: Object.assign({}, existing.options, current.options),
+ data: Object.assign({}, existing.data, current.data)
+ }) : current;
+ return merged;
+ }, {}); // IE11 does not support Object.values
+
+ return Object.keys(merged).map(function (key) {
+ return merged[key];
+ });
+ }
+
+ function getViewportRect(element) {
+ var win = getWindow(element);
+ var html = getDocumentElement(element);
+ var visualViewport = win.visualViewport;
+ var width = html.clientWidth;
+ var height = html.clientHeight;
+ var x = 0;
+ var y = 0; // NB: This isn't supported on iOS <= 12. If the keyboard is open, the popper
+ // can be obscured underneath it.
+ // Also, `html.clientHeight` adds the bottom bar height in Safari iOS, even
+ // if it isn't open, so if this isn't available, the popper will be detected
+ // to overflow the bottom of the screen too early.
+
+ if (visualViewport) {
+ width = visualViewport.width;
+ height = visualViewport.height; // Uses Layout Viewport (like Chrome; Safari does not currently)
+ // In Chrome, it returns a value very close to 0 (+/-) but contains rounding
+ // errors due to floating point numbers, so we need to check precision.
+ // Safari returns a number <= 0, usually < -1 when pinch-zoomed
+ // Feature detection fails in mobile emulation mode in Chrome.
+ // Math.abs(win.innerWidth / visualViewport.scale - visualViewport.width) <
+ // 0.001
+ // Fallback here: "Not Safari" userAgent
+
+ if (!/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
+ x = visualViewport.offsetLeft;
+ y = visualViewport.offsetTop;
+ }
+ }
+
+ return {
+ width: width,
+ height: height,
+ x: x + getWindowScrollBarX(element),
+ y: y
+ };
+ }
+
+ var max = Math.max;
+ var min = Math.min;
+ var round = Math.round;
+
+ // of the `` and `` rect bounds if horizontally scrollable
+
+ function getDocumentRect(element) {
+ var _element$ownerDocumen;
+
+ var html = getDocumentElement(element);
+ var winScroll = getWindowScroll(element);
+ var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body;
+ var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0);
+ var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0);
+ var x = -winScroll.scrollLeft + getWindowScrollBarX(element);
+ var y = -winScroll.scrollTop;
+
+ if (getComputedStyle(body || html).direction === 'rtl') {
+ x += max(html.clientWidth, body ? body.clientWidth : 0) - width;
+ }
+
+ return {
+ width: width,
+ height: height,
+ x: x,
+ y: y
+ };
+ }
+
+ function contains(parent, child) {
+ var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method
+
+ if (parent.contains(child)) {
+ return true;
+ } // then fallback to custom implementation with Shadow DOM support
+ else if (rootNode && isShadowRoot(rootNode)) {
+ var next = child;
+
+ do {
+ if (next && parent.isSameNode(next)) {
+ return true;
+ } // $FlowFixMe[prop-missing]: need a better way to handle this...
+
+
+ next = next.parentNode || next.host;
+ } while (next);
+ } // Give up, the result is false
+
+
+ return false;
+ }
+
+ function rectToClientRect(rect) {
+ return Object.assign({}, rect, {
+ left: rect.x,
+ top: rect.y,
+ right: rect.x + rect.width,
+ bottom: rect.y + rect.height
+ });
+ }
+
+ function getInnerBoundingClientRect(element) {
+ var rect = getBoundingClientRect(element);
+ rect.top = rect.top + element.clientTop;
+ rect.left = rect.left + element.clientLeft;
+ rect.bottom = rect.top + element.clientHeight;
+ rect.right = rect.left + element.clientWidth;
+ rect.width = element.clientWidth;
+ rect.height = element.clientHeight;
+ rect.x = rect.left;
+ rect.y = rect.top;
+ return rect;
+ }
+
+ function getClientRectFromMixedType(element, clippingParent) {
+ return clippingParent === viewport ? rectToClientRect(getViewportRect(element)) : isHTMLElement(clippingParent) ? getInnerBoundingClientRect(clippingParent) : rectToClientRect(getDocumentRect(getDocumentElement(element)));
+ } // A "clipping parent" is an overflowable container with the characteristic of
+ // clipping (or hiding) overflowing elements with a position different from
+ // `initial`
+
+
+ function getClippingParents(element) {
+ var clippingParents = listScrollParents(getParentNode(element));
+ var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle(element).position) >= 0;
+ var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element;
+
+ if (!isElement(clipperElement)) {
+ return [];
+ } // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414
+
+
+ return clippingParents.filter(function (clippingParent) {
+ return isElement(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body';
+ });
+ } // Gets the maximum area that the element is visible in due to any number of
+ // clipping parents
+
+
+ function getClippingRect(element, boundary, rootBoundary) {
+ var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary);
+ var clippingParents = [].concat(mainClippingParents, [rootBoundary]);
+ var firstClippingParent = clippingParents[0];
+ var clippingRect = clippingParents.reduce(function (accRect, clippingParent) {
+ var rect = getClientRectFromMixedType(element, clippingParent);
+ accRect.top = max(rect.top, accRect.top);
+ accRect.right = min(rect.right, accRect.right);
+ accRect.bottom = min(rect.bottom, accRect.bottom);
+ accRect.left = max(rect.left, accRect.left);
+ return accRect;
+ }, getClientRectFromMixedType(element, firstClippingParent));
+ clippingRect.width = clippingRect.right - clippingRect.left;
+ clippingRect.height = clippingRect.bottom - clippingRect.top;
+ clippingRect.x = clippingRect.left;
+ clippingRect.y = clippingRect.top;
+ return clippingRect;
+ }
+
+ function getVariation(placement) {
+ return placement.split('-')[1];
+ }
+
+ function getMainAxisFromPlacement(placement) {
+ return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y';
+ }
+
+ function computeOffsets(_ref) {
+ var reference = _ref.reference,
+ element = _ref.element,
+ placement = _ref.placement;
+ var basePlacement = placement ? getBasePlacement(placement) : null;
+ var variation = placement ? getVariation(placement) : null;
+ var commonX = reference.x + reference.width / 2 - element.width / 2;
+ var commonY = reference.y + reference.height / 2 - element.height / 2;
+ var offsets;
+
+ switch (basePlacement) {
+ case top:
+ offsets = {
+ x: commonX,
+ y: reference.y - element.height
+ };
+ break;
+
+ case bottom:
+ offsets = {
+ x: commonX,
+ y: reference.y + reference.height
+ };
+ break;
+
+ case right:
+ offsets = {
+ x: reference.x + reference.width,
+ y: commonY
+ };
+ break;
+
+ case left:
+ offsets = {
+ x: reference.x - element.width,
+ y: commonY
+ };
+ break;
+
+ default:
+ offsets = {
+ x: reference.x,
+ y: reference.y
+ };
+ }
+
+ var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null;
+
+ if (mainAxis != null) {
+ var len = mainAxis === 'y' ? 'height' : 'width';
+
+ switch (variation) {
+ case start:
+ offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2);
+ break;
+
+ case end:
+ offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2);
+ break;
+ }
+ }
+
+ return offsets;
+ }
+
+ function getFreshSideObject() {
+ return {
+ top: 0,
+ right: 0,
+ bottom: 0,
+ left: 0
+ };
+ }
+
+ function mergePaddingObject(paddingObject) {
+ return Object.assign({}, getFreshSideObject(), paddingObject);
+ }
+
+ function expandToHashMap(value, keys) {
+ return keys.reduce(function (hashMap, key) {
+ hashMap[key] = value;
+ return hashMap;
+ }, {});
+ }
+
+ function detectOverflow(state, options) {
+ if (options === void 0) {
+ options = {};
+ }
+
+ var _options = options,
+ _options$placement = _options.placement,
+ placement = _options$placement === void 0 ? state.placement : _options$placement,
+ _options$boundary = _options.boundary,
+ boundary = _options$boundary === void 0 ? clippingParents : _options$boundary,
+ _options$rootBoundary = _options.rootBoundary,
+ rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary,
+ _options$elementConte = _options.elementContext,
+ elementContext = _options$elementConte === void 0 ? popper : _options$elementConte,
+ _options$altBoundary = _options.altBoundary,
+ altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary,
+ _options$padding = _options.padding,
+ padding = _options$padding === void 0 ? 0 : _options$padding;
+ var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));
+ var altContext = elementContext === popper ? reference : popper;
+ var referenceElement = state.elements.reference;
+ var popperRect = state.rects.popper;
+ var element = state.elements[altBoundary ? altContext : elementContext];
+ var clippingClientRect = getClippingRect(isElement(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary);
+ var referenceClientRect = getBoundingClientRect(referenceElement);
+ var popperOffsets = computeOffsets({
+ reference: referenceClientRect,
+ element: popperRect,
+ strategy: 'absolute',
+ placement: placement
+ });
+ var popperClientRect = rectToClientRect(Object.assign({}, popperRect, popperOffsets));
+ var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect
+ // 0 or negative = within the clipping rect
+
+ var overflowOffsets = {
+ top: clippingClientRect.top - elementClientRect.top + paddingObject.top,
+ bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom,
+ left: clippingClientRect.left - elementClientRect.left + paddingObject.left,
+ right: elementClientRect.right - clippingClientRect.right + paddingObject.right
+ };
+ var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element
+
+ if (elementContext === popper && offsetData) {
+ var offset = offsetData[placement];
+ Object.keys(overflowOffsets).forEach(function (key) {
+ var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1;
+ var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x';
+ overflowOffsets[key] += offset[axis] * multiply;
+ });
+ }
+
+ return overflowOffsets;
+ }
+
+ var INVALID_ELEMENT_ERROR = 'Popper: Invalid reference or popper argument provided. They must be either a DOM element or virtual element.';
+ var INFINITE_LOOP_ERROR = 'Popper: An infinite loop in the modifiers cycle has been detected! The cycle has been interrupted to prevent a browser crash.';
+ var DEFAULT_OPTIONS = {
+ placement: 'bottom',
+ modifiers: [],
+ strategy: 'absolute'
+ };
+
+ function areValidElements() {
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return !args.some(function (element) {
+ return !(element && typeof element.getBoundingClientRect === 'function');
+ });
+ }
+
+ function popperGenerator(generatorOptions) {
+ if (generatorOptions === void 0) {
+ generatorOptions = {};
+ }
+
+ var _generatorOptions = generatorOptions,
+ _generatorOptions$def = _generatorOptions.defaultModifiers,
+ defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def,
+ _generatorOptions$def2 = _generatorOptions.defaultOptions,
+ defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2;
+ return function createPopper(reference, popper, options) {
+ if (options === void 0) {
+ options = defaultOptions;
+ }
+
+ var state = {
+ placement: 'bottom',
+ orderedModifiers: [],
+ options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions),
+ modifiersData: {},
+ elements: {
+ reference: reference,
+ popper: popper
+ },
+ attributes: {},
+ styles: {}
+ };
+ var effectCleanupFns = [];
+ var isDestroyed = false;
+ var instance = {
+ state: state,
+ setOptions: function setOptions(options) {
+ cleanupModifierEffects();
+ state.options = Object.assign({}, defaultOptions, state.options, options);
+ state.scrollParents = {
+ reference: isElement(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [],
+ popper: listScrollParents(popper)
+ }; // Orders the modifiers based on their dependencies and `phase`
+ // properties
+
+ var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers
+
+ state.orderedModifiers = orderedModifiers.filter(function (m) {
+ return m.enabled;
+ }); // Validate the provided modifiers so that the consumer will get warned
+ // if one of the modifiers is invalid for any reason
+
+ {
+ var modifiers = uniqueBy([].concat(orderedModifiers, state.options.modifiers), function (_ref) {
+ var name = _ref.name;
+ return name;
+ });
+ validateModifiers(modifiers);
+
+ if (getBasePlacement(state.options.placement) === auto) {
+ var flipModifier = state.orderedModifiers.find(function (_ref2) {
+ var name = _ref2.name;
+ return name === 'flip';
+ });
+
+ if (!flipModifier) {
+ console.error(['Popper: "auto" placements require the "flip" modifier be', 'present and enabled to work.'].join(' '));
+ }
+ }
+
+ var _getComputedStyle = getComputedStyle(popper),
+ marginTop = _getComputedStyle.marginTop,
+ marginRight = _getComputedStyle.marginRight,
+ marginBottom = _getComputedStyle.marginBottom,
+ marginLeft = _getComputedStyle.marginLeft; // We no longer take into account `margins` on the popper, and it can
+ // cause bugs with positioning, so we'll warn the consumer
+
+
+ if ([marginTop, marginRight, marginBottom, marginLeft].some(function (margin) {
+ return parseFloat(margin);
+ })) {
+ console.warn(['Popper: CSS "margin" styles cannot be used to apply padding', 'between the popper and its reference element or boundary.', 'To replicate margin, use the `offset` modifier, as well as', 'the `padding` option in the `preventOverflow` and `flip`', 'modifiers.'].join(' '));
+ }
+ }
+
+ runModifierEffects();
+ return instance.update();
+ },
+ // Sync update – it will always be executed, even if not necessary. This
+ // is useful for low frequency updates where sync behavior simplifies the
+ // logic.
+ // For high frequency updates (e.g. `resize` and `scroll` events), always
+ // prefer the async Popper#update method
+ forceUpdate: function forceUpdate() {
+ if (isDestroyed) {
+ return;
+ }
+
+ var _state$elements = state.elements,
+ reference = _state$elements.reference,
+ popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements
+ // anymore
+
+ if (!areValidElements(reference, popper)) {
+ {
+ console.error(INVALID_ELEMENT_ERROR);
+ }
+
+ return;
+ } // Store the reference and popper rects to be read by modifiers
+
+
+ state.rects = {
+ reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'),
+ popper: getLayoutRect(popper)
+ }; // Modifiers have the ability to reset the current update cycle. The
+ // most common use case for this is the `flip` modifier changing the
+ // placement, which then needs to re-run all the modifiers, because the
+ // logic was previously ran for the previous placement and is therefore
+ // stale/incorrect
+
+ state.reset = false;
+ state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier
+ // is filled with the initial data specified by the modifier. This means
+ // it doesn't persist and is fresh on each update.
+ // To ensure persistent data, use `${name}#persistent`
+
+ state.orderedModifiers.forEach(function (modifier) {
+ return state.modifiersData[modifier.name] = Object.assign({}, modifier.data);
+ });
+ var __debug_loops__ = 0;
+
+ for (var index = 0; index < state.orderedModifiers.length; index++) {
+ {
+ __debug_loops__ += 1;
+
+ if (__debug_loops__ > 100) {
+ console.error(INFINITE_LOOP_ERROR);
+ break;
+ }
+ }
+
+ if (state.reset === true) {
+ state.reset = false;
+ index = -1;
+ continue;
+ }
+
+ var _state$orderedModifie = state.orderedModifiers[index],
+ fn = _state$orderedModifie.fn,
+ _state$orderedModifie2 = _state$orderedModifie.options,
+ _options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2,
+ name = _state$orderedModifie.name;
+
+ if (typeof fn === 'function') {
+ state = fn({
+ state: state,
+ options: _options,
+ name: name,
+ instance: instance
+ }) || state;
+ }
+ }
+ },
+ // Async and optimistically optimized update – it will not be executed if
+ // not necessary (debounced to run at most once-per-tick)
+ update: debounce(function () {
+ return new Promise(function (resolve) {
+ instance.forceUpdate();
+ resolve(state);
+ });
+ }),
+ destroy: function destroy() {
+ cleanupModifierEffects();
+ isDestroyed = true;
+ }
+ };
+
+ if (!areValidElements(reference, popper)) {
+ {
+ console.error(INVALID_ELEMENT_ERROR);
+ }
+
+ return instance;
+ }
+
+ instance.setOptions(options).then(function (state) {
+ if (!isDestroyed && options.onFirstUpdate) {
+ options.onFirstUpdate(state);
+ }
+ }); // Modifiers have the ability to execute arbitrary code before the first
+ // update cycle runs. They will be executed in the same order as the update
+ // cycle. This is useful when a modifier adds some persistent data that
+ // other modifiers need to use, but the modifier is run after the dependent
+ // one.
+
+ function runModifierEffects() {
+ state.orderedModifiers.forEach(function (_ref3) {
+ var name = _ref3.name,
+ _ref3$options = _ref3.options,
+ options = _ref3$options === void 0 ? {} : _ref3$options,
+ effect = _ref3.effect;
+
+ if (typeof effect === 'function') {
+ var cleanupFn = effect({
+ state: state,
+ name: name,
+ instance: instance,
+ options: options
+ });
+
+ var noopFn = function noopFn() {};
+
+ effectCleanupFns.push(cleanupFn || noopFn);
+ }
+ });
+ }
+
+ function cleanupModifierEffects() {
+ effectCleanupFns.forEach(function (fn) {
+ return fn();
+ });
+ effectCleanupFns = [];
+ }
+
+ return instance;
+ };
+ }
+
+ var passive = {
+ passive: true
+ };
+
+ function effect$2(_ref) {
+ var state = _ref.state,
+ instance = _ref.instance,
+ options = _ref.options;
+ var _options$scroll = options.scroll,
+ scroll = _options$scroll === void 0 ? true : _options$scroll,
+ _options$resize = options.resize,
+ resize = _options$resize === void 0 ? true : _options$resize;
+ var window = getWindow(state.elements.popper);
+ var scrollParents = [].concat(state.scrollParents.reference, state.scrollParents.popper);
+
+ if (scroll) {
+ scrollParents.forEach(function (scrollParent) {
+ scrollParent.addEventListener('scroll', instance.update, passive);
+ });
+ }
+
+ if (resize) {
+ window.addEventListener('resize', instance.update, passive);
+ }
+
+ return function () {
+ if (scroll) {
+ scrollParents.forEach(function (scrollParent) {
+ scrollParent.removeEventListener('scroll', instance.update, passive);
+ });
+ }
+
+ if (resize) {
+ window.removeEventListener('resize', instance.update, passive);
+ }
+ };
+ } // eslint-disable-next-line import/no-unused-modules
+
+
+ var eventListeners = {
+ name: 'eventListeners',
+ enabled: true,
+ phase: 'write',
+ fn: function fn() {},
+ effect: effect$2,
+ data: {}
+ };
+
+ function popperOffsets(_ref) {
+ var state = _ref.state,
+ name = _ref.name;
+ // Offsets are the actual position the popper needs to have to be
+ // properly positioned near its reference element
+ // This is the most basic placement, and will be adjusted by
+ // the modifiers in the next step
+ state.modifiersData[name] = computeOffsets({
+ reference: state.rects.reference,
+ element: state.rects.popper,
+ strategy: 'absolute',
+ placement: state.placement
+ });
+ } // eslint-disable-next-line import/no-unused-modules
+
+
+ var popperOffsets$1 = {
+ name: 'popperOffsets',
+ enabled: true,
+ phase: 'read',
+ fn: popperOffsets,
+ data: {}
+ };
+
+ var unsetSides = {
+ top: 'auto',
+ right: 'auto',
+ bottom: 'auto',
+ left: 'auto'
+ }; // Round the offsets to the nearest suitable subpixel based on the DPR.
+ // Zooming can change the DPR, but it seems to report a value that will
+ // cleanly divide the values into the appropriate subpixels.
+
+ function roundOffsetsByDPR(_ref) {
+ var x = _ref.x,
+ y = _ref.y;
+ var win = window;
+ var dpr = win.devicePixelRatio || 1;
+ return {
+ x: round(round(x * dpr) / dpr) || 0,
+ y: round(round(y * dpr) / dpr) || 0
+ };
+ }
+
+ function mapToStyles(_ref2) {
+ var _Object$assign2;
+
+ var popper = _ref2.popper,
+ popperRect = _ref2.popperRect,
+ placement = _ref2.placement,
+ offsets = _ref2.offsets,
+ position = _ref2.position,
+ gpuAcceleration = _ref2.gpuAcceleration,
+ adaptive = _ref2.adaptive,
+ roundOffsets = _ref2.roundOffsets;
+
+ var _ref3 = roundOffsets === true ? roundOffsetsByDPR(offsets) : typeof roundOffsets === 'function' ? roundOffsets(offsets) : offsets,
+ _ref3$x = _ref3.x,
+ x = _ref3$x === void 0 ? 0 : _ref3$x,
+ _ref3$y = _ref3.y,
+ y = _ref3$y === void 0 ? 0 : _ref3$y;
+
+ var hasX = offsets.hasOwnProperty('x');
+ var hasY = offsets.hasOwnProperty('y');
+ var sideX = left;
+ var sideY = top;
+ var win = window;
+
+ if (adaptive) {
+ var offsetParent = getOffsetParent(popper);
+ var heightProp = 'clientHeight';
+ var widthProp = 'clientWidth';
+
+ if (offsetParent === getWindow(popper)) {
+ offsetParent = getDocumentElement(popper);
+
+ if (getComputedStyle(offsetParent).position !== 'static') {
+ heightProp = 'scrollHeight';
+ widthProp = 'scrollWidth';
+ }
+ } // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it
+
+
+ offsetParent = offsetParent;
+
+ if (placement === top) {
+ sideY = bottom; // $FlowFixMe[prop-missing]
+
+ y -= offsetParent[heightProp] - popperRect.height;
+ y *= gpuAcceleration ? 1 : -1;
+ }
+
+ if (placement === left) {
+ sideX = right; // $FlowFixMe[prop-missing]
+
+ x -= offsetParent[widthProp] - popperRect.width;
+ x *= gpuAcceleration ? 1 : -1;
+ }
+ }
+
+ var commonStyles = Object.assign({
+ position: position
+ }, adaptive && unsetSides);
+
+ if (gpuAcceleration) {
+ var _Object$assign;
+
+ return Object.assign({}, commonStyles, (_Object$assign = {}, _Object$assign[sideY] = hasY ? '0' : '', _Object$assign[sideX] = hasX ? '0' : '', _Object$assign.transform = (win.devicePixelRatio || 1) < 2 ? "translate(" + x + "px, " + y + "px)" : "translate3d(" + x + "px, " + y + "px, 0)", _Object$assign));
+ }
+
+ return Object.assign({}, commonStyles, (_Object$assign2 = {}, _Object$assign2[sideY] = hasY ? y + "px" : '', _Object$assign2[sideX] = hasX ? x + "px" : '', _Object$assign2.transform = '', _Object$assign2));
+ }
+
+ function computeStyles(_ref4) {
+ var state = _ref4.state,
+ options = _ref4.options;
+ var _options$gpuAccelerat = options.gpuAcceleration,
+ gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat,
+ _options$adaptive = options.adaptive,
+ adaptive = _options$adaptive === void 0 ? true : _options$adaptive,
+ _options$roundOffsets = options.roundOffsets,
+ roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets;
+
+ {
+ var transitionProperty = getComputedStyle(state.elements.popper).transitionProperty || '';
+
+ if (adaptive && ['transform', 'top', 'right', 'bottom', 'left'].some(function (property) {
+ return transitionProperty.indexOf(property) >= 0;
+ })) {
+ console.warn(['Popper: Detected CSS transitions on at least one of the following', 'CSS properties: "transform", "top", "right", "bottom", "left".', '\n\n', 'Disable the "computeStyles" modifier\'s `adaptive` option to allow', 'for smooth transitions, or remove these properties from the CSS', 'transition declaration on the popper element if only transitioning', 'opacity or background-color for example.', '\n\n', 'We recommend using the popper element as a wrapper around an inner', 'element that can have any CSS property transitioned for animations.'].join(' '));
+ }
+ }
+
+ var commonStyles = {
+ placement: getBasePlacement(state.placement),
+ popper: state.elements.popper,
+ popperRect: state.rects.popper,
+ gpuAcceleration: gpuAcceleration
+ };
+
+ if (state.modifiersData.popperOffsets != null) {
+ state.styles.popper = Object.assign({}, state.styles.popper, mapToStyles(Object.assign({}, commonStyles, {
+ offsets: state.modifiersData.popperOffsets,
+ position: state.options.strategy,
+ adaptive: adaptive,
+ roundOffsets: roundOffsets
+ })));
+ }
+
+ if (state.modifiersData.arrow != null) {
+ state.styles.arrow = Object.assign({}, state.styles.arrow, mapToStyles(Object.assign({}, commonStyles, {
+ offsets: state.modifiersData.arrow,
+ position: 'absolute',
+ adaptive: false,
+ roundOffsets: roundOffsets
+ })));
+ }
+
+ state.attributes.popper = Object.assign({}, state.attributes.popper, {
+ 'data-popper-placement': state.placement
+ });
+ } // eslint-disable-next-line import/no-unused-modules
+
+
+ var computeStyles$1 = {
+ name: 'computeStyles',
+ enabled: true,
+ phase: 'beforeWrite',
+ fn: computeStyles,
+ data: {}
+ };
+
+ // and applies them to the HTMLElements such as popper and arrow
+
+ function applyStyles(_ref) {
+ var state = _ref.state;
+ Object.keys(state.elements).forEach(function (name) {
+ var style = state.styles[name] || {};
+ var attributes = state.attributes[name] || {};
+ var element = state.elements[name]; // arrow is optional + virtual elements
+
+ if (!isHTMLElement(element) || !getNodeName(element)) {
+ return;
+ } // Flow doesn't support to extend this property, but it's the most
+ // effective way to apply styles to an HTMLElement
+ // $FlowFixMe[cannot-write]
+
+
+ Object.assign(element.style, style);
+ Object.keys(attributes).forEach(function (name) {
+ var value = attributes[name];
+
+ if (value === false) {
+ element.removeAttribute(name);
+ } else {
+ element.setAttribute(name, value === true ? '' : value);
+ }
+ });
+ });
+ }
+
+ function effect$1(_ref2) {
+ var state = _ref2.state;
+ var initialStyles = {
+ popper: {
+ position: state.options.strategy,
+ left: '0',
+ top: '0',
+ margin: '0'
+ },
+ arrow: {
+ position: 'absolute'
+ },
+ reference: {}
+ };
+ Object.assign(state.elements.popper.style, initialStyles.popper);
+ state.styles = initialStyles;
+
+ if (state.elements.arrow) {
+ Object.assign(state.elements.arrow.style, initialStyles.arrow);
+ }
+
+ return function () {
+ Object.keys(state.elements).forEach(function (name) {
+ var element = state.elements[name];
+ var attributes = state.attributes[name] || {};
+ var styleProperties = Object.keys(state.styles.hasOwnProperty(name) ? state.styles[name] : initialStyles[name]); // Set all values to an empty string to unset them
+
+ var style = styleProperties.reduce(function (style, property) {
+ style[property] = '';
+ return style;
+ }, {}); // arrow is optional + virtual elements
+
+ if (!isHTMLElement(element) || !getNodeName(element)) {
+ return;
+ }
+
+ Object.assign(element.style, style);
+ Object.keys(attributes).forEach(function (attribute) {
+ element.removeAttribute(attribute);
+ });
+ });
+ };
+ } // eslint-disable-next-line import/no-unused-modules
+
+
+ var applyStyles$1 = {
+ name: 'applyStyles',
+ enabled: true,
+ phase: 'write',
+ fn: applyStyles,
+ effect: effect$1,
+ requires: ['computeStyles']
+ };
+
+ function distanceAndSkiddingToXY(placement, rects, offset) {
+ var basePlacement = getBasePlacement(placement);
+ var invertDistance = [left, top].indexOf(basePlacement) >= 0 ? -1 : 1;
+
+ var _ref = typeof offset === 'function' ? offset(Object.assign({}, rects, {
+ placement: placement
+ })) : offset,
+ skidding = _ref[0],
+ distance = _ref[1];
+
+ skidding = skidding || 0;
+ distance = (distance || 0) * invertDistance;
+ return [left, right].indexOf(basePlacement) >= 0 ? {
+ x: distance,
+ y: skidding
+ } : {
+ x: skidding,
+ y: distance
+ };
+ }
+
+ function offset(_ref2) {
+ var state = _ref2.state,
+ options = _ref2.options,
+ name = _ref2.name;
+ var _options$offset = options.offset,
+ offset = _options$offset === void 0 ? [0, 0] : _options$offset;
+ var data = placements.reduce(function (acc, placement) {
+ acc[placement] = distanceAndSkiddingToXY(placement, state.rects, offset);
+ return acc;
+ }, {});
+ var _data$state$placement = data[state.placement],
+ x = _data$state$placement.x,
+ y = _data$state$placement.y;
+
+ if (state.modifiersData.popperOffsets != null) {
+ state.modifiersData.popperOffsets.x += x;
+ state.modifiersData.popperOffsets.y += y;
+ }
+
+ state.modifiersData[name] = data;
+ } // eslint-disable-next-line import/no-unused-modules
+
+
+ var offset$1 = {
+ name: 'offset',
+ enabled: true,
+ phase: 'main',
+ requires: ['popperOffsets'],
+ fn: offset
+ };
+
+ var hash$1 = {
+ left: 'right',
+ right: 'left',
+ bottom: 'top',
+ top: 'bottom'
+ };
+ function getOppositePlacement(placement) {
+ return placement.replace(/left|right|bottom|top/g, function (matched) {
+ return hash$1[matched];
+ });
+ }
+
+ var hash = {
+ start: 'end',
+ end: 'start'
+ };
+ function getOppositeVariationPlacement(placement) {
+ return placement.replace(/start|end/g, function (matched) {
+ return hash[matched];
+ });
+ }
+
+ function computeAutoPlacement(state, options) {
+ if (options === void 0) {
+ options = {};
+ }
+
+ var _options = options,
+ placement = _options.placement,
+ boundary = _options.boundary,
+ rootBoundary = _options.rootBoundary,
+ padding = _options.padding,
+ flipVariations = _options.flipVariations,
+ _options$allowedAutoP = _options.allowedAutoPlacements,
+ allowedAutoPlacements = _options$allowedAutoP === void 0 ? placements : _options$allowedAutoP;
+ var variation = getVariation(placement);
+ var placements$1 = variation ? flipVariations ? variationPlacements : variationPlacements.filter(function (placement) {
+ return getVariation(placement) === variation;
+ }) : basePlacements;
+ var allowedPlacements = placements$1.filter(function (placement) {
+ return allowedAutoPlacements.indexOf(placement) >= 0;
+ });
+
+ if (allowedPlacements.length === 0) {
+ allowedPlacements = placements$1;
+
+ {
+ console.error(['Popper: The `allowedAutoPlacements` option did not allow any', 'placements. Ensure the `placement` option matches the variation', 'of the allowed placements.', 'For example, "auto" cannot be used to allow "bottom-start".', 'Use "auto-start" instead.'].join(' '));
+ }
+ } // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions...
+
+
+ var overflows = allowedPlacements.reduce(function (acc, placement) {
+ acc[placement] = detectOverflow(state, {
+ placement: placement,
+ boundary: boundary,
+ rootBoundary: rootBoundary,
+ padding: padding
+ })[getBasePlacement(placement)];
+ return acc;
+ }, {});
+ return Object.keys(overflows).sort(function (a, b) {
+ return overflows[a] - overflows[b];
+ });
+ }
+
+ function getExpandedFallbackPlacements(placement) {
+ if (getBasePlacement(placement) === auto) {
+ return [];
+ }
+
+ var oppositePlacement = getOppositePlacement(placement);
+ return [getOppositeVariationPlacement(placement), oppositePlacement, getOppositeVariationPlacement(oppositePlacement)];
+ }
+
+ function flip(_ref) {
+ var state = _ref.state,
+ options = _ref.options,
+ name = _ref.name;
+
+ if (state.modifiersData[name]._skip) {
+ return;
+ }
+
+ var _options$mainAxis = options.mainAxis,
+ checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,
+ _options$altAxis = options.altAxis,
+ checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis,
+ specifiedFallbackPlacements = options.fallbackPlacements,
+ padding = options.padding,
+ boundary = options.boundary,
+ rootBoundary = options.rootBoundary,
+ altBoundary = options.altBoundary,
+ _options$flipVariatio = options.flipVariations,
+ flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio,
+ allowedAutoPlacements = options.allowedAutoPlacements;
+ var preferredPlacement = state.options.placement;
+ var basePlacement = getBasePlacement(preferredPlacement);
+ var isBasePlacement = basePlacement === preferredPlacement;
+ var fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipVariations ? [getOppositePlacement(preferredPlacement)] : getExpandedFallbackPlacements(preferredPlacement));
+ var placements = [preferredPlacement].concat(fallbackPlacements).reduce(function (acc, placement) {
+ return acc.concat(getBasePlacement(placement) === auto ? computeAutoPlacement(state, {
+ placement: placement,
+ boundary: boundary,
+ rootBoundary: rootBoundary,
+ padding: padding,
+ flipVariations: flipVariations,
+ allowedAutoPlacements: allowedAutoPlacements
+ }) : placement);
+ }, []);
+ var referenceRect = state.rects.reference;
+ var popperRect = state.rects.popper;
+ var checksMap = new Map();
+ var makeFallbackChecks = true;
+ var firstFittingPlacement = placements[0];
+
+ for (var i = 0; i < placements.length; i++) {
+ var placement = placements[i];
+
+ var _basePlacement = getBasePlacement(placement);
+
+ var isStartVariation = getVariation(placement) === start;
+ var isVertical = [top, bottom].indexOf(_basePlacement) >= 0;
+ var len = isVertical ? 'width' : 'height';
+ var overflow = detectOverflow(state, {
+ placement: placement,
+ boundary: boundary,
+ rootBoundary: rootBoundary,
+ altBoundary: altBoundary,
+ padding: padding
+ });
+ var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top;
+
+ if (referenceRect[len] > popperRect[len]) {
+ mainVariationSide = getOppositePlacement(mainVariationSide);
+ }
+
+ var altVariationSide = getOppositePlacement(mainVariationSide);
+ var checks = [];
+
+ if (checkMainAxis) {
+ checks.push(overflow[_basePlacement] <= 0);
+ }
+
+ if (checkAltAxis) {
+ checks.push(overflow[mainVariationSide] <= 0, overflow[altVariationSide] <= 0);
+ }
+
+ if (checks.every(function (check) {
+ return check;
+ })) {
+ firstFittingPlacement = placement;
+ makeFallbackChecks = false;
+ break;
+ }
+
+ checksMap.set(placement, checks);
+ }
+
+ if (makeFallbackChecks) {
+ // `2` may be desired in some cases – research later
+ var numberOfChecks = flipVariations ? 3 : 1;
+
+ var _loop = function _loop(_i) {
+ var fittingPlacement = placements.find(function (placement) {
+ var checks = checksMap.get(placement);
+
+ if (checks) {
+ return checks.slice(0, _i).every(function (check) {
+ return check;
+ });
+ }
+ });
+
+ if (fittingPlacement) {
+ firstFittingPlacement = fittingPlacement;
+ return "break";
+ }
+ };
+
+ for (var _i = numberOfChecks; _i > 0; _i--) {
+ var _ret = _loop(_i);
+
+ if (_ret === "break") break;
+ }
+ }
+
+ if (state.placement !== firstFittingPlacement) {
+ state.modifiersData[name]._skip = true;
+ state.placement = firstFittingPlacement;
+ state.reset = true;
+ }
+ } // eslint-disable-next-line import/no-unused-modules
+
+
+ var flip$1 = {
+ name: 'flip',
+ enabled: true,
+ phase: 'main',
+ fn: flip,
+ requiresIfExists: ['offset'],
+ data: {
+ _skip: false
+ }
+ };
+
+ function getAltAxis(axis) {
+ return axis === 'x' ? 'y' : 'x';
+ }
+
+ function within(min$1, value, max$1) {
+ return max(min$1, min(value, max$1));
+ }
+
+ function preventOverflow(_ref) {
+ var state = _ref.state,
+ options = _ref.options,
+ name = _ref.name;
+ var _options$mainAxis = options.mainAxis,
+ checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,
+ _options$altAxis = options.altAxis,
+ checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis,
+ boundary = options.boundary,
+ rootBoundary = options.rootBoundary,
+ altBoundary = options.altBoundary,
+ padding = options.padding,
+ _options$tether = options.tether,
+ tether = _options$tether === void 0 ? true : _options$tether,
+ _options$tetherOffset = options.tetherOffset,
+ tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset;
+ var overflow = detectOverflow(state, {
+ boundary: boundary,
+ rootBoundary: rootBoundary,
+ padding: padding,
+ altBoundary: altBoundary
+ });
+ var basePlacement = getBasePlacement(state.placement);
+ var variation = getVariation(state.placement);
+ var isBasePlacement = !variation;
+ var mainAxis = getMainAxisFromPlacement(basePlacement);
+ var altAxis = getAltAxis(mainAxis);
+ var popperOffsets = state.modifiersData.popperOffsets;
+ var referenceRect = state.rects.reference;
+ var popperRect = state.rects.popper;
+ var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset(Object.assign({}, state.rects, {
+ placement: state.placement
+ })) : tetherOffset;
+ var data = {
+ x: 0,
+ y: 0
+ };
+
+ if (!popperOffsets) {
+ return;
+ }
+
+ if (checkMainAxis || checkAltAxis) {
+ var mainSide = mainAxis === 'y' ? top : left;
+ var altSide = mainAxis === 'y' ? bottom : right;
+ var len = mainAxis === 'y' ? 'height' : 'width';
+ var offset = popperOffsets[mainAxis];
+ var min$1 = popperOffsets[mainAxis] + overflow[mainSide];
+ var max$1 = popperOffsets[mainAxis] - overflow[altSide];
+ var additive = tether ? -popperRect[len] / 2 : 0;
+ var minLen = variation === start ? referenceRect[len] : popperRect[len];
+ var maxLen = variation === start ? -popperRect[len] : -referenceRect[len]; // We need to include the arrow in the calculation so the arrow doesn't go
+ // outside the reference bounds
+
+ var arrowElement = state.elements.arrow;
+ var arrowRect = tether && arrowElement ? getLayoutRect(arrowElement) : {
+ width: 0,
+ height: 0
+ };
+ var arrowPaddingObject = state.modifiersData['arrow#persistent'] ? state.modifiersData['arrow#persistent'].padding : getFreshSideObject();
+ var arrowPaddingMin = arrowPaddingObject[mainSide];
+ var arrowPaddingMax = arrowPaddingObject[altSide]; // If the reference length is smaller than the arrow length, we don't want
+ // to include its full size in the calculation. If the reference is small
+ // and near the edge of a boundary, the popper can overflow even if the
+ // reference is not overflowing as well (e.g. virtual elements with no
+ // width or height)
+
+ var arrowLen = within(0, referenceRect[len], arrowRect[len]);
+ var minOffset = isBasePlacement ? referenceRect[len] / 2 - additive - arrowLen - arrowPaddingMin - tetherOffsetValue : minLen - arrowLen - arrowPaddingMin - tetherOffsetValue;
+ var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + tetherOffsetValue : maxLen + arrowLen + arrowPaddingMax + tetherOffsetValue;
+ var arrowOffsetParent = state.elements.arrow && getOffsetParent(state.elements.arrow);
+ var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent.clientTop || 0 : arrowOffsetParent.clientLeft || 0 : 0;
+ var offsetModifierValue = state.modifiersData.offset ? state.modifiersData.offset[state.placement][mainAxis] : 0;
+ var tetherMin = popperOffsets[mainAxis] + minOffset - offsetModifierValue - clientOffset;
+ var tetherMax = popperOffsets[mainAxis] + maxOffset - offsetModifierValue;
+
+ if (checkMainAxis) {
+ var preventedOffset = within(tether ? min(min$1, tetherMin) : min$1, offset, tether ? max(max$1, tetherMax) : max$1);
+ popperOffsets[mainAxis] = preventedOffset;
+ data[mainAxis] = preventedOffset - offset;
+ }
+
+ if (checkAltAxis) {
+ var _mainSide = mainAxis === 'x' ? top : left;
+
+ var _altSide = mainAxis === 'x' ? bottom : right;
+
+ var _offset = popperOffsets[altAxis];
+
+ var _min = _offset + overflow[_mainSide];
+
+ var _max = _offset - overflow[_altSide];
+
+ var _preventedOffset = within(tether ? min(_min, tetherMin) : _min, _offset, tether ? max(_max, tetherMax) : _max);
+
+ popperOffsets[altAxis] = _preventedOffset;
+ data[altAxis] = _preventedOffset - _offset;
+ }
+ }
+
+ state.modifiersData[name] = data;
+ } // eslint-disable-next-line import/no-unused-modules
+
+
+ var preventOverflow$1 = {
+ name: 'preventOverflow',
+ enabled: true,
+ phase: 'main',
+ fn: preventOverflow,
+ requiresIfExists: ['offset']
+ };
+
+ var toPaddingObject = function toPaddingObject(padding, state) {
+ padding = typeof padding === 'function' ? padding(Object.assign({}, state.rects, {
+ placement: state.placement
+ })) : padding;
+ return mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));
+ };
+
+ function arrow(_ref) {
+ var _state$modifiersData$;
+
+ var state = _ref.state,
+ name = _ref.name,
+ options = _ref.options;
+ var arrowElement = state.elements.arrow;
+ var popperOffsets = state.modifiersData.popperOffsets;
+ var basePlacement = getBasePlacement(state.placement);
+ var axis = getMainAxisFromPlacement(basePlacement);
+ var isVertical = [left, right].indexOf(basePlacement) >= 0;
+ var len = isVertical ? 'height' : 'width';
+
+ if (!arrowElement || !popperOffsets) {
+ return;
+ }
+
+ var paddingObject = toPaddingObject(options.padding, state);
+ var arrowRect = getLayoutRect(arrowElement);
+ var minProp = axis === 'y' ? top : left;
+ var maxProp = axis === 'y' ? bottom : right;
+ var endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len];
+ var startDiff = popperOffsets[axis] - state.rects.reference[axis];
+ var arrowOffsetParent = getOffsetParent(arrowElement);
+ var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0;
+ var centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is
+ // outside of the popper bounds
+
+ var min = paddingObject[minProp];
+ var max = clientSize - arrowRect[len] - paddingObject[maxProp];
+ var center = clientSize / 2 - arrowRect[len] / 2 + centerToReference;
+ var offset = within(min, center, max); // Prevents breaking syntax highlighting...
+
+ var axisProp = axis;
+ state.modifiersData[name] = (_state$modifiersData$ = {}, _state$modifiersData$[axisProp] = offset, _state$modifiersData$.centerOffset = offset - center, _state$modifiersData$);
+ }
+
+ function effect(_ref2) {
+ var state = _ref2.state,
+ options = _ref2.options;
+ var _options$element = options.element,
+ arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element;
+
+ if (arrowElement == null) {
+ return;
+ } // CSS selector
+
+
+ if (typeof arrowElement === 'string') {
+ arrowElement = state.elements.popper.querySelector(arrowElement);
+
+ if (!arrowElement) {
+ return;
+ }
+ }
+
+ {
+ if (!isHTMLElement(arrowElement)) {
+ console.error(['Popper: "arrow" element must be an HTMLElement (not an SVGElement).', 'To use an SVG arrow, wrap it in an HTMLElement that will be used as', 'the arrow.'].join(' '));
+ }
+ }
+
+ if (!contains(state.elements.popper, arrowElement)) {
+ {
+ console.error(['Popper: "arrow" modifier\'s `element` must be a child of the popper', 'element.'].join(' '));
+ }
+
+ return;
+ }
+
+ state.elements.arrow = arrowElement;
+ } // eslint-disable-next-line import/no-unused-modules
+
+
+ var arrow$1 = {
+ name: 'arrow',
+ enabled: true,
+ phase: 'main',
+ fn: arrow,
+ effect: effect,
+ requires: ['popperOffsets'],
+ requiresIfExists: ['preventOverflow']
+ };
+
+ function getSideOffsets(overflow, rect, preventedOffsets) {
+ if (preventedOffsets === void 0) {
+ preventedOffsets = {
+ x: 0,
+ y: 0
+ };
+ }
+
+ return {
+ top: overflow.top - rect.height - preventedOffsets.y,
+ right: overflow.right - rect.width + preventedOffsets.x,
+ bottom: overflow.bottom - rect.height + preventedOffsets.y,
+ left: overflow.left - rect.width - preventedOffsets.x
+ };
+ }
+
+ function isAnySideFullyClipped(overflow) {
+ return [top, right, bottom, left].some(function (side) {
+ return overflow[side] >= 0;
+ });
+ }
+
+ function hide(_ref) {
+ var state = _ref.state,
+ name = _ref.name;
+ var referenceRect = state.rects.reference;
+ var popperRect = state.rects.popper;
+ var preventedOffsets = state.modifiersData.preventOverflow;
+ var referenceOverflow = detectOverflow(state, {
+ elementContext: 'reference'
+ });
+ var popperAltOverflow = detectOverflow(state, {
+ altBoundary: true
+ });
+ var referenceClippingOffsets = getSideOffsets(referenceOverflow, referenceRect);
+ var popperEscapeOffsets = getSideOffsets(popperAltOverflow, popperRect, preventedOffsets);
+ var isReferenceHidden = isAnySideFullyClipped(referenceClippingOffsets);
+ var hasPopperEscaped = isAnySideFullyClipped(popperEscapeOffsets);
+ state.modifiersData[name] = {
+ referenceClippingOffsets: referenceClippingOffsets,
+ popperEscapeOffsets: popperEscapeOffsets,
+ isReferenceHidden: isReferenceHidden,
+ hasPopperEscaped: hasPopperEscaped
+ };
+ state.attributes.popper = Object.assign({}, state.attributes.popper, {
+ 'data-popper-reference-hidden': isReferenceHidden,
+ 'data-popper-escaped': hasPopperEscaped
+ });
+ } // eslint-disable-next-line import/no-unused-modules
+
+
+ var hide$1 = {
+ name: 'hide',
+ enabled: true,
+ phase: 'main',
+ requiresIfExists: ['preventOverflow'],
+ fn: hide
+ };
+
+ var defaultModifiers$1 = [eventListeners, popperOffsets$1, computeStyles$1, applyStyles$1];
+ var createPopper$1 = /*#__PURE__*/popperGenerator({
+ defaultModifiers: defaultModifiers$1
+ }); // eslint-disable-next-line import/no-unused-modules
+
+ var defaultModifiers = [eventListeners, popperOffsets$1, computeStyles$1, applyStyles$1, offset$1, flip$1, preventOverflow$1, arrow$1, hide$1];
+ var createPopper = /*#__PURE__*/popperGenerator({
+ defaultModifiers: defaultModifiers
+ }); // eslint-disable-next-line import/no-unused-modules
+
+ exports.applyStyles = applyStyles$1;
+ exports.arrow = arrow$1;
+ exports.computeStyles = computeStyles$1;
+ exports.createPopper = createPopper;
+ exports.createPopperLite = createPopper$1;
+ exports.defaultModifiers = defaultModifiers;
+ exports.detectOverflow = detectOverflow;
+ exports.eventListeners = eventListeners;
+ exports.flip = flip$1;
+ exports.hide = hide$1;
+ exports.offset = offset$1;
+ exports.popperGenerator = popperGenerator;
+ exports.popperOffsets = popperOffsets$1;
+ exports.preventOverflow = preventOverflow$1;
+
+ Object.defineProperty(exports, '__esModule', { value: true });
+
+})));
diff --git a/src/main/resources/com/fr/fineui/core/9.worker.js b/src/main/resources/com/fr/fineui/core/9.worker.js
new file mode 100644
index 0000000..e23d73c
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/9.worker.js
@@ -0,0 +1,47 @@
+!(function () {
+ BI.initWorker = function () {
+ function createWatcher (model, keyOrFn, cb, options) {
+ options = options || {};
+ return Fix.watch(model, keyOrFn, cb, BI.extend(options, {
+ store: model
+ }));
+ }
+
+ var models = {}, watches = {};
+ addEventListener("message", function (e) {
+ var data = e.data;
+ switch (data.eventType) {
+ case "action":
+ models[data.name][data.action].apply(models[data.name], data.args);
+ break;
+ case "destroy":
+ BI.each(watches[data.name], function (i, unwatches) {
+ unwatches = BI.isArray(unwatches) ? unwatches : [unwatches];
+ BI.each(unwatches, function (j, unwatch) {
+ unwatch();
+ });
+ });
+ delete models[data.name];
+ delete watches[data.name];
+ break;
+ case "create":
+ var store = models[data.name] = BI.Models.getModel(data.type, data.options);
+ watches[data.name] = [];
+ BI.each(data.watches, function (i, key) {
+ watches[data.name].push(createWatcher(store.model, key, function (newValue, oldValue) {
+ postMessage(BI.extend({}, data, {
+ eventType: "watch",
+ currentWatchType: key
+ }, {args: [newValue, oldValue]}));
+ }));
+ });
+ postMessage(BI.extend({}, data, {
+ eventType: "create"
+ }, {msg: store.model}));
+ break;
+ default:
+ break;
+ }
+ }, false);
+ };
+}());
diff --git a/src/main/resources/com/fr/fineui/core/__test__/alias.test.js b/src/main/resources/com/fr/fineui/core/__test__/alias.test.js
new file mode 100644
index 0000000..5416bd9
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/__test__/alias.test.js
@@ -0,0 +1,135 @@
+/**
+ * Created by windy on 2018/01/23.
+ */
+describe("aliasFunctionTest", function () {
+
+ before(function () {
+ BI.specialCharsMap = {
+ "\\\\": "0",
+ ".": "1",
+ "/": "2"
+ };
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("htmlEncode和htmlDecode", function () {
+
+ var targetString = "1 2& ";
+ var encodeString = BI.htmlEncode(targetString);
+ expect(encodeString).to.equal("<a>1 2&</a>");
+ expect(BI.htmlDecode(encodeString)).to.equal(targetString);
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("encodeURIComponent和decodeURIComponent", function () {
+ var targetString = "tableName./\\";
+ var encodeString = BI.encodeURIComponent(targetString);
+ expect(encodeString).to.equal("tableName120");
+ expect(BI.decodeURIComponent(encodeString)).to.equal(targetString);
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("cjkEncode 和 cjkDecode ", function () {
+ expect(BI.cjkEncode("测试")).to.eql("[6d4b][8bd5]");
+ expect(BI.cjkEncode(123)).to.eql(123);
+ expect(BI.cjkDecode("[6d4b][8bd5]")).to.eql("测试");
+ expect(BI.cjkDecode("6d4b 8bd5")).to.eql("6d4b 8bd5");
+ expect(BI.cjkDecode(null)).to.eql("");
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("jsonEncode 和 jsonDecode", function () {
+ var jsonString = '{"a":1,"b":"测\\"试","c":[5,6],"d":null,"e":false}';
+ var obj = {
+ a: 1,
+ b: '测"试',
+ c: [5, 6],
+ d: null,
+ e: false,
+ };
+ expect(BI.jsonEncode(obj)).to.eql(jsonString);
+ expect(BI.jsonDecode(jsonString)).to.eql(obj);
+
+ expect(BI.jsonEncode({ a: function(){return 1} })).to.eql('{"a":function(){return 1}}');
+ expect(BI.jsonDecode("{__time__:878313600000}")).to.eql(new Date(878313600000));
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("getEncodeURL", function () {
+ expect(BI.getEncodeURL("design/{tableName}/{fieldName}",{tableName: "A", fieldName: "a"})).to.eql("design/A/a");
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("contentFormat", function () {
+ expect(BI.contentFormat("", "DTyyyy-MM-dd")).to.eql("");
+ expect(BI.contentFormat(878313600000, "")).to.eql("878313600000");
+ expect(BI.contentFormat("test", "T")).to.eql("test");
+ expect(BI.contentFormat(878313600000, "E")).to.eql("9E11");
+ expect(BI.contentFormat(1000.23456789, "0,000.####")).to.eql("1,000.2346");
+ expect(BI.contentFormat(879051600000, "DTyyyy-MM-dd")).to.eql("1997-11-09");
+ expect(BI.contentFormat(879051600000, "DTyyyy-MM-dd HH:mm:ss a")).to.eql("1997-11-09 13:00:00 pm");
+ expect(BI.contentFormat(879051600000, "DTyyyy-MM-dd hh:mm:ss a")).to.eql("1997-11-09 01:00:00 pm");
+ expect(BI.contentFormat(879051600000, "DTyyy-M-d H:m:s a")).to.eql("97-11-9 13:0:0 pm");
+ expect(BI.contentFormat(879048000000, "DTyyy-M-d h:m:s a")).to.eql("97-11-9 12:0:0 pm");
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("parseFmt", function () {
+ expect(BI.parseFmt("yyyy-MM-dd HH:mm:ss")).to.eql("%Y-%X-%d %H:%M:%S");
+ expect(BI.parseFmt("yyyy-MM-d hh:mm:ss")).to.eql("%Y-%X-%e %I:%M:%S");
+ expect(BI.parseFmt("")).to.eql("");
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("str2Date", function () {
+ expect(BI.str2Date('2013-12-12', 'yyyy-MM-dd')).to.eql(new Date(2013, 11, 12));
+ expect(BI.str2Date('2013-12-12', 123)).to.eql(null);
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("object2Number", function () {
+ expect(BI.object2Number(null)).to.eql(0);
+ expect(BI.object2Number(123)).to.eql(123);
+ expect(BI.object2Number("1.23")).to.eql(1.23);
+ expect(BI.object2Number({ a: 2 })).to.eql(NaN);
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("object2Date", function () {
+ expect(BI.object2Date(null)).to.eql(new Date());
+ expect(BI.object2Date(new Date(1997, 10, 9))).to.eql(new Date(1997, 10, 9));
+ expect(BI.object2Date(879051600000)).to.eql(new Date(879051600000));
+ expect(BI.object2Time("1997-11-09")).to.eql(new Date(1997, 10, 9));
+ expect(BI.object2Date({ a: 2 })).to.eql(new Date());
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("object2Time", function () {
+ expect(BI.object2Time(null)).to.eql(new Date());
+ expect(BI.object2Time(new Date(1997, 11, 9))).to.eql(new Date(1997, 11, 9));
+ expect(BI.object2Time("1997-11-09 13:00:00")).to.eql(new Date(1997, 10, 9, 13, 0, 0));
+ expect(BI.object2Time("13:00:00")).to.eql(new Date(1970, 0, 1, 13, 0, 0));
+ });
+});
diff --git a/src/main/resources/com/fr/fineui/core/__test__/base.test.js b/src/main/resources/com/fr/fineui/core/__test__/base.test.js
new file mode 100644
index 0000000..558191d
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/__test__/base.test.js
@@ -0,0 +1,444 @@
+/**
+ * Created by windy on 2018/01/24.
+ */
+describe("baseFunctionTest", function () {
+
+ before(function () {
+
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("formatEl和stripEL", function () {
+ var obj1 = {
+ type: "a",
+ pro: {},
+ items: []
+ };
+
+ var obj2 = {
+ el: {
+ type: "a",
+ pro: {},
+ items: []
+ }
+ };
+ expect(BI.formatEL(obj1)).to.deep.equal(obj2);
+ expect(BI.formatEL(obj2)).to.deep.equal(obj2);
+ expect(BI.stripEL(obj1)).to.deep.equal(obj1);
+ expect(BI.stripEL(obj2)).to.deep.equal(obj1);
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("encodeURIComponent和decodeURIComponent", function () {
+ var targetString = "tableName./\\";
+ var encodeString = BI.encodeURIComponent(targetString);
+ expect(encodeString).to.equal("tableName120");
+ expect(BI.decodeURIComponent(encodeString)).to.equal(targetString);
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("count", function () {
+ var a = [];
+ expect(BI.count(1, 100)).to.equal(99);
+ BI.count(0, 100, function (v) {
+ a[v] = 0;
+ });
+ expect(a.length).to.equal(100);
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("concat", function () {
+ expect(BI.concat([1], [2])).to.deep.equal([1, 2]);
+ expect(BI.concat(1, 2)).to.equal("12");
+ expect(BI.concat("1", "2")).to.equal("12");
+ expect(BI.concat({a: 1}, {b: 1})).to.deep.equal({a: 1, b: 1});
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("remove", function () {
+ var a = [1, 2, 3, 4, 5, 6];
+ BI.remove(a, function (i, v) {
+ return v === 4;
+ });
+ expect(a).to.deep.equal([1, 2, 3, 5, 6]);
+ var b = {
+ a: 1,
+ b: 2
+ };
+ BI.remove(b, function (key) {
+ return key === "a";
+ });
+ expect(b).to.deep.equal({
+ b: 2
+ });
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("removeAt", function () {
+ var a = [1, 2, 3, 4, 5, 6, 7, 8, 9];
+ BI.removeAt(a, 2);
+ expect(a).to.deep.equal([1, 2, 4, 5, 6, 7, 8, 9]);
+ var b = {
+ a: 1,
+ b: 2
+ };
+ BI.removeAt(b, "a");
+ expect(b).to.deep.equal({
+ b: 2
+ });
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("makeArray", function () {
+ var a = BI.makeArray(2, 1);
+ expect(a).to.deep.equal([1, 1]);
+ });
+
+
+ /**
+ * test_author_windy
+ */
+ it("concat-string", function () {
+ // concat-string
+ expect(BI.concat("a", "b", "c")).to.equal("abc");
+
+ // concat-array
+ expect(BI.concat([1], [2], [3])).to.deep.equal([1, 2, 3]);
+
+ // concat-object-array
+ expect(BI.concat([{text: 1, value: 1}], [{text: 2, value: 2}], [{text: 3, value: 3}])).to.deep.equal([{text: 1, value: 1}, {text: 2, value: 2}, {text: 3, value: 3}]);
+
+ // concat-object
+ expect(BI.concat({a: 1}, {b: 2}, {c: 3})).to.deep.equal({
+ a: 1,
+ b: 2,
+ c: 3
+ });
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("assert-warning", function () {
+ expect(BI.assert("a", "a")).to.equal(true);
+ expect(BI.assert("a", function (v) {
+ return v === "a";
+ })).to.equal(true);
+ var test = "";
+ try {
+ BI.assert("a", function (v) {
+ return v === "c";
+ });
+ } catch (e) {
+ test = true;
+ }
+ expect(test).to.equal(true);
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("packageItems", function () {
+ expect(BI.packageItems([{
+ type: "a",
+ text: "b"
+ }, {
+ type: "b",
+ text: "c"
+ }], [{
+ type: "bi.vertical"
+ }, {
+ type: "bi.center_adapt"
+ }])).to.deep.equal([{
+ type: "bi.vertical",
+ items: [{
+ el: {
+ type: "bi.center_adapt",
+ items: [{
+ el: {
+ type: "a",
+ text: "b"
+ }
+ }]
+ }
+ }]
+ }, {
+ type: "bi.vertical",
+ items: [{
+ el: {
+ type: "bi.center_adapt",
+ items: [{
+ el: {
+ type: "b",
+ text: "c"
+ }
+ }]
+ }
+ }]
+ }]);
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("inverse", function () {
+ expect(BI.inverse(7, 1)).to.equal(6);
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("key", function () {
+ var a = {
+ c: 1,
+ d: 2
+ };
+ expect(BI.firstKey(a)).to.equal("c");
+ expect(BI.lastKey(a)).to.equal("d");
+ expect(BI.firstObject(a)).to.equal(1);
+ expect(BI.lastObject(a)).to.equal(2);
+ });
+
+
+ /**
+ * test_author_windy
+ */
+ it("back", function () {
+ var a = [{
+ c: 1,
+ d: 2
+ }, {
+ c: 3,
+ d: 4
+ }, {
+ c: 5,
+ d: 6
+ }];
+ var c = [];
+ BI.backEach(a, function (idx, v) {
+ c.push(v.d);
+ });
+ expect(c).to.deep.equal([6, 4, 2]);
+ expect(BI.backEvery(a, function (idx, v) {
+ return v.c = 1;
+ })).to.equal(true);
+ expect(BI.backFindKey({
+ c: 5,
+ d: 6
+ }, function (value, key) {
+ return key === "c";
+ })).to.equal("c");
+ expect(BI.backFind({
+ c: 5,
+ d: 6
+ }, function (v, key) {
+ return v === 5;
+ })).to.deep.equal(5);
+ });
+
+
+ /**
+ * test_author_windy
+ */
+ it("others", function () {
+ expect(BI.abc2Int("B")).to.equal(2);
+ expect(BI.int2Abc(2)).to.equal("B");
+ expect(BI.has({a: "1", b: "2"}, "a")).to.equal(true);
+ expect(Object.isFrozen(BI.freeze({a: "1", b: "2"}))).to.equal(true);
+ expect(BI.isCapitalEqual("A", "a")).to.equal(true);
+ expect(BI.isEmptyString("a")).to.equal(false);
+ expect(BI.isNotEmptyString("a")).to.equal(true);
+ expect(BI.isWindow("a")).to.equal(false);
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("deepFunc", function () {
+ expect(BI.isDeepMatch({b: {c: {e: 3}}, d:2}, {b: {c: {e: 3}}, d:2})).to.equal(true);
+ expect(BI.deepIndexOf([{a: 1}, {b: 2}, {c: 3}], {c: 3})).to.equal(2);
+ var remove = [{a: 1}, {b: 2}, {c: 3}];
+ BI.deepRemove(remove, {c: 3});
+ expect(remove).to.deep.equal([{a: 1}, {b: 2}]);
+ expect(BI.deepWithout([{a: 1}, {b: 2}, {c: 3}], {c: 3})).to.deep.equal([{a: 1}, {b: 2}]);
+ expect(BI.deepUnique([{c: 3}, {a: 1}, {b: 2}, {c: 3}, {c: 3}])).to.deep.equal([{c: 3}, {a: 1}, {b: 2}]);
+ expect(BI.deepDiff({a: 1, b: 2}, {a: 1, c: 2})).to.deep.equal(["b", "c"]);
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("number", function () {
+ expect(BI.parseSafeInt(9007199254740992)).to.equal(9007199254740991);
+ expect(BI.isNegativeInteger(-3)).to.equal(true);
+ expect(BI.isFloat(1.2)).to.equal(true);
+ expect(BI.isOdd(1)).to.equal(true);
+ expect(BI.isOdd("a")).to.equal(false);
+ expect(BI.isEven("a")).to.equal(false);
+ expect(BI.isEven(2)).to.equal(true);
+ expect(BI.sum([1, 2, 3, 4, 5, 6, 7])).to.equal(28);
+ expect(BI.average([1, 2, 3, 4, 5, 6, 7])).to.equal(4);
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("string", function () {
+ expect(BI.toLowerCase("AAAAA")).to.equal("aaaaa");
+ expect(BI.isLiteral("AAAAA")).to.equal(false);
+ expect(BI.stripQuotes("AAAAA")).to.equal("AAAAA");
+ expect(BI.camelize("background-color")).to.equal("backgroundColor");
+ expect(BI.escape("'\\")).to.equal("\\'\\\\");
+ expect(BI.leftPad("123", 5, "0")).to.equal("00123");
+ const cls = "my-class", text = "Some text";
+ expect(BI.format("{1}
", cls, text)).to.equal("Some text
");
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("checkDateVoid", function () {
+ const minDate = "1900-02-02";
+ const maxDate = "2099-11-29";
+ expect(BI.checkDateVoid(1899, 2, 2, minDate, maxDate)).to.eql(["y"]);
+ expect(BI.checkDateVoid(2100, 2, 2, minDate, maxDate)).to.eql(["y", 1]);
+ expect(BI.checkDateVoid(1900, 1, 2, minDate, maxDate)).to.eql(["m"]);
+ expect(BI.checkDateVoid(2099, 12, 2, minDate, maxDate)).to.eql(["m", 1]);
+ expect(BI.checkDateVoid(1900, 2, 1, minDate, maxDate)).to.eql(["d"]);
+ expect(BI.checkDateVoid(2099, 11, 30, minDate, maxDate)).to.eql(["d", 1]);
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("parseDateTime", function () {
+ expect(BI.parseDateTime("19971109", "%y%x%d")).to.eql(BI.getDate(1997, 10, 9));
+ expect(BI.parseDateTime("12:34:56", "%H:%M:%S")).to.eql(BI.getDate(1935, 0, 25, 12, 34, 56));
+ expect(BI.parseDateTime("1997-11-09 3:23:23 pm", "%y-%x-%d %H:%M:%S %P")).to.eql(BI.getDate(1997, 10, 9, 15, 23, 23));
+ expect(BI.parseDateTime("1997-11-09 15:23:23 am", "%y-%x-%d %H:%M:%S %P")).to.eql(BI.getDate(1997, 10, 9, 3, 23, 23));
+ expect(BI.parseDateTime("a-b-c d:e:f", "%y-%x-%d %H:%M:%S").toString()).to.eql(BI.getDate().toString());
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("getDate 和 getTime", function () {
+ expect(BI.getDate().toString()).to.eql(new Date().toString());
+ expect(BI.getDate(1997)).to.eql(new Date(1997));
+ expect(BI.getDate(1997, 10)).to.eql(new Date(1997, 10));
+ expect(BI.getDate(1997, 10, 9)).to.eql(new Date(1997, 10, 9));
+ expect(BI.getDate(1997, 10, 9, 12)).to.eql(new Date(1997, 10, 9, 12));
+ expect(BI.getDate(1997, 10, 9, 12, 34)).to.eql(new Date(1997, 10, 9, 12, 34));
+ expect(BI.getDate(1997, 10, 9, 12, 34, 56)).to.eql(new Date(1997, 10, 9, 12, 34, 56));
+ expect(BI.getDate(1997, 10, 9, 12, 34, 56, 78)).to.eql(new Date(1997, 10, 9, 12, 34, 56, 78));
+ expect(BI.getTime()).to.eql(new Date().getTime());
+ expect(BI.getTime(1997)).to.eql(new Date(1997).getTime());
+ expect(BI.getTime(1997, 10)).to.eql(new Date(1997, 10).getTime());
+ expect(BI.getTime(1997, 10, 9)).to.eql(new Date(1997, 10, 9).getTime());
+ expect(BI.getTime(1997, 10, 9, 12)).to.eql(new Date(1997, 10, 9, 12).getTime());
+ expect(BI.getTime(1997, 10, 9, 12, 34)).to.eql(new Date(1997, 10, 9, 12, 34).getTime());
+ expect(BI.getTime(1997, 10, 9, 12, 34, 56)).to.eql(new Date(1997, 10, 9, 12, 34, 56).getTime());
+ expect(BI.getTime(1997, 10, 9, 12, 34, 56, 78)).to.eql(new Date(1997, 10, 9, 12, 34, 56, 78).getTime());
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("数字相关方法补充", function () {
+ const iteratee = function (a, b) {
+ return a > b ? a : b;
+ };
+ expect(BI.isNaturalNumber(1.25)).to.eql(false);
+ expect(BI.isPositiveInteger(-15)).to.eql(false);
+ expect(BI.isNegativeInteger(+15)).to.eql(false);
+ expect(BI.isFloat(15)).to.eql(false);
+ expect(BI.sum([4, 3, 2, 1], iteratee)).to.eql(12);
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("集合相关方法补充", function () {
+ const array = [{
+ user: "barney",
+ active: true,
+ }, {
+ user: "fred",
+ active: false,
+ }, {
+ user: "pebbles",
+ active: false,
+ }];
+ expect(BI.backEvery(array, (index, value) => value.user === "kobi")).to.eql(false);
+ expect(BI.backFind(array, ["active", false])).to.eql(array[2]);
+ expect(BI.abc2Int("ABCD999")).to.eql(0);
+ expect(BI.int2Abc(0)).to.eql("");
+ expect(BI.int2Abc(26)).to.eql("Z");
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("数组相关方法补充", function () {
+ expect(BI.makeArrayByArray([], 5)).to.eql([]);
+ expect(BI.uniq(null, true, (a, b) => a > b)).to.eql([]);
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("对象相关方法补充", function () {
+ var obj = {
+ a: 1,
+ b: 2,
+ c: 3,
+ };
+ expect(BI.has(obj, [])).to.eql(false);
+ expect(BI.has(obj, ["a", "b"])).to.eql(true);
+ expect(BI.freeze("1")).to.eql("1");
+ });
+
+ /**
+ * test_author_kobi
+ **/
+ it("deep方法补充", function () {
+ var obj = {
+ a: 1,
+ b: 2,
+ c: {
+ d: 3,
+ e: {
+ f: 4,
+ },
+ },
+ };
+ expect(BI.isDeepMatch(null, { d: 3, e: { f: 4 } })).to.eql(false);
+ expect(BI.isDeepMatch(obj, { d: 3, e: { f: 5 } })).to.eql(false);
+ expect(BI.deepIndexOf(obj, { d: 3, e: { f: 5 } })).to.eql(-1);
+ expect(BI.deepRemove(obj, { d: 3, e: { f: 4 } })).to.eql(true);
+ expect(BI.deepWithout(obj, { d: 3, e: { f: 4 } })).to.eql({ a: 1, b: 2 });
+ });
+
+ /**
+ * test_author_teller
+ * 只传一个时分秒format的时间进去后,在某些情况下,返回的是当前时间,然而想要的是返回正确的时分秒
+ */
+ it("parseDateTime2", function () {
+ var date = BI.getDate();
+ expect(BI.parseDateTime("14:13:16", "%H:%M:%S").getTime()).to.eql(BI.getDate(date.getFullYear(), date.getMonth(), 14, 14, 13, 16).getTime());
+ });
+});
diff --git a/src/main/resources/com/fr/fineui/core/__test__/context.test.js b/src/main/resources/com/fr/fineui/core/__test__/context.test.js
new file mode 100644
index 0000000..8038fa9
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/__test__/context.test.js
@@ -0,0 +1,322 @@
+/**
+ * Created by guy on 2018/01/23.
+ */
+describe("contextTest", function () {
+
+ before(function () {
+ });
+
+ /**
+ * test_author_guy
+ */
+ it("context测试", function () {
+
+ var ParentStore = BI.inherit(Fix.Model, {
+ state: function () {
+ return {
+ context: "默认context"
+ };
+ },
+ childContext: ["context"]
+ });
+ BI.model("demo.model.parent_store", ParentStore);
+
+ var ChildStore = BI.inherit(Fix.Model, {
+ context: ["context"],
+ computed: {
+ currContext: function () {
+ return this.model.context;
+ }
+ },
+ actions: {
+ changeContext: function () {
+ this.model.context = "改变后的context";
+ }
+ }
+ });
+ BI.model("demo.model.child_store", ChildStore);
+
+ var Label = BI.inherit(BI.Widget, {
+ _store: function () {
+ return BI.Models.getModel("demo.model.child_store");
+ },
+ render: function () {
+ return {
+ type: "bi.label",
+ text: this.model.currContext
+ };
+ }
+ });
+ BI.shortcut("demo.label", Label);
+
+ var Demo = BI.inherit(BI.Widget, {
+ _store: function () {
+ return BI.Models.getModel("demo.model.parent_store");
+ },
+
+ render: function () {
+ var self = this;
+ return {
+ type: "demo.label",
+ ref: function (_ref) {
+ self.child = _ref;
+ }
+ };
+ }
+ });
+ BI.shortcut("demo.demo", Demo);
+
+ var demo = BI.Test.createWidget({
+ type: "demo.demo"
+ });
+
+ expect(demo.child.model.currContext).to.equal("默认context");
+ demo.child.store.changeContext();
+ expect(demo.model.context).to.equal("改变后的context");
+ demo.destroy();
+
+ });
+
+ /**
+ * test_author_guy
+ */
+ it("异步context测试-loader的populate测试", function (done) {
+
+ var ParentStore = BI.inherit(Fix.Model, {
+ state: function () {
+ return {
+ context: "默认context"
+ };
+ },
+ childContext: ["context"]
+ });
+ BI.model("demo.model.parent_store", ParentStore);
+
+ var ChildStore = BI.inherit(Fix.Model, {
+ context: ["context"],
+ computed: {
+ currContext: function () {
+ return this.model.context;
+ }
+ },
+ actions: {
+ changeContext: function () {
+ this.model.context = "改变后的context";
+ }
+ }
+ });
+ BI.model("demo.model.child_store", ChildStore);
+
+ var Label = BI.inherit(BI.Widget, {
+ _store: function () {
+ return BI.Models.getModel("demo.model.child_store");
+ },
+ render: function () {
+ return {
+ type: "bi.label",
+ text: this.model.currContext
+ };
+ }
+ });
+ BI.shortcut("demo.label", Label);
+
+ var Demo = BI.inherit(BI.Widget, {
+ _store: function () {
+ return BI.Models.getModel("demo.model.parent_store");
+ },
+
+ render: function () {
+ var items = BI.map([1, 2, 3], function (i, v) {
+ return {
+ text: v,
+ value: v,
+ title: v
+ };
+ });
+ var self = this;
+ this.labels = {};
+ return {
+ type: "bi.loader",
+ ref: function (_ref) {
+ self.child = _ref;
+ },
+ itemsCreator: function (options, populate) {
+ setTimeout(function () {
+ populate(BI.map(items.slice((options.times - 1) * 10, options.times * 10), function (i, v) {
+ return BI.extend(v, {
+ type: "demo.label",
+ ref: function (_ref) {
+ self.labels[i] = _ref;
+ },
+ height: 25
+ });
+ }));
+ }, 0);
+ },
+ hasNext: function (options) {
+ return options.times * 10 < items.length;
+ }
+ };
+ }
+ });
+ BI.shortcut("demo.demo", Demo);
+
+ var demo = BI.Test.createWidget({
+ type: "demo.demo"
+ });
+ demo.child.populate();
+
+ setTimeout(function () {
+ expect(demo.labels[0].model.currContext).to.equal("默认context");
+ demo.destroy();
+ done();
+ }, 0);
+ });
+
+ /**
+ * test_author_guy
+ */
+ it("异步context测试-beforeInit测试", function (done) {
+
+ var ParentStore = BI.inherit(Fix.Model, {
+ state: function () {
+ return {
+ context: "默认context"
+ };
+ },
+ childContext: ["context"]
+ });
+ BI.model("demo.model.parent_store", ParentStore);
+
+ var ChildStore = BI.inherit(Fix.Model, {
+ context: ["context"],
+ computed: {
+ currContext: function () {
+ return this.model.context;
+ }
+ },
+ actions: {
+ changeContext: function () {
+ this.model.context = "改变后的context";
+ }
+ }
+ });
+ BI.model("demo.model.child_store", ChildStore);
+
+ var Label = BI.inherit(BI.Widget, {
+ _store: function () {
+ return BI.Models.getModel("demo.model.child_store");
+ },
+ render: function () {
+ return {
+ type: "bi.label",
+ text: this.model.currContext
+ };
+ }
+ });
+ BI.shortcut("demo.label", Label);
+
+ var Demo = BI.inherit(BI.Widget, {
+ _store: function () {
+ return BI.Models.getModel("demo.model.parent_store");
+ },
+
+ beforeInit: function (callback) {
+ setTimeout(callback, 0);
+ },
+
+ render: function () {
+ var self = this;
+ return {
+ type: "demo.label",
+ ref: function (_ref) {
+ self.child = _ref;
+ }
+ };
+ }
+ });
+ BI.shortcut("demo.demo", Demo);
+
+ var demo = BI.Test.createWidget({
+ type: "demo.demo"
+ });
+
+ setTimeout(function () {
+ expect(demo.child.model.currContext).to.equal("默认context");
+ demo.destroy();
+ done();
+ }, 0);
+ });
+
+ /**
+ * test_author_guy
+ */
+ it("inject测试", function () {
+
+ var ParentStore = BI.inherit(Fix.Model, {
+ state: function () {
+ return {
+ context: "默认context"
+ };
+ },
+ childContext: ["context"]
+ });
+ BI.model("demo.model.parent_store", ParentStore);
+
+ var ChildStore = BI.inherit(Fix.Model, {
+ inject: ["context"],
+ computed: {
+ currContext: function () {
+ return this.model.context;
+ }
+ },
+ actions: {
+ changeContext: function () {
+ this.model.context = "改变后的context";
+ }
+ }
+ });
+ BI.model("demo.model.child_store", ChildStore);
+
+ var Label = BI.inherit(BI.Widget, {
+ _store: function () {
+ return BI.Models.getModel("demo.model.child_store");
+ },
+ render: function () {
+ return {
+ type: "bi.label",
+ text: this.model.currContext
+ };
+ }
+ });
+ BI.shortcut("demo.label", Label);
+
+ var Demo = BI.inherit(BI.Widget, {
+ _store: function () {
+ return BI.Models.getModel("demo.model.parent_store");
+ },
+
+ render: function () {
+ var self = this;
+ return {
+ type: "demo.label",
+ ref: function (_ref) {
+ self.child = _ref;
+ }
+ };
+ }
+ });
+ BI.shortcut("demo.demo", Demo);
+
+ var demo = BI.Test.createWidget({
+ type: "demo.demo"
+ });
+
+ expect(demo.child.model.currContext).to.equal("默认context");
+ demo.child.store.changeContext();
+ expect(demo.model.context).to.equal("默认context");
+ demo.destroy();
+
+ });
+
+});
diff --git a/src/main/resources/com/fr/fineui/core/__test__/widget.test.js b/src/main/resources/com/fr/fineui/core/__test__/widget.test.js
new file mode 100644
index 0000000..2c3d601
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/__test__/widget.test.js
@@ -0,0 +1,38 @@
+/**
+ * Created by guy on 2018/01/23.
+ */
+describe("widgetTest", function () {
+
+ before(function () {
+ });
+
+ /**
+ * test_author_guy
+ */
+ it("widget生命周期测试", function () {
+
+ var Demo = BI.inherit(BI.Widget, {
+ render: function () {
+ return {
+ type: "bi.label",
+ text: "old"
+ };
+ }
+ });
+ BI.shortcut("demo.demo", Demo);
+
+ var demo = BI.Test.createWidget({
+ type: "demo.demo",
+ render: function () {
+ return {
+ type: "bi.label",
+ text: "new"
+ };
+ }
+ });
+
+ expect(demo.element.text()).to.equal("new");
+ demo.destroy();
+ });
+
+});
diff --git a/src/main/resources/com/fr/fineui/core/action/action.js b/src/main/resources/com/fr/fineui/core/action/action.js
new file mode 100644
index 0000000..3639c00
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/action/action.js
@@ -0,0 +1,35 @@
+/**
+ * guy
+ * 由一个元素切换到另一个元素的行为
+ * @class BI.Action
+ * @extends BI.OB
+ * @abstract
+ */
+BI.Action = BI.inherit(BI.OB, {
+ props: function () {
+ return {
+ src: null,
+ tar: null
+ };
+ },
+
+ actionPerformed: function (src, tar, callback) {
+
+ },
+
+ actionBack: function (tar, src, callback) {
+
+ }
+});
+
+BI.ActionFactory = {
+ createAction: function (key, options) {
+ var action;
+ switch (key) {
+ case "show":
+ action = BI.ShowAction;
+ break;
+ }
+ return new action(options);
+ }
+};
diff --git a/src/main/resources/com/fr/fineui/core/action/action.show.js b/src/main/resources/com/fr/fineui/core/action/action.show.js
new file mode 100644
index 0000000..68296ae
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/action/action.show.js
@@ -0,0 +1,19 @@
+/**
+ * guy
+ * 由一个元素切换到另一个元素的行为
+ * @class BI.ShowAction
+ * @extends BI.Action
+ */
+BI.ShowAction = BI.inherit(BI.Action, {
+ actionPerformed: function (src, tar, callback) {
+ tar = tar || this.options.tar;
+ tar.setVisible(true);
+ callback && callback();
+ },
+
+ actionBack: function (tar, src, callback) {
+ tar = tar || this.options.tar;
+ tar.setVisible(false);
+ callback && callback();
+ }
+});
diff --git a/src/main/resources/com/fr/fineui/core/behavior/0.behavior.js b/src/main/resources/com/fr/fineui/core/behavior/0.behavior.js
new file mode 100644
index 0000000..3645f87
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/behavior/0.behavior.js
@@ -0,0 +1,32 @@
+BI.BehaviorFactory = {
+ createBehavior: function (key, options) {
+ var behavior;
+ switch (key) {
+ case "highlight":
+ behavior = BI.HighlightBehavior;
+ break;
+ case "redmark":
+ behavior = BI.RedMarkBehavior;
+ break;
+ }
+ return new behavior(options);
+ }
+};
+
+/**
+ * guy
+ * 行为控件
+ * @class BI.Behavior
+ * @extends BI.OB
+ */
+BI.Behavior = BI.inherit(BI.OB, {
+ _defaultConfig: function () {
+ return BI.extend(BI.Behavior.superclass._defaultConfig.apply(this, arguments), {
+ rule: function () {return true;}
+ });
+ },
+
+ doBehavior: function () {
+
+ }
+});
diff --git a/src/main/resources/com/fr/fineui/core/behavior/behavior.highlight.js b/src/main/resources/com/fr/fineui/core/behavior/behavior.highlight.js
new file mode 100644
index 0000000..683bc06
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/behavior/behavior.highlight.js
@@ -0,0 +1,33 @@
+/**
+ * guy
+ *
+ * @class BI.HighlightBehavior
+ * @extends BI.Behavior
+ */
+BI.HighlightBehavior = BI.inherit(BI.Behavior, {
+ doBehavior: function (items) {
+ var args = Array.prototype.slice.call(arguments, 1),
+ o = this.options;
+ BI.each(items, function (i, item) {
+ if (item instanceof BI.Single) {
+ var rule = o.rule(item.getValue(), item);
+
+ function doBe (run) {
+ if (run === true) {
+ item.doHighLight && item.doHighLight.apply(item, args);
+ } else {
+ item.unHighLight && item.unHighLight.apply(item, args);
+ }
+ }
+
+ if (BI.isFunction(rule)) {
+ rule(doBe);
+ } else {
+ doBe(rule);
+ }
+ } else {
+ item.doBehavior && item.doBehavior.apply(item, args);
+ }
+ });
+ }
+});
diff --git a/src/main/resources/com/fr/fineui/core/behavior/behavior.redmark.js b/src/main/resources/com/fr/fineui/core/behavior/behavior.redmark.js
new file mode 100644
index 0000000..b9c6572
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/behavior/behavior.redmark.js
@@ -0,0 +1,23 @@
+/**
+ * guy
+ * 标红行为
+ * @class BI.RedMarkBehavior
+ * @extends BI.Behavior
+ */
+BI.RedMarkBehavior = BI.inherit(BI.Behavior, {
+ doBehavior: function (items) {
+ var args = Array.prototype.slice.call(arguments, 1),
+ o = this.options;
+ BI.each(items, function (i, item) {
+ if(item instanceof BI.Single) {
+ if (o.rule(item.getValue(), item)) {
+ item.doRedMark && item.doRedMark.apply(item, args);
+ } else {
+ item.doRedMark && item.unRedMark.apply(item, args);
+ }
+ } else {
+ item.doBehavior && item.doBehavior.apply(item, args);
+ }
+ });
+ }
+});
diff --git a/src/main/resources/com/fr/fineui/core/constant/date.i18n.js b/src/main/resources/com/fr/fineui/core/constant/date.i18n.js
new file mode 100644
index 0000000..67f8c93
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/constant/date.i18n.js
@@ -0,0 +1,66 @@
+BI.prepares.push(function () {
+ BI.Date = BI.Date || {};
+ // 牵扯到国际化这些常量在页面加载后再生效
+ // full day names
+ BI.Date._DN = [BI.i18nText("BI-Basic_Sunday"),
+ BI.i18nText("BI-Basic_Monday"),
+ BI.i18nText("BI-Basic_Tuesday"),
+ BI.i18nText("BI-Basic_Wednesday"),
+ BI.i18nText("BI-Basic_Thursday"),
+ BI.i18nText("BI-Basic_Friday"),
+ BI.i18nText("BI-Basic_Saturday"),
+ BI.i18nText("BI-Basic_Sunday")];
+
+ // short day names
+ BI.Date._SDN = [BI.i18nText("BI-Basic_Simple_Sunday"),
+ BI.i18nText("BI-Basic_Simple_Monday"),
+ BI.i18nText("BI-Basic_Simple_Tuesday"),
+ BI.i18nText("BI-Basic_Simple_Wednesday"),
+ BI.i18nText("BI-Basic_Simple_Thursday"),
+ BI.i18nText("BI-Basic_Simple_Friday"),
+ BI.i18nText("BI-Basic_Simple_Saturday"),
+ BI.i18nText("BI-Basic_Simple_Sunday")];
+
+ // Monday first, etc.
+ BI.Date._FD = 1;
+
+ // full month namesdat
+ BI.Date._MN = [
+ BI.i18nText("BI-Basic_January"),
+ BI.i18nText("BI-Basic_February"),
+ BI.i18nText("BI-Basic_March"),
+ BI.i18nText("BI-Basic_April"),
+ BI.i18nText("BI-Basic_May"),
+ BI.i18nText("BI-Basic_June"),
+ BI.i18nText("BI-Basic_July"),
+ BI.i18nText("BI-Basic_August"),
+ BI.i18nText("BI-Basic_September"),
+ BI.i18nText("BI-Basic_October"),
+ BI.i18nText("BI-Basic_November"),
+ BI.i18nText("BI-Basic_December")];
+
+ // short month names
+ BI.Date._SMN = [0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11];
+
+ BI.Date._QN = ["", BI.i18nText("BI-Quarter_1"),
+ BI.i18nText("BI-Quarter_2"),
+ BI.i18nText("BI-Quarter_3"),
+ BI.i18nText("BI-Quarter_4")];
+
+ /** Adds the number of days array to the Date object. */
+ BI.Date._MD = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+
+ // 实际上无论周几作为一周的第一天,周初周末都是在-6-0间做偏移,用一个数组就可以
+ BI.Date._OFFSET = [0, -1, -2, -3, -4, -5, -6];
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/core/constant/events.js b/src/main/resources/com/fr/fineui/core/constant/events.js
new file mode 100644
index 0000000..2faa259
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/constant/events.js
@@ -0,0 +1,438 @@
+/**
+ * 事件集合
+ * @class BI.Events
+ */
+_.extend(BI, {
+ Events: {
+
+ /**
+ * @static
+ * @property keydown事件
+ */
+ KEYDOWN: "_KEYDOWN",
+
+ /**
+ * @static
+ * @property 回撤事件
+ */
+ BACKSPACE: "_BACKSPACE",
+
+ /**
+ * @static
+ * @property 空格事件
+ */
+ SPACE: "_SPACE",
+
+ /**
+ * @static
+ * @property 回车事件
+ */
+ ENTER: "_ENTER",
+
+ /**
+ * @static
+ * @property 确定事件
+ */
+ CONFIRM: "_CONFIRM",
+
+ /**
+ * @static
+ * @property 错误事件
+ */
+ ERROR: "_ERROR",
+
+ /**
+ * @static
+ * @property 暂停事件
+ */
+ PAUSE: "_PAUSE",
+
+ /**
+ * @static
+ * @property destroy事件
+ */
+ DESTROY: "_DESTROY",
+
+ /**
+ * @static
+ * @property 挂载事件
+ */
+ MOUNT: "_MOUNT",
+
+ /**
+ * @static
+ * @property 取消挂载事件
+ */
+ UNMOUNT: "_UNMOUNT",
+
+ /**
+ * @static
+ * @property 清除选择
+ */
+ CLEAR: "_CLEAR",
+
+ /**
+ * @static
+ * @property 添加数据
+ */
+ ADD: "_ADD",
+
+ /**
+ * @static
+ * @property 正在编辑状态事件
+ */
+ EDITING: "_EDITING",
+
+ /**
+ * @static
+ * @property 空状态事件
+ */
+ EMPTY: "_EMPTY",
+
+ /**
+ * @static
+ * @property 显示隐藏事件
+ */
+ VIEW: "_VIEW",
+
+ /**
+ * @static
+ * @property 窗体改变大小
+ */
+ RESIZE: "_RESIZE",
+
+ /**
+ * @static
+ * @property 编辑前事件
+ */
+ BEFOREEDIT: "_BEFOREEDIT",
+
+ /**
+ * @static
+ * @property 编辑后事件
+ */
+ AFTEREDIT: "_AFTEREDIT",
+
+ /**
+ * @static
+ * @property 开始编辑事件
+ */
+ STARTEDIT: "_STARTEDIT",
+
+ /**
+ * @static
+ * @property 停止编辑事件
+ */
+ STOPEDIT: "_STOPEDIT",
+
+ /**
+ * @static
+ * @property 值改变事件
+ */
+ CHANGE: "_CHANGE",
+
+ /**
+ * @static
+ * @property 下拉弹出菜单事件
+ */
+ EXPAND: "_EXPAND",
+
+ /**
+ * @static
+ * @property 关闭下拉菜单事件
+ */
+ COLLAPSE: "_COLLAPSE",
+
+ /**
+ * @static
+ * @property 回调事件
+ */
+ CALLBACK: "_CALLBACK",
+
+ /**
+ * @static
+ * @property 点击事件
+ */
+ CLICK: "_CLICK",
+
+ /**
+ * @static
+ * @property 状态改变事件,一般是用在复选按钮和单选按钮
+ */
+ STATECHANGE: "_STATECHANGE",
+
+ /**
+ * @static
+ * @property 状态改变前事件
+ */
+ BEFORESTATECHANGE: "_BEFORESTATECHANGE",
+
+
+ /**
+ * @static
+ * @property 初始化事件
+ */
+ INIT: "_INIT",
+
+ /**
+ * @static
+ * @property 初始化后事件
+ */
+ AFTERINIT: "_AFTERINIT",
+
+ /**
+ * @static
+ * @property 滚动条滚动事件
+ */
+ SCROLL: "_SCROLL",
+
+
+ /**
+ * @static
+ * @property 开始加载事件
+ */
+ STARTLOAD: "_STARTLOAD",
+
+ /**
+ * @static
+ * @property 加载后事件
+ */
+ AFTERLOAD: "_AFTERLOAD",
+
+
+ /**
+ * @static
+ * @property 提交前事件
+ */
+ BS: "beforesubmit",
+
+ /**
+ * @static
+ * @property 提交后事件
+ */
+ AS: "aftersubmit",
+
+ /**
+ * @static
+ * @property 提交完成事件
+ */
+ SC: "submitcomplete",
+
+ /**
+ * @static
+ * @property 提交失败事件
+ */
+ SF: "submitfailure",
+
+ /**
+ * @static
+ * @property 提交成功事件
+ */
+ SS: "submitsuccess",
+
+ /**
+ * @static
+ * @property 校验提交前事件
+ */
+ BVW: "beforeverifywrite",
+
+ /**
+ * @static
+ * @property 校验提交后事件
+ */
+ AVW: "afterverifywrite",
+
+ /**
+ * @static
+ * @property 校验后事件
+ */
+ AV: "afterverify",
+
+ /**
+ * @static
+ * @property 填报前事件
+ */
+ BW: "beforewrite",
+
+ /**
+ * @static
+ * @property 填报后事件
+ */
+ AW: "afterwrite",
+
+ /**
+ * @static
+ * @property 填报成功事件
+ */
+ WS: "writesuccess",
+
+ /**
+ * @static
+ * @property 填报失败事件
+ */
+ WF: "writefailure",
+
+ /**
+ * @static
+ * @property 添加行前事件
+ */
+ BA: "beforeappend",
+
+ /**
+ * @static
+ * @property 添加行后事件
+ */
+ AA: "afterappend",
+
+ /**
+ * @static
+ * @property 删除行前事件
+ */
+ BD: "beforedelete",
+
+ /**
+ * @static
+ * @property 删除行后事件
+ */
+ AD: "beforedelete",
+
+ /**
+ * @static
+ * @property 未提交离开事件
+ */
+ UC: "unloadcheck",
+
+
+ /**
+ * @static
+ * @property PDF导出前事件
+ */
+ BTOPDF: "beforetopdf",
+
+ /**
+ * @static
+ * @property PDF导出后事件
+ */
+ ATOPDF: "aftertopdf",
+
+ /**
+ * @static
+ * @property Excel导出前事件
+ */
+ BTOEXCEL: "beforetoexcel",
+
+ /**
+ * @static
+ * @property Excel导出后事件
+ */
+ ATOEXCEL: "aftertoexcel",
+
+ /**
+ * @static
+ * @property Word导出前事件
+ */
+ BTOWORD: "beforetoword",
+
+ /**
+ * @static
+ * @property Word导出后事件
+ */
+ ATOWORD: "aftertoword",
+
+ /**
+ * @static
+ * @property 图片导出前事件
+ */
+ BTOIMAGE: "beforetoimage",
+
+ /**
+ * @static
+ * @property 图片导出后事件
+ */
+ ATOIMAGE: "aftertoimage",
+
+ /**
+ * @static
+ * @property HTML导出前事件
+ */
+ BTOHTML: "beforetohtml",
+
+ /**
+ * @static
+ * @property HTML导出后事件
+ */
+ ATOHTML: "aftertohtml",
+
+ /**
+ * @static
+ * @property Excel导入前事件
+ */
+ BIMEXCEL: "beforeimportexcel",
+
+ /**
+ * @static
+ * @property Excel导出后事件
+ */
+ AIMEXCEL: "afterimportexcel",
+
+ /**
+ * @static
+ * @property PDF打印前事件
+ */
+ BPDFPRINT: "beforepdfprint",
+
+ /**
+ * @static
+ * @property PDF打印后事件
+ */
+ APDFPRINT: "afterpdfprint",
+
+ /**
+ * @static
+ * @property Flash打印前事件
+ */
+ BFLASHPRINT: "beforeflashprint",
+
+ /**
+ * @static
+ * @property Flash打印后事件
+ */
+ AFLASHPRINT: "afterflashprint",
+
+ /**
+ * @static
+ * @property Applet打印前事件
+ */
+ BAPPLETPRINT: "beforeappletprint",
+
+ /**
+ * @static
+ * @property Applet打印后事件
+ */
+ AAPPLETPRINT: "afterappletprint",
+
+ /**
+ * @static
+ * @property 服务器打印前事件
+ */
+ BSEVERPRINT: "beforeserverprint",
+
+ /**
+ * @static
+ * @property 服务器打印后事件
+ */
+ ASERVERPRINT: "afterserverprint",
+
+ /**
+ * @static
+ * @property 邮件发送前事件
+ */
+ BEMAIL: "beforeemail",
+
+ /**
+ * @static
+ * @property 邮件发送后事件
+ */
+ AEMAIL: "afteremail"
+ }
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/core/constant/var.js b/src/main/resources/com/fr/fineui/core/constant/var.js
new file mode 100644
index 0000000..a5bad8f
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/constant/var.js
@@ -0,0 +1,140 @@
+/**
+ * 常量
+ */
+
+_.extend(BI, {
+ MAX: 0xfffffffffffffff,
+ MIN: -0xfffffffffffffff,
+ EVENT_RESPONSE_TIME: 200,
+ zIndex_layer: 1e5,
+ zIndex_popover: 1e6,
+ zIndex_popup: 1e7,
+ zIndex_masker: 1e8,
+ zIndex_tip: 1e9,
+ emptyStr: "",
+ pixUnit: "px",
+ pixRatio: 1,
+ emptyFn: function () {
+ },
+ empty: null,
+ Key: {
+ 48: "0",
+ 49: "1",
+ 50: "2",
+ 51: "3",
+ 52: "4",
+ 53: "5",
+ 54: "6",
+ 55: "7",
+ 56: "8",
+ 57: "9",
+ 65: "a",
+ 66: "b",
+ 67: "c",
+ 68: "d",
+ 69: "e",
+ 70: "f",
+ 71: "g",
+ 72: "h",
+ 73: "i",
+ 74: "j",
+ 75: "k",
+ 76: "l",
+ 77: "m",
+ 78: "n",
+ 79: "o",
+ 80: "p",
+ 81: "q",
+ 82: "r",
+ 83: "s",
+ 84: "t",
+ 85: "u",
+ 86: "v",
+ 87: "w",
+ 88: "x",
+ 89: "y",
+ 90: "z",
+ 96: "0",
+ 97: "1",
+ 98: "2",
+ 99: "3",
+ 100: "4",
+ 101: "5",
+ 102: "6",
+ 103: "7",
+ 104: "8",
+ 105: "9",
+ 106: "*",
+ 107: "+",
+ 109: "-",
+ 110: ".",
+ 111: "/"
+ },
+ KeyCode: {
+ BACKSPACE: 8,
+ COMMA: 188,
+ DELETE: 46,
+ DOWN: 40,
+ END: 35,
+ ENTER: 13,
+ ESCAPE: 27,
+ HOME: 36,
+ LEFT: 37,
+ NUMPAD_ADD: 107,
+ NUMPAD_DECIMAL: 110,
+ NUMPAD_DIVIDE: 111,
+ NUMPAD_ENTER: 108,
+ NUMPAD_MULTIPLY: 106,
+ NUMPAD_SUBTRACT: 109,
+ PAGE_DOWN: 34,
+ PAGE_UP: 33,
+ PERIOD: 190,
+ RIGHT: 39,
+ SPACE: 32,
+ TAB: 9,
+ UP: 38
+ },
+ Status: {
+ SUCCESS: 1,
+ WRONG: 2,
+ START: 3,
+ END: 4,
+ WAITING: 5,
+ READY: 6,
+ RUNNING: 7,
+ OUTOFBOUNDS: 8,
+ NULL: -1
+ },
+ Direction: {
+ Top: "top",
+ Bottom: "bottom",
+ Left: "left",
+ Right: "right",
+ Custom: "custom"
+ },
+ Axis: {
+ Vertical: "vertical",
+ Horizontal: "horizontal"
+ },
+ Selection: {
+ Default: -2,
+ None: -1,
+ Single: 0,
+ Multi: 1,
+ All: 2
+ },
+ HorizontalAlign: {
+ Left: "left",
+ Right: "right",
+ Center: "center",
+ Stretch: "stretch"
+ },
+ VerticalAlign: {
+ Middle: "middle",
+ Top: "top",
+ Bottom: "bottom",
+ Stretch: "stretch"
+ },
+ StartOfWeek: 1,
+ BlankSplitChar: "\u200b \u200b",
+});
diff --git a/src/main/resources/com/fr/fineui/core/controller/0.controller.js b/src/main/resources/com/fr/fineui/core/controller/0.controller.js
new file mode 100644
index 0000000..4eba2c0
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/controller/0.controller.js
@@ -0,0 +1,11 @@
+/**
+ * guy
+ * 控制器
+ * Controller层超类
+ * @class BI.Controller
+ * @extends BI.OB
+ * @abstract
+ */
+BI.Controller = BI.inherit(BI.OB, {
+});
+BI.Controller.EVENT_CHANGE = "__EVENT_CHANGE__";
diff --git a/src/main/resources/com/fr/fineui/core/controller/controller.broadcast.js b/src/main/resources/com/fr/fineui/core/controller/controller.broadcast.js
new file mode 100644
index 0000000..89b2f0c
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/controller/controller.broadcast.js
@@ -0,0 +1,45 @@
+/**
+ * 广播
+ *
+ * Created by GUY on 2015/12/23.
+ * @class
+ */
+BI.BroadcastController = BI.inherit(BI.Controller, {
+ init: function () {
+ this._broadcasts = {};
+ },
+
+ on: function (name, fn) {
+ var self = this;
+ if (!this._broadcasts[name]) {
+ this._broadcasts[name] = [];
+ }
+ this._broadcasts[name].push(fn);
+ return function () {
+ self.remove(name, fn);
+ };
+ },
+
+ send: function (name) {
+ var args = [].slice.call(arguments, 1);
+ BI.each(this._broadcasts[name], function (i, fn) {
+ fn.apply(null, args);
+ });
+ },
+
+ remove: function (name, fn) {
+ var self = this;
+ if (fn) {
+ BI.remove(this._broadcasts[name], function (idx) {
+ return self._broadcasts[name].indexOf(fn) === idx;
+ });
+ this._broadcasts[name].remove(fn);
+ if (this._broadcasts[name].length === 0) {
+ delete this._broadcasts[name];
+ }
+ } else {
+ delete this._broadcasts[name];
+ }
+ return this;
+ }
+});
diff --git a/src/main/resources/com/fr/fineui/core/controller/controller.bubbles.js b/src/main/resources/com/fr/fineui/core/controller/controller.bubbles.js
new file mode 100644
index 0000000..a705ef4
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/controller/controller.bubbles.js
@@ -0,0 +1,104 @@
+/**
+ * 气泡图控制器
+ * 控制气泡图的显示方向
+ *
+ * Created by GUY on 2015/8/21.
+ * @class
+ */
+BI.BubblesController = BI.inherit(BI.Controller, {
+ init: function () {
+ this.storeBubbles = {};
+ this.storePoppers = {};
+ },
+
+ /**
+ *
+ * @param name
+ * @param text
+ * @param context
+ * @param offsetStyle center, left, right三种类型, 默认left
+ * @returns {BI.BubblesController}
+ */
+ show: function (name, text, context, opt) {
+ opt || (opt = {});
+ var container = opt.container || context;
+ var offsetStyle = opt.offsetStyle || "left";
+ var level = opt.level || "error";
+ var adjustYOffset = opt.adjustYOffset || 0;
+ var adjustXOffset = opt.adjustXOffset || 0;
+ // var fixed = opt.fixed !== false;
+
+ if (!this.storeBubbles[name]) {
+ this.storeBubbles[name] = BI.createWidget({
+ type: "bi.label",
+ cls: "bi-bubble" + " bubble-" + level,
+ text: text,
+ hgap: 5,
+ height: 18
+ });
+ }
+ var bubble = this.storeBubbles[name];
+ if (bubble.getText() !== text) {
+ bubble.setText(text);
+ }
+
+ BI.createWidget({
+ type: "bi.default",
+ element: container,
+ items: [{
+ el: bubble
+ }]
+ });
+ if (this.storePoppers[name]) {
+ this.storePoppers[name].destroy();
+ }
+ this.storePoppers[name] = BI.Popper.createPopper(context.element[0], bubble.element[0], {
+ placement: ({
+ left: "top-start",
+ center: "top",
+ right: "top-end"
+ })[offsetStyle],
+ strategy: "fixed",
+ modifiers: [
+ {
+ name: "offset",
+ options: {
+ offset: [adjustXOffset, adjustYOffset]
+ }
+ }
+ ]
+ });
+ return this;
+ },
+
+ hide: function (name) {
+ this.remove(name);
+ return this;
+ },
+
+ has: function (name) {
+ return this.storeBubbles[name] != null;
+ },
+
+ remove: function (name) {
+ if (!this.has(name)) {
+ return this;
+ }
+ this.storeBubbles[name].destroy();
+ this.storePoppers[name] && this.storePoppers[name].destroy();
+ delete this.storeBubbles[name];
+ return this;
+ },
+
+ removeAll: function () {
+ BI.each(this.storeBubbles, function (name, bubble) {
+ bubble.destroy();
+ });
+ BI.each(this.storePoppers, function (name, popper) {
+ popper.destroy();
+ });
+ this.storeBubbles = {};
+ this.storePoppers = {};
+ return this;
+ }
+});
diff --git a/src/main/resources/com/fr/fineui/core/controller/controller.layer.js b/src/main/resources/com/fr/fineui/core/controller/controller.layer.js
new file mode 100644
index 0000000..98470c5
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/controller/controller.layer.js
@@ -0,0 +1,169 @@
+/**
+ * 弹出层面板控制器, z-index在10w层级
+ *
+ * Created by GUY on 2015/6/24.
+ * @class
+ */
+BI.LayerController = BI.inherit(BI.Controller, {
+ props: function () {
+ return {
+ render: "body"
+ };
+ },
+
+ init: function () {
+ this.layerManager = {};
+ this.layouts = {};
+ this.zindex = BI.zIndex_layer;
+ BI.Resizers.add("layerController" + BI.uniqueId(), BI.bind(this._resize, this));
+ },
+
+ _resize: function () {
+ BI.each(this.layouts, function (i, layer) {
+ if (layer.element.is(":visible")) {
+ layer.element.trigger("__resize__");
+ }
+ });
+ },
+
+ make: function (name, container, op, context) {
+ if (BI.isWidget(container)) {
+ op = op || {};
+ op.container = container;
+ } else {
+ context = op;
+ op = container;
+ }
+ return this.create(name, null, op, context);
+ },
+
+ create: function (name, from, op, context) {
+ if (this.has(name)) {
+ return this.get(name);
+ }
+ op || (op = {});
+ var offset = op.offset || {};
+ var w = from;
+ if (BI.isWidget(from)) {
+ w = from.element;
+ }
+ if (BI.isNotEmptyString(w)) {
+ w = BI.Widget._renderEngine.createElement(w);
+ }
+ if (this.has(name)) {
+ return this.get(name);
+ }
+ var widget = BI.createWidget((op.render || {}), BI.extend({
+ type: "bi.layout"
+ }, op), context);
+ var layout = BI.createWidget({
+ type: "bi.absolute",
+ invisible: true,
+ items: [{
+ el: widget,
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0
+ }]
+ }, context);
+ BI.createWidget({
+ type: "bi.absolute",
+ element: op.container || this.options.render,
+ items: [{
+ el: layout,
+ left: offset.left || 0,
+ right: offset.right || 0,
+ top: offset.top || 0,
+ bottom: offset.bottom || 0
+ }]
+ });
+ if (w) {
+ layout.element.addClass("bi-popup-view");
+ layout.element.css({
+ left: w.offset().left + (offset.left || 0),
+ top: w.offset().top + (offset.top || 0),
+ width: offset.width || (w.outerWidth() - (offset.left || 0) - (offset.right || 0)) || "",
+ height: offset.height || (w.outerHeight() - (offset.top || 0) - (offset.bottom || 0)) || ""
+ });
+ layout.element.on("__resize__", function () {
+ w.is(":visible") &&
+ layout.element.css({
+ left: w.offset().left + (offset.left || 0),
+ top: w.offset().top + (offset.top || 0),
+ width: offset.width || (w.outerWidth() - (offset.left || 0) - (offset.right || 0)) || "",
+ height: offset.height || (w.outerHeight() - (offset.top || 0) - (offset.bottom || 0)) || ""
+ });
+ });
+ }
+ this.add(name, widget, layout);
+ return widget;
+ },
+
+ show: function (name, callback) {
+ if (!this.has(name)) {
+ return this;
+ }
+ this._getLayout(name).visible();
+ this._getLayout(name).element.css("z-index", this.zindex++).show(0, callback).trigger("__resize__");
+ return this;
+ },
+
+ hide: function (name, callback) {
+ if (!this.has(name)) {
+ return this;
+ }
+ this._getLayout(name).invisible();
+ this._getLayout(name).element.hide(0, callback);
+ return this;
+ },
+
+ isVisible: function (name) {
+ return this.has(name) && this._getLayout(name).isVisible();
+ },
+
+ add: function (name, layer, layout) {
+ if (this.has(name)) {
+ throw new Error("不能创建同名的Layer");
+ }
+ layout.setVisible(false);
+ this.layerManager[name] = layer;
+ this.layouts[name] = layout;
+ layout.element.css("z-index", this.zindex++);
+ return this;
+ },
+
+ _getLayout: function (name) {
+ return this.layouts[name];
+ },
+
+ get: function (name) {
+ return this.layerManager[name];
+ },
+
+ has: function (name) {
+ return this.layerManager[name] != null;
+ },
+
+ remove: function (name) {
+ if (!this.has(name)) {
+ return this;
+ }
+ this.layerManager[name].destroy();
+ this.layouts[name].destroy();
+ delete this.layerManager[name];
+ delete this.layouts[name];
+ return this;
+ },
+
+ removeAll: function () {
+ var self = this;
+ BI.each(BI.keys(this.layerManager), function (index, name) {
+ self.layerManager[name].destroy();
+ self.layouts[name].destroy();
+ });
+ this.layerManager = {};
+ this.layouts = {};
+ return this;
+ }
+});
diff --git a/src/main/resources/com/fr/fineui/core/controller/controller.masker.js b/src/main/resources/com/fr/fineui/core/controller/controller.masker.js
new file mode 100644
index 0000000..0b55b07
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/controller/controller.masker.js
@@ -0,0 +1,12 @@
+/**
+ * 遮罩面板, z-index在1亿层级
+ *
+ * Created by GUY on 2015/6/24.
+ * @class
+ */
+BI.MaskersController = BI.inherit(BI.LayerController, {
+ init: function () {
+ BI.MaskersController.superclass.init.apply(this, arguments);
+ this.zindex = BI.zIndex_masker;
+ }
+});
diff --git a/src/main/resources/com/fr/fineui/core/controller/controller.popover.js b/src/main/resources/com/fr/fineui/core/controller/controller.popover.js
new file mode 100644
index 0000000..fbe7e29
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/controller/controller.popover.js
@@ -0,0 +1,167 @@
+/**
+ * guy
+ * popover弹出层控制器, z-index在100w层级
+ * @class BI.popoverController
+ * @extends BI.Controller
+ */
+BI.PopoverController = BI.inherit(BI.Controller, {
+ props: function () {
+ return {
+ modal: true, // 模态窗口
+ render: "body"
+ };
+ },
+
+ init: function () {
+ this.modal = this.options.modal;
+ this.floatManager = {};
+ this.floatLayer = {};
+ this.floatContainer = {};
+ this.floatOpened = {};
+ this.zindex = BI.zIndex_popover;
+ this.zindexMap = {};
+ },
+
+ create: function (name, options, context) {
+ if (this.has(name)) {
+ return this;
+ }
+ var popover = BI.createWidget(options || {}, {
+ type: "bi.popover"
+ }, context);
+ this.add(name, popover, options, context);
+ return this;
+ },
+
+ open: function (name) {
+ if (!this.has(name)) {
+ return this;
+ }
+ if (!this.floatOpened[name]) {
+ this.floatOpened[name] = true;
+ var container = this.floatContainer[name];
+ container.element.css("zIndex", this.zindex++);
+ this.modal && container.element.__hasZIndexMask__(this.zindexMap[name]) && container.element.__releaseZIndexMask__(this.zindexMap[name]);
+ this.zindexMap[name] = this.zindex;
+ this.modal && container.element.__buildZIndexMask__(this.zindex++);
+ this.get(name).setZindex(this.zindex++);
+ this.floatContainer[name].visible();
+ var popover = this.get(name);
+ popover.show && popover.show();
+ var W = BI.Widget._renderEngine.createElement(this.options.render).width(), H = BI.Widget._renderEngine.createElement(this.options.render).height();
+ var w = popover.element.width(), h = popover.element.height();
+ var left = (W - w) / 2, top = (H - h) / 2;
+ if (left < 0) {
+ left = 0;
+ }
+ if (top < 0) {
+ top = 0;
+ }
+ popover.element.css({
+ left: left / BI.pixRatio + BI.pixUnit,
+ top: top / BI.pixRatio + BI.pixUnit
+ });
+ }
+ return this;
+ },
+
+ close: function (name) {
+ if (!this.has(name)) {
+ return this;
+ }
+ if (this.floatOpened[name]) {
+ delete this.floatOpened[name];
+ this.floatContainer[name].invisible();
+ this.modal && this.floatContainer[name].element.__releaseZIndexMask__(this.zindexMap[name]);
+ }
+ return this;
+ },
+
+ show: function (name) {
+ return this.open(name);
+ },
+
+ hide: function (name) {
+ return this.close(name);
+ },
+
+ isVisible: function (name) {
+ return this.has(name) && this.floatOpened[name] === true;
+ },
+
+ add: function (name, popover, options, context) {
+ var self = this;
+ options || (options = {});
+ if (this.has(name)) {
+ return this;
+ }
+ this.floatContainer[name] = BI.createWidget({
+ type: "bi.absolute",
+ cls: "bi-popup-view",
+ items: [{
+ el: (this.floatLayer[name] = BI.createWidget({
+ type: "bi.absolute",
+ items: [popover]
+ }, context)),
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0
+ }]
+ });
+ this.floatManager[name] = popover;
+ (function (key) {
+ popover.on(BI.Popover.EVENT_CLOSE, function () {
+ self.close(key);
+ });
+ })(name);
+ BI.createWidget({
+ type: "bi.absolute",
+ element: options.container || this.options.render,
+ items: [{
+ el: this.floatContainer[name],
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0
+ }]
+ });
+ return this;
+ },
+
+ get: function (name) {
+ return this.floatManager[name];
+ },
+
+ has: function (name) {
+ return BI.isNotNull(this.floatManager[name]);
+ },
+
+ remove: function (name) {
+ if (!this.has(name)) {
+ return this;
+ }
+ this.floatContainer[name].destroy();
+ this.modal && this.floatContainer[name].element.__releaseZIndexMask__(this.zindexMap[name]);
+ delete this.floatManager[name];
+ delete this.floatLayer[name];
+ delete this.zindexMap[name];
+ delete this.floatContainer[name];
+ delete this.floatOpened[name];
+ return this;
+ },
+
+ removeAll: function () {
+ var self = this;
+ BI.each(this.floatContainer, function (name, container) {
+ container.destroy();
+ self.modal && self.floatContainer[name].element.__releaseZIndexMask__(self.zindexMap[name]);
+ });
+ this.floatManager = {};
+ this.floatLayer = {};
+ this.floatContainer = {};
+ this.floatOpened = {};
+ this.zindexMap = {};
+ return this;
+ }
+});
diff --git a/src/main/resources/com/fr/fineui/core/controller/controller.resizer.js b/src/main/resources/com/fr/fineui/core/controller/controller.resizer.js
new file mode 100644
index 0000000..a471ceb
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/controller/controller.resizer.js
@@ -0,0 +1,68 @@
+/**
+ * window.resize 控制器
+ *
+ * Created by GUY on 2015/6/24.
+ * @class
+ */
+BI.ResizeController = BI.inherit(BI.Controller, {
+ init: function () {
+ var self = this;
+ this.resizerManger = {};
+ var fn = BI.debounce(function (ev) {
+ // if (BI.isWindow(ev.target)) {
+ self._resize(ev);
+ // }
+ }, 30);
+ if ("onorientationchange" in _global) {
+ _global.onorientationchange = fn;
+ } else {
+ BI.Widget._renderEngine.createElement(_global).resize(fn);
+ }
+ },
+
+ _resize: function (ev) {
+ BI.each(this.resizerManger, function (key, resizer) {
+ if (resizer instanceof BI.$) {
+ if (resizer.is(":visible")) {
+ resizer.trigger("__resize__");
+ }
+ return;
+ }
+ if (resizer instanceof BI.Layout) {
+ resizer.resize();
+ return;
+ }
+ if (BI.isFunction(resizer)) {
+ resizer(ev);
+ return;
+ }
+ });
+ },
+
+ add: function (name, resizer) {
+ var self = this;
+ if (this.has(name)) {
+ return this;
+ }
+ this.resizerManger[name] = resizer;
+ return function () {
+ self.remove(name);
+ };
+ },
+
+ get: function (name) {
+ return this.resizerManger[name];
+ },
+
+ has: function (name) {
+ return this.resizerManger[name] != null;
+ },
+
+ remove: function (name) {
+ if (!this.has(name)) {
+ return this;
+ }
+ delete this.resizerManger[name];
+ return this;
+ }
+});
diff --git a/src/main/resources/com/fr/fineui/core/controller/controller.tooltips.js b/src/main/resources/com/fr/fineui/core/controller/controller.tooltips.js
new file mode 100644
index 0000000..5e4db15
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/controller/controller.tooltips.js
@@ -0,0 +1,140 @@
+/**
+ * tooltip控制器
+ * 控制tooltip的显示, 且页面中只有一个tooltip显示
+ *
+ * Created by GUY on 2015/9/8.
+ * @class BI.TooltipsController
+ * @extends BI.Controller
+ */
+BI.TooltipsController = BI.inherit(BI.Controller, {
+ init: function () {
+ this.tooltipsManager = {};
+ this.showingTips = {};// 存储正在显示的tooltip
+ },
+
+ _createTooltip: function (text, level) {
+ return BI.createWidget({
+ type: "bi.tooltip",
+ text: text,
+ level: level,
+ stopEvent: true
+ });
+ },
+
+ // opt: {container: '', belowMouse: false}
+ show: function (e, name, text, level, context, opt) {
+ opt || (opt = {});
+ var self = this;
+ BI.each(this.showingTips, function (i, tip) {
+ self.hide(i);
+ });
+ this.showingTips = {};
+ if (!this.has(name)) {
+ this.create(name, text, level, opt.container || "body");
+ }
+ if (!opt.belowMouse) {
+ var offset = context.element.offset();
+ var bounds = context.element.bounds();
+ if (bounds.height === 0 || bounds.width === 0) {
+ return;
+ }
+ var top = offset.top + bounds.height + 5;
+ }
+ var tooltip = this.get(name);
+ tooltip.setText(text);
+ tooltip.element.css({
+ left: "0px",
+ top: "0px"
+ });
+ tooltip.visible();
+ tooltip.element.height(tooltip.element[0].scrollHeight);
+ this.showingTips[name] = true;
+ // scale影响要计算在内
+ // var scale = context.element.offset().left / context.element.get(0).getBoundingClientRect().left;
+ // var x = (e.pageX || e.clientX) * scale + 15, y = (e.pageY || e.clientY) * scale + 15;
+ var x = (e.pageX || e.clientX) + 15, y = (e.pageY || e.clientY) + 15;
+ if (x + tooltip.element.outerWidth() > BI.Widget._renderEngine.createElement("body").outerWidth()) {
+ x -= tooltip.element.outerWidth() + 15;
+ }
+ var bodyHeight = BI.Widget._renderEngine.createElement("body").outerHeight();
+ if (y + tooltip.element.outerHeight() > bodyHeight || top + tooltip.element.outerHeight() > bodyHeight) {
+ y -= tooltip.element.outerHeight() + 15;
+ !opt.belowMouse && (y = Math.min(y, offset.top - tooltip.element.outerHeight() - 5));
+ } else {
+ !opt.belowMouse && (y = Math.max(y, top));
+ }
+ tooltip.element.css({
+ left: x < 0 ? 0 : x / BI.pixRatio + BI.pixUnit,
+ top: y < 0 ? 0 : y / BI.pixRatio + BI.pixUnit
+ });
+ tooltip.element.hover(function () {
+ self.remove(name);
+ context.element.trigger("mouseleave.title" + context.getName());
+ });
+ return this;
+ },
+
+ hide: function (name, callback) {
+ if (!this.has(name)) {
+ return this;
+ }
+ delete this.showingTips[name];
+ this.get(name).element.hide(0, callback);
+ this.get(name).invisible();
+ return this;
+ },
+
+ create: function (name, text, level, context) {
+ if (!this.has(name)) {
+ var tooltip = this._createTooltip(text, level);
+ this.add(name, tooltip);
+ BI.createWidget({
+ type: "bi.absolute",
+ element: context || "body",
+ items: [{
+ el: tooltip
+ }]
+ });
+ tooltip.invisible();
+ }
+ return this.get(name);
+ },
+
+ add: function (name, bubble) {
+ if (this.has(name)) {
+ return this;
+ }
+ this.set(name, bubble);
+ return this;
+ },
+
+ get: function (name) {
+ return this.tooltipsManager[name];
+ },
+
+ set: function (name, bubble) {
+ this.tooltipsManager[name] = bubble;
+ },
+
+ has: function (name) {
+ return this.tooltipsManager[name] != null;
+ },
+
+ remove: function (name) {
+ if (!this.has(name)) {
+ return this;
+ }
+ this.tooltipsManager[name].destroy();
+ delete this.tooltipsManager[name];
+ return this;
+ },
+
+ removeAll: function () {
+ BI.each(this.tooltipsManager, function (name, tooltip) {
+ tooltip.destroy();
+ });
+ this.tooltipsManager = {};
+ this.showingTips = {};
+ return this;
+ }
+});
diff --git a/src/main/resources/com/fr/fineui/core/func/__test__/date.test.js b/src/main/resources/com/fr/fineui/core/func/__test__/date.test.js
new file mode 100644
index 0000000..042fd16
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/func/__test__/date.test.js
@@ -0,0 +1,31 @@
+/**
+ * Created by windy on 2018/01/23.
+ */
+describe("dateFunctionTest", function () {
+
+ before(function () {
+
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("getWeekNumber", function () {
+ expect(BI.print(BI.getDate(2005, 0, 1), "%Y-%W")).to.equal("2004-53");
+ expect(BI.print(BI.getDate(2005, 0, 2), "%Y-%W")).to.equal("2004-53");
+ expect(BI.print(BI.getDate(2005, 11, 31), "%Y-%W")).to.equal("2005-52");
+ expect(BI.print(BI.getDate(2007, 0, 1), "%Y-%W")).to.equal("2007-01");
+ expect(BI.print(BI.getDate(2007, 11, 30), "%Y-%W")).to.equal("2007-52");
+ expect(BI.print(BI.getDate(2007, 11, 31), "%Y-%W")).to.equal("2008-01");
+ expect(BI.print(BI.getDate(2008, 0, 1), "%Y-%W")).to.equal("2008-01");
+ expect(BI.print(BI.getDate(2008, 11, 28), "%Y-%W")).to.equal("2008-52");
+ expect(BI.print(BI.getDate(2008, 11, 29), "%Y-%W")).to.equal("2009-01");
+ expect(BI.print(BI.getDate(2008, 11, 30), "%Y-%W")).to.equal("2009-01");
+ expect(BI.print(BI.getDate(2008, 11, 31), "%Y-%W")).to.equal("2009-01");
+ expect(BI.print(BI.getDate(2009, 0, 1), "%Y-%W")).to.equal("2009-01");
+ expect(BI.print(BI.getDate(2009, 11, 31), "%Y-%W")).to.equal("2009-53");
+ expect(BI.print(BI.getDate(2010, 0, 1), "%Y-%W")).to.equal("2009-53");
+ expect(BI.print(BI.getDate(2010, 0, 2), "%Y-%W")).to.equal("2009-53");
+ expect(BI.print(BI.getDate(2010, 0, 3), "%Y-%W")).to.equal("2009-53");
+ });
+});
diff --git a/src/main/resources/com/fr/fineui/core/func/__test__/function.test.js b/src/main/resources/com/fr/fineui/core/func/__test__/function.test.js
new file mode 100644
index 0000000..388af5f
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/func/__test__/function.test.js
@@ -0,0 +1,24 @@
+/**
+ * @Author: lei.wang
+ * @Maintainers: lei.wang
+ * @Date: 2019-04-16
+ */
+describe("core-function-test", function () {
+ /**
+ * test_author_lei.wang
+ */
+ it("createDistinctName-支持字符串数组", function () {
+ var names = ["name", "name1"];
+ expect(BI.Func.createDistinctName(names, "name")).to.equal("name2");
+ expect(BI.Func.createDistinctName(names, "name2")).to.equal("name2");
+ });
+
+ /**
+ * test_author_lei.wang
+ */
+ it("createDistinctName-支持对象数组数组", function () {
+ var names = [{ name: "name" }, { name: "name1" }];
+ expect(BI.Func.createDistinctName(names, "name")).to.equal("name2");
+ expect(BI.Func.createDistinctName(names, "name2")).to.equal("name2");
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/core/func/__test__/string.test.js b/src/main/resources/com/fr/fineui/core/func/__test__/string.test.js
new file mode 100644
index 0000000..94ecb99
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/func/__test__/string.test.js
@@ -0,0 +1,19 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2020/6/5
+ */
+describe("dateFunctionTest", function () {
+
+ before(function () {
+
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("getWeekNumber", function () {
+ expect(BI.replaceAll(null, "A", "b")).to.equal(null);
+ expect(BI.replaceAll("A", "A", "b")).to.equal("b");
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/core/func/alias.js b/src/main/resources/com/fr/fineui/core/func/alias.js
new file mode 100644
index 0000000..da5af0f
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/func/alias.js
@@ -0,0 +1,934 @@
+(function () {
+ var _global;
+ if (typeof window !== "undefined") {
+ _global = window;
+ } else if (typeof global !== "undefined") {
+ _global = global;
+ } else if (typeof self !== "undefined") {
+ _global = self;
+ } else {
+ _global = this;
+ }
+ if (!_global.BI) {
+ _global.BI = {};
+ }
+
+ function isEmpty (value) {
+ // 判断是否为空值
+ var result = value === "" || value === null || value === undefined;
+ return result;
+ }
+
+ // 判断是否是无效的日期
+ function isInvalidDate (date) {
+ return date == "Invalid Date" || date == "NaN";
+ }
+
+ /**
+ * CHART-1400
+ * 使用数值计算的方式来获取任意数值的科学技术表示值。
+ * 科学计数格式
+ */
+ function _eFormat (text, fmt) {
+ text = +text;
+
+ return eFormat(text, fmt);
+
+ /**
+ * 科学计数格式具体计算过程
+ * @param num
+ * @param format {String}有两种形式,
+ * 1、"0.00E00"这样的字符串表示正常的科学计数表示,只不过规定了数值精确到百分位,
+ * 而数量级的绝对值如果是10以下的时候在前面补零。
+ * 2、 "##0.0E0"这样的字符串则规定用科学计数法表示之后的数值的整数部分是三位,精确到十分位,
+ * 数量级没有规定,因为没见过实数里有用科学计数法表示之后E的后面会小于一位的情况(0无所谓)。
+ * @returns {*}
+ */
+ function eFormat (num, format) {
+ var neg = num < 0 ? (num *= -1, "-") : "",
+ magnitudeNeg = "";
+
+ var funcName = num > 0 && num < 1 ? "floor" : "ceil"; // -0.9999->-1
+ // 数量级
+ var magnitude = Math[funcName](Math.log(num) / Math.log(10));
+
+ if (!isFinite(magnitude)) {
+ return format.replace(/#/ig, "").replace(/\.e/ig, "E");
+ }
+
+ num = num / Math.pow(10, magnitude);
+
+ // 让num转化成[1, 10)区间上的数
+ if (num > 0 && num < 1) {
+ num *= 10;
+ magnitude -= 1;
+ }
+
+ // 计算出format中需要显示的整数部分的位数,然后更新这个数值,也更新数量级
+ var integerLen = getInteger(magnitude, format);
+ integerLen > 1 && (magnitude -= integerLen - 1, num *= Math.pow(10, integerLen - 1));
+
+ magnitude < 0 && (magnitudeNeg = "-", magnitude *= -1);
+
+ // 获取科学计数法精确到的位数
+ var precision = getPrecision(format);
+ // 判断num经过四舍五入之后是否有进位
+ var isValueCarry = isValueCarried(num);
+
+ num *= Math.pow(10, precision);
+ num = Math.round(num);
+ // 如果出现进位的情况,将num除以10
+ isValueCarry && (num /= 10, magnitude += magnitudeNeg === "-" ? -1 : 1);
+ num /= Math.pow(10, precision);
+
+ // 小数部分保留precision位
+ num = num.toFixed(precision);
+ // 格式化指数的部分
+ magnitude = formatExponential(format, magnitude, magnitudeNeg);
+
+ return neg + num + "E" + magnitude;
+ }
+
+ // 获取format格式规定的数量级的形式
+ function formatExponential (format, num, magnitudeNeg) {
+ num += "";
+ if (!/e/ig.test(format)) {
+ return num;
+ }
+ format = format.split(/e/ig)[1];
+
+ while (num.length < format.length) {
+ num = "0" + num;
+ }
+
+ // 如果magnitudeNeg是一个"-",而且num正好全是0,那么就别显示负号了
+ var isAllZero = true;
+ for (var i = 0, len = num.length; i < len; i++) {
+ if (!isAllZero) {
+ continue;
+ }
+ isAllZero = num.charAt(i) === "0";
+ }
+ magnitudeNeg = isAllZero ? "" : magnitudeNeg;
+
+ return magnitudeNeg + num;
+ }
+
+ // 获取format规定的科学计数法精确到的位数
+ function getPrecision (format) {
+ if (!/e/ig.test(format)) {
+ return 0;
+ }
+ var arr = format.split(/e/ig)[0].split(".");
+
+ return arr.length > 1 ? arr[1].length : 0;
+ }
+
+ // 获取数值科学计数法表示之后整数的位数
+ // 这边我们还需要考虑#和0的问题
+ function getInteger (magnitude, format) {
+ if (!/e/ig.test(format)) {
+ return 0;
+ }
+ // return format.split(/e/ig)[0].split(".")[0].length;
+
+ var formatLeft = format.split(/e/ig)[0].split(".")[0], i, f, len = formatLeft.length;
+ var valueLeftLen = 0;
+
+ for (i = 0; i < len; i++) {
+ f = formatLeft.charAt(i);
+ // "#"所在的位置到末尾长度小于等于值的整数部分长度,那么这个#才可以占位
+ if (f == 0 || (f == "#" && (len - i <= magnitude + 1))) {
+ valueLeftLen++;
+ }
+ }
+
+ return valueLeftLen;
+ }
+
+ // 判断num通过round函数之后是否有进位
+ function isValueCarried (num) {
+ var roundNum = Math.round(num);
+ num = (num + "").split(".")[0];
+ roundNum = (roundNum + "").split(".")[0];
+ return num.length !== roundNum.length;
+ }
+ }
+
+ //'#.##'之类的格式处理 1.324e-18 这种的科学数字
+ function _dealNumberPrecision (text, fright) {
+ if (/[eE]/.test(text)) {
+ var precision = 0, i = 0, ch;
+
+ if (/[%‰]$/.test(fright)) {
+ precision = /[%]$/.test(fright) ? 2 : 3;
+ }
+
+ for (var len = fright.length; i < len; i++) {
+ if ((ch = fright.charAt(i)) == "0" || ch == "#") {
+ precision++;
+ }
+ }
+ return Number(text).toFixed(precision);
+ }
+
+ return text;
+ }
+
+ /**
+ * 数字格式
+ */
+ function _numberFormat (text, format) {
+ var text = text + "";
+
+ //在调用数字格式的时候如果text里没有任何数字则不处理
+ if (!(/[0-9]/.test(text)) || !format) {
+ return text;
+ }
+
+ // 数字格式,区分正负数
+ var numMod = format.indexOf(";");
+ if (numMod > -1) {
+ if (text >= 0) {
+ return _numberFormat(text + "", format.substring(0, numMod));
+ }
+ return _numberFormat((-text) + "", format.substr(numMod + 1));
+
+ } else {
+ // 兼容格式处理负数的情况(copy:fr-jquery.format.js)
+ if (+text < 0 && format.charAt(0) !== "-") {
+ return _numberFormat((-text) + "", "-" + format);
+ }
+ }
+
+ var fp = format.split("."), fleft = fp[0] || "", fright = fp[1] || "";
+ text = _dealNumberPrecision(text, fright);
+ var tp = text.split("."), tleft = tp[0] || "", tright = tp[1] || "";
+
+ // 百分比,千分比的小数点移位处理
+ if (/[%‰]$/.test(format)) {
+ var paddingZero = /[%]$/.test(format) ? "00" : "000";
+ tright += paddingZero;
+ tleft += tright.substr(0, paddingZero.length);
+ tleft = tleft.replace(/^0+/gi, "");
+ tright = tright.substr(paddingZero.length).replace(/0+$/gi, "");
+ }
+ var right = _dealWithRight(tright, fright);
+ if (right.leftPlus) {
+ // 小数点后有进位
+ tleft = parseInt(tleft) + 1 + "";
+
+ tleft = isNaN(tleft) ? "1" : tleft;
+ }
+ right = right.num;
+ var left = _dealWithLeft(tleft, fleft);
+ if (!(/[0-9]/.test(left))) {
+ left = left + "0";
+ }
+ if (!(/[0-9]/.test(right))) {
+ return left + right;
+ } else {
+ return left + "." + right;
+ }
+ }
+
+ /**
+ * 处理小数点右边小数部分
+ * @param tright 右边内容
+ * @param fright 右边格式
+ * @returns {JSON} 返回处理结果和整数部分是否需要进位
+ * @private
+ */
+ function _dealWithRight (tright, fright) {
+ var right = "", j = 0, i = 0;
+ for (var len = fright.length; i < len; i++) {
+ var ch = fright.charAt(i);
+ var c = tright.charAt(j);
+ switch (ch) {
+ case "0":
+ if (isEmpty(c)) {
+ c = "0";
+ }
+ right += c;
+ j++;
+ break;
+ case "#":
+ right += c;
+ j++;
+ break;
+ default :
+ right += ch;
+ break;
+ }
+ }
+ var rll = tright.substr(j);
+ var result = {};
+ if (!isEmpty(rll) && rll.charAt(0) > 4) {
+ // 有多余字符,需要四舍五入
+ result.leftPlus = true;
+ var numReg = right.match(/^[0-9]+/);
+ if (numReg) {
+ var num = numReg[0];
+ var orilen = num.length;
+ var newnum = parseInt(num) + 1 + "";
+ // 进位到整数部分
+ if (newnum.length > orilen) {
+ newnum = newnum.substr(1);
+ } else {
+ newnum = BI.leftPad(newnum, orilen, "0");
+ result.leftPlus = false;
+ }
+ right = right.replace(/^[0-9]+/, newnum);
+ }
+ }
+ result.num = right;
+ return result;
+ }
+
+ /**
+ * 处理小数点左边整数部分
+ * @param tleft 左边内容
+ * @param fleft 左边格式
+ * @returns {string} 返回处理结果
+ * @private
+ */
+ function _dealWithLeft (tleft, fleft) {
+ var left = "";
+ var j = tleft.length - 1;
+ var combo = -1, last = -1;
+ var i = fleft.length - 1;
+ for (; i >= 0; i--) {
+ var ch = fleft.charAt(i);
+ var c = tleft.charAt(j);
+ switch (ch) {
+ case "0":
+ if (isEmpty(c)) {
+ c = "0";
+ }
+ last = -1;
+ left = c + left;
+ j--;
+ break;
+ case "#":
+ last = i;
+ left = c + left;
+ j--;
+ break;
+ case ",":
+ if (!isEmpty(c)) {
+ // 计算一个,分隔区间的长度
+ var com = fleft.match(/,[#0]+/);
+ if (com) {
+ combo = com[0].length - 1;
+ }
+ left = "," + left;
+ }
+ break;
+ default :
+ left = ch + left;
+ break;
+ }
+ }
+ if (last > -1) {
+ // 处理剩余字符
+ var tll = tleft.substr(0, j + 1);
+ left = left.substr(0, last) + tll + left.substr(last);
+ }
+ if (combo > 0) {
+ // 处理,分隔区间
+ var res = left.match(/[0-9]+,/);
+ if (res) {
+ res = res[0];
+ var newstr = "", n = res.length - 1 - combo;
+ for (; n >= 0; n = n - combo) {
+ newstr = res.substr(n, combo) + "," + newstr;
+ }
+ var lres = res.substr(0, n + combo);
+ if (!isEmpty(lres)) {
+ newstr = lres + "," + newstr;
+ }
+ }
+ left = left.replace(/[0-9]+,/, newstr);
+ }
+ return left;
+ }
+
+
+ BI.cjkEncode = function (text) {
+ // alex:如果非字符串,返回其本身(cjkEncode(234) 返回 ""是不对的)
+ if (typeof text !== "string") {
+ return text;
+ }
+
+ var newText = "";
+ for (var i = 0; i < text.length; i++) {
+ var code = text.charCodeAt(i);
+ if (code >= 128 || code === 91 || code === 93) {// 91 is "[", 93 is "]".
+ newText += "[" + code.toString(16) + "]";
+ } else {
+ newText += text.charAt(i);
+ }
+ }
+
+ return newText;
+ };
+
+ /**
+ * 将cjkEncode处理过的字符串转化为原始字符串
+ *
+ * @static
+ * @param text 需要做解码的字符串
+ * @return {String} 解码后的字符串
+ */
+ BI.cjkDecode = function (text) {
+ if (text == null) {
+ return "";
+ }
+ // 查找没有 "[", 直接返回. kunsnat:数字的时候, 不支持indexOf方法, 也是直接返回.
+ if (!isNaN(text) || text.indexOf("[") == -1) {
+ return text;
+ }
+
+ var newText = "";
+ for (var i = 0; i < text.length; i++) {
+ var ch = text.charAt(i);
+ if (ch == "[") {
+ var rightIdx = text.indexOf("]", i + 1);
+ if (rightIdx > i + 1) {
+ var subText = text.substring(i + 1, rightIdx);
+ // james:主要是考虑[CDATA[]]这样的值的出现
+ if (subText.length > 0) {
+ ch = String.fromCharCode(eval("0x" + subText));
+ }
+
+ i = rightIdx;
+ }
+ }
+
+ newText += ch;
+ }
+
+ return newText;
+ };
+
+ // replace the html special tags
+ var SPECIAL_TAGS = {
+ "&": "&",
+ "\"": """,
+ "<": "<",
+ ">": ">",
+ " ": " "
+ };
+ BI.htmlEncode = function (text) {
+ return BI.isNull(text) ? "" : BI.replaceAll(text + "", "&|\"|<|>|\\s", function (v) {
+ return SPECIAL_TAGS[v] ? SPECIAL_TAGS[v] : " ";
+ });
+ };
+ // html decode
+ BI.htmlDecode = function (text) {
+ return BI.isNull(text) ? "" : BI.replaceAll(text + "", "&|"|<|>| ", function (v) {
+ switch (v) {
+ case "&":
+ return "&";
+ case """:
+ return "\"";
+ case "<":
+ return "<";
+ case ">":
+ return ">";
+ case " ":
+ default:
+ return " ";
+ }
+ });
+ };
+
+ BI.cjkEncodeDO = function (o) {
+ if (BI.isPlainObject(o)) {
+ var result = {};
+ _.each(o, function (v, k) {
+ if (!(typeof v === "string")) {
+ v = BI.jsonEncode(v);
+ }
+ // wei:bug 43338,如果key是中文,cjkencode后o的长度就加了1,ie9以下版本死循环,所以新建对象result。
+ k = BI.cjkEncode(k);
+ result[k] = BI.cjkEncode(v);
+ });
+ return result;
+ }
+ return o;
+ };
+
+ BI.jsonEncode = function (o) {
+ // james:这个Encode是抄的EXT的
+ var useHasOwn = !!{}.hasOwnProperty;
+
+ // crashes Safari in some instances
+ // var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
+
+ var m = {
+ "\b": "\\b",
+ "\t": "\\t",
+ "\n": "\\n",
+ "\f": "\\f",
+ "\r": "\\r",
+ "\"": "\\\"",
+ "\\": "\\\\"
+ };
+
+ var encodeString = function (s) {
+ if (/["\\\x00-\x1f]/.test(s)) {
+ return "\"" + s.replace(/([\x00-\x1f\\"])/g, function (a, b) {
+ var c = m[b];
+ if (c) {
+ return c;
+ }
+ c = b.charCodeAt();
+ return "\\u00" +
+ Math.floor(c / 16).toString(16) +
+ (c % 16).toString(16);
+ }) + "\"";
+ }
+ return "\"" + s + "\"";
+ };
+
+ var encodeArray = function (o) {
+ var a = ["["], b, i, l = o.length, v;
+ for (i = 0; i < l; i += 1) {
+ v = o[i];
+ switch (typeof v) {
+ case "undefined":
+ case "function":
+ case "unknown":
+ break;
+ default:
+ if (b) {
+ a.push(",");
+ }
+ a.push(v === null ? "null" : BI.jsonEncode(v));
+ b = true;
+ }
+ }
+ a.push("]");
+ return a.join("");
+ };
+
+ if (typeof o === "undefined" || o === null) {
+ return "null";
+ } else if (BI.isArray(o)) {
+ return encodeArray(o);
+ } else if (o instanceof Date) {
+ /*
+ * alex:原来只是把年月日时分秒简单地拼成一个String,无法decode
+ * 现在这么处理就可以decode了,但是JS.jsonDecode和Java.JSONObject也要跟着改一下
+ */
+ return BI.jsonEncode({
+ __time__: o.getTime()
+ });
+ } else if (typeof o === "string") {
+ return encodeString(o);
+ } else if (typeof o === "number") {
+ return isFinite(o) ? String(o) : "null";
+ } else if (typeof o === "boolean") {
+ return String(o);
+ } else if (BI.isFunction(o)) {
+ return String(o);
+ }
+ var a = ["{"], b, i, v;
+ for (i in o) {
+ if (!useHasOwn || o.hasOwnProperty(i)) {
+ v = o[i];
+ switch (typeof v) {
+ case "undefined":
+ case "unknown":
+ break;
+ default:
+ if (b) {
+ a.push(",");
+ }
+ a.push(BI.jsonEncode(i), ":",
+ v === null ? "null" : BI.jsonEncode(v));
+ b = true;
+ }
+ }
+ }
+ a.push("}");
+ return a.join("");
+
+ };
+
+ BI.jsonDecode = function (text) {
+
+ try {
+ // 注意0啊
+ // var jo = $.parseJSON(text) || {};
+ var jo = BI.$ ? BI.$.parseJSON(text) : _global.JSON.parse(text);
+ if (jo == null) {
+ jo = {};
+ }
+ } catch (e) {
+ /*
+ * richie:浏览器只支持标准的JSON字符串转换,而jQuery会默认调用浏览器的window.JSON.parse()函数进行解析
+ * 比如:var str = "{'a':'b'}",这种形式的字符串转换为JSON就会抛异常
+ */
+ try {
+ jo = new Function("return " + text)() || {};
+ } catch (e) {
+ // do nothing
+ }
+ if (jo == null) {
+ jo = [];
+ }
+ }
+ if (!_hasDateInJson(text)) {
+ return jo;
+ }
+
+ function _hasDateInJson (json) {
+ if (!json || typeof json !== "string") {
+ return false;
+ }
+ return json.indexOf("__time__") != -1;
+ }
+
+ return (function (o) {
+ if (typeof o === "string") {
+ return o;
+ }
+ if (o && o.__time__ != null) {
+ return new Date(o.__time__);
+ }
+ for (var a in o) {
+ if (o[a] == o || typeof o[a] === "object" || _.isFunction(o[a])) {
+ break;
+ }
+ o[a] = arguments.callee(o[a]);
+ }
+
+ return o;
+ })(jo);
+ };
+
+ /**
+ * 获取编码后的url
+ * @param urlTemplate url模板
+ * @param param 参数
+ * @returns {*|String}
+ * @example
+ * BI.getEncodeURL("design/{tableName}/{fieldName}",{tableName: "A", fieldName: "a"}) // design/A/a
+ */
+ BI.getEncodeURL = function (urlTemplate, param) {
+ return BI.replaceAll(urlTemplate, "\\{(.*?)\\}", function (ori, str) {
+ return BI.encodeURIComponent(BI.isObject(param) ? param[str] : param);
+ });
+ };
+
+ BI.encodeURIComponent = function (url) {
+ BI.specialCharsMap = BI.specialCharsMap || {};
+ url = url || "";
+ url = BI.replaceAll(url + "", BI.keys(BI.specialCharsMap || []).join("|"), function (str) {
+ switch (str) {
+ case "\\":
+ return BI.specialCharsMap["\\\\"] || str;
+ default:
+ return BI.specialCharsMap[str] || str;
+ }
+ });
+ return _global.encodeURIComponent(url);
+ };
+
+ BI.decodeURIComponent = function (url) {
+ var reserveSpecialCharsMap = {};
+ BI.each(BI.specialCharsMap, function (initialChar, encodeChar) {
+ reserveSpecialCharsMap[encodeChar] = initialChar === "\\\\" ? "\\" : initialChar;
+ });
+ url = url || "";
+ url = BI.replaceAll(url + "", BI.keys(reserveSpecialCharsMap || []).join("|"), function (str) {
+ return reserveSpecialCharsMap[str] || str;
+ });
+ return _global.decodeURIComponent(url);
+ };
+
+ BI.contentFormat = function (cv, fmt) {
+ if (isEmpty(cv)) {
+ // 原值为空,返回空字符
+ return "";
+ }
+ var text = cv.toString();
+ if (isEmpty(fmt)) {
+ // 格式为空,返回原字符
+ return text;
+ }
+ if (fmt.match(/^T/)) {
+ // T - 文本格式
+ return text;
+ } else if (fmt.match(/^D/)) {
+ // D - 日期(时间)格式
+ if (!(cv instanceof Date)) {
+ if (typeof cv === "number") {
+ // 毫秒数类型
+ cv = new Date(cv);
+ } else {
+ //字符串类型转化为date类型
+ cv = new Date(Date.parse(("" + cv).replace(/-|\./g, "/")));
+ }
+ }
+ if (!isInvalidDate(cv) && !BI.isNull(cv)) {
+ var needTrim = fmt.match(/^DT/);
+ text = BI.date2Str(cv, fmt.substring(needTrim ? 2 : 1));
+ }
+ } else if (fmt.match(/E/)) {
+ // 科学计数格式
+ text = _eFormat(text, fmt);
+ } else {
+ // 数字格式
+ text = _numberFormat(text, fmt);
+ }
+ // ¤ - 货币格式
+ text = text.replace(/¤/g, "¥");
+ return text;
+ };
+
+ /**
+ * 将Java提供的日期格式字符串装换为JS识别的日期格式字符串
+ * @class FR.parseFmt
+ * @param fmt 日期格式
+ * @returns {String}
+ */
+ BI.parseFmt = function (fmt) {
+ if (!fmt) {
+ return "";
+ }
+ //日期
+ fmt = String(fmt)
+ //年
+ .replace(/y{4,}/g, "%Y")//yyyy的时候替换为Y
+ .replace(/y{2}/g, "%y")//yy的时候替换为y
+ //月
+ .replace(/M{4,}/g, "%b")//MMMM的时候替换为b,八
+ .replace(/M{3}/g, "%B")//MMM的时候替换为M,八月
+ .replace(/M{2}/g, "%X")//MM的时候替换为X,08
+ .replace(/M{1}/g, "%x")//M的时候替换为x,8
+ .replace(/a{1}/g, "%p");
+ //天
+ if (new RegExp("d{2,}", "g").test(fmt)) {
+ fmt = fmt.replace(/d{2,}/g, "%d");//dd的时候替换为d
+ } else {
+ fmt = fmt.replace(/d{1}/g, "%e");//d的时候替换为j
+ }
+ //时
+ if (new RegExp("h{2,}", "g").test(fmt)) {//12小时制
+ fmt = fmt.replace(/h{2,}/g, "%I");
+ } else {
+ fmt = fmt.replace(/h{1}/g, "%I");
+ }
+ if (new RegExp("H{2,}", "g").test(fmt)) {//24小时制
+ fmt = fmt.replace(/H{2,}/g, "%H");
+ } else {
+ fmt = fmt.replace(/H{1}/g, "%H");
+ }
+ fmt = fmt.replace(/m{2,}/g, "%M")//分
+ //秒
+ .replace(/s{2,}/g, "%S");
+
+ return fmt;
+ };
+
+ /**
+ * 把字符串按照对应的格式转化成日期对象
+ *
+ * @example
+ * var result = BI.str2Date('2013-12-12', 'yyyy-MM-dd');//Thu Dec 12 2013 00:00:00 GMT+0800
+ *
+ * @class BI.str2Date
+ * @param str 字符串
+ * @param format 日期格式
+ * @returns {*}
+ */
+ BI.str2Date = function (str, format) {
+ if (typeof str != "string" || typeof format != "string") {
+ return null;
+ }
+ var fmt = BI.parseFmt(format);
+ return BI.parseDateTime(str, fmt);
+ };
+
+ /**
+ * 把日期对象按照指定格式转化成字符串
+ *
+ * @example
+ * var date = new Date('Thu Dec 12 2013 00:00:00 GMT+0800');
+ * var result = BI.date2Str(date, 'yyyy-MM-dd');//2013-12-12
+ *
+ * @class BI.date2Str
+ * @param date 日期
+ * @param format 日期格式
+ * @returns {String}
+ */
+ BI.date2Str = function (date, format) {
+ if (!date) {
+ return "";
+ }
+ // O(len(format))
+ var len = format.length, result = "";
+ if (len > 0) {
+ var flagch = format.charAt(0), start = 0, str = flagch;
+ for (var i = 1; i < len; i++) {
+ var ch = format.charAt(i);
+ if (flagch !== ch) {
+ result += compileJFmt({
+ char: flagch,
+ str: str,
+ len: i - start
+ }, date);
+ flagch = ch;
+ start = i;
+ str = flagch;
+ } else {
+ str += ch;
+ }
+ }
+ result += compileJFmt({
+ char: flagch,
+ str: str,
+ len: len - start
+ }, date);
+ }
+ return result;
+
+ function compileJFmt (jfmt, date) {
+ var str = jfmt.str, len = jfmt.len, ch = jfmt["char"];
+ switch (ch) {
+ case "E": // 星期
+ str = BI.Date._DN[date.getDay()];
+ break;
+ case "y": // 年
+ if (len <= 3) {
+ str = (date.getFullYear() + "").slice(2, 4);
+ } else {
+ str = date.getFullYear();
+ }
+ break;
+ case "M": // 月
+ if (len > 2) {
+ str = BI.Date._MN[date.getMonth()];
+ } else if (len < 2) {
+ str = date.getMonth() + 1;
+ } else {
+ str = BI.leftPad(date.getMonth() + 1 + "", 2, "0");
+ }
+ break;
+ case "d": // 日
+ if (len > 1) {
+ str = BI.leftPad(date.getDate() + "", 2, "0");
+ } else {
+ str = date.getDate();
+ }
+ break;
+ case "h": // 时(12)
+ var hour = date.getHours() % 12;
+ if (hour === 0) {
+ hour = 12;
+ }
+ if (len > 1) {
+ str = BI.leftPad(hour + "", 2, "0");
+ } else {
+ str = hour;
+ }
+ break;
+ case "H": // 时(24)
+ if (len > 1) {
+ str = BI.leftPad(date.getHours() + "", 2, "0");
+ } else {
+ str = date.getHours();
+ }
+ break;
+ case "m":
+ if (len > 1) {
+ str = BI.leftPad(date.getMinutes() + "", 2, "0");
+ } else {
+ str = date.getMinutes();
+ }
+ break;
+ case "s":
+ if (len > 1) {
+ str = BI.leftPad(date.getSeconds() + "", 2, "0");
+ } else {
+ str = date.getSeconds();
+ }
+ break;
+ case "a":
+ str = date.getHours() < 12 ? "am" : "pm";
+ break;
+ case "z":
+ str = BI.getTimezone(date);
+ break;
+ default:
+ str = jfmt.str;
+ break;
+ }
+ return str;
+ }
+ };
+
+ BI.object2Number = function (value) {
+ if (value == null) {
+ return 0;
+ }
+ if (typeof value === "number") {
+ return value;
+ }
+ var str = value + "";
+ if (str.indexOf(".") === -1) {
+ return parseInt(str);
+ }
+ return parseFloat(str);
+ };
+
+ BI.object2Date = function (obj) {
+ if (obj == null) {
+ return new Date();
+ }
+ if (obj instanceof Date) {
+ return obj;
+ } else if (typeof obj === "number") {
+ return new Date(obj);
+ }
+ var str = obj + "";
+ str = str.replace(/-/g, "/");
+ var dt = new Date(str);
+ if (!isInvalidDate(dt)) {
+ return dt;
+ }
+
+ return new Date();
+
+ };
+
+ BI.object2Time = function (obj) {
+ if (obj == null) {
+ return new Date();
+ }
+ if (obj instanceof Date) {
+ return obj;
+ }
+ var str = obj + "";
+ str = str.replace(/-/g, "/");
+ var dt = new Date(str);
+ if (!isInvalidDate(dt)) {
+ return dt;
+ }
+ if (str.indexOf("/") === -1 && str.indexOf(":") !== -1) {
+ dt = new Date("1970/01/01 " + str);
+ if (!isInvalidDate(dt)) {
+ return dt;
+ }
+ }
+ dt = BI.parseDateTime(str, "HH:mm:ss");
+ if (!isInvalidDate(dt)) {
+ return dt;
+ }
+ return new Date();
+
+ };
+})();
diff --git a/src/main/resources/com/fr/fineui/core/func/array.js b/src/main/resources/com/fr/fineui/core/func/array.js
new file mode 100644
index 0000000..7111530
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/func/array.js
@@ -0,0 +1,22 @@
+/**
+ * 对数组对象的扩展
+ * @class Array
+ */
+_.extend(BI, {
+
+ pushArray: function (sArray, array) {
+ for (var i = 0; i < array.length; i++) {
+ sArray.push(array[i]);
+ }
+ },
+ pushDistinct: function (sArray, obj) {
+ if (!BI.contains(sArray, obj)) {
+ sArray.push(obj);
+ }
+ },
+ pushDistinctArray: function (sArray, array) {
+ for (var i = 0, len = array.length; i < len; i++) {
+ BI.pushDistinct(sArray, array[i]);
+ }
+ }
+});
diff --git a/src/main/resources/com/fr/fineui/core/func/date.js b/src/main/resources/com/fr/fineui/core/func/date.js
new file mode 100644
index 0000000..2b75540
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/func/date.js
@@ -0,0 +1,245 @@
+/** Constants used for time computations */
+BI.Date = BI.Date || {};
+BI.Date.SECOND = 1000;
+BI.Date.MINUTE = 60 * BI.Date.SECOND;
+BI.Date.HOUR = 60 * BI.Date.MINUTE;
+BI.Date.DAY = 24 * BI.Date.HOUR;
+BI.Date.WEEK = 7 * BI.Date.DAY;
+
+_.extend(BI, {
+ /**
+ * 获取时区
+ * @returns {String}
+ */
+ getTimezone: function (date) {
+ return date.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
+ },
+
+ /** Returns the number of days in the current month */
+ getMonthDays: function (date, month) {
+ var year = date.getFullYear();
+ if (typeof month === "undefined") {
+ month = date.getMonth();
+ }
+ if (((0 == (year % 4)) && ((0 != (year % 100)) || (0 == (year % 400)))) && month == 1) {
+ return 29;
+ }
+ return BI.Date._MD[month];
+
+ },
+
+ /**
+ * 获取每月的最后一天
+ * @returns {Date}
+ */
+ getLastDateOfMonth: function (date) {
+ return BI.getDate(date.getFullYear(), date.getMonth(), BI.getMonthDays(date));
+ },
+
+ /** Returns the number of day in the year. */
+ getDayOfYear: function (date) {
+ var now = BI.getDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
+ var then = BI.getDate(date.getFullYear(), 0, 0, 0, 0, 0);
+ var time = now - then;
+ return Math.floor(time / BI.Date.DAY);
+ },
+
+ /** Returns the number of the week in year, as defined in ISO 8601. */
+ getWeekNumber: function (date) {
+ var d = BI.getDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
+ var week = d.getDay();
+ var startOfWeek = BI.StartOfWeek % 7;
+ var middleDay = (startOfWeek + 3) % 7;
+ middleDay = middleDay || 7;
+ // 偏移到周周首之前需要多少天
+ var offsetWeekStartCount = week < startOfWeek ? (7 + week - startOfWeek) : (week - startOfWeek);
+ var offsetWeekMiddleCount = middleDay < startOfWeek ? (7 + middleDay - startOfWeek) : (middleDay - startOfWeek);
+ d.setDate(d.getDate() - offsetWeekStartCount + offsetWeekMiddleCount);
+ var ms = d.valueOf();
+ d.setMonth(0);
+ d.setDate(1);
+ return Math.floor((ms - d.valueOf()) / (7 * 864e5)) + 1;
+ },
+
+ getQuarter: function (date) {
+ return Math.floor(date.getMonth() / 3) + 1;
+ },
+
+ // 离当前时间多少天的时间
+ getOffsetDate: function (date, offset) {
+ return BI.getDate(BI.getTime(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()) + offset * 864e5);
+ },
+
+ getOffsetQuarter: function (date, n) {
+ var dt = BI.getDate(BI.getTime(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()));
+ var day = dt.getDate();
+ var monthDay = BI.getMonthDays(BI.getDate(dt.getFullYear(), dt.getMonth() + BI.parseInt(n) * 3, 1));
+ if (day > monthDay) {
+ day = monthDay;
+ }
+ dt.setDate(day);
+ dt.setMonth(dt.getMonth() + parseInt(n) * 3);
+ return dt;
+ },
+
+ // 得到本季度的起始月份
+ getQuarterStartMonth: function (date) {
+ var quarterStartMonth = 0;
+ var nowMonth = date.getMonth();
+ if (nowMonth < 3) {
+ quarterStartMonth = 0;
+ }
+ if (2 < nowMonth && nowMonth < 6) {
+ quarterStartMonth = 3;
+ }
+ if (5 < nowMonth && nowMonth < 9) {
+ quarterStartMonth = 6;
+ }
+ if (nowMonth > 8) {
+ quarterStartMonth = 9;
+ }
+ return quarterStartMonth;
+ },
+ // 获得本季度的起始日期
+ getQuarterStartDate: function (date) {
+ return BI.getDate(date.getFullYear(), BI.getQuarterStartMonth(date), 1);
+ },
+ // 得到本季度的结束日期
+ getQuarterEndDate: function (date) {
+ var quarterEndMonth = BI.getQuarterStartMonth(date) + 2;
+ return BI.getDate(date.getFullYear(), quarterEndMonth, BI.getMonthDays(date, quarterEndMonth));
+ },
+
+ // 指定日期n个月之前或之后的日期
+ getOffsetMonth: function (date, n) {
+ var dt = BI.getDate(BI.getTime(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()));
+ var day = dt.getDate();
+ var monthDay = BI.getMonthDays(BI.getDate(dt.getFullYear(), dt.getMonth() + parseInt(n), 1));
+ if (day > monthDay) {
+ day = monthDay;
+ }
+ dt.setDate(day);
+ dt.setMonth(dt.getMonth() + parseInt(n));
+ return dt;
+ },
+
+ // 获得本周的起始日期
+ getWeekStartDate: function (date) {
+ var w = date.getDay();
+ var startOfWeek = BI.StartOfWeek % 7;
+ return BI.getOffsetDate(date, BI.Date._OFFSET[w < startOfWeek ? (7 + w - startOfWeek) : (w - startOfWeek)]);
+ },
+ // 得到本周的结束日期
+ getWeekEndDate: function (date) {
+ var w = date.getDay();
+ var startOfWeek = BI.StartOfWeek % 7;
+ return BI.getOffsetDate(date, BI.Date._OFFSET[w < startOfWeek ? (7 + w - startOfWeek) : (w - startOfWeek)] + 6);
+ },
+
+ // 格式化打印日期
+ print: function (date, str) {
+ var m = date.getMonth();
+ var d = date.getDate();
+ var y = date.getFullYear();
+ var yWith4number = y + "";
+ while (yWith4number.length < 4) {
+ yWith4number = "0" + yWith4number;
+ }
+ var wn = BI.getWeekNumber(date);
+ var qr = BI.getQuarter(date);
+ var w = date.getDay();
+ var s = {};
+ var hr = date.getHours();
+ var pm = (hr >= 12);
+ var ir = (pm) ? (hr - 12) : hr;
+ var dy = BI.getDayOfYear(date);
+ if (ir == 0) {
+ ir = 12;
+ }
+ var min = date.getMinutes();
+ var sec = date.getSeconds();
+ s["%a"] = BI.Date._SDN[w]; // abbreviated weekday name [FIXME: I18N]
+ s["%A"] = BI.Date._DN[w]; // full weekday name
+ s["%b"] = BI.Date._SMN[m]; // abbreviated month name [FIXME: I18N]
+ s["%B"] = BI.Date._MN[m]; // full month name
+ // FIXME: %c : preferred date and time representation for the current locale
+ s["%C"] = 1 + Math.floor(y / 100); // the century number
+ s["%d"] = (d < 10) ? ("0" + d) : d; // the day of the month (range 01 to 31)
+ s["%e"] = d; // the day of the month (range 1 to 31)
+ // FIXME: %D : american date style: %m/%d/%y
+ // FIXME: %E, %F, %G, %g, %h (man strftime)
+ s["%H"] = (hr < 10) ? ("0" + hr) : hr; // hour, range 00 to 23 (24h format)
+ s["%I"] = (ir < 10) ? ("0" + ir) : ir; // hour, range 01 to 12 (12h format)
+ s["%j"] = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy; // day of the year (range 001 to 366)
+ s["%k"] = hr + ""; // hour, range 0 to 23 (24h format)
+ s["%l"] = ir + ""; // hour, range 1 to 12 (12h format)
+ s["%X"] = (m < 9) ? ("0" + (1 + m)) : (1 + m); // month, range 01 to 12
+ s["%x"] = m + 1; // month, range 1 to 12
+ s["%M"] = (min < 10) ? ("0" + min) : min; // minute, range 00 to 59
+ s["%n"] = "\n"; // a newline character
+ s["%p"] = pm ? "PM" : "AM";
+ s["%P"] = pm ? "pm" : "am";
+ // FIXME: %r : the time in am/pm notation %I:%M:%S %p
+ // FIXME: %R : the time in 24-hour notation %H:%M
+ s["%s"] = Math.floor(date.getTime() / 1000);
+ s["%S"] = (sec < 10) ? ("0" + sec) : sec; // seconds, range 00 to 59
+ s["%t"] = "\t"; // a tab character
+ // FIXME: %T : the time in 24-hour notation (%H:%M:%S)
+ s["%U"] = s["%W"] = s["%V"] = (wn < 10) ? ("0" + wn) : wn;
+ s["%u"] = w + 1; // the day of the week (range 1 to 7, 1 = MON)
+ s["%w"] = w; // the day of the week (range 0 to 6, 0 = SUN)
+ // FIXME: %x : preferred date representation for the current locale without the time
+ // FIXME: %X : preferred time representation for the current locale without the date
+ s["%y"] = yWith4number.substr(2, 2); // year without the century (range 00 to 99)
+ s["%Y"] = yWith4number; // year with the century
+ s["%%"] = "%"; // a literal '%' character
+ s["%q"] = "0" + qr;
+ s["%Q"] = qr;
+
+ var re = /%./g;
+ BI.isKhtml = BI.isKhtml || function () {
+ if(!_global.navigator) {
+ return false;
+ }
+ return /Konqueror|Safari|KHTML/i.test(navigator.userAgent);
+ };
+
+ // 包含年周的格式化,ISO8601标准周的计数会影响年
+ if ((str.indexOf("%Y") !== -1 || str.indexOf("%y") !== -1) && (str.indexOf("%W") !== -1 || str.indexOf("%U") !== -1 || str.indexOf("%V") !== -1)) {
+ switch (wn) {
+ // 如果周数是1,但是当前却在12月,表示此周数为下一年的
+ case 1:
+ if (m === 11) {
+ s["%y"] = parseInt(s["%y"]) + 1;
+ s["%Y"] = parseInt(s["%Y"]) + 1;
+ }
+ break;
+ // 如果周数是53,但是当前却在1月,表示此周数为上一年的
+ case 53:
+ if (m === 0) {
+ s["%y"] = parseInt(s["%y"]) - 1;
+ s["%Y"] = parseInt(s["%Y"]) - 1;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!BI.isKhtml()) {
+ return str.replace(re, function (par) {
+ return s[par] || par;
+ });
+ }
+ var a = str.match(re);
+ for (var i = 0; i < a.length; i++) {
+ var tmp = s[a[i]];
+ if (tmp) {
+ re = new RegExp(a[i], "g");
+ str = str.replace(re, tmp);
+ }
+ }
+
+ return str;
+ }
+});
diff --git a/src/main/resources/com/fr/fineui/core/func/function.js b/src/main/resources/com/fr/fineui/core/func/function.js
new file mode 100644
index 0000000..05c8fe7
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/func/function.js
@@ -0,0 +1,171 @@
+/**
+ * 基本的函数
+ * Created by GUY on 2015/6/24.
+ */
+BI.Func = BI.Func || {};
+_.extend(BI.Func, {
+ /**
+ * 创建唯一的名字
+ * @param array
+ * @param name
+ * @returns {*}
+ */
+ createDistinctName: function (array, name) {
+ var src = name, idx = 1;
+ name = name || "";
+ while (true) {
+ if (BI.every(array, function (i, item) {
+ return BI.isKey(item) ? item !== name : item.name !== name;
+ })) {
+ break;
+ }
+ name = src + (idx++);
+ }
+ return name;
+ },
+
+ /**
+ * 获取字符宽度
+ * @param str
+ * @return {number}
+ */
+ getGBWidth: function (str) {
+ str = str + "";
+ str = str.replace(/[^\x00-\xff]/g, "xx");
+ return Math.ceil(str.length / 2);
+ },
+
+ /**
+ * 获取搜索结果
+ * @param items
+ * @param keyword
+ * @param param 搜索哪个属性
+ * @param clone 是否需要deepClone
+ */
+ getSearchResult: function (items, keyword, param, clone) {
+ var isArray = BI.isArray(items);
+ items = isArray ? BI.flatten(items) : items;
+ param || (param = "text");
+ BI.isNull(clone) && (clone = true);
+ if (!BI.isKey(keyword)) {
+ return {
+ find: clone ? BI.deepClone(items) : items,
+ match: isArray ? [] : {}
+ };
+ }
+ var t, text, py;
+ keyword = BI.toUpperCase(keyword);
+ var matched = isArray ? [] : {}, find = isArray ? [] : {};
+ BI.each(items, function (i, item) {
+ // 兼容item为null的处理
+ if (BI.isNull(item)) {
+ return;
+ }
+ clone && (item = BI.deepClone(item));
+ t = BI.stripEL(item);
+ text = BI.find([t[param], t.text, t.value, t.name, t], function (index, val) {
+ return BI.isNotNull(val);
+ });
+
+ if (BI.isNull(text) || BI.isObject(text)) return;
+
+ py = BI.makeFirstPY(text, {
+ splitChar: "\u200b"
+ });
+ text = BI.toUpperCase(text);
+ py = BI.toUpperCase(py);
+ var pidx;
+ if (text.indexOf(keyword) > -1) {
+ if (text === keyword) {
+ isArray ? matched.push(item) : (matched[i] = item);
+ } else {
+ isArray ? find.push(item) : (find[i] = item);
+ }
+ // BI-56386 这边两个pid / text.length是为了防止截取的首字符串不是完整的,但光这样做还不够,即时错位了,也不能说明就不符合条件
+ } else if (pidx = py.indexOf(keyword), (pidx > -1)) {
+ if (text === keyword || keyword.length === text.length) {
+ isArray ? matched.push(item) : (matched[i] = item);
+ } else {
+ isArray ? find.push(item) : (find[i] = item);
+ }
+ }
+ });
+ return {
+ match: matched,
+ find: find
+ };
+ },
+
+ /**
+ * 获取按GB2312排序的结果
+ * @param items
+ * @param key
+ * @return {any[]}
+ */
+ getSortedResult: function (items, key) {
+ var getTextOfItem = BI.isFunction(key) ? key :
+ function (item, key) {
+ if (BI.isNotNull(key)) {
+ return item[key];
+ }
+ if (BI.isNotNull(item.text)) {
+ return item.text;
+ }
+ if (BI.isNotNull(item.value)) {
+ return item.value;
+ }
+ return item;
+ };
+
+ return items.sort(function (item1, item2) {
+ var str1 = getTextOfItem(item1, key);
+ var str2 = getTextOfItem(item2, key);
+ if (BI.isNull(str1) && BI.isNull(str2)) {
+ return 0;
+ }
+ if (BI.isNull(str1)) {
+ return -1;
+ }
+ if (BI.isNull(str2)) {
+ return 1;
+ }
+ if (str1 === str2) {
+ return 0;
+ }
+ var len1 = str1.length, len2 = str2.length;
+ for (var i = 0; i < len1 && i < len2; i++) {
+ var char1 = str1[i];
+ var char2 = str2[i];
+ if (char1 !== char2) {
+ // 找不到的字符都往后面放
+ return (BI.isNull(BI.CODE_INDEX[char1]) ? BI.MAX : BI.CODE_INDEX[char1]) - (BI.isNull(BI.CODE_INDEX[char2]) ? BI.MAX : BI.CODE_INDEX[char2]);
+ }
+ }
+ return len1 - len2;
+ });
+ }
+});
+
+_.extend(BI, {
+ beforeFunc: function (sFunc, func) {
+ var __self = sFunc;
+ return function () {
+ if (func.apply(sFunc, arguments) === false) {
+ return false;
+ }
+ return __self.apply(sFunc, arguments);
+ };
+ },
+
+ afterFunc: function (sFunc, func) {
+ var __self = sFunc;
+ return function () {
+ var ret = __self.apply(sFunc, arguments);
+ if (ret === false) {
+ return false;
+ }
+ func.apply(sFunc, arguments);
+ return ret;
+ };
+ }
+});
diff --git a/src/main/resources/com/fr/fineui/core/func/number.js b/src/main/resources/com/fr/fineui/core/func/number.js
new file mode 100644
index 0000000..17083ca
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/func/number.js
@@ -0,0 +1,156 @@
+_.extend(BI, {
+ // 给Number类型增加一个add方法,调用起来更加方便。
+ add: function (num, arg) {
+ return accAdd(arg, num);
+
+ /**
+ ** 加法函数,用来得到精确的加法结果
+ ** 说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
+ ** 调用:accAdd(arg1,arg2)
+ ** 返回值:arg1加上arg2的精确结果
+ **/
+ function accAdd (arg1, arg2) {
+ var r1, r2, m, c;
+ try {
+ r1 = arg1.toString().split(".")[1].length;
+ } catch (e) {
+ r1 = 0;
+ }
+ try {
+ r2 = arg2.toString().split(".")[1].length;
+ } catch (e) {
+ r2 = 0;
+ }
+ c = Math.abs(r1 - r2);
+ m = Math.pow(10, Math.max(r1, r2));
+ if (c > 0) {
+ var cm = Math.pow(10, c);
+ if (r1 > r2) {
+ arg1 = Number(arg1.toString().replace(".", ""));
+ arg2 = Number(arg2.toString().replace(".", "")) * cm;
+ } else {
+ arg1 = Number(arg1.toString().replace(".", "")) * cm;
+ arg2 = Number(arg2.toString().replace(".", ""));
+ }
+ } else {
+ arg1 = Number(arg1.toString().replace(".", ""));
+ arg2 = Number(arg2.toString().replace(".", ""));
+ }
+ return (arg1 + arg2) / m;
+ }
+ },
+
+ // 给Number类型增加一个sub方法,调用起来更加方便。
+ sub: function (num, arg) {
+ return accSub(num, arg);
+
+ /**
+ ** 减法函数,用来得到精确的减法结果
+ ** 说明:javascript的减法结果会有误差,在两个浮点数相减的时候会比较明显。这个函数返回较为精确的减法结果。
+ ** 调用:accSub(arg1,arg2)
+ ** 返回值:arg1加上arg2的精确结果
+ **/
+ function accSub (arg1, arg2) {
+ var r1, r2, m, n;
+ try {
+ r1 = arg1.toString().split(".")[1].length;
+ } catch (e) {
+ r1 = 0;
+ }
+ try {
+ r2 = arg2.toString().split(".")[1].length;
+ } catch (e) {
+ r2 = 0;
+ }
+ m = Math.pow(10, Math.max(r1, r2)); // last modify by deeka //动态控制精度长度
+ n = (r1 >= r2) ? r1 : r2;
+ return ((arg1 * m - arg2 * m) / m).toFixed(n);
+ }
+ },
+
+ // 给Number类型增加一个mul方法,调用起来更加方便。
+ mul: function (num, arg) {
+ return accMul(arg, num);
+
+ /**
+ ** 乘法函数,用来得到精确的乘法结果
+ ** 说明:javascript的乘法结果会有误差,在两个浮点数相乘的时候会比较明显。这个函数返回较为精确的乘法结果。
+ ** 调用:accMul(arg1,arg2)
+ ** 返回值:arg1乘以 arg2的精确结果
+ **/
+ function accMul (arg1, arg2) {
+ var m = 0, s1 = arg1.toString(), s2 = arg2.toString();
+ try {
+ m += s1.split(".")[1].length;
+ } catch (e) {
+ }
+ try {
+ m += s2.split(".")[1].length;
+ } catch (e) {
+ }
+ return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
+ }
+ },
+
+ // 给Number类型增加一个div方法,调用起来更加方便。
+ div: function (num, arg) {
+ return accDivide(num, arg);
+
+ /**
+ * Return digits length of a number
+ * @param {*number} num Input number
+ */
+ function digitLength (num) {
+ // Get digit length of e
+ var eSplit = num.toString().split(/[eE]/);
+ var len = (eSplit[0].split(".")[1] || "").length - (+(eSplit[1] || 0));
+ return len > 0 ? len : 0;
+ }
+ /**
+ * 把小数转成整数,支持科学计数法。如果是小数则放大成整数
+ * @param {*number} num 输入数
+ */
+ function float2Fixed (num) {
+ if (num.toString().indexOf("e") === -1) {
+ return Number(num.toString().replace(".", ""));
+ }
+ var dLen = digitLength(num);
+ return dLen > 0 ? num * Math.pow(10, dLen) : num;
+ }
+
+ /**
+ * 精确乘法
+ */
+ function times (num1, num2) {
+ var others = [];
+ for (var _i = 2; _i < arguments.length; _i++) {
+ others[_i - 2] = arguments[_i];
+ }
+ if (others.length > 0) {
+ return times.apply(void 0, [times(num1, num2), others[0]].concat(others.slice(1)));
+ }
+ var num1Changed = float2Fixed(num1);
+ var num2Changed = float2Fixed(num2);
+ var baseNum = digitLength(num1) + digitLength(num2);
+ var leftValue = num1Changed * num2Changed;
+ return leftValue / Math.pow(10, baseNum);
+ }
+
+ /**
+ * 精确除法
+ */
+ function accDivide (num1, num2) {
+ var others = [];
+ for (var _i = 2; _i < arguments.length; _i++) {
+ others[_i - 2] = arguments[_i];
+ }
+ if (others.length > 0) {
+ return accDivide.apply(void 0, [accDivide(num1, num2), others[0]].concat(others.slice(1)));
+ }
+ var num1Changed = float2Fixed(num1);
+ var num2Changed = float2Fixed(num2);
+ return times((num1Changed / num2Changed), Math.pow(10, digitLength(num2) - digitLength(num1)));
+ }
+ }
+
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/core/func/string.js b/src/main/resources/com/fr/fineui/core/func/string.js
new file mode 100644
index 0000000..e4b613f
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/func/string.js
@@ -0,0 +1,123 @@
+/**
+ * 对字符串对象的扩展
+ * @class String
+ */
+_.extend(BI, {
+
+ /**
+ * 判断字符串是否已指定的字符串开始
+ * @param str source字符串
+ * @param {String} startTag 指定的开始字符串
+ * @return {Boolean} 如果字符串以指定字符串开始则返回true,否则返回false
+ */
+ startWith: function (str, startTag) {
+ str = str || "";
+ if (startTag == null || startTag == "" || str.length === 0 || startTag.length > str.length) {
+ return false;
+ }
+ return str.substr(0, startTag.length) == startTag;
+ },
+ /**
+ * 判断字符串是否以指定的字符串结束
+ * @param str source字符串
+ * @param {String} endTag 指定的字符串
+ * @return {Boolean} 如果字符串以指定字符串结束则返回true,否则返回false
+ */
+ endWith: function (str, endTag) {
+ if (endTag == null || endTag == "" || str.length === 0 || endTag.length > str.length) {
+ return false;
+ }
+ return str.substring(str.length - endTag.length) == endTag;
+ },
+
+ /**
+ * 获取url中指定名字的参数
+ * @param str source字符串
+ * @param {String} name 参数的名字
+ * @return {String} 参数的值
+ */
+ getQuery: function (str, name) {
+ var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
+ var r = str.substr(str.indexOf("?") + 1).match(reg);
+ if (r) {
+ return unescape(r[2]);
+ }
+ return null;
+ },
+
+ /**
+ * 给url加上给定的参数
+ * @param str source字符串
+ * @param {Object} paras 参数对象,是一个键值对对象
+ * @return {String} 添加了给定参数的url
+ */
+ appendQuery: function (str, paras) {
+ if (!paras) {
+ return str;
+ }
+ var src = str;
+ // 没有问号说明还没有参数
+ if (src.indexOf("?") === -1) {
+ src += "?";
+ }
+ // 如果以问号结尾,说明没有其他参数
+ if (BI.endWith(src, "?") !== false) {
+ } else {
+ src += "&";
+ }
+ _.each(paras, function (value, name) {
+ if (typeof(name) === "string") {
+ src += name + "=" + value + "&";
+ }
+ });
+ src = src.substr(0, src.length - 1);
+ return src;
+ },
+ /**
+ * 将所有符合第一个字符串所表示的字符串替换成为第二个字符串
+ * @param str source字符串
+ * @param {String} s1 要替换的字符串的正则表达式
+ * @param {String} s2 替换的结果字符串
+ * @returns {String} 替换后的字符串
+ */
+ replaceAll: function (str, s1, s2) {
+ return BI.isString(str) ? str.replace(new RegExp(s1, "gm"), s2) : str;
+ },
+ /**
+ * 总是让字符串以指定的字符开头
+ * @param str source字符串
+ * @param {String} start 指定的字符
+ * @returns {String} 以指定字符开头的字符串
+ */
+ perfectStart: function (str, start) {
+ if (BI.startWith(str, start)) {
+ return str;
+ }
+ return start + str;
+
+ },
+
+ /**
+ * 获取字符串中某字符串的所有项位置数组
+ * @param str source字符串
+ * @param {String} sub 子字符串
+ * @return {Number[]} 子字符串在父字符串中出现的所有位置组成的数组
+ */
+ allIndexOf: function (str, sub) {
+ if (typeof sub !== "string") {
+ return [];
+ }
+ var location = [];
+ var offset = 0;
+ while (str.length > 0) {
+ var loc = str.indexOf(sub);
+ if (loc === -1) {
+ break;
+ }
+ location.push(offset + loc);
+ str = str.substring(loc + sub.length, str.length);
+ offset += loc + sub.length;
+ }
+ return location;
+ }
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/core/h.js b/src/main/resources/com/fr/fineui/core/h.js
new file mode 100644
index 0000000..c3c117f
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/h.js
@@ -0,0 +1,45 @@
+BI.Fragment = function () {
+};
+
+BI.h = function (type, props, children) {
+ if (children != null) {
+ if (!BI.isArray(children)) {
+ children = [children];
+ }
+ } else {
+ children = [];
+ }
+ if (arguments.length > 3) {
+ for (var i = 3; i < arguments.length; i++) {
+ if (BI.isArray(arguments[i])) {
+ children = children.concat(arguments[i]);
+ } else {
+ children.push(arguments[i]);
+ }
+ }
+ }
+ if (type === BI.Fragment) {
+ return children;
+ }
+ if (BI.isFunction(type)) {
+ type = type.xtype || type;
+ }
+ if (type === "el") {
+ return BI.extend({
+ el: children[0]
+ }, props);
+ }
+ if (type === "left") {
+ return BI.extend({
+ left: children
+ }, props);
+ }
+ if (type === "right") {
+ return BI.extend({
+ right: children
+ }, props);
+ }
+ return BI.extend({
+ type: type
+ }, children.length > 0 ? {items: children} : {}, props);
+};
diff --git a/src/main/resources/com/fr/fineui/core/listener/listener.show.js b/src/main/resources/com/fr/fineui/core/listener/listener.show.js
new file mode 100644
index 0000000..af22365
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/listener/listener.show.js
@@ -0,0 +1,48 @@
+/**
+ * guy
+ * 检测某个Widget的EventChange事件然后去show某个card
+ * @type {*|void|Object}
+ * @class BI.ShowListener
+ * @extends BI.OB
+ */
+BI.ShowListener = BI.inherit(BI.OB, {
+ props: function () {
+ return {
+ eventObj: BI.createWidget(),
+ cardLayout: null,
+ cardNameCreator: function (v) {
+ return v;
+ },
+ cardCreator: BI.emptyFn,
+ afterCardCreated: BI.emptyFn,
+ afterCardShow: BI.emptyFn
+ };
+ },
+
+ init: function () {
+ var self = this, o = this.options;
+ if (o.eventObj) {
+ o.eventObj.on(BI.Controller.EVENT_CHANGE, function (type, v, ob) {
+ if (type === BI.Events.CLICK) {
+ v = v || o.eventObj.getValue();
+ v = BI.isArray(v) ? (v.length > 1 ? v.toString() : v[0]) : v;
+ if (BI.isNull(v)) {
+ throw new Error("不能为null");
+ }
+ var cardName = o.cardNameCreator(v);
+ if (!o.cardLayout.isCardExisted(cardName)) {
+ var card = o.cardCreator(cardName);
+ o.cardLayout.addCardByName(cardName, card);
+ o.afterCardCreated(cardName);
+ }
+ o.cardLayout.showCardByName(cardName);
+ BI.nextTick(function () {
+ o.afterCardShow(cardName);
+ self.fireEvent(BI.ShowListener.EVENT_CHANGE, cardName);
+ });
+ }
+ });
+ }
+ }
+});
+BI.ShowListener.EVENT_CHANGE = "EVENT_CHANGE";
diff --git a/src/main/resources/com/fr/fineui/core/loader/loader.style.js b/src/main/resources/com/fr/fineui/core/loader/loader.style.js
new file mode 100644
index 0000000..ad5500e
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/loader/loader.style.js
@@ -0,0 +1,50 @@
+/**
+ * style加载管理器
+ *
+ * Created by GUY on 2015/9/7.
+ * @class
+ */
+BI.StyleLoaderManager = BI.inherit(BI.OB, {
+ _defaultConfig: function () {
+ return BI.extend(BI.StyleLoaderManager.superclass._defaultConfig.apply(this, arguments), {});
+ },
+
+ _init: function () {
+ BI.StyleLoaderManager.superclass._init.apply(this, arguments);
+ this.stylesManager = {};
+ },
+
+ loadStyle: function (name, styleString) {
+ if(!_global.document) {
+ return;
+ }
+ var d = document, styles = d.createElement("style");
+ d.getElementsByTagName("head")[0].appendChild(styles);
+ styles.setAttribute("type", "text/css");
+ if (styles.styleSheet) {
+ styles.styleSheet.cssText = styleString;
+ } else {
+ styles.appendChild(document.createTextNode(styleString));
+ }
+ this.stylesManager[name] = styles;
+
+ return this;
+ },
+
+ get: function (name) {
+ return this.stylesManager[name];
+ },
+
+ has: function (name) {
+ return this.stylesManager[name] != null;
+ },
+
+ removeStyle: function (name) {
+ if (!this.has(name)) {
+ return this;
+ }
+ this.stylesManager[name].parentNode.removeChild(this.stylesManager[name]);
+ delete this.stylesManager[name];
+ return this;
+ }
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/core/logic/logic.js b/src/main/resources/com/fr/fineui/core/logic/logic.js
new file mode 100644
index 0000000..e5de9d2
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/logic/logic.js
@@ -0,0 +1,81 @@
+/**
+ * @class BI.Logic
+ * @extends BI.OB
+ */
+BI.Logic = BI.inherit(BI.OB, {
+ createLogic: function () {
+ return this.options || {};
+ }
+});
+
+BI.LogicFactory = {
+ Type: {
+ Vertical: "vertical",
+ Horizontal: "horizontal",
+ Table: "table",
+ HorizontalFill: "horizontal_fill"
+ },
+ createLogic: function (key, options) {
+ var logic;
+ switch (key) {
+ case BI.LogicFactory.Type.Vertical:
+ logic = BI.VerticalLayoutLogic;
+ break;
+ case BI.LogicFactory.Type.Horizontal:
+ logic = BI.HorizontalLayoutLogic;
+ break;
+ case BI.LogicFactory.Type.Table:
+ logic = BI.TableLayoutLogic;
+ break;
+ case BI.LogicFactory.Type.HorizontalFill:
+ logic = BI.HorizontalFillLayoutLogic;
+ break;
+ default :
+ logic = BI.Logic;
+ break;
+ }
+ return new logic(options).createLogic();
+ },
+
+ createLogicTypeByDirection: function (direction) {
+ switch (direction) {
+ case BI.Direction.Top:
+ case BI.Direction.Bottom:
+ case BI.Direction.Custom:
+ return BI.LogicFactory.Type.Vertical;
+ break;
+ case BI.Direction.Left:
+ case BI.Direction.Right:
+ return BI.LogicFactory.Type.Horizontal;
+ }
+ },
+
+ createLogicItemsByDirection: function (direction) {
+ var layout;
+ var items = Array.prototype.slice.call(arguments, 1);
+ items = BI.map(items, function (i, item) {
+ if (BI.isWidget(item)) {
+ return {
+ el: item,
+ width: item.options.width,
+ height: item.options.height
+ };
+ }
+ return item;
+ });
+ switch (direction) {
+ case BI.Direction.Bottom:
+ layout = BI.LogicFactory.Type.Vertical;
+ items.reverse();
+ break;
+ case BI.Direction.Right:
+ layout = BI.LogicFactory.Type.Horizontal;
+ items.reverse();
+ break;
+ case BI.Direction.Custom:
+ items = items.slice(1);
+ break;
+ }
+ return items;
+ }
+};
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/core/logic/logic.layout.js b/src/main/resources/com/fr/fineui/core/logic/logic.layout.js
new file mode 100644
index 0000000..d86d914
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/logic/logic.layout.js
@@ -0,0 +1,196 @@
+/**
+ * guy
+ * 上下布局逻辑
+ * 上下布局的时候要考虑到是动态布局还是静态布局
+ *
+ * @class BI.VerticalLayoutLogic
+ * @extends BI.Logic
+ */
+BI.VerticalLayoutLogic = BI.inherit(BI.Logic, {
+ props: function () {
+ return {
+ dynamic: false,
+ scrollable: null,
+ scrolly: false,
+ scrollx: false,
+ items: [],
+ hgap: 0,
+ vgap: 0,
+ lgap: 0,
+ rgap: 0,
+ tgap: 0,
+ bgap: 0
+ };
+ },
+
+ createLogic: function () {
+ var layout, o = this.options;
+ if (o.dynamic) {
+ layout = "bi.vertical";
+ } else {
+ layout = "bi.vtape";
+ }
+ return {
+ type: layout,
+ scrollable: o.scrollable,
+ scrolly: o.scrolly,
+ scrollx: o.scrollx,
+ hgap: o.hgap,
+ vgap: o.vgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap,
+ items: o.items
+ };
+ }
+});
+
+
+/**
+ * guy
+ * 左右布局逻辑
+ * 左右布局的时候要考虑到是动态布局还是静态布局
+ *
+ * @class BI.HorizontalLayoutLogic
+ * @extends BI.Logic
+ */
+BI.HorizontalLayoutLogic = BI.inherit(BI.Logic, {
+ props: function () {
+ return {
+ dynamic: false,
+ scrollable: null,
+ scrolly: false,
+ scrollx: false,
+ items: [],
+ hgap: 0,
+ vgap: 0,
+ lgap: 0,
+ rgap: 0,
+ tgap: 0,
+ bgap: 0
+ };
+ },
+
+ createLogic: function () {
+ var layout, o = this.options;
+ if (o.dynamic) {
+ layout = "bi.vertical_adapt";
+ } else {
+ layout = "bi.htape";
+ }
+ return {
+ type: layout,
+ scrollable: o.scrollable,
+ scrolly: o.scrolly,
+ scrollx: o.scrollx,
+ hgap: o.hgap,
+ vgap: o.vgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap,
+ items: o.items
+ };
+ }
+});
+
+/**
+ * guy
+ * 表格布局逻辑
+ * 表格布局的时候要考虑到是动态布局还是静态布局
+ *
+ * @class BI.TableLayoutLogic
+ * @extends BI.OB
+ */
+BI.TableLayoutLogic = BI.inherit(BI.Logic, {
+ props: function () {
+ return {
+ dynamic: false,
+ scrollable: null,
+ scrolly: false,
+ scrollx: false,
+ columns: 0,
+ rows: 0,
+ columnSize: [],
+ rowSize: [],
+ hgap: 0,
+ vgap: 0,
+ items: []
+ };
+ },
+
+ createLogic: function () {
+ var layout, o = this.options;
+ if (o.dynamic) {
+ layout = "bi.table";
+ } else {
+ layout = "bi.window";
+ }
+ return {
+ type: layout,
+ scrollable: o.scrollable,
+ scrolly: o.scrolly,
+ scrollx: o.scrollx,
+ columns: o.columns,
+ rows: o.rows,
+ columnSize: o.columnSize,
+ rowSize: o.rowSize,
+ hgap: o.hgap,
+ vgap: o.vgap,
+ items: o.items
+ };
+ }
+});
+
+/**
+ * guy
+ * 左右充满布局逻辑
+ *
+ * @class BI.HorizontalFillLayoutLogic
+ * @extends BI.Logic
+ */
+BI.HorizontalFillLayoutLogic = BI.inherit(BI.Logic, {
+ props: function () {
+ return {
+ dynamic: false,
+ scrollable: null,
+ scrolly: false,
+ scrollx: false,
+ items: [],
+ hgap: 0,
+ vgap: 0,
+ lgap: 0,
+ rgap: 0,
+ tgap: 0,
+ bgap: 0
+ };
+ },
+
+ createLogic: function () {
+ var layout, o = this.options;
+ var columnSize = [];
+ BI.each(o.items, function (i, item) {
+ columnSize.push(item.width || 0);
+ });
+ if (o.dynamic) {
+ layout = "bi.horizontal_fill";
+ } else {
+ layout = "bi.htape";
+ }
+ return {
+ type: layout,
+ columnSize: columnSize,
+ scrollable: o.scrollable,
+ scrolly: o.scrolly,
+ scrollx: o.scrollx,
+ hgap: o.hgap,
+ vgap: o.vgap,
+ lgap: o.lgap,
+ rgap: o.rgap,
+ tgap: o.tgap,
+ bgap: o.bgap,
+ items: o.items
+ };
+ }
+});
diff --git a/src/main/resources/com/fr/fineui/core/platform/web/config.js b/src/main/resources/com/fr/fineui/core/platform/web/config.js
new file mode 100644
index 0000000..ccfd4fe
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/platform/web/config.js
@@ -0,0 +1,191 @@
+// 工程配置
+BI.prepares.push(function () {
+ // 注册布局
+ // adapt类布局优先级规则
+ // 1、在非IE且支持flex的浏览器下使用flex布局
+ // 2、IE或者不支持flex的浏览器下使用inline布局
+ // 3、在2的情况下如果布局的items大于1的话使用display:table的布局
+ // 4、在3的情况下如果IE版本低于8使用table标签布局
+ var _isSupportFlex;
+ var isSupportFlex = function () {
+ if (_isSupportFlex == null) {
+ _isSupportFlex = !!(BI.isSupportCss3 && BI.isSupportCss3("flex"));
+ }
+ return _isSupportFlex;
+ };
+ BI.Plugin.configWidget("bi.horizontal", function (ob) {
+ var supportFlex = isSupportFlex();
+ // // 在横向自适应场景下我们需要使用table的自适应撑出滚动条的特性(flex处理不了这种情况)
+ // // 主要出现在center_adapt或者horizontal_adapt的场景,或者主动设置horizontalAlign的场景
+ // if (ob.horizontalAlign === BI.HorizontalAlign.Center || ob.horizontalAlign === BI.HorizontalAlign.Stretch) {
+ // return BI.extend({}, ob, {type: "bi.table_adapt"});
+ // }
+ if (supportFlex) {
+ return BI.extend({}, ob, {type: "bi.flex_horizontal"});
+ }
+ return BI.extend({
+ scrollx: true
+ }, ob, {type: "bi.inline"});
+ });
+ BI.Plugin.configWidget("bi.inline", function (ob) {
+ // 当列宽既需要自动列宽又需要自适应列宽时,inline布局也处理不了了,降级table处理吧
+ var hasAutoAndFillColumnSize = false;
+ if (ob.columnSize && ob.columnSize.length > 0) {
+ if (ob.columnSize.indexOf("") >= 0 && ob.columnSize.indexOf("fill") >= 0) {
+ hasAutoAndFillColumnSize = true;
+ }
+ } else {
+ var hasAuto = false, hasFill = false;
+ BI.each(ob.items, function (i, item) {
+ if (item.width === "fill") {
+ hasFill = true;
+ } else if (BI.isNull(item.width) || item.width === "") {
+ hasAuto = true;
+ }
+ });
+ hasAutoAndFillColumnSize = hasAuto && hasFill;
+ }
+
+ if (hasAutoAndFillColumnSize) {
+ // 宽度是不是受限
+ if ((ob.scrollable !== true && ob.scrollx !== true) || ob.horizontalAlign === BI.HorizontalAlign.Stretch) {
+ return BI.extend({
+ verticalAlign: BI.VerticalAlign.Top
+ }, ob, {type: "bi.horizontal_float_fill"});
+ }
+ return BI.extend({
+ horizontalAlign: BI.HorizontalAlign.Stretch
+ }, ob, {type: "bi.table_adapt"});
+ }
+ return ob;
+ });
+ BI.Plugin.configWidget("bi.center_adapt", function (ob) {
+ var supportFlex = isSupportFlex();
+ // var isAdapt = !ob.horizontalAlign || ob.horizontalAlign === BI.HorizontalAlign.Center || ob.horizontalAlign === BI.HorizontalAlign.Stretch;
+ // if (!isAdapt || justOneItem) {
+ if (supportFlex) {
+ return BI.extend({}, ob, {type: "bi.flex_center_adapt"});
+ }
+ return BI.extend({}, ob, {type: "bi.inline_center_adapt"});
+ // }
+ // return ob;
+ });
+ BI.Plugin.configWidget("bi.vertical_adapt", function (ob) {
+ var supportFlex = isSupportFlex();
+ // var isAdapt = ob.horizontalAlign === BI.HorizontalAlign.Center || ob.horizontalAlign === BI.HorizontalAlign.Stretch;
+ // if (!isAdapt || justOneItem) {
+ if (supportFlex) {
+ return BI.extend({}, ob, {type: "bi.flex_vertical_center_adapt"});
+ }
+ return BI.extend({}, ob, {type: "bi.inline_vertical_adapt"});
+ // }
+ // return ob;
+ });
+ BI.Plugin.configWidget("bi.horizontal_adapt", function (ob) {
+ var justOneItem = (ob.items && ob.items.length <= 1);
+ var isAdapt = !ob.horizontalAlign || ob.horizontalAlign === BI.HorizontalAlign.Center || ob.horizontalAlign === BI.HorizontalAlign.Stretch;
+ var verticalAlignTop = !ob.verticalAlign || ob.verticalAlign === BI.VerticalAlign.TOP;
+ if (verticalAlignTop && justOneItem) {
+ return BI.extend({}, ob, {type: "bi.horizontal_auto"});
+ }
+ var supportFlex = isSupportFlex();
+ // 在横向自适应场景下我们需要使用table的自适应撑出滚动条的特性(flex处理不了这种情况)
+ // 主要出现在center_adapt或者horizontal_adapt的场景,或者主动设置horizontalAlign的场景
+ if (isAdapt) {
+ return BI.extend({
+ horizontalAlign: BI.HorizontalAlign.Center
+ }, ob, {type: "bi.table_adapt"});
+ }
+ if (supportFlex) {
+ return BI.extend({
+ horizontalAlign: BI.HorizontalAlign.Center,
+ scrollx: false
+ }, ob, {type: "bi.flex_horizontal"});
+ }
+ return BI.extend({
+ horizontalAlign: BI.HorizontalAlign.Center
+ }, ob, {type: "bi.table_adapt"});
+ });
+ BI.Plugin.configWidget("bi.horizontal_float", function (ob) {
+ if (isSupportFlex()) {
+ return BI.extend({}, ob, {type: "bi.flex_horizontal_adapt"});
+ }
+ if (ob.items && ob.items.length <= 1) {
+ return BI.extend({}, ob, {type: "bi.inline_horizontal_adapt"});
+ }
+ return ob;
+ });
+
+ BI.Plugin.configWidget("bi.horizontal_fill", function (ob) {
+ if (isSupportFlex()) {
+ return BI.extend({
+ horizontalAlign: BI.HorizontalAlign.Stretch,
+ verticalAlign: BI.VerticalAlign.Stretch,
+ scrollx: false
+ }, ob, {type: "bi.flex_horizontal"});
+ }
+ if ((ob.horizontalAlign && ob.horizontalAlign !== BI.HorizontalAlign.Stretch) || (ob.scrollable === true || ob.scrollx === true)) {
+ // 宽度不受限,要用table布局
+ return BI.extend({
+ horizontalAlign: BI.HorizontalAlign.Stretch,
+ verticalAlign: BI.VerticalAlign.Stretch
+ }, ob, {type: "bi.table_adapt"});
+ }
+ return BI.extend({}, ob, {type: "bi.horizontal_float_fill"});
+ });
+ BI.Plugin.configWidget("bi.vertical_fill", function (ob) {
+ if (isSupportFlex()) {
+ return BI.extend({
+ horizontalAlign: BI.HorizontalAlign.Stretch,
+ verticalAlign: BI.VerticalAlign.Stretch,
+ scrolly: false
+ }, ob, {type: "bi.flex_vertical"});
+ }
+ return BI.extend({}, ob, {type: "bi.vtape"});
+ });
+
+ BI.Plugin.configWidget("bi.left_right_vertical_adapt", function (ob) {
+ if (isSupportFlex()) {
+ // IE下其实也是可以使用flex布局的,只要排除掉出现滚动条的情况
+ // if (!BI.isIE() || (ob.scrollable !== true && ob.scrolly !== true)) {
+ return BI.extend({}, ob, {type: "bi.flex_left_right_vertical_adapt"});
+ // }
+ }
+ return ob;
+ });
+ BI.Plugin.configWidget("bi.flex_horizontal", function (ob) {
+ if (ob.scrollable === true || ob.scrollx !== false) {
+ if (ob.hgap > 0 || ob.rgap > 0) {// flex中最后一个margin-right不生效
+ return BI.extend({}, ob, {type: "bi.flex_scrollable_horizontal"});
+ }
+ }
+ });
+ BI.Plugin.configWidget("bi.flex_vertical", function (ob) {
+ if (ob.scrollable === true || ob.scrollx === true) {
+ if (ob.hgap > 0 || ob.rgap > 0) {// flex中最后一个margin-right不生效
+ return BI.extend({}, ob, {type: "bi.flex_scrollable_vertical"});
+ }
+ }
+ });
+
+ BI.Plugin.configWidget("bi.radio", function (ob) {
+ if (BI.isIE() && BI.getIEVersion() <= 9) {
+ return BI.extend({}, ob, {type: "bi.image_radio"});
+ }
+ return ob;
+ });
+
+ BI.Plugin.configWidget("bi.checkbox", function (ob) {
+ if (BI.isIE() && BI.getIEVersion() <= 9) {
+ return BI.extend({}, ob, {type: "bi.image_checkbox"});
+ }
+ return ob;
+ });
+
+ BI.Plugin.configWidget("bi.half_icon_button", function (ob) {
+ if (BI.isIE() && BI.getIEVersion() < 9) {
+ return ob;
+ }
+ return BI.extend({}, ob, {type: "bi.half_button"});
+ });
+});
diff --git a/src/main/resources/com/fr/fineui/core/platform/web/detectElementResize.js b/src/main/resources/com/fr/fineui/core/platform/web/detectElementResize.js
new file mode 100644
index 0000000..b34d217
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/platform/web/detectElementResize.js
@@ -0,0 +1,161 @@
+/**
+ * Detect Element Resize.
+ * Forked in order to guard against unsafe 'window' and 'document' references.
+ *
+ * https://github.com/sdecima/javascript-detect-element-resize
+ * Sebastian Decima
+ *
+ * version: 0.5.3
+ **/
+!(function () {
+ var attachEvent = _global.document && _global.document.attachEvent,
+ stylesCreated = false;
+
+ if (_global.document && !attachEvent) {
+ var requestFrame = (function () {
+ var raf = _global.requestAnimationFrame || _global.mozRequestAnimationFrame || _global.webkitRequestAnimationFrame ||
+ function (fn) { return _global.setTimeout(fn, 20); };
+ return function (fn) { return raf(fn); };
+ })();
+
+ var cancelFrame = (function () {
+ var cancel = _global.cancelAnimationFrame || _global.mozCancelAnimationFrame || _global.webkitCancelAnimationFrame ||
+ _global.clearTimeout;
+ return function (id) { return cancel(id); };
+ })();
+
+ var resetTriggers = function (element) {
+ var triggers = element.__resizeTriggers__,
+ expand = triggers.firstElementChild,
+ contract = triggers.lastElementChild,
+ expandChild = expand.firstElementChild;
+ contract.scrollLeft = contract.scrollWidth;
+ contract.scrollTop = contract.scrollHeight;
+ expandChild.style.width = expand.offsetWidth + 1 + "px";
+ expandChild.style.height = expand.offsetHeight + 1 + "px";
+ expand.scrollLeft = expand.scrollWidth;
+ expand.scrollTop = expand.scrollHeight;
+ };
+
+ var checkTriggers = function (element) {
+ return element.offsetWidth !== element.__resizeLast__.width ||
+ element.offsetHeight !== element.__resizeLast__.height;
+ };
+
+ var scrollListener = function (e) {
+ var element = this;
+ resetTriggers(this);
+ if (this.__resizeRAF__) cancelFrame(this.__resizeRAF__);
+ this.__resizeRAF__ = requestFrame(function () {
+ if (checkTriggers(element)) {
+ element.__resizeLast__.width = element.offsetWidth;
+ element.__resizeLast__.height = element.offsetHeight;
+ element.__resizeListeners__.forEach(function (fn) {
+ fn.call(element, e);
+ });
+ }
+ });
+ };
+
+ /* Detect CSS Animations support to detect element display/re-attach */
+ var animation = false,
+ animationstring = "animation",
+ keyframeprefix = "",
+ animationstartevent = "animationstart",
+ domPrefixes = "Webkit Moz O ms".split(" "),
+ startEvents = "webkitAnimationStart animationstart oAnimationStart MSAnimationStart".split(" "),
+ pfx = "";
+ {
+ var elm = document.createElement("fakeelement");
+ if (elm.style.animationName !== undefined) {
+ animation = true;
+ }
+
+ if (animation === false) {
+ for (var i = 0; i < domPrefixes.length; i++) {
+ if (elm.style[domPrefixes[i] + "AnimationName"] !== undefined) {
+ pfx = domPrefixes[i];
+ animationstring = pfx + "Animation";
+ keyframeprefix = "-" + pfx.toLowerCase() + "-";
+ animationstartevent = startEvents[i];
+ animation = true;
+ break;
+ }
+ }
+ }
+ }
+
+ var animationName = "resizeanim";
+ var animationKeyframes = "@" + keyframeprefix + "keyframes " + animationName + " { from { opacity: 0; } to { opacity: 0; } } ";
+ var animationStyle = keyframeprefix + "animation: 1ms " + animationName + "; ";
+ }
+
+ var createStyles = function () {
+ if (!stylesCreated) {
+ // opacity:0 works around a chrome bug https://code.google.com/p/chromium/issues/detail?id=286360
+ var css = (animationKeyframes ? animationKeyframes : "") +
+ ".resize-triggers { " + (animationStyle ? animationStyle : "") + "visibility: hidden; opacity: 0; } " +
+ ".resize-triggers, .resize-triggers > div, .contract-trigger:before { content: \" \"; display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; } .resize-triggers > div { background: #eee; overflow: auto; } .contract-trigger:before { width: 200%; height: 200%; }",
+ head = document.head || document.getElementsByTagName("head")[0],
+ style = document.createElement("style");
+
+ style.type = "text/css";
+ if (style.styleSheet) {
+ style.styleSheet.cssText = css;
+ } else {
+ style.appendChild(document.createTextNode(css));
+ }
+
+ head.appendChild(style);
+ stylesCreated = true;
+ }
+ };
+
+ var addResizeListener = function (element, fn) {
+ if (attachEvent) {
+ element.attachEvent("onresize", fn);
+ BI.nextTick(fn);
+ } else {
+ if (!element.__resizeTriggers__) {
+ if (getComputedStyle(element).position === "static") element.style.position = "relative";
+ createStyles();
+ element.__resizeLast__ = {};
+ element.__resizeListeners__ = [];
+ (element.__resizeTriggers__ = document.createElement("div")).className = "resize-triggers";
+ element.__resizeTriggers__.innerHTML = "" +
+ "
";
+ element.appendChild(element.__resizeTriggers__);
+ resetTriggers(element);
+ element.addEventListener("scroll", scrollListener, true);
+
+ /* Listen for a css animation to detect element display/re-attach */
+ animationstartevent && element.__resizeTriggers__.addEventListener(animationstartevent, function (e) {
+ if (e.animationName === animationName) {resetTriggers(element);}
+ });
+ }
+ element.__resizeListeners__.push(fn);
+ }
+ };
+ var removeResizeListener = function (element, fn) {
+ if (attachEvent) element.detachEvent("onresize", fn);
+ else {
+ element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1);
+ if (!element.__resizeListeners__.length) {
+ element.removeEventListener("scroll", scrollListener);
+ element.__resizeTriggers__ = !element.removeChild(element.__resizeTriggers__);
+ }
+ }
+ };
+
+ BI.ResizeDetector = {
+ addResizeListener: function (widget, fn) {
+ addResizeListener(widget.element[0], fn);
+ return function () {
+ removeResizeListener(widget.element[0], fn);
+ };
+ },
+ removeResizeListener: function (widget, fn) {
+ removeResizeListener(widget.element[0], fn);
+ }
+ };
+})();
diff --git a/src/main/resources/com/fr/fineui/core/platform/web/dom.js b/src/main/resources/com/fr/fineui/core/platform/web/dom.js
new file mode 100644
index 0000000..63583c0
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/platform/web/dom.js
@@ -0,0 +1,834 @@
+/**
+ * 对DOM操作的通用函数
+ * @type {{}}
+ */
+!(function () {
+ BI.DOM = BI.DOM || {};
+
+ BI.extend(BI.DOM, {
+ ready: function (fn) {
+ BI.Widget._renderEngine.createElement(document).ready(fn);
+ }
+ });
+
+ BI.extend(BI.DOM, {
+
+ patchProps: function (fromElement, toElement) {
+ var elemData = BI.jQuery._data(fromElement[0]);
+ var events = elemData.events;
+ BI.each(events, function (eventKey, event) {
+ BI.each(event, function (i, handler) {
+ toElement.on(eventKey + (handler.namespace ? ("." + handler.namespace) : ""), handler);
+ });
+ });
+ var fromChildren = fromElement.children(), toChildren = toElement.children();
+ if (fromChildren.length !== toChildren.length) {
+ throw new Error("不匹配");
+ }
+ BI.each(fromChildren, function (i, child) {
+ BI.DOM.patchProps(BI.jQuery(child), BI.jQuery(toChildren[i]));
+ });
+ BI.each(fromElement.data("__widgets"), function (i, widget) {
+ widget.element = toElement;
+ });
+ },
+ /**
+ * 把dom数组或元素悬挂起来,使其不对html产生影响
+ * @param dom
+ */
+ hang: function (doms) {
+ if (BI.isEmpty(doms)) {
+ return;
+ }
+ var frag = BI.Widget._renderEngine.createFragment();
+ BI.each(doms, function (i, dom) {
+ dom instanceof BI.Widget && (dom = dom.element);
+ dom instanceof BI.$ && dom[0] && frag.appendChild(dom[0]);
+ });
+ return frag;
+ },
+
+ isExist: function (obj) {
+ return BI.Widget._renderEngine.createElement("body").find(obj.element).length > 0;
+ },
+
+ // 预加载图片
+ preloadImages: function (srcArray, onload) {
+ var count = 0, images = [];
+
+ function complete () {
+ count++;
+ if (count >= srcArray.length) {
+ onload();
+ }
+ }
+
+ BI.each(srcArray, function (i, src) {
+ images[i] = new Image();
+ images[i].src = src;
+ images[i].onload = function () {
+ complete();
+ };
+ images[i].onerror = function () {
+ complete();
+ };
+ });
+ },
+
+ getTextSizeWidth: function (text, fontSize) {
+ var span = BI.Widget._renderEngine.createElement(" ").addClass("text-width-span").appendTo("body");
+
+ if (fontSize == null) {
+ fontSize = 12;
+ }
+ fontSize = fontSize + "px";
+
+ span.css("font-size", fontSize).text(text);
+
+ var width = span.width();
+ span.remove();
+
+ return width;
+ },
+
+ getTextSizeHeight: function (text, fontSize) {
+ var span = BI.Widget._renderEngine.createElement(" ").addClass("text-width-span").appendTo("body");
+
+ if (fontSize == null) {
+ fontSize = 12;
+ }
+ fontSize = fontSize + "px";
+
+ span.css("font-size", fontSize).text(text);
+
+ var height = span.height();
+ span.remove();
+
+ return height;
+ },
+
+ // 获取滚动条的宽度,页面display: none时候获取到的为0
+ getScrollWidth: function () {
+ if (BI.isNull(this._scrollWidth) || this._scrollWidth === 0) {
+ var ul = BI.Widget._renderEngine.createElement("").width(50).height(50).css({
+ position: "absolute",
+ top: "-9999px",
+ overflow: "scroll"
+ }).appendTo("body");
+ this._scrollWidth = ul[0].offsetWidth - ul[0].clientWidth;
+ ul.destroy();
+ }
+ return this._scrollWidth;
+ },
+
+ getImage: function (param, fillStyle, backgroundColor) {
+ var canvas = document.createElement("canvas");
+ var ratio = 2;
+ BI.Widget._renderEngine.createElement("body").append(canvas);
+
+ var ctx = canvas.getContext("2d");
+ ctx.font = "12px Helvetica Neue,Arial,PingFang SC,Hiragino Sans GB,Microsoft YaHei,微软雅黑,Heiti,黑体,sans-serif";
+ var w = ctx.measureText(param).width + 4;
+ canvas.width = w * ratio;
+ canvas.height = 16 * ratio;
+ ctx.font = 12 * ratio + "px Helvetica Neue,Arial,PingFang SC,Hiragino Sans GB,Microsoft YaHei,微软雅黑,Heiti,黑体,sans-serif";
+ ctx.fillStyle = fillStyle || "#3685f2";
+ ctx.textBaseline = "middle";
+ // ctx.fillStyle = "#EAF2FD";
+ ctx.fillText(param, 2 * ratio, 9 * ratio);
+ BI.Widget._renderEngine.createElement(canvas).destroy();
+ var backColor = backgroundColor || "rgba(54, 133, 242, 0.1)";
+ // IE可以放大缩小所以要固定最大最小宽高
+ return {
+ width: w,
+ height: 16,
+ src: canvas.toDataURL("image/png"),
+ style: "background-color: " + backColor + ";vertical-align: middle; margin: 0 1px; width:" + w + "px;height: 16px; max-width:" + w + "px;max-height: 16px; min-width:" + w + "px;min-height: 16px",
+ param: param
+ };
+ }
+ });
+
+ BI.extend(BI.DOM, {
+ isColor: function (color) {
+ return color && (this.isRGBColor(color) || this.isHexColor(color));
+ },
+
+ isRGBColor: function (color) {
+ if (!color) {
+ return false;
+ }
+ return color.substr(0, 3) === "rgb";
+ },
+
+ isHexColor: function (color) {
+ if (!color) {
+ return false;
+ }
+ return color[0] === "#" && color.length === 7;
+ },
+
+ isDarkColor: function (hex) {
+ if (!hex || !this.isHexColor(hex)) {
+ return false;
+ }
+ var rgb = this.rgb2json(this.hex2rgb(hex));
+ var grayLevel = Math.round(rgb.r * 0.299 + rgb.g * 0.587 + rgb.b * 0.114);
+ if (grayLevel < 192/** 网上给的是140**/) {
+ return true;
+ }
+ return false;
+ },
+
+ // 获取对比颜色
+ getContrastColor: function (color) {
+ if (!color || !this.isColor(color)) {
+ return "";
+ }
+ if (this.isDarkColor(color)) {
+ return "#FFFFFF";
+ }
+ return "#3D4D66";
+ },
+
+ rgb2hex: function (rgbColour) {
+ if (!rgbColour || rgbColour.substr(0, 3) != "rgb") {
+ return "";
+ }
+ var rgbValues = rgbColour.match(/\d+(\.\d+)?/g);
+ var red = BI.parseInt(rgbValues[0]);
+ var green = BI.parseInt(rgbValues[1]);
+ var blue = BI.parseInt(rgbValues[2]);
+
+ var hexColour = "#" + this.int2hex(red) + this.int2hex(green) + this.int2hex(blue);
+
+ return hexColour;
+ },
+
+ rgb2json: function (rgbColour) {
+ if (!rgbColour) {
+ return {};
+ }
+ if (!this.isRGBColor(rgbColour)) {
+ return {};
+ }
+ var rgbValues = rgbColour.match(/\d+(\.\d+)?/g);
+ return {
+ r: BI.parseInt(rgbValues[0]),
+ g: BI.parseInt(rgbValues[1]),
+ b: BI.parseInt(rgbValues[2])
+ };
+ },
+
+ rgba2json: function (rgbColour) {
+ if (!rgbColour) {
+ return {};
+ }
+ var rgbValues = rgbColour.match(/\d+(\.\d+)?/g);
+ return {
+ r: BI.parseInt(rgbValues[0]),
+ g: BI.parseInt(rgbValues[1]),
+ b: BI.parseInt(rgbValues[2]),
+ a: BI.parseFloat(rgbValues[3])
+ };
+ },
+
+ json2rgb: function (rgb) {
+ if (!BI.isKey(rgb.r) || !BI.isKey(rgb.g) || !BI.isKey(rgb.b)) {
+ return "";
+ }
+ return "rgb(" + rgb.r + "," + rgb.g + "," + rgb.b + ")";
+ },
+
+ json2rgba: function (rgba) {
+ if (!BI.isKey(rgba.r) || !BI.isKey(rgba.g) || !BI.isKey(rgba.b)) {
+ return "";
+ }
+ return "rgba(" + rgba.r + "," + rgba.g + "," + rgba.b + "," + rgba.a + ")";
+ },
+
+ int2hex: function (strNum) {
+ var hexdig = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"];
+
+ return hexdig[strNum >>> 4] + "" + hexdig[strNum & 15];
+ },
+
+ hex2rgb: function (color) {
+ if (!color) {
+ return "";
+ }
+ if (!this.isHexColor(color)) {
+ return color;
+ }
+ var tempValue = "rgb(", colorArray;
+
+ if (color.length === 7) {
+ colorArray = [BI.parseInt("0x" + color.substring(1, 3)),
+ BI.parseInt("0x" + color.substring(3, 5)),
+ BI.parseInt("0x" + color.substring(5, 7))];
+ } else if (color.length === 4) {
+ colorArray = [BI.parseInt("0x" + color.substring(1, 2)),
+ BI.parseInt("0x" + color.substring(2, 3)),
+ BI.parseInt("0x" + color.substring(3, 4))];
+ }
+ tempValue += colorArray[0] + ",";
+ tempValue += colorArray[1] + ",";
+ tempValue += colorArray[2] + ")";
+
+ return tempValue;
+ },
+
+ rgba2rgb: function (rgbColor, bgColor) {
+ if (BI.isNull(bgColor)) {
+ bgColor = 1;
+ }
+ if (rgbColor.substr(0, 4) != "rgba") {
+ return "";
+ }
+ var rgbValues = rgbColor.match(/\d+(\.\d+)?/g);
+ if (rgbValues.length < 4) {
+ return "";
+ }
+ var R = BI.parseFloat(rgbValues[0]);
+ var G = BI.parseFloat(rgbValues[1]);
+ var B = BI.parseFloat(rgbValues[2]);
+ var A = BI.parseFloat(rgbValues[3]);
+
+ return "rgb(" + Math.floor(255 * (bgColor * (1 - A)) + R * A) + "," +
+ Math.floor(255 * (bgColor * (1 - A)) + G * A) + "," +
+ Math.floor(255 * (bgColor * (1 - A)) + B * A) + ")";
+ }
+ });
+
+ BI.extend(BI.DOM, {
+
+ getLeftPosition: function (combo, popup, extraWidth) {
+ return {
+ left: combo.element.offset().left - popup.element.outerWidth() - (extraWidth || 0)
+ };
+ },
+
+ getInnerLeftPosition: function (combo, popup, extraWidth) {
+ return {
+ left: combo.element.offset().left + (extraWidth || 0)
+ };
+ },
+
+ getRightPosition: function (combo, popup, extraWidth) {
+ var el = combo.element;
+ return {
+ left: el.offset().left + el.outerWidth() + (extraWidth || 0)
+ };
+ },
+
+ getInnerRightPosition: function (combo, popup, extraWidth) {
+ var el = combo.element, viewBounds = popup.element.bounds();
+ return {
+ left: el.offset().left + el.outerWidth() - viewBounds.width - (extraWidth || 0)
+ };
+ },
+
+ getTopPosition: function (combo, popup, extraHeight) {
+ return {
+ top: combo.element.offset().top - popup.element.outerHeight() - (extraHeight || 0)
+ };
+ },
+
+ getBottomPosition: function (combo, popup, extraHeight) {
+ var el = combo.element;
+ return {
+ top: el.offset().top + el.outerHeight() + (extraHeight || 0)
+ };
+ },
+
+ isLeftSpaceEnough: function (combo, popup, extraWidth) {
+ return BI.DOM.getLeftPosition(combo, popup, extraWidth).left >= 0;
+ },
+
+ isInnerLeftSpaceEnough: function (combo, popup, extraWidth) {
+ var viewBounds = popup.element.bounds(),windowBounds = BI.Widget._renderEngine.createElement("body").bounds();
+ return BI.DOM.getInnerLeftPosition(combo, popup, extraWidth).left + viewBounds.width <= windowBounds.width;
+ },
+
+ isRightSpaceEnough: function (combo, popup, extraWidth) {
+ var viewBounds = popup.element.bounds(),
+ windowBounds = BI.Widget._renderEngine.createElement("body").bounds();
+ return BI.DOM.getRightPosition(combo, popup, extraWidth).left + viewBounds.width <= windowBounds.width;
+ },
+
+ isInnerRightSpaceEnough: function (combo, popup, extraWidth) {
+ return BI.DOM.getInnerRightPosition(combo, popup, extraWidth).left >= 0;
+ },
+
+ isTopSpaceEnough: function (combo, popup, extraHeight) {
+ return BI.DOM.getTopPosition(combo, popup, extraHeight).top >= 0;
+ },
+
+ isBottomSpaceEnough: function (combo, popup, extraHeight) {
+ var viewBounds = popup.element.bounds(),
+ windowBounds = BI.Widget._renderEngine.createElement("body").bounds();
+ return BI.DOM.getBottomPosition(combo, popup, extraHeight).top + viewBounds.height <= windowBounds.height;
+ },
+
+ isRightSpaceLarger: function (combo) {
+ var windowBounds = BI.Widget._renderEngine.createElement("body").bounds();
+ return windowBounds.width - combo.element.offset().left - combo.element.bounds().width >= combo.element.offset().left;
+ },
+
+ isBottomSpaceLarger: function (combo) {
+ var windowBounds = BI.Widget._renderEngine.createElement("body").bounds();
+ return windowBounds.height - combo.element.offset().top - combo.element.bounds().height >= combo.element.offset().top;
+ },
+
+ _getLeftAlignPosition: function (combo, popup, extraWidth) {
+ var viewBounds = popup.element.bounds(),
+ windowBounds = BI.Widget._renderEngine.createElement("body").bounds();
+ var left = combo.element.offset().left + extraWidth;
+ if (left + viewBounds.width > windowBounds.width) {
+ left = windowBounds.width - viewBounds.width;
+ }
+ return left;
+ },
+
+ getLeftAlignPosition: function (combo, popup, extraWidth) {
+ var left = this._getLeftAlignPosition(combo, popup, extraWidth);
+ var dir = "";
+ // 如果放不下,优先使用RightAlign, 如果RightAlign也放不下, 再使用left=0
+ if (left < 0) {
+ left = this._getRightAlignPosition(combo, popup, extraWidth);
+ dir = "left";
+ }
+ if (left < 0) {
+ left = 0;
+ }
+ return {
+ left: left,
+ dir: dir || "right"
+ };
+ },
+
+ getLeftAdaptPosition: function (combo, popup, extraWidth) {
+ if (BI.DOM.isLeftSpaceEnough(combo, popup, extraWidth)) {
+ return BI.DOM.getLeftPosition(combo, popup, extraWidth);
+ }
+ return {
+ left: 0
+ };
+ },
+
+ _getRightAlignPosition: function (combo, popup, extraWidth) {
+ var comboBounds = combo.element.bounds(), viewBounds = popup.element.bounds();
+ return combo.element.offset().left + comboBounds.width - viewBounds.width - extraWidth;
+ },
+
+ getRightAlignPosition: function (combo, popup, extraWidth) {
+ var left = this._getRightAlignPosition(combo, popup, extraWidth);
+ var dir = "";
+ // 如果放不下,优先使用LeftAlign, 如果LeftAlign也放不下, 再使用left=0
+ if (left < 0) {
+ left = this._getLeftAlignPosition(combo, popup, extraWidth);
+ dir = "right";
+ }
+ if (left < 0) {
+ left = 0;
+ }
+ return {
+ left: left,
+ dir: dir || "left"
+ };
+ },
+
+ getRightAdaptPosition: function (combo, popup, extraWidth) {
+ if (BI.DOM.isRightSpaceEnough(combo, popup, extraWidth)) {
+ return BI.DOM.getRightPosition(combo, popup, extraWidth);
+ }
+ return {
+ left: BI.Widget._renderEngine.createElement("body").bounds().width - popup.element.bounds().width
+ };
+ },
+
+ getTopAlignPosition: function (combo, popup, extraHeight, needAdaptHeight) {
+ var comboOffset = combo.element.offset();
+ var comboBounds = combo.element.bounds(), popupBounds = popup.element.bounds(),
+ windowBounds = BI.Widget._renderEngine.createElement("body").bounds();
+ var top, adaptHeight, dir;
+ if (BI.DOM.isBottomSpaceEnough(combo, popup, -1 * comboBounds.height + extraHeight)) {
+ top = comboOffset.top + extraHeight;
+ } else if (needAdaptHeight) {
+ top = comboOffset.top + extraHeight;
+ adaptHeight = windowBounds.height - top;
+ } else if (BI.DOM.isTopSpaceEnough(combo, popup, -1 * comboBounds.height + extraHeight)) {
+ // 下方空间不足且不允许调整高度的情况下,优先使用上对齐
+ top = comboOffset.top + comboBounds.height - popupBounds.height - extraHeight;
+ dir = "top";
+ } else {
+ top = windowBounds.height - popupBounds.height;
+ if (top < extraHeight) {
+ adaptHeight = windowBounds.height - extraHeight;
+ }
+ }
+ if (top < extraHeight) {
+ top = extraHeight;
+ }
+ return adaptHeight ? {
+ top: top,
+ adaptHeight: adaptHeight,
+ dir: dir || "bottom"
+ } : {
+ top: top,
+ dir: dir || "bottom"
+ };
+ },
+
+ getTopAdaptPosition: function (combo, popup, extraHeight, needAdaptHeight) {
+ var popupBounds = popup.element.bounds(),
+ windowBounds = BI.Widget._renderEngine.createElement("body").bounds();
+ if (BI.DOM.isTopSpaceEnough(combo, popup, extraHeight)) {
+ return BI.DOM.getTopPosition(combo, popup, extraHeight);
+ }
+ if (needAdaptHeight) {
+ return {
+ top: 0,
+ adaptHeight: combo.element.offset().top - extraHeight
+ };
+ }
+ if (popupBounds.height + extraHeight > windowBounds.height) {
+ return {
+ top: 0,
+ adaptHeight: windowBounds.height - extraHeight
+ };
+ }
+ return {
+ top: 0
+ };
+ },
+
+ getBottomAlignPosition: function (combo, popup, extraHeight, needAdaptHeight) {
+ var comboOffset = combo.element.offset();
+ var comboBounds = combo.element.bounds(), popupBounds = popup.element.bounds(),
+ windowBounds = BI.Widget._renderEngine.createElement("body").bounds();
+ var top, adaptHeight, dir;
+ if (BI.DOM.isTopSpaceEnough(combo, popup, -1 * comboBounds.height + extraHeight)) {
+ top = comboOffset.top + comboBounds.height - popupBounds.height - extraHeight;
+ } else if (needAdaptHeight) {
+ top = 0;
+ adaptHeight = comboOffset.top + comboBounds.height - extraHeight;
+ } else if (BI.DOM.isBottomSpaceEnough(combo, popup, -1 * comboBounds.height + extraHeight)) {
+ // 上方空间不足且不允许调整高度的情况下,优先使用下对齐
+ top = comboOffset.top + extraHeight;
+ dir = "bottom";
+ } else {
+ top = 0;
+ if (popupBounds.height + extraHeight > windowBounds.height) {
+ adaptHeight = windowBounds.height - extraHeight;
+ }
+ }
+ if (top < 0) {
+ top = 0;
+ }
+ return adaptHeight ? {
+ top: top,
+ adaptHeight: adaptHeight,
+ dir: dir || "top"
+ } : {
+ top: top,
+ dir: dir || "top"
+ };
+ },
+
+ getBottomAdaptPosition: function (combo, popup, extraHeight, needAdaptHeight) {
+ var comboOffset = combo.element.offset();
+ var comboBounds = combo.element.bounds(), popupBounds = popup.element.bounds(),
+ windowBounds = BI.Widget._renderEngine.createElement("body").bounds();
+ if (BI.DOM.isBottomSpaceEnough(combo, popup, extraHeight)) {
+ return BI.DOM.getBottomPosition(combo, popup, extraHeight);
+ }
+ if (needAdaptHeight) {
+ return {
+ top: comboOffset.top + comboBounds.height + extraHeight,
+ adaptHeight: windowBounds.height - comboOffset.top - comboBounds.height - extraHeight
+ };
+ }
+ if (popupBounds.height + extraHeight > windowBounds.height) {
+ return {
+ top: extraHeight,
+ adaptHeight: windowBounds.height - extraHeight
+ };
+ }
+ return {
+ top: windowBounds.height - popupBounds.height - extraHeight
+ };
+ },
+
+ getCenterAdaptPosition: function (combo, popup) {
+ var comboOffset = combo.element.offset();
+ var comboBounds = combo.element.bounds(), popupBounds = popup.element.bounds(),
+ windowBounds = BI.Widget._renderEngine.createElement("body").bounds();
+ var left;
+ if (comboOffset.left + comboBounds.width / 2 + popupBounds.width / 2 > windowBounds.width) {
+ left = windowBounds.width - popupBounds.width;
+ } else {
+ left = comboOffset.left + comboBounds.width / 2 - popupBounds.width / 2;
+ }
+ if (left < 0) {
+ left = 0;
+ }
+ return {
+ left: left
+ };
+ },
+
+ getMiddleAdaptPosition: function (combo, popup) {
+ var comboOffset = combo.element.offset();
+ var comboBounds = combo.element.bounds(), popupBounds = popup.element.bounds(),
+ windowBounds = BI.Widget._renderEngine.createElement("body").bounds();
+ var top;
+ if (comboOffset.top + comboBounds.height / 2 + popupBounds.height / 2 > windowBounds.height) {
+ top = windowBounds.height - popupBounds.height;
+ } else {
+ top = comboOffset.top + comboBounds.height / 2 - popupBounds.height / 2;
+ }
+ if (top < 0) {
+ top = 0;
+ }
+ return {
+ top: top
+ };
+ },
+
+ getComboPositionByDirections: function (combo, popup, extraWidth, extraHeight, needAdaptHeight, directions) {
+ extraWidth || (extraWidth = 0);
+ extraHeight || (extraHeight = 0);
+ var i, direct;
+ var leftRight = [], topBottom = [], innerLeftRight = [];
+ var isNeedAdaptHeight = false, tbFirst = false, lrFirst = false;
+ var left, top, pos, firstDir = directions[0];
+ for (i = 0; i < directions.length; i++) {
+ direct = directions[i];
+ switch (direct) {
+ case "left":
+ leftRight.push(direct);
+ break;
+ case "right":
+ leftRight.push(direct);
+ break;
+ case "top":
+ topBottom.push(direct);
+ break;
+ case "bottom":
+ topBottom.push(direct);
+ break;
+ case "innerLeft":
+ innerLeftRight.push(direct);
+ break;
+ case "innerRight":
+ innerLeftRight.push(direct);
+ break;
+ }
+ }
+ for (i = 0; i < directions.length; i++) {
+ direct = directions[i];
+ switch (direct) {
+ case "left":
+ if (!isNeedAdaptHeight) {
+ var tW = tbFirst ? extraHeight : extraWidth, tH = tbFirst ? 0 : extraHeight;
+ if (BI.DOM.isLeftSpaceEnough(combo, popup, tW)) {
+ left = BI.DOM.getLeftPosition(combo, popup, tW).left;
+ if (topBottom[0] === "bottom") {
+ pos = BI.DOM.getTopAlignPosition(combo, popup, tH, needAdaptHeight);
+ } else {
+ pos = BI.DOM.getBottomAlignPosition(combo, popup, tH, needAdaptHeight);
+ }
+ pos.dir = "left," + pos.dir;
+ if (tbFirst) {
+ pos.change = "left";
+ }
+ pos.left = left;
+ return pos;
+ }
+ }
+ lrFirst = true;
+ break;
+ case "right":
+ if (!isNeedAdaptHeight) {
+ var tW = tbFirst ? extraHeight : extraWidth, tH = tbFirst ? extraWidth : extraHeight;
+ if (BI.DOM.isRightSpaceEnough(combo, popup, tW)) {
+ left = BI.DOM.getRightPosition(combo, popup, tW).left;
+ if (topBottom[0] === "bottom") {
+ pos = BI.DOM.getTopAlignPosition(combo, popup, tH, needAdaptHeight);
+ } else {
+ pos = BI.DOM.getBottomAlignPosition(combo, popup, tH, needAdaptHeight);
+ }
+ pos.dir = "right," + pos.dir;
+ if (tbFirst) {
+ pos.change = "right";
+ }
+ pos.left = left;
+ return pos;
+ }
+ }
+ lrFirst = true;
+ break;
+ case "top":
+ var tW = lrFirst ? extraHeight : extraWidth, tH = lrFirst ? extraWidth : extraHeight;
+ if (BI.DOM.isTopSpaceEnough(combo, popup, tH)) {
+ top = BI.DOM.getTopPosition(combo, popup, tH).top;
+ if (leftRight[0] === "right") {
+ pos = BI.DOM.getLeftAlignPosition(combo, popup, tW, needAdaptHeight);
+ } else {
+ pos = BI.DOM.getRightAlignPosition(combo, popup, tW);
+ }
+ pos.dir = "top," + pos.dir;
+ if (lrFirst) {
+ pos.change = "top";
+ }
+ pos.top = top;
+ return pos;
+ }
+ if (needAdaptHeight) {
+ isNeedAdaptHeight = true;
+ }
+ tbFirst = true;
+ break;
+ case "bottom":
+ var tW = lrFirst ? extraHeight : extraWidth, tH = lrFirst ? extraWidth : extraHeight;
+ if (BI.DOM.isBottomSpaceEnough(combo, popup, tH)) {
+ top = BI.DOM.getBottomPosition(combo, popup, tH).top;
+ if (leftRight[0] === "right") {
+ pos = BI.DOM.getLeftAlignPosition(combo, popup, tW, needAdaptHeight);
+ } else {
+ pos = BI.DOM.getRightAlignPosition(combo, popup, tW);
+ }
+ pos.dir = "bottom," + pos.dir;
+ if (lrFirst) {
+ pos.change = "bottom";
+ }
+ pos.top = top;
+ return pos;
+ }
+ if (needAdaptHeight) {
+ isNeedAdaptHeight = true;
+ }
+ tbFirst = true;
+ break;
+ case "innerLeft":
+ if (!isNeedAdaptHeight) {
+ var tW = tbFirst ? extraHeight : extraWidth, tH = tbFirst ? 0 : extraHeight;
+ if (BI.DOM.isInnerLeftSpaceEnough(combo, popup, tW)) {
+ left = BI.DOM.getInnerLeftPosition(combo, popup, tW).left;
+ if (topBottom[0] === "bottom") {
+ pos = BI.DOM.getTopAlignPosition(combo, popup, tH, needAdaptHeight);
+ } else {
+ pos = BI.DOM.getBottomAlignPosition(combo, popup, tH, needAdaptHeight);
+ }
+ pos.dir = "innerLeft," + pos.dir;
+ if (tbFirst) {
+ pos.change = "innerLeft";
+ }
+ pos.left = left;
+ return pos;
+ }
+ }
+ lrFirst = true;
+ break;
+ case "innerRight":
+ if (!isNeedAdaptHeight) {
+ var tW = tbFirst ? extraHeight : extraWidth, tH = tbFirst ? extraWidth : extraHeight;
+ if (BI.DOM.isInnerRightSpaceEnough(combo, popup, tW)) {
+ left = BI.DOM.getInnerRightPosition(combo, popup, tW).left;
+ if (topBottom[0] === "bottom") {
+ pos = BI.DOM.getTopAlignPosition(combo, popup, tH, needAdaptHeight);
+ } else {
+ pos = BI.DOM.getBottomAlignPosition(combo, popup, tH, needAdaptHeight);
+ }
+ pos.dir = "innerLeft," + pos.dir;
+ if (tbFirst) {
+ pos.change = "innerRight";
+ }
+ pos.left = left;
+ return pos;
+ }
+ }
+ break;
+
+ }
+ }
+
+ // 此处为四个方向放不下时挑空间最大的方向去放置, 也就是说我设置了弹出方向为"bottom,left",
+ // 最后发现实际弹出方向可能是"top,left",那么此时外界获取popup的方向应该是"top,left"
+ switch (directions[0]) {
+ case "left":
+ case "right":
+ if (BI.DOM.isRightSpaceLarger(combo)) {
+ left = BI.DOM.getRightAdaptPosition(combo, popup, extraWidth).left;
+ firstDir = "right";
+ } else {
+ left = BI.DOM.getLeftAdaptPosition(combo, popup, extraWidth).left;
+ firstDir = "left";
+ }
+ if (topBottom[0] === "bottom") {
+ pos = BI.DOM.getTopAlignPosition(combo, popup, extraHeight, needAdaptHeight);
+ pos.left = left;
+ pos.dir = firstDir + "," + pos.dir;
+ return pos;
+ }
+ pos = BI.DOM.getBottomAlignPosition(combo, popup, extraHeight, needAdaptHeight);
+ pos.left = left;
+ pos.dir = firstDir + "," + pos.dir;
+ return pos;
+ default :
+ if (BI.DOM.isBottomSpaceLarger(combo)) {
+ pos = BI.DOM.getBottomAdaptPosition(combo, popup, extraHeight, needAdaptHeight);
+ firstDir = "bottom";
+ } else {
+ pos = BI.DOM.getTopAdaptPosition(combo, popup, extraHeight, needAdaptHeight);
+ firstDir = "top";
+ }
+ if (leftRight[0] === "right") {
+ left = BI.DOM.getLeftAlignPosition(combo, popup, extraWidth, needAdaptHeight).left;
+ pos.left = left;
+ pos.dir = firstDir + "," + pos.dir;
+ return pos;
+ }
+ left = BI.DOM.getRightAlignPosition(combo, popup, extraWidth).left;
+ pos.left = left;
+ pos.dir = firstDir + "," + pos.dir;
+ return pos;
+ }
+ },
+
+
+ getComboPosition: function (combo, popup, extraWidth, extraHeight, needAdaptHeight, directions, offsetStyle) {
+ extraWidth || (extraWidth = 0);
+ extraHeight || (extraHeight = 0);
+ var bodyHeight = BI.Widget._renderEngine.createElement("body").bounds().height - extraHeight;
+ var maxHeight = Math.min(popup.attr("maxHeight") || bodyHeight, bodyHeight);
+ popup.resetHeight && popup.resetHeight(maxHeight);
+ var position = BI.DOM.getComboPositionByDirections(combo, popup, extraWidth, extraHeight, needAdaptHeight, directions || ["bottom", "top", "right", "left"]);
+ switch (offsetStyle) {
+ case "center":
+ if (position.change) {
+ var p = BI.DOM.getMiddleAdaptPosition(combo, popup);
+ position.top = p.top;
+ } else {
+ var p = BI.DOM.getCenterAdaptPosition(combo, popup);
+ position.left = p.left;
+ }
+ break;
+ case "middle":
+ if (position.change) {
+ var p = BI.DOM.getCenterAdaptPosition(combo, popup);
+ position.left = p.left;
+ } else {
+ var p = BI.DOM.getMiddleAdaptPosition(combo, popup);
+ position.top = p.top;
+ }
+ break;
+ }
+ if (needAdaptHeight === true) {
+ popup.resetHeight && popup.resetHeight(Math.min(bodyHeight - position.top, maxHeight));
+ }
+ return position;
+ }
+ });
+})();
diff --git a/src/main/resources/com/fr/fineui/core/platform/web/function.js b/src/main/resources/com/fr/fineui/core/platform/web/function.js
new file mode 100644
index 0000000..9a2f5f8
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/platform/web/function.js
@@ -0,0 +1,129 @@
+// 浏览器相关方法
+_.extend(BI, {
+ isIE: function () {
+ if(!_global.navigator) {
+ return false;
+ }
+ if (this.__isIE == null) {
+ this.__isIE = /(msie|trident)/i.test(navigator.userAgent.toLowerCase());
+ }
+ return this.__isIE;
+ },
+
+ getIEVersion: function () {
+ if(!_global.navigator) {
+ return 0;
+ }
+ if (this.__IEVersion != null) {
+ return this.__IEVersion;
+ }
+ var version = 0;
+ var agent = navigator.userAgent.toLowerCase();
+ var v1 = agent.match(/(?:msie\s([\w.]+))/);
+ var v2 = agent.match(/(?:trident.*rv:([\w.]+))/);
+ if (v1 && v2 && v1[1] && v2[1]) {
+ version = Math.max(v1[1] * 1, v2[1] * 1);
+ } else if (v1 && v1[1]) {
+ version = v1[1] * 1;
+ } else if (v2 && v2[1]) {
+ version = v2[1] * 1;
+ } else {
+ version = 0;
+ }
+ return this.__IEVersion = version;
+ },
+
+ isIE9Below: function () {
+ if (!BI.isIE()) {
+ return false;
+ }
+ return this.getIEVersion() < 9;
+ },
+
+ isEdge: function () {
+ if(!_global.navigator) {
+ return false;
+ }
+ return /edge/i.test(navigator.userAgent.toLowerCase());
+ },
+
+ isChrome: function () {
+ if(!_global.navigator) {
+ return false;
+ }
+ return /chrome/i.test(navigator.userAgent.toLowerCase());
+ },
+
+ isFireFox: function () {
+ if(!_global.navigator) {
+ return false;
+ }
+ return /firefox/i.test(navigator.userAgent.toLowerCase());
+ },
+
+ isOpera: function () {
+ if(!_global.navigator) {
+ return false;
+ }
+ return /opera/i.test(navigator.userAgent.toLowerCase());
+ },
+
+ isSafari: function () {
+ if(!_global.navigator) {
+ return false;
+ }
+ return /safari/i.test(navigator.userAgent.toLowerCase()) && !/chrome/i.test(navigator.userAgent.toLowerCase());
+ },
+
+ isKhtml: function () {
+ if(!_global.navigator) {
+ return false;
+ }
+ return /Konqueror|Safari|KHTML/i.test(navigator.userAgent);
+ },
+
+ isMac: function () {
+ if(!_global.navigator) {
+ return false;
+ }
+ return /macintosh|mac os x/i.test(navigator.userAgent);
+ },
+
+ isWindows: function () {
+ if(!_global.navigator) {
+ return false;
+ }
+ return /windows|win32/i.test(navigator.userAgent);
+ },
+
+ isSupportCss3: function (style) {
+ if(!_global.document) {
+ return false;
+ }
+ var prefix = ["webkit", "Moz", "ms", "o"],
+ i, len,
+ humpString = [],
+ htmlStyle = document.documentElement.style,
+ _toHumb = function (string) {
+ if (!BI.isString(string)) {
+ return "";
+ }
+
+ return string.replace(/-(\w)/g, function ($0, $1) {
+ return $1.toUpperCase();
+ });
+ };
+
+ for ( i = 0; i < prefix.length; i++) {
+ humpString.push(_toHumb(prefix[i] + "-" + style));
+ }
+ humpString.push(_toHumb(style));
+
+ for (i = 0, len = humpString.length; i < len; i++) {
+ if (humpString[i] in htmlStyle) {
+ return true;
+ }
+ }
+ return false;
+ }
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/core/platform/web/jquery/__test__/fn.test.js b/src/main/resources/com/fr/fineui/core/platform/web/jquery/__test__/fn.test.js
new file mode 100644
index 0000000..edcbe5b
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/platform/web/jquery/__test__/fn.test.js
@@ -0,0 +1,76 @@
+/**
+ * @author windy
+ * @version 2.0
+ * Created by windy on 2019/12/9
+ */
+
+describe("标红test", function () {
+
+ /**
+ * test_author_windy
+ */
+ it("无多音字标红", function () {
+ var a = BI.Test.createWidget({
+ type: "bi.layout",
+ });
+ a.element.__textKeywordMarked__("无多音字", "w");
+ expect(a.element.html()).to.equal("
无 多音字");
+ a.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("含有多音字标红", function () {
+ var a = BI.Test.createWidget({
+ type: "bi.layout",
+ });
+ a.element.__textKeywordMarked__("长期协议", "z");
+ expect(a.element.html()).to.equal("
长 期协议");
+ a.element.__textKeywordMarked__("长期协议", "c");
+ expect(a.element.html()).to.equal("
长 期协议");
+ a.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("多音字错位标红", function () {
+ var a = BI.Test.createWidget({
+ type: "bi.layout",
+ });
+ a.element.__textKeywordMarked__("呵呵呵", "h");
+ expect(a.element.html()).to.equal("
呵 呵 呵 ");
+ a.element.__textKeywordMarked__("呵呵呵", "hh");
+ expect(a.element.html()).to.equal("
呵呵 呵");
+ a.element.__textKeywordMarked__("呵呵呵", "hhh");
+ expect(a.element.html()).to.equal("
呵呵呵 ");
+ a.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("原文和拼音都匹配标红", function () {
+ var a = BI.Test.createWidget({
+ type: "bi.layout",
+ });
+ a.element.__textKeywordMarked__("啊a", "a");
+ expect(a.element.html()).to.equal("
啊 a ");
+ a.element.__textKeywordMarked__("a啊", "a");
+ expect(a.element.html()).to.equal("
a 啊 ");
+ a.destroy();
+ });
+
+ /**
+ * test_author_windy
+ */
+ it("中文拼音", function () {
+ var a = BI.Test.createWidget({
+ type: "bi.layout",
+ });
+ a.element.__textKeywordMarked__("日期", "日期");
+ expect(a.element.html()).to.equal("
日期 ");
+ a.destroy();
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/com/fr/fineui/core/platform/web/jquery/_jquery.js b/src/main/resources/com/fr/fineui/core/platform/web/jquery/_jquery.js
new file mode 100644
index 0000000..48adf70
--- /dev/null
+++ b/src/main/resources/com/fr/fineui/core/platform/web/jquery/_jquery.js
@@ -0,0 +1,11022 @@
+/*!
+ * jQuery JavaScript Library v1.12.4
+ * http://jquery.com/
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ *
+ * Copyright jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2016-05-20T17:17Z
+ */
+
+(function( global, factory ) {
+
+ if ( typeof module === "object" && typeof module.exports === "object" ) {
+ // For CommonJS and CommonJS-like environments where a proper `window`
+ // is present, execute the factory and get jQuery.
+ // For environments that do not have a `window` with a `document`
+ // (such as Node.js), expose a factory as module.exports.
+ // This accentuates the need for the creation of a real `window`.
+ // e.g. var jQuery = require("jquery")(window);
+ // See ticket #14549 for more info.
+ module.exports = global.document ?
+ factory( global, true ) :
+ function( w ) {
+ if ( !w.document ) {
+ throw new Error( "jQuery requires a window with a document" );
+ }
+ return factory( w );
+ };
+ } else {
+ factory( global );
+ }
+
+// Pass this if window is not defined yet
+}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
+
+// Support: Firefox 18+
+// Can't be in strict mode, several libs including ASP.NET trace
+// the stack via arguments.caller.callee and Firefox dies if
+// you try to trace through "use strict" call chains. (#13335)
+//"use strict";
+ var deletedIds = [];
+
+ var document = window.document;
+
+ var slice = deletedIds.slice;
+
+ var concat = deletedIds.concat;
+
+ var push = deletedIds.push;
+
+ var indexOf = deletedIds.indexOf;
+
+ var class2type = {};
+
+ var toString = class2type.toString;
+
+ var hasOwn = class2type.hasOwnProperty;
+
+ var support = {};
+
+
+
+ var
+ version = "1.12.4",
+
+ // Define a local copy of jQuery
+ jQuery = function( selector, context ) {
+
+ // The jQuery object is actually just the init constructor 'enhanced'
+ // Need init if jQuery is called (just allow error to be thrown if not included)
+ return new jQuery.fn.init( selector, context );
+ },
+
+ // Support: Android<4.1, IE<9
+ // Make sure we trim BOM and NBSP
+ rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
+
+ // Matches dashed string for camelizing
+ rmsPrefix = /^-ms-/,
+ rdashAlpha = /-([\da-z])/gi,
+
+ // Used by jQuery.camelCase as callback to replace()
+ fcamelCase = function( all, letter ) {
+ return letter.toUpperCase();
+ };
+
+ jQuery.fn = jQuery.prototype = {
+
+ // The current version of jQuery being used
+ jquery: version,
+
+ constructor: jQuery,
+
+ // Start with an empty selector
+ selector: "",
+
+ // The default length of a jQuery object is 0
+ length: 0,
+
+ toArray: function() {
+ return slice.call( this );
+ },
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num != null ?
+
+ // Return just the one element from the set
+ ( num < 0 ? this[ num + this.length ] : this[ num ] ) :
+
+ // Return all the elements in a clean array
+ slice.call( this );
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems ) {
+
+ // Build a new jQuery matched element set
+ var ret = jQuery.merge( this.constructor(), elems );
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+ ret.context = this.context;
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Execute a callback for every element in the matched set.
+ each: function( callback ) {
+ if(arguments.length>1){
+ throw new Error("调用兼容出错");
+ }
+ return jQuery.each( this, callback );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map( this, function( elem, i ) {
+ return callback.call( elem, i, elem );
+ } ) );
+ },
+
+ slice: function() {
+ return this.pushStack( slice.apply( this, arguments ) );
+ },
+
+ first: function() {
+ return this.eq( 0 );
+ },
+
+ last: function() {
+ return this.eq( -1 );
+ },
+
+ eq: function( i ) {
+ var len = this.length,
+ j = +i + ( i < 0 ? len : 0 );
+ return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
+ },
+
+ end: function() {
+ return this.prevObject || this.constructor();
+ },
+
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: push,
+ sort: deletedIds.sort,
+ splice: deletedIds.splice
+ };
+
+ jQuery.extend = jQuery.fn.extend = function() {
+ var src, copyIsArray, copy, name, options, clone,
+ target = arguments[ 0 ] || {},
+ i = 1,
+ length = arguments.length,
+ deep = false;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+
+ // skip the boolean and the target
+ target = arguments[ i ] || {};
+ i++;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
+ target = {};
+ }
+
+ // extend jQuery itself if only one argument is passed
+ if ( i === length ) {
+ target = this;
+ i--;
+ }
+
+ for ( ; i < length; i++ ) {
+
+ // Only deal with non-null/undefined values
+ if ( ( options = arguments[ i ] ) != null ) {
+
+ // Extend the base object
+ for ( name in options ) {
+ src = target[ name ];
+ copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( name === "__proto__" || target === copy ) {
+ continue;
+ }
+
+ // Recurse if we're merging plain objects or arrays
+ if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
+ ( copyIsArray = jQuery.isArray( copy ) ) ) ) {
+
+ if ( copyIsArray ) {
+ copyIsArray = false;
+ clone = src && jQuery.isArray( src ) ? src : [];
+
+ } else {
+ clone = src && jQuery.isPlainObject( src ) ? src : {};
+ }
+
+ // Never move original objects, clone them
+ target[ name ] = jQuery.extend( deep, clone, copy );
+
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+ };
+
+ jQuery.extend( {
+
+ // Unique for each copy of jQuery on the page
+ expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
+
+ // Assume jQuery is ready without the ready module
+ isReady: true,
+
+ error: function( msg ) {
+ throw new Error( msg );
+ },
+
+ noop: function() {},
+
+ // See test/unit/core.js for details concerning isFunction.
+ // Since version 1.3, DOM methods and functions like alert
+ // aren't supported. They return false on IE (#2968).
+ isFunction: function( obj ) {
+ return jQuery.type( obj ) === "function";
+ },
+
+ isArray: Array.isArray || function( obj ) {
+ return jQuery.type( obj ) === "array";
+ },
+
+ isWindow: function( obj ) {
+ /* jshint eqeqeq: false */
+ return obj != null && obj == obj.window;
+ },
+
+ isNumeric: function( obj ) {
+
+ // parseFloat NaNs numeric-cast false positives (null|true|false|"")
+ // ...but misinterprets leading-number strings, particularly hex literals ("0x...")
+ // subtraction forces infinities to NaN
+ // adding 1 corrects loss of precision from parseFloat (#15100)
+ var realStringObj = obj && obj.toString();
+ return !jQuery.isArray( obj ) && ( realStringObj - parseFloat( realStringObj ) + 1 ) >= 0;
+ },
+
+ isEmptyObject: function( obj ) {
+ var name;
+ for ( name in obj ) {
+ return false;
+ }
+ return true;
+ },
+
+ isPlainObject: function( obj ) {
+ var key;
+
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if ( !obj || jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ try {
+
+ // Not own constructor property must be Object
+ if ( obj.constructor &&
+ !hasOwn.call( obj, "constructor" ) &&
+ !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
+ return false;
+ }
+ } catch ( e ) {
+
+ // IE8,9 Will throw exceptions on certain host objects #9897
+ return false;
+ }
+
+ // Support: IE<9
+ // Handle iteration over inherited properties before own properties.
+ if ( !support.ownFirst ) {
+ for ( key in obj ) {
+ return hasOwn.call( obj, key );
+ }
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+ for ( key in obj ) {}
+
+ return key === undefined || hasOwn.call( obj, key );
+ },
+
+ type: function( obj ) {
+ if ( obj == null ) {
+ return obj + "";
+ }
+ return typeof obj === "object" || typeof obj === "function" ?
+ class2type[ toString.call( obj ) ] || "object" :
+ typeof obj;
+ },
+
+ // Workarounds based on findings by Jim Driscoll
+ // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
+ globalEval: function( data ) {
+ if ( data && jQuery.trim( data ) ) {
+
+ // We use execScript on Internet Explorer
+ // We use an anonymous function so that context is window
+ // rather than jQuery in Firefox
+ ( window.execScript || function( data ) {
+ window[ "eval" ].call( window, data ); // jscs:ignore requireDotNotation
+ } )( data );
+ }
+ },
+
+ // Convert dashed to camelCase; used by the css and data modules
+ // Microsoft forgot to hump their vendor prefix (#9572)
+ camelCase: function( string ) {
+ return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
+ },
+
+ each: function( obj, callback ) {
+ var length, i = 0;
+
+ if ( isArrayLike( obj ) ) {
+ length = obj.length;
+ for ( ; i < length; i++ ) {
+ if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( i in obj ) {
+ if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
+ break;
+ }
+ }
+ }
+
+ return obj;
+ },
+
+ // Support: Android<4.1, IE<9
+ trim: function( text ) {
+ return text == null ?
+ "" :
+ ( text + "" ).replace( rtrim, "" );
+ },
+
+ // results is for internal usage only
+ makeArray: function( arr, results ) {
+ var ret = results || [];
+
+ if ( arr != null ) {
+ if ( isArrayLike( Object( arr ) ) ) {
+ jQuery.merge( ret,
+ typeof arr === "string" ?
+ [ arr ] : arr
+ );
+ } else {
+ push.call( ret, arr );
+ }
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, arr, i ) {
+ var len;
+
+ if ( arr ) {
+ if ( indexOf ) {
+ return indexOf.call( arr, elem, i );
+ }
+
+ len = arr.length;
+ i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
+
+ for ( ; i < len; i++ ) {
+
+ // Skip accessing in sparse arrays
+ if ( i in arr && arr[ i ] === elem ) {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+ },
+
+ merge: function( first, second ) {
+ var len = +second.length,
+ j = 0,
+ i = first.length;
+
+ while ( j < len ) {
+ first[ i++ ] = second[ j++ ];
+ }
+
+ // Support: IE<9
+ // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists)
+ if ( len !== len ) {
+ while ( second[ j ] !== undefined ) {
+ first[ i++ ] = second[ j++ ];
+ }
+ }
+
+ first.length = i;
+
+ return first;
+ },
+
+ grep: function( elems, callback, invert ) {
+ var callbackInverse,
+ matches = [],
+ i = 0,
+ length = elems.length,
+ callbackExpect = !invert;
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( ; i < length; i++ ) {
+ callbackInverse = !callback( elems[ i ], i );
+ if ( callbackInverse !== callbackExpect ) {
+ matches.push( elems[ i ] );
+ }
+ }
+
+ return matches;
+ },
+
+ // arg is for internal usage only
+ map: function( elems, callback, arg ) {
+ var length, value,
+ i = 0,
+ ret = [];
+
+ // Go through the array, translating each of the items to their new values
+ if ( isArrayLike( elems ) ) {
+ length = elems.length;
+ for ( ; i < length; i++ ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret.push( value );
+ }
+ }
+
+ // Go through every key on the object,
+ } else {
+ for ( i in elems ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret.push( value );
+ }
+ }
+ }
+
+ // Flatten any nested arrays
+ return concat.apply( [], ret );
+ },
+
+ // A global GUID counter for objects
+ guid: 1,
+
+ // Bind a function to a context, optionally partially applying any
+ // arguments.
+ proxy: function( fn, context ) {
+ var args, proxy, tmp;
+
+ if ( typeof context === "string" ) {
+ tmp = fn[ context ];
+ context = fn;
+ fn = tmp;
+ }
+
+ // Quick check to determine if target is callable, in the spec
+ // this throws a TypeError, but we will just return undefined.
+ if ( !jQuery.isFunction( fn ) ) {
+ return undefined;
+ }
+
+ // Simulated bind
+ args = slice.call( arguments, 2 );
+ proxy = function() {
+ return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
+ };
+
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ proxy.guid = fn.guid = fn.guid || jQuery.guid++;
+
+ return proxy;
+ },
+
+ now: function() {
+ return +( new Date() );
+ },
+
+ // jQuery.support is not used in Core but other projects attach their
+ // properties to it so it needs to exist.
+ support: support
+ } );
+
+// JSHint would error on this code due to the Symbol not being defined in ES5.
+// Defining this global in .jshintrc would create a danger of using the global
+// unguarded in another place, it seems safer to just disable JSHint for these
+// three lines.
+ /* jshint ignore: start */
+ if ( typeof Symbol === "function" ) {
+ jQuery.fn[ Symbol.iterator ] = deletedIds[ Symbol.iterator ];
+ }
+ /* jshint ignore: end */
+
+// Populate the class2type map
+ jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
+ function( i, name ) {
+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
+ } );
+
+ function isArrayLike( obj ) {
+
+ // Support: iOS 8.2 (not reproducible in simulator)
+ // `in` check used to prevent JIT error (gh-2145)
+ // hasOwn isn't used here due to false negatives
+ // regarding Nodelist length in IE
+ var length = !!obj && "length" in obj && obj.length,
+ type = jQuery.type( obj );
+
+ if ( type === "function" || jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ return type === "array" || length === 0 ||
+ typeof length === "number" && length > 0 && ( length - 1 ) in obj;
+ }
+ var Sizzle =
+ /*!
+ * Sizzle CSS Selector Engine v2.2.1
+ * http://sizzlejs.com/
+ *
+ * Copyright jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2015-10-17
+ */
+ (function( window ) {
+
+ var i,
+ support,
+ Expr,
+ getText,
+ isXML,
+ tokenize,
+ compile,
+ select,
+ outermostContext,
+ sortInput,
+ hasDuplicate,
+
+ // Local document vars
+ setDocument,
+ document,
+ docElem,
+ documentIsHTML,
+ rbuggyQSA,
+ rbuggyMatches,
+ matches,
+ contains,
+
+ // Instance-specific data
+ expando = "sizzle" + 1 * new Date(),
+ preferredDoc = window.document,
+ dirruns = 0,
+ done = 0,
+ classCache = createCache(),
+ tokenCache = createCache(),
+ compilerCache = createCache(),
+ sortOrder = function( a, b ) {
+ if ( a === b ) {
+ hasDuplicate = true;
+ }
+ return 0;
+ },
+
+ // General-purpose constants
+ MAX_NEGATIVE = 1 << 31,
+
+ // Instance methods
+ hasOwn = ({}).hasOwnProperty,
+ arr = [],
+ pop = arr.pop,
+ push_native = arr.push,
+ push = arr.push,
+ slice = arr.slice,
+ // Use a stripped-down indexOf as it's faster than native
+ // http://jsperf.com/thor-indexof-vs-for/5
+ indexOf = function( list, elem ) {
+ var i = 0,
+ len = list.length;
+ for ( ; i < len; i++ ) {
+ if ( list[i] === elem ) {
+ return i;
+ }
+ }
+ return -1;
+ },
+
+ booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
+
+ // Regular expressions
+
+ // http://www.w3.org/TR/css3-selectors/#whitespace
+ whitespace = "[\\x20\\t\\r\\n\\f]",
+
+ // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
+ identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
+
+ // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
+ attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
+ // Operator (capture 2)
+ "*([*^$|!~]?=)" + whitespace +
+ // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
+ "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
+ "*\\]",
+
+ pseudos = ":(" + identifier + ")(?:\\((" +
+ // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
+ // 1. quoted (capture 3; capture 4 or capture 5)
+ "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
+ // 2. simple (capture 6)
+ "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
+ // 3. anything else (capture 2)
+ ".*" +
+ ")\\)|)",
+
+ // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
+ rwhitespace = new RegExp( whitespace + "+", "g" ),
+ rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
+
+ rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
+ rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
+
+ rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
+
+ rpseudo = new RegExp( pseudos ),
+ ridentifier = new RegExp( "^" + identifier + "$" ),
+
+ matchExpr = {
+ "ID": new RegExp( "^#(" + identifier + ")" ),
+ "CLASS": new RegExp( "^\\.(" + identifier + ")" ),
+ "TAG": new RegExp( "^(" + identifier + "|[*])" ),
+ "ATTR": new RegExp( "^" + attributes ),
+ "PSEUDO": new RegExp( "^" + pseudos ),
+ "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
+ "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
+ "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
+ "bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
+ // For use in libraries implementing .is()
+ // We use this for POS matching in `select`
+ "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
+ whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
+ },
+
+ rinputs = /^(?:input|select|textarea|button)$/i,
+ rheader = /^h\d$/i,
+
+ rnative = /^[^{]+\{\s*\[native \w/,
+
+ // Easily-parseable/retrievable ID or TAG or CLASS selectors
+ rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
+
+ rsibling = /[+~]/,
+ rescape = /'|\\/g,
+
+ // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
+ runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
+ funescape = function( _, escaped, escapedWhitespace ) {
+ var high = "0x" + escaped - 0x10000;
+ // NaN means non-codepoint
+ // Support: Firefox<24
+ // Workaround erroneous numeric interpretation of +"0x"
+ return high !== high || escapedWhitespace ?
+ escaped :
+ high < 0 ?
+ // BMP codepoint
+ String.fromCharCode( high + 0x10000 ) :
+ // Supplemental Plane codepoint (surrogate pair)
+ String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
+ },
+
+ // Used for iframes
+ // See setDocument()
+ // Removing the function wrapper causes a "Permission Denied"
+ // error in IE
+ unloadHandler = function() {
+ setDocument();
+ };
+
+// Optimize for push.apply( _, NodeList )
+ try {
+ push.apply(
+ (arr = slice.call( preferredDoc.childNodes )),
+ preferredDoc.childNodes
+ );
+ // Support: Android<4.0
+ // Detect silently failing push.apply
+ arr[ preferredDoc.childNodes.length ].nodeType;
+ } catch ( e ) {
+ push = { apply: arr.length ?
+
+ // Leverage slice if possible
+ function( target, els ) {
+ push_native.apply( target, slice.call(els) );
+ } :
+
+ // Support: IE<9
+ // Otherwise append directly
+ function( target, els ) {
+ var j = target.length,
+ i = 0;
+ // Can't trust NodeList.length
+ while ( (target[j++] = els[i++]) ) {}
+ target.length = j - 1;
+ }
+ };
+ }
+
+ function Sizzle( selector, context, results, seed ) {
+ var m, i, elem, nid, nidselect, match, groups, newSelector,
+ newContext = context && context.ownerDocument,
+
+ // nodeType defaults to 9, since context defaults to document
+ nodeType = context ? context.nodeType : 9;
+
+ results = results || [];
+
+ // Return early from calls with invalid selector or context
+ if ( typeof selector !== "string" || !selector ||
+ nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
+
+ return results;
+ }
+
+ // Try to shortcut find operations (as opposed to filters) in HTML documents
+ if ( !seed ) {
+
+ if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
+ setDocument( context );
+ }
+ context = context || document;
+
+ if ( documentIsHTML ) {
+
+ // If the selector is sufficiently simple, try using a "get*By*" DOM method
+ // (excepting DocumentFragment context, where the methods don't exist)
+ if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) {
+
+ // ID selector
+ if ( (m = match[1]) ) {
+
+ // Document context
+ if ( nodeType === 9 ) {
+ if ( (elem = context.getElementById( m )) ) {
+
+ // Support: IE, Opera, Webkit
+ // TODO: identify versions
+ // getElementById can match elements by name instead of ID
+ if ( elem.id === m ) {
+ results.push( elem );
+ return results;
+ }
+ } else {
+ return results;
+ }
+
+ // Element context
+ } else {
+
+ // Support: IE, Opera, Webkit
+ // TODO: identify versions
+ // getElementById can match elements by name instead of ID
+ if ( newContext && (elem = newContext.getElementById( m )) &&
+ contains( context, elem ) &&
+ elem.id === m ) {
+
+ results.push( elem );
+ return results;
+ }
+ }
+
+ // Type selector
+ } else if ( match[2] ) {
+ push.apply( results, context.getElementsByTagName( selector ) );
+ return results;
+
+ // Class selector
+ } else if ( (m = match[3]) && support.getElementsByClassName &&
+ context.getElementsByClassName ) {
+
+ push.apply( results, context.getElementsByClassName( m ) );
+ return results;
+ }
+ }
+
+ // Take advantage of querySelectorAll
+ if ( support.qsa &&
+ !compilerCache[ selector + " " ] &&
+ (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
+
+ if ( nodeType !== 1 ) {
+ newContext = context;
+ newSelector = selector;
+
+ // qSA looks outside Element context, which is not what we want
+ // Thanks to Andrew Dupont for this workaround technique
+ // Support: IE <=8
+ // Exclude object elements
+ } else if ( context.nodeName.toLowerCase() !== "object" ) {
+
+ // Capture the context ID, setting it first if necessary
+ if ( (nid = context.getAttribute( "id" )) ) {
+ nid = nid.replace( rescape, "\\$&" );
+ } else {
+ context.setAttribute( "id", (nid = expando) );
+ }
+
+ // Prefix every selector in the list
+ groups = tokenize( selector );
+ i = groups.length;
+ nidselect = ridentifier.test( nid ) ? "#" + nid : "[id='" + nid + "']";
+ while ( i-- ) {
+ groups[i] = nidselect + " " + toSelector( groups[i] );
+ }
+ newSelector = groups.join( "," );
+
+ // Expand context for sibling selectors
+ newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
+ context;
+ }
+
+ if ( newSelector ) {
+ try {
+ push.apply( results,
+ newContext.querySelectorAll( newSelector )
+ );
+ return results;
+ } catch ( qsaError ) {
+ } finally {
+ if ( nid === expando ) {
+ context.removeAttribute( "id" );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // All others
+ return select( selector.replace( rtrim, "$1" ), context, results, seed );
+ }
+
+ /**
+ * Create key-value caches of limited size
+ * @returns {function(string, object)} Returns the Object data after storing it on itself with
+ * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
+ * deleting the oldest entry
+ */
+ function createCache() {
+ var keys = [];
+
+ function cache( key, value ) {
+ // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
+ if ( keys.push( key + " " ) > Expr.cacheLength ) {
+ // Only keep the most recent entries
+ delete cache[ keys.shift() ];
+ }
+ return (cache[ key + " " ] = value);
+ }
+ return cache;
+ }
+
+ /**
+ * Mark a function for special use by Sizzle
+ * @param {Function} fn The function to mark
+ */
+ function markFunction( fn ) {
+ fn[ expando ] = true;
+ return fn;
+ }
+
+ /**
+ * Support testing using an element
+ * @param {Function} fn Passed the created div and expects a boolean result
+ */
+ function assert( fn ) {
+ var div = document.createElement("div");
+
+ try {
+ return !!fn( div );
+ } catch (e) {
+ return false;
+ } finally {
+ // Remove from its parent by default
+ if ( div.parentNode ) {
+ div.parentNode.removeChild( div );
+ }
+ // release memory in IE
+ div = null;
+ }
+ }
+
+ /**
+ * Adds the same handler for all of the specified attrs
+ * @param {String} attrs Pipe-separated list of attributes
+ * @param {Function} handler The method that will be applied
+ */
+ function addHandle( attrs, handler ) {
+ var arr = attrs.split("|"),
+ i = arr.length;
+
+ while ( i-- ) {
+ Expr.attrHandle[ arr[i] ] = handler;
+ }
+ }
+
+ /**
+ * Checks document order of two siblings
+ * @param {Element} a
+ * @param {Element} b
+ * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
+ */
+ function siblingCheck( a, b ) {
+ var cur = b && a,
+ diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
+ ( ~b.sourceIndex || MAX_NEGATIVE ) -
+ ( ~a.sourceIndex || MAX_NEGATIVE );
+
+ // Use IE sourceIndex if available on both nodes
+ if ( diff ) {
+ return diff;
+ }
+
+ // Check if b follows a
+ if ( cur ) {
+ while ( (cur = cur.nextSibling) ) {
+ if ( cur === b ) {
+ return -1;
+ }
+ }
+ }
+
+ return a ? 1 : -1;
+ }
+
+ /**
+ * Returns a function to use in pseudos for input types
+ * @param {String} type
+ */
+ function createInputPseudo( type ) {
+ return function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && elem.type === type;
+ };
+ }
+
+ /**
+ * Returns a function to use in pseudos for buttons
+ * @param {String} type
+ */
+ function createButtonPseudo( type ) {
+ return function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && elem.type === type;
+ };
+ }
+
+ /**
+ * Returns a function to use in pseudos for positionals
+ * @param {Function} fn
+ */
+ function createPositionalPseudo( fn ) {
+ return markFunction(function( argument ) {
+ argument = +argument;
+ return markFunction(function( seed, matches ) {
+ var j,
+ matchIndexes = fn( [], seed.length, argument ),
+ i = matchIndexes.length;
+
+ // Match elements found at the specified indexes
+ while ( i-- ) {
+ if ( seed[ (j = matchIndexes[i]) ] ) {
+ seed[j] = !(matches[j] = seed[j]);
+ }
+ }
+ });
+ });
+ }
+
+ /**
+ * Checks a node for validity as a Sizzle context
+ * @param {Element|Object=} context
+ * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
+ */
+ function testContext( context ) {
+ return context && typeof context.getElementsByTagName !== "undefined" && context;
+ }
+
+// Expose support vars for convenience
+ support = Sizzle.support = {};
+
+ /**
+ * Detects XML nodes
+ * @param {Element|Object} elem An element or a document
+ * @returns {Boolean} True iff elem is a non-HTML XML node
+ */
+ isXML = Sizzle.isXML = function( elem ) {
+ // documentElement is verified for cases where it doesn't yet exist
+ // (such as loading iframes in IE - #4833)
+ var documentElement = elem && (elem.ownerDocument || elem).documentElement;
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
+ };
+
+ /**
+ * Sets document-related variables once based on the current document
+ * @param {Element|Object} [doc] An element or document object to use to set the document
+ * @returns {Object} Returns the current document
+ */
+ setDocument = Sizzle.setDocument = function( node ) {
+ var hasCompare, parent,
+ doc = node ? node.ownerDocument || node : preferredDoc;
+
+ // Return early if doc is invalid or already selected
+ if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
+ return document;
+ }
+
+ // Update global variables
+ document = doc;
+ docElem = document.documentElement;
+ documentIsHTML = !isXML( document );
+
+ // Support: IE 9-11, Edge
+ // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936)
+ if ( (parent = document.defaultView) && parent.top !== parent ) {
+ // Support: IE 11
+ if ( parent.addEventListener ) {
+ parent.addEventListener( "unload", unloadHandler, false );
+
+ // Support: IE 9 - 10 only
+ } else if ( parent.attachEvent ) {
+ parent.attachEvent( "onunload", unloadHandler );
+ }
+ }
+
+ /* Attributes
+ ---------------------------------------------------------------------- */
+
+ // Support: IE<8
+ // Verify that getAttribute really returns attributes and not properties
+ // (excepting IE8 booleans)
+ support.attributes = assert(function( div ) {
+ div.className = "i";
+ return !div.getAttribute("className");
+ });
+
+ /* getElement(s)By*
+ ---------------------------------------------------------------------- */
+
+ // Check if getElementsByTagName("*") returns only elements
+ support.getElementsByTagName = assert(function( div ) {
+ div.appendChild( document.createComment("") );
+ return !div.getElementsByTagName("*").length;
+ });
+
+ // Support: IE<9
+ support.getElementsByClassName = rnative.test( document.getElementsByClassName );
+
+ // Support: IE<10
+ // Check if getElementById returns elements by name
+ // The broken getElementById methods don't pick up programatically-set names,
+ // so use a roundabout getElementsByName test
+ support.getById = assert(function( div ) {
+ docElem.appendChild( div ).id = expando;
+ return !document.getElementsByName || !document.getElementsByName( expando ).length;
+ });
+
+ // ID find and filter
+ if ( support.getById ) {
+ Expr.find["ID"] = function( id, context ) {
+ if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
+ var m = context.getElementById( id );
+ return m ? [ m ] : [];
+ }
+ };
+ Expr.filter["ID"] = function( id ) {
+ var attrId = id.replace( runescape, funescape );
+ return function( elem ) {
+ return elem.getAttribute("id") === attrId;
+ };
+ };
+ } else {
+ // Support: IE6/7
+ // getElementById is not reliable as a find shortcut
+ delete Expr.find["ID"];
+
+ Expr.filter["ID"] = function( id ) {
+ var attrId = id.replace( runescape, funescape );
+ return function( elem ) {
+ var node = typeof elem.getAttributeNode !== "undefined" &&
+ elem.getAttributeNode("id");
+ return node && node.value === attrId;
+ };
+ };
+ }
+
+ // Tag
+ Expr.find["TAG"] = support.getElementsByTagName ?
+ function( tag, context ) {
+ if ( typeof context.getElementsByTagName !== "undefined" ) {
+ return context.getElementsByTagName( tag );
+
+ // DocumentFragment nodes don't have gEBTN
+ } else if ( support.qsa ) {
+ return context.querySelectorAll( tag );
+ }
+ } :
+
+ function( tag, context ) {
+ var elem,
+ tmp = [],
+ i = 0,
+ // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
+ results = context.getElementsByTagName( tag );
+
+ // Filter out possible comments
+ if ( tag === "*" ) {
+ while ( (elem = results[i++]) ) {
+ if ( elem.nodeType === 1 ) {
+ tmp.push( elem );
+ }
+ }
+
+ return tmp;
+ }
+ return results;
+ };
+
+ // Class
+ Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
+ if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) {
+ return context.getElementsByClassName( className );
+ }
+ };
+
+ /* QSA/matchesSelector
+ ---------------------------------------------------------------------- */
+
+ // QSA and matchesSelector support
+
+ // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
+ rbuggyMatches = [];
+
+ // qSa(:focus) reports false when true (Chrome 21)
+ // We allow this because of a bug in IE8/9 that throws an error
+ // whenever `document.activeElement` is accessed on an iframe
+ // So, we allow :focus to pass through QSA all the time to avoid the IE error
+ // See http://bugs.jquery.com/ticket/13378
+ rbuggyQSA = [];
+
+ if ( (support.qsa = rnative.test( document.querySelectorAll )) ) {
+ // Build QSA regex
+ // Regex strategy adopted from Diego Perini
+ assert(function( div ) {
+ // Select is set to empty string on purpose
+ // This is to test IE's treatment of not explicitly
+ // setting a boolean content attribute,
+ // since its presence should be enough
+ // http://bugs.jquery.com/ticket/12359
+ docElem.appendChild( div ).innerHTML = "
" +
+ "
" +
+ " ";
+
+ // Support: IE8, Opera 11-12.16
+ // Nothing should be selected when empty strings follow ^= or $= or *=
+ // The test attribute must be unknown in Opera but "safe" for WinRT
+ // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
+ if ( div.querySelectorAll("[msallowcapture^='']").length ) {
+ rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
+ }
+
+ // Support: IE8
+ // Boolean attributes and "value" are not treated correctly
+ if ( !div.querySelectorAll("[selected]").length ) {
+ rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
+ }
+
+ // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+
+ if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
+ rbuggyQSA.push("~=");
+ }
+
+ // Webkit/Opera - :checked should return selected option elements
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ // IE8 throws error here and will not see later tests
+ if ( !div.querySelectorAll(":checked").length ) {
+ rbuggyQSA.push(":checked");
+ }
+
+ // Support: Safari 8+, iOS 8+
+ // https://bugs.webkit.org/show_bug.cgi?id=136851
+ // In-page `selector#id sibing-combinator selector` fails
+ if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) {
+ rbuggyQSA.push(".#.+[+~]");
+ }
+ });
+
+ assert(function( div ) {
+ // Support: Windows 8 Native Apps
+ // The type and name attributes are restricted during .innerHTML assignment
+ var input = document.createElement("input");
+ input.setAttribute( "type", "hidden" );
+ div.appendChild( input ).setAttribute( "name", "D" );
+
+ // Support: IE8
+ // Enforce case-sensitivity of name attribute
+ if ( div.querySelectorAll("[name=d]").length ) {
+ rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
+ }
+
+ // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
+ // IE8 throws error here and will not see later tests
+ if ( !div.querySelectorAll(":enabled").length ) {
+ rbuggyQSA.push( ":enabled", ":disabled" );
+ }
+
+ // Opera 10-11 does not throw on post-comma invalid pseudos
+ div.querySelectorAll("*,:x");
+ rbuggyQSA.push(",.*:");
+ });
+ }
+
+ if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
+ docElem.webkitMatchesSelector ||
+ docElem.mozMatchesSelector ||
+ docElem.oMatchesSelector ||
+ docElem.msMatchesSelector) )) ) {
+
+ assert(function( div ) {
+ // Check to see if it's possible to do matchesSelector
+ // on a disconnected node (IE 9)
+ support.disconnectedMatch = matches.call( div, "div" );
+
+ // This should fail with an exception
+ // Gecko does not error, returns false instead
+ matches.call( div, "[s!='']:x" );
+ rbuggyMatches.push( "!=", pseudos );
+ });
+ }
+
+ rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
+ rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
+
+ /* Contains
+ ---------------------------------------------------------------------- */
+ hasCompare = rnative.test( docElem.compareDocumentPosition );
+
+ // Element contains another
+ // Purposefully self-exclusive
+ // As in, an element does not contain itself
+ contains = hasCompare || rnative.test( docElem.contains ) ?
+ function( a, b ) {
+ var adown = a.nodeType === 9 ? a.documentElement : a,
+ bup = b && b.parentNode;
+ return a === bup || !!( bup && bup.nodeType === 1 && (
+ adown.contains ?
+ adown.contains( bup ) :
+ a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
+ ));
+ } :
+ function( a, b ) {
+ if ( b ) {
+ while ( (b = b.parentNode) ) {
+ if ( b === a ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+
+ /* Sorting
+ ---------------------------------------------------------------------- */
+
+ // Document order sorting
+ sortOrder = hasCompare ?
+ function( a, b ) {
+
+ // Flag for duplicate removal
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ // Sort on method existence if only one input has compareDocumentPosition
+ var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
+ if ( compare ) {
+ return compare;
+ }
+
+ // Calculate position if both inputs belong to the same document
+ compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
+ a.compareDocumentPosition( b ) :
+
+ // Otherwise we know they are disconnected
+ 1;
+
+ // Disconnected nodes
+ if ( compare & 1 ||
+ (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
+
+ // Choose the first element that is related to our preferred document
+ if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
+ return -1;
+ }
+ if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
+ return 1;
+ }
+
+ // Maintain original order
+ return sortInput ?
+ ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
+ 0;
+ }
+
+ return compare & 4 ? -1 : 1;
+ } :
+ function( a, b ) {
+ // Exit early if the nodes are identical
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ var cur,
+ i = 0,
+ aup = a.parentNode,
+ bup = b.parentNode,
+ ap = [ a ],
+ bp = [ b ];
+
+ // Parentless nodes are either documents or disconnected
+ if ( !aup || !bup ) {
+ return a === document ? -1 :
+ b === document ? 1 :
+ aup ? -1 :
+ bup ? 1 :
+ sortInput ?
+ ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
+ 0;
+
+ // If the nodes are siblings, we can do a quick check
+ } else if ( aup === bup ) {
+ return siblingCheck( a, b );
+ }
+
+ // Otherwise we need full lists of their ancestors for comparison
+ cur = a;
+ while ( (cur = cur.parentNode) ) {
+ ap.unshift( cur );
+ }
+ cur = b;
+ while ( (cur = cur.parentNode) ) {
+ bp.unshift( cur );
+ }
+
+ // Walk down the tree looking for a discrepancy
+ while ( ap[i] === bp[i] ) {
+ i++;
+ }
+
+ return i ?
+ // Do a sibling check if the nodes have a common ancestor
+ siblingCheck( ap[i], bp[i] ) :
+
+ // Otherwise nodes in our document sort first
+ ap[i] === preferredDoc ? -1 :
+ bp[i] === preferredDoc ? 1 :
+ 0;
+ };
+
+ return document;
+ };
+
+ Sizzle.matches = function( expr, elements ) {
+ return Sizzle( expr, null, null, elements );
+ };
+
+ Sizzle.matchesSelector = function( elem, expr ) {
+ // Set document vars if needed
+ if ( ( elem.ownerDocument || elem ) !== document ) {
+ setDocument( elem );
+ }
+
+ // Make sure that attribute selectors are quoted
+ expr = expr.replace( rattributeQuotes, "='$1']" );
+
+ if ( support.matchesSelector && documentIsHTML &&
+ !compilerCache[ expr + " " ] &&
+ ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
+ ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {
+
+ try {
+ var ret = matches.call( elem, expr );
+
+ // IE 9's matchesSelector returns false on disconnected nodes
+ if ( ret || support.disconnectedMatch ||
+ // As well, disconnected nodes are said to be in a document
+ // fragment in IE 9
+ elem.document && elem.document.nodeType !== 11 ) {
+ return ret;
+ }
+ } catch (e) {}
+ }
+
+ return Sizzle( expr, document, null, [ elem ] ).length > 0;
+ };
+
+ Sizzle.contains = function( context, elem ) {
+ // Set document vars if needed
+ if ( ( context.ownerDocument || context ) !== document ) {
+ setDocument( context );
+ }
+ return contains( context, elem );
+ };
+
+ Sizzle.attr = function( elem, name ) {
+ // Set document vars if needed
+ if ( ( elem.ownerDocument || elem ) !== document ) {
+ setDocument( elem );
+ }
+
+ var fn = Expr.attrHandle[ name.toLowerCase() ],
+ // Don't get fooled by Object.prototype properties (jQuery #13807)
+ val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
+ fn( elem, name, !documentIsHTML ) :
+ undefined;
+
+ return val !== undefined ?
+ val :
+ support.attributes || !documentIsHTML ?
+ elem.getAttribute( name ) :
+ (val = elem.getAttributeNode(name)) && val.specified ?
+ val.value :
+ null;
+ };
+
+ Sizzle.error = function( msg ) {
+ throw new Error( "Syntax error, unrecognized expression: " + msg );
+ };
+
+ /**
+ * Document sorting and removing duplicates
+ * @param {ArrayLike} results
+ */
+ Sizzle.uniqueSort = function( results ) {
+ var elem,
+ duplicates = [],
+ j = 0,
+ i = 0;
+
+ // Unless we *know* we can detect duplicates, assume their presence
+ hasDuplicate = !support.detectDuplicates;
+ sortInput = !support.sortStable && results.slice( 0 );
+ results.sort( sortOrder );
+
+ if ( hasDuplicate ) {
+ while ( (elem = results[i++]) ) {
+ if ( elem === results[ i ] ) {
+ j = duplicates.push( i );
+ }
+ }
+ while ( j-- ) {
+ results.splice( duplicates[ j ], 1 );
+ }
+ }
+
+ // Clear input after sorting to release objects
+ // See https://github.com/jquery/sizzle/pull/225
+ sortInput = null;
+
+ return results;
+ };
+
+ /**
+ * Utility function for retrieving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+ getText = Sizzle.getText = function( elem ) {
+ var node,
+ ret = "",
+ i = 0,
+ nodeType = elem.nodeType;
+
+ if ( !nodeType ) {
+ // If no nodeType, this is expected to be an array
+ while ( (node = elem[i++]) ) {
+ // Do not traverse comment nodes
+ ret += getText( node );
+ }
+ } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+ // Use textContent for elements
+ // innerText usage removed for consistency of new lines (jQuery #11153)
+ if ( typeof elem.textContent === "string" ) {
+ return elem.textContent;
+ } else {
+ // Traverse its children
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+ ret += getText( elem );
+ }
+ }
+ } else if ( nodeType === 3 || nodeType === 4 ) {
+ return elem.nodeValue;
+ }
+ // Do not include comment or processing instruction nodes
+
+ return ret;
+ };
+
+ Expr = Sizzle.selectors = {
+
+ // Can be adjusted by the user
+ cacheLength: 50,
+
+ createPseudo: markFunction,
+
+ match: matchExpr,
+
+ attrHandle: {},
+
+ find: {},
+
+ relative: {
+ ">": { dir: "parentNode", first: true },
+ " ": { dir: "parentNode" },
+ "+": { dir: "previousSibling", first: true },
+ "~": { dir: "previousSibling" }
+ },
+
+ preFilter: {
+ "ATTR": function( match ) {
+ match[1] = match[1].replace( runescape, funescape );
+
+ // Move the given value to match[3] whether quoted or unquoted
+ match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
+
+ if ( match[2] === "~=" ) {
+ match[3] = " " + match[3] + " ";
+ }
+
+ return match.slice( 0, 4 );
+ },
+
+ "CHILD": function( match ) {
+ /* matches from matchExpr["CHILD"]
+ 1 type (only|nth|...)
+ 2 what (child|of-type)
+ 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
+ 4 xn-component of xn+y argument ([+-]?\d*n|)
+ 5 sign of xn-component
+ 6 x of xn-component
+ 7 sign of y-component
+ 8 y of y-component
+ */
+ match[1] = match[1].toLowerCase();
+
+ if ( match[1].slice( 0, 3 ) === "nth" ) {
+ // nth-* requires argument
+ if ( !match[3] ) {
+ Sizzle.error( match[0] );
+ }
+
+ // numeric x and y parameters for Expr.filter.CHILD
+ // remember that false/true cast respectively to 0/1
+ match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
+ match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
+
+ // other types prohibit arguments
+ } else if ( match[3] ) {
+ Sizzle.error( match[0] );
+ }
+
+ return match;
+ },
+
+ "PSEUDO": function( match ) {
+ var excess,
+ unquoted = !match[6] && match[2];
+
+ if ( matchExpr["CHILD"].test( match[0] ) ) {
+ return null;
+ }
+
+ // Accept quoted arguments as-is
+ if ( match[3] ) {
+ match[2] = match[4] || match[5] || "";
+
+ // Strip excess characters from unquoted arguments
+ } else if ( unquoted && rpseudo.test( unquoted ) &&
+ // Get excess from tokenize (recursively)
+ (excess = tokenize( unquoted, true )) &&
+ // advance to the next closing parenthesis
+ (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
+
+ // excess is a negative index
+ match[0] = match[0].slice( 0, excess );
+ match[2] = unquoted.slice( 0, excess );
+ }
+
+ // Return only captures needed by the pseudo filter method (type and argument)
+ return match.slice( 0, 3 );
+ }
+ },
+
+ filter: {
+
+ "TAG": function( nodeNameSelector ) {
+ var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
+ return nodeNameSelector === "*" ?
+ function() { return true; } :
+ function( elem ) {
+ return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
+ };
+ },
+
+ "CLASS": function( className ) {
+ var pattern = classCache[ className + " " ];
+
+ return pattern ||
+ (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
+ classCache( className, function( elem ) {
+ return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" );
+ });
+ },
+
+ "ATTR": function( name, operator, check ) {
+ return function( elem ) {
+ var result = Sizzle.attr( elem, name );
+
+ if ( result == null ) {
+ return operator === "!=";
+ }
+ if ( !operator ) {
+ return true;
+ }
+
+ result += "";
+
+ return operator === "=" ? result === check :
+ operator === "!=" ? result !== check :
+ operator === "^=" ? check && result.indexOf( check ) === 0 :
+ operator === "*=" ? check && result.indexOf( check ) > -1 :
+ operator === "$=" ? check && result.slice( -check.length ) === check :
+ operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
+ operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
+ false;
+ };
+ },
+
+ "CHILD": function( type, what, argument, first, last ) {
+ var simple = type.slice( 0, 3 ) !== "nth",
+ forward = type.slice( -4 ) !== "last",
+ ofType = what === "of-type";
+
+ return first === 1 && last === 0 ?
+
+ // Shortcut for :nth-*(n)
+ function( elem ) {
+ return !!elem.parentNode;
+ } :
+
+ function( elem, context, xml ) {
+ var cache, uniqueCache, outerCache, node, nodeIndex, start,
+ dir = simple !== forward ? "nextSibling" : "previousSibling",
+ parent = elem.parentNode,
+ name = ofType && elem.nodeName.toLowerCase(),
+ useCache = !xml && !ofType,
+ diff = false;
+
+ if ( parent ) {
+
+ // :(first|last|only)-(child|of-type)
+ if ( simple ) {
+ while ( dir ) {
+ node = elem;
+ while ( (node = node[ dir ]) ) {
+ if ( ofType ?
+ node.nodeName.toLowerCase() === name :
+ node.nodeType === 1 ) {
+
+ return false;
+ }
+ }
+ // Reverse direction for :only-* (if we haven't yet done so)
+ start = dir = type === "only" && !start && "nextSibling";
+ }
+ return true;
+ }
+
+ start = [ forward ? parent.firstChild : parent.lastChild ];
+
+ // non-xml :nth-child(...) stores cache data on `parent`
+ if ( forward && useCache ) {
+
+ // Seek `elem` from a previously-cached index
+
+ // ...in a gzip-friendly way
+ node = parent;
+ outerCache = node[ expando ] || (node[ expando ] = {});
+
+ // Support: IE <9 only
+ // Defend against cloned attroperties (jQuery gh-1709)
+ uniqueCache = outerCache[ node.uniqueID ] ||
+ (outerCache[ node.uniqueID ] = {});
+
+ cache = uniqueCache[ type ] || [];
+ nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
+ diff = nodeIndex && cache[ 2 ];
+ node = nodeIndex && parent.childNodes[ nodeIndex ];
+
+ while ( (node = ++nodeIndex && node && node[ dir ] ||
+
+ // Fallback to seeking `elem` from the start
+ (diff = nodeIndex = 0) || start.pop()) ) {
+
+ // When found, cache indexes on `parent` and break
+ if ( node.nodeType === 1 && ++diff && node === elem ) {
+ uniqueCache[ type ] = [ dirruns, nodeIndex, diff ];
+ break;
+ }
+ }
+
+ } else {
+ // Use previously-cached element index if available
+ if ( useCache ) {
+ // ...in a gzip-friendly way
+ node = elem;
+ outerCache = node[ expando ] || (node[ expando ] = {});
+
+ // Support: IE <9 only
+ // Defend against cloned attroperties (jQuery gh-1709)
+ uniqueCache = outerCache[ node.uniqueID ] ||
+ (outerCache[ node.uniqueID ] = {});
+
+ cache = uniqueCache[ type ] || [];
+ nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
+ diff = nodeIndex;
+ }
+
+ // xml :nth-child(...)
+ // or :nth-last-child(...) or :nth(-last)?-of-type(...)
+ if ( diff === false ) {
+ // Use the same loop as above to seek `elem` from the start
+ while ( (node = ++nodeIndex && node && node[ dir ] ||
+ (diff = nodeIndex = 0) || start.pop()) ) {
+
+ if ( ( ofType ?
+ node.nodeName.toLowerCase() === name :
+ node.nodeType === 1 ) &&
+ ++diff ) {
+
+ // Cache the index of each encountered element
+ if ( useCache ) {
+ outerCache = node[ expando ] || (node[ expando ] = {});
+
+ // Support: IE <9 only
+ // Defend against cloned attroperties (jQuery gh-1709)
+ uniqueCache = outerCache[ node.uniqueID ] ||
+ (outerCache[ node.uniqueID ] = {});
+
+ uniqueCache[ type ] = [ dirruns, diff ];
+ }
+
+ if ( node === elem ) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Incorporate the offset, then check against cycle size
+ diff -= last;
+ return diff === first || ( diff % first === 0 && diff / first >= 0 );
+ }
+ };
+ },
+
+ "PSEUDO": function( pseudo, argument ) {
+ // pseudo-class names are case-insensitive
+ // http://www.w3.org/TR/selectors/#pseudo-classes
+ // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
+ // Remember that setFilters inherits from pseudos
+ var args,
+ fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
+ Sizzle.error( "unsupported pseudo: " + pseudo );
+
+ // The user may use createPseudo to indicate that
+ // arguments are needed to create the filter function
+ // just as Sizzle does
+ if ( fn[ expando ] ) {
+ return fn( argument );
+ }
+
+ // But maintain support for old signatures
+ if ( fn.length > 1 ) {
+ args = [ pseudo, pseudo, "", argument ];
+ return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
+ markFunction(function( seed, matches ) {
+ var idx,
+ matched = fn( seed, argument ),
+ i = matched.length;
+ while ( i-- ) {
+ idx = indexOf( seed, matched[i] );
+ seed[ idx ] = !( matches[ idx ] = matched[i] );
+ }
+ }) :
+ function( elem ) {
+ return fn( elem, 0, args );
+ };
+ }
+
+ return fn;
+ }
+ },
+
+ pseudos: {
+ // Potentially complex pseudos
+ "not": markFunction(function( selector ) {
+ // Trim the selector passed to compile
+ // to avoid treating leading and trailing
+ // spaces as combinators
+ var input = [],
+ results = [],
+ matcher = compile( selector.replace( rtrim, "$1" ) );
+
+ return matcher[ expando ] ?
+ markFunction(function( seed, matches, context, xml ) {
+ var elem,
+ unmatched = matcher( seed, null, xml, [] ),
+ i = seed.length;
+
+ // Match elements unmatched by `matcher`
+ while ( i-- ) {
+ if ( (elem = unmatched[i]) ) {
+ seed[i] = !(matches[i] = elem);
+ }
+ }
+ }) :
+ function( elem, context, xml ) {
+ input[0] = elem;
+ matcher( input, null, xml, results );
+ // Don't keep the element (issue #299)
+ input[0] = null;
+ return !results.pop();
+ };
+ }),
+
+ "has": markFunction(function( selector ) {
+ return function( elem ) {
+ return Sizzle( selector, elem ).length > 0;
+ };
+ }),
+
+ "contains": markFunction(function( text ) {
+ text = text.replace( runescape, funescape );
+ return function( elem ) {
+ return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
+ };
+ }),
+
+ // "Whether an element is represented by a :lang() selector
+ // is based solely on the element's language value
+ // being equal to the identifier C,
+ // or beginning with the identifier C immediately followed by "-".
+ // The matching of C against the element's language value is performed case-insensitively.
+ // The identifier C does not have to be a valid language name."
+ // http://www.w3.org/TR/selectors/#lang-pseudo
+ "lang": markFunction( function( lang ) {
+ // lang value must be a valid identifier
+ if ( !ridentifier.test(lang || "") ) {
+ Sizzle.error( "unsupported lang: " + lang );
+ }
+ lang = lang.replace( runescape, funescape ).toLowerCase();
+ return function( elem ) {
+ var elemLang;
+ do {
+ if ( (elemLang = documentIsHTML ?
+ elem.lang :
+ elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
+
+ elemLang = elemLang.toLowerCase();
+ return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
+ }
+ } while ( (elem = elem.parentNode) && elem.nodeType === 1 );
+ return false;
+ };
+ }),
+
+ // Miscellaneous
+ "target": function( elem ) {
+ var hash = window.location && window.location.hash;
+ return hash && hash.slice( 1 ) === elem.id;
+ },
+
+ "root": function( elem ) {
+ return elem === docElem;
+ },
+
+ "focus": function( elem ) {
+ return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
+ },
+
+ // Boolean properties
+ "enabled": function( elem ) {
+ return elem.disabled === false;
+ },
+
+ "disabled": function( elem ) {
+ return elem.disabled === true;
+ },
+
+ "checked": function( elem ) {
+ // In CSS3, :checked should return both checked and selected elements
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ var nodeName = elem.nodeName.toLowerCase();
+ return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
+ },
+
+ "selected": function( elem ) {
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ if ( elem.parentNode ) {
+ elem.parentNode.selectedIndex;
+ }
+
+ return elem.selected === true;
+ },
+
+ // Contents
+ "empty": function( elem ) {
+ // http://www.w3.org/TR/selectors/#empty-pseudo
+ // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
+ // but not by others (comment: 8; processing instruction: 7; etc.)
+ // nodeType < 6 works because attributes (2) do not appear as children
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+ if ( elem.nodeType < 6 ) {
+ return false;
+ }
+ }
+ return true;
+ },
+
+ "parent": function( elem ) {
+ return !Expr.pseudos["empty"]( elem );
+ },
+
+ // Element/input types
+ "header": function( elem ) {
+ return rheader.test( elem.nodeName );
+ },
+
+ "input": function( elem ) {
+ return rinputs.test( elem.nodeName );
+ },
+
+ "button": function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && elem.type === "button" || name === "button";
+ },
+
+ "text": function( elem ) {
+ var attr;
+ return elem.nodeName.toLowerCase() === "input" &&
+ elem.type === "text" &&
+
+ // Support: IE<8
+ // New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
+ ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
+ },
+
+ // Position-in-collection
+ "first": createPositionalPseudo(function() {
+ return [ 0 ];
+ }),
+
+ "last": createPositionalPseudo(function( matchIndexes, length ) {
+ return [ length - 1 ];
+ }),
+
+ "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ return [ argument < 0 ? argument + length : argument ];
+ }),
+
+ "even": createPositionalPseudo(function( matchIndexes, length ) {
+ var i = 0;
+ for ( ; i < length; i += 2 ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "odd": createPositionalPseudo(function( matchIndexes, length ) {
+ var i = 1;
+ for ( ; i < length; i += 2 ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ var i = argument < 0 ? argument + length : argument;
+ for ( ; --i >= 0; ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ var i = argument < 0 ? argument + length : argument;
+ for ( ; ++i < length; ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ })
+ }
+ };
+
+ Expr.pseudos["nth"] = Expr.pseudos["eq"];
+
+// Add button/input type pseudos
+ for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
+ Expr.pseudos[ i ] = createInputPseudo( i );
+ }
+ for ( i in { submit: true, reset: true } ) {
+ Expr.pseudos[ i ] = createButtonPseudo( i );
+ }
+
+// Easy API for creating new setFilters
+ function setFilters() {}
+ setFilters.prototype = Expr.filters = Expr.pseudos;
+ Expr.setFilters = new setFilters();
+
+ tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
+ var matched, match, tokens, type,
+ soFar, groups, preFilters,
+ cached = tokenCache[ selector + " " ];
+
+ if ( cached ) {
+ return parseOnly ? 0 : cached.slice( 0 );
+ }
+
+ soFar = selector;
+ groups = [];
+ preFilters = Expr.preFilter;
+
+ while ( soFar ) {
+
+ // Comma and first run
+ if ( !matched || (match = rcomma.exec( soFar )) ) {
+ if ( match ) {
+ // Don't consume trailing commas as valid
+ soFar = soFar.slice( match[0].length ) || soFar;
+ }
+ groups.push( (tokens = []) );
+ }
+
+ matched = false;
+
+ // Combinators
+ if ( (match = rcombinators.exec( soFar )) ) {
+ matched = match.shift();
+ tokens.push({
+ value: matched,
+ // Cast descendant combinators to space
+ type: match[0].replace( rtrim, " " )
+ });
+ soFar = soFar.slice( matched.length );
+ }
+
+ // Filters
+ for ( type in Expr.filter ) {
+ if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
+ (match = preFilters[ type ]( match ))) ) {
+ matched = match.shift();
+ tokens.push({
+ value: matched,
+ type: type,
+ matches: match
+ });
+ soFar = soFar.slice( matched.length );
+ }
+ }
+
+ if ( !matched ) {
+ break;
+ }
+ }
+
+ // Return the length of the invalid excess
+ // if we're just parsing
+ // Otherwise, throw an error or return tokens
+ return parseOnly ?
+ soFar.length :
+ soFar ?
+ Sizzle.error( selector ) :
+ // Cache the tokens
+ tokenCache( selector, groups ).slice( 0 );
+ };
+
+ function toSelector( tokens ) {
+ var i = 0,
+ len = tokens.length,
+ selector = "";
+ for ( ; i < len; i++ ) {
+ selector += tokens[i].value;
+ }
+ return selector;
+ }
+
+ function addCombinator( matcher, combinator, base ) {
+ var dir = combinator.dir,
+ checkNonElements = base && dir === "parentNode",
+ doneName = done++;
+
+ return combinator.first ?
+ // Check against closest ancestor/preceding element
+ function( elem, context, xml ) {
+ while ( (elem = elem[ dir ]) ) {
+ if ( elem.nodeType === 1 || checkNonElements ) {
+ return matcher( elem, context, xml );
+ }
+ }
+ } :
+
+ // Check against all ancestor/preceding elements
+ function( elem, context, xml ) {
+ var oldCache, uniqueCache, outerCache,
+ newCache = [ dirruns, doneName ];
+
+ // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching
+ if ( xml ) {
+ while ( (elem = elem[ dir ]) ) {
+ if ( elem.nodeType === 1 || checkNonElements ) {
+ if ( matcher( elem, context, xml ) ) {
+ return true;
+ }
+ }
+ }
+ } else {
+ while ( (elem = elem[ dir ]) ) {
+ if ( elem.nodeType === 1 || checkNonElements ) {
+ outerCache = elem[ expando ] || (elem[ expando ] = {});
+
+ // Support: IE <9 only
+ // Defend against cloned attroperties (jQuery gh-1709)
+ uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {});
+
+ if ( (oldCache = uniqueCache[ dir ]) &&
+ oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
+
+ // Assign to newCache so results back-propagate to previous elements
+ return (newCache[ 2 ] = oldCache[ 2 ]);
+ } else {
+ // Reuse newcache so results back-propagate to previous elements
+ uniqueCache[ dir ] = newCache;
+
+ // A match means we're done; a fail means we have to keep checking
+ if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ };
+ }
+
+ function elementMatcher( matchers ) {
+ return matchers.length > 1 ?
+ function( elem, context, xml ) {
+ var i = matchers.length;
+ while ( i-- ) {
+ if ( !matchers[i]( elem, context, xml ) ) {
+ return false;
+ }
+ }
+ return true;
+ } :
+ matchers[0];
+ }
+
+ function multipleContexts( selector, contexts, results ) {
+ var i = 0,
+ len = contexts.length;
+ for ( ; i < len; i++ ) {
+ Sizzle( selector, contexts[i], results );
+ }
+ return results;
+ }
+
+ function condense( unmatched, map, filter, context, xml ) {
+ var elem,
+ newUnmatched = [],
+ i = 0,
+ len = unmatched.length,
+ mapped = map != null;
+
+ for ( ; i < len; i++ ) {
+ if ( (elem = unmatched[i]) ) {
+ if ( !filter || filter( elem, context, xml ) ) {
+ newUnmatched.push( elem );
+ if ( mapped ) {
+ map.push( i );
+ }
+ }
+ }
+ }
+
+ return newUnmatched;
+ }
+
+ function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
+ if ( postFilter && !postFilter[ expando ] ) {
+ postFilter = setMatcher( postFilter );
+ }
+ if ( postFinder && !postFinder[ expando ] ) {
+ postFinder = setMatcher( postFinder, postSelector );
+ }
+ return markFunction(function( seed, results, context, xml ) {
+ var temp, i, elem,
+ preMap = [],
+ postMap = [],
+ preexisting = results.length,
+
+ // Get initial elements from seed or context
+ elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
+
+ // Prefilter to get matcher input, preserving a map for seed-results synchronization
+ matcherIn = preFilter && ( seed || !selector ) ?
+ condense( elems, preMap, preFilter, context, xml ) :
+ elems,
+
+ matcherOut = matcher ?
+ // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
+ postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
+
+ // ...intermediate processing is necessary
+ [] :
+
+ // ...otherwise use results directly
+ results :
+ matcherIn;
+
+ // Find primary matches
+ if ( matcher ) {
+ matcher( matcherIn, matcherOut, context, xml );
+ }
+
+ // Apply postFilter
+ if ( postFilter ) {
+ temp = condense( matcherOut, postMap );
+ postFilter( temp, [], context, xml );
+
+ // Un-match failing elements by moving them back to matcherIn
+ i = temp.length;
+ while ( i-- ) {
+ if ( (elem = temp[i]) ) {
+ matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
+ }
+ }
+ }
+
+ if ( seed ) {
+ if ( postFinder || preFilter ) {
+ if ( postFinder ) {
+ // Get the final matcherOut by condensing this intermediate into postFinder contexts
+ temp = [];
+ i = matcherOut.length;
+ while ( i-- ) {
+ if ( (elem = matcherOut[i]) ) {
+ // Restore matcherIn since elem is not yet a final match
+ temp.push( (matcherIn[i] = elem) );
+ }
+ }
+ postFinder( null, (matcherOut = []), temp, xml );
+ }
+
+ // Move matched elements from seed to results to keep them synchronized
+ i = matcherOut.length;
+ while ( i-- ) {
+ if ( (elem = matcherOut[i]) &&
+ (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) {
+
+ seed[temp] = !(results[temp] = elem);
+ }
+ }
+ }
+
+ // Add elements to results, through postFinder if defined
+ } else {
+ matcherOut = condense(
+ matcherOut === results ?
+ matcherOut.splice( preexisting, matcherOut.length ) :
+ matcherOut
+ );
+ if ( postFinder ) {
+ postFinder( null, results, matcherOut, xml );
+ } else {
+ push.apply( results, matcherOut );
+ }
+ }
+ });
+ }
+
+ function matcherFromTokens( tokens ) {
+ var checkContext, matcher, j,
+ len = tokens.length,
+ leadingRelative = Expr.relative[ tokens[0].type ],
+ implicitRelative = leadingRelative || Expr.relative[" "],
+ i = leadingRelative ? 1 : 0,
+
+ // The foundational matcher ensures that elements are reachable from top-level context(s)
+ matchContext = addCombinator( function( elem ) {
+ return elem === checkContext;
+ }, implicitRelative, true ),
+ matchAnyContext = addCombinator( function( elem ) {
+ return indexOf( checkContext, elem ) > -1;
+ }, implicitRelative, true ),
+ matchers = [ function( elem, context, xml ) {
+ var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
+ (checkContext = context).nodeType ?
+ matchContext( elem, context, xml ) :
+ matchAnyContext( elem, context, xml ) );
+ // Avoid hanging onto element (issue #299)
+ checkContext = null;
+ return ret;
+ } ];
+
+ for ( ; i < len; i++ ) {
+ if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
+ matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
+ } else {
+ matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
+
+ // Return special upon seeing a positional matcher
+ if ( matcher[ expando ] ) {
+ // Find the next relative operator (if any) for proper handling
+ j = ++i;
+ for ( ; j < len; j++ ) {
+ if ( Expr.relative[ tokens[j].type ] ) {
+ break;
+ }
+ }
+ return setMatcher(
+ i > 1 && elementMatcher( matchers ),
+ i > 1 && toSelector(
+ // If the preceding token was a descendant combinator, insert an implicit any-element `*`
+ tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
+ ).replace( rtrim, "$1" ),
+ matcher,
+ i < j && matcherFromTokens( tokens.slice( i, j ) ),
+ j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
+ j < len && toSelector( tokens )
+ );
+ }
+ matchers.push( matcher );
+ }
+ }
+
+ return elementMatcher( matchers );
+ }
+
+ function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
+ var bySet = setMatchers.length > 0,
+ byElement = elementMatchers.length > 0,
+ superMatcher = function( seed, context, xml, results, outermost ) {
+ var elem, j, matcher,
+ matchedCount = 0,
+ i = "0",
+ unmatched = seed && [],
+ setMatched = [],
+ contextBackup = outermostContext,
+ // We must always have either seed elements or outermost context
+ elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
+ // Use integer dirruns iff this is the outermost matcher
+ dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
+ len = elems.length;
+
+ if ( outermost ) {
+ outermostContext = context === document || context || outermost;
+ }
+
+ // Add elements passing elementMatchers directly to results
+ // Support: IE<9, Safari
+ // Tolerate NodeList properties (IE: "length"; Safari:
) matching elements by id
+ for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
+ if ( byElement && elem ) {
+ j = 0;
+ if ( !context && elem.ownerDocument !== document ) {
+ setDocument( elem );
+ xml = !documentIsHTML;
+ }
+ while ( (matcher = elementMatchers[j++]) ) {
+ if ( matcher( elem, context || document, xml) ) {
+ results.push( elem );
+ break;
+ }
+ }
+ if ( outermost ) {
+ dirruns = dirrunsUnique;
+ }
+ }
+
+ // Track unmatched elements for set filters
+ if ( bySet ) {
+ // They will have gone through all possible matchers
+ if ( (elem = !matcher && elem) ) {
+ matchedCount--;
+ }
+
+ // Lengthen the array for every element, matched or not
+ if ( seed ) {
+ unmatched.push( elem );
+ }
+ }
+ }
+
+ // `i` is now the count of elements visited above, and adding it to `matchedCount`
+ // makes the latter nonnegative.
+ matchedCount += i;
+
+ // Apply set filters to unmatched elements
+ // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`
+ // equals `i`), unless we didn't visit _any_ elements in the above loop because we have
+ // no element matchers and no seed.
+ // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that
+ // case, which will result in a "00" `matchedCount` that differs from `i` but is also
+ // numerically zero.
+ if ( bySet && i !== matchedCount ) {
+ j = 0;
+ while ( (matcher = setMatchers[j++]) ) {
+ matcher( unmatched, setMatched, context, xml );
+ }
+
+ if ( seed ) {
+ // Reintegrate element matches to eliminate the need for sorting
+ if ( matchedCount > 0 ) {
+ while ( i-- ) {
+ if ( !(unmatched[i] || setMatched[i]) ) {
+ setMatched[i] = pop.call( results );
+ }
+ }
+ }
+
+ // Discard index placeholder values to get only actual matches
+ setMatched = condense( setMatched );
+ }
+
+ // Add matches to results
+ push.apply( results, setMatched );
+
+ // Seedless set matches succeeding multiple successful matchers stipulate sorting
+ if ( outermost && !seed && setMatched.length > 0 &&
+ ( matchedCount + setMatchers.length ) > 1 ) {
+
+ Sizzle.uniqueSort( results );
+ }
+ }
+
+ // Override manipulation of globals by nested matchers
+ if ( outermost ) {
+ dirruns = dirrunsUnique;
+ outermostContext = contextBackup;
+ }
+
+ return unmatched;
+ };
+
+ return bySet ?
+ markFunction( superMatcher ) :
+ superMatcher;
+ }
+
+ compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
+ var i,
+ setMatchers = [],
+ elementMatchers = [],
+ cached = compilerCache[ selector + " " ];
+
+ if ( !cached ) {
+ // Generate a function of recursive functions that can be used to check each element
+ if ( !match ) {
+ match = tokenize( selector );
+ }
+ i = match.length;
+ while ( i-- ) {
+ cached = matcherFromTokens( match[i] );
+ if ( cached[ expando ] ) {
+ setMatchers.push( cached );
+ } else {
+ elementMatchers.push( cached );
+ }
+ }
+
+ // Cache the compiled function
+ cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
+
+ // Save selector and tokenization
+ cached.selector = selector;
+ }
+ return cached;
+ };
+
+ /**
+ * A low-level selection function that works with Sizzle's compiled
+ * selector functions
+ * @param {String|Function} selector A selector or a pre-compiled
+ * selector function built with Sizzle.compile
+ * @param {Element} context
+ * @param {Array} [results]
+ * @param {Array} [seed] A set of elements to match against
+ */
+ select = Sizzle.select = function( selector, context, results, seed ) {
+ var i, tokens, token, type, find,
+ compiled = typeof selector === "function" && selector,
+ match = !seed && tokenize( (selector = compiled.selector || selector) );
+
+ results = results || [];
+
+ // Try to minimize operations if there is only one selector in the list and no seed
+ // (the latter of which guarantees us context)
+ if ( match.length === 1 ) {
+
+ // Reduce context if the leading compound selector is an ID
+ tokens = match[0] = match[0].slice( 0 );
+ if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
+ support.getById && context.nodeType === 9 && documentIsHTML &&
+ Expr.relative[ tokens[1].type ] ) {
+
+ context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
+ if ( !context ) {
+ return results;
+
+ // Precompiled matchers will still verify ancestry, so step up a level
+ } else if ( compiled ) {
+ context = context.parentNode;
+ }
+
+ selector = selector.slice( tokens.shift().value.length );
+ }
+
+ // Fetch a seed set for right-to-left matching
+ i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
+ while ( i-- ) {
+ token = tokens[i];
+
+ // Abort if we hit a combinator
+ if ( Expr.relative[ (type = token.type) ] ) {
+ break;
+ }
+ if ( (find = Expr.find[ type ]) ) {
+ // Search, expanding context for leading sibling combinators
+ if ( (seed = find(
+ token.matches[0].replace( runescape, funescape ),
+ rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
+ )) ) {
+
+ // If seed is empty or no tokens remain, we can return early
+ tokens.splice( i, 1 );
+ selector = seed.length && toSelector( tokens );
+ if ( !selector ) {
+ push.apply( results, seed );
+ return results;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ // Compile and execute a filtering function if one is not provided
+ // Provide `match` to avoid retokenization if we modified the selector above
+ ( compiled || compile( selector, match ) )(
+ seed,
+ context,
+ !documentIsHTML,
+ results,
+ !context || rsibling.test( selector ) && testContext( context.parentNode ) || context
+ );
+ return results;
+ };
+
+// One-time assignments
+
+// Sort stability
+ support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
+
+// Support: Chrome 14-35+
+// Always assume duplicates if they aren't passed to the comparison function
+ support.detectDuplicates = !!hasDuplicate;
+
+// Initialize against the default document
+ setDocument();
+
+// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
+// Detached nodes confoundingly follow *each other*
+ support.sortDetached = assert(function( div1 ) {
+ // Should return 1, but returns 4 (following)
+ return div1.compareDocumentPosition( document.createElement("div") ) & 1;
+ });
+
+// Support: IE<8
+// Prevent attribute/property "interpolation"
+// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
+ if ( !assert(function( div ) {
+ div.innerHTML = " ";
+ return div.firstChild.getAttribute("href") === "#" ;
+ }) ) {
+ addHandle( "type|href|height|width", function( elem, name, isXML ) {
+ if ( !isXML ) {
+ return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
+ }
+ });
+ }
+
+// Support: IE<9
+// Use defaultValue in place of getAttribute("value")
+ if ( !support.attributes || !assert(function( div ) {
+ div.innerHTML = " ";
+ div.firstChild.setAttribute( "value", "" );
+ return div.firstChild.getAttribute( "value" ) === "";
+ }) ) {
+ addHandle( "value", function( elem, name, isXML ) {
+ if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
+ return elem.defaultValue;
+ }
+ });
+ }
+
+// Support: IE<9
+// Use getAttributeNode to fetch booleans when getAttribute lies
+ if ( !assert(function( div ) {
+ return div.getAttribute("disabled") == null;
+ }) ) {
+ addHandle( booleans, function( elem, name, isXML ) {
+ var val;
+ if ( !isXML ) {
+ return elem[ name ] === true ? name.toLowerCase() :
+ (val = elem.getAttributeNode( name )) && val.specified ?
+ val.value :
+ null;
+ }
+ });
+ }
+
+ return Sizzle;
+
+ })( window );
+
+
+
+ jQuery.find = Sizzle;
+ jQuery.expr = Sizzle.selectors;
+ jQuery.expr[ ":" ] = jQuery.expr.pseudos;
+ jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;
+ jQuery.text = Sizzle.getText;
+ jQuery.isXMLDoc = Sizzle.isXML;
+ jQuery.contains = Sizzle.contains;
+
+
+
+ var dir = function( elem, dir, until ) {
+ var matched = [],
+ truncate = until !== undefined;
+
+ while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {
+ if ( elem.nodeType === 1 ) {
+ if ( truncate && jQuery( elem ).is( until ) ) {
+ break;
+ }
+ matched.push( elem );
+ }
+ }
+ return matched;
+ };
+
+
+ var siblings = function( n, elem ) {
+ var matched = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType === 1 && n !== elem ) {
+ matched.push( n );
+ }
+ }
+
+ return matched;
+ };
+
+
+ var rneedsContext = jQuery.expr.match.needsContext;
+
+ var rsingleTag = ( /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/ );
+
+
+
+ var risSimple = /^.[^:#\[\.,]*$/;
+
+// Implement the identical functionality for filter and not
+ function winnow( elements, qualifier, not ) {
+ if ( jQuery.isFunction( qualifier ) ) {
+ return jQuery.grep( elements, function( elem, i ) {
+ /* jshint -W018 */
+ return !!qualifier.call( elem, i, elem ) !== not;
+ } );
+
+ }
+
+ if ( qualifier.nodeType ) {
+ return jQuery.grep( elements, function( elem ) {
+ return ( elem === qualifier ) !== not;
+ } );
+
+ }
+
+ if ( typeof qualifier === "string" ) {
+ if ( risSimple.test( qualifier ) ) {
+ return jQuery.filter( qualifier, elements, not );
+ }
+
+ qualifier = jQuery.filter( qualifier, elements );
+ }
+
+ return jQuery.grep( elements, function( elem ) {
+ return ( jQuery.inArray( elem, qualifier ) > -1 ) !== not;
+ } );
+ }
+
+ jQuery.filter = function( expr, elems, not ) {
+ var elem = elems[ 0 ];
+
+ if ( not ) {
+ expr = ":not(" + expr + ")";
+ }
+
+ return elems.length === 1 && elem.nodeType === 1 ?
+ jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
+ jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
+ return elem.nodeType === 1;
+ } ) );
+ };
+
+ jQuery.fn.extend( {
+ find: function( selector ) {
+ var i,
+ ret = [],
+ self = this,
+ len = self.length;
+
+ if ( typeof selector !== "string" ) {
+ return this.pushStack( jQuery( selector ).filter( function() {
+ for ( i = 0; i < len; i++ ) {
+ if ( jQuery.contains( self[ i ], this ) ) {
+ return true;
+ }
+ }
+ } ) );
+ }
+
+ for ( i = 0; i < len; i++ ) {
+ jQuery.find( selector, self[ i ], ret );
+ }
+
+ // Needed because $( selector, context ) becomes $( context ).find( selector )
+ ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
+ ret.selector = this.selector ? this.selector + " " + selector : selector;
+ return ret;
+ },
+ filter: function( selector ) {
+ return this.pushStack( winnow( this, selector || [], false ) );
+ },
+ not: function( selector ) {
+ return this.pushStack( winnow( this, selector || [], true ) );
+ },
+ is: function( selector ) {
+ return !!winnow(
+ this,
+
+ // If this is a positional/relative selector, check membership in the returned set
+ // so $("p:first").is("p:last") won't return true for a doc with two "p".
+ typeof selector === "string" && rneedsContext.test( selector ) ?
+ jQuery( selector ) :
+ selector || [],
+ false
+ ).length;
+ }
+ } );
+
+
+// Initialize a jQuery object
+
+
+// A central reference to the root jQuery(document)
+ var rootjQuery,
+
+ // A simple way to check for HTML strings
+ // Prioritize #id over to avoid XSS via location.hash (#9521)
+ // Strict HTML recognition (#11290: must start with <)
+ rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
+
+ init = jQuery.fn.init = function( selector, context, root ) {
+ var match, elem;
+
+ // HANDLE: $(""), $(null), $(undefined), $(false)
+ if ( !selector ) {
+ return this;
+ }
+
+ // init accepts an alternate rootjQuery
+ // so migrate can support jQuery.sub (gh-2101)
+ root = root || rootjQuery;
+
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ if ( selector.charAt( 0 ) === "<" &&
+ selector.charAt( selector.length - 1 ) === ">" &&
+ selector.length >= 3 ) {
+
+ // Assume that strings that start and end with <> are HTML and skip the regex check
+ match = [ null, selector, null ];
+
+ } else {
+ match = rquickExpr.exec( selector );
+ }
+
+ // Match html or make sure no context is specified for #id
+ if ( match && ( match[ 1 ] || !context ) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[ 1 ] ) {
+ context = context instanceof jQuery ? context[ 0 ] : context;
+
+ // scripts is true for back-compat
+ // Intentionally let the error be thrown if parseHTML is not present
+ jQuery.merge( this, jQuery.parseHTML(
+ match[ 1 ],
+ context && context.nodeType ? context.ownerDocument || context : document,
+ true
+ ) );
+
+ // HANDLE: $(html, props)
+ if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
+ for ( match in context ) {
+
+ // Properties of context are called as methods if possible
+ if ( jQuery.isFunction( this[ match ] ) ) {
+ this[ match ]( context[ match ] );
+
+ // ...and otherwise set as attributes
+ } else {
+ this.attr( match, context[ match ] );
+ }
+ }
+ }
+
+ return this;
+
+ // HANDLE: $(#id)
+ } else {
+ elem = document.getElementById( match[ 2 ] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id !== match[ 2 ] ) {
+ return rootjQuery.find( selector );
+ }
+
+ // Otherwise, we inject the element directly into the jQuery object
+ this.length = 1;
+ this[ 0 ] = elem;
+ }
+
+ this.context = document;
+ this.selector = selector;
+ return this;
+ }
+
+ // HANDLE: $(expr, $(...))
+ } else if ( !context || context.jquery ) {
+ return ( context || root ).find( selector );
+
+ // HANDLE: $(expr, context)
+ // (which is just equivalent to: $(context).find(expr)
+ } else {
+ return this.constructor( context ).find( selector );
+ }
+
+ // HANDLE: $(DOMElement)
+ } else if ( selector.nodeType ) {
+ this.context = this[ 0 ] = selector;
+ this.length = 1;
+ return this;
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) ) {
+ return typeof root.ready !== "undefined" ?
+ root.ready( selector ) :
+
+ // Execute immediately if ready is not present
+ selector( jQuery );
+ }
+
+ if ( selector.selector !== undefined ) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
+
+ return jQuery.makeArray( selector, this );
+ };
+
+// Give the init function the jQuery prototype for later instantiation
+ init.prototype = jQuery.fn;
+
+// Initialize central reference
+ rootjQuery = jQuery( document );
+
+
+ var rparentsprev = /^(?:parents|prev(?:Until|All))/,
+
+ // methods guaranteed to produce a unique set when starting from a unique set
+ guaranteedUnique = {
+ children: true,
+ contents: true,
+ next: true,
+ prev: true
+ };
+
+ jQuery.fn.extend( {
+ has: function( target ) {
+ var i,
+ targets = jQuery( target, this ),
+ len = targets.length;
+
+ return this.filter( function() {
+ for ( i = 0; i < len; i++ ) {
+ if ( jQuery.contains( this, targets[ i ] ) ) {
+ return true;
+ }
+ }
+ } );
+ },
+
+ closest: function( selectors, context ) {
+ var cur,
+ i = 0,
+ l = this.length,
+ matched = [],
+ pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
+ jQuery( selectors, context || this.context ) :
+ 0;
+
+ for ( ; i < l; i++ ) {
+ for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {
+
+ // Always skip document fragments
+ if ( cur.nodeType < 11 && ( pos ?
+ pos.index( cur ) > -1 :
+
+ // Don't pass non-elements to Sizzle
+ cur.nodeType === 1 &&
+ jQuery.find.matchesSelector( cur, selectors ) ) ) {
+
+ matched.push( cur );
+ break;
+ }
+ }
+ }
+
+ return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );
+ },
+
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
+
+ // No argument, return index in parent
+ if ( !elem ) {
+ return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
+ }
+
+ // index in selector
+ if ( typeof elem === "string" ) {
+ return jQuery.inArray( this[ 0 ], jQuery( elem ) );
+ }
+
+ // Locate the position of the desired element
+ return jQuery.inArray(
+
+ // If it receives a jQuery object, the first element is used
+ elem.jquery ? elem[ 0 ] : elem, this );
+ },
+
+ add: function( selector, context ) {
+ return this.pushStack(
+ jQuery.uniqueSort(
+ jQuery.merge( this.get(), jQuery( selector, context ) )
+ )
+ );
+ },
+
+ addBack: function( selector ) {
+ return this.add( selector == null ?
+ this.prevObject : this.prevObject.filter( selector )
+ );
+ }
+ } );
+
+ function sibling( cur, dir ) {
+ do {
+ cur = cur[ dir ];
+ } while ( cur && cur.nodeType !== 1 );
+
+ return cur;
+ }
+
+ jQuery.each( {
+ parent: function( elem ) {
+ var parent = elem.parentNode;
+ return parent && parent.nodeType !== 11 ? parent : null;
+ },
+ parents: function( elem ) {
+ return dir( elem, "parentNode" );
+ },
+ parentsUntil: function( elem, i, until ) {
+ return dir( elem, "parentNode", until );
+ },
+ next: function( elem ) {
+ return sibling( elem, "nextSibling" );
+ },
+ prev: function( elem ) {
+ return sibling( elem, "previousSibling" );
+ },
+ nextAll: function( elem ) {
+ return dir( elem, "nextSibling" );
+ },
+ prevAll: function( elem ) {
+ return dir( elem, "previousSibling" );
+ },
+ nextUntil: function( elem, i, until ) {
+ return dir( elem, "nextSibling", until );
+ },
+ prevUntil: function( elem, i, until ) {
+ return dir( elem, "previousSibling", until );
+ },
+ siblings: function( elem ) {
+ return siblings( ( elem.parentNode || {} ).firstChild, elem );
+ },
+ children: function( elem ) {
+ return siblings( elem.firstChild );
+ },
+ contents: function( elem ) {
+ return jQuery.nodeName( elem, "iframe" ) ?
+ elem.contentDocument || elem.contentWindow.document :
+ jQuery.merge( [], elem.childNodes );
+ }
+ }, function( name, fn ) {
+ jQuery.fn[ name ] = function( until, selector ) {
+ var ret = jQuery.map( this, fn, until );
+
+ if ( name.slice( -5 ) !== "Until" ) {
+ selector = until;
+ }
+
+ if ( selector && typeof selector === "string" ) {
+ ret = jQuery.filter( selector, ret );
+ }
+
+ if ( this.length > 1 ) {
+
+ // Remove duplicates
+ if ( !guaranteedUnique[ name ] ) {
+ ret = jQuery.uniqueSort( ret );
+ }
+
+ // Reverse order for parents* and prev-derivatives
+ if ( rparentsprev.test( name ) ) {
+ ret = ret.reverse();
+ }
+ }
+
+ return this.pushStack( ret );
+ };
+ } );
+ var rnotwhite = ( /\S+/g );
+
+
+
+// Convert String-formatted options into Object-formatted ones
+ function createOptions( options ) {
+ var object = {};
+ jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
+ object[ flag ] = true;
+ } );
+ return object;
+ }
+
+ /*
+ * Create a callback list using the following parameters:
+ *
+ * options: an optional list of space-separated options that will change how
+ * the callback list behaves or a more traditional option object
+ *
+ * By default a callback list will act like an event callback list and can be
+ * "fired" multiple times.
+ *
+ * Possible options:
+ *
+ * once: will ensure the callback list can only be fired once (like a Deferred)
+ *
+ * memory: will keep track of previous values and will call any callback added
+ * after the list has been fired right away with the latest "memorized"
+ * values (like a Deferred)
+ *
+ * unique: will ensure a callback can only be added once (no duplicate in the list)
+ *
+ * stopOnFalse: interrupt callings when a callback returns false
+ *
+ */
+ jQuery.Callbacks = function( options ) {
+
+ // Convert options from String-formatted to Object-formatted if needed
+ // (we check in cache first)
+ options = typeof options === "string" ?
+ createOptions( options ) :
+ jQuery.extend( {}, options );
+
+ var // Flag to know if list is currently firing
+ firing,
+
+ // Last fire value for non-forgettable lists
+ memory,
+
+ // Flag to know if list was already fired
+ fired,
+
+ // Flag to prevent firing
+ locked,
+
+ // Actual callback list
+ list = [],
+
+ // Queue of execution data for repeatable lists
+ queue = [],
+
+ // Index of currently firing callback (modified by add/remove as needed)
+ firingIndex = -1,
+
+ // Fire callbacks
+ fire = function() {
+
+ // Enforce single-firing
+ locked = options.once;
+
+ // Execute callbacks for all pending executions,
+ // respecting firingIndex overrides and runtime changes
+ fired = firing = true;
+ for ( ; queue.length; firingIndex = -1 ) {
+ memory = queue.shift();
+ while ( ++firingIndex < list.length ) {
+
+ // Run callback and check for early termination
+ if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
+ options.stopOnFalse ) {
+
+ // Jump to end and forget the data so .add doesn't re-fire
+ firingIndex = list.length;
+ memory = false;
+ }
+ }
+ }
+
+ // Forget the data if we're done with it
+ if ( !options.memory ) {
+ memory = false;
+ }
+
+ firing = false;
+
+ // Clean up if we're done firing for good
+ if ( locked ) {
+
+ // Keep an empty list if we have data for future add calls
+ if ( memory ) {
+ list = [];
+
+ // Otherwise, this object is spent
+ } else {
+ list = "";
+ }
+ }
+ },
+
+ // Actual Callbacks object
+ self = {
+
+ // Add a callback or a collection of callbacks to the list
+ add: function() {
+ if ( list ) {
+
+ // If we have memory from a past run, we should fire after adding
+ if ( memory && !firing ) {
+ firingIndex = list.length - 1;
+ queue.push( memory );
+ }
+
+ ( function add( args ) {
+ jQuery.each( args, function( _, arg ) {
+ if ( jQuery.isFunction( arg ) ) {
+ if ( !options.unique || !self.has( arg ) ) {
+ list.push( arg );
+ }
+ } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) {
+
+ // Inspect recursively
+ add( arg );
+ }
+ } );
+ } )( arguments );
+
+ if ( memory && !firing ) {
+ fire();
+ }
+ }
+ return this;
+ },
+
+ // Remove a callback from the list
+ remove: function() {
+ jQuery.each( arguments, function( _, arg ) {
+ var index;
+ while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
+ list.splice( index, 1 );
+
+ // Handle firing indexes
+ if ( index <= firingIndex ) {
+ firingIndex--;
+ }
+ }
+ } );
+ return this;
+ },
+
+ // Check if a given callback is in the list.
+ // If no argument is given, return whether or not list has callbacks attached.
+ has: function( fn ) {
+ return fn ?
+ jQuery.inArray( fn, list ) > -1 :
+ list.length > 0;
+ },
+
+ // Remove all callbacks from the list
+ empty: function() {
+ if ( list ) {
+ list = [];
+ }
+ return this;
+ },
+
+ // Disable .fire and .add
+ // Abort any current/pending executions
+ // Clear all callbacks and values
+ disable: function() {
+ locked = queue = [];
+ list = memory = "";
+ return this;
+ },
+ disabled: function() {
+ return !list;
+ },
+
+ // Disable .fire
+ // Also disable .add unless we have memory (since it would have no effect)
+ // Abort any pending executions
+ lock: function() {
+ locked = true;
+ if ( !memory ) {
+ self.disable();
+ }
+ return this;
+ },
+ locked: function() {
+ return !!locked;
+ },
+
+ // Call all callbacks with the given context and arguments
+ fireWith: function( context, args ) {
+ if ( !locked ) {
+ args = args || [];
+ args = [ context, args.slice ? args.slice() : args ];
+ queue.push( args );
+ if ( !firing ) {
+ fire();
+ }
+ }
+ return this;
+ },
+
+ // Call all the callbacks with the given arguments
+ fire: function() {
+ self.fireWith( this, arguments );
+ return this;
+ },
+
+ // To know if the callbacks have already been called at least once
+ fired: function() {
+ return !!fired;
+ }
+ };
+
+ return self;
+ };
+
+
+ jQuery.extend( {
+
+ Deferred: function( func ) {
+ var tuples = [
+
+ // action, add listener, listener list, final state
+ [ "resolve", "done", jQuery.Callbacks( "once memory" ), "resolved" ],
+ [ "reject", "fail", jQuery.Callbacks( "once memory" ), "rejected" ],
+ [ "notify", "progress", jQuery.Callbacks( "memory" ) ]
+ ],
+ state = "pending",
+ promise = {
+ state: function() {
+ return state;
+ },
+ always: function() {
+ deferred.done( arguments ).fail( arguments );
+ return this;
+ },
+ then: function( /* fnDone, fnFail, fnProgress */ ) {
+ var fns = arguments;
+ return jQuery.Deferred( function( newDefer ) {
+ jQuery.each( tuples, function( i, tuple ) {
+ var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
+
+ // deferred[ done | fail | progress ] for forwarding actions to newDefer
+ deferred[ tuple[ 1 ] ]( function() {
+ var returned = fn && fn.apply( this, arguments );
+ if ( returned && jQuery.isFunction( returned.promise ) ) {
+ returned.promise()
+ .progress( newDefer.notify )
+ .done( newDefer.resolve )
+ .fail( newDefer.reject );
+ } else {
+ newDefer[ tuple[ 0 ] + "With" ](
+ this === promise ? newDefer.promise() : this,
+ fn ? [ returned ] : arguments
+ );
+ }
+ } );
+ } );
+ fns = null;
+ } ).promise();
+ },
+
+ // Get a promise for this deferred
+ // If obj is provided, the promise aspect is added to the object
+ promise: function( obj ) {
+ return obj != null ? jQuery.extend( obj, promise ) : promise;
+ }
+ },
+ deferred = {};
+
+ // Keep pipe for back-compat
+ promise.pipe = promise.then;
+
+ // Add list-specific methods
+ jQuery.each( tuples, function( i, tuple ) {
+ var list = tuple[ 2 ],
+ stateString = tuple[ 3 ];
+
+ // promise[ done | fail | progress ] = list.add
+ promise[ tuple[ 1 ] ] = list.add;
+
+ // Handle state
+ if ( stateString ) {
+ list.add( function() {
+
+ // state = [ resolved | rejected ]
+ state = stateString;
+
+ // [ reject_list | resolve_list ].disable; progress_list.lock
+ }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
+ }
+
+ // deferred[ resolve | reject | notify ]
+ deferred[ tuple[ 0 ] ] = function() {
+ deferred[ tuple[ 0 ] + "With" ]( this === deferred ? promise : this, arguments );
+ return this;
+ };
+ deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
+ } );
+
+ // Make the deferred a promise
+ promise.promise( deferred );
+
+ // Call given func if any
+ if ( func ) {
+ func.call( deferred, deferred );
+ }
+
+ // All done!
+ return deferred;
+ },
+
+ // Deferred helper
+ when: function( subordinate /* , ..., subordinateN */ ) {
+ var i = 0,
+ resolveValues = slice.call( arguments ),
+ length = resolveValues.length,
+
+ // the count of uncompleted subordinates
+ remaining = length !== 1 ||
+ ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
+
+ // the master Deferred.
+ // If resolveValues consist of only a single Deferred, just use that.
+ deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
+
+ // Update function for both resolve and progress values
+ updateFunc = function( i, contexts, values ) {
+ return function( value ) {
+ contexts[ i ] = this;
+ values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
+ if ( values === progressValues ) {
+ deferred.notifyWith( contexts, values );
+
+ } else if ( !( --remaining ) ) {
+ deferred.resolveWith( contexts, values );
+ }
+ };
+ },
+
+ progressValues, progressContexts, resolveContexts;
+
+ // add listeners to Deferred subordinates; treat others as resolved
+ if ( length > 1 ) {
+ progressValues = new Array( length );
+ progressContexts = new Array( length );
+ resolveContexts = new Array( length );
+ for ( ; i < length; i++ ) {
+ if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
+ resolveValues[ i ].promise()
+ .progress( updateFunc( i, progressContexts, progressValues ) )
+ .done( updateFunc( i, resolveContexts, resolveValues ) )
+ .fail( deferred.reject );
+ } else {
+ --remaining;
+ }
+ }
+ }
+
+ // if we're not waiting on anything, resolve the master
+ if ( !remaining ) {
+ deferred.resolveWith( resolveContexts, resolveValues );
+ }
+
+ return deferred.promise();
+ }
+ } );
+
+
+// The deferred used on DOM ready
+ var readyList;
+
+ jQuery.fn.ready = function( fn ) {
+
+ // Add the callback
+ jQuery.ready.promise().done( fn );
+
+ return this;
+ };
+
+ jQuery.extend( {
+
+ // Is the DOM ready to be used? Set to true once it occurs.
+ isReady: false,
+
+ // A counter to track how many items to wait for before
+ // the ready event fires. See #6781
+ readyWait: 1,
+
+ // Hold (or release) the ready event
+ holdReady: function( hold ) {
+ if ( hold ) {
+ jQuery.readyWait++;
+ } else {
+ jQuery.ready( true );
+ }
+ },
+
+ // Handle when the DOM is ready
+ ready: function( wait ) {
+
+ // Abort if there are pending holds or we're already ready
+ if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
+ return;
+ }
+
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If a normal DOM Ready event fired, decrement, and wait if need be
+ if ( wait !== true && --jQuery.readyWait > 0 ) {
+ return;
+ }
+
+ // If there are functions bound, to execute
+ readyList.resolveWith( document, [ jQuery ] );
+
+ // Trigger any bound ready events
+ if ( jQuery.fn.triggerHandler ) {
+ jQuery( document ).triggerHandler( "ready" );
+ jQuery( document ).off( "ready" );
+ }
+ }
+ } );
+
+ /**
+ * Clean-up method for dom ready events
+ */
+ function detach() {
+ if ( document.addEventListener ) {
+ document.removeEventListener( "DOMContentLoaded", completed );
+ window.removeEventListener( "load", completed );
+
+ } else {
+ document.detachEvent( "onreadystatechange", completed );
+ window.detachEvent( "onload", completed );
+ }
+ }
+
+ /**
+ * The ready event handler and self cleanup method
+ */
+ function completed() {
+
+ // readyState === "complete" is good enough for us to call the dom ready in oldIE
+ if ( document.addEventListener ||
+ window.event.type === "load" ||
+ document.readyState === "complete" ) {
+
+ detach();
+ jQuery.ready();
+ }
+ }
+
+ jQuery.ready.promise = function( obj ) {
+ if ( !readyList ) {
+
+ readyList = jQuery.Deferred();
+
+ // Catch cases where $(document).ready() is called
+ // after the browser event has already occurred.
+ // Support: IE6-10
+ // Older IE sometimes signals "interactive" too soon
+ if ( document.readyState === "complete" ||
+ ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
+
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
+ window.setTimeout( jQuery.ready );
+
+ // Standards-based browsers support DOMContentLoaded
+ } else if ( document.addEventListener ) {
+
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", completed );
+
+ // A fallback to window.onload, that will always work
+ window.addEventListener( "load", completed );
+
+ // If IE event model is used
+ } else {
+
+ // Ensure firing before onload, maybe late but safe also for iframes
+ document.attachEvent( "onreadystatechange", completed );
+
+ // A fallback to window.onload, that will always work
+ window.attachEvent( "onload", completed );
+
+ // If IE and not a frame
+ // continually check to see if the document is ready
+ var top = false;
+
+ try {
+ top = window.frameElement == null && document.documentElement;
+ } catch ( e ) {}
+
+ if ( top && top.doScroll ) {
+ ( function doScrollCheck() {
+ if ( !jQuery.isReady ) {
+
+ try {
+
+ // Use the trick by Diego Perini
+ // http://javascript.nwbox.com/IEContentLoaded/
+ top.doScroll( "left" );
+ } catch ( e ) {
+ return window.setTimeout( doScrollCheck, 50 );
+ }
+
+ // detach all dom ready events
+ detach();
+
+ // and execute any waiting functions
+ jQuery.ready();
+ }
+ } )();
+ }
+ }
+ }
+ return readyList.promise( obj );
+ };
+
+// Kick off the DOM ready check even if the user does not
+ jQuery.ready.promise();
+
+
+
+
+// Support: IE<9
+// Iteration over object's inherited properties before its own
+ var i;
+ for ( i in jQuery( support ) ) {
+ break;
+ }
+ support.ownFirst = i === "0";
+
+// Note: most support tests are defined in their respective modules.
+// false until the test is run
+ support.inlineBlockNeedsLayout = false;
+
+// Execute ASAP in case we need to set body.style.zoom
+ jQuery( function() {
+
+ // Minified: var a,b,c,d
+ var val, div, body, container;
+
+ body = document.getElementsByTagName( "body" )[ 0 ];
+ if ( !body || !body.style ) {
+
+ // Return for frameset docs that don't have a body
+ return;
+ }
+
+ // Setup
+ div = document.createElement( "div" );
+ container = document.createElement( "div" );
+ container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px";
+ body.appendChild( container ).appendChild( div );
+
+ if ( typeof div.style.zoom !== "undefined" ) {
+
+ // Support: IE<8
+ // Check if natively block-level elements act like inline-block
+ // elements when setting their display to 'inline' and giving
+ // them layout
+ div.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1";
+
+ support.inlineBlockNeedsLayout = val = div.offsetWidth === 3;
+ if ( val ) {
+
+ // Prevent IE 6 from affecting layout for positioned elements #11048
+ // Prevent IE from shrinking the body in IE 7 mode #12869
+ // Support: IE<8
+ body.style.zoom = 1;
+ }
+ }
+
+ body.removeChild( container );
+ } );
+
+
+ ( function() {
+ var div = document.createElement( "div" );
+
+ // Support: IE<9
+ support.deleteExpando = true;
+ try {
+ delete div.test;
+ } catch ( e ) {
+ support.deleteExpando = false;
+ }
+
+ // Null elements to avoid leaks in IE.
+ div = null;
+ } )();
+ var acceptData = function( elem ) {
+ var noData = jQuery.noData[ ( elem.nodeName + " " ).toLowerCase() ],
+ nodeType = +elem.nodeType || 1;
+
+ // Do not set data on non-element DOM nodes because it will not be cleared (#8335).
+ return nodeType !== 1 && nodeType !== 9 ?
+ false :
+
+ // Nodes accept data unless otherwise specified; rejection can be conditional
+ !noData || noData !== true && elem.getAttribute( "classid" ) === noData;
+ };
+
+
+
+
+ var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
+ rmultiDash = /([A-Z])/g;
+
+ function dataAttr( elem, key, data ) {
+
+ // If nothing was found internally, try to fetch any
+ // data from the HTML5 data-* attribute
+ if ( data === undefined && elem.nodeType === 1 ) {
+
+ var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
+
+ data = elem.getAttribute( name );
+
+ if ( typeof data === "string" ) {
+ try {
+ data = data === "true" ? true :
+ data === "false" ? false :
+ data === "null" ? null :
+
+ // Only convert to a number if it doesn't change the string
+ +data + "" === data ? +data :
+ rbrace.test( data ) ? jQuery.parseJSON( data ) :
+ data;
+ } catch ( e ) {}
+
+ // Make sure we set the data so it isn't changed later
+ jQuery.data( elem, key, data );
+
+ } else {
+ data = undefined;
+ }
+ }
+
+ return data;
+ }
+
+// checks a cache object for emptiness
+ function isEmptyDataObject( obj ) {
+ var name;
+ for ( name in obj ) {
+
+ // if the public data object is empty, the private is still empty
+ if ( name === "data" && jQuery.isEmptyObject( obj[ name ] ) ) {
+ continue;
+ }
+ if ( name !== "toJSON" ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ function internalData( elem, name, data, pvt /* Internal Use Only */ ) {
+ if ( !acceptData( elem ) ) {
+ return;
+ }
+
+ var ret, thisCache,
+ internalKey = jQuery.expando,
+
+ // We have to handle DOM nodes and JS objects differently because IE6-7
+ // can't GC object references properly across the DOM-JS boundary
+ isNode = elem.nodeType,
+
+ // Only DOM nodes need the global jQuery cache; JS object data is
+ // attached directly to the object so GC can occur automatically
+ cache = isNode ? jQuery.cache : elem,
+
+ // Only defining an ID for JS objects if its cache already exists allows
+ // the code to shortcut on the same path as a DOM node with no cache
+ id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
+
+ // Avoid doing any more work than we need to when trying to get data on an
+ // object that has no data at all
+ if ( ( !id || !cache[ id ] || ( !pvt && !cache[ id ].data ) ) &&
+ data === undefined && typeof name === "string" ) {
+ return;
+ }
+
+ if ( !id ) {
+
+ // Only DOM nodes need a new unique ID for each element since their data
+ // ends up in the global cache
+ if ( isNode ) {
+ id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++;
+ } else {
+ id = internalKey;
+ }
+ }
+
+ if ( !cache[ id ] ) {
+
+ // Avoid exposing jQuery metadata on plain JS objects when the object
+ // is serialized using JSON.stringify
+ cache[ id ] = isNode ? {} : { toJSON: jQuery.noop };
+ }
+
+ // An object can be passed to jQuery.data instead of a key/value pair; this gets
+ // shallow copied over onto the existing cache
+ if ( typeof name === "object" || typeof name === "function" ) {
+ if ( pvt ) {
+ cache[ id ] = jQuery.extend( cache[ id ], name );
+ } else {
+ cache[ id ].data = jQuery.extend( cache[ id ].data, name );
+ }
+ }
+
+ thisCache = cache[ id ];
+
+ // jQuery data() is stored in a separate object inside the object's internal data
+ // cache in order to avoid key collisions between internal data and user-defined
+ // data.
+ if ( !pvt ) {
+ if ( !thisCache.data ) {
+ thisCache.data = {};
+ }
+
+ thisCache = thisCache.data;
+ }
+
+ if ( data !== undefined ) {
+ thisCache[ jQuery.camelCase( name ) ] = data;
+ }
+
+ // Check for both converted-to-camel and non-converted data property names
+ // If a data property was specified
+ if ( typeof name === "string" ) {
+
+ // First Try to find as-is property data
+ ret = thisCache[ name ];
+
+ // Test for null|undefined property data
+ if ( ret == null ) {
+
+ // Try to find the camelCased property
+ ret = thisCache[ jQuery.camelCase( name ) ];
+ }
+ } else {
+ ret = thisCache;
+ }
+
+ return ret;
+ }
+
+ function internalRemoveData( elem, name, pvt ) {
+ if ( !acceptData( elem ) ) {
+ return;
+ }
+
+ var thisCache, i,
+ isNode = elem.nodeType,
+
+ // See jQuery.data for more information
+ cache = isNode ? jQuery.cache : elem,
+ id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
+
+ // If there is already no cache entry for this object, there is no
+ // purpose in continuing
+ if ( !cache[ id ] ) {
+ return;
+ }
+
+ if ( name ) {
+
+ thisCache = pvt ? cache[ id ] : cache[ id ].data;
+
+ if ( thisCache ) {
+
+ // Support array or space separated string names for data keys
+ if ( !jQuery.isArray( name ) ) {
+
+ // try the string as a key before any manipulation
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+
+ // split the camel cased version by spaces unless a key with the spaces exists
+ name = jQuery.camelCase( name );
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+ name = name.split( " " );
+ }
+ }
+ } else {
+
+ // If "name" is an array of keys...
+ // When data is initially created, via ("key", "val") signature,
+ // keys will be converted to camelCase.
+ // Since there is no way to tell _how_ a key was added, remove
+ // both plain key and camelCase key. #12786
+ // This will only penalize the array argument path.
+ name = name.concat( jQuery.map( name, jQuery.camelCase ) );
+ }
+
+ i = name.length;
+ while ( i-- ) {
+ delete thisCache[ name[ i ] ];
+ }
+
+ // If there is no data left in the cache, we want to continue
+ // and let the cache object itself get destroyed
+ if ( pvt ? !isEmptyDataObject( thisCache ) : !jQuery.isEmptyObject( thisCache ) ) {
+ return;
+ }
+ }
+ }
+
+ // See jQuery.data for more information
+ if ( !pvt ) {
+ delete cache[ id ].data;
+
+ // Don't destroy the parent cache unless the internal data object
+ // had been the only thing left in it
+ if ( !isEmptyDataObject( cache[ id ] ) ) {
+ return;
+ }
+ }
+
+ // Destroy the cache
+ if ( isNode ) {
+ jQuery.cleanData( [ elem ], true );
+
+ // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
+ /* jshint eqeqeq: false */
+ } else if ( support.deleteExpando || cache != cache.window ) {
+ /* jshint eqeqeq: true */
+ delete cache[ id ];
+
+ // When all else fails, undefined
+ } else {
+ cache[ id ] = undefined;
+ }
+ }
+
+ jQuery.extend( {
+ cache: {},
+
+ // The following elements (space-suffixed to avoid Object.prototype collisions)
+ // throw uncatchable exceptions if you attempt to set expando properties
+ noData: {
+ "applet ": true,
+ "embed ": true,
+
+ // ...but Flash objects (which have this classid) *can* handle expandos
+ "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
+ },
+
+ hasData: function( elem ) {
+ elem = elem.nodeType ? jQuery.cache[ elem[ jQuery.expando ] ] : elem[ jQuery.expando ];
+ return !!elem && !isEmptyDataObject( elem );
+ },
+
+ data: function( elem, name, data ) {
+ return internalData( elem, name, data );
+ },
+
+ removeData: function( elem, name ) {
+ return internalRemoveData( elem, name );
+ },
+
+ // For internal use only.
+ _data: function( elem, name, data ) {
+ return internalData( elem, name, data, true );
+ },
+
+ _removeData: function( elem, name ) {
+ return internalRemoveData( elem, name, true );
+ }
+ } );
+
+ jQuery.fn.extend( {
+ data: function( key, value ) {
+ var i, name, data,
+ elem = this[ 0 ],
+ attrs = elem && elem.attributes;
+
+ // Special expections of .data basically thwart jQuery.access,
+ // so implement the relevant behavior ourselves
+
+ // Gets all values
+ if ( key === undefined ) {
+ if ( this.length ) {
+ data = jQuery.data( elem );
+
+ if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
+ i = attrs.length;
+ while ( i-- ) {
+
+ // Support: IE11+
+ // The attrs elements can be null (#14894)
+ if ( attrs[ i ] ) {
+ name = attrs[ i ].name;
+ if ( name.indexOf( "data-" ) === 0 ) {
+ name = jQuery.camelCase( name.slice( 5 ) );
+ dataAttr( elem, name, data[ name ] );
+ }
+ }
+ }
+ jQuery._data( elem, "parsedAttrs", true );
+ }
+ }
+
+ return data;
+ }
+
+ // Sets multiple values
+ if ( typeof key === "object" ) {
+ return this.each( function() {
+ jQuery.data( this, key );
+ } );
+ }
+
+ return arguments.length > 1 ?
+
+ // Sets one value
+ this.each( function() {
+ jQuery.data( this, key, value );
+ } ) :
+
+ // Gets one value
+ // Try to fetch any internally stored data first
+ elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined;
+ },
+
+ removeData: function( key ) {
+ return this.each( function() {
+ jQuery.removeData( this, key );
+ } );
+ }
+ } );
+
+
+ jQuery.extend( {
+ queue: function( elem, type, data ) {
+ var queue;
+
+ if ( elem ) {
+ type = ( type || "fx" ) + "queue";
+ queue = jQuery._data( elem, type );
+
+ // Speed up dequeue by getting out quickly if this is just a lookup
+ if ( data ) {
+ if ( !queue || jQuery.isArray( data ) ) {
+ queue = jQuery._data( elem, type, jQuery.makeArray( data ) );
+ } else {
+ queue.push( data );
+ }
+ }
+ return queue || [];
+ }
+ },
+
+ dequeue: function( elem, type ) {
+ type = type || "fx";
+
+ var queue = jQuery.queue( elem, type ),
+ startLength = queue.length,
+ fn = queue.shift(),
+ hooks = jQuery._queueHooks( elem, type ),
+ next = function() {
+ jQuery.dequeue( elem, type );
+ };
+
+ // If the fx queue is dequeued, always remove the progress sentinel
+ if ( fn === "inprogress" ) {
+ fn = queue.shift();
+ startLength--;
+ }
+
+ if ( fn ) {
+
+ // Add a progress sentinel to prevent the fx queue from being
+ // automatically dequeued
+ if ( type === "fx" ) {
+ queue.unshift( "inprogress" );
+ }
+
+ // clear up the last queue stop function
+ delete hooks.stop;
+ fn.call( elem, next, hooks );
+ }
+
+ if ( !startLength && hooks ) {
+ hooks.empty.fire();
+ }
+ },
+
+ // not intended for public consumption - generates a queueHooks object,
+ // or returns the current one
+ _queueHooks: function( elem, type ) {
+ var key = type + "queueHooks";
+ return jQuery._data( elem, key ) || jQuery._data( elem, key, {
+ empty: jQuery.Callbacks( "once memory" ).add( function() {
+ jQuery._removeData( elem, type + "queue" );
+ jQuery._removeData( elem, key );
+ } )
+ } );
+ }
+ } );
+
+ jQuery.fn.extend( {
+ queue: function( type, data ) {
+ var setter = 2;
+
+ if ( typeof type !== "string" ) {
+ data = type;
+ type = "fx";
+ setter--;
+ }
+
+ if ( arguments.length < setter ) {
+ return jQuery.queue( this[ 0 ], type );
+ }
+
+ return data === undefined ?
+ this :
+ this.each( function() {
+ var queue = jQuery.queue( this, type, data );
+
+ // ensure a hooks for this queue
+ jQuery._queueHooks( this, type );
+
+ if ( type === "fx" && queue[ 0 ] !== "inprogress" ) {
+ jQuery.dequeue( this, type );
+ }
+ } );
+ },
+ dequeue: function( type ) {
+ return this.each( function() {
+ jQuery.dequeue( this, type );
+ } );
+ },
+ clearQueue: function( type ) {
+ return this.queue( type || "fx", [] );
+ },
+
+ // Get a promise resolved when queues of a certain type
+ // are emptied (fx is the type by default)
+ promise: function( type, obj ) {
+ var tmp,
+ count = 1,
+ defer = jQuery.Deferred(),
+ elements = this,
+ i = this.length,
+ resolve = function() {
+ if ( !( --count ) ) {
+ defer.resolveWith( elements, [ elements ] );
+ }
+ };
+
+ if ( typeof type !== "string" ) {
+ obj = type;
+ type = undefined;
+ }
+ type = type || "fx";
+
+ while ( i-- ) {
+ tmp = jQuery._data( elements[ i ], type + "queueHooks" );
+ if ( tmp && tmp.empty ) {
+ count++;
+ tmp.empty.add( resolve );
+ }
+ }
+ resolve();
+ return defer.promise( obj );
+ }
+ } );
+
+
+ ( function() {
+ var shrinkWrapBlocksVal;
+
+ support.shrinkWrapBlocks = function() {
+ if ( shrinkWrapBlocksVal != null ) {
+ return shrinkWrapBlocksVal;
+ }
+
+ // Will be changed later if needed.
+ shrinkWrapBlocksVal = false;
+
+ // Minified: var b,c,d
+ var div, body, container;
+
+ body = document.getElementsByTagName( "body" )[ 0 ];
+ if ( !body || !body.style ) {
+
+ // Test fired too early or in an unsupported environment, exit.
+ return;
+ }
+
+ // Setup
+ div = document.createElement( "div" );
+ container = document.createElement( "div" );
+ container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px";
+ body.appendChild( container ).appendChild( div );
+
+ // Support: IE6
+ // Check if elements with layout shrink-wrap their children
+ if ( typeof div.style.zoom !== "undefined" ) {
+
+ // Reset CSS: box-sizing; display; margin; border
+ div.style.cssText =
+
+ // Support: Firefox<29, Android 2.3
+ // Vendor-prefix box-sizing
+ "-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" +
+ "box-sizing:content-box;display:block;margin:0;border:0;" +
+ "padding:1px;width:1px;zoom:1";
+ div.appendChild( document.createElement( "div" ) ).style.width = "5px";
+ shrinkWrapBlocksVal = div.offsetWidth !== 3;
+ }
+
+ body.removeChild( container );
+
+ return shrinkWrapBlocksVal;
+ };
+
+ } )();
+ var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source;
+
+ var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );
+
+
+ var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
+
+ var isHidden = function( elem, el ) {
+
+ // isHidden might be called from jQuery#filter function;
+ // in that case, element will be second argument
+ elem = el || elem;
+ return jQuery.css( elem, "display" ) === "none" ||
+ !jQuery.contains( elem.ownerDocument, elem );
+ };
+
+
+
+ function adjustCSS( elem, prop, valueParts, tween ) {
+ var adjusted,
+ scale = 1,
+ maxIterations = 20,
+ currentValue = tween ?
+ function() { return tween.cur(); } :
+ function() { return jQuery.css( elem, prop, "" ); },
+ initial = currentValue(),
+ unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
+
+ // Starting value computation is required for potential unit mismatches
+ initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) &&
+ rcssNum.exec( jQuery.css( elem, prop ) );
+
+ if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {
+
+ // Trust units reported by jQuery.css
+ unit = unit || initialInUnit[ 3 ];
+
+ // Make sure we update the tween properties later on
+ valueParts = valueParts || [];
+
+ // Iteratively approximate from a nonzero starting point
+ initialInUnit = +initial || 1;
+
+ do {
+
+ // If previous iteration zeroed out, double until we get *something*.
+ // Use string for doubling so we don't accidentally see scale as unchanged below
+ scale = scale || ".5";
+
+ // Adjust and apply
+ initialInUnit = initialInUnit / scale;
+ jQuery.style( elem, prop, initialInUnit + unit );
+
+ // Update scale, tolerating zero or NaN from tween.cur()
+ // Break the loop if scale is unchanged or perfect, or if we've just had enough.
+ } while (
+ scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations
+ );
+ }
+
+ if ( valueParts ) {
+ initialInUnit = +initialInUnit || +initial || 0;
+
+ // Apply relative offset (+=/-=) if specified
+ adjusted = valueParts[ 1 ] ?
+ initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :
+ +valueParts[ 2 ];
+ if ( tween ) {
+ tween.unit = unit;
+ tween.start = initialInUnit;
+ tween.end = adjusted;
+ }
+ }
+ return adjusted;
+ }
+
+
+// Multifunctional method to get and set values of a collection
+// The value/s can optionally be executed if it's a function
+ var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
+ var i = 0,
+ length = elems.length,
+ bulk = key == null;
+
+ // Sets many values
+ if ( jQuery.type( key ) === "object" ) {
+ chainable = true;
+ for ( i in key ) {
+ access( elems, fn, i, key[ i ], true, emptyGet, raw );
+ }
+
+ // Sets one value
+ } else if ( value !== undefined ) {
+ chainable = true;
+
+ if ( !jQuery.isFunction( value ) ) {
+ raw = true;
+ }
+
+ if ( bulk ) {
+
+ // Bulk operations run against the entire set
+ if ( raw ) {
+ fn.call( elems, value );
+ fn = null;
+
+ // ...except when executing function values
+ } else {
+ bulk = fn;
+ fn = function( elem, key, value ) {
+ return bulk.call( jQuery( elem ), value );
+ };
+ }
+ }
+
+ if ( fn ) {
+ for ( ; i < length; i++ ) {
+ fn(
+ elems[ i ],
+ key,
+ raw ? value : value.call( elems[ i ], i, fn( elems[ i ], key ) )
+ );
+ }
+ }
+ }
+
+ return chainable ?
+ elems :
+
+ // Gets
+ bulk ?
+ fn.call( elems ) :
+ length ? fn( elems[ 0 ], key ) : emptyGet;
+ };
+ var rcheckableType = ( /^(?:checkbox|radio)$/i );
+
+ var rtagName = ( /<([\w:-]+)/ );
+
+ var rscriptType = ( /^$|\/(?:java|ecma)script/i );
+
+ var rleadingWhitespace = ( /^\s+/ );
+
+ var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|" +
+ "details|dialog|figcaption|figure|footer|header|hgroup|main|" +
+ "mark|meter|nav|output|picture|progress|section|summary|template|time|video";
+
+
+
+ function createSafeFragment( document ) {
+ var list = nodeNames.split( "|" ),
+ safeFrag = document.createDocumentFragment();
+
+ if ( safeFrag.createElement ) {
+ while ( list.length ) {
+ safeFrag.createElement(
+ list.pop()
+ );
+ }
+ }
+ return safeFrag;
+ }
+
+
+ ( function() {
+ var div = document.createElement( "div" ),
+ fragment = document.createDocumentFragment(),
+ input = document.createElement( "input" );
+
+ // Setup
+ div.innerHTML = " a ";
+
+ // IE strips leading whitespace when .innerHTML is used
+ support.leadingWhitespace = div.firstChild.nodeType === 3;
+
+ // Make sure that tbody elements aren't automatically inserted
+ // IE will insert them into empty tables
+ support.tbody = !div.getElementsByTagName( "tbody" ).length;
+
+ // Make sure that link elements get serialized correctly by innerHTML
+ // This requires a wrapper element in IE
+ support.htmlSerialize = !!div.getElementsByTagName( "link" ).length;
+
+ // Makes sure cloning an html5 element does not cause problems
+ // Where outerHTML is undefined, this still works
+ support.html5Clone =
+ document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav>";
+
+ // Check if a disconnected checkbox will retain its checked
+ // value of true after appended to the DOM (IE6/7)
+ input.type = "checkbox";
+ input.checked = true;
+ fragment.appendChild( input );
+ support.appendChecked = input.checked;
+
+ // Make sure textarea (and checkbox) defaultValue is properly cloned
+ // Support: IE6-IE11+
+ div.innerHTML = "";
+ support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
+
+ // #11217 - WebKit loses check when the name is after the checked attribute
+ fragment.appendChild( div );
+
+ // Support: Windows Web Apps (WWA)
+ // `name` and `type` must use .setAttribute for WWA (#14901)
+ input = document.createElement( "input" );
+ input.setAttribute( "type", "radio" );
+ input.setAttribute( "checked", "checked" );
+ input.setAttribute( "name", "t" );
+
+ div.appendChild( input );
+
+ // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3
+ // old WebKit doesn't clone checked state correctly in fragments
+ support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+ // Support: IE<9
+ // Cloned elements keep attachEvent handlers, we use addEventListener on IE9+
+ support.noCloneEvent = !!div.addEventListener;
+
+ // Support: IE<9
+ // Since attributes and properties are the same in IE,
+ // cleanData must set properties to undefined rather than use removeAttribute
+ div[ jQuery.expando ] = 1;
+ support.attributes = !div.getAttribute( jQuery.expando );
+
+ // Support: IE <=9 only
+ // IE <=9 replaces tags with their contents when inserted outside of
+ // the select element.
+ div.innerHTML = " ";
+ support.option = !!div.lastChild;
+ } )();
+
+
+// We have to close these tags to support XHTML (#13200)
+ var wrapMap = {
+ // option: [ 1, "", " " ],
+ legend: [ 1, "", " " ],
+ area: [ 1, "", " " ],
+
+ // Support: IE8
+ param: [ 1, "", " " ],
+ thead: [ 1, "" ],
+ tr: [ 2, "" ],
+ col: [ 2, "" ],
+ td: [ 3, "" ],
+
+ // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
+ // unless wrapped in a div with non-breaking characters in front of it.
+ _default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X", "
" ]
+ };
+
+// // Support: IE8-IE9
+// wrapMap.optgroup = wrapMap.option;
+
+ wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+ wrapMap.th = wrapMap.td;
+
+ // Support: IE <=9 only
+ if ( !support.option ) {
+ wrapMap.optgroup = wrapMap.option = [ 1, "", " " ];
+ }
+
+ function getAll( context, tag ) {
+ var elems, elem,
+ i = 0,
+ found = typeof context.getElementsByTagName !== "undefined" ?
+ context.getElementsByTagName( tag || "*" ) :
+ typeof context.querySelectorAll !== "undefined" ?
+ context.querySelectorAll( tag || "*" ) :
+ undefined;
+
+ if ( !found ) {
+ for ( found = [], elems = context.childNodes || context;
+ ( elem = elems[ i ] ) != null;
+ i++
+ ) {
+ if ( !tag || jQuery.nodeName( elem, tag ) ) {
+ found.push( elem );
+ } else {
+ jQuery.merge( found, getAll( elem, tag ) );
+ }
+ }
+ }
+
+ return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
+ jQuery.merge( [ context ], found ) :
+ found;
+ }
+
+
+// Mark scripts as having already been evaluated
+ function setGlobalEval( elems, refElements ) {
+ var elem,
+ i = 0;
+ for ( ; ( elem = elems[ i ] ) != null; i++ ) {
+ jQuery._data(
+ elem,
+ "globalEval",
+ !refElements || jQuery._data( refElements[ i ], "globalEval" )
+ );
+ }
+ }
+
+
+ var rhtml = /<|?\w+;/,
+ rtbody = / from table fragments
+ if ( !support.tbody ) {
+
+ // String was a , *may* have spurious
+ elem = tag === "table" && !rtbody.test( elem ) ?
+ tmp.firstChild :
+
+ // String was a bare or
+ wrap[ 1 ] === "" && !rtbody.test( elem ) ?
+ tmp :
+ 0;
+
+ j = elem && elem.childNodes.length;
+ while ( j-- ) {
+ if ( jQuery.nodeName( ( tbody = elem.childNodes[ j ] ), "tbody" ) &&
+ !tbody.childNodes.length ) {
+
+ elem.removeChild( tbody );
+ }
+ }
+ }
+
+ jQuery.merge( nodes, tmp.childNodes );
+
+ // Fix #12392 for WebKit and IE > 9
+ tmp.textContent = "";
+
+ // Fix #12392 for oldIE
+ while ( tmp.firstChild ) {
+ tmp.removeChild( tmp.firstChild );
+ }
+
+ // Remember the top-level container for proper cleanup
+ tmp = safe.lastChild;
+ }
+ }
+ }
+
+ // Fix #11356: Clear elements from fragment
+ if ( tmp ) {
+ safe.removeChild( tmp );
+ }
+
+ // Reset defaultChecked for any radios and checkboxes
+ // about to be appended to the DOM in IE 6/7 (#8060)
+ if ( !support.appendChecked ) {
+ jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked );
+ }
+
+ i = 0;
+ while ( ( elem = nodes[ i++ ] ) ) {
+
+ // Skip elements already in the context collection (trac-4087)
+ if ( selection && jQuery.inArray( elem, selection ) > -1 ) {
+ if ( ignored ) {
+ ignored.push( elem );
+ }
+
+ continue;
+ }
+
+ contains = jQuery.contains( elem.ownerDocument, elem );
+
+ // Append to fragment
+ tmp = getAll( safe.appendChild( elem ), "script" );
+
+ // Preserve script evaluation history
+ if ( contains ) {
+ setGlobalEval( tmp );
+ }
+
+ // Capture executables
+ if ( scripts ) {
+ j = 0;
+ while ( ( elem = tmp[ j++ ] ) ) {
+ if ( rscriptType.test( elem.type || "" ) ) {
+ scripts.push( elem );
+ }
+ }
+ }
+ }
+
+ tmp = null;
+
+ return safe;
+ }
+
+
+ ( function() {
+ var i, eventName,
+ div = document.createElement( "div" );
+
+ // Support: IE<9 (lack submit/change bubble), Firefox (lack focus(in | out) events)
+ for ( i in { submit: true, change: true, focusin: true } ) {
+ eventName = "on" + i;
+
+ if ( !( support[ i ] = eventName in window ) ) {
+
+ // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP)
+ div.setAttribute( eventName, "t" );
+ support[ i ] = div.attributes[ eventName ].expando === false;
+ }
+ }
+
+ // Null elements to avoid leaks in IE.
+ div = null;
+ } )();
+
+
+ var rformElems = /^(?:input|select|textarea)$/i,
+ rkeyEvent = /^key/,
+ rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,
+ rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+ rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
+
+ function returnTrue() {
+ return true;
+ }
+
+ function returnFalse() {
+ return false;
+ }
+
+// Support: IE9
+// See #13393 for more info
+ function safeActiveElement() {
+ try {
+ return document.activeElement;
+ } catch ( err ) { }
+ }
+
+ function on( elem, types, selector, data, fn, one ) {
+ var origFn, type;
+
+ // Types can be a map of types/handlers
+ if ( typeof types === "object" ) {
+
+ // ( types-Object, selector, data )
+ if ( typeof selector !== "string" ) {
+
+ // ( types-Object, data )
+ data = data || selector;
+ selector = undefined;
+ }
+ for ( type in types ) {
+ on( elem, type, selector, data, types[ type ], one );
+ }
+ return elem;
+ }
+
+ if ( data == null && fn == null ) {
+
+ // ( types, fn )
+ fn = selector;
+ data = selector = undefined;
+ } else if ( fn == null ) {
+ if ( typeof selector === "string" ) {
+
+ // ( types, selector, fn )
+ fn = data;
+ data = undefined;
+ } else {
+
+ // ( types, data, fn )
+ fn = data;
+ data = selector;
+ selector = undefined;
+ }
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ } else if ( !fn ) {
+ return elem;
+ }
+
+ if ( one === 1 ) {
+ origFn = fn;
+ fn = function( event ) {
+
+ // Can use an empty set, since event contains the info
+ jQuery().off( event );
+ return origFn.apply( this, arguments );
+ };
+
+ // Use same guid so caller can remove using origFn
+ fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
+ }
+ return elem.each( function() {
+ jQuery.event.add( this, types, fn, data, selector );
+ } );
+ }
+
+ /*
+ * Helper functions for managing events -- not part of the public interface.
+ * Props to Dean Edwards' addEvent library for many of the ideas.
+ */
+ jQuery.event = {
+
+ global: {},
+
+ add: function( elem, types, handler, data, selector ) {
+ var tmp, events, t, handleObjIn,
+ special, eventHandle, handleObj,
+ handlers, type, namespaces, origType,
+ elemData = jQuery._data( elem );
+
+ // Don't attach events to noData or text/comment nodes (but allow plain objects)
+ if ( !elemData ) {
+ return;
+ }
+
+ // Caller can pass in an object of custom data in lieu of the handler
+ if ( handler.handler ) {
+ handleObjIn = handler;
+ handler = handleObjIn.handler;
+ selector = handleObjIn.selector;
+ }
+
+ // Make sure that the handler has a unique ID, used to find/remove it later
+ if ( !handler.guid ) {
+ handler.guid = jQuery.guid++;
+ }
+
+ // Init the element's event structure and main handler, if this is the first
+ if ( !( events = elemData.events ) ) {
+ events = elemData.events = {};
+ }
+ if ( !( eventHandle = elemData.handle ) ) {
+ eventHandle = elemData.handle = function( e ) {
+
+ // Discard the second event of a jQuery.event.trigger() and
+ // when an event is called after a page has unloaded
+ return typeof jQuery !== "undefined" &&
+ ( !e || jQuery.event.triggered !== e.type ) ?
+ jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
+ undefined;
+ };
+
+ // Add elem as a property of the handle fn to prevent a memory leak
+ // with IE non-native events
+ eventHandle.elem = elem;
+ }
+
+ // Handle multiple events separated by a space
+ types = ( types || "" ).match( rnotwhite ) || [ "" ];
+ t = types.length;
+ while ( t-- ) {
+ tmp = rtypenamespace.exec( types[ t ] ) || [];
+ type = origType = tmp[ 1 ];
+ namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
+
+ // There *must* be a type, no attaching namespace-only handlers
+ if ( !type ) {
+ continue;
+ }
+
+ // If event changes its type, use the special event handlers for the changed type
+ special = jQuery.event.special[ type ] || {};
+
+ // If selector defined, determine special event api type, otherwise given type
+ type = ( selector ? special.delegateType : special.bindType ) || type;
+
+ // Update special based on newly reset type
+ special = jQuery.event.special[ type ] || {};
+
+ // handleObj is passed to all event handlers
+ handleObj = jQuery.extend( {
+ type: type,
+ origType: origType,
+ data: data,
+ handler: handler,
+ guid: handler.guid,
+ selector: selector,
+ needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
+ namespace: namespaces.join( "." )
+ }, handleObjIn );
+
+ // Init the event handler queue if we're the first
+ if ( !( handlers = events[ type ] ) ) {
+ handlers = events[ type ] = [];
+ handlers.delegateCount = 0;
+
+ // Only use addEventListener/attachEvent if the special events handler returns false
+ if ( !special.setup ||
+ special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+
+ // Bind the global event handler to the element
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, eventHandle, false );
+
+ } else if ( elem.attachEvent ) {
+ elem.attachEvent( "on" + type, eventHandle );
+ }
+ }
+ }
+
+ if ( special.add ) {
+ special.add.call( elem, handleObj );
+
+ if ( !handleObj.handler.guid ) {
+ handleObj.handler.guid = handler.guid;
+ }
+ }
+
+ // Add to the element's handler list, delegates in front
+ if ( selector ) {
+ handlers.splice( handlers.delegateCount++, 0, handleObj );
+ } else {
+ handlers.push( handleObj );
+ }
+
+ // Keep track of which events have ever been used, for event optimization
+ jQuery.event.global[ type ] = true;
+ }
+
+ // Nullify elem to prevent memory leaks in IE
+ elem = null;
+ },
+
+ // Detach an event or set of events from an element
+ remove: function( elem, types, handler, selector, mappedTypes ) {
+ var j, handleObj, tmp,
+ origCount, t, events,
+ special, handlers, type,
+ namespaces, origType,
+ elemData = jQuery.hasData( elem ) && jQuery._data( elem );
+
+ if ( !elemData || !( events = elemData.events ) ) {
+ return;
+ }
+
+ // Once for each type.namespace in types; type may be omitted
+ types = ( types || "" ).match( rnotwhite ) || [ "" ];
+ t = types.length;
+ while ( t-- ) {
+ tmp = rtypenamespace.exec( types[ t ] ) || [];
+ type = origType = tmp[ 1 ];
+ namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
+
+ // Unbind all events (on this namespace, if provided) for the element
+ if ( !type ) {
+ for ( type in events ) {
+ jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
+ }
+ continue;
+ }
+
+ special = jQuery.event.special[ type ] || {};
+ type = ( selector ? special.delegateType : special.bindType ) || type;
+ handlers = events[ type ] || [];
+ tmp = tmp[ 2 ] &&
+ new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" );
+
+ // Remove matching events
+ origCount = j = handlers.length;
+ while ( j-- ) {
+ handleObj = handlers[ j ];
+
+ if ( ( mappedTypes || origType === handleObj.origType ) &&
+ ( !handler || handler.guid === handleObj.guid ) &&
+ ( !tmp || tmp.test( handleObj.namespace ) ) &&
+ ( !selector || selector === handleObj.selector ||
+ selector === "**" && handleObj.selector ) ) {
+ handlers.splice( j, 1 );
+
+ if ( handleObj.selector ) {
+ handlers.delegateCount--;
+ }
+ if ( special.remove ) {
+ special.remove.call( elem, handleObj );
+ }
+ }
+ }
+
+ // Remove generic event handler if we removed something and no more handlers exist
+ // (avoids potential for endless recursion during removal of special event handlers)
+ if ( origCount && !handlers.length ) {
+ if ( !special.teardown ||
+ special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
+
+ jQuery.removeEvent( elem, type, elemData.handle );
+ }
+
+ delete events[ type ];
+ }
+ }
+
+ // Remove the expando if it's no longer used
+ if ( jQuery.isEmptyObject( events ) ) {
+ delete elemData.handle;
+
+ // removeData also checks for emptiness and clears the expando if empty
+ // so use it instead of delete
+ jQuery._removeData( elem, "events" );
+ }
+ },
+
+ trigger: function( event, data, elem, onlyHandlers ) {
+ var handle, ontype, cur,
+ bubbleType, special, tmp, i,
+ eventPath = [ elem || document ],
+ type = hasOwn.call( event, "type" ) ? event.type : event,
+ namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : [];
+
+ cur = tmp = elem = elem || document;
+
+ // Don't do events on text and comment nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return;
+ }
+
+ // focus/blur morphs to focusin/out; ensure we're not firing them right now
+ if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
+ return;
+ }
+
+ if ( type.indexOf( "." ) > -1 ) {
+
+ // Namespaced trigger; create a regexp to match event type in handle()
+ namespaces = type.split( "." );
+ type = namespaces.shift();
+ namespaces.sort();
+ }
+ ontype = type.indexOf( ":" ) < 0 && "on" + type;
+
+ // Caller can pass in a jQuery.Event object, Object, or just an event type string
+ event = event[ jQuery.expando ] ?
+ event :
+ new jQuery.Event( type, typeof event === "object" && event );
+
+ // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
+ event.isTrigger = onlyHandlers ? 2 : 3;
+ event.namespace = namespaces.join( "." );
+ event.rnamespace = event.namespace ?
+ new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) :
+ null;
+
+ // Clean up the event in case it is being reused
+ event.result = undefined;
+ if ( !event.target ) {
+ event.target = elem;
+ }
+
+ // Clone any incoming data and prepend the event, creating the handler arg list
+ data = data == null ?
+ [ event ] :
+ jQuery.makeArray( data, [ event ] );
+
+ // Allow special events to draw outside the lines
+ special = jQuery.event.special[ type ] || {};
+ if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
+ return;
+ }
+
+ // Determine event propagation path in advance, per W3C events spec (#9951)
+ // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+ if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
+
+ bubbleType = special.delegateType || type;
+ if ( !rfocusMorph.test( bubbleType + type ) ) {
+ cur = cur.parentNode;
+ }
+ for ( ; cur; cur = cur.parentNode ) {
+ eventPath.push( cur );
+ tmp = cur;
+ }
+
+ // Only add window if we got to document (e.g., not plain obj or detached DOM)
+ if ( tmp === ( elem.ownerDocument || document ) ) {
+ eventPath.push( tmp.defaultView || tmp.parentWindow || window );
+ }
+ }
+
+ // Fire handlers on the event path
+ i = 0;
+ while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {
+
+ event.type = i > 1 ?
+ bubbleType :
+ special.bindType || type;
+
+ // jQuery handler
+ handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] &&
+ jQuery._data( cur, "handle" );
+
+ if ( handle ) {
+ handle.apply( cur, data );
+ }
+
+ // Native handler
+ handle = ontype && cur[ ontype ];
+ if ( handle && handle.apply && acceptData( cur ) ) {
+ event.result = handle.apply( cur, data );
+ if ( event.result === false ) {
+ event.preventDefault();
+ }
+ }
+ }
+ event.type = type;
+
+ // If nobody prevented the default action, do it now
+ if ( !onlyHandlers && !event.isDefaultPrevented() ) {
+
+ if (
+ ( !special._default ||
+ special._default.apply( eventPath.pop(), data ) === false
+ ) && acceptData( elem )
+ ) {
+
+ // Call a native DOM method on the target with the same name name as the event.
+ // Can't use an .isFunction() check here because IE6/7 fails that test.
+ // Don't do default actions on window, that's where global variables be (#6170)
+ if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) {
+
+ // Don't re-trigger an onFOO event when we call its FOO() method
+ tmp = elem[ ontype ];
+
+ if ( tmp ) {
+ elem[ ontype ] = null;
+ }
+
+ // Prevent re-triggering of the same event, since we already bubbled it above
+ jQuery.event.triggered = type;
+ try {
+ elem[ type ]();
+ } catch ( e ) {
+
+ // IE<9 dies on focus/blur to hidden element (#1486,#12518)
+ // only reproducible on winXP IE8 native, not IE9 in IE8 mode
+ }
+ jQuery.event.triggered = undefined;
+
+ if ( tmp ) {
+ elem[ ontype ] = tmp;
+ }
+ }
+ }
+ }
+
+ return event.result;
+ },
+
+ dispatch: function( event ) {
+
+ // Make a writable jQuery.Event from the native event object
+ event = jQuery.event.fix( event );
+
+ var i, j, ret, matched, handleObj,
+ handlerQueue = [],
+ args = slice.call( arguments ),
+ handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [],
+ special = jQuery.event.special[ event.type ] || {};
+
+ // Use the fix-ed jQuery.Event rather than the (read-only) native event
+ args[ 0 ] = event;
+ event.delegateTarget = this;
+
+ // Call the preDispatch hook for the mapped type, and let it bail if desired
+ if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
+ return;
+ }
+
+ // Determine handlers
+ handlerQueue = jQuery.event.handlers.call( this, event, handlers );
+
+ // Run delegates first; they may want to stop propagation beneath us
+ i = 0;
+ while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {
+ event.currentTarget = matched.elem;
+
+ j = 0;
+ while ( ( handleObj = matched.handlers[ j++ ] ) &&
+ !event.isImmediatePropagationStopped() ) {
+
+ // Triggered event must either 1) have no namespace, or 2) have namespace(s)
+ // a subset or equal to those in the bound event (both can have no namespace).
+ if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) {
+
+ event.handleObj = handleObj;
+ event.data = handleObj.data;
+
+ ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
+ handleObj.handler ).apply( matched.elem, args );
+
+ if ( ret !== undefined ) {
+ if ( ( event.result = ret ) === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+ }
+ }
+ }
+
+ // Call the postDispatch hook for the mapped type
+ if ( special.postDispatch ) {
+ special.postDispatch.call( this, event );
+ }
+
+ return event.result;
+ },
+
+ handlers: function( event, handlers ) {
+ var i, matches, sel, handleObj,
+ handlerQueue = [],
+ delegateCount = handlers.delegateCount,
+ cur = event.target;
+
+ // Support (at least): Chrome, IE9
+ // Find delegate handlers
+ // Black-hole SVG instance trees (#13180)
+ //
+ // Support: Firefox<=42+
+ // Avoid non-left-click in FF but don't block IE radio events (#3861, gh-2343)
+ if ( delegateCount && cur.nodeType &&
+ ( event.type !== "click" || isNaN( event.button ) || event.button < 1 ) ) {
+
+ /* jshint eqeqeq: false */
+ for ( ; cur != this; cur = cur.parentNode || this ) {
+ /* jshint eqeqeq: true */
+
+ // Don't check non-elements (#13208)
+ // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
+ if ( cur.nodeType === 1 && ( cur.disabled !== true || event.type !== "click" ) ) {
+ matches = [];
+ for ( i = 0; i < delegateCount; i++ ) {
+ handleObj = handlers[ i ];
+
+ // Don't conflict with Object.prototype properties (#13203)
+ sel = handleObj.selector + " ";
+
+ if ( matches[ sel ] === undefined ) {
+ matches[ sel ] = handleObj.needsContext ?
+ jQuery( sel, this ).index( cur ) > -1 :
+ jQuery.find( sel, this, null, [ cur ] ).length;
+ }
+ if ( matches[ sel ] ) {
+ matches.push( handleObj );
+ }
+ }
+ if ( matches.length ) {
+ handlerQueue.push( { elem: cur, handlers: matches } );
+ }
+ }
+ }
+ }
+
+ // Add the remaining (directly-bound) handlers
+ if ( delegateCount < handlers.length ) {
+ handlerQueue.push( { elem: this, handlers: handlers.slice( delegateCount ) } );
+ }
+
+ return handlerQueue;
+ },
+
+ fix: function( event ) {
+ if ( event[ jQuery.expando ] ) {
+ return event;
+ }
+
+ // Create a writable copy of the event object and normalize some properties
+ var i, prop, copy,
+ type = event.type,
+ originalEvent = event,
+ fixHook = this.fixHooks[ type ];
+
+ if ( !fixHook ) {
+ this.fixHooks[ type ] = fixHook =
+ rmouseEvent.test( type ) ? this.mouseHooks :
+ rkeyEvent.test( type ) ? this.keyHooks :
+ {};
+ }
+ copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
+
+ event = new jQuery.Event( originalEvent );
+
+ i = copy.length;
+ while ( i-- ) {
+ prop = copy[ i ];
+ event[ prop ] = originalEvent[ prop ];
+ }
+
+ // Support: IE<9
+ // Fix target property (#1925)
+ if ( !event.target ) {
+ event.target = originalEvent.srcElement || document;
+ }
+
+ // Support: Safari 6-8+
+ // Target should not be a text node (#504, #13143)
+ if ( event.target.nodeType === 3 ) {
+ event.target = event.target.parentNode;
+ }
+
+ // Support: IE<9
+ // For mouse/key events, metaKey==false if it's undefined (#3368, #11328)
+ event.metaKey = !!event.metaKey;
+
+ return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
+ },
+
+ // Includes some event props shared by KeyEvent and MouseEvent
+ props: ( "altKey bubbles cancelable ctrlKey currentTarget detail eventPhase " +
+ "metaKey relatedTarget shiftKey target timeStamp view which" ).split( " " ),
+
+ fixHooks: {},
+
+ keyHooks: {
+ props: "char charCode key keyCode".split( " " ),
+ filter: function( event, original ) {
+
+ // Add which for key events
+ if ( event.which == null ) {
+ event.which = original.charCode != null ? original.charCode : original.keyCode;
+ }
+
+ return event;
+ }
+ },
+
+ mouseHooks: {
+ props: ( "button buttons clientX clientY fromElement offsetX offsetY " +
+ "pageX pageY screenX screenY toElement" ).split( " " ),
+ filter: function( event, original ) {
+ var body, eventDoc, doc,
+ button = original.button,
+ fromElement = original.fromElement;
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && original.clientX != null ) {
+ eventDoc = event.target.ownerDocument || document;
+ doc = eventDoc.documentElement;
+ body = eventDoc.body;
+
+ event.pageX = original.clientX +
+ ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
+ ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+ event.pageY = original.clientY +
+ ( doc && doc.scrollTop || body && body.scrollTop || 0 ) -
+ ( doc && doc.clientTop || body && body.clientTop || 0 );
+ }
+
+ // Add relatedTarget, if necessary
+ if ( !event.relatedTarget && fromElement ) {
+ event.relatedTarget = fromElement === event.target ?
+ original.toElement :
+ fromElement;
+ }
+
+ // Add which for click: 1 === left; 2 === middle; 3 === right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && button !== undefined ) {
+ event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
+ }
+
+ return event;
+ }
+ },
+
+ special: {
+ load: {
+
+ // Prevent triggered image.load events from bubbling to window.load
+ noBubble: true
+ },
+ focus: {
+
+ // Fire native event if possible so blur/focus sequence is correct
+ trigger: function() {
+ if ( this !== safeActiveElement() && this.focus ) {
+ try {
+ this.focus();
+ return false;
+ } catch ( e ) {
+
+ // Support: IE<9
+ // If we error on focus to hidden element (#1486, #12518),
+ // let .trigger() run the handlers
+ }
+ }
+ },
+ delegateType: "focusin"
+ },
+ blur: {
+ trigger: function() {
+ if ( this === safeActiveElement() && this.blur ) {
+ this.blur();
+ return false;
+ }
+ },
+ delegateType: "focusout"
+ },
+ click: {
+
+ // For checkbox, fire native event so checked state will be right
+ trigger: function() {
+ if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) {
+ this.click();
+ return false;
+ }
+ },
+
+ // For cross-browser consistency, don't fire native .click() on links
+ _default: function( event ) {
+ return jQuery.nodeName( event.target, "a" );
+ }
+ },
+
+ beforeunload: {
+ postDispatch: function( event ) {
+
+ // Support: Firefox 20+
+ // Firefox doesn't alert if the returnValue field is not set.
+ if ( event.result !== undefined && event.originalEvent ) {
+ event.originalEvent.returnValue = event.result;
+ }
+ }
+ }
+ },
+
+ // Piggyback on a donor event to simulate a different one
+ simulate: function( type, elem, event ) {
+ var e = jQuery.extend(
+ new jQuery.Event(),
+ event,
+ {
+ type: type,
+ isSimulated: true
+
+ // Previously, `originalEvent: {}` was set here, so stopPropagation call
+ // would not be triggered on donor event, since in our own
+ // jQuery.event.stopPropagation function we had a check for existence of
+ // originalEvent.stopPropagation method, so, consequently it would be a noop.
+ //
+ // Guard for simulated events was moved to jQuery.event.stopPropagation function
+ // since `originalEvent` should point to the original event for the
+ // constancy with other events and for more focused logic
+ }
+ );
+
+ jQuery.event.trigger( e, null, elem );
+
+ if ( e.isDefaultPrevented() ) {
+ event.preventDefault();
+ }
+ }
+ };
+
+ jQuery.removeEvent = document.removeEventListener ?
+ function( elem, type, handle ) {
+
+ // This "if" is needed for plain objects
+ if ( elem.removeEventListener ) {
+ elem.removeEventListener( type, handle );
+ }
+ } :
+ function( elem, type, handle ) {
+ var name = "on" + type;
+
+ if ( elem.detachEvent ) {
+
+ // #8545, #7054, preventing memory leaks for custom events in IE6-8
+ // detachEvent needed property on element, by name of that event,
+ // to properly expose it to GC
+ if ( typeof elem[ name ] === "undefined" ) {
+ elem[ name ] = null;
+ }
+
+ elem.detachEvent( name, handle );
+ }
+ };
+
+ jQuery.Event = function( src, props ) {
+
+ // Allow instantiation without the 'new' keyword
+ if ( !( this instanceof jQuery.Event ) ) {
+ return new jQuery.Event( src, props );
+ }
+
+ // Event object
+ if ( src && src.type ) {
+ this.originalEvent = src;
+ this.type = src.type;
+
+ // Events bubbling up the document may have been marked as prevented
+ // by a handler lower down the tree; reflect the correct value.
+ this.isDefaultPrevented = src.defaultPrevented ||
+ src.defaultPrevented === undefined &&
+
+ // Support: IE < 9, Android < 4.0
+ src.returnValue === false ?
+ returnTrue :
+ returnFalse;
+
+ // Event type
+ } else {
+ this.type = src;
+ }
+
+ // Put explicitly provided properties onto the event object
+ if ( props ) {
+ jQuery.extend( this, props );
+ }
+
+ // Create a timestamp if incoming event doesn't have one
+ this.timeStamp = src && src.timeStamp || jQuery.now();
+
+ // Mark it as fixed
+ this[ jQuery.expando ] = true;
+ };
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+ jQuery.Event.prototype = {
+ constructor: jQuery.Event,
+ isDefaultPrevented: returnFalse,
+ isPropagationStopped: returnFalse,
+ isImmediatePropagationStopped: returnFalse,
+
+ preventDefault: function() {
+ var e = this.originalEvent;
+
+ this.isDefaultPrevented = returnTrue;
+ if ( !e ) {
+ return;
+ }
+
+ // If preventDefault exists, run it on the original event
+ if ( e.preventDefault ) {
+ e.preventDefault();
+
+ // Support: IE
+ // Otherwise set the returnValue property of the original event to false
+ } else {
+ e.returnValue = false;
+ }
+ },
+ stopPropagation: function() {
+ var e = this.originalEvent;
+
+ this.isPropagationStopped = returnTrue;
+
+ if ( !e || this.isSimulated ) {
+ return;
+ }
+
+ // If stopPropagation exists, run it on the original event
+ if ( e.stopPropagation ) {
+ e.stopPropagation();
+ }
+
+ // Support: IE
+ // Set the cancelBubble property of the original event to true
+ e.cancelBubble = true;
+ },
+ stopImmediatePropagation: function() {
+ var e = this.originalEvent;
+
+ this.isImmediatePropagationStopped = returnTrue;
+
+ if ( e && e.stopImmediatePropagation ) {
+ e.stopImmediatePropagation();
+ }
+
+ this.stopPropagation();
+ }
+ };
+
+// Create mouseenter/leave events using mouseover/out and event-time checks
+// so that event delegation works in jQuery.
+// Do the same for pointerenter/pointerleave and pointerover/pointerout
+//
+// Support: Safari 7 only
+// Safari sends mouseenter too often; see:
+// https://code.google.com/p/chromium/issues/detail?id=470258
+// for the description of the bug (it existed in older Chrome versions as well).
+ jQuery.each( {
+ mouseenter: "mouseover",
+ mouseleave: "mouseout",
+ pointerenter: "pointerover",
+ pointerleave: "pointerout"
+ }, function( orig, fix ) {
+ jQuery.event.special[ orig ] = {
+ delegateType: fix,
+ bindType: fix,
+
+ handle: function( event ) {
+ var ret,
+ target = this,
+ related = event.relatedTarget,
+ handleObj = event.handleObj;
+
+ // For mouseenter/leave call the handler if related is outside the target.
+ // NB: No relatedTarget if the mouse left/entered the browser window
+ if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) {
+ event.type = handleObj.origType;
+ ret = handleObj.handler.apply( this, arguments );
+ event.type = fix;
+ }
+ return ret;
+ }
+ };
+ } );
+
+// IE submit delegation
+ if ( !support.submit ) {
+
+ jQuery.event.special.submit = {
+ setup: function() {
+
+ // Only need this for delegated form submit events
+ if ( jQuery.nodeName( this, "form" ) ) {
+ return false;
+ }
+
+ // Lazy-add a submit handler when a descendant form may potentially be submitted
+ jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
+
+ // Node name check avoids a VML-related crash in IE (#9807)
+ var elem = e.target,
+ form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ?
+
+ // Support: IE <=8
+ // We use jQuery.prop instead of elem.form
+ // to allow fixing the IE8 delegated submit issue (gh-2332)
+ // by 3rd party polyfills/workarounds.
+ jQuery.prop( elem, "form" ) :
+ undefined;
+
+ if ( form && !jQuery._data( form, "submit" ) ) {
+ jQuery.event.add( form, "submit._submit", function( event ) {
+ event._submitBubble = true;
+ } );
+ jQuery._data( form, "submit", true );
+ }
+ } );
+
+ // return undefined since we don't need an event listener
+ },
+
+ postDispatch: function( event ) {
+
+ // If form was submitted by the user, bubble the event up the tree
+ if ( event._submitBubble ) {
+ delete event._submitBubble;
+ if ( this.parentNode && !event.isTrigger ) {
+ jQuery.event.simulate( "submit", this.parentNode, event );
+ }
+ }
+ },
+
+ teardown: function() {
+
+ // Only need this for delegated form submit events
+ if ( jQuery.nodeName( this, "form" ) ) {
+ return false;
+ }
+
+ // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
+ jQuery.event.remove( this, "._submit" );
+ }
+ };
+ }
+
+// IE change delegation and checkbox/radio fix
+ if ( !support.change ) {
+
+ jQuery.event.special.change = {
+
+ setup: function() {
+
+ if ( rformElems.test( this.nodeName ) ) {
+
+ // IE doesn't fire change on a check/radio until blur; trigger it on click
+ // after a propertychange. Eat the blur-change in special.change.handle.
+ // This still fires onchange a second time for check/radio after blur.
+ if ( this.type === "checkbox" || this.type === "radio" ) {
+ jQuery.event.add( this, "propertychange._change", function( event ) {
+ if ( event.originalEvent.propertyName === "checked" ) {
+ this._justChanged = true;
+ }
+ } );
+ jQuery.event.add( this, "click._change", function( event ) {
+ if ( this._justChanged && !event.isTrigger ) {
+ this._justChanged = false;
+ }
+
+ // Allow triggered, simulated change events (#11500)
+ jQuery.event.simulate( "change", this, event );
+ } );
+ }
+ return false;
+ }
+
+ // Delegated event; lazy-add a change handler on descendant inputs
+ jQuery.event.add( this, "beforeactivate._change", function( e ) {
+ var elem = e.target;
+
+ if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "change" ) ) {
+ jQuery.event.add( elem, "change._change", function( event ) {
+ if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
+ jQuery.event.simulate( "change", this.parentNode, event );
+ }
+ } );
+ jQuery._data( elem, "change", true );
+ }
+ } );
+ },
+
+ handle: function( event ) {
+ var elem = event.target;
+
+ // Swallow native change events from checkbox/radio, we already triggered them above
+ if ( this !== elem || event.isSimulated || event.isTrigger ||
+ ( elem.type !== "radio" && elem.type !== "checkbox" ) ) {
+
+ return event.handleObj.handler.apply( this, arguments );
+ }
+ },
+
+ teardown: function() {
+ jQuery.event.remove( this, "._change" );
+
+ return !rformElems.test( this.nodeName );
+ }
+ };
+ }
+
+// Support: Firefox
+// Firefox doesn't have focus(in | out) events
+// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
+//
+// Support: Chrome, Safari
+// focus(in | out) events fire after focus & blur events,
+// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
+// Related ticket - https://code.google.com/p/chromium/issues/detail?id=449857
+ if ( !support.focusin ) {
+ jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+
+ // Attach a single capturing handler on the document while someone wants focusin/focusout
+ var handler = function( event ) {
+ jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) );
+ };
+
+ jQuery.event.special[ fix ] = {
+ setup: function() {
+ var doc = this.ownerDocument || this,
+ attaches = jQuery._data( doc, fix );
+
+ if ( !attaches ) {
+ doc.addEventListener( orig, handler, true );
+ }
+ jQuery._data( doc, fix, ( attaches || 0 ) + 1 );
+ },
+ teardown: function() {
+ var doc = this.ownerDocument || this,
+ attaches = jQuery._data( doc, fix ) - 1;
+
+ if ( !attaches ) {
+ doc.removeEventListener( orig, handler, true );
+ jQuery._removeData( doc, fix );
+ } else {
+ jQuery._data( doc, fix, attaches );
+ }
+ }
+ };
+ } );
+ }
+
+ jQuery.fn.extend( {
+
+ on: function( types, selector, data, fn ) {
+ return on( this, types, selector, data, fn );
+ },
+ one: function( types, selector, data, fn ) {
+ return on( this, types, selector, data, fn, 1 );
+ },
+ off: function( types, selector, fn ) {
+ var handleObj, type;
+ if ( types && types.preventDefault && types.handleObj ) {
+
+ // ( event ) dispatched jQuery.Event
+ handleObj = types.handleObj;
+ jQuery( types.delegateTarget ).off(
+ handleObj.namespace ?
+ handleObj.origType + "." + handleObj.namespace :
+ handleObj.origType,
+ handleObj.selector,
+ handleObj.handler
+ );
+ return this;
+ }
+ if ( typeof types === "object" ) {
+
+ // ( types-object [, selector] )
+ for ( type in types ) {
+ this.off( type, selector, types[ type ] );
+ }
+ return this;
+ }
+ if ( selector === false || typeof selector === "function" ) {
+
+ // ( types [, fn] )
+ fn = selector;
+ selector = undefined;
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ }
+ return this.each( function() {
+ jQuery.event.remove( this, types, fn, selector );
+ } );
+ },
+
+ trigger: function( type, data ) {
+ return this.each( function() {
+ jQuery.event.trigger( type, data, this );
+ } );
+ },
+ triggerHandler: function( type, data ) {
+ var elem = this[ 0 ];
+ if ( elem ) {
+ return jQuery.event.trigger( type, data, elem, true );
+ }
+ }
+ } );
+
+
+ var rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
+ rnoshimcache = new RegExp( "<(?:" + nodeNames + ")[\\s/>]", "i" ),
+
+ // Support: IE 10-11, Edge 10240+
+ // In IE/Edge using regex groups here causes severe slowdowns.
+ // See https://connect.microsoft.com/IE/feedback/details/1736512/
+ rnoInnerhtml = /