Files
PrestaShop/modules/mondialrelay/js/gmap.js
T
2011-08-02 11:43:58 +00:00

2127 lines
61 KiB
JavaScript

/*
* GMAP3 Plugin for JQuery
* Version : 3.4
* Date : July 04, 2011
* 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.init = function (id){
if (!st[id]){
st[id] = [];
}
}
this.empty = function (id){
var i;
if (!st[id]) {
return true;
}
for(i = 0; i < st[id].length; i++){
if (st[id][i]){
return false
}
}
return true;
}
this.add = function (id, v){
this.init(id);
st[id].push(v);
}
this.addNext = function (id, v){
var t=[], i, k = 0;
this.init(id);
for(i = 0; i < st[id].length; i++){
if (!st[id][i]){
continue;
}
if (k == 1) {
t.push(v);
}
t.push(st[id][i]);
k++;
}
if (k < 2) {
t.push(v);
}
st[id] = t;
}
this.get = function (id){
var i;
if (st[id]){
for(i = 0; i < st[id].length; i++){
if (st[id][i]) {
return st[id][i];
}
}
}
return false;
}
this.ack = function (id){
var i;
if (st[id]) {
for(i = 0; i < st[id].length; i++){
if (st[id][i]) {
delete st[id][i];
break;
}
}
if (this.empty(id)){
delete st[id];
}
}
}
}
/***************************************************************************/
/* 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){
stored.push({data:data, obj:obj});
}
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();
}
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, 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();
cnt = 0;
keys = {};
for(i = 0; i < markers.length; i++){
if (!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){
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;
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 */
/***************************************************************************/
var gmap3 = {
_ids:{},
_properties:['events','onces','options','apply', 'callback', 'data', 'tag'],
_default:{
verbose:false,
unit: 'mi',
init:{
mapTypeId : google.maps.MapTypeId.ROADMAP,
center:[46.578498,2.457275],
zoom: 2
}
},
_running:{
},
_stack: new Stack(),
/**
* @desc create default structure if not existing
**/
_init: function($this, id){
if (!this._ids[id]) {
this._ids[id] = {
$this:$this,
styles: {},
stored:{},
map:null
};
}
},
/**
* @desc store actions to do in a stack manager
**/
_plan: function($this, id, list){
var k;
this._init($this, id);
for(k = 0; k < list.length; k++) {
this._stack.add(id, list[k] );
}
this._run(id);
},
/**
* @desc return true if action has to be executed directly
**/
_isDirect: function(id, todo){
var action = this._ival(todo, 'action'),
directs = {
distance :true,
earthradius :true,
get :true
};
return action in directs;
},
/**
* @desc execute action directly
**/
_direct: function(id, todo){
var action = this._ival(todo, 'action');
if (action.substr(0,1) == ':'){
action = action.substr(1);
}
return this[action](id, $.extend({}, action in this._default ? this._default[action] : {}, todo.args ? todo.args : todo));
},
/**
* @desc store one action to do in a stack manager after the first
**/
_planNext: function(id, a){
var $this = this._jObject(id);
this._init($this, id);
this._stack.addNext(id, a);
},
/**
* @desc called when action in finished, to acknoledge the current in stack and start next one
**/
_end: function(id){
delete this._running[id];
this._stack.ack(id);
this._run(id);
},
/**
* @desc if not running, start next action in stack
**/
_run: function(id){
if (this._running[id]) return;
var a = this._stack.get(id);
if (!a) return;
this._running[id] = true;
this._proceed(id, a);
},
_geocoder: null,
_getGeocoder: function(){
if (!this._geocoder) this._geocoder = new google.maps.Geocoder();
return this._geocoder;
},
_directionsService: null,
_getDirectionsService: function(){
if (!this._directionsService) this._directionsService = new google.maps.DirectionsService();
return this._directionsService;
},
_elevationService: null,
_getElevationService: function(){
if (!this._elevationService) this._elevationService = new google.maps.ElevationService();
return this._elevationService;
},
_maxZoomService:null,
_getMaxZoomService: function(){
if (!this._maxZoomService) this._maxZoomService = new google.maps.MaxZoomService();
return this._maxZoomService;
},
_getMap: function( id ){
return this._ids[id].map;
},
_setMap: function (id, map){
this._ids[id].map = map;
},
_jObject: function( id ){
return this._ids[id].$this;
},
_addStyle: function(id, styleId, style){
this._ids[id].styles[ styleId ] = style;
},
_getStyles: function(id){
return this._ids[id].styles;
},
_getStyle: function(id, styleId){
return this._ids[id].styles[ styleId ];
},
_styleExist: function(id, styleId){
return this._ids[id] && this._ids[id].styles[ styleId ];
},
_getDirectionRenderer: function(id){
return this._getStored(id, 'directionrenderer');
},
_exist: function(id){
return this._ids[id].map ? true : false;
},
/**
* @desc return last non-null object
**/
_getStored: function(id, name, last, tag){
if (!this._ids[id].stored[name] || !this._ids[id].stored[name].length){
return null;
}
var t = this._ids[id].stored[name],
i,
idx = last ? t.length : -1,
add = last ? -1 : 1;
for(i=0; i<t.length; i++){
idx += add;
if (t[idx]){
if (tag !== undefined) {
if ( (t[idx].tag === undefined) || ($.inArray(t[idx].tag, tag) < 0) ){
continue;
}
}
return t[idx].obj;
}
}
return null;
},
/**
* @desc return an object from its reference
**/
_getStoredId: function(id, ref){
ref = ref.split('-');
if ((ref.length == 2) && this._ids[id].stored[ref[0]] && this._ids[id].stored[ref[0]][ref[1]]){
return this._ids[id].stored[ref[0]][ref[1]].obj;
}
return null;
},
/**
* @desc add an object in the stored structure
**/
_store: function(id, name, obj, todo){
name = name.toLowerCase();
if (!this._ids[id].stored[name]){
this._ids[id].stored[name] = [];
}
this._ids[id].stored[name].push({obj:obj, tag:this._ival(todo, 'tag')});
return name + '-' + (this._ids[id].stored[name].length-1);
},
/**
* @desc remove an object from the stored structure
**/
_unstore: function(id, name, tag, pop){
var idx, t = this._ids[id].stored[name];
if (!t) return false;
if (tag !== undefined){
if (pop){
for(idx = t.length - 1; idx >= 0; idx--){
if ( (t[idx] !== undefined) && (t[idx].tag !== undefined) && ($.inArray(t[idx].tag, tag) >= 0) ){
break;
}
}
} else {
for(idx = 0; idx < t.length; idx++){
if ( (t[idx] !== undefined) && (t[idx].tag !== undefined) && ($.inArray(t[idx].tag, tag) >= 0) ){
break;
}
}
}
} else {
idx = pop ? t.length - 1 : 0;
}
if ( !(idx in t) ) {
return false;
}
// Google Map element
if (typeof(t[idx].obj.setMap) === 'function') {
t[idx].obj.setMap(null);
}
// JQuery
if (typeof(t[idx].obj.remove) === 'function') {
t[idx].obj.remove();
}
// internal (cluster)
if (typeof(t[idx].obj.free) === 'function') {
t[idx].obj.free();
}
delete t[idx].obj;
if (tag !== undefined){
this._ids[id].stored[name] = this._rmFromArray(t,idx);
} else {
if (pop) {
t.pop();
} else {
t.shift();
}
}
return true;
},
/**
* @desc manage remove objects
**/
_clear: function(id, list, last, first, tag){
var n, i;
if (!list || !list.length){
list = [];
for(k in this._ids[id].stored){
list.push(k);
}
} else {
list = this._array(list);
}
for(i = 0; i < list.length; i++){
if (list[i]){
n = list[i].toLowerCase();
if (!this._ids[id].stored[n]) continue;
if (last){
this._unstore(id, n, tag, true);
} else if (first){
this._unstore(id, n, tag, false);
} else {
while (this._unstore(id, n, tag, false));
}
}
}
},
/**
* @desc return true if "init" action must be run
**/
_autoInit: function(name){
var i,
names = [
'init',
'geolatlng',
'getlatlng',
'getroute',
'getelevation',
'addstyledmap',
'setdefault',
'destroy'
];
if ( !name ) {
return true;
}
for(i = 0; i < names.length; i++){
if (names[i] === name) {
return false;
}
}
return true;
},
/**
* @desc call functions associated
* @param
* id : string
* action : string : function wanted
*
* options : {}
*
* O1 : {}
* O2 : {}
* ...
* On : {}
* => On : option : {}
* action : string : function name
* ... (depending of functions called)
*
* args : [] : parameters for directs call to map
* target? : object : replace map to call function
**/
_proceed: function(id, todo){
todo = todo || {};
var action = this._ival(todo, 'action') || 'init',
iaction = action.toLowerCase(),
ok = true,
target = this._ival(todo, 'target'),
args = this._ival(todo, 'args'),
map, out;
if ( !this._exist(id) && this._autoInit(iaction) ){
this.init(id, $.extend({}, this._default.init, todo.args && todo.args.map ? todo.args.map : todo.map ? todo.map : {}), true);
}
if (!target && !args && (iaction in this) && (typeof(this[iaction]) === 'function')){
// framework functions
this[iaction](id, $.extend({}, iaction in this._default ? this._default[iaction] : {}, todo.args ? todo.args : todo)); // call fnc and extends defaults data
} else {
if (target && (typeof(target) === 'object')){
if (typeof(target[action]) === 'function'){
out = target[action].apply(target, todo.args ? todo.args : []);
} else ok = false;
// gm direct function : no result so not rewrited, directly wrapped using array "args" as parameters (ie. setOptions, addMapType, ...)
} else {
map = this._getMap(id);
if (typeof(map[action]) === 'function'){
out = map[action].apply(map, todo.args ? todo.args : [] );
} else ok = false;
}
if (!ok && this._default.verbose) alert("unknown action : " + action);
this._callback(id, out, todo);
this._end(id);
}
},
/**
* @desc call a function of framework or google map object of the instance
* @param
* id : string : instance
* fncName : string : function name
* ... (depending on function called)
**/
_call: function(/* id, fncName [, ...] */){
if ( (arguments.length < 2) || (!this._exist(arguments[0])) ){
return ;
}
var i, id = arguments[0],
fname = arguments[1],
map = this._getMap(id),
args = [];
if (typeof(map[ fname ]) !== 'function') {
return;
}
for(i=2; i<arguments.length; i++){
args.push(arguments[i]);
}
return map[ fname ].apply( map, args );
},
/**
* @desc convert data to array
**/
_array: function(mixed){
var k, a = [];
if (mixed !== undefined){
if (typeof(mixed) === 'object'){
for(k in mixed) {
a.push(mixed[k]);
}
} else{
a.push(mixed);
}
}
return a;
},
/**
* @desc create a new Array without some entries
**/
_rmFromArray: function(a, key){
var k, r = [];
for(k in a){
if (k != key){
r.push(a[k]);
}
}
return r;
},
/**
* @desc init if not and manage map subcall (zoom, center)
**/
_subcall: function(id, todo, latLng){
var opts = {};
if (!todo.map) return;
if (!latLng) {
latLng = this._ival(todo.map, 'latlng');
}
if (!this._exist(id)){
if (latLng) {
opts = {center:latLng};
}
this.init(id, $.extend({}, todo.map, opts), true);
} else {
if (todo.map.center && latLng) this._call(id, "setCenter", latLng);
if (todo.map.zoom !== undefined) this._call(id, "setZoom", todo.map.zoom);
if (todo.map.mapTypeId !== undefined) this._call(id, "setMapTypeId", todo.map.mapTypeId);
}
},
/**
* @desc attach an event to a sender (once)
**/
_attachEvent: function(id, sender, name, f, data, once){
var that=this, $o = this._jObject(id);
google.maps.event['addListener'+(once?'Once':'')](sender, name, function(event) {
f.apply($o, [sender, event, data]);
});
},
/**
* @desc attach events from a container to a sender
* todo[
* events => { eventName => function, }
* onces => { eventName => function, }
* data => mixed data
* ]
**/
_attachEvents : function(id, sender, todo){
var name;
if (!todo) return
if (todo.events){
for(name in todo.events){
if (typeof(todo.events[name]) === 'function'){
this._attachEvent(id, sender, name, todo.events[name], todo.data, false);
}
}
}
if (todo.onces){
for(name in todo.onces){
if (typeof(todo.onces[name]) === 'function'){
this._attachEvent(id, sender, name, todo.onces[name], todo.data, true);
}
}
}
},
/**
* @desc execute callback functions
**/
_callback: function(mixed, result, todo){
var k, $j;
if (typeof(todo.callback) === 'function') {
$j = typeof(mixed) === 'number' ? this._jObject(mixed) : mixed;
todo.callback.apply($j, [result]);
} else if (typeof(todo.callback) === 'object') {
for(k in todo.callback){
if (!$j) $j = typeof(mixed) === 'number' ? this._jObject(mixed) : mixed;
if (typeof(todo.callback[k]) === 'function') todo.callback[k].apply($j, [result]);
}
}
},
/**
* @desc execute end functions
**/
_manageEnd: function(id, sender, todo, internal){
var k, c;
if (sender && (typeof(sender) === 'object')){
this._attachEvents(id, sender, todo);
for(k in todo.apply){
c = todo.apply[k];
if(!c.action) continue;
if (typeof(sender[c.action]) !== 'function') {
continue;
}
if (c.args) {
sender[c.action].apply(sender, c.args);
} else {
sender[c.action]();
}
}
}
if (!internal) {
this._callback(id, sender, todo);
this._end(id);
}
},
/**
* @desc return true if mixed is usable as number
**/
_isNumeric: function (mixed){
return (typeof(mixed) === 'number' || typeof(mixed) === 'string') && mixed !== '' && !isNaN(mixed);
},
/**
* @desc convert mixed [ lat, lng ] objet by google.maps.LatLng
**/
_latLng: function(mixed, emptyReturnMixed, noFlat){
var k, latLng={}, i=0,
empty = emptyReturnMixed ? mixed : null;
if (!mixed || (typeof(mixed) === 'string')){
return empty;
}
if (mixed.latLng) {
return this._latLng(mixed.latLng);
}
if (typeof(mixed.lat) === 'function') {
return mixed;
} else if ( this._isNumeric(mixed.lat) ) {
return new google.maps.LatLng(mixed.lat, mixed.lng);
} else if ( !noFlat ){
for(k in mixed){
if ( !this._isNumeric(mixed[k]) ) return empty;
latLng[i?'lng':'lat'] = mixed[k];
if (i) break;
i++;
}
if (i) return new google.maps.LatLng(latLng.lat, latLng.lng);
}
return empty;
},
_count: function(mixed){
var k, c = 0;
for(k in mixed) c++;
return c;
},
/**
* @desc convert mixed [ sw, ne ] object by google.maps.LatLngBounds
**/
_latLngBounds: function(mixed, flatAllowed, emptyReturnMixed){
var empty, cnt, ne, sw, k, t, ok, nesw, i;
if (!mixed) {
return null;
}
empty = emptyReturnMixed ? mixed : null;
if (typeof(mixed.getCenter) === 'function') {
return mixed;
}
cnt = this._count(mixed);
if (cnt == 2){
if (mixed.ne && mixed.sw){
ne = this._latLng(mixed.ne);
sw = this._latLng(mixed.sw);
} else {
for(k in mixed){
if (!ne) {
ne = this._latLng(mixed[k]);
} else {
sw = this._latLng(mixed[k]);
}
}
}
if (sw && ne) return new google.maps.LatLngBounds(sw, ne);
return empty;
} else if (cnt == 4){
t = ['n', 'e', 's', 'w'];
ok=true;
for(i in t) ok &= this._isNumeric(mixed[t[i]]);
if (ok) return new google.maps.LatLngBounds(this._latLng([mixed.s, mixed.w]), this._latLng([mixed.n, mixed.e]));
if (flatAllowed){
i=0;
nesw={};
for(k in mixed){
if (!this._isNumeric(mixed[k])) return empty;
nesw[t[i]] = mixed[k];
i++;
}
return new google.maps.LatLngBounds(this._latLng([nesw.s, nesw.w]), this._latLng([nesw.n, nesw.e]));
}
}
return empty;
},
/**
* @desc search an (insensitive) key
**/
_ikey: function(object, key){
if (key.toLowerCase){
key = key.toLowerCase();
for(var k in object){
if (k.toLowerCase && (k.toLowerCase() == key)) return k;
}
}
return false;
},
/**
* @desc search an (insensitive) key
**/
_ival: function(object, key, def){
var k = this._ikey(object, key);
if ( k ) return object[k];
return def;
},
/**
* @desc return true if at least one key is set in object
* nb: keys in lowercase
**/
_hasKey: function(object, keys){
var n, k;
if (!object || !keys) return false;
for(n in object){
if (n.toLowerCase){
n = n.toLowerCase();
for(k in keys){
if (n == keys[k]) return true;
}
}
}
return false;
},
/**
* @desc return a standard object
* nb: include in lowercase
**/
_extractObject: function(todo, include, r){
if (this._hasKey(todo, this._properties) || this._hasKey(todo, include)){
var k, p, ip, r={};
for(k in this._properties){
p=this._properties[k];
ip = this._ikey(todo, p);
r[p] = ip ? todo[ip] : {};
}
for(k in include){
p=include [k];
ip = this._ikey(todo, p);
if (ip) r[p] = todo[ip];
}
return r;
} else {
r.options= {};
for(k in todo){
if (k === 'action') continue;
r.options[k] = todo[k];
}
return r;
}
},
/**
* @desc identify object from object list or parameters list : [ objectName:{data} ] or [ otherObject:{}, ] or [ object properties ]
* nb: include, exclude in lowercase
**/
_object: function(name, todo, include, exclude){
var k = this._ikey(todo, name),
p, r = {}, keys=['map'],
cb='callback';
r[cb] = this._ival(todo, cb);
if (k) return this._extractObject(todo[k], include, r);
for(k in exclude) keys.push(exclude[k]);
if (!this._hasKey(todo, keys)) r = this._extractObject(todo, include, r);
for(k in this._properties){
p=this._properties[k];
if (!r[p]) r[p] = {};
}
return r;
},
/**
* @desc Returns the geographical coordinates from an address and call internal method
**/
_resolveLatLng: function(id, todo, method, all){
var address = this._ival(todo, 'address'),
region, params,
that = this, cb;
if ( address ){
cb = function(results, status) {
if (status == google.maps.GeocoderStatus.OK){
that[method](id, todo, all ? results : results[0].geometry.location);
} else {
if (that._default.verbose){
alert('Geocode error : ' + status);
}
that[method](id, todo, false);
}
};
if (typeof(address) === 'object'){
params = address;
} else {
params = { 'address': address };
region = this._ival(todo, 'region');
if (region){
params.region = region;
}
}
this._getGeocoder().geocode( params, cb );
} else {
this[method](id, todo, this._latLng(todo, false, true));
}
},
/*============================*/
/* PUBLIC */
/*============================*/
/**
* @desc Destroy an existing instance
**/
destroy: function(id, todo){
var k, $j;
if (this._ids[id]){
this._clear(id);
this._ids[id].$this.empty();
if (this._ids[id].bl) delete this._ids[id].bl;
for(k in this._ids[id].styles){
delete this._ids[id].styles[ k ];
}
delete this._ids[id].map;
$j = this._jObject(id);
delete this._ids[id];
this._callback($j, null, todo);
}
this._end(id);
},
/**
* @desc Initialize google map object an attach it to the dom element (using id)
**/
init: function(id, todo, internal){
var o, opts, map, styles, k, $this;
if ( (id == '') || (this._exist(id)) ) return this._end(id);
o = this._object('map', todo);
if ( (typeof(o.options.center) === 'boolean') && o.options.center) {
return false; // wait for an address resolution
}
opts = $.extend({}, this._default.init, o.options);
if (!opts.center) {
opts.center = [this._default.init.center.lat, this._default.init.center.lng];
}
opts.center = this._latLng(opts.center);
$this = this._jObject(id);
this._setMap(id, new google.maps.Map($this.get(0), opts));
map = this._getMap(id);
// add previous added styles
styles = this._getStyles( id );
for(k in styles) map.mapTypes.set(k, styles[k]);
this._manageEnd(id, map, o, internal);
return true;
},
/**
* @desc Returns the geographical coordinates from an address
**/
getlatlng: function(id, todo){
this._resolveLatLng(id, todo, '_getLatLng', true);
},
_getLatLng: function(id, todo, results){
this._manageEnd(id, results, todo);
},
/**
* @desc Return address from latlng
**/
getaddress: function(id, todo){
var callback,
$this = this._jObject(id),
latLng = this._latLng(todo, false, true),
address = this._ival(todo, 'address'),
params = latLng ? {latLng:latLng} : ( address ? (typeof(address) === 'string' ? {address:address} : address) : null),
cb = this._ival(todo, 'callback');
if (params && cb && typeof(cb) === 'function') {
callback = function(results, status) {
var out = status == google.maps.GeocoderStatus.OK ? results : false;
cb.apply($this, [out, status]);
};
this._getGeocoder().geocode(params, callback);
}
this._end(id);
},
/**
* @desc Return a route
**/
getroute: function(id, todo){
var callback,
$this = this._jObject(id),
cb = this._ival(todo, 'callback');
if ( (typeof(cb) === 'function') && todo.options ) {
todo.options.origin = this._latLng(todo.options.origin, true);
todo.options.destination = this._latLng(todo.options.destination, true);
callback = function(results, status) {
var out = status == google.maps.DirectionsStatus.OK ? results : false;
cb.apply($this, [out, status]);
};
this._getDirectionsService().route( todo.options, callback );
}
this._end(id);
},
/**
* @desc return elevation
**/
getelevation: function(id, todo){
var callback, latLng, ls, k, path, samples,
locations = [],
$this = this._jObject(id),
cb = this._ival(todo, 'callback'),
latLng = this._ival(todo, 'latlng');
if (cb && typeof(cb) === 'function') {
callback = function(results, status) {
var out = status == google.maps.ElevationStatus.OK ? results : false;
cb.apply($this, [out, status]);
};
if (latLng){
locations.push( this._latLng(latLng) );
} else {
ls = this._ival(todo, 'locations');
if (ls){
for(k in ls){
locations.push( this._latLng(ls[k]) );
}
}
}
if (locations.length){
this._getElevationService().getElevationForLocations({locations:locations}, callback);
} else {
path = this._ival(todo, 'path');
samples = this._ival(todo, 'samples');
if (path && samples){
for(k in path){
locations.push(this._latLng(path[k]));
}
if (locations.length){
this._getElevationService().getElevationAlongPath( {path:locations, samples:samples}, callback );
}
}
}
}
this._end(id);
},
/**
* @desc Add a marker to a map after address resolution
* if [infowindow] add an infowindow attached to the marker
**/
addmarker: function(id, todo){
this._resolveLatLng(id, todo, '_addMarker');
},
_addMarker: function(id, todo, latLng, internal){
var result, oi, to,
n = 'marker', niw = 'infowindow',
o = this._object(n, todo, ['to']);
if (!internal){
if (!latLng) {
this._manageEnd(id, false, o);
return;
}
this._subcall(id, todo, latLng);
} else if (!latLng){
return;
}
if (o.to){
to = this._getStoredId(id, 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(id, result, o);
}
} else {
o.options.position = latLng;
o.options.map = this._getMap(id);
result = new google.maps.Marker(o.options);
if ( todo[niw] ){
oi = this._object(niw, todo[niw], ['open']);
if ( (oi['open'] === undefined) || oi['open'] ) {
oi.apply = this._array(oi.apply);
oi.apply.unshift({action:'open', args:[this._getMap(id), result]});
}
oi.action = 'add'+niw;
this._planNext(id, oi);
}
if (!internal){
this._store(id, n, result, o);
this._manageEnd(id, result, o);
}
}
return result;
},
/**
* @desc Add markers (without address resolution)
**/
addmarkers: function(id, todo){
if (this._ival(todo, 'clusters')){
this._addclusteredmarkers(id, todo);
} else {
this._addmarkers(id, todo);
}
},
_addmarkers: function(id, todo){
var result, o, k, latLng, marker, options = {}, tmp, to,
n = 'marker',
markers = this._ival(todo, 'markers');
this._subcall(id, todo);
if ( !markers || (typeof(markers) !== 'object') ) {
return this._end(id);
}
o = this._object(n, todo, ['to', 'markers']);
if (o.to){
to = this._getStoredId(id, o.to);
result = to && (typeof(to.add) === 'function');
if (result){
for(k in markers){
latLng = this._latLng(markers[k]);
if (!latLng) continue;
to.add(latLng, markers[k]);
}
if (typeof(to.redraw) === 'function'){
to.redraw();
}
}
this._manageEnd(id, result, o);
} else {
$.extend(true, options, o.options);
options.map = this._getMap(id);
result = [];
for(k in markers){
latLng = this._latLng(markers[k]);
if (!latLng) continue;
if (markers[k].options){
tmp = {};
$.extend(true, tmp, options, markers[k].options);
o.options = tmp;
} else {
o.options = options;
}
o.options.position = latLng;
marker = new google.maps.Marker(o.options);
result.push(marker);
o.data = markers[k].data;
o.tag = markers[k].tag;
this._store(id, n, marker, o);
this._manageEnd(id, marker, o, true);
}
o.options = options; // restore previous for futur use
this._callback(id, result, todo);
this._end(id);
}
},
resize: function(id)
{
google.maps.event.trigger(this._getMap(id), 'resize');
},
getscale: function(id, todo, internal){
var map = this._getMap(id),
zoom = map.getZoom(),
scales = [
77.864462034120315,
45.42785688110077,
16.220730575856892,
6.879509682822463,
3.5034960477802986,
1.8034610362879133,
0.9127181102723314,
0.4598746767146186,
0.23053567913908648,
0.11545247438886701,
0.05775371939320953,
0.02881647975962874,
0.014414070716531697,
0.007207618499622224,
0.003603886381819732,
0.0018015948787526637,
0.0009008246767800296,
0.0004504160086085826,
0.00022520761796505934,
0.00011260535432642145,
0.00005630113180858676
];
scale = scales[zoom];
if (!internal){
this._callback(id, scale, todo);
this._end(id);
}
return scale;
},
_addclusteredmarkers:function(id, todo){
var clusterer, i, latLng, storeId,
that = this,
radius = this._ival(todo, 'radius'),
markers = this._ival(todo, 'markers'),
styles = this._ival(todo, 'clusters');
if (! this._getMap(id).getBounds() ){ // map not initialised => bounds not available
// wait for map
google.maps.event.addListenerOnce(
this._getMap(id),
'bounds_changed',
function() {
that._addclusteredmarkers(id, todo);
}
);
return;
}
if (typeof(radius) === 'number'){
clusterer = new Clusterer();
for (i = 0 ; i < markers.length; i++){
latLng = this._latLng(markers[i]);
clusterer.add(latLng, markers[i]);
}
storeId = this._initClusters(id, todo, clusterer, radius, styles);
}
this._callback(id, storeId, todo);
this._end(id);
},
_initClusters: function(id, todo, clusterer, radius, styles){
var that=this,
map = this._getMap(id);
clusterer.setRedraw(function(force){
var same, clusters = clusterer.clusters(map, radius, force);
if (clusters){
same = clusterer.freeDiff(clusters);
that._displayClusters(id, 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 this._store(id, 'cluster', clusterer, todo);
},
_displayClusters: function(id, todo, clusterer, clusters, same, styles){
var k, i, ii, m, done, obj, cluster, options = {}, tmp,
atodo,
ctodo = this._ival(todo, 'cluster') || {},
mtodo = this._ival(todo, 'marker') || todo;
for(i = 0; i < clusters.length; i++){
if (i in same){
continue;
}
cluster = clusters[i];
done = false;
if (cluster.idx.length > 1){
m = 0;
for(k in styles){
if ( (k > m) && (k <= cluster.idx.length) ){
m = k;
}
}
if (styles[m]){
atodo = {
content:styles[m].content.replace('CLUSTER_COUNT', cluster.idx.length),
offset:{
x: -this._ival(styles[m], 'width')/2,
y: -this._ival(styles[m], 'height')/2
}
};
obj = this._addOverlay(id, atodo, this._latLng(cluster), true);
ctodo.data = {
latLng: this._latLng(cluster),
markers:[]
};
for(ii=0; ii<cluster.idx.length; ii++){
ctodo.data.markers.push(
clusterer.get(cluster.idx[ii]).marker
);
}
this._attachEvents(id, obj, ctodo);
clusterer.store(cluster, obj);
done = true;
}
}
if (!done){
$.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(id, mtodo, mtodo.latLng, true);
this._attachEvents(id, obj, mtodo);
clusterer.store(cluster, obj);
}
mtodo.options = options; // restore previous for futur use
}
}
},
/**
* @desc Add an infowindow after address resolution
**/
addinfowindow: function(id, todo){
this._resolveLatLng(id, todo, '_addInfoWindow');
},
_addInfoWindow: function(id, todo, latLng){
var o, infowindow, args = [],
n = 'infowindow';
this._subcall(id, todo, latLng);
o = this._object(n, todo, ['open', 'anchor']);
if (latLng) {
o.options.position = latLng;
}
infowindow = new google.maps.InfoWindow(o.options);
if ( (o.open === undefined) || o.open ){
o.apply = this._array(o.apply);
args.push(this._getMap(id));
if (o.anchor){
args.push(o.anchor);
}
o.apply.unshift({action:'open', args:args});
}
this._store(id, n, infowindow, o);
this._manageEnd(id, infowindow, o);
},
/**
* @desc add a polygone / polylin on a map
**/
addpolyline: function(id, todo){
this._addPoly(id, todo, 'Polyline', 'path');
},
addpolygon: function(id, todo){
this._addPoly(id, todo, 'Polygon', 'paths');
},
_addPoly: function(id, todo, poly, path){
var i,
obj, latLng,
o = this._object(poly.toLowerCase(), todo, [path]);
if (o[path]){
o.options[path] = [];
for(i = 0; i < o[path].length; i++){
latLng = this._latLng(o[path][i]);
if (latLng){
o.options[path].push(this._latLng(o[path][i]));
}
}
}
obj = new google.maps[poly](o.options);
obj.setMap(this._getMap(id));
this._store(id, poly.toLowerCase(), obj, o);
this._manageEnd(id, obj, o);
},
/**
* @desc add a circle
**/
addcircle: function(id, todo){
this._resolveLatLng(id, todo, '_addCircle');
},
_addCircle: function(id, todo, latLng ){
var c, n = 'circle',
o = this._object(n, todo);
if (!latLng) latLng = this._latLng(o.options.center);
if (!latLng) return this._manageEnd(id, false, o);
this._subcall(id, todo, latLng);
o.options.center = latLng;
o.options.map = this._getMap(id);
c = new google.maps.Circle(o.options);
this._store(id, n, c, o);
this._manageEnd(id, c, o);
},
/**
* @desc add a rectangle
**/
addrectangle: function(id, todo){
this._resolveLatLng(id, todo, '_addRectangle');
},
_addRectangle: function(id, todo, latLng ){
var r, n = 'rectangle',
o = this._object(n, todo);
o.options.bounds = this._latLngBounds(o.options.bounds, true);
if (!o.options.bounds) return this._manageEnd(id, false, o);
this._subcall(id, todo, o.options.bounds.getCenter());
o.options.map = this._getMap(id);
r = new google.maps.Rectangle(o.options);
this._store(id, n, r, o);
this._manageEnd(id, r, o);
},
/**
* @desc add an overlay to a map after address resolution
**/
addoverlay: function(id, todo){
this._resolveLatLng(id, todo, '_addOverlay');
},
_addOverlay: function(id, todo, latLng, internal){
var ov, map,
o = this._object('overlay', todo),
opts = $.extend({
pane: 'floatPane',
content: '',
offset:{
x:0,y:0
}
},
o.options),
$div = $('<div></div>'),
listeners = [];
this._subcall(id, todo, latLng);
map = this._getMap(id);
$div
.css('border', 'none')
.css('borderWidth', '0px')
.css('position', 'absolute');
$div.append($(opts.content));
function f() {
google.maps.OverlayView.call(this);
this.setMap(map);
}
f.prototype = new google.maps.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){
this._store(id, 'overlay', ov, o);
this._manageEnd(id, ov, o);
}
return ov;
},
/**
* @desc add fixed panel to a map
**/
addfixpanel: function(id, todo){
var n = 'fixpanel',
o = this._object(n, todo),
x=0, 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._jObject(id).width() - $c.width() - o.options.right;
} else if (o.options.center){
x = (this._jObject(id).width() - $c.width()) / 2;
}
if (o.options.top !== undefined){
y = o.options.top;
} else if (o.options.bottom !== undefined){
y = this._jObject(id).height() - $c.height() - o.options.bottom;
} else if (o.options.middle){
y = (this._jObject(id).height() - $c.height()) / 2
}
$div = $('<div></div>')
.css('position', 'absolute')
.css('top', y+'px')
.css('left', x+'px')
.css('z-index', '1000')
.append(o.options.content);
this._jObject(id).first().prepend($div);
this._attachEvents(id, this._getMap(id), o);
this._store(id, n, $div, o);
this._callback(id, $div, o);
}
this._end(id);
},
/**
* @desc Add a direction renderer to a map
**/
adddirectionsrenderer: function(id, todo, internal){
var n = 'directionrenderer',
dr, o = this._object(n, todo, ['panelId']);
this._clear(id, n);
o.options.map = this._getMap(id);
dr = new google.maps.DirectionsRenderer(o.options);
if (o.panelId) {
dr.setPanel(document.getElementById(o.panelId));
}
this._store(id, n, dr, o);
this._manageEnd(id, dr, o, internal);
},
/**
* @desc Set direction panel to a dom element from it ID
**/
setdirectionspanel: function(id, todo){
var dr, o = this._object('directionpanel', todo, ['id']);
if (o.id) {
dr = this._getDirectionRenderer(id);
dr.setPanel(document.getElementById(o.id));
}
this._manageEnd(id, dr, o);
},
/**
* @desc Set directions on a map (create Direction Renderer if needed)
**/
setdirections: function(id, todo){
var dr, o = this._object('directions', todo);
if (todo) o.options.directions = todo.directions ? todo.directions : (todo.options && todo.options.directions ? todo.options.directions : null);
if (o.options.directions) {
dr = this._getDirectionRenderer(id);
if (!dr) {
this.adddirectionsrenderer(id, o, true);
dr = this._getDirectionRenderer(id);
} else {
dr.setDirections(o.options.directions);
}
}
this._manageEnd(id, dr, o);
},
/**
* @desc set a streetview to a map
**/
setstreetview: function(id, todo){
var o = this._object('streetview', todo, ['id']),
panorama;
if (o.options.position){
o.options.position = this._latLng(o.options.position);
}
panorama = new google.maps.StreetViewPanorama(document.getElementById(o.id),o.options);
this._getMap(id).setStreetView(panorama);
this._manageEnd(id, panorama, o);
},
/**
* @desc add a kml layer to a map
**/
addkmllayer: function(id, todo){
var n = 'kmllayer',
o = this._object(n, todo, ['url']),
kml;
o.options.map = this._getMap(id);
kml = new google.maps.KmlLayer(o.url, o.options);
this._store(id, n, kml, o);
this._manageEnd(id, kml, o);
},
/**
* @desc add a traffic layer to a map
**/
addtrafficlayer: function(id, todo){
var n = 'trafficlayer',
o = this._object(n, todo),
tl = this._getStored(id, n);
if (!tl){
tl = new google.maps.TrafficLayer();
tl.setMap(this._getMap(id));
this._store(id, n, tl, o);
}
this._manageEnd(id, tl, o);
},
/**
* @desc set a bicycling layer to a map
**/
addbicyclinglayer: function(id, todo){
var n = 'bicyclinglayer',
o = this._object(n, todo),
bl = this._getStored(id, n);
if (!bl){
bl = new google.maps.BicyclingLayer();
bl.setMap(this._getMap(id));
this._store(id, n, bl, o);
}
this._manageEnd(id, bl, o);
},
/**
* @desc add a ground overlay to a map
**/
addgroundoverlay: function(id, todo){
var n = 'groundoverlay',
o = this._object(n, todo, ['bounds', 'url']),
ov;
o.bounds = this._latLngBounds(o.bounds);
if (o.bounds && o.url){
ov = new google.maps.GroundOverlay(o.url, o.bounds);
ov.setMap(this._getMap(id));
this._store(id, n, ov, o);
}
this._manageEnd(id, ov, o);
},
/**
* @desc Geolocalise the user and return a LatLng
**/
geolatlng: function(id, todo){
var geo,
cb = this._ival(todo, 'callback'),
$this = this._jObject(id);
if (typeof(cb) === 'function') {
if(navigator.geolocation) {
browserSupportFlag = true;
navigator.geolocation.getCurrentPosition(function(position) {
var out = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
cb.apply($this, [out]);
}, function() {
var out = false;
cb.apply($this, [out]);
});
} else if (google.gears) {
browserSupportFlag = true;
geo = google.gears.factory.create('beta.geolocation');
geo.getCurrentPosition(function(position) {
var out = new google.maps.LatLng(position.latitude,position.longitude);
cb.apply($this, [out]);
}, function() {
out = false;
cb.apply($this, [out]);
});
} else {
out = false;
cb.apply($this, [out]);
}
}
this._end(id);
},
/**
* @desc Add a style to a map
**/
addstyledmap: function(id, todo, internal){
var o = this._object('styledmap', todo, ['id', 'style']),
style;
if (o.style && o.id && !this._styleExist(id, o.id)) {
style = new google.maps.StyledMapType(o.style, o.options);
this._addStyle(id, o.id, style);
if (this._getMap(id)) this._getMap(id).mapTypes.set(o.id, style);
}
this._manageEnd(id, style, o, internal);
},
/**
* @desc Set a style to a map (add it if needed)
**/
setstyledmap: function(id, todo){
var o = this._object('styledmap', todo, ['id', 'style']),
style;
if (o.id) {
this.addstyledmap(id, o, true);
style = this._getStyle(id, o.id);
if (style) {
this._getMap(id).setMapTypeId(o.id);
this._callback(id, style, todo);
}
}
this._manageEnd(id, style, o);
},
/**
* @desc Remove objects from a map
**/
clear: function(id, todo){
var list = this._array(this._ival(todo, 'list') || this._ival(todo, 'name')),
last = this._ival(todo, 'last', false),
first = this._ival(todo, 'first', false),
tag = this._ival(todo, 'tag');
if (tag !== undefined){
tag = this._array(tag);
}
this._clear(id, list, last, first, tag);
this._end(id);
},
/**
* @desc Return Google object(s) wanted
**/
get: function(id, todo){
var name = this._ival(todo, 'name') || 'map',
first= this._ival(todo, 'first'),
all = this._ival(todo, 'all'),
tag = this._ival(todo, 'tag'),
r, i, t;
name = name.toLowerCase();
if (tag !== undefined){
tag = this._array(tag);
}
if (name == 'map'){
return this._getMap(id);
}
if (first){
return this._getStored(id, name, false, tag);
} else if (all){
r = new Array();
t = this._ids[id].stored[name];
if (t){
for(i = 0; i < t.length; i++){
if (!t[i]){
continue;
}
if (tag !== undefined) {
if ( (t[i].tag === undefined) || ($.inArray(t[i].tag, tag) < 0) ){
continue;
}
}
r.push(t[i].obj);
}
}
return r;
} else {
return this._getStored(id, name, true, tag);
}
},
/**
* @desc return the radius of the earth depending on the unit
**/
earthradius: function(unit){
unit = unit ? unit : this._default.unit;
return (typeof(unit) === 'string' && (unit.toLowerCase() === 'km')) ? 6371 : 3959;
},
/**
* @desc the distance between 2 latLng depending on the unit
**/
distance: function(id, todo){
var unit = this._ival(todo, 'unit'),
a = this._latLng(this._ival(todo, 'a')),
b = this._latLng(this._ival(todo, 'b')),
e,f,g,h, dist;
if (a && b){
e=(Math.PI*a.lat()/180);
f=(Math.PI*a.lng()/180);
g=(Math.PI*b.lat()/180);
h=(Math.PI*b.lng()/180);
dist = this.earthradius(unit)*Math.acos(Math.min(Math.cos(e)*Math.cos(g)*Math.cos(f)*Math.cos(h)+Math.cos(e)*Math.sin(f)*Math.cos(g)*Math.sin(h)+Math.sin(e)*Math.sin(g),1));
}
return dist;
},
/**
* @desc return the max zoom of a latlng
**/
getmaxzoom: function(id, todo){
this._resolveLatLng(id, todo, '_getMaxZoom');
},
_getMaxZoom: function(id, todo, latLng){
var callback,
$this = this._jObject(id),
cb = this._ival(todo, 'callback');
if (cb && typeof(cb) === 'function') {
callback = function(result) {
var zoom = result.status == google.maps.MaxZoomStatus.OK ? result.zoom : false;
cb.apply($this, [zoom, result.status]);
};
this._getMaxZoomService().getMaxZoomAtLatLng(latLng, callback);
}
this._end(id);
},
/**
* @desc modify default values
**/
setdefault: function(id, todo, internal){
for(var k in todo){
if (typeof(this._default[k]) === 'object'){
this._default[k] = jQuery.extend({}, this._default[k], todo[k]);
} else {
this._default[k] = todo[k];
}
}
if (!internal){
this._end(id);
}
},
/**
* @desc : autofit a map using its overlays (markers, rectangles ...)
**/
autofit: function(id, todo, internal){
var n, i, stored, obj, empty = true, bounds = new google.maps.LatLngBounds();
if (id in this._ids){
for(n in this._ids[id].stored){
stored = this._ids[id].stored[n];
for(i = 0; i < stored.length; i++){
obj = stored[i].obj;
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){
this._getMap(id).fitBounds(bounds);
}
}
if (!internal){
this._manageEnd(id, empty ? false : bounds, todo, internal);
}
}
};
var globalId = 0;
$.fn.gmap3 = function(){
var a,i,
todo = [],
results = [],
empty = true;
for(i=0; i<arguments.length; i++){
a = arguments[i] || {};
if (typeof(a) === 'string'){
a = {action:a};
}
if (a.action && (a.action.substr(0, 1) == ':')){
a.action = a.action.substr(1);
}
todo.push(a);
}
if (!todo.length) todo.push({});
$.each(this, function() {
var $this = $(this),
id = $this.data('id');
empty = false;
if (!id){
id = ++globalId;
$this.data('id', id);
}
if (todo.length == 1){
if (gmap3._isDirect(id, todo[0])){
results.push(gmap3._direct(id, todo[0]));
} else {
gmap3._plan($this, id, todo);
}
} else {
gmap3._plan($this, id, todo);
}
});
if (results.length){
if (results.length === 1){ // 1 css selector
return results[0];
} else {
return results;
}
}
if (empty && (arguments.length == 2) && (typeof(arguments[0]) === 'string') && (arguments[0].toLowerCase() === 'setdefault')){
gmap3.setdefault(0, arguments[1], true);
}
return this;
}
}(jQuery));