jsbeans

jsbeans  1.0.0

jsbeans > jsbeans > GMaps.js (source view)
Search:
 
Filters
/*!
 * Copyright (c) 2009 Francesco Mele jsbeans@francescomele.com
 *
 * This Software is licenced under the LGPL Licence (GNU Lesser General 
 * Public License).
 * In addition to the LGPL Licence the Software is subject to the 
 * following conditions:
 * 
 * 	i	every modification must be public and comunicated to the Author
 * 	ii	every "jsbean" added to this library must be self consistent 
 * 		except for the dependence from jsbeans-x.x.x.js
 * 	iii	copyright notice and this permission notice shall be included 
 * 		in all copies or substantial portions of the Software
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 * Utility Object to check availability of Google Maps™ API during loading cycle.
 * @namespace jsbeans
 * @class GMaps
 * @param id {String} A mandatory (but not used) identifier. it may be used for extra check, for example for map container emptyness.
 * @param [options] {JSON} See <code class="param">options</code> property. Optional.
 * @constructor
 */
jsbeans.GMaps = function(id, options) {
	/**
	 * Instance {@property} <code class="methd">status</code> is set to <code>jsbeans.GMaps.READY</code> when everything works fine!
	 * @property jsbeans.GMaps.READY
	 * @type String
	 * @static
	 * @final
	 * @default "READY"
	 * */
	jsbeans.GMaps.READY = "READY";
	/**
	 * Instance {@property} <code class="methd">status</code> is set to <code>jsbeans.GMaps.ERROR</code> when script are NOT loaded (e.g. connection lost)!
	 * @property jsbeans.GMaps.ERROR
	 * @type String
	 * @static
	 * @final
	 * @default "ERROR"
	 * */
	jsbeans.GMaps.ERROR = "ERROR";
	/**
	 * Instance {@property} <code class="methd">status</code> is set to <code>jsbeans.GMaps.LOADING</code> between the invocation of {@method} <code class="methd">load</code> and the script load.
	 * @property jsbeans.GMaps.LOADING
	 * @type String
	 * @static
	 * @final
	 * @default "LOADING"
	 * */
	jsbeans.GMaps.LOADING = "LOADING";
	/**
	 * Instance {@property} <code class="methd">status</code> is set to <code>jsbeans.GMaps.NONE</code> as first status, when no calls have been made, yet.
	 * @property jsbeans.GMaps.NONE
	 * @type String
	 * @static
	 * @final
	 * @default "NONE"
	 * */
	jsbeans.GMaps.NONE = "NONE";
	/**
	 * Says if a {@method} <code class="methd">load</code> has been already made to avoid loading Google Maps APIs&trade; twice.
	 * @property jsbeans.GMaps.ALREADY_LOADED
	 * @type Boolean
	 * @static
	 * @default false
	 * */
	jsbeans.GMaps.ALREADY_LOADED = false;
	/**
	 * Resets {@property} <code class="param">jsbeans.GMaps.ALREADY_LOADED</code> to <code>false</code> so {@method} <code class="methd">load</code> may be called again.<br/>
	 * Plaese, note that Google warns you not to load its script twice to avoid unexpected behaviour. You may force script removing by passing {@param} <code class="param">remove</code> as <code>true</code>.
	 * @method reset
	 * @param [remove] {Boolean} If <code>true</code> will <strong>try</strong> to remove Google script from page. Optional.
	 * @static
	 * */
	jsbeans.GMaps.reset = function() {
		jsbeans.GMaps.ALREADY_LOADED = false;
		if (arguments[0] === true) {
			var apis = false, gstatic = false;
			var scripts = document.getElementsByTagName("script");
			for (var i = 0, script; script = scripts[i]; i++) {
				if (script.src && script.src.indexOf("maps.googleapis.com") != -1) {
					script.parentNode.removeChild(script);
					apis = true;
				}
				if (script.src && script.src.indexOf("maps.gstatic.com") != -1) {
					script.parentNode.removeChild(script);
					gstatic = true;
				}
				// exit if both has been removed
				if (apis && gstatic) {
					break;
				}
			}
		}
	};
	// instance fields and methods
	var self = this;
	if (!id) {
		throw new Error("jsbeans.GMaps: parameter 'id' is required but missing!");
	}
	/**
	 * A required String usually representing the id of the map container.<br/>
	 * Should be unique.
	 * @property id
	 * @type String
	 * */
	this.id = id;
	/**
	 * A String representing the status of current loading APIs.<br/>
	 * May be one of: <code>jsbeans.GMaps.NONE</code>, <code>jsbeans.GMaps.ERROR</code>, <code>jsbeans.GMaps.LOADING</code>, <code>jsbeans.GMaps.READY</code>
	 * @property status
	 * @type String
	 * */
	this.status = jsbeans.GMaps.NONE;
	/**
	 * Generic options container. See details for defaults.<br/>
	 * Apart from defaults developer may set any other key/value pair as they will be sent to Google as request parameters. So if developer want to specify some other parameter, the version for example, this is the right place (for version: {v: 3.7}).
	 * @property options
	 * @type JSON
	 * */
	this.options = options || {};
	// defaults
	/**
	 * Google libraries to load. Default to <code>["places"]</code>.
	 * @property options.libraries
	 * @type Array
	 * */
	this.options.libraries = this.options.libraries || ["places"];
	/**
	 * Language of map and controls. Default to <code>en_US</code>.<br/>
	 * May be any of accepted language by Google. Example: {language: "it"} so "it_ALL" will be loaded.
	 * @property options.language
	 * @type String
	 * */
	this.options.language = this.options.language || "en_US";
	/**
	 * Tells browser to retrieve current position (may be unoccurate due to ISP/wifi/3G connection). Default to <code>true</code>.
	 * @property options.sensor
	 * @type boolean
	 * */
	this.options.sensor = typeof(this.options.sensor) != "undefined" ? "" + this.options.sensor : "true";
	/**
	 * Callback function invoked in case of error. Default to empty.
	 * @property options.onerror
	 * @type Function
	 * */
	this.options.onerror = this.options.onerror || function(){};
	/**
	 * Sets the number of milliseconds to wait before checking if script has been loaded.
	 * @property options.timeout
	 * @type Integer
	 * */
	this.options.timeout = this.options.timeout || 10000;
	/**
	 * Loads Google Maps APIs on page appending the right <code>script</code> tag on head. It uses the {@param} <code class="param">options</code> to send parameters to server.
	 * @method load
	 * */
	this.load = function() {
		if (jsbeans.GMaps.ALREADY_LOADED) {
			// no need to warn user as script is already available
			return;
		}
		// to manage multiple instance we create a callback function on the fly
		var customCallbackName = "jsbeansGMaps_" + self.id + "_" + (new Date).getTime();
		var previousCallback = self.options.callback;
		window[customCallbackName] = function() {
			// if here Google APIs have been successfully loaded
			self.status = jsbeans.GMaps.READY;
			// set to true so subsequent calls may be stopped
			jsbeans.GMaps.ALREADY_LOADED = true;
			// custom callback
			if (!!previousCallback) {
				eval("(" + previousCallback + "())");
			}
		};
		// loading Google script
		self.status = jsbeans.GMaps.LOADING;
		var script = document.createElement("script");
	    var url = "http://maps.googleapis.com/maps/api/js?";
	    var pars = ["callback=" + customCallbackName];
	    for (var p in self.options) {
	    if (p != "callback" && typeof(self.options[p]) != "function") {
		    	pars.push(p + "=" + encodeURIComponent(self.options[p]));
	    	}
	    }
	    
	    script.src = url + (pars.join("&").replace(/%20/g, "+"));
	    script.type = "text/javascript";
	 
	    script.addEventListener("error", function(e) {
	    	self.status = jsbeans.GMaps.ERROR;
	    	if (!!self.options.onerror) {
	    		self.options.onerror();
	    	}
	    }, false);
	 
	    script.addEventListener("load", function(e) {
	        setTimeout(function() {
	            if (self.status == jsbeans.GMaps.LOADING) {
	            	self.status = jsbeans.GMaps.ERROR;
	            }
	        }, self.timeout);
	    }, false);
	 
	    document.getElementsByTagName("head")[0].appendChild(script);
	};
};

Copyright © 2016 Francesco Mele. All rights reserved.