Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*!
- * GMAP3 Plugin for JQuery
- * Version : 5.1.1
- * Date : 2013-05-25
- * 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-2012 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 ($, undef) {
- /***************************************************************************/
- /* GMAP3 DEFAULTS */
- /***************************************************************************/
- // defaults are defined later in the code to pass the rails asset pipeline and
- //jasmine while google library is not loaded
- var defaults, gId = 0;
- function initDefaults() {
- if (!defaults) {
- defaults = {
- verbose: false,
- queryLimit: {
- attempt: 5,
- delay: 250, // setTimeout(..., delay + random);
- random: 250
- },
- 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,
- ImageMapType : google.maps.ImageMapType
- },
- map: {
- mapTypeId : google.maps.MapTypeId.ROADMAP,
- center: [46.578498, 2.457275],
- zoom: 2
- },
- overlay: {
- pane: "floatPane",
- content: "",
- offset: {
- x: 0,
- y: 0
- }
- },
- geoloc: {
- getCurrentPosition: {
- maximumAge: 60000,
- timeout: 5000
- }
- }
- }
- }
- }
- function globalId(id, simulate){
- return id !== undef ? id : "gmap3_" + (simulate ? gId + 1 : ++gId);
- }
- /**
- * Return true if current version of Google Maps is equal or above to these in parameter
- * @param version {string} Minimal version required
- * @return {Boolean}
- */
- function googleVersionMin(version) {
- var i,
- gmVersion = google.maps.version.split(".");
- version = version.split(".");
- for(i = 0; i < gmVersion.length; i++) {
- gmVersion[i] = parseInt(gmVersion[i], 10);
- }
- for(i = 0; i < version.length; i++) {
- version[i] = parseInt(version[i], 10);
- if (gmVersion.hasOwnProperty(i)) {
- if (gmVersion[i] < version[i]) {
- return false;
- }
- } else {
- return false;
- }
- }
- return true;
- }
- /**
- * attach events from a container to a sender
- * todo[
- * events => { eventName => function, }
- * onces => { eventName => function, }
- * data => mixed data
- * ]
- **/
- function attachEvents($container, args, sender, id, senders){
- if (args.todo.events || args.todo.onces) {
- var context = {
- id: id,
- data: args.todo.data,
- tag: args.todo.tag
- };
- if (args.todo.events){
- $.each(args.todo.events, function(name, f){
- var that = $container, fn = f;
- if ($.isArray(f)) {
- that = f[0];
- fn = f[1]
- }
- google.maps.event.addListener(sender, name, function(event) {
- fn.apply(that, [senders ? senders : sender, event, context]);
- });
- });
- }
- if (args.todo.onces){
- $.each(args.todo.onces, function(name, f){
- var that = $container, fn = f;
- if ($.isArray(f)) {
- that = f[0];
- fn = f[1]
- }
- google.maps.event.addListenerOnce(sender, name, function(event) {
- fn.apply(that, [senders ? senders : sender, event, context]);
- });
- });
- }
- }
- }
- /***************************************************************************/
- /* STACK */
- /***************************************************************************/
- function Stack (){
- var st = [];
- this.empty = function (){
- return !st.length;
- };
- this.add = function(v){
- st.push(v);
- };
- this.get = function (){
- return st.length ? st[0] : false;
- };
- this.ack = function (){
- st.shift();
- };
- }
- /***************************************************************************/
- /* TASK */
- /***************************************************************************/
- function Task(ctx, onEnd, todo){
- var session = {},
- that = this,
- current,
- resolve = {
- latLng:{ // function => bool (=> address = latLng)
- map:false,
- marker:false,
- infowindow:false,
- circle:false,
- overlay: false,
- getlatlng: false,
- getmaxzoom: false,
- getelevation: false,
- streetviewpanorama: false,
- getaddress: true
- },
- geoloc:{
- getgeoloc: true
- }
- };
- if (typeof todo === "string"){
- todo = unify(todo);
- }
- function unify(todo){
- var result = {};
- result[todo] = {};
- return result;
- }
- function next(){
- var k;
- for(k in todo){
- if (k in session){ // already run
- continue;
- }
- return k;
- }
- }
- this.run = function (){
- var k, opts;
- while(k = next()){
- if (typeof ctx[k] === "function"){
- current = k;
- opts = $.extend(true, {}, defaults[k] || {}, todo[k].options || {});
- if (k in resolve.latLng){
- if (todo[k].values){
- resolveAllLatLng(todo[k].values, ctx, ctx[k], {todo:todo[k], opts:opts, session:session});
- } else {
- resolveLatLng(ctx, ctx[k], resolve.latLng[k], {todo:todo[k], opts:opts, session:session});
- }
- } else if (k in resolve.geoloc){
- geoloc(ctx, ctx[k], {todo:todo[k], opts:opts, session:session});
- } else {
- ctx[k].apply(ctx, [{todo:todo[k], opts:opts, session:session}]);
- }
- return; // wait until ack
- } else {
- session[k] = null;
- }
- }
- onEnd.apply(ctx, [todo, session]);
- };
- this.ack = function(result){
- session[current] = result;
- that.run.apply(that, []);
- };
- }
- function getKeys(obj){
- var k, keys = [];
- for(k in obj){
- keys.push(k);
- }
- return keys;
- }
- function tuple(args, value){
- var todo = {};
- // "copy" the common data
- if (args.todo){
- for(var k in args.todo){
- if ((k !== "options") && (k !== "values")){
- todo[k] = args.todo[k];
- }
- }
- }
- // "copy" some specific keys from value first else args.todo
- var i, keys = ["data", "tag", "id", "events", "onces"];
- for(i=0; i<keys.length; i++){
- copyKey(todo, keys[i], value, args.todo);
- }
- // create an extended options
- todo.options = $.extend({}, args.opts || {}, value.options || {});
- return todo;
- }
- /**
- * copy a key content
- **/
- function copyKey(target, key){
- for(var i=2; i<arguments.length; i++){
- if (key in arguments[i]){
- target[key] = arguments[i][key];
- return;
- }
- }
- }
- /***************************************************************************/
- /* GEOCODERCACHE */
- /***************************************************************************/
- function GeocoderCache(){
- var cache = [];
- this.get = function(request){
- if (cache.length){
- var i, j, k, item, eq,
- keys = getKeys(request);
- for(i=0; i<cache.length; i++){
- item = cache[i];
- eq = keys.length == item.keys.length;
- for(j=0; (j<keys.length) && eq; j++){
- k = keys[j];
- eq = k in item.request;
- if (eq){
- if ((typeof request[k] === "object") && ("equals" in request[k]) && (typeof request[k] === "function")){
- eq = request[k].equals(item.request[k]);
- } else{
- eq = request[k] === item.request[k];
- }
- }
- }
- if (eq){
- return item.results;
- }
- }
- }
- };
- this.store = function(request, results){
- cache.push({request:request, keys:getKeys(request), results:results});
- };
- }
- /***************************************************************************/
- /* OVERLAYVIEW */
- /***************************************************************************/
- function OverlayView(map, opts, latLng, $div) {
- var that = this, listeners = [];
- defaults.classes.OverlayView.call(this);
- this.setMap(map);
- this.onAdd = function() {
- var panes = this.getPanes();
- if (opts.pane in panes) {
- $(panes[opts.pane]).append($div);
- }
- $.each("dblclick click mouseover mousemove mouseout mouseup mousedown".split(" "), function(i, name){
- listeners.push(
- google.maps.event.addDomListener($div[0], name, function(e) {
- $.Event(e).stopPropagation();
- google.maps.event.trigger(that, name, [e]);
- that.draw();
- })
- );
- });
- listeners.push(
- google.maps.event.addDomListener($div[0], "contextmenu", function(e) {
- $.Event(e).stopPropagation();
- google.maps.event.trigger(that, "rightclick", [e]);
- that.draw();
- })
- );
- };
- this.getPosition = function(){
- return latLng;
- };
- this.setPosition = function(newLatLng){
- latLng = newLatLng;
- this.draw();
- };
- this.draw = function() {
- var ps = this.getProjection().fromLatLngToDivPixel(latLng);
- $div
- .css("left", (ps.x+opts.offset.x) + "px")
- .css("top" , (ps.y+opts.offset.y) + "px");
- };
- this.onRemove = function() {
- for (var i = 0; i < listeners.length; i++) {
- google.maps.event.removeListener(listeners[i]);
- }
- $div.remove();
- };
- this.hide = function() {
- $div.hide();
- };
- this.show = function() {
- $div.show();
- };
- this.toggle = function() {
- if ($div) {
- if ($div.is(":visible")){
- this.show();
- } else {
- this.hide();
- }
- }
- };
- this.toggleDOM = function() {
- if (this.getMap()) {
- this.setMap(null);
- } else {
- this.setMap(map);
- }
- };
- this.getDOMElement = function() {
- return $div[0];
- };
- }
- /***************************************************************************/
- /* CLUSTERING */
- /***************************************************************************/
- /**
- * Usefull to get a projection
- * => done in a function, to let dead-code analyser works without google library loaded
- **/
- function newEmptyOverlay(map, radius){
- function Overlay(){
- this.onAdd = function(){};
- this.onRemove = function(){};
- this.draw = function(){};
- return defaults.classes.OverlayView.apply(this, []);
- }
- Overlay.prototype = defaults.classes.OverlayView.prototype;
- var obj = new Overlay();
- obj.setMap(map);
- return obj;
- }
- /**
- * Class InternalClusterer
- * This class manage clusters thanks to "todo" objects
- *
- * Note:
- * Individuals marker are created on the fly thanks to the todo objects, they are
- * first set to null to keep the indexes synchronised with the todo list
- * This is the "display" function, set by the gmap3 object, which uses theses data
- * to create markers when clusters are not required
- * To remove a marker, the objects are deleted and set not null in arrays
- * markers[key]
- * = null : marker exist but has not been displayed yet
- * = false : marker has been removed
- **/
- function InternalClusterer($container, map, raw){
- var updating = false,
- updated = false,
- redrawing = false,
- ready = false,
- enabled = true,
- that = this,
- events = [],
- store = {}, // combin of index (id1-id2-...) => object
- ids = {}, // unique id => index
- idxs = {}, // index => unique id
- markers = [], // index => marker
- todos = [], // index => todo or null if removed
- values = [], // index => value
- overlay = newEmptyOverlay(map, raw.radius),
- timer, projection,
- ffilter, fdisplay, ferror; // callback function
- main();
- function prepareMarker(index) {
- if (!markers[index]) {
- delete todos[index].options.map;
- markers[index] = new defaults.classes.Marker(todos[index].options);
- attachEvents($container, {todo: todos[index]}, markers[index], todos[index].id);
- }
- }
- /**
- * return a marker by its id, null if not yet displayed and false if no exist or removed
- **/
- this.getById = function(id){
- if (id in ids) {
- prepareMarker(ids[id]);
- return markers[ids[id]];
- }
- return false;
- };
- /**
- * remove one object from the store
- **/
- this.rm = function (id) {
- var index = ids[id];
- if (markers[index]){ // can be null
- markers[index].setMap(null);
- }
- delete markers[index];
- markers[index] = false;
- delete todos[index];
- todos[index] = false;
- delete values[index];
- values[index] = false;
- delete ids[id];
- delete idxs[index];
- updated = true;
- };
- /**
- * remove a marker by its id
- **/
- this.clearById = function(id){
- if (id in ids){
- this.rm(id);
- return true;
- }
- };
- /**
- * remove objects from the store
- **/
- this.clear = function(last, first, tag){
- var start, stop, step, index, i,
- list = [],
- check = ftag(tag);
- if (last) {
- start = todos.length - 1;
- stop = -1;
- step = -1;
- } else {
- start = 0;
- stop = todos.length;
- step = 1;
- }
- for (index = start; index != stop; index += step) {
- if (todos[index]) {
- if (!check || check(todos[index].tag)){
- list.push(idxs[index]);
- if (first || last) {
- break;
- }
- }
- }
- }
- for (i = 0; i < list.length; i++) {
- this.rm(list[i]);
- }
- };
- // add a "marker todo" to the cluster
- this.add = function(todo, value){
- todo.id = globalId(todo.id);
- this.clearById(todo.id);
- ids[todo.id] = markers.length;
- idxs[markers.length] = todo.id;
- markers.push(null); // null = marker not yet created / displayed
- todos.push(todo);
- values.push(value);
- updated = true;
- };
- // add a real marker to the cluster
- this.addMarker = function(marker, todo){
- todo = todo || {};
- todo.id = globalId(todo.id);
- this.clearById(todo.id);
- if (!todo.options){
- todo.options = {};
- }
- todo.options.position = marker.getPosition();
- attachEvents($container, {todo:todo}, marker, todo.id);
- ids[todo.id] = markers.length;
- idxs[markers.length] = todo.id;
- markers.push(marker);
- todos.push(todo);
- values.push(todo.data || {});
- updated = true;
- };
- // return a "marker todo" by its index
- this.todo = function(index){
- return todos[index];
- };
- // return a "marker value" by its index
- this.value = function(index){
- return values[index];
- };
- // return a marker by its index
- this.marker = function(index){
- if (index in markers) {
- prepareMarker(index);
- return markers[index];
- }
- return false;
- };
- // return a marker by its index
- this.markerIsSet = function(index){
- return Boolean(markers[index]);
- };
- // store a new marker instead if the default "false"
- this.setMarker = function(index, marker){
- markers[index] = marker;
- };
- // link the visible overlay to the logical data (to hide overlays later)
- this.store = function(cluster, obj, shadow){
- store[cluster.ref] = {obj:obj, shadow:shadow};
- };
- // free all objects
- this.free = function(){
- for(var i = 0; i < events.length; i++){
- google.maps.event.removeListener(events[i]);
- }
- events = [];
- $.each(store, function(key){
- flush(key);
- });
- store = {};
- $.each(todos, function(i){
- todos[i] = null;
- });
- todos = [];
- $.each(markers, function(i){
- if (markers[i]){ // false = removed
- markers[i].setMap(null);
- delete markers[i];
- }
- });
- markers = [];
- $.each(values, function(i){
- delete values[i];
- });
- values = [];
- ids = {};
- idxs = {};
- };
- // link the display function
- this.filter = function(f){
- ffilter = f;
- redraw();
- };
- // enable/disable the clustering feature
- this.enable = function(value){
- if (enabled != value){
- enabled = value;
- redraw();
- }
- };
- // link the display function
- this.display = function(f){
- fdisplay = f;
- };
- // link the errorfunction
- this.error = function(f){
- ferror = f;
- };
- // lock the redraw
- this.beginUpdate = function(){
- updating = true;
- };
- // unlock the redraw
- this.endUpdate = function(){
- updating = false;
- if (updated){
- redraw();
- }
- };
- // extends current bounds with internal markers
- this.autofit = function(bounds){
- for(var i=0; i<todos.length; i++){
- if (todos[i]){
- bounds.extend(todos[i].options.position);
- }
- }
- };
- // bind events
- function main(){
- projection = overlay.getProjection();
- if (!projection){
- setTimeout(function(){
- main.apply(that, []);
- },
- 25);
- return;
- }
- ready = true;
- events.push(google.maps.event.addListener(map, "zoom_changed", function(){delayRedraw();}));
- events.push(google.maps.event.addListener(map, "bounds_changed", function(){delayRedraw();}));
- redraw();
- }
- // flush overlays
- function flush(key){
- if (typeof store[key] === "object"){ // is overlay
- if (typeof(store[key].obj.setMap) === "function") {
- store[key].obj.setMap(null);
- }
- if (typeof(store[key].obj.remove) === "function") {
- store[key].obj.remove();
- }
- if (typeof(store[key].shadow.remove) === "function") {
- store[key].obj.remove();
- }
- if (typeof(store[key].shadow.setMap) === "function") {
- store[key].shadow.setMap(null);
- }
- delete store[key].obj;
- delete store[key].shadow;
- } else if (markers[key]){ // marker not removed
- markers[key].setMap(null);
- // don't remove the marker object, it may be displayed later
- }
- delete store[key];
- }
- /**
- * return the distance between 2 latLng couple into meters
- * Params :
- * Lat1, Lng1, Lat2, Lng2
- * LatLng1, Lat2, Lng2
- * Lat1, Lng1, LatLng2
- * LatLng1, LatLng2
- **/
- function distanceInMeter(){
- var lat1, lat2, lng1, lng2, e, f, g, h;
- if (arguments[0] instanceof google.maps.LatLng){
- lat1 = arguments[0].lat();
- lng1 = arguments[0].lng();
- if (arguments[1] instanceof google.maps.LatLng){
- lat2 = arguments[1].lat();
- lng2 = arguments[1].lng();
- } else {
- lat2 = arguments[1];
- lng2 = arguments[2];
- }
- } else {
- lat1 = arguments[0];
- lng1 = arguments[1];
- if (arguments[2] instanceof google.maps.LatLng){
- lat2 = arguments[2].lat();
- lng2 = arguments[2].lng();
- } else {
- lat2 = arguments[2];
- lng2 = arguments[3];
- }
- }
- e = Math.PI*lat1/180;
- f = Math.PI*lng1/180;
- g = Math.PI*lat2/180;
- h = Math.PI*lng2/180;
- return 1000*6371 * 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));
- }
- // extend the visible bounds
- function extendsMapBounds(){
- var radius = distanceInMeter(map.getCenter(), map.getBounds().getNorthEast()),
- circle = new google.maps.Circle({
- center: map.getCenter(),
- radius: 1.25 * radius // + 25%
- });
- return circle.getBounds();
- }
- // return an object where keys are store keys
- function getStoreKeys(){
- var keys = {}, k;
- for(k in store){
- keys[k] = true;
- }
- return keys;
- }
- // async the delay function
- function delayRedraw(){
- clearTimeout(timer);
- timer = setTimeout(function(){
- redraw();
- },
- 25);
- }
- // generate bounds extended by radius
- function extendsBounds(latLng) {
- var p = projection.fromLatLngToDivPixel(latLng),
- ne = projection.fromDivPixelToLatLng(new google.maps.Point(p.x+raw.radius, p.y-raw.radius)),
- sw = projection.fromDivPixelToLatLng(new google.maps.Point(p.x-raw.radius, p.y+raw.radius));
- return new google.maps.LatLngBounds(sw, ne);
- }
- // run the clustering process and call the display function
- function redraw(){
- if (updating || redrawing || !ready){
- return;
- }
- var keys = [], used = {},
- zoom = map.getZoom(),
- forceDisabled = ("maxZoom" in raw) && (zoom > raw.maxZoom),
- previousKeys = getStoreKeys(),
- i, j, k, indexes, check = false, bounds, cluster, position, previous, lat, lng, loop;
- // reset flag
- updated = false;
- if (zoom > 3){
- // extend the bounds of the visible map to manage clusters near the boundaries
- bounds = extendsMapBounds();
- // check contain only if boundaries are valid
- check = bounds.getSouthWest().lng() < bounds.getNorthEast().lng();
- }
- // calculate positions of "visibles" markers (in extended bounds)
- for(i=0; i<todos.length; i++){
- if (todos[i] && (!check || bounds.contains(todos[i].options.position)) && (!ffilter || ffilter(values[i]))){
- keys.push(i);
- }
- }
- // for each "visible" marker, search its neighbors to create a cluster
- // we can't do a classical "for" loop, because, analysis can bypass a marker while focusing on cluster
- while(1){
- i=0;
- while(used[i] && (i<keys.length)){ // look for the next marker not used
- i++;
- }
- if (i == keys.length){
- break;
- }
- indexes = [];
- if (enabled && !forceDisabled){
- loop = 10;
- do{
- previous = indexes;
- indexes = [];
- loop--;
- if (previous.length){
- position = bounds.getCenter()
- } else {
- position = todos[ keys[i] ].options.position;
- }
- bounds = extendsBounds(position);
- for(j=i; j<keys.length; j++){
- if (used[j]){
- continue;
- }
- if (bounds.contains(todos[ keys[j] ].options.position)){
- indexes.push(j);
- }
- }
- } while( (previous.length < indexes.length) && (indexes.length > 1) && loop);
- } else {
- for(j=i; j<keys.length; j++){
- if (used[j]){
- continue;
- }
- indexes.push(j);
- break;
- }
- }
- cluster = {indexes:[], ref:[]};
- lat = lng = 0;
- for(k=0; k<indexes.length; k++){
- used[ indexes[k] ] = true;
- cluster.indexes.push(keys[indexes[k]]);
- cluster.ref.push(keys[indexes[k]]);
- lat += todos[ keys[indexes[k]] ].options.position.lat();
- lng += todos[ keys[indexes[k]] ].options.position.lng();
- }
- lat /= indexes.length;
- lng /= indexes.length;
- cluster.latLng = new google.maps.LatLng(lat, lng);
- cluster.ref = cluster.ref.join("-");
- if (cluster.ref in previousKeys){ // cluster doesn't change
- delete previousKeys[cluster.ref]; // remove this entry, these still in this array will be removed
- } else { // cluster is new
- if (indexes.length === 1){ // alone markers are not stored, so need to keep the key (else, will be displayed every time and marker will blink)
- store[cluster.ref] = true;
- }
- fdisplay(cluster);
- }
- }
- // flush the previous overlays which are not still used
- $.each(previousKeys, function(key){
- flush(key);
- });
- redrawing = false;
- }
- }
- /**
- * Class Clusterer
- * a facade with limited method for external use
- **/
- function Clusterer(id, internalClusterer){
- this.id = function(){
- return id;
- };
- this.filter = function(f){
- internalClusterer.filter(f);
- };
- this.enable = function(){
- internalClusterer.enable(true);
- };
- this.disable = function(){
- internalClusterer.enable(false);
- };
- this.add = function(marker, todo, lock){
- if (!lock) {
- internalClusterer.beginUpdate();
- }
- internalClusterer.addMarker(marker, todo);
- if (!lock) {
- internalClusterer.endUpdate();
- }
- };
- this.getById = function(id){
- return internalClusterer.getById(id);
- };
- this.clearById = function(id, lock){
- var result;
- if (!lock) {
- internalClusterer.beginUpdate();
- }
- result = internalClusterer.clearById(id);
- if (!lock) {
- internalClusterer.endUpdate();
- }
- return result;
- };
- this.clear = function(last, first, tag, lock){
- if (!lock) {
- internalClusterer.beginUpdate();
- }
- internalClusterer.clear(last, first, tag);
- if (!lock) {
- internalClusterer.endUpdate();
- }
- };
- }
- /***************************************************************************/
- /* STORE */
- /***************************************************************************/
- function Store(){
- var store = {}, // name => [id, ...]
- objects = {}; // id => object
- function normalize(res) {
- return {
- id: res.id,
- name: res.name,
- object:res.obj,
- tag:res.tag,
- data:res.data
- };
- }
- /**
- * add a mixed to the store
- **/
- this.add = function(args, name, obj, sub){
- var todo = args.todo || {},
- id = globalId(todo.id);
- if (!store[name]){
- store[name] = [];
- }
- if (id in objects){ // object already exists: remove it
- this.clearById(id);
- }
- objects[id] = {obj:obj, sub:sub, name:name, id:id, tag:todo.tag, data:todo.data};
- store[name].push(id);
- return id;
- };
- /**
- * return a stored object by its id
- **/
- this.getById = function(id, sub, full){
- if (id in objects){
- if (sub) {
- return objects[id].sub
- } else if (full) {
- return normalize(objects[id]);
- }
- return objects[id].obj;
- }
- return false;
- };
- /**
- * return a stored value
- **/
- this.get = function(name, last, tag, full){
- var n, id, check = ftag(tag);
- if (!store[name] || !store[name].length){
- return null;
- }
- n = store[name].length;
- while(n){
- n--;
- id = store[name][last ? n : store[name].length - n - 1];
- if (id && objects[id]){
- if (check && !check(objects[id].tag)){
- continue;
- }
- return full ? normalize(objects[id]) : objects[id].obj;
- }
- }
- return null;
- };
- /**
- * return all stored values
- **/
- this.all = function(name, tag, full){
- var result = [],
- check = ftag(tag),
- find = function(n){
- var i, id;
- for(i=0; i<store[n].length; i++){
- id = store[n][i];
- if (id && objects[id]){
- if (check && !check(objects[id].tag)){
- continue;
- }
- result.push(full ? normalize(objects[id]) : objects[id].obj);
- }
- }
- };
- if (name in store){
- find(name);
- } else if (name === undef){ // internal use only
- for(name in store){
- find(name);
- }
- }
- return result;
- };
- /**
- * hide and remove an object
- **/
- function rm(obj){
- // Google maps element
- if (typeof(obj.setMap) === "function") {
- obj.setMap(null);
- }
- // jQuery
- if (typeof(obj.remove) === "function") {
- obj.remove();
- }
- // internal (cluster)
- if (typeof(obj.free) === "function") {
- obj.free();
- }
- obj = null;
- }
- /**
- * remove one object from the store
- **/
- this.rm = function(name, check, pop){
- var idx, id;
- if (!store[name]) {
- return false;
- }
- if (check){
- if (pop){
- for(idx = store[name].length - 1; idx >= 0; idx--){
- id = store[name][idx];
- if ( check(objects[id].tag) ){
- break;
- }
- }
- } else {
- for(idx = 0; idx < store[name].length; idx++){
- id = store[name][idx];
- if (check(objects[id].tag)){
- break;
- }
- }
- }
- } else {
- idx = pop ? store[name].length - 1 : 0;
- }
- if ( !(idx in store[name]) ) {
- return false;
- }
- return this.clearById(store[name][idx], idx);
- };
- /**
- * remove object from the store by its id
- **/
- this.clearById = function(id, idx){
- if (id in objects){
- var i, name = objects[id].name;
- for(i=0; idx === undef && i<store[name].length; i++){
- if (id === store[name][i]){
- idx = i;
- }
- }
- rm(objects[id].obj);
- if(objects[id].sub){
- rm(objects[id].sub);
- }
- delete objects[id];
- store[name].splice(idx, 1);
- return true;
- }
- return false;
- };
- /**
- * return an object from a container object in the store by its id
- * ! for now, only cluster manage this feature
- **/
- this.objGetById = function(id){
- var result;
- if (store["clusterer"]) {
- for(var idx in store["clusterer"]){
- if ((result = objects[store["clusterer"][idx]].obj.getById(id)) !== false){
- return result;
- }
- }
- }
- return false;
- };
- /**
- * remove object from a container object in the store by its id
- * ! for now, only cluster manage this feature
- **/
- this.objClearById = function(id){
- if (store["clusterer"]) {
- for(var idx in store["clusterer"]){
- if (objects[store["clusterer"][idx]].obj.clearById(id)){
- return true;
- }
- }
- }
- return null;
- };
- /**
- * remove objects from the store
- **/
- this.clear = function(list, last, first, tag){
- var k, i, name, check = ftag(tag);
- if (!list || !list.length){
- list = [];
- for(k in store){
- list.push(k);
- }
- } else {
- list = array(list);
- }
- for(i=0; i<list.length; i++){
- name = list[i];
- if (last){
- this.rm(name, check, true);
- } else if (first){
- this.rm(name, check, false);
- } else { // all
- while(this.rm(name, check, false));
- }
- }
- };
- /**
- * remove object from a container object in the store by its tags
- * ! for now, only cluster manage this feature
- **/
- this.objClear = function(list, last, first, tag){
- if (store["clusterer"] && ($.inArray("marker", list) >= 0 || !list.length)) {
- for(var idx in store["clusterer"]){
- objects[store["clusterer"][idx]].obj.clear(last, first, tag);
- }
- }
- };
- }
- /***************************************************************************/
- /* GMAP3 GLOBALS */
- /***************************************************************************/
- var services = {},
- geocoderCache = new GeocoderCache();
- //-----------------------------------------------------------------------//
- // Service tools
- //-----------------------------------------------------------------------//
- function geocoder(){
- if (!services.geocoder) {
- services.geocoder = new google.maps.Geocoder();
- }
- return services.geocoder;
- }
- function directionsService(){
- if (!services.directionsService) {
- services.directionsService = new google.maps.DirectionsService();
- }
- return services.directionsService;
- }
- function elevationService(){
- if (!services.elevationService) {
- services.elevationService = new google.maps.ElevationService();
- }
- return services.elevationService;
- }
- function maxZoomService(){
- if (!services.maxZoomService) {
- services.maxZoomService = new google.maps.MaxZoomService();
- }
- return services.maxZoomService;
- }
- function distanceMatrixService(){
- if (!services.distanceMatrixService) {
- services.distanceMatrixService = new google.maps.DistanceMatrixService();
- }
- return services.distanceMatrixService;
- }
- //-----------------------------------------------------------------------//
- // Unit tools
- //-----------------------------------------------------------------------//
- function error(){
- if (defaults.verbose){
- var i, err = [];
- if (window.console && (typeof console.error === "function") ){
- for(i=0; i<arguments.length; i++){
- err.push(arguments[i]);
- }
- console.error.apply(console, err);
- } else {
- err = "";
- for(i=0; i<arguments.length; i++){
- err += arguments[i].toString() + " " ;
- }
- alert(err);
- }
- }
- }
- /**
- * 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 !== undef){
- 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;
- }
- /**
- * create a function to check a tag
- */
- function ftag(tag){
- if (tag){
- if (typeof tag === "function"){
- return tag;
- }
- tag = array(tag);
- return function(val){
- if (val === undef){
- return false;
- }
- if (typeof val === "object"){
- for(var i=0; i<val.length; i++){
- if($.inArray(val[i], tag) >= 0){
- return true;
- }
- }
- return false;
- }
- return $.inArray(val, tag) >= 0;
- }
- }
- }
- /**
- * 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 (mixed instanceof google.maps.LatLng) {
- 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 && $.isArray(mixed)){
- 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){
- var ne, sw;
- if (!mixed || mixed instanceof google.maps.LatLngBounds) {
- return mixed || null;
- }
- if ($.isArray(mixed)){
- 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 null;
- }
- /**
- * resolveLatLng
- **/
- function resolveLatLng(ctx, method, runLatLng, args, attempt){
- var latLng = runLatLng ? toLatLng(args.todo, false, true) : false,
- conf = latLng ? {latLng:latLng} : (args.todo.address ? (typeof(args.todo.address) === "string" ? {address:args.todo.address} : args.todo.address) : false),
- cache = conf ? geocoderCache.get(conf) : false,
- that = this;
- if (conf){
- attempt = attempt || 0; // convert undefined to int
- if (cache){
- args.latLng = cache.results[0].geometry.location;
- args.results = cache.results;
- args.status = cache.status;
- method.apply(ctx, [args]);
- } else {
- if (conf.location){
- conf.location = toLatLng(conf.location);
- }
- if (conf.bounds){
- conf.bounds = toLatLngBounds(conf.bounds);
- }
- geocoder().geocode(
- conf,
- function(results, status) {
- if (status === google.maps.GeocoderStatus.OK){
- geocoderCache.store(conf, {results:results, status:status});
- args.latLng = results[0].geometry.location;
- args.results = results;
- args.status = status;
- method.apply(ctx, [args]);
- } else if ( (status === google.maps.GeocoderStatus.OVER_QUERY_LIMIT) && (attempt < defaults.queryLimit.attempt) ){
- setTimeout(
- function(){
- resolveLatLng.apply(that, [ctx, method, runLatLng, args, attempt+1]);
- },
- defaults.queryLimit.delay + Math.floor(Math.random() * defaults.queryLimit.random)
- );
- } else {
- error("geocode failed", status, conf);
- args.latLng = args.results = false;
- args.status = status;
- method.apply(ctx, [args]);
- }
- }
- );
- }
- } else {
- args.latLng = toLatLng(args.todo, false, true);
- method.apply(ctx, [args]);
- }
- }
- function resolveAllLatLng(list, ctx, method, args){
- var that = this, i = -1;
- function resolve(){
- // look for next address to resolve
- do{
- i++;
- }while( (i < list.length) && !("address" in list[i]) );
- // no address found, so run method
- if (i >= list.length){
- method.apply(ctx, [args]);
- return;
- }
- resolveLatLng(
- that,
- function(args){
- delete args.todo;
- $.extend(list[i], args);
- resolve.apply(that, []); // resolve next (using apply avoid too much recursion)
- },
- true,
- {todo:list[i]}
- );
- }
- resolve();
- }
- /**
- * geolocalise the user and return a LatLng
- **/
- function geoloc(ctx, method, args){
- var is_echo = false; // sometime, a kind of echo appear, this trick will notice once the first call is run to ignore the next one
- if (navigator && navigator.geolocation){
- navigator.geolocation.getCurrentPosition(
- function(pos) {
- if (is_echo){
- return;
- }
- is_echo = true;
- args.latLng = new google.maps.LatLng(pos.coords.latitude,pos.coords.longitude);
- method.apply(ctx, [args]);
- },
- function() {
- if (is_echo){
- return;
- }
- is_echo = true;
- args.latLng = false;
- method.apply(ctx, [args]);
- },
- args.opts.getCurrentPosition
- );
- } else {
- args.latLng = false;
- method.apply(ctx, [args]);
- }
- }
- /***************************************************************************/
- /* GMAP3 */
- /***************************************************************************/
- function Gmap3($this){
- var that = this,
- stack = new Stack(),
- store = new Store(),
- map = null,
- task;
- //-----------------------------------------------------------------------//
- // Stack tools
- //-----------------------------------------------------------------------//
- /**
- * store actions to execute in a stack manager
- **/
- this._plan = function(list){
- for(var k = 0; k < list.length; k++) {
- stack.add(new Task(that, end, list[k]));
- }
- run();
- };
- /**
- * if not running, start next action in stack
- **/
- function run(){
- if (!task && (task = stack.get())){
- task.run();
- }
- }
- /**
- * called when action in finished, to acknoledge the current in stack and start next one
- **/
- function end(){
- task = null;
- stack.ack();
- run.call(that); // restart to high level scope
- }
- //-----------------------------------------------------------------------//
- // Tools
- //-----------------------------------------------------------------------//
- /**
- * execute callback functions
- **/
- function callback(args){
- if (args.todo.callback) {
- var params = Array.prototype.slice.call(arguments, 1);
- if (typeof args.todo.callback === "function") {
- args.todo.callback.apply($this, params);
- } else if ($.isArray(args.todo.callback)) {
- if (typeof args.todo.callback[1] === "function") {
- args.todo.callback[1].apply(args.todo.callback[0], params);
- }
- }
- }
- }
- /**
- * execute ending functions
- **/
- function manageEnd(args, obj, id){
- if (id){
- attachEvents($this, args, obj, id);
- }
- callback(args, obj);
- task.ack(obj);
- }
- /**
- * initialize the map if not yet initialized
- **/
- function newMap(latLng, args){
- args = args || {};
- if (map) {
- if (args.todo && args.todo.options){
- if (args.todo.options.center) {
- args.todo.options.center = toLatLng(args.todo.options.center);
- }
- map.setOptions(args.todo.options);
- }
- } else {
- var opts = args.opts || $.extend(true, {}, defaults.map, args.todo && args.todo.options ? args.todo.options : {});
- opts.center = latLng || toLatLng(opts.center);
- map = new defaults.classes.Map($this.get(0), opts);
- }
- }
- /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
- => function with latLng resolution
- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
- /**
- * Initialize google.maps.Map object
- **/
- this.map = function(args){
- newMap(args.latLng, args);
- attachEvents($this, args, map);
- manageEnd(args, map);
- };
- /**
- * destroy an existing instance
- **/
- this.destroy = function(args){
- store.clear();
- $this.empty();
- if (map){
- map = null;
- }
- manageEnd(args, true);
- };
- /**
- * add an infowindow
- **/
- this.infowindow = function(args){
- var objs = [], multiple = "values" in args.todo;
- if (!multiple){
- if (args.latLng) {
- args.opts.position = args.latLng;
- }
- args.todo.values = [{options:args.opts}];
- }
- $.each(args.todo.values, function(i, value){
- var id, obj, todo = tuple(args, value);
- todo.options.position = todo.options.position ? toLatLng(todo.options.position) : toLatLng(value.latLng);
- if (!map){
- newMap(todo.options.position);
- }
- obj = new defaults.classes.InfoWindow(todo.options);
- if (obj && ((todo.open === undef) || todo.open)){
- if (multiple){
- obj.open(map, todo.anchor ? todo.anchor : undef);
- } else {
- obj.open(map, todo.anchor ? todo.anchor : (args.latLng ? undef : (args.session.marker ? args.session.marker : undef)));
- }
- }
- objs.push(obj);
- id = store.add({todo:todo}, "infowindow", obj);
- attachEvents($this, {todo:todo}, obj, id);
- });
- manageEnd(args, multiple ? objs : objs[0]);
- };
- /**
- * add a circle
- **/
- this.circle = function(args){
- var objs = [], multiple = "values" in args.todo;
- if (!multiple){
- args.opts.center = args.latLng || toLatLng(args.opts.center);
- args.todo.values = [{options:args.opts}];
- }
- if (!args.todo.values.length){
- manageEnd(args, false);
- return;
- }
- $.each(args.todo.values, function(i, value){
- var id, obj, todo = tuple(args, value);
- todo.options.center = todo.options.center ? toLatLng(todo.options.center) : toLatLng(value);
- if (!map){
- newMap(todo.options.center);
- }
- todo.options.map = map;
- obj = new defaults.classes.Circle(todo.options);
- objs.push(obj);
- id = store.add({todo:todo}, "circle", obj);
- attachEvents($this, {todo:todo}, obj, id);
- });
- manageEnd(args, multiple ? objs : objs[0]);
- };
- /**
- * add an overlay
- **/
- this.overlay = function(args, internal){
- var objs = [], multiple = "values" in args.todo;
- if (!multiple){
- args.todo.values = [{latLng: args.latLng, options: args.opts}];
- }
- if (!args.todo.values.length){
- manageEnd(args, false);
- return;
- }
- if (!OverlayView.__initialised) {
- OverlayView.prototype = new defaults.classes.OverlayView();
- OverlayView.__initialised = true;
- }
- $.each(args.todo.values, function(i, value){
- var id, obj, todo = tuple(args, value),
- $div = $(document.createElement("div")).css({
- border: "none",
- borderWidth: "0px",
- position: "absolute"
- });
- $div.append(todo.options.content);
- obj = new OverlayView(map, todo.options, toLatLng(todo) || toLatLng(value), $div);
- objs.push(obj);
- $div = null; // memory leak
- if (!internal){
- id = store.add(args, "overlay", obj);
- attachEvents($this, {todo:todo}, obj, id);
- }
- });
- if (internal){
- return objs[0];
- }
- manageEnd(args, multiple ? objs : objs[0]);
- };
- /**
- * returns address structure from latlng
- **/
- this.getaddress = function(args){
- callback(args, args.results, args.status);
- task.ack();
- };
- /**
- * returns latlng from an address
- **/
- this.getlatlng = function(args){
- callback(args, args.results, args.status);
- task.ack();
- };
- /**
- * return the max zoom of a location
- **/
- this.getmaxzoom = function(args){
- maxZoomService().getMaxZoomAtLatLng(
- args.latLng,
- function(result) {
- callback(args, result.status === google.maps.MaxZoomStatus.OK ? result.zoom : false, status);
- task.ack();
- }
- );
- };
- /**
- * return the elevation of a location
- **/
- this.getelevation = function(args){
- var i, locations = [],
- f = function(results, status){
- callback(args, status === google.maps.ElevationStatus.OK ? results : false, status);
- task.ack();
- };
- if (args.latLng){
- locations.push(args.latLng);
- } else {
- locations = array(args.todo.locations || []);
- for(i=0; i<locations.length; i++){
- locations[i] = toLatLng(locations[i]);
- }
- }
- if (locations.length){
- elevationService().getElevationForLocations({locations:locations}, f);
- } else {
- if (args.todo.path && args.todo.path.length){
- for(i=0; i<args.todo.path.length; i++){
- locations.push(toLatLng(args.todo.path[i]));
- }
- }
- if (locations.length){
- elevationService().getElevationAlongPath({path:locations, samples:args.todo.samples}, f);
- } else {
- task.ack();
- }
- }
- };
- /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
- => function without latLng resolution
- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
- /**
- * define defaults values
- **/
- this.defaults = function(args){
- $.each(args.todo, function(name, value){
- if (typeof defaults[name] === "object"){
- defaults[name] = $.extend({}, defaults[name], value);
- } else {
- defaults[name] = value;
- }
- });
- task.ack(true);
- };
- /**
- * add a rectangle
- **/
- this.rectangle = function(args){
- var objs = [], multiple = "values" in args.todo;
- if (!multiple){
- args.todo.values = [{options:args.opts}];
- }
- if (!args.todo.values.length){
- manageEnd(args, false);
- return;
- }
- $.each(args.todo.values, function(i, value){
- var id, obj, todo = tuple(args, value);
- todo.options.bounds = todo.options.bounds ? toLatLngBounds(todo.options.bounds) : toLatLngBounds(value);
- if (!map){
- newMap(todo.options.bounds.getCenter());
- }
- todo.options.map = map;
- obj = new defaults.classes.Rectangle(todo.options);
- objs.push(obj);
- id = store.add({todo:todo}, "rectangle", obj);
- attachEvents($this, {todo:todo}, obj, id);
- });
- manageEnd(args, multiple ? objs : objs[0]);
- };
- /**
- * add a polygone / polyline
- **/
- function poly(args, poly, path){
- var objs = [], multiple = "values" in args.todo;
- if (!multiple){
- args.todo.values = [{options:args.opts}];
- }
- if (!args.todo.values.length){
- manageEnd(args, false);
- return;
- }
- newMap();
- $.each(args.todo.values, function(_, value){
- var id, i, j, obj, todo = tuple(args, value);
- if (todo.options[path]){
- if (todo.options[path][0][0] && $.isArray(todo.options[path][0][0])){
- for(i=0; i<todo.options[path].length; i++){
- for(j=0; j<todo.options[path][i].length; j++){
- todo.options[path][i][j] = toLatLng(todo.options[path][i][j]);
- }
- }
- } else {
- for(i=0; i<todo.options[path].length; i++){
- todo.options[path][i] = toLatLng(todo.options[path][i]);
- }
- }
- }
- todo.options.map = map;
- obj = new google.maps[poly](todo.options);
- objs.push(obj);
- id = store.add({todo:todo}, poly.toLowerCase(), obj);
- attachEvents($this, {todo:todo}, obj, id);
- });
- manageEnd(args, multiple ? objs : objs[0]);
- }
- this.polyline = function(args){
- poly(args, "Polyline", "path");
- };
- this.polygon = function(args){
- poly(args, "Polygon", "paths");
- };
- /**
- * add a traffic layer
- **/
- this.trafficlayer = function(args){
- newMap();
- var obj = store.get("trafficlayer");
- if (!obj){
- obj = new defaults.classes.TrafficLayer();
- obj.setMap(map);
- store.add(args, "trafficlayer", obj);
- }
- manageEnd(args, obj);
- };
- /**
- * add a bicycling layer
- **/
- this.bicyclinglayer = function(args){
- newMap();
- var obj = store.get("bicyclinglayer");
- if (!obj){
- obj = new defaults.classes.BicyclingLayer();
- obj.setMap(map);
- store.add(args, "bicyclinglayer", obj);
- }
- manageEnd(args, obj);
- };
- /**
- * add a ground overlay
- **/
- this.groundoverlay = function(args){
- args.opts.bounds = toLatLngBounds(args.opts.bounds);
- if (args.opts.bounds){
- newMap(args.opts.bounds.getCenter());
- }
- var id, obj = new defaults.classes.GroundOverlay(args.opts.url, args.opts.bounds, args.opts.opts);
- obj.setMap(map);
- id = store.add(args, "groundoverlay", obj);
- manageEnd(args, obj, id);
- };
- /**
- * set a streetview
- **/
- this.streetviewpanorama = function(args){
- if (!args.opts.opts){
- args.opts.opts = {};
- }
- if (args.latLng){
- args.opts.opts.position = args.latLng;
- } else if (args.opts.opts.position){
- args.opts.opts.position = toLatLng(args.opts.opts.position);
- }
- if (args.todo.divId){
- args.opts.container = document.getElementById(args.todo.divId)
- } else if (args.opts.container){
- args.opts.container = $(args.opts.container).get(0);
- }
- var id, obj = new defaults.classes.StreetViewPanorama(args.opts.container, args.opts.opts);
- if (obj){
- map.setStreetView(obj);
- }
- id = store.add(args, "streetviewpanorama", obj);
- manageEnd(args, obj, id);
- };
- this.kmllayer = function(args){
- var objs = [], multiple = "values" in args.todo;
- if (!multiple){
- args.todo.values = [{options:args.opts}];
- }
- if (!args.todo.values.length){
- manageEnd(args, false);
- return;
- }
- $.each(args.todo.values, function(i, value){
- var id, obj, options, todo = tuple(args, value);
- if (!map){
- newMap();
- }
- options = todo.options;
- // compatibility 5.0-
- if (todo.options.opts) {
- options = todo.options.opts;
- if (todo.options.url) {
- options.url = todo.options.url;
- }
- }
- // -- end --
- options.map = map;
- if (googleVersionMin("3.10")) {
- obj = new defaults.classes.KmlLayer(options);
- } else {
- obj = new defaults.classes.KmlLayer(options.url, options);
- }
- objs.push(obj);
- id = store.add({todo:todo}, "kmllayer", obj);
- attachEvents($this, {todo:todo}, obj, id);
- });
- manageEnd(args, multiple ? objs : objs[0]);
- };
- /**
- * add a fix panel
- **/
- this.panel = function(args){
- newMap();
- var id, x= 0, y=0, $content,
- $div = $(document.createElement("div"));
- $div.css({
- position: "absolute",
- zIndex: 1000,
- visibility: "hidden"
- });
- if (args.opts.content){
- $content = $(args.opts.content);
- $div.append($content);
- $this.first().prepend($div);
- if (args.opts.left !== undef){
- x = args.opts.left;
- } else if (args.opts.right !== undef){
- x = $this.width() - $content.width() - args.opts.right;
- } else if (args.opts.center){
- x = ($this.width() - $content.width()) / 2;
- }
- if (args.opts.top !== undef){
- y = args.opts.top;
- } else if (args.opts.bottom !== undef){
- y = $this.height() - $content.height() - args.opts.bottom;
- } else if (args.opts.middle){
- y = ($this.height() - $content.height()) / 2
- }
- $div.css({
- top: y,
- left: x,
- visibility: "visible"
- });
- }
- id = store.add(args, "panel", $div);
- manageEnd(args, $div, id);
- $div = null; // memory leak
- };
- /**
- * Create an InternalClusterer object
- **/
- function createClusterer(raw){
- var internalClusterer = new InternalClusterer($this, map, raw),
- todo = {},
- styles = {},
- thresholds = [],
- isInt = /^[0-9]+$/,
- calculator,
- k;
- for(k in raw){
- if (isInt.test(k)){
- thresholds.push(1*k); // cast to int
- styles[k] = raw[k];
- styles[k].width = styles[k].width || 0;
- styles[k].height = styles[k].height || 0;
- } else {
- todo[k] = raw[k];
- }
- }
- thresholds.sort(function (a, b) { return a > b});
- // external calculator
- if (todo.calculator){
- calculator = function(indexes){
- var data = [];
- $.each(indexes, function(i, index){
- data.push(internalClusterer.value(index));
- });
- return todo.calculator.apply($this, [data]);
- };
- } else {
- calculator = function(indexes){
- return indexes.length;
- };
- }
- // set error function
- internalClusterer.error(function(){
- error.apply(that, arguments);
- });
- // set display function
- internalClusterer.display(function(cluster){
- var i, style, atodo, obj, offset,
- cnt = calculator(cluster.indexes);
- // look for the style to use
- if (raw.force || cnt > 1) {
- for(i = 0; i < thresholds.length; i++) {
- if (thresholds[i] <= cnt) {
- style = styles[thresholds[i]];
- }
- }
- }
- if (style){
- offset = style.offset || [-style.width/2, -style.height/2];
- // create a custom overlay command
- // nb: 2 extends are faster that a deeper extend
- atodo = $.extend({}, todo);
- atodo.options = $.extend({
- pane: "overlayLayer",
- content:style.content ? style.content.replace("CLUSTER_COUNT", cnt) : "",
- offset:{
- x: ("x" in offset ? offset.x : offset[0]) || 0,
- y: ("y" in offset ? offset.y : offset[1]) || 0
- }
- },
- todo.options || {});
- obj = that.overlay({todo:atodo, opts:atodo.options, latLng:toLatLng(cluster)}, true);
- atodo.options.pane = "floatShadow";
- atodo.options.content = $(document.createElement("div")).width(style.width+"px").height(style.height+"px").css({cursor:"pointer"});
- shadow = that.overlay({todo:atodo, opts:atodo.options, latLng:toLatLng(cluster)}, true);
- // store data to the clusterer
- todo.data = {
- latLng: toLatLng(cluster),
- markers:[]
- };
- $.each(cluster.indexes, function(i, index){
- todo.data.markers.push(internalClusterer.value(index));
- if (internalClusterer.markerIsSet(index)){
- internalClusterer.marker(index).setMap(null);
- }
- });
- attachEvents($this, {todo:todo}, shadow, undef, {main:obj, shadow:shadow});
- internalClusterer.store(cluster, obj, shadow);
- } else {
- $.each(cluster.indexes, function(i, index){
- internalClusterer.marker(index).setMap(map);
- });
- }
- });
- return internalClusterer;
- }
- /**
- * add a marker
- **/
- this.marker = function(args){
- var multiple = "values" in args.todo,
- init = !map;
- if (!multiple){
- args.opts.position = args.latLng || toLatLng(args.opts.position);
- args.todo.values = [{options:args.opts}];
- }
- if (!args.todo.values.length){
- manageEnd(args, false);
- return;
- }
- if (init){
- newMap();
- }
- if (args.todo.cluster && !map.getBounds()){ // map not initialised => bounds not available : wait for map if clustering feature is required
- google.maps.event.addListenerOnce(map, "bounds_changed", function() { that.marker.apply(that, [args]); });
- return;
- }
- if (args.todo.cluster){
- var clusterer, internalClusterer;
- if (args.todo.cluster instanceof Clusterer){
- clusterer = args.todo.cluster;
- internalClusterer = store.getById(clusterer.id(), true);
- } else {
- internalClusterer = createClusterer(args.todo.cluster);
- clusterer = new Clusterer(globalId(args.todo.id, true), internalClusterer);
- store.add(args, "clusterer", clusterer, internalClusterer);
- }
- internalClusterer.beginUpdate();
- $.each(args.todo.values, function(i, value){
- var todo = tuple(args, value);
- todo.options.position = todo.options.position ? toLatLng(todo.options.position) : toLatLng(value);
- if (todo.options.position) {
- todo.options.map = map;
- if (init){
- map.setCenter(todo.options.position);
- init = false;
- }
- internalClusterer.add(todo, value);
- }
- });
- internalClusterer.endUpdate();
- manageEnd(args, clusterer);
- } else {
- var objs = [];
- $.each(args.todo.values, function(i, value){
- var id, obj, todo = tuple(args, value);
- todo.options.position = todo.options.position ? toLatLng(todo.options.position) : toLatLng(value);
- if (todo.options.position) {
- todo.options.map = map;
- if (init){
- map.setCenter(todo.options.position);
- init = false;
- }
- obj = new defaults.classes.Marker(todo.options);
- objs.push(obj);
- id = store.add({todo:todo}, "marker", obj);
- attachEvents($this, {todo:todo}, obj, id);
- }
- });
- manageEnd(args, multiple ? objs : objs[0]);
- }
- };
- /**
- * return a route
- **/
- this.getroute = function(args){
- args.opts.origin = toLatLng(args.opts.origin, true);
- args.opts.destination = toLatLng(args.opts.destination, true);
- directionsService().route(
- args.opts,
- function(results, status) {
- callback(args, status == google.maps.DirectionsStatus.OK ? results : false, status);
- task.ack();
- }
- );
- };
- /**
- * add a direction renderer
- **/
- this.directionsrenderer = function(args){
- args.opts.map = map;
- var id, obj = new google.maps.DirectionsRenderer(args.opts);
- if (args.todo.divId){
- obj.setPanel(document.getElementById(args.todo.divId));
- } else if (args.todo.container){
- obj.setPanel($(args.todo.container).get(0));
- }
- id = store.add(args, "directionsrenderer", obj);
- manageEnd(args, obj, id);
- };
- /**
- * returns latLng of the user
- **/
- this.getgeoloc = function(args){
- manageEnd(args, args.latLng);
- };
- /**
- * add a style
- **/
- this.styledmaptype = function(args){
- newMap();
- var obj = new defaults.classes.StyledMapType(args.todo.styles, args.opts);
- map.mapTypes.set(args.todo.id, obj);
- manageEnd(args, obj);
- };
- /**
- * add an imageMapType
- **/
- this.imagemaptype = function(args){
- newMap();
- var obj = new defaults.classes.ImageMapType(args.opts);
- map.mapTypes.set(args.todo.id, obj);
- manageEnd(args, obj);
- };
- /**
- * autofit a map using its overlays (markers, rectangles ...)
- **/
- this.autofit = function(args){
- var bounds = new google.maps.LatLngBounds();
- $.each(store.all(), function(i, obj){
- if (obj.getPosition){
- bounds.extend(obj.getPosition());
- } else if (obj.getBounds){
- bounds.extend(obj.getBounds().getNorthEast());
- bounds.extend(obj.getBounds().getSouthWest());
- } else if (obj.getPaths){
- obj.getPaths().forEach(function(path){
- path.forEach(function(latLng){
- bounds.extend(latLng);
- });
- });
- } else if (obj.getPath){
- obj.getPath().forEach(function(latLng){
- bounds.extend(latLng);""
- });
- } else if (obj.getCenter){
- bounds.extend(obj.getCenter());
- } else if (obj instanceof Clusterer){
- obj = store.getById(obj.id(), true);
- if (obj){
- obj.autofit(bounds);
- }
- }
- });
- if (!bounds.isEmpty() && (!map.getBounds() || !map.getBounds().equals(bounds))){
- if ("maxZoom" in args.todo){
- // fitBouds Callback event => detect zoom level and check maxZoom
- google.maps.event.addListenerOnce(
- map,
- "bounds_changed",
- function() {
- if (this.getZoom() > args.todo.maxZoom){
- this.setZoom(args.todo.maxZoom);
- }
- }
- );
- }
- map.fitBounds(bounds);
- }
- manageEnd(args, true);
- };
- /**
- * remove objects from a map
- **/
- this.clear = function(args){
- if (typeof args.todo === "string"){
- if (store.clearById(args.todo) || store.objClearById(args.todo)){
- manageEnd(args, true);
- return;
- }
- args.todo = {name:args.todo};
- }
- if (args.todo.id){
- $.each(array(args.todo.id), function(i, id){
- store.clearById(id) || store.objClearById(id);
- });
- } else {
- store.clear(array(args.todo.name), args.todo.last, args.todo.first, args.todo.tag);
- store.objClear(array(args.todo.name), args.todo.last, args.todo.first, args.todo.tag);
- }
- manageEnd(args, true);
- };
- /**
- * run a function on each items selected
- **/
- this.exec = function(args){
- var that = this;
- $.each(array(args.todo.func), function(i, func){
- $.each(that.get(args.todo, true, args.todo.hasOwnProperty("full") ? args.todo.full : true), function(j, res){
- func.call($this, res);
- });
- });
- manageEnd(args, true);
- };
- /**
- * return objects previously created
- **/
- this.get = function(args, direct, full){
- var name, res,
- todo = direct ? args : args.todo;
- if (!direct) {
- full = todo.full;
- }
- if (typeof todo === "string"){
- res = store.getById(todo, false, full) || store.objGetById(todo);
- if (res === false){
- name = todo;
- todo = {};
- }
- } else {
- name = todo.name;
- }
- if (name === "map"){
- res = map;
- }
- if (!res){
- res = [];
- if (todo.id){
- $.each(array(todo.id), function(i, id) {
- res.push(store.getById(id, false, full) || store.objGetById(id));
- });
- if (!$.isArray(todo.id)) {
- res = res[0];
- }
- } else {
- $.each(name ? array(name) : [undef], function(i, aName) {
- var result;
- if (todo.first){
- result = store.get(aName, false, todo.tag, full);
- if (result) res.push(result);
- } else if (todo.all){
- $.each(store.all(aName, todo.tag, full), function(i, result){
- res.push(result);
- });
- } else {
- result = store.get(aName, true, todo.tag, full);
- if (result) res.push(result);
- }
- });
- if (!todo.all && !$.isArray(name)) {
- res = res[0];
- }
- }
- }
- res = $.isArray(res) || !todo.all ? res : [res];
- if (direct){
- return res;
- } else {
- manageEnd(args, res);
- }
- };
- /**
- * return the distance between an origin and a destination
- *
- **/
- this.getdistance = function(args){
- var i;
- args.opts.origins = array(args.opts.origins);
- for(i=0; i<args.opts.origins.length; i++){
- args.opts.origins[i] = toLatLng(args.opts.origins[i], true);
- }
- args.opts.destinations = array(args.opts.destinations);
- for(i=0; i<args.opts.destinations.length; i++){
- args.opts.destinations[i] = toLatLng(args.opts.destinations[i], true);
- }
- distanceMatrixService().getDistanceMatrix(
- args.opts,
- function(results, status) {
- callback(args, status === google.maps.DistanceMatrixStatus.OK ? results : false, status);
- task.ack();
- }
- );
- };
- /**
- * trigger events on the map
- **/
- this.trigger = function(args){
- if (typeof args.todo === "string"){
- google.maps.event.trigger(map, args.todo);
- } else {
- var options = [map, args.todo.eventName];
- if (args.todo.var_args) {
- $.each(args.todo.var_args, function(i, v){
- options.push(v);
- });
- }
- google.maps.event.trigger.apply(google.maps.event, options);
- }
- callback(args);
- task.ack();
- };
- }
- /**
- * Return true if get is a direct call
- * it means :
- * - get is the only key
- * - get has no callback
- * @param obj {Object} The request to check
- * @return {Boolean}
- */
- function isDirectGet(obj) {
- var k;
- if (!typeof obj === "object" || !obj.hasOwnProperty("get")){
- return false;
- }
- for(k in obj) {
- if (k !== "get") {
- return false;
- }
- }
- return !obj.get.hasOwnProperty("callback");
- }
- //-----------------------------------------------------------------------//
- // jQuery plugin
- //-----------------------------------------------------------------------//
- $.fn.gmap3 = function(){
- var i, list = [], empty = true, results = [];
- // init library
- initDefaults();
- // store all arguments in a todo list
- for(i=0; i<arguments.length; i++){
- if (arguments[i]){
- list.push(arguments[i]);
- }
- }
- // resolve empty call - run init
- if (!list.length) {
- list.push("map");
- }
- // 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);
- }
- if (list.length === 1 && (list[0] === "get" || isDirectGet(list[0]))){
- if (list[0] === "get") {
- results.push(gmap3.get("map", true));
- } else {
- results.push(gmap3.get(list[0].get, true, list[0].get.full));
- }
- } 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;
- }
- }
- return this;
- }
- })(jQuery);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement