[IMP] owl: update library

This commit is contained in:
Géry Debongnie
2019-04-16 10:20:59 +02:00
parent a9376670b5
commit 7d55724839
+254 -142
View File
@@ -857,12 +857,12 @@
if (parent instanceof Component) { if (parent instanceof Component) {
p = parent; p = parent;
this.env = parent.env; this.env = parent.env;
parent.__widget__.children[id] = this; parent.__owl__.children[id] = this;
} }
else { else {
this.env = parent; this.env = parent;
} }
this.__widget__ = { this.__owl__ = {
id: id, id: id,
vnode: null, vnode: null,
isStarted: false, isStarted: false,
@@ -878,7 +878,7 @@
}; };
} }
get el() { get el() {
return this.__widget__.vnode ? this.__widget__.vnode.elm : null; return this.__owl__.vnode ? this.__owl__.vnode.elm : null;
} }
/** /**
* willStart is an asynchronous hook that can be implemented to perform some * willStart is an asynchronous hook that can be implemented to perform some
@@ -971,7 +971,7 @@
} }
async mount(target) { async mount(target) {
const vnode = await this._prepare(); const vnode = await this._prepare();
if (this.__widget__.isDestroyed) { if (this.__owl__.isDestroyed) {
// widget was destroyed before we get here... // widget was destroyed before we get here...
return; return;
} }
@@ -979,8 +979,8 @@
target.appendChild(this.el); target.appendChild(this.el);
if (document.body.contains(target)) { if (document.body.contains(target)) {
this._visitSubTree(w => { this._visitSubTree(w => {
if (!w.__widget__.isMounted && this.el.contains(w.el)) { if (!w.__owl__.isMounted && this.el.contains(w.el)) {
w.__widget__.isMounted = true; w.__owl__.isMounted = true;
w.mounted(); w.mounted();
return true; return true;
} }
@@ -991,9 +991,9 @@
unmount() { unmount() {
if (this.el) { if (this.el) {
this._visitSubTree(w => { this._visitSubTree(w => {
if (w.__widget__.isMounted) { if (w.__owl__.isMounted) {
w.willUnmount(); w.willUnmount();
w.__widget__.isMounted = false; w.__owl__.isMounted = false;
return true; return true;
} }
return false; return false;
@@ -1002,38 +1002,38 @@
} }
} }
async render(force = false) { async render(force = false) {
if (this.__widget__.isDestroyed) { if (this.__owl__.isDestroyed) {
return; return;
} }
const renderVDom = this._render(force); const renderVDom = this._render(force);
const renderId = this.__widget__.renderId; const renderId = this.__owl__.renderId;
const vnode = await renderVDom; const vnode = await renderVDom;
if (renderId === this.__widget__.renderId) { if (renderId === this.__owl__.renderId) {
// we only update the vnode and the actual DOM if no other rendering // we only update the vnode and the actual DOM if no other rendering
// occurred between now and when the render method was initially called. // occurred between now and when the render method was initially called.
this._patch(vnode); this._patch(vnode);
} }
} }
destroy() { destroy() {
if (!this.__widget__.isDestroyed) { if (!this.__owl__.isDestroyed) {
for (let id in this.__widget__.children) { for (let id in this.__owl__.children) {
this.__widget__.children[id].destroy(); this.__owl__.children[id].destroy();
} }
if (this.__widget__.isMounted) { if (this.__owl__.isMounted) {
this.willUnmount(); this.willUnmount();
} }
if (this.el) { if (this.el) {
this.el.remove(); this.el.remove();
this.__widget__.isMounted = false; this.__owl__.isMounted = false;
delete this.__widget__.vnode; delete this.__owl__.vnode;
} }
if (this.__widget__.parent) { if (this.__owl__.parent) {
let id = this.__widget__.id; let id = this.__owl__.id;
delete this.__widget__.parent.__widget__.children[id]; delete this.__owl__.parent.__owl__.children[id];
this.__widget__.parent = null; this.__owl__.parent = null;
} }
this.clear(); this.clear();
this.__widget__.isDestroyed = true; this.__owl__.isDestroyed = true;
} }
} }
shouldUpdate(nextProps) { shouldUpdate(nextProps) {
@@ -1049,20 +1049,16 @@
* mode or not. * mode or not.
*/ */
async updateEnv(nextEnv) { async updateEnv(nextEnv) {
if (this.__widget__.parent && this.__widget__.parent.env === this.env) { if (this.__owl__.parent && this.__owl__.parent.env === this.env) {
this.env = Object.create(this.env); this.env = Object.create(this.env);
} }
Object.assign(this.env, nextEnv); Object.assign(this.env, nextEnv);
if (this.__widget__.isMounted) { if (this.__owl__.isMounted) {
await this.render(true); await this.render(true);
} }
this.patched(); this.patched();
} }
async updateProps(nextProps, forceUpdate = false) { async updateProps(nextProps, forceUpdate = false) {
if (nextProps === this.__widget__.renderProps && !forceUpdate) {
await this.__widget__.renderPromise;
return;
}
const shouldUpdate = forceUpdate || this.shouldUpdate(nextProps); const shouldUpdate = forceUpdate || this.shouldUpdate(nextProps);
return shouldUpdate ? this._updateProps(nextProps) : Promise.resolve(); return shouldUpdate ? this._updateProps(nextProps) : Promise.resolve();
} }
@@ -1080,7 +1076,7 @@
return; return;
} }
Object.assign(this.state, nextState); Object.assign(this.state, nextState);
if (this.__widget__.isStarted) { if (this.__owl__.isStarted) {
await this.render(); await this.render();
} }
this.patched(); this.patched();
@@ -1095,36 +1091,36 @@
this.patched(); this.patched();
} }
_patch(vnode) { _patch(vnode) {
this.__widget__.renderPromise = null; this.__owl__.renderPromise = null;
if (this.__widget__.vnode) { if (this.__owl__.vnode) {
this.willPatch(); this.willPatch();
this.__widget__.vnode = patch$1(this.__widget__.vnode, vnode); this.__owl__.vnode = patch$1(this.__owl__.vnode, vnode);
} }
else { else {
this.__widget__.vnode = patch$1(document.createElement(vnode.sel), vnode); this.__owl__.vnode = patch$1(document.createElement(vnode.sel), vnode);
} }
} }
async _prepare() { async _prepare() {
this.__widget__.renderProps = this.props; this.__owl__.renderProps = this.props;
this.__widget__.renderPromise = this.willStart().then(() => { this.__owl__.renderPromise = this.willStart().then(() => {
if (this.__widget__.isDestroyed) { if (this.__owl__.isDestroyed) {
return Promise.resolve(h("div")); return Promise.resolve(h("div"));
} }
this.__widget__.isStarted = true; this.__owl__.isStarted = true;
if (this.inlineTemplate) { if (this.inlineTemplate) {
this.env.qweb.addTemplate(this.inlineTemplate, this.inlineTemplate, true); this.env.qweb.addTemplate(this.inlineTemplate, this.inlineTemplate, true);
} }
return this._render(); return this._render();
}); });
return this.__widget__.renderPromise; return this.__owl__.renderPromise;
} }
async _render(force = false) { async _render(force = false) {
this.__widget__.renderId++; this.__owl__.renderId++;
const promises = []; const promises = [];
const template = this.inlineTemplate || this.template; const template = this.inlineTemplate || this.template;
let vnode = this.env.qweb.render(template, this, { let vnode = this.env.qweb.render(template, this, {
promises, promises,
handlers: this.__widget__.boundHandlers, handlers: this.__owl__.boundHandlers,
forceUpdate: force forceUpdate: force
}); });
// this part is critical for the patching process to be done correctly. The // this part is critical for the patching process to be done correctly. The
@@ -1132,28 +1128,28 @@
// will update its own vnode representation without the knowledge of the // will update its own vnode representation without the knowledge of the
// parent widget. With this, we make sure that the parent widget will be // parent widget. With this, we make sure that the parent widget will be
// able to patch itself properly after // able to patch itself properly after
vnode.key = this.__widget__.id; vnode.key = this.__owl__.id;
this.__widget__.renderProps = this.props; this.__owl__.renderProps = this.props;
this.__widget__.renderPromise = Promise.all(promises).then(() => vnode); this.__owl__.renderPromise = Promise.all(promises).then(() => vnode);
return this.__widget__.renderPromise; return this.__owl__.renderPromise;
} }
/** /**
* Only called by qweb t-widget directive * Only called by qweb t-widget directive
*/ */
_mount(vnode, elm) { _mount(vnode, elm) {
this.__widget__.vnode = patch$1(elm, vnode); this.__owl__.vnode = patch$1(elm, vnode);
this.__mount(); this.__mount();
return this.__widget__.vnode; return this.__owl__.vnode;
} }
__mount() { __mount() {
if (this.__widget__.isMounted) { if (this.__owl__.isMounted) {
return; return;
} }
if (this.__widget__.parent) { if (this.__owl__.parent) {
if (this.__widget__.parent.__widget__.isMounted) { if (this.__owl__.parent.__owl__.isMounted) {
this.__widget__.isMounted = true; this.__owl__.isMounted = true;
this.mounted(); this.mounted();
const children = this.__widget__.children; const children = this.__owl__.children;
for (let id in children) { for (let id in children) {
children[id].__mount(); children[id].__mount();
} }
@@ -1163,7 +1159,7 @@
_visitSubTree(callback) { _visitSubTree(callback) {
const shouldVisitChildren = callback(this); const shouldVisitChildren = callback(this);
if (shouldVisitChildren) { if (shouldVisitChildren) {
const children = this.__widget__.children; const children = this.__owl__.children;
for (let id in children) { for (let id in children) {
children[id]._visitSubTree(callback); children[id]._visitSubTree(callback);
} }
@@ -1215,6 +1211,7 @@
this.nextID = 1; this.nextID = 1;
this.code = []; this.code = [];
this.variables = {}; this.variables = {};
this.definedVariables = {};
this.escaping = false; this.escaping = false;
this.parentNode = null; this.parentNode = null;
this.rootNode = null; this.rootNode = null;
@@ -1315,11 +1312,13 @@
else if (c.match(/\W/) && invar.length) { else if (c.match(/\W/) && invar.length) {
// TODO: Should check for possible spaces before dot // TODO: Should check for possible spaces before dot
if (chars[invarPos - 1] !== "." && RESERVED_WORDS.indexOf(invar) < 0) { if (chars[invarPos - 1] !== "." && RESERVED_WORDS.indexOf(invar) < 0) {
invar = if (!(invar in this.definedVariables)) {
WORD_REPLACEMENT[invar] || invar =
(invar in this.variables && WORD_REPLACEMENT[invar] ||
this.formatExpression(this.variables[invar])) || (invar in this.variables &&
"context['" + invar + "']"; this.formatExpression(this.variables[invar])) ||
"context['" + invar + "']";
}
} }
r += invar; r += invar;
invar = ""; invar = "";
@@ -1359,6 +1358,17 @@
return classes.join(" "); return classes.join(" ");
} }
}; };
this.directiveNames = {
as: 1,
name: 1,
value: 1,
att: 1,
attf: 1,
props: 1,
key: 1,
keepalive: 1,
debug: 1
};
[ [
forEachDirective, forEachDirective,
escDirective, escDirective,
@@ -1378,6 +1388,7 @@
} }
addDirective(dir) { addDirective(dir) {
this.directives.push(dir); this.directives.push(dir);
this.directiveNames[dir.name] = 1;
this.directives.sort((d1, d2) => d1.priority - d2.priority); this.directives.sort((d1, d2) => d1.priority - d2.priority);
} }
/** /**
@@ -1538,8 +1549,18 @@
const attributes = node.attributes; const attributes = node.attributes;
const validDirectives = []; const validDirectives = [];
let withHandlers = false; let withHandlers = false;
// maybe this is not optimal: we iterate on all attributes here, and again
// just after for each directive.
for (let i = 0; i < attributes.length; i++) {
let attrName = attributes[i].name;
if (attrName.startsWith("t-")) {
let dName = attrName.slice(2).split("-")[0];
if (!(dName in this.directiveNames)) {
throw new Error(`Unknown QWeb directive: '${attrName}'`);
}
}
}
for (let directive of this.directives) { for (let directive of this.directives) {
// const value = attributes[i].textContent!;
let fullName; let fullName;
let value; let value;
for (let i = 0; i < attributes.length; i++) { for (let i = 0; i < attributes.length; i++) {
@@ -1735,19 +1756,21 @@
return; return;
} }
if (typeof value === "string") { if (typeof value === "string") {
const exprID = ctx.generateID(); let exprID = value;
ctx.addLine(`var e${exprID} = ${ctx.formatExpression(value)};`); if (!(value in ctx.definedVariables)) {
ctx.addIf(`e${exprID} || e${exprID} === 0`); exprID = `_${ctx.generateID()}`;
let text = `e${exprID}`; ctx.addLine(`var ${exprID} = ${ctx.formatExpression(value)};`);
}
ctx.addIf(`${exprID} || ${exprID} === 0`);
if (!ctx.parentNode) { if (!ctx.parentNode) {
throw new Error("Should not have a text node without a parent"); throw new Error("Should not have a text node without a parent");
} }
if (ctx.escaping) { if (ctx.escaping) {
ctx.addLine(`c${ctx.parentNode}.push({text: ${text}});`); ctx.addLine(`c${ctx.parentNode}.push({text: ${exprID}});`);
} }
else { else {
let fragID = ctx.generateID(); let fragID = ctx.generateID();
ctx.addLine(`var frag${fragID} = this.utils.getFragment(e${exprID})`); ctx.addLine(`var frag${fragID} = this.utils.getFragment(${exprID})`);
let tempNodeID = ctx.generateID(); let tempNodeID = ctx.generateID();
ctx.addLine(`var p${tempNodeID} = {hook: {`); ctx.addLine(`var p${tempNodeID} = {hook: {`);
ctx.addLine(` insert: n => n.elm.parentNode.replaceChild(frag${fragID}, n.elm),`); ctx.addLine(` insert: n => n.elm.parentNode.replaceChild(frag${fragID}, n.elm),`);
@@ -1801,7 +1824,11 @@
const variable = node.getAttribute("t-set"); const variable = node.getAttribute("t-set");
let value = node.getAttribute("t-value"); let value = node.getAttribute("t-value");
if (value) { if (value) {
ctx.variables[variable] = value; const varName = `_${ctx.generateID()}`;
const formattedValue = ctx.formatExpression(value);
ctx.addLine(`var ${varName} = ${formattedValue}`);
ctx.definedVariables[varName] = formattedValue;
ctx.variables[variable] = varName;
} }
else { else {
ctx.variables[variable] = node.childNodes; ctx.variables[variable] = node.childNodes;
@@ -1862,12 +1889,32 @@
nodeCopy.removeAttribute("t-call"); nodeCopy.removeAttribute("t-call");
// extract variables from nodecopy // extract variables from nodecopy
const tempCtx = new Context(); const tempCtx = new Context();
tempCtx.nextID = ctx.rootContext.nextID;
qweb._compileNode(nodeCopy, tempCtx); qweb._compileNode(nodeCopy, tempCtx);
const vars = Object.assign({}, ctx.variables, tempCtx.variables); const vars = Object.assign({}, ctx.variables, tempCtx.variables);
var definedVariables = Object.assign({}, ctx.definedVariables, tempCtx.definedVariables);
ctx.rootContext.nextID = tempCtx.nextID;
// open new scope, if necessary
const hasNewVariables = Object.keys(definedVariables).length > 0;
if (hasNewVariables) {
ctx.addLine("{");
ctx.indent();
}
// add new variables, if any
for (let key in definedVariables) {
ctx.addLine(`let ${key} = ${definedVariables[key]}`);
}
// compile sub template
const subCtx = ctx const subCtx = ctx
.subContext("caller", nodeCopy) .subContext("caller", nodeCopy)
.subContext("variables", Object.create(vars)); .subContext("variables", Object.create(vars))
.subContext("definedVariables", Object.create(definedVariables));
qweb._compileNode(nodeTemplate, subCtx); qweb._compileNode(nodeTemplate, subCtx);
// close new scope
if (hasNewVariables) {
ctx.dedent();
ctx.addLine("}");
}
return true; return true;
} }
}; };
@@ -1941,7 +1988,7 @@
ctx.addLine("//WIDGET"); ctx.addLine("//WIDGET");
ctx.rootContext.shouldDefineOwner = true; ctx.rootContext.shouldDefineOwner = true;
let props = node.getAttribute("t-props"); let props = node.getAttribute("t-props");
let keepAlive = node.getAttribute("t-keep-alive") ? true : false; let keepAlive = node.getAttribute("t-keepalive") ? true : false;
// t-on- events... // t-on- events...
const events = []; const events = [];
const attributes = node.attributes; const attributes = node.attributes;
@@ -1981,17 +2028,17 @@
: ctx.inLoop : ctx.inLoop
? `String(-${widgetID} - i)` ? `String(-${widgetID} - i)`
: String(widgetID); : String(widgetID);
ctx.addLine(`let w${widgetID} = ${templateID} in context.__widget__.cmap ? context.__widget__.children[context.__widget__.cmap[${templateID}]] : false;`); ctx.addLine(`let w${widgetID} = ${templateID} in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[${templateID}]] : false;`);
ctx.addLine(`let props${widgetID} = ${props || "{}"};`); ctx.addLine(`let props${widgetID} = ${props || "{}"};`);
ctx.addLine(`let isNew${widgetID} = !w${widgetID};`); ctx.addLine(`let isNew${widgetID} = !w${widgetID};`);
// check if we can reuse current rendering promise // check if we can reuse current rendering promise
ctx.addIf(`w${widgetID} && w${widgetID}.__widget__.renderPromise`); ctx.addIf(`w${widgetID} && w${widgetID}.__owl__.renderPromise`);
ctx.addIf(`w${widgetID}.__widget__.isStarted`); ctx.addIf(`w${widgetID}.__owl__.isStarted`);
ctx.addLine(`def${defID} = w${widgetID}.updateProps(props${widgetID}, extra.forceUpdate);`); ctx.addLine(`def${defID} = w${widgetID}.updateProps(props${widgetID}, extra.forceUpdate);`);
ctx.addElse(); ctx.addElse();
ctx.addLine(`isNew${widgetID} = true`); ctx.addLine(`isNew${widgetID} = true`);
ctx.addIf(`props${widgetID} === w${widgetID}.__widget__.renderProps`); ctx.addIf(`props${widgetID} === w${widgetID}.__owl__.renderProps`);
ctx.addLine(`def${defID} = w${widgetID}.__widget__.renderPromise;`); ctx.addLine(`def${defID} = w${widgetID}.__owl__.renderPromise;`);
ctx.addElse(); ctx.addElse();
ctx.addLine(`w${widgetID}.destroy();`); ctx.addLine(`w${widgetID}.destroy();`);
ctx.addLine(`w${widgetID} = false`); ctx.addLine(`w${widgetID} = false`);
@@ -2003,7 +2050,7 @@
ctx.addLine(`def${defID} = w${widgetID}.updateProps(props${widgetID}, extra.forceUpdate);`); ctx.addLine(`def${defID} = w${widgetID}.updateProps(props${widgetID}, extra.forceUpdate);`);
ctx.addElse(); ctx.addElse();
ctx.addLine(`w${widgetID} = new context.widgets['${value}'](owner, props${widgetID});`); ctx.addLine(`w${widgetID} = new context.widgets['${value}'](owner, props${widgetID});`);
ctx.addLine(`context.__widget__.cmap[${templateID}] = w${widgetID}.__widget__.id;`); ctx.addLine(`context.__owl__.cmap[${templateID}] = w${widgetID}.__owl__.id;`);
for (let [event, method] of events) { for (let [event, method] of events) {
ctx.addLine(`w${widgetID}.on('${event}', owner, owner['${method}'])`); ctx.addLine(`w${widgetID}.on('${event}', owner, owner['${method}'])`);
} }
@@ -2015,9 +2062,9 @@
ctx.closeIf(); ctx.closeIf();
ctx.closeIf(); ctx.closeIf();
ctx.addIf(`isNew${widgetID}`); ctx.addIf(`isNew${widgetID}`);
ctx.addLine(`def${defID} = def${defID}.then(vnode=>{let pvnode=h(vnode.sel, {key: ${templateID}});c${ctx.parentNode}[_${dummyID}_index]=pvnode;pvnode.data.hook = {insert(vn){let nvn=w${widgetID}._mount(vnode, vn.elm);pvnode.elm=nvn.elm},remove(){w${widgetID}.${keepAlive ? "unmount" : "destroy"}()},destroy(){w${widgetID}.${keepAlive ? "unmount" : "destroy"}()}}; w${widgetID}.__widget__.pvnode = pvnode;});`); ctx.addLine(`def${defID} = def${defID}.then(vnode=>{let pvnode=h(vnode.sel, {key: ${templateID}});c${ctx.parentNode}[_${dummyID}_index]=pvnode;pvnode.data.hook = {insert(vn){let nvn=w${widgetID}._mount(vnode, vn.elm);pvnode.elm=nvn.elm},remove(){w${widgetID}.${keepAlive ? "unmount" : "destroy"}()},destroy(){w${widgetID}.${keepAlive ? "unmount" : "destroy"}()}}; w${widgetID}.__owl__.pvnode = pvnode;});`);
ctx.addElse(); ctx.addElse();
ctx.addLine(`def${defID} = def${defID}.then(()=>{if (w${widgetID}.__widget__.isDestroyed) {return};let vnode;if (!w${widgetID}.__widget__.vnode){vnode=w${widgetID}.__widget__.pvnode} else { vnode=h(w${widgetID}.__widget__.vnode.sel, {key: ${templateID}});vnode.elm=w${widgetID}.el;vnode.data.hook = {insert(a){a.elm.parentNode.replaceChild(w${widgetID}.el,a.elm);a.elm=w${widgetID}.el;w${widgetID}.__mount();},remove(){w${widgetID}.${keepAlive ? "unmount" : "destroy"}()}, destroy() {w${widgetID}.${keepAlive ? "unmount" : "destroy"}()}}}c${ctx.parentNode}[_${dummyID}_index]=vnode;});`); ctx.addLine(`def${defID} = def${defID}.then(()=>{if (w${widgetID}.__owl__.isDestroyed) {return};let vnode;if (!w${widgetID}.__owl__.vnode){vnode=w${widgetID}.__owl__.pvnode} else { vnode=h(w${widgetID}.__owl__.vnode.sel, {key: ${templateID}});vnode.elm=w${widgetID}.el;vnode.data.hook = {insert(a){a.elm.parentNode.replaceChild(w${widgetID}.el,a.elm);a.elm=w${widgetID}.el;w${widgetID}.__mount();},remove(){w${widgetID}.${keepAlive ? "unmount" : "destroy"}()}, destroy() {w${widgetID}.${keepAlive ? "unmount" : "destroy"}()}}}c${ctx.parentNode}[_${dummyID}_index]=vnode;});`);
ctx.closeIf(); ctx.closeIf();
ctx.addLine(`extra.promises.push(def${defID});`); ctx.addLine(`extra.promises.push(def${defID});`);
if (node.getAttribute("t-if") || node.getAttribute("t-else")) { if (node.getAttribute("t-if") || node.getAttribute("t-else")) {
@@ -2059,6 +2106,7 @@
class Store extends EventBus { class Store extends EventBus {
constructor(config, options = {}) { constructor(config, options = {}) {
super(); super();
this._commitLevel = 0;
this._isMutating = false; this._isMutating = false;
this.history = []; this.history = [];
this.debug = options.debug || false; this.debug = options.debug || false;
@@ -2090,42 +2138,52 @@
}); });
} }
} }
async commit(type, payload) { commit(type, payload) {
if (!this.mutations[type]) { if (!this.mutations[type]) {
throw new Error(`[Error] mutation ${type} is undefined`); throw new Error(`[Error] mutation ${type} is undefined`);
} }
const currentRev = this.observer.__rev__; this._commitLevel++;
const currentRev = this.observer.rev;
this._isMutating = true; this._isMutating = true;
this.observer.allowMutations = true; this.observer.allowMutations = true;
this.mutations[type].call(null, { state: this.state, set: this.observer.set }, payload); const res = this.mutations[type].call(null, {
this.observer.allowMutations = false; commit: this.commit.bind(this),
if (this.debug) { state: this.state,
this.history.push({ set: this.observer.set
state: this.state, }, payload);
mutation: type, if (this._commitLevel === 1) {
payload: payload this.observer.allowMutations = false;
if (this.debug) {
this.history.push({
state: this.state,
mutation: type,
payload: payload
});
}
Promise.resolve().then(() => {
if (this._isMutating) {
this._isMutating = false;
if (currentRev !== this.observer.rev) {
this.trigger("update", this.state);
}
}
}); });
} }
await Promise.resolve(); this._commitLevel--;
if (this._isMutating) { return res;
this._isMutating = false;
if (currentRev !== this.observer.__rev__) {
this.trigger("update", this.state);
}
}
} }
} }
function makeObserver() { function makeObserver() {
const observer = { const observer = {
__rev__: 0, rev: 1,
allowMutations: true, allowMutations: true,
observe: observe, observe: observe,
set: set set: set
}; };
function set(target, key, value) { function set(target, key, value) {
addProp(target, key, value); addProp(target, key, value);
target.__rev__++; target.__owl__.rev++;
observer.__rev__++; observer.rev++;
} }
function addProp(obj, key, value) { function addProp(obj, key, value) {
Object.defineProperty(obj, key, { Object.defineProperty(obj, key, {
@@ -2138,19 +2196,24 @@
throw new Error(`State cannot be changed outside a mutation! (key: "${key}", val: "${newVal}")`); throw new Error(`State cannot be changed outside a mutation! (key: "${key}", val: "${newVal}")`);
} }
if (newVal !== value) { if (newVal !== value) {
unobserve(value);
value = newVal; value = newVal;
observe(newVal); observe(newVal, obj);
obj.__rev__++; obj.__owl__.rev++;
observer.__rev__++; observer.rev++;
let parent = obj;
do {
parent.__owl__.deepRev++;
} while ((parent = parent.__owl__.parent));
} }
} }
}); });
observe(value); observe(value, obj);
} }
function observeObj(obj) { function observeObj(obj, parent) {
const keys = Object.keys(obj); const keys = Object.keys(obj);
obj.__rev__ = 0; obj.__owl__ = { rev: 1, deepRev: 1, parent };
Object.defineProperty(obj, "__rev__", { enumerable: false }); Object.defineProperty(obj, "__owl__", { enumerable: false });
for (let key of keys) { for (let key of keys) {
addProp(obj, key, obj[key]); addProp(obj, key, obj[key]);
} }
@@ -2169,8 +2232,12 @@
for (let method of methodsToPatch) { for (let method of methodsToPatch) {
const initialMethod = ArrayProto[method]; const initialMethod = ArrayProto[method];
ModifiedArrayProto[method] = function (...args) { ModifiedArrayProto[method] = function (...args) {
observer.__rev__++; observer.rev++;
this.__rev__++; this.__owl__.rev++;
let parent = this;
do {
parent.__owl__.deepRev++;
} while ((parent = parent.__owl__.parent));
let inserted; let inserted;
switch (method) { switch (method) {
case "push": case "push":
@@ -2183,33 +2250,42 @@
} }
if (inserted) { if (inserted) {
for (let elem of inserted) { for (let elem of inserted) {
observe(elem); observe(elem, this);
} }
} }
return initialMethod.call(this, ...args); return initialMethod.call(this, ...args);
}; };
} }
function observeArr(arr) { function observeArr(arr, parent) {
arr.__rev__ = 0; arr.__owl__ = { rev: 1, deepRev: 1, parent };
Object.defineProperty(arr, "__rev__", { enumerable: false }); Object.defineProperty(arr, "__owl__", { enumerable: false });
arr.__proto__ = ModifiedArrayProto; arr.__proto__ = ModifiedArrayProto;
for (let i = 0; i < arr.length; i++) { for (let i = 0; i < arr.length; i++) {
observe(arr[i]); observe(arr[i], arr);
} }
} }
function observe(value) { function observe(value, parent) {
if (value === null) {
// fun fact: typeof null === 'object'
return;
}
if (typeof value !== "object") { if (typeof value !== "object") {
return; return;
} }
if ("__rev__" in value) { if ("__owl__" in value) {
// already observed // already observed
return; return;
} }
if (Array.isArray(value)) { if (Array.isArray(value)) {
observeArr(value); observeArr(value, parent);
} }
else { else {
observeObj(value); observeObj(value, parent);
}
}
function unobserve(target) {
if (target !== null && typeof target === "object") {
delete target.__owl__;
} }
} }
return observer; return observer;
@@ -2217,15 +2293,44 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Connect function // Connect function
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
function setStoreProps(__widget__, storeProps) { function revNumber(o) {
__widget__.currentStoreProps = storeProps; if (o !== null && typeof o === "object" && o.__owl__) {
__widget__.currentStoreRevs = {}; return o.__owl__.rev;
__widget__.currentStoreRev = storeProps.__rev__;
for (let key in storeProps) {
__widget__.currentStoreRevs[key] = storeProps[key].__rev__;
} }
return 0;
} }
function connect(mapStateToProps) { function deepRevNumber(o) {
if (o !== null && typeof o === "object" && o.__owl__) {
return o.__owl__.deepRev;
}
return 0;
}
function connect(mapStateToProps, options = {}) {
let hashFunction = options.hashFunction || null;
if (!hashFunction) {
let deep = "deep" in options ? options.deep : true;
let defaultRevFunction = deep ? deepRevNumber : revNumber;
hashFunction = function ({ storeProps }, options) {
const { currentStoreProps } = options;
if ("__owl__" in storeProps) {
return defaultRevFunction(storeProps);
}
let hash = 0;
for (let key in storeProps) {
const val = storeProps[key];
const hashVal = defaultRevFunction(val);
if (hashVal === 0) {
if (val !== currentStoreProps[key]) {
options.didChange = true;
}
}
else {
hash += hashVal;
}
}
return hash;
};
}
return function (Comp) { return function (Comp) {
return class extends Comp { return class extends Comp {
constructor(parent, props) { constructor(parent, props) {
@@ -2234,30 +2339,37 @@
const storeProps = mapStateToProps(env.store.state, ownProps); const storeProps = mapStateToProps(env.store.state, ownProps);
const mergedProps = Object.assign({}, props || {}, storeProps); const mergedProps = Object.assign({}, props || {}, storeProps);
super(parent, mergedProps); super(parent, mergedProps);
setStoreProps(this.__widget__, storeProps); this.__owl__.ownProps = ownProps;
this.__widget__.ownProps = ownProps; this.__owl__.currentStoreProps = storeProps;
this.__owl__.storeHash = hashFunction({
state: env.store.state,
storeProps: storeProps,
revNumber,
deepRevNumber
}, {
currentStoreProps: storeProps
});
} }
mounted() { mounted() {
this.env.store.on("update", this, () => { this.env.store.on("update", this, () => {
const ownProps = this.__widget__.ownProps; const ownProps = this.__owl__.ownProps;
const storeProps = mapStateToProps(this.env.store.state, ownProps); const storeProps = mapStateToProps(this.env.store.state, ownProps);
let didChange = false; const options = {
if (this.__widget__.currentStoreRev !== storeProps.__rev__) { currentStoreProps: this.__owl__.currentStoreProps
setStoreProps(this.__widget__, storeProps); };
const storeHash = hashFunction({
state: this.env.store.state,
storeProps: storeProps,
revNumber,
deepRevNumber
}, options);
let didChange = options.didChange;
if (storeHash !== this.__owl__.storeHash) {
didChange = true; didChange = true;
} this.__owl__.storeHash = storeHash;
else {
const revs = this.__widget__.currentStoreRevs;
for (let key in storeProps) {
const val = storeProps[key];
if (val.__rev__ !== revs[key]) {
didChange = true;
revs[key] = val.__rev__;
this.__widget__.currentStoreProps[key] = val;
}
}
} }
if (didChange) { if (didChange) {
this.__owl__.currentStoreProps = storeProps;
this.updateProps(ownProps, false); this.updateProps(ownProps, false);
} }
}); });
@@ -2268,11 +2380,11 @@
super.willUnmount(); super.willUnmount();
} }
updateProps(nextProps, forceUpdate) { updateProps(nextProps, forceUpdate) {
if (this.__widget__.ownProps !== nextProps) { if (this.__owl__.ownProps !== nextProps) {
this.__widget__.currentStoreProps = mapStateToProps(this.env.store.state, nextProps); this.__owl__.currentStoreProps = mapStateToProps(this.env.store.state, nextProps);
} }
this.__widget__.ownProps = nextProps; this.__owl__.ownProps = nextProps;
const mergedProps = Object.assign({}, nextProps, this.__widget__.currentStoreProps); const mergedProps = Object.assign({}, nextProps, this.__owl__.currentStoreProps);
return super.updateProps(mergedProps, forceUpdate); return super.updateProps(mergedProps, forceUpdate);
} }
}; };
@@ -2296,7 +2408,7 @@
exports.extras = extras; exports.extras = extras;
exports._version = '0.6.0'; exports._version = '0.6.0';
exports._date = '2019-04-12T13:25:16.523Z'; exports._date = '2019-04-16T08:20:25.817Z';
exports._hash = 'd328360'; exports._hash = '9dd8a36';
}(this.owl = this.owl || {})); }(this.owl = this.owl || {}));