Selection.containsNode() returning true even when Node is not contained - dom

From https://developer.mozilla.org/en-US/docs/Web/API/Selection/containsNode
Parameters
node
The node that is being looked for in the selection. partialContainment Optional
When true, containsNode() returns true when a part of the node is part of the selection. When false, containsNode() only returns true
when the entire node is part of the selection. If not specified, the
default value false is used.

I wrote a workaround (in typescript)
function getRangePartiallySelectedNodes(
selection: Selection,
parent: Node
): Node[] {
return [
// containsNode doesn't work as expected, always gives partial nodes
...(selection.containsNode(parent, true) ? [parent] : []),
...(parent.hasChildNodes()
? Array.from(parent.childNodes)
.map((node) => getRangePartiallySelectedNodes(selection, node))
.flat()
: []),
];
}
function isAncestor(node: Node, possibleAncestor: Node): boolean {
if (!node || !node.parentNode) return false;
return (
node.parentNode.isSameNode(possibleAncestor) ||
isAncestor(node.parentNode, possibleAncestor)
);
}
export function selectionContainsNodes(
sel: Selection,
// limitation: the selection must be within the elementEditing node
elementEditing: HTMLElement
): boolean {
if (sel.isCollapsed) return false;
const nodes = getRangePartiallySelectedNodes(sel, elementEditing);
// sel.anchorNode and sel.focusNode may be parents of the actual selection start instead of the selection themselves
return nodes.some(
(node) =>
!isAncestor(sel.anchorNode, node) &&
!node.isSameNode(sel.anchorNode) &&
!isAncestor(sel.focusNode, node) &&
!node.isSameNode(sel.focusNode)
);

Related

Getting locations of the variable declarations

I am developing an extension which requires me to get the locations of the variable declarations.
For example,
var x = 5;
console.log(x);
Does the VS Code API provide functionality like getVariableLocations() which will return the position of the var x = 5;?
You can get the document symbols by running 'vscode.executeDocumentSymbolProvider'.
Here's an example that executes the command on the active document, and then converts the nested list of symbols (each DocumentSymbol can have children) into a flat list filtered by SymbolKind.Variable:
function findVars(symbols: vscode.DocumentSymbol[]): vscode.DocumentSymbol[] {
var vars =
symbols.filter(symbol => symbol.kind === vscode.SymbolKind.Variable);
return vars.concat(symbols.map(symbol => findVars(symbol.children))
.reduce((a, b) => a.concat(b), []));
}
var activeEditor = vscode.window.activeTextEditor;
if (activeEditor !== undefined) {
vscode.commands
.executeCommand<vscode.DocumentSymbol[]>(
'vscode.executeDocumentSymbolProvider', activeEditor.document.uri)
.then(symbols => {
if (symbols !== undefined) {
for (const variable of findVars(symbols)) {
console.log(variable.name);
}
}
});
}
When running this on this code snippet itself, it logs activeEditor, vars and variable. You can check the position with DocumentSymbol.range.

Efficiently combine many IObservable<bool> streams with boolean operators

I'm looking to combine many IObservable<bool> streams such that when the latest value for all of them is true, a true is emitted, and otherwise a false is emitted.
CombinedLast would allow me to build something like this for two streams easily, but a) I'm not sure the API easily allows thousands of streams to be combined and b) I'm not sure how efficient it would be even if it could.
All is kinda similar to what I want except I'm assuming that works over a single sequence and once false cannot dynamically changes back to true.
Also I need the values to be "distinct until changed", although the DistintUntilChanged operator may not be efficient for this?
I'm hoping for an O(1) algorithm.
A good approach for combining the latest is to start with a IObservable<IObservable<T>> and turn it in to a IObservable<T[]>. This becomes a very dynamic way to combine as many values you need.
Here's an extension method to do this:
public static IObservable<T[]> CombineLatest<T>(this IObservable<IObservable<T>> sources)
{
return
sources.Publish(ss =>
Observable.Create<T[]>(o =>
{
var composite = new CompositeDisposable();
var list = new List<T>();
composite.Add(
ss.Subscribe(source =>
{
var index = list.Count;
list.Add(default(T));
composite.Add(source.Subscribe(x => list[index] = x));
}));
composite.Add(ss.Merge().Select(x => list.ToArray()).Subscribe(o));
return composite;
}));
}
This nicely creates and tracks all subscriptions and uses a closure to define the index that each subscription needs to use to update its value in the list that is used for output.
If you use it like this:
var sources = new Subject<IObservable<bool>>();
var output = sources.CombineLatest();
output.Subscribe(x => Console.WriteLine(x));
var s1 = new Subject<bool>();
sources.OnNext(s1);
s1.OnNext(true);
var s2 = new Subject<bool>();
sources.OnNext(s2);
s2.OnNext(false);
var s3 = new Subject<bool>();
sources.OnNext(s3);
s3.OnNext(true);
s2.OnNext(true);
s1.OnNext(false);
Then you get this output:
If you change the definition of output to var output = sources.CombineLatest().Select(xs => xs.Aggregate((x, y) => x & y)); then you get the output that I think you're after:
True
False
False
True
False
I don't know how to do this in a classically functional way and still achieve O(1). This used mutable state, and is O(1) for observing each message, but O(n) for memory:
public IObservable<bool> CombineBooleans(this IObservable<bool>[] source)
{
return source.Select((o, i) => o.Select(b => (value: b, index: i)))
.Merge()
.Scan((array: new bool[source.Length], countFalse: source.Length), (state, item) =>
{
var countFalse = state.countFalse;
if (state.array[item.index] == item.value)
return (state.array, countFalse); //nothing to change, emit same state
else if (state.array[item.index]) //previous/current state is true, becoming false
{
countFalse++;
state.array[item.index] = false;
}
else //previous/current state is false, becoming true
{
countFalse--;
state.array[item.index] = true;
}
return (state.array, countFalse);
})
.Scan((countFalse: source.Length, oldCountFalse: source.Length), (state, item) => (countFalse: item.countFalse, oldCountFalse: state.countFalse))
.SelectMany(state =>
state.countFalse == 1 && state.oldCountFalse == 0
? Observable.Return(false)
: state.countFalse == 0 && state.oldCountFalse == 1
? Observable.Return(true)
: Observable.Empty<bool>()
)
.Publish()
.RefCount();
}
EDIT: Added .Publish().Refcount() to eliminate multiple-subscriber bugs.

Scala - recursive function not returning the object

I have the following object structure:
case class Node(id:Int,children:List[Node])
Example:
NodeA
id: 1
children:[
NodeA1:
id: 2
children:
NodeA11
...
]
NodeB1:
id: 3
children:[]
NodeC1:
id: 17
children: [
NodeC11:
id:11
children:[
NodeC111:
id: 19
children: []
]
]
...
I would like to create a recursive looping to get the Node that has a specific Id but i'm stuck in how to keep running the fuction if the iD isn't found and the object has any object on children list. My function only works to get the first node (ex.: Id = 1).
Here is what i'm trying to do:
def getNode(id:Int, node:Node) : Node = {
var result:Node = null
if(node.id == id){
return node
} else if(node.children.size > 0 ){
for(children <- node.children){
result = getNode(id, children)
if(result.id == id){
return result
}
}
}
return result
}
Function getNode really should return Option[Node] to account for searches for an id missing in the Node tree.
And in that case you can compose Options of recursive calls:
def getNode(id:Int, node:Node): Option[Node] =
if (node.id == id) Some(node)
else node.children.collectFirst(Function.unlift(getNode(id, _)))
In imperative case you don't need the check for list length: just return None/null after the loop where you check every child (or don't check if there aren't any children).
def getNode(id:Int, node:Node) : Option[Node] = {
if (node.id == id) Some(node)
else {
for (child <- node.children) {
val result = getNode(id, child)
// At this point `result` is Some(nodeWeAreLookingFor)
// if it is in the subtree of the current `child`
// or None otherwise
if (result.isDefined) return result
}
None
}
}
For Java you can of course replace Option with null, but in Scala this idea is naturally modelled by Option

Trying to work with down() method from ExtJS 4.2.1

I am trying to find a specific element from my page using ExtJS 4 so I can do modifications on it.
I know its id so it should not be a problem BUT
-I tried Ext.getCmp('theId') and it just return me undefined
-I tried to use down('theId') method by passing through the view and I still get a nullresponse.
As I know the id of the element I tried again the two methods by setting manually the id and it didn't work neither.
Do these two methods not function?
How should I do?
Here is the concerned part of the code :
listeners: {
load: function(node, records, successful, eOpts) {
var ownertree = records.store.ownerTree;
var boundView = ownertree.dockedItems.items[1].view.id;
var generalId = boundView+'-record-';
// Add row stripping on leaf nodes when a node is expanded
//
// Adding the same feature to the whole tree instead of leaf nodes
// would not be much more complicated but it would require iterating
// the whole tree starting with the root node to build a list of
// all visible nodes. The same function would need to be called
// on expand, collapse, append, insert, remove and load events.
if (!node.tree.root.data.leaf) {
// Process each child node
node.tree.root.cascadeBy(function(currentChild) {
// Process only leaf
if (currentChild.data.leaf) {
var nodeId = ""+generalId+currentChild.internalId;
var index = currentChild.data.index;
if ((index % 2) == 0) {
// even node
currentChild.data.cls.replace('tree-odd-node', '')
currentChild.data.cls = 'tree-even-node';
} else {
// odd node
currentChild.data.cls.replace('tree-even-node', '')
currentChild.data.cls = 'tree-odd-node';
}
// Update CSS classes
currentChild.triggerUIUpdate();
console.log(nodeId);
console.log(ownertree.view.body);
console.log(Ext.getCmp(nodeId));
console.log(Ext.getCmp('treeview-1016-record-02001001'));
console.log(ownertree.view.body.down(nodeId));
console.log(ownertree.view.body.down('treeview-1016-record-02001001'));
}
});
}
}
You can see my console.log at the end.
Here is what they give me on the javascript console (in the right order):
treeview-1016-record-02001001
The precise id I am looking for. And I also try manually in case...
h {dom: table#treeview-1016-table.x-treeview-1016-table x-grid-table, el: h, id: "treeview-1016gridBody", $cache: Object, lastBox: Object…}
I checked every configs of this item and its dom and it is exactly the part of the dom I am looking for, which is the view containing my tree. The BIG parent
And then:
undefined
undefined
null
null
Here is the item I want to access:
<tr role="row" id="treeview-1016-record-02001001" ...>
And I checked there is no id duplication anywhere...
I asked someone else who told me these methods do not work. The problem is I need to access this item to modify its cls.
I would appreciate any idea.
You are looking for Ext.get(id). Ext.getCmp(id) is used for Ext.Components, and Ext.get(id) is used for Ext.dom.Elements. See the docs here: http://docs.sencha.com/extjs/4.2.1/#!/api/Ext-method-get
Ok so finally I used the afteritemexpand listener. With the ids I get the elements I am looking for with your Ext.get(id) method kevhender :).
The reason is that the dom elements where not completely loaded when I used my load listener (it was just the store) so the Ext.get(id) method couldn't get the the element correctly. I first used afterlayout listener, that was correct but too often called and the access to the id was not so easy.
So, here is how I did finally :
listeners: {
load: function(node, records, successful, eOpts) {
var ownertree = records.store.ownerTree;
var boundView = ownertree.dockedItems.items[1].view.id;
var generalId = boundView+'-record-';
if (!node.tree.root.data.leaf) {
// Process each child node
node.tree.root.cascadeBy(function(currentChild) {
// Process only leaf
if (currentChild.data.leaf) {
var nodeId = ""+generalId+currentChild.internalId;
var index = currentChild.data.index;
if ( (index % 2) == 0 && ids.indexOf(nodeId) == -1 ) {
ids[indiceIds] = nodeId;
indiceIds++;
}
console.log(ids);
}
});
}
},
afteritemexpand: function( node, index, item, eOpts ){
/* This commented section below could replace the load but the load is done during store loading while afteritemexpand is done after expanding an item.
So, load listener makes saving time AND makes loading time constant. That is not the case if we just consider the commented section below because
the more you expand nodes, the more items it will have to get and so loading time is more and more important
*/
// var domLeaf = Ext.get(item.id).next();
// for ( var int = 0; int < node.childNodes.length; int++) {
// if (node.childNodes[int].data.leaf && (int % 2) == 0) {
// if (ids.indexOf(domLeaf.id) == -1) {
// ids[indiceIds] = domLeaf.id;
// indiceIds++;
// }
// }
// domLeaf = domLeaf.next();
// }
for ( var int = 0; int < ids.length; int++) {
domLeaf = Ext.get(ids[int]);
if (domLeaf != null) {
for ( var int2 = 0; int2 < domLeaf.dom.children.length; int2++) {
if (domLeaf.dom.children[int2].className.search('tree-even-node') == -1){
domLeaf.dom.children[int2].className += ' tree-even-node';
}
}
}
}
},
With ids an Array of the ids I need to set the class.
Thank you for the method.

tinymce.dom.replace throws an exception concerning parentNode

I'm writing a tinyMce plugin which contains a section of code, replacing one element for another. I'm using the editor's dom instance to create the node I want to insert, and I'm using the same instance to do the replacement.
My code is as follows:
var nodeData =
{
"data-widgetId": data.widget.widgetKey(),
"data-instanceKey": "instance1",
src: "/content/images/icon48/cog.png",
class: "widgetPlaceholder",
title: data.widget.getInfo().name
};
var nodeToInsert = ed.dom.create("img", nodeData);
// Insert this content into the editor window
if (data.mode == 'add') {
tinymce.DOM.add(ed.getBody(), nodeToInsert);
}
else if (data.mode == 'edit' && data.selected != null) {
var instanceKey = $(data.selected).attr("data-instancekey");
var elementToReplace = tinymce.DOM.select("[data-instancekey=" + instanceKey + "]");
if (elementToReplace.length === 1) {
ed.dom.replace(elementToReplace[0], nodeToInsert);
}
else {
throw new "No element to replace with that instance key";
}
}
TinyMCE breaks during the replace, here:
replace : function(n, o, k) {
var t = this;
if (is(o, 'array'))
n = n.cloneNode(true);
return t.run(o, function(o) {
if (k) {
each(tinymce.grep(o.childNodes), function(c) {
n.appendChild(c);
});
}
return o.parentNode.replaceChild(n, o);
});
},
..with the error Cannot call method 'replaceChild' of null.
I've verified that the two argument's being passed into replace() are not null and that their parentNode fields are instantiated. I've also taken care to make sure that the elements are being created and replace using the same document instance (I understand I.E has an issue with this).
I've done all this development in Google Chrome, but I receive the same errors in Firefox 4 and IE8 also. Has anyone else come across this?
Thanks in advance
As it turns out, I was simply passing in the arguments in the wrong order. I should have been passing the node I wanted to insert first, and the node I wanted to replace second.