JavaScript Async Loop does not return result - javascript

I was having some logic problem when trying to do a function in JavaScript. Basically I have a pointArr to store the coordinates along the route. Then I got a moveNext() which takes in each coordinates along the route and plot onto the map. Then inside the moveNext(), I got another array which is busList. If the coordinates along the route match the coordinates of busList, then I minus the totalBusStopLeft by one. Here is the code where I call the moveNext():
getAllBusLoc(function(busList) {
//At first I set the totalBusLeft by the length of busList which is 13 in this case
var totalBusLoc = busList.length;
document.getElementById("busStopLeft").innerHTML = totalBusLoc;
//Then I set the value to busList for the minus calculation
busLeft = totalBusLoc;
timeout = 1500;
pointArr.forEach(function(coord,index){
setTimeout(function(){
moveNext(coord.x, coord.y, index, busList, busLeft);
}, timeout * index);
});
});
function moveNext(coordx, coordy, k, busList, busLeft){
document.getElementById("busStopLeft").innerHTML = busLeft;
//pointToCompare is the coordinates in the route but not the coordinates of busList
var pointToCompare = coordx + "," + coordy;
//If the coordinates in route matches the coordinate in busList, I minus busLeft by one
if(busList.indexOf(pointToCompare) > -1){
parseFloat(busLeft--);
}
//Code to Add marker
}
However, with this code, my Html component busStopLeft keep showing 13 which is the totalBusLoc but not the busLeft. Any ideas?
Thanks in advance.
EDIT
totalBusLoc = busList.length;
document.getElementById("busStopLeft").innerHTML = totalBusLoc;
timeout = 1500;
pointArr.forEach(function(coord,index){
setTimeout(function(busLeft){
moveNext(coord.x, coord.y, index, busList, totalBusLoc);
}, timeout * index);
});
});
function moveNext(coordx, coordy, k, busList, totalBusLoc, callback){
var pointToCompare = coordx + "," + coordy;
if(busList.indexOf(pointToCompare) > -1){
parseFloat(totalBusLoc--);
document.getElementById("busStopLeft").innerHTML = totalBusLoc;
callback(totalBusLoc);
}
}

Related

How to retrieve values of translate3d?

This question has already been asked before on StackOverflow, what is different with mine is that I'd like to be able to retrieve negative values and also be able to retrieve it with another parameter in transform which would be rotateZ(...deg) in my case.
I've followed another post on how I can get values of translate3d, the suggested code works and it gets the positiv values but not negative ones (the minus sign is missing).
Another problem is that when I add a new paramater nothing works anymore, I'd like to be able to add whatever paramaters and still make it work.
I think this comes from an issue with the Regex, see http://jsfiddle.net/Lindow/3gBYB/81/
Regular expression :
transform.match(/matrix(?:(3d)\(-{0,1}\d+(?:, -{0,1}\d+)*(?:, (-{0,1}\d+))(?:, (-{0,1}\d+))(?:, (-{0,1}\d+)), -{0,1}\d+\)|\(-{0,1}\d+(?:, -{0,1}\d+)*(?:, (-{0,1}\d+))(?:, (-{0,1}\d+))\))/)
Javascript :
function getTransform(el) {
var transform = window.getComputedStyle(el, null).getPropertyValue('-webkit-transform');
var results = transform.match(/matrix(?:(3d)\(-{0,1}\d+(?:, -{0,1}\d+)*(?:, (-{0,1}\d+))(?:, (-{0,1}\d+))(?:, (-{0,1}\d+)), -{0,1}\d+\)|\(-{0,1}\d+(?:, -{0,1}\d+)*(?:, (-{0,1}\d+))(?:, (-{0,1}\d+))\))/);
if(!results) return [0, 0, 0];
if(results[1] == '3d') return results.slice(2,5);
results.push(0);
return results.slice(5, 8); // returns the [X,Y,Z,1] values
}
var slider = document.getElementById('slider');
var translation = getTransform(slider);
var translationX = translation[0];
var translationY = translation[1];
var absX = Math.abs(translationX);
var absY = Math.abs(translationY);
alert(absX + ' : ' + absY);
HTML/CSS :
<div id="slider"></div>
----
#slider {
-webkit-transform:translate3d(-393px, -504px, 0) rotateZ(30deg);
// remove rotateZ to make it work with positiv values
// number in translate3d may vary from -9999 to 9999
}
Any suggestions ?
I've adapted your JSFiddle answer to provide a solution - updated your regex to allow for negative numbers, and added a method, "rotate_degree", that will calculate the rotation angle from the computedStyle matrix. This value is placed as an integer at the end of the results array.
Solution is on JSFiddle here: http://jsfiddle.net/Lymelight/3gBYB/89/
function getTransform(el) {
var transform = window.getComputedStyle(el, null).getPropertyValue('-webkit-transform');
function rotate_degree(matrix) {
if(matrix !== 'none') {
var values = matrix.split('(')[1].split(')')[0].split(',');
var a = values[0];
var b = values[1];
var angle = Math.round(Math.atan2(b, a) * (180/Math.PI));
} else {
var angle = 0;
}
return (angle < 0) ? angle +=360 : angle;
}
var results = transform.match(/matrix(?:(3d)\(-{0,1}\d+\.?\d*(?:, -{0,1}\d+\.?\d*)*(?:, (-{0,1}\d+\.?\d*))(?:, (-{0,1}\d+\.?\d*))(?:, (-{0,1}\d+\.?\d*)), -{0,1}\d+\.?\d*\)|\(-{0,1}\d+\.?\d*(?:, -{0,1}\d+\.?\d*)*(?:, (-{0,1}\d+\.?\d*))(?:, (-{0,1}\d+\.?\d*))\))/);
var result = [0,0,0];
if(results){
if(results[1] == '3d'){
result = results.slice(2,5);
} else {
results.push(0);
result = results.slice(5, 9); // returns the [X,Y,Z,1] value;
}
result.push(rotate_degree(transform));
};
return result;
}
var slider = document.getElementById('slider');
var translation = getTransform(slider);
console.log(translation);
var translationX = translation[0];
var translationY = translation[1];
var absX = Math.abs(translationX);
var absY = Math.abs(translationY);
console.log('TrX: ' + translationX + ', TrY: ' + translationY + ' , Rotation Angle: ' + translation[3]);
alert('TrX: ' + translationX + ', TrY: ' + translationY + ' , Rotation Angle: ' + translation[3])
I've also found another solution,
for (HTML) :
<div style="translate3d(-393px, -504px, 0) rotateZ(30deg);">...</div>
do (JavaScript) :
// get translate3d(..px, ..px, 0px) rotateZ(30deg)
function matrixToArray(matrix) {
return matrix.substr(7, matrix.length - 8).split(', ');
}
function matrix_translate3d(pos) {
var matrix_list = [];
matrix = matrixToArray($(pos).css("-webkit-transform"));
x = matrix[4].replace(/px/gi, '');
y = matrix[5].replace(/px/gi, '');
matrix_list.push(parseInt(x));
matrix_list.push(parseInt(y));
return matrix_list;
}
var matrix_position = matrix_translate3d(...);
// matrix_position[0], matrix_position[1]
Short solution.

How to get the right index for a delayed trigger?

Following problem: I have an angular module with $http.get to get some google coordinates. This function triggers another function. This function again triggers another function. It will all make sense in a moment.
Angular Module:
var myApp = angular.module('wmw', []);
myApp.controller('MainCtrl', function ($scope, $http) {
//Angular Method
$scope.getTargetCords = function (data) {
$http.get(data).success(function (response) {
$(document).triggerHandler('my_func:data-received', [response]);
});
};
});
onSucess:
var onSuccess = function(position) {
currentLat = position.coords.latitude ;
currentLng = position.coords.longitude;
for(i = 0; i<locations.length;i++){
var destUrl = 'http://maps.googleapis.com/maps/api/geocode/xml?address=' + locations[i][ 'street' ] + ',' + locations[i][ 'city' ] + ',Deutschland' + '&sensor=true';
var MyAngularScope = angular.element($("#MainCtrlId")).scope();
MyAngularScope.getTargetCords('http://maps.googleapis.com/maps/api/geocode/xml?address=' + locations[i][ 'street' ] + ',' + locations[i][ 'city' ] + ',Deutschland' + '&sensor=true');
}
};
navigator.geolocation.getCurrentPosition(onSuccess, onError);
The two triggers:
$(document).on('my_func:data-received', function(event, response) {
map[s] = response;
s++;
if(s === locations.length){
$(document).triggerHandler('allData');
}
});
$(document).on('allData', function(){
var thecoords = [];
var distance = [];
$('#filter-list').empty();
for(var i = 0; i < locations.length; i++){
thecoords[0] = $(map[i]).find('lat').first().text();
thecoords[1] = $(map[i]).find('lng').first().text();
distance[i] = calculateDistance(currentLat, currentLng, thecoords[0], thecoords[1]);
locations[i]['distance'] = distance[i];
}
locations.sort(function(a,b)
{ return a.distance - b.distance;}
);
for(var i = 0;i < locations.length; i++){
distance[i] = locations[i]['distance'].toFixed(2);
distance[i] += ' KM';
locations[i]['distance'] = distance[i];
}
$('.loading').hide();
for(var i = 0; i<=5; i++){
addItemToList(locations[i]);
}
});
What's happening? With those functions I retrieve the current location, the dest location and calculate the difference in KM via a lat./long. calc function which I found on the web. .loading is just a div with "Calculating route..." and a transparent grey background. So once everything is finished, The "Distance" of every route will change to the calculated distance.
The problem with this: in my ".on('my_func:data-received')" I am using the variable "s" which is 0 at the start. In my logic I thought, that this would then put the responses one after another in my "map". But now I realised, that the "data-received" are not called one after another, but each time when data is retrieved. So when locations[0] is calling the $http.get and then after this locations[1] is calling the $http.get, it could happen, that locations[1] retrieves the data earlier. How could I have my "s" always be the right number? So that when I have locations[1] calling $http.get map[1] will be locations[1] response?
My head is exploding, as I cant find a solution to this problem, although it seems to be so basic.
Thank you in advance!
Since restructuring your application is not an option, another reasonably quick way of getting the right order is mapping the response data to the original array. The response contains the url which is built using data from the array which might give you what you need.

Cubism.js getting correct metrics but not drawing horizon chart

I'm plotting about 15 named metrics on cubism.js. I'm seeing the stat values being return as metrics. Also, if I scroll over the visualization, the ruler is showing me the proper values. However, its just not drawing the graph. When I inspect the horizon div, this is what I see:
<span class="value" style=""></span>
As you can see, the span is empty. It should look something like this:
<span class="value" style="">"10"</span>
Here's the code I'm using to create the metrics
var context = cubism.context()
.serverDelay(0)
.clientDelay(0)
.step(1*5*60)
.size(1440);
//aggregate 'metrics'. Each component has a 'metric' which contains its stats over time
for(model in models){
var attributes = models[model].attributes;
var name = attributes['name'];
var curContext = getMetric(name, attributes);
statsList.push(curContext);
}
function getMetric(name, attributes){
return context.metric(function(start, stop, step, callback){
var statValues = [];
//convert start and stop to milliseconds
start = +start;
stop = +stop;
//depending on component type, push the correct attribute
var type = attributes['type'];
var stat = attributes['stat'];
//cubism expects metrics over time stored in 'slots'. these slots are indexed
//using 'start' and 'stop'
//if the metric already exists, we store the stats in each of the slots
if(initializedMetrics[name]){
while(start < stop){
start+= step;
statValues.push(stat);
}
} else{
//if the metric is not initialized, we fill the preceeding slots with NaN
initializedMetrics[name] = true;
while (start < (stop - step)) {
start += step;
statValues.push(NaN);
}
statValues.push(stat);
}
if(stat < min){
min = stat;
}
if(stat > max){
max = stat;
}
//console.log("pushing stat", name, stat)
callback(null, statValues);
}, name);
}
d3.select(this.loc).selectAll(".axis")
.data(["top", "bottom"])
.enter().append("div")
.attr("class", function(d) { return d + " axis"; })
.each(function(d) { d3.select(this).call(context.axis().ticks(12).orient(d)); });
d3.select(this.loc).append("div")
.attr("class", "rule")
.call(context.rule());
d3.select(this.loc).selectAll(".horizon")
.data(statsList)
.enter().insert("div", ".bottom")
.attr("class", "horizon")
.call(context.horizon().height(20));
var size = this.getSize();
context.on("focus", function(i) {
d3.selectAll(".value").style("right", i == null ? null : context.size() - i + "px");
});
},
getSize: function () {
return $(this.loc).width();
},
To phrase this as a single question, at what step is the 'value' in the horizon div set?
Just to clarify, I am seeing the named horizon divs created. Just no timeseries path being drawn.

Receiving WatchPosition only once

I use this code to get the first position ,and i want to keep getting it.
var init = function()
{
navigator.geolocation.getCurrentPosition(function(position)
{
new_position = position;
}, onError, {});
watchID = navigator.geolocation.watchPosition(UserLocationCalculation, onError, {maximumAge: 1000, timeout: 5000, enableHighAccuracy: true});
}
var UserLocationCalculation = function(position)
{
var d;
//alert("Position" + " Latitude " + position.coords.latitude + " Longitude " + position.coords.longitude);
if(new_position == 0)
{
new_position = position;
}
else
{
//Change the positions around
old_position = new_position;
new_position = position;
var conv_old_position = new google.maps.LatLng(old_position.coords.latitude, old_position.coords.longitude);
var conv_new_position = new google.maps.LatLng(new_position.coords.latitude, new_position.coords.longitude);
d = google.maps.geometry.spherical.computeDistanceBetween(conv_old_position, conv_new_position);
run_total = parseInt(run_total);
d = parseInt(d);
run_total += d;
navigator.geolocation.clearWatch( watchID );
}
}
Now i keep an old_position, a new_position and a run_total as global variables. I save the last position as the new, and the one before that as old, and then calculate the distance and add this to a global variable.
But the watchPosition calls the UserLocationCalculation only once, after that, it just doesn't call it anymore.
Your UserLocationCalculation function is only being called once because at the end of the else clause, you are calling navigator.geolocation.clearWatch( watchID );. Since you are calling this, the device stops watching for position changes, so it never fires again.
From the docs: Stop watching for changes to the device's location referenced by the watchID parameter.

How to apply a javascript function to a multiple div Classes?

I have a function that creates a gallery of flickr sets pulled from my flickr account. I am getting the set numbers from a database and using a while loop to display the first image from the set. Each element of the table has the same class and i am applying a Javascript function to them. Unfortunately each table element is displaying the same photo, the last one pulled from the database.
$(document).ready(function() {
var flickrUrl="";
$('.gallery_table_data').each(function(){
flickrUrl = $(this).attr('title');
$('.flickr_div').flickrGallery({
"flickrSet" : flickrUrl,
"flickrKey" : "54498f94e844cb09c23a76808693730a"
});
});
});
and the images dont show up at all? can anyone help??
Here is the flickr jquery in case that's the problem:
var flickrhelpers = null;
(function(jQuery) {
jQuery.fn.flickrGallery = function(args) {
var $element = jQuery(this), // reference to the jQuery version of the current DOM element
element = this; // reference to the actual DOM element
// Public methods
var methods = {
init : function () {
// Extend the default options
settings = jQuery.extend({}, defaults, args);
// Make sure the api key and setID are passed
if (settings.flickrKey === null || settings.flickrSet === null) {
alert('You must pass an API key and a Flickr setID');
return;
}
// CSS jqfobject overflow for aspect ratio
element.css("overflow","hidden");
// Get the Flickr Set :)
$.getJSON("http://api.flickr.com/services/rest/?format=json&method=flickr.photosets.getPhotos&photoset_id=" + settings.flickrSet + "&api_key=" + settings.flickrKey + "&jsoncallback=?", function(flickrData){
var length = 1;
var thumbHTML = '';
for (i=0; i<length; i++) {
var photoURL = 'http://farm' + flickrData.photoset.photo[i].farm + '.' + 'static.flickr.com/' + flickrData.photoset.photo[i].server + '/' + flickrData.photoset.photo[i].id + '_' + flickrData.photoset.photo[i].secret +'.jpg'
settings.imgArray[i] = photoURL;
settings.titleArray[i] = flickrData.photoset.photo[i].title;
}
// Get the position of the element Flickr jqfobj will be loaded into
settings.x = element.offset().left;
settings.y = element.offset().top;
settings.c = settings.x + (element.width() / 2);
settings.ct = settings.y + (element.height() / 2);
// When data is set, load first image.
flickrhelpers.navImg(0);
});
}
}
// Helper functions here
flickrhelpers = {
navImg : function (index) {
// Set the global index
currentIndex = index;
// Create an image Obj with the URL from array
var thsImage = null;
thsImage = new Image();
thsImage.src = settings.imgArray[index];
// Set global imgObj to jQuery img Object
settings.fImg = $( thsImage );
// Display the image
element.html('');
element.html('<img class="thsImage" src=' + settings.imgArray[index] + ' border=0>');
// Call to function to take loader away once image is fully loaded
$(".thsImage").load(function() {
// Set the aspect ratio
var w = $(".thsImage").width();
var h = $(".thsImage").height();
if (w > h) {
var fRatio = w/h;
$(".thsImage").css("width",element.width());
$(".thsImage").css("height",Math.round(element.width() * (1/fRatio)));
} else {
var fRatio = h/w;
$(".thsImage").css("height",element.height());
$(".thsImage").css("width",Math.round(element.height() * (1/fRatio)));
}
if (element.outerHeight() > $(".thsImage").outerHeight()) {
var thisHalfImage = $(".thsImage").outerHeight()/2;
var thisTopOffset = (element.outerHeight()/2) - thisHalfImage;
$(".thsImage").css("margin-top",thisTopOffset+"px");
}
if (settings.titleArray[currentIndex] != "") {
$(".flickr_count").append(settings.titleArray[currentIndex]);
}
});
},
toggleUp : function() {
$("#flickr_thumbs").slideUp("slow");
}
}
// Hooray, defaults
var defaults = {
"flickrSet" : null,
"flickrKey" : null,
"x" : 0, // Object X
"y" : 0, // Object Y
"c" : 0, // Object center point
"ct" : 0, // Object center point from top
"mX" : 0, // Mouse X
"mY" : 0, // Mouse Y
"imgArray" : [], // Array to hold urls to flickr images
"titleArray" : [], // Array to hold image titles if they exist
"currentIndex" : 0, // Default image index
"fImg" : null, // For checking if the image jqfobject is loaded.
}
// For extending the defaults!
var settings = {}
// Init this thing
jQuery(document).ready(function () {
methods.init();
});
// Sort of like an init() but re-positions dynamic elements if browser resized.
$(window).resize(function() {
// Get the position of the element Flickr jqfobj will be loaded into
settings.x = element.offset().left;
settings.y = element.offset().top;
settings.c = settings.x + (element.width() / 2);
settings.ct = settings.y + (element.height() / 2);
});
}
})(jQuery);
The big problem is in your $.each loop. I am going to assume the plugin will work for all the elements you are looping over although have doubts that it will.
WHen you select $('.flickr_div') on each pass it affects all the elements in page with that class...so only the last pass of loop is valid
$(document).ready(function() {
var flickrUrl="";
$('.gallery_table_data').each(function(){
flickrUrl = $(this).attr('title');
/* this is your problem , is selecting all ".flickr_div" in page on each loop*/
//$('.flickr_div').flickrGallery({
/* without seeing your html structure am assuming
next class is inside "this"
try: */
$(this).find('.flickr_div').flickrGallery({
"flickrSet" : flickrUrl,
"flickrKey" : "54498f94e844cb09c23a76808693730a"
});
});
});
EDIT This same concept of using find() should also be refactoered into code within the plugin. Plugin should have all ID's replaced with classes.
Plugin really does not look well constructed for multiple instances within a page
I might be wrong here, but won't this (in your flickrGallery object)
$("body").append('<div id="flickr_loader"></div>');`
create multiple elements with the same ID? And the same for images in flickrhelpers:
element.html('<img id="thsImage" src=' + settings.imgArray[index] + ' border=0>');

Categories