Map route click event not working in Titanium Appcelerator - google-maps-markers

I'm trying to add a clickable route on Geocoder example from Appcelerator. The problem is that I'm not getting any event when clicking at the route object.
Here's my code:
var cord1= {
latitude:29.078685,
longitude:-110.971205,
};
var cord2= {
latitude:29.081496,
longitude:-110.959232,
};
var route1 = [cord1, cord2];
var route = MapModule.createRoute({
points : route1,
color : "red",
width : 5.0
});
route.addEventListener('click', function(e){
Ti.APP.info(e);
});
$.mapview.addRoute(route);

The Modules.Map.Route object doesn't have any events. None of the map objects do, except the map view itself, and we can use the mapview's click event to listen for clicks, and then check the clicksource property to see what was clicked on the map.
The catch is that routes won't generate a click event, but polylines do, so the workaround is to use a polyline and look for the clicksource in the mapview's click event. Something like this should work:
var coord1 = [-110.971205, 29.078685];
var coord2 = [-110.959232, 29.081496];
var route1 = [coord1, coord2];
var route = MapModule.createPolyline({
points: route1,
strokeColor: "#ff0000",
strokeWidth: 5
});
$.mapview.addPolyline(route);
$.mapview.addEventListener('click', function (e) {
//check the clicksource for 'polyline'
console.log(e.clicksource);
});

Related

Dynamic checkbox toggle to stack / unstack AMCharts 4 XY columnseries / bar chart

I have a checkbox placed separately of my XY columnseries chart. The original chart has each series.stacked = true. This works fine. I have a listener on the checkbox to toggle from the stacked columns to independent columns. It sets (toggles) the same stacked property on each series. Unfortunately nothing updates. I have tried calling invalidateData() on the chart after the property assignment - but that also doesn't work to update the stacking / unstacking function.
$("#chartAssetsTimelineIndividualColumns").change(function () {
chartAssetsTimeline.series.values.forEach(function (series) {
series.stacked = !this.checked;
});
});
this doesn't refer to the input element when you're inside the forEach method as it is scoped to the window object at that point. You'll want to save a reference to it and use that instead, or just use the event object that the change method provides. Also, you should use the each function instead of iterating over the read-only values array.
$('#chartAssetsTimelineIndividualColumns').change(function() {
var that = this;
chartAssetsTimelineIndividualColumns.series.each(function(series) {
series.stacked = !that.checked;
});
});
// or
$('#chartAssetsTimelineIndividualColumns').change(function(e) {
chartAssetsTimelineIndividualColumns.series.each(function(series) {
series.stacked = !e.target.checked;
});
});
// or, preferably, with VanillaJS:
document.getElementById('chartAssetsTimelineIndividualColumns').addEventListener('change', function(e) {
chartAssetsTimelineIndividualColumns.series.each(function(series) {
series.stacked = !e.target.checked;
});
});

Replicate event pinch from a div to openlayer map

Good afternoon,
I have a div over the ol3 map with one list of marker. When the user clic on a marker, the center's map is updated with the coordinate of this marker.
I tried that the user have the possibility to zoom on the map when he "pinch" on the layer on top.
I successed to intercept the event on the "layerOnTop" and replicate it to the map layer. With console.log, I saw that the job is correctly done but there is any reaction on the map.
You can see the code : http://jsfiddle.net/2ek4j3a4/
var center = ol.proj.transform([4.90756, 45.5172], 'EPSG:4326', 'EPSG:3857');
var map = new ol.Map({
layers: [new ol.layer.Tile({source: new ol.source.OSM()})],
target: 'map',
controls: ol.control.defaults({attributionOptions: ({ collapsible: false})}),
view: new ol.View({center: center,zoom: 12})
});
$('#layerOnTop').on("touchmove", function(event) {
var scale = event.originalEvent.touches;
if (scale.length==2) {
event.preventDefault();
var bottomEvent = new $.Event("touchmove");
bottomEvent.pageX = event.pageX;
bottomEvent.pageY = event.pageY;
$("#map").trigger(bottomEvent);
}
});
$('#map').on("touchmove", function(event) {console.log('ok')})
I did my test with an Android phone.
Somebody have an idea, please ?
I finally found :) There was 2 errors in my code :
1) target should be the canvas element in div#map and not #map himself
2) I fixed the touch identifier by Date.now() and add +1 for the second, it was certainely too big
See below the code if one day somebody search ...
$('#layerOnTop').on("touchstart touchmove touchend", function(event) {
view = document.getElementById('map').ownerDocument.defaultView;
if (event.originalEvent.touches.length==2) {
var target = document.getElementById('map').getElementsByTagName('canvas')[0];
var pageX1 = event.originalEvent.touches[0].pageX;
var pageY1 = event.originalEvent.touches[0].pageY;
var pageX2 = event.originalEvent.touches[1].pageX;
var pageY2 = event.originalEvent.touches[1].pageY;
var touch1 = document.createTouch(view, target, 0, pageX1, pageY1, 0, 0);
var touch2 = document.createTouch(view, target, 1, pageX2, pageY2, 0, 0);
var touchList = document.createTouchList(touch1, touch2);
var touchEvent = new TouchEvent(event.type, {cancelable: true, bubbles: true, touches: touchList, targetTouches: touchList, changedTouches: touchList})
target.dispatchEvent(touchEvent);
}
});
http://jsfiddle.net/2ek4j3a4/7/

How to emit and listen event between pagemod and widget?

I am using the addon-sdk. I have a widget and upon clicking the widget I want to do something with the website (be it modifying the page or reading the deep DOM).
So my thought after reading https://developer.mozilla.org/en-US/Add-ons/SDK/High-Level_APIs/page-mod#Communicating_With_Content_Scripts would be:
pagemod activated on matched url (in this case ANY)
click on widget which satisfies left-click event. It then emits widget-click.
Pagemod receives widget-click event and fires back an event called from-pagemod.
from-pagemod does something to the webpage.
I see the following output in stdout:
console.log: project: about to emit widget-click
console.log: project: after emitting widget-click
So pagemod didn't receive that event or it was never set up. I am not sure what is missing from this simple test case. Any help is appreciated.
Here is lib/main.js.
var widgets = require('sdk/widget');
var pageMod = require("sdk/page-mod");
var data = require("sdk/self").data;
var tabs = require("sdk/tabs");
exports.main = function() {
var widget = widgets.Widget({
label: "widget label",
id: "widget-id",
contentURL: data.url("off.png"),
contentScriptFile: [data.url("widget.js"), data.url("page.js")]
});
widget.port.on("left-click", function() {
console.log("about to emit widget-click");
widget.port.emit("widget-click", "foo");
console.log("after emitting widget-click");
});
var page = pageMod.PageMod({
include: "*",
contentScriptWhen: "end",
contentScriptFile: data.url("page.js"),
onAttach: function(worker) {
worker.port.on("widget-click", function(msg) {
console.log("on widget-click, ready to emit from-pagemod event");
worker.port.emit("from-pagemod", "foo");
});
}
});
};
Here is page.js
self.port.on("from-pagemod", function(msg) {
console.log("inside from-pagemod listener");
// read DOM or modify the DOM
});
Here is widget.js
this.addEventListener('click', function(event) {
if(event.button == 0 && event.shiftKey == false)
self.port.emit('left-click');
}, true);
Edit 2, Answering the actual question:
If you want to do something to the page on widget click when the content script is already attached, register your widget listener somewhere you have access to the page-worker. You could do this by putting your widget.port.on code inside the pageMod's onAttach(), but then it would only work for the most recently attached page. The best way to make it functional would be to store all workers then check if the current tab has a worker when the widget is clicked, like so:
main.js partial
var workers = [];
widget.port.on("left-click", function() {
console.log("about to emit widget-click");
var worker = getWorker(tabs.activeTab);
if (worker) {
worker.port.emit("widget-click", "foo");
console.log("after emitting widget-click");
}
});
var page = pageMod.PageMod({
include: "*", // TODO: make more specific
contentScriptWhen: "end",
contentScriptFile: data.url("page.js"),
onAttach: function(worker) {
workers.push(worker);
worker.on('detach', function() {
detachWorker(worker);
});
// could be written as
// worker.on('detach', detachWorker.bind(null, worker);
}
});
function detachWorker(worker) {
var index = workers.indexOf(worker);
if(index!==-1) workerArray.splice(index, 1);
}
function getWorker(workers, tab) {
for (var i = workers.length - 1; i >= 0; i--) {
if (workers[i].tab===tab) return worker;
}
}
page.js
self.port.on("widget-click", function(msg) {
console.log("inside widget-click listener");
// read DOM or modify the DOM
});
The reason that your solution wasn't working is that you assumed that events were somehow linked to the file rather than an object.
Old answer: You're making it way too complicated.
Just attach the content script on click (as opposed to adding a content script to every single page that does nothing unless it receives an event)
main.js
var widgets = require('sdk/widget');
var data = require("sdk/self").data;
var tabs = require("sdk/tabs");
exports.main = function() {
var widget = widgets.Widget({
label: "widget label",
id: "widget-id",
contentURL: data.url("off.png"),
contentScriptFile: data.url("widget.js")
});
widget.port.on("left-click", function() {
console.log("about to attach script");
var worker = tabs.activeTab.attach({
contentScriptFile: data.url("page.js");
});
worker.port.on('message from content script', function(someVariable){
//Now I can do something with someVariable in main.js
});
});
};
page.js
// read DOM or modify the DOM
//I'm done, I'll send info back to the main script. This is optional
self.port.emit('message from content script', someVariable);
Edit: Read Modifying the Page Hosted by a Tab for more info. Also, the Tutorials page is a good place to start when you're trying to do something you haven't done before with the SDK. It's a good way to step back and think of alternatives for trying to achieve your goal.
a port is a communication channel between a content script and an add-on component: your Widget may interact with its content via widget.js, and your pagemod with the matched webpage content via page.js. When you do widget.port.emit("widget-click", "foo"); this message can only be listened by widget.js using self.port.on('widget-click') not by the pagemod instance. Since you widget and pagemod are objects that share the main.js scope they can talk each other by just accessing its properties and methods.

Get latitude and longitude of marker (onclick)

How can you create an listener to a marker and get its latitude and longitude. When I create an listener to each marker of a click event, I can do stuff like alert on the click, but how can I get the coords of the marker i.e click -> this.getLat/getLng, etc. when clicked on?
Try this:
marker = new google.maps.Marker({
position: latlng,
map: map
}); //end marker
//Add listener
google.maps.event.addListener(marker, "click", function (event) {
alert(this.position);
}); //end addListener
As a followup to Bryan Weaver's excellent answer, if you want to SAVE latitude and longitude separately, you must use lat() and lng(). the position property is not a regular string.
bryan's code would become:
marker = new google.maps.Marker({
position: latlng,
map: map
}); //end marker
//Add listener
google.maps.event.addListener(marker, "click", function (event) {
var latitude = this.position.lat();
var longitude = this.position.lng();
alert(this.position);
}); //end addListener
you can apply lat() and lng() directly on a variable (latLng)
var latitude = latLng.lat();
var longitude = latLng.lng();
All the answers here are good,
but I have tried something which kind of works like Google Maps.
When you click somewhere on the map, a marker shows up but when you click somewhere else the old marker goes away and the new marker shows up with latitude and longitude in the text field.
Here's the DEMO
// In the following example, markers appear when the user clicks on the map.
// The markers are stored in an array.
// The user can then click an option to hide, show or delete the markers.
var map;
var markers = [];
function initMap() {
var haightAshbury = {lat: 23.2748308, lng: 77.4519248};
map = new google.maps.Map(document.getElementById('map'), {
zoom: 16.3, // Set the zoom level manually
center: haightAshbury,
mapTypeId: 'terrain'
});
// This event listener will call addMarker() when the map is clicked.
map.addListener('click', function(event) {
if (markers.length >= 1) {
deleteMarkers();
}
addMarker(event.latLng);
document.getElementById('lat').value = event.latLng.lat();
document.getElementById('long').value = event.latLng.lng();
});
}
// Adds a marker to the map and push to the array.
function addMarker(location) {
var marker = new google.maps.Marker({
position: location,
map: map
});
markers.push(marker);
}
// Sets the map on all markers in the array.
function setMapOnAll(map) {
for (var i = 0; i < markers.length; i++) {
markers[i].setMap(map);
}
}
// Removes the markers from the map, but keeps them in the array.
function clearMarkers() {
setMapOnAll(null);
}
// Deletes all markers in the array by removing references to them.
function deleteMarkers() {
clearMarkers();
markers = [];
}
</script>
<script async defer
src="http://maps.google.com/maps/api/js?sensor=false&callback=initMap">
</script>
So what this code does is, when you click somewhere on the map, the values of latitude and longitude shows up in the text field.

Google Maps within a dynamically loaded jquery Accordion

I'm trying to load a google map within a jquery ui accordion with contents loaded by ajax.
$("h2", "#accordion").click(function(e) {
var contentDiv = $(this).next("div");
if (contentDiv.children().length == 1)
{
contentDiv.load($(this).find("a").attr("href"));
contentDiv.ready(function(){
var latlng = new google.maps.LatLng(-34.397, 150.644);
var myOptions = {
zoom: 8,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
})
}
});
That's my current code (Google Maps API V3), but it redirects to the url of the href when I click currently, obviously with no google map because there's no javascript in this response. If I comment out the map line everything works fine (except the map itself).
Well, the thread is dead but since I lost some hours on similar problem I feel like sharing my solution.
I have a jqueryui accordeon with a googlemap in each pane.
The problem is accordeon have no size for hidden panes, and googlemaps uses that size to render the maps.
So I save every 'map' and 'marker' (gmaps terminology) in an indexed array (var maps) and when user changes accordeon pane, I 'refresh' the corresponding gmap.
The catch there is that event resize is not enough, you have to force zoom for redrawn and even then because of the size it had on a closed pane, your marker for that map is completelly off screen so you have recenter it based on the marker position.
$(document).ready(function(){
if($('div.map_canvas').length>0) initializeMap(); //init map
if($('#accordion').length>0) $("#accordion .items").accordion(); //init accordeon
$("#accordion .items").bind("accordionchange", function(event, Element) {
current=maps[Element.options.active];
google.maps.event.trigger(current.map, 'resize'); //resize
current.map.setZoom( current.map.getZoom() ); //force redrawn
current.map.setCenter(current.marker.getPosition()); //recenter on marker
})
});
var maps=Array();
function initializeMap() {
var geocoder;
$('div.map_canvas').each(function(index,Element) {
var address = $(Element).text();
var myOptions = {
zoom: 15,
navigationControl: true,
navigationControlOptions: { style: google.maps.NavigationControlStyle.SMALL },
mapTypeControl: true,
mapTypeControlOptions: {style: google.maps.MapTypeControlStyle.DROPDOWN_MENU},
scaleControl: true,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
geocoder = new google.maps.Geocoder();
geocoder.geocode({'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
myOptions.center = results[0].geometry.location;
map = new google.maps.Map(Element, myOptions);
marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location,
});
maps.push({marker:marker,map:map}); //save those
} else {
alert('The address could not be found for the following reason: ' + status);
}
});
})
}
It didn't occur to me until now, but since the content of each accordion is dynamically loaded, this constitutes an illegal cross site request. The map must be obtained from my own web server and transmitted to the ajax request in the accordion or the google map must be loaded once and manipulated into the dom of the accordion panes being loaded.

Resources