/* External API
================================================== */
if(typeof VMM != 'undefined' && typeof VMM.ExternalAPI == 'undefined') {
	
	VMM.ExternalAPI = ({
		
		keys: {
			google:				"",
			flickr:				"",
			twitter:			""
		},
		
		keys_master: {
			vp:			"Pellentesque nibh felis, eleifend id, commodo in, interdum vitae, leo",
			flickr:		"RAIvxHY4hE/Elm5cieh4X5ptMyDpj7MYIxziGxi0WGCcy1s+yr7rKQ==",
			google:		"jwNGnYw4hE9lmAez4ll0QD+jo6SKBJFknkopLS4FrSAuGfIwyj57AusuR0s8dAo=",
			twitter:	""
		},
		
		init: function() {
			return this;
		},
		
		setKeys: function(d) {
			VMM.ExternalAPI.keys	= d;
		},
		
		pushQues: function() {
			
			if (VMM.master_config.googlemaps.active) {
				VMM.ExternalAPI.googlemaps.pushQue();
			}
			if (VMM.master_config.youtube.active) {
				VMM.ExternalAPI.youtube.pushQue();
			}
			if (VMM.master_config.soundcloud.active) {
				VMM.ExternalAPI.soundcloud.pushQue();
			}
			if (VMM.master_config.googledocs.active) {
				VMM.ExternalAPI.googledocs.pushQue();
			}
			if (VMM.master_config.googleplus.active) {
				VMM.ExternalAPI.googleplus.pushQue();
			}
			if (VMM.master_config.wikipedia.active) {
				VMM.ExternalAPI.wikipedia.pushQue();
			}
			if (VMM.master_config.vimeo.active) {
				VMM.ExternalAPI.vimeo.pushQue();
			}
			if (VMM.master_config.vine.active) {
				VMM.ExternalAPI.vine.pushQue();
			}
			if (VMM.master_config.twitter.active) {
				VMM.ExternalAPI.twitter.pushQue();
			}
			if (VMM.master_config.flickr.active) {
				VMM.ExternalAPI.flickr.pushQue();
			}
			if (VMM.master_config.webthumb.active) {
				VMM.ExternalAPI.webthumb.pushQue();
			}
		},
		
		twitter: {
			tweetArray: [],
			
			get: function(m) {
				var tweet = {mid: m.id, id: m.uid};
				VMM.master_config.twitter.que.push(tweet);
				VMM.master_config.twitter.active = true;
				//VMM.master_config.api.pushques.push(VMM.ExternalAPI.twitter.pushQue);
				
			},
			
			create: function(tweet, callback) {
				
				var id				= tweet.mid.toString(),
					error_obj		= { twitterid: tweet.mid },
					the_url			= "//api.twitter.com/1/statuses/show.json?id=" + tweet.mid + "&include_entities=true&callback=?";
					//twitter_timeout	= setTimeout(VMM.ExternalAPI.twitter.errorTimeOut, VMM.master_config.timers.api, tweet),
					//callback_timeout= setTimeout(callback, VMM.master_config.timers.api, tweet);
				
				VMM.ExternalAPI.twitter.getOEmbed(tweet, callback);
				
				/*
				// Disabled thanks to twitter's new api
				
				VMM.getJSON(the_url, function(d) {
					var id		= d.id_str,
						twit	= "<blockquote><p>",
						td		= VMM.Util.linkify_with_twitter(d.text, "_blank");
					
					//	TWEET CONTENT	
					twit += td;
					twit += "</p></blockquote>";
					
					//	TWEET MEDIA
					if (typeof d.entities.media != 'undefined') {
						if (d.entities.media[0].type == "photo") {
							//twit += "<img src=' " + d.entities.media[0].media_url + "'  alt=''>"
						}
					}
					
					//	TWEET AUTHOR
					twit += "<div class='vcard author'>";
					twit += "<a class='screen-name url' href='https://twitter.com/" + d.user.screen_name + "' data-screen-name='" + d.user.screen_name + "' target='_blank'>";
					twit += "<span class='avatar'><img src=' " + d.user.profile_image_url + "'  alt=''></span>";
					twit += "<span class='fn'>" + d.user.name + "</span>";
					twit += "<span class='nickname'>@" + d.user.screen_name + "<span class='thumbnail-inline'></span></span>";
					twit += "</a>";
					twit += "</div>";
				
					
				
					VMM.attachElement("#"+tweet.id.toString(), twit );
					VMM.attachElement("#text_thumb_"+tweet.id.toString(), d.text );
					VMM.attachElement("#marker_content_" + tweet.id.toString(), d.text );
					
				})
				.error(function(jqXHR, textStatus, errorThrown) {
					trace("TWITTER error");
					trace("TWITTER ERROR: " + textStatus + " " + jqXHR.responseText);
					VMM.attachElement("#"+tweet.id, VMM.MediaElement.loadingmessage("ERROR LOADING TWEET " + tweet.mid) );
				})
				.success(function(d) {
					clearTimeout(twitter_timeout);
					clearTimeout(callback_timeout);
					callback();
				});
				
				*/
			},
			
			errorTimeOut: function(tweet) {
				trace("TWITTER JSON ERROR TIMEOUT " + tweet.mid);
				VMM.attachElement("#"+tweet.id.toString(), VMM.MediaElement.loadingmessage("Still waiting on Twitter: " + tweet.mid) );
				// CHECK RATE STATUS
				VMM.getJSON("//api.twitter.com/1/account/rate_limit_status.json", function(d) {
					trace("REMAINING TWITTER API CALLS " + d.remaining_hits);
					trace("TWITTER RATE LIMIT WILL RESET AT " + d.reset_time);
					var mes = "";
					if (d.remaining_hits == 0) {
						mes		= 	"<p>You've reached the maximum number of tweets you can load in an hour.</p>";
						mes 	+=	"<p>You can view tweets again starting at: <br/>" + d.reset_time + "</p>";
					} else {
						mes		=	"<p>Still waiting on Twitter. " + tweet.mid + "</p>";
						//mes 	= 	"<p>Tweet " + id + " was not found.</p>";
					}
					VMM.attachElement("#"+tweet.id.toString(), VMM.MediaElement.loadingmessage(mes) );
				});
				
			},
			
			errorTimeOutOembed: function(tweet) {
				trace("TWITTER JSON ERROR TIMEOUT " + tweet.mid);
				VMM.attachElement("#"+tweet.id.toString(), VMM.MediaElement.loadingmessage("Still waiting on Twitter: " + tweet.mid) );
			},
			
			pushQue: function() {
				if (VMM.master_config.twitter.que.length > 0) {
					VMM.ExternalAPI.twitter.create(VMM.master_config.twitter.que[0], VMM.ExternalAPI.twitter.pushQue);
					VMM.Util.removeRange(VMM.master_config.twitter.que,0);
				}
			},
						
			getOEmbed: function(tweet, callback) {
				
				var the_url = "//api.twitter.com/1/statuses/oembed.json?id=" + tweet.mid + "&omit_script=true&include_entities=true&callback=?",
					twitter_timeout	= setTimeout(VMM.ExternalAPI.twitter.errorTimeOutOembed, VMM.master_config.timers.api, tweet);
					//callback_timeout= setTimeout(callback, VMM.master_config.timers.api, tweet);
				
				VMM.getJSON(the_url, function(d) {
					var twit	= "",
						tuser	= "";
					
					
					//	TWEET CONTENT
					twit += d.html.split("<\/p>\&mdash;")[0] + "</p></blockquote>";
					tuser = d.author_url.split("twitter.com\/")[1];
					
					
					//	TWEET AUTHOR
					twit += "<div class='vcard author'>";
					twit += "<a class='screen-name url' href='" + d.author_url + "' target='_blank'>";
					twit += "<span class='avatar'></span>";
					twit += "<span class='fn'>" + d.author_name + "</span>";
					twit += "<span class='nickname'>@" + tuser + "<span class='thumbnail-inline'></span></span>";
					twit += "</a>";
					twit += "</div>";
					
					VMM.attachElement("#"+tweet.id.toString(), twit );
					VMM.attachElement("#text_thumb_"+tweet.id.toString(), d.html );
					VMM.attachElement("#marker_content_" + tweet.id.toString(), d.html );
				})
				.error(function(jqXHR, textStatus, errorThrown) {
					trace("TWITTER error");
					trace("TWITTER ERROR: " + textStatus + " " + jqXHR.responseText);
					clearTimeout(twitter_timeout);
					//clearTimeout(callback_timeout);
					VMM.attachElement("#"+tweet.id, VMM.MediaElement.loadingmessage("ERROR LOADING TWEET " + tweet.mid) );
				})
				.success(function(d) {
					clearTimeout(twitter_timeout);
					// clearTimeout(callback_timeout);
					callback();
				});
				
			},
			
			getHTML: function(id) {
				//var the_url = document.location.protocol + "//api.twitter.com/1/statuses/oembed.json?id=" + id+ "&callback=?";
				var the_url = "//api.twitter.com/1/statuses/oembed.json?id=" + id+ "&omit_script=true&include_entities=true&callback=?";
				VMM.getJSON(the_url, VMM.ExternalAPI.twitter.onJSONLoaded);
			},
			
			onJSONLoaded: function(d) {
				trace("TWITTER JSON LOADED");
				var id = d.id;
				VMM.attachElement("#"+id, VMM.Util.linkify_with_twitter(d.html) );
			},
			
			parseTwitterDate: function(d) {
				var date = new Date(Date.parse(d));
				/*
				var t = d.replace(/(\d{1,2}[:]\d{2}[:]\d{2}) (.*)/, '$2 $1');
				t = t.replace(/(\+\S+) (.*)/, '$2 $1');
				var date = new Date(Date.parse(t)).toLocaleDateString();
				var time = new Date(Date.parse(t)).toLocaleTimeString();
				*/
				return date;
			},
			
			prettyParseTwitterDate: function(d) {
				var date = new Date(Date.parse(d));
				return VMM.Date.prettyDate(date, true);
			},
			
			getTweets: function(tweets) {
				var tweetArray = [];
				var number_of_tweets = tweets.length;
				
				for(var i = 0; i < tweets.length; i++) {
					
					var twitter_id = "";
					
					
					/* FIND THE TWITTER ID
					================================================== */
					if (tweets[i].tweet.match("status\/")) {
						twitter_id = tweets[i].tweet.split("status\/")[1];
					} else if (tweets[i].tweet.match("statuses\/")) {
						twitter_id = tweets[i].tweet.split("statuses\/")[1];
					} else {
						twitter_id = "";
					}
					
					/* FETCH THE DATA
					================================================== */
					var the_url = "//api.twitter.com/1/statuses/show.json?id=" + twitter_id + "&include_entities=true&callback=?";
					VMM.getJSON(the_url, function(d) {
						
						var tweet = {}
						/* FORMAT RESPONSE
						================================================== */
						var twit = "<div class='twitter'><blockquote><p>";
						var td = VMM.Util.linkify_with_twitter(d.text, "_blank");
						twit += td;
						twit += "</p>";
						
						twit += "— " + d.user.name + " (<a href='https://twitter.com/" + d.user.screen_name + "'>@" + d.user.screen_name + "</a>) <a href='https://twitter.com/" + d.user.screen_name + "/status/" + d.id + "'>" + VMM.ExternalAPI.twitter.prettyParseTwitterDate(d.created_at) + " </a></blockquote></div>";
						
						tweet.content = twit;
						tweet.raw = d;
						
						tweetArray.push(tweet);
						
						
						/* CHECK IF THATS ALL OF THEM
						================================================== */
						if (tweetArray.length == number_of_tweets) {
							var the_tweets = {tweetdata: tweetArray}
							VMM.fireEvent(global, "TWEETSLOADED", the_tweets);
						}
					})
					.success(function() { trace("second success"); })
					.error(function() { trace("error"); })
					.complete(function() { trace("complete"); });
					
				}
					
				
			},
			
			getTweetSearch: function(tweets, number_of_tweets) {
				var _number_of_tweets = 40;
				if (number_of_tweets != null && number_of_tweets != "") {
					_number_of_tweets = number_of_tweets;
				}
				
				var the_url = "//search.twitter.com/search.json?q=" + tweets + "&rpp=" + _number_of_tweets + "&include_entities=true&result_type=mixed";
				var tweetArray = [];
				VMM.getJSON(the_url, function(d) {
					
					/* FORMAT RESPONSE
					================================================== */
					for(var i = 0; i < d.results.length; i++) {
						var tweet = {}
						var twit = "<div class='twitter'><blockquote><p>";
						var td = VMM.Util.linkify_with_twitter(d.results[i].text, "_blank");
						twit += td;
						twit += "</p>";
						twit += "— " + d.results[i].from_user_name + " (<a href='https://twitter.com/" + d.results[i].from_user + "'>@" + d.results[i].from_user + "</a>) <a href='https://twitter.com/" + d.results[i].from_user + "/status/" + d.id + "'>" + VMM.ExternalAPI.twitter.prettyParseTwitterDate(d.results[i].created_at) + " </a></blockquote></div>";
						tweet.content = twit;
						tweet.raw = d.results[i];
						tweetArray.push(tweet);
					}
					var the_tweets = {tweetdata: tweetArray}
					VMM.fireEvent(global, "TWEETSLOADED", the_tweets);
				});
				
			},
			
			prettyHTML: function(id, secondary) {
				var id = id.toString();
				var error_obj = {
					twitterid: id
				};
				var the_url = "//api.twitter.com/1/statuses/show.json?id=" + id + "&include_entities=true&callback=?";
				var twitter_timeout = setTimeout(VMM.ExternalAPI.twitter.errorTimeOut, VMM.master_config.timers.api, id);
				
				VMM.getJSON(the_url, VMM.ExternalAPI.twitter.formatJSON)
					.error(function(jqXHR, textStatus, errorThrown) {
						trace("TWITTER error");
						trace("TWITTER ERROR: " + textStatus + " " + jqXHR.responseText);
						VMM.attachElement("#twitter_"+id, "<p>ERROR LOADING TWEET " + id + "</p>" );
					})
					.success(function(d) {
						clearTimeout(twitter_timeout);
						if (secondary) {
							VMM.ExternalAPI.twitter.secondaryMedia(d);
						}
					});
			},
			
			
			
			formatJSON: function(d) {
				var id = d.id_str;
				
				var twit = "<blockquote><p>";
				var td = VMM.Util.linkify_with_twitter(d.text, "_blank");
				//td = td.replace(/(@([\w]+))/g,"<a href='http://twitter.com/$2' target='_blank'>$1</a>");
				//td = td.replace(/(#([\w]+))/g,"<a href='http://twitter.com/#search?q=%23$2' target='_blank'>$1</a>");
				twit += td;
				twit += "</p></blockquote>";
				//twit += " <a href='https://twitter.com/" + d.user.screen_name + "/status/" + d.id_str + "' target='_blank' alt='link to original tweet' title='link to original tweet'>" + "<span class='created-at'></span>" + " </a>";
				
				twit += "<div class='vcard author'>";
				twit += "<a class='screen-name url' href='https://twitter.com/" + d.user.screen_name + "' data-screen-name='" + d.user.screen_name + "' target='_blank'>";
				twit += "<span class='avatar'><img src=' " + d.user.profile_image_url + "'  alt=''></span>";
				twit += "<span class='fn'>" + d.user.name + "</span>";
				twit += "<span class='nickname'>@" + d.user.screen_name + "<span class='thumbnail-inline'></span></span>";
				twit += "</a>";
				twit += "</div>";
				
				if (typeof d.entities.media != 'undefined') {
					if (d.entities.media[0].type == "photo") {
						twit += "<img src=' " + d.entities.media[0].media_url + "'  alt=''>"
					}
				}
				
				VMM.attachElement("#twitter_"+id.toString(), twit );
				VMM.attachElement("#text_thumb_"+id.toString(), d.text );
				
			}
			
			
		},
		
		googlemaps: {
			
			maptype: "TERRAIN", // see also below for default if this is a google type
			
			setMapType: function(d) {
				if (d != "") {
					VMM.ExternalAPI.googlemaps.maptype = d;
				}
			},
			
			get: function(m) {
				var timer, 
					api_key,
					map_url;
				
				m.vars = VMM.Util.getUrlVars(m.id);
				
				if (VMM.ExternalAPI.keys.google != "") {
					api_key = VMM.ExternalAPI.keys.google;
				} else {
					api_key = Aes.Ctr.decrypt(VMM.ExternalAPI.keys_master.google, VMM.ExternalAPI.keys_master.vp, 256);
				}
				
				
				/*
					Investigating a google map api change on the latest release that causes custom map types to stop working
					http://stackoverflow.com/questions/13486271/google-map-markermanager-cannot-call-method-substr-of-undefined
					soulution is to use api ver 3.9
				*/
				map_url = "//maps.googleapis.com/maps/api/js?key=" + api_key + "&v=3.9&libraries=places&sensor=false&callback=VMM.ExternalAPI.googlemaps.onMapAPIReady";
				
				if (VMM.master_config.googlemaps.active) {
					VMM.master_config.googlemaps.que.push(m);
				} else {
					VMM.master_config.googlemaps.que.push(m);
					
					if (VMM.master_config.googlemaps.api_loaded) {
						
					} else {
						LoadLib.js(map_url, function() {
							trace("Google Maps API Library Loaded");
						});
					}
				}
			},
			
			create: function(m) {
				VMM.ExternalAPI.googlemaps.createAPIMap(m);
			},
			
			createiFrameMap: function(m) {
				var embed_url		= m.id + "&output=embed",
					mc				= "",
					unique_map_id	= m.uid.toString() + "_gmap";
					
				mc				+= "<div class='google-map' id='" + unique_map_id + "' style='width=100%;height=100%;'>";
				mc				+= "<iframe width='100%' height='100%' frameborder='0' scrolling='no' marginheight='0' marginwidth='0' src='" + embed_url + "'></iframe>";
				mc				+= "</div>";
				
				VMM.attachElement("#" + m.uid, mc);
				
			},
			
			createAPIMap: function(m) {
				var map_attribution	= "",
					layer,
					map,
					map_options,
					unique_map_id			= m.uid.toString() + "_gmap",
					map_attribution_html	= "",
					location				= new google.maps.LatLng(41.875696,-87.624207),
					latlong,
					zoom					= 11,
					has_location			= false,
					has_zoom				= false,
					api_limit				= false,
					map_bounds;
					
				
				function mapProvider(name) {
					if (name in VMM.ExternalAPI.googlemaps.map_providers) {
						map_attribution = VMM.ExternalAPI.googlemaps.map_attribution[VMM.ExternalAPI.googlemaps.map_providers[name].attribution];
						return VMM.ExternalAPI.googlemaps.map_providers[name];
					} else {
						if (VMM.ExternalAPI.googlemaps.defaultType(name)) {
							trace("GOOGLE MAP DEFAULT TYPE");
							return google.maps.MapTypeId[name.toUpperCase()];
						} else {
							trace("Not a maptype: " + name );
						}
					}
				}
				
				google.maps.VeriteMapType = function(name) {
					if (VMM.ExternalAPI.googlemaps.defaultType(name)) {
						return google.maps.MapTypeId[name.toUpperCase()];
					} else {
						var provider = 			mapProvider(name);
						return google.maps.ImageMapType.call(this, {
							"getTileUrl": function(coord, zoom) {
								var index = 	(zoom + coord.x + coord.y) % VMM.ExternalAPI.googlemaps.map_subdomains.length;
								var retURL =  provider.url
										.replace("{S}", VMM.ExternalAPI.googlemaps.map_subdomains[index])
										.replace("{Z}", zoom)
										.replace("{X}", coord.x)
										.replace("{Y}", coord.y)
										.replace("{z}", zoom)
										.replace("{x}", coord.x)
										.replace("{y}", coord.y);

								// trace(retURL);
								return retURL;
							},
							"tileSize": 		new google.maps.Size(256, 256),
							"name":				name,
							"minZoom":			provider.minZoom,
							"maxZoom":			provider.maxZoom
						});
					}
				};
				
				google.maps.VeriteMapType.prototype = new google.maps.ImageMapType("_");
				
				/* Make the Map
				================================================== */
				
				if (VMM.ExternalAPI.googlemaps.maptype != "") {
					if (VMM.ExternalAPI.googlemaps.defaultType(VMM.ExternalAPI.googlemaps.maptype)) {
						layer				=	google.maps.MapTypeId[VMM.ExternalAPI.googlemaps.maptype.toUpperCase()];
					} else {
						layer				=	VMM.ExternalAPI.googlemaps.maptype;
					}
				} else {
					layer				=	google.maps.MapTypeId['TERRAIN'];
				}
				
				var new_google_url_regex = new RegExp(/@([0-9\.\-]+),([0-9\.\-]+),(\d+)z/);

				if (m.id.match(new_google_url_regex)) {
					var match = m.id.match(new_google_url_regex)
					lat = parseFloat(match[1]);
					lng = parseFloat(match[2]);
					location = new google.maps.LatLng(lat,lng);
					zoom = parseFloat(match[3]);
					has_location = has_zoom = true;
				} else {
					if (type.of(VMM.Util.getUrlVars(m.id)["ll"]) == "string") {
							has_location			= true;
							latlong					= VMM.Util.getUrlVars(m.id)["ll"].split(",");
							location				= new google.maps.LatLng(parseFloat(latlong[0]),parseFloat(latlong[1]));
							
						} else if (type.of(VMM.Util.getUrlVars(m.id)["sll"]) == "string") {
							latlong					= VMM.Util.getUrlVars(m.id)["sll"].split(",");
							location				= new google.maps.LatLng(parseFloat(latlong[0]),parseFloat(latlong[1]));
						} 
						
						if (type.of(VMM.Util.getUrlVars(m.id)["z"]) == "string") {
							has_zoom				=	true;
							zoom					=	parseFloat(VMM.Util.getUrlVars(m.id)["z"]);
						}
				}				
					
				map_options = {
					zoom:						zoom,
					draggable: 					false, 
					disableDefaultUI:			true,
					mapTypeControl:				false,
					zoomControl:				true,
					zoomControlOptions: {
						style:					google.maps.ZoomControlStyle.SMALL,
						position:				google.maps.ControlPosition.TOP_RIGHT
					},
					center: 					location,
					mapTypeId:					layer,
					mapTypeControlOptions: {
				        mapTypeIds:				[layer]
				    }
				}
				
				VMM.attachElement("#" + m.uid, "<div class='google-map' id='" + unique_map_id + "' style='width=100%;height=100%;'></div>");
				
				map		= new google.maps.Map(document.getElementById(unique_map_id), map_options);
				
				if (VMM.ExternalAPI.googlemaps.defaultType(VMM.ExternalAPI.googlemaps.maptype)) {
					
				} else {
					map.mapTypes.set(layer, new google.maps.VeriteMapType(layer));
					// ATTRIBUTION
					map_attribution_html =	"<div class='map-attribution'><div class='attribution-text'>" + map_attribution + "</div></div>";
					VMM.appendElement("#"+unique_map_id, map_attribution_html);
				}
				
				// DETERMINE IF KML IS POSSIBLE 
				if (type.of(VMM.Util.getUrlVars(m.id)["msid"]) == "string") {
					loadKML();
				} else {
					//loadPlaces();
					if (type.of(VMM.Util.getUrlVars(m.id)["q"]) == "string") {
						geocodePlace();
					} 
				}
				
				// GEOCODE
				function geocodePlace() {


					
					var geocoder	= new google.maps.Geocoder(),
						address		= VMM.Util.getUrlVars(m.id)["q"],
						marker;
						
					if (address.match("loc:")) {
						var address_latlon = address.split(":")[1].split("+");
						location = new google.maps.LatLng(parseFloat(address_latlon[0]),parseFloat(address_latlon[1]));
						has_location = true;
					}
						
					geocoder.geocode( { 'address': address}, function(results, status) {
						if (status == google.maps.GeocoderStatus.OK) {
							
							marker = new google.maps.Marker({
								map: map,
								position: results[0].geometry.location
							});
							
							// POSITION MAP
							//map.setCenter(results[0].geometry.location);
							//map.panTo(location);
							if (typeof results[0].geometry.viewport != 'undefined') {
								map.fitBounds(results[0].geometry.viewport);
							} else if (typeof results[0].geometry.bounds != 'undefined') {
								map.fitBounds(results[0].geometry.bounds);
							} else {
								map.setCenter(results[0].geometry.location);
							}
							
							if (has_location) {
								map.panTo(location);
							}
							if (has_zoom) {
								map.setZoom(zoom);
							}
							
						} else {
							trace("Geocode for " + address + " was not successful for the following reason: " + status);
							trace("TRYING PLACES SEARCH");
							
							if (has_location) {
								map.panTo(location);
							}
							if (has_zoom) {
								map.setZoom(zoom);
							}
							
							loadPlaces();
						}
					});
				}
				
				// PLACES
				function loadPlaces() {
					var place,
						search_request,
						infowindow,
						search_bounds,
						bounds_sw,
						bounds_ne;
					
					place_search	= new google.maps.places.PlacesService(map);
					infowindow		= new google.maps.InfoWindow();
					
					search_request = {
						query:		"",
						types:		['country', 'neighborhood', 'political', 'locality', 'geocode']
					};
					
					if (type.of(VMM.Util.getUrlVars(m.id)["q"]) == "string") {
						search_request.query	= VMM.Util.getUrlVars(m.id)["q"];
					}
					
					if (has_location) {
						search_request.location	= location;
						search_request.radius	= "15000";
					} else {
						bounds_sw = new google.maps.LatLng(-89.999999,-179.999999);
						bounds_ne = new google.maps.LatLng(89.999999,179.999999);
						search_bounds = new google.maps.LatLngBounds(bounds_sw,bounds_ne);
						
						//search_request.location	= search_bounds;
					}
					
					place_search.textSearch(search_request, placeResults);
					
					function placeResults(results, status) {
						
						if (status == google.maps.places.PlacesServiceStatus.OK) {
							
							for (var i = 0; i < results.length; i++) {
								//createMarker(results[i]);
							}
							
							if (has_location) {
								map.panTo(location);
							} else {
								if (results.length >= 1) {
									map.panTo(results[0].geometry.location);
									if (has_zoom) {
										map.setZoom(zoom);
									}
								} 
							}
							
							
						} else {
							trace("Place search for " + search_request.query + " was not successful for the following reason: " + status);
							// IF There's a problem loading the map, load a simple iFrame version instead
							trace("YOU MAY NEED A GOOGLE MAPS API KEY IN ORDER TO USE THIS FEATURE OF TIMELINEJS");
							trace("FIND OUT HOW TO GET YOUR KEY HERE: https://developers.google.com/places/documentation/#Authentication");
							
							
							if (has_location) {
								map.panTo(location);
								if (has_zoom) {
									map.setZoom(zoom);
								}
							} else {
								trace("USING SIMPLE IFRAME MAP EMBED");
								if (m.id[0].match("https")) { 
									m.id = m.url[0].replace("https", "http");
								}
								VMM.ExternalAPI.googlemaps.createiFrameMap(m);
							}
							
						}
						
					}
					
					function createMarker(place) {
						var marker, placeLoc;
						
						placeLoc = place.geometry.location;
						marker = new google.maps.Marker({
							map: map,
							position: place.geometry.location
						});

						google.maps.event.addListener(marker, 'click', function() {
							infowindow.setContent(place.name);
							infowindow.open(map, this);
						});
					}
					
				}
				
				function loadPlacesAlt() {
					var api_key,
						places_url,
						has_key		= false;
						
					trace("LOADING PLACES API FOR GOOGLE MAPS");
						
					if (VMM.ExternalAPI.keys.google != "") {
						api_key	= VMM.ExternalAPI.keys.google;
						has_key	= true;
					} else {
						trace("YOU NEED A GOOGLE MAPS API KEY IN ORDER TO USE THIS FEATURE OF TIMELINEJS");
						trace("FIND OUT HOW TO GET YOUR KEY HERE: https://developers.google.com/places/documentation/#Authentication");
					}
					
					places_url		= "https://maps.googleapis.com/maps/api/place/textsearch/json?key=" + api_key + "&sensor=false&language=" + m.lang + "&";
					
					if (type.of(VMM.Util.getUrlVars(m.id)["q"]) == "string") {
						places_url	+= "query=" + VMM.Util.getUrlVars(m.id)["q"];
					}
					
					if (has_location) {
						places_url	+= "&location=" + location;
					} 
					
					if (has_key) {
						VMM.getJSON(places_url, function(d) {
							trace("PLACES JSON");
						
							var places_location 	= "",
								places_bounds		= "",
								places_bounds_ne	= "",
								places_bounds_sw	= "";
						
							trace(d);
						
							if (d.status == "OVER_QUERY_LIMIT") {
								trace("OVER_QUERY_LIMIT");
								if (has_location) {
									map.panTo(location);
									if (has_zoom) {
										map.setZoom(zoom);
									}
								} else {
									trace("DOING TRADITIONAL MAP IFRAME EMBED UNTIL QUERY LIMIT RESTORED");
									api_limit = true;
									VMM.ExternalAPI.googlemaps.createiFrameMap(m);
								}
							
							} else {
								if (d.results.length >= 1) {
									//location = new google.maps.LatLng(parseFloat(d.results[0].geometry.location.lat),parseFloat(d.results[0].geometry.location.lng));
									//map.panTo(location);
							
									places_bounds_ne	= new google.maps.LatLng(parseFloat(d.results[0].geometry.viewport.northeast.lat),parseFloat(d.results[0].geometry.viewport.northeast.lng));
									places_bounds_sw	= new google.maps.LatLng(parseFloat(d.results[0].geometry.viewport.southwest.lat),parseFloat(d.results[0].geometry.viewport.southwest.lng));
							
									places_bounds = new google.maps.LatLngBounds(places_bounds_sw, places_bounds_ne)
									map.fitBounds(places_bounds);
							
								} else {
									trace("NO RESULTS");
								}
						
								if (has_location) {
									map.panTo(location);
								} 
								if (has_zoom) {
									map.setZoom(zoom);
								}
							}
						
						})
						.error(function(jqXHR, textStatus, errorThrown) {
							trace("PLACES JSON ERROR");
							trace("PLACES JSON ERROR: " + textStatus + " " + jqXHR.responseText);
						})
						.success(function(d) {
							trace("PLACES JSON SUCCESS");
						});
					} else {
						if (has_location) {
							map.panTo(location);
							if (has_zoom) {
								map.setZoom(zoom);
							}
						} else {
							trace("DOING TRADITIONAL MAP IFRAME EMBED BECAUSE NO GOOGLE MAP API KEY WAS PROVIDED");
							VMM.ExternalAPI.googlemaps.createiFrameMap(m);
						}
					}
					
					
				}
				
				// KML
				function loadKML() {
					var kml_url, kml_layer, infowindow, text;
					
					kml_url				= m.id + "&output=kml";
					kml_url				= kml_url.replace("&output=embed", "");
					kml_layer			= new google.maps.KmlLayer(kml_url, {preserveViewport:true});
					infowindow			= new google.maps.InfoWindow();
					kml_layer.setMap(map);
					
					google.maps.event.addListenerOnce(kml_layer, "defaultviewport_changed", function() {
					   
						if (has_location) {
							map.panTo(location);
						} else {
							map.fitBounds(kml_layer.getDefaultViewport() );
						}
						if (has_zoom) {
							map.setZoom(zoom);
						}
					});
					
					google.maps.event.addListener(kml_layer, 'click', function(kmlEvent) {
						text			= kmlEvent.featureData.description;
						showInfoWindow(text);
						
						function showInfoWindow(c) {
							infowindow.setContent(c);
							infowindow.open(map);
						}
					});
				}
				
				
			},
			
			pushQue: function() {
				for(var i = 0; i < VMM.master_config.googlemaps.que.length; i++) {
					VMM.ExternalAPI.googlemaps.create(VMM.master_config.googlemaps.que[i]);
				}
				VMM.master_config.googlemaps.que = [];
			},
			
			onMapAPIReady: function() {
				VMM.master_config.googlemaps.map_active = true;
				VMM.master_config.googlemaps.places_active = true;
				VMM.ExternalAPI.googlemaps.onAPIReady();
			},
			
			onPlacesAPIReady: function() {
				VMM.master_config.googlemaps.places_active = true;
				VMM.ExternalAPI.googlemaps.onAPIReady();
			},
			
			onAPIReady: function() {
				if (!VMM.master_config.googlemaps.active) {
					if (VMM.master_config.googlemaps.map_active && VMM.master_config.googlemaps.places_active) {
						VMM.master_config.googlemaps.active = true;
						VMM.ExternalAPI.googlemaps.pushQue();
					}
				}
			},
			
			defaultType: function(name) {
				if (name.toLowerCase() == "satellite" || name.toLowerCase() == "hybrid" || name.toLowerCase() == "terrain" || name.toLowerCase() == "roadmap") {
					return true;
				} else {
					return false;
				}
			},
			
			map_subdomains: ["", "a.", "b.", "c.", "d."],
			
			map_attribution: {
				"stamen": 			"Map tiles by <a href='http://stamen.com'>Stamen Design</a>, under <a href='http://creativecommons.org/licenses/by/3.0'>CC BY 3.0</a>. Data by <a href='http://openstreetmap.org'>OpenStreetMap</a>, under <a href='http://creativecommons.org/licenses/by-sa/3.0'>CC BY SA</a>.",
				"apple": 			"Map data &copy; 2012  Apple, Imagery &copy; 2012 Apple",
				"osm":				"&copy; <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors"
			},
									
			map_providers: {
				"toner": {
					"url": 			"//{S}tile.stamen.com/toner/{Z}/{X}/{Y}.png",
					"minZoom": 		0,
					"maxZoom": 		20,
					"attribution": 	"stamen"
					
				},
				"toner-lines": {
					"url": 			"//{S}tile.stamen.com/toner-lines/{Z}/{X}/{Y}.png",
					"minZoom": 		0,
					"maxZoom": 		20,
					"attribution": 	"stamen"
				},
				"toner-labels": {
					"url": 			"//{S}tile.stamen.com/toner-labels/{Z}/{X}/{Y}.png",
					"minZoom": 		0,
					"maxZoom": 		20,
					"attribution": 	"stamen"
				},
				"sterrain": {
					"url": 			"//{S}tile.stamen.com/terrain/{Z}/{X}/{Y}.jpg",
					"minZoom": 		4,
					"maxZoom": 		20,
					"attribution": 	"stamen"
				},
				"apple": {
					"url": 			"//gsp2.apple.com/tile?api=1&style=slideshow&layers=default&lang=en_US&z={z}&x={x}&y={y}&v=9",
					"minZoom": 		4,
					"maxZoom": 		14,
					"attribution": 	"apple"
				},
				"watercolor": {
					"url": 			"//{S}tile.stamen.com/watercolor/{Z}/{X}/{Y}.jpg",
					"minZoom": 		3,
					"maxZoom": 		16,
					"attribution": 	"stamen"
				},
				"osm": {
					"url": 			"//tile.openstreetmap.org/{z}/{x}/{y}.png",
					"minZoom": 		3,
					"maxZoom": 		18,
					"attribution":		"osm"
				}
			}
		},
		
		googleplus: {
			
			get: function(m) {
				var api_key;
				var gplus = {user: m.user, activity: m.id, id: m.uid};
				
				VMM.master_config.googleplus.que.push(gplus);
				VMM.master_config.googleplus.active = true;
			},
			
			create: function(gplus, callback) {
				var mediaElem			= "",
					api_key				= "",
					g_activity			= "",
					g_content			= "",
					g_attachments		= "",
					gperson_api_url,
					gactivity_api_url;
					googleplus_timeout	= setTimeout(VMM.ExternalAPI.googleplus.errorTimeOut, VMM.master_config.timers.api, gplus),
					callback_timeout	= setTimeout(callback, VMM.master_config.timers.api, gplus);
					
				
				if (VMM.master_config.Timeline.api_keys.google != "") {
					api_key = VMM.master_config.Timeline.api_keys.google;
				} else {
					api_key = Aes.Ctr.decrypt(VMM.master_config.api_keys_master.google, VMM.master_config.vp, 256);
				}
				
				gperson_api_url = "https://www.googleapis.com/plus/v1/people/" + gplus.user + "/activities/public?alt=json&maxResults=100&fields=items(id,url)&key=" + api_key;
				
				//mediaElem	=	"<iframe class='doc' frameborder='0' width='100%' height='100%' src='" + gplus.url + "&amp;embedded=true'></iframe>";
				mediaElem = "GOOGLE PLUS API CALL";
				
				VMM.getJSON(gperson_api_url, function(p_data) {
					for(var i = 0; i < p_data.items.length; i++) {
						trace("loop");
						if (p_data.items[i].url.split("posts/")[1] == gplus.activity) {
							trace("FOUND IT!!");
							
							g_activity = p_data.items[i].id;
							gactivity_api_url = "https://www.googleapis.com/plus/v1/activities/" + g_activity + "?alt=json&key=" + api_key;
							
							VMM.getJSON(gactivity_api_url, function(a_data) {
								trace(a_data);
								//a_data.url
								//a_data.image.url
								//a_data.actor.displayName
								//a_data.provider.title
								//a_data.object.content
								
								//g_content		+=	"<h4>" + a_data.title + "</h4>";
								
								if (typeof a_data.annotation != 'undefined') {
									g_content	+=	"<div class='googleplus-annotation'>'" + a_data.annotation + "</div>";
									g_content	+=	a_data.object.content;
								} else {
									g_content	+=	a_data.object.content;
								}
								
								if (typeof a_data.object.attachments != 'undefined') {
									
									//g_attachments	+=	"<div class='googleplus-attachemnts'>";
									
									for(var k = 0; k < a_data.object.attachments.length; k++) {
										if (a_data.object.attachments[k].objectType == "photo") {
											g_attachments	=	"<a href='" + a_data.object.url + "' target='_blank'>" + "<img src='" + a_data.object.attachments[k].image.url + "' class='article-thumb'></a>" + g_attachments;
										} else if (a_data.object.attachments[k].objectType == "video") {
											g_attachments	=	"<img src='" + a_data.object.attachments[k].image.url + "' class='article-thumb'>" + g_attachments;
											g_attachments	+=	"<div>";
											g_attachments	+=	"<a href='" + a_data.object.attachments[k].url + "' target='_blank'>"
											g_attachments	+=	"<h5>" + a_data.object.attachments[k].displayName + "</h5>";
											//g_attachments	+=	"<p>" + a_data.object.attachments[k].content + "</p>";
											g_attachments	+=	"</a>";
											g_attachments	+=	"</div>";
										} else if (a_data.object.attachments[k].objectType == "article") {
											g_attachments	+=	"<div>";
											g_attachments	+=	"<a href='" + a_data.object.attachments[k].url + "' target='_blank'>"
											g_attachments	+=	"<h5>" + a_data.object.attachments[k].displayName + "</h5>";
											g_attachments	+=	"<p>" + a_data.object.attachments[k].content + "</p>";
											g_attachments	+=	"</a>";
											g_attachments	+=	"</div>";
										}
										
										trace(a_data.object.attachments[k]);
									}
									
									g_attachments	=	"<div class='googleplus-attachments'>" + g_attachments + "</div>";
								}
								
								//mediaElem		=	"<div class='googleplus'>";
								mediaElem		=	"<div class='googleplus-content'>" + g_content + g_attachments + "</div>";

								mediaElem		+=	"<div class='vcard author'><a class='screen-name url' href='" + a_data.url + "' target='_blank'>";
								mediaElem		+=	"<span class='avatar'><img src='" + a_data.actor.image.url + "' style='max-width: 32px; max-height: 32px;'></span>"
								mediaElem		+=	"<span class='fn'>" + a_data.actor.displayName + "</span>";
								mediaElem		+=	"<span class='nickname'><span class='thumbnail-inline'></span></span>";
								mediaElem		+=	"</a></div>";
								
								VMM.attachElement("#googleplus_" + gplus.activity, mediaElem);
								
								
							});
							
							break;
						}
					}
					
					
					
				})
				.error(function(jqXHR, textStatus, errorThrown) {
					var error_obj = VMM.parseJSON(jqXHR.responseText);
					trace(error_obj.error.message);
					VMM.attachElement("#googleplus_" + gplus.activity, VMM.MediaElement.loadingmessage("<p>ERROR LOADING GOOGLE+ </p><p>" + error_obj.error.message + "</p>"));
				})
				.success(function(d) {
					clearTimeout(googleplus_timeout);
					clearTimeout(callback_timeout);
					callback();
				});
				
				
				
			},
			
			pushQue: function() {
				if (VMM.master_config.googleplus.que.length > 0) {
					VMM.ExternalAPI.googleplus.create(VMM.master_config.googleplus.que[0], VMM.ExternalAPI.googleplus.pushQue);
					VMM.Util.removeRange(VMM.master_config.googleplus.que,0);
				}
				/*
				for(var i = 0; i < VMM.master_config.googleplus.que.length; i++) {
					VMM.ExternalAPI.googleplus.create(VMM.master_config.googleplus.que[i]);
				}
				VMM.master_config.googleplus.que = [];
				*/
			},
			
			errorTimeOut: function(gplus) {
				trace("GOOGLE+ JSON ERROR TIMEOUT " + gplus.activity);
				VMM.attachElement("#googleplus_" + gplus.activity, VMM.MediaElement.loadingmessage("<p>Still waiting on GOOGLE+ </p><p>" + gplus.activity + "</p>"));
				
			}
			
		},
		
		googledocs: {
			
			get: function(m) {
				VMM.master_config.googledocs.que.push(m);
				VMM.master_config.googledocs.active = true;
			},
			
			create: function(m) {
				var mediaElem = ""; 
				if (m.id.match(/docs.google.com/i)) {
					mediaElem	=	"<iframe class='doc' frameborder='0' width='100%' height='100%' src='" + m.id + "&amp;embedded=true'></iframe>";
				} else {
					mediaElem	=	"<iframe class='doc' frameborder='0' width='100%' height='100%' src='" + "//docs.google.com/viewer?url=" + m.id + "&amp;embedded=true'></iframe>";
				}
				VMM.attachElement("#"+m.uid, mediaElem);
			},
			
			pushQue: function() {
				
				for(var i = 0; i < VMM.master_config.googledocs.que.length; i++) {
					VMM.ExternalAPI.googledocs.create(VMM.master_config.googledocs.que[i]);
				}
				VMM.master_config.googledocs.que = [];
			}
		
		},
		
		flickr: {
			
			get: function(m) {
				VMM.master_config.flickr.que.push(m);
				VMM.master_config.flickr.active = true;
			},
			
			create: function(m, callback) {
				var api_key,
					callback_timeout= setTimeout(callback, VMM.master_config.timers.api, m);
					
				if (typeof VMM.master_config.Timeline != 'undefined' && VMM.master_config.Timeline.api_keys.flickr != "") {
					api_key = VMM.master_config.Timeline.api_keys.flickr;
				} else {
					api_key = Aes.Ctr.decrypt(VMM.master_config.api_keys_master.flickr, VMM.master_config.vp, 256)
				}
				var the_url = "https://api.flickr.com/services/rest/?method=flickr.photos.getSizes&api_key=" + api_key + "&photo_id=" + m.id + "&format=json&jsoncallback=?";
				
				VMM.getJSON(the_url, function(d) {
					var flickr_id = VMM.ExternalAPI.flickr.getFlickrIdFromUrl(d.sizes.size[0].url);
				
					var flickr_large_id = "#" + m.uid,
						flickr_thumb_id = "#" + m.uid + "_thumb";
						//flickr_thumb_id = "flickr_" + uid + "_thumb";
				
					var flickr_img_size,
						flickr_img_thumb,
						flickr_size_found = false,
						flickr_best_size = "Large";
				
					flickr_best_size = VMM.ExternalAPI.flickr.sizes(VMM.master_config.sizes.api.height);
					for(var i = 0; i < d.sizes.size.length; i++) {
						if (d.sizes.size[i].label == flickr_best_size) {
							
							flickr_size_found = true;
							flickr_img_size = d.sizes.size[i].source;
						}
					}
					if (!flickr_size_found) {
						flickr_img_size = d.sizes.size[d.sizes.size.length - 2].source;
					}
				
					flickr_img_thumb = d.sizes.size[0].source;
					VMM.Lib.attr(flickr_large_id, "src", flickr_img_size);
					//VMM.attachElement(flickr_large_id, "<a href='" + flick.link + "' target='_blank'><img src='" + flickr_img_size + "'></a>");
					VMM.attachElement(flickr_thumb_id, "<img src='" + flickr_img_thumb + "'>");
					
				})
				.error(function(jqXHR, textStatus, errorThrown) {
					trace("FLICKR error");
					trace("FLICKR ERROR: " + textStatus + " " + jqXHR.responseText);
				})
				.success(function(d) {
					clearTimeout(callback_timeout);
					callback();
				});
				
			},
			
			pushQue: function() {
				if (VMM.master_config.flickr.que.length > 0) {
					VMM.ExternalAPI.flickr.create(VMM.master_config.flickr.que[0], VMM.ExternalAPI.flickr.pushQue);
					VMM.Util.removeRange(VMM.master_config.flickr.que,0);
				}
			},
			
			sizes: function(s) {
				var _size = "";
				if (s <= 75) {
					_size = "Thumbnail";
				} else if (s <= 180) {
					_size = "Small";
				} else if (s <= 240) {
					_size = "Small 320";
				} else if (s <= 375) {
					_size = "Medium";
				} else if (s <= 480) {
					_size = "Medium 640";
				} else if (s <= 600) {
					_size = "Large";
				} else {
					_size = "Large";
				}
				
				return _size;
			},

			getFlickrIdFromUrl: function(url) {
				var idx = url.indexOf("flickr.com/photos/");
				if (idx == -1) return null; 
				var pos = idx + "flickr.com/photos/".length;
				var photo_info = url.substr(pos)
				if (photo_info.indexOf('/') == -1) return null;
				if (photo_info.indexOf('/') == 0) photo_info = photo_info.substr(1);
				return photo_info.split("/")[1];
			}
		},
		
		instagram: {
			get: function(m, thumb) {
				if (thumb) {
					return "//instagr.am/p/" + m.id + "/media/?size=t";
				} else {
					return "//instagr.am/p/" + m.id + "/media/?size=" + VMM.ExternalAPI.instagram.sizes(VMM.master_config.sizes.api.height);
				}
			},
			
			sizes: function(s) {
				var _size = "";
				if (s <= 150) {
					_size = "t";
				} else if (s <= 306) {
					_size = "m";
				} else {
					_size = "l";
				}
				
				return _size;
			},

			isInstagramUrl: function(url) {
				return url.match("instagr.am/p/") || url.match("instagram.com/p/");
			},

			getInstagramIdFromUrl: function(url) {
				try {
					return url.split("\/p\/")[1].split("/")[0];	
				} catch(e) {
					trace("Invalid Instagram url: " + url);
					return null;
				}
				
			}
		},
		
		soundcloud: {
			
			get: function(m) {
				VMM.master_config.soundcloud.que.push(m);
				VMM.master_config.soundcloud.active = true;
			},
			
			create: function(m, callback) {
				var the_url = "//soundcloud.com/oembed?url=" + m.id + "&maxheight=168&format=js&callback=?";
				VMM.getJSON(the_url, function(d) {
					VMM.attachElement("#"+m.uid, d.html);
					callback();
				});
			},
			
			pushQue: function() {
				if (VMM.master_config.soundcloud.que.length > 0) {
					VMM.ExternalAPI.soundcloud.create(VMM.master_config.soundcloud.que[0], VMM.ExternalAPI.soundcloud.pushQue);
					VMM.Util.removeRange(VMM.master_config.soundcloud.que,0);
				}
			}
			
		},
		
		wikipedia: {
			
			get: function(m) {
				VMM.master_config.wikipedia.que.push(m);
				VMM.master_config.wikipedia.active = true;
			},
			
			create: function(m, callback) {
				var the_url = "//" + m.lang + ".wikipedia.org/w/api.php?action=query&prop=extracts&redirects=&titles=" + m.id + "&exintro=1&format=json&callback=?";
				callback_timeout= setTimeout(callback, VMM.master_config.timers.api, m);
				
				if ( VMM.Browser.browser == "Explorer" && parseInt(VMM.Browser.version, 10) >= 7 && window.XDomainRequest) {
					var temp_text	=	"<h4><a href='http://" + VMM.master_config.language.api.wikipedia + ".wikipedia.org/wiki/" + m.id + "' target='_blank'>" + m.url + "</a></h4>";
					temp_text		+=	"<span class='wiki-source'>" + VMM.master_config.language.messages.wikipedia + "</span>";
					temp_text		+=	"<p>Wikipedia entry unable to load using Internet Explorer 8 or below.</p>";
					VMM.attachElement("#"+m.uid, temp_text );
				}
				
				VMM.getJSON(the_url, function(d) {
					if (d.query) {
						var wiki_extract,
							wiki_title, 
							_wiki = "", 
							wiki_text = "", 
							wiki_number_of_paragraphs = 1, 
							wiki_text_array = [];
						
						wiki_extract = VMM.Util.getObjectAttributeByIndex(d.query.pages, 0).extract;
						wiki_title = VMM.Util.getObjectAttributeByIndex(d.query.pages, 0).title;
						
						if (wiki_extract.match("<p>")) {
							wiki_text_array = wiki_extract.split("<p>");
						} else {
							wiki_text_array.push(wiki_extract);
						}
					
						for(var i = 0; i < wiki_text_array.length; i++) {
							if (i+1 <= wiki_number_of_paragraphs && i+1 < wiki_text_array.length) {
								wiki_text	+= "<p>" + wiki_text_array[i+1];
							}
						}
					
						_wiki		=	"<h4><a href='http://" + VMM.master_config.language.api.wikipedia + ".wikipedia.org/wiki/" + wiki_title + "' target='_blank'>" + wiki_title + "</a></h4>";
						_wiki		+=	"<span class='wiki-source'>" + VMM.master_config.language.messages.wikipedia + "</span>";
						_wiki		+=	VMM.Util.linkify_wikipedia(wiki_text);
					
						if (wiki_extract.match("REDIRECT")) {
						
						} else {
							VMM.attachElement("#"+m.uid, _wiki );
						}
					}
					//callback();
				})
				.error(function(jqXHR, textStatus, errorThrown) {
					trace("WIKIPEDIA error");
					trace("WIKIPEDIA ERROR: " + textStatus + " " + jqXHR.responseText);
					trace(errorThrown);
					
					VMM.attachElement("#"+m.uid, VMM.MediaElement.loadingmessage("<p>Wikipedia is not responding</p>"));
					// TRY AGAIN?
					clearTimeout(callback_timeout);
					if (VMM.master_config.wikipedia.tries < 4) {
						trace("WIKIPEDIA ATTEMPT " + VMM.master_config.wikipedia.tries);
						trace(m);
						VMM.master_config.wikipedia.tries++;
						VMM.ExternalAPI.wikipedia.create(m, callback);
					} else {
						callback();
					}
					
				})
				.success(function(d) {
					VMM.master_config.wikipedia.tries = 0;
					clearTimeout(callback_timeout);
					callback();
				});
				
				
			},
			
			pushQue: function() {
				
				if (VMM.master_config.wikipedia.que.length > 0) {
					trace("WIKIPEDIA PUSH QUE " + VMM.master_config.wikipedia.que.length);
					VMM.ExternalAPI.wikipedia.create(VMM.master_config.wikipedia.que[0], VMM.ExternalAPI.wikipedia.pushQue);
					VMM.Util.removeRange(VMM.master_config.wikipedia.que,0);
				}

			}
			
		},
		
		youtube: {
			
			get: function(m) {
				var the_url = "//gdata.youtube.com/feeds/api/videos/" + m.id + "?v=2&alt=jsonc&callback=?";
					
				VMM.master_config.youtube.que.push(m);
				
				if (!VMM.master_config.youtube.active) {
					if (!VMM.master_config.youtube.api_loaded) {
						LoadLib.js('//www.youtube.com/player_api', function() {
							trace("YouTube API Library Loaded");
						});
					}
				}
				
				// THUMBNAIL
				VMM.getJSON(the_url, function(d) {
					VMM.ExternalAPI.youtube.createThumb(d, m)
				});
				
			},
			
			create: function(m) {
				if (typeof(m.start) != 'undefined') {
					
					var vidstart			= m.start.toString(),
						vid_start_minutes	= 0,
						vid_start_seconds	= 0;
						
					if (vidstart.match('m')) {
						vid_start_minutes = parseInt(vidstart.split("m")[0], 10);
						vid_start_seconds = parseInt(vidstart.split("m")[1].split("s")[0], 10);
						m.start = (vid_start_minutes * 60) + vid_start_seconds;
					} else {
						m.start = 0;
					}
				} else {
					m.start = 0;
				}
				
				var p = {
					active: 				false,
					player: 				{},
					name:					m.uid,
					playing:				false,
					hd:						false
				};
				
				if (typeof(m.hd) != 'undefined') {
					p.hd = true;
				}
				
				p.player[m.id] = new YT.Player(m.uid, {
					height: 				'390',
					width: 					'640',
					playerVars: {
						enablejsapi:		1,
						color: 				'white',
						showinfo:			0,
						theme:				'light',
						start:				m.start,
						rel:				0
					},
					videoId: m.id,
					events: {
						'onReady': 			VMM.ExternalAPI.youtube.onPlayerReady,
						'onStateChange': 	VMM.ExternalAPI.youtube.onStateChange
					}
				});
				
				VMM.master_config.youtube.array.push(p);
			},
			
			createThumb: function(d, m) {
				trace("CREATE THUMB");
				trace(d);
				trace(m);
				if (typeof d.data != 'undefined') {
					var thumb_id = "#" + m.uid + "_thumb";
					VMM.attachElement(thumb_id, "<img src='" + d.data.thumbnail.sqDefault + "'>");
					
				}
			},
			
			pushQue: function() {
				for(var i = 0; i < VMM.master_config.youtube.que.length; i++) {
					VMM.ExternalAPI.youtube.create(VMM.master_config.youtube.que[i]);
				}
				VMM.master_config.youtube.que = [];
			},
			
			onAPIReady: function() {
				VMM.master_config.youtube.active = true;
				VMM.ExternalAPI.youtube.pushQue();
			},
			
			stopPlayers: function() {
				for(var i = 0; i < VMM.master_config.youtube.array.length; i++) {
					if (VMM.master_config.youtube.array[i].playing) {
						if (typeof VMM.master_config.youtube.array[i].player.the_name !== 'undefined') {
							VMM.master_config.youtube.array[i].player.the_name.stopVideo();
						}
					}
				}
			},
			 
			onStateChange: function(e) {
				for(var i = 0; i < VMM.master_config.youtube.array.length; i++) {
					for (var z in VMM.master_config.youtube.array[i].player) {
						if (VMM.master_config.youtube.array[i].player[z] == e.target) {
							VMM.master_config.youtube.array[i].player.the_name = VMM.master_config.youtube.array[i].player[z];
						}
					}
					if (VMM.master_config.youtube.array[i].player.the_name == e.target) {
						if (e.data == YT.PlayerState.PLAYING) {
							VMM.master_config.youtube.array[i].playing = true;
							if (VMM.master_config.youtube.array[i].hd === false) {
								VMM.master_config.youtube.array[i].hd = true;
								VMM.master_config.youtube.array[i].player.the_name.setPlaybackQuality("hd720");
							}
						}
					}
				}
			},
			
			onPlayerReady: function(e) {
				
			}
			
			
		},
		
		vimeo: {
			
			get: function(m) {
				VMM.master_config.vimeo.que.push(m);
				VMM.master_config.vimeo.active = true;
			},
			
			create: function(m, callback) {
				trace("VIMEO CREATE");
				
				// THUMBNAIL
				var thumb_url	= "//vimeo.com/api/v2/video/" + m.id + ".json",
					video_url	= "//player.vimeo.com/video/" + m.id + "?title=0&amp;byline=0&amp;portrait=0&amp;color=ffffff";
					
				VMM.getJSON(thumb_url, function(d) {
					VMM.ExternalAPI.vimeo.createThumb(d, m);
					callback();
				});
				
				
				// VIDEO
				VMM.attachElement("#" + m.uid, "<iframe autostart='false' frameborder='0' width='100%' height='100%' src='" + video_url + "'></iframe>");
				
			},
			
			createThumb: function(d, m) {
				trace("VIMEO CREATE THUMB");
				var thumb_id = "#" + m.uid + "_thumb";
				VMM.attachElement(thumb_id, "<img src='" + d[0].thumbnail_small + "'>");
			},
			
			pushQue: function() {
				if (VMM.master_config.vimeo.que.length > 0) {
					VMM.ExternalAPI.vimeo.create(VMM.master_config.vimeo.que[0], VMM.ExternalAPI.vimeo.pushQue);
					VMM.Util.removeRange(VMM.master_config.vimeo.que,0);
				}
			}
			
		},
		
		vine: {
			
			get: function(m) {
				VMM.master_config.vine.que.push(m);
				VMM.master_config.vine.active = true;
			},
			
			create: function(m, callback) {
				return;
				
			},
			
			pushQue: function() {
				if (VMM.master_config.vine.que.length > 0) {
					VMM.ExternalAPI.vine.create(VMM.master_config.vine.que[0], VMM.ExternalAPI.vine.pushQue);
					VMM.Util.removeRange(VMM.master_config.vine.que,0);
				}
			}
			
		},
		
		webthumb: {
			
			get: function(m, thumb) {
				VMM.master_config.webthumb.que.push(m);
				VMM.master_config.webthumb.active = true;
			},
			
			sizes: function(s) {
				var _size = "";
				if (s <= 150) {
					_size = "t";
				} else if (s <= 306) {
					_size = "m";
				} else {
					_size = "l";
				}
				
				return _size;
			},
			
			create: function(m) {
				trace("WEB THUMB CREATE");
				
				var thumb_url	= "//api.pagepeeker.com/v2/thumbs.php?";
					url			= m.id.replace("http://", "");//.split("/")[0];
					
				// Main Image
				VMM.attachElement("#" + m.uid, "<a href='" + m.id + "' target='_blank'><img src='" + thumb_url + "size=x&url=" + url + "'></a>");
				
				// Thumb
				VMM.attachElement("#" + m.uid + "_thumb", "<img src='" + thumb_url + "size=t&url=" + url + "'>");
			},
			
			pushQue: function() {
				for(var i = 0; i < VMM.master_config.webthumb.que.length; i++) {
					VMM.ExternalAPI.webthumb.create(VMM.master_config.webthumb.que[i]);
				}
				VMM.master_config.webthumb.que = [];
			}
		}
	
	}).init();
	
}

/*  YOUTUBE API READY
	Can't find a way to customize this callback and keep it in the VMM namespace
	Youtube wants it to be this function. 
================================================== */
function onYouTubePlayerAPIReady() {
	trace("GLOBAL YOUTUBE API CALLED")
	VMM.ExternalAPI.youtube.onAPIReady();
}
