2223 lines
64 KiB
JavaScript
2223 lines
64 KiB
JavaScript
|
/*
|
||
|
* GMAP3 Plugin for JQuery
|
||
|
* Version : 4.1
|
||
|
* Date : 2011-11-18
|
||
|
* Licence : GPL v3 : http://www.gnu.org/licenses/gpl.html
|
||
|
* Author : DEMONTE Jean-Baptiste
|
||
|
* Contact : jbdemonte@gmail.com
|
||
|
* Web site : http://gmap3.net
|
||
|
*
|
||
|
* Copyright (c) 2010-2011 Jean-Baptiste DEMONTE
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions are met:
|
||
|
*
|
||
|
* - Redistributions of source code must retain the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer.
|
||
|
* - Redistributions in binary form must reproduce the above
|
||
|
* copyright notice, this list of conditions and the following
|
||
|
* disclaimer in the documentation and/or other materials provided
|
||
|
* with the distribution.
|
||
|
* - Neither the name of the author nor the names of its contributors
|
||
|
* may be used to endorse or promote products derived from this
|
||
|
* software without specific prior written permission.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||
|
*/
|
||
|
|
||
|
(function ($) {
|
||
|
|
||
|
/***************************************************************************/
|
||
|
/* STACK */
|
||
|
/***************************************************************************/
|
||
|
function Stack (){
|
||
|
var st = [];
|
||
|
this.empty = function (){
|
||
|
for(var i = 0; i < st.length; i++){
|
||
|
if (st[i]){
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
this.add = function(v){
|
||
|
st.push(v);
|
||
|
}
|
||
|
this.addNext = function ( v){
|
||
|
var t=[], i, k = 0;
|
||
|
for(i = 0; i < st.length; i++){
|
||
|
if (!st[i]){
|
||
|
continue;
|
||
|
}
|
||
|
if (k == 1) {
|
||
|
t.push(v);
|
||
|
}
|
||
|
t.push(st[i]);
|
||
|
k++;
|
||
|
}
|
||
|
if (k < 2) {
|
||
|
t.push(v);
|
||
|
}
|
||
|
st = t;
|
||
|
}
|
||
|
this.get = function (){
|
||
|
for(var i = 0; i < st.length; i++){
|
||
|
if (st[i]) {
|
||
|
return st[i];
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
this.ack = function (){
|
||
|
for(var i = 0; i < st.length; i++){
|
||
|
if (st[i]) {
|
||
|
delete st[i];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (this.empty()){
|
||
|
st = [];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/***************************************************************************/
|
||
|
/* STORE */
|
||
|
/***************************************************************************/
|
||
|
function Store(){
|
||
|
var store = {};
|
||
|
|
||
|
/**
|
||
|
* add a mixed to the store
|
||
|
**/
|
||
|
this.add = function(name, obj, todo){
|
||
|
name = name.toLowerCase();
|
||
|
if (!store[name]){
|
||
|
store[name] = [];
|
||
|
}
|
||
|
store[name].push({obj:obj, tag:ival(todo, 'tag')});
|
||
|
return name + '-' + (store[name].length-1);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* return a stored mixed
|
||
|
**/
|
||
|
this.get = function(name, last, tag){
|
||
|
var i, idx, add;
|
||
|
name = name.toLowerCase();
|
||
|
if (!store[name] || !store[name].length){
|
||
|
return null;
|
||
|
}
|
||
|
idx = last ? store[name].length : -1;
|
||
|
add = last ? -1 : 1;
|
||
|
for(i=0; i<store[name].length; i++){
|
||
|
idx += add;
|
||
|
if (store[name][idx]){
|
||
|
if (tag !== undefined) {
|
||
|
if ( (store[name][idx].tag === undefined) || ($.inArray(store[name][idx].tag, tag) < 0) ){
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
return store[name][idx].obj;
|
||
|
}
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* return all stored mixed
|
||
|
**/
|
||
|
this.all = function(name, tag){
|
||
|
var i, result = [];
|
||
|
name = name.toLowerCase();
|
||
|
if (!store[name] || !store[name].length){
|
||
|
return result;
|
||
|
}
|
||
|
for(i=0; i<store[name].length; i++){
|
||
|
if (!store[name][i]){
|
||
|
continue;
|
||
|
}
|
||
|
if ( (tag !== undefined) && ( (store[name][i].tag === undefined) || ($.inArray(store[name][i].tag, tag) < 0) ) ){
|
||
|
continue;
|
||
|
}
|
||
|
result.push(store[name][i].obj);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* return all storation groups
|
||
|
**/
|
||
|
this.names = function(){
|
||
|
var name, result = [];
|
||
|
for(name in store){
|
||
|
result.push(name);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* return an object from its reference
|
||
|
**/
|
||
|
this.refToObj = function(ref){
|
||
|
ref = ref.split('-'); // name - idx
|
||
|
if ((ref.length == 2) && store[ref[0]] && store[ref[0]][ref[1]]){
|
||
|
return store[ref[0]][ref[1]].obj;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* remove one object from the store
|
||
|
**/
|
||
|
this.rm = function(name, tag, pop){
|
||
|
var idx, i, tmp;
|
||
|
name = name.toLowerCase();
|
||
|
if (!store[name]) {
|
||
|
return false;
|
||
|
}
|
||
|
if (tag !== undefined){
|
||
|
if (pop){
|
||
|
for(idx = store[name].length - 1; idx >= 0; idx--){
|
||
|
if ( (store[name][idx] !== undefined) && (store[name][idx].tag !== undefined) && ($.inArray(store[name][idx].tag, tag) >= 0) ){
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
for(idx = 0; idx < store[name].length; idx++){
|
||
|
if ( (store[name][idx] !== undefined) && (store[name][idx].tag !== undefined) && ($.inArray(store[name][idx].tag, tag) >= 0) ){
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
idx = pop ? store[name].length - 1 : 0;
|
||
|
}
|
||
|
if ( !(idx in store[name]) ) {
|
||
|
return false;
|
||
|
}
|
||
|
// Google maps element
|
||
|
if (typeof(store[name][idx].obj.setMap) === 'function') {
|
||
|
store[name][idx].obj.setMap(null);
|
||
|
}
|
||
|
// jQuery
|
||
|
if (typeof(store[name][idx].obj.remove) === 'function') {
|
||
|
store[name][idx].obj.remove();
|
||
|
}
|
||
|
// internal (cluster)
|
||
|
if (typeof(store[name][idx].obj.free) === 'function') {
|
||
|
store[name][idx].obj.free();
|
||
|
}
|
||
|
delete store[name][idx].obj;
|
||
|
if (tag !== undefined){
|
||
|
tmp = [];
|
||
|
for(i=0; i<store[name].length; i++){
|
||
|
if (i !== idx){
|
||
|
tmp.push(store[name][i]);
|
||
|
}
|
||
|
}
|
||
|
store[name] = tmp;
|
||
|
} else {
|
||
|
if (pop) {
|
||
|
store[name].pop();
|
||
|
} else {
|
||
|
store[name].shift();
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* remove objects from the store
|
||
|
**/
|
||
|
this.clear = function(list, last, first, tag){
|
||
|
var k, i, name;
|
||
|
if (!list || !list.length){
|
||
|
list = [];
|
||
|
for(k in store){
|
||
|
list.push(k);
|
||
|
}
|
||
|
} else {
|
||
|
list = array(list);
|
||
|
}
|
||
|
for(i=0; i<list.length; i++){
|
||
|
if (list[i]){
|
||
|
name = list[i].toLowerCase();
|
||
|
if (!store[name]){
|
||
|
continue;
|
||
|
}
|
||
|
if (last){
|
||
|
this.rm(name, tag, true);
|
||
|
} else if (first){
|
||
|
this.rm(name, tag, false);
|
||
|
} else {
|
||
|
// all
|
||
|
while (this.rm(name, tag, false));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/***************************************************************************/
|
||
|
/* CLUSTERER */
|
||
|
/***************************************************************************/
|
||
|
|
||
|
function Clusterer(){
|
||
|
var markers = [], events=[], stored=[], latest=[], redrawing = false, redraw;
|
||
|
|
||
|
this.events = function(){
|
||
|
for(var i=0; i<arguments.length; i++){
|
||
|
events.push(arguments[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.startRedraw = function(){
|
||
|
if (!redrawing){
|
||
|
redrawing = true;
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
this.endRedraw = function(){
|
||
|
redrawing = false;
|
||
|
}
|
||
|
|
||
|
this.redraw = function(){
|
||
|
var i, args = [], that = this;
|
||
|
for(i=0; i<arguments.length; i++){
|
||
|
args.push(arguments[i]);
|
||
|
}
|
||
|
if (this.startRedraw){
|
||
|
redraw.apply(that, args);
|
||
|
this.endRedraw();
|
||
|
} else {
|
||
|
setTimeout(function(){
|
||
|
that.redraw.apply(that, args);
|
||
|
},
|
||
|
50
|
||
|
);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
this.setRedraw = function(fnc){
|
||
|
redraw = fnc;
|
||
|
}
|
||
|
|
||
|
this.store = function(data, obj, shadow){
|
||
|
stored.push({data:data, obj:obj, shadow:shadow});
|
||
|
}
|
||
|
|
||
|
this.free = function(){
|
||
|
for(var i = 0; i < events.length; i++){
|
||
|
google.maps.event.removeListener(events[i]);
|
||
|
}
|
||
|
events=[];
|
||
|
this.freeAll();
|
||
|
}
|
||
|
|
||
|
this.freeIndex = function(i){
|
||
|
if (typeof(stored[i].obj.setMap) === 'function') {
|
||
|
stored[i].obj.setMap(null);
|
||
|
}
|
||
|
if (typeof(stored[i].obj.remove) === 'function') {
|
||
|
stored[i].obj.remove();
|
||
|
}
|
||
|
if (stored[i].shadow){ // only overlays has shadow
|
||
|
if (typeof(stored[i].shadow.remove) === 'function') {
|
||
|
stored[i].obj.remove();
|
||
|
}
|
||
|
if (typeof(stored[i].shadow.setMap) === 'function') {
|
||
|
stored[i].shadow.setMap(null);
|
||
|
}
|
||
|
delete stored[i].shadow;
|
||
|
}
|
||
|
delete stored[i].obj;
|
||
|
delete stored[i].data;
|
||
|
delete stored[i];
|
||
|
}
|
||
|
|
||
|
this.freeAll = function(){
|
||
|
var i;
|
||
|
for(i = 0; i < stored.length; i++){
|
||
|
if (stored[i]) {
|
||
|
this.freeIndex(i);
|
||
|
}
|
||
|
}
|
||
|
stored = [];
|
||
|
}
|
||
|
|
||
|
this.freeDiff = function(clusters){
|
||
|
var i, j, same = {}, idx = [];
|
||
|
for(i=0; i<clusters.length; i++){
|
||
|
idx.push( clusters[i].idx.join('-') );
|
||
|
}
|
||
|
for(i = 0; i < stored.length; i++){
|
||
|
if (!stored[i]) {
|
||
|
continue;
|
||
|
}
|
||
|
j = $.inArray(stored[i].data.idx.join('-'), idx);
|
||
|
if (j >= 0){
|
||
|
same[j] = true;
|
||
|
} else {
|
||
|
this.freeIndex(i);
|
||
|
}
|
||
|
}
|
||
|
return same;
|
||
|
}
|
||
|
|
||
|
this.add = function(latLng, marker){
|
||
|
markers.push({latLng:latLng, marker:marker});
|
||
|
}
|
||
|
|
||
|
this.get = function(i){
|
||
|
return markers[i];
|
||
|
}
|
||
|
|
||
|
this.clusters = function(map, radius, maxZoom, force){
|
||
|
var proj = map.getProjection(),
|
||
|
nwP = proj.fromLatLngToPoint(
|
||
|
new google.maps.LatLng(
|
||
|
map.getBounds().getNorthEast().lat(),
|
||
|
map.getBounds().getSouthWest().lng()
|
||
|
)
|
||
|
),
|
||
|
i, j, j2, p, x, y, k, k2,
|
||
|
z = map.getZoom(),
|
||
|
pos = {},
|
||
|
saved = {},
|
||
|
unik = {},
|
||
|
clusters = [],
|
||
|
cluster,
|
||
|
chk,
|
||
|
lat, lng, keys, cnt,
|
||
|
bounds = map.getBounds(),
|
||
|
noClusters = maxZoom && (maxZoom <= map.getZoom()),
|
||
|
chkContain = map.getZoom() > 2;
|
||
|
|
||
|
cnt = 0;
|
||
|
keys = {};
|
||
|
for(i = 0; i < markers.length; i++){
|
||
|
if (chkContain && !bounds.contains(markers[i].latLng)){
|
||
|
continue;
|
||
|
}
|
||
|
p = proj.fromLatLngToPoint(markers[i].latLng);
|
||
|
pos[i] = [
|
||
|
Math.floor((p.x - nwP.x) * Math.pow(2, z)),
|
||
|
Math.floor((p.y - nwP.y) * Math.pow(2, z))
|
||
|
];
|
||
|
keys[i] = true;
|
||
|
cnt++;
|
||
|
}
|
||
|
// check if visible markers have changed
|
||
|
if (!force && !noClusters){
|
||
|
for(k = 0; k < latest.length; k++){
|
||
|
if( k in keys ){
|
||
|
cnt--;
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!cnt){
|
||
|
return false; // no change
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// save current keys to check later if an update has been done
|
||
|
latest = keys;
|
||
|
|
||
|
keys = [];
|
||
|
for(i in pos){
|
||
|
x = pos[i][0];
|
||
|
y = pos[i][1];
|
||
|
if ( !(x in saved) ){
|
||
|
saved[x] = {};
|
||
|
}
|
||
|
if (!( y in saved[x]) ) {
|
||
|
saved[x][y] = i;
|
||
|
unik[i] = {};
|
||
|
keys.push(i);
|
||
|
}
|
||
|
unik[ saved[x][y] ][i] = true;
|
||
|
}
|
||
|
radius = Math.pow(radius, 2);
|
||
|
delete(saved);
|
||
|
|
||
|
k = 0;
|
||
|
while(1){
|
||
|
while((k <keys.length) && !(keys[k] in unik)){
|
||
|
k++;
|
||
|
}
|
||
|
if (k == keys.length){
|
||
|
break;
|
||
|
}
|
||
|
i = keys[k];
|
||
|
lat = pos[i][0];
|
||
|
lng = pos[i][1];
|
||
|
saved = null;
|
||
|
|
||
|
|
||
|
if (noClusters){
|
||
|
saved = {lat:lat, lng:lng, idx:[i]};
|
||
|
} else {
|
||
|
do{
|
||
|
cluster = {lat:0, lng:0, idx:[]};
|
||
|
for(k2 = k; k2<keys.length; k2++){
|
||
|
if (!(keys[k2] in unik)){
|
||
|
continue;
|
||
|
}
|
||
|
j = keys[k2];
|
||
|
if ( Math.pow(lat - pos[j][0], 2) + Math.pow(lng-pos[j][1], 2) <= radius ){
|
||
|
for(j2 in unik[j]){
|
||
|
cluster.lat += markers[j2].latLng.lat();
|
||
|
cluster.lng += markers[j2].latLng.lng();
|
||
|
cluster.idx.push(j2);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
cluster.lat /= cluster.idx.length;
|
||
|
cluster.lng /= cluster.idx.length;
|
||
|
if (!saved){
|
||
|
chk = cluster.idx.length > 1;
|
||
|
saved = cluster;
|
||
|
} else {
|
||
|
chk = cluster.idx.length > saved.idx.length;
|
||
|
if (chk){
|
||
|
saved = cluster;
|
||
|
}
|
||
|
}
|
||
|
if (chk){
|
||
|
p = proj.fromLatLngToPoint( new google.maps.LatLng(saved.lat, saved.lng) );
|
||
|
lat = Math.floor((p.x - nwP.x) * Math.pow(2, z));
|
||
|
lng = Math.floor((p.y - nwP.y) * Math.pow(2, z));
|
||
|
}
|
||
|
} while(chk);
|
||
|
}
|
||
|
|
||
|
for(k2 = 0; k2 < saved.idx.length; k2++){
|
||
|
if (saved.idx[k2] in unik){
|
||
|
delete(unik[saved.idx[k2]]);
|
||
|
}
|
||
|
}
|
||
|
clusters.push(saved);
|
||
|
}
|
||
|
return clusters;
|
||
|
}
|
||
|
|
||
|
this.getBounds = function(){
|
||
|
var i, bounds = new google.maps.LatLngBounds();
|
||
|
for(i=0; i<markers.length; i++){
|
||
|
bounds.extend(markers[i].latLng);
|
||
|
}
|
||
|
return bounds;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/***************************************************************************/
|
||
|
/* GMAP3 GLOBALS */
|
||
|
/***************************************************************************/
|
||
|
|
||
|
var _default = {
|
||
|
verbose:false,
|
||
|
queryLimit:{
|
||
|
attempt:5,
|
||
|
delay:250, // setTimeout(..., delay + random);
|
||
|
random:250
|
||
|
},
|
||
|
init:{
|
||
|
mapTypeId : google.maps.MapTypeId.ROADMAP,
|
||
|
center:[46.578498,2.457275],
|
||
|
zoom: 2
|
||
|
},
|
||
|
classes:{
|
||
|
Map : google.maps.Map,
|
||
|
Marker : google.maps.Marker,
|
||
|
InfoWindow : google.maps.InfoWindow,
|
||
|
Circle : google.maps.Circle,
|
||
|
Rectangle : google.maps.Rectangle,
|
||
|
OverlayView : google.maps.OverlayView,
|
||
|
StreetViewPanorama: google.maps.StreetViewPanorama,
|
||
|
KmlLayer : google.maps.KmlLayer,
|
||
|
TrafficLayer : google.maps.TrafficLayer,
|
||
|
BicyclingLayer : google.maps.BicyclingLayer,
|
||
|
GroundOverlay : google.maps.GroundOverlay,
|
||
|
StyledMapType : google.maps.StyledMapType
|
||
|
}
|
||
|
},
|
||
|
_properties = ['events','onces','options','apply', 'callback', 'data', 'tag'],
|
||
|
_noInit = ['init', 'geolatlng', 'getlatlng', 'getroute', 'getelevation', 'getdistance', 'addstyledmap', 'setdefault', 'destroy'],
|
||
|
_directs = ['get'],
|
||
|
geocoder = directionsService = elevationService = maxZoomService = distanceMatrixService = null;
|
||
|
|
||
|
function setDefault(values){
|
||
|
for(var k in values){
|
||
|
if (typeof(_default[k]) === 'object'){
|
||
|
_default[k] = $.extend({}, _default[k], values[k]);
|
||
|
} else {
|
||
|
_default[k] = values[k];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function autoInit(iname){
|
||
|
if (!iname){
|
||
|
return true;
|
||
|
}
|
||
|
for(var i = 0; i < _noInit.length; i++){
|
||
|
if (_noInit[i] === iname) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* return true if action has to be executed directly
|
||
|
**/
|
||
|
function isDirect (todo){
|
||
|
var action = ival(todo, 'action');
|
||
|
for(var i = 0; i < _directs.length; i++){
|
||
|
if (_directs[i] === action) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------//
|
||
|
// Objects tools
|
||
|
//-----------------------------------------------------------------------//
|
||
|
|
||
|
/**
|
||
|
* return the real key by an insensitive seach
|
||
|
**/
|
||
|
function ikey (object, key){
|
||
|
if (key.toLowerCase){
|
||
|
key = key.toLowerCase();
|
||
|
for(var k in object){
|
||
|
if (k.toLowerCase && (k.toLowerCase() == key)) {
|
||
|
return k;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* return the value of real key by an insensitive seach
|
||
|
**/
|
||
|
function ival (object, key, def){
|
||
|
var k = ikey(object, key);
|
||
|
return k ? object[k] : def;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* return true if at least one key is set in object
|
||
|
* nb: keys in lowercase
|
||
|
**/
|
||
|
function hasKey (object, keys){
|
||
|
var n, k;
|
||
|
if (!object || !keys) {
|
||
|
return false;
|
||
|
}
|
||
|
keys = array(keys);
|
||
|
for(n in object){
|
||
|
if (n.toLowerCase){
|
||
|
n = n.toLowerCase();
|
||
|
for(k in keys){
|
||
|
if (n == keys[k]) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* return a standard object
|
||
|
* nb: include in lowercase
|
||
|
**/
|
||
|
function extractObject (todo, include, result/* = {} */){
|
||
|
if (hasKey(todo, _properties) || hasKey(todo, include)){ // #1 classical object definition
|
||
|
var i, k;
|
||
|
// get defined properties values from todo
|
||
|
for(i=0; i<_properties.length; i++){
|
||
|
k = ikey(todo, _properties[i]);
|
||
|
result[ _properties[i] ] = k ? todo[k] : {};
|
||
|
}
|
||
|
if (include && include.length){
|
||
|
for(i=0; i<include.length; i++){
|
||
|
if(k = ikey(todo, include[i])){
|
||
|
result[ include[i] ] = todo[k];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
} else { // #2 simplified object (all excepted "action" are options properties)
|
||
|
result.options= {};
|
||
|
for(k in todo){
|
||
|
if (k !== 'action'){
|
||
|
result.options[k] = todo[k];
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* identify object from object list or parameters list : [ objectName:{data} ] or [ otherObject:{}, ] or [ object properties ]
|
||
|
* nb: include, exclude in lowercase
|
||
|
**/
|
||
|
function getObject(name, todo, include, exclude){
|
||
|
var iname = ikey(todo, name),
|
||
|
i, result = {}, keys=['map'];
|
||
|
// include callback from high level
|
||
|
result['callback'] = ival(todo, 'callback');
|
||
|
include = array(include);
|
||
|
exclude = array(exclude);
|
||
|
if (iname) {
|
||
|
return extractObject(todo[iname], include, result);
|
||
|
}
|
||
|
if (exclude && exclude.length){
|
||
|
for(i=0; i<exclude.length; i++) {
|
||
|
keys.push(exclude[i]);
|
||
|
}
|
||
|
}
|
||
|
if (!hasKey(todo, keys)){
|
||
|
result = extractObject(todo, include, result);
|
||
|
}
|
||
|
// initialize missing properties
|
||
|
for(i=0; i<_properties.length; i++){
|
||
|
if (_properties[i] in result){
|
||
|
continue;
|
||
|
}
|
||
|
result[ _properties[i] ] = {};
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------//
|
||
|
// Service tools
|
||
|
//-----------------------------------------------------------------------//
|
||
|
|
||
|
function getGeocoder(){
|
||
|
if (!geocoder) {
|
||
|
geocoder = new google.maps.Geocoder();
|
||
|
}
|
||
|
return geocoder;
|
||
|
}
|
||
|
|
||
|
function getDirectionsService(){
|
||
|
if (!directionsService) {
|
||
|
directionsService = new google.maps.DirectionsService();
|
||
|
}
|
||
|
return directionsService;
|
||
|
}
|
||
|
|
||
|
function getElevationService(){
|
||
|
if (!elevationService) {
|
||
|
elevationService = new google.maps.ElevationService();
|
||
|
}
|
||
|
return elevationService;
|
||
|
}
|
||
|
|
||
|
function getMaxZoomService(){
|
||
|
if (!maxZoomService) {
|
||
|
maxZoomService = new google.maps.MaxZoomService();
|
||
|
}
|
||
|
return maxZoomService;
|
||
|
}
|
||
|
|
||
|
function getDistanceMatrixService(){
|
||
|
if (!distanceMatrixService) {
|
||
|
distanceMatrixService = new google.maps.DistanceMatrixService();
|
||
|
}
|
||
|
return distanceMatrixService;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------//
|
||
|
// Unit tools
|
||
|
//-----------------------------------------------------------------------//
|
||
|
|
||
|
/**
|
||
|
* return true if mixed is usable as number
|
||
|
**/
|
||
|
function numeric(mixed){
|
||
|
return (typeof(mixed) === 'number' || typeof(mixed) === 'string') && mixed !== '' && !isNaN(mixed);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* convert data to array
|
||
|
**/
|
||
|
function array(mixed){
|
||
|
var k, a = [];
|
||
|
if (mixed !== undefined){
|
||
|
if (typeof(mixed) === 'object'){
|
||
|
if (typeof(mixed.length) === 'number') {
|
||
|
a = mixed;
|
||
|
} else {
|
||
|
for(k in mixed) {
|
||
|
a.push(mixed[k]);
|
||
|
}
|
||
|
}
|
||
|
} else{
|
||
|
a.push(mixed);
|
||
|
}
|
||
|
}
|
||
|
return a;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* convert mixed [ lat, lng ] objet to google.maps.LatLng
|
||
|
**/
|
||
|
function toLatLng (mixed, emptyReturnMixed, noFlat){
|
||
|
var empty = emptyReturnMixed ? mixed : null;
|
||
|
if (!mixed || (typeof(mixed) === 'string')){
|
||
|
return empty;
|
||
|
}
|
||
|
// defined latLng
|
||
|
if (mixed.latLng) {
|
||
|
return toLatLng(mixed.latLng);
|
||
|
}
|
||
|
// google.maps.LatLng object
|
||
|
if (typeof(mixed.lat) === 'function') {
|
||
|
return mixed;
|
||
|
}
|
||
|
// {lat:X, lng:Y} object
|
||
|
else if ( numeric(mixed.lat) ) {
|
||
|
return new google.maps.LatLng(mixed.lat, mixed.lng);
|
||
|
}
|
||
|
// [X, Y] object
|
||
|
else if ( !noFlat && mixed.length){ // and "no flat" object allowed
|
||
|
if ( !numeric(mixed[0]) || !numeric(mixed[1]) ) {
|
||
|
return empty;
|
||
|
}
|
||
|
return new google.maps.LatLng(mixed[0], mixed[1]);
|
||
|
}
|
||
|
return empty;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* convert mixed [ sw, ne ] object by google.maps.LatLngBounds
|
||
|
**/
|
||
|
function toLatLngBounds(mixed, flatAllowed, emptyReturnMixed){
|
||
|
var ne, sw, empty;
|
||
|
if (!mixed) {
|
||
|
return null;
|
||
|
}
|
||
|
empty = emptyReturnMixed ? mixed : null;
|
||
|
if (typeof(mixed.getCenter) === 'function') {
|
||
|
return mixed;
|
||
|
}
|
||
|
if (mixed.length){
|
||
|
if (mixed.length == 2){
|
||
|
ne = toLatLng(mixed[0]);
|
||
|
sw = toLatLng(mixed[1]);
|
||
|
} else if (mixed.length == 4){
|
||
|
ne = toLatLng([mixed[0], mixed[1]]);
|
||
|
sw = toLatLng([mixed[2], mixed[3]]);
|
||
|
}
|
||
|
} else {
|
||
|
if ( ('ne' in mixed) && ('sw' in mixed) ){
|
||
|
ne = toLatLng(mixed.ne);
|
||
|
sw = toLatLng(mixed.sw);
|
||
|
} else if ( ('n' in mixed) && ('e' in mixed) && ('s' in mixed) && ('w' in mixed) ){
|
||
|
ne = toLatLng([mixed.n, mixed.e]);
|
||
|
sw = toLatLng([mixed.s, mixed.w]);
|
||
|
}
|
||
|
}
|
||
|
if (ne && sw){
|
||
|
return new google.maps.LatLngBounds(sw, ne);
|
||
|
}
|
||
|
return empty;
|
||
|
}
|
||
|
|
||
|
/***************************************************************************/
|
||
|
/* GMAP3 */
|
||
|
/***************************************************************************/
|
||
|
|
||
|
function Gmap3($this){
|
||
|
|
||
|
var stack = new Stack(),
|
||
|
store = new Store(),
|
||
|
map = null,
|
||
|
styles = {},
|
||
|
running = false;
|
||
|
|
||
|
//-----------------------------------------------------------------------//
|
||
|
// Stack tools
|
||
|
//-----------------------------------------------------------------------//
|
||
|
|
||
|
/**
|
||
|
* store actions to execute in a stack manager
|
||
|
**/
|
||
|
this._plan = function(list){
|
||
|
for(var k = 0; k < list.length; k++) {
|
||
|
stack.add(list[k]);
|
||
|
}
|
||
|
this._run();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* store one action to execute in a stack manager after the current
|
||
|
**/
|
||
|
this._planNext = function(todo){
|
||
|
stack.addNext(todo);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* execute action directly
|
||
|
**/
|
||
|
this._direct = function(todo){
|
||
|
var action = ival(todo, 'action');
|
||
|
return this[action]($.extend({}, action in _default ? _default[action] : {}, todo.args ? todo.args : todo));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* called when action in finished, to acknoledge the current in stack and start next one
|
||
|
**/
|
||
|
this._end = function(){
|
||
|
running = false;
|
||
|
stack.ack();
|
||
|
this._run();
|
||
|
},
|
||
|
/**
|
||
|
* if not running, start next action in stack
|
||
|
**/
|
||
|
this._run = function(){
|
||
|
if (running) {
|
||
|
return;
|
||
|
}
|
||
|
var todo = stack.get();
|
||
|
if (!todo) {
|
||
|
return;
|
||
|
}
|
||
|
running = true;
|
||
|
this._proceed(todo);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------//
|
||
|
// Call tools
|
||
|
//-----------------------------------------------------------------------//
|
||
|
|
||
|
/**
|
||
|
* run the appropriated function
|
||
|
**/
|
||
|
this._proceed = function(todo){
|
||
|
todo = todo || {};
|
||
|
var action = ival(todo, 'action') || 'init',
|
||
|
iaction = action.toLowerCase(),
|
||
|
ok = true,
|
||
|
target = ival(todo, 'target'),
|
||
|
args = ival(todo, 'args'),
|
||
|
out;
|
||
|
// check if init should be run automatically
|
||
|
if ( !map && autoInit(iaction) ){
|
||
|
this.init($.extend({}, _default.init, todo.args && todo.args.map ? todo.args.map : todo.map ? todo.map : {}), true);
|
||
|
}
|
||
|
|
||
|
// gmap3 function
|
||
|
if (!target && !args && (iaction in this) && (typeof(this[iaction]) === 'function')){
|
||
|
this[iaction]($.extend({}, iaction in _default ? _default[iaction] : {}, todo.args ? todo.args : todo)); // call fnc and extends defaults data
|
||
|
} else {
|
||
|
// "target" object function
|
||
|
if (target && (typeof(target) === 'object')){
|
||
|
if (ok = (typeof(target[action]) === 'function')){
|
||
|
out = target[action].apply(target, todo.args ? todo.args : []);
|
||
|
}
|
||
|
// google.maps.Map direct function : no result so not rewrited, directly wrapped using array "args" as parameters (ie. setOptions, addMapType, ...)
|
||
|
} else if (map){
|
||
|
if (ok = (typeof(map[action]) === 'function')){
|
||
|
out = map[action].apply(map, todo.args ? todo.args : [] );
|
||
|
}
|
||
|
}
|
||
|
if (!ok && _default.verbose) {
|
||
|
alert("unknown action : " + action);
|
||
|
}
|
||
|
this._callback(out, todo);
|
||
|
this._end();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* returns the geographical coordinates from an address and call internal or given method
|
||
|
**/
|
||
|
this._resolveLatLng = function(todo, method, all, attempt){
|
||
|
var address = ival(todo, 'address'),
|
||
|
params,
|
||
|
that = this,
|
||
|
fnc = typeof(method) === 'function' ? method : that[method];
|
||
|
if ( address ){
|
||
|
if (!attempt){ // convert undefined to int
|
||
|
attempt = 0;
|
||
|
}
|
||
|
if (typeof(address) === 'object'){
|
||
|
params = address;
|
||
|
} else {
|
||
|
params = {'address': address};
|
||
|
}
|
||
|
getGeocoder().geocode(
|
||
|
params,
|
||
|
function(results, status) {
|
||
|
if (status === google.maps.GeocoderStatus.OK){
|
||
|
fnc.apply(that, [todo, all ? results : results[0].geometry.location]);
|
||
|
} else if ( (status === google.maps.GeocoderStatus.OVER_QUERY_LIMIT) && (attempt < _default.queryLimit.attempt) ){
|
||
|
setTimeout(function(){
|
||
|
that._resolveLatLng(todo, method, all, attempt+1);
|
||
|
},
|
||
|
_default.queryLimit.delay + Math.floor(Math.random() * _default.queryLimit.random)
|
||
|
);
|
||
|
} else {
|
||
|
if (_default.verbose){
|
||
|
alert('Geocode error : ' + status);
|
||
|
}
|
||
|
fnc.apply(that, [todo, false]);;
|
||
|
}
|
||
|
}
|
||
|
);
|
||
|
} else {
|
||
|
fnc.apply(that, [todo, toLatLng(todo, false, true)]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* returns the geographical coordinates from an array of object using "address" and call internal method
|
||
|
**/
|
||
|
this._resolveAllLatLng = function(todo, property, method){
|
||
|
var that = this,
|
||
|
i = -1,
|
||
|
solveNext = function(){
|
||
|
do{
|
||
|
i++;
|
||
|
}while( (i < todo[property].length) && !('address' in todo[property][i]) );
|
||
|
if (i < todo[property].length){
|
||
|
(function(todo){
|
||
|
that._resolveLatLng(
|
||
|
todo,
|
||
|
function(todo, latLng){
|
||
|
todo.latLng = latLng;
|
||
|
solveNext.apply(that, []); // solve next or execute exit method
|
||
|
}
|
||
|
);
|
||
|
})(todo[property][i]);
|
||
|
} else {
|
||
|
that[method](todo);
|
||
|
}
|
||
|
};
|
||
|
solveNext();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* call a function of framework or google map object of the instance
|
||
|
**/
|
||
|
this._call = function(/* fncName [, ...] */){
|
||
|
var i, fname = arguments[0], args = [];
|
||
|
if ( !arguments.length || !map || (typeof(map[fname]) !== 'function') ){
|
||
|
return;
|
||
|
}
|
||
|
for(i=1; i<arguments.length; i++){
|
||
|
args.push(arguments[i]);
|
||
|
}
|
||
|
return map[fname].apply(map, args);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* init if not and manage map subcall (zoom, center)
|
||
|
**/
|
||
|
this._subcall = function(todo, latLng){
|
||
|
var opts = {};
|
||
|
if (!todo.map) return;
|
||
|
if (!latLng) {
|
||
|
latLng = ival(todo.map, 'latlng');
|
||
|
}
|
||
|
if (!map){
|
||
|
if (latLng) {
|
||
|
opts = {center:latLng};
|
||
|
}
|
||
|
this.init($.extend({}, todo.map, opts), true);
|
||
|
} else {
|
||
|
if (todo.map.center && latLng){
|
||
|
this._call("setCenter", latLng);
|
||
|
}
|
||
|
if (todo.map.zoom !== undefined){
|
||
|
this._call("setZoom", todo.map.zoom);
|
||
|
}
|
||
|
if (todo.map.mapTypeId !== undefined){
|
||
|
this._call("setMapTypeId", todo.map.mapTypeId);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* attach an event to a sender
|
||
|
**/
|
||
|
this._attachEvent = function(sender, name, fnc, data, once){
|
||
|
google.maps.event['addListener'+(once?'Once':'')](sender, name, function(event) {
|
||
|
fnc.apply($this, [sender, event, data]);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* attach events from a container to a sender
|
||
|
* todo[
|
||
|
* events => { eventName => function, }
|
||
|
* onces => { eventName => function, }
|
||
|
* data => mixed data
|
||
|
* ]
|
||
|
**/
|
||
|
this._attachEvents = function(sender, todo){
|
||
|
var name;
|
||
|
if (!todo) {
|
||
|
return
|
||
|
}
|
||
|
if (todo.events){
|
||
|
for(name in todo.events){
|
||
|
if (typeof(todo.events[name]) === 'function'){
|
||
|
this._attachEvent(sender, name, todo.events[name], todo.data, false);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (todo.onces){
|
||
|
for(name in todo.onces){
|
||
|
if (typeof(todo.onces[name]) === 'function'){
|
||
|
this._attachEvent(sender, name, todo.onces[name], todo.data, true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* execute callback functions
|
||
|
**/
|
||
|
this._callback = function(result, todo){
|
||
|
if (typeof(todo.callback) === 'function') {
|
||
|
todo.callback.apply($this, [result]);
|
||
|
} else if (typeof(todo.callback) === 'object') {
|
||
|
for(var i=0; i<todo.callback.length; i++){
|
||
|
if (typeof(todo.callback[i]) === 'function') {
|
||
|
todo.callback[k].apply($this, [result]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* execute ending functions
|
||
|
**/
|
||
|
this._manageEnd = function(result, todo, internal){
|
||
|
var i, apply;
|
||
|
if (result && (typeof(result) === 'object')){
|
||
|
// attach events
|
||
|
this._attachEvents(result, todo);
|
||
|
// execute "apply"
|
||
|
if (todo.apply && todo.apply.length){
|
||
|
for(i=0; i<todo.apply.length; i++){
|
||
|
apply = todo.apply[i];
|
||
|
// need an existing "action" function in the result object
|
||
|
if(!apply.action || (typeof(result[apply.action]) !== 'function') ) {
|
||
|
continue;
|
||
|
}
|
||
|
if (apply.args) {
|
||
|
result[apply.action].apply(result, apply.args);
|
||
|
} else {
|
||
|
result[apply.action]();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (!internal) {
|
||
|
this._callback(result, todo);
|
||
|
this._end();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------//
|
||
|
// gmap3 functions
|
||
|
//-----------------------------------------------------------------------//
|
||
|
|
||
|
/**
|
||
|
* destroy an existing instance
|
||
|
**/
|
||
|
this.destroy = function(todo){
|
||
|
var k;
|
||
|
store.clear();
|
||
|
$this.empty();
|
||
|
for(k in styles){
|
||
|
delete styles[ k ];
|
||
|
}
|
||
|
styles = {};
|
||
|
if (map){
|
||
|
delete map;
|
||
|
}
|
||
|
this._callback(null, todo);
|
||
|
this._end();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialize google.maps.Map object
|
||
|
**/
|
||
|
this.init = function(todo, internal){
|
||
|
var o, k, opts;
|
||
|
if (map) { // already initialized
|
||
|
return this._end();
|
||
|
}
|
||
|
|
||
|
o = getObject('map', todo);
|
||
|
if ( (typeof(o.options.center) === 'boolean') && o.options.center) {
|
||
|
return false; // wait for an address resolution
|
||
|
}
|
||
|
opts = $.extend({}, _default.init, o.options);
|
||
|
if (!opts.center) {
|
||
|
opts.center = [_default.init.center.lat, _default.init.center.lng];
|
||
|
}
|
||
|
opts.center = toLatLng(opts.center);
|
||
|
map = new _default.classes.Map($this.get(0), opts);
|
||
|
|
||
|
// add previous added styles
|
||
|
for(k in styles) {
|
||
|
map.mapTypes.set(k, styles[k]);
|
||
|
}
|
||
|
|
||
|
this._manageEnd(map, o, internal);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* returns the geographical coordinates from an address
|
||
|
**/
|
||
|
this.getlatlng = function(todo){
|
||
|
this._resolveLatLng(todo, '_getLatLng', true);
|
||
|
},
|
||
|
|
||
|
this._getLatLng = function(todo, results){
|
||
|
this._manageEnd(results, todo);
|
||
|
},
|
||
|
|
||
|
|
||
|
/**
|
||
|
* returns address from latlng
|
||
|
**/
|
||
|
this.getaddress = function(todo, attempt){
|
||
|
var latLng = toLatLng(todo, false, true),
|
||
|
address = ival(todo, 'address'),
|
||
|
params = latLng ? {latLng:latLng} : ( address ? (typeof(address) === 'string' ? {address:address} : address) : null),
|
||
|
callback = ival(todo, 'callback'),
|
||
|
that = this;
|
||
|
if (!attempt){ // convert undefined to int
|
||
|
attempt = 0;
|
||
|
}
|
||
|
if (params && typeof(callback) === 'function') {
|
||
|
getGeocoder().geocode(
|
||
|
params,
|
||
|
function(results, status) {
|
||
|
if ( (status === google.maps.GeocoderStatus.OVER_QUERY_LIMIT) && (attempt < _default.queryLimit.attempt) ){
|
||
|
setTimeout(function(){
|
||
|
that.getaddress(todo, attempt+1);
|
||
|
},
|
||
|
_default.queryLimit.delay + Math.floor(Math.random() * _default.queryLimit.random)
|
||
|
);
|
||
|
} else {
|
||
|
var out = status === google.maps.GeocoderStatus.OK ? results : false;
|
||
|
callback.apply($this, [out, status]);
|
||
|
if (!out && _default.verbose){
|
||
|
alert('Geocode error : ' + status);
|
||
|
}
|
||
|
that._end();
|
||
|
}
|
||
|
}
|
||
|
);
|
||
|
} else {
|
||
|
this._end();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* return a route
|
||
|
**/
|
||
|
this.getroute = function(todo){
|
||
|
var callback = ival(todo, 'callback'),
|
||
|
that = this;
|
||
|
if ( (typeof(callback) === 'function') && todo.options ) {
|
||
|
todo.options.origin = toLatLng(todo.options.origin, true);
|
||
|
todo.options.destination = toLatLng(todo.options.destination, true);
|
||
|
getDirectionsService().route(
|
||
|
todo.options,
|
||
|
function(results, status) {
|
||
|
var out = status == google.maps.DirectionsStatus.OK ? results : false;
|
||
|
callback.apply($this, [out, status]);
|
||
|
that._end();
|
||
|
}
|
||
|
);
|
||
|
} else {
|
||
|
this._end();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* return the elevation of a location
|
||
|
**/
|
||
|
this.getelevation = function(todo){
|
||
|
var fnc, path, samples, i,
|
||
|
locations = [],
|
||
|
callback = ival(todo, 'callback'),
|
||
|
latLng = ival(todo, 'latlng'),
|
||
|
that = this;
|
||
|
|
||
|
if (typeof(callback) === 'function'){
|
||
|
fnc = function(results, status){
|
||
|
var out = status === google.maps.ElevationStatus.OK ? results : false;
|
||
|
callback.apply($this, [out, status]);
|
||
|
that._end();
|
||
|
};
|
||
|
if (latLng){
|
||
|
locations.push(toLatLng(latLng));
|
||
|
} else {
|
||
|
locations = ival(todo, 'locations') || [];
|
||
|
if (locations){
|
||
|
locations = array(locations);
|
||
|
for(i=0; i<locations.length; i++){
|
||
|
locations[i] = toLatLng(locations[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (locations.length){
|
||
|
getElevationService().getElevationForLocations({locations:locations}, fnc);
|
||
|
} else {
|
||
|
path = ival(todo, 'path');
|
||
|
samples = ival(todo, 'samples');
|
||
|
if (path && samples){
|
||
|
for(i=0; i<path.length; i++){
|
||
|
locations.push(toLatLng(path[i]));
|
||
|
}
|
||
|
if (locations.length){
|
||
|
getElevationService().getElevationAlongPath({path:locations, samples:samples}, fnc);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
this._end();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* return the distance between an origin and a destination
|
||
|
*
|
||
|
**/
|
||
|
this.getdistance = function(todo){
|
||
|
var i,
|
||
|
callback = ival(todo, 'callback'),
|
||
|
that = this;
|
||
|
if ( (typeof(callback) === 'function') && todo.options && todo.options.origins && todo.options.destinations ) {
|
||
|
// origins and destinations are array containing one or more address strings and/or google.maps.LatLng objects
|
||
|
todo.options.origins = array(todo.options.origins);
|
||
|
for(i=0; i<todo.options.origins.length; i++){
|
||
|
todo.options.origins[i] = toLatLng(todo.options.origins[i], true);
|
||
|
}
|
||
|
todo.options.destinations = array(todo.options.destinations);
|
||
|
for(i=0; i<todo.options.destinations.length; i++){
|
||
|
todo.options.destinations[i] = toLatLng(todo.options.destinations[i], true);
|
||
|
}
|
||
|
getDistanceMatrixService().getDistanceMatrix(
|
||
|
todo.options,
|
||
|
function(results, status) {
|
||
|
var out = status == google.maps.DistanceMatrixStatus.OK ? results : false;
|
||
|
callback.apply($this, [out, status]);
|
||
|
that._end();
|
||
|
}
|
||
|
);
|
||
|
} else {
|
||
|
this._end();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add a marker to a map after address resolution
|
||
|
* if [infowindow] add an infowindow attached to the marker
|
||
|
**/
|
||
|
this.addmarker = function(todo){
|
||
|
this._resolveLatLng(todo, '_addMarker');
|
||
|
}
|
||
|
|
||
|
this._addMarker = function(todo, latLng, internal){
|
||
|
var result, oi, to,
|
||
|
o = getObject('marker', todo, 'to');
|
||
|
if (!internal){
|
||
|
if (!latLng) {
|
||
|
this._manageEnd(false, o);
|
||
|
return;
|
||
|
}
|
||
|
this._subcall(todo, latLng);
|
||
|
} else if (!latLng){
|
||
|
return;
|
||
|
}
|
||
|
if (o.to){
|
||
|
to = store.refToObj(o.to);
|
||
|
result = to && (typeof(to.add) === 'function');
|
||
|
if (result){
|
||
|
to.add(latLng, todo);
|
||
|
if (typeof(to.redraw) === 'function'){
|
||
|
to.redraw();
|
||
|
}
|
||
|
}
|
||
|
if (!internal){
|
||
|
this._manageEnd(result, o);
|
||
|
}
|
||
|
} else {
|
||
|
o.options.position = latLng;
|
||
|
o.options.map = map;
|
||
|
result = new _default.classes.Marker(o.options);
|
||
|
if (hasKey(todo, 'infowindow')){
|
||
|
oi = getObject('infowindow', todo['infowindow'], 'open');
|
||
|
// if "open" is not defined, add it in first position
|
||
|
if ( (oi.open === undefined) || oi.open ){
|
||
|
oi.apply = array(oi.apply);
|
||
|
oi.apply.unshift({action:'open', args:[map, result]});
|
||
|
}
|
||
|
oi.action = 'addinfowindow';
|
||
|
this._planNext(oi);
|
||
|
}
|
||
|
if (!internal){
|
||
|
store.add('marker', result, o);
|
||
|
this._manageEnd(result, o);
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* add markers (without address resolution)
|
||
|
**/
|
||
|
this.addmarkers = function(todo){
|
||
|
if (ival(todo, 'clusters')){
|
||
|
this._resolveAllLatLng(todo, 'markers', '_addclusteredmarkers');
|
||
|
} else {
|
||
|
this._resolveAllLatLng(todo, 'markers', '_addmarkers');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this._addmarkers = function(todo){
|
||
|
var result, o, i, latLng, marker, options = {}, tmp, to,
|
||
|
markers = ival(todo, 'markers');
|
||
|
this._subcall(todo);
|
||
|
if (typeof(markers) !== 'object') {
|
||
|
return this._end();
|
||
|
}
|
||
|
o = getObject('marker', todo, ['to', 'markers']);
|
||
|
|
||
|
if (o.to){
|
||
|
to = store.refToObj(o.to);
|
||
|
result = to && (typeof(to.add) === 'function');
|
||
|
if (result){
|
||
|
for(i=0; i<markers.length; i++){
|
||
|
if (latLng = toLatLng(markers[i])) {
|
||
|
to.add(latLng, markers[i]);
|
||
|
}
|
||
|
}
|
||
|
if (typeof(to.redraw) === 'function'){
|
||
|
to.redraw();
|
||
|
}
|
||
|
}
|
||
|
this._manageEnd(result, o);
|
||
|
} else {
|
||
|
$.extend(true, options, o.options);
|
||
|
options.map = map;
|
||
|
result = [];
|
||
|
for(i=0; i<markers.length; i++){
|
||
|
if (latLng = toLatLng(markers[i])){
|
||
|
if (markers[i].options){
|
||
|
tmp = {};
|
||
|
$.extend(true, tmp, options, markers[i].options);
|
||
|
o.options = tmp;
|
||
|
} else {
|
||
|
o.options = options;
|
||
|
}
|
||
|
o.options.position = latLng;
|
||
|
marker = new _default.classes.Marker(o.options);
|
||
|
result.push(marker);
|
||
|
o.data = markers[i].data;
|
||
|
o.tag = markers[i].tag;
|
||
|
store.add('marker', marker, o);
|
||
|
this._manageEnd(marker, o, true);
|
||
|
}
|
||
|
}
|
||
|
o.options = options; // restore previous for futur use
|
||
|
this._callback(result, todo);
|
||
|
this._end();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this._addclusteredmarkers = function(todo){
|
||
|
var clusterer, i, latLng, storeId,
|
||
|
that = this,
|
||
|
radius = ival(todo, 'radius'),
|
||
|
maxZoom = ival(todo, 'maxZoom'),
|
||
|
markers = ival(todo, 'markers'),
|
||
|
styles = ival(todo, 'clusters');
|
||
|
|
||
|
if (!map.getBounds()){ // map not initialised => bounds not available
|
||
|
// wait for map
|
||
|
google.maps.event.addListenerOnce(
|
||
|
map,
|
||
|
'bounds_changed',
|
||
|
function() {
|
||
|
that._addclusteredmarkers(todo);
|
||
|
}
|
||
|
);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (typeof(radius) === 'number'){
|
||
|
clusterer = new Clusterer();
|
||
|
for(i=0 ; i<markers.length; i++){
|
||
|
latLng = toLatLng(markers[i]);
|
||
|
clusterer.add(latLng, markers[i]);
|
||
|
}
|
||
|
storeId = this._initClusters(todo, clusterer, radius, maxZoom, styles);
|
||
|
}
|
||
|
|
||
|
this._callback(storeId, todo);
|
||
|
this._end();
|
||
|
}
|
||
|
|
||
|
|
||
|
this._initClusters = function(todo, clusterer, radius, maxZoom, styles){
|
||
|
var that = this;
|
||
|
|
||
|
clusterer.setRedraw(function(force){
|
||
|
var same, clusters = clusterer.clusters(map, radius, maxZoom, force);
|
||
|
if (clusters){
|
||
|
same = clusterer.freeDiff(clusters);
|
||
|
that._displayClusters(todo, clusterer, clusters, same, styles);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
clusterer.events(
|
||
|
google.maps.event.addListener(
|
||
|
map,
|
||
|
'zoom_changed',
|
||
|
function() {
|
||
|
clusterer.redraw(true);
|
||
|
}
|
||
|
),
|
||
|
google.maps.event.addListener(
|
||
|
map,
|
||
|
'bounds_changed',
|
||
|
function() {
|
||
|
clusterer.redraw();
|
||
|
}
|
||
|
)
|
||
|
);
|
||
|
|
||
|
clusterer.redraw();
|
||
|
return store.add('cluster', clusterer, todo);
|
||
|
}
|
||
|
|
||
|
this._displayClusters = function(todo, clusterer, clusters, same, styles){
|
||
|
var k, i, ii, m, done, obj, shadow, cluster, options, tmp, w, h,
|
||
|
atodo,
|
||
|
ctodo = hasKey(todo, 'cluster') ? getObject('', ival(todo, 'cluster')) : {},
|
||
|
mtodo = hasKey(todo, 'marker') ? getObject('', ival(todo, 'marker')) : {};
|
||
|
for(i=0; i<clusters.length; i++){
|
||
|
if (i in same){
|
||
|
continue;
|
||
|
}
|
||
|
cluster = clusters[i];
|
||
|
done = false;
|
||
|
if (cluster.idx.length > 1){
|
||
|
// look for the cluster design to use
|
||
|
m = 0;
|
||
|
for(k in styles){
|
||
|
if ( (k > m) && (k <= cluster.idx.length) ){
|
||
|
m = k;
|
||
|
}
|
||
|
}
|
||
|
if (styles[m]){ // cluster defined for the current markers count
|
||
|
w = ival(styles[m], 'width');
|
||
|
h = ival(styles[m], 'height');
|
||
|
|
||
|
// create a custom _addOverlay command
|
||
|
atodo = {};
|
||
|
$.extend(
|
||
|
true,
|
||
|
atodo,
|
||
|
ctodo,
|
||
|
{ options:{
|
||
|
pane: 'overlayLayer',
|
||
|
content:styles[m].content.replace('CLUSTER_COUNT', cluster.idx.length),
|
||
|
offset:{
|
||
|
x: -w/2,
|
||
|
y: -h/2
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
);
|
||
|
obj = this._addOverlay(atodo, toLatLng(cluster), true);
|
||
|
atodo.options.pane = 'floatShadow';
|
||
|
atodo.options.content = $('<div></div>');
|
||
|
atodo.options.content.width(w);
|
||
|
atodo.options.content.height(h);
|
||
|
shadow = this._addOverlay(atodo, toLatLng(cluster), true);
|
||
|
|
||
|
// store data to the clusterer
|
||
|
ctodo.data = {
|
||
|
latLng: toLatLng(cluster),
|
||
|
markers:[]
|
||
|
};
|
||
|
for(ii=0; ii<cluster.idx.length; ii++){
|
||
|
ctodo.data.markers.push(
|
||
|
clusterer.get(cluster.idx[ii]).marker
|
||
|
);
|
||
|
}
|
||
|
this._attachEvents(shadow, ctodo);
|
||
|
clusterer.store(cluster, obj, shadow);
|
||
|
done = true;
|
||
|
}
|
||
|
}
|
||
|
if (!done){ // cluster not defined (< min count) or = 1 so display all markers of the current cluster
|
||
|
// save the defaults options for the markers
|
||
|
options = {};
|
||
|
$.extend(true, options, mtodo.options);
|
||
|
for(ii = 0; ii <cluster.idx.length; ii++){
|
||
|
m = clusterer.get(cluster.idx[ii]);
|
||
|
mtodo.latLng = m.latLng;
|
||
|
mtodo.data = m.marker.data;
|
||
|
mtodo.tag = m.marker.tag;
|
||
|
if (m.marker.options){
|
||
|
tmp = {};
|
||
|
$.extend(true, tmp, options, m.marker.options);
|
||
|
mtodo.options = tmp;
|
||
|
} else {
|
||
|
mtodo.options = options;
|
||
|
}
|
||
|
obj = this._addMarker(mtodo, mtodo.latLng, true);
|
||
|
this._attachEvents(obj, mtodo);
|
||
|
clusterer.store(cluster, obj);
|
||
|
}
|
||
|
mtodo.options = options; // restore previous for futur use
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* add an infowindow after address resolution
|
||
|
**/
|
||
|
this.addinfowindow = function(todo){
|
||
|
this._resolveLatLng(todo, '_addInfoWindow');
|
||
|
}
|
||
|
|
||
|
this._addInfoWindow = function(todo, latLng){
|
||
|
var o, infowindow, args = [];
|
||
|
this._subcall(todo, latLng);
|
||
|
o = getObject('infowindow', todo, ['open', 'anchor']);
|
||
|
if (latLng) {
|
||
|
o.options.position = latLng;
|
||
|
}
|
||
|
infowindow = new _default.classes.InfoWindow(o.options);
|
||
|
if ( (o.open === undefined) || o.open ){
|
||
|
o.apply = array(o.apply);
|
||
|
args.push(map);
|
||
|
if (o.anchor){
|
||
|
args.push(o.anchor);
|
||
|
}
|
||
|
o.apply.unshift({action:'open', args:args});
|
||
|
}
|
||
|
store.add('infowindow', infowindow, o);
|
||
|
this._manageEnd(infowindow, o);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* add a polygone / polylin on a map
|
||
|
**/
|
||
|
this.addpolyline = function(todo){
|
||
|
this._addPoly(todo, 'Polyline', 'path');
|
||
|
}
|
||
|
|
||
|
this.addpolygon = function(todo){
|
||
|
this._addPoly(todo, 'Polygon', 'paths');
|
||
|
}
|
||
|
|
||
|
this._addPoly = function(todo, poly, path){
|
||
|
var i,
|
||
|
obj, latLng,
|
||
|
o = getObject(poly.toLowerCase(), todo, path);
|
||
|
if (o[path]){
|
||
|
o.options[path] = [];
|
||
|
for(i=0; i<o[path].length; i++){
|
||
|
if (latLng = toLatLng(o[path][i])){
|
||
|
o.options[path].push(latLng);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
obj = new google.maps[poly](o.options);
|
||
|
obj.setMap(map);
|
||
|
store.add(poly.toLowerCase(), obj, o);
|
||
|
this._manageEnd(obj, o);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* add a circle
|
||
|
**/
|
||
|
this.addcircle = function(todo){
|
||
|
this._resolveLatLng(todo, '_addCircle');
|
||
|
}
|
||
|
|
||
|
this._addCircle = function(todo, latLng){
|
||
|
var c, o = getObject('circle', todo);
|
||
|
if (!latLng) {
|
||
|
latLng = toLatLng(o.options.center);
|
||
|
}
|
||
|
if (!latLng) {
|
||
|
return this._manageEnd(false, o);
|
||
|
}
|
||
|
this._subcall(todo, latLng);
|
||
|
o.options.center = latLng;
|
||
|
o.options.map = map;
|
||
|
c = new _default.classes.Circle(o.options);
|
||
|
store.add('circle', c, o);
|
||
|
this._manageEnd(c, o);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* add a rectangle
|
||
|
**/
|
||
|
this.addrectangle = function(todo){
|
||
|
this._resolveLatLng(todo, '_addRectangle');
|
||
|
}
|
||
|
|
||
|
this._addRectangle = function(todo, latLng ){
|
||
|
var r, o = getObject('rectangle', todo);
|
||
|
o.options.bounds = toLatLngBounds(o.options.bounds, true);
|
||
|
if (!o.options.bounds) {
|
||
|
return this._manageEnd(false, o);
|
||
|
}
|
||
|
this._subcall(todo, o.options.bounds.getCenter());
|
||
|
o.options.map = map;
|
||
|
r = new _default.classes.Rectangle(o.options);
|
||
|
store.add('rectangle', r, o);
|
||
|
this._manageEnd(r, o);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* add an overlay to a map after address resolution
|
||
|
**/
|
||
|
this.addoverlay = function(todo){
|
||
|
this._resolveLatLng(todo, '_addOverlay');
|
||
|
}
|
||
|
|
||
|
this._addOverlay = function(todo, latLng, internal){
|
||
|
var ov,
|
||
|
o = getObject('overlay', todo),
|
||
|
opts = $.extend({
|
||
|
pane: 'floatPane',
|
||
|
content: '',
|
||
|
offset:{
|
||
|
x:0,y:0
|
||
|
}
|
||
|
},
|
||
|
o.options),
|
||
|
$div = $('<div></div>'),
|
||
|
listeners = [];
|
||
|
|
||
|
$div
|
||
|
.css('border', 'none')
|
||
|
.css('borderWidth', '0px')
|
||
|
.css('position', 'absolute');
|
||
|
$div.append(opts.content);
|
||
|
|
||
|
function f() {
|
||
|
_default.classes.OverlayView.call(this);
|
||
|
this.setMap(map);
|
||
|
}
|
||
|
|
||
|
f.prototype = new _default.classes.OverlayView();
|
||
|
|
||
|
f.prototype.onAdd = function() {
|
||
|
var panes = this.getPanes();
|
||
|
if (opts.pane in panes) {
|
||
|
$(panes[opts.pane]).append($div);
|
||
|
}
|
||
|
}
|
||
|
f.prototype.draw = function() {
|
||
|
var overlayProjection = this.getProjection(),
|
||
|
ps = overlayProjection.fromLatLngToDivPixel(latLng),
|
||
|
that = this;
|
||
|
|
||
|
$div
|
||
|
.css('left', (ps.x+opts.offset.x) + 'px')
|
||
|
.css('top' , (ps.y+opts.offset.y) + 'px');
|
||
|
|
||
|
$.each( ("dblclick click mouseover mousemove mouseout mouseup mousedown").split(" "), function( i, name ) {
|
||
|
listeners.push(
|
||
|
google.maps.event.addDomListener($div[0], name, function(e) {
|
||
|
google.maps.event.trigger(that, name);
|
||
|
})
|
||
|
);
|
||
|
});
|
||
|
listeners.push(
|
||
|
google.maps.event.addDomListener($div[0], "contextmenu", function(e) {
|
||
|
google.maps.event.trigger(that, "rightclick");
|
||
|
})
|
||
|
);
|
||
|
}
|
||
|
f.prototype.onRemove = function() {
|
||
|
for (var i = 0; i < listeners.length; i++) {
|
||
|
google.maps.event.removeListener(listeners[i]);
|
||
|
}
|
||
|
$div.remove();
|
||
|
}
|
||
|
f.prototype.hide = function() {
|
||
|
$div.hide();
|
||
|
}
|
||
|
f.prototype.show = function() {
|
||
|
$div.show();
|
||
|
}
|
||
|
f.prototype.toggle = function() {
|
||
|
if ($div) {
|
||
|
if ($div.is(':visible')){
|
||
|
this.show();
|
||
|
} else {
|
||
|
this.hide();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
f.prototype.toggleDOM = function() {
|
||
|
if (this.getMap()) {
|
||
|
this.setMap(null);
|
||
|
} else {
|
||
|
this.setMap(map);
|
||
|
}
|
||
|
}
|
||
|
f.prototype.getDOMElement = function() {
|
||
|
return $div[0];
|
||
|
}
|
||
|
ov = new f();
|
||
|
if (!internal){
|
||
|
store.add('overlay', ov, o);
|
||
|
this._manageEnd(ov, o);
|
||
|
}
|
||
|
return ov;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* add a fix panel to a map
|
||
|
**/
|
||
|
this.addfixpanel = function(todo){
|
||
|
var o = getObject('fixpanel', todo),
|
||
|
x=y=0, $c, $div;
|
||
|
if (o.options.content){
|
||
|
$c = $(o.options.content);
|
||
|
|
||
|
if (o.options.left !== undefined){
|
||
|
x = o.options.left;
|
||
|
} else if (o.options.right !== undefined){
|
||
|
x = $this.width() - $c.width() - o.options.right;
|
||
|
} else if (o.options.center){
|
||
|
x = ($this.width() - $c.width()) / 2;
|
||
|
}
|
||
|
|
||
|
if (o.options.top !== undefined){
|
||
|
y = o.options.top;
|
||
|
} else if (o.options.bottom !== undefined){
|
||
|
y = $this.height() - $c.height() - o.options.bottom;
|
||
|
} else if (o.options.middle){
|
||
|
y = ($this.height() - $c.height()) / 2
|
||
|
}
|
||
|
|
||
|
$div = $('<div></div>')
|
||
|
.css('position', 'absolute')
|
||
|
.css('top', y+'px')
|
||
|
.css('left', x+'px')
|
||
|
.css('z-index', '1000')
|
||
|
.append($c);
|
||
|
|
||
|
$this.first().prepend($div);
|
||
|
this._attachEvents(map, o);
|
||
|
store.add('fixpanel', $div, o);
|
||
|
this._callback($div, o);
|
||
|
}
|
||
|
this._end();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* add a direction renderer to a map
|
||
|
**/
|
||
|
this.adddirectionsrenderer = function(todo, internal){
|
||
|
var dr, o = getObject('directionrenderer', todo, 'panelId');
|
||
|
o.options.map = map;
|
||
|
dr = new google.maps.DirectionsRenderer(o.options);
|
||
|
if (o.panelId) {
|
||
|
dr.setPanel(document.getElementById(o.panelId));
|
||
|
}
|
||
|
store.add('directionrenderer', dr, o);
|
||
|
this._manageEnd(dr, o, internal);
|
||
|
return dr;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* set a direction panel to a dom element from its ID
|
||
|
**/
|
||
|
this.setdirectionspanel = function(todo){
|
||
|
var dr = store.get('directionrenderer'),
|
||
|
o = getObject('directionpanel', todo, 'id');
|
||
|
if (dr && o.id) {
|
||
|
dr.setPanel(document.getElementById(o.id));
|
||
|
}
|
||
|
this._manageEnd(dr, o);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* set directions on a map (create Direction Renderer if needed)
|
||
|
**/
|
||
|
this.setdirections = function(todo){
|
||
|
var dr = store.get('directionrenderer'),
|
||
|
o = getObject('directions', todo);
|
||
|
if (todo) {
|
||
|
o.options.directions = todo.directions ? todo.directions : (todo.options && todo.options.directions ? todo.options.directions : null);
|
||
|
}
|
||
|
if (o.options.directions) {
|
||
|
if (!dr) {
|
||
|
dr = this.adddirectionsrenderer(o, true);
|
||
|
} else {
|
||
|
dr.setDirections(o.options.directions);
|
||
|
}
|
||
|
}
|
||
|
this._manageEnd(dr, o);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* set a streetview to a map
|
||
|
**/
|
||
|
this.setstreetview = function(todo){
|
||
|
var panorama,
|
||
|
o = getObject('streetview', todo, 'id');
|
||
|
if (o.options.position){
|
||
|
o.options.position = toLatLng(o.options.position);
|
||
|
}
|
||
|
panorama = new _default.classes.StreetViewPanorama(document.getElementById(o.id),o.options);
|
||
|
if (panorama){
|
||
|
map.setStreetView(panorama);
|
||
|
}
|
||
|
this._manageEnd(panorama, o);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* add a kml layer to a map
|
||
|
**/
|
||
|
this.addkmllayer = function(todo){
|
||
|
var kml,
|
||
|
o = getObject('kmllayer', todo, 'url');
|
||
|
o.options.map = map;
|
||
|
if (typeof(o.url) === 'string'){
|
||
|
kml = new _default.classes.KmlLayer(o.url, o.options);
|
||
|
}
|
||
|
store.add('kmllayer', kml, o);
|
||
|
this._manageEnd(kml, o);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* add a traffic layer to a map
|
||
|
**/
|
||
|
this.addtrafficlayer = function(todo){
|
||
|
var o = getObject('trafficlayer', todo),
|
||
|
tl = store.get('trafficlayer');
|
||
|
if (!tl){
|
||
|
tl = new _default.classes.TrafficLayer();
|
||
|
tl.setMap(map);
|
||
|
store.add('trafficlayer', tl, o);
|
||
|
}
|
||
|
this._manageEnd(tl, o);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* add a bicycling layer to a map
|
||
|
**/
|
||
|
this.addbicyclinglayer = function(todo){
|
||
|
var o = getObject('bicyclinglayer', todo),
|
||
|
bl = store.get('bicyclinglayer');
|
||
|
if (!bl){
|
||
|
bl = new _default.classes.BicyclingLayer();
|
||
|
bl.setMap(map);
|
||
|
store.add('bicyclinglayer', bl, o);
|
||
|
}
|
||
|
this._manageEnd(bl, o);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* add a ground overlay to a map
|
||
|
**/
|
||
|
this.addgroundoverlay = function(todo){
|
||
|
var ov,
|
||
|
o = getObject('groundoverlay', todo, ['bounds', 'url']);
|
||
|
o.bounds = toLatLngBounds(o.bounds);
|
||
|
if (o.bounds && (typeof(o.url) === 'string')){
|
||
|
ov = new _default.classes.GroundOverlay(o.url, o.bounds);
|
||
|
ov.setMap(map);
|
||
|
store.add('groundoverlay', ov, o);
|
||
|
}
|
||
|
this._manageEnd(ov, o);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* geolocalise the user and return a LatLng
|
||
|
**/
|
||
|
this.geolatlng = function(todo){
|
||
|
var callback = ival(todo, 'callback');
|
||
|
if (typeof(callback) === 'function') {
|
||
|
if(navigator.geolocation) {
|
||
|
navigator.geolocation.getCurrentPosition(
|
||
|
function(position) {
|
||
|
var out = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
|
||
|
callback.apply($this, [out]);
|
||
|
},
|
||
|
function() {
|
||
|
var out = false;
|
||
|
callback.apply($this, [out]);
|
||
|
}
|
||
|
);
|
||
|
} else if (google.gears) {
|
||
|
google.gears.factory.create('beta.geolocation').getCurrentPosition(
|
||
|
function(position) {
|
||
|
var out = new google.maps.LatLng(position.latitude,position.longitude);
|
||
|
callback.apply($this, [out]);
|
||
|
},
|
||
|
function() {
|
||
|
out = false;
|
||
|
callback.apply($this, [out]);
|
||
|
}
|
||
|
);
|
||
|
} else {
|
||
|
callback.apply($this, [false]);
|
||
|
}
|
||
|
}
|
||
|
this._end();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* add a style to a map
|
||
|
**/
|
||
|
this.addstyledmap = function(todo, internal){
|
||
|
var o = getObject('styledmap', todo, ['id', 'style']);
|
||
|
if (o.style && o.id && !styles[o.id]) {
|
||
|
styles[o.id] = new _default.classes.StyledMapType(o.style, o.options);
|
||
|
if (map) {
|
||
|
map.mapTypes.set(o.id, styles[o.id]);
|
||
|
}
|
||
|
}
|
||
|
this._manageEnd(styles[o.id], o, internal);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* set a style to a map (add it if needed)
|
||
|
**/
|
||
|
this.setstyledmap = function(todo){
|
||
|
var o = getObject('styledmap', todo, ['id', 'style']);
|
||
|
if (o.id) {
|
||
|
this.addstyledmap(o, true);
|
||
|
if (styles[o.id]) {
|
||
|
map.setMapTypeId(o.id);
|
||
|
this._callback(styles[o.id], todo);
|
||
|
}
|
||
|
}
|
||
|
this._manageEnd(styles[o.id], o);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* remove objects from a map
|
||
|
**/
|
||
|
this.clear = function(todo){
|
||
|
var list = array(ival(todo, 'list') || ival(todo, 'name')),
|
||
|
last = ival(todo, 'last', false),
|
||
|
first = ival(todo, 'first', false),
|
||
|
tag = ival(todo, 'tag');
|
||
|
if (tag !== undefined){
|
||
|
tag = array(tag);
|
||
|
}
|
||
|
store.clear(list, last, first, tag);
|
||
|
this._end();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* return objects previously created
|
||
|
**/
|
||
|
this.get = function(todo){
|
||
|
var name = ival(todo, 'name') || 'map',
|
||
|
first= ival(todo, 'first'),
|
||
|
all = ival(todo, 'all'),
|
||
|
tag = ival(todo, 'tag');
|
||
|
name = name.toLowerCase();
|
||
|
if (name === 'map'){
|
||
|
return map;
|
||
|
}
|
||
|
if (tag !== undefined){
|
||
|
tag = array(tag);
|
||
|
}
|
||
|
if (first){
|
||
|
return store.get(name, false, tag);
|
||
|
} else if (all){
|
||
|
return store.all(name, tag);
|
||
|
} else {
|
||
|
return store.get(name, true, tag);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* return the max zoom of a location
|
||
|
**/
|
||
|
this.getmaxzoom = function(todo){
|
||
|
this._resolveLatLng(todo, '_getMaxZoom');
|
||
|
}
|
||
|
|
||
|
this._getMaxZoom = function(todo, latLng){
|
||
|
var callback = ival(todo, 'callback'),
|
||
|
that = this;
|
||
|
if (callback && typeof(callback) === 'function') {
|
||
|
getMaxZoomService().getMaxZoomAtLatLng(
|
||
|
latLng,
|
||
|
function(result) {
|
||
|
var zoom = result.status === google.maps.MaxZoomStatus.OK ? result.zoom : false;
|
||
|
callback.apply($this, [zoom, result.status]);
|
||
|
that._end();
|
||
|
}
|
||
|
);
|
||
|
} else {
|
||
|
this._end();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* modify default values
|
||
|
**/
|
||
|
this.setdefault = function(todo){
|
||
|
setDefault(todo);
|
||
|
this._end();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* autofit a map using its overlays (markers, rectangles ...)
|
||
|
**/
|
||
|
this.autofit = function(todo, internal){
|
||
|
var names, list, obj, i, j,
|
||
|
empty = true,
|
||
|
bounds = new google.maps.LatLngBounds(),
|
||
|
maxZoom = ival(todo, 'maxZoom', null);
|
||
|
|
||
|
names = store.names();
|
||
|
for(i=0; i<names.length; i++){
|
||
|
list = store.all(names[i]);
|
||
|
for(j=0; j<list.length; j++){
|
||
|
obj = list[j];
|
||
|
if (obj.getPosition){
|
||
|
bounds.extend(obj.getPosition());
|
||
|
empty = false;
|
||
|
} else if (obj.getBounds){
|
||
|
bounds.extend(obj.getBounds().getNorthEast());
|
||
|
bounds.extend(obj.getBounds().getSouthWest());
|
||
|
empty = false;
|
||
|
} else if (obj.getPaths){
|
||
|
obj.getPaths().forEach(function(path){
|
||
|
path.forEach(function(latLng){
|
||
|
bounds.extend(latLng);
|
||
|
empty = false;
|
||
|
});
|
||
|
});
|
||
|
} else if (obj.getPath){
|
||
|
obj.getPath().forEach(function(latLng){
|
||
|
bounds.extend(latLng);
|
||
|
empty = false;
|
||
|
});
|
||
|
} else if (obj.getCenter){
|
||
|
bounds.extend(obj.getCenter());
|
||
|
empty = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!empty && (!map.getBounds() || !map.getBounds().equals(bounds))){
|
||
|
if (maxZoom !== null){
|
||
|
// fitBouds Callback event => detect zoom level and check maxZoom
|
||
|
google.maps.event.addListenerOnce(
|
||
|
map,
|
||
|
'bounds_changed',
|
||
|
function() {
|
||
|
if (this.getZoom() > maxZoom){
|
||
|
this.setZoom(maxZoom);
|
||
|
}
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
map.fitBounds(bounds);
|
||
|
}
|
||
|
if (!internal){
|
||
|
this._manageEnd(empty ? false : bounds, todo, internal);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
//-----------------------------------------------------------------------//
|
||
|
// jQuery plugin
|
||
|
//-----------------------------------------------------------------------//
|
||
|
|
||
|
$.fn.gmap3 = function(){
|
||
|
var i, args, list = [], empty = true, results = [];
|
||
|
// store all arguments in a todo list
|
||
|
for(i=0; i<arguments.length; i++){
|
||
|
args = arguments[i] || {};
|
||
|
// resolve string todo - action without parameters can be simplified as string
|
||
|
if (typeof(args) === 'string'){
|
||
|
args = {action:args};
|
||
|
}
|
||
|
list.push(args);
|
||
|
}
|
||
|
// resolve empty call - run init
|
||
|
if (!list.length) {
|
||
|
list.push({});
|
||
|
}
|
||
|
// loop on each jQuery object
|
||
|
$.each(this, function() {
|
||
|
var $this = $(this),
|
||
|
gmap3 = $this.data('gmap3');
|
||
|
empty = false;
|
||
|
if (!gmap3){
|
||
|
gmap3 = new Gmap3($this);
|
||
|
$this.data('gmap3', gmap3);
|
||
|
}
|
||
|
// direct call : bypass jQuery method (not stackable, return mixed)
|
||
|
if ( (list.length == 1) && (isDirect(list[0])) ){
|
||
|
results.push(gmap3._direct(list[0]));
|
||
|
} else {
|
||
|
gmap3._plan(list);
|
||
|
}
|
||
|
});
|
||
|
// return for direct call (only)
|
||
|
if (results.length){
|
||
|
if (results.length === 1){ // 1 css selector
|
||
|
return results[0];
|
||
|
} else {
|
||
|
return results;
|
||
|
}
|
||
|
}
|
||
|
// manage setDefault call
|
||
|
if (empty && (arguments.length == 2) && (typeof(arguments[0]) === 'string') && (arguments[0].toLowerCase() === 'setdefault')){
|
||
|
setDefault(arguments[1]);
|
||
|
}
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
}(jQuery));
|