Thursday, July 26, 2007

iTube - YouTube Done My Way

When I began using Greasemonkey about a year ago I was intrigued by some claims that Greasemonkey would cause large scale disruptions of website business models. Greasemonkey is a Firefox plugin that allows you to inject user-defined scripts into a web page after a page has loaded, which gives you the opportunity to change the look and even the contents of a single page or whole site (at least your view of the page/site).

All of the Greasemonkey scripts I've come across so far make small modifications to particular pages or sites, but nothing on the scale of a major face lift for a whole site.

Well, I finally got fed up with the usability and lots of useless crapola on YouTube and decided to redesign the site my way with the help of Greasemonkey. Here are my requirements for "iTube", which is YouTube done my way:
  • Only 2 functions: video search and video watch
  • The home page will contain only the YouTube logo and a search box
  • The search results page will contain only the YouTube logo, search box and scrollable results
  • The watch page will contain only the video title, video player and related videos. The video title will also be a download hyper link. The video player will by default fill most of the browser window and have a clickable list of other sizes to choose from. A stop/play toggle button will be added to the right of the title; when you click stop the player will be replaced by a "Video download stopped" image, which has the effect of truly stopping the video download. (Right now if you click pause the video continues to download in the background, chewing up bandwidth and preventing you from downloading the video as an FLV until after the video has completely downloaded. gggrrr.)
No more promoted videos. No more user comments. No more Presidential Debates. Just videos! So much more usable!

I drew inspiration and some code snippets from the following Greasemonkey scripts:
  • YouTubeLite - showed my how to manipulate CSS to hide what I didn't want to see
  • YousableTube - showed my how resize the video player and turn the video title into a download link
  • Youtube Stop Video Download - showed me how to stop video download by replacing it with an image
Enough talk. Show me the scripts!

To achieve the above requirements I had to create three scripts: one for the YouTube home page, one for the YouTube search results and one for the YouTube watch page.

If you have Greasemonkey installed you can install my iTube scripts by clicking on these links:
or by cut-and-paste of the following:

itubehome.user.js

// ==UserScript==
// @name iTube Home - YouTube Home My Way
// @author Robert Maldon
// @date July 25, 2007
// @namespace http://robertmaldon.blogspot.com/
// @description Get rid of all crapola from the YouTube home page, leaving only the logo and search box
// @include *youtube.com/
// @exclude
// ==/UserScript==

function addGlobalStyle(css) {
var head, style;
head = document.getElementsByTagName('head')[0];
if (!head) {
return;
}
style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = css;
head.appendChild(style);
}

var body = document.getElementsByTagName("body")[0];

function moveToBodyEnd(divName) {
var div = document.getElementById(divName);
var parent = div.parentNode;
parent.removeChild(div);
body.appendChild(div);
}

// Hide all of the baseDiv content, which is most of the crapola
addGlobalStyle('#baseDiv { display: none ! important; }');

// Move the logo and search box to the end of the body
moveToBodyEnd("logoTagDiv");
moveToBodyEnd("searchDiv");


itubesearch.user.js

// ==UserScript==
// @name iTube Search - YouTube Search My Way
// @author Robert Maldon
// @date July 25, 2007
// @namespace http://robertmaldon.blogspot.com/
// @description Get rid of all of the crapola on the YouTube search results page, leaving only the results.
// @include *youtube.com/results?*
// @exclude
// ==/UserScript==

function addGlobalStyle(css) {
var head, style;
head = document.getElementsByTagName('head')[0];
if (!head) {
return;
}
style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = css;
head.appendChild(style);
}

var body = document.getElementsByTagName("body")[0];

function getElementsByClassName(classname, node) {
if (!node) {
node = body;
}
var a = [];
var re = new RegExp('\\b' + classname + '\\b');
var els = node.getElementsByTagName("*");
for (var i = 0, j = els.length; i < j; i++) {
if (re.test(els[i].className)) {
a.push(els[i]);
}
}
return a;
}

function moveToBodyEnd(divName) {
var div = document.getElementById(divName);
if (!div) {
div = getElementsByClassName(divName, body)[0];
}
var parent = div.parentNode;
parent.removeChild(div);
body.appendChild(div);
}

// Hide all of the baseDiv content, which is most of the crapola
addGlobalStyle('#baseDiv { display: none ! important; }');

// Move the results from the baseDiv to the end of the body
moveToBodyEnd("logoTagDiv");
moveToBodyEnd("searchDiv");
moveToBodyEnd("searchSectionHeader");
moveToBodyEnd("mainContent");
moveToBodyEnd("searchFooterBox");

// Add a copy of the searchDiv to the bottom of the page
var searchDiv = document.getElementById("searchDiv");
body.appendChild(searchDiv.cloneNode(true));


itubewatch.user.js

// ==UserScript==
// @name iTube Watch - Watch YouTube Video My Way
// @author Robert Maldon
// @date July 25, 2007
// @namespace http://robertmaldon.blogspot.com/
// @description Get rid of a lot of most of the crapola from the YouTube watch page, turn the title into a download link, create a resizeable and stopable video, and leave the related videos section.
// @include *youtube.com/watch*
// @exclude
// ==/UserScript==

// Combines ideas and code snippets from the following greasemonkey scripts:
// YouTubeLite - http://userscripts.org/scripts/show/4113
// Youtube Stop Video Download - http://userscripts.org/scripts/review/7917
// YousableTube - http://userscripts.org/scripts/show/5906

///////////////////////// START OF USER CONFIGURATION ////////////////////////

// Default video player size (a floating point number or either "fill" or "max")
var videoSize = "fill";

///////////////////////// END OF USER CONFIGURATION /////////////////////////

function addGlobalStyle(css) {
var head, style;
head = document.getElementsByTagName('head')[0];
if (!head) {
return;
}
style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = css;
head.appendChild(style);
}

var body = document.getElementsByTagName("body")[0];

function getElementsByClassName(classname, node) {
if (!node) {
node = body;
}
var a = [];
var re = new RegExp('\\b' + classname + '\\b');
var els = node.getElementsByTagName("*");
for (var i = 0, j = els.length; i < j; i++) {
if (re.test(els[i].className)) {
a.push(els[i]);
}
}
return a;
}

function moveToBodyEnd(divName, index) {
var div = document.getElementById(divName);
if (!div) {
div = getElementsByClassName(divName, body)[index];
}
var parent = div.parentNode;
parent.removeChild(div);
body.appendChild(div);
}

//add inline css that hides the crapola we don't want to see
addGlobalStyle('#baseDiv { display: none ! important; }');

var playerDiv, title, img_stop, img_play, img_video_stopped;

var embed, embedId, embedWidth, embedHeight, embedQuality, embedBgColor, embedName, embedSrc, embedType;

window.addEventListener(
'load',
function() { _init() },
true
);

function _init() {
playerDiv = document.getElementById("playerDiv");
embed = document.getElementById("movie_player");
title = document.getElementById("video_title");
if (!title) {
title = document.getElementById("vidTitle");
}

_initImgs();

//create link 'stop video'
var link = document.createElement('a');
link.setAttribute('href','javascript:void(0)');
link.addEventListener('click', _stopVideoPlz, true);
link.appendChild(img_stop);
title.innerHTML += " ";
title.appendChild(link);

//backup video info
embedId = embed.getAttribute('id');
embedWidth = embed.getAttribute('width');
embedHeight = embed.getAttribute('height');
embedQuality = embed.getAttribute('quality');
embedBgColor = embed.getAttribute('bgcolor');
embedName = embed.getAttribute('name');
embedSrc = embed.getAttribute('src');
embedType = embed.getAttribute('type');

// By default stop the video
_stopVideoPlz();
}

function _stopVideoPlz() {
//remove video
playerDiv.removeChild(embed);

//create link 'play video'
title.removeChild(img_stop.parentNode);//remove link
var link = document.createElement('a');
link.setAttribute('href','javascript:void(0)');
link.addEventListener('click', _playVideoPlz, true);
link.appendChild(img_play);
title.appendChild(link);

//add image 'video stopped'
link = document.createElement('a');
link.setAttribute('href','javascript:void(0)');
link.addEventListener('click', _playVideoPlz, true);
link.appendChild(img_video_stopped);
playerDiv.appendChild(link);
}

function _playVideoPlz() {
//create link 'stop video'
title.removeChild(img_play.parentNode);//remove link
var link = document.createElement('a');
link.setAttribute('href','javascript:void(0)');
link.addEventListener('click',_stopVideoPlz, true);
link.appendChild(img_stop);
title.appendChild(link);

//add video
embed = document.createElement('embed');
embed.setAttribute('id', embedId);
embed.setAttribute('width', embedWidth);
embed.setAttribute('height', embedHeight);
embed.setAttribute('quality', embedQuality);
embed.setAttribute('bgcolor', embedBgColor);
embed.setAttribute('name', embedName);
embed.setAttribute('src', embedSrc);
embed.setAttribute('type', embedType);
playerDiv.removeChild(img_video_stopped.parentNode);//remove link
playerDiv.appendChild(embed);
}


// Images converted by http://software.hixie.ch/utilities/cgi/data/data
function _initImgs() {
img_stop = document.createElement('img');
img_stop.setAttribute('border','0');
img_stop.src = "data:image/gif,GIF89a%3C%00%14%00%E6%00%00%00%00%00%E5%E7%F5%AF%AF%AF%7D%83%99fm%7D%DE%DE%DE%3F%3F%3"
+"F%A3%C3%D1%121O%FF%FF%FF%BD%BD%BDOOO%CC%CC%CC%99%99%99t%7B%90%1F%1F%1F333%EF%EF%EF%B3%D1%E1%94%AE%"
+"BAu%7B%8E%A4%A4%A6%90%94%9F%AD%AF%BAfff%7F%7F%7F%F7%F7%F7%B4%C0%D1%D7%D6%D6%84%87%94%E6%E6%E6%C5%C"
+"5%C5%B4%B5%B6%B7%D6%E6%8F%8F%8F%95%9B%AC%0F%0F%0F%84%93%9Cjv%81%9F%A1%A9%9E%AE%BFw%89%94%B4%B5%BAd"
+"ks%D9%DC%E9%CE%D3%E4%A6%C6%D4%8F%92%97%BA%CB%DD%A7%AB%B8%C5%C6%D2%9F%BB%C8%BA%D6%E6%9A%A4%B4%AE%B3%"
+"C3%85%8F%9Diq%7F%90%94%A1l%80%88%7C%82%94%CC%CC%D7%80%9A%A5%AE%CA%D9%C8%CD%DE%A3%B2%C1y%7F%92et%7B"
+"%AA%C0%D1%97%A4%B2ss%8Cfff%E1%E2%EB%C0%C0%C2%7D~%84%E4%E5%EB%C3%D3%E5%A4%A5%AD%AC%AE%B3%BA%BF%CD%B8"
+"%BA%BD%BF%D1%E3%85%8C%A1%98%B5%C1p%84%8C%AC%C2%D2%80%85%99%99%99%99%9A%B2%BF%D3%D5%E3%A4%A8%B4%B5%B"
+"5%C5%98%9B%A4%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%0"
+"0%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%0"
+"0%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%0"
+"0%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%04%14%00%FF%00%2C%00%00%00%00%3C%00%14%00%00%07%FF%"
+"80%09%11%20%0D%85%86%87%88%89%8A%8B%8C%8CO%11%82%15H%90%09%95%96%97%98%99%9A%9B%9C%99%11%0A%15%11O%"
+"1F%99%1A%05%0C%05%94%9D%AB%AC%9D%0A%0A%0D%AA%82'%3BA%0E%B7%5B%1E%AD%BB%BC%09%1E%85%97%1FUUMHH%179%1"
+"4A%A4%BD%CD%9C%C0%95%05AA%0C%11%D6GX18%D4%CE%DD%98%D0%09%16%3B%1F%11%A7%1F%1F2%3FD%04%1D%9B%05%19%1"
+"9%0C%DE%9D%D0%05%0E%20%E5%01%FA%FAN0)%04%F201%80%20%40%C0%82%04%06%90%CC%CB%04%ED%C9%0E%0E%11%18%04"
+"%40%401%80%0D(%13pT%C8%04%CFR%81%07%18%B6%24%D8b%00%83%BC%0C%243%F4%82%C6%A4J%AA%0F%13%2B%D6%A01%C3"
+"D%83LHHl)%20%08%82%08%06%5B%16D%7C%90%00%C0%A4%07%0AwAk%B2%83%87%12%89%14%11%04%40AC%8A%90%9B81%3C0"
+"%80Pa%C2J%09%01T%EA%A8%14%EB%87%22%17X%F0%D8%A7%8FJ%88%1E%2B%9B%40tB%FA%F5kW%B1%09%C8%B6%82%16a%98%"
+"93%16N%B4%5C%002%24%C4%01!Ita%12%40%E9A%81%AF%1D%23%3C%88%80%D7%40%C0%BDX%13%A8%180%60%C3%12(Ph%D0p1"
+"%E5%C5F%9C%10%0C%40%10)%00d%84%05%10V%17U%BDbe%E6%04Y%A2L%B9%E2C%82%8F%09%3A%1AT%D0%B0%10o3p%0944%1"
+"9q%A3%84s%0BL%04%10%2F%DE%AD%90%AC%04%05%400%D9%AE%82%C3%C2%85%BF%9E(%F8N%9E%13%88G%15%14(._%DE%03%8"
+"8P%82%9E4%9AO%BF%FE%A2G%09%02%01%00%3B";

img_play = document.createElement('img');
img_play.setAttribute('border','0');
img_play.src = "data:image/gif,GIF89a%3C%00%14%00%E6%00%00%00%00%00%C6%E0%F3%94%AE%BAm%83%9Fn%7D%8E%CC%CC%CC1Qr%F0%F1"
+"%FB%18%2C%3F%9F%BB%C8%E6%E6%E6%99%99%99333%AE%CA%D9%E5%E7%F5%B8%BA%BD%DE%DE%DE%3F%3F%3Ffm%7D%84%87%"
+"94%1F%1F%1F%A4%A4%A6%84%93%9C%C3%C4%C6d%7C%98%B4%B5%B6fff%C4%D1%E6%AA%AA%AE-Lm%CD%DD%F0%CA%E3%F6%C0"
+"%C0%C2%7F%7F%7FOOO%F7%F7%F7%80%85%99y%7F%92%EF%EF%EF%99%99%99%0F%0F%0F%FF%FF%FF%8F%8F%8Fjr%80%9F%A1"
+"%A9%D7%D6%D6%C1%CA%DE%121O%A7%C8%D6w%89%94%D7%DD%EF%DB%E1%F3%C0%E1%F1%B8%C5%D6dks%AB%AF%BC%C5%C9%CD"
+"%7D%83%99%98%9B%A4%9B%A8%B6l%80%88t%7B%90%F0%F1%F9%80%9A%A5%7C%82%94%DA%DE%EAet%7B%8F%92%97%98%B5%C"
+"1%C6%E6%F7%B4%B5%BA%A5%B4%CC%85%8C%A1%1B%3BZ%B3%D1%E1%D3%E0%F4qw%8B%F4%F4%FC%AC%AE%B3%C4%D8%EC%1C3K"
+"%C7%CD%D3%BD%BD%BD%CB%CC%D4%7D~%84%A4%A5%AD%90%94%9F%DE%E3%F6u%7B%8Eq%85%8Dhr%7F%85%8F%9D%BC%C5%D6o"
+"%85%A1%C3%D5%E8%90%94%A1%AA%C9%D8%C2%C6%D6%95%9B%AC%A4%A8%B4%DD%E0%EE%9A%B2%BF%C8%DE%F1%00%00%00%00"
+"%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00"
+"%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00"
+"%00%00%00%00%00!%F9%04%04%14%00%FF%00%2C%00%00%00%00%3C%00%14%00%00%07%FF%80)%26%0F%0B%85%86%87%88%"
+"89%8A%8B%8C%8C%0F%26%82%15%20%23)%95%96%97%98%99%9A%9B%9C%9A%26R%15%83%17%99%23%10%05%10%90%9D%AA%A"
+"B%AARR%0B%A9%95%26%2C%40%25%3D%B7%3A%0A%AC%BB%BC)%0A%85%97%17%24%24%1C%20%207_X%258%BD%CD%9D%C0%95%"
+"10%25%25%05%96%07d7%2B%D4%CE%DC%98%D0)V%40%D5%96%3E%0E2%3B%12%13%94%98%26%C6%E3%3A%10%DD%9A%D0%10%3D"
+"%19%94%26-)M%0E3%5E1%12%C6%5D%02A!%84%08%0D)%22%80%90%97%09%DA%03%20%FAR%E0%40%40%20%8893%02%B4T%C8"
+"%04%22B%25%06%1D%8DE%88%A0%22%85%0AH%26J%F6%82V%85D%BC%14Q%A0%BC%E80%60%C3%87%04%2B%16p%F4%98%82A%01"
+"%85%05L%98%A0P%40CI%15%08W%EAL%E1%04%C8%94%26M%B8%241%D0%E5%C8%93%22D%84%2C%1DX0%02B%85%10BD%40%01%A"
+"2%00%83%9E%02wA%BB%C0%E4%86%83%03%5C%9D0%B8%B8%B2%24%00%8D%1F62p%04%F9R!%85%93%0ASP%D0q%B6%194%13%C3"
+"%C2%5C%911c%86%87%00E%60%08%A1%A2%0BS%C7K%0A%01%08%05i%12%80%0Eg%DF%8C%E4%C8Q%C3%C3%E3%00%01%C0d%19%"
+"B2q'f%10!(h%E0%0C%01E%2C%5E%DFR%8CA%92%A5L%03%25%60%04%F0XPa%1D7%A4%DCr%8Fp%22f%8B%85%E7V%AA81%CE%8"
+"DhrX%98%20d%A8R%85%83%91%88%0C%BB%FDz%20%25%BCyN%19%1EU%90R%F9%FCy%05%19B%09%22%D4%A8%BE%FD%FB%8A%1E"
+"%A5%08%04%00%3B";

img_video_stopped = document.createElement('img');
img_video_stopped.setAttribute('border','0');
img_video_stopped.src = "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%01%C2%00%00%01r%08%02%00%0"
+"0%00%E9%B0Mg%00%00%00%03sBIT%08%08%08%DB%E1O%E0%00%00%00%09pHYs%00%00%0B%12%00%00%"
+"0B%12%01%D2%DD~%FC%00%00%00%25tEXtSoftware%00Macromedia%20Fireworks%20MX%202004%87"
+"v%AC%CF%00%00%00%16tEXtCreation%20Time%0003%2F13%2F07%9E%84%EE%00%00%00%18%19IDATx"
+"%9C%ED%DDil%1Cg%FD%C0%F1%99%D9%D9%FBr6v%D6%89%93%B46q%E3P%9A%C4%24%A98%1A%B5%1C%25"
+"%85%D2J%81%16%95%AAJ%00U%01%A1J%A5%0A%EF8%D2%22%E0%05%95%A2%02AT%BCA%BC%E2%0D%A8m%0"
+"E%E4%86VBi%B9D%82%92%94%24%24%AD%ED%9A%D4N%7C%DB%7Bx%EF%9D%FF%8B%DF%3F%93%E9%ECz%BC"
+"%3Eb%AF%DD%EF%E7E4~f%E6%99g%1E%EF%FE%FC%5C3Q%0F%1D%3A%A4%00%00%00%00%00%00%00%00%00"
+"%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%0"
+"0%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%"
+"00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%"
+"00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%"
+"00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00"
+"%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%0"
+"0%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%0"
+"0%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%0"
+"0%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%0"
+"0%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%0"
+"0%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%0"
+"0%00%00%00%00%00%00%00%00%00%00%00%00%00%00%60%E1%18%86a%18%C6t%89U%F7%3A%9CxKKU%CF%"
+"97%98Cn%B7%FA%06k%B4%08U%8D%85%A2-u%01P%9D%AA%AA%CA%FB%BF%D2%B2-%E9%AA%AA%CA%06%16%1"
+"6%91%0Bs%A0%2Fu%010%17%D6%90%3A%5D%8AmW%D5%BD%0E%BB%9C%2F%E7%90%83%ED%60%C30*w%D96%9"
+"C%0B0%DD1%D6%90WuW-7e%3D%DDL%A9%BC%85%CA%ABT%DEK-%BB%9C%EF%A8%C6%92%A3%DE%D0%1A%5D%0"
+"9l%DF%FF%AA%BB*%F7%3A%EC%9A%7F%E6%D6D%87%F1%87%19%0B0%DD1%B5%DCK-75%AB%23%1Dj%A0%F6%"
+"5D%B5%DC%05%8D%E2%E5%850Z%BF%ACM%A4%F946%D5%1B%94j_%D4%CA6%D7%AC2%AF%CC%A1r8%A2r%AF%"
+"F5%1Eg%D5%0E%B5%A6X%EF%AB%96%02W%B2eb%9ERY%5D%0E%B54%AB%5D%B3%FA%8D%60%B9%20%8C%AE%10"
+"%B6%40%60e%DC0%AB%B3j%3Cl%C6%1C%E6%B6%AB%C6K%CC%F3%BE%1Cj%A6%C6%DC%E6%B6k%FE%BF%11%D"
+"4%15%C2(%96%AB%F9%0C%23%3A%0C%B3%02%B3E%18%ADk%D6~_-%13%26%D35p%9C%BB%C03%06%11%87%"
+"C3%AA%EEr%5Ef0%5BU%2F%E1%9Ca-%F7U%B5Bf%9B%DB%DCv%CD%FF7%82%BAB%DF%A1%DE9O%13%DB%8E1%"
+"CDa%97sH%9Am%0E%D6b%3B%8F%A2%DA~t%BE%B5y%DE%F2%8C7eM%AF%3A%C8%3B%FF%02%CC%E1D%A6%EF"
+"%EB%1C%AD%D1%95%C0%3A72%DD%AE%CA%1F%A7%DB%9E%5B%E6s%1B9%9DQ-%05%9En%3A%A8%F6a%D99%D"
+"Fc-'%3A%FC%0A%A6%CB%9F%88%09%60%E5s%98%9B%AAe%DA%0A%2B%09%ADQ%00%98%17%C2(%00%00%00%"
+"00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%"
+"00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%"
+"00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%"
+"00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%"
+"00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%"
+"00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%0"
+"0%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%0"
+"0%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%0"
+"0%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%0"
+"0%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%0"
+"0%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00p%2B%A9%"
+"EF%BD%F7%DER%97%01%F8%A0%F8%EDo%7F%FB%CC3%CFLLL%2CuA%EA%C8%0A%A8%13%DD0%8C%A5.%03%F0"
+"%01b%18%06_%3A%9B%E5%5E'z%B9%5C%5E%EA2%00%1F%14%12%2C%F8%D2Y%AD%80%3A%B1%87QM%D3%22%9"
+"1%88%CF%E7%D34%CD%E3%F1%CC%FF%02%3F%FE%F1%8F%9Fy%E6%99%6008%DB%13%CB%E5r%3E%9F%2F%97%"
+"CB%89D%22%9B%CD%CE%BF%24%C0%92%93f%D7%B2%0E%19%0Bn%05%D4%89%3D%8C%C6%E3q%BF%DF%BF%B0%"
+"D7%98%5Bs%5D%D34%9F%CF%A7(J%20%10%18%18%18%20%92b%05X%01!c%C1%AD%80%3Ay_%18%8D%C5bu%"
+"12Cm%E2%F1x__%DF%B2%AEh%40Y%11!c%C1%AD%80%3A%B9%19F5M%8B%C5b%0B~%81%05%19%3Cv%B9%5C%A"
+"BV%AD%1A%1E%1E%5E%90%22%A1n%F5%F7%F7%1F%3Dz4%9B%CD%0E%0C%0C%2CuY%E6e%DD%BAu%3E%9F%EF"
+"%E1%87%1Fnii%B1%A6%AF%80%90%B1%E0%16%A7NTUu%B9%5C%A9T%CA%E3%F1%18%86%E1r%B9%5C.W%3A%"
+"9D%F6%FB%FD%BA%AE'%93%C9%40%20P.%97u%5D%CF%E5r%85B%C1%E7%F3%B9%5C%AEL%26%E3%F1x%F2%F9"
+"%BC%A2(~%BF%DF%E5r%25%93I%AF%D7k%CB%FCf%18%95%1E%F4t%8E%1D%3Bv%C7%1Dwl%DE%BCy%B6%A5%2"
+"F%97%CB%0BRA%5E%AF%D79%1F%B7%DB%EDv%BB%CD%1F%A7%A6%A6%E6%7FQ%2C%B2%C3%87%0Fg2%99%A5.%"
+"C5%02x%FB%ED%B7%15Ey%E7%9Dw%9E%7F%FE%F9%CA%BD%1F%B40z%E4%C8%91%A7%9Ez%CA%F9%98%E9%EAD"
+"UU%09%B2%9A%A6%C91%AA%AA%CA.%B3%7Df%18%86%AA%AAf%BA%C90%0CM%D3d%5B%D343%073%A5%5C.%AB"
+"%AAjn%C8%B6%14%C6%9An%FE()%95M%C3%9Ba%D4%F9W%FB%DCs%CF)%8A%F2%D5%AF~%F5%C0%81%03%E1p%"
+"D8%B9Flw%B2%20%FDz%87%BFW%B1X%2C%16%8BUN%88MLL%8C%8E%8E%E6r%B9%F9_%1D%8B%60jjj%85u8R%"
+"A9%94%EDCk%18F%B1X%AC%E70%1A%08%04%DCn%B7%7C%9B%B2%D9l%3E%9F%9F%FF7%E8W%BF%FA%D5%B7%B"
+"F%FD%ED%E9%F6%3A%D7I%B1X%EC%EA%EAz%EF%BD%F72%99%8C%AE%EB%12O4M3%E3%A6%9C%E8r%B9%E4%60"
+"%D9.%97%CB%B9%5C%EE%EE%BB%EF%DE%BD%7B%B7%1CV(%14%26%26%26V%AF%5E%ED%F5z%C7%C7%C7e%06%"
+"5BZ%A0%13%13%13%1E%8F%A7X%2C%E6r%B9T*%B5j%D5*%97%CBe%18F%3A%9DVU%D5%E3%F1%A8%AAZ(%14R"
+"%A9T.%97%0B%85B%12I%AD%85%D4%AD%11%DD%B9.%BE%F9%CDo%FE%E9O%7F%3Av%EC%D8%C1%83%07%1Fz%"
+"E8%A1%DA%2B%B1%D2%B3%CF%3E%7B%FC%F8qEQB%A1PWW%97%AD!%9CL%26%BF%FB%DD%EF~%FF%FB%DF%DF%B"
+"0a%835%BD%B2%84%9A%A6%DD~%FB%ED%D3%8D%E7644444%F4%F7%F7%2F%EB%95%BD%1F%1C~%BF%3F%9DN%2"
+"Fu)%16%98%EDC%2B_%E9%BA%5D%23%19%8B%C5%D6%AE%5DkK%1C%1A%1A%9A%F3%9F%B7%7F%FD%EB_%BBv%"
+"EDR%1Coy%C6%3A%19%1A%1A%8AD%222s%23a%BD%5C.K%AF%5CQ%14i%A8%AA%AAZ*%95J%A5%92%C7%E3%91%"
+"80%90%CB%E5%CE%9F%3F%FF%A9O%7DJb%AB%AE%EB%ABW%AFN%24%12%5D%5D%5D%03%03%03.%97kbb%22%18"
+"%0C%26%12%89h4%AA(%CA%D4%D4%94t%E4C%A1P%22%91H%24%12%D2%03%96%9C%7D%3E_4%1Amjjz%F4%D1G"
+"S%A9%94%AD%847%5B%A3%A5R%C9%F9%CF%8E%A6i_%FC%E2%17%7B%7B%7B%0F%1F%3E%7C%F4%E8%D1%A7%9F"
+"~%BA%BD%BD%7D%C6z%94%3F5%B6%9C%8F%1F%3F%FE%C3%1F%FEPQ%94%1F%FD%E8G%BD%BD%BDmmm%E6%AE%B"
+"7%DF~%FB%A9%A7%9EJ%A5RSSS%A9T%CA%EC%A7%CB%E2'%5B%E6k%D6%AC%D14-%97%CB%5D%BDz%F5%1F%FF%F"
+"8%C7%D5%ABW%25%DD%EF%F7%B7%B7%B7%7F%FA%D3%9FV%14%25%12%89%A4R)%19%DD%40%9D%5Bya%D4%F6%"
+"A1%D5u%5D%3A%89KU%1Eg%F2u%EB%EA%EAz%E9%A5%97%02%81%C0%9E%3D%7B%1Ex%E0%01%5B_xV%BE%FE%F"
+"5%AF%9F%3F%7F%5Eq%EC%EC%3A%D7%89%A6i%E3%E3%E3%CD%CD%CD%9A%A6%15%0A%85t%3A%9DL%26%F3%F9"
+"%BC%A6i%BA%AEK%CE%A5RI%D34%19%F1lhh%08%87%C3%5E%AF%D7%EB%F5%F6%F7%F7%87%C3%E1%D1%D1Q%C"
+"9%CA0%8C%7C%3E%3F66%B6%7F%FF%FE%ED%DB%B7%97%CBeit%CB%00%A8%CB%E5%CA%E5rg%CE%9C9v%ECX8%"
+"1C%DE%B8q%A3%04%E8b%B1X*%95%0C%C3(%14%0A%C3%C3%C3%12%8B%ED%B7%60%0D%A3%CE%8B%8A%24%067"
+"55%7D%E1%0B_%B8t%E9%D2%D7%BE%F6%B5%2F%7D%E9K%FB%F7%EF%0F%85B%0Eg%B9%DDnUU%2Bs%B6%B6%10"
+"%CD%BDo%BE%F9%E6%CF~%F6%B3%8F%7F%FC%E3%AF%BD%F6%9A%D4N%A9T%92%5D%95a4%14%0Ai%9A%96J%A5"
+".%5D%BA%F4%FB%DF%FF%DE%AC)%D9%B8r%E5%CA%993g%BE%F5%ADo)%8A%12%0E%87GFF%EA%B6%09%00%D3%"
+"CA%1B%D1%B6%7Dh%3D%1E%CF%7C%A2%D2%AD%26%0D7%F9%AE%99%8D%8FZF!%A6%A6%A6%5Ey%E5%95%CB%97"
+"%2F%3F%FB%EC%B3%92%F2%E2%8B%2F%CA%B7O%CEu%C8%C1%B9N%24%D8I%A7%7B%7C%7C%FC%DE%7B%EF%DD%"
+"B1c%87t%EDs%B9%5C0%18%2C%16%8B%C5b1%16%8B%5D%BBv%AD%B1%B1%F1%D4%A9S%7F%FF%FB%DFw%ED%DA"
+"%95L%26%95%1B%03%A0r%17%F2%E9%9A%98%98%E8%E8%E8H%A5RCCC%D9lV%D7%F5X%2C6%3A%3A%1A%08%04"
+"t%5DollL%A7%D3%0D%0D%0D%B9%5CN%F2%F7x%3C%91HD%E6%A3%AE%5C%B9%E2%F7%FB%2B%8Bz3%8CV%B6%1"
+"9m%A4X%A2%AD%AD%AD%B9%B9%F9%D4%A9S%AF%BE%FA%EA%81%03%07%EE%BF%FF~%E7%3A%AA%CC%D9%1AFe%"
+"EF%9F%FF%FC%E7%23G%8E%7C%F2%93%9F%5C%B7n%9D5%5D%D8%C2%A89%EF6%3C%3C%7C%E2%C4%09%19%16%"
+"B8~%FD%BAD%E4%E6%E6f%AF%D7%3B11%F1%F2%CB%2F%7F%F6%B3%9FU%14%E5%F2%D9%B3%2Fuu9%DC%1D%16"
+"%C1%86%0D%1B%1E%7B%EC1%87%03l%AD%D1%BF%FC%E5%2F%B2q%DF%7D%F7M%972%B7%83e%AF5%3D%95J%9D"
+"%3E%7DZ%B6w%EE%DC)%1B%95)%26%87%83%AD%D9.%AF0*M%0D%5D%D7%E5%0B%25%CD%BD%19%A7%D1_%7B%E"
+"D%B5%A3G%8F%C6%E3%F1%ABW%AF%9AG%BE%F8%E2%8B%07%0E%1CP%16%22%8C%CA%86%F4%FD%3F%F2%91%8F"
+"%1C%3F~%FC%E2%C5%8B%A5R%C9%EB%F5f%B3Y%89%B0%8A%A2%04%83%C1h4%FA%D8c%8F%BD%FE%FA%EB%B9%"
+"5Cn%ED%DA%B5%E7%CE%9D3%0B%AFiZ(%14%92k%05%83A%B3%8D%AC%EBzOO%8F%A6in%B7%7Btt%B4X%2C%0E"
+"%0E%0En%D8%B0A%06%01t%5D%CF%E7%F32T%EA%F7%FB3%99%8C%0C%BC%DA%0A9%8B%D6%A85%8C%8Am%DB%B"
+"6%0D%0F%0F%FF%FA%D7%BF%3Ey%F2%E47%BE%F1%8D%D6%D6%D6%E9%EA%A82%E7%C9%C9Is%3B%9B%CD%FE%F"
+"2%97%BF%7C%E3%8D7%EE%B9%E7%9Eh4j%EE%B2%9Ee%0B%A3%F2%C7AQ%94%BF%FD%EDo.%97%CB%EF%F7%FF%"
+"FB%DF%FF%CEd2%9B7oN%26%93g%CE%9C%E9%EC%EC%8C%C5b%7D%7D%7D%3D%3D%3DMMM%EB%DB%DB%AF%FC%E"
+"2%17%0Ew%87%C5%E1%FC%85%B4%86QsX%CD%DC%AEL%99%DB%C1%95%973%0C%E3%F4%E9%D3%E6(%DE%E9%D3"
+"%A7w%EE%DC)%FF%CA%01%D6m%DB%8FU%0F6%2Fg%BB_%E9%9C%D5m%18-%14%0A%D9lVUU%19%5E%94~d%A1P%"
+"98%AE%C0%97%2F_%FE%DD%EF~W(%14%1E%7C%F0%C1%F5%EB%D7%BF%F0%C2%0B%E5r%F97%BF%F9Me%00u%B8"
+"e%E7%3A1%97%FA%A8%AA%9A%CB%E5%F2%F9%FC%E5%CB%97%0F%1E%3C%B8i%D3%26EQ%26''ed%D30%8Ck%D7"
+"%AE%1D%3At%C8%EDv%97J%A5%600(%BF%5C3%8C%16%8BE%99%A4%92%C6Y2%99%D4u%BDT*%8D%8F%8F%87B%"
+"A1%7C%3E%DF%DF%DF_(%14%1A%1A%1A%D6%ACY%D3%D8%D88%3E%3E.%23%AD%81%40%20%12%89%C8%F0%A8%0"
+"C%1D%CC%D0%1A%9Dm%18U%14%C5%E7%F3%7D%F8%C3%1F%1E%1C%1C%3Cx%F0%E0%03%0F%3C%F0%E5%2F%7F%"
+"D9%F6%DC%A7%AC%B1%AA%CCy%7C%7C%5C6%D2%E9%F4%0B%2F%BC%F0%DF%FF%FE%F7%EE%BB%EF.%97%CBf%B"
+"A%ED%2C%5B%CFBFr3%99L%22%91%F0%F9%7C%DD%DD%DD%8D%8D%8D2%C6%DA%DC%DC%7C%FB%ED%B7%9F%3E%"
+"7D%BA%A9%A9%C9%EDv%BF%FB%EE%BB%F2%99hii%E9%EF%EFw%B8A%DCj3%B6kl%03%2F%D6%1Fe%BB2%C5%8C_"
+"%B5%1Cl%B2%85%C5%AA%C7TU%F5D%87%1B%A9%7C%D8%DA%A1%12%0E%1F%3E%3Cc%CE%B3%25%8D8s1%90%F5"
+"G%DB.EQ%F6%EC%D9%F3%B9%CF%7D%AE%5C.KkT%BA%F62%A5c%CD%F3%7B%DF%FB%DE%D8%D8%98l%EF%DC%B9s"
+"%CB%96-%8A%A2%C8%10%A4%84%D1'%9F%7CR%A99%8C%3A%D7%89L%8B%CB%BC%BC%D7%EBu%B9%5C%A5Ri%D3%"
+"A6M%17%2F%5E%94p%3F%3C%3C%5C(%14%0A%85%82%8C%F2%8D%8D%8D%A9%AA%1A%8DFe%9C%D7%CCY%0E%96~"
+"%BA%FCy%909%25EQ%86%87%87%3D%1EO%A1P%F0%FB%FD%89Dbrr2%9F%CFK%D3U%16%99j%9A%96%CF%E7e%20"
+"%B5j%C4%7F%DFSL%B5%8C%8DV%15%8DF%F3%F9%7CWW%D7%F6%ED%DB%EF%B8%E3%8E%CA%8Aph%8D%BE%FA%EA"
+"%AB%17.%5C%D8%B4iS%E5%F4%82%F5%ACR%A9d-j%26%93)%95J%A9TJBdOOO%5B%5B%5B__%9Fy%80%CB%E5%1"
+"A%19%19imm%CD%E7%F3Rr%AF%D7k.%22%C3%92%B8%15%0D%B1Z%82%DA%12%5E%AE%F2~%1DVRwww%2B7%E2%9"
+"A%7CV%2B%E3%9D%F9%19%AEL%AC%1A1g%95O%22%91%90EZ%F2%B5%9A.%8C%8E%8D%8D%3D%F8%E0%83%8A%A2"
+"%9C8q%22%1E%8F%9B!U%A9%16%3Ag%0C%A3%CEu%22aT%D34%97%CB%95%CDfGFF%A4%A82*%E8%F5z%13%89%8"
+"4%C4Y%E9%E0%A7%D3ii%DE%BA%5C.s%CD%A9%A2(2G%14%8DFs%B9%DC%A9S%A7%24%B6d2%19%E9%A7g2%19%"
+"9F%CF%D7%DF%DF%DF%D3%D3%D3%DA%DA*%81X%D6%03H%A8%F5x%3C%ABV%AD%B2fhus%C1%D3%8C3%F5U%5B%A"
+"3%8A%A2%E4%F3%F9%89%89%89p8%FC%9D%EF%7Cg%CB%96-%B6Y%82%40%20099Y%99%B3%D9%EA%DC%B1cGWWW"
+"oo%EF%EA%D5%ABm%C7X%CF%B2%AD%3F%9D%9A%9A*%14%0A%B9%5CNn%B2P(%98%D3%F4B%AAI%1EQ%900%1A%0"
+"C%06o%C5cZ%A8%5D%24%12%F9%A0M%F4%D9%EEW%BE%0E%D3UB%2C%16%5B%B4%88Y5%1F%99%B3%B5%B5F%F3%"
+"F9%7Ce%81%CD%D0i%8D%A1%CA%FB%FB%01%95%FFV%E5%5C'%B2%E4H%96%B2%FA%7C%3EY%BDs%FD%FA%F5h4"
+"%9AN%A7%07%06%06%D2%E9t8%1C%D64%AD%BF%BF%7Fpp0%97%CB%C94%BD%DC%82%197%CA%E5r%A1P(%16%8B"
+"%3E%9F%EF%E4%C9%93%7D%7D%7D~%BF%DF%EB%F5%E6%F3y%E9%E9%7B%3C%1EY%97*%BDX%C30%1A%1A%1Ad%"
+"0EJ%BA%D4%A9T%CA%EB%F5J%24%B5%15%F2fkT%A2%F5t%B7%AAT%0B%A3%86ad%B3%D9%5C.%B7w%EF%DE%3D"
+"%7B%F6%A8%AA%3A%3E%3En%BBF%B1X%CC%E7%F3%0E%AD%D1%DBn%BB%ED%07%3F%F8%C1%91%23G%06%06%06%"
+"02%81%80%B5%8Ba%3BK%96%89%89%5C.755e%FE%BE%5B%5B%5B%2F_%BEl%BBD%7B%7B%BB%CF%E7SU5%95J%1"
+"9%86%E1%F1x*%235%16S8%1C%AE%87a%C1%1A%3B%E6%0B%C2v%BF%F2upx%90d%CE%11%D3%9A%E2%B0%CB9%1"
+"Fy%D8%D1%1AF%93%C9d%D5%007%5D%18%9DCk%D4%B9Nd%19%93%D7%EB%F5%FB%FD%85B%C10%8CU%ABV%FD%F"
+"4%A7%3F%1D%1D%1D%F5%F9%7C%81%40%40%02W.%97%8BF%A3%1D%1D%1D%D2%3Dw%B9%5C%91HDy%FF8%92%DB"
+"%ED%1E%1A%1Ar%BB%DD%3E%9Fo%F3%E6%CD%C1%60P%D7u%BF%DF%AF%AA%AA%04Sy%F4%20%14%0Ay%BD%5EY%E"
+"7%24%97%F0%F9%7Cn%B7%3B%95J%F9%7C%3EY%3Ed%7Bb%EA%7DSL%CE%8B%2BmaT%1A%DB%1D%1D%1D%07%0E%"
+"1C%88D%22ccc%E6%E2%A4%F7%5D%40%D7%ABN%5EY%A7%98TU%7D%F2%C9'%FF%F0%87%3F%5C%BCx%D1z%8C%F"
+"5%2CM%D3%AC%8F%B2j%9A%26%E5%91e%0A%9F%F9%CCg%D2%E9%B4%F9%26%7F%AF%D7%7B%EF%BD%F7%AEY%B"
+"3%C60%8CT*%95%CDf%8B%C5%A2%C7%E3%A15%BA%B4f%15Fe%DE%C6%DC%AE%9A%A2%DC%88%895%1E%BC%F8%"
+"97%AB%7C%8A%C9a%C6%E6%91G%1E%A9L%AC%7C%C6%B12e%A1%0E%93%D5%2F%B6N%BD%AE%EB%D6%C7%AC%15"
+"E%F1%FB%FDf%ABeppP%26%F4E%B9%5C%96%25%99J%CDa%D4%B9N%24%8CJS%D4%E5r%5D%BAt%E9C%1F%FA%D"
+"0%96-%5B%22%91%C8%F8%F8%B8%AA%AA%E6%A3M%F2%BC%E6%EB%AF%BF%9E%CDf7n%DC%98J%A5%AC%7Dpy%1"
+"E)%93%C9LMM%B5%B5%B5%05%83AY2%25%E9%12%2Be%24T%06!%A5%12%A2%D1%A8Lmy%3C%9Eh4%9AH%24d%8"
+"8%C3)%8C%3A%8F%8D%DA%A2dcc%E3%13O%3C%B1u%EB%D6%91%91%11%5B%87%DAJF%24%9Ds%96%C9%C1G%1F"
+"%7D%F4%E5%97_%3Ew%EE%9C5%DD%DCv%B9%5C%D6%DFe%20%10%90%AEz%B1X%94%D7%40%EC%DB%B7%EF%8D7"
+"%DE%E8%EB%EB%F3z%BD%BBw%EF%8E%C7%E3%8A%A2d2%19%E9%2F%B4n%D8%F0%F0%C3%0F%3B%94%01%8B%20"
+"%97%CB%0D%0D%0D%D5~%7Ce%F8sH%A9%E5%E0%D9%A6%CF%FFr%953%F5%0E%8B%7B%1A%1B%1B%AB%A6%2F%1"
+"Ay%E6%C5%D6%A9%97WrX%0F%FB%C9O~%A2(J%26%93%91%25%8F%8A%A2X%1F%CD%FC%F9%CF%7F%3E%AB0%EA"
+"%5C'%12%0AC%A1P%24%12ioo%1F%1A%1Az%E7%9Dwd%D4%D2%1C6%95H%9A%CF%E7%25%1A%EC%DD%BBW%02%9"
+"F%F9%98%93rc%86%26%1A%8D%CA)%99L%26%12%89D%22%11%C9%CA%2C%83%AE%EB%B2%DE%CB%EF%F7%17%8B"
+"%C5%40%20%20%7D%F4H%24%22kT%E5%F9Q%A70Z%FB%C3%B3%D2%8BO%A7%D3rK%0EG%CA%9D8%E7l%EE%FD%F"
+"C%E7%3F%BF~%FD%FA%13'N%D8%D2%15Eq%BB%DD%D6%0B%B9%DD%EE%C6%C6%C6w%DF%7D7%95J%15%8B%C5%B"
+"6%B66%5D%D7m%CBWGGG%BB%BB%BB%8B%C5%A2%AE%EB%B15k%AC%13P%C0%E2%B0%7D%3B%A4UQ%0F%23%1BU%"
+"C9C%E5%D2%836%7F4%DF%FCk%E3%F5z%EF%BF%FF%FE%1D%3Bv%BC%F2%CA%2B%17.%5C0%87%20e%EF%FE%FD"
+"%FBk%0C%A3%CEu%92%CF%E7%0B%85%C2%E8%E8%A8%A6i%DB%B7o%CFf%B3%86a%04%02%01%19%A6%94%08%9"
+"8%CF%E7%25%F0%C9T%95%AC'%BDv%EDZ%26%931%E7%C7%E4.%24%F2%CAd%BD%D7%EB%D5u%BD%5C.%87%C3a"
+"%97%CBU%2C%16eI%A9%AC%12UU5%14%0A%19%86%E1%F3%F9%A4%A1%3A66%26M7%A70%3A%E3%82'!%BDx%9F"
+"%CF%D7%D3%D3S%CB%13%962%CA%60%CB9%12%89X%1F%A8%B2%EEmoo%7F%E8%A1%87%DE%7C%F3M%5Bz%E5%5"
+"C%DE%DA%B5k%FB%FA%FA%12%89D2%99%9C%9C%9Clmm%5D%BF~%BD%EC%9A%9A%9A%EA%E9%E9%E9%ED%EDU%1"
+"4%C50%8C%ED%DB%B7W%5D4%0B%DCj%B6O%9D%B5%7DT%87%24n%CA%E8%A1%A2(%99LF%A2%AAC%81%1B%1A%1"
+"A%F6%EF%DF%DF%DD%DD%7D%F4%E8Q%C5rk%FB%F6%ED%AB1%8C%3A%D7%89%DF%EF%EF%EC%EC%EC%EF%EF%97"
+"Hm%18%86%0C2%94%CBe%99%FB%92%F6%A3%B41e%1EI%BA%AD%E9t%FA%2B_%F9%8A%99%B34%13%A5q-O%3D%"
+"C9%02%26%99e%92)%2C%B7%DB%1D%0E%87%A5%B3%2Fs%F4%12%A0K%A5%D2%C8%C8H%7F%7F%FF%5Dw%DD%25"
+"%81h%EE%9Dz%E9%C5%DFy%E7%9D%BD%BD%BD%B6qe%07%D2%FB%B6%E5%FC%F8%E3%8Fo%DA%B4I~U%93%93%9"
+"3%B6%BD---%FB%F6%ED%1B%1C%1C%B4%A5W~%22w%ED%DAu%F6%EC%D9%BE%BE%BET*e%AE%09%B5%8E%88%EB"
+"%BA%BE%7B%F7%EE5k%D6%D4%ED%07%17V%F1x%7Cppp%A9K%B1%60%E2%F1x%E5%07%CF%B6t%AF%AEH%A0%F1"
+"%F9%7C%B2%FA%3B%99L%A6R)y%A7%91%F3%89%AD%AD%ADO%3F%FD%B4R%F1%25%DD%BAuk-%0B%9E%1C%EAdr"
+"r%B2%A5%A5%E5%13%9F%F8%C4%B6m%DB%94%1B%EF%C43%CF%92%97%92%94%CBe%E9tJ%245W%26%24%12%09"
+"%B3%BB%2Ca!%16%8Butt%9C%3F%7F%3E%99LJ%7C4%0C%A3T*%85%C3a%99%A7%D2u%5D%9E%89jhh%90%E9%B"
+"5%86%86%86%B1%B11%F9%DF%95%3E%F6%B1%8Fi%9A%26%D7%B5%16%B2%D6%D6%E8%23%8F%3Cr%DF%7D%F7%"
+"8D%8E%8E%FE%F3%9F%FF%94%CB%D4%A8%5C.W%0D%D0%FF%F9%CF%7F%1C%CE%CAf%B3%D69(e%BAU%AF%BA%B"
+"Es%E7%CE%40%20p%F6%ECY%B3il%86%D1%0D%1B6tvv%AE%5E%BD%BAn%3F%B5%B09x%F0%E0s%CF%3D%B72%5"
+"EP%12%0C%06%0F%1D%3Ad%FB%ECM%B7%F0%B0N%14%8B%C5d2%99L%26%A5''%9A%9B%9B%E7%5C%E0%E7%9F%"
+"7F~%C60%EA%5C'%D2f%1C%1C%1C%3Cy%F2%E4%DC%CA%60%BDP2%99%BC%EB%AE%BB%F6%EE%DD%2B%2F%B2%B2"
+"%06eg%E6%C2%A9%AA%11%FF%E6%BAQ%8F%C7%E30%82%E9v%BB%FF%FA%D7%BF%CE%A1%E8%12F%E7%FF%CAB%1"
+"9%A7%A8%BAk%CB%96-mmm%3D%3D%3D%D6%B5%04%AD%AD%ADMMMJ%1D%BF%94%0C%95%B6n%DD%FA%C7%3F%FE"
+"q%A9K%B1%90l%1F%3F%99%0A%AF%DB%CF%E4%BAu%EBd%A2%C6L%D1u%5DV%BC%CC'%DB'%9Ex%C2!%87%C5%A"
+"C%13YE%FE%D6%5Bo%BD%F5%D6%5B%E5%1B%CFG%CD*%079%C5VZ%5D%9A%AC%8A%A2%A8%AA%1A%0E%87o%C5%"
+"7Bsg%1C.%98%91%BC%FF%CAy%B2%AF%F2%CD%FCu%FB7%1F%1FX%D2%AA%A8%E7O%E6%C6%8D%1Bm)%F3%2F%ED"
+"%E3%8F%3F%EE%90%C9%E2%D7%89u%D1%D1%1C%C2%B7%EDQ%A0X%2C%A6%CB%EC%95%FC%1C%8F%C7'''%2B%D"
+"F%A67%1F%D3u%EAk%E7r%B9ZZZl3%F5%C0rT%FFat%F1-%F7%3A%F1%F9%7Cz%3C%1E%FF%DF%FF%FE'%3Fk%9"
+"A%D6%D1%D1%D1%DD%DD%7D%FD%FA%F5%AAk%E9%E7%40%DE%068%E70%1A%0C%067o%DE%5C%CB%207P%FFd%1"
+"0%90%0F%B3%D5r%AF%93x%3C%AE%CB%AB9%AD%FF%8FXkkk%D5W%DE%CDY6%9B%BD%E7%9E%7B%E6%93%C3%F2%"
+"ADb%C0%AA%CE%A7%98%96%C4%B2%AE%93%40%20%D0%DC%DC%AC%2B%8A%D2%D9%D9)%EB4%01%DCR%CB%BD%03"
+"%7B%2B%2C%EB%3A%E9%EC%ECT%14EW%14%A5%A9%A9%E9%A3%1F%FD%E8%B9s%E7%9C_M%02%60%9Ed%8Ck%99%"
+"86%8C%5Bd%99%D6%89%DB%ED%DE%B6m%9B%3C%BF%FB%FF%EF%14%B8%ED%B6%DB%9A%9A%9A.%5C%B8000%400"
+"%05n%11%09%19u%BB%E0iI%2C%BB%3Aq%BB%DD%EB%D6%AD%BB%F3%CE%3B%03%81%80%A4%FC%1FLF%F0Qe%8"
+"8%F7%9E%00%00%00%00IEND%AEB%60%82";
}

// YouTube Video URL: http://www.youtube.com/watch?v=[videoId]
// YouTube Download URL: http://youtube.com/get_video?video_id=[videoId]&t=[tId]

// Get video download URL and make the title into a link
var player = document.getElementById("movie_player");
if (!player) {
return;
}

var tId = player.src.match(/t=([^&]+)/)[1];
var videoId = window.location.href.split("?")[1].match(/v=([^&]+)/)[1];
var videoURL = 'http://www.youtube.com/get_video?video_id=' + videoId + '&t=' + tId;

var vidTitleName = "video_title";
var vidTitle = document.getElementById(vidTitleName);
if (!vidTitle) {
vidTitleName = "vidTitle";
vidTitle = document.getElementById(vidTitleName);
}
vidTitle.innerHTML = '<a href="' + videoURL
+ '" type="video/x-flv" style="text-decoration:none">' + vidTitle.innerHTML + '</a>';
moveToBodyEnd(vidTitleName);

var playerSrc = player.src;
playerSrc.replace("player.swf", "player2.swf");
if (player.src.indexOf("&fs=") == -1) {
playerSrc += "&fs=1";
}
if (player.src.indexOf("&watch2=") == -1) {
playerSrc += "&amp;watch2=1";
}
player.src = playerSrc;
moveToBodyEnd("playerDiv");

// Add video resize links
var watchFull = null;
if (unsafeWindow.fullscreenUrl) {
watchFull = unsafeWindow.fullscreenUrl.replace(/\&s=[^&amp;amp;]*/, "");
} else {
watchFull = unsafeWindow.openFull.toString().match(/(\/watch_fullscreen\?video_id=[^"']*)/)[1];
watchFull = watchFull.replace(/\&s=[^&amp;amp;]*/, "");
}
var linkDiv = document.createElement("div");
linkDiv.id = 'resizeLinks';
linkDiv.innerHTML =
" <a id='resizeLink0' href='javascript:void(null)' style='text-decoration:none' onclick='resizeVideo(1)'>1x</a> \
- <a id='resizeLink1' href='javascript:void(null)' style='text-decoration:none' onclick='resizeVideo(1.25)'>1.25x</a> \
- <a id='resizeLink2' href='javascript:void(null)' style='text-decoration:none' onclick='resizeVideo(1.5)'>1.5x</a> \
- <a id='resizeLink3' href='javascript:void(null)' style='text-decoration:none' onclick='resizeVideo(1.75)'>1.75x</a> \
- <a id='resizeLink4' href='javascript:void(null)' style='text-decoration:none' onclick='resizeVideo(2)'>2x</a> \
- <a id='resizeLink5' href='javascript:void(null)' style='text-decoration:none' onclick='resizeVideo(\"fill\")'>fill</a> \
- <a id='resizeLink6' href='javascript:void(null)' style='text-decoration:none' onclick='resizeVideo(\"max\")'>max</a> \
- <a id='resizeLink7' href='" + watchFull + "' style='text-decoration:none'>full</a> \
- <a id='resizeLink8' href='javascript:void(null)' style='text-decoration:none' onclick='openFull()'>fullscreen</a>";
body.appendChild(linkDiv);

// Function used to resize video
unsafeWindow.resizeVideo = function(aSize) {
var player = document.getElementById("movie_player");
var newH, newW;
if (aSize == "fill" || aSize == "max") {
var ch = document.documentElement.clientHeight;
ch -= aSize == "fill" ? 60 : 32;
var cw = document.documentElement.clientWidth - player.offsetParent.offsetLeft - 7;
var vw = (425 / 318) * ch;
if (vw <= cw) {
newW = Math.round(vw);
newH = Math.round(ch) + 32;
} else {
newW = Math.round(cw);
newH = Math.round((318 / 425) * cw) + 32;
}
} else {
newW = Math.round(425 * aSize);
newH = Math.round(318 * aSize) + 32;
}
player.height = newH;
player.width = newW;

// This is an ugly hack to force a page redraw...
var resizeLinks = document.getElementById("resizeLinks");
resizeLinks.style.display = "none";

// Scroll to video top (doesn't work without timer...)
var vidTitle = document.getElementById("video_title");
if (!vidTitle) {
vidTitle = document.getElementById("vidTitle");
}
var scrollHeight;
if (aSize == "max") {
scrollHeight = player.offsetTop + 8;
} else {
scrollHeight = vidTitle.offsetTop + 5;
}
window.setTimeout("scrollTo(0, " + scrollHeight + ");", 1);

resizeLinks.style.display = "inline";
}

// Resize the video to default size
if (videoSize) {
unsafeWindow.resizeVideo(videoSize);
}

// 'original' styles
var aboutExploreDiv = document.getElementById("aboutExploreDiv");
if (aboutExploreDiv) {
moveToBodyEnd("exploreDiv");
moveToBodyEnd("aboutVidDiv");
}

// new 'beta' styles
var otherVidsDiv = document.getElementById("otherVidsDiv");
if (otherVidsDiv) {
moveToBodyEnd("channelVidsDiv");
moveToBodyEnd("videoDetailsDiv");
moveToBodyEnd("wsWrapper", 1);
}

Thursday, July 19, 2007

Greenspan Comes Out Of Retirement For One More Interest Rate Hike

WASHINGTON, DC—Confirming a rumor that first appeared in March on the FDIC Fan Forum message board, former Federal Reserve chairman Alan Greenspan came out of retirement Tuesday to raise interest rates on federal funds by a quarter of a point.

"You may remember this one from 1989," said Greenspan, barely audible above the roar of an estimated crowd of 20,000 gathered in front of the Marriner S. Eccles Building. "But before I start, I think I'm gonna need [current Federal Reserve chairman] Ben [Bernanke]'s help with this. C'mon up here, Ben."

Greenspan refused to comment on buzz that he was planning a five-nation comeback tour to stabilize international housing markets.

And one Sopranos fan finds closure from the ambiguous series ending.

Saturday, July 14, 2007

Bring iPhone-like navigation to your webapp with iUI

The author of the useful Firebug Firefox add-on has written a small CSS bundle he calls iUI that you can use to give your plain HTML application the look and feel of the native iPhone UI.

Try out the Digg iUI example! Code will soon be available from here.

Google helps theives crack safe

Proving that Google can be useful for every situation in life:

The two burglars had door keys, pass codes and combinations for the safes at a Colorado Springs indoor amusement center.

The burglars... spent an hour and 15 minutes trying to open three safes, apparently unaware that some types require the dial to be turned two or three times.

They finally did a Google search for “how to open a safe” and “how to crack a safe” on a computer in the next room.

...The Google query apparently worked: The burglars haven’t been caught, and they did get about $12,000.

Wednesday, July 04, 2007

WPF development using Java and Eclipse Europa (SWT)

One of the much hyped features of .NET 3 is Windows Presentation Foundation (WPF), a graphical subsystem framework, which includes (amongst other things) a markup language binding called XAML (which is really a rip off of XUL which has been the core of Mozilla/Firefox for years, but that's another story :)).

A couple of days ago the Eclipse foundation released a new version of Eclipse called Europa. It has some cool new features, one of which that caught my eye is a port of the Eclipse native widget set SWT to WPF.

For fun I thought it would be interesting to see how hard it would be to "port" a WPF example to SWT/WPF.

The Application Resources Sample is a really simple "pure" XAML application (i.e. does not mix code with presentation markup) that consists of two XML files:

page1.xaml

<!--<SnippetConsumingPage>-->
<StackPanel
Name="root"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Button Height="50" Width="250" Style="{StaticResource GelButton}" Content="Button 1" />
<Button Height="50" Width="250" Style="{StaticResource GelButton}" Content="Button 2" />
</StackPanel>


and

app.xml

<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="page1.xaml"
>
<Application.Resources>
<Style TargetType="Button" x:Key="GelButton" >
<Setter Property="Margin" Value="1,2,1,2"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Canvas>
<Rectangle
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
x:Name="mainRect"
RadiusX="10" RadiusY="10">
<Rectangle.Fill>
<RadialGradientBrush GradientOrigin="0.5,0.5">
<GradientStop Color="LimeGreen" Offset="0.0" />
<GradientStop Color="Green" Offset="1.0" />
</RadialGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle
Margin="1,1,2,2"
RadiusX="10" RadiusY="10"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Opacity="0.6">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="White" Offset="0.0" />
<GradientStop Color="Transparent" Offset="0.5" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<StackPanel HorizontalAlignment="Center" >
<ContentPresenter Content="{TemplateBinding Content}" Margin="15,10,15,5"/>
</StackPanel>
</Canvas>
<ControlTemplate.Triggers >
<Trigger Property="Button.IsFocused" Value="True" >
<Setter TargetName="mainRect" Property="Fill">
<Setter.Value>
<RadialGradientBrush GradientOrigin="0.5,0.5">
<GradientStop Color="LightSkyBlue" Offset="0.0" />
<GradientStop Color="RoyalBlue" Offset="1.0" />
</RadialGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
</Application>


which displays two buttons that change color when focus changes.

So, what does the equivalent SWT/WPF look like?

SWT/WPF currently has support for configuring Resources via XAML, but top level Controls like Button, Label, etc must be defined using the equivalent SWT widgets.

Prerequisties
Refactor app.xaml slightly so that the style markup is wrapped by a ResourceDictionary instead of Application.Resources

ButtonStyle.xaml

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Button">
<Setter Property="Margin" Value="1,2,1,2"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Canvas>
<Rectangle
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
x:Name="mainRect"
RadiusX="10" RadiusY="10">
<Rectangle.Fill>
<RadialGradientBrush GradientOrigin="0.5,0.5">
<GradientStop Color="LimeGreen" Offset="0.0" />
<GradientStop Color="Green" Offset="1.0" />
</RadialGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle
Margin="1,1,2,2"
RadiusX="10" RadiusY="10"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Opacity="0.6">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="White" Offset="0.0" />
<GradientStop Color="Transparent" Offset="0.5" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<StackPanel HorizontalAlignment="Center" >
<ContentPresenter Content="{TemplateBinding Content}" Margin="15,10,15,5"/>
</StackPanel>
</Canvas>
<ControlTemplate.Triggers >
<Trigger Property="Button.IsFocused" Value="True" >
<Setter TargetName="mainRect" Property="Fill">
<Setter.Value>
<RadialGradientBrush GradientOrigin="0.5,0.5">
<GradientStop Color="LightSkyBlue" Offset="0.0" />
<GradientStop Color="RoyalBlue" Offset="1.0" />
</RadialGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>


and the Java code to create and layout the buttons would look something like:


package robertmaldon;

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class SwtWpfExampleMain {

public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setSize(300, 150);
shell.setLayout(new RowLayout());

final Button button1 = new Button(shell, SWT.FLAT);
button1.setData("ResourceDictionary", "C:\\workspace\\swt-wpf-example\\config\\ButtonStyle.xaml");
button1.setText("Button 1");
button1.setSize(250, 50);
button1.setBounds(0, 0, 250, 50);

final Button button2 = new Button(shell, SWT.FLAT);
button2.setText("Button 2");
button2.setSize(250, 50);
button2.setBounds(0, 50, 250, 50);

shell.open();

// Set up the event loop.
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
// If no more entries in event queue
display.sleep();
}
}

display.dispose();
}
}


In the example above I XAMLize the style of the first button and leave the second button with the default style.

The trick here is that Button eventually extends org.eclipse.swt.widgets.Widget that supports XAML loading via the setData(key, value) method. The key can be one of two strings:
  • "XAML" - the value must be a String containing XAML markup
  • "ResourceDictionary" - the value is a URI to a XAML file
SWT/WPF support is a bit raw right now but shows a lot of promise.