/////////////////// Cycle Class
var Cycle = function(){
    // public
    this.interval = 5;
    this.incycle = false;
    this.cyclesize = 3;

    // output ids
    this.status_id = null;
    this.output_id = null;
    this.output_last_id = null;
    this.rest_id = null;
    
    // callbacks
    this.parse_jsons = function(obj){
	return obj;
    };
    this.json2html = null;
	this.post_add = null;
    
    // data_url (please set by client)
    this.data_url = null;
    
    //private
    Cycle.id ++;
    this._id = Cycle.id;
    Cycle.list[this._id] = this;
    this._cyclecount = this.interval;
    this._jsonbuff = null;
    this._jsons = [];
    this._prediv = null;
    this._divlist = [];
}

// class vars/method
Cycle.id = 0;
Cycle.list = [];
Cycle.cycle = function(id){
    Cycle.list[id]._cycle();
}

// method define
with (Cycle) {

    // initailize

    // please set below properties.
    // this.interval = 5;
    // this.data_url = null;
    
    // callbacks
    // this.parse_jsons
    // this.json2html

    prototype.init_ids = function(status,output,output_last,rest){
	this.status_id = status;
	this.output_id = output;
	this.output_last_id = output_last;
	this.rest_id = rest;
    }
    
    // for oparations
    prototype.interval_reset = function(n){
	this.interval = n;
	this._cyclecount = 0;
    }

    prototype.buffer_reset = function(){
	this._jsonbuff = null;
	this._jsons = [];
	this._cyclecount = this.interval;
    }

    prototype.start = function(){
	this.incycle = true;
	this._cycle();
    }

    prototype.stop = function(){
	this.incycle = false;
    }

    // main functions for cycle (private)

    prototype._cycle = function() {
	if (this.incycle == false) {return;}
	this._cyclecount ++;
	this.printbyid(this.rest_id,this.interval - this._cyclecount + 1);
	if (this._cyclecount > this.interval) {
	    this._cyclecount = 0;
	    this._display();
	}
	if (this.incycle == true) {
	    var myself = this._cycle;
	    setTimeout('Cycle.cycle('+this._id+');', 999);
	} // 1000msec = 1sec
    }

    prototype._display = function(){
	var entry = this._get_json();
	if (entry == null) {return;}
	html = this.json2html(entry);
	this._add_div(html);
    }

    prototype._add_div = function(html){
	if (this._prediv == null){
	    this._prediv = document.getElementById(this.output_last_id);
	}
	var div = document.createElement("DIV");
	div.innerHTML = html;
	
	var outputdiv = document.getElementById(this.output_id);
	outputdiv.insertBefore(div,this._prediv);
	
	this._prediv = div;
    if (this.post_add != null){
        this.post_add(div);
    }
	
	this._divlist.push(div);
	if (this._divlist.length > this.cyclesize) {
	    var deletediv = this._divlist.shift();
	    outputdiv.removeChild(deletediv);
	};
    }

    prototype._get_json = function(){
	if (this._jsonbuff == null){
	    this._charge_buffer();
	}
	if ((this._jsons.length) == 0) {
	    if (this._jsonbuff != null && this._jsonbuff != false){
		this._jsons = this._jsonbuff;
		this._charge_buffer();
		//this._jsonbuff = null;
	    } else {
		return null;
	    }
	}
	return this._jsons.shift();
    }

    prototype._charge_buffer = function(){
	// not reentrant
	if (this._jsonbuff == false){
	    return;
	}
	this._jsonbuff = false; 
	
	var myself = this;
	var callback = function(rtxt){
	    var obj = null;
	    try{
		var obj = myself.str2object(rtxt);
		myself._jsonbuff = myself.parse_jsons(obj);
		myself.printbyid(myself.status_id,"");
	    } catch (e){
		myself._jsonbuff = null; // to re-charge
		myself.printbyid(myself.status_id,"json error");
	    }
	}
	
	this.printbyid(this.status_id,"json accessing...");
	XMLHTTP.get_responseText(this.data_url,callback);
    }

    /// utils
    prototype.str2object = function(str){
	var obj = null;
	eval("obj = " + str);
	return obj;
    }

    prototype.printbyid = function(id,data){
	if (id != null){
	    var elem = document.getElementById(id);
	    elem.innerHTML = data;
	}
    }


}

////// client code sample ////

/*
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=euc-jp">
<script language="JavaScript" src="xmlhttprequest.js"></script>
<script language="JavaScript" src="cycle.js"></script>
<script language="JavaScript">
<!--
// cycle object initialize

var cycleobj = Cycle.new;
cycleobj.date_url = "hatena2.cgi?of=20";
cycleobj.init_ids("update","output","last","wait");
cycleobj.parse_jsons = function(obj){
    return obj['entrys'];
}

cycleobj.json2html = function(jsonobj){
    var hatenaburl = "http://b.hatena.ne.jp/entry/";
    
    ret = '<h2 class="entry-title">' + jsonobj.name +'</h2>';
    ret += '<div id="entry-info">';
    ret += '<p class="body">'+ jsonobj.contents +'</p>';
    ret += '<p class="footnote">';
    ret += '*&nbsp;URL：<a href="'+jsonobj.url+'"target="_blank">';
    ret += jsonobj.url+ '</a></p>';
    ret += '<p class="footnote">';
    ret += '*&nbsp;<a href="'+hatenaburl + jsonobj.url+'"target="_blank">';
    ret += 'はてなブックマーク</a></p>';
    ret += '<p class="footnote">';
    ret += '*&nbsp; カテゴリ :'+jsonobj.category+'</p>';
    ret += '<p class="footnote">';
    ret+= '*&nbsp; キーワード：'+jsonobj.keywords.join(' &nbsp; ')+'</p>';
    ret += '</div>';
    return ret;
}

// interface
function ireset(){
    var interval = parseInt(document.getElementById('interval').value);
    if (interval != NaN){
        cycleobj.interval_reset(interval);
    }
}

function update_query(){
    var category = document.getElementById('cate').value;
    var key = document.getElementById('key').value;
    cycleobj.data_url = "hatena2.cgi?of=20&cate="+category + "&key="+key;
    cycleobj.buffer_reset();
}

function cyclestart(){
    document.body.style.backgroundColor = "white";
    cycleobj.start();
}

function cyclestop(){
    document.body.style.backgroundColor = "#cccccc";
    cycleobj.stop();
}

function changestate(){
    if (cycleobj.incycle){
	cyclestop();
    } else{
	cyclestart();
    }
}

<!--
</script>
</head>
<body onclick="changestate();" onload="cyclestart();">
カテゴリ <input id="cate" type="text" onkeyup="update_query();"></input>
キーワード <input id="key" type="text" onkeyup="update_query();"></input>
更新間隔 <input id="interval" type="text" value="5" onkeyup="ireset();"></input>
<br/>
    残り:<span id='wait'></span> <span id='update'></span>
<br/>

<hr/>
<div>
<div id ='output'>
<span id ='last'> </span>
</div>
</div>
</body>
</html>
*/

