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