From 8156a9ef6fbfb52d43524213b952c587d824cc1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Wed, 17 Apr 2019 11:44:35 +0200 Subject: [PATCH] [IMP] owl: update lib + update samples --- libs/owl.js | 313 ++++++++++++++++++++++++------------------------- src/app.js | 25 ++-- src/samples.js | 20 ++-- 3 files changed, 174 insertions(+), 184 deletions(-) diff --git a/libs/owl.js b/libs/owl.js index b621abba..cfc8af5e 100644 --- a/libs/owl.js +++ b/libs/owl.js @@ -662,6 +662,138 @@ } } + //------------------------------------------------------------------------------ + // Observer + //------------------------------------------------------------------------------ + const methodsToPatch = [ + "push", + "pop", + "shift", + "unshift", + "splice", + "sort", + "reverse" + ]; + const ArrayProto = Array.prototype; + const ModifiedArrayProto = Object.create(ArrayProto); + for (let method of methodsToPatch) { + const initialMethod = ArrayProto[method]; + ModifiedArrayProto[method] = function (...args) { + this.__observer__.notifyChange(); + this.__owl__.rev++; + let parent = this; + do { + parent.__owl__.deepRev++; + } while ((parent = parent.__owl__.parent)); + let inserted; + switch (method) { + case "push": + case "unshift": + inserted = args; + break; + case "splice": + inserted = args.slice(2); + break; + } + if (inserted) { + for (let elem of inserted) { + this.__observer__.observe(elem, this); + } + } + return initialMethod.call(this, ...args); + }; + } + class Observer { + constructor() { + this.rev = 1; + this.allowMutations = true; + this.dirty = false; + } + notifyCB() { } + notifyChange() { + this.rev++; + this.dirty = true; + Promise.resolve().then(() => { + if (this.dirty) { + this.dirty = false; + this.notifyCB(); + } + }); + } + observe(value, parent) { + if (value === null) { + // fun fact: typeof null === 'object' + return; + } + if (typeof value !== "object") { + return; + } + if ("__owl__" in value) { + // already observed + return; + } + if (Array.isArray(value)) { + this._observeArr(value, parent); + } + else { + this._observeObj(value, parent); + } + } + set(target, key, value) { + this._addProp(target, key, value); + target.__owl__.rev++; + this.notifyChange(); + } + unobserve(target) { + if (target !== null && typeof target === "object") { + delete target.__owl__; + } + } + _observeObj(obj, parent) { + const keys = Object.keys(obj); + obj.__owl__ = { rev: 1, deepRev: 1, parent }; + Object.defineProperty(obj, "__owl__", { enumerable: false }); + for (let key of keys) { + this._addProp(obj, key, obj[key]); + } + } + _observeArr(arr, parent) { + arr.__owl__ = { rev: 1, deepRev: 1, parent }; + Object.defineProperty(arr, "__owl__", { enumerable: false }); + arr.__proto__ = Object.create(ModifiedArrayProto); + arr.__proto__.__observer__ = this; + for (let i = 0; i < arr.length; i++) { + this.observe(arr[i], arr); + } + } + _addProp(obj, key, value) { + var self = this; + Object.defineProperty(obj, key, { + enumerable: true, + get() { + return value; + }, + set(newVal) { + if (!self.allowMutations) { + throw new Error(`State cannot be changed outside a mutation! (key: "${key}", val: "${newVal}")`); + } + if (newVal !== value) { + self.unobserve(value); + value = newVal; + self.observe(newVal, obj); + obj.__owl__.rev++; + let parent = obj; + do { + parent.__owl__.deepRev++; + } while ((parent = parent.__owl__.parent)); + self.notifyChange(); + } + } + }); + this.observe(value, obj); + } + } + function escape(str) { if (str === undefined) { return ""; @@ -874,7 +1006,8 @@ renderId: 1, renderPromise: null, renderProps: props || null, - boundHandlers: {} + boundHandlers: {}, + observer: new Observer() }; } get el() { @@ -920,9 +1053,6 @@ * It is not called on the initial render. This is useful to get some * information which are in the DOM. For example, the current position of the * scrollbar - * - * Note that at this point, it is not safe to rerender the widget. In - * particular, updateState calls should be avoided. */ willPatch() { } /** @@ -977,6 +1107,7 @@ } this._patch(vnode); target.appendChild(this.el); + this._observeState(); if (document.body.contains(target)) { this._visitSubTree(w => { if (!w.__owl__.isMounted && this.el.contains(w.el)) { @@ -1056,31 +1187,11 @@ if (this.__owl__.isMounted) { await this.render(true); } - this.patched(); } async updateProps(nextProps, forceUpdate = false) { const shouldUpdate = forceUpdate || this.shouldUpdate(nextProps); return shouldUpdate ? this._updateProps(nextProps) : Promise.resolve(); } - /** - * This is the safest update method for widget: its job is to update the state - * and rerender (if widget is mounted). - * - * Notes: - * - it checks if we do not add extra keys to the state. - * - it is ok to call updateState before the widget is started. In that - * case, it will simply update the state and will not rerender - */ - async updateState(nextState) { - if (Object.keys(nextState).length === 0) { - return; - } - Object.assign(this.state, nextState); - if (this.__owl__.isStarted) { - await this.render(); - } - this.patched(); - } //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- @@ -1088,13 +1199,13 @@ await this.willUpdateProps(nextProps); this.props = nextProps; await this.render(); - this.patched(); } _patch(vnode) { this.__owl__.renderPromise = null; if (this.__owl__.vnode) { this.willPatch(); this.__owl__.vnode = patch$1(this.__owl__.vnode, vnode); + this.patched(); } else { this.__owl__.vnode = patch$1(document.createElement(vnode.sel), vnode); @@ -1145,6 +1256,7 @@ if (this.__owl__.isMounted) { return; } + this._observeState(); if (this.__owl__.parent) { if (this.__owl__.parent.__owl__.isMounted) { this.__owl__.isMounted = true; @@ -1165,6 +1277,12 @@ } } } + _observeState() { + if (Object.keys(this.state).length) { + this.__owl__.observer.observe(this.state); + this.__owl__.observer.notifyCB = this.render.bind(this); + } + } } const RESERVED_WORDS = "true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,this,typeof,eval,void,Math,RegExp,Array,Object,Date".split(","); @@ -1938,6 +2056,9 @@ ctx.addLine(`context.${name} = _${keysID}[i];`); ctx.addLine(`context.${name}_value = _${valuesID}[i];`); const nodeCopy = node.cloneNode(true); + if (nodeCopy.tagName !== "t" && !nodeCopy.hasAttribute("t-key")) { + console.warn(`Directive t-foreach should always be used with a t-key! (in template: '${ctx.templateName}')`); + } nodeCopy.removeAttribute("t-foreach"); qweb._compileNode(nodeCopy, ctx); ctx.dedent(); @@ -2066,142 +2187,24 @@ } }; - const methodsToPatch = [ - "push", - "pop", - "shift", - "unshift", - "splice", - "sort", - "reverse" - ]; - const ArrayProto = Array.prototype; - const ModifiedArrayProto = Object.create(ArrayProto); - for (let method of methodsToPatch) { - const initialMethod = ArrayProto[method]; - ModifiedArrayProto[method] = function (...args) { - this.__observer__.rev++; - this.__owl__.rev++; - let parent = this; - do { - parent.__owl__.deepRev++; - } while ((parent = parent.__owl__.parent)); - let inserted; - switch (method) { - case "push": - case "unshift": - inserted = args; - break; - case "splice": - inserted = args.slice(2); - break; - } - if (inserted) { - for (let elem of inserted) { - this.__observer__.observe(elem, this); - } - } - return initialMethod.call(this, ...args); - }; - } - function makeObserver() { - const observer = { - rev: 1, - allowMutations: true, - observe: observe, - set: set - }; - function set(target, key, value) { - addProp(target, key, value); - target.__owl__.rev++; - observer.rev++; - } - function addProp(obj, key, value) { - Object.defineProperty(obj, key, { - enumerable: true, - get() { - return value; - }, - set(newVal) { - if (!observer.allowMutations) { - throw new Error(`State cannot be changed outside a mutation! (key: "${key}", val: "${newVal}")`); - } - if (newVal !== value) { - unobserve(value); - value = newVal; - observe(newVal, obj); - obj.__owl__.rev++; - observer.rev++; - let parent = obj; - do { - parent.__owl__.deepRev++; - } while ((parent = parent.__owl__.parent)); - } - } - }); - observe(value, obj); - } - function observeObj(obj, parent) { - const keys = Object.keys(obj); - obj.__owl__ = { rev: 1, deepRev: 1, parent }; - Object.defineProperty(obj, "__owl__", { enumerable: false }); - for (let key of keys) { - addProp(obj, key, obj[key]); - } - } - function observeArr(arr, parent) { - arr.__owl__ = { rev: 1, deepRev: 1, parent }; - Object.defineProperty(arr, "__owl__", { enumerable: false }); - arr.__proto__ = Object.create(ModifiedArrayProto); - arr.__proto__.__observer__ = observer; - for (let i = 0; i < arr.length; i++) { - observe(arr[i], arr); - } - } - function observe(value, parent) { - if (value === null) { - // fun fact: typeof null === 'object' - return; - } - if (typeof value !== "object") { - return; - } - if ("__owl__" in value) { - // already observed - return; - } - if (Array.isArray(value)) { - observeArr(value, parent); - } - else { - observeObj(value, parent); - } - } - function unobserve(target) { - if (target !== null && typeof target === "object") { - delete target.__owl__; - } - } - return observer; - } - class Store extends EventBus { constructor(config, options = {}) { super(); this._commitLevel = 0; - this._isMutating = false; this.history = []; this.debug = options.debug || false; this.state = config.state || {}; this.actions = config.actions; this.mutations = config.mutations; this.env = config.env; - this.observer = makeObserver(); + this.observer = new Observer(); + this.observer.notifyCB = this.trigger.bind(this, "update"); this.observer.allowMutations = false; this.observer.observe(this.state); if (this.debug) { this.history.push({ state: this.state }); } + this.set = this.observer.set.bind(this.observer); } dispatch(action, payload) { if (!this.actions[action]) { @@ -2225,13 +2228,11 @@ throw new Error(`[Error] mutation ${type} is undefined`); } this._commitLevel++; - const currentRev = this.observer.rev; - this._isMutating = true; this.observer.allowMutations = true; const res = this.mutations[type].call(null, { commit: this.commit.bind(this), state: this.state, - set: this.observer.set + set: this.set }, payload); if (this._commitLevel === 1) { this.observer.allowMutations = false; @@ -2242,14 +2243,6 @@ payload: payload }); } - Promise.resolve().then(() => { - if (this._isMutating) { - this._isMutating = false; - if (currentRev !== this.observer.rev) { - this.trigger("update", this.state); - } - } - }); } this._commitLevel--; return res; @@ -2365,9 +2358,9 @@ exports.connect = connect; exports.Store = Store; - exports._version = '0.6.0'; - exports._date = '2019-04-16T15:27:25.412Z'; - exports._hash = 'e19540f'; + exports._version = '0.7.0'; + exports._date = '2019-04-17T09:08:09.148Z'; + exports._hash = '4807389'; exports._url = 'https://github.com/odoo/owl'; }(this.owl = this.owl || {})); diff --git a/src/app.js b/src/app.js index 71600deb..9851e280 100644 --- a/src/app.js +++ b/src/app.js @@ -76,7 +76,7 @@ class TabbedEditor extends Component { const mode = MODES[tab]; this.editor.session.setMode(mode); - this.updateState({ currentTab: tab }); + this.state.currentTab = tab; } onMouseDown(ev) { @@ -105,7 +105,7 @@ const TEMPLATE = `