Friday, September 30, 2011

YouTube Link Title

// ==UserScript==
// @name           YouTube Link Title
// @description    Adds video title to YouTube links. Embeds videos on click. Supports Ajax sites.
// @author         kuehlschrank
// @homepage       http://userscripts.org/scripts/show/83584
// @version        2011.9.30
// @icon           http://s3.amazonaws.com/uso_ss/icon/83584/large.png
// @updateURL      http://userscripts.org/scripts/source/83584.meta.js
// @include        http*
// @exclude        http*//*.google.*/*
// @exclude        http*//*.youtube.com/*
// @exclude        http://userscripts.org/scripts/show/83584
// ==/UserScript==


// =======================================================================================
//                                C O N F I G U R A T I O N
// =======================================================================================
var embed_on_click = true;
var embed_mode     = 'center'; // 'center', 'right' or 'inline'
var lights_out     = false;    // embed mode must be 'center' or 'right'
var autoscroll     = true;
// =======================================================================================


function onLoad() {
 var cache = new Cache();
 processLinks(document, cache);
  document.addEventListener('DOMNodeInserted', function(e) {
   if(e.target.nodeType != 1) return;
   var f = function() { processLinks(e.target, cache); };
   window.setTimeout(f, 200);
  }, false);
}


function processLinks(node, cache) {
 var i = 0, list = node.querySelectorAll('a[href*="youtube.com/watch"], a[href*="youtu.be/"], a[href*="youtube.com/v/"], a[href*="http://t.co/"][data-expanded-url*="youtu"]');

 if(list.length < 1) {
  return;
 }

 function processChunk() {
  var a, numCacheHits = 0, numRequests = 0;
  while(a = list[i++]) {
   try {
    var match, vid, url = (a.href.indexOf('http://t.co/') != -1) ? a.title : a.href;
    if(match = url.match(/(v[=\/]|youtu\.be\/)([a-z0-9_-]+)/i)) {
     vid = match[2];
    } else {
     continue;
    }
    var title = cache.get(vid);
    if(title) {
     addTitle(a, title, vid, cache);
     numCacheHits++;
    } else {
     request(a, vid, cache);
     numRequests++;
    }
   } catch(ex) {
    GM_log('Error while processing "' + a.href + '": ' + ex);
   }
   if(numRequests == 5) {
    return window.setTimeout(processChunk, 750);
   } else if(numCacheHits == 30) {
    return window.setTimeout(processChunk, 100);
   }
  }
  list = null;
 }

 processChunk();
}


function request(a, vid, cache) {
 GM_xmlhttpRequest({
  method: 'GET',
  url: 'http://gdata.youtube.com/feeds/api/videos/' + vid + '?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode',
  onload: function(req) {
   var title;
   if(req.status == 404 && req.responseText == 'Video not found') {
    title = 'Video not found\tn/a';
   } else if(req.status == 403 && req.responseText == 'Private video') {
    title = 'Private video\tn/a';
   } else {
    try {
     var entry = JSON.parse(req.responseText).entry;
     title = entry.title.$t;
     if(entry.app$control && entry.app$control.yt$state && entry.app$control.yt$state.reasonCode != 'limitedSyndication') {
      title += '\tn/a';
     } else if(typeof entry.yt$noembed == 'object') {
      title += '\tnoembed';
     }
    } catch(ex) {
     GM_log('API response for video ' + vid + ' could not parsed: ' + ex + ', HTTP ' + req.status);
     return;
    }
   }
   cache.set(vid, title);
   addTitle(a, title, vid, cache);
  }
 });
}


function addTitle(a, title, vid, cache) {
 title = title.split('\t');

 var ytIconNormal = 'DEp/AAAc7gASI/8AT1X/ABNwnQBeo8oArGZEAMiJZgCrq/8Aqub2ANTT/wD+/NgA////AOy8vAAAAAAA8zMzMzMzMz8yIiIiIiIiIzPUndS9k52zM9TT1NPZ0jMz1NPU09ndszPU09Tdk7uzM9MzM9MzMzM93TMz0zMzM7MzMzMzMzM73d3d3d3d3d3doM1gjWB93d2gyg0MXA3d3aDKDQwNDd3dYO1gjQ0N3d0ODd3d3d3d1h1+zd3d3d2AAQ';
 var ytIconGray   = 'WFhYAGNjYwCJiYkAuLi4ANTU1AD8/PwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiAhERERERERIiJjVmNWUkZSImNiY2JkYiIiY2JjYmRmUiJjYmNmUlZSImIiImIiIiImZiIiYiIiIlIiIiIiIiIlZmZmZmZmZmZmUGYwNkAmZmZgZQYGJgZmZlBmBgYGBmZmQEYwNgYGZmYFBmZmZmZmZBYkZmZmZmYAAA';
 var img = '<img alt="YouTube: " style="height:16px;width:16px;display:inline;float:none;position:relative;bottom:-2px;margin:0 2px;border:0" src="data:image/x-icon;base64,AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAABILAAASCwAAEAAAABAAAAAAAAAA' + (embed_on_click && title[1] == 'noembed' ? ytIconGray : ytIconNormal) + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"/>';

 if(title[1] == 'n/a') {
  a.style.textDecoration = 'line-through';
 }

 if(a.textContent.indexOf('youtube.com/') != -1 || a.textContent.indexOf('youtu.be/') != -1) {
  a.innerHTML = img + '<b><i>' + title[0] + '</i></b>';
 } else {
  a.title = title[0];
  if(a.innerHTML.indexOf('<img') == -1) {
   a.innerHTML = img + a.innerHTML;
  }
 }

 if(embed_on_click && title[1] != 'noembed') {
  a.setAttribute('kuehlschrank.vid', vid);
  a.addEventListener('click', onLinkClick, true);
 }
}


function onKeyDown(e) {
 if(e.keyCode == 27) {
  removePlayer();
 }
}


function onLinkClick(e) {
 if(e.ctrlKey || e.altKey || e.shiftKey || e.metaKey || e.button != 0) {
  return;
 }
 e.preventDefault();
 removePlayer();
 var vid = this.getAttribute('kuehlschrank.vid'), t, matches;
 if(embed_on_click && (matches = this.href.match(/\Wt=([\dms]+)/))) {
  t = matches[1];
 }
 insertPlayer(vid, t, this);
}


function insertPlayer(vid, t, link) {
 if(lights_out && embed_mode != 'inline') {
  var bg = document.createElement('div');
  bg.id = 'kuehlschrank.youtube-player-bg';
  bg.style.cssText = 'position:fixed;z-index:9999;top:0;right:0;bottom:0;left:0;background-color:#000000;opacity:0.8';
  document.body.appendChild(bg);
  bg.addEventListener('click', function(e) { if(bg.style.opacity > 0) bg.parentNode.removeChild(bg); }, true);
 }
 var big = GM_getValue('big', false);
 var iWidth = 640, iHeight = 390;
 if(big && embed_mode != 'inline') {
  iWidth = 853; iHeight = 510;
 }
 var iframe = document.createElement('iframe');
 iframe.style.cssText = 'display:' + (embed_mode == 'inline' ? 'inline' : 'block') +';width:' + iWidth + 'px;height:' + iHeight + 'px;background-color:#000;border:2px solid #333';
 iframe.src = 'http://www.youtube.com/embed/' + vid + '?autoplay=1&rel=1' + (t ? '#t=' + t : '');
 if(embed_mode == 'inline') {
  link.parentNode.replaceChild(iframe, link);
  if(autoscroll) {
   scrollTo(0, getOffsetTop(iframe) - iframe.clientHeight/2);
  }
 } else {
  var div = document.createElement('div');
  div.id = 'kuehlschrank.youtube-player';
  div.style.cssText = 'display:block;position:fixed;z-index:10000;width:' + (iWidth+4) + 'px;height:' + (iHeight+30) + 'px;top:50%;' + (embed_mode == 'right' ? 'right:10px' : 'left:50%') + ';margin:-280px 0 0 -' + (iWidth/2+2) + 'px;text-align:right;';
  var aResize = document.createElement('a');
  aResize.style.cssText = 'cursor:pointer;font-weight:bold;font-size: 12px;color:#58bdd3;background-color:#333;padding:1px 12px 1px 12px;text-decoration:none;display;opacity:0.7;border-right:1px solid #222222;';
  aResize.textContent = big ? '-' : '+';
  aResize.addEventListener('click', function(e) { e.preventDefault(); GM_setValue('big', !big); removePlayer(); insertPlayer(vid, t); }, true);
  div.appendChild(aResize);
  var aClose = document.createElement('a');
  aClose.style.cssText = 'cursor:pointer;font-weight:bold;font-size: 12px;color:#d36558;background-color:#333;padding:1px 20px 1px 20px;text-decoration:none;display;opacity:0.7;';
  aClose.textContent = 'X';
  aClose.addEventListener('click', function(e) { e.preventDefault(); removePlayer(); }, true);
  div.appendChild(aClose);
  div.appendChild(iframe);
  document.body.appendChild(div);
  document.addEventListener('keydown', onKeyDown, true);
  if(autoscroll) {
   link.scrollIntoView(true);
  }
 }
}


function getOffsetTop(node) {
 var top = node.offsetTop;
 while(node = node.offsetParent) {
  top += node.offsetTop;
 }
 return top;
}


function removePlayer() {
 if(lights_out) {
  var bg = document.getElementById('kuehlschrank.youtube-player-bg');
  if(bg) {
   bg.parentNode.removeChild(bg);
  }
 }
 var player = document.getElementById('kuehlschrank.youtube-player');
 if(player) {
  player.parentNode.removeChild(player);
 }
 document.removeEventListener('keydown', onKeyDown, true);
}


function Cache() {

 this.get = function(key) {
  if(cache == null) {
   this.load();
  }
  return cache[key];
 }

 this.set = function(key, value) {
  if(cache == null) {
   this.load();
  }
  cache[key] = value;
  if(!dirty) {
   dirty = true;
   window.addEventListener('unload', this.save, false);
  }
 }

 this.save = function() {
  if(dirty) {
   try {
    var overflow = count_properties(cache) - 350;
    if(overflow > 0) {
     remove_properties(cache, 50 + overflow);
    }
    GM_setValue('cache', JSON.stringify(cache));
   } catch(ex) { GM_log('Error while saving cache: ' + ex); }
   window.removeEventListener('unload', this.save, false);
   dirty = false;
  }
 }

 this.load = function() {
  try { cache = JSON.parse(GM_getValue('cache')); } catch(ex) { }
  if(cache == null || typeof(cache) != 'object') {
   cache = {};
  }
 }

 function count_properties(o) {
  var n = 0;
  for(var p in o)
   n += Object.prototype.hasOwnProperty.call(o, p);
  return n;
 }

 function remove_properties(o, num) {
  var i = 0;
  for(var p in o) {
   if(Object.prototype.hasOwnProperty.call(o, p)) {
    delete o[p];
    if(++i == num)
     return;
   }
  }
 }

 var cache = null;
 var dirty  = false;

}


window.setTimeout(onLoad, 200);

0 comments:

Post a Comment