// ==UserScript==
// @name            Spray Date semi deluxe; less cruft, more features.
// @version         1.4
// @namespace       http://www.lysator.liu.se/~jhs/userscript
// @description     Multi-purpose UI overhaul script to make Spray Date more user friendly and less full of cruft. * Drops lots of marketing junk, Deluxe teasers, irrelevant off-site links and static page headers/footer cruft. * Reorganizes views for readability, reformatting user links with fix-width columns first (sex, age) followed by name (bold, when the user has an image, unwound so >9 letter names are always shown in full) and optional deluxe stars and hearts. * Adds zoomable user images and hover images. * Gives tabs keyboard shortcuts, Alt+M for messages and Alt+1..N for your most recent visitors. * Adds a Visitors tab to other people. * Makes the visitors table dates ISO formatted. * Makes the visitors table sortable by clicking column headers. * Replaces the animated user banner with a zoomed-up version of the profile user image, when available. Otherwise just removed the banner. * Adds a client side "favourites" list to which you can add people by Alt-clicking their names in visitor lists et c. (This presently does not fetch their online status, except when you encounter their names somewhere by chance.)
// @include         http://spraydate.spray.se/spraydate/*
// ==/UserScript==

var unknown_after_ms = 60*60*1e3; // after 1h, online colours go black

try {
  var me = document.forms.namedItem('changelist');
  me = me ? me.elements.namedItem('id').value : '';
  var path = location.pathname;
  var profile_url = '"/spraydate/personal/personal.jsp?id="';
  var register_url = '"/spraydate/registration/vip_registration.jsp"';
  foreach( '//*[@id="banner_wrapper"]', function(x){ x.style.visibility = 'hidden'; } );
  foreach( '//a[starts-with(@href,'+ profile_url +')][text()]', fix_name );
  foreach( '//*[contains(text(),"...")][ancestor-or-self::*[@title]]',
	   show_full_name );
  foreach( '//*[@id="search_tabs"]', add_visitor_link );
  foreach( '//img[starts-with(@id,"b:")]', make_zoomable );
  foreach( '//*[@id="search_tabs"]/div/a', add_access_key );
  foreach( '//a[@href="/spraydate/personal/inbox.jsp"][1]',
	   function( a ) { a.accessKey = 'M'; } );
  foreach( '//tr/td[4][contains(text(),":") and contains(text(),"/")]', time );
  foreach( '//td[@class="bold"][not(a)]', sort_by_column );
  foreach( '//i[text()="Spray Date \226 h\344r finns singlarna"]|//a[@target '+
	   'or @href='+ register_url +' or contains(@onclick,"DeluxeTeaser")]'+
	   '|//h3[text()="Spray Date tipsar"]|*[@id="globalfooter"]/p'+
	   '|//*[@id="globaltopcurve" or @id="globalheader" or '+
	   '@id="globalwidebanner" or @id="bannerbottomcurve" or '+
	   '@id="skyscraper" or @id="globalcopyright"]', remove_junk_link );
  foreach( '//ul[@class="users"]/li/a[starts-with(@href,'+ profile_url +')]',
	   add_numeric_visitor_access_key );
  redraw_favourites();
  unsafeWindow.USERLINK_IMAGE_ALLOWED = true;
  try{ unsafeWindow.init(); }catch(e){}

switch( path )
{
  case '/spraydate/search/search_result.jsp': return alter_search();
  case '/spraydate/search/search_advance.jsp': return search_many();
  default:
    if( path.match( /^\/spraydate\/personal\// ) )
      if( location.search && location.search != '?id='+me )
	replace_photo_ad_with_large_face();
}
} catch(e){alert(e)};

function add_visitor_link( div )
{
  with( location ) if( search.length )
  {
    var url = 'visitors_other.jsp'+ search, at = href.indexOf( url ) > -1;
    appendTo( <div class={at ? 'active' : ''}>
	      <a href={url}>Bes&#246;kare</a></div>,
	      div );
    /*
    div.innerHTML += '<div class="'+ (at ? 'active' : '') +
      '"><a href="'+ url +'">Bes\366kare</a></div>';
    */
  }
}

function add_numeric_visitor_access_key( a, n )
{
  if( ++n <= 9 ) a.accessKey = n;
}

function add_access_key( a )
{
  var rename = { '\326versikt' : 'Presentation' };
  var title = rename[a.textContent] || a.textContent, i, k;
  if( title == 'Statistik' && location.href.match( 'visitors_other.jsp' ) )
    a.parentNode.className = '';
  for( i=0; i<title.length; i++ )
  {
    k = title.charAt( i ).toUpperCase();
    a.setAttribute( 'accesskey', k );
    a.innerHTML = title.replace( new RegExp( '(.*?)('+ k +')(.*)', 'i' ),
				 '$1<u>$2</u>$3' );
    break;
  }
}

function replace_banner( x )
{
  x.style.visibility = 'hidden';
  var img = $('b:1'); if( !img ) return;
  var url = img.src.replace( /thumbnails(.*)gif/, 'images$1jpg' );
  img = document.createElement( 'img' );
  img.addEventListener( 'load', (function(x){return function(){show_face(x);};})(img), false );
  img.src = url;
}

function show_face( image )
{
  var me = $('large_face');
  var sw = image.width, sh = image.height;
  var ca = document.createElement( 'canvas' );
  var dw = ca.width = 158, dh = ca.height = 188;
  me.parentNode.replaceChild( ca, me );
  var ctx = ca.getContext( '2d' );
  var tow = dw, toh = dw * sh / sw;
  var tox = 0,  toy = (dh - toh)/2;
  if( toh > dh )
  {
    toh = dh;
    tow = dh * sw / sh;
    tox = (dw - tow)/2;
    toy = 0;
  }
  //prompt( sw+'x'+sh, tow+'x'+toh );
  ctx.drawImage( image, tox, toy, tow, toh );
}

function remove_junk_link( a )
{
  var p = a.parentNode, c;
  p.removeChild( a );
  while( c = p.firstChild )
    if( c.nodeType == 3 && c.nodeValue.match( /^\s*$/ ) )
      p.removeChild( c );
    else
      break;
  if( !p.firstChild )
    remove_junk_link( p );
}

function show_full_name( node )
{
  if( node.parentNode.nodeName != 'LI' )
    node.innerHTML = node.title || node.parentNode.title;
}

function time( t )
{
  var x = t.textContent.split( /\D/ ), d = new Date;
  d.setDate( parseInt( x[0], 10 ) );
  d.setMonth( parseInt( x[1], 10 )-1 );
  d.setFullYear( parseInt( x[2], 10 )+2000 );
  d.setHours( parseInt( x[3], 10 ) );
  d.setMinutes( parseInt( x[4], 10 ) );
  t.setAttribute( 'key', d.getTime() );
  t.innerHTML = d.getFullYear()+'-'+x[1]+'-'+x[0]+' '+x[3]+':'+x[4];
}

function sort_by_column( th )
{
  var by = th.textContent, tr = th.parentNode, tb = tr.parentNode, span;
  th.style.cursor = 'pointer';
  th.addEventListener( 'click', function( e )
  {
    var c, i, ths = tr.getElementsByTagName( 'td' ), order, rows, table;
    for( i=0; i<ths.length; i++ )
      if( ths[i] == th )
	c = i;
    order = th.getAttribute( 'order' ) == 'desc' ? 'asc' : 'desc';
    th.setAttribute( 'order', order );
    span = document.createElement( 'span' );
    span.style.fontWeight = 'bold';
    span.innerHTML = order == 'desc' ? '&darr;' : '&uarr;';// '&#8679;' : '&#8681;';
    if( th.lastChild.nodeName == 'SPAN' ) th.removeChild( th.lastChild );
    th.appendChild( span );
    rows = tb.getElementsByTagName( 'tr' );
    table = Array.prototype.slice.call( rows, 2 ).sort( function( a, b )
    {
      var A = a.getElementsByTagName( 'td' );
      var B = b.getElementsByTagName( 'td' );
      a = A[c].innerHTML.toLowerCase();
      b = B[c].innerHTML.toLowerCase();
      return a > b ? -1 : a == b ? 0 : 1;
    } );
    if( order == 'desc' ) table = table.reverse();
    for( i=2; i<rows.length; i++ ) tb.removeChild( rows[i] );
    for( i=0; i<table.length; i++ ) tb.appendChild( table[i] );
  }, false );
}

// reorganize sex, age, name and deluxestar to that order
function fix_name( a, N )
{
  var p = a.parentNode, i, n, name = a, x = document.createRange(), h;
  if( name.getAttribute( 'xuserimg' ) == 1 )
    name.style.fontWeight = 'bold';
  x.selectNode( name );
  if( a.previousSibling ) a = a.previousSibling;
  for( i=0; a && i<8; i++ )
  {
    if( a.nodeName == 'IMG' )
    {
      if( a.getAttribute( 'gender' ) )
      {
	a.style.paddingRight = '3px';
	p.removeChild( a );
	p.insertBefore( a, name );
	x.setStartBefore( a );
      }
      else if( a.getAttribute( 'match' ) )
	x.setEndAfter( a );
      else if( a.src.match( '/spraydate/inc/images/starsAnimated.gif$' ) )
      {
	a.style.paddingLeft = '3px';
	a.src += '#'; // don't move it twice
	p.removeChild( a );
	p.insertBefore( a, name.nextSibling );
	x.setEndAfter( a );
	continue;
      }
    }
    else if( a.nodeType == 3 && (n = parseInt(a.nodeValue)) )
    {
      p.removeChild( a );
      p.insertBefore( document.createTextNode( n+' ' ), name );
    }
    a = a.nextSibling;
  }
  name.addEventListener( 'click', (function( n, x )
  {
    var h = document.createElement( 'div' );
    h.appendChild( x.cloneContents() );
    h = h.innerHTML;
    update_favourites( n, h, 'update' );
    return function( e )
    {
      if( e.altKey )
      {
	e.preventDefault();
	e.stopPropagation();
	update_favourites( n, h, 'add' );
      }
    };
  })( name.title, x ), false );
}

function get_favourites( name )
{
  var names = GM_getValue( '[favourites]', '' ), i, x;
  if( names )
  {
    x = names.split(',');
    names = {};
    for( i=0; i<x.length; i++ )
      names[x[i]] = GM_getValue( x[i], '' );
  }
  names = names || {};
  return name ? names[name] : names;
}

function update_favourites( name, html, action )
{
  var changed = false, fav = get_favourites(), i, names = [];
  if( action == 'add' ||
     (action == 'update' && fav[ name ]) )
  {
    GM_setValue( name, fav[name] = (new Date).getTime()+':'+html );
    changed = true;
    if( action == 'add' )
    {
      for( i in fav )
	names.push( i );
      GM_setValue( '[favourites]', names.join(',') );
    }
  }
  if( changed )
    redraw_favourites();
}

function redraw_favourites()
{
  foreach( '//div[@class="boxcontent"][preceding::div[text()="Favoriter"]]',
	   render_favourites );
}

function render_favourites( node )
{
  var all = get_favourites(), i, t, data, html = '', T = (new Date).getTime();
  //var names = GM_getValue( '[favourites]', '' ).split(',');
  for( i in all )
  {
    if( data = /^(\d+):(.*)$/.exec( all[i] ) )
    {
      t = T - data[1];
      data = data[2];
    }
    else
    {
      t = Infinity;
      data = all[i];
    }
    if( t > unknown_after_ms )
      data = data.replace( / class=["']?o(n|ff)line['"]?/gi, '' );
    html += data + '<br />';
  }
  node.innerHTML = html || 'Inga favoriter angivna';
}

function make_zoomable( img )
{
  var a = $( 'a'+ img.id.substr(1) );
  a.parentNode.innerHTML = '<img id='+ a.id +' onload=ziFull(this) src='+
    (img.src.replace( /thumbnails(_small)?(.*)gif/, 'images$2jpg') ) +' />';
}

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

function foreach( xpath, cb )
{
  var nodes = all( xpath ), i;
  for( i=0; i<nodes.length; i++ )
    cb( nodes[i], i );
}

function replace_photo_ad_with_large_face()
{
  var img, url;
  var node = get( '//div[@id="banner_wrapper"]/ancestor::div[4]' );
  erase( node );
  var root = '/html/body/div[1]/div[1]/div[6]/div[2]/div[2]/div[1]';
  //erase( root+'/div[2]/div[1]' );
  var img = get( '//img[@id="b:1"]' ), url, css;
  if( img )
  {
    if( $('large_face') ) replace_banner( $('large_face') );
    // url = img.src.replace( /thumbnails(.*)gif/, 'images$1jpg' );
    // css = 'background:url('+url+') no-repeat 50% 50%;height:200px;';
    // style( root+'/div[2]', css );
  }
  else
  {
    style( root + '/div[1]', 'width:auto;' );
    style( root + '//span[@class="topmiddle"]', 'width:446px;' );
    style( root + '//span[@class="bottommiddle"]', 'width:446px;' );
  }
}

function search_many()
{
  var howmany = $( 'PageSelectBox' ), input;
  if( howmany )
  {
    input = document.createElement( 'input' );
    input.value = howmany.value;
    input.name = howmany.name;
    input.type = 'text';
    input.size = 4;
    howmany.parentNode.replaceChild( input, howmany );
  }
}

function alter_search()
{
  search_many();
  var base = 'http://spraydate.spray.se/spraydate/', name, div, img;
  var people = all( '//div[@class="result-name"]/a[1]' ), a, i;
  for( i=people.length-1; i>=0; i-- )
  {
    a = people[i];
    name = a.innerHTML;//a.textContent;
    div = document.createElement( 'div' );
    img = document.createElement( 'img' );
    img.setAttribute( 'style', 'max-height:64px; max-width:64px;' );
    img.src = base +'userimages/_'+ name[0] +'/'+ name +'.jpg';
    div.setAttribute( 'style', 'width:64px;height:64px;float:left;' );
    div.appendChild( img );
    a.parentNode.insertBefore( div, a );
    a.parentNode.style.height = '80px';
  }
}

function all( 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;
}

function get( xpath )
{
  if( typeof xpath != 'string' ) return xpath;
  var type = XPathResult.FIRST_ORDERED_NODE_TYPE, n = null;
  return document.evaluate( xpath, document, n, type, n ).singleNodeValue;
}

function erase( node )
{
  if( !(node = get( node )) ) return ( 'no erase' );
  var h = node.offsetHeight;
  var w = node.offsetWidth;
  var div = node.ownerDocument.createElement( 'div' );
  div.setAttribute( 'style', 'height:'+ h +';width:'+ w +';' );
  node.parentNode.insertBefore( div, node );
  node.style.display = 'none';
  div.id = 'large_face';
  return div;
}

function style( node, style )
{
  if( !(node = get( node )) ) return ('no styling');
  node.setAttribute( 'style', style );
}

function appendTo( e4x, node, doc )
{
  return node.appendChild( importNode( e4x, doc || node.ownerDocument ) );
}

function importNode( e4x, doc )
{
  var me = importNode, xhtml, domTree, importMe;
  me.Const = me.Const || { mimeType: 'text/xml' };
  me.Static = me.Static || {};
  me.Static.parser = me.Static.parser || new DOMParser;
  xhtml = <testing xmlns="http://www.w3.org/1999/xhtml" />;
  xhtml.test = e4x;
  domTree = me.Static.parser.parseFromString( xhtml.toXMLString(),
					      me.Const.mimeType );
  importMe = domTree.documentElement.firstChild;
  while( importMe && importMe.nodeType != 1 )
    importMe = importMe.nextSibling;
  if( !doc ) doc = document;
  return importMe ? doc.importNode( importMe, true ) : null;
}
