phonemap

By Les Papier Last update Apr 8, 2006 — Installed 842 times. Daily Installs: 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0
// phonemap

// This file is licensed under the BSD-new license:
// http://www.opensource.org/licenses/bsd-license.php
//
// Comments/Questions: Les Papier at paperless@excite.com
//
// Built with: Open source and "Don't repeat yourself" principles in mind...
//
// Tested with: Firefox 1.5.01 (GM 0.6.4) and IE 6.x (Turnabout plug-in)

// ==UserScript==
// @name          phonemap
// @namespace     http://paperless.blogdrive.com
// @description   Lookup address of US phone number on a map, with a call option
// @include       *
// ==/UserScript==


(function ()
{

//patern for US phone numbers (only) with & without area code
var phoneRE = /(\s|\n|\.|;|:|>)?(\d{3}(-|\.|\s)\d{3}(-|\.)\d{4}|\(\d{3}\)(\s||)\d{3}(-|\.)\d{4}|(\s|\n|\.|;|:|>)\d{3}\-\d{4})/;
var DialDefault = '+1';				// set to US for Skype calling
var ACDefault = '207';				// set your own default area code for ###-#### phones
var GoogleMatchRE = /Phonebook results/ig;
var GoogleSearchUrl = 'http://www.google.com/search?q=';
var MapsUrl = 'http://maps.google.com/maps?li=';
var UrlParms = 'bwp&q=';				// Google search parms
var ExhcSearchUrl = 'http://www.telcodata.us/query/queryexchangexml.html?npa=';	// for echange lookups
//http://www.telcodata.us/query/queryexchangexml.html?npa=207&nxx=865
var Pics =
{
	upA: "data:image/gif;base64,R0lGODlhDwAPAPMAAKbK8ABgwCBgwACAwCCAwECAwGCAwICAwECgwGCgwICgwP//AP///wAAAAAAAAAAACH5BAUAAAwALAAAAAAPAA8AAARWkMlJazUpGytPKkQgEMVGKQihIkFLmFKiEsgStsWktAGR2AOXKcEjLI43QSLGCxyBrSWDGH2mRNJd64nswaiAcJgwyOmoLlWposgg3ogEjN1WKDj4SgQAOw==",
	downA: "data:image/gif;base64,R0lGODlhDwAPAPMAAKbK8ABgwCBgwACAwCCAwECAwGCAwICAwECgwGCgwICgwP//AP///wAAAAAAAAAAACH5BAUAAAwALAAAAAAPAA8AAARWkMlJa1UqYStNQiCSaJVREGigBuRUDAQgy2oyGURALDy/KpKEQIfoJVY2xjEA661YwaGul6t1qs1ENQCUFFY7BIqQvK7EKET3dhoSCokDh+EZGeb4SQQAOw==",
	lookup: "data:image/gif;base64,R0lGODlhDwAPAPMAAABgwCBgwACAwCCAwICAwICgwP//AP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAUAAAcALAAAAAAPAA8AAAQ58MhJKx0BhGEn+KDQfUZZAlxFGoWpUQRQtq3xenLN2gEFmq6eTwa0ESoxzelmATk7zQ9UJZ3irJ0IADs=",
	CellImg: "data:image/gif;base64,R0lGODlhDwAPAPMAAKbK8ABgwCBgwACAwCCAwECAwGCAwICAwGCgwICgwP//AP///wAAAAAAAAAAAAAAACH5BAUAAAsALAAAAAAPAA8AAARScMlJazUoGytNUUogEMVGGUQArkNJFSogywpBTGg8AzWRSAiBbtb7LRABQm2ptCGAtp6t6esUmiFmFdobKZOUg3K1EggqCYI3JTBazGY3Zy6JAAA7"
};

var nonIE = document.getElementById && !document.all;
var topelement = nonIE ? 'HTML' : 'BODY';
var mHeight = nonIE ? '45px' : '50px';
var openToggle=true;
var isdrag=false;
var x,y;
var dobj;


function movemouse(e)
{
  if (isdrag)
  {
    dobj.style.left = nonIE ? tx + e.clientX - x : tx + event.clientX - x;
    dobj.style.top  = nonIE ? ty + e.clientY - y : ty + event.clientY - y;
    return false;
  }
}

function stopmove(e)
{isdrag=false; return false;}

function selectmouse(e)
{
  var fobj = nonIE ? e.target : event.srcElement;
  while (fobj.tagName != topelement && fobj.id != 'phonePopZ') //e.target.id != 'PhoneMap'fobj.className != "dragme"
  {
    fobj = nonIE ? fobj.parentNode : fobj.parentElement;
  }
  if (fobj.id == 'phonePopZ')
  {
    isdrag = true;
    dobj = fobj;
    tx = parseInt(dobj.style.left+0);
    ty = parseInt(dobj.style.top+0);
    x = nonIE ? e.clientX : event.clientX;
    y = nonIE ? e.clientY : event.clientY;
    return false;
  }
}

function openclosePopup()
{
	  var m = document.getElementById('phonePopZ');
	  var oc =document.getElementById('OpenICgwP');
	  var p = document.getElementById('MapICgwP');
	  var c = document.getElementById('CallgwICgwP');
	  if (openToggle)
	  {
		  oc.src = Pics.upA; oc.title ='Close';
		  m.style.height = mHeight; m.style.backgroundColor = "#abc";
		  p.style.display = 'inline'; c.style.display = 'inline';
	  }
	  else
	  {
		  oc.src = Pics.downA; oc.title ='Open or Hold & Drag';
		  m.style.height = '14px'; m.style.backgroundColor = "#cfc";
		  p.style.display = 'none'; c.style.display = 'none';
  	  }
	  openToggle = !openToggle;
}

function closeit()
{
	var m = document.getElementById('phonePopZ'); m.style.height = '0px'; m.style.display = 'none';
}

function checkSelected(e)
{

	phone = nonIE ? e.target.id : e.srcElement.id;
	if (phone != 'MapICgwP')
		return;
	phone = (window.getSelection ? window.getSelection() : document.getSelection ? document.getSelection() : document.selection.createRange().text);
	var m = document.getElementById('MapICgwP');
	if (phone == '')
		 phone = m.title;						// check last selected phone again
	phoneRE.lastIndex = 0;
	var is = phoneRE.exec(' '+phone.toString().replace(/[\f\n\r\t\v]/g,''));
	if (is)
	{
		document.getElementById('phonePopZ').style.backgroundColor = "#cfc";
		document.getElementById('CallgwICgwP').style.display = 'none';
		var phoneOrig = is[0].replace(/^[\d\s\.]/,'');
		m.title = phoneOrig;
		mapPhone.lookupPhoneMap(phoneOrig);
	}
}

function annotate(phoneOrig, href)
{
	if (href)
	{
		document.getElementById('phonePopZ').style.backgroundColor = (href.split(',').length > 2)?"#cfc" : "#abc";
		document.getElementById('MapICgwP').title = phoneOrig+' : '+href.substr(href.indexOf('q=')+2).replace(/[\s+]/g,' ');
		skypePhone(phoneOrig, href);
		window.open(href,'','top=50,width=400,height=400,resizable=yes');
	}
}

function setupEvent(elm, fName)
{  if (window.getSelection)
  	elm.addEventListener('click', fName, true);
  else
  	elm.attachEvent('onclick', fName);		// IE handler
}

function menupop(title)
{
  var dd=document;
  var ww=window;
  while (parent.dd) dd=parent.dd;
  while (parent.ww) ww=parent.ww;
  var menu = dd.createElement("span");
  menu.id = "phonePopZ";
  menu.title = title;
  var winWidth= dd.body.clientWidth;
  var winHeight=(nonIE)?ww.innerHeight :(ww.document.documentElement && ww.document.documentElement.clientHeight)?ww.document.documentElement.clientHeight:ww.document.body.clientHeight;
	with (menu.style)
	{
		position  =  "absolute";  height = "14px"; width = nonIE? "15px" : "21px";
		backgroundColor = "#cfc"; padding = "2px";border = "1px solid green";
		top = Math.round(winHeight/2).toString()+"px";
		left = (winWidth-22).toString()+"px";		//dd.body.clientWidth
		opacity = "0.85"; filter = "alpha(opacity=90)";
	}

	if (nonIE)
	{
		menu.addEventListener('mousedown', selectmouse, false);
		menu.addEventListener('mousemove', movemouse, false);
		menu.addEventListener('mouseup', stopmove, false);
		menu.addEventListener('mouseout', stopmove, false);
		menu.addEventListener('dblclick', closeit, false);
	}
	  else
	{// IE handler
		menu.attachEvent('onmousedown', selectmouse);
		menu.attachEvent('onmousemove', movemouse);
		menu.attachEvent('onmouseup', stopmove);
		menu.attachEvent('onmouseout', stopmove);
		menu.attachEvent('ondblclick', closeit);
	}

  var openclose = dd.createElement("img");
  openclose.id = "OpenICgwP";
  openclose.src = Pics.downA;
  openclose.title="Open";
  with (openclose.style)
  	{ position = "absolute"; left = "2px"; top = "1px"; margin = "0px"; width = "14px"; border = "none"; cursor = "pointer"; }
  setupEvent(openclose, openclosePopup);
  menu.appendChild( openclose );

  var lookup = dd.createElement("a");
  var lookupImg = dd.createElement("img");
  lookupImg.src = Pics.lookup;
  lookupImg.style.border = "none";
  lookupImg.id = "MapICgwP";
  lookupImg.title="Lookup Selected Phone";
  lookup.appendChild(lookupImg);
  with (lookupImg.style)
  	{position = "absolute"; cursor = 'pointer'; left = "3px"; height = "16px"; top = "18px"; width = "14px"; border = "none"; display = 'none' }
  setupEvent(lookupImg, checkSelected);

  menu.appendChild(lookup);

  var call = dd.createElement("a");
  var callImg = dd.createElement("img");
  with (callImg.style)
  	{position = "absolute"; left = "2px"; width = "14px"; top = "33px"; margin = "0px";  border = "none"; display = 'none' }
  callImg.src = Pics.CellImg;
  callImg.id = "CallgwICgwP";
  callImg.title="Call";
  call.appendChild(callImg);
  call.href = "callto:";
  call.style.height = "12px";
  menu.appendChild( call );

  return menu;
}

function getPhone(phone)
{
	// Saving time looking up same phone number(s)
	var Cached = GM_getValue("phoneZ");
	if (!Cached)
		return null;
	var ix = Cached.indexOf(phone.replace(/[\-]/g, ''));
	if (ix == -1)
		return null;
	return Cached.substr(ix).split('|',2)[1];
}

function getExchange(phone)
{
	// Saving time looking up same exchange(s)
	var Cached = GM_getValue("phoneZ");
	if (!Cached)
		return null;
	var ix = Cached.indexOf(phone.substr(0,6));
	if (ix == -1)
		return null;
	Cached = Cached.substr(ix).split('|',2)[1].split(',');
	if (Cached.length > 2)
		return Cached[1]+','+Cached[2];
	return Cached.join(',');
}

function putPhone(phone, href)
{
	if (!href)
		return;

	var Cached = GM_getValue("phoneZ");
	if (Cached && Cached.length > 1000)				// Cache holds info on about 25 phones
	{	// remove some older phones
		 var cp = Cached.split('|');
		 cp.splice(0,10);
		 Cached = cp.join('|');
	}
	Cached = Cached + phone.replace(/[\-]/g, '') + '|' + href.substr(href.indexOf('q=')+2)+ '|';
	GM_setValue("phoneZ", Cached);
}

function skypePhone(phone, href)
{
	var c = document.getElementById('CallgwICgwP');
	c.style.display = 'inline';
	c.title = 'Call '+phone;
	if (href && href.indexOf('\t') > 0)
		c.title += ' (Cell)';
	c.parentNode.href='callto://' +DialDefault + formatUSTel(phone);
}

function formatUSTel(phoneOrig)
{
	var phone = phoneOrig.replace(/[^\d]/g, '');
	if (phone.length == 7)
		return ACDefault+'-'+phone.substr(0,3)+'-'+phone.substr(3,4)
	else
		return phone.substr(0,3)+'-'+phone.substr(3,3)+'-'+phone.substr(6,4);
}

var mapPhone =
{
	xpath: function(expr, doc) 			// mimics simple xpath processing that IE dosn't support
	{
	  var ret = [];
	  if (!expr)
		return ret;
	  if (!doc)
		doc = document;
	  var args = expr.replace(/[\.\[\]\/\(\)',]/g, "").split("@").join("=").split("=");
	  var items = doc.getElementsByTagName(args[0]);
	  for (var i = 0; i < items.length; ++i)
	  {
		if (args.length > 1)
		{	// check for attribute match
			for (var x = 0; x < items[i].attributes.length; x++)
				if (items[i].attributes[x].nodeName == args[1])
				{
					ret.push(items[i]); break; break;
				}
		}
		else
		  ret.push(items[i]);
	  }
	  return ret;
	},

	lookupPhoneMap: function(phoneOrig)
	{
		var phone = formatUSTel(phoneOrig);
		var ac = phone.substr(0,3);
		if (ac == '800' || ac == '888' || ac == '877' || ac == '866')
		{
			skypePhone(phoneOrig, null);
			return;
		}
		var href = getPhone(phone);

		// check if phone info is cached
		if (href)
		{
			annotate(phoneOrig, MapsUrl+UrlParms+href);
			return;
		}
		// get phone address from Google
		GM_xmlhttpRequest
		  (
		   {
			method:  'GET',
			url:     GoogleSearchUrl + encodeURIComponent(phone),
			onload:  function(results)
			{
			  if (!GoogleMatchRE.test(results.responseText) )
				mapPhone.lookupExchange(phoneOrig);
			  else
			  { // look for 1st http://maps.google.com/maps reference
				//turn the text into a dom object

				var doc = document.createElement('div');
				doc.innerHTML = results.responseText;
				var xpathVar = ".//a[@href]";
				var links = mapPhone.xpath(xpathVar, doc);
				if (links)
					for (var n in links)
					{
						var href = links[n].getAttribute('href');
						if (href.indexOf(MapsUrl) != -1)
						{
							 putPhone(phone, href);			// save time and cache phone info
							 annotate(phoneOrig, href);
							 break;
						}
					} // end for
			   } // end if
			 }
			}
		) // end GM_xmlhttpRequest
	},

	lookupExchange: function(phoneOrig)
	{
		var phone = formatUSTel(phoneOrig);
		var href = getExchange(phone);		// check if exchange info is cached
		if (href)
			annotate(phoneOrig, MapsUrl+UrlParms+href);
		else
		// get US town, state based on area code & exchange
		GM_xmlhttpRequest
		  (
		   {
			method:  'GET',
			url:     ExhcSearchUrl + phone.substr(0,3) +'&nxx='+phone.substr(4,3),
			onload:  function(results)
			{
				if (results.responseText.indexOf('<valid>YES') > 0)
				{
					//turn the text into a dom object and extract US town and state
					var doc = document.createElement('div');
					doc.innerHTML = results.responseText;
					var xpathVar = ".//englishname";
					var nodes = mapPhone.xpath(xpathVar, doc);
					if (nodes[0].firstChild == null)
					{	// try other name
						xpathVar = ".//ratecenter";
						nodes = mapPhone.xpath(xpathVar, doc);
					}
					var href = MapsUrl+UrlParms;
					if (nodes)
						href = href + nodes[0].firstChild.textContent.replace(/[\ ]/g, '+');
					xpathVar = ".//state";
					nodes = mapPhone.xpath(xpathVar, doc);
					if (nodes)
						href = href +',+' + nodes[0].firstChild.textContent;
					xpathVar = ".//companytype";
					nodes = mapPhone.xpath(xpathVar, doc);
					if (nodes && nodes[0].firstChild.textContent == 'WIRELESS')
						href = href + '\t';
					annotate(phoneOrig, href);
					putPhone(phone, href);
				} // end if
			 }
			}
		) // end GM_xmlhttpRequest
	}

} // end mapPhone


document.getElementsByTagName(topelement)[0].appendChild(menupop('Select a phone to map or Double Click to Exit'));


})();