How to get the node a jstree context menu was triggered on? - javascript

Trying to get a jstree contextmenu up to trigger node-specific actions. Very helpful for starters: Doc to jstree.defaults.contextmenu.items
Sadly, one of the most interesting points is incomplete: What parameters are served to the action function assigned? Doing experiments, I already found it apparently has only one parameter (at least parameter 2 was undefined).
I progressed to connect action to
function action_on_context(data)
{
let s = '';
$.each(data, function(key, val) { s+=key + '=>' + Object.keys(val) + ', '});
console.log("Context Action: " + s);
}
Which yields me
Context Action:
item => icon,label,title,action,
reference => 0,length,prevObject,
element => 0,length,
position => x,y,
item: As the name says, just the config I gave to the menu option.
reference: Appears to relate to the link connected to the node.
element: Apparently is the li tag of the context menu option itself.
position: Is this the x,y mouse coordinates?
So far reference is my best hunch:
console.log(data.reference.attr('id'));
logs me the id of the anchor that seems to equal <tree_node_id_the_context_menu_was_opened_on>_anchor.
The question boils down to: "Is there a better way to obtain the node the context menu was opened on?"

I think, I can answer my own question:
reference actually is the anchor embedded within the <li> that defines the HTML making up the tree entry the context menu was opened upon.
Thus this parenting <li> element holds the id attribute of the tree node. And since reference is presented as a JQuery object, all I need is the jquery command
data.reference.parent().attr('id');

Related

Send DOM node object via chrome.tabs.sendMessage

I am writing a Chrome extension. I need to pass an element object from the content script to the background script.
The goal:
The extension is about record and replay user actions.
The data is saved on extension`s localstorage on different object for each tab (by tab ID).
The data structure is a list of {x: x, y:y, element: element}
When the user wants to replay, I am using a loop for each object on the list and using .click() on the element
Code in content script:
The function that sends a message to the background script:
function addToEventHistory(cords) {
console.log(cords)
chrome.runtime.sendMessage({action: "addToEventHistory", cords: cords}, function(response) {
return response;
});
}
The function that get the element and sens it:
mouseClick: function(e) {
var target = e.target || e.srcElement
var clickEvent = {x: e.pageX, y: e.pageY, element: target}
addToEventHistory(clickEvent)
}
The code in the background script:
var tabId = sender.tab.id;
var existingRecords = JSON.parse(localStorage.getItem('record_'+tabId)) || [];
existingRecords.push(request.cords)
console.log(request.cords)
localStorage.setItem('record_'+tabId, JSON.stringify(existingRecords));
sendResponse();
The problem is that the element that I am sending is recieved as an empty object. notice to the console.log on send and recieve. the outputs are:
sending:
Object {x: 1205, y: 1067, element: div#content.snippet-hidden}
receiving:
Object {x: 1205, y: 1067, element: Object}
* the element Object is empty and has only _proto_
What is the reason?
How can I solve this issue?
Looks like the issue is not serialize the DOM object, because the object looks ok right before the sending and not ok at receiving..
You can't send a DOM element as a runtime.sendMessage() message
The message in runtime.sendMessage() must be "a JSON-ifiable object". DOM elements/nodes are not JSON-ifiable. Thus, you can not send them. In your case, you are trying to send the target of the click event.
What you will need to do instead of trying to serialize the DOM element is, ultimately, determined by why you need this information in your background script.
If you want to identify the element, you will need to determine a unique selector. One way to do this would be to assign a unique ID to the element and pass that ID in your message. However, that will only be effective if you are wanting to refer to the DOM node during the time that page is loaded within that tab. Obviously, any ID you assign will not be available once the browser has left the page, or loaded it in a different tab. Thus, that alternative is only viable for identifying an element for the life of the current page. However, for an application where you were wanting to just store the actual DOM element, assigning a unique ID would be a valid solution. In other words, storing the DOM element would only be valid for the life of the page, so assigning a unique ID would be valid for the same time period (life of the current page).
If you want methods which will uniquely identify the element when the page is re-loaded, you will need to use a different method than assigning an ID. What to use will depend largely on how you are going about selecting the element when you are wanting to use it and how resilient you want the selection to be with respect to changes in the page structure (e.g. on pages where the structure is dynamic, you may need to use other methods than would work on a static page).
For your application, where you want to record and playback user actions, you will need to determine if you want to record these actions based on where the mouse is within the page, or based on the elements upon which the user initiates events. This is a common problem for applications/languages which are used to record/playback/simulate user actions. Commonly, the user is given the option as to how they want such user interaction to be recorded (e.g. by location or element). If you choose to store the user actions only by the location of the mouse at the time an event occurred, then you can use Document.elementFromPoint() to determine which element is now at that point and send the event to that element. However, when doing so, you will also need to track the scrolling state of the document and determine if you are going to store the location of the mouse based on the location of the mouse within the current display, or relative to the document.
I used a workaround to click the element,
besides save the element and then using element.click() I used the cords to click the element without saving the element itself:
document.elementFromPoint(cords.x - window.pageXOffset, cords.y - window.pageYOffset).click();

Can't access children of Konva Stage after cloning

I have a problem with konvajs. I have a konva Stage that I clone into a temporary Stage, so I can revert changes made by a user, when the user cancels.
The way I do this is, that I clone the existing Stage into a temporary one, destroy the children of the origin and after that I move the children of the temporary Stage back to the original and destroy the temporary Stage. The problem is, when I try to access the children now, for example via findOne('#id-of-child'), I get undefined, even though the children exist.
Here's what I've done so far:
clone: function()
{
var cloned_stage = this.stage.clone();
Array.each(this.stage.getChildren(), function(layer, lidx) {
if (layer.attrs.id) {
// setting the id to the ones, the children had before
cloned_stage.children[lidx].attrs.id = layer.attrs.id;
}
});
return cloned_stage;
},
restore: function(tmp_stage)
{
this.stage.destroyChildren();
Array.each(tmp_stage.getChildren(), function(layer, lidx) {
var tmp_layer = layer.clone();
tmp_layer.attrs.id = layer.attrs.id;
tmp_layer.moveTo(this.stage);
}.bind(this));
tmp_stage.destroy();
this.stage.draw();
},
Now when the user opens the toolbox to change something, the current stage is cloned with the "clone" function and when the user cancels his changes, the "restore" function is called with the cloned stage as parameter.
But after that when I try to do things like the following they do not work as expected.
some_child_of_the_stage.getLayer(); -> returns null
var edit_layer = this.stage.findOne('#edit-layer'); -> returns undefined
But the "some_child_of_the_stage" does exist and has a layer of course and the stage has a child with the id "edit-layer".
Me and my colleague are at our wit's end.
I appreciate any help and suggestions thank you.
Update:
A short fiddle showing the problem via console.log:
fiddle
It is better not to touch attrs property of a node and use public getters and setters.
Konva has special logic for storing id property. Selector by id #edit-layer may not work because of direct access to attrs id.
You can use name property fo that case.
https://jsfiddle.net/s36hepvg/12/

ngjstree: error when select node automatically

I'm using NgJsTree (https://github.com/ezraroi/ngJsTree) for create a tree. I would like that the option choose by user was saved; so I'm saving the user's choice and the full path in a pair of variable. In particular case, I get the full path like this data.instance.get_path(data.node,'/'); and the selected node in this way data.instance.get_node(data.selected[i])
I trigger the loaded event with this function:
openSelectedNode = function(e, data){
var nodes = config.path.split("/");
for(var i=0;i<nodes.length;i++){
$(this).jstree("open_node", $('#' + nodes[i].replace(" ", "")));
}
$(this).jstree('select_node', $('#' + nodes[nodes.length-1] ));
}
So in this way, when the tree is load, I can reopen the tree and select the correct node. The code works, but in console I have this error:
Uncaught TypeError: Cannot read property 'parents' of undefined
It isn't the right approach? Am i doing any errors?
Thanks in advance.
Regards
It looks like you might be calling node.parents somewhere, which happened to me. Also, I would approach your problem different.
I would:
Use the instance provided by ngJStree to reference the tree, and then use
var selectedNode = vm.treeInstance.jstree(true).get_selected();
Store that nodes id, then set its state to "selected: true" before you create the the tree the next time. It should be already selected that way.

Extending DOMWindow object

I'm a total JavaScript/jQuery n00b, so please forgive me in advance.
Imagine there's a webpage (W1) which displays a list of items (I1, I2, ...). Now there's a second webpage W2 which displays W1 in a FancyBox'ed iframe (<a class="iframe" href="addressof(W1)">Pick items</a>).
Each item inside a W1 has a JavaScript handler attached to its click event, which does this:
function SetOpenerClient(id, name,isHoldings)
{
var _hfBackPath = document.getElementById('ctl00_main_hfBackPath');
var url = _hfBackPath.value;
window.navigate(url +"?id="+id+"&name="+name+"&isHol="+isHoldings);
}
The usual way this whole mess is used is by window.open()ing a window and providing a "backpath":
window.open(addressof(W1) + "?backpath=" + addressOfHandlerThisWebSite,
"selectClient",
"status=no,toolbar=no,menubar=no,location=no,scrollbars=yes")
Now here's whay my investigations show. Whenever a user click on an item inside of W1, it niavigates back to backpath (_hfBackPath from SetOpenerClient()), which happens to be a specially crafted page which grabs id querystring parameter, stuffs it inside a Session and does all kinds of other stuff.
When W1 gets opened inside an iframe, Chrome complains:
Uncaught TypeError: Object [object DOMWindow] has no method 'navigate'
Here's whay I want (by the way, there's $50 bill glued under your chair) and I really hope this is possible. I want to "redefine" window.navigate method to so that I could handle item selection manually. Is this possible? My naive attempts of setting
DOMWindow.navigate = function () { alert("window.navigate"); };
apparently don't do anything.

How do I prevent the Javascript InfoVis SpaceTree `ST.select()` method from collapsing nodes?

I'm using the JavaScript InfoVis Toolkit and in particular the SpaceTree visualisation.
I need to expand all of the tree and then show a path from a particular leaf node back to the root.
I've got the tree to expand just fine but it's the selection of a leaf node and highlighting the path back to the root that's causing me some problems.
I'm using the ST.select(node, onComplete) function to select the leaf node I'm interested in and indeed the path back to the root (lines and nodes) are highlighted.
To do this I implemented the onBeforePlotNode and onBeforePlotLine ST.Controller methods to allow me to highlight the nodes back to the root and their plotlines:
onBeforePlotNode: function(node){
//add some color to the nodes in the path between the
//root node and the selected node.
if (node.selected) {
node.data.$color = "#dddddd";
} else {
delete node.data.$color;
}
},
onBeforePlotLine: function(adj){
if (adj.nodeFrom.selected && adj.nodeTo.selected) {
adj.data.$color = "#33CC33";
adj.data.$lineWidth = 5;
} else {
delete adj.data.$color;
delete adj.data.$lineWidth;
}
}
The problem is that when I call ST.select() to highlight the leaf node all child nodes beneath this level are collapsed/hidden.
To see this in action I've uploaded a couple of examples:
Full tree expansion - leaf not selected
Leaf selected - path shown, but all children below node N2 missing
You may need to scroll down if your browser window is a bit small.
So my question is, how do I show nodes from a leaf node back to the root node in JavaScript InfoVis without collapsing level 3's children (level 1 being the root)?
If there was a way to find my leafe node and walk the tree back to the root (setting styles on the way) then I'd be happing doing that
OK after digging through all that code, cluttering it with console.log() calls and breakpoints, I found it.
It has to do with the inital onClick call, the fact that the graph as an update loop that's running in the background and the fact that everything besides onClick seems to ignore the busy state of the graph.
What happens
onClick gets called and triggers a chain of events, part of them is asynchronous
select is being called which is more or less synchronous and does its work
onClick finally gets done and one of it's side effects is that it re-expands the graph
select has set clickedNode and now onClick uses the newly set value of it and screws up
Solution
We need to redesign select so it respects the busy state of the graph:
select: function(id, onComplete) {
var that = this;
if (this.busy) {
window.setTimeout(function() {
that.select(id, onComplete);
}, 1);
return;
}
// original select code follows here (remove the old var that = this; assignment)
That's all, we simply check for the busy state and delay select until it's false.
This should also be applied to all other function besides onClick that are called from the outside, the library designer here did a bad job of indicating what has side effects and what has not though.
Did you try setting "constrained: false" in ST's properties? That solved it for me.
http://thejit.org/static/v20/Docs/files/Visualizations/Spacetree-js.html

Categories