From a8a8867506dcbc6994a879f34042456ed3ec9b96 Mon Sep 17 00:00:00 2001 From: saqimtiaz Date: Thu, 30 Jan 2025 13:03:58 +0100 Subject: [PATCH 1/3] feat(eventcatcher): provide access to event properties as JSON --- core/modules/utils/dom/dom.js | 80 ++++++++++++++++++++++------ core/modules/widgets/eventcatcher.js | 50 ++++++++++++++--- 2 files changed, 107 insertions(+), 23 deletions(-) diff --git a/core/modules/utils/dom/dom.js b/core/modules/utils/dom/dom.js index 5f33bbeea..2eb17e606 100644 --- a/core/modules/utils/dom/dom.js +++ b/core/modules/utils/dom/dom.js @@ -311,6 +311,48 @@ exports.getLocationPath = function() { return window.location.toString().split("#")[0]; }; +exports.copyEventProperties = function(event) { + var seen = new Set(); + + function isDOMElement(value) { + return value instanceof Node || value instanceof Window; + } + + function safeCopy(obj) { + //skip ciruclar references + if(seen.has(obj)) { + return "[Circular reference]"; + } + //skip functions + if(typeof obj !== "object" || obj === null) { + return obj; + } + //skip DOM elements + if(isDOMElement(obj)) { + return "[DOM Element]"; + } + //copy each element of the array + if(Array.isArray(obj)) { + return obj.map(safeCopy); + } + + seen.add(obj); + var copy = {}, key; + for(key in obj) { + try{ + copy[key] = safeCopy(obj[key]); + } catch(e) { + copy[key] = "[Unserializable]"; + } + } + return copy; + } + + var result = safeCopy(event); + seen.clear(); + return result; +}; + /* Collect DOM variables */ @@ -352,25 +394,29 @@ exports.collectDOMVariables = function(selectedNode,domNode,event) { variables["tv-widgetnode-width"] = domNode.offsetWidth.toString(); variables["tv-widgetnode-height"] = domNode.offsetHeight.toString(); } + if(event) { + var eventProperties = $tw.utils.copyEventProperties(event); + variables["event-properties"] = JSON.stringify(eventProperties,null,2); - if(event && ("clientX" in event) && ("clientY" in event)) { - if(selectedNode) { - // Add variables for event X and Y position relative to selected node - selectedNodeRect = selectedNode.getBoundingClientRect(); - variables["event-fromselected-posx"] = (event.clientX - selectedNodeRect.left).toString(); - variables["event-fromselected-posy"] = (event.clientY - selectedNodeRect.top).toString(); - } - - if(domNode) { - // Add variables for event X and Y position relative to event catcher node - domNodeRect = domNode.getBoundingClientRect(); - variables["event-fromcatcher-posx"] = (event.clientX - domNodeRect.left).toString(); - variables["event-fromcatcher-posy"] = (event.clientY - domNodeRect.top).toString(); - } + if(("clientX" in event) && ("clientY" in event)) { + if(selectedNode) { + // Add variables for event X and Y position relative to selected node + selectedNodeRect = selectedNode.getBoundingClientRect(); + variables["event-fromselected-posx"] = (event.clientX - selectedNodeRect.left).toString(); + variables["event-fromselected-posy"] = (event.clientY - selectedNodeRect.top).toString(); + } - // Add variables for event X and Y position relative to the viewport - variables["event-fromviewport-posx"] = event.clientX.toString(); - variables["event-fromviewport-posy"] = event.clientY.toString(); + if(domNode) { + // Add variables for event X and Y position relative to event catcher node + domNodeRect = domNode.getBoundingClientRect(); + variables["event-fromcatcher-posx"] = (event.clientX - domNodeRect.left).toString(); + variables["event-fromcatcher-posy"] = (event.clientY - domNodeRect.top).toString(); + } + + // Add variables for event X and Y position relative to the viewport + variables["event-fromviewport-posx"] = event.clientX.toString(); + variables["event-fromviewport-posy"] = event.clientY.toString(); + } } return variables; }; diff --git a/core/modules/widgets/eventcatcher.js b/core/modules/widgets/eventcatcher.js index dad9503c6..fffd8949f 100644 --- a/core/modules/widgets/eventcatcher.js +++ b/core/modules/widgets/eventcatcher.js @@ -23,6 +23,49 @@ Inherit from the base widget class */ EventWidget.prototype = new Widget(); + +function getEventPropertiesJSON(event) { + var seen = new Set(); + + function isDOMElement(value) { + return value instanceof Node || value instanceof Window; + } + + function safeCopy(obj) { + //skip ciruclar references + if(seen.has(obj)) { + return "[Circular reference]"; + } + //skip functions + if(typeof obj !== "object" || obj === null) { + return obj; + } + //skip DOM elements + if(isDOMElement(obj)) { + return "[DOM Element]"; + } + //copy each element of the array + if(Array.isArray(obj)) { + return obj.map(safeCopy); + } + + seen.add(obj); + var copy = {}, key; + for(key in obj) { + try{ + copy[key] = safeCopy(obj[key]); + } catch(e) { + copy[key] = "[Unserializable]"; + } + } + return copy; + } + + var result = safeCopy(event); + seen.clear(); + return result; +}; + /* Render this widget into the DOM */ @@ -50,8 +93,6 @@ EventWidget.prototype.render = function(parent,nextSibling) { actions = self.getAttribute("$"+type) || self.getAttribute("actions-"+type), stopPropagation = self.getAttribute("stopPropagation","onaction"), selectedNode = event.target, - selectedNodeRect, - catcherNodeRect, variables = {}; // Firefox can fire dragover and dragenter events on text nodes instead of their parents if(selectedNode.nodeType === 3) { @@ -70,13 +111,10 @@ EventWidget.prototype.render = function(parent,nextSibling) { if(selectedNode === domNode) { return false; } - // Only set up variables if we have actions to invoke - if(actions) { - variables = $tw.utils.collectDOMVariables(selectedNode,self.domNode,event); - } } // Execute our actions with the variables if(actions) { + variables = $tw.utils.collectDOMVariables(selectedNode,self.domNode,event); // Add a variable for the modifier key variables.modifier = $tw.keyboardManager.getEventModifierKeyDescriptor(event); // Add a variable for the mouse button From f13f684f163c9a63f3c00848700defc75ea671b6 Mon Sep 17 00:00:00 2001 From: saqimtiaz Date: Thu, 30 Jan 2025 13:15:12 +0100 Subject: [PATCH 2/3] feat(eventcatcher): provide access to event properties as JSON --- core/modules/widgets/eventcatcher.js | 43 ---------------------------- 1 file changed, 43 deletions(-) diff --git a/core/modules/widgets/eventcatcher.js b/core/modules/widgets/eventcatcher.js index fffd8949f..bd80b1bfa 100644 --- a/core/modules/widgets/eventcatcher.js +++ b/core/modules/widgets/eventcatcher.js @@ -23,49 +23,6 @@ Inherit from the base widget class */ EventWidget.prototype = new Widget(); - -function getEventPropertiesJSON(event) { - var seen = new Set(); - - function isDOMElement(value) { - return value instanceof Node || value instanceof Window; - } - - function safeCopy(obj) { - //skip ciruclar references - if(seen.has(obj)) { - return "[Circular reference]"; - } - //skip functions - if(typeof obj !== "object" || obj === null) { - return obj; - } - //skip DOM elements - if(isDOMElement(obj)) { - return "[DOM Element]"; - } - //copy each element of the array - if(Array.isArray(obj)) { - return obj.map(safeCopy); - } - - seen.add(obj); - var copy = {}, key; - for(key in obj) { - try{ - copy[key] = safeCopy(obj[key]); - } catch(e) { - copy[key] = "[Unserializable]"; - } - } - return copy; - } - - var result = safeCopy(event); - seen.clear(); - return result; -}; - /* Render this widget into the DOM */ From 7aa82c4c57bc05016d49399e91d83781379474ee Mon Sep 17 00:00:00 2001 From: saqimtiaz Date: Thu, 30 Jan 2025 16:48:52 +0100 Subject: [PATCH 3/3] refactor: moved new method out of dom.js --- core/modules/utils/dom/dom.js | 44 +---------------------------------- core/modules/utils/utils.js | 42 +++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/core/modules/utils/dom/dom.js b/core/modules/utils/dom/dom.js index 2eb17e606..6bf874cac 100644 --- a/core/modules/utils/dom/dom.js +++ b/core/modules/utils/dom/dom.js @@ -311,48 +311,6 @@ exports.getLocationPath = function() { return window.location.toString().split("#")[0]; }; -exports.copyEventProperties = function(event) { - var seen = new Set(); - - function isDOMElement(value) { - return value instanceof Node || value instanceof Window; - } - - function safeCopy(obj) { - //skip ciruclar references - if(seen.has(obj)) { - return "[Circular reference]"; - } - //skip functions - if(typeof obj !== "object" || obj === null) { - return obj; - } - //skip DOM elements - if(isDOMElement(obj)) { - return "[DOM Element]"; - } - //copy each element of the array - if(Array.isArray(obj)) { - return obj.map(safeCopy); - } - - seen.add(obj); - var copy = {}, key; - for(key in obj) { - try{ - copy[key] = safeCopy(obj[key]); - } catch(e) { - copy[key] = "[Unserializable]"; - } - } - return copy; - } - - var result = safeCopy(event); - seen.clear(); - return result; -}; - /* Collect DOM variables */ @@ -395,7 +353,7 @@ exports.collectDOMVariables = function(selectedNode,domNode,event) { variables["tv-widgetnode-height"] = domNode.offsetHeight.toString(); } if(event) { - var eventProperties = $tw.utils.copyEventProperties(event); + var eventProperties = $tw.utils.copyObjectPropertiesSafe(event); variables["event-properties"] = JSON.stringify(eventProperties,null,2); if(("clientX" in event) && ("clientY" in event)) { diff --git a/core/modules/utils/utils.js b/core/modules/utils/utils.js index 234de0c75..76f872581 100644 --- a/core/modules/utils/utils.js +++ b/core/modules/utils/utils.js @@ -292,6 +292,48 @@ exports.extendDeepCopy = function(object,extendedProperties) { return result; }; +exports.copyObjectPropertiesSafe = function(object) { + var seen = new Set(); + + function isDOMElement(value) { + return value instanceof Node || value instanceof Window; + } + + function safeCopy(obj) { + //skip ciruclar references + if(seen.has(obj)) { + return "[Circular reference]"; + } + //skip functions + if(typeof obj !== "object" || obj === null) { + return obj; + } + //skip DOM elements + if(isDOMElement(obj)) { + return "[DOM Element]"; + } + //copy each element of the array + if(Array.isArray(obj)) { + return obj.map(safeCopy); + } + + seen.add(obj); + var copy = {}, key; + for(key in obj) { + try{ + copy[key] = safeCopy(obj[key]); + } catch(e) { + copy[key] = "[Unserializable]"; + } + } + return copy; + } + + var result = safeCopy(object); + seen.clear(); + return result; +}; + exports.deepFreeze = function deepFreeze(object) { var property, key; if(object) {