Reputation: 1384
Am trying to laod tweets into a div after looping them from yahoo placemaker. They are loading on the div but the information shown by them is placemaker's last result.
This is the code..
function getLocation(user, date, profile_img, text,url) {
var templates = [];
templates[0] = '<div><div></div><h2 class="firstHeading">'+user+'</h2><div>'+text+'</div><div><p><a href="' + url + '"target="_blank">'+url+'</a></p></div><p>Date Posted- '+date+'</p></div>';
templates[1] = '<table width="320" border="0"><tr><td class="user" colspan="2" rowspan="1">'+user+'</td></tr><tr><td width="45"><a href="'+profile_img+'"><img src="'+profile_img+'" width="55" height="50"/></a></td><td width="186">'+text+'<p><a href="' + url + '"target="_blank">'+url+'</a></p></td></tr></table><hr>';
templates[2] = '<div><div></div><h2 class="firstHeading">'+user+'</h2><div>'+text+'</div><div><p><a href="' + url + '"target="_blank">'+url+'</a></p></div><p>Date Posted- '+date+'</p></div>';
templates[3] = '<table width="320" border="0"><tr><td class="user" colspan="2" rowspan="1">'+user+'</td></tr><tr><td width="45"><a href="'+profile_img+'"><img src="'+profile_img+'" width="55" height="50"/></a></td><td width="186">'+text+'<p><a href="' + url + '"target="_blank">'+url+'</a></p></td></tr></table><hr>';
var geocoder = new google.maps.Geocoder();
Placemaker.getPlaces(text, function (o) {
console.log(o);
if (!$.isArray(o.match)) {
var latitude = o.match.place.centroid.latitude;
var longitude = o.match.place.centroid.longitude;
var myLatLng = new google.maps.LatLng(latitude, longitude);
var marker = new google.maps.Marker({
icon: profile_img,
title: user,
map: map,
position: myLatLng
});
var infowindow = new google.maps.InfoWindow({
content: templates[0].replace('user',user).replace('text',text).replace('url',url).replace('date',date)
});
var $tweet = $(templates[1].replace('%user',user).replace(/%profile_img/g,profile_img).replace('%text',text).replace('%url',url));
$('#user-banner').css("visibility","visible");$('#news-banner').css("visibility","visible");
$('#news-tweets').css("overflow","scroll").append($tweet);
function openInfoWindow() {
infowindow.open(map, marker);
}
google.maps.event.addListener(marker, 'click', openInfoWindow);
$tweet.find(".user").on('click', openInfoWindow);
bounds.extend(myLatLng);
}
});
}
Upvotes: 0
Views: 185
Reputation: 18078
ak,
After some experimentation, I tracked down the problem to Placemaker.js
not being able to handle multiple simultaneous requests. It's tempting to think you can overcome the problem with closures to remember loop-generated data but this doesn't work. The fix needs to be within Placemaker
and I opted to refactor it as a jQuery plugin. jQuery affords the possibility of returning a promise
from the getPlaces
method, making it similar to jQuery's native $.ajax()
, $.get()
etc.
/* ******************************************************************************
* Yahoo! Placemaker.js factored as a jQuery Plugin
* ******************************************************************************
* by Beetroot-Beetroot: http://stackoverflow.com/users/1142252/beetroot-beetroot
* ******************************************************************************
* For example of usage, see : http://stackoverflow.com/questions/12253544/
* ******************************************************************************
* All rights reserved
* Please keep this attribution intact
* ******************************************************************************
*/
(function($){
// **********************************
// ***** Start: Private Members *****
var pluginName = 'Placemaker';
var config = {
appID: ''
}
// ***** Fin: Private Members *****
// ********************************
// *********************************
// ***** Start: Public Methods *****
var methods = {
config: function(obj) {
$.extend(config, obj);
},
getPlaces: function(data) {
var that = this;//jQuery object
var def = new $.Deferred();
if(config.appID === '') {
def.rejectWith(this, [{message: pluginName + ' plugin application ID is not set'}]);
return def.promise();
}
var query = [
'select * from geo.placemaker where documentContent="' + data.text + '" and documentType="text/plain"'
];
if(data.locale) {
query.push('and inputLanguage="' + data.locale + '"');
}
query.push('and appid="' + config.appID + '"');
var url = [
'http://query.yahooapis.com/v1/public/yql?q=' + encodeURIComponent(query.join(' ')),
'format=json',
'env=http%3A%2F%2Fdatatables.org%2Falltables.env',
'callback=?' // callback=Placemaker.retrieve ????
];
$.ajax({
url: url.join('&'),
type: 'GET',
dataType: 'JSON',
cache: false
}).done(function(o) {
if(o.query && o.query.results && o.query.results.matches) {
def.resolveWith(that, [o.query.results.matches, data]);
}
else {
def.rejectWith(that, [{message:'no locations found'}]);
}
}).fail(function(jqXHR, textStatus, errorThrown) {
def.rejectWith(that, [{message: textStatus}]);
});
return def.promise();
}
};
// ***** Fin: Public Methods *****
// *******************************
// *****************************
// ***** Start: Supervisor *****
$.fn[pluginName] = function( method ) {
if ( methods[method] ) {
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || !method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist in jQuery.' + pluginName );
}
};
// ***** Fin: Supervisor *****
// ***************************
})( jQuery );
You can hard-code your appID in your own copy of the plugin or set it like this :
$().Placemaker('config', {'appID': '..........'});
Note that .Placemaker()
needs to be invoked on a jQuery object. For the 'config' method, any selector will do, so an empty jQuery object, $()
will suffice.
The rest of your code, including a plugin call, will look like this :
$(function() {
// *** fixed data ***
var mapOptions = {
center: new google.maps.LatLng(35.74651, -39.46289),
zoom: 2,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var templates = [];
templates[0] = '<div><h2 class="firstHeading">%user</h2><div>%text</div><div><a href="%url" target="_blank">%url</a></div><div>Date Posted- %date</div></div>';
templates[1] = '<table width="320" border="0"><tr><td class="user" colspan="2">%user</td></tr><tr><td width="45"><a href="%profile_img"><img src="%profile_img" width="55" height="50"/></a></td><td width="186">%text<p><a href="%url" target="_blank">%url</a></p></td></tr></table><hr/>';
templates[3] = "https://api.twitter.com/1/statuses/user_timeline.json?include_entities=true&include_rts=false&screen_name=%val&count=10&callback=?";
$$ = { //cache of jQuery objects
news_tweets: $("#news-tweets"),
user_banner: $('#user-banner')
};
// *** functions ***
function news_tweets(value1) {
$$.news_tweets.empty();
var bounds = new google.maps.LatLngBounds(); //??
var map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
$.getJSON(templates[3].replace('%val', value1), function(data) {
var d, len = data.length;
for (var i = 0; i < len; i++) {
var item = data[i];
d = {
text: item.text,
user : item.user.name,
date: item.created_at,
profile_img: item.user.profile_image_url,
url: (item.entities && item.entities.urls && item.entities.urls.length) ? item.entities.urls[0].url : '',
locale: null
}
d.$tweet = $(templates[1].replace('%user', d.user).replace(/%profile_img/g, d.profile_img).replace('%text', d.text).replace(/%url/g, d.url))
.appendTo($$.news_tweets.css("overflow", "scroll"))
.find(".user")
.Placemaker('getPlaces', d) //.Placemaker('getPlaces') returns a promise
.done(function(o, d) {
var m = ($.isArray(o.match) ? o.match[0] : o.match) || {};
if(m.place) {
var myLatLng = new google.maps.LatLng(m.place.centroid.latitude, m.place.centroid.longitude);
var marker = new google.maps.Marker({
icon: d.profile_img,
title: d.user,
map: map,
position: myLatLng
});
var infowindow = new google.maps.InfoWindow({
content: templates[0].replace('%user', d.user).replace('%text', d.text).replace(/%url/g, d.url).replace('%date', d.date)
});
function openInfoWindow() {
infowindow.open(map, marker);
}
google.maps.event.addListener(marker, 'click', openInfoWindow);
this.each(function() { //`this` is already a jQuery object. Re-wrapping as `$(this)` is not necessary.
$(this).on('click', openInfoWindow).css({'color':'#900', 'text-decoration':'underline', 'cursor':'pointer'});//$(this) is an individual tweet in #news_tweets.
});
bounds.extend(myLatLng); //??
}
}).fail(function(err) {
console.log(err.message);
});
}
});
}
// *** event handlers ***
$("#newsTypes").on('click', 'img', function() {
news_tweets($(this).data('type'));
//user_tweets("euronews");
});
});
At the heart of this, you will find the following structure :
for(...) {
var d = {...}; //object map comprising both data and options
$(htmlString).appendTo(...).find(...).Placemaker('getPlaces', d).done(fn{}).fail(fn{});
}
It is important to note that :
.Placemaker('getPlaces', d)
, methods in the chain return a jQuery promise object..done()
and the .fail()
handler, this
is equivalent to the original standard jQuery object..Placemaker('getPlaces', d)
, reappears as the second argument of the .done()
handler. This feature allows us to call .Placemaker('getPlaces', d)
in a for loop without needing to specifically put the data into a closure or storing it in the DOM with .data()
. In this regard, the 'getPlaces' method effectively serves as a closure as well as providing the required asynchronous lookup behaviour.All this will be judged as thoroughly confusing or totally clever depending on your point of view.
Upvotes: 1
Reputation: 1802
From what I see your code is written to .replace()
the contents of the div's through each iteration. You need to add some indexing to your template array.
Try something like:
var index=0;
var templates = [];
templates[index]={
'0':'<div><div></div><h2....',
'1':'<table width="320".....',
'2':'<div><div></div><h2....',
'3':'<table width="320"....'
}
Then put your Placemaker.getPlaces
block in a for loop
You update your templates with templates[index]["0"]=whatever
.
I think this the logic you should be pursuing. Hope this all helps.
Upvotes: 0