Thursday, June 11, 2009

Hovering Bing Search using jQuery

Taking the Bing API for another test drive, I wrote up a jQuery / JSON Ajax driven website tool as well. The following little thing will show a semi-transparent hovering box as you select text on a web site. If you click the box, a Bing search will be made, and the top 10 results will be shown in the popup.




Feel free to give it a go at the example page. The initial JavaScript is shown in full shortly, but for updated source code and examples, I suggest you head over to the HoverBing CodePlex project I just created.


(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
appId: settings.appId,
numResults: settings.numResults,
sources: settings.sources,
title: settings.title,

activateSearchPopup: function(x, y)
{
var popup = $("#SearchPopup");
if(x && y)
{
popup
.css({
cursor: "pointer",
left: x + 5,
top: y + 5
})
.show();
HoverBingObject.repositionToFitScreen();
HoverBingObject.fadePopupByDistance(x, y);
}
popup.data("SearchPopup_State", "waiting");
$().bind('mousemove', HoverBingObject.mouseMoveSearchPopup);
},

create: function()
{
HoverBingObject.initSearchPopup();
$().mouseup(function(e) {
var selText = getSelectedText();
var popup = $("#SearchPopup");
var state = popup.data("SearchPopup_State");
if((state == undefined || state == "hidden") &&
selText.length > 0)
{
popup.data("SearchPopup_Query", HoverBingObject.washQueryText(selText));
HoverBingObject.activateSearchPopup(e.pageX, e.pageY);
}
else if(state != "stapled")
{
HoverBingObject.deactivateSearchPopup();
}
});
$().mousedown(function(e) {
var popup = $("#SearchPopup");
var state = popup.data("SearchPopup_State");
if(state != undefined && state == "waiting")
{
HoverBingObject.deactivateSearchPopup();
}
});
},

deactivateSearchPopup: function()
{
var popup = $("#SearchPopup");
popup.find("span").empty();
popup
.fadeTo(0, 0)
.hide()
.data("SearchPopup_State", "hidden");
$().unbind('mousemove', HoverBingObject.mouseMoveSearchPopup);
},

fadePopupByDistance: function(mouseX, mouseY)
{
var popup = $("#SearchPopup");
var pos = popup.position();
pos.left += popup.width() * 0.5;
pos.top += popup.height() * 0.5;
var dist = Math.round(Math.sqrt(Math.pow(mouseX - pos.left, 2) + Math.pow(mouseY - pos.top, 2)));
popup.fadeTo(0, Math.max(1 - dist / 500, 0));
},

initSearchPopup: function()
{
// Create box
var box = document.createElement("div");
$(box)
.attr("id", "SearchPopup")
.attr("class", "HoverBing")
.css({
position: "absolute",
opacity: 0
})
.mouseenter(function() {
var state = $("#SearchPopup").data("SearchPopup_State");
if(state == "waiting")
{
HoverBingObject.stapleSearchPopup();
}
})
.mouseleave(function() {
var popup = $("#SearchPopup");
var state = popup.data("SearchPopup_State");
if(state == "stapled")
{
HoverBingObject.activateSearchPopup();
}
})
.data("SearchPopup_State", "hidden");

// Create header control
var header = document.createElement("div");
$(header)
.attr("class", "Header")
.html(HoverBingObject.title);

// Create content control
var content = document.createElement("span");
$(content)
.attr("class", "Content")

// Append header and content to outer box
$(box)
.append(header)
.append(content);

// Add outer box to body
$(document.body).append(box);
},

mouseMoveSearchPopup: function(e)
{
HoverBingObject.fadePopupByDistance(e.pageX, e.pageY);
},

onClickSearchPopup: function()
{
var popup = $("#SearchPopup");
var query = popup.data("SearchPopup_Query");
var contents = popup.find("span").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)
{
var popup = $("#SearchPopup");
var contents = popup.find("span").empty();

if(data.SearchResponse == null)
{
contents.html("No search results returned");
return;
}

$.each(data.SearchResponse.Web.Results, function(i ,item) {
contents.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 popup = $("#SearchPopup");
var pos = popup.position();
if(pos.left + popup.width() > $(window).width())
{
popup.css({left: $(window).width() - popup.width()});
}
if(pos.top + popup.height() > $(window).height())
{
popup.css({top: $(window).height() - popup.height()});
}
},

stapleSearchPopup: function()
{
$("#SearchPopup")
.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",
appId: "96AE4D816B34AA03F44EEBC53F4C23F9A146C011",
numResults: 10,
sources: "web"
}, options);

if(typeof(HoverBingObject) == 'undefined')
{
createHoverBingObject(settings);
HoverBingObject.create();
}
}
})();


And there you have it. To use it, link the jQuery-1.3.2 javascript, include the above JavaScript, and the CSS styles found on the CodePlex site linked from the top, then activate it by using a jQuery ready handler such as:

$(function() { HoverBing(); });

No comments: