// ==UserScript==
// @name           nicohojo
// @namespace      nico hojo
// @description    niconicodouga hojo script    ver.alpha
// @version        0.02
// @include        http://www.nicovideo.jp/watch/*
// @require        http://test.grn-web.net/js/jquery.js
// ==/UserScript==


var GSDB =[];
var l = unsafeWindow;

(function(){
with (D()) {

function NicoGS_HTML(){this.initialize.apply(this, arguments);}
NicoGS_HTML.prototype = {
	initialize: function(site_url) {
		this.title = document.getElementsByTagName('h1').item(0).getElementsByTagName('a').item(0).innerHTML;
		this.player_top = document.getElementById('WATCHHEADER');
		this.player_under = document.getElementById('WATCHFOOTER');
		this.flv = l.window.document.getElementById('flvplayer');
		this.site_url = site_url;
		this.nicovlink = null;
		this.button = {
		option:[
			{func: 'NicoGS.display' 	, rtval: "'GS_pre_command'" , value: "Pre", color: "#000000" },
			{func: 'NicoGS.cmd_del' 	, rtval: "" , value: "Del", color: "#0000FF" },
			{func: 'NicoGS.come_del' 	, rtval: "" , value: "CDel", color: "#0000FF" },
		],
		color:[
			{func: 'NicoGS.cmd' 	, rtval: "1,'white'" , value: "", color: "#FFFFFF" },
			{func: 'NicoGS.cmd' 	, rtval: "1,'red'" , value: "", color: "#FF0000" },
			{func: 'NicoGS.cmd' 	, rtval: "1,'pink'" , value: "", color: "#FF8080" },
			{func: 'NicoGS.cmd' 	, rtval: "1,'orange'" , value: "", color: "#FFCC00" },
			{func: 'NicoGS.cmd' 	, rtval: "1,'yellow'" , value: "", color: "#FFFF00" },
			{func: 'NicoGS.cmd' 	, rtval: "1,'green'" , value: "", color: "#00FF00" },
			{func: 'NicoGS.cmd' 	, rtval: "1,'cyan'" , value: "", color: "#00FFFF" },
			{func: 'NicoGS.cmd' 	, rtval: "1,'blue'" , value: "", color: "#0000FF" },
			{func: 'NicoGS.cmd' 	, rtval: "1,'purple'" , value: "", color: "#C000FF" },
			{func: 'NicoGS.cmd' 	, rtval: "1,'black'" , value: "", color: "#000000" }
		],
		pcolor:[
			{func: 'NicoGS.cmd' 	, rtval: "1,'white2'" , value: "", color: "#CCCC99" },
			{func: 'NicoGS.cmd' 	, rtval: "1,'red2'" , value: "", color: "#CC0033" },
			{func: 'NicoGS.cmd' 	, rtval: "1,'orange2'" , value: "", color: "#FF6600" },
			{func: 'NicoGS.cmd' 	, rtval: "1,'yellow2'" , value: "", color: "#999900" },
			{func: 'NicoGS.cmd' 	, rtval: "1,'green2'" , value: "", color: "#00CC66" },
			{func: 'NicoGS.cmd' 	, rtval: "1,'blue2'" , value: "", color: "#33FFFC" },
			{func: 'NicoGS.cmd' 	, rtval: "1,'purple2'" , value: "", color: "#6633CC" }
		],
		size:[
			{func: 'NicoGS.cmd' 	, rtval: "2,'small'" , value: "S", color: "#000000" },
			{func: 'NicoGS.cmd' 	, rtval: "2,'medium'" , value: "M", color: "#000000" },
			{func: 'NicoGS.cmd' 	, rtval: "2,'big'" , value: "B", color: "#000000" }
		],
		posi:[
			{func: 'NicoGS.cmd' 	, rtval: "3,'ue'" , value: "\u4E0A", color: "#000000" },
			{func: 'NicoGS.cmd' 	, rtval: "3,'naka'" , value: "\u4E2D", color: "#000000" },
			{func: 'NicoGS.cmd' 	, rtval: "3,'shita'" , value: "\u4E0B", color: "#000000" }
		],
		sonota:[
			{func: 'NicoGS.cmd' 	, rtval: "4,'sage'" , value: "sage", color: "#000000" }
		]};
		this.sub_button = {
		minn:[
			{func: 'NicoGS.cmd' 	, rtval: "3,'ue'" , value: "上", color: "#000000" },
			{func: 'NicoGS.cmd' 	, rtval: "3,'naka'" , value: "中", color: "#000000" },
			{func: 'NicoGS.cmd' 	, rtval: "3,'shita'" , value: "下", color: "#000000" }
		],
		maru:[
			{func: 'NicoGS.cmd' 	, rtval: "3,'ue'" , value: "上", color: "#000000" },
			{func: 'NicoGS.cmd' 	, rtval: "3,'naka'" , value: "中", color: "#000000" },
			{func: 'NicoGS.cmd' 	, rtval: "3,'shita'" , value: "下", color: "#000000" }
		],
		space:[
			{func: 'NicoGS.cmd' 	, rtval: "3,'ue'" , value: "上", color: "#000000" },
			{func: 'NicoGS.cmd' 	, rtval: "3,'naka'" , value: "中", color: "#000000" },
			{func: 'NicoGS.cmd' 	, rtval: "3,'shita'" , value: "下", color: "#000000" }
		]};

	},
	makebt:function(t){
		var div = document.createElement('input');
		div.type = 'button';
	    div.className = "TXT12";
	    div.value = t.value;
	    div.addEventListener('click', function(){ eval(t.func+'('+t.rtval+')')}, false);
	    with(div.style){
	    	color = t.color;
	    	if(t.value == ''){
	    		backgroundColor = t.color;
	    		width = '35px';
	    	}
	    }
		return div;
	},
	MakeHTML:function(){

		var that = this;
		//上枠
		var div = document.createElement('div');
		div.id = 'GS_player_top';
		div.type = 'button';
	    div.className = "TXT12";
	    //div.innerHTML = 'TOP';
		this.player_top.appendChild(div);

		//下枠
		var udiv = document.createElement('div');
		udiv.id = 'GS_player_under';
		udiv.type = 'button';
	    udiv.className = "TXT12";
		$("#WATCHFOOTER").prepend(udiv);

		//send BOX
		var sdiv = document.createElement('div');
		sdiv.id = 'GS_send_box';
	    sdiv.className = "TXT12";
	    sdiv.innerHTML = '\u30B3\u30DE\u30F3\u30C9<input type="text" id="GS_cmd_box" size="20">\u30B3\u30E1\u30F3\u30C8<input type="text" id="GS_come_box" size="70"><input type="button" value="\u9001\u4FE1" id="GS_send" style="width: 50px;">|<input type="button" value="TEST" id="GS_send_test"  style="width: 50px;">';
		udiv.appendChild(sdiv);

		$('#GS_send').click(function(){
			NicoGS.send();
		});
		$('#GS_send_test').click(function(){
			NicoGS.send_test()
		});

		//ステータス枠
		var div = document.createElement('div');
		div.id = 'GS_status';
		div.type = 'button';
	    div.className = "TXT12";
		udiv.appendChild(div);

		//コマンド枠---------------------------
		var div = document.createElement('div');
		div.id = 'GS_command';
		div.type = 'button';
	    div.className = "TXT12";

	    $(this.button.option).each(function(){
	    	div.appendChild(that.makebt(this));
	    });
		udiv.appendChild(div);
	    //カラー
	    $(this.button.color).each(function(){
	    	div.appendChild(that.makebt(this));
	    });
		udiv.appendChild(div);

		//プレミアムカラー
		var pdiv = document.createElement('span');
		pdiv.id = 'GS_pre_command';
		pdiv.type = 'button';
	    pdiv.className = "TXT12";
	    pdiv.style.display = 'none';
	    $(this.button.pcolor).each(function(){
	    	pdiv.appendChild(that.makebt(this));
	    });
	    div.appendChild(pdiv);

	    //size
	    $(this.button.size).each(function(){
	    	log(this);
	    	div.appendChild(that.makebt(this));
	    });
	    udiv.appendChild(div);


	    //posi
	    $(this.button.posi).each(function(){
	    	log(this);
	    	div.appendChild(that.makebt(this));
	    });
	    udiv.appendChild(div);

	    //sonota
	    $(this.button.sonota).each(function(){
	    	log(this);
	    	div.appendChild(that.makebt(this));
	    });
	    udiv.appendChild(div);


		//PAD枠--------------------------------------
		var div = document.createElement('div');
		div.id = 'GS_PAD';
		div.type = 'button';
	    div.className = "TXT12";
		udiv.appendChild(div);




	},
	DL_link:function(dl){

		if (this.nicovlink == null ){
			log('DL_start_1');
			this.nicovlink = document.createElement('span');
			this.nicovlink.id = 'GS_DLLink';
			if(dl[2] == 's'){
				dl[1] = dl[1] + 'as3';
			}
			log(dl);
			this.nicovlink.innerHTML = '&nbsp;&nbsp;<a href="'+ dl[1] +'"  >[DOWNLOAD]</a>';
			if(dl[1].lastIndexOf("low") != -1){
				this.nicovlink.innerHTML += '<img src="'+this.site_url+'/img/flv.gif" width="28" height="15" alt="flv" border="0" /><span style="color:#339900;font-size:70%;font-weight:bold;">エコノミ</span>';
			}else{
				switch(dl[2]){
					case "v":
						var ext = '<img src="'+this.site_url+'/img/flv.gif" width="28" height="15" alt="flv" border="0" />';
					break;
					case "s":
						var ext = '<img src="'+this.site_url+'/img/swf.gif" width="30" height="15" alt="swf" border="0" />';
					break;
					case "m":
						var ext = '<img src="'+this.site_url+'/img/mp4.gif" width="31" height="15" alt="mp4" border="0" />';
					break;
				}
				this.nicovlink.innerHTML += ext;
			}
			log('DL_start_3');

			this.player_top.appendChild(this.nicovlink);
			this.player_top.innerHTML += ' <a target="_blank" href="'+this.site_url+'videodata/'+NicoGS.videoid+'/">詳細</a>';
			log('DL_END');
			$('#GS_DLLink').click(function(){
				return NicoGS.VideoType(dl[2]);
			});
		}
	}
}



function Nicogs_Hojo(){this.initialize.apply(this, arguments);}
Nicogs_Hojo.prototype = {
	initialize: function(){
		this.videoid = location.pathname.match(/[a-z][a-z][0-9]+|[0-9]+/);
		this.site_url = 'http://test.grn-web.net/';
		this.api_getflv = 'http://www.nicovideo.jp/api/getflv/';
		this.api_getpostkey = 'http://www.nicovideo.jp/api/getpostkey/';
		this.thured_id = null;
		this.blocknumber = null;
		this.ticket = null;
		this.ms_url = null;
		this.HTML = new NicoGS_HTML(this.site_url);
		this.HTML.MakeHTML();
		this.cmd_list = ['','','','',''];
		this.come = '';
	},
	cmd:function(n,val){

		this.cmd_list[n] = val;
		var cmd = this.cmd_list.join(' ');
		$('#GS_cmd_box').val(cmd);
		log(this.cmd_list);

	},
	cmd_del:function(){
		$('#GS_cmd_box').val('');
		this.cmd_list = ['','','','',''];
	},
	come_del:function(){
		$('#GS_come_box').val('');

	},
	display:function(t){
		$('#'+t).toggle();
	},
	api_linklist:function(){
		var url = this.api_getflv + this.videoid;
		new l.Ajax.Request(
			url,
			{
				method: 'GET',
				asynchronous: false,
				onComplete:	this.APILink_Responsp
			});
		this.ms_url = GSDB.ms_url;
		this.userid = GSDB.userid;
		this.thread_id = GSDB.thread_id;
		this.video_link = GSDB.video_link;
		log('DLLINK');
		this.HTML.DL_link(this.video_link);

	},
	comeGetParameter:function(){

	},
	comeGetTicket:function(){
		var murl = this.ms_url;
		var xml = '<packet><thread thread="'+this.thread_id+'" version="20061206" res_from="-1" user_id="'+this.userid+'"/></packet>';
		var that = this;
		var options={
			  method:"POST",
			  headers:{"Content-type":"application/xml"},
			  data:xml,
			  url:murl,
			  onload:function(result){
			  	that.Ticket_Response(result);
			  }
		};
		log(options);
		return options;
		/*xhttp(options).next(function(){
			that.comeGetPostkey();
		});*/
	},
	comeGetPostkey2:function(){
		var url = this.api_getpostkey + '?thread='+this.thread_id + '&block_no=' + this.block_no;
		var that = this;
		var options={
			  method:"GET",
			  //headers:{"Content-type":"application/xml"},
			  url:url,
			  onload:function(result){
					that.Postkey_Response(result);
			  }
		};
		log(options);
		return options;
	},
	comeGetPostkey:function(){
		var url = this.api_getpostkey + '?thread='+this.thread_id + '&block_no=' + this.block_no;
		log(url);
		log(this);
		log(t);
		var that = this;
		new l.Ajax.Request(
			url,
			{
				method: 'GET',
				asynchronous: false,
				onComplete:	that.Postkey_Response,
				onFailure:function(e){
					alert(e);
				}
			});
	},
	comePost:function(command,come){
		this.vpos = 100;
		var murl = this.ms_url;
		var xml = '<chat thread="'+this.thread_id+'" vpos="'+this.vpos+'" mail="184 '+command+'" ticket="'+this.ticket+'" user_id="'+this.userid+'" postkey="'+this.postkey+'">'+come+'</chat>';
		log(xml);
		var that = this;
		var options={
			  method:"POST",
			  headers:{"Content-type":"application/xml"},
			  data:xml,
			  url:murl,
			  /* onload:function(result){
			  	that.CPost_Response(result);
			  } */
		};
		log(options);
		//xhttp(options);
		return options;
		this.post_time = parseInt((new Date)/1000);
		this.post_flag = true;

	},
	APILink_Responsp:function(result){
		var txt = decodeURIComponent(result.responseText);
		GSDB.ms_url 	= /&ms=([^&]+)/.exec(txt)[1];
		GSDB.userid 	= /&user_id=([0-9]+)/.exec(txt)[1];
		GSDB.thread_id 	= /thread_id=([0-9]+)/.exec(txt)[1];
		GSDB.video_link = /&url=(http:.+\?(.)=[0-9.]+[low]*)/.exec(txt);
	},
	Ticket_Response:function(result,that){
		var txt = result.responseText;
		this.ticket = /ticket="([0-9a-zA-Z]+)"/.exec(txt)[1];
		var last_res = /last_res="([0-9]+)"/.exec(txt)[1];
		this.block_no = Math.floor((last_res - 0 + 1)/100);
		//that.ticket = this.ticket;
		//that.block_no = this.block_no;
		log('result');
		log(this);
		log(this.block_no);
		log(this.ticket);
	},
	Postkey_Response:function(result){
		var txt = result.responseText;
		this.postkey = /postkey=([0-9a-zA-Z]+)/.exec(txt)[1];
		log(this.postkey);
	},
	CPost_Response:function(result){
		console.log(result.responseText);
		var txt = result.responseText;
		var status = /status="([0-9a-zA-Z]+?)"/.exec(txt)[1];
		if(status != 0){
			alert('メッセージ送信失敗(status='+status+')');
		}
	},
	setVpos:function(t){
		if(t){
			this.vpos = t;
		}else{
			this.vpos = this.HTML.flv.ext_getPlayheadTime();
		}
	},
	send_test:function(){
		var cmd = $('#GS_cmd_box').val();
		var come = $('#GS_come_box').val();
		this.setVpos();

		this.HTML.flv.ext_sendLocalMessage(come,cmd,this.vpos);

	},
	send:function(){
		var cmd = $('#GS_cmd_box').val();
		var come = $('#GS_come_box').val();
		this.setVpos();
		log(this);
		var that = this;

		next(
			function(){
				var options = that.comeGetTicket();
				log(that.block_no);
				return xhttp(options);//.next(that.Ticket_Response);
			}
		).next(
			function(){
				var options = that.comeGetPostkey2();
				return xhttp(options);
			}
		).next(
			function(){
				var options = that.comePost(cmd,come);
				return xhttp(options).next(that.CPost_Response);
			}
		).error(
			function(){alert("send Erorr");}
		);

	},
	getAuthor:function(){
		var options={
				  method:"GET",
				  //headers:{"Content-type":"application/xml"},
				  url:NicoGS.site_url+'api/getdata/author/'+NicoGS.videoid+'?callback=NicoGS.author',
				  onload:function(result){
				  	log(result.responseText);
					  eval(result.responseText);
				  }
			};
		log(options);
		xhttp(options);
	},
	author:function(j){
		var author = document.createElement('span');
		author.innerHTML = ' 投稿者:';
		var link = document.createElement('a');
		link.setAttribute('href',NicoGS.site_url+'author/'+encodeURIComponent(j.author));
		link.setAttribute('target','_blank');
		link.innerHTML = j.author;
		author.appendChild(link);
		document.getElementById('WATCHHEADER').appendChild(author);
	},
    VideoType:function(type){
		//this.TitleCopy(type);
    	switch(type){
	    	case "m":
	    	case "s":
	    		alert('この動画は「' + this.VideoExt(type) + "」です。\n右クリックして ｢対象ファイルを保存｣ から動画を保存してください。");
		    	return false;
		    	break;
	    	case "v":
	    	break;
    	}
    	return true;
    },
    VideoExt:function(val){
    	var ext;
		switch(val){
			case "v":
				ext = 'flv';
			break;
			case "s":
				ext = 'swf';
			break;
			case "m":
				ext = 'mp4';
			break;
		}
		return ext;
    }
};

function log(l){
	//console.log(l);
}

log('start');
var NicoGS = new Nicogs_Hojo();
NicoGS.api_linklist();
NicoGS.getAuthor();

}


/*
	同期通信用関数
*/

// JSDeferred 0.2.2 Copyright (c) 2007 cho45 ( www.lowreal.net )
// See http://coderepos.org/share/wiki/JSDeferred
function D () {


function Deferred () { return (this instanceof Deferred) ? this.init() : new Deferred() }
Deferred.ok = function (x) { return x };
Deferred.ng = function (x) { throw  x };
Deferred.prototype = {
	init : function () {
		this._next    = null;
		this.callback = {
			ok: Deferred.ok,
			ng: Deferred.ng
		};
		return this;
	},

	next  : function (fun) { return this._post("ok", fun) },
	error : function (fun) { return this._post("ng", fun) },
	call  : function (val) { return this._fire("ok", val) },
	fail  : function (err) { return this._fire("ng", err) },

	cancel : function () {
		(this.canceller || function () {})();
		return this.init();
	},

	_post : function (okng, fun) {
		this._next =  new Deferred();
		this._next.callback[okng] = fun;
		return this._next;
	},

	_fire : function (okng, value) {
		var next = "ok";
		try {
			value = this.callback[okng].call(this, value);
		} catch (e) {
			next  = "ng";
			value = e;
		}
		if (value instanceof Deferred) {
			value._next = this._next;
		} else {
			if (this._next) this._next._fire(next, value);
		}
		return this;
	}
};

Deferred.next_default = function (fun) {
	var d = new Deferred();
	var id = setTimeout(function () { d.call() }, 0);
	d.canceller = function () { clearTimeout(id) };
	if (fun) d.callback.ok = fun;
	return d;
};
Deferred.next_faster_way_readystatechange = (!window.opera && /\bMSIE\b/.test(navigator.userAgent)) && function (fun) {
	var d = new Deferred();
	var t = new Date().getTime();
	if (t - arguments.callee._prev_timeout_called < 150) {
		var cancel = false;
		var script = document.createElement("script");
		script.type = "text/javascript";
		script.src  = "javascript:";
		script.onreadystatechange = function () {
			if (!cancel) {
				d.canceller();
				d.call();
			}
		};
		d.canceller = function () {
			if (!cancel) {
				cancel = true;
				script.onreadystatechange = null;
				document.body.removeChild(script);
			}
		};
		document.body.appendChild(script);
	} else {
		arguments.callee._prev_timeout_called = t;
		var id = setTimeout(function () { d.call() }, 0);
		d.canceller = function () { clearTimeout(id) };
	}
	if (fun) d.callback.ok = fun;
	return d;
};
Deferred.next_faster_way_Image = ((typeof(Image) != "undefined") && document.addEventListener) && function (fun) {
	var d = new Deferred();
	var img = new Image();
	var handler = function () {
		d.canceller();
		d.call();
	};
	img.addEventListener("load", handler, false);
	img.addEventListener("error", handler, false);
	d.canceller = function () {
		img.removeEventListener("load", handler, false);
		img.removeEventListener("error", handler, false);
	};
	img.src = "data:,/ _ / X";
	if (fun) d.callback.ok = fun;
	return d;
};
Deferred.next = Deferred.next_faster_way_readystatechange ||
                Deferred.next_faster_way_Image ||
                Deferred.next_default;

Deferred.wait = function (n) {
	var d = new Deferred(), t = new Date();
	var id = setTimeout(function () {
		d.call((new Date).getTime() - t.getTime());
	}, n * 1000);
	d.canceller = function () { clearTimeout(id) };
	return d;
};

Deferred.call = function (f ) {
	var args = Array.prototype.slice.call(arguments, 1);
	return Deferred.next(function () {
		return f.apply(this, args);
	});
};

Deferred.parallel = function (dl) {
	var ret = new Deferred(), values = {}, num = 0;
	for (var i in dl) if (dl.hasOwnProperty(i)) (function (d, i) {
		d.next(function (v) {
			values[i] = v;
			if (--num <= 0) {
				if (dl instanceof Array) {
					values.length = dl.length;
					values = Array.prototype.slice.call(values, 0);
				}
				ret.call(values);
			}
		}).error(function (e) {
			ret.fail(e);
		});
		num++;
	})(dl[i], i);

	if (!num) Deferred.next(function () { ret.call() });
	ret.canceller = function () {
		for (var i in dl) if (dl.hasOwnProperty(i)) {
			dl[i].cancel();
		}
	};
	return ret;
};


Deferred.loop = function (n, fun) {
	var o = {
		begin : n.begin || 0,
		end   : (typeof n.end == "number") ? n.end : n - 1,
		step  : n.step  || 1,
		last  : false,
		prev  : null
	};
	var ret, step = o.step;
	return Deferred.next(function () {
		function _loop (i) {
			if (i <= o.end) {
				if ((i + step) > o.end) {
					o.last = true;
					o.step = o.end - i + 1;
				}
				o.prev = ret;
				ret = fun.call(this, i, o);
				if (ret instanceof Deferred) {
					return ret.next(function (r) {
						ret = r;
						return Deferred.call(_loop, i + step);
					});
				} else {
					return Deferred.call(_loop, i + step);
				}
			} else {
				return ret;
			}
		}
		return (o.begin <= o.end) ? Deferred.call(_loop, o.begin) : null;
	});
};

Deferred.register = function (name, fun) {
	this.prototype[name] = function () {
		var a = arguments;
		return this.next(function () {
			return fun.apply(this, a);
		});
	};
};

Deferred.register("loop", Deferred.loop);
Deferred.register("wait", Deferred.wait);

Deferred.define = function (obj, list) {
	if (!list) list = ["parallel", "wait", "next", "call", "loop"];
	if (!obj)  obj  = (function getGlobal () { return this })();
	for (var i = 0; i < list.length; i++) {
		var n = list[i];
		obj[n] = Deferred[n];
	}
	return Deferred;
};



function xhttp (opts) {
	var d = Deferred();
	if (opts.onload)  d = d.next(opts.onload);
	if (opts.onerror) d = d.error(opts.onerror);
	opts.onload = function (res) {
		d.call(res);
	};
	opts.onerror = function (res) {
		d.fail(res);
	};
	setTimeout(function () {
		GM_xmlhttpRequest(opts);
	}, 0);
	return d;
}
xhttp.get  = function (url)       { return xhttp({method:"get",  url:url}) };
xhttp.post = function (url, data) { return xhttp({method:"post", url:url, data:data, headers:{"Content-Type":"application/x-www-form-urlencoded"}}) };


function http (opts) {
	var d = Deferred();
	var req = new XMLHttpRequest();
	req.open(opts.method, opts.url, true);
	if (opts.headers) {
		for (var k in opts.headers) if (opts.headers.hasOwnProperty(k)) {
			req.setRequestHeader(k, opts.headers[k]);
		}
	}
	req.onreadystatechange = function () {
		if (req.readyState == 4) d.call(req);
	};
	req.send(opts.data || null);
	d.xhr = req;
	return d;
}
http.get   = function (url)       { return http({method:"get",  url:url}) };
http.post  = function (url, data) { return http({method:"post", url:url, data:data, headers:{"Content-Type":"application/x-www-form-urlencoded"}}) };
http.jsonp = function (url, params) {
	if (!params) params = {};

	var Global = (function () { return this })();
	var d = Deferred();
	var cbname = params["callback"];
	if (!cbname) do {
		cbname = "callback" + String(Math.random()).slice(2);
	} while (typeof(Global[cbname]) != "undefined");

	params["callback"] = cbname;

	url += (url.indexOf("?") == -1) ? "?" : "&";

	for (var name in params) if (params.hasOwnProperty(name)) {
		url = url + encodeURIComponent(name) + "=" + encodeURIComponent(params[name]) + "&";
	}

	var script = document.createElement('script');
	script.type    = "text/javascript";
	script.charset = "utf-8";
	script.src     = url;
	document.body.appendChild(script);

	Global[cbname] = function callback (data) {
		delete Global[cbname];
		document.body.removeChild(script);
		d.call(data);
	};
	return d;
};

Deferred.Deferred = Deferred;
Deferred.http     = http;
Deferred.xhttp    = xhttp;
return Deferred;
}// End of JSDeferred


/*****************************************************/


















})();




















