PageSourceSearch

https://rsez.lv/wp-content/plugins/codespacing-progress-map/assets/js/gmap3/gmap3.js?ver=5.7.1

js rsez.lv collected 2026-10-02 19:10:55 UTC 68,434 bytes, 2,612 lines download raw bytes

1/*!
2 *  GMAP3 Plugin for jQuery
3 *  Version   : 6.1.0
4 *  Date      : 2016-01-01
5 *  Author    : DEMONTE Jean-Baptiste
6 *  Contact   : [email protected]
7 *  Web site  : http://gmap3.net
8 *  Licence   : GPL v3 : http://www.gnu.org/licenses/gpl.html
9 *  
10 *  Copyright (c) 2010-2014 Jean-Baptiste DEMONTE
11 *  All rights reserved.
12 */
13;(function ($, undef) {
14
15var defaults, gm,
16  gId = 0,
17  isFunction = $.isFunction,
18  isArray = $.isArray;
19
20function isObject(m) {
21  return typeof m === "object";
22}
23
24function isString(m) {
25  return typeof m === "string";
26}
27
28function isNumber(m) {
29  return typeof m === "number";
30}
31
32function isUndefined(m) {
33  return m === undef;
34}
35
36/**
37 * Initialize default values
38 * defaults are defined at first gmap3 call to pass the rails asset pipeline and jasmine while google library is not yet loaded
39 */
40function initDefaults() {
41  gm = google.maps;
42  if (!defaults) {
43    defaults = {
44      verbose: false,
45      queryLimit: {
46        attempt: 5,
47        delay: 250, // setTimeout(..., delay + random);
48        random: 250
49      },
50      classes: (function () {
51        var r = {};
52        $.each("Map Marker InfoWindow Circle Rectangle OverlayView StreetViewPanorama KmlLayer TrafficLayer TransitLayer BicyclingLayer GroundOverlay StyledMapType ImageMapType".split(" "), function (_, k) {
53          r[k] = gm[k];
54        });
55        return r;
56      }()),
57      map: {
58        mapTypeId : gm.MapTypeId.ROADMAP,
59        center: [46.578498, 2.457275],
60        zoom: 2
61      },
62      overlay: {
63        pane: "floatPane",
64        content: "",
65        offset: {
66          x: 0,
67          y: 0
68        }
69      },
70      geoloc: {
71        getCurrentPosition: {
72          maximumAge: 60000,
73          timeout: 5000,
74		  enableHighAccuracy: true,  // @ Added by Codespacing to fix issue when user ignores Geolocation prompt
75        }
76      }
77    }
78  }
79}
80
81
82/**
83 * Generate a new ID if not defined
84 * @param id {string} (optional)
85 * @param simulate {boolean} (optional)
86 * @returns {*}
87 */
88function globalId(id, simulate) {
89  return isUndefined(id) ? "gmap3_" + (simulate ? gId + 1 : ++gId) : id;
90}
91
92
93/**
94 * Return true if current version of Google Maps is equal or above to these in parameter
95 * @param version {string} Minimal version required
96 * @return {Boolean}
97 */
98function googleVersionMin(version) {
99  var i,
100    gmVersion = gm.version.split(".");
101  version = version.split(".");
102  for (i = 0; i < gmVersion.length; i++) {
103    gmVersion[i] = parseInt(gmVersion[i], 10);
104  }
105  for (i = 0; i < version.length; i++) {
106    version[i] = parseInt(version[i], 10);
107    if (gmVersion.hasOwnProperty(i)) {
108      if (gmVersion[i] < version[i]) {
109        return false;
110      }
111    } else {
112      return false;
113    }
114  }
115  return true;
116}
117
118
119/**
120 * attach events from a container to a sender
121 * td[
122 *  events => { eventName => function, }
123 *  onces  => { eventName => function, }
124 *  data   => mixed data
125 * ]
126 **/
127function attachEvents($container, args, sender, id, senders) {
128  var td = args.td || {},
129    context = {
130      id: id,
131      data: td.data,
132      tag: td.tag
133    };
134  function bind(items, handler) {
135    if (items) {
136      $.each(items, function (name, f) {
137        var self = $container, fn = f;
138        if (isArray(f)) {
139          self = f[0];
140          fn = f[1];
141        }
142        handler(sender, name, function (event) {
143          fn.apply(self, [senders || sender, event, context]);
144        });
145      });
146    }
147  }
148  bind(td.events, gm.event.addListener);
149  bind(td.onces, gm.event.addListenerOnce);
150}
151
152/**
153 * Extract keys from object
154 * @param obj {object}
155 * @returns {Array}
156 */
157function getKeys(obj) {
158  var k, keys = [];
159  for (k in obj) {
160    if (obj.hasOwnProperty(k)) {
161      keys.push(k);
162    }
163  }
164  return keys;
165}
166
167/**
168 * copy a key content
169 **/
170function copyKey(target, key) {
171  var i,
172    args = arguments;
173  for (i = 2; i < args.length; i++) {
174    if (key in args[i]) {
175      if (args[i].hasOwnProperty(key)) {
176        target[key] = args[i][key];
177        return;
178      }
179    }
180  }
181}
182
183/**
184 * Build a tuple
185 * @param args {object}
186 * @param value {object}
187 * @returns {object}
188 */
189function tuple(args, value) {
190  var k, i,
191    keys = ["data", "tag", "id", "events",  "onces"],
192    td = {};
193
194  // "copy" the common data
195  if (args.td) {
196    for (k in args.td) {
197      if (args.td.hasOwnProperty(k)) {
198        if ((k !== "options") && (k !== "values")) {
199          td[k] = args.td[k];
200        }
201      }
202    }
203  }
204  // "copy" some specific keys from value first else args.td
205  for (i = 0; i < keys.length; i++) {
206    copyKey(td, keys[i], value, args.td);
207  }
208
209  // create an extended options
210  td.options = $.extend({}, args.opts || {}, value.options || {});
211
212  return td;
213}
214
215/**
216 * Log error
217 */
218function error() {
219  if (defaults.verbose) {
220    var i, err = [];
221    if (window.console && (isFunction(console.error))) {
222      for (i = 0; i < arguments.length; i++) {
223        err.push(arguments[i]);
224      }
225      console.error.apply(console, err);
226    } else {
227      err = "";
228      for (i = 0; i < arguments.length; i++) {
229        err += arguments[i].toString() + " ";
230      }
231      alert(err);
232    }
233  }
234}
235
236/**
237 * return true if mixed is usable as number
238 **/
239function numeric(mixed) {
240  return (isNumber(mixed) || isString(mixed)) && mixed !== "" && !isNaN(mixed);
241}
242
243/**
244 * convert data to array
245 **/
246function array(mixed) {
247  var k, a = [];
248  if (!isUndefined(mixed)) {
249    if (isObject(mixed)) {
250      if (isNumber(mixed.length)) {
251        a = mixed;
252      } else {
253        for (k in mixed) {
254          a.push(mixed[k]);
255        }
256      }
257    } else {
258      a.push(mixed);
259    }
260  }
261  return a;
262}
263
264/**
265 * create a function to check a tag
266 */
267function ftag(tag) {
268  if (tag) {
269    if (isFunction(tag)) {
270      return tag;
271    }
272    tag = array(tag);
273    return function (val) {
274      var i;
275      if (isUndefined(val)) {
276        return false;
277      }
278      if (isObject(val)) {
279        for (i = 0; i < val.length; i++) {
280          if ($.inArray(val[i], tag) >= 0) {
281            return true;
282          }
283        }
284        return false;
285      }
286      return $.inArray(val, tag) >= 0;
287    };
288  }
289}
290
291
292/**
293 * convert mixed [ lat, lng ] objet to gm.LatLng
294 **/
295function toLatLng(mixed, emptyReturnMixed, noFlat) {
296  var empty = emptyReturnMixed ? mixed : null;
297  if (!mixed || (isString(mixed))) {
298    return empty;
299  }
300  // defined latLng
301  if (mixed.latLng) {
302    return toLatLng(mixed.latLng);
303  }
304  // gm.LatLng object
305  if (mixed instanceof gm.LatLng) {
306    return mixed;
307  }
308  // {lat:X, lng:Y} object
309  if (numeric(mixed.lat)) {
310    return new gm.LatLng(mixed.lat, mixed.lng);
311  }
312  // [X, Y] object
313  if (!noFlat && isArray(mixed)) {
314    if (!numeric(mixed[0]) || !numeric(mixed[1])) {
315      return empty;
316    }
317    return new gm.LatLng(mixed[0], mixed[1]);
318  }
319  return empty;
320}
321
322/**
323 * convert mixed [ sw, ne ] object by gm.LatLngBounds
324 **/
325function toLatLngBounds(mixed) {
326  var ne, sw;
327  if (!mixed || mixed instanceof gm.LatLngBounds) {
328    return mixed || null;
329  }
330  if (isArray(mixed)) {
331    if (mixed.length === 2) {
332      ne = toLatLng(mixed[0]);
333      sw = toLatLng(mixed[1]);
334    } else if (mixed.length === 4) {
335      ne = toLatLng([mixed[0], mixed[1]]);
336      sw = toLatLng([mixed[2], mixed[3]]);
337    }
338  } else {
339    if (("ne" in mixed) && ("sw" in mixed)) {
340      ne = toLatLng(mixed.ne);
341      sw = toLatLng(mixed.sw);
342    } else if (("n" in mixed) && ("e" in mixed) && ("s" in mixed) && ("w" in mixed)) {
343      ne = toLatLng([mixed.n, mixed.e]);
344      sw = toLatLng([mixed.s, mixed.w]);
345    }
346  }
347  if (ne && sw) {
348    return new gm.LatLngBounds(sw, ne);
349  }
350  return null;
351}
352
353/**
354 * resolveLatLng
355 **/
356function resolveLatLng(ctx, method, runLatLng, args, attempt) {
357  var latLng = runLatLng ? toLatLng(args.td, false, true) : false,
358    conf = latLng ?  {latLng: latLng} : (args.td.address ? (isString(args.td.address) ? {address: args.td.address} : args.td.address) : false),
359    cache = conf ? geocoderCache.get(conf) : false,
360    self = this;
361  if (conf) {
362    attempt = attempt || 0; // convert undefined to int
363    if (cache) {
364      args.latLng = cache.results[0].geometry.location;
365      args.results = cache.results;
366      args.status = cache.status;
367      method.apply(ctx, [args]);
368    } else {
369      if (conf.location) {
370        conf.location = toLatLng(conf.location);
371      }
372      if (conf.bounds) {
373        conf.bounds = toLatLngBounds(conf.bounds);
374      }
375      geocoder().geocode(
376        conf,
377        function (results, status) {
378          if (status === gm.GeocoderStatus.OK) {
379            geocoderCache.store(conf, {results: results, status: status});
380            args.latLng = results[0].geometry.location;
381            args.results = results;
382            args.status = status;
383            method.apply(ctx, [args]);
384          } else if ((status === gm.GeocoderStatus.OVER_QUERY_LIMIT) && (attempt < defaults.queryLimit.attempt)) {
385            setTimeout(
386              function () {
387                resolveLatLng.apply(self, [ctx, method, runLatLng, args, attempt + 1]);
388              },
389              defaults.queryLimit.delay + Math.floor(Math.random() * defaults.queryLimit.random)
390            );
391          } else {
392            error("geocode failed", status, conf);
393            args.latLng = args.results = false;
394            args.status = status;
395            method.apply(ctx, [args]);
396          }
397        }
398      );
399    }
400  } else {
401    args.latLng = toLatLng(args.td, false, true);
402    method.apply(ctx, [args]);
403  }
404}
405
406function resolveAllLatLng(list, ctx, method, args) {
407  var self = this, i = -1;
408
409  function resolve() {
410    // look for next address to resolve
411    do {
412      i++;
413    } while ((i < list.length) && !("address" in list[i]));
414
415    // no address found, so run method
416    if (i >= list.length) {
417      method.apply(ctx, [args]);
418      return;
419    }
420
421    resolveLatLng(
422      self,
423      function (args) {
424        delete args.td;
425        $.extend(list[i], args);
426        resolve.apply(self, []); // resolve next (using apply avoid too much recursion)
427      },
428      true,
429      {td: list[i]}
430    );
431  }
432  resolve();
433}
434
435
436
437/**
438 * geolocalise the user and return a LatLng
439 **/
440function geoloc(ctx, method, args) {
441  // @Start | Added by Codespacing to fix issue when user ignores Geolocation prompt
442  var location_timeout = setTimeout(function(){
443  	  args.latLng = false;
444	  method.apply(ctx, [args]);
445  }, 5000);
446  // @End | Codespacing
447  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
448  if (navigator && navigator.geolocation) {
449    navigator.geolocation.getCurrentPosition(
450      function (pos) {
451        clearTimeout(location_timeout);// @Added by Codespacing to fix issue when user ignores Geolocation prompt
452		if (!is_echo) {
453          is_echo = true;
454          args.latLng = new gm.LatLng(pos.coords.latitude, pos.coords.longitude);
455          method.apply(ctx, [args]);
456        }
457      },
458      function () {
459		clearTimeout(location_timeout);// @Added by Codespacing to fix issue when user ignores Geolocation prompt
460        if (!is_echo) {
461          is_echo = true;
462          args.latLng = false;
463          method.apply(ctx, [args]);
464        }
465      },
466      args.opts.getCurrentPosition
467    );	
468  } else {
469	clearTimeout(location_timeout);// @Added by Codespacing to fix issue when user ignores Geolocation prompt
470    args.latLng = false;
471    method.apply(ctx, [args]);
472  }
473}
474
475/**
476 * Return true if get is a direct call
477 * it means :
478 *   - get is the only key
479 *   - get has no callback
480 * @param obj {Object} The request to check
481 * @return {Boolean}
482 */
483function isDirectGet(obj) {
484  var k,
485    result = false;
486  if (isObject(obj) && obj.hasOwnProperty("get")) {
487    for (k in obj) {
488      if (k !== "get") {
489        return false;
490      }
491    }
492    result = !obj.get.hasOwnProperty("callback");
493  }
494  return result;
495}
496var services = {},
497  geocoderCache = new GeocoderCache();
498
499
500function geocoder(){
501  if (!services.geocoder) {
502    services.geocoder = new gm.Geocoder();
503  }
504  return services.geocoder;
505}
506/**
507 * Class GeocoderCache
508 * @constructor
509 */
510function GeocoderCache() {
511  var cache = [];
512
513  this.get = function (request) {
514    if (cache.length) {
515      var i, j, k, item, eq,
516        keys = getKeys(request);
517      for (i = 0; i < cache.length; i++) {
518        item = cache[i];
519        eq = keys.length === item.keys.length;
520        for (j = 0;
520 (j < keys.length) && eq; j++) {
521          k = keys[j];
522          eq = k in item.request;
523          if (eq) {
524            if (isObject(request[k]) && ("equals" in request[k]) && isFunction(request[k])) {
525              eq = request[k].equals(item.request[k]);
526            } else {
527              eq = request[k] === item.request[k];
528            }
529          }
530        }
531        if (eq) {
532          return item.results;
533        }
534      }
535    }
536  };
537
538  this.store = function (request, results) {
539    cache.push({request: request, keys: getKeys(request), results: results});
540  };
541}
542/**
543 * Class Stack
544 * @constructor
545 */
546function Stack() {
547  var st = [],
548    self = this;
549
550  self.empty = function () {
551    return !st.length;
552  };
553
554  self.add = function (v) {
555    st.push(v);
556  };
557
558  self.get = function () {
559    return st.length ? st[0] : false;
560  };
561
562  self.ack = function () {
563    st.shift();
564  };
565}
566/**
567 * Class Store
568 * @constructor
569 */
570function Store() {
571  var store = {}, // name => [id, ...]
572    objects = {}, // id => object
573    self = this;
574
575  function normalize(res) {
576    return {
577      id: res.id,
578      name: res.name,
579      object: res.obj,
580      tag: res.tag,
581      data: res.data
582    };
583  }
584
585  /**
586   * add a mixed to the store
587   **/
588  self.add = function (args, name, obj, sub) {
589    var td = args.td || {},
590      id = globalId(td.id);
591    if (!store[name]) {
592      store[name] = [];
593    }
594    if (id in objects) { // object already exists: remove it
595      self.clearById(id);
596    }
597    objects[id] = {obj: obj, sub: sub, name: name, id: id, tag: td.tag, data: td.data};
598    store[name].push(id);
599    return id;
600  };
601
602  /**
603   * return a stored object by its id
604   **/
605  self.getById = function (id, sub, full) {
606    var result = false;
607    if (id in objects) {
608      if (sub) {
609        result = objects[id].sub;
610      } else if (full) {
611        result = normalize(objects[id]);
612      } else {
613        result = objects[id].obj;
614      }
615    }
616    return result;
617  };
618
619  /**
620   * return a stored value
621   **/
622  self.get = function (name, last, tag, full) {
623    var n, id, check = ftag(tag);
624    if (!store[name] || !store[name].length) {
625      return null;
626    }
627    n = store[name].length;
628    while (n) {
629      n--;
630      id = store[name][last ? n : store[name].length - n - 1];
631      if (id && objects[id]) {
632        if (check && !check(objects[id].tag)) {
633          continue;
634        }
635        return full ? normalize(objects[id]) : objects[id].obj;
636      }
637    }
638    return null;
639  };
640
641  /**
642   * return all stored values
643   **/
644  self.all = function (name, tag, full) {
645    var result = [],
646      check = ftag(tag),
647      find = function (n) {
648        var i, id;
649        for (i = 0; i < store[n].length; i++) {
650          id = store[n][i];
651          if (id && objects[id]) {
652            if (check && !check(objects[id].tag)) {
653              continue;
654            }
655            result.push(full ? normalize(objects[id]) : objects[id].obj);
656          }
657        }
658      };
659    if (name in store) {
660      find(name);
661    } else if (isUndefined(name)) { // internal use only
662      for (name in store) {
663        find(name);
664      }
665    }
666    return result;
667  };
668
669  /**
670   * hide and remove an object
671   **/
672  function rm(obj) {
673    // Google maps element
674    if (isFunction(obj.setMap)) {
675      obj.setMap(null);
676    }
677    // jQuery
678    if (isFunction(obj.remove)) {
679      obj.remove();
680    }
681    // internal (cluster)
682    if (isFunction(obj.free)) {
683      obj.free();
684    }
685    obj = null;
686  }
687
688  /**
689   * remove one object from the store
690   **/
691  self.rm = function (name, check, pop) {
692    var idx, id;
693    if (!store[name]) {
694      return false;
695    }
696    if (check) {
697      if (pop) {
698        for (idx = store[name].length - 1; idx >= 0; idx--) {
699          id = store[name][idx];
700          if (check(objects[id].tag)) {
701            break;
702          }
703        }
704      } else {
705        for (idx = 0; idx < store[name].length; idx++) {
706          id = store[name][idx];
707          if (check(objects[id].tag)) {
708            break;
709          }
710        }
711      }
712    } else {
713      idx = pop ? store[name].length - 1 : 0;
714    }
715    if (!(idx in store[name])) {
716      return false;
717    }
718    return self.clearById(store[name][idx], idx);
719  };
720
721  /**
722   * remove object from the store by its id
723   **/
724  self.clearById = function (id, idx) {
725    if (id in objects) {
726      var i, name = objects[id].name;
727      for (i = 0; isUndefined(idx) && i < store[name].length; i++) {
728        if (id === store[name][i]) {
729          idx = i;
730        }
731      }
732      rm(objects[id].obj);
733      if (objects[id].sub) {
734        rm(objects[id].sub);
735      }
736      delete objects[id];
737      store[name].splice(idx, 1);
738      return true;
739    }
740    return false;
741  };
742
743  /**
744   * return an object from a container object in the store by its id
745   * ! for now, only cluster manage this feature
746   **/
747  self.objGetById = function (id) {
748    var result, idx;
749    if (store.clusterer) {
750      for (idx in store.clusterer) {
751        if ((result = objects[store.clusterer[idx]].obj.getById(id)) !== false) {
752          return result;
753        }
754      }
755    }
756    return false;
757  };
758
759  /**
760   * remove object from a container object in the store by its id
761   * ! for now, only cluster manage this feature
762   **/
763  self.objClearById = function (id) {
764    var idx;
765    if (store.clusterer) {
766      for (idx in store.clusterer) {
767        if (objects[store.clusterer[idx]].obj.clearById(id)) {
768          return true;
769        }
770      }
771    }
772    return null;
773  };
774
775  /**
776   * remove objects from the store
777   **/
778  self.clear = function (list, last, first, tag) {
779    var k, i, name,
780      check = ftag(tag);
781    if (!list || !list.length) {
782      list = [];
783      for (k in store) {
784        list.push(k);
785      }
786    } else {
787      list = array(list);
788    }
789    for (i = 0; i < list.length; i++) {
790      name = list[i];
791      if (last) {
792        self.rm(name, check, true);
793      } else if (first) {
794        self.rm(name, check, false);
795      } else { // all
796        while (self.rm(name, check, false)) {
797        }
798      }
799    }
800  };
801
802  /**
803   * remove object from a container object in the store by its tags
804   * ! for now, only cluster manage this feature
805   **/
806  self.objClear = function (list, last, first, tag) {
807    var idx;
808    if (store.clusterer && ($.inArray("marker", list) >= 0 || !list.length)) {
809      for (idx in store.clusterer) {
810        objects[store.clusterer[idx]].obj.clear(last, first, tag);
811      }
812    }
813  };
814}
815/**
816 * Class Task
817 * @param ctx
818 * @param onEnd
819 * @param td
820 * @constructor
821 */
822function Task(ctx, onEnd, td) {
823  var session = {},
824    self = this,
825    current,
826    resolve = {
827      latLng: { // function => bool (=> address = latLng)
828        map: false,
829        marker: false,
830        infowindow: false,
831        circle: false,
832        overlay: false,
833        getlatlng: false,
834        getmaxzoom: false,
835        getelevation: false,
836        streetviewpanorama: false,
837        getaddress: true
838      },
839      geoloc: {
840        getgeoloc: true
841      }
842    };
843
844  function unify(td) {
845    var result = {};
846    result[td] = {};
847    return result;
848  }
849
850  if (isString(td)) {
851    td =  unify(td);
852  }
853
854  function next() {
855    var k;
856    for (k in td) {
857      if (td.hasOwnProperty(k) && !session.hasOwnProperty(k)) {
858        return k;
859      }
860    }
861  }
862
863  self.run = function () {
864    var k, opts;
865    while (k = next()) {
866      if (isFunction(ctx[k])) {
867        current = k;
868        opts = $.extend(true, {}, defaults[k] || {}, td[k].options || {});
869        if (k in resolve.latLng) {
870          if (td[k].values) {
871            resolveAllLatLng(td[k].values, ctx, ctx[k], {td: td[k], opts: opts, session: session});
872          } else {
873            resolveLatLng(ctx, ctx[k], resolve.latLng[k], {td: td[k], opts: opts, session: session});
874          }
875        } else if (k in resolve.geoloc) {
876          geoloc(ctx, ctx[k], {td: td[k], opts: opts, session: session});
877        } else {
878          ctx[k].apply(ctx, [{td: td[k], opts: opts, session: session}]);
879        }
880        return; // wait until ack
881      } else {
882        session[k] = null;
883      }
884    }
885    onEnd.apply(ctx, [td, session]);
886  };
887
888  self.ack = function(result){
889    session[current] = result;
890    self.run.apply(self, []);
891  };
892}
893
894function directionsService(){
895  if (!services.ds) {
896    services.ds = new gm.DirectionsService();
897  }
898  return services.ds;
899}
900
901function distanceMatrixService() {
902  if (!services.dms) {
903    services.dms = new gm.DistanceMatrixService();
904  }
905  return services.dms;
906}
907
908function maxZoomService() {
909  if (!services.mzs) {
910    services.mzs = new gm.MaxZoomService();
911  }
912  return services.mzs;
913}
914
915function elevationService() {
916  if (!services.es) {
917    services.es = new gm.ElevationService();
918  }
919  return services.es;
920}
921
922  /**
923   * Usefull to get a projection
924   * => done in a function, to let dead-code analyser works without google library loaded
925   **/
926  function newEmptyOverlay(map, radius) {
927    function Overlay() {
928      var self = this;
929      self.onAdd = function () {};
930      self.onRemove = function () {};
931      self.draw = function () {};
932      return defaults.classes.OverlayView.apply(self, []);
933    }
934    Overlay.prototype = defaults.classes.OverlayView.prototype;
935    var obj = new Overlay();
936    obj.setMap(map);
937    return obj;
938  }
939
940/**
941 * Class InternalClusterer
942 * This class manage clusters thanks to "td" objects
943 *
944 * Note:
945 * Individuals marker are created on the fly thanks to the td objects, they are
946 * first set to null to keep the indexes synchronised with the td list
947 * This is the "display" function, set by the gmap3 object, which uses theses data
948 * to create markers when clusters are not required
949 * To remove a marker, the objects are deleted and set not null in arrays
950 *    markers[key]
951 *      = null : marker exist but has not been displayed yet
952 *      = false : marker has been removed
953 **/
954function InternalClusterer($container, map, raw) {
955  var timer, projection,
956    ffilter, fdisplay, ferror, // callback function
957    updating = false,
958    updated = false,
959    redrawing = false,
960    ready = false,
961    enabled = true,
962    self = this,
963    events =  [],
964    store = {},   // combin of index (id1-id2-...) => object
965    ids = {},     // unique id => index
966    idxs = {},    // index => unique id
967    markers = [], // index => marker
968    tds = [],   // index => td or null if removed
969    values = [],  // index => value
970    overlay = newEmptyOverlay(map, raw.radius);
971
972  main();
973
974  function prepareMarker(index) {
975    if (!markers[index]) {
976      delete tds[index].options.map;
977      markers[index] = new defaults.classes.Marker(tds[index].options);
978      attachEvents($container, {td: tds[index]}, markers[index], tds[index].id);
979    }
980  }
981
982  /**
983   * return a marker by its id, null if not yet displayed and false if no exist or removed
984   **/
985  self.getById = function (id) {
986    if (id in ids) {
987      prepareMarker(ids[id]);
988      return  markers[ids[id]];
989    }
990    return false;
991  };
992
993  /**
994   * remove one object from the store
995   **/
996  self.rm = function (id) {
997    var index = ids[id];
998    if (markers[index]) { // can be null
999      markers[index].setMap(null);
1000    }
1001    delete markers[index];
1002    markers[index] = false;
1003
1004    delete tds[index];
1005    tds[index] = false;
1006
1007    delete values[index];
1008    values[index] = false;
1009
1010    delete ids[id];
1011    delete idxs[index];
1012    updated = true;
1013  };
1014
1015  /**
1016   * remove a marker by its id
1017   **/
1018  self.clearById = function (id) {
1019    if (id in ids){
1020      self.rm(id);
1021      return true;
1022    }
1023  };
1024
1025  /**
1026   * remove objects from the store
1027   **/
1028  self.clear = function (last, first, tag) {
1029    var start, stop, step, index, i,
1030      list = [],
1031      check = ftag(tag);
1032    if (last) {
1033      start = tds.length - 1;
1034      stop = -1;
1035      step = -1;
1036    } else {
1037      start = 0;
1038      stop =  tds.length;
1039      step = 1;
1040    }
1041    for (index = start; index !== stop; index += step) {
1042      if (tds[index]) {
1043        if (!check || check(tds[index].tag)) {
1044          list.push(idxs[index]);
1045          if (first || last) {
1046            break;
1047          }
1048        }
1049      }
1050    }
1051    for (i = 0; i < list.length; i++) {
1052      self.rm(list[i]);
1053    }
1054  };
1055
1056  // add a "marker td" to the cluster
1057  self.add = function (td, value) {
1058    td.id = globalId(td.id);
1059    self.clearById(td.id);
1060    ids[td.id] = markers.length;
1061    idxs[markers.length] = td.id;
1062    markers.push(null); // null = marker not yet created / displayed
1063    tds.push(td);
1064    values.push(value);
1065    updated = true;
1066  };
1067
1068  // add a real marker to the cluster
1069  self.addMarker = function (marker, td) {
1070    td = td || {};
1071    td.id = globalId(td.id);
1072    self.clearById(td.id);
1073    if (!td.options) {
1074      td.options = {};
1075    }
1076    td.options.position = marker.getPosition();
1077    attachEvents($container, {td: td}, marker, td.id);
1078    ids[td.id] = markers.length;
1079    idxs[markers.length] = td.id;
1080    markers.push(marker);
1081    tds.push(td);
1082    values.push(td.data || {});
1083    updated = true;
1084  };
1085
1086  // return a "marker td" by its index
1087  self.td = function (index) {
1088    return tds[index];
1089  };
1090
1091  // return a "marker value" by its index
1092  self.value = function (index) {
1093    return values[index];
1094  };
1095
1096  // return a marker by its index
1097  self.marker = function (index) {
1098    if (index in markers) {
1099      prepareMarker(index);
1100      return  markers[index];
1101    }
1102    return false;
1103  };
1104
1105  // return a marker by its index
1106  self.markerIsSet = function (index) {
1107    return Boolean(markers[index]);
1108  };
1109
1110  // store a new marker instead if the default "false"
1111  self.setMarker = function (index, marker) {
1112    markers[index] = marker;
1113  };
1114
1115  // link the visible overlay to the logical data (to hide overlays later)
1116  self.store = function (cluster, obj, shadow) {
1117    store[cluster.ref] = {obj: obj, shadow: shadow};
1118  };
1119
1120  // free all objects
1121  self.free = function () {
1122    var i;
1123    for(i = 0; i < events.length; i++) {
1124      gm.event.removeListener(events[i]);
1125    }
1126    events = [];
1127
1128    $.each(store, function (key) {
1129      flush(key);
1130    });
1131    store = {};
1132
1133    $.each(tds, function (i) {
1134      tds[i] = null;
1135    });
1136    tds = [];
1137
1138    $.each(markers, function (i) {
1139      if (markers[i]) { // false = removed
1140        markers[i].setMap(null);
1141        delete markers[i];
1142      }
1143    });
1144    markers = [];
1145
1146    $.each(values, function (i) {
1147      delete values[i];
1148    });
1149    values = [];
1150
1151    ids = {};
1152    idxs = {};
1153  };
1154
1155  // link the display function
1156  self.filter = function (f) {
1157    ffilter = f;
1158    redraw();
1159  };
1160
1161  // enable/disable the clustering feature
1162  self.enable = function (value) {
1163    if (enabled !== value) {
1164      enabled = value;
1165      redraw();
1166    }
1167  };
1168
1169  // link the display function
1170  self.display = function (f) {
1171    fdisplay = f;
1172  };
1173
1174  // link the errorfunction
1175  self.error = function (f) {
1176    ferror = f;
1177  };
1178
1179  // lock the redraw
1180  self.beginUpdate = function () {
1181    updating = true;
1182  };
1183
1184  // unlock the redraw
1185  self.endUpdate = function () {
1186    updating = false;
1187    if (updated) {
1188      redraw();
1189    }
1190  };
1191
1192  // extends current bounds with internal markers
1193  self.autofit = function (bounds) {
1194    var i;
1195    for (i = 0; i < tds.length; i++) {
1196      if (tds[i]) {
1197        bounds.extend(tds[i].options.position);
1198      }
1199    }
1200  };
1201
1202  // bind events
1203  function main() {
1204    projection = overlay.getProjection();
1205    if (!projection) {
1206      setTimeout(function () { main.apply(self, []); }, 25);
1207      return;
1208    }
1209    ready = true;
1210    events.push(gm.event.addListener(map, "zoom_changed", delayRedraw));
1211    events.push(gm.event.addListener(map, "bounds_changed", delayRedraw));
1212    redraw();
1213  }
1214
1215  // flush overlays
1216  function flush(key) {
1217    if (isObject(store[key])) { // is overlay
1218      if (isFunction(store[key].obj.setMap)) {
1219        store[key].obj.setMap(null);
1220      }
1221      if (isFunction(store[key].obj.remove)) {
1222        store[key].obj.remove();
1223      }
1224      if (isFunction(store[key].shadow.remove)) {
1225        store[key].obj.remove();
1226      }
1227      if (isFunction(store[key].shadow.setMap)) {
1228        store[key].shadow.setMap(null);
1229      }
1230      delete store[key].obj;
1231      delete store[key].shadow;
1232    } else if (markers[key]) { // marker not removed
1233      markers[key].setMap(null);
1234      // don't remove the marker object, it may be displayed later
1235    }
1236    delete store[key];
1237  }
1238
1239  /**
1240   * return the distance between 2 latLng couple into meters
1241   * Params :
1242   *  Lat1, Lng1, Lat2, Lng2
1243   *  LatLng1, Lat2, Lng2
1244   *  Lat1, Lng1, LatLng2
1245   *  LatLng1, LatLng2
1246   **/
1247  function distanceInMeter() {
1248    var lat1, lat2, lng1, lng2, e, f, g, h,
1249      cos = Math.cos,
1250      sin = Math.sin,
1251      args = arguments;
1252    if (args[0] instanceof gm.LatLng) {
1253      lat1 = args[0].lat();
1254      lng1 = args[0].lng();
1255      if (args[1] instanceof gm.LatLng) {
1256        lat2 = args[1].lat();
1257        lng2 = args[1].lng();
1258      } else {
1259        lat2 = args[1];
1260        lng2 = args[2];
1261      }
1262    } else {
1263      lat1 = args[0];
1264      lng1 = args[1];
1265      if (args[2] instanceof gm.LatLng) {
1266        lat2 = args[2].lat();
1267        lng2 = args[2].lng();
1268      } else {
1269        lat2 = args[2];
1270        lng2 = args[3];
1271      }
1272    }
1273    e = Math.PI * lat1 / 180;
1274    f = Math.PI * lng1 / 180;
1275    g = Math.PI * lat2 / 180;
1276    h = Math.PI * lng2 / 180;
1277    return 1000 * 6371 * Math.acos(Math.min(cos(e) * cos(g) * cos(f) * cos(h) + cos(e) * sin(f) * cos(g) * sin(h) + sin(e) * sin(g), 1));
1278  }
1279
1280  // extend the visible bounds
1281  function extendsMapBounds() {
1282    var radius = distanceInMeter(map.getCenter(), map.getBounds().getNorthEast()),
1283      circle = new gm.Circle({
1284        center: map.getCenter(),
1285        radius: 1.25 * radius // + 25%
1286      });
1287    return circle.getBounds();
1288  }
1289
1290  // return an object where keys are store keys
1291  function getStoreKeys() {
1292    var k,
1293      keys = {};
1294    for (k in store) {
1295      keys[k] = true;
1296    }
1297    return keys;
1298  }
1299
1300  // async the delay function
1301  function delayRedraw() {
1302    clearTimeout(timer);
1303    timer = setTimeout(redraw, 25);
1304  }
1305
1306  // generate bounds extended by radius
1307  function extendsBounds(latLng) {
1308    var p = projection.fromLatLngToDivPixel(latLng),
1309      ne = projection.fromDivPixelToLatLng(new gm.Point(p.x + raw.radius, p.y - raw.radius)),
1310      sw = projection.fromDivPixelToLatLng(new gm.Point(p.x - raw.radius, p.y + raw.radius));
1311    return new gm.LatLngBounds(sw, ne);
1312  }
1313
1314  // run the clustering process and call the display function
1315  function redraw() {
1316    if (updating || redrawing || !ready) {
1317      return;
1318    }
1319
1320    var i, j, k, indexes, check = false, bounds, cluster, position, previous, lat, lng, loop,
1321      keys = [],
1322      used = {},
1323      zoom = map.getZoom(),
1324      forceDisabled = ("maxZoom" in raw) && (zoom > raw.maxZoom),
1325      previousKeys = getStoreKeys();
1326
1327    // reset flag
1328    updated = false;
1329
1330    if (zoom > 3) {
1331      // extend the bounds of the visible map to manage clusters near the boundaries
1332      bounds = extendsMapBounds();
1333
1334      // check contain only if boundaries are valid
1335      check = bounds.getSouthWest().lng() < bounds.getNorthEast().lng();
1336    }
1337
1338    // calculate positions of "visibles" markers (in extended bounds)
1339    for (i = 0; i < tds.length; i++) {
1340      if (tds[i] && (!check || bounds.contains(tds[i].options.position)) && (!ffilter || ffilter(values[i]))) {
1341        keys.push(i);
1342      }
1343    }
1344
1345    // for each "visible" marker, search its neighbors to create a cluster
1346    // we can't do a classical "for" loop, because, analysis can bypass a marker while focusing on cluster
1347    while (1) {
1348      i = 0;
1349      while (used[i] && (i < keys.length)) { // look for the next marker not used
1350        i++;
1351      }
1352      if (i === keys.length) {
1353        break;
1354      }
1355
1356      indexes = [];
1357
1358      if (enabled && !forceDisabled) {
1359        loop = 10;
1360        do {
1361          previous = indexes;
1362          indexes = [];
1363          loop--;
1364
1365          if (previous.length) {
1366            position = bounds.getCenter();
1367          } else {
1368            position = tds[keys[i]].options.position;
1369          }
1370          bounds = extendsBounds(position);
1371
1372          for (j = i; j < keys.length; j++) {
1373            if (used[j]) {
1374              continue;
1375            }
1376            if (bounds.contains(tds[keys[j]].options.position)) {
1377              indexes.push(j);
1378            }
1379          }
1380        } while ((previous.length < indexes.length) && (indexes.length > 1) && loop);
1381      } else {
1382        for (j = i; j < keys.length; j++) {
1383          if (!used[j]) {
1384            indexes.push(j);
1385            break;
1386          }
1387        }
1388      }
1389
1390      cluster = {indexes: [], ref: []};
1391      lat = lng = 0;
1392      for (k = 0; k < indexes.length; k++) {
1393        used[indexes[k]] = true;
1394        cluster.indexes.push(keys[indexes[k]]);
1395        cluster.ref.push(keys[indexes[k]]);
1396        lat += tds[keys[indexes[k]]].options.position.lat();
1397        lng += tds[keys[indexes[k]]].options.position.lng();
1398      }
1399      lat /= indexes.length;
1400      lng /= indexes.length;
1401      cluster.latLng = new gm.LatLng(lat, lng);
1402
1403      cluster.ref = cluster.ref.join("-");
1404
1405      if (cluster.ref in previousKeys) { // cluster doesn't change
1406        delete previousKeys[cluster.ref]; // remove this entry, these still in this array will be removed
1407      } else { // cluster is new
1408        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)
1409          store[cluster.ref] = true;
1410        }
1411        fdisplay(cluster);
1412      }
1413    }
1414
1415    // flush the previous overlays which are not still used
1416    $.each(previousKeys, function (key) {
1417      flush(key);
1418    });
1419    redrawing = false;
1420  }
1421}
1422/**
1423 * Class Clusterer
1424 * a facade with limited method for external use
1425 **/
1426function Clusterer(id, internalClusterer) {
1427  var self = this;
1428  self.id = function () {
1429    return id;
1430  };
1431  self.filter = function (f) {
1432    internalClusterer.filter(f);
1433  };
1434  self.enable = function () {
1435    internalClusterer.enable(true);
1436  };
1437  self.disable = function () {
1438    internalClusterer.enable(false);
1439  };
1440  self.add = function (marker, td, lock) {
1441    if (!lock) {
1442      internalClusterer.beginUpdate();
1443    }
1444    internalClusterer.addMarker(marker, td);
1445    if (!lock) {
1446      internalClusterer.endUpdate();
1447    }
1448  };
1449  self.getById = function (id) {
1450    return internalClusterer.getById(id);
1451  };
1452  self.clearById = function (id, lock) {
1453    var result;
1454    if (!lock) {
1455      internalClusterer.beginUpdate();
1456    }
1457    result = internalClusterer.clearById(id);
1458    if (!lock) {
1459      internalClusterer.endUpdate();
1460    }
1461    return result;
1462  };
1463  self.clear = function (last, first, tag, lock) {
1464    if (!lock) {
1465      internalClusterer.beginUpdate();
1466    }
1467    internalClusterer.clear(last, first, tag);
1468    if (!lock) {
1469      internalClusterer.endUpdate();
1470    }
1471  };
1472}
1473
1474/**
1475 * Class OverlayView
1476 * @constructor
1477 */
1478function OverlayView(map, opts, latLng, $div) {
1479  var self = this,
1480    listeners = [];
1481
1482  defaults.classes.OverlayView.call(self);
1483  self.setMap(map);
1484
1485  self.onAdd = function () {
1486    var panes = self.getPanes();
1487    if (opts.pane in panes) {
1488      $(panes[opts.pane]).append($div);
1489    }
1490    $.each("dblclick click mouseover mousemove mouseout mouseup mousedown".split(" "), function (i, name) {
1491      listeners.push(
1492        gm.event.addDomListener($div[0], name, function (e) {
1493          $.Event(e).stopPropagation();
1494          gm.event.trigger(self, name, [e]);
1495          self.draw();
1496        })
1497      );
1498    });
1499    listeners.push(
1500      gm.event.addDomListener($div[0], "contextmenu", function (e) {
1501        $.Event(e).stopPropagation();
1502        gm.event.trigger(self, "rightclick", [e]);
1503        self.draw();
1504      })
1505    );
1506  };
1507
1508  self.getPosition = function () {
1509    return latLng;
1510  };
1511
1512  self.setPosition = function (newLatLng) {
1513    latLng = newLatLng;
1514    self.draw();
1515  };
1516
1517  self.draw = function () {
1518    var ps = self.getProjection().fromLatLngToDivPixel(latLng);
1519    $div
1520      .css("left", (ps.x + opts.offset.x) + "px")
1521      .css("top", (ps.y + opts.offset.y) + "px");
1522  };
1523
1524  self.onRemove = function () {
1525    var i;
1526    for (i = 0; i < listeners.length; i++) {
1527      gm.event.removeListener(listeners[i]);
1528    }
1529    $div.remove();
1530  };
1531
1532  self.hide = function () {
1533    $div.hide();
1534  };
1535
1536  self.show = function () {
1537    $div.show();
1538  };
1539
1540  self.toggle = function () {
1541    if ($div) {
1542      if ($div.is(":visible")) {
1543        self.show();
1544      } else {
1545        self.hide();
1546      }
1547    }
1548  };
1549
1550  self.toggleDOM = function () {
1551    self.setMap(self.getMap() ? null : map);
1552  };
1553
1554  self.getDOMElement = function () {
1555    return $div[0];
1556  };
1557}
1558
1559function Gmap3($this) {
1560  var self = this,
1561    stack = new Stack(),
1562    store = new Store(),
1563    map = null,
1564    task;
1565
1566  /**
1567   * if not running, start next action in stack
1568   **/
1569  function run() {
1570    if (!task && (task = stack.get())) {
1571      task.run();
1572    }
1573  }
1574
1575  /**
1576   * called when action in finished, to acknoledge the current in stack and start next one
1577   **/
1578  function end() {
1579    task = null;
1580    stack.ack();
1581    run.call(self); // restart to high level scope
1582  }
1583
1584//-----------------------------------------------------------------------//
1585// Tools
1586//-----------------------------------------------------------------------//
1587
1588  /**
1589   * execute callback functions
1590   **/
1591  function callback(args) {
1592    var params,
1593      cb = args.td.callback;
1594    if (cb) {
1595      params = Array.prototype.slice.call(arguments, 1);
1596      if (isFunction(cb)) {
1597        cb.apply($this, params);
1598      } else if (isArray(cb)) {
1599        if (isFunction(cb[1])) {
1600          cb[1].apply(cb[0], params);
1601        }
1602      }
1603    }
1604  }
1605
1606  /**
1607   * execute ending functions
1608   **/
1609  function manageEnd(args, obj, id) {
1610    if (id) {
1611      attachEvents($this, args, obj, id);
1612    }
1613    callback(args, obj);
1614	// @Start | Added by Codespacing to fix issue when user ignores Geolocation prompt
1615	if (typeof task.ack === "function"){
1616    	task.ack(obj);
1617	}
1618	// @End | Codespacing
1619  }
1620
1621  /**
1622   * initialize the map if not yet initialized
1623   **/
1624  function newMap(latLng, args) {
1625    args = args || {};
1626    var opts = args.td && args.td.options ? args.td.options : 0;
1627    if (map) {
1628      if (opts) {
1629        if (opts.center) {
1630          opts.center = toLatLng(opts.center);
1631        }
1632        map.setOptions(opts);
1633      }
1634    } else {
1635      opts = args.opts || $.extend(true, {}, defaults.map, opts || {});
1636      opts.center = latLng || toLatLng(opts.center);
1637      map = new defaults.classes.Map($this.get(0), opts);
1638    }
1639  }
1640
1641  /**
1642   * store actions to execute in a stack manager
1643   **/
1644  self._plan = function (list) {
1645    var k;
1646    for (k = 0; k < list.length; k++) {
1647      stack.add(new Task(self, end, list[k]));
1648    }
1649    run();
1650  };
1651
1652  /**
1653   * Initialize gm.Map object
1654   **/
1655  self.map = function (args) {
1656    newMap(args.latLng, args);
1657    attachEvents($this, args, map);
1658    manageEnd(args, map);
1659  };
1660
1661  /**
1662   * destroy an existing instance
1663   **/
1664  self.destroy = function (args) {
1665    store.clear();
1666    $this.empty();
1667    if (map) {
1668      map = null;
1669    }
1670    manageEnd(args, true);
1671  };
1672
1673  /**
1674   * add an overlay
1675   **/
1676  self.overlay = function (args, internal) {
1677    var objs = [],
1678      multiple = "values" in args.td;
1679    if (!multiple) {
1680      args.td.values = [{latLng: args.latLng, options: args.opts}];
1681    }
1682    if (!args.td.values.length) {
1683      manageEnd(args, false);
1684      return;
1685    }
1686    if (!OverlayView.__initialised) {
1687      OverlayView.prototype = new defaults.classes.OverlayView();
1688      OverlayView.__initialised = true;
1689    }
1690    $.each(args.td.values, function (i, value) {
1691      var id, obj, td = tuple(args, value),
1692        $div = $(document.createElement("div")).css({
1693          border: "none",
1694          borderWidth: 0,
1695          position: "absolute"
1696        });
1697      $div.append(td.options.content);
1698      obj = new OverlayView(map, td.options, toLatLng(td) || toLatLng(value), $div);
1699      objs.push(obj);
1700      $div = null; // memory leak
1701      if (!internal) {
1702        id = store.add(args, "overlay", obj);
1703        attachEvents($this, {td: td}, obj, id);
1704      }
1705    });
1706    if (internal) {
1707      return objs[0];
1708    }
1709    manageEnd(args, multiple ? objs : objs[0]);
1710  };
1711
1712  /**
1713   * Create an InternalClusterer object
1714   **/
1715  function createClusterer(raw) {
1716    var internalClusterer = new InternalClusterer($this, map, raw),
1717      td = {},
1718      styles = {},
1719      thresholds = [],
1720      isInt = /^[0-9]+$/,
1721      calculator,
1722      k;
1723
1724    for (k in raw) {
1725      if (isInt.test(k)) {
1726        thresholds.push(1 * k); // cast to int
1727        styles[k] = raw[k];
1728        styles[k].width = styles[k].width || 0;
1729        styles[k].height = styles[k].height || 0;
1730      } else {
1731        td[k] = raw[k];
1732      }
1733    }
1734    thresholds.sort(function (a, b) { return a > b; });
1735
1736    // external calculator
1737    if (td.calculator) {
1738      calculator = function (indexes) {
1739        var data = [];
1740        $.each(indexes, function (i, index) {
1741          data.push(internalClusterer.value(index));
1742        });
1743        return td.calculator.apply($this, [data]);
1744      };
1745    } else {
1746      calculator = function (indexes) {
1747        return indexes.length;
1748      };
1749    }
1750
1751    // set error function
1752    internalClusterer.error(function () {
1753      error.apply(self, arguments);
1754    });
1755
1756    // set display function
1757    internalClusterer.display(function (cluster) {
1758      var i, style, atd, obj, offset, shadow,
1759        cnt = calculator(cluster.indexes);
1760
1761      // look for the style to use
1762      if (raw.force || cnt > 1) {
1763        for (i = 0; i < thresholds.length; i++) {
1764          if (thresholds[i] <= cnt) {
1765            style = styles[thresholds[i]];
1766          }
1767        }
1768      }
1769
1770      if (style) {
1771        offset = style.offset || [-style.width/2, -style.height/2];
1772        // create a custom overlay command
1773        // nb: 2 extends are faster self a deeper extend
1774        atd = $.extend({}, td);
1775        atd.options = $.extend({
1776            pane: "overlayLayer",
1777            content: style.content ? style.content.replace("CLUSTER_COUNT", cnt) : "",
1778            offset: {
1779              x: ("x" in offset ? offset.x : offset[0]) || 0,
1780              y: ("y" in offset ? offset.y : offset[1]) || 0
1781            }
1782          },
1783          td.options || {});
1784
1785        obj = self.overlay({td: atd, opts: atd.options, latLng: toLatLng(cluster)}, true);
1786
1787        atd.options.pane = "floatShadow";
1788        atd.options.content = $(document.createElement("div")).width(style.width + "px").height(style.height + "px").css({cursor: "pointer"});
1789        shadow = self.overlay({td: atd, opts: atd.options, latLng: toLatLng(cluster)}, true);
1790
1791        // store data to the clusterer
1792        td.data = {
1793          latLng: toLatLng(cluster),
1794          markers:[]
1795        };
1796        $.each(cluster.indexes, function(i, index){
1797          td.data.markers.push(internalClusterer.value(index));
1798          if (internalClusterer.markerIsSet(index)){
1799            internalClusterer.marker(index).setMap(null);
1800          }
1801        });
1802        attachEvents($this, {td: td}, shadow, undef, {main: obj, shadow: shadow});
1803        internalClusterer.store(cluster, obj, shadow);
1804      } else {
1805        $.each(cluster.indexes, function (i, index) {
1806          internalClusterer.marker(index).setMap(map);
1807        });
1808      }
1809    });
1810
1811    return internalClusterer;
1812  }
1813
1814  /**
1815   *  add a marker
1816   **/
1817  self.marker = function (args) {
1818    var objs,
1819      clusterer, internalClusterer,
1820      multiple = "values" in args.td,
1821      init = !map;
1822    if (!multiple) {
1823      args.opts.position = args.latLng || toLatLng(args.opts.position);
1824      args.td.values = [{options: args.opts}];
1825    }
1826    if (!args.td.values.length) {
1827      manageEnd(args, false);
1828      return;
1829    }
1830    if (init) {
1831      newMap();
1832    }
1833    if (args.td.cluster && !map.getBounds()) { // map not initialised => bounds not available : wait for map if clustering feature is required
1834      gm.event.addListenerOnce(map, "bounds_changed", function () { self.marker.apply(self, [args]); });
1835      return;
1836    }
1837    if (args.td.cluster) {
1838      if (args.td.cluster instanceof Clusterer) {
1839        clusterer = args.td.cluster;
1840        internalClusterer = store.getById(clusterer.id(), true);
1841      } else {
1842        internalClusterer = createClusterer(args.td.cluster);
1843        clusterer = new Clusterer(globalId(args.td.id, true), internalClusterer);
1844        store.add(args, "clusterer", clusterer, internalClusterer);
1845      }
1846      internalClusterer.beginUpdate();
1847
1848      $.each(args.td.values, function (i, value) {
1849        var td = tuple(args, value);
1850        td.options.position = td.options.position ? toLatLng(td.options.position) : toLatLng(value);
1851        if (td.options.position) {
1852          td.options.map = map;
1853          if (init) {
1854            map.setCenter(td.options.position);
1855            init = false;
1856          }
1857          internalClusterer.add(td, value);
1858        }
1859      });
1860
1861      internalClusterer.endUpdate();
1862      manageEnd(args, clusterer);
1863
1864    } else {
1865      objs = [];
1866      $.each(args.td.values, function (i, value) {
1867        var id, obj,
1868          td = tuple(args, value);
1869        td.options.position = td.options.position ? toLatLng(td.options.position) : toLatLng(value);
1870        if (td.options.position) {
1871          td.options.map = map;
1872          if (init) {
1873            map.setCenter(td.options.position);
1874            init = false;
1875          }
1876          obj = new defaults.classes.Marker(td.options);
1877          objs.push(obj);
1878          id = store.add({td: td}, "marker", obj);
1879          attachEvents($this, {td: td}, obj, id);
1880        }
1881      });
1882      manageEnd(args, multiple ? objs : objs[0]);
1883    }
1884  };
1885
1886  /**
1887   * return a route
1888   **/
1889  self.getroute = function (args) {
1890    args.opts.origin = toLatLng(args.opts.origin, true);
1891    args.opts.destination = toLatLng(args.opts.destination, true);
1892    directionsService().route(
1893      args.opts,
1894      function (results, status) {
1895        callback(args, status === gm.DirectionsStatus.OK ? results : false, status);
1896        task.ack();
1897      }
1898    );
1899  };
1900
1901  /**
1902   * return the distance between an origin and a destination
1903   *
1904   **/
1905  self.getdistance = function (args) {
1906    var i;
1907    args.opts.origins = array(args.opts.origins);
1908    for (i = 0; i < args.opts.origins.length; i++) {
1909      args.opts.origins[i] = toLatLng(args.opts.origins[i], true);
1910    }
1911    args.opts.destinations = array(args.opts.destinations);
1912    for (i = 0; i < args.opts.destinations.length; i++) {
1913      args.opts.destinations[i] = toLatLng(args.opts.destinations[i], true);
1914    }
1915    distanceMatrixService().getDistanceMatrix(
1916      args.opts,
1917      function (results, status) {
1918        callback(args, status === gm.DistanceMatrixStatus.OK ? results : false, status);
1919        task.ack();
1920      }
1921    );
1922  };
1923
1924  /**
1925   * add an infowindow
1926   **/
1927  self.infowindow = function (args) {
1928    var objs = [],
1929      multiple = "values" in args.td;
1930    if (!multiple) {
1931      if (args.latLng) {
1932        args.opts.position = args.latLng;
1933      }
1934      args.td.values = [{options: args.opts}];
1935    }
1936    $.each(args.td.values, function (i, value) {
1937      var id, obj,
1938        td = tuple(args, value);
1939      td.options.position = td.options.position ? toLatLng(td.options.position) : toLatLng(value.latLng);
1940      if (!map) {
1941        newMap(td.options.position);
1942      }
1943      obj = new defaults.classes.InfoWindow(td.options);
1944      if (obj && (isUndefined(td.open) || td.open)) {
1945        if (multiple) {
1946          obj.open(map, td.anchor || undef);
1947        } else {
1948          obj.open(map, td.anchor || (args.latLng ? undef : (args.session.marker ? args.session.marker : undef)));
1949        }
1950      }
1951      objs.push(obj);
1952      id = store.add({td: td}, "infowindow", obj);
1953      attachEvents($this, {td: td}, obj, id);
1954    });
1955    manageEnd(args, multiple ? objs : objs[0]);
1956  };
1957
1958  /**
1959   * add a circle
1960   **/
1961  self.circle = function (args) {
1962    var objs = [],
1963      multiple = "values" in args.td;
1964    if (!multiple) {
1965      args.opts.center = args.latLng || toLatLng(args.opts.center);
1966      args.td.values = [{options: args.opts}];
1967    }
1968    if (!args.td.values.length) {
1969      manageEnd(args, false);
1970      return;
1971    }
1972    $.each(args.td.values, function (i, value) {
1973      var id, obj,
1974        td = tuple(args, value);
1975      td.options.center = td.options.center ? toLatLng(td.options.center) : toLatLng(value);
1976      if (!map) {
1977        newMap(td.options.center);
1978      }
1979      td.options.map = map;
1980      obj = new defaults.classes.Circle(td.options);
1981      objs.push(obj);
1982      id = store.add({td: td}, "circle", obj);
1983      attachEvents($this, {td: td}, obj, id);
1984    });
1985    manageEnd(args, multiple ? objs : objs[0]);
1986  };
1987
1988  /**
1989   * returns address structure from latlng
1990   **/
1991  self.getaddress = function (args) {
1992    callback(args, args.results, args.status);
1993    task.ack();
1994  };
1995
1996  /**
1997   * returns latlng from an address
1998   **/
1999  self.getlatlng = function (args) {
2000    callback(args, args.results, args.status);
2001    task.ack();
2002  };
2003
2004  /**
2005   * return the max zoom of a location
2006   **/
2007  self.getmaxzoom = function (args) {
2008    maxZoomService().getMaxZoomAtLatLng(
2009      args.latLng,
2010      function (result) {
2011        callback(args, result.status === gm.MaxZoomStatus.OK ? result.zoom : false, status);
2012        task.ack();
2013      }
2014    );
2015  };
2016
2017  /**
2018   * return the elevation of a location
2019   **/
2020  self.getelevation = function (args) {
2021    var i,
2022      locations = [],
2023      f = function (results, status) {
2024        callback(args, status === gm.ElevationStatus.OK ? results : false, status);
2025        task.ack();
2026      };
2027
2028    if (args.latLng) {
2029      locations.push(args.latLng);
2030    } else {
2031      locations = array(args.td.locations || []);
2032      for (i = 0; i < locations.length; i++) {
2033        locations[i] = toLatLng(locations[i]);
2034      }
2035    }
2036    if (locations.length) {
2037      elevationService().getElevationForLocations({locations: locations}, f);
2038    } else {
2039      if (args.td.path && args.td.path.length) {
2040        for (i = 0; i < args.td.path.length; i++) {
2041          locations.push(toLatLng(args.td.path[i]));
2042        }
2043      }
2044      if (locations.length) {
2045        elevationService().getElevationAlongPath({path: locations, samples:args.td.samples}, f);
2046      } else {
2047        task.ack();
2048      }
2049    }
2050  };
2051
2052  /**
2053   * define defaults values
2054   **/
2055  self.defaults = function (args) {
2056    $.each(args.td, function(name, value) {
2057      if (isObject(defaults[name])) {
2058        defaults[name] = $.extend({}, defaults[name], value);
2059      } else {
2060        defaults[name] = value;
2061      }
2062    });
2063    task.ack(true);
2064  };
2065
2066  /**
2067   * add a rectangle
2068   **/
2069  self.rectangle = function (args) {
2070    var objs = [],
2071      multiple = "values" in args.td;
2072    if (!multiple) {
2073      args.td.values = [{options: args.opts}];
2074    }
2075    if (!args.td.values.length) {
2076      manageEnd(args, false);
2077      return;
2078    }
2079    $.each(args.td.values, function (i, value) {
2080      var id, obj,
2081        td = tuple(args, value);
2082      td.options.bounds = td.options.bounds ? toLatLngBounds(td.options.bounds) : toLatLngBounds(value);
2083      if (!map) {
2084        newMap(td.options.bounds.getCenter());
2085      }
2086      td.options.map = map;
2087
2088      obj = new defaults.classes.Rectangle(td.options);
2089      objs.push(obj);
2090      id = store.add({td: td}, "rectangle", obj);
2091      attachEvents($this, {td: td}, obj, id);
2092    });
2093    manageEnd(args, multiple ? objs : objs[0]);
2094  };
2095
2096  /**
2097   * add a polygone / polyline
2098   **/
2099  function poly(args, poly, path) {
2100    var objs = [],
2101      multiple = "values" in args.td;
2102    if (!multiple) {
2103      args.td.values = [{options: args.opts}];
2104    }
2105    if (!args.td.values.length) {
2106      manageEnd(args, false);
2107      return;
2108    }
2109    newMap();
2110    $.each(args.td.values, function (_, value) {
2111      var id, i, j, obj,
2112        td = tuple(args, value);
2113      if (td.options[path]) {
2114        //if (td.options[path][0][0] && isArray(td.options[path][0][0])) {
2115		if (td.options[path][0][0] && isObject(td.options[path][0][0])) { // <= @Edited by CodeSpacing: Fixed an issue where it wasn't possible to handle polygons with holes [Changed "isArray()" by "isObject()"]!
2116          for (i = 0; i < td.options[path].length; i++) {
2117            for (j = 0; j < td.options[path][i].length; j++) {
2118              td.options[path][i][j] = toLatLng(td.options[path][i][j]);
2119            }
2120          }
2121        }else {
2122          for (i = 0; i < td.options[path].length; i++) {
2123            td.options[path][i] = toLatLng(td.options[path][i]);
2124          }
2125        }
2126      }
2127      td.options.map = map;
2128      obj = new gm[poly](td.options);
2129      objs.push(obj);
2130      id = store.add({td: td}, poly.toLowerCase(), obj);
2131      attachEvents($this, {td: td}, obj, id);
2132    });
2133    manageEnd(args, multiple ? objs : objs[0]);
2134  }
2135
2136  self.polyline = function (args) {
2137    poly(args, "Polyline", "path");
2138  };
2139
2140  self.polygon = function (args) {
2141    poly(args, "Polygon", "paths");
2142  };
2143
2144  /**
2145   * add a traffic layer
2146   **/
2147  self.trafficlayer = function (args) {
2148    newMap();
2149    var obj = store.get("trafficlayer");
2150    if (!obj) {
2151      obj = new defaults.classes.TrafficLayer();
2152      obj.setMap(map);
2153      store.add(args, "trafficlayer", obj);
2154    }
2155    manageEnd(args, obj);
2156  };
2157
2158
2159  /**
2160   * add a transit layer
2161   **/
2162  self.transitlayer = function (args) {
2163    newMap();
2164    var obj = store.get("transitlayer");
2165    if (!obj) {
2166      obj = new defaults.classes.TransitLayer();
2167      obj.setMap(map);
2168      store.add(args, "transitlayer", obj);
2169    }
2170    manageEnd(args, obj);
2171  };
2172
2173  /**
2174   * add a bicycling layer
2175   **/
2176  self.bicyclinglayer = function (args) {
2177    newMap();
2178    var obj = store.get("bicyclinglayer");
2179    if (!obj) {
2180      obj = new defaults.classes.BicyclingLayer();
2181      obj.setMap(map);
2182      store.add(args, "bicyclinglayer", obj);
2183    }
2184    manageEnd(args, obj);
2185  };
2186
2187  /**
2188   * add a ground overlay
2189   **/
2190  self.groundoverlay = function (args) {
2191    args.opts.bounds = toLatLngBounds(args.opts.bounds);
2192    if (args.opts.bounds){
2193      newMap(args.opts.bounds.getCenter());
2194    }
2195    var id,
2196      obj = new defaults.classes.GroundOverlay(args.opts.url, args.opts.bounds, args.opts.opts);
2197    obj.setMap(map);
2198    id = store.add(args, "groundoverlay", obj);
2199    manageEnd(args, obj, id);
2200  };
2201
2202  /**
2203   * add multiple ground overlays
2204   * by "Hisham Radi" (CodeSpacing)
2205   **/
2206  self.groundoverlays = function (args) {
2207	if (!args.td.values.length) {
2208      manageEnd(args, false);
2209      return;
2210    }
2211	$.each(args.td.values, function (_, value) {
2212		var id, obj,
2213		td = tuple(args, value);
2214		if (td.options.bounds) {
2215			var latLngBounds = toLatLngBounds(td.options.bounds);
2216			newMap(latLngBounds.getCenter());
2217			var id,
2218	  		obj = new defaults.classes.GroundOverlay(td.options.url, latLngBounds, td.options.opts);
2219			obj.setMap(map);
2220			id = store.add(args, "groundoverlay", obj);
2221			manageEnd(args, obj, id);		
2222		}
2223	});
2224  };
2225
2226  /**
2227   * set a streetview
2228   **/
2229  self.streetviewpanorama = function (args) {
2230    if (!args.opts.opts) {
2231      args.opts.opts = {};
2232    }
2233    if (args.latLng) {
2234      args.opts.opts.position = args.latLng;
2235    } else if (args.opts.opts.position) {
2236      args.opts.opts.position = toLatLng(args.opts.opts.position);
2237    }
2238    if (args.td.divId) {
2239      args.opts.container = document.getElementById(args.td.divId);
2240    } else if (args.opts.container) {
2241      args.opts.container = $(args.opts.container).get(0);
2242    }
2243    var id, obj = new defaults.classes.StreetViewPanorama(args.opts.container, args.opts.opts);
2244    if (obj) {
2245      map.setStreetView(obj);
2246    }
2247    id = store.add(args, "streetviewpanorama", obj);
2248    manageEnd(args, obj, id);
2249  };
2250
2251  self.kmllayer = function (args) {
2252    var objs = [],
2253      multiple = "values" in args.td;
2254    if (!multiple) {
2255      args.td.values = [{options: args.opts}];
2256    }
2257    if (!args.td.values.length) {
2258      manageEnd(args, false);
2259      return;
2260    }
2261    $.each(args.td.values, function (i, value) {
2262      var id, obj, options,
2263        td = tuple(args, value);
2264      if (!map) {
2265        newMap();
2266      }
2267      options = td.options;
2268      // compatibility 5.0-
2269      if (td.options.opts) {
2270        options = td.options.opts;
2271        if (td.options.url) {
2272          options.url = td.options.url;
2273        }
2274      }
2275      // -- end --
2276      options.map = map;
2277      if (googleVersionMin("3.10")) {
2278        obj = new defaults.classes.KmlLayer(options);
2279      } else {
2280        obj = new defaults.classes.KmlLayer(options.url, options);
2281      }
2282      objs.push(obj);
2283      id = store.add({td: td}, "kmllayer", obj);
2284      attachEvents($this, {td: td}, obj, id);
2285    });
2286    manageEnd(args, multiple ? objs : objs[0]);
2287  };
2288
2289  /**
2290   * add a fix panel
2291   **/
2292  self.panel = function (args) {
2293    newMap();
2294    var id, $content,
2295      x = 0,
2296      y = 0,
2297      $div = $(document.createElement("div"));
2298
2299    $div.css({
2300      position: "absolute",
2301      zIndex: 1000,
2302      visibility: "hidden"
2303    });
2304
2305    if (args.opts.content) {
2306      $content = $(args.opts.content);
2307      $div.append($content);
2308      $this.first().prepend($div);
2309
2310      if (!isUndefined(args.opts.left)) {
2311        x = args.opts.left;
2312      } else if (!isUndefined(args.opts.right)) {
2313        x = $this.width() - $content.width() - args.opts.right;
2314      } else if (args.opts.center) {
2315        x = ($this.width() - $content.width()) / 2;
2316      }
2317
2318      if (!isUndefined(args.opts.top)) {
2319        y = args.opts.top;
2320      } else if (!isUndefined(args.opts.bottom)) {
2321        y = $this.height() - $content.height() - args.opts.bottom;
2322      } else if (args.opts.middle) {
2323        y = ($this.height() - $content.height()) / 2
2324      }
2325
2326      $div.css({
2327        top: y,
2328        left: x,
2329        visibility: "visible"
2330      });
2331    }
2332
2333    id = store.add(args, "panel", $div);
2334    manageEnd(args, $div, id);
2335    $div = null; // memory leak
2336  };
2337
2338  /**
2339   * add a direction renderer
2340   **/
2341  self.directionsrenderer = function (args) {
2342    args.opts.map = map;
2343    var id,
2344      obj = new gm.DirectionsRenderer(args.opts);
2345    if (args.td.divId) {
2346      obj.setPanel(document.getElementById(args.td.divId));
2347    } else if (args.td.container) {
2348      obj.setPanel($(args.td.container).get(0));
2349    }
2350    id = store.add(args, "directionsrenderer", obj);
2351    manageEnd(args, obj, id);
2352  };
2353
2354  /**
2355   * returns latLng of the user
2356   **/
2357  self.getgeoloc = function (args) {
2358    manageEnd(args, args.latLng);
2359  };
2360
2361  /**
2362   * add a style
2363   **/
2364  self.styledmaptype = function (args) {
2365    newMap();
2366    var obj = new defaults.classes.StyledMapType(args.td.styles, args.opts);
2367    map.mapTypes.set(args.td.id, obj);
2368    manageEnd(args, obj);
2369  };
2370
2371  /**
2372   * add an imageMapType
2373   **/
2374  self.imagemaptype = function (args) {
2375    newMap();
2376    var obj = new defaults.classes.ImageMapType(args.opts);
2377    map.mapTypes.set(args.td.id, obj);
2378    manageEnd(args, obj);
2379  };
2380
2381  /**
2382   * autofit a map using its overlays (markers, rectangles ...)
2383   **/
2384  self.autofit = function (args) {
2385    var bounds = new gm.LatLngBounds();
2386    $.each(store.all(), function (i, obj) {
2387      if (obj.getPosition && obj.getPosition()) {
2388		if(obj.visible){ // @Added by Codespacing to ignore hidden markers
2389          bounds.extend(obj.getPosition());
2390		}
2391      } else if (obj.getBounds && obj.getBounds()) {
2392        bounds.extend(obj.getBounds().getNorthEast());
2393        bounds.extend(obj.getBounds().getSouthWest());
2394      } else if (obj.getPaths && obj.getPaths()) {
2395        obj.getPaths().forEach(function (path) {
2396          path.forEach(function (latLng) {
2397            bounds.extend(latLng);
2398          });
2399        });
2400      } else if (obj.getPath && obj.getPath()) {
2401        obj.getPath().forEach(function (latLng) {
2402          bounds.extend(latLng);
2403        });
2404      } else if (obj.getCenter && obj.getCenter()) {
2405        bounds.extend(obj.getCenter());
2406	  // @Start | Added by Codespacing to add KML layers bounds to map
2407	  } else if (obj.getDefaultViewport && obj.getDefaultViewport()) {
2408		if(obj.status === 'OK'){
2409			var defaultViewport = obj.getDefaultViewport();
2410			if(defaultViewport.getNorthEast()){
2411			  var KmlNE = toLatLng(defaultViewport.getNorthEast());
2412			  bounds.extend(KmlNE);
2413			}else if(defaultViewport.getSouthWest()){
2414			  var KmlSW = toLatLng(defaultViewport.getSouthWest());
2415			  bounds.extend(KmlSW);
2416			}
2417		}
2418	  // @End | Codespacing
2419      } else if (typeof Clusterer === "function" && obj instanceof Clusterer) {
2420        obj = store.getById(obj.id(), true);
2421        if (obj) {
2422          obj.autofit(bounds);
2423        }
2424      }
2425    });
2426
2427    if (!bounds.isEmpty() && (!map.getBounds() || !map.getBounds().equals(bounds))) {
2428      if ("maxZoom" in args.td) {
2429        // fitBouds Callback event => detect zoom level and check maxZoom
2430        gm.event.addListenerOnce(
2431          map,
2432          "bounds_changed",
2433          function () {
2434            if (this.getZoom() > args.td.maxZoom) {
2435              this.setZoom(args.td.maxZoom);
2436            }
2437          }
2438        );
2439      }
2440      map.fitBounds(bounds);
2441    }
2442    manageEnd(args, true);
2443  };
2444
2445  /**
2446   * remove objects from a map
2447   **/
2448  self.clear = function (args) {
2449    if (isString(args.td)) {
2450      if (store.clearById(args.td) || store.objClearById(args.td)) {
2451        manageEnd(args, true);
2452        return;
2453      }
2454      args.td = {name: args.td};
2455    }
2456    if (args.td.id) {
2457      $.each(array(args.td.id), function (i, id) {
2458        store.clearById(id) || store.objClearById(id);
2459      });
2460    } else {
2461      store.clear(array(args.td.name), args.td.last, args.td.first, args.td.tag);
2462      store.objClear(array(args.td.name), args.td.last, args.td.first, args.td.tag);
2463    }
2464    manageEnd(args, true);
2465  };
2466
2467  /**
2468   * return objects previously created
2469   **/
2470  self.get = function (args, direct, full) {
2471    var name, res,
2472      td = direct ? args : args.td;
2473    if (!direct) {
2474      full = td.full;
2475    }
2476    if (isString(td)) {
2477      res = store.getById(td, false, full) || store.objGetById(td);
2478      if (res === false) {
2479        name = td;
2480        td = {};
2481      }
2482    } else {
2483      name = td.name;
2484    }
2485    if (name === "map") {
2486      res = map;
2487    }
2488    if (!res) {
2489      res = [];
2490      if (td.id) {
2491        $.each(array(td.id), function (i, id) {
2492          res.push(store.getById(id, false, full) || store.objGetById(id));
2493        });
2494        if (!isArray(td.id)) {
2495          res = res[0];
2496        }
2497      } else {
2498        $.each(name ? array(name) : [undef], function (i, aName) {
2499          var result;
2500          if (td.first) {
2501            result = store.get(aName, false, td.tag, full);
2502            if (result) {
2503              res.push(result);
2504            }
2505          } else if (td.all) {
2506            $.each(store.all(aName, td.tag, full), function (i, result) {
2507              res.push(result);
2508            });
2509          } else {
2510            result = store.get(aName, true, td.tag, full);
2511            if (result) {
2512              res.push(result);
2513            }
2514          }
2515        });
2516        if (!td.all && !isArray(name)) {
2517          res = res[0];
2518        }
2519      }
2520    }
2521    res = isArray(res) || !td.all ? res : [res];
2522    if (direct) {
2523      return res;
2524    } else {
2525      manageEnd(args, res);
2526    }
2527  };
2528
2529  /**
2530   * run a function on each items selected
2531   **/
2532  self.exec = function (args) {
2533    $.each(array(args.td.func), function (i, func) {
2534      $.each(self.get(args.td, true, args.td.hasOwnProperty("full") ? args.td.full : true), function (j, res) {
2535        func.call($this, res);
2536      });
2537    });
2538    manageEnd(args, true);
2539  };
2540
2541  /**
2542   * trigger events on the map
2543   **/
2544  self.trigger = function (args) {
2545    if (isString(args.td)) {
2546      gm.event.trigger(map, args.td);
2547    } else {
2548      var options = [map, args.td.eventName];
2549      if (args.td.var_args) {
2550        $.each(args.td.var_args, function (i, v) {
2551          options.push(v);
2552        });
2553      }
2554      gm.event.trigger.apply(gm.event, options);
2555    }
2556    callback(args);
2557    task.ack();
2558  };
2559}
2560
2561$.fn.gmap3 = function () {
2562  var i,
2563    list = [],
2564    empty = true,
2565    results = [];
2566
2567  // init library
2568  initDefaults();
2569
2570  // store all arguments in a td list
2571  for (i = 0; i < arguments.length; i++) {
2572    if (arguments[i]) {
2573      list.push(arguments[i]);
2574    }
2575  }
2576
2577  // resolve empty call - run init
2578  if (!list.length) {
2579    list.push("map");
2580  }
2581
2582  // loop on each jQuery object
2583  $.each(this, function () {
2584    var $this = $(this),
2585      gmap3 = $this.data("gmap3");
2586    empty = false;
2587    if (!gmap3) {
2588      gmap3 = new Gmap3($this);
2589      $this.data("gmap3", gmap3);
2590    }
2591    if (list.length === 1 && (list[0] === "get" || isDirectGet(list[0]))) {
2592      if (list[0] === "get") {
2593        results.push(gmap3.get("map", true));
2594      } else {
2595        results.push(gmap3.get(list[0].get, true, list[0].get.full));
2596      }
2597    } else {
2598      gmap3._plan(list);
2599    }
2600  });
2601
2602  // return for direct call only
2603  if (results.length) {
2604    if (results.length === 1) { // 1 css selector
2605      return results[0];
2606    }
2607    return results;
2608  }
2609
2610  return this;
2611};
2612})(jQuery);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.