mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
247 lines
7.1 KiB
TypeScript
247 lines
7.1 KiB
TypeScript
import { Module, VNode, VNodeData } from "./vdom";
|
|
|
|
//------------------------------------------------------------------------------
|
|
// module/props.ts
|
|
//------------------------------------------------------------------------------
|
|
|
|
function updateProps(oldVnode: VNode, vnode: VNode): void {
|
|
var key: string,
|
|
cur: any,
|
|
old: any,
|
|
elm = vnode.elm,
|
|
oldProps = (oldVnode.data as VNodeData).props,
|
|
props = (vnode.data as VNodeData).props;
|
|
|
|
if (!oldProps && !props) return;
|
|
if (oldProps === props) return;
|
|
oldProps = oldProps || {};
|
|
props = props || {};
|
|
|
|
for (key in oldProps) {
|
|
if (!props[key]) {
|
|
delete (elm as any)[key];
|
|
}
|
|
}
|
|
for (key in props) {
|
|
cur = props[key];
|
|
old = oldProps[key];
|
|
if (old !== cur && (key !== "value" || (elm as any)[key] !== cur)) {
|
|
(elm as any)[key] = cur;
|
|
}
|
|
}
|
|
}
|
|
|
|
export const propsModule = {
|
|
create: updateProps,
|
|
update: updateProps,
|
|
} as Module;
|
|
|
|
//------------------------------------------------------------------------------
|
|
// module/eventlisteners.ts
|
|
//------------------------------------------------------------------------------
|
|
|
|
function invokeHandler(handler: any, vnode?: VNode, event?: Event): void {
|
|
if (typeof handler === "function") {
|
|
// call function handler
|
|
handler.call(vnode, event, vnode);
|
|
} else if (typeof handler === "object") {
|
|
// call handler with arguments
|
|
if (typeof handler[0] === "function") {
|
|
// special case for single argument for performance
|
|
if (handler.length === 2) {
|
|
handler[0].call(vnode, handler[1], event, vnode);
|
|
} else {
|
|
var args = handler.slice(1);
|
|
args.push(event);
|
|
args.push(vnode);
|
|
handler[0].apply(vnode, args);
|
|
}
|
|
} else {
|
|
// call multiple handlers
|
|
for (let i = 0, iLen = handler.length; i < iLen; i++) {
|
|
invokeHandler(handler[i], vnode, event);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function handleEvent(event: Event, vnode: VNode) {
|
|
var name = event.type,
|
|
on = (vnode.data as VNodeData).on;
|
|
|
|
// call event handler(s) if exists
|
|
if (on) {
|
|
if (on[name]) {
|
|
invokeHandler(on[name], vnode, event);
|
|
} else if (on["!" + name]) {
|
|
invokeHandler(on["!" + name], vnode, event);
|
|
}
|
|
}
|
|
}
|
|
|
|
function createListener() {
|
|
return function handler(event: Event) {
|
|
handleEvent(event, (handler as any).vnode);
|
|
};
|
|
}
|
|
|
|
function updateEventListeners(oldVnode: VNode, vnode?: VNode): void {
|
|
var oldOn = (oldVnode.data as VNodeData).on,
|
|
oldListener = (oldVnode as any).listener,
|
|
oldElm: Element = oldVnode.elm as Element,
|
|
on = vnode && (vnode.data as VNodeData).on,
|
|
elm: Element = (vnode && vnode.elm) as Element,
|
|
name: string;
|
|
|
|
// optimization for reused immutable handlers
|
|
if (oldOn === on) {
|
|
return;
|
|
}
|
|
|
|
// remove existing listeners which no longer used
|
|
if (oldOn && oldListener) {
|
|
// if element changed or deleted we remove all existing listeners unconditionally
|
|
if (!on) {
|
|
for (name in oldOn) {
|
|
// remove listener if element was changed or existing listeners removed
|
|
const capture = name.charAt(0) === "!";
|
|
name = capture ? name.slice(1) : name;
|
|
oldElm.removeEventListener(name, oldListener, capture);
|
|
}
|
|
} else {
|
|
for (name in oldOn) {
|
|
// remove listener if existing listener removed
|
|
if (!on[name]) {
|
|
const capture = name.charAt(0) === "!";
|
|
name = capture ? name.slice(1) : name;
|
|
oldElm.removeEventListener(name, oldListener, capture);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// add new listeners which has not already attached
|
|
if (on) {
|
|
// reuse existing listener or create new
|
|
var listener = ((vnode as any).listener = (oldVnode as any).listener || createListener());
|
|
// update vnode for listener
|
|
listener.vnode = vnode;
|
|
|
|
// if element changed or added we add all needed listeners unconditionally
|
|
if (!oldOn) {
|
|
for (name in on) {
|
|
// add listener if element was changed or new listeners added
|
|
const capture = name.charAt(0) === "!";
|
|
name = capture ? name.slice(1) : name;
|
|
elm.addEventListener(name, listener, capture);
|
|
}
|
|
} else {
|
|
for (name in on) {
|
|
// add listener if new listener added
|
|
if (!oldOn[name]) {
|
|
const capture = name.charAt(0) === "!";
|
|
name = capture ? name.slice(1) : name;
|
|
elm.addEventListener(name, listener, capture);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export const eventListenersModule = {
|
|
create: updateEventListeners,
|
|
update: updateEventListeners,
|
|
destroy: updateEventListeners,
|
|
} as Module;
|
|
|
|
//------------------------------------------------------------------------------
|
|
// attributes.ts
|
|
//------------------------------------------------------------------------------
|
|
|
|
const xlinkNS = "http://www.w3.org/1999/xlink";
|
|
const xmlNS = "http://www.w3.org/XML/1998/namespace";
|
|
const colonChar = 58;
|
|
const xChar = 120;
|
|
|
|
function updateAttrs(oldVnode: VNode, vnode: VNode): void {
|
|
var key: string,
|
|
elm: Element = vnode.elm as Element,
|
|
oldAttrs = (oldVnode.data as VNodeData).attrs,
|
|
attrs = (vnode.data as VNodeData).attrs;
|
|
|
|
if (!oldAttrs && !attrs) return;
|
|
if (oldAttrs === attrs) return;
|
|
oldAttrs = oldAttrs || {};
|
|
attrs = attrs || {};
|
|
|
|
// update modified attributes, add new attributes
|
|
for (key in attrs) {
|
|
const cur = attrs[key];
|
|
const old = oldAttrs[key];
|
|
if (old !== cur) {
|
|
if (cur === true) {
|
|
elm.setAttribute(key, "");
|
|
} else if (cur === false) {
|
|
elm.removeAttribute(key);
|
|
} else {
|
|
if (key.charCodeAt(0) !== xChar) {
|
|
elm.setAttribute(key, cur);
|
|
} else if (key.charCodeAt(3) === colonChar) {
|
|
// Assume xml namespace
|
|
elm.setAttributeNS(xmlNS, key, cur);
|
|
} else if (key.charCodeAt(5) === colonChar) {
|
|
// Assume xlink namespace
|
|
elm.setAttributeNS(xlinkNS, key, cur);
|
|
} else {
|
|
elm.setAttribute(key, cur);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// remove removed attributes
|
|
// use `in` operator since the previous `for` iteration uses it (.i.e. add even attributes with undefined value)
|
|
// the other option is to remove all attributes with value == undefined
|
|
for (key in oldAttrs) {
|
|
if (!(key in attrs)) {
|
|
elm.removeAttribute(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
export const attrsModule = {
|
|
create: updateAttrs,
|
|
update: updateAttrs,
|
|
} as Module;
|
|
|
|
//------------------------------------------------------------------------------
|
|
// class.ts
|
|
//------------------------------------------------------------------------------
|
|
function updateClass(oldVnode: VNode, vnode: VNode): void {
|
|
var cur: any,
|
|
name: string,
|
|
elm: Element,
|
|
oldClass = (oldVnode.data as VNodeData).class,
|
|
klass = (vnode.data as VNodeData).class;
|
|
|
|
if (!oldClass && !klass) return;
|
|
if (oldClass === klass) return;
|
|
oldClass = oldClass || {};
|
|
klass = klass || {};
|
|
|
|
elm = vnode.elm as Element;
|
|
|
|
for (name in oldClass) {
|
|
if (name && !klass[name]) {
|
|
elm.classList.remove(name);
|
|
}
|
|
}
|
|
for (name in klass) {
|
|
cur = klass[name];
|
|
if (cur !== oldClass[name]) {
|
|
(elm.classList as any)[cur ? "add" : "remove"](name);
|
|
}
|
|
}
|
|
}
|
|
|
|
export const classModule = { create: updateClass, update: updateClass } as Module;
|