diff --git a/src/core/0.foundation.js b/src/core/0.foundation.js index 6a251f80c..16c0d76a9 100644 --- a/src/core/0.foundation.js +++ b/src/core/0.foundation.js @@ -1,7 +1,5 @@ /** * Created by richie on 15/7/8. - * - * 初始化 _global 对象??为什么不是 globalThis */ let _global = undefined; if (typeof window !== "undefined") { diff --git a/src/core/2.base.js b/src/core/2.base.js index b0e166893..dce352030 100644 --- a/src/core/2.base.js +++ b/src/core/2.base.js @@ -1634,5 +1634,3 @@ export function getTime() { return dt.getTime(); } - -export const lodashUtils = BI._; diff --git a/src/core/conflict.js b/src/core/conflict.js deleted file mode 100644 index 823b174fb..000000000 --- a/src/core/conflict.js +++ /dev/null @@ -1,6 +0,0 @@ -// if (!_global.$ && !_global.jQuery) { -// _global.jQuery = _global.$ = BI.jQuery; -// } -// if (!_global._) { -// _global._ = BI._; -// } diff --git a/src/core/index.js b/src/core/index.js index 861f82bcb..78a4d5090 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -34,4 +34,10 @@ export * from "./platform/web"; export * from "./utils"; -export { shortcut, provider } from "./decorator"; \ No newline at end of file +export { shortcut, provider } from "./decorator"; + +import _ from "./1.lodash"; + +export { + _ +}; diff --git a/src/fix/fix.compact.js b/src/fix/fix.compact.js new file mode 100644 index 000000000..4cae6dd0d --- /dev/null +++ b/src/fix/fix.compact.js @@ -0,0 +1,231 @@ +import { + _, + isArray, + each, + isKey, + isPlainObject, + extend, + isFunction, + Widget, + Providers +} from "@/core"; +import { Fix } from "./fix"; + +function initWatch(vm, watch) { + vm._watchers || (vm._watchers = []); + for (const key in watch) { + const handler = watch[key]; + if (isArray(handler)) { + for (let i = 0; i < handler.length; i++) { + vm._watchers.push(createWatcher(vm, key, handler[i])); + } + } else { + vm._watchers.push(createWatcher(vm, key, handler)); + } + } + each(vm.$watchDelayCallbacks, (i, watchDelayCallback) => { + let innerWatch = watchDelayCallback[0]; + const innerHandler = watchDelayCallback[1]; + if (isKey(innerWatch)) { + const key = innerWatch; + innerWatch = {}; + innerWatch[key] = innerHandler; + } + for (const key in innerWatch) { + const handler = innerWatch[key]; + if (isArray(handler)) { + for (let i = 0; i < handler.length; i++) { + vm._watchers.push(createWatcher(vm, key, handler[i])); + } + } else { + vm._watchers.push(createWatcher(vm, key, handler)); + } + } + }); +} + +function createWatcher(vm, keyOrFn, cb, options) { + if (isPlainObject(cb)) { + options = cb; + cb = cb.handler; + } + options = options || {}; + + return Fix.watch( + vm.model, + keyOrFn, + _.bind(cb, vm), + extend(options, { + store: vm.store + }) + ); +} + +let target = null; +const targetStack = []; + +function pushTarget(_target) { + if (target) targetStack.push(target); + Fix.Model.target = target = _target; +} + +function popTarget() { + Fix.Model.target = target = targetStack.pop(); +} + +export const Model = Fix.Model; + +const oldWatch = Fix.watch; +Fix.watch = function (model, expOrFn, cb, options) { + if (isPlainObject(cb)) { + options = cb; + cb = cb.handler; + } + if (typeof cb === "string") { + cb = model[cb]; + } + + return oldWatch.call( + this, + model, + expOrFn, + function () { + options && options.store && pushTarget(options.store); + let res; + try { + res = cb.apply(this, arguments); + } catch (e) { + console.error(e); + } + options && options.store && popTarget(); + + return res; + }, + options + ); +}; + +Widget.findStore = function findStore(widget) { + if (target != null) { + return target; + } + widget = widget || Widget.context; + let p = widget; + while (p) { + if (p instanceof Fix.Model || p.store || p.__cacheStore) { + break; + } + p = p._parent || (p.options && p.options.element) || p._context; + } + if (p) { + if (p instanceof Fix.Model) { + return (widget.__cacheStore = p); + } + widget.__cacheStore = p.store || p.__cacheStore; + + return p.__cacheStore || p.store; + } +}; + +function createStore() { + let needPop = false; + const workerMode = + Providers.getProvider("bi.provider.system").getWorkerMode(); + if (workerMode && this._worker) { + return; + } + if (this.store) { + pushTarget(this.store); + + return true; + } + if (this._store || this.options._store) { + const store = Widget.findStore( + this.options.context || + this._parent || + this.options.element || + this._context + ); + if (store) { + pushTarget(store); + needPop = true; + } + this.store = (this._store || this.options._store).call(this); + this.store && (this.store._widget = this); + needPop && popTarget(); + needPop = false; + pushTarget(this.store); + if (this.store instanceof Fix.Model) { + this.model = this.store.model; + } else { + this.model = this.store; + } + needPop = true; + } + + return needPop; +} + +const _init = Widget.prototype._init; +Widget.prototype._init = function () { + const needPop = createStore.call(this); + try { + _init.apply(this, arguments); + } catch (e) { + console.error(e); + } + needPop && popTarget(); +}; + +const __initWatch = Widget.prototype.__initWatch; +Widget.prototype.__initWatch = function () { + __initWatch.apply(this, arguments); + const workerMode = + Providers.getProvider("bi.provider.system").getWorkerMode(); + if (workerMode && this._worker) { + return; + } + if (this._store) { + initWatch(this, this.watch); + } +}; + +const unMount = Widget.prototype.__destroy; +Widget.prototype.__destroy = function () { + try { + unMount.apply(this, arguments); + } catch (e) { + console.error(e); + } + this.store && isFunction(this.store.destroy) && this.store.destroy(); + each(this._watchers, (i, unwatches) => { + unwatches = isArray(unwatches) ? unwatches : [unwatches]; + each(unwatches, (j, unwatch) => { + unwatch(); + }); + }); + this._watchers && (this._watchers = []); + if (this.store) { + this.store._parent && (this.store._parent = null); + this.store._widget && (this.store._widget = null); + this.store = null; + } + delete this.__cacheStore; +}; + +_.each(["_render", "__afterRender", "_mount", "__afterMount"], (name) => { + const old = Widget.prototype[name]; + old && + (Widget.prototype[name] = function () { + this.store && pushTarget(this.store); + let res; + try { + res = old.apply(this, arguments); + } catch (e) { + console.error(e); + } + this.store && popTarget(); + + return res; + }); +}); diff --git a/src/fix/fix.js b/src/fix/fix.js new file mode 100644 index 000000000..6c9640e1f --- /dev/null +++ b/src/fix/fix.js @@ -0,0 +1,1546 @@ +import { _ } from "@/core"; + +function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +} + +function noop(a, b, c) {} + +function isNative(Ctor) { + return typeof Ctor === "function" && /native code/.test(Ctor.toString()); +} + +const hasProto = "__proto__" in {}; + +const _toString = Object.prototype.toString; + +function isPlainObject(obj) { + return _toString.call(obj) === "[object Object]"; +} + +function isConfigurable(obj, key) { + let configurable = true; + const property = + Object.getOwnPropertyDescriptor && + Object.getOwnPropertyDescriptor(obj, key); + if (property && property.configurable === false) { + configurable = false; + } + + return configurable; +} + +function isExtensible(obj) { + if (Object.isExtensible) { + return Object.isExtensible(obj); + } + let name = ""; + while (obj.hasOwnProperty(name)) { + name += "?"; + } + obj[name] = true; + const returnValue = obj.hasOwnProperty(name); + delete obj[name]; + + return returnValue; +} + +function remove(arr, item) { + if (arr && arr.length) { + const _index = arr.indexOf(item); + if (_index > -1) { + return arr.splice(_index, 1); + } + } +} + +// const bailRE = /[^\w.$]/ + +function parsePath(path) { + // 正常表达式比较慢,能不要的就不要了 + // if (bailRE.test(path)) { + // return + // } + const segments = path.split("."); + + return function (obj) { + for (let i = 0; i < segments.length; i++) { + if (!obj) return; + obj = obj[segments[i]]; + } + + return obj; + }; +} + +const nextTick = (function () { + const callbacks = []; + let pending = false; + let timerFunc = void 0; + + function nextTickHandler() { + pending = false; + const copies = callbacks.slice(0); + callbacks.length = 0; + for (let i = 0; i < copies.length; i++) { + copies[i](); + } + } + + // An asynchronous deferring mechanism. + // In pre 2.4, we used to use microtasks (Promise/MutationObserver) + // but microtasks actually has too high a priority and fires in between + // supposedly sequential events (e.g. #4521, #6690) or even between + // bubbling of the same event (#6566). Technically setImmediate should be + // the ideal choice, but it's not available everywhere; and the only polyfill + // that consistently queues the callback after all DOM events triggered in the + // same loop is by using MessageChannel. + /* istanbul ignore if */ + if (typeof setImmediate !== "undefined" && isNative(setImmediate)) { + timerFunc = function timerFunc() { + setImmediate(nextTickHandler); + }; + } else if ( + typeof MessageChannel !== "undefined" && + (isNative(MessageChannel) || + // PhantomJS + MessageChannel.toString() === "[object MessageChannelConstructor]") + ) { + const channel = new MessageChannel(); + const port = channel.port2; + channel.port1.onmessage = nextTickHandler; + timerFunc = function timerFunc() { + port.postMessage(1); + }; + } else if (typeof Promise !== "undefined" && isNative(Promise)) { + /* istanbul ignore next */ + // use microtask in non-DOM environments, e.g. Weex + const p = Promise.resolve(); + timerFunc = function timerFunc() { + p.then(nextTickHandler); + }; + } else { + // fallback to setTimeout + timerFunc = function timerFunc() { + setTimeout(nextTickHandler, 0); + }; + } + + return function queueNextTick(cb, ctx) { + let _resolve = void 0; + callbacks.push(() => { + if (cb) { + try { + cb.call(ctx); + } catch (e) { + console.error(e); + } + } else if (_resolve) { + _resolve(ctx); + } + }); + if (!pending) { + pending = true; + timerFunc(); + } + // $flow-disable-line + if (!cb && typeof Promise !== "undefined") { + return new Promise((resolve, reject) => { + _resolve = resolve; + }); + } + }; +})(); + +let falsy; +const $$skipArray = { + __ob__: falsy, + $accessors: falsy, + $vbthis: falsy, + $vbsetter: falsy +}; + +let uid = 0; + +/** + * A dep is an observable that can have multiple + * directives subscribing to it. + */ + +const Dep = (function () { + function Dep() { + _classCallCheck(this, Dep); + + this.id = uid++; + this.subs = []; + } + + Dep.prototype.addSub = function addSub(sub) { + this.subs.push(sub); + }; + + Dep.prototype.removeSub = function removeSub(sub) { + remove(this.subs, sub); + }; + + Dep.prototype.depend = function depend() { + if (Dep.target) { + Dep.target.addDep(this); + } + }; + + Dep.prototype.notify = function notify(options) { + // stabilize the subscriber list first + const subs = this.subs.slice(); + for (let i = 0, l = subs.length; i < l; i++) { + subs[i].update(options); + } + }; + + return Dep; +})(); + +// the current target watcher being evaluated. +// this is globally unique because there could be only one +// watcher being evaluated at any time. + +Dep.target = null; +const targetStack = []; + +function pushTarget(_target) { + if (Dep.target) targetStack.push(Dep.target); + Dep.target = _target; +} + +function popTarget() { + Dep.target = targetStack.pop(); +} + +// 如果浏览器不支持ecma262v5的Object.defineProperties或者存在BUG,比如IE8 +// 标准浏览器使用__defineGetter__, __defineSetter__实现 +let canHideProperty = true; +try { + Object.defineProperty({}, "_", { + value: "x" + }); + delete $$skipArray.$vbsetter; + delete $$skipArray.$vbthis; +} catch (e) { + /* istanbul ignore next*/ + canHideProperty = false; +} + +let createViewModel = Object.defineProperties; +let defineProperty = void 0; + +/* istanbul ignore if*/ +if (!canHideProperty) { + if ("__defineGetter__" in {}) { + defineProperty = function defineProperty(obj, prop, desc) { + if ("value" in desc) { + obj[prop] = desc.value; + } + if ("get" in desc) { + obj.__defineGetter__(prop, desc.get); + } + if ("set" in desc) { + obj.__defineSetter__(prop, desc.set); + } + + return obj; + }; + createViewModel = function createViewModel(obj, descs) { + for (const prop in descs) { + if (descs.hasOwnProperty(prop)) { + defineProperty(obj, prop, descs[prop]); + } + } + + return obj; + }; + } +} + +const createViewModel$1 = createViewModel; + +const queue = []; +const activatedChildren = []; +let has = {}; +let waiting = false; +let flushing = false; +let index = 0; + +function resetSchedulerState() { + index = queue.length = activatedChildren.length = 0; + has = {}; + waiting = flushing = false; +} + +function flushSchedulerQueue() { + flushing = true; + let watcher = void 0, + id = void 0, + options = void 0; + + // Sort queue before flush. + // This ensures that: + // 1. Components are updated from parent to child. (because parent is always + // created before the child) + // 2. A component's user watchers are run before its render watcher (because + // user watchers are created before the render watcher) + // 3. If a component is destroyed during a parent component's watcher run, + // its watchers can be skipped. + queue.sort((a, b) => a.id - b.id); + + // do not cache length because more watchers might be pushed + // as we run existing watchers + for (index = 0; index < queue.length; index++) { + watcher = queue[index].watcher; + options = queue[index].options; + id = watcher.id; + has[id] = null; + watcher.run(options); + } + + resetSchedulerState(); +} + +function queueWatcher(watcher, options) { + const id = watcher.id; + if (has[id] == null) { + has[id] = true; + if (!flushing) { + queue.push({ watcher, options }); + } else { + // if already flushing, splice the watcher based on its id + // if already past its id, it will be run next immediately. + let i = queue.length - 1; + while (i > index && queue[i].watcher.id > watcher.id) { + i--; + } + queue.splice(i + 1, 0, { watcher, options }); + } + // queue the flush + if (!waiting) { + waiting = true; + nextTick(flushSchedulerQueue); + } + } +} + +let uid$1 = 0; + +const Watcher = (function () { + function Watcher(vm, expOrFn, cb, options) { + _classCallCheck(this, Watcher); + + this.vm = vm; + // vm._watchers || (vm._watchers = []) + // vm._watchers.push(this) + // options + if (options) { + this.deep = !!options.deep; + this.user = !!options.user; + this.lazy = !!options.lazy; + this.sync = !!options.sync; + } else { + this.deep = this.user = this.lazy = this.sync = false; + } + this.cb = cb; + this.id = ++uid$1; // uid for batching + this.active = true; + this.dirty = this.lazy; // for lazy watchers + this.deps = []; + this.newDeps = []; + this.depIds = new Set(); + this.newDepIds = new Set(); + this.expression = ""; + // parse expression for getter + if (typeof expOrFn === "function") { + this.getter = expOrFn; + } else { + this.getter = parsePath(expOrFn); + if (!this.getter) { + this.getter = function () {}; + } + } + this.value = this.lazy ? undefined : this.get(); + } + + Watcher.prototype.get = function get() { + pushTarget(this); + let value = void 0; + const vm = this.vm; + try { + value = this.getter.call(vm, vm); + } catch (e) { + // if (this.user) { + // } else { + // console.error(e) + // } + } finally { + // "touch" every property so they are all tracked as + // dependencies for deep watching + if (this.deep) { + traverse(value); + } + popTarget(); + this.cleanupDeps(); + } + + return value; + }; + + Watcher.prototype.addDep = function addDep(dep) { + const id = dep.id; + if (!this.newDepIds.has(id)) { + this.newDepIds.add(id); + this.newDeps.push(dep); + if (!this.depIds.has(id)) { + dep.addSub(this); + } + } + }; + + Watcher.prototype.cleanupDeps = function cleanupDeps() { + let i = this.deps.length; + while (i--) { + const dep = this.deps[i]; + if (!this.newDepIds.has(dep.id)) { + dep.removeSub(this); + } + } + let tmp = this.depIds; + this.depIds = this.newDepIds; + this.newDepIds = tmp; + this.newDepIds.clear(); + tmp = this.deps; + this.deps = this.newDeps; + this.newDeps = tmp; + this.newDeps.length = 0; + }; + + Watcher.prototype.update = function update(options) { + /* istanbul ignore else */ + if (this.lazy) { + this.dirty = true; + } else if (this.sync) { + this.run(options); + } else { + queueWatcher(this, options); + } + }; + + Watcher.prototype.run = function run(options) { + if (this.active) { + const value = this.get(); + if ( + value !== this.value || + // Deep watchers and watchers on Object/Arrays should fire even + // when the value is the same, because the value may + // have mutated. + (_.isObject(value) && options && options.refresh) || + this.deep + ) { + // set new value + const oldValue = this.value; + this.value = value; + if (this.user) { + try { + this.cb.call(this.vm, value, oldValue, options); + } catch (e) { + console.error(e); + } + } else { + try { + this.cb.call(this.vm, value, oldValue, options); + } catch (e) { + console.error(e); + } + } + } + } + }; + + Watcher.prototype.evaluate = function evaluate() { + this.value = this.get(); + this.dirty = false; + }; + + Watcher.prototype.depend = function depend() { + let i = this.deps.length; + while (i--) { + this.deps[i].depend(); + } + }; + + Watcher.prototype.teardown = function teardown() { + if (this.active) { + // remove self from vm's watcher list + // this is a somewhat expensive operation so we skip it + // if the vm is being destroyed. + remove(this.vm && this.vm._watchers, this); + let i = this.deps.length; + while (i--) { + this.deps[i].removeSub(this); + } + this.active = false; + } + }; + + return Watcher; +})(); + +const seenObjects = new Set(); + +function traverse(val) { + seenObjects.clear(); + _traverse(val, seenObjects); +} + +function _traverse(val, seen) { + let i = void 0, + keys = void 0; + const isA = _.isArray(val); + if (!isA && !_.isObject(val)) { + return; + } + if (val.__ob__) { + const depId = val.__ob__.dep.id; + if (seen.has(depId)) { + return; + } + seen.add(depId); + } + if (isA) { + i = val.length; + while (i--) { + _traverse(val[i], seen); + } + } else { + keys = _.keys(val); + i = keys.length; + while (i--) { + _traverse(val[keys[i]], seen); + } + } +} + +const arrayProto = Array.prototype; +const arrayMethods = []; +_.each( + ["push", "pop", "shift", "unshift", "splice", "sort", "reverse"], + (method) => { + const original = arrayProto[method]; + arrayMethods[method] = function mutator() { + for ( + var _len = arguments.length, args = Array(_len), _key2 = 0; + _key2 < _len; + _key2++ + ) { + args[_key2] = arguments[_key2]; + } + + const ob = this.__ob__; + let inserted = void 0; + switch (method) { + case "push": + case "unshift": + inserted = args; + break; + case "splice": + inserted = args.slice(2); + break; + } + if (inserted) inserted = ob.observeArray(inserted); + switch (method) { + case "push": + case "unshift": + args = inserted; + break; + case "splice": + if (args.length > 2) { + args = [args[0], args[1]].concat( + inserted ? inserted : [] + ); + } + break; + } + const result = original.apply(this, args); + notify(ob.parent, ob.parentKey, ob.dep, true); + + return result; + }; + } +); + +const arrayKeys = _.keys(arrayMethods); + +const observerState = { + shouldConvert: true +}; + +function def(obj, key, val, enumerable) { + Object.defineProperty(obj, key, { + value: val, + enumerable: !!enumerable, + writable: true, + configurable: true + }); +} + +/** + * Observer class that are attached to each observed + * object. Once attached, the observer converts target + * object's property keys into getter/setters that + * collect dependencies and dispatches updates. + */ + +const Observer = (function () { + function Observer(value) { + _classCallCheck(this, Observer); + + this.value = value; + this.dep = new Dep(); + this.vmCount = 0; + if (_.isArray(value)) { + const augment = hasProto ? protoAugment : copyAugment; + augment(value, arrayMethods, arrayKeys); + this.model = this.observeArray(value); + } else { + this.model = this.walk(value); + } + def(this.model, "__ob__", this); + } + + Observer.prototype.walk = function walk(obj) { + return defineReactive(obj, this); + }; + + Observer.prototype.observeArray = function observeArray(items) { + for (let i = 0, l = items.length; i < l; i++) { + const ob = observe(items[i], this, i); + items[i] = ob ? ob.model : items[i]; + } + + return items; + }; + + return Observer; +})(); + +function protoAugment(target, src, keys) { + /* eslint-disable no-proto */ + target.__proto__ = src; + /* eslint-enable no-proto */ +} + +/* istanbul ignore next */ +function copyAugment(target, src, keys) { + for (let i = 0, l = keys.length; i < l; i++) { + const key = keys[i]; + target[key] = src[key]; + } +} + +function observe(value, parentObserver, parentKey) { + if (!_.isObject(value)) { + return; + } + let ob = void 0; + if (value.__ob__ instanceof Observer) { + ob = value.__ob__; + } else if ( + observerState.shouldConvert && + isExtensible(value) && + (_.isArray(value) || isPlainObject(value)) + ) { + ob = new Observer(value); + } + if (ob) { + ob.parent = parentObserver || ob.parent; + ob.parentKey = parentKey; + } + + return ob; +} + +function notify(observer, key, dep, refresh) { + dep.notify({ observer, key, refresh }); + if (observer) { + // 触发a.*绑定的依赖 + _.each(observer._deps, (dep) => { + dep.notify({ observer, key }); + }); + // 触发a.**绑定的依赖 + let parent = observer, + root = observer, + route = key || ""; + while (parent) { + _.each(parent._scopeDeps, (dep) => { + dep.notify({ observer, key }); + }); + if (parent.parentKey != null) { + route = `${parent.parentKey}.${route}`; + } + root = parent; + parent = parent.parent; + } + for (const _key in root._globalDeps) { + const reg = new RegExp(_key); + if (reg.test(route)) { + for (let i = 0; i < root._globalDeps[_key].length; i++) { + root._globalDeps[_key][i].notify({ + observer, + key: _key + }); + } + } + } + } +} + +function defineReactive(obj, observer, shallow) { + const props = {}; + let model = void 0; + _.each(obj, (val, key) => { + if (key in $$skipArray) { + return; + } + const configurable = isConfigurable(obj, key); + const dep = (observer && observer[`__dep${key}`]) || new Dep(); + observer && (observer[`__dep${key}`] = dep); + let childOb = configurable && !shallow && observe(val, observer, key); + props[key] = { + enumerable: true, + configurable: true, + get: function reactiveGetter() { + const value = childOb ? childOb.model : val; + if (Dep.target) { + dep.depend(); + if (childOb) { + childOb.dep.depend(); + if (_.isArray(value)) { + dependArray(value); + } + } + } + + return value; + }, + set: function reactiveSetter(newVal) { + const value = childOb ? childOb.model : val; + if ( + newVal === value || + (newVal !== newVal && value !== value) + ) { + return; + } + val = newVal; + childOb = + configurable && !shallow && observe(newVal, observer, key); + if (childOb && value && value.__ob__) { + childOb._scopeDeps = value.__ob__._scopeDeps; + childOb._deps = value.__ob__._deps; + } + obj[key] = childOb ? childOb.model : newVal; + notify(model.__ob__, key, dep); + } + }; + }); + + return (model = createViewModel$1(obj, props)); +} + +function defineReactiveProperty(obj, key, val, shallow) { + const dep = new Dep(); + + const configurable = isConfigurable(obj, key); + if (!configurable) { + return; + } + + if (arguments.length === 2) { + val = obj[key]; + } + + let childOb = !shallow && observe(val); + Object.defineProperty(obj, key, { + enumerable: true, + configurable: true, + get: function reactiveGetter() { + const value = childOb ? childOb.model : val; + if (Dep.target) { + dep.depend(); + if (childOb) { + childOb.dep.depend(); + if (_.isArray(value)) { + dependArray(value); + } + } + } + + return value; + }, + set: function reactiveSetter(newVal) { + const value = childOb ? childOb.model : val; + if (newVal === value || (newVal !== newVal && value !== value)) { + return; + } + + childOb = configurable && !shallow && observe(newVal); + val = newVal; + obj[key] = childOb ? childOb.model : newVal; + dep.notify(); + } + }); +} + +/** + * Set a property on an object. Adds the new property and + * triggers change notification if the property doesn't + * already exist. + */ +function set(target, key, val) { + if (_.isArray(target)) { + target.length = Math.max(target.length, key); + target.splice(key, 1, val); + + return val; + } + if (_.has(target, key)) { + target[key] = val; + + return val; + } + const ob = target.__ob__; + if (!ob) { + target[key] = val; + + return val; + } + ob.value[key] = val; + target = defineReactive(ob.value, ob); + notify(ob, key, ob.dep); + + return target; +} + +function freeze() { + return Object.freeze.apply(null, arguments); +} + +/** + * Delete a property and trigger change if necessary. + */ +function del(target, key) { + if (_.isArray(target)) { + target.splice(key, 1); + + return; + } + const ob = target.__ob__; + if (!_.has(target, key)) { + return; + } + if (!ob) { + delete target[key]; + + return target; + } + delete ob.value[key]; + target = defineReactive(ob.value, ob); + notify(ob, key, ob.dep); + + return target; +} + +/** + * Collect dependencies on array elements when the array is touched, since + * we cannot intercept array element access like property getters. + */ +function dependArray(value) { + for (var e, i = 0, l = value.length; i < l; i++) { + e = value[i]; + e && e.__ob__ && e.__ob__.dep.depend(); + if (_.isArray(e)) { + dependArray(e); + } + } +} + +let falsy$1; +const operators = { + "||": falsy$1, + "&&": falsy$1, + "(": falsy$1, + ")": falsy$1 +}; + +function runBinaryFunction(binarys) { + let expr = ""; + for (let i = 0, len = binarys.length; i < len; i++) { + if (_.isBoolean(binarys[i]) || _.has(operators, binarys[i])) { + expr += binarys[i]; + } else { + expr += "false"; + } + } + + return new Function(`return ${expr}`)(); +} + +function routeToRegExp(route) { + route = route + .replace(/\*\*/g, "[a-zA-Z0-9_]+") + .replace(/\*./g, "[a-zA-Z0-9_]+."); + + return `^${route}$`; +} + +function watch(model, expOrFn, cb, options) { + if (isPlainObject(cb)) { + options = cb; + cb = cb.handler; + } + if (typeof cb === "string") { + cb = model[cb]; + } + options = options || {}; + options.user = true; + let exps = void 0; + if ( + _.isFunction(expOrFn) || + !(exps = expOrFn.match(/[a-zA-Z0-9_.*]+|[|][|]|[&][&]|[(]|[)]/g)) || + (exps.length === 1 && expOrFn.indexOf("*") < 0) + ) { + const watcher = new Watcher(model, expOrFn, cb, options); + if (options.immediate) { + cb(watcher.value); + } + + return function unwatchFn() { + watcher.teardown(); + }; + } + const watchers = []; + let fns = exps.slice(); + let complete = false, + running = false; + const callback = function callback(index, newValue, oldValue, attrs) { + if (complete === true) { + return; + } + fns[index] = true; + if (runBinaryFunction(fns)) { + complete = true; + cb(newValue, oldValue, attrs); + } + if (options && options.sync) { + complete = false; + running = false; + fns = exps.slice(); + } else { + if (!running) { + running = true; + nextTick(() => { + complete = false; + running = false; + fns = exps.slice(); + }); + } + } + }; + _.each(exps, (exp, i) => { + if (_.has(operators, exp)) { + return; + } + if (exp.indexOf("*") >= 0) { + // a.**或a.*形式 + if ( + /^[1-9a-zA-Z.]+(\*\*$|\*$)/.test(exp) || + exp === "**" || + exp === "*" + ) { + const isGlobal = exp.indexOf("**") >= 0; + if (isGlobal) { + // a.**的形式 + exp = exp.replace(".**", ""); + } else { + // a.*的形式 + exp = exp.replace(".*", ""); + } + const getter = + exp === "**" || exp === "*" + ? function (m) { + return m; + } + : parsePath(exp); + const v = getter.call(model, model); + const _dep = new Dep(); + if (isGlobal) { + (v.__ob__._scopeDeps || (v.__ob__._scopeDeps = [])).push( + _dep + ); + } else { + (v.__ob__._deps || (v.__ob__._deps = [])).push(_dep); + } + const _w = new Watcher( + model, + () => { + _dep.depend(); + + return NaN; + }, + (newValue, oldValue, attrs) => { + callback( + i, + newValue, + oldValue, + _.extend({ index: i }, attrs) + ); + }, + options + ); + watchers.push(() => { + _w.teardown(); + v.__ob__._scopeDeps && remove(v.__ob__._scopeDeps, _dep); + v.__ob__._deps && remove(v.__ob__._deps, _dep); + }); + + return; + } + // **.a.**的情况,场景:a.b.c, 如果用b.**监听, a被重新赋值b上的_scopeDes就不存在了 + if (/^(\*\*\.)+[1-9a-zA-Z]+(\.\*\*$)/.test(exp)) { + // 先获取到能获取到的对象 + const _paths = exp.split("."); + const _currentModel = model[_paths[1]]; + exp = `${_paths[1]}.**`; + // 补全路径 + let _parent = _currentModel.__ob__.parent, + _root = _currentModel.__ob__; + while (_parent) { + exp = `*.${exp}`; + _root = _parent; + _parent = _parent.parent; + } + const _regStr = routeToRegExp(exp); + const _dep2 = new Dep(); + _root._globalDeps || (_root._globalDeps = {}); + if (_.isArray(_root._globalDeps[_regStr])) { + _root._globalDeps[_regStr].push(_dep2); + } else { + _root._globalDeps[_regStr] = [_dep2]; + } + + const _w2 = new Watcher( + _currentModel, + () => { + _dep2.depend(); + + return NaN; + }, + (newValue, oldValue, attrs) => { + callback( + i, + newValue, + oldValue, + _.extend({ index: i }, attrs) + ); + }, + options + ); + watchers.push(() => { + if (_root._globalDeps) { + remove(_root._globalDeps[_regStr], _dep2); + + if (_root._globalDeps[_regStr].length === 0) { + delete _root._globalDeps[_regStr]; + _w2.teardown(); + } + } + }); + + return; + } + // 再有结尾有*的就不支持了 + if (exp[exp.length - 1] === "*") { + throw new Error("not support"); + } + // 其他含有*的情况,如*.a,*.*.a,a.*.a + let currentModel = model; + // 先获取到能获取到的对象 + const paths = exp.split("."); + for (let _i = 0, len = paths.length; _i < len; _i++) { + if (paths[_i] === "*") { + break; + } + currentModel = model[paths[_i]]; + } + exp = exp.substr(exp.indexOf("*")); + // 补全路径 + let parent = currentModel.__ob__.parent, + root = currentModel.__ob__; + while (parent) { + exp = `*.${exp}`; + root = parent; + parent = parent.parent; + } + const regStr = routeToRegExp(exp); + const dep = new Dep(); + root._globalDeps || (root._globalDeps = {}); + if (_.isArray(root._globalDeps[regStr])) { + root._globalDeps[regStr].push(dep); + } else { + root._globalDeps[regStr] = [dep]; + } + + const w = new Watcher( + currentModel, + () => { + dep.depend(); + + return NaN; + }, + (newValue, oldValue, attrs) => { + callback( + i, + newValue, + oldValue, + _.extend({ index: i }, attrs) + ); + }, + options + ); + watchers.push(() => { + if (root._globalDeps) { + remove(root._globalDeps[regStr], dep); + if (root._globalDeps[regStr].length === 0) { + delete root._globalDeps[regStr]; + w.teardown(); + } + } + }); + + return; + } + const watcher = new Watcher( + model, + exp, + (newValue, oldValue, attrs) => { + callback(i, newValue, oldValue, _.extend({ index: i }, attrs)); + }, + options + ); + watchers.push(() => { + watcher.teardown(); + }); + }); + + return watchers; +} + +const mixinInjection = {}; + +function getMixins(type) { + return mixinInjection[type]; +} + +function mixin(xtype, cls) { + mixinInjection[xtype] = _.cloneDeep(cls); +} + +const computedWatcherOptions = { lazy: true }; +let REACTIVE = true; + +function initState(vm, state) { + if (state) { + vm.$$state = REACTIVE ? observe(state).model : state; + } +} + +function initComputed(vm, computed) { + const watchers = (vm._computedWatchers = {}); + defineComputed(vm, computed); + for (const key in computed) { + watchers[key] = defineComputedWatcher(vm, computed[key]); + } +} + +function defineComputedWatcher(vm, userDef) { + const context = vm.$$model ? vm.model : vm; + const getter = typeof userDef === "function" ? userDef : userDef.get; + + return new Watcher(context, getter || noop, noop, computedWatcherOptions); +} + +function defineOneComputedGetter(vm, key, userDef) { + const shouldCache = true; + const sharedPropertyDefinition = { + enumerable: true, + configurable: true, + get: noop, + set: noop + }; + if (typeof userDef === "function") { + sharedPropertyDefinition.get = createComputedGetter(vm, key); + sharedPropertyDefinition.set = noop; + } else { + sharedPropertyDefinition.get = userDef.get + ? shouldCache && userDef.cache !== false + ? createComputedGetter(vm, key) + : userDef.get + : noop; + sharedPropertyDefinition.set = userDef.set ? userDef.set : noop; + } + + return sharedPropertyDefinition; +} + +function defineComputed(vm, computed) { + const props = {}; + for (const key in computed) { + if (!(key in vm)) { + props[key] = defineOneComputedGetter(vm, key, computed[key]); + } + } + vm.$$computed = createViewModel$1({}, props); +} + +function createComputedGetter(vm, key) { + return function computedGetter() { + const watcher = vm._computedWatchers && vm._computedWatchers[key]; + if (watcher) { + if (watcher.dirty) { + watcher.evaluate(); + } + if (REACTIVE && Dep.target) { + watcher.depend(); + } + + return watcher.value; + } + }; +} + +function initWatch(vm, watch$$1) { + vm._watchers || (vm._watchers = []); + for (const key in watch$$1) { + const handler = watch$$1[key]; + if (_.isArray(handler)) { + for (let i = 0; i < handler.length; i++) { + vm._watchers.push(createWatcher(vm, key, handler[i])); + } + } else { + vm._watchers.push(createWatcher(vm, key, handler)); + } + } +} + +function createWatcher(vm, keyOrFn, cb, options) { + if (isPlainObject(cb)) { + options = cb; + cb = cb.handler; + } + if (typeof cb === "string") { + cb = vm[cb]; + } + + return watch( + vm.model, + keyOrFn, + _.bind(cb, vm.$$model ? vm.model : vm), + options + ); +} + +function initMethods(vm, methods) { + for (const key in methods) { + vm[key] = + methods[key] == null + ? noop + : _.bind(methods[key], vm.$$model ? vm.model : vm); + } +} + +function initMixins(vm, mixins) { + mixins = (mixins || []).slice(0); + + _.each(mixins.reverse(), (mixinType) => { + const mixin$$1 = getMixins(mixinType); + + for (const key in mixin$$1) { + if (typeof mixin$$1[key] !== "function") continue; + + if (_.has(vm, key)) continue; + + vm[key] = _.bind(mixin$$1[key], vm.$$model ? vm.model : vm); + } + }); +} + +function defineProps(vm, keys) { + const props = {}; + // if (typeof Proxy === 'function') { + // return vm.model = new Proxy(props, { + // has: function (target, key) { + // return keys.indexOf(key) > -1; + // }, + // get: function (target, key) { + // if (key in $$skipArray) { + // return props[key] + // } + // if (vm.$$computed && key in vm.$$computed) { + // return vm.$$computed[key] + // } + // if (vm.$$state && key in vm.$$state) { + // return vm.$$state[key] + // } + // return vm.$$model[key] + // }, + // set: function (target, key, val) { + // if (key in $$skipArray) { + // return props[key] = val + // } + // if (vm.$$state && key in vm.$$state) { + // return vm.$$state[key] = val + // } + // if (vm.$$model && key in vm.$$model) { + // return vm.$$model[key] = val + // } + // } + // }) + // } + + const _loop = function _loop(i, len) { + const key = keys[i]; + if (!(key in $$skipArray)) { + props[key] = { + enumerable: true, + configurable: true, + get: function get() { + if (vm.$$computed && key in vm.$$computed) { + return vm.$$computed[key]; + } + if (vm.$$state && key in vm.$$state) { + return vm.$$state[key]; + } + if (vm.$$model && key in vm.$$model) { + return vm.$$model[key]; + } + let p = vm._parent; + while (p) { + if (p.$$context && key in p.$$context) { + return p.$$context[key]; + } + p = p._parent; + } + }, + set: function set(val) { + if (vm.$$state && key in vm.$$state) { + return (vm.$$state[key] = val); + } + if (vm.$$model && key in vm.$$model) { + return (vm.$$model[key] = val); + } + let p = vm._parent; + while (p) { + if (p.$$context && key in p.$$context) { + return (p.$$context[key] = val); + } + p = p._parent; + } + } + }; + } + }; + + for (let i = 0, len = keys.length; i < len; i++) { + _loop(i, len); + } + vm.model = createViewModel$1({}, props); +} + +function defineContext(vm, keys) { + const props = {}; + + const _loop2 = function _loop2(i, len) { + const key = keys[i]; + if (!(key in $$skipArray)) { + props[key] = { + enumerable: true, + configurable: true, + get: function get() { + return vm.model[key]; + }, + set: function set(val) { + return (vm.model[key] = val); + } + }; + } + }; + + for (let i = 0, len = keys.length; i < len; i++) { + _loop2(i, len); + } + vm.$$context = createViewModel$1({}, props); +} + +function getInjectValue(vm, key) { + let p = vm._parent; + while (p) { + if (p.$$context && key in p.$$context) { + return p.$$context[key]; + } + p = p._parent; + } +} + +function getInjectValues(vm) { + const inject = vm.inject || []; + const result = {}; + _.each(inject, (key) => { + result[key] = getInjectValue(vm, key); + }); + + return result; +} + +const Model = (function () { + function Model() { + _classCallCheck(this, Model); + } + + Model.prototype._constructor = function _constructor( + model, + destroyHandler + ) { + if (model instanceof Observer || model instanceof Model) { + model = model.model; + } + if (model && model.__ob__) { + this.$$model = model; + } else { + this.options = model || {}; + } + this._parent = Model.target; + const state = _.isFunction(this.state) ? this.state() : this.state; + const computed = this.computed; + const context = this.context; + const inject = this.inject; + const childContext = this.childContext; + const provide = this.provide; + const watch$$1 = this.watch; + const actions = this.actions; + const keys = _.keys(this.$$model) + .concat(_.keys(state)) + .concat(_.keys(computed)) + .concat(inject || []) + .concat(context || []); + const mixins = this.mixins; + defineProps(this, keys); + // deprecated + childContext && defineContext(this, childContext); + provide && defineContext(this, provide); + this.$$model && (this.model.__ob__ = this.$$model.__ob__); + initMixins(this, mixins); + this.init(); + initState(this, _.extend(getInjectValues(this), state)); + initComputed(this, computed); + REACTIVE && initWatch(this, watch$$1); + initMethods(this, actions); + this.created && this.created(); + this._destroyHandler = destroyHandler; + if (this.$$model) { + return this.model; + } + }; + + Model.prototype._init = function _init() {}; + + Model.prototype.init = function init() { + this._init(); + }; + + Model.prototype.destroy = function destroy() { + for (const _key3 in this._computedWatchers) { + this._computedWatchers[_key3].teardown(); + } + _.each(this._watchers, (unwatches) => { + unwatches = _.isArray(unwatches) ? unwatches : [unwatches]; + _.each(unwatches, (unwatch) => { + unwatch(); + }); + }); + this._watchers && (this._watchers = []); + this.destroyed && this.destroyed(); + this.$$model = null; + this.$$computed = null; + this.$$state = null; + this._destroyHandler && this._destroyHandler(); + }; + + return Model; +})(); + +function define(model) { + return REACTIVE ? new Observer(model).model : model; +} + +const reactive = define; + +function config(options) { + options || (options = {}); + if ("reactive" in options) { + REACTIVE = options.reactive; + } +} + +function toJSON(model) { + let result = void 0; + if (_.isArray(model)) { + result = []; + for (let i = 0, len = model.length; i < len; i++) { + result[i] = toJSON(model[i]); + } + } else if (model && isPlainObject(model)) { + result = {}; + for (const _key4 in model) { + if (!_.has($$skipArray, _key4)) { + result[_key4] = toJSON(model[_key4]); + } + } + } else { + result = model; + } + + return result; +} + +const version = "2.0"; + +export const Fix = { + version, + $$skipArray, + mixin, + Model, + define, + reactive, + config, + observerState, + Observer, + observe, + notify, + defineReactive, + defineReactiveProperty, + set, + freeze, + del, + Watcher, + pushTarget, + popTarget, + watch, + toJSON +}; diff --git a/src/fix/index.js b/src/fix/index.js new file mode 100644 index 000000000..a4596c87d --- /dev/null +++ b/src/fix/index.js @@ -0,0 +1,2 @@ +export * from "./fix"; +export * from "./fix.compact"; diff --git a/src/index.js b/src/index.js index 2f3f17564..90bde9f4b 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,8 @@ +import "./polyfill"; export * from "./core"; export * from "./base"; export * from "./case"; export * from "./widget"; export * from "./component"; + +export * from "./fix"; diff --git a/src/polyfill/console.js b/src/polyfill/console.js deleted file mode 100644 index 8687d9b9e..000000000 --- a/src/polyfill/console.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * 特殊情况 - * Created by wang on 15/6/23. - */ -// 解决console未定义问题 guy -_global.console = _global.console || (function () { - var c = {}; - c.log = c.warn = c.debug = c.info = c.error = c.time = c.dir = c.profile - = c.clear = c.exception = c.trace = c.assert = function () { - }; - return c; -})(); diff --git a/src/polyfill/index.js b/src/polyfill/index.js index 578b1573b..3f5cde431 100644 --- a/src/polyfill/index.js +++ b/src/polyfill/index.js @@ -1,4 +1,3 @@ -export * from "./console"; export * from "./event"; export * from "./number"; export * from "./sort"; \ No newline at end of file