UNPKG

@hippy/react

Version:

Hippy react framework

1,503 lines (1,486 loc) 221 kB
/*! * @hippy/react v3.3.4-rc.1 * Build at: Wed Mar 05 2025 15:28:06 GMT+0800 (中国标准时间) * * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2025 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import React from 'react'; import reactReconciler from '@hippy/react-reconciler'; /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ if (!global.__GLOBAL__) { global.__GLOBAL__ = {}; } global.__GLOBAL__.nodeId = 0; global.__GLOBAL__.animationId = 0; const { asyncStorage: AsyncStorage$2, bridge: Bridge$2, device: Device$2, document: UIManager, register: HippyRegister$2, on: addEventListener$2, off: removeEventListener$2, emit: dispatchEvent$1, } = global.Hippy; var HippyGlobal = /*#__PURE__*/Object.freeze({ __proto__: null, addEventListener: addEventListener$2, removeEventListener: removeEventListener$2, dispatchEvent: dispatchEvent$1, AsyncStorage: AsyncStorage$2, Bridge: Bridge$2, Device: Device$2, HippyRegister: HippyRegister$2, UIManager: UIManager }); /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Single root instance let rootContainer; let rootViewId; const fiberNodeCache = new Map(); function setRootContainer(rootId, root) { rootViewId = rootId; rootContainer = root; } function getRootContainer() { return rootContainer; } function getRootViewId() { if (!rootViewId) { throw new Error('getRootViewId must execute after setRootContainer'); } return rootViewId; } function findNodeByCondition(condition) { if (!rootContainer) { return null; } const { current: root } = rootContainer; const queue = [root]; while (queue.length) { const targetNode = queue.shift(); if (!targetNode) { break; } if (condition(targetNode)) { return targetNode; } if (targetNode.child) { queue.push(targetNode.child); } if (targetNode.sibling) { queue.push(targetNode.sibling); } } return null; } function findNodeById(nodeId) { return findNodeByCondition(node => node.stateNode && node.stateNode.nodeId === nodeId); } /** * preCacheFiberNode - cache FiberNode * @param {Fiber} targetNode * @param {number} nodeId */ function preCacheFiberNode(targetNode, nodeId) { fiberNodeCache.set(nodeId, targetNode); } /** * unCacheFiberNode - delete Fiber Node from cache * @param {number} nodeId */ function unCacheFiberNode(nodeId) { fiberNodeCache.delete(nodeId); } /** * getElementFromFiber - get ElementNode by Fiber * @param {number} fiberNode */ function getElementFromFiber(fiberNode) { return (fiberNode === null || fiberNode === void 0 ? void 0 : fiberNode.stateNode) || null; } /** * getFiberNodeFromId - get FiberNode by nodeId * @param {number} nodeId */ function getFiberNodeFromId(nodeId) { return fiberNodeCache.get(nodeId) || null; } /** * unCacheFiberNodeOnIdle - recursively delete FiberNode cache on idle * @param {ElementNode|number} node */ function unCacheFiberNodeOnIdle(node) { requestIdleCallback((deadline) => { // if idle time exists or callback invoked when timeout if (deadline.timeRemaining() > 0 || deadline.didTimeout) { recursivelyUnCacheFiberNode(node); } }, { timeout: 50 }); // 50ms to avoid blocking user operation } /** * recursivelyUnCacheFiberNode - delete ViewNode cache recursively * @param {ElementNode|number} node */ function recursivelyUnCacheFiberNode(node) { if (typeof node === 'number') { // if leaf node (e.g. text node) unCacheFiberNode(node); } else if (node) { unCacheFiberNode(node.nodeId); if (Array.isArray(node.childNodes)) { node.childNodes.forEach(node => recursivelyUnCacheFiberNode(node)); } } } /** * requestIdleCallback polyfill * @param {Function} cb * @param {{timeout: number}} [options] */ function requestIdleCallback(cb, options) { if (!global.requestIdleCallback) { return setTimeout(() => { cb({ didTimeout: false, timeRemaining() { return Infinity; }, }); }, 1); } return global.requestIdleCallback(cb, options); } // Event Name Index const NATIVE_EVENT_INDEX = 1; const eventHandlerType = { ADD: 0, REMOVE: 1, }; const relativeToRefType = { BEFORE: -1, AFTER: 1, }; const eventNamesMap = { // onPressIn: ['onPressIn', 'onTouchDown'], // onPressOut: ['onPressOut', 'onTouchEnd'], onTouchStart: ['onTouchStart', 'onTouchDown'], onPress: ['onPress', 'onClick'], }; const DOMEventPhase = { NONE: 0, CAPTURING_PHASE: 1, AT_TARGET: 2, BUBBLING_PHASE: 3, }; const nativeEventMap = { onClick: 'click', onLongClick: 'longclick', // onPressIn: 'touchstart', // normalization // onPressOut: 'touchend', // normalization onPressIn: 'pressin', onPressOut: 'pressout', onTouchDown: 'touchstart', onTouchStart: 'touchstart', onTouchEnd: 'touchend', onTouchMove: 'touchmove', onTouchCancel: 'touchcancel', }; function isNativeGesture$1(name) { return !!nativeEventMap[name]; } function translateToNativeEventName(name) { return name.replace(/^(on)?/g, '').toLocaleLowerCase(); } function isTextNode(targetNode) { return (targetNode && targetNode.nativeName === 'Text') || ['p', 'span'].indexOf(targetNode.tagName) !== -1; } /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const IS_NUMBER_REG = new RegExp(/^\d+$/); let silent = false; let defaultBubbles = false; /** * Trace running information */ function trace(...context) { // In production build or silent if (isTraceEnabled()) { console.log(...context); } } /** * Warning information output */ function warn(...context) { // In production build if (!isDev()) { return; } console.warn(...context); } /** * Convert unicode string to normal string * @param {string} text - The unicode string input */ function unicodeToChar(text) { return text.replace(/\\u[\dA-F]{4}|\\x[\dA-F]{2}/gi, match => String.fromCharCode(parseInt(match.replace(/\\u|\\x/g, ''), 16))); } const captureEventReg = new RegExp('^on.+Capture$'); /** * ensure capture event name * @param {any} eventName */ function isCaptureEvent(eventName) { return captureEventReg.test(eventName); } function hasTargetEvent(key, events) { return (typeof events !== 'undefined' && typeof events[key] === 'object' && !!events[key]); } /** * Convert to string as possible */ const numberRegEx = new RegExp('^(?=.+)[+-]?\\d*\\.?\\d*([Ee][+-]?\\d+)?$'); /** * Try to convert something to number * * @param {any} input - The input try to convert number */ function tryConvertNumber(input) { if (typeof input === 'number') { return input; } if (typeof input === 'string' && numberRegEx.test(input)) { try { return parseFloat(input); } catch (err) { return input; } } return input; } /** * Determine input is function. * * @param {any} input - The input will determine is function. * @returns {boolean} */ function isFunction(input) { return Object.prototype.toString.call(input) === '[object Function]'; } /** * Determine a string is number. * @param {string} input - the input will determine is number. * @returns {boolean} */ function isNumber(input) { return IS_NUMBER_REG.test(input); } /** * Make trace be silent. * @param {boolean} silentArg - The silent flag for log */ function setSilent(silentArg) { silent = silentArg; } /** * is development environment */ function isDev() { return process.env.NODE_ENV !== 'production'; } /** * is Trace silent * @returns {boolean} */ function isTraceEnabled() { return isDev() && !silent; } /** * set bubbles config, default is false * @param bubbles */ function setBubbles(bubbles = false) { defaultBubbles = bubbles; } /** * get bubbles config * @returns boolean */ function isGlobalBubble() { return defaultBubbles; } /** * Convert Image url to specific type * @param url - image path */ function convertImgUrl(url) { if (url && !/^(http|https):\/\//.test(url) && url.indexOf('assets') > -1) { if (isDev()) { const addStr1 = 'http://'; return `${addStr1}127.0.0.1:${process.env.PORT}/${url}`; } const addStr2 = 'hpfile://'; return `${addStr2}./${url}`; } return url; } function deepCopy(data, hash = new WeakMap()) { if (typeof data !== 'object' || data === null) { throw new TypeError('deepCopy data is object'); } // is it data existed in WeakMap if (hash.has(data)) { return hash.get(data); } const newData = {}; const dataKeys = Object.keys(data); dataKeys.forEach((value) => { const currentDataValue = data[value]; if (typeof currentDataValue !== 'object' || currentDataValue === null) { newData[value] = currentDataValue; } else if (Array.isArray(currentDataValue)) { newData[value] = [...currentDataValue]; } else if (currentDataValue instanceof Set) { newData[value] = new Set([...currentDataValue]); } else if (currentDataValue instanceof Map) { newData[value] = new Map([...currentDataValue]); } else { hash.set(data, data); newData[value] = deepCopy(currentDataValue, hash); } }); return newData; } function isStyleNotEmpty(style) { if (typeof style === 'string') { return style.trim() !== ''; } return style !== null && style !== undefined; } /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ class HippyEventHub { constructor(eventName) { this.handlerContainer = {}; this.nextIdForHandler = 0; this.eventName = eventName; } getEventListeners() { return Object.keys(this.handlerContainer) .filter(key => this.handlerContainer[key]) .map(key => this.handlerContainer[key]); } getHandlerSize() { return Object.keys(this.handlerContainer).length; } addEventHandler(handler, callContext) { if (!handler) { throw new TypeError('Invalid arguments for addEventHandler'); } const currId = this.nextIdForHandler; this.nextIdForHandler += 1; const eventHandlerWrapper = { id: currId, eventHandler: handler, context: callContext, }; const idAttrName = `eventHandler_${currId}`; this.handlerContainer[idAttrName] = eventHandlerWrapper; return currId; } notifyEvent(...eventParams) { Object.keys(this.handlerContainer).forEach((key) => { const instance = this.handlerContainer[key]; if (!instance || !instance.eventHandler) { return; } if (instance.context) { instance.eventHandler.call(instance.context, ...eventParams); } else { instance.eventHandler(...eventParams); } }); } removeEventHandler(handlerId) { if (typeof handlerId !== 'number') { throw new TypeError('Invalid arguments for removeEventHandler'); } const idAttrName = `eventHandler_${handlerId}`; if (this.handlerContainer[idAttrName]) { delete this.handlerContainer[idAttrName]; } } } /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ class Event { /** * constructor * @param eventName - handler name, e.g. onClick * @param currentTarget - currentTarget is the node which the handler bind to * @param target - target is the node which triggered the real event */ constructor(eventName, currentTarget, target) { this.type = eventName; this.bubbles = true; // currentTarget is the node which the handler bind to this.currentTarget = currentTarget; // target is the node which triggered the real event this.target = target; } stopPropagation() { this.bubbles = false; } preventDefault() { // noop } } /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const eventHubs = new Map(); const componentName$2 = ['%c[event]%c', 'color: green', 'color: auto']; function isNodePropFunction(prop, nextNodeItem) { return !!(nextNodeItem.memoizedProps && typeof nextNodeItem.memoizedProps[prop] === 'function'); } /** * dispatchGestureEvent - dispatch event * @param {string} eventName * @param {NativeEvent} nativeEvent * @param {Fiber} currentItem * @param {Fiber} targetItem * @param {any} params * @param {HippyTypes.DOMEvent} domEvent */ function dispatchGestureEvent(eventName, nativeEvent, currentItem, targetItem, params, domEvent) { try { let isStopBubble = false; const targetNode = getElementFromFiber(targetItem); const currentTargetNode = getElementFromFiber(currentItem); const { eventPhase } = domEvent; // handle target & capture phase event if (isNodePropFunction(eventName, currentItem) && isCaptureEvent(eventName) && [DOMEventPhase.AT_TARGET, DOMEventPhase.CAPTURING_PHASE].indexOf(eventPhase) > -1) { const syntheticEvent = new Event(eventName, currentTargetNode, targetNode); Object.assign(syntheticEvent, { eventPhase }, params); currentItem.memoizedProps[eventName](syntheticEvent); if (!syntheticEvent.bubbles && domEvent) { domEvent.stopPropagation(); } } if (isNodePropFunction(eventName, currentItem) && !isCaptureEvent(eventName) && [DOMEventPhase.AT_TARGET, DOMEventPhase.BUBBLING_PHASE].indexOf(eventPhase) > -1) { // handle target & bubbling phase event const syntheticEvent = new Event(eventName, currentTargetNode, targetNode); Object.assign(syntheticEvent, { eventPhase }, params); isStopBubble = currentItem.memoizedProps[eventName](syntheticEvent); // If callback have no return, use global bubble config to set isStopBubble. if (typeof isStopBubble !== 'boolean') { isStopBubble = !isGlobalBubble(); } // event bubbles flag has higher priority if (!syntheticEvent.bubbles) { isStopBubble = true; } if (isStopBubble && domEvent) { domEvent.stopPropagation(); } } } catch (err) { console.error(err); } } /** * dispatchUIEvent - dispatch ui event * @param {string} eventName * @param {NativeEvent} nativeEvent * @param {Fiber} currentItem * @param {Fiber} targetItem * @param {any} params * @param {HippyTypes.DOMEvent} domEvent */ function dispatchUIEvent(eventName, nativeEvent, currentItem, targetItem, params, domEvent) { let isStopBubble = false; const targetNode = getElementFromFiber(targetItem); const currentTargetNode = getElementFromFiber(currentItem); try { const { eventPhase } = domEvent; // handle target & bubbling phase event if (isNodePropFunction(eventName, currentItem) && !isCaptureEvent(eventName) && [DOMEventPhase.AT_TARGET, DOMEventPhase.BUBBLING_PHASE].indexOf(eventPhase) > -1) { const syntheticEvent = new Event(eventName, currentTargetNode, targetNode); Object.assign(syntheticEvent, { eventPhase }, params); currentItem.memoizedProps[eventName](syntheticEvent); isStopBubble = !isGlobalBubble(); // event bubbles flag has higher priority if (!syntheticEvent.bubbles) { isStopBubble = true; } if (isStopBubble && domEvent) { domEvent.stopPropagation(); } } } catch (err) { console.error(err); } } function receiveComponentEvent(nativeEvent, domEvent) { trace(...componentName$2, 'receiveComponentEvent', nativeEvent); if (!nativeEvent || !domEvent) { warn(...componentName$2, 'receiveComponentEvent', 'nativeEvent or domEvent not exist'); return; } const { id, currentId, nativeName, originalName, params = {} } = nativeEvent; const currentTargetNode = getFiberNodeFromId(currentId); const targetNode = getFiberNodeFromId(id); if (!currentTargetNode || !targetNode) { warn(...componentName$2, 'receiveComponentEvent', 'currentTargetNode or targetNode not exist'); return; } if (isNativeGesture$1(nativeName)) { dispatchGestureEvent(originalName, nativeEvent, currentTargetNode, targetNode, params, domEvent); } else { dispatchUIEvent(originalName, nativeEvent, currentTargetNode, targetNode, params, domEvent); } } function getHippyEventHub(eventName) { if (typeof eventName !== 'string') { throw new TypeError(`Invalid eventName for getHippyEventHub: ${eventName}`); } return eventHubs.get(eventName) || null; } function registerNativeEventHub(eventName) { trace(...componentName$2, 'registerNativeEventHub', eventName); if (typeof eventName !== 'string') { throw new TypeError(`Invalid eventName for registerNativeEventHub: ${eventName}`); } let targetEventHub = eventHubs.get(eventName); if (!targetEventHub) { targetEventHub = new HippyEventHub(eventName); eventHubs.set(eventName, targetEventHub); } return targetEventHub; } function unregisterNativeEventHub(eventName) { if (typeof eventName !== 'string') { throw new TypeError(`Invalid eventName for unregisterNativeEventHub: ${eventName}`); } if (eventHubs.has(eventName)) { eventHubs.delete(eventName); } } function receiveNativeEvent(nativeEvent) { trace(...componentName$2, 'receiveNativeEvent', nativeEvent); if (!nativeEvent || !Array.isArray(nativeEvent) || nativeEvent.length < 2) { throw new TypeError(`Invalid params for receiveNativeEvent: ${JSON.stringify(nativeEvent)}`); } const [eventName, eventParams] = nativeEvent; if (typeof eventName !== 'string') { throw new TypeError('Invalid arguments for nativeEvent eventName'); } const currEventHub = getHippyEventHub(eventName); if (!currEventHub) { return; } currEventHub.notifyEvent(eventParams); } const EventDispatcher = { registerNativeEventHub, getHippyEventHub, unregisterNativeEventHub, receiveNativeEvent, receiveComponentEvent, }; if (global.__GLOBAL__) { global.__GLOBAL__.jsModuleList.EventDispatcher = EventDispatcher; } /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ class EventEmitterRevoker { constructor(id, listener) { this.callback = id; this.bindListener = listener; } remove() { if (typeof this.callback !== 'number' || !this.bindListener) { return; } this.bindListener.removeCallback(this.callback); this.bindListener = undefined; } } /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ class HippyEventListener { constructor(event) { this.eventName = event; this.listenerIdList = []; } unregister() { const eventHub = EventDispatcher.getHippyEventHub(this.eventName); if (!eventHub) { throw new ReferenceError(`No listeners for ${this.eventName}`); } const listenerIdSize = this.listenerIdList.length; for (let i = 0; i < listenerIdSize; i += 1) { eventHub.removeEventHandler(this.listenerIdList[i]); } this.listenerIdList = []; if (eventHub.getHandlerSize() === 0) { EventDispatcher.unregisterNativeEventHub(this.eventName); } } getSize() { return this.listenerIdList.length; } addCallback(handleFunc, callContext) { if (typeof handleFunc !== 'function') { throw new TypeError('Invalid addCallback function arguments'); } const targetEventHub = EventDispatcher.registerNativeEventHub(this.eventName); if (!targetEventHub) { throw new ReferenceError(`No listeners for ${this.eventName}`); } const listenerId = targetEventHub.addEventHandler(handleFunc, callContext); if (typeof listenerId !== 'number') { throw new Error('Fail to addEventHandler in addCallback function'); } this.listenerIdList.push(listenerId); return listenerId; } removeCallback(callbackId) { if (typeof callbackId !== 'number') { throw new TypeError('Invalid arguments for removeCallback'); } const targetEventHub = EventDispatcher.getHippyEventHub(this.eventName); if (!targetEventHub) { throw new ReferenceError(`No listeners for ${this.eventName}`); } targetEventHub.removeEventHandler(callbackId); const listenerIdSize = this.listenerIdList.length; for (let i = 0; i < listenerIdSize; i += 1) { if (callbackId === this.listenerIdList[i]) { this.listenerIdList.splice(i, 1); break; } } } } /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ function getNameForEvent(event) { if (typeof event !== 'string') { throw new TypeError('Invalid arguments for getNameForEvent'); } return `eventEmitter_${event}`; } class HippyEventEmitter { constructor(sharedListeners) { if (sharedListeners && typeof sharedListeners === 'object') { this.hippyEventListeners = sharedListeners; } else { this.hippyEventListeners = {}; } } sharedListeners() { return this.hippyEventListeners; } addListener(event, callback, context) { if (typeof event !== 'string' || typeof callback !== 'function') { throw new TypeError('Invalid arguments for addListener'); } let registeredListener = this.hippyEventListeners[getNameForEvent(event)]; if (!registeredListener) { registeredListener = new HippyEventListener(event); this.hippyEventListeners[getNameForEvent(event)] = registeredListener; } const listenerId = registeredListener.addCallback(callback, context); if (typeof listenerId !== 'number') { throw new Error('Fail to addCallback in addListener'); } return new EventEmitterRevoker(listenerId, registeredListener); } removeAllListeners(event) { if (typeof event !== 'string') { throw new TypeError('Invalid arguments for removeAllListeners'); } const registeredListener = this.hippyEventListeners[getNameForEvent(event)]; if (registeredListener) { registeredListener.unregister(); delete this.hippyEventListeners[getNameForEvent(event)]; } } emit(event, param) { if (typeof event !== 'string') { return false; } const eventHub = EventDispatcher.getHippyEventHub(event); if (!eventHub) { return false; } eventHub.notifyEvent(param); return true; } listenerSize(event) { if (typeof event !== 'string') { throw new TypeError('Invalid arguments for listenerSize'); } const registeredListener = this.hippyEventListeners[getNameForEvent(event)]; if (registeredListener) { return registeredListener.getSize(); } return 0; } } HippyEventEmitter.emit = HippyEventEmitter.prototype.emit; /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2022 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const globalEventListeners = {}; function addListener(event, callback, context) { if (!globalEventListeners[event]) { globalEventListeners[event] = { eventListener: new HippyEventListener(event), eventMap: new Map(), }; } const { eventListener, eventMap } = globalEventListeners[event]; const listenerId = eventListener.addCallback(callback, context); if (typeof listenerId !== 'number') { throw new Error('Fail to addCallback in addListener'); } eventMap.set(callback, listenerId); } function removeListener(event, callback) { // remove specific listener for this event const eventInfo = globalEventListeners[event]; if (!eventInfo) { return warn(`Event [${event}] has not been registered yet in EventBus`); } const { eventListener, eventMap } = eventInfo; // remove all listeners for this event if (!callback) { eventListener.unregister(); eventMap.clear(); delete globalEventListeners[event]; } else { // remove specific listener for this event const listenerId = eventMap.get(callback); if (typeof listenerId !== 'number') { return warn(`The listener for event [${event}] cannot be found to remove`); } eventListener.removeCallback(listenerId); eventMap.delete(callback); // if listeners size is 0, means this event info needs to be deleted if (eventMap.size === 0) { delete globalEventListeners[event]; } } } const EventBus = { on: (events, callback, context) => { if ((typeof events !== 'string' && !Array.isArray(events)) || typeof callback !== 'function') { throw new TypeError('Invalid arguments for EventBus.on()'); } if (Array.isArray(events)) { events.forEach((event) => { addListener(event, callback, context); }); } else { addListener(events, callback, context); } return EventBus; }, off: (events, callback) => { if (typeof events !== 'string' && !Array.isArray(events)) { throw new TypeError('The event argument is not string or array for EventBus.off()'); } if (Array.isArray(events)) { events.forEach((event) => { removeListener(event, callback); }); } else { removeListener(events, callback); } return EventBus; }, sizeOf(event) { if (typeof event !== 'string') { throw new TypeError('The event argument is not string for EventBus.sizeOf()'); } const eventInfo = globalEventListeners[event]; if (eventInfo === null || eventInfo === void 0 ? void 0 : eventInfo.eventMap) { return eventInfo.eventMap.size; } return 0; }, emit(event, ...param) { if (typeof event !== 'string') { throw new TypeError('The event argument is not string for EventBus.emit()'); } const eventHub = EventDispatcher.getHippyEventHub(event); if (!eventHub) { warn(`Event [${event}] has not been registered yet in EventBus`); return EventBus; } eventHub.notifyEvent(...param); return EventBus; }, }; /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ function call(...args) { return `\\(\\s*(${args.join(')\\s*,\\s*(')})\\s*\\)`; } const colors = { transparent: 0x00000000, aliceblue: 0xf0f8ffff, antiquewhite: 0xfaebd7ff, aqua: 0x00ffffff, aquamarine: 0x7fffd4ff, azure: 0xf0ffffff, beige: 0xf5f5dcff, bisque: 0xffe4c4ff, black: 0x000000ff, blanchedalmond: 0xffebcdff, blue: 0x0000ffff, blueviolet: 0x8a2be2ff, brown: 0xa52a2aff, burlywood: 0xdeb887ff, burntsienna: 0xea7e5dff, cadetblue: 0x5f9ea0ff, chartreuse: 0x7fff00ff, chocolate: 0xd2691eff, coral: 0xff7f50ff, cornflowerblue: 0x6495edff, cornsilk: 0xfff8dcff, crimson: 0xdc143cff, cyan: 0x00ffffff, darkblue: 0x00008bff, darkcyan: 0x008b8bff, darkgoldenrod: 0xb8860bff, darkgray: 0xa9a9a9ff, darkgreen: 0x006400ff, darkgrey: 0xa9a9a9ff, darkkhaki: 0xbdb76bff, darkmagenta: 0x8b008bff, darkolivegreen: 0x556b2fff, darkorange: 0xff8c00ff, darkorchid: 0x9932ccff, darkred: 0x8b0000ff, darksalmon: 0xe9967aff, darkseagreen: 0x8fbc8fff, darkslateblue: 0x483d8bff, darkslategray: 0x2f4f4fff, darkslategrey: 0x2f4f4fff, darkturquoise: 0x00ced1ff, darkviolet: 0x9400d3ff, deeppink: 0xff1493ff, deepskyblue: 0x00bfffff, dimgray: 0x696969ff, dimgrey: 0x696969ff, dodgerblue: 0x1e90ffff, firebrick: 0xb22222ff, floralwhite: 0xfffaf0ff, forestgreen: 0x228b22ff, fuchsia: 0xff00ffff, gainsboro: 0xdcdcdcff, ghostwhite: 0xf8f8ffff, gold: 0xffd700ff, goldenrod: 0xdaa520ff, gray: 0x808080ff, green: 0x008000ff, greenyellow: 0xadff2fff, grey: 0x808080ff, honeydew: 0xf0fff0ff, hotpink: 0xff69b4ff, indianred: 0xcd5c5cff, indigo: 0x4b0082ff, ivory: 0xfffff0ff, khaki: 0xf0e68cff, lavender: 0xe6e6faff, lavenderblush: 0xfff0f5ff, lawngreen: 0x7cfc00ff, lemonchiffon: 0xfffacdff, lightblue: 0xadd8e6ff, lightcoral: 0xf08080ff, lightcyan: 0xe0ffffff, lightgoldenrodyellow: 0xfafad2ff, lightgray: 0xd3d3d3ff, lightgreen: 0x90ee90ff, lightgrey: 0xd3d3d3ff, lightpink: 0xffb6c1ff, lightsalmon: 0xffa07aff, lightseagreen: 0x20b2aaff, lightskyblue: 0x87cefaff, lightslategray: 0x778899ff, lightslategrey: 0x778899ff, lightsteelblue: 0xb0c4deff, lightyellow: 0xffffe0ff, lime: 0x00ff00ff, limegreen: 0x32cd32ff, linen: 0xfaf0e6ff, magenta: 0xff00ffff, maroon: 0x800000ff, mediumaquamarine: 0x66cdaaff, mediumblue: 0x0000cdff, mediumorchid: 0xba55d3ff, mediumpurple: 0x9370dbff, mediumseagreen: 0x3cb371ff, mediumslateblue: 0x7b68eeff, mediumspringgreen: 0x00fa9aff, mediumturquoise: 0x48d1ccff, mediumvioletred: 0xc71585ff, midnightblue: 0x191970ff, mintcream: 0xf5fffaff, mistyrose: 0xffe4e1ff, moccasin: 0xffe4b5ff, navajowhite: 0xffdeadff, navy: 0x000080ff, oldlace: 0xfdf5e6ff, olive: 0x808000ff, olivedrab: 0x6b8e23ff, orange: 0xffa500ff, orangered: 0xff4500ff, orchid: 0xda70d6ff, palegoldenrod: 0xeee8aaff, palegreen: 0x98fb98ff, paleturquoise: 0xafeeeeff, palevioletred: 0xdb7093ff, papayawhip: 0xffefd5ff, peachpuff: 0xffdab9ff, peru: 0xcd853fff, pink: 0xffc0cbff, plum: 0xdda0ddff, powderblue: 0xb0e0e6ff, purple: 0x800080ff, rebeccapurple: 0x663399ff, red: 0xff0000ff, rosybrown: 0xbc8f8fff, royalblue: 0x4169e1ff, saddlebrown: 0x8b4513ff, salmon: 0xfa8072ff, sandybrown: 0xf4a460ff, seagreen: 0x2e8b57ff, seashell: 0xfff5eeff, sienna: 0xa0522dff, silver: 0xc0c0c0ff, skyblue: 0x87ceebff, slateblue: 0x6a5acdff, slategray: 0x708090ff, slategrey: 0x708090ff, snow: 0xfffafaff, springgreen: 0x00ff7fff, steelblue: 0x4682b4ff, tan: 0xd2b48cff, teal: 0x008080ff, thistle: 0xd8bfd8ff, tomato: 0xff6347ff, turquoise: 0x40e0d0ff, violet: 0xee82eeff, wheat: 0xf5deb3ff, white: 0xffffffff, whitesmoke: 0xf5f5f5ff, yellow: 0xffff00ff, yellowgreen: 0x9acd32ff, }; const NUMBER = '[-+]?\\d*\\.?\\d+'; const PERCENTAGE = `${NUMBER}%`; const matchers = { rgb: new RegExp(`rgb${call(NUMBER, NUMBER, NUMBER)}`), rgba: new RegExp(`rgba${call(NUMBER, NUMBER, NUMBER, NUMBER)}`), hsl: new RegExp(`hsl${call(NUMBER, PERCENTAGE, PERCENTAGE)}`), hsla: new RegExp(`hsla${call(NUMBER, PERCENTAGE, PERCENTAGE, NUMBER)}`), hex3: /^#([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, hex4: /^#([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, hex6: /^#([0-9a-fA-F]{6})$/, hex8: /^#([0-9a-fA-F]{8})$/, }; /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ function parse255(str) { const int = parseInt(str, 10); if (int < 0) { return 0; } if (int > 255) { return 255; } return int; } function parse1(str) { const num = parseFloat(str); if (num < 0) { return 0; } if (num > 1) { return 255; } return Math.round(num * 255); } function hue2rgb(p, q, tx) { let t = tx; if (t < 0) { t += 1; } if (t > 1) { t -= 1; } if (t < 1 / 6) { return p + (q - p) * 6 * t; } if (t < 1 / 2) { return q; } if (t < 2 / 3) { return p + (q - p) * (2 / 3 - t) * 6; } return p; } function hslToRgb(h, s, l) { const q = l < 0.5 ? l * (1 + s) : l + s - l * s; const p = 2 * l - q; const r = hue2rgb(p, q, h + 1 / 3); const g = hue2rgb(p, q, h); const b = hue2rgb(p, q, h - 1 / 3); return ((Math.round(r * 255) << 24) | (Math.round(g * 255) << 16) | (Math.round(b * 255) << 8)); } function parse360(str) { const int = parseFloat(str); return (((int % 360) + 360) % 360) / 360; } function parsePercentage(str) { const int = parseFloat(str); if (int < 0) { return 0; } if (int > 100) { return 1; } return int / 100; } function baseColor(color) { let match; if (typeof color === 'number') { if (color >>> 0 === color && color >= 0 && color <= 0xffffffff) { return color; } return null; } match = matchers.hex6.exec(color); if (Array.isArray(match)) { return parseInt(`${match[1]}ff`, 16) >>> 0; } if (Object.hasOwnProperty.call(colors, color)) { return colors[color]; } match = matchers.rgb.exec(color); if (Array.isArray(match)) { return ((parse255(match[1]) << 24) // r | (parse255(match[2]) << 16) // g | (parse255(match[3]) << 8) // b | 0x000000ff // a ) >>> 0; } match = matchers.rgba.exec(color); if (match) { return ((parse255(match[1]) << 24) // r | (parse255(match[2]) << 16) // g | (parse255(match[3]) << 8) // b | parse1(match[4]) // a ) >>> 0; } match = matchers.hex3.exec(color); if (match) { return parseInt(`${match[1] + match[1] // r + match[2] + match[2] // g + match[3] + match[3] // b }ff`, // a 16) >>> 0; } match = matchers.hex8.exec(color); if (match) { return parseInt(match[1], 16) >>> 0; } match = matchers.hex4.exec(color); if (match) { return parseInt(match[1] + match[1] // r + match[2] + match[2] // g + match[3] + match[3] // b + match[4] + match[4], // a 16) >>> 0; } match = matchers.hsl.exec(color); if (match) { return (hslToRgb(parse360(match[1]), // h parsePercentage(match[2]), // s parsePercentage(match[3])) | 0x000000ff // a ) >>> 0; } match = matchers.hsla.exec(color); if (match) { return (hslToRgb(parse360(match[1]), // h parsePercentage(match[2]), // s parsePercentage(match[3])) | parse1(match[4]) // a ) >>> 0; } return null; } /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Parse the color value to integer that native understand. * * @param {string} color - The color value. */ function colorParse(color) { if (Number.isInteger(color)) { return color; } let int32Color = baseColor(color); if (int32Color === null) { return 0; } int32Color = (int32Color << 24 | int32Color >>> 8) >>> 0; return int32Color; } /** * Parse the color values array to integer array that native understand. * * @param {string[]} colorArray The color values array. */ function colorArrayParse(colorArray) { if (!Array.isArray(colorArray)) { warn('Input color value is not a array', colorArray); return [0]; } return colorArray.map(color => colorParse(color)); } /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ function repeatCountDict(repeatCount) { if (repeatCount === 'loop') { return -1; } return repeatCount; } /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2022 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * parse value of special value type * @param valueType * @param originalValue */ function parseValue(valueType, originalValue) { if (valueType === 'color' && ['number', 'string'].indexOf(typeof originalValue) >= 0) { return colorParse(originalValue); } return originalValue; } const animationEvent$1 = { START: 'animationstart', END: 'animationend', CANCEL: 'animationcancel', REPEAT: 'animationrepeat', }; /** * Better performance of Animation solution. * * It pushes the animation scheme to native at once. */ class Animation { constructor(config) { var _a; let startValue; if (((_a = config.startValue) === null || _a === void 0 ? void 0 : _a.constructor) && config.startValue.constructor.name === 'Animation') { startValue = { animationId: config.startValue.animationId }; } else { const { startValue: tempStartValue } = config; startValue = parseValue(config.valueType, tempStartValue); } const toValue = parseVal