I am working in Google Maps and have three+ tile overlays to create. An example:
Tile Overlay
var parkingOptions = { //Parking Overlay
getTileUrl: function(coord, zoom) {
return "/maps/tiles/tilesparking/" + zoom + "_" + coord.x + "_" + coord.y + ".png";
},
tileSize: new google.maps.Size(256, 256)
};
var parkingMapType = new google.maps.ImageMapType(parkingOptions);
However, to avoid 404 errors by missing tiles outside of my mapping range, my code is a little more complex; thus, I intended to make a loop where a specific keyword assigned to each overlay (here given as "parking") would be inserted into the above code. Thus:
For Loop
var tileNames = ["base", "parking", "access"];
for (var i = 0; i < tileNames.length; i++) {
//insert Tile Overlay code here
};
However, I have one particular issue: I can not find a way to take the string from the tileNames array and use them in initializing the two variables in the Tile Overlay code. Ideally, I would like to achieve something like this:
Attempt 1
var tileNames[1] + "Options" = { //ideally: var parkingOptions = {
//insert remaining code
};
However this doesn't work, nor did I really expect it to. Neither would trying to create those full strings and trying to insert it into the initialization:
Attempt 2
var newOptions = tileNames[1] + "Options";
var newOptions = {
//insert remaining code
};
Thus, is there a way to do place a string into initializing variables?
Note: I have included my own alternative solution to the problem as an answer. It should work, but it destroys the names of the variables and replaces it with a nondescript array variable. I preferably would like to retain a descriptive variable name as they are used often in adding and hiding the overlays in the resulting code.
Solution For this question anyways...
var tileNames = ["beloit", "parking", "access"];
var mapType = {};
for (var i = 0; i < tileNames.length; i++) {
var tileOptions = {
getTileUrl: function(coord, zoom) {
return "/maps/tiles/tiles" + tileNames[i] + "/" + zoom + "_" + coord.x + "_" + coord.y + ".png";
},
tileSize: new google.maps.Size(256, 256)
};
mapType[tileNames[i]] = new google.maps.ImageMapType(tileOptions);
};
The other part of the puzzle, the "tileNames[i]" in the getTileUrl function is undefined because the function wants it when it is executed rather than placing the name string into the function; however, that is a new question to be found here: Javascript: Making a variable static when defining a function in a loop?
you can't do this:
var tileNames[1] + "Options" = {
//insert remaining code
};
but you can do this:
window['a'] = 'b';
alert(a); // shows 'b'
or if you are in a function
this['a'] = 'b';
EDIT:
var obj = {};
obj.a = 'a';
// obj.a == obj['a']
alert(obj['a']) // alerts a
Now, I don't know if the above is indeed possible (and I would somewhat prefer it did as I'll explain below), but in drafting this question, the various other Q/A I read beforehand began to make more sense. Their general message: "use arrays":
Possible Solution
(Edited; should be usable now.)
var tileNames = ["beloit", "parking", "access"]; //Would be used more than once.
var tileMapType = [];
for (var i = 0; i < tileNames.length; i++) {
var tileOptions = {
getTileUrl: function(coord, zoom) {
return "/maps/tiles/tiles"+tileNames[i]+"/" + zoom + "_" + coord.x + "_" + coord.y + ".png";
},
tileSize: new google.maps.Size(256, 256)
};
tileMapType[i] = new google.maps.ImageMapType(tileOptions);
};
The only issue with this approach is that, in the code which follows, I have to place these MapTypes into the map as well as toggle them as visible and invisible, and using the nondescript tileMapType[x] to do so hinders overall readability. (Perhaps this doesn't matter as much as I think it does, but still. >.>)
Assuming I've understood your question, you can encapsulate your tiles in an object and refer to the layer by the name you provide:
var tileNames = ["base", "parking", "access"];
var Tiles = {};
for (var i = 0; i < tileNames.length; i++) {
Tiles[tileNames[i]] = makeOverlay(tileNames[i], coords, zoom);
};
//iterate the maps
for (var tile in Tiles)
alert(Tiles[tile].someGoogleMapPropery)
//individually
alert (Tiles.base.someGoogleMapPropery)
alert (Tiles.parking.someGoogleMapPropery)
//or
alert (Tiles["access"].someGoogleMapPropery)
function makeOverlay(name, coords, zoom) {
return new google.maps.ImageMapType({
getTileUrl: function(coord, zoom) {
return "/maps/tiles/tiles" + name + "/" + zoom + "_" + coord.x + "_" + coord.y + ".png";
},
tileSize: new google.maps.Size(256, 256)
});
}
It's possible to read variables like that using eval(), but not set them.
Example:
var a = "b";
var b = "c";
eval (a); // returns "c"
Related
I have a XML file
<lle:Event>
<lle:eventid>ID01</lle:eventid>
<lle:collab>
<lle:name>Jane Doe</lle:name>
<lle:support>Carer</lle:support>
<lle:supportTime>8am - 8pm</lle:supportTime>
<lle:location>
<lle:lat>1.3117216424564617</lle:lat>
<lle:lng>103.8149642944336</lle:lng>
</lle:location>
</lle:collab>
</lle:Event>
<lle:Event>
<lle:eventid>ID02</lle:eventid>
<lle:collab>
<lle:name>Peter Smith</lle:name>
<lle:support>Carer</lle:support>
<lle:supportTime>8am - 8pm</lle:supportTime>
<lle:location>
<lle:lat>1.3772782313341114</lle:lat>
<lle:lng>103.89873504638672</lle:lng>
</lle:location>
</lle:collab>
</lle:Event>
<lle:Event>
...
</lle:Event>
<lle:Event>
<lle:eventid>ID08</lle:eventid>
<lle:collab>
<lle:name>Ang</lle:name>
<lle:support>Doctor</lle:support>
<lle:supportTime>8am - 8pm</lle:supportTime>
<lle:img>avatar.png</lle:img>
<lle:location>
<lle:lat>1.3577459437939223</lle:lat>
<lle:lng>103.84522878271483</lle:lng>
</lle:location>
</lle:collab>
</lle:Event>
Some lle:Event has lle:img element while some don't.
I am trying to differentiate them using javascript in order to use a custom image marker on Google map for those with lle:img element.
var events = xml.documentElement.getElementsByTagName("lle:Event");
console.log(events);
for (var i = 0; i < events.length; i++) {
var name = xml.documentElement.getElementsByTagName("lle:name")[i].textContent;
var support = xml.documentElement.getElementsByTagName("lle:support")[i].textContent;
var supportTime = xml.documentElement.getElementsByTagName("lle:supportTime")[i].textContent;
var lat = xml.documentElement.getElementsByTagName("lle:lat")[i].textContent;
var lng = xml.documentElement.getElementsByTagName("lle:lng")[i].textContent;
latLng = new google.maps.LatLng(lat, lng);
if (events[i].getElementsByTagName("lle:img").length > 0 ) {
var imgPath = xml.documentElement.getElementsByTagName("lle:img")[i].textContent;
console.log(imgPath);
var marker = new google.maps.Marker({
position: latLng,
map,
title: "Name: " + name + "\nSupport: " + support + "\nSupport Time: " + supportTime,
});
} else if (!events[i].getElementsByTagName("lle:img").length > 0) {
var marker = new google.maps.Marker({
position: latLng,
map,
title: "Name: " + name + "\nSupport: " + support + "\nSupport Time: " + supportTime,
});
}
In the above, I check for whether lle:img exist using
events[i].getElementsByTagName("lle:img").length > 0
However when i try to get imgPath with getElementsByTagName("lle:img")[i].textContent with
var imgPath = xml.documentElement.getElementsByTagName("lle:img")[i].textContent;
the console.log results always shows undefined.
Im very sure the the lle:img element is there but somehow it is still returning undefined. Is there anything wrong that im doing here?
This is what you check in your if
events[i].getElementsByTagName("lle:img").length
and this is what you use to set imgPath
xml.documentElement.getElementsByTagName("lle:img")[i].textContent
see the difference?
In particular, i is the index to the event. It makes no sense to use it to index the "lle:img" elements. You presumably have fewer "lle:img" elements than events, so your index is out of range.
Your else condition is also buggy. It probably works because !...length will be true when length is 0, and true > 0, but that's just lucky. You don't need it at all.
try this
if (events[i].getElementsByTagName("lle:img").length > 0 ) {
var imgPath = events[i].getElementsByTagName("lle:img")[0].textContent;
console.log(imgPath);
...
} else {
...
}
better yet, reduce the number of times you parse it and save your found elements:
var images = events[i].getElementsByTagName("lle:img")
if (images.length > 0) {
var imgPath = images[0].textContent
...
}
else {
...
}
This code is for internal, offline, single user use, IE only. The code looks at a folder, and lists all files including those in subfolders. It sorts through the data based on some date fields and datelastmodified. It also uses and if to throw out thumbs.db entries. All of the data is put into a table.
My issue is that this script can take a long time to get the data. I would like to add a progress bar but the progress bar cant update while the script is running. After some research it looks like SetTimeOut can allow the page elements to be updated as the script runs, therefore allowing the progress bar to work and looking overall cleaner. However I can not figure out of to implement SetTimeOut into my existing code.
<script type="text/javascript">
var fso = new ActiveXObject("Scripting.FileSystemObject");
function ShowFolderFileList(folderspec) {
var beginningdate = new Date(startdate.value);
var finishdate = new Date(enddate.value);
var s = "";
var f = fso.GetFolder(folderspec);
var subfolders = new Enumerator(f.SubFolders);
for (subfolders.moveFirst(); !subfolders.atEnd(); subfolders.moveNext()) {
s += ShowFolderFileList(subfolders.item().path);
}
// display all file path names.
var fc = new Enumerator(f.files);
for (i = 0; !fc.atEnd(); fc.moveNext()) {
if (fc.item().name != "Thumbs.db") {
var dateModified = fc.item().DatelastModified;
if (dateModified >= beginningdate && dateModified <= finishdate) {
Date.prototype.toDateString = function () {
return [this.getMonth() + 1, '/', this.getDate(), '/', this.getFullYear()].join('');
}
var dateModifiedClean = (new Date(fc.item().DatelastModified).toDateString());
s += "<table border=0 width=100% cellspacing=0><tr " + ((i % 2) ? "" : "bgcolor=#EBF1DE") + "><td width=75%><font class=find><b>" + fc.item().ParentFolder.name + "</b>" + " - " + fc.item().name + "</font></td><td width=25% align=right><font class=find>" + dateModifiedClean + "</font></td></tr>";
i++;
}
}
}
var results = s + "</table>";
return results;
}
function listFiles() {
outPut.innerHTML = ShowFolderFileList('*Path to scan*');
}
</script>
outPut is the ID of a div tag where the results table is displayed. A button calls the listfiles function.
Unfortunately I can't easily paste the whole script that generates the variable, but I don't see it would be relevant either. Please instruct for more details, if necessary.
Javascript shows this:
console.log(gl.boxes);
shows:
[{element:{0:{jQuery19104057279333682955:9}, context:{jQuery19104057279333682955:9}, length:1}, boxName:"testi1", boxX:1, boxY:"180"}]
so gl.boxes[0] should exist, right? Still...
console.log(gl.boxes[0])
shows: undefined.
So what can I be missing here?
EDIT:
I will paste some more code about the generation of gl.boxes. Should be mostly about creating the variable as array first:
gl.boxes = [];
Then there is a function that handles creating and pushing new objects:
this.addBox = function (box) {
var retBox = {};
retBox.element = $(document.createElement('div'));
retBox.boxName = box.boxName;
retBox.boxX = box.boxX ? box.boxX : rootParent.defaultX;
retBox.boxY = box.boxY ? box.boxY : rootParent.defaultY;
retBox.element
.html(retBox.boxName)
.addClass(rootParent.boxClass)
.offset({ left: retBox.boxX, top: retBox.boxY })
.draggable({
stop: gl.URLs.dragStopDiv(retBox)
});
retBox.element.appendTo(rootParent.containerDiv);
gl.boxes.push(retBox);
return retBox;
};
The objects are created based on URL. Ie. in this test I have inline JS:
gl.objects.addBox({"boxName":"testi1","boxX":"50","boxY":"180"});
Only other place where the gl.boxes is being used is generating a URL based on the objects:
for(key in gl.boxes) {
var position = gl.boxes[key].element.position();
uri +=
"boxName["+key+"]="+gl.boxes[key].boxName+"&"+
"boxX["+key+"]="+position.left+"&"+
"boxY["+key+"]="+position.top+"&";
}
Maybe you need to change your loop to use indexes:
var i = 0,
position = {};
for (i = 0; i < gl.box.length; i += 1) {
position = gl.boxes[i].element.position();
uri += "boxName[" + i + "]=" + gl.boxes[i].boxName + "&" + "boxX[" + i + "]=" + position.left + "&" + "boxY[" + i + "]=" + position.top + "&";
}
Let's say I have:
var directions = [ "name", "start_address", "end_address", "order_date" ];
I'm trying to find a slick, fast way to turn that array into this:
data: {
"directions[name]" : directions_name.val(),
"directions[start_address]" : directions_start_address.val(),
"directions[end_address]" : directions_end_address.val(),
"directions[order_date]" : directions_order_date.val()
}
Notice the pattern. The name of the array "directions" is the prefix to the values.
I'm interested how people can either do this or at least suggest a way for me to try.
Any tips would be appreciated.
Thanks!
EDIT **
Thanks for the suggestions so far. However, I forgot to mention that the array "directions" needs to be dynamic.
For example, I could use:
places = ["name", "location"]
should return
data: {
"places[name]" : places_name.val(),
"places[location]" : places_location.val()
}
alpha = ["blue", "orange"]
should return
data: {
"alpha[blue]" : alpha_blue.val(),
"alpha[orange]" : alpha_orange.val()
}
So basically I could just pass an array into a function and it return that data object.
var directions = ["name", "start_address", .... ];
var data = someCoolFunction( directions );
Hope that makes sense.
** EDIT **************
I want to thank everyone for their help. I ended up going a different route. After thinking about it, I decided to put some meta information in the HTML form itself. And, I stick to a naming convention. So that an HTML form has the information it needs (WITHOUT being bloated) to tell jQuery where to POST the information. This is what I ended up doing (for those interested):
// addBox
// generic new object box.
function addBox(name, options) {
var self = this;
var title = "Add " + name.charAt(0).toUpperCase() + name.slice(1);
var url = name.match(/s$/) ? name.toLowerCase() : name.toLowerCase() + "s";
allFields.val(""); tips.text("");
$("#dialog-form-" + name).dialog( $.extend(default_dialog_options, {
title: title,
buttons: [
{ // Add Button
text: title,
click: function(){
var bValid = true;
allFields.removeClass( "ui-state-error" );
var data = {};
$("#dialog-form-" + name + " input[type=text]").each(function() { // each is fine for small loops :-)
var stripped_name = this["name"].replace(name + "_", "");
data[name + "[" + stripped_name + "]"] = $("#dialog-form-" + name + " #" + name + "_" + stripped_name).val();
});
// verify required fields are set
$("#dialog-form-" + name + " input[type=text].required").each(function() {
bValid = bValid && checkLength( $(this), $(this).attr("name").replace("_", " "), 3, 64 );
});
// find sliders
$("#dialog-form-" + name + " .slider").each( function() {
data[name + "[" + $(this).attr("data-name") + "]"] = $(this).slider( "option", "value" );
});
data["trip_id"] = trip_id;
if(options != null) { $.extend(data, options); } // add optional key/values
if(bValid) {
$(".ui-button").attr("disabled", true);
$.ajax( { url : "/" + url, type : "POST", data : data } );
}
}
},
{ text: "Cancel", click: function(){$( this ).dialog( "close" );} }
]
}));
}
It's really unclear what you want here. Perhaps you should give the interface to the function you want, and an example of some code which sets up some sample variables and calls the function.
What you seem to be asking for is to dynamically find variables which you have already declared in the environment, such as directions_name and directions_start_address, and call the val() method on each of them, then construct a dictionary mapping strings to those results. But the keys of the dictionary contain JavaScript syntax. Are you sure that's what you want?
function transform(name)
{
var data = {};
var names = window[name];
for (var i=0; i<names.length; i++)
{
data[name + "[" + names[i] + "]"] = window[name + "_" + names[i]].val();
}
return data;
}
Edit: To use JQuery to look up objects by ID instead of the above approach (which looks up global variables by name):
function transform(name)
{
var data = {};
var names = $("#" + name);
for (var i=0; i<names.length; i++)
{
data[name + "[" + names[i] + "]"] = $("#" + name + "_" + names[i]).val();
}
return data;
}
This will look up the name in the global space of the window (which will work in a browser anyway). You call that function with "directions" as the argument. For example:
var directions = [ "name", "start_address", "end_address", "order_date" ];
var directions_name = {"val": function() {return "Joe";}};
var directions_start_address = {"val": function() {return "14 X Street";}};
var directions_end_address = {"val": function() {return "12 Y Street";}};
var directions_order_date = {"val": function() {return "1/2/3";}};
data = transform("directions");
Is that what you want?
(Note: I see someone else posted a solution using $ and "#" ... I think that's JQuery syntax, right? This works without JQuery.)
Edit: Note that this lets you use a dynamic value for "directions". But I'm still not sure why you want those keys to be "directions[name]", "directions[start_address]", instead of "name", "start_address", etc. Much easier to look up.
Edit: I fixed my sample code to use functions in the values. Is this really what you want? It would be easier if you weren't calling val() with parens.
Like this:
var data = { };
for(var i = 0; i < directions.length; i++) {
var name = directions[i];
data["directions[" + name + "]"] = $('#directions_' + name).val();
}
Or,
data["directions[" + name + "]"] = directionsElements[name].val();
EDIT: You can pass an array and a prefix.
Given:
// Positions the bars relative to scale
this.gDrawBars = function() {
// Go through all the bars
for (i = 0; i < this.gData.length; i++) {
// Check part of it is within range
if (this.gDisplayFrom < this.gData[i][2] || this.gDisplayTo > this.gData[i][1]) {
// Is the entire bar showing
var isEntireBarInRange = (this.gDisplayFrom < this.gData[i][2] && this.gDisplayTo > this.gData[i][1]);
var div = document.createElement('div');
div.id = "gBar" + i;
div.className = 'gBar';
div.innerHTML = this.gData[i][0];
var self = this;
div.onmouseover = function() {
gBarHighlight(this, this.gData[i][1], this.gData[i][2]);
};
div.onmouseout = function() {
gBarUnHighlight(this, this.gData[i][1], this.gData[i][2]);
};
this.gContainer.appendChild(div);
//this.gContainer.innerHTML += "<div id=\"gBar" + i + "\" class=\"gBar\" onmouseover=\"gBarHighlight(this, '" + this.gData[i][1] + "', '" + this.gData[i][2] + "')\" onmouseout=\"gBarUnHighlight(this, '" + this.gData[i][1] + "', '" + this.gData[i][2] + "')\">" + this.gData[i][0] + "</div>";
The commented line at the bottom works fine, but I'm trying to change it to add these functions dynamically. It needs to pass this.gData[i][1] into the functions but it can't, because the i value has no meaning outside the loop.
How can I get around this? IE, make the function recognise it's being passed a value to use and not a reference.
You need to retain the value of i in a new execution context.
Place the code that assigns the handlers into a named function, and call that in the loop, passing i as an argument.
Place this function before the for loop:
function setupMouseOverOut( el, i ){
el.onmouseover = function() {
gBarHighlight(this, this.gData[i][1], this.gData[i][2]);
};
el.onmouseout = function() {
gBarUnHighlight(this, this.gData[i][1], this.gData[i][2]);
};
}
...then call it in the for loop:
setupMouseOverOut( div, i );
This way the value of i that you passed out of the for loop is retained in the new execution context of the setupMouseOverOut() function call, and the new functions you set as handlers will refer to that local variable.
It's not a function, it's an event. You need to add it as an event to the element:
div.addEventListener('mouseover', function() {
// ...
});
Note that when you do it this way you don't have that 'on' word there.