Lunarstorm Blog touch-ups

By Johan Sundström Last update Mar 14, 2006 — Installed 663 times.
// ==UserScript==
// @name           Lunarstorm Blog touch-ups
// @namespace      http://www.lysator.liu.se/~jhs/userscript
// @description    Adds hover hilighting of blog entries in the calendar panel, drops the McD footer, changes links to blogs to point to the blog list overview (and call it diary) as it was in old times, makes the blog log embolden visitors to the post whose visitors you tried to peek at, and expands entry titles in lists to their full, non-abbreviated length. Shift-click the Diary link to start writing a new entry, alt-click it to see recent visitors, alt+shift-click it to see recent comments, and shift-click an entry title (in your blog list overview) to edit its post contents. The visitors view also gets ads replaced with a view filter widget, and the whole visitor log is loaded into the same page.
// @include        http://www.lunarstorm.se/*
// ==/UserScript==

if( !is_normal_pageview() ) return;

var debug = 0 ? function( e ){ alert( e ); } : function(){};

var visitor_xpath = '//tr[starts-with(@class,"mlist")]' +
  '/td/a[contains(@href,"/usr/usr_presentation.aspx?")]';

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;
  }
};

// /blg/blg_view.aspx?UserID={id} to /blg/blg_viewList.aspx?userID={id}
function link_to_blog_view( a )
{
  a.pathname = a.pathname.replace( /\/blg\/blg_view\./, '/blg/blg_viewList.' );
  a.innerHTML = a.innerHTML.replace( /Blogg?/i, 'Dagbok' );
  EventMgr.add( a, 'click', diary_click, true );
}

function diary_click( e )
{
  var type = !!e.altKey * 2 + !!e.shiftKey;
  if( type )
  {
    e.preventDefault();
    e.stopPropagation();
    location.href = '/blg/blg_'+(['compose','log','comments'][--type])+'.aspx';
  }
}

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

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

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;
}

function foreach( xpath, cb )
{
  var nodes = $x( xpath ), i, e = 0;
  for( i=0; i<nodes.length; i++ )
    e += cb( nodes[i], i ) || 0;
  return e;
}

function remove( xpath )
{
  foreach( xpath, function( node ){ node.parentNode.removeChild( node ); } );
}

function parse_lunar_date( date )
{
  //alert( 'parse_lunar_date ' + date );
  var months = 'jan,feb,mar,apr,maj,jun,jul,aug,sep,okt,nov,dec'.split( ',' );
  var month = {}, y, m, d, H, M, i;
  for( i=0; i<months.length; i++ )
    month[months[i]] = i;
  if( i = /^\s*\S+\s+(\d+)\s+(\S\S\S)\s+(\d+)?\s*(\d\d):(\d\d)/.exec( date ) )
  {
    if( i[3] )
    {
      y = parseInt( i[3], 10 );
      if( y < 100 ) y += 2000;
    }
    else
      y = (new Date).getFullYear();
    m = month[i[2].toLowerCase()];
    d = i[1];
    H = i[4];
    M = i[5];
    //alert( '> ' + y + '-' +(m+1)+ '-' +d+ ' ' +H+':'+M );
    return new Date( y, m, d, H, M );
  }
  if( i = /^i(dag|g\345r|g&aring;r)\s+(\d\d):(\d\d)/i.exec( date ) )
  {
    var date = new Date();
    if( i[1] != 'dag' )
      date = new Date( date.getTime() - 864e5 );
    date.setHours( i[2] );
    date.setMinutes( i[3] );
    date.setSeconds( 0 );
    date.setMilliseconds( 0 );
    return date;
  }
}

function change_bg_cb( re, color, year, month, tr )
{
  return function( e )
  {
    if( tr ) unsafeWindow.hovering = tr;
    var urls = Array.prototype.slice.call( document.links ).filter( re ), a;
    if( urls.length )
      while( a = urls.shift() )
	a.parentNode.style.backgroundColor = color;
    else if( color && month && year >= 2000 )
      with(unsafeWindow)
      {
	currentYear = year;
	currentMonth = month;
	doCalenderRemoteCall();
	//setTimeout( change_bg_cb( re, color, year, month ), 0 );
      }
  };
}

var entry_dates = {}; // YYYY-M-D to matching (left) list tr node

function change_li_bg_cb( td, hoverp )
{
  return function()
  {
    var a = td.getElementsByTagName( 'a' ).item( 0 ), tr, date;
    if( !a ||
	!(date = a.search.match( /publishdate=([\d-]+)/i )) ||
	!(tr = entry_dates[date[1]]) )
      return;
    var synthetic_event = document.createEvent( 'MouseEvents' );
    synthetic_event.initEvent( hoverp ? 'mouseover' : 'mouseout', true, true );
    tr.dispatchEvent( synthetic_event );
  };
}

function shift_click_to_edit( e )
{
  var edit = '/blg/blg_compose.aspx';
  var a = e.target;
  if( e.shiftKey )
  {
    e.preventDefault();
    e.stopPropagation();
    location.href = edit + a.search.replace( /&UserID=.*/i, '' );
  }
}

// postlist: /blg/blg_view.aspx?postID={pid}&UserID={uid}
// calendar: /blg/blg_view.aspx?userID={uid}&publishDate=2006-3-14
function show_hover_colors()
{
  try {
  var url = 'td/a[contains(@href,"blg_view.aspx")]'; // post url xpath
  var rdc = 'td/a[contains(@id,"_hrfReadCount")]'; // post read count xpath
  var pat = 'td[contains(@id,"_d")]/text()'; // date node xpath pattern
  var lxp = '//tr[@onmouseover]['+ url +']'; // list item xpath pattern
  var cre = 'blg_view.aspx\\?userID=[^&]+&publishDate=';
  var trs = $x( lxp ), tr, td, re, a, r, t, y, m, d, x, y, i;
  var mep = !get( '//img[contains(@src,"_gfx/blg_nav_myblog.gif")]' );
  for( ; tr = trs.shift(); )
  {
    if( !(a = get( url, tr )) ||
        !(t = get( pat, tr )) ||
	!(d = parse_lunar_date( t.nodeValue )) )
      continue;
    a.innerHTML = a.title;
    a.title = '';
    if( (r = get( rdc, tr )) )
      r.href = '/blg/blg_log.aspx'+ a.search;
    d = [ y = d.getFullYear(), m = d.getMonth()+1, d.getDate()].join( '-' );
    entry_dates[d] = tr;
    re = new RegExp( cre + d + '$', 'i' );
    EventMgr.add( tr, 'mouseover', change_bg_cb(re,'#FFD6BD',y,m,tr), false );
    EventMgr.add( tr, 'mouseout',  change_bg_cb(re,''), false );
    if( !mep ) continue; // can't edit other people's posts
    EventMgr.add( a, 'click', shift_click_to_edit, false );
  }
  for( x=1; x<=7; x++ )
    for( y=1; y<=6; y++ )
    {
      td = $( 'd'+ x +'_'+ y );
      EventMgr.add( td, 'mouseover', change_li_bg_cb( td, 1 ), false );
      EventMgr.add( td, 'mouseout',  change_li_bg_cb( td, 0 ), false );
    }
  }catch(e){alert(e)};
}

function fix_post_title( td )
{
  td.innerHTML = td.title;
  td.style.whiteSpace = 'pre';
  td.style.paddingRight = '5px';
  var postid = location.search.match( /PostID={?([^&}]+)}?/i );
  var click = td.getAttribute( 'onclick' );
  if( postid && click && click.match( new RegExp('postid='+postid[1], 'i') ) )
    td.style.fontWeight = 'bold';
}

function fix_blog_log_links( a )
{
  foreach( '//a[starts-with(@href,"blg_log.aspx")]', function( a ) {
    a.search += location.search.replace( '?', '&' ).replace( /&page=\d+/, '' );
  });
}

function set_page_title()
{
  var date = $x( '//td[@class="mainraw"]//div[@class="mtext"]' )[0];
  var title = $x( '//td[@class="mainraw"]//div[@class="hds"]' )[0];
  date = parse_lunar_date( date.textContent );
  document.title = format_iso_date( date )+': '+title.textContent;
}

function format_iso_date( date )
{
  var y = date.getFullYear(), m = date.getMonth()+1, d = date.getDate();
  return [y, m<10?'0'+m:m, d<10?'0'+d:d].join( '-' );
}

try{
remove( '//img[@src="_gfx/blg_footer.jpg"]' ); // drop ugly McD footer.
foreach( '//a[contains(@href,"/blg/blg_view")][text()="Blogg"]',
	 link_to_blog_view );
switch( location.pathname.toLowerCase() )
{
  /*
  case '/blg/blg_compose.aspx': // set a decently large blog editor size
    $('txtTempEditor').setAttribute( 'rows', '22' ); break;
  */
  case '/blg/blg_list.aspx': dont_read_on_imagezoom(); break;
  case '/blg/blg_viewlist.aspx': show_hover_colors(); break;
  case '/blg/blg_view.aspx': // Point the blog log link to this post's entries
    fix_blog_log_links();
    set_page_title(); break;
  case '/blg/blg_log.aspx': // expand abbreviated post titles
    fix_blog_log_links();
    foreach( '//td[contains(@id,"_tdTitle")]', fix_post_title );
    hook_up_view_filter(); // add text input to filter visitors
    if( !location.search.match( /page=/i ) )
      pull_in_full_blog_log();
    break;
}
}catch(e){alert(e)}

function pull_in_full_blog_log()
{
  var xp = visitor_xpath + '/../..';
  var url = 'http://www.lunarstorm.se/blg/blg_log.aspx?page=', p;
  var next = $x( xp ).pop().nextSibling;
  for( p=2; p<5; p++ )
  {
    var tr = document.createElement( 'tr' );
    tr.id = 'page'+p;
    next.parentNode.insertBefore( tr, next );
    get_url_xpath( url + p, xp, inject_bloglog );
  }
}

function inject_bloglog( nodes, xhr, url )
{
  var page = parseInt( url.charAt( url.length-1 ), 10 );
  var here = $( 'page'+ page );
  // GM_log( 'inject_bloglog '+ page +' '+ nodes.length );
  while( nodes.length )
  {
    var node = document.importNode( nodes.shift(), true );
    node.style.display = 'none'; // temporarily hide
    here.parentNode.insertBefore( node, here );
  }
  here.parentNode.removeChild( here );
  filter_view(); // show by view filter
}

function get_url_xpath( url, xpath, cb/*( [DOMNodes], xhr, url )*/ )
{
  get_url_dom( url, function( xml, xhr, url )
  {
    cb( $x( xpath, xml ), xhr, url );
  } );
}

// returns false for pages fetched via get_url_dom()
function is_normal_pageview()
{
  return (window == top) || ('lunar_main' == window.name);
}

function get_url_dom( url, cb/*( xml, xhr, url )*/ )
{
  // GM_log( 'loading '+url );
  GM_xmlhttpRequest({ method:'GET', url:url, onload:function( xhr )
  {
    if( xhr.responseXML )
      cb( xhr.responseXML, xhr, url );
    else
      makeDOM( xhr.responseText, xhr, url, cb );
  } });
}

function makeDOM( html, xhr, url, cb )
{
  //GM_log( 'makeDOM['+ html.length +'] '+url );
  // create an IFRAME to write the document into. the iframe must be added
  // to the document and rendered (eg display != none) to be property 
  // initialized.
  var iframe = document.createElement( 'iframe' );
  iframe.style.height = iframe.style.width = '0';
  iframe.style.visibility = 'hidden';
  iframe.style.position = 'absolute';
  document.body.appendChild( iframe );

  // give it a URL so that it will create a .contentDocument property. Make
  // the URL be the current page's URL so that we can communicate with it.
  // Otherwise, same-origin policy would prevent us.
  iframe.contentWindow.location.href = url;

  html = html.replace( /[\n\r]+/g, ' ' ). // indefinite load time sans this(?!)
    replace( /<script.*?<\/script>/ig, '' ). // (not interested in script code)
    replace( /<body(\s+[^="']*=("[^"]*"|'[^']*'|[^'"\s]\S*))*\s*onload=("[^"]*"|'[^']*'|[^'"]\S*)/ig, '<body$1' );

  // write the received content into the document
  var doc = iframe.contentDocument;
  iframe.contentDocument.open( 'text/html' );
  iframe.contentDocument.write( html );
  iframe.contentDocument.close();

  // wait for the DOM to be available, then do something with the document
  iframe.contentDocument.addEventListener( 'DOMContentLoaded', function()
  {
    //GM_log( 'DOMContentLoaded!' );
    cb( iframe.contentDocument.documentElement, xhr, url );
    //GM_log( $x( '//td', iframe.contentDocument.documentElement ).length );
  }, false);

  //GM_log( 'written html' );
}

var filter_input;
var visible_visitors;

function hook_up_view_filter()
{
  $x( '//td[text()="Annons"]' )[0].innerHTML = 'Filtrera l\xE4sarlistan';
  var td = $( 'externalAdsFrame' ).parentNode;
  td.style.background = '#FFF';
  td.style.padding = '2px 0 1px';
  td.style.border = '1px solid #000';
  td.style.borderTop = '0';
  td.style.lineHeight = '25px';
  var inputs = ['Namn:', 'Ort:', 'Inl\xE4gg:', 'Tidpunkt:'], html = '';
  for( var i=0; i<inputs.length; i++ )
    html += '<div style="width:70px; text-align:right; float:left;">'+
	    '<u>'+ inputs[i].charAt(0) +'</u>'+ inputs[i].substr(1) +
	    '</div><div style="float:right; width:130px;">'+
	    '<input type="text" style="width:128px; margin:1px 0;" '+
	    'accessKey="'+ inputs[i].charAt(0) +'"></div><br />';
  td.innerHTML = html;
  filter_input = inputs = td.getElementsByTagName( 'input' );
  for( var i=0; i<inputs.length; i++ )
  {
    EventMgr.add( inputs[i], 'keyup', filter_view, false );
    EventMgr.add( inputs[i], 'onchange', filter_view, false );
  }
}

function filter_view( e )
{
  //GM_log( (new Date).getTime() );
  //if( location.pathname.toLowerCase() != '/blg/blg_log.aspx' )
  //  return;
  foreach( visitor_xpath, filter_visible_lines );
}

function filter_visible_lines( node, line )
{
  if( !line )
    visible_visitors = 0;
  var line = node.parentNode.parentNode;
  var cols = $x( 'td[text()]', line );
/*
  var data = node.nextSibling.nodeValue.substr( 1 );
  var name = node.textContent; name += data;
  var city = /^.\d*,? ?(.*)/.exec( data )[1];
  var age = parseInt( data.substr( 1 ), 10 );
  var sex = data.charAt( 0 );
*/
  var matches, matcher, data;
  for( var i=0; i<cols.length; i++ )
  {
    data = cols[i].textContent.replace( /\s+/g, ' ' );
    matcher = filter_input[i].value;
    if( matcher.toLowerCase() != matcher ) // case sensitive?
      matches = data.match( matcher );
    else
      matches = data.toLowerCase().match( matcher );
    if( !matches )
      break;
  }
  if( matches )
  {
    var oddp = visible_visitors++ & 1;
    line.style.backgroundColor = oddp ? '#FFF' : '#E9F0F2';
    line.className = 'mlist' + oddp;
    line.style.display = '';
  }
  else
    line.style.display = 'none';
}