// HoverBing v0.2
// http://hoverbing.codeplex.com
// By Einar Otto Stangvik (http://einaros.blogspot.com)
//
// Published under The MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person 
// obtaining a copy of this software and associated documentation 
// files (the "Software"), to deal in the Software without 
// restriction, including without limitation the rights to use, 
// copy, modify, merge, publish, distribute, sublicense, and/or 
// sell copies of the Software, and to permit persons to whom the 
// Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be 
// included in all copies or substantial portions of the Software.

(function()
{
	getSelectedText = function()
	{
		if(window.getSelection){
			return window.getSelection().toString();
		}
		else if(document.getSelection){
			return document.getSelection();
		}
		else if(document.selection){
			return document.selection.createRange().text;
		}
	}   

	alternate = function(counter, norm, alt)
	{
		return counter % 2 == 0 ? alt : norm;
	}

	createHoverBingObject = function(settings) 
	{
		HoverBingObject = 
		{
			// Settings
			anchor: settings.anchor,
			appId: settings.appId,
			offset: settings.offset,
			numResults: settings.numResults,
			sources: settings.sources,
			title: settings.title,
			titleSearching: settings.titleSearching,
			titleResultsReceived: settings.titleResultsReceived,
			
			// State variables
			popupBox: null,
			headerBox: null,
			resultsBox: null,
			
			activateSearchPopup: function(x, y)
			{
				if(x && y)
				{
					// Showing from invisible state
					var top = 0;
					var left = 0;
					var w = HoverBingObject.popupBox.width();
					var h = HoverBingObject.popupBox.height();
					
					switch(HoverBingObject.anchor)
					{
						default:
						case 'topleft': top = 0; left = 0; break;
						case 'topright': top = 0; left = -w; break;
						case 'bottomleft': top = -h; left = -w; break;
						case 'bottomright': top = -h; left = 0; break;
					}
					
					HoverBingObject.popupBox
						.css({
							cursor: "pointer",
							left: x + left + HoverBingObject.offset.left,
							top: y + top + HoverBingObject.offset.top
						})
						.show();
					HoverBingObject.headerBox.html(HoverBingObject.title)
					HoverBingObject.repositionToFitScreen();
					HoverBingObject.fadePopupByDistance(x, y);
				}
				
				// Update state data
				HoverBingObject.popupBox.data("SearchPopup_State", "waiting");
				// Track mouse movements, for UI adjustments
				$().bind('mousemove', HoverBingObject.mouseMoveSearchPopup);
			},

			create: function() 
			{
				HoverBingObject.initSearchPopup();
				$().mouseup(function(e) {
						var selText = getSelectedText();
						var state = HoverBingObject.popupBox.data("SearchPopup_State");
						if((state == undefined || state == "hidden") && 
						   selText.length > 0)
						{
							HoverBingObject.popupBox.data("SearchPopup_Query", HoverBingObject.washQueryText(selText));
							HoverBingObject.activateSearchPopup(e.pageX, e.pageY);
						}
						else if(state != "stapled")
						{
							HoverBingObject.deactivateSearchPopup();
						}
					});
				$().mousedown(function(e) {
						var state = HoverBingObject.popupBox.data("SearchPopup_State");
						if(state != undefined && state == "waiting")
						{
							HoverBingObject.deactivateSearchPopup();
						}
					});
			},

			deactivateSearchPopup: function()
			{
				HoverBingObject.resultsBox.empty();
				HoverBingObject.popupBox
					.fadeTo(0, 0)
					.hide()
					.data("SearchPopup_State", "hidden");
				$().unbind('mousemove', HoverBingObject.mouseMoveSearchPopup);
			},

			fadePopupByDistance: function(mouseX, mouseY)
			{
				var pos = HoverBingObject.popupBox.position();
				pos.left += HoverBingObject.popupBox.width() * 0.5;
				pos.top += HoverBingObject.popupBox.height() * 0.5;
				var dist = Math.round(Math.sqrt(Math.pow(mouseX - pos.left, 2) + Math.pow(mouseY - pos.top, 2)));
				HoverBingObject.popupBox.fadeTo(0, Math.max(1 - dist / 500, 0));
			},
			
			initSearchPopup: function()
			{
				// Create box
				HoverBingObject.popupBox = $(document.createElement("div"))
					.attr("id", "SearchPopup")
					.attr("class", "HoverBing")
					.css({
							position: "absolute",
							opacity: 0
						})
					.mouseenter(function() {
							var state = HoverBingObject.popupBox.data("SearchPopup_State");
							if(state == "waiting") 
							{
								HoverBingObject.stapleSearchPopup();
							}
						})
					.mouseleave(function() {
							var state = HoverBingObject.popupBox.data("SearchPopup_State");
							if(state == "stapled") 
							{
								HoverBingObject.activateSearchPopup();
							}
						})
					.data("SearchPopup_State", "hidden");
				
				// Create header control
				HoverBingObject.headerBox = $(document.createElement("div"))
					.attr("class", "Header")
					.html(HoverBingObject.title);

				// Create content control
				HoverBingObject.resultsBox = $(document.createElement("span"))
					.attr("class", "Content")
				
				// Append header and content to outer box
				HoverBingObject.popupBox
					.append(HoverBingObject.headerBox)
					.append(HoverBingObject.resultsBox);
					
				// Add outer box to body
				$(document.body).append(HoverBingObject.popupBox);
			},

			mouseMoveSearchPopup: function(e)
			{
				HoverBingObject.fadePopupByDistance(e.pageX, e.pageY);
			},

			onClickSearchPopup: function()
			{
				// Update the UI
				HoverBingObject.headerBox.toggleClass("loading");
				HoverBingObject.headerBox.html(HoverBingObject.titleSearching)

				// Build and fire an ajax query
				var query = HoverBingObject.popupBox.data("SearchPopup_Query");
				var contents = HoverBingObject.resultsBox.empty();
				$.getJSON("http://api.search.live.net/json.aspx" + 
							"?AppId=" + HoverBingObject.appId + 
							"&Market=en-US&Query=" + query + 
							"&Sources=" + HoverBingObject.sources +
							"&Web.Count=" + HoverBingObject.numResults + 
							"&JsonType=callback&JsonCallback=?", 
							HoverBingObject.onSearchResultsReceived);
			},

			onSearchResultsReceived: function(data)
			{
				HoverBingObject.headerBox.html(HoverBingObject.titleResultsReceived);
				HoverBingObject.headerBox.toggleClass("loading");

				if(data.SearchResponse == null) 
				{
					contents.html("No search results returned");
					return;
				}
				
				$.each(data.SearchResponse.Web.Results, function(i ,item) {
						HoverBingObject.resultsBox.append(
								$(document.createElement("a"))
									.attr("class", alternate(i, "ResultLine", "ResultLineAlt"))
									.css({
											display: 'block'
										})
									.click(function() {
										HoverBingObject.deactivateSearchPopup();
									})
									.mouseenter(function() {
											$(this).addClass("ResultLineHover");
										})
									.mouseleave(function() {
											$(this).removeClass("ResultLineHover");
										})
									.text(item.Title)
									.attr("href", item.Url)
									.attr("target", "_blank")
							);
					});
					
				HoverBingObject.repositionToFitScreen();
			},
			
			repositionToFitScreen: function()
			{
				var pos = HoverBingObject.popupBox.position();
				if(pos.left + HoverBingObject.popupBox.width() > $(window).width())
				{
					HoverBingObject.popupBox.css({left: $(window).width() - HoverBingObject.popupBox.width()});
				}
				else if(pos.left < 0)
				{
					HoverBingObject.popupBox.css({left: 0});
				}
				
				if(pos.top + HoverBingObject.popupBox.height() > $(window).height())
				{
					HoverBingObject.popupBox.css({top: $(window).height() - HoverBingObject.popupBox.height()});
				}
				else if(pos.top < 0)
				{
					HoverBingObject.popupBox.css({top: 0});
				}
			},

			stapleSearchPopup: function()
			{
				HoverBingObject.popupBox
					.data("SearchPopup_State", "stapled")
					.fadeTo(0, 1)
					.one('click', HoverBingObject.onClickSearchPopup);
				$().unbind('mousemove', HoverBingObject.mouseMoveSearchPopup);
			},
			
			washQueryText: function(text)
			{
				return escape(text.replace(/[^'"A-z0-9]/g, "+")).substr(0, 100);
			}
		}
	}
	
	window.HoverBing = function(options)
	{
		settings = jQuery.extend({
				title: "Click to Bing Search",
				titleSearching: "Bing Searching",
				titleResultsReceived: "Bing Search Results",
				appId: "96AE4D816B34AA03F44EEBC53F4C23F9A146C011",
				numResults: 10,
				sources: "web",
				anchor: "topright",
				offset: {left: -10, top: 10}
			}, options);

		if(typeof(HoverBingObject) == 'undefined')
		{
			createHoverBingObject(settings);
			HoverBingObject.create();
		}
	}
})();
