Match metrics sidepane

By Johan Sundström Last update Jun 23, 2006 — Installed 450 times.
// ==UserScript==
// @name           Match metrics sidepane
// @namespace      http://www.lysator.liu.se/~jhs/userscript
// @description    Shows an improved match metrics table in a fixed-position top right sidepane. Hover the dots to highlight and focus the relevant portion of the profile page (or vice versa). Also makes the profile title and tag line the window title, to make browser tabs easier to identify by content.
// @include        http://www.match.com/profile/showprofile.aspx?*
// ==/UserScript==

// support methods:

function $( id )
{
  return document.getElementById( id );
}

// list nodes matching this expression, optionally relative to the node `root'
function $x( xpath, root )
{
  var doc = root ? root.evaluate ? root : root.ownerDocument : document;
  var got = doc.evaluate( xpath, root||doc, null, 0, null ), next, result = [];
  while( next = got.iterateNext() )
    result.push( next );
  return result;
}

// run the passed cb( node, index ) on all nodes matching the expression
function foreach( xpath, cb, root )
{
  var nodes = $x( xpath, root ), i, e = 0;
  for( i=0; i<nodes.length; i++ )
    e += cb( nodes[i], i ) || 0;
  return e;
}

function ws_trim( x )
{
  return (typeof x=='string' ? x : '').replace( /^\s+|\s+$/g, '' );
}

function get_coords_of( node )
{
  if( typeof node.offsetLeft=='undefined' && node.parentNode )
    return get_coords_of( node.parentNode );
  var x = 0, y = 0, top = node, s = getComputedStyle( top, '' );
  do {
    x += node.offsetLeft;
    y += node.offsetTop;
  } while( node = node.offsetParent );
  return { x:x, y:y, w:parseInt(s.width), h:parseInt(s.height), node:top };
}

EventMgr = // avoid leaking event handlers
{
  _registry:null,
  initialize:function() {
    if(this._registry == null) {
      this._registry = [];
      EventMgr.add(window, "_unload", this.cleanup);
    }
  },
  add:function(obj, type, fn, useCapture) {
    this.initialize();
    if(typeof obj == "string")
      obj = document.getElementById(obj);
    if(obj == null || fn == null)
      return false;
    if(type=="unload") {
      // call later when cleanup is called. don't hook up
      this._registry.push({obj:obj, type:type, fn:fn, useCapture:useCapture});
      return true;
    }
    var realType = type=="_unload"?"unload":type;
    obj.addEventListener(realType, fn, useCapture);
    this._registry.push({obj:obj, type:type, fn:fn, useCapture:useCapture});
    return true;
  },
  cleanup:function() {
    for(var i = 0; i < EventMgr._registry.length; i++) {
      with(EventMgr._registry[i]) {
        if(type=="unload")
	  fn();
        else {
	  if(type=="_unload") type = "unload";
	  obj.removeEventListener(type,fn,useCapture);
        }
      }
    }
    EventMgr._registry = null;
  }
};

function hilite( node, other, name, color, zoomp )
{
  return function() {
    try {
      var at = get_coords_of( node );
      if( zoomp )
	scrollTo( 0, Math.round( at.y + at.h/2 - innerHeight/2 ) );
      at.node.style.background = color;
      get_coords_of( name ).node.style.background = color;
      get_coords_of( other ).node.style.background = color;
    } catch(e) { GM_log( 'Error hilighting:'+ e ); }
  };
}

var name_to_data = {
  Age: [seeking_ages, is_age],
  'Eye color': 'Eyes',
  'Hair color': 'Hair',
  Smoking: 'Smoke',
  Drinking: 'Drink',
  Job: ['Job', 'Occupation'],
  Religion: ['Faith', 'Religion'],
  Marital: 'Relationships',
  PlanChild: 'Want kids',
  Children: 'Have kids',
  Diet: [null, 'Daily diet'],
  Dislikes: ['Turn-offs', null],
  Likes: ['Turn-ons', null],
  Exercise: 'Exercise habits',
  'Exercise Type': [null, 'Sports and exercise'],
  'Have pet': [null, 'Pets I have'],
  'Like pet': [null, 'Pets I like'],
  Politic: 'Politics',
  'Interests': [null, 'Interests']
};

function details()
{
  return $x( 'id("profileDetails")/p[1]/text()' );
}

function is_age( debug )
{
  var data = details()[0]; // i e "31-year-old woman"
  return { text:ws_trim( data.nodeValue ), node:data, debug:debug };
}

function seeking_ages( debug )
{
  var data = details()[2]; // i e "seeking men 26-44"
  return { text:ws_trim( data.nodeValue ), node:data, debug:debug };
}

function find_datum_by_name( name, direction )
{
  var at = $( 'mainContentColumn' );
  var got = $x( './/td[preceding-sibling::td/text()="'+ name +':"]', at );
  var node = got[1-direction];
  return { text:ws_trim( node.textContent ), node:node.parentNode };
}

function find_data_in_page( name, how, direction )
{
  if( !(how instanceof Array) )
    how = [how, how];
  var pick = how[direction];
  GM_log( pick );
  if( pick === null )
    throw( 'N/A' );
  if( typeof pick == 'function' )
    return pick( name, direction );
  return find_datum_by_name( pick||name, how[0]==how[1] ? direction : 1 );
}

// on hovering margin tds, show how I match your expectations, or you mine
function link_matches( tr )
{
  var tds = $x( 'td', tr );
  var what = ws_trim( tds[1].textContent );
  var fetch = name_to_data[what];

  // i=0: how I match your expectations;
  // i=1: how you match my expectations.
  for( var i=0,c=0,data; i<2; i++,c+=2 )
  {
    try {
      data = find_data_in_page( what, fetch, i );
    } catch( e )
    { continue; }
    if( data && data.text )
      GM_log( what+': '+data.text );
    else
      GM_log( fetch );
    if( data.text )
      tds[c].setAttribute( 'title', data.text );
    if( data.node )
    {
      var c0 = data.node, c1 = tds[c], c2 = tds[1];
      EventMgr.add( c1, 'mouseover', hilite( c0, c1, c2, 'yellow', 1 ) );
      EventMgr.add( c1, 'mouseout',  hilite( c0, c1, c2, '' ) );
      EventMgr.add( c0, 'mouseover', hilite( c1, c0, c2, 'yellow' ) );
      EventMgr.add( c0, 'mouseout',  hilite( c1, c0, c2, '' ) );
    }
  }
}

function init()
{
  // move the metrics table to the top right
  GM_addStyle( '#metricsTable { background:#FFF; width:210px; }\n' +
	       '#matchMetricsContent\n' +
	       '{ width:218px; position:fixed; top:12px; left:782px; }' );

  var profile_name = $x('id("pnlViewProfileWorkarea")//h1')[0].textContent;
  var profile_tagline = $('profileHeadline').textContent;
  document.title = profile_name +': '+ profile_tagline;

  var metrics = $( 'metricsTable' );
  foreach( './/tr', link_matches, metrics );
}

init();