I am trying to write an inventory system for a game using the AFrame library. I have a custom component that is set up to set the item i'm picking up to invisible and turn the in hand item visible. However when ever it runs it won't work.
Currently, i have a click eventlistener set up check when clicked. When clicked it is set to set the target item invisible (el) and the object in hand visible (handObj). It appears as tho the click function isn't evening running as I have a console.log set up to just check if the function ran or not.
Edit: I created a glitch with an example project
https://glitch.com/~tundra-ironclad
var hands = [null, null];
AFRAME.registerComponent('pickUp', {
schema: {
handObj: {type: 'selector', default: ''},
id: {type: 'string', default: ''}
},
init: function() {
var el = this.el;
var data = this.data;
el.addEventListener('click', function() {
//pickup="target= #right
console.log("hello");
if(hands[0] == null) hands[0] = data.id;
else if (hands[1] == null) hands[1] = data.id;
else break;
el.setAttribute('visible', 'false');
data.handObj.setAttribute('visible', 'true');
});
}
});
<a-box id="left1"
color="#AA0000" class="clickable"
position="8.000 0.200 7" depth = ".25" height = ".25" width = ".25"
event-set__enter="_event: mouseenter; material.color: #FF0000"
event-set__leave="_event: mouseleave; material.color: #AA0000"
pickUp="handObj: #left2; id: left"
>
I don't understand why the click function isn't working at all since i have copied the formatting from tutorials and those components work fine.
Your example freezes for me, but try pick-up instead of pickUp. HTML attributes are case-insensitive. We should add a warning for that.
Related
I'm trying to give a user the ability to swap around the toolbar between presets based on a button press. Ideally that button would be in the CKEditor screen, but I can play around with it.
I've found this SO Article in which I get the concept that you destroy your instance, and re-initialize it with a 'New Config'.
To follow suit I took one of the higher ranked responses, modified it to match my table, and threw it onto a button.
function toolbarSwap(){
var editor = CKEDITOR.instances.editor;
if (editor) { editor.destroy(true); }
CKEDITOR.config.toolbar_Basic = [['Bold','Italic','Underline',
'-','JustifyLeft','JustifyCenter','JustifyRight','-','Undo','Redo']];
CKEDITOR.config.toolbar = 'Basic';
CKEDITOR.config.width=400;
CKEDITOR.config.height=300;
CKEDITOR.replace('editor', CKEDITOR.config);
}
The replace command concerns me since I can't see it working, as to if the data within the editor will go away but either way nothing is happening when I click the button that runs this function.
Is this the best method, or is there a way I can do this within CKEditor directly?
Update
function toolbarSwap(){
var editor = CKEDITOR.instances['editor'];
if (editor) { editor.destroy(true); }
CKEDITOR.config.toolbar_Basic = [['Bold','Italic','Underline',
'-','JustifyLeft','JustifyCenter','JustifyRight','-','Undo','Redo']];
CKEDITOR.config.toolbar = 'Basic';
CKEDITOR.config.width=400;
CKEDITOR.config.height=300;
CKEDITOR.replace('editor', CKEDITOR.config);
}
It seems like modified the instantiation of editor with the ID resolves the issue of it finding it, but the Editor is being emptied every time I click it. Is there a way to reload the config, instead of destroying the instance?
Update 2
function changeToolBar() {
var expanded = true;
if (expanded === true) {
var myToolBar = [{ name: 'verticalCustomToolbar', groups: [ 'basicstyles'], items: [ 'Bold', 'Italic'] }];
var config = {};
config.toolbar = myToolBar;
CKEDITOR.instances.editor.destroy();//destroy the existing editor
CKEDITOR.replace('editor', config);
expanded = false;
} else {
CKEDITOR.instances.editor.destroy();//destroy the existing editor
CKEDITOR.replace('editor', {toolbar: 'Full'});
expanded = true;
console.log("Expand me")
};
}
Here is where I'm at so far. I am not losing the data during the re-init, but I am unable to ever get the 'else' statement to trigger.
My Function was correct, but the var being initialized -in- the function was resetting it's purpose everything. AKA It's -always- true on click
<button type="button" onclick="changeToolBar()">Swap It</button>
function changeToolBar() {
if (expanded === true) {
var myToolBar = [{ name: 'verticalCustomToolbar', groups: [ 'basicstyles'], items: [ 'Bold', 'Italic'] }]; //Generic placeholder
var config = {};
config.toolbar = myToolBar;
CKEDITOR.instances.editor.destroy();//destroy the existing editor
CKEDITOR.replace('editor', config);
console.log(expanded); // Logging to ensure change
expanded = false;
console.log(expanded); // Logging to ensure the change
} else {
CKEDITOR.instances.editor.destroy();//destroy the existing editor
CKEDITOR.replace('editor', {toolbar: 'Full'}); // Toolbar 'full' is a default one
expanded = true;
console.log("Expand me") // Logging if it works
};
}
Can also swap in Full with a predefined config for toolbars.
On my page I'm rendering a tree based on jsTree angular directive. I'm trying to achieve a showing tooltip on hover for particularly node which has a description field. I have checked list of API for jsTree and have found on hover event listener hover_node, with help angular directive for jsTree I have setup my scope function to this listener
<js-tree
tree-data="scope"
tree-model="tree"
tree-events="hover_node:nodeHovered"
tree-core="tree_core"
></js-tree>
$scope.nodeHovered = function (e, data) {
var dataNode = data.node;
var original = dataNode.original;
if (typeof original.description !== 'undefined'){
var nodeDescription = original.description;
$(original).tooltip({
title: nodeDescription,
animation: true
});
}
};
My function works well when I hover on any node, I can see the object data in console, so I wrote a piece of code which allows to see the description in console if typeof node description is not an equal undefined. It works well for console but when I added function bootstrap tooltip for this node that to show the description as tooltip, I've got next error
Uncaught TypeError: doc.addEventListener is not a function
As I could understand from some topics here, the nature of this mistake is arrays don't have addEventListener function, but I'm working with an object which is already defined thru the library, so could anybody help to find what I'm missing? I appreciate any help.
Plunker
my full code
var treeFolders = [];
$scope.tree = [];
$http.get("folders.json")
.then(function (res) {
treeFolders = res.data;
angular.forEach(treeFolders, function(obj){
if(!("parent" in obj)){
obj.parent = "#";
}
})
})
$scope.load = function(){
$scope.tree = treeFolders
};
$scope.tree_core = {
multiple: false, // disable multiple node selection
check_callback: function (operation, node, node_parent, node_position, more) {
return true; // allow all other operations
},
themes : {
theme : "default",
dots : true,
icons : true
}
};
$scope.nodeHovered = function (e, data) {
var dataNode = data.node;
var original = dataNode.original;
if (typeof original.description !== 'undefined'){
var nodeDescription = original.description;
$(original).tooltip({
title: nodeDescription,
animation: true
});
}
};
html
<button class="btn btn-default" ng-click="load()">Load Tree</button>
<js-tree
tree-data="scope"
tree-model="tree"
tree-events="hover_node:nodeHovered"
tree-core="tree_core"
></js-tree>
This is because the original object that you are passing to jQuery in the following code is a jsTree node, not a DOM element:
$(original).tooltip({
title: nodeDescription,
animation: true
});
Instead use the get_node method of jsTree to retrieve the jQuery wrapped DOM element from the node:
var liElement = data.instance.get_node(data.node, true);
This will give you the entire li element, but you probably want the tooltip placed relative to the a element:
var aElement = liElement.children('a').first();
Then:
aElement.tooltip({
title: nodeDescription,
animation: true
}).tooltip('show');
Note that I have added an extra call to tooltip at the end. This is for the tooltip to be shown the first time the element is hovered. Without this the tooltip would be created on the first hover, then not shown until the next.
Demo: https://plnkr.co/edit/itMSrsroTVswD3RLbs7O?p=preview
I use Leaflet.Control.Search for searching markers by GeoJSON features and it works OK. Now I have to type first letters to find marker, but I want to choose them from drop down list with all markers in it. Is it way to do it?
If you want a dropdown list with all your markers, you are better off creating a custom control rather than trying to modify Leaflet.Control.Search. Creating a control with a select element that contains all your markers is a little complicated, but certainly far simpler than tweaking the code of someone else's finished project.
Start by creating an empty control:
var selector = L.control({
position: 'topright'
});
To put content within the control, you can use the control's .onAdd method. Create a container div for the control using L.DomUtil.create, which in this context will automatically assign the class leaflet-control, allowing any content within the div to display on the map and behave like a control should behave. Then create a select element within the div. Give it a default option if you want. Most importantly, give it an id, so that you can refer to it later:
selector.onAdd = function(map) {
var div = L.DomUtil.create('div', 'mySelector');
div.innerHTML = '<select id="marker_select"><option value="init">(select item)</option></select>';
return div;
};
Now that the control knows what to do when added to the map, go ahead and add it:
selector.addTo(map);
To add all your markers as options in the selector, you can use the .eachLayer method, which iterates through all the markers in the group and calls a function for each. For each layer, create an option element and append it to the select element, using the id assigned above. Assuming that you have created a GeoJSON layer called markerLayer, which has a property called STATION that you want to use as the option text, it would look like this:
markerLayer.eachLayer(function(layer) {
var optionElement = document.createElement("option");
optionElement.innerHTML = layer.feature.properties.STATION;
optionElement.value = layer._leaflet_id;
L.DomUtil.get("marker_select").appendChild(optionElement);
});
Here, we're relying on the fact that each layer gets assigned a unique internal id number, _leaflet_id, when created. We set each option's value attribute to the corresponding layer's _leaflet_id, so that when the option is selected, we have a way to access the marker.
Finally, to get the control to actually do something when you select one of the options, add some event listeners, using the id of the selector element:
var marker_select = L.DomUtil.get("marker_select");
L.DomEvent.addListener(marker_select, 'click', function(e) {
L.DomEvent.stopPropagation(e);
});
L.DomEvent.addListener(marker_select, 'change', changeHandler);
The click listener with the stopPropagation method is to prevent clicks on the selector from propagating through to the map if they overlap with the map pane, which could immediately unselect the layer you are trying to highlight. The change listener will run a handler function, which you can set to do anything you want. Here, I've set it to open the popup for a marker when its corresponding option is selected:
function changeHandler(e) {
if (e.target.value == "init") {
map.closePopup();
} else {
markerLayer.getLayer(e.target.value).openPopup();
}
}
And that's it! Here is an example fiddle with all this working together:
http://jsfiddle.net/nathansnider/ev3kojon/
EDIT:
If you are using the MarkerCluster plugin, you can modify the change handler function to work with clustered markers using the .zoomToShowLayer method:
function changeHandler(e) {
if (e.target.value == "init") {
map.closePopup();
} else {
var selected = markerLayer.getLayer(e.target.value);
markerClusterLayer.zoomToShowLayer(selected, function() {
selected.openPopup();
})
}
}
Example:
http://jsfiddle.net/nathansnider/oqk6u0sL/
(I also updated the original code and example to use the .getLayer method rather than ._layers[e.target.value], because this is a cleaner way to access the marker based on its id)
There would be a way to slightly modify Leaflet-search plugin so that it shows the full list of markers when user clicks on the magnifier button (i.e. when user expands the search control).
As if the search was triggered for 0 typed letter.
Using option minLength: 0 without modifying the plugin code does not trigger a search without typing unfortunately.
L.Control.Search.mergeOptions({
minLength: 0 // Show full list when no text is typed.
});
L.Control.Search.include({
_handleKeypress: function(e) {
switch (e.keyCode) {
case 27: //Esc
this.collapse();
break;
case 13: //Enter
if (this._countertips == 1)
this._handleArrowSelect(1);
this._handleSubmit(); //do search
break;
case 38: //Up
this._handleArrowSelect(-1);
break;
case 40: //Down
this._handleArrowSelect(1);
break;
case 37: //Left
case 39: //Right
case 16: //Shift
case 17: //Ctrl
//case 32://Space
break;
case 8: //backspace
case 46: //delete
this._autoTypeTmp = false;
if (this._collapsing) { // break only if collapsing.
break;
}
default: //All keys
this._doSearch(); // see below
}
},
// externalized actual search process so that we can trigger it after control expansion.
_doSearch: function() {
if (this._input.value.length)
this._cancel.style.display = 'block';
else
this._cancel.style.display = 'none';
if (this._input.value.length >= this.options.minLength) {
var that = this;
clearTimeout(this.timerKeypress);
this.timerKeypress = setTimeout(function() {
that._fillRecordsCache();
}, this.options.delayType);
} else
this._hideTooltip();
},
expand: function(toggle) {
toggle = typeof toggle === 'boolean' ? toggle : true;
this._input.style.display = 'block';
L.DomUtil.addClass(this._container, 'search-exp');
if (toggle !== false) {
this._input.focus();
this._map.on('dragstart click', this.collapse, this);
}
this.fire('search_expanded');
this._doSearch(); // Added to trigger a search when expanding the control.
return this;
},
collapse: function() {
this._hideTooltip();
this._collapsing = true; // added to prevent showing tooltip when collapsing
this.cancel();
this._collapsing = false; // added to prevent showing tooltip when collapsing
this._alert.style.display = 'none';
this._input.blur();
if (this.options.collapsed) {
this._input.style.display = 'none';
this._cancel.style.display = 'none';
L.DomUtil.removeClass(this._container, 'search-exp');
if (this.options.hideMarkerOnCollapse) {
this._markerLoc.hide();
}
this._map.off('dragstart click', this.collapse, this);
}
this.fire('search_collapsed');
return this;
}
});
Include this code in your JavaScript before instantiating L.Control.Search.
Demo: http://jsfiddle.net/ve2huzxw/190/
The big drawback of this solution is that the list of markers is built within the map container. As such, if it is too big, it will be cropped on the bottom, whereas a true select (drop-down) input would "overflow" beyond the container, as in nathansnider's solution.
I have customized form of multi-field in a component having two variations.
In one variation of my component I want to hide a field (title) which is inside custom-multi-field . I am using the following JavaScript code in listener.
This code is not working. Where am I wrong?
function() {
var dialog = this.findParentByType('dialog');
var contenttype = dialog.getField("./type").getValue();
var teaserlinks = dialog.getField("./teaserlinks");
var title = dialog.getField("./teaserlinks").getField("./title");
alert(title);
if(contenttype == 'variation-1'){
teaserlinks.show();
title.hide();
}
else if(contenttype == 'variation-2'){
teaserlinks.show();
}
}
Try using the hidden property of node. Initially set the hidden property to true and in javascript file change the hidden property to false (or as per your requirement).
Few imp points first before answer:
you have to write listener in your widget file only.
below is the sample code where in I have 2 fields. 1st field is mytext field and another field is myselection. On changing the value in myselection field I am toggling visibility of my text field.
Below is snippet:
this.mytext = new CQ.form.textField({...})
this.myselection = new CQ.form.Selection({
fieldLabel:"my selection",
type:"select",
width : "325",
allowBlank:false,
defaultType:"String[]",
fieldDescription : "Select value from dropdown",
options: "/a/b/c.json",
listeners : {
selectionchanged : function(){
var mytext = this.findParentByType('mywidget').mytext;
mytext.hide();
}
}
});
I hope this will be helpful.
I have no knowledge about aem and Adobe CQ5 but I can give you some hints how to debug your script.
First of all don't use alert for debugging! (BTW what does alert(title); show?)
I would recommend to open the browser console (e.g. Press <F12> on Firefox and switch to the tab "Console").
Herein the browser displays all exceptions and error messages. Additionally you can output some text with console.log("...");` from your script.
Here is my edit of your program. Perhaps the output can help you.
function()
{
var dialog = this.findParentByType('dialog');
var contenttype = dialog.getField("./type").getValue();
var teaserlinks = dialog.getField("./teaserlinks");
var title = dialog.getField("./teaserlinks").getField("./title");
console.dir(title);
console.log(contenttype);
teaserlinks.show();
if(contenttype == 'variation-1')
{
title.hide();
}
else if(contenttype == 'variation-2')
{
title.show();
}
}
And, console.dir(<object>); shows you the object structure to one level deep.
I have an instance of Sigma.Js 1.0.0 rendering a graph in my Canvas element. (The code is below, but you can simply scroll to Step 2 of Tutorial on the main sigmajs.org page.
As you can see from that code, when the node is clicked, clickNode event occurs, which then applies filtering to the graph, showing only the clicked node and its neighborhood and dimming the others. That's quite clear.
However, how would I make exactly the same thing happen from the outside? Suppose I have the graph rendered already and I have a Tag Cloud next to it. And I want that when I click on a #hashtag, only that node is shown in the graph and the rest are dimmed. How would I do that?
Thanks!
<div id="sigma-container"></div>
<script src="path/to/sigma.js"></script>
<script src="path/to/sigma.parsers.min.gexf.js"></script>
<script>
// Add a method to the graph model that returns an
// object with every neighbors of a node inside:
sigma.classes.graph.addMethod('neighbors', function(nodeId) {
var k,
neighbors = {},
index = this.allNeighborsIndex[nodeId] || {};
for (k in index)
neighbors[k] = this.nodesIndex[k];
return neighbors;
});
sigma.parsers.gexf(
'path/to/les-miserables.gexf',
{
container: 'sigma-container'
},
function(s) {
// We first need to save the original colors of our
// nodes and edges, like this:
s.graph.nodes().forEach(function(n) {
n.originalColor = n.color;
});
s.graph.edges().forEach(function(e) {
e.originalColor = e.color;
});
// When a node is clicked, we check for each node
// if it is a neighbor of the clicked one. If not,
// we set its color as grey, and else, it takes its
// original color.
// We do the same for the edges, and we only keep
// edges that have both extremities colored.
s.bind('clickNode', function(e) {
var nodeId = e.data.node.id,
toKeep = s.graph.neighbors(nodeId);
toKeep[nodeId] = e.data.node;
s.graph.nodes().forEach(function(n) {
if (toKeep[n.id])
n.color = n.originalColor;
else
n.color = '#eee';
});
s.graph.edges().forEach(function(e) {
if (toKeep[e.source] && toKeep[e.target])
e.color = e.originalColor;
else
e.color = '#eee';
});
// Since the data has been modified, we need to
// call the refresh method to make the colors
// update effective.
s.refresh();
});
// When the stage is clicked, we just color each
// node and edge with its original color.
s.bind('clickStage', function(e) {
s.graph.nodes().forEach(function(n) {
n.color = n.originalColor;
});
s.graph.edges().forEach(function(e) {
e.color = e.originalColor;
});
// Same as in the previous event:
s.refresh();
});
}
);
</script>
<!-- [...] -->
I hope this goes some way to answering your question.
You have a tagcloud full of words, and when a word is clicked, you want to trigger the neighbors method on your sigma instance, for which you need the node id.
Simply put, you need the function which is called when the #hashtag is clicked, to be in the same scope as the sigma instantiation.
s= new sigma({
settings: {...}
})
//more code instantiating methods etc
//let's assume your tags are in elements with class='tagword' and have the hashtag stored in a 'name' attribute
$('.tagword').on('click', function(){
var name = this.attr('name');
s.graph.nodes().forEach(function(n){
if (n.label == name){
//use the node to trigger an event in sigma
//i.e. s.graph.neighbors(n.id);
};
};
};