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