// ==UserScript==
// @name           TweetMonkey
// @namespace      http://d.hatena.ne.jp/elm200
// @description    Tweet anywhere & anytime on the browser
// @include        *
// ==/UserScript==

// TweetMonkey
// Author: Eiji Sakai (eijisakai@gmail.com)
// August 22, 2009
// Distributed under MIT License

(function() {

// === User Settings ===
var username = '(your twitter account)';
var password = '(your password)';
// === end of User Settings ===

/**
 * Base64 encoding
 * http://www.ietf.org/rfc/rfc2045.txt
 */

var Base64 = function() {
	this.initialize();
};

Base64.prototype.initialize = function() {
	this.symbols = [];
	var startChar = "A".charCodeAt(0);
	for(var i = 0; i < 26; i++) {
		this.symbols.push(String.fromCharCode(startChar + i));
	}
	var startChar = "a".charCodeAt(0);
	for(var i = 0; i < 26; i++) {
		this.symbols.push(String.fromCharCode(startChar + i));
	}
	var startChar = "0".charCodeAt(0);
	for(var i = 0; i < 10; i++) {
		this.symbols.push(String.fromCharCode(startChar + i));
	}
	this.symbols.push("+", "/");
	
	this.encodeMap = [];
	for(var i = 0; i < this.symbols.length; i++) {
		this.encodeMap[i] = this.symbols[i];
	}
	
	this.decodeMap = [];
	for(var i = 0; i < this.symbols.length; i++) {
		this.decodeMap[this.symbols[i]] = i;
	}
	this.decodeMap["="] = null;
};

Base64.prototype.encode = function(octets) {
	var i;
	var map = this.encodeMap;
	var encoded = [];
	for (i = 0, len = Math.floor(octets.length / 3) * 3;
		i < len; i += 3) {
		var b0 = octets[i];
		var b1 = octets[i + 1];
		var b2 = octets[i + 2];
		var qs = map[(b0 >> 2) & 0x3f] 
	  				+ map[((b0 << 4) + (b1 >> 4)) & 0x3f]
	  				+ map[((b1 << 2) + (b2 >> 6)) & 0x3f]
	  				+ map[b2 & 0x3f];
		encoded.push(qs);
	}

	switch(octets.length % 3) {
		case 1:
			var b0 = octets[i];
			var qs = map[(b0 >> 2) & 0x3f] 
			       + map[(b0 << 4) & 0x3f]
						 + "=="; 
			encoded.push(qs);
			break;
		case 2:
			var b0 = octets[i];
		  var b1 = octets[i + 1];			
			var qs = map[(b0 >> 2) & 0x3f] 
						 + map[((b0 << 4) + (b1 >> 4)) & 0x3f] 
						 + map[(b1 << 2) & 0x3f] 
						 + "=";
			encoded.push(qs);
		  break;
	}
	
	return encoded.join("");
};

Base64.prototype.decode = function(encoded) {
	if(encoded.length % 4 != 0) {
		throw "encoded.length must be a multiple of 4.";
	}
	
	var decoded = [];
	var map = this.decodeMap;
	for (var i = 0, len = encoded.length; i < len; i += 4) {
		var b0 = map[encoded[i]];
		var b1 = map[encoded[i + 1]];
		var b2 = map[encoded[i + 2]];
		var b3 = map[encoded[i + 3]];
		
		var d0 = ((b0 << 2) + (b1 >> 4)) & 0xff;
		decoded.push(d0);
		
		if(b2 == null) break; // encoded[i + 1] == "="
		
		var d1 = ((b1 << 4) + (b2 >> 2)) & 0xff;
		decoded.push(d1);
		
		if(b3 == null) break; // encoded[i + 2] == "="
		
	  var d2 = ((b2 << 6) + b3) & 0xff;
		decoded.push(d2);
		
	}
	
	return decoded;
};

Base64.prototype.uriEncodedToOctets = function(uriEncoded) {
	var octets = [];
	for(var i = 0, len = uriEncoded.length; i < len; i++) {
		// Note that IE6 doesn't allow an expression like "uriEncoded[i]";
		var c = uriEncoded.charAt(i);
		var b;
		if (c == "%") {
			var hex = uriEncoded.charAt(++i) + uriEncoded.charAt(++i);
			b = parseInt(hex, 16);
		} else {
			b = c.charCodeAt(0);
		}
		octets.push(b);
  }
	return octets;
};

Base64.prototype.encodeStringAsUTF8 = function(utf8str) {
	var uriEncoded = encodeURIComponent(utf8str);
	var octets = this.uriEncodedToOctets(uriEncoded);
	return this.encode(octets);
};

Base64.prototype.octetsToUriEncoded = function(octets) {
	var uriEncoded = [];
	
	for(var i = 0, len = octets.length; i < len; i++) {
		var hex = octets[i].toString(16);
		hex = ("0" + hex).substr(hex.length - 1, 2);
		uriEncoded.push("%" + hex);
  }
	return uriEncoded.join("");
};

Base64.prototype.decodeStringAsUTF8 = function(encoded) {
	var octets = this.decode(encoded);
	var uriEncoded = this.octetsToUriEncoded(octets);
	return 	decodeURIComponent(uriEncoded);
};
var base64 = new Base64();

function main() {
	var id = "twmk_root";
	var root = document.getElementById(id);
	if (root) document.body.removeChild(root);  
	root = document.createElement('div');
	root.id = id;
	root.innerHTML = "<DIV style=\"font-family: 'Arial'; text-align: left; letter-spacing: 0px; position:fixed; right: 10px; bottom: 10px; width: 550px; height:130px; background-color: #fff;  -moz-border-radius: 30px; border: 10px solid #9AE4E8;\"> 	<STYLE type=\"text/css\"> 	.twmk_status-btn { 		BACKGROUND-IMAGE: url(http://s.twimg.com/a/1250203207/images/round-btn.gif); COLOR: #666 	} 	.twmk_status-btn:hover { 		BACKGROUND-IMAGE: url(http://s.twimg.com/a/1250203207/images/round-btn-hover.gif); COLOR: #444 	} 	.twmk_status-btn[disabled] { 		BACKGROUND-IMAGE: url(http://s.twimg.com/a/1250203207/images/round-btn.gif); CURSOR: default; COLOR: #aaa 	} 	</STYLE> 	<DIV style=\"font-size: 14pt; font-weight: normal; position:absolute; left:16px; top:12px; width:250px; height: 20px; letter-spacing: -1px; \">What do you think?</DIV> 	<DIV style=\"font-size: 8pt;  font-weight: normal; position:absolute; left:170px; top:20px; width:150px; height: 20px; \"><A id=\"twmk_insert_url\" href=\"javascript:void(0)\">Add short URL of this site</A></DIV> 	<IMG id=\"twmk_loader\"alt=\"Loader\" src=\"http://s.twimg.com/a/1250203207/images/spinner.gif\" style=\"display:none; color: #ccc; position:absolute; left:510px; top: 13px;\" /> 	<DIV id=\"twmk_remaining\"style=\"font-family: Georgia,serif; font-size: 22pt;  font-weight: bold; color: #ccc; position:absolute; left:490px; top: 3px; width:50px; height: 20px; text-align: right;\">140</DIV> 	<TEXTAREA id=\"twmk_status\" accessKey=\"u\" tabIndex=\"1\" cols=\"40\" autocomplete=\"off\" style=\"font-family: MS UI gothic; sans serif; overflow: auto; resize: none;  font-size: 10pt;  position: absolute;  left:20px; top:39px; width:513px; height:41px\"></TEXTAREA> 	<INPUT disabled=\"true\" class=\"twmk_status-btn\" id=\"twmk_update-submit\" tabIndex=\"2\" type=\"button\" value=\"update\" style=\" position: absolute;  left:423px; top:90px; width:115px; height:32px; PADDING-RIGHT: 14px; PADDING-LEFT: 5px; FLOAT: right; PADDING-BOTTOM: 0px; PADDING-TOP: 0px; BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 14px;  BORDER-BOTTOM-WIDTH: 0px; MARGIN-LEFT: 3px;  BORDER-RIGHT-WIDTH: 0px\" /> 	<DIV id=\"twmk_latest\" style=\" font-size: 8pt;  font-weight: normal; color: #666666; position:absolute; left:17px; top: 92px; width:380px; height:30px; \"></DIV> </DIV> "
	document.body.appendChild(root);

	setTimeout(function() {
		var ta = document.getElementById("twmk_status");
		var btn = document.getElementById("twmk_update-submit");
		var num = document.getElementById("twmk_remaining");
		var loader = document.getElementById("twmk_loader");
		var latest = document.getElementById("twmk_latest");
		var btn_insert_url = document.getElementById("twmk_insert_url");

		ta.addEventListener("keyup", function(e) {
			var text = ta.value;
			var remaining =  140 - text.length;

			if(remaining < 0) {
				btn.disabled = true;
				num.style.color = "#D40D12";
			} else if(remaining < 10) {
				btn.disabled = false;
				num.style.color = "#D40D12";
			}  else if(remaining < 20) {
				btn.disabled = false;
				num.style.color = "#5C0002";
			}  else {
				btn.disabled = false;
				num.style.color = "#ccc";
			}
			num.innerHTML = remaining;
		}, false);

		btn.addEventListener("click", function(e) {
			num.style.display = "none";
			loader.style.display = "block";
			var text = ta.value;
			var base64string = base64.encodeStringAsUTF8(username + ":" + password);
			
			GM_xmlhttpRequest({
				method: 'POST',
				url: 'http://twitter.com/statuses/update.json',
				headers: {
				    'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey/0.8',
				    'Accept': 'application/atom+xml,application/xml,text/xml,text/json',
				    'Authorization': 'Basic ' + base64string,
				    'Content-Type': 'application/x-www-form-urlencoded',	
				},
				data:  'status=' + encodeURIComponent(text),
				onload: function(response) {
					if(response.status != 200) alert("An error occurred!");					
					btn.disabled = true;
					ta.value = "";
					num.innerHTML = "140";
					num.style.display = "block";
					loader.style.display = "none";
					ta.focus();
					latest.innerHTML = "<b>Latest:</b> " + text + " less than 5 seconds ago";
				}
			});
			
		}, false);

		btn_insert_url.addEventListener("click", function(e) {
		     num.style.display = "none";
		     loader.style.display = "block";

		     var originalUrl = location.href;

		     var bitlyUrl = 'http://api.bit.ly/shorten?version=2.0.1';
		     var bitlyAccount = 'twbk';
		     var bitlyApiKey = 'R_909aa7469fa36f70ed850445f19a8470';	
		
         	     var apiUrl = bitlyUrl + '&login=' + bitlyAccount;
		     apiUrl += '&apiKey=' + bitlyApiKey;
		     apiUrl += '&longUrl=' + encodeURI(originalUrl);

		     console.debug('bit.ly URL is ' + apiUrl);

	              // call bit.ly api
	              // thanks to http://www.in-vitro.jp/blog/index.cgi/WebService/20090703_01.htm
	              var shortUrl = '';
		      GM_xmlhttpRequest({
				method: "GET",
				url: apiUrl,
				onload: function(bitlyResponse){
				    console.debug('Receive bit.ly API response.' + bitlyResponse.responseText); 

				    var json = eval('(' + bitlyResponse.responseText + ')');
				    var statusCode = json.statusCode;
				    if(statusCode != 'OK'){
					console.debug('Status code of bit.ly API response is not OK.' + statusCode);
					return;
				    }
				    shortUrl = json.results[originalUrl].shortUrl;
				    ta.value = ta.value + shortUrl ;
				    ta.focus();
				    num.style.display = "block";
				    loader.style.display = "none";
				}
		      });
			
		}, false);

	}, 300);
}

main();

})();