Kinetic.js / Javascript: calling a variable as a property without using eval()? - javascript

I am trying to teach myself HTML5 and JavaScript, and would appreciate any help.
I solved a problem by using eval(), and was wondering if there was another way to solve it without using eval(). Everything I've tried doesn't seem to work.
I have two arrays, defined like this:
var europe = {
shapes: {
AL: "M520.651,114.27l-0.257,0.900l0.385,1.160l1.029,0.643l0,0.644l-0.901,0.386l-0.128,0.901l-1.288,1.287l-0.386-0.128l-0.127-0.644l-1.417-0.900l-0.259-1.288l0.259-1.803l0.256-0.901l-0.384-0.386l-0.258-0.901l1.287-1.288l0.129,0.516l0.771-0.258l0.516,0.773l0.643,0.257l-0.130-1.030z",
AM: "M582.697,116.33l3.605,-0.515l0.642,0.772l1.032,0.386l-0.516,0.773l1.416,0.900l-0.772,0.902l1.159,0.643l1.158,0.516l0.129,1.801l-1.029,0.129l-1.032,-1.544l0,-0.515l-1.287,0.129l-0.771,-0.772l-0.516,0l-1.029,-0.773l-2.059,-0.643l0.256,-1.288l0.386,0.901z",
AT: "M510.996,97.278l-0.257,1.158l-1.545,0l0.643,0.643l-0.900,1.674l-0.515,0.515l-2.446,0l-1.289,0.644l-2.315-0.258l-3.734-0.644l-0.644-0.900l-2.703,0.386l-0.258,0.514l-1.672-0.386l-1.416,0l-1.160-0.514l0.385-0.644l-0.128-0.515l0.903-0.128l1.285,0.772l0.387-0.772l2.446,0.128l1.931-0.515l1.287,0.128l0.773,0.515l0.258-0.386l-0.387-1.802l1.030-0.386l0.901-1.158l2.058,0.772l1.417-1.030l1.030-0.258l2.061,0.901l1.286-0.129l1.158,0.516l-0.127,0.256l-0.257-0.903z",
},
names: {
AL: "Albania",
AM: "Armenia",
AT: "Austria",
}
};
And what I am doing is iterating over the path data in europe.shapes to draw it, and then displaying the matching data in europe.names using this code:
for(var euroKey in europe.shapes) {
var pathEuro = new Kinetic.Path({
data: europe.shapes[euroKey],
name: euroKey,
fill: 'yellow',
stroke: '#555',
strokeWidth: 1
});
pathEuro.on('mouseover', function() {
var euroText = eval('europe.names.'+this.getName());
writeMessage(euroText);
this.setFill('#CCCCCC');
this.moveTo(topLayer);
topLayer.drawScene();
});
pathEuro.on('mouseout', function() {
writeMessage('');
this.setFill('#eee');
this.moveTo(mapLayer);
topLayer.draw();
});
textLayer.add(countryText);
mapLayer.add(pathEuro);
};
This is mostly stolen from the tutorial here: http://www.html5canvastutorials.com/labs/html5-canvas-world-map-svg-path-with-kineticjs/ but I am trying to add in the mouseover text of the country name.
This method using eval() works, but as I doing this on my own I don't want to learn bad habits.
The whole code is up on GitHub here: https://github.com/malkie-labs/html5-world-map if anyone is really, really bored and wants to take a look. Any constructive criticism or suggestions are appreciated!
Thanks!
-Scott

If you are only using eval to access variable name object members, you can do it w/o eval:
eval('europe.names.'+this.getName());
simply becomes
europe.names[this.getName()];

Related

Unable to access GeoJSON properties with ":" using JS variable

I am currently following the tutorial on Mapbox to build a store locator with a map.
I'm having problems displaying data from my GeoJSON file that I got from Turbo Overpass. Example of the GeoJSON being used :
"properties": {
"#id": "node/5750820619",
"addr:city": "Caissargues",
"addr:housenumber": "180",
"addr:postcode": "30132",
"addr:street": "Avenue de la Vistrenque",
"description:covid19": "horaires légèrement réduits",
"name": "Bio Marché",
},
In my Javascript file I can access properties such as name using this code :
var details = listing.appendChild(document.createElement('div'));
details.innerHTML = prop.addr:city;
...but am unable to figure out how to access the addr:city or addr:postcode field as I either get an unexpected identified in vs code or it returns a undefined value in the website. I've tried using prop.addr:city;, prop.addr/':city'/; and other ways proposed on websites.
If someone could point me in the right direction or propose the solution I would be very grateful.
Thanks !
You should access these properties like this:
details.innerHTML = prop['addr:city'];
This works because in Javascript object properties can always be accessed using these brackets.
For example this:
const a = prop.a;
is equal to
const a = prop['a'];
However using the last syntax Javascript is a lot more relaxed. You could even do this:
const property = prop['some property with lots of spaces'];

Binding javascript (d3.js) to shiny

First, I am fairly unfamiliar with javascript and its library d3.js, but I am familiar with R. Creating dashboards using Shiny has been fun and easy (thanks to stackoverflow). Now I want to expand it by connect d3 elements to it.
I'm looking for information sources on how to actually bind javascript to Shiny (R dashboard) and explain what is actually going on.
Background:
I did the tutorial on js and jquery on w3schools and learned (a bit) about d3 using Scott Murray's book (Interactive Data visualization for the web). I hoped this would be enough to make me understand the examples and explanation concerning how to build custom input/output bindings on the Shiny website:
http://shiny.rstudio.com/articles/building-inputs.html
But unfortunately I don't and I can't seem to find any examples which are in minimal working code. Many examples on github are to complex for me to dissect, most probably because of my little experience with javascript. Here is an examples of custom input binding with javascript:
https://github.com/jcheng5/shiny-js-examples/tree/master/input
Here is an example of an input & output binding I try to unfold:
<script src="http://d3js.org/d3.v3.js"></script>
<script type="text/javascript">
(function(){
// Probably not idiomatic javascript.
this.countValue=0;
// BEGIN: FUNCTION
updateView = function(message) {
var svg = d3.select(".d3io").select("svg")
svg.append("text")
.transition()
.attr("x",message[0])
.attr("y",message[1])
.text(countValue)
.each("end",function(){
if(countValue<100) {
countValue+=1;
$(".d3io").trigger("change");
}
})
}
// END: FUNCTION
//BEGIN: OUTPUT BINDING
var d3OutputBinding = new Shiny.OutputBinding();
$.extend(d3OutputBinding, {
find: function(scope) {
return $(scope).find(".d3io");
},
renderError: function(el,error) {
console.log("Foe");
},
renderValue: function(el,data) {
updateView(data);
console.log("Friend");
}
});
Shiny.outputBindings.register(d3OutputBinding);
//END: OUTPUT BINDING
//BEGIN: INPUT BINDING
var d3InputBinding = new Shiny.InputBinding();
$.extend(d3InputBinding, {
find: function(scope) {
return $(scope).find(".d3io");
},
getValue: function(el) {
return countValue;
},
subscribe: function(el, callback) {
$(el).on("change.d3InputBinding", function(e) {
callback();
});
}
});
Shiny.inputBindings.register(d3InputBinding);
//END: OUTPUT BINDING
})()
</script>
Where "d3io" is a div element in the ui, updateView() is a function. Here is the ui:
#UI
library(shiny)
d3IO <- function(inputoutputID) {
div(id=inputoutputID,class=inputoutputID,tag("svg","")) #; eerst zat ; erbij, maar werkt blijkbaar ook zonder
}
# Define UI for shiny d3 chatter application
shinyUI(pageWithSidebar(
# Application title
headerPanel("D3 Javascript chatter",
"Demo of how to create D3 I/O and cumulative data transfer"),
sidebarPanel(
tags$p("This widget is a demonstration of how to wire shiny direct to javascript, without any input elements."),
tags$p("Each time a transition ends, the client asks the server for another packet of information, and adds it
to the existing set"),
tags$p("I can't claim this is likely to be idiomatic javascript, because I'm a novice, but it allows d3 apps
to do progressive rendering. In real use, a more complex request/response protocol will probably be
required. -AlexBBrown")
),
mainPanel(
includeHTML("d3widget.js"),
d3IO("d3io") #Creates div element that d3 selects
)
))
Here is the server file:
# SERVER
library(shiny)
# Define server logic required to respond to d3 requests
shinyServer(function(input, output) {
# Generate a plot of the requested variable against mpg and only
# include outliers if requested
output$d3io <- reactive(function() {
if (is.null(input$d3io)) {
0;
} else {
list(rnorm(1)*400+200,rnorm(1)*400+200);
}
})
})
Specific questions:
1) The server.r seems to get input called "d3io" (input$d3io) since this is not defined in ui.r, I reasoned it must come from the javascript file. Which element does it actually refer to?
2) I have trouble understanding the custom binding part:
var d3OutputBinding = new Shiny.OutputBinding();
$.extend(d3OutputBinding, {
find: function(scope) {
return $(scope).find(".d3io");
},
renderError: function(el,error) {
console.log("Foe");
},
renderValue: function(el,data) {
updateView(data);
console.log("Friend");
}
});
Shiny.outputBindings.register(d3OutputBinding);
My understanding is:
Create a new shiny outputbinding, first find the class .d3io (div element), if error then write to console "Foe" (is this special code?), if not error then renderValue using the function updateView using data (Where does it receive this value from?) and write to console "Friend". Finally register output.
Hope you guys can help! I'm creating a document with the steps on "The necessary steps to learn how to implement javascript into shiny when you don't know any javascript", I would love that!:)
Cheers,
Long
Hi Sweetbabyjesus (so fun to say). You had two questions:
1) The server.r seems to get input called "d3io" (input$d3io) since this is not defined in ui.r, I reasoned it must come from the javascript file. Which element does it actually refer to?
That phrase input$d3io has the following components:
input is a parameter passed into the function - it's a list that
stores the current values of all the widgets in the app.
$ is the member selector.
d3io refers to the content of the div element with that id
('d3IO("d3io")') in the mainPanel of the UI.
2) I have trouble understanding the custom binding part:
var d3OutputBinding = new Shiny.OutputBinding();
That's right, this creates an instance of Shiny.OutputBinding and assigns it to the variable d3OutputBinding.
$.extend(d3OutputBinding, {
find: function(scope) {
return $(scope).find(".d3io");
},
renderError: function(el,error) {
console.log("Foe");
},
renderValue: function(el,data) {
updateView(data);
console.log("Friend");
}
});
This code extends the behaviour of d3OutputBinding with three functions called find, renderError and renderValue. Those three functions are required for a Shiny.OutputBinding.
find is the key because it returns a list of elements that should be passed into the two render functions via their el parameter. Notice it's returning elements whose css class is "d3io" - that's the same div mentioned earlier.
Note that extend() is a function of jQuery javascript library, and the $ in this context is an alias for the jQuery object.
Shiny.outputBindings.register(d3OutputBinding);
Lets Shiny know that this newly configured object should be put to use now.
Cheers, Nick
I'm going to take a step back and assume you want the amazing results D3 is capable of, but aren't necessarily tied to D3. Essentially, I'll be answering this question:
What are the necessary steps to learn how to implement JavaScript into Shiny when you don't know any JavaScript?
While D3 is amazingly powerful, it's also notoriously difficult to master - even for many folks who are quite comfortable with JavaScript. While I love D3 and use it almost every day, I'd recommend against it in this case. Instead, there's library called Plotly, which uses D3 in the background, but is built specifically for the scientific community and data scientists, so it's very friendly to the R community.
They have a thorough tutorial for connecting to Shiny and even have a ggplot2 converter if you're already familiar with that syntax, as many in the R world are. Unless your needs are very unusual, Plotly will likely serve your needs just as well as writing directly in D3, with a much more friendly learning curve.
Are you familiar with the rCharts package? It can work pretty well with Shiny and most of the output options are based on D3 variants. Two examples.
Very busy with work, I haven't had the chance to post it. Note that this is a workaround using the customMessageHandler (and I'm not using custom input/output binding). Here goes:
Objective: Send data from data frame to create a D3JS tree using customMessageHandler.
Path: I've managed to send data in data.frame format to a d3js tree. After you click the actionbutton, it changes the data in the data frame to JSON format, then sends it to the js file which creates the tree. The data of the tree is hard-coded on "server.r".
Where's the code? On my github!
https://github.com/SweetBabyJesus/shiny-d3js-simple-binding
Original: I created a tree algorithm based on CHAID to create insights from large datasets. People can upload their csv to the dashboard which then spits out the d3js tree:) The code is somewhat lengthy, therefore I cut it down for you and created a minimal code example.
Hope you like it.
Cheers,
Long

Three.js object definition not defining

I am working my way through a set of tutorials and have come across an error I cant figure out...
I know it is probably me being blind due to my lazy upbringing with IDE's that have red underlines for syntax errors but I kinda need this done soon!
The error is
TypeError: Tetris.boundingBoxConfig is undefined
file:///C:/Users/Timmy/Documents/Emergent%20Tech/Tetris/js/tetris.js
Line 127
here i define Tetris.boundingBoxConfig
var boundingBoxConfig = {
width: 360,
height: 360,
depth: 1200,
splitX: 6,
splitY: 6,
splitZ: 20
};
Tetris.boundingBoxConfig = boundingBoxConfig;
and here is line 127
mesh.position.x = (x - Tetris.boundingBoxConfig.splitX/2)*Tetris.blockSize + Tetris.blockSize/2;
if you need more of the code let me know and I will edit. Any Help would be much appreciated! please only constructive criticism
EDIT Definition of Tetris Object
var Tetris = {};
There was a second suggested way of doing it like this:
window.Tetris = window.Tetris || {};
but i don't really get how that way works
EDIT 2
Not sure if this helps the clarify the issue
var boundingBox = new THREE.Mesh(
new THREE.CubeGeometry(
boundingBoxConfig.width, boundingBoxConfig.height, boundingBoxConfig.depth,
boundingBoxConfig.splitX, boundingBoxConfig.splitY, boundingBoxConfig.splitZ),
new THREE.MeshBasicMaterial( { color: 0xffaa00, wireframe: true } )
);
Tetris.scene.add(boundingBox);
Inside your Tetris function, try changing
Tetris.boundingBoxConfig = boundingBoxConfig;
To
this.boundingBoxConfig = { //your definition };
so that your property becomes public.
Turns out this was due to me using local files in Firefox. I knew that Chrome had security issues when loading the files from a local disk so went with Firefox believing there was not a similar problem due to other projects working. I was wrong.
Lesson: Never use local files always use a server.
Could you please include the code where you have defined the "Tetris" object? (ie var Tetris blah blah blah)

jQuery equivalent of YUI Lang.sub function

I am learning to work with Parse API and found this tutorial which uses handlebars.js for templating. Since I am comfortable with jQuery and have no experience in YUI so I am trying to simply re-write the above tutorial in jQuery.
But I am stuck in the following part:
var content = Y.Lang.sub(Y.one('#todo-items-template').getHTML(), {
content: item.get('content'),
id: item.id,
isComplete: item.get('isComplete')
});
where, content, id and isComplete are fields in Parse web store.
Now, my question is: what is the jQuery equivalent of above Lang.sub YUI function.
Here's the very basics, adapt or extend to your needs:
$.sub = function(str, obj) {
return str.replace(/\{([^}]+)\}/g, function(_, m) {
return obj[m];
});
};
console.log($.sub('Hello {foo}', { foo: 'World' }));
//=> Hello World
I would suggest pulling in the Handlebars JS repository; It's a very popular solution and is very powerful. Since it's popular, it's probably a good idea to learn how to use it since you'll likely be running into again in the future.
Other templating frameworks are listed here: http://microjs.com/#templating

Adding IDs to Fabric.js elements

I am creating a 'map like' application in Canvas using the Fabric.js library. I essentially put an image down and then lay circles on top of the image with the correct coordinates. The dot locations and names come through as JSON data received from an ajax call. I need a way to refer to any individual circle so that I can fetch more information regarding that circle (each circle has a popup with more detailed information).
I can't find anywhere in the documentation that explains how to do this, I have tried adding an ID to an object like this:
tokens.add(new fabric.Circle({ radius: 25, fill: '#f55', top: 100, left: 70, id: "foo" }));
with no luck retrieving it. Any help would be greatly appreciated. Also this is my first interaction with this community, so I apologize if my question isn't detailed enough, or if there is some other problem with it.
You can add an attribute with fabric.object.prototype.
So, add :
fabric.Object.prototype.uid = ... ;
You can Extend fabric.Object class and add your own Property But easiest way would be using fabric.util.Object.extend().
You can add as many as custom property you want and access it using object.toObject().id.
extend(object, prop) {
object.toObject = ((toObject) => {
return function () {
return fabric.util.object.extend(toObject.call(this), prop);
};
})(object.toObject);
}
and then
this.extend(line , {
customType:'ArrowLine',
id:this.randomId(),
CommentId:activeObject.toObject().id, extensionId
});

Categories

Resources