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); + }; +})();