diff --git a/examples/tab-context.html b/examples/tab-context.html
new file mode 100644
index 000000000..c7e4324a0
--- /dev/null
+++ b/examples/tab-context.html
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/base/combination/tab.js b/src/base/combination/tab.js
new file mode 100644
index 000000000..637ee95f3
--- /dev/null
+++ b/src/base/combination/tab.js
@@ -0,0 +1,153 @@
+/**
+ * Created by GUY on 2015/6/26.
+ */
+
+BI.Tab = BI.inherit(BI.Widget, {
+ _defaultConfig: function () {
+ return BI.extend(BI.Tab.superclass._defaultConfig.apply(this, arguments), {
+ baseCls: "bi-tab",
+ direction: "top", // top, bottom, left, right, custom
+ single: false, // 是不是单页面
+ logic: {
+ dynamic: false
+ },
+ showIndex: false,
+ tab: false,
+ cardCreator: function (v) {
+ return BI.createWidget();
+ }
+ });
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ if (BI.isObject(o.tab)) {
+ this.tab = BI.createWidget(this.options.tab, {type: "bi.button_group"});
+ this.tab.on(BI.Controller.EVENT_CHANGE, function (type, value, obj) {
+ self.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
+ });
+ }
+ this.cardMap = {};
+ this.layout = BI.createWidget({
+ type: "bi.card"
+ });
+
+ 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.tab, this.layout)
+ }))));
+
+ var listener = new BI.ShowListener({
+ eventObj: this.tab,
+ cardLayout: this.layout,
+ cardCreator: function (v) {
+ var card = o.cardCreator.apply(self, arguments);
+ self.cardMap[v] = card;
+ return card;
+ },
+ afterCardShow: function (v) {
+ self._deleteOtherCards(v);
+ self.curr = v;
+ }
+ });
+ listener.on(BI.ShowListener.EVENT_CHANGE, function (value) {
+ self.fireEvent(BI.Tab.EVENT_CHANGE, value, self);
+ });
+ },
+
+ _deleteOtherCards: function (currCardName) {
+ var self = this, o = this.options;
+ if (o.single === true) {
+ BI.each(this.cardMap, function (name, card) {
+ if (name !== (currCardName + "")) {
+ self.layout.deleteCardByName(name);
+ delete self.cardMap[name];
+ }
+ });
+ }
+ },
+
+ _assertCard: function (v) {
+ if (!this.layout.isCardExisted(v)) {
+ var card = this.options.cardCreator(v);
+ this.cardMap[v] = card;
+ this.layout.addCardByName(v, card);
+ }
+ },
+
+ created: function () {
+ var o = this.options;
+ if (o.showIndex !== false) {
+ this.setSelect(o.showIndex);
+ }
+ },
+
+ setSelect: function (v) {
+ this.tab && this.tab.setValue(v);
+ this._assertCard(v);
+ this.layout.showCardByName(v);
+ this._deleteOtherCards(v);
+ if (this.curr !== v) {
+ this.curr = v;
+ }
+ },
+
+ removeTab: function (cardname) {
+ var self = this, o = this.options;
+ BI.any(this.cardMap, function (name, card) {
+ if (BI.isEqual(name, (cardname + ""))) {
+ self.layout.deleteCardByName(name);
+ delete self.cardMap[name];
+ return true;
+ }
+ });
+ },
+
+ getSelect: function () {
+ return this.curr;
+ },
+
+ getSelectedTab: function () {
+ return this.layout.getShowingCard();
+ },
+
+ getTab: function (v) {
+ this._assertCard(v);
+ return this.layout.getCardByName(v);
+ },
+
+ setValue: function (v) {
+ var card = this.layout.getShowingCard();
+ if (card) {
+ card.setValue(v);
+ }
+ },
+
+ getValue: function () {
+ var card = this.layout.getShowingCard();
+ if (card) {
+ return card.getValue();
+ }
+ },
+
+ populate: function () {
+ var card = this.layout.getShowingCard();
+ if (card) {
+ return card.populate && card.populate.apply(card, arguments);
+ }
+ },
+
+ empty: function () {
+ this.layout.deleteAllCard();
+ this.cardMap = {};
+ },
+
+ destroy: function () {
+ this.cardMap = {};
+ BI.Tab.superclass.destroy.apply(this, arguments);
+ }
+});
+BI.Tab.EVENT_CHANGE = "EVENT_CHANGE";
+
+BI.shortcut("bi.tab", BI.Tab);
diff --git a/src/core/base.js b/src/core/base.js
new file mode 100644
index 000000000..b7948bf33
--- /dev/null
+++ b/src/core/base.js
@@ -0,0 +1,1308 @@
+/**
+ * 基本函数
+ * 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 || (BI.View && widget instanceof BI.View);
+ },
+
+ createWidgets: function (items, options, context) {
+ if (!BI.isArray(items)) {
+ throw new Error("cannot create Widgets");
+ }
+ 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));
+ });
+ },
+
+ 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 || (BI.View && item.el instanceof BI.View)) {
+ 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 (sb, sp, overrides) {
+ if (typeof sp === "object") {
+ overrides = sp;
+ sp = sb;
+ 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)?%$/.exec(w) || w == "auto" || /^\d+px$/.exec(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/core/shortcut.js b/src/core/shortcut.js
new file mode 100644
index 000000000..9f3b8492b
--- /dev/null
+++ b/src/core/shortcut.js
@@ -0,0 +1,92 @@
+(function () {
+ var kv = {};
+ BI.shortcut = BI.component = BI.shortcut || function (xtype, cls) {
+ if (kv[xtype] != null) {
+ _global.console && console.error("shortcut:[" + xtype + "] has been registed");
+ }
+ 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;
+ if (context) {
+ pushed = true;
+ BI.Widget.pushContext(context);
+ }
+ var widget = new cls();
+ widget._initProps(config);
+ widget._constructed();
+ widget._initRoot();
+ // if (!lazy || config.element || config.root) {
+ widget._lazyConstructor();
+ // }
+ pushed && BI.Widget.popContext();
+ return widget;
+ };
+
+ BI.createWidget = BI.createWidget || function (item, options, context, lazy) {
+ // 先把准备环境准备好
+ BI.init();
+ var el, w;
+ item || (item = {});
+ if (BI.isWidget(options)) {
+ context = options;
+ options = {};
+ } else {
+ options || (options = {});
+ }
+ if (BI.isEmpty(item) && BI.isEmpty(options)) {
+ return BI.createWidget({
+ type: "bi.layout"
+ });
+ }
+ if (BI.isWidget(item)) {
+ return item;
+ }
+ if (item.type || options.type) {
+ el = BI.extend({}, options, item);
+ w = BI.Plugin.getWidget(el.type, el);
+ w.listeners = (w.listeners || []).concat([{
+ eventName: BI.Events.MOUNT,
+ action: function () {
+ BI.Plugin.getObject(el.type, this);
+ }
+ }]);
+ return w.type === el.type ? createWidget(w, context, lazy) : BI.createWidget(BI.extend({/**important**/}, el, {type: w.type}), options, context, lazy);
+ }
+ if (item.el && (item.el.type || options.type)) {
+ el = BI.extend({}, options, item.el);
+ w = BI.Plugin.getWidget(el.type, el);
+ w.listeners = (w.listeners || []).concat([{
+ eventName: BI.Events.MOUNT,
+ action: function () {
+ BI.Plugin.getObject(el.type, this);
+ }
+ }]);
+ return w.type === el.type ? createWidget(w, context, lazy) : BI.createWidget(BI.extend({/**important**/}, el, {type: w.type}), options, context, lazy);
+ }
+ if (BI.isWidget(item.el)) {
+ return item.el;
+ }
+ throw new Error("无法根据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/core/widget.js b/src/core/widget.js
new file mode 100644
index 000000000..42628f1d4
--- /dev/null
+++ b/src/core/widget.js
@@ -0,0 +1,750 @@
+/**
+ * Widget超类
+ * @class BI.Widget
+ * @extends BI.OB
+ *
+ * @cfg {JSON} options 配置属性
+ */
+
+!(function () {
+ function callLifeHook (self, life) {
+ var hook = self.options[life] || self[life];
+ if (hook) {
+ var hooks = 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,
+
+ tag: null,
+ disabled: false,
+ invisible: false,
+ invalid: false,
+ baseCls: "",
+ extraCls: "",
+ cls: "",
+ css: null,
+ updateMode: "manual" // manual / auto
+ });
+ },
+
+ _constructor: function () {
+
+ },
+
+ // 覆盖父类的_constructor方法,widget不走ob的生命周期
+ _constructed: function () {
+ if (this.setup) {
+ pushTarget(this);
+ this.service = this.setup(this.options);
+ this.render = BI.isPlainObject(this.service) ? this.service.render : this.service;
+ 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;
+
+ function render () {
+ if (self.options.beforeRender || self.beforeRender) {
+ (self.options.beforeRender || self.beforeRender).call(self, BI.bind(self._render, self));
+ } else {
+ self._render();
+ }
+ }
+
+ if (this.options.beforeInit || this.beforeInit) {
+ this.__asking = true;
+ (this.options.beforeInit || this.beforeInit).call(this, render);
+ if (this.__asking === true) {
+ this.__async = true;
+ }
+ } else {
+ render();
+ }
+ },
+
+ _render: function () {
+ this.__asking = false;
+ callLifeHook(this, "beforeCreate");
+ this._initElement();
+ this._initEffects();
+ callLifeHook(this, "created");
+ },
+
+ _initCurrent: function () {
+ var o = this.options;
+ if (o._baseCls || o.baseCls || o.extraCls || o.cls) {
+ this.element.addClass((o._baseCls || "") + " " + (o.baseCls || "") + " " + (o.extraCls || "") + " " + (o.cls || ""));
+ }
+ if (o.attributes) {
+ this.element.attr(o.attributes);
+ }
+ if (o.data) {
+ this.element.data(o.data);
+ }
+ if (o.css) {
+ this.element.css(o.css);
+ }
+ },
+
+ /**
+ * 初始化根节点
+ * @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;
+ if (o.element instanceof BI.Widget) {
+ this._parent = o.element;
+ this._parent.addWidget(this.widgetName, this);
+ } else {
+ this._isRoot = true;
+ }
+ } else if (o.element) {
+ // if (o.root !== true) {
+ // throw new Error("root is a required property");
+ // }
+ this.element = BI.Widget._renderEngine.createElement(this);
+ this._isRoot = true;
+ } else {
+ this.element = BI.Widget._renderEngine.createElement(this);
+ }
+ this.element._isWidget = true;
+ 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 o = this.options;
+ if (o.disabled || o.invalid) {
+ if (this.options.disabled) {
+ this.setEnable(false);
+ }
+ if (this.options.invalid) {
+ this.setValid(false);
+ }
+ }
+ },
+
+ _initState: function () {
+ this._isMounted = false;
+ },
+
+ _initElement: function () {
+ var self = this;
+ var render = BI.isFunction(this.options.render) ? this.options.render : this.render;
+ var els = render && render.call(this);
+ if (BI.isPlainObject(els)) {
+ els = [els];
+ }
+ if (BI.isArray(els)) {
+ BI.each(els, function (i, el) {
+ if (el) {
+ BI._lazyCreateWidget(el, {
+ element: self
+ });
+ }
+ });
+ }
+ // if (this._isRoot === true || !(this instanceof BI.Layout)) {
+ this._mount();
+ // }
+ },
+
+ _setParent: function (parent) {
+ this._parent = parent;
+ },
+
+ /**
+ *
+ * @param force 是否强制挂载子节点
+ * @param deep 子节点是否也是按照当前force处理
+ * @param lifeHook 生命周期钩子触不触发,默认触发
+ * @param predicate 递归每个widget的回调
+ * @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 && callLifeHook(this, "beforeMount");
+ this._isMounted = true;
+ 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);
+ }
+ lifeHook !== false && callLifeHook(this, "mounted");
+ this.fireEvent(BI.Events.MOUNT);
+ predicate && predicate(this);
+ }
+ },
+
+ _mountChildren: null,
+
+ _update: function (nextProps, shouldUpdate) {
+ var o = this.options;
+ callLifeHook(this, "beforeUpdate");
+ if (shouldUpdate) {
+ var res = this.update && this.update(nextProps, shouldUpdate);
+ } else if (BI.isNull(shouldUpdate)) {
+ // 默认使用shallowCompare的方式进行更新
+ var nextChange = {};
+ BI.each(nextProps, function (key, value) {
+ if (o[key] !== value) {
+ nextChange[key] = value;
+ }
+ });
+ var res = this.update && BI.isNotEmptyObject(nextChange) && this.update(nextChange);
+ }
+ 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("name has already been existed");
+ }
+ 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) {
+ var self = this;
+ 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();
+ },
+
+ isolate: function () {
+ if (this._parent) {
+ this._parent.removeWidget(this);
+ }
+ BI.DOM.hang([this]);
+ },
+
+ empty: function () {
+ BI.each(this._children, function (i, widget) {
+ widget && widget._unMount && widget._unMount();
+ });
+ this._children = {};
+ this.element.empty();
+ },
+
+ // 默认的populate方法就是干掉重来
+ reset: function () {
+ this.purgeListeners();
+ this.empty();
+ this._initCurrent();
+ this._init();
+ 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 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.apply(this, arguments);
+ return origin;
+ };
+ return current.$storeDelegate = proxy;
+ }
+ current._store = function () {
+ var st = _store.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.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.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);
+ };
+})();