Files
App-Installer-For-Windows-8…/shared/html/js/event.js
T
2025-12-07 15:18:40 +08:00

403 lines
13 KiB
JavaScript

(function(global) {
"use strict";
if (!global.Windows) global.Windows = {};
if (!global.Windows.UI) global.Windows.UI = {};
if (!global.Windows.UI.Event) global.Windows.UI.Event = {};
var Monitor = (function() {
var _sIdAttr = "data-monitor-id";
var _idCounter = 1;
var _aRegistry = {};
var _typeRegistry = {}; // 按事件类型分类缓存
var _polling = false;
var _loopHandle = null;
var _cleanupThreshold = 30000; // 30秒清理一次
var _lastCleanup = Date.now();
var _checkInterval = 200; // 节流时间
var _eventTypes = [
"resize",
"position",
"attribute",
"child"
];
// 缓存 DOM 元素引用
var _elementCache = {};
function _ensureId(el) {
if (!el.getAttribute(_sIdAttr)) {
el.setAttribute(_sIdAttr, "monitor_" + (_idCounter++));
}
return el.getAttribute(_sIdAttr);
}
function _getElementById(id) {
if (_elementCache[id] && _elementCache[id].parentNode) {
return _elementCache[id];
}
var el = document.querySelector("[" + _sIdAttr + "=\"" + id + "\"]");
if (el) _elementCache[id] = el;
return el;
}
function _getAttrSnapshot(el) {
var attrs = {};
for (var i = 0; i < el.attributes.length; i++) {
var attr = el.attributes[i];
attrs[attr.name] = attr.value;
}
attrs["_rect"] = el.getBoundingClientRect();
return attrs;
}
function _hasChanged(snapshotA, snapshotB) {
for (var key in snapshotA) {
if (snapshotA.hasOwnProperty(key)) {
if (key === "_rect") {
var a = snapshotA[key],
b = snapshotB[key];
if (!b || a.top !== b.top || a.left !== b.left || a.width !== b.width || a.height !== b.height) {
return true;
}
} else {
if (snapshotA[key] !== snapshotB[key]) {
return true;
}
}
}
}
return false;
}
function _pollOnce() {
var now = Date.now();
// 按事件类型遍历,减少不必要检查
for (var type in _typeRegistry) {
if (!_typeRegistry.hasOwnProperty(type)) continue;
var list = _typeRegistry[type];
for (var i = 0; i < list.length; i++) {
var item = list[i];
var el = _getElementById(item.id);
if (!el) {
list.splice(i--, 1);
delete _elementCache[item.id];
continue;
}
var newSnapshot = _getAttrSnapshot(el);
if (_hasChanged(item.snapshot, newSnapshot)) {
item.snapshot = newSnapshot;
try {
item.callback.call(el, { type: type });
} catch (ex) {
console.error("Monitor callback error:", ex);
}
}
}
}
// 清理过期节点
if (now - _lastCleanup > _cleanupThreshold) {
_cleanup();
_lastCleanup = now;
}
}
function _startLoop() {
if (_polling) return;
_polling = true;
function loop() {
_pollOnce();
_loopHandle = global.requestAnimationFrame ? requestAnimationFrame(loop) : setTimeout(loop, _checkInterval);
}
loop();
}
function _stopLoop() {
_polling = false;
if (_loopHandle) {
if (global.cancelAnimationFrame) cancelAnimationFrame(_loopHandle);
else clearTimeout(_loopHandle);
_loopHandle = null;
}
}
function _cleanup() {
for (var type in _typeRegistry) {
if (!_typeRegistry.hasOwnProperty(type)) continue;
var list = _typeRegistry[type];
for (var i = 0; i < list.length; i++) {
if (!_getElementById(list[i].id)) {
list.splice(i--, 1);
delete _elementCache[list[i].id];
}
}
}
}
function observe(el, type, callback) {
if (_eventTypes.indexOf(type) < 0) throw new Error("Unsupported event type: " + type);
var id = _ensureId(el);
if (!_typeRegistry[type]) _typeRegistry[type] = [];
_typeRegistry[type].push({
id: id,
callback: callback,
snapshot: _getAttrSnapshot(el)
});
_startLoop();
}
function detach(el, type, callback) {
if (!_typeRegistry[type]) return;
var id = el.getAttribute(_sIdAttr);
if (!id) return;
var list = _typeRegistry[type];
for (var i = 0; i < list.length; i++) {
if (list[i].id === id && (!callback || list[i].callback === callback)) {
list.splice(i--, 1);
delete _elementCache[id];
}
}
}
function clearAll() {
_typeRegistry = {};
_elementCache = {};
_stopLoop();
}
return {
/**
* 监听元素变化,并触发回调函数。
* @param {Element} el 目标元素
* @param {string} type 事件类型,如 "resize", "position", "attribute", "child"
* @param {function} callback 回调函数,参数为事件对象
*/
observe: observe,
/**
* 取消监听元素变化。
* @param {Element} el 目标元素
* @param {string} type 事件类型,如 "resize", "position", "attribute", "child"
* @param {function} [callback] 回调函数,如果指定,则只移除指定的回调函数,否则移除所有回调函数。
*/
detach: detach,
/**
* 清除所有监听。
*/
clearAll: clearAll,
/**
* 事件类型枚举。
*/
EventType: {
/** 元素尺寸变化 */
resize: "resize",
/** 元素位置变化 */
position: "position",
/** 元素属性变化 */
attribute: "attribute",
/** 子元素变化 */
child: "child"
}
};
})();
global.Windows.UI.Event.Monitor = Monitor;
})(window);
/*
// 1) 监听元素尺寸变化
var el = document.getElementById("box");
Windows.UI.Event.Monitor.observe(el, "resize", function (e) {
console.log("resized", e.oldValue, e.newValue, e.rect);
});
// 2) 监听属性变化
Windows.UI.Event.Monitor.observe(el, "attributeChange", function (e) {
console.log("attrs changed", e.detail); // detail.added / removed / changed
});
// 3) 监听附着/分离
Windows.UI.Event.Monitor.observe(el, "attach", function (e) {
console.log("attached to doc");
});
Windows.UI.Event.Monitor.observe(el, "detach", function (e) {
console.log("detached from doc");
});
// 4) 取消监听
Windows.UI.Event.Monitor.unobserve(el, "resize", handler);
*/
(function(global) {
"use strict";
var EventUtil = {};
/**
* 添加事件,兼容 IE10/IE11
* @param {Element|Window|Document} el 目标元素
* @param {string} sType 事件类型,如 "click", "resize", "scroll"
* @param {function} pfHandler 回调函数
* @param {boolean} [bUseCapture] 是否捕获阶段,默认 false
*/
EventUtil.addEvent = function(el, sType, pfHandler, bUseCapture) {
if (!el || typeof sType !== "string" || typeof pfHandler !== "function") return;
bUseCapture = !!bUseCapture;
if (el.addEventListener) {
// 标准方式
el.addEventListener(sType, pfHandler, bUseCapture);
} else if (el.attachEvent) {
// IE8-9 fallback
el.attachEvent("on" + sType, pfHandler);
} else {
// 最原始方式
var oldHandler = el["on" + sType];
el["on" + sType] = function(e) {
if (oldHandler) oldHandler(e || window.event);
pfHandler(e || window.event);
};
}
};
/**
* 移除事件,兼容 IE10/IE11
* @param {Element|Window|Document} el 目标元素
* @param {string} sType 事件类型,如 "click", "resize", "scroll"
* @param {function} pfHandler 回调函数
* @param {boolean} [bUseCapture] 是否捕获阶段,默认 false
*/
EventUtil.removeEvent = function(el, sType, pfHandler, bUseCapture) {
if (!el || typeof sType !== "string" || typeof pfHandler !== "function") return;
bUseCapture = !!bUseCapture;
if (el.removeEventListener) {
el.removeEventListener(sType, pfHandler, bUseCapture);
} else if (el.detachEvent) {
el.detachEvent("on" + sType, pfHandler);
} else {
var oldHandler = el["on" + sType];
if (oldHandler === pfHandler) {
el["on" + sType] = null;
}
}
};
// 暴露到全局命名空间
if (typeof module !== "undefined" && module.exports) {
module.exports = {
Windows: {
UI: {
Event: {
Util: EventUtil
}
}
}
};
} else {
global.Windows = global.Windows || {};
global.Windows.UI = global.Windows.UI || {};
global.Windows.UI.Event = global.Windows.UI.Event || {};
global.Windows.UI.Event.Util = EventUtil;
}
})(this);
/*
使用示例:
var handler = function (e) {
console.log("事件触发", e.type);
};
// 添加事件
Windows.UI.Event.Util.addEvent(window, "resize", handler);
// 删除事件
Windows.UI.Event.Util.removeEvent(window, "resize", handler);
*/
(function(global) {
"use strict";
/**
*
* @param {function} fn
* @param {number} delay
* @param {boolean} immediate 是否在第一次立即执行(可选,默认 false)
* @returns {function} 返回一个新的函数,该函数在 delay 时间后执行 fn 函数,如果在 delay 时间内再次调用该函数,则会重新计时。
*/
function debounce(fn, delay, immediate) {
var timer = null;
var lastCall = 0;
return function() {
var context = this;
var args = arguments;
var now = +new Date();
var callNow = immediate && !timer;
if (now - lastCall >= delay) {
lastCall = now;
if (callNow) {
fn.apply(context, args);
}
}
clearTimeout(timer);
timer = setTimeout(function() {
lastCall = +new Date();
if (!immediate) {
fn.apply(context, args);
}
}, delay);
};
}
module.exports = { debounce: debounce };
})(this);
(function(global) {
"use strict";
var eToEvent = {}; // 存储元素和回调
var lastContent = {}; // 存储上一次的 textContent
// 注册文本变化事件
global.setTextChangeEvent = function(el, fn) {
if (!el || typeof fn !== "function") return;
var id = el.__textChangeId;
if (!id) {
id = Math.random().toString(36).substr(2, 9);
el.__textChangeId = id;
lastContent[id] = el.textContent;
}
eToEvent[id] = { el: el, callback: fn };
};
// 定时轮询
setInterval(function() {
var keys = Object.keys(eToEvent);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var obj = eToEvent[key];
var el = obj.el;
var currentText = el.textContent;
if (currentText !== lastContent[key]) {
lastContent[key] = currentText;
try {
obj.callback.call(el, currentText);
} catch (e) {
// 忽略回调错误
if (typeof console !== "undefined") console.error(e);
}
}
}
}, 20);
})(this);