/*! * jQuery.appear * https://github.com/bas2k/jquery.appear/ * http://code.google.com/p/jquery-appear/ * * Copyright (c) 2009 Michael Hixson * Copyright (c) 2012 Alexander Brovikov * Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php) */ (function($) { $.fn.appear = function(fn, options) { var settings = $.extend({ //arbitrary data to pass to fn data: undefined, //call fn only on the first appear? one: true, // X & Y accuracy accX: 0, accY: 0 }, options); return this.each(function() { var t = $(this); //whether the element is currently visible t.appeared = false; if (!fn) { //trigger the custom event t.trigger('appear', settings.data); return; } var w = $(window); //fires the appear event when appropriate var check = function() { //is the element hidden? if (!t.is(':visible')) { //it became hidden t.appeared = false; return; } //is the element inside the visible window? var a = w.scrollLeft(); var b = w.scrollTop(); var o = t.offset(); var x = o.left; var y = o.top; var ax = settings.accX; var ay = settings.accY; var th = t.height(); var wh = w.height(); var tw = t.width(); var ww = w.width(); if (y + th + ay >= b && y <= b + wh + ay && x + tw + ax >= a && x <= a + ww + ax) { //trigger the custom event if (!t.appeared) t.trigger('appear', settings.data); } else { //it scrolled out of view t.appeared = false; } }; //create a modified fn with some additional logic var modifiedFn = function() { //mark the element as visible t.appeared = true; //is this supposed to happen only once? if (settings.one) { //remove the check w.unbind('scroll', check); var i = $.inArray(check, $.fn.appear.checks); if (i >= 0) $.fn.appear.checks.splice(i, 1); } //trigger the original fn fn.apply(this, arguments); }; //bind the modified fn to the element if (settings.one) t.one('appear', settings.data, modifiedFn); else t.bind('appear', settings.data, modifiedFn); //check whenever the window scrolls w.scroll(check); //check whenever the dom changes $.fn.appear.checks.push(check); //check now (check)(); }); }; //keep a queue of appearance checks $.extend($.fn.appear, { checks: [], timeout: null, //process the queue checkAll: function() { var length = $.fn.appear.checks.length; if (length > 0) while (length--) ($.fn.appear.checks[length])(); }, //check the queue asynchronously run: function() { if ($.fn.appear.timeout) clearTimeout($.fn.appear.timeout); $.fn.appear.timeout = setTimeout($.fn.appear.checkAll, 20); } }); //run checks when these methods are called $.each(['append', 'prepend', 'after', 'before', 'attr', 'removeAttr', 'addClass', 'removeClass', 'toggleClass', 'remove', 'css', 'show', 'hide'], function(i, n) { var old = $.fn[n]; if (old) { $.fn[n] = function() { var r = old.apply(this, arguments); $.fn.appear.run(); return r; } } }); })(jQuery); (function($) { $.fn.countTo = function(options) { // merge the default plugin settings with the custom options options = $.extend({}, $.fn.countTo.defaults, options || {}); // how many times to update the value, and how much to increment the value on each update var loops = Math.ceil(options.speed / options.refreshInterval), increment = (options.to - options.from) / loops; return $(this).each(function() { var _this = this, loopCount = 0, value = options.from, interval = setInterval(updateTimer, options.refreshInterval); function updateTimer() { value += increment; loopCount++; $(_this).html(value.toFixed(options.decimals)); if (typeof(options.onUpdate) == 'function') { options.onUpdate.call(_this, value); } if (loopCount >= loops) { clearInterval(interval); value = options.to; if (typeof(options.onComplete) == 'function') { options.onComplete.call(_this, value); } } } }); }; $.fn.countTo.defaults = { from: 0, // the number the element should start at to: 100, // the number the element should end at speed: 1000, // how long it should take to count between the target numbers refreshInterval: 100, // how often the element should be updated decimals: 0, // the number of decimal places to show onUpdate: null, // callback method for every time the element is updated, onComplete: null, // callback method for when the element finishes updating }; })(jQuery); /*! * GMAP3 Plugin for jQuery * Version : 6.0.0 * Date : 2014-04-25 * Author : DEMONTE Jean-Baptiste * Contact : jbdemonte@gmail.com * Web site : http://gmap3.net * Licence : GPL v3 : http://www.gnu.org/licenses/gpl.html * * Copyright (c) 2010-2014 Jean-Baptiste DEMONTE * All rights reserved. */ ;(function ($, undef) { var defaults, gm, gId = 0, isFunction = $.isFunction, isArray = $.isArray; function isObject(m) { return typeof m === "object"; } function isString(m) { return typeof m === "string"; } function isNumber(m) { return typeof m === "number"; } function isUndefined(m) { return m === undef; } /** * Initialize default values * defaults are defined at first gmap3 call to pass the rails asset pipeline and jasmine while google library is not yet loaded */ function initDefaults() { gm = google.maps; if (!defaults) { defaults = { verbose: false, queryLimit: { attempt: 5, delay: 250, // setTimeout(..., delay + random); random: 250 }, classes: (function () { var r = {}; $.each("Map Marker InfoWindow Circle Rectangle OverlayView StreetViewPanorama KmlLayer TrafficLayer BicyclingLayer GroundOverlay StyledMapType ImageMapType".split(" "), function (_, k) { r[k] = gm[k]; }); return r; }()), map: { mapTypeId : gm.MapTypeId.ROADMAP, center: [46.578498, 2.457275], zoom: 2 }, overlay: { pane: "floatPane", content: "", offset: { x: 0, y: 0 } }, geoloc: { getCurrentPosition: { maximumAge: 60000, timeout: 5000 } } } } } /** * Generate a new ID if not defined * @param id {string} (optional) * @param simulate {boolean} (optional) * @returns {*} */ function globalId(id, simulate) { return isUndefined(id) ? "gmap3_" + (simulate ? gId + 1 : ++gId) : id; } /** * Return true if current version of Google Maps is equal or above to these in parameter * @param version {string} Minimal version required * @return {Boolean} */ function googleVersionMin(version) { var i, gmVersion = gm.version.split("."); version = version.split("."); for (i = 0; i < gmVersion.length; i++) { gmVersion[i] = parseInt(gmVersion[i], 10); } for (i = 0; i < version.length; i++) { version[i] = parseInt(version[i], 10); if (gmVersion.hasOwnProperty(i)) { if (gmVersion[i] < version[i]) { return false; } } else { return false; } } return true; } /** * attach events from a container to a sender * td[ * events => { eventName => function, } * onces => { eventName => function, } * data => mixed data * ] **/ function attachEvents($container, args, sender, id, senders) { var td = args.td || {}, context = { id: id, data: td.data, tag: td.tag }; function bind(items, handler) { if (items) { $.each(items, function (name, f) { var self = $container, fn = f; if (isArray(f)) { self = f[0]; fn = f[1]; } handler(sender, name, function (event) { fn.apply(self, [senders || sender, event, context]); }); }); } } bind(td.events, gm.event.addListener); bind(td.onces, gm.event.addListenerOnce); } /** * Extract keys from object * @param obj {object} * @returns {Array} */ function getKeys(obj) { var k, keys = []; for (k in obj) { if (obj.hasOwnProperty(k)) { keys.push(k); } } return keys; } /** * copy a key content **/ function copyKey(target, key) { var i, args = arguments; for (i = 2; i < args.length; i++) { if (key in args[i]) { if (args[i].hasOwnProperty(key)) { target[key] = args[i][key]; return; } } } } /** * Build a tuple * @param args {object} * @param value {object} * @returns {object} */ function tuple(args, value) { var k, i, keys = ["data", "tag", "id", "events", "onces"], td = {}; // "copy" the common data if (args.td) { for (k in args.td) { if (args.td.hasOwnProperty(k)) { if ((k !== "options") && (k !== "values")) { td[k] = args.td[k]; } } } } // "copy" some specific keys from value first else args.td for (i = 0; i < keys.length; i++) { copyKey(td, keys[i], value, args.td); } // create an extended options td.options = $.extend({}, args.opts || {}, value.options || {}); return td; } /** * Log error */ function error() { if (defaults.verbose) { var i, err = []; if (window.console && (isFunction(console.error))) { for (i = 0; i < arguments.length; i++) { err.push(arguments[i]); } console.error.apply(console, err); } else { err = ""; for (i = 0; i < arguments.length; i++) { err += arguments[i].toString() + " "; } alert(err); } } } /** * return true if mixed is usable as number **/ function numeric(mixed) { return (isNumber(mixed) || isString(mixed)) && mixed !== "" && !isNaN(mixed); } /** * convert data to array **/ function array(mixed) { var k, a = []; if (!isUndefined(mixed)) { if (isObject(mixed)) { if (isNumber(mixed.length)) { a = mixed; } else { for (k in mixed) { a.push(mixed[k]); } } } else { a.push(mixed); } } return a; } /** * create a function to check a tag */ function ftag(tag) { if (tag) { if (isFunction(tag)) { return tag; } tag = array(tag); return function (val) { var i; if (isUndefined(val)) { return false; } if (isObject(val)) { for (i = 0; i < val.length; i++) { if ($.inArray(val[i], tag) >= 0) { return true; } } return false; } return $.inArray(val, tag) >= 0; }; } } /** * convert mixed [ lat, lng ] objet to gm.LatLng **/ function toLatLng(mixed, emptyReturnMixed, noFlat) { var empty = emptyReturnMixed ? mixed : null; if (!mixed || (isString(mixed))) { return empty; } // defined latLng if (mixed.latLng) { return toLatLng(mixed.latLng); } // gm.LatLng object if (mixed instanceof gm.LatLng) { return mixed; } // {lat:X, lng:Y} object if (numeric(mixed.lat)) { return new gm.LatLng(mixed.lat, mixed.lng); } // [X, Y] object if (!noFlat && isArray(mixed)) { if (!numeric(mixed[0]) || !numeric(mixed[1])) { return empty; } return new gm.LatLng(mixed[0], mixed[1]); } return empty; } /** * convert mixed [ sw, ne ] object by gm.LatLngBounds **/ function toLatLngBounds(mixed) { var ne, sw; if (!mixed || mixed instanceof gm.LatLngBounds) { return mixed || null; } if (isArray(mixed)) { if (mixed.length === 2) { ne = toLatLng(mixed[0]); sw = toLatLng(mixed[1]); } else if (mixed.length === 4) { ne = toLatLng([mixed[0], mixed[1]]); sw = toLatLng([mixed[2], mixed[3]]); } } else { if (("ne" in mixed) && ("sw" in mixed)) { ne = toLatLng(mixed.ne); sw = toLatLng(mixed.sw); } else if (("n" in mixed) && ("e" in mixed) && ("s" in mixed) && ("w" in mixed)) { ne = toLatLng([mixed.n, mixed.e]); sw = toLatLng([mixed.s, mixed.w]); } } if (ne && sw) { return new gm.LatLngBounds(sw, ne); } return null; } /** * resolveLatLng **/ function resolveLatLng(ctx, method, runLatLng, args, attempt) { var latLng = runLatLng ? toLatLng(args.td, false, true) : false, conf = latLng ? {latLng: latLng} : (args.td.address ? (isString(args.td.address) ? {address: args.td.address} : args.td.address) : false), cache = conf ? geocoderCache.get(conf) : false, self = this; if (conf) { attempt = attempt || 0; // convert undefined to int if (cache) { args.latLng = cache.results[0].geometry.location; args.results = cache.results; args.status = cache.status; method.apply(ctx, [args]); } else { if (conf.location) { conf.location = toLatLng(conf.location); } if (conf.bounds) { conf.bounds = toLatLngBounds(conf.bounds); } geocoder().geocode( conf, function (results, status) { if (status === gm.GeocoderStatus.OK) { geocoderCache.store(conf, {results: results, status: status}); args.latLng = results[0].geometry.location; args.results = results; args.status = status; method.apply(ctx, [args]); } else if ((status === gm.GeocoderStatus.OVER_QUERY_LIMIT) && (attempt < defaults.queryLimit.attempt)) { setTimeout( function () { resolveLatLng.apply(self, [ctx, method, runLatLng, args, attempt + 1]); }, defaults.queryLimit.delay + Math.floor(Math.random() * defaults.queryLimit.random) ); } else { error("geocode failed", status, conf); args.latLng = args.results = false; args.status = status; method.apply(ctx, [args]); } } ); } } else { args.latLng = toLatLng(args.td, false, true); method.apply(ctx, [args]); } } function resolveAllLatLng(list, ctx, method, args) { var self = this, i = -1; function resolve() { // look for next address to resolve do { i++; } while ((i < list.length) && !("address" in list[i])); // no address found, so run method if (i >= list.length) { method.apply(ctx, [args]); return; } resolveLatLng( self, function (args) { delete args.td; $.extend(list[i], args); resolve.apply(self, []); // resolve next (using apply avoid too much recursion) }, true, {td: list[i]} ); } resolve(); } /** * geolocalise the user and return a LatLng **/ function geoloc(ctx, method, args) { var is_echo = false; // sometime, a kind of echo appear, this trick will notice once the first call is run to ignore the next one if (navigator && navigator.geolocation) { navigator.geolocation.getCurrentPosition( function (pos) { if (!is_echo) { is_echo = true; args.latLng = new gm.LatLng(pos.coords.latitude, pos.coords.longitude); method.apply(ctx, [args]); } }, function () { if (!is_echo) { is_echo = true; args.latLng = false; method.apply(ctx, [args]); } }, args.opts.getCurrentPosition ); } else { args.latLng = false; method.apply(ctx, [args]); } } /** * Return true if get is a direct call * it means : * - get is the only key * - get has no callback * @param obj {Object} The request to check * @return {Boolean} */ function isDirectGet(obj) { var k, result = false; if (isObject(obj) && obj.hasOwnProperty("get")) { for (k in obj) { if (k !== "get") { return false; } } result = !obj.get.hasOwnProperty("callback"); } return result; } var services = {}, geocoderCache = new GeocoderCache(); function geocoder(){ if (!services.geocoder) { services.geocoder = new gm.Geocoder(); } return services.geocoder; } /** * Class GeocoderCache * @constructor */ function GeocoderCache() { var cache = []; this.get = function (request) { if (cache.length) { var i, j, k, item, eq, keys = getKeys(request); for (i = 0; i < cache.length; i++) { item = cache[i]; eq = keys.length === item.keys.length; for (j = 0; (j < keys.length) && eq; j++) { k = keys[j]; eq = k in item.request; if (eq) { if (isObject(request[k]) && ("equals" in request[k]) && isFunction(request[k])) { eq = request[k].equals(item.request[k]); } else { eq = request[k] === item.request[k]; } } } if (eq) { return item.results; } } } }; this.store = function (request, results) { cache.push({request: request, keys: getKeys(request), results: results}); }; } /** * Class Stack * @constructor */ function Stack() { var st = [], self = this; self.empty = function () { return !st.length; }; self.add = function (v) { st.push(v); }; self.get = function () { return st.length ? st[0] : false; }; self.ack = function () { st.shift(); }; } /** * Class Store * @constructor */ function Store() { var store = {}, // name => [id, ...] objects = {}, // id => object self = this; function normalize(res) { return { id: res.id, name: res.name, object: res.obj, tag: res.tag, data: res.data }; } /** * add a mixed to the store **/ self.add = function (args, name, obj, sub) { var td = args.td || {}, id = globalId(td.id); if (!store[name]) { store[name] = []; } if (id in objects) { // object already exists: remove it self.clearById(id); } objects[id] = {obj: obj, sub: sub, name: name, id: id, tag: td.tag, data: td.data}; store[name].push(id); return id; }; /** * return a stored object by its id **/ self.getById = function (id, sub, full) { var result = false; if (id in objects) { if (sub) { result = objects[id].sub; } else if (full) { result = normalize(objects[id]); } else { result = objects[id].obj; } } return result; }; /** * return a stored value **/ self.get = function (name, last, tag, full) { var n, id, check = ftag(tag); if (!store[name] || !store[name].length) { return null; } n = store[name].length; while (n) { n--; id = store[name][last ? n : store[name].length - n - 1]; if (id && objects[id]) { if (check && !check(objects[id].tag)) { continue; } return full ? normalize(objects[id]) : objects[id].obj; } } return null; }; /** * return all stored values **/ self.all = function (name, tag, full) { var result = [], check = ftag(tag), find = function (n) { var i, id; for (i = 0; i < store[n].length; i++) { id = store[n][i]; if (id && objects[id]) { if (check && !check(objects[id].tag)) { continue; } result.push(full ? normalize(objects[id]) : objects[id].obj); } } }; if (name in store) { find(name); } else if (isUndefined(name)) { // internal use only for (name in store) { find(name); } } return result; }; /** * hide and remove an object **/ function rm(obj) { // Google maps element if (isFunction(obj.setMap)) { obj.setMap(null); } // jQuery if (isFunction(obj.remove)) { obj.remove(); } // internal (cluster) if (isFunction(obj.free)) { obj.free(); } obj = null; } /** * remove one object from the store **/ self.rm = function (name, check, pop) { var idx, id; if (!store[name]) { return false; } if (check) { if (pop) { for (idx = store[name].length - 1; idx >= 0; idx--) { id = store[name][idx]; if (check(objects[id].tag)) { break; } } } else { for (idx = 0; idx < store[name].length; idx++) { id = store[name][idx]; if (check(objects[id].tag)) { break; } } } } else { idx = pop ? store[name].length - 1 : 0; } if (!(idx in store[name])) { return false; } return self.clearById(store[name][idx], idx); }; /** * remove object from the store by its id **/ self.clearById = function (id, idx) { if (id in objects) { var i, name = objects[id].name; for (i = 0; isUndefined(idx) && i < store[name].length; i++) { if (id === store[name][i]) { idx = i; } } rm(objects[id].obj); if (objects[id].sub) { rm(objects[id].sub); } delete objects[id]; store[name].splice(idx, 1); return true; } return false; }; /** * return an object from a container object in the store by its id * ! for now, only cluster manage this feature **/ self.objGetById = function (id) { var result, idx; if (store.clusterer) { for (idx in store.clusterer) { if ((result = objects[store.clusterer[idx]].obj.getById(id)) !== false) { return result; } } } return false; }; /** * remove object from a container object in the store by its id * ! for now, only cluster manage this feature **/ self.objClearById = function (id) { var idx; if (store.clusterer) { for (idx in store.clusterer) { if (objects[store.clusterer[idx]].obj.clearById(id)) { return true; } } } return null; }; /** * remove objects from the store **/ self.clear = function (list, last, first, tag) { var k, i, name, check = ftag(tag); if (!list || !list.length) { list = []; for (k in store) { list.push(k); } } else { list = array(list); } for (i = 0; i < list.length; i++) { name = list[i]; if (last) { self.rm(name, check, true); } else if (first) { self.rm(name, check, false); } else { // all while (self.rm(name, check, false)) { } } } }; /** * remove object from a container object in the store by its tags * ! for now, only cluster manage this feature **/ self.objClear = function (list, last, first, tag) { var idx; if (store.clusterer && ($.inArray("marker", list) >= 0 || !list.length)) { for (idx in store.clusterer) { objects[store.clusterer[idx]].obj.clear(last, first, tag); } } }; } /** * Class Task * @param ctx * @param onEnd * @param td * @constructor */ function Task(ctx, onEnd, td) { var session = {}, self = this, current, resolve = { latLng: { // function => bool (=> address = latLng) map: false, marker: false, infowindow: false, circle: false, overlay: false, getlatlng: false, getmaxzoom: false, getelevation: false, streetviewpanorama: false, getaddress: true }, geoloc: { getgeoloc: true } }; function unify(td) { var result = {}; result[td] = {}; return result; } if (isString(td)) { td = unify(td); } function next() { var k; for (k in td) { if (td.hasOwnProperty(k) && !session.hasOwnProperty(k)) { return k; } } } self.run = function () { var k, opts; while (k = next()) { if (isFunction(ctx[k])) { current = k; opts = $.extend(true, {}, defaults[k] || {}, td[k].options || {}); if (k in resolve.latLng) { if (td[k].values) { resolveAllLatLng(td[k].values, ctx, ctx[k], {td: td[k], opts: opts, session: session}); } else { resolveLatLng(ctx, ctx[k], resolve.latLng[k], {td: td[k], opts: opts, session: session}); } } else if (k in resolve.geoloc) { geoloc(ctx, ctx[k], {td: td[k], opts: opts, session: session}); } else { ctx[k].apply(ctx, [{td: td[k], opts: opts, session: session}]); } return; // wait until ack } else { session[k] = null; } } onEnd.apply(ctx, [td, session]); }; self.ack = function(result){ session[current] = result; self.run.apply(self, []); }; } function directionsService(){ if (!services.ds) { services.ds = new gm.DirectionsService(); } return services.ds; } function distanceMatrixService() { if (!services.dms) { services.dms = new gm.DistanceMatrixService(); } return services.dms; } function maxZoomService() { if (!services.mzs) { services.mzs = new gm.MaxZoomService(); } return services.mzs; } function elevationService() { if (!services.es) { services.es = new gm.ElevationService(); } return services.es; } /** * Usefull to get a projection * => done in a function, to let dead-code analyser works without google library loaded **/ function newEmptyOverlay(map, radius) { function Overlay() { var self = this; self.onAdd = function () {}; self.onRemove = function () {}; self.draw = function () {}; return defaults.classes.OverlayView.apply(self, []); } Overlay.prototype = defaults.classes.OverlayView.prototype; var obj = new Overlay(); obj.setMap(map); return obj; } /** * Class InternalClusterer * This class manage clusters thanks to "td" objects * * Note: * Individuals marker are created on the fly thanks to the td objects, they are * first set to null to keep the indexes synchronised with the td list * This is the "display" function, set by the gmap3 object, which uses theses data * to create markers when clusters are not required * To remove a marker, the objects are deleted and set not null in arrays * markers[key] * = null : marker exist but has not been displayed yet * = false : marker has been removed **/ function InternalClusterer($container, map, raw) { var timer, projection, ffilter, fdisplay, ferror, // callback function updating = false, updated = false, redrawing = false, ready = false, enabled = true, self = this, events = [], store = {}, // combin of index (id1-id2-...) => object ids = {}, // unique id => index idxs = {}, // index => unique id markers = [], // index => marker tds = [], // index => td or null if removed values = [], // index => value overlay = newEmptyOverlay(map, raw.radius); main(); function prepareMarker(index) { if (!markers[index]) { delete tds[index].options.map; markers[index] = new defaults.classes.Marker(tds[index].options); attachEvents($container, {td: tds[index]}, markers[index], tds[index].id); } } /** * return a marker by its id, null if not yet displayed and false if no exist or removed **/ self.getById = function (id) { if (id in ids) { prepareMarker(ids[id]); return markers[ids[id]]; } return false; }; /** * remove one object from the store **/ self.rm = function (id) { var index = ids[id]; if (markers[index]) { // can be null markers[index].setMap(null); } delete markers[index]; markers[index] = false; delete tds[index]; tds[index] = false; delete values[index]; values[index] = false; delete ids[id]; delete idxs[index]; updated = true; }; /** * remove a marker by its id **/ self.clearById = function (id) { if (id in ids){ self.rm(id); return true; } }; /** * remove objects from the store **/ self.clear = function (last, first, tag) { var start, stop, step, index, i, list = [], check = ftag(tag); if (last) { start = tds.length - 1; stop = -1; step = -1; } else { start = 0; stop = tds.length; step = 1; } for (index = start; index !== stop; index += step) { if (tds[index]) { if (!check || check(tds[index].tag)) { list.push(idxs[index]); if (first || last) { break; } } } } for (i = 0; i < list.length; i++) { self.rm(list[i]); } }; // add a "marker td" to the cluster self.add = function (td, value) { td.id = globalId(td.id); self.clearById(td.id); ids[td.id] = markers.length; idxs[markers.length] = td.id; markers.push(null); // null = marker not yet created / displayed tds.push(td); values.push(value); updated = true; }; // add a real marker to the cluster self.addMarker = function (marker, td) { td = td || {}; td.id = globalId(td.id); self.clearById(td.id); if (!td.options) { td.options = {}; } td.options.position = marker.getPosition(); attachEvents($container, {td: td}, marker, td.id); ids[td.id] = markers.length; idxs[markers.length] = td.id; markers.push(marker); tds.push(td); values.push(td.data || {}); updated = true; }; // return a "marker td" by its index self.td = function (index) { return tds[index]; }; // return a "marker value" by its index self.value = function (index) { return values[index]; }; // return a marker by its index self.marker = function (index) { if (index in markers) { prepareMarker(index); return markers[index]; } return false; }; // return a marker by its index self.markerIsSet = function (index) { return Boolean(markers[index]); }; // store a new marker instead if the default "false" self.setMarker = function (index, marker) { markers[index] = marker; }; // link the visible overlay to the logical data (to hide overlays later) self.store = function (cluster, obj, shadow) { store[cluster.ref] = {obj: obj, shadow: shadow}; }; // free all objects self.free = function () { var i; for(i = 0; i < events.length; i++) { gm.event.removeListener(events[i]); } events = []; $.each(store, function (key) { flush(key); }); store = {}; $.each(tds, function (i) { tds[i] = null; }); tds = []; $.each(markers, function (i) { if (markers[i]) { // false = removed markers[i].setMap(null); delete markers[i]; } }); markers = []; $.each(values, function (i) { delete values[i]; }); values = []; ids = {}; idxs = {}; }; // link the display function self.filter = function (f) { ffilter = f; redraw(); }; // enable/disable the clustering feature self.enable = function (value) { if (enabled !== value) { enabled = value; redraw(); } }; // link the display function self.display = function (f) { fdisplay = f; }; // link the errorfunction self.error = function (f) { ferror = f; }; // lock the redraw self.beginUpdate = function () { updating = true; }; // unlock the redraw self.endUpdate = function () { updating = false; if (updated) { redraw(); } }; // extends current bounds with internal markers self.autofit = function (bounds) { var i; for (i = 0; i < tds.length; i++) { if (tds[i]) { bounds.extend(tds[i].options.position); } } }; // bind events function main() { projection = overlay.getProjection(); if (!projection) { setTimeout(function () { main.apply(self, []); }, 25); return; } ready = true; events.push(gm.event.addListener(map, "zoom_changed", delayRedraw)); events.push(gm.event.addListener(map, "bounds_changed", delayRedraw)); redraw(); } // flush overlays function flush(key) { if (isObject(store[key])) { // is overlay if (isFunction(store[key].obj.setMap)) { store[key].obj.setMap(null); } if (isFunction(store[key].obj.remove)) { store[key].obj.remove(); } if (isFunction(store[key].shadow.remove)) { store[key].obj.remove(); } if (isFunction(store[key].shadow.setMap)) { store[key].shadow.setMap(null); } delete store[key].obj; delete store[key].shadow; } else if (markers[key]) { // marker not removed markers[key].setMap(null); // don't remove the marker object, it may be displayed later } delete store[key]; } /** * return the distance between 2 latLng couple into meters * Params : * Lat1, Lng1, Lat2, Lng2 * LatLng1, Lat2, Lng2 * Lat1, Lng1, LatLng2 * LatLng1, LatLng2 **/ function distanceInMeter() { var lat1, lat2, lng1, lng2, e, f, g, h, cos = Math.cos, sin = Math.sin, args = arguments; if (args[0] instanceof gm.LatLng) { lat1 = args[0].lat(); lng1 = args[0].lng(); if (args[1] instanceof gm.LatLng) { lat2 = args[1].lat(); lng2 = args[1].lng(); } else { lat2 = args[1]; lng2 = args[2]; } } else { lat1 = args[0]; lng1 = args[1]; if (args[2] instanceof gm.LatLng) { lat2 = args[2].lat(); lng2 = args[2].lng(); } else { lat2 = args[2]; lng2 = args[3]; } } e = Math.PI * lat1 / 180; f = Math.PI * lng1 / 180; g = Math.PI * lat2 / 180; h = Math.PI * lng2 / 180; return 1000 * 6371 * Math.acos(Math.min(cos(e) * cos(g) * cos(f) * cos(h) + cos(e) * sin(f) * cos(g) * sin(h) + sin(e) * sin(g), 1)); } // extend the visible bounds function extendsMapBounds() { var radius = distanceInMeter(map.getCenter(), map.getBounds().getNorthEast()), circle = new gm.Circle({ center: map.getCenter(), radius: 1.25 * radius // + 25% }); return circle.getBounds(); } // return an object where keys are store keys function getStoreKeys() { var k, keys = {}; for (k in store) { keys[k] = true; } return keys; } // async the delay function function delayRedraw() { clearTimeout(timer); timer = setTimeout(redraw, 25); } // generate bounds extended by radius function extendsBounds(latLng) { var p = projection.fromLatLngToDivPixel(latLng), ne = projection.fromDivPixelToLatLng(new gm.Point(p.x + raw.radius, p.y - raw.radius)), sw = projection.fromDivPixelToLatLng(new gm.Point(p.x - raw.radius, p.y + raw.radius)); return new gm.LatLngBounds(sw, ne); } // run the clustering process and call the display function function redraw() { if (updating || redrawing || !ready) { return; } var i, j, k, indexes, check = false, bounds, cluster, position, previous, lat, lng, loop, keys = [], used = {}, zoom = map.getZoom(), forceDisabled = ("maxZoom" in raw) && (zoom > raw.maxZoom), previousKeys = getStoreKeys(); // reset flag updated = false; if (zoom > 3) { // extend the bounds of the visible map to manage clusters near the boundaries bounds = extendsMapBounds(); // check contain only if boundaries are valid check = bounds.getSouthWest().lng() < bounds.getNorthEast().lng(); } // calculate positions of "visibles" markers (in extended bounds) for (i = 0; i < tds.length; i++) { if (tds[i] && (!check || bounds.contains(tds[i].options.position)) && (!ffilter || ffilter(values[i]))) { keys.push(i); } } // for each "visible" marker, search its neighbors to create a cluster // we can't do a classical "for" loop, because, analysis can bypass a marker while focusing on cluster while (1) { i = 0; while (used[i] && (i < keys.length)) { // look for the next marker not used i++; } if (i === keys.length) { break; } indexes = []; if (enabled && !forceDisabled) { loop = 10; do { previous = indexes; indexes = []; loop--; if (previous.length) { position = bounds.getCenter(); } else { position = tds[keys[i]].options.position; } bounds = extendsBounds(position); for (j = i; j < keys.length; j++) { if (used[j]) { continue; } if (bounds.contains(tds[keys[j]].options.position)) { indexes.push(j); } } } while ((previous.length < indexes.length) && (indexes.length > 1) && loop); } else { for (j = i; j < keys.length; j++) { if (!used[j]) { indexes.push(j); break; } } } cluster = {indexes: [], ref: []}; lat = lng = 0; for (k = 0; k < indexes.length; k++) { used[indexes[k]] = true; cluster.indexes.push(keys[indexes[k]]); cluster.ref.push(keys[indexes[k]]); lat += tds[keys[indexes[k]]].options.position.lat(); lng += tds[keys[indexes[k]]].options.position.lng(); } lat /= indexes.length; lng /= indexes.length; cluster.latLng = new gm.LatLng(lat, lng); cluster.ref = cluster.ref.join("-"); if (cluster.ref in previousKeys) { // cluster doesn't change delete previousKeys[cluster.ref]; // remove this entry, these still in this array will be removed } else { // cluster is new if (indexes.length === 1) { // alone markers are not stored, so need to keep the key (else, will be displayed every time and marker will blink) store[cluster.ref] = true; } fdisplay(cluster); } } // flush the previous overlays which are not still used $.each(previousKeys, function (key) { flush(key); }); redrawing = false; } } /** * Class Clusterer * a facade with limited method for external use **/ function Clusterer(id, internalClusterer) { var self = this; self.id = function () { return id; }; self.filter = function (f) { internalClusterer.filter(f); }; self.enable = function () { internalClusterer.enable(true); }; self.disable = function () { internalClusterer.enable(false); }; self.add = function (marker, td, lock) { if (!lock) { internalClusterer.beginUpdate(); } internalClusterer.addMarker(marker, td); if (!lock) { internalClusterer.endUpdate(); } }; self.getById = function (id) { return internalClusterer.getById(id); }; self.clearById = function (id, lock) { var result; if (!lock) { internalClusterer.beginUpdate(); } result = internalClusterer.clearById(id); if (!lock) { internalClusterer.endUpdate(); } return result; }; self.clear = function (last, first, tag, lock) { if (!lock) { internalClusterer.beginUpdate(); } internalClusterer.clear(last, first, tag); if (!lock) { internalClusterer.endUpdate(); } }; } /** * Class OverlayView * @constructor */ function OverlayView(map, opts, latLng, $div) { var self = this, listeners = []; defaults.classes.OverlayView.call(self); self.setMap(map); self.onAdd = function () { var panes = self.getPanes(); if (opts.pane in panes) { $(panes[opts.pane]).append($div); } $.each("dblclick click mouseover mousemove mouseout mouseup mousedown".split(" "), function (i, name) { listeners.push( gm.event.addDomListener($div[0], name, function (e) { $.Event(e).stopPropagation(); gm.event.trigger(self, name, [e]); self.draw(); }) ); }); listeners.push( gm.event.addDomListener($div[0], "contextmenu", function (e) { $.Event(e).stopPropagation(); gm.event.trigger(self, "rightclick", [e]); self.draw(); }) ); }; self.getPosition = function () { return latLng; }; self.setPosition = function (newLatLng) { latLng = newLatLng; self.draw(); }; self.draw = function () { var ps = self.getProjection().fromLatLngToDivPixel(latLng); $div .css("left", (ps.x + opts.offset.x) + "px") .css("top", (ps.y + opts.offset.y) + "px"); }; self.onRemove = function () { var i; for (i = 0; i < listeners.length; i++) { gm.event.removeListener(listeners[i]); } $div.remove(); }; self.hide = function () { $div.hide(); }; self.show = function () { $div.show(); }; self.toggle = function () { if ($div) { if ($div.is(":visible")) { self.show(); } else { self.hide(); } } }; self.toggleDOM = function () { self.setMap(self.getMap() ? null : map); }; self.getDOMElement = function () { return $div[0]; }; } function Gmap3($this) { var self = this, stack = new Stack(), store = new Store(), map = null, task; /** * if not running, start next action in stack **/ function run() { if (!task && (task = stack.get())) { task.run(); } } /** * called when action in finished, to acknoledge the current in stack and start next one **/ function end() { task = null; stack.ack(); run.call(self); // restart to high level scope } //-----------------------------------------------------------------------// // Tools //-----------------------------------------------------------------------// /** * execute callback functions **/ function callback(args) { var params, cb = args.td.callback; if (cb) { params = Array.prototype.slice.call(arguments, 1); if (isFunction(cb)) { cb.apply($this, params); } else if (isArray(cb)) { if (isFunction(cb[1])) { cb[1].apply(cb[0], params); } } } } /** * execute ending functions **/ function manageEnd(args, obj, id) { if (id) { attachEvents($this, args, obj, id); } callback(args, obj); task.ack(obj); } /** * initialize the map if not yet initialized **/ function newMap(latLng, args) { args = args || {}; var opts = args.td && args.td.options ? args.td.options : 0; if (map) { if (opts) { if (opts.center) { opts.center = toLatLng(opts.center); } map.setOptions(opts); } } else { opts = args.opts || $.extend(true, {}, defaults.map, opts || {}); opts.center = latLng || toLatLng(opts.center); map = new defaults.classes.Map($this.get(0), opts); } } /** * store actions to execute in a stack manager **/ self._plan = function (list) { var k; for (k = 0; k < list.length; k++) { stack.add(new Task(self, end, list[k])); } run(); }; /** * Initialize gm.Map object **/ self.map = function (args) { newMap(args.latLng, args); attachEvents($this, args, map); manageEnd(args, map); }; /** * destroy an existing instance **/ self.destroy = function (args) { store.clear(); $this.empty(); if (map) { map = null; } manageEnd(args, true); }; /** * add an overlay **/ self.overlay = function (args, internal) { var objs = [], multiple = "values" in args.td; if (!multiple) { args.td.values = [{latLng: args.latLng, options: args.opts}]; } if (!args.td.values.length) { manageEnd(args, false); return; } if (!OverlayView.__initialised) { OverlayView.prototype = new defaults.classes.OverlayView(); OverlayView.__initialised = true; } $.each(args.td.values, function (i, value) { var id, obj, td = tuple(args, value), $div = $(document.createElement("div")).css({ border: "none", borderWidth: 0, position: "absolute" }); $div.append(td.options.content); obj = new OverlayView(map, td.options, toLatLng(td) || toLatLng(value), $div); objs.push(obj); $div = null; // memory leak if (!internal) { id = store.add(args, "overlay", obj); attachEvents($this, {td: td}, obj, id); } }); if (internal) { return objs[0]; } manageEnd(args, multiple ? objs : objs[0]); }; /** * Create an InternalClusterer object **/ function createClusterer(raw) { var internalClusterer = new InternalClusterer($this, map, raw), td = {}, styles = {}, thresholds = [], isInt = /^[0-9]+$/, calculator, k; for (k in raw) { if (isInt.test(k)) { thresholds.push(1 * k); // cast to int styles[k] = raw[k]; styles[k].width = styles[k].width || 0; styles[k].height = styles[k].height || 0; } else { td[k] = raw[k]; } } thresholds.sort(function (a, b) { return a > b; }); // external calculator if (td.calculator) { calculator = function (indexes) { var data = []; $.each(indexes, function (i, index) { data.push(internalClusterer.value(index)); }); return td.calculator.apply($this, [data]); }; } else { calculator = function (indexes) { return indexes.length; }; } // set error function internalClusterer.error(function () { error.apply(self, arguments); }); // set display function internalClusterer.display(function (cluster) { var i, style, atd, obj, offset, shadow, cnt = calculator(cluster.indexes); // look for the style to use if (raw.force || cnt > 1) { for (i = 0; i < thresholds.length; i++) { if (thresholds[i] <= cnt) { style = styles[thresholds[i]]; } } } if (style) { offset = style.offset || [-style.width/2, -style.height/2]; // create a custom overlay command // nb: 2 extends are faster self a deeper extend atd = $.extend({}, td); atd.options = $.extend({ pane: "overlayLayer", content: style.content ? style.content.replace("CLUSTER_COUNT", cnt) : "", offset: { x: ("x" in offset ? offset.x : offset[0]) || 0, y: ("y" in offset ? offset.y : offset[1]) || 0 } }, td.options || {}); obj = self.overlay({td: atd, opts: atd.options, latLng: toLatLng(cluster)}, true); atd.options.pane = "floatShadow"; atd.options.content = $(document.createElement("div")).width(style.width + "px").height(style.height + "px").css({cursor: "pointer"}); shadow = self.overlay({td: atd, opts: atd.options, latLng: toLatLng(cluster)}, true); // store data to the clusterer td.data = { latLng: toLatLng(cluster), markers:[] }; $.each(cluster.indexes, function(i, index){ td.data.markers.push(internalClusterer.value(index)); if (internalClusterer.markerIsSet(index)){ internalClusterer.marker(index).setMap(null); } }); attachEvents($this, {td: td}, shadow, undef, {main: obj, shadow: shadow}); internalClusterer.store(cluster, obj, shadow); } else { $.each(cluster.indexes, function (i, index) { internalClusterer.marker(index).setMap(map); }); } }); return internalClusterer; } /** * add a marker **/ self.marker = function (args) { var objs, clusterer, internalClusterer, multiple = "values" in args.td, init = !map; if (!multiple) { args.opts.position = args.latLng || toLatLng(args.opts.position); args.td.values = [{options: args.opts}]; } if (!args.td.values.length) { manageEnd(args, false); return; } if (init) { newMap(); } if (args.td.cluster && !map.getBounds()) { // map not initialised => bounds not available : wait for map if clustering feature is required gm.event.addListenerOnce(map, "bounds_changed", function () { self.marker.apply(self, [args]); }); return; } if (args.td.cluster) { if (args.td.cluster instanceof Clusterer) { clusterer = args.td.cluster; internalClusterer = store.getById(clusterer.id(), true); } else { internalClusterer = createClusterer(args.td.cluster); clusterer = new Clusterer(globalId(args.td.id, true), internalClusterer); store.add(args, "clusterer", clusterer, internalClusterer); } internalClusterer.beginUpdate(); $.each(args.td.values, function (i, value) { var td = tuple(args, value); td.options.position = td.options.position ? toLatLng(td.options.position) : toLatLng(value); if (td.options.position) { td.options.map = map; if (init) { map.setCenter(td.options.position); init = false; } internalClusterer.add(td, value); } }); internalClusterer.endUpdate(); manageEnd(args, clusterer); } else { objs = []; $.each(args.td.values, function (i, value) { var id, obj, td = tuple(args, value); td.options.position = td.options.position ? toLatLng(td.options.position) : toLatLng(value); if (td.options.position) { td.options.map = map; if (init) { map.setCenter(td.options.position); init = false; } obj = new defaults.classes.Marker(td.options); objs.push(obj); id = store.add({td: td}, "marker", obj); attachEvents($this, {td: td}, obj, id); } }); manageEnd(args, multiple ? objs : objs[0]); } }; /** * return a route **/ self.getroute = function (args) { args.opts.origin = toLatLng(args.opts.origin, true); args.opts.destination = toLatLng(args.opts.destination, true); directionsService().route( args.opts, function (results, status) { callback(args, status === gm.DirectionsStatus.OK ? results : false, status); task.ack(); } ); }; /** * return the distance between an origin and a destination * **/ self.getdistance = function (args) { var i; args.opts.origins = array(args.opts.origins); for (i = 0; i < args.opts.origins.length; i++) { args.opts.origins[i] = toLatLng(args.opts.origins[i], true); } args.opts.destinations = array(args.opts.destinations); for (i = 0; i < args.opts.destinations.length; i++) { args.opts.destinations[i] = toLatLng(args.opts.destinations[i], true); } distanceMatrixService().getDistanceMatrix( args.opts, function (results, status) { callback(args, status === gm.DistanceMatrixStatus.OK ? results : false, status); task.ack(); } ); }; /** * add an infowindow **/ self.infowindow = function (args) { var objs = [], multiple = "values" in args.td; if (!multiple) { if (args.latLng) { args.opts.position = args.latLng; } args.td.values = [{options: args.opts}]; } $.each(args.td.values, function (i, value) { var id, obj, td = tuple(args, value); td.options.position = td.options.position ? toLatLng(td.options.position) : toLatLng(value.latLng); if (!map) { newMap(td.options.position); } obj = new defaults.classes.InfoWindow(td.options); if (obj && (isUndefined(td.open) || td.open)) { if (multiple) { obj.open(map, td.anchor || undef); } else { obj.open(map, td.anchor || (args.latLng ? undef : (args.session.marker ? args.session.marker : undef))); } } objs.push(obj); id = store.add({td: td}, "infowindow", obj); attachEvents($this, {td: td}, obj, id); }); manageEnd(args, multiple ? objs : objs[0]); }; /** * add a circle **/ self.circle = function (args) { var objs = [], multiple = "values" in args.td; if (!multiple) { args.opts.center = args.latLng || toLatLng(args.opts.center); args.td.values = [{options: args.opts}]; } if (!args.td.values.length) { manageEnd(args, false); return; } $.each(args.td.values, function (i, value) { var id, obj, td = tuple(args, value); td.options.center = td.options.center ? toLatLng(td.options.center) : toLatLng(value); if (!map) { newMap(td.options.center); } td.options.map = map; obj = new defaults.classes.Circle(td.options); objs.push(obj); id = store.add({td: td}, "circle", obj); attachEvents($this, {td: td}, obj, id); }); manageEnd(args, multiple ? objs : objs[0]); }; /** * returns address structure from latlng **/ self.getaddress = function (args) { callback(args, args.results, args.status); task.ack(); }; /** * returns latlng from an address **/ self.getlatlng = function (args) { callback(args, args.results, args.status); task.ack(); }; /** * return the max zoom of a location **/ self.getmaxzoom = function (args) { maxZoomService().getMaxZoomAtLatLng( args.latLng, function (result) { callback(args, result.status === gm.MaxZoomStatus.OK ? result.zoom : false, status); task.ack(); } ); }; /** * return the elevation of a location **/ self.getelevation = function (args) { var i, locations = [], f = function (results, status) { callback(args, status === gm.ElevationStatus.OK ? results : false, status); task.ack(); }; if (args.latLng) { locations.push(args.latLng); } else { locations = array(args.td.locations || []); for (i = 0; i < locations.length; i++) { locations[i] = toLatLng(locations[i]); } } if (locations.length) { elevationService().getElevationForLocations({locations: locations}, f); } else { if (args.td.path && args.td.path.length) { for (i = 0; i < args.td.path.length; i++) { locations.push(toLatLng(args.td.path[i])); } } if (locations.length) { elevationService().getElevationAlongPath({path: locations, samples:args.td.samples}, f); } else { task.ack(); } } }; /** * define defaults values **/ self.defaults = function (args) { $.each(args.td, function(name, value) { if (isObject(defaults[name])) { defaults[name] = $.extend({}, defaults[name], value); } else { defaults[name] = value; } }); task.ack(true); }; /** * add a rectangle **/ self.rectangle = function (args) { var objs = [], multiple = "values" in args.td; if (!multiple) { args.td.values = [{options: args.opts}]; } if (!args.td.values.length) { manageEnd(args, false); return; } $.each(args.td.values, function (i, value) { var id, obj, td = tuple(args, value); td.options.bounds = td.options.bounds ? toLatLngBounds(td.options.bounds) : toLatLngBounds(value); if (!map) { newMap(td.options.bounds.getCenter()); } td.options.map = map; obj = new defaults.classes.Rectangle(td.options); objs.push(obj); id = store.add({td: td}, "rectangle", obj); attachEvents($this, {td: td}, obj, id); }); manageEnd(args, multiple ? objs : objs[0]); }; /** * add a polygone / polyline **/ function poly(args, poly, path) { var objs = [], multiple = "values" in args.td; if (!multiple) { args.td.values = [{options: args.opts}]; } if (!args.td.values.length) { manageEnd(args, false); return; } newMap(); $.each(args.td.values, function (_, value) { var id, i, j, obj, td = tuple(args, value); if (td.options[path]) { if (td.options[path][0][0] && isArray(td.options[path][0][0])) { for (i = 0; i < td.options[path].length; i++) { for (j = 0; j < td.options[path][i].length; j++) { td.options[path][i][j] = toLatLng(td.options[path][i][j]); } } } else { for (i = 0; i < td.options[path].length; i++) { td.options[path][i] = toLatLng(td.options[path][i]); } } } td.options.map = map; obj = new gm[poly](td.options); objs.push(obj); id = store.add({td: td}, poly.toLowerCase(), obj); attachEvents($this, {td: td}, obj, id); }); manageEnd(args, multiple ? objs : objs[0]); } self.polyline = function (args) { poly(args, "Polyline", "path"); }; self.polygon = function (args) { poly(args, "Polygon", "paths"); }; /** * add a traffic layer **/ self.trafficlayer = function (args) { newMap(); var obj = store.get("trafficlayer"); if (!obj) { obj = new defaults.classes.TrafficLayer(); obj.setMap(map); store.add(args, "trafficlayer", obj); } manageEnd(args, obj); }; /** * add a bicycling layer **/ self.bicyclinglayer = function (args) { newMap(); var obj = store.get("bicyclinglayer"); if (!obj) { obj = new defaults.classes.BicyclingLayer(); obj.setMap(map); store.add(args, "bicyclinglayer", obj); } manageEnd(args, obj); }; /** * add a ground overlay **/ self.groundoverlay = function (args) { args.opts.bounds = toLatLngBounds(args.opts.bounds); if (args.opts.bounds){ newMap(args.opts.bounds.getCenter()); } var id, obj = new defaults.classes.GroundOverlay(args.opts.url, args.opts.bounds, args.opts.opts); obj.setMap(map); id = store.add(args, "groundoverlay", obj); manageEnd(args, obj, id); }; /** * set a streetview **/ self.streetviewpanorama = function (args) { if (!args.opts.opts) { args.opts.opts = {}; } if (args.latLng) { args.opts.opts.position = args.latLng; } else if (args.opts.opts.position) { args.opts.opts.position = toLatLng(args.opts.opts.position); } if (args.td.divId) { args.opts.container = document.getElementById(args.td.divId); } else if (args.opts.container) { args.opts.container = $(args.opts.container).get(0); } var id, obj = new defaults.classes.StreetViewPanorama(args.opts.container, args.opts.opts); if (obj) { map.setStreetView(obj); } id = store.add(args, "streetviewpanorama", obj); manageEnd(args, obj, id); }; self.kmllayer = function (args) { var objs = [], multiple = "values" in args.td; if (!multiple) { args.td.values = [{options: args.opts}]; } if (!args.td.values.length) { manageEnd(args, false); return; } $.each(args.td.values, function (i, value) { var id, obj, options, td = tuple(args, value); if (!map) { newMap(); } options = td.options; // compatibility 5.0- if (td.options.opts) { options = td.options.opts; if (td.options.url) { options.url = td.options.url; } } // -- end -- options.map = map; if (googleVersionMin("3.10")) { obj = new defaults.classes.KmlLayer(options); } else { obj = new defaults.classes.KmlLayer(options.url, options); } objs.push(obj); id = store.add({td: td}, "kmllayer", obj); attachEvents($this, {td: td}, obj, id); }); manageEnd(args, multiple ? objs : objs[0]); }; /** * add a fix panel **/ self.panel = function (args) { newMap(); var id, $content, x = 0, y = 0, $div = $(document.createElement("div")); $div.css({ position: "absolute", zIndex: 1000, visibility: "hidden" }); if (args.opts.content) { $content = $(args.opts.content); $div.append($content); $this.first().prepend($div); if (!isUndefined(args.opts.left)) { x = args.opts.left; } else if (!isUndefined(args.opts.right)) { x = $this.width() - $content.width() - args.opts.right; } else if (args.opts.center) { x = ($this.width() - $content.width()) / 2; } if (!isUndefined(args.opts.top)) { y = args.opts.top; } else if (!isUndefined(args.opts.bottom)) { y = $this.height() - $content.height() - args.opts.bottom; } else if (args.opts.middle) { y = ($this.height() - $content.height()) / 2 } $div.css({ top: y, left: x, visibility: "visible" }); } id = store.add(args, "panel", $div); manageEnd(args, $div, id); $div = null; // memory leak }; /** * add a direction renderer **/ self.directionsrenderer = function (args) { args.opts.map = map; var id, obj = new gm.DirectionsRenderer(args.opts); if (args.td.divId) { obj.setPanel(document.getElementById(args.td.divId)); } else if (args.td.container) { obj.setPanel($(args.td.container).get(0)); } id = store.add(args, "directionsrenderer", obj); manageEnd(args, obj, id); }; /** * returns latLng of the user **/ self.getgeoloc = function (args) { manageEnd(args, args.latLng); }; /** * add a style **/ self.styledmaptype = function (args) { newMap(); var obj = new defaults.classes.StyledMapType(args.td.styles, args.opts); map.mapTypes.set(args.td.id, obj); manageEnd(args, obj); }; /** * add an imageMapType **/ self.imagemaptype = function (args) { newMap(); var obj = new defaults.classes.ImageMapType(args.opts); map.mapTypes.set(args.td.id, obj); manageEnd(args, obj); }; /** * autofit a map using its overlays (markers, rectangles ...) **/ self.autofit = function (args) { var bounds = new gm.LatLngBounds(); $.each(store.all(), function (i, obj) { if (obj.getPosition) { bounds.extend(obj.getPosition()); } else if (obj.getBounds) { bounds.extend(obj.getBounds().getNorthEast()); bounds.extend(obj.getBounds().getSouthWest()); } else if (obj.getPaths) { obj.getPaths().forEach(function (path) { path.forEach(function (latLng) { bounds.extend(latLng); }); }); } else if (obj.getPath) { obj.getPath().forEach(function (latLng) { bounds.extend(latLng); }); } else if (obj.getCenter) { bounds.extend(obj.getCenter()); } else if (typeof Clusterer === "function" && obj instanceof Clusterer) { obj = store.getById(obj.id(), true); if (obj) { obj.autofit(bounds); } } }); if (!bounds.isEmpty() && (!map.getBounds() || !map.getBounds().equals(bounds))) { if ("maxZoom" in args.td) { // fitBouds Callback event => detect zoom level and check maxZoom gm.event.addListenerOnce( map, "bounds_changed", function () { if (this.getZoom() > args.td.maxZoom) { this.setZoom(args.td.maxZoom); } } ); } map.fitBounds(bounds); } manageEnd(args, true); }; /** * remove objects from a map **/ self.clear = function (args) { if (isString(args.td)) { if (store.clearById(args.td) || store.objClearById(args.td)) { manageEnd(args, true); return; } args.td = {name: args.td}; } if (args.td.id) { $.each(array(args.td.id), function (i, id) { store.clearById(id) || store.objClearById(id); }); } else { store.clear(array(args.td.name), args.td.last, args.td.first, args.td.tag); store.objClear(array(args.td.name), args.td.last, args.td.first, args.td.tag); } manageEnd(args, true); }; /** * return objects previously created **/ self.get = function (args, direct, full) { var name, res, td = direct ? args : args.td; if (!direct) { full = td.full; } if (isString(td)) { res = store.getById(td, false, full) || store.objGetById(td); if (res === false) { name = td; td = {}; } } else { name = td.name; } if (name === "map") { res = map; } if (!res) { res = []; if (td.id) { $.each(array(td.id), function (i, id) { res.push(store.getById(id, false, full) || store.objGetById(id)); }); if (!isArray(td.id)) { res = res[0]; } } else { $.each(name ? array(name) : [undef], function (i, aName) { var result; if (td.first) { result = store.get(aName, false, td.tag, full); if (result) { res.push(result); } } else if (td.all) { $.each(store.all(aName, td.tag, full), function (i, result) { res.push(result); }); } else { result = store.get(aName, true, td.tag, full); if (result) { res.push(result); } } }); if (!td.all && !isArray(name)) { res = res[0]; } } } res = isArray(res) || !td.all ? res : [res]; if (direct) { return res; } else { manageEnd(args, res); } }; /** * run a function on each items selected **/ self.exec = function (args) { $.each(array(args.td.func), function (i, func) { $.each(self.get(args.td, true, args.td.hasOwnProperty("full") ? args.td.full : true), function (j, res) { func.call($this, res); }); }); manageEnd(args, true); }; /** * trigger events on the map **/ self.trigger = function (args) { if (isString(args.td)) { gm.event.trigger(map, args.td); } else { var options = [map, args.td.eventName]; if (args.td.var_args) { $.each(args.td.var_args, function (i, v) { options.push(v); }); } gm.event.trigger.apply(gm.event, options); } callback(args); task.ack(); }; } $.fn.gmap3 = function () { var i, list = [], empty = true, results = []; // init library initDefaults(); // store all arguments in a td list for (i = 0; i < arguments.length; i++) { if (arguments[i]) { list.push(arguments[i]); } } // resolve empty call - run init if (!list.length) { list.push("map"); } // loop on each jQuery object $.each(this, function () { var $this = $(this), gmap3 = $this.data("gmap3"); empty = false; if (!gmap3) { gmap3 = new Gmap3($this); $this.data("gmap3", gmap3); } if (list.length === 1 && (list[0] === "get" || isDirectGet(list[0]))) { if (list[0] === "get") { results.push(gmap3.get("map", true)); } else { results.push(gmap3.get(list[0].get, true, list[0].get.full)); } } else { gmap3._plan(list); } }); // return for direct call only if (results.length) { if (results.length === 1) { // 1 css selector return results[0]; } return results; } return this; }; })(jQuery); /*! * imagesLoaded PACKAGED v3.0.2 * JavaScript is all like "You images are done yet or what?" */ /*! * EventEmitter v4.1.0 - git.io/ee * Oliver Caldwell * MIT license * @preserve */ (function (exports) { // Place the script in strict mode 'use strict'; /** * Class for managing events. * Can be extended to provide event functionality in other classes. * * @class Manages event registering and emitting. */ function EventEmitter() {} // Shortcuts to improve speed and size // Easy access to the prototype var proto = EventEmitter.prototype, nativeIndexOf = Array.prototype.indexOf ? true : false; /** * Finds the index of the listener for the event in it's storage array. * * @param {Function} listener Method to look for. * @param {Function[]} listeners Array of listeners to search through. * @return {Number} Index of the specified listener, -1 if not found * @api private */ function indexOfListener(listener, listeners) { // Return the index via the native method if possible if (nativeIndexOf) { return listeners.indexOf(listener); } // There is no native method // Use a manual loop to find the index var i = listeners.length; while (i--) { // If the listener matches, return it's index if (listeners[i] === listener) { return i; } } // Default to returning -1 return -1; } /** * Fetches the events object and creates one if required. * * @return {Object} The events storage object. * @api private */ proto._getEvents = function () { return this._events || (this._events = {}); }; /** * Returns the listener array for the specified event. * Will initialise the event object and listener arrays if required. * Will return an object if you use a regex search. The object contains keys for each matched event. So /ba[rz]/ might return an object containing bar and baz. But only if you have either defined them with defineEvent or added some listeners to them. * Each property in the object response is an array of listener functions. * * @param {String|RegExp} evt Name of the event to return the listeners from. * @return {Function[]|Object} All listener functions for the event. */ proto.getListeners = function (evt) { // Create a shortcut to the storage object // Initialise it if it does not exists yet var events = this._getEvents(), response, key; // Return a concatenated array of all matching events if // the selector is a regular expression. if (typeof evt === 'object') { response = {}; for (key in events) { if (events.hasOwnProperty(key) && evt.test(key)) { response[key] = events[key]; } } } else { response = events[evt] || (events[evt] = []); } return response; }; /** * Fetches the requested listeners via getListeners but will always return the results inside an object. This is mainly for internal use but others may find it useful. * * @param {String|RegExp} evt Name of the event to return the listeners from. * @return {Object} All listener functions for an event in an object. */ proto.getListenersAsObject = function (evt) { var listeners = this.getListeners(evt), response; if (listeners instanceof Array) { response = {}; response[evt] = listeners; } return response || listeners; }; /** * Adds a listener function to the specified event. * The listener will not be added if it is a duplicate. * If the listener returns true then it will be removed after it is called. * If you pass a regular expression as the event name then the listener will be added to all events that match it. * * @param {String|RegExp} evt Name of the event to attach the listener to. * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling. * @return {Object} Current instance of EventEmitter for chaining. */ proto.addListener = function (evt, listener) { var listeners = this.getListenersAsObject(evt), key; for (key in listeners) { if (listeners.hasOwnProperty(key) && indexOfListener(listener, listeners[key]) === -1) { listeners[key].push(listener); } } // Return the instance of EventEmitter to allow chaining return this; }; /** * Alias of addListener */ proto.on = proto.addListener; /** * Defines an event name. This is required if you want to use a regex to add a listener to multiple events at once. If you don't do this then how do you expect it to know what event to add to? Should it just add to every possible match for a regex? No. That is scary and bad. * You need to tell it what event names should be matched by a regex. * * @param {String} evt Name of the event to create. * @return {Object} Current instance of EventEmitter for chaining. */ proto.defineEvent = function (evt) { this.getListeners(evt); return this; }; /** * Uses defineEvent to define multiple events. * * @param {String[]} evts An array of event names to define. * @return {Object} Current instance of EventEmitter for chaining. */ proto.defineEvents = function (evts) { for (var i = 0; i < evts.length; i += 1) { this.defineEvent(evts[i]); } return this; }; /** * Removes a listener function from the specified event. * When passed a regular expression as the event name, it will remove the listener from all events that match it. * * @param {String|RegExp} evt Name of the event to remove the listener from. * @param {Function} listener Method to remove from the event. * @return {Object} Current instance of EventEmitter for chaining. */ proto.removeListener = function (evt, listener) { var listeners = this.getListenersAsObject(evt), index, key; for (key in listeners) { if (listeners.hasOwnProperty(key)) { index = indexOfListener(listener, listeners[key]); if (index !== -1) { listeners[key].splice(index, 1); } } } // Return the instance of EventEmitter to allow chaining return this; }; /** * Alias of removeListener */ proto.off = proto.removeListener; /** * Adds listeners in bulk using the manipulateListeners method. * If you pass an object as the second argument you can add to multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. You can also pass it an event name and an array of listeners to be added. * You can also pass it a regular expression to add the array of listeners to all events that match it. * Yeah, this function does quite a bit. That's probably a bad thing. * * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add to multiple events at once. * @param {Function[]} [listeners] An optional array of listener functions to add. * @return {Object} Current instance of EventEmitter for chaining. */ proto.addListeners = function (evt, listeners) { // Pass through to manipulateListeners return this.manipulateListeners(false, evt, listeners); }; /** * Removes listeners in bulk using the manipulateListeners method. * If you pass an object as the second argument you can remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. * You can also pass it an event name and an array of listeners to be removed. * You can also pass it a regular expression to remove the listeners from all events that match it. * * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to remove from multiple events at once. * @param {Function[]} [listeners] An optional array of listener functions to remove. * @return {Object} Current instance of EventEmitter for chaining. */ proto.removeListeners = function (evt, listeners) { // Pass through to manipulateListeners return this.manipulateListeners(true, evt, listeners); }; /** * Edits listeners in bulk. The addListeners and removeListeners methods both use this to do their job. You should really use those instead, this is a little lower level. * The first argument will determine if the listeners are removed (true) or added (false). * If you pass an object as the second argument you can add/remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. * You can also pass it an event name and an array of listeners to be added/removed. * You can also pass it a regular expression to manipulate the listeners of all events that match it. * * @param {Boolean} remove True if you want to remove listeners, false if you want to add. * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add/remove from multiple events at once. * @param {Function[]} [listeners] An optional array of listener functions to add/remove. * @return {Object} Current instance of EventEmitter for chaining. */ proto.manipulateListeners = function (remove, evt, listeners) { // Initialise any required variables var i, value, single = remove ? this.removeListener : this.addListener, multiple = remove ? this.removeListeners : this.addListeners; // If evt is an object then pass each of it's properties to this method if (typeof evt === 'object' && !(evt instanceof RegExp)) { for (i in evt) { if (evt.hasOwnProperty(i) && (value = evt[i])) { // Pass the single listener straight through to the singular method if (typeof value === 'function') { single.call(this, i, value); } else { // Otherwise pass back to the multiple function multiple.call(this, i, value); } } } } else { // So evt must be a string // And listeners must be an array of listeners // Loop over it and pass each one to the multiple method i = listeners.length; while (i--) { single.call(this, evt, listeners[i]); } } // Return the instance of EventEmitter to allow chaining return this; }; /** * Removes all listeners from a specified event. * If you do not specify an event then all listeners will be removed. * That means every event will be emptied. * You can also pass a regex to remove all events that match it. * * @param {String|RegExp} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed. * @return {Object} Current instance of EventEmitter for chaining. */ proto.removeEvent = function (evt) { var type = typeof evt, events = this._getEvents(), key; // Remove different things depending on the state of evt if (type === 'string') { // Remove all listeners for the specified event delete events[evt]; } else if (type === 'object') { // Remove all events matching the regex. for (key in events) { if (events.hasOwnProperty(key) && evt.test(key)) { delete events[key]; } } } else { // Remove all listeners in all events delete this._events; } // Return the instance of EventEmitter to allow chaining return this; }; /** * Emits an event of your choice. * When emitted, every listener attached to that event will be executed. * If you pass the optional argument array then those arguments will be passed to every listener upon execution. * Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately. * So they will not arrive within the array on the other side, they will be separate. * You can also pass a regular expression to emit to all events that match it. * * @param {String|RegExp} evt Name of the event to emit and execute listeners for. * @param {Array} [args] Optional array of arguments to be passed to each listener. * @return {Object} Current instance of EventEmitter for chaining. */ proto.emitEvent = function (evt, args) { var listeners = this.getListenersAsObject(evt), i, key, response; for (key in listeners) { if (listeners.hasOwnProperty(key)) { i = listeners[key].length; while (i--) { // If the listener returns true then it shall be removed from the event // The function is executed either with a basic call or an apply if there is an args array response = args ? listeners[key][i].apply(null, args) : listeners[key][i](); if (response === true) { this.removeListener(evt, listeners[key][i]); } } } } // Return the instance of EventEmitter to allow chaining return this; }; /** * Alias of emitEvent */ proto.trigger = proto.emitEvent; /** * Subtly different from emitEvent in that it will pass its arguments on to the listeners, as opposed to taking a single array of arguments to pass on. * As with emitEvent, you can pass a regex in place of the event name to emit to all events that match it. * * @param {String|RegExp} evt Name of the event to emit and execute listeners for. * @param {...*} Optional additional arguments to be passed to each listener. * @return {Object} Current instance of EventEmitter for chaining. */ proto.emit = function (evt) { var args = Array.prototype.slice.call(arguments, 1); return this.emitEvent(evt, args); }; // Expose the class either via AMD or the global object if (typeof define === 'function' && define.amd) { define(function () { return EventEmitter; }); } else { exports.EventEmitter = EventEmitter; } }(this)); /*! * eventie v1.0.3 * event binding helper * eventie.bind( elem, 'click', myFn ) * eventie.unbind( elem, 'click', myFn ) */ /*jshint browser: true, undef: true, unused: true */ /*global define: false */ ( function( window ) { 'use strict'; var docElem = document.documentElement; var bind = function() {}; if ( docElem.addEventListener ) { bind = function( obj, type, fn ) { obj.addEventListener( type, fn, false ); }; } else if ( docElem.attachEvent ) { bind = function( obj, type, fn ) { obj[ type + fn ] = fn.handleEvent ? function() { var event = window.event; // add event.target event.target = event.target || event.srcElement; fn.handleEvent.call( fn, event ); } : function() { var event = window.event; // add event.target event.target = event.target || event.srcElement; fn.call( obj, event ); }; obj.attachEvent( "on" + type, obj[ type + fn ] ); }; } var unbind = function() {}; if ( docElem.removeEventListener ) { unbind = function( obj, type, fn ) { obj.removeEventListener( type, fn, false ); }; } else if ( docElem.detachEvent ) { unbind = function( obj, type, fn ) { obj.detachEvent( "on" + type, obj[ type + fn ] ); try { delete obj[ type + fn ]; } catch ( err ) { // can't delete window object properties obj[ type + fn ] = undefined; } }; } var eventie = { bind: bind, unbind: unbind }; // transport if ( typeof define === 'function' && define.amd ) { // AMD define( eventie ); } else { // browser global window.eventie = eventie; } })( this ); /*! * imagesLoaded v3.0.2 * JavaScript is all like "You images are done yet or what?" */ ( function( window ) { 'use strict'; var $ = window.jQuery; var console = window.console; var hasConsole = typeof console !== 'undefined'; // -------------------------- helpers -------------------------- // // extend objects function extend( a, b ) { for ( var prop in b ) { a[ prop ] = b[ prop ]; } return a; } var objToString = Object.prototype.toString; function isArray( obj ) { return objToString.call( obj ) === '[object Array]'; } // turn element or nodeList into an array function makeArray( obj ) { var ary = []; if ( isArray( obj ) ) { // use object if already an array ary = obj; } else if ( typeof obj.length === 'number' ) { // convert nodeList to array for ( var i=0, len = obj.length; i < len; i++ ) { ary.push( obj[i] ); } } else { // array of single index ary.push( obj ); } return ary; } // -------------------------- -------------------------- // function defineImagesLoaded( EventEmitter, eventie ) { /** * @param {Array, Element, NodeList, String} elem * @param {Object or Function} options - if function, use as callback * @param {Function} onAlways - callback function */ function ImagesLoaded( elem, options, onAlways ) { // coerce ImagesLoaded() without new, to be new ImagesLoaded() if ( !( this instanceof ImagesLoaded ) ) { return new ImagesLoaded( elem, options ); } // use elem as selector string if ( typeof elem === 'string' ) { elem = document.querySelectorAll( elem ); } this.elements = makeArray( elem ); this.options = extend( {}, this.options ); if ( typeof options === 'function' ) { onAlways = options; } else { extend( this.options, options ); } if ( onAlways ) { this.on( 'always', onAlways ); } this.getImages(); if ( $ ) { // add jQuery Deferred object this.jqDeferred = new $.Deferred(); } // HACK check async to allow time to bind listeners var _this = this; setTimeout( function() { _this.check(); }); } ImagesLoaded.prototype = new EventEmitter(); ImagesLoaded.prototype.options = {}; ImagesLoaded.prototype.getImages = function() { this.images = []; // filter & find items if we have an item selector for ( var i=0, len = this.elements.length; i < len; i++ ) { var elem = this.elements[i]; // filter siblings if ( elem.nodeName === 'IMG' ) { this.addImage( elem ); } // find children var childElems = elem.querySelectorAll('img'); // concat childElems to filterFound array for ( var j=0, jLen = childElems.length; j < jLen; j++ ) { var img = childElems[j]; this.addImage( img ); } } }; /** * @param {Image} img */ ImagesLoaded.prototype.addImage = function( img ) { var loadingImage = new LoadingImage( img ); this.images.push( loadingImage ); }; ImagesLoaded.prototype.check = function() { var _this = this; var checkedCount = 0; var length = this.images.length; this.hasAnyBroken = false; // complete if no images if ( !length ) { this.complete(); return; } function onConfirm( image, message ) { if ( _this.options.debug && hasConsole ) { console.log( 'confirm', image, message ); } _this.progress( image ); checkedCount++; if ( checkedCount === length ) { _this.complete(); } return true; // bind once } for ( var i=0; i < length; i++ ) { var loadingImage = this.images[i]; loadingImage.on( 'confirm', onConfirm ); loadingImage.check(); } }; ImagesLoaded.prototype.progress = function( image ) { this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded; this.emit( 'progress', this, image ); if ( this.jqDeferred ) { this.jqDeferred.notify( this, image ); } }; ImagesLoaded.prototype.complete = function() { var eventName = this.hasAnyBroken ? 'fail' : 'done'; this.isComplete = true; this.emit( eventName, this ); this.emit( 'always', this ); if ( this.jqDeferred ) { var jqMethod = this.hasAnyBroken ? 'reject' : 'resolve'; this.jqDeferred[ jqMethod ]( this ); } }; // -------------------------- jquery -------------------------- // if ( $ ) { $.fn.imagesLoaded = function( options, callback ) { var instance = new ImagesLoaded( this, options, callback ); return instance.jqDeferred.promise( $(this) ); }; } // -------------------------- -------------------------- // var cache = {}; function LoadingImage( img ) { this.img = img; } LoadingImage.prototype = new EventEmitter(); LoadingImage.prototype.check = function() { // first check cached any previous images that have same src var cached = cache[ this.img.src ]; if ( cached ) { this.useCached( cached ); return; } // add this to cache cache[ this.img.src ] = this; // If complete is true and browser supports natural sizes, // try to check for image status manually. if ( this.img.complete && this.img.naturalWidth !== undefined ) { // report based on naturalWidth this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); return; } // If none of the checks above matched, simulate loading on detached element. var proxyImage = this.proxyImage = new Image(); eventie.bind( proxyImage, 'load', this ); eventie.bind( proxyImage, 'error', this ); proxyImage.src = this.img.src; }; LoadingImage.prototype.useCached = function( cached ) { if ( cached.isConfirmed ) { this.confirm( cached.isLoaded, 'cached was confirmed' ); } else { var _this = this; cached.on( 'confirm', function( image ) { _this.confirm( image.isLoaded, 'cache emitted confirmed' ); return true; // bind once }); } }; LoadingImage.prototype.confirm = function( isLoaded, message ) { this.isConfirmed = true; this.isLoaded = isLoaded; this.emit( 'confirm', this, message ); }; // trigger specified handler for event type LoadingImage.prototype.handleEvent = function( event ) { var method = 'on' + event.type; if ( this[ method ] ) { this[ method ]( event ); } }; LoadingImage.prototype.onload = function() { this.confirm( true, 'onload' ); this.unbindProxyEvents(); }; LoadingImage.prototype.onerror = function() { this.confirm( false, 'onerror' ); this.unbindProxyEvents(); }; LoadingImage.prototype.unbindProxyEvents = function() { eventie.unbind( this.proxyImage, 'load', this ); eventie.unbind( this.proxyImage, 'error', this ); }; // ----- ----- // return ImagesLoaded; } // -------------------------- transport -------------------------- // if ( typeof define === 'function' && define.amd ) { // AMD define( [ 'eventEmitter', 'eventie' ], defineImagesLoaded ); } else { // browser global window.imagesLoaded = defineImagesLoaded( window.EventEmitter, window.eventie ); } })( window ); /*! * Isotope PACKAGED v3.0.6 * * Licensed GPLv3 for open source use * or Isotope Commercial License for commercial use * * https://isotope.metafizzy.co * Copyright 2010-2018 Metafizzy */ !function(t,e){"function"==typeof define&&define.amd?define("jquery-bridget/jquery-bridget",["jquery"],function(i){return e(t,i)}):"object"==typeof module&&module.exports?module.exports=e(t,require("jquery")):t.jQueryBridget=e(t,t.jQuery)}(window,function(t,e){"use strict";function i(i,s,a){function u(t,e,o){var n,s="$()."+i+'("'+e+'")';return t.each(function(t,u){var h=a.data(u,i);if(!h)return void r(i+" not initialized. Cannot call methods, i.e. "+s);var d=h[e];if(!d||"_"==e.charAt(0))return void r(s+" is not a valid method");var l=d.apply(h,o);n=void 0===n?l:n}),void 0!==n?n:t}function h(t,e){t.each(function(t,o){var n=a.data(o,i);n?(n.option(e),n._init()):(n=new s(o,e),a.data(o,i,n))})}a=a||e||t.jQuery,a&&(s.prototype.option||(s.prototype.option=function(t){a.isPlainObject(t)&&(this.options=a.extend(!0,this.options,t))}),a.fn[i]=function(t){if("string"==typeof t){var e=n.call(arguments,1);return u(this,t,e)}return h(this,t),this},o(a))}function o(t){!t||t&&t.bridget||(t.bridget=i)}var n=Array.prototype.slice,s=t.console,r="undefined"==typeof s?function(){}:function(t){s.error(t)};return o(e||t.jQuery),i}),function(t,e){"function"==typeof define&&define.amd?define("ev-emitter/ev-emitter",e):"object"==typeof module&&module.exports?module.exports=e():t.EvEmitter=e()}("undefined"!=typeof window?window:this,function(){function t(){}var e=t.prototype;return e.on=function(t,e){if(t&&e){var i=this._events=this._events||{},o=i[t]=i[t]||[];return o.indexOf(e)==-1&&o.push(e),this}},e.once=function(t,e){if(t&&e){this.on(t,e);var i=this._onceEvents=this._onceEvents||{},o=i[t]=i[t]||{};return o[e]=!0,this}},e.off=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){var o=i.indexOf(e);return o!=-1&&i.splice(o,1),this}},e.emitEvent=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){i=i.slice(0),e=e||[];for(var o=this._onceEvents&&this._onceEvents[t],n=0;n