/* Copyright (c) 2006, 2007 Lev Walkin <vlm@lionet.info>. All rights reserved.
 * You may copy and modify this script as long as the above copyright notice,
 * this condition and the following disclaimer is left intact.
 * This software is provided by the author "AS IS" and no warranties are
 * implied, including fitness for a particular purpose. In no event shall
 * the author be liable for any damages arising in any way out of the use
 * of this software, even if advised of the possibility of such damage.
 * $Date: 2007-11-02 15:01:30 -0700 (Fri, 02 Nov 2007) $
 */

if(!window.$JCA) {
  var $JCA = [];
  var $JCLT = {
	leaveComment: 'Leave a comment',
	nameLabel: 'Your name:',
	emailLabel: 'Send replies to email:',
	emailNote: '(if provided, email will not be displayed or shared)',
	ratingLabel: 'Rating:',
	commentLabel: 'Comment:',
	submit: 'Submit comment',
	cancel: 'Cancel',
	tooShort: 'Your message is too short',
	tooLong: 'Message size should not exceed 1500 bytes',
	junkCtl: 'Junk control',
	byVotes: 'by',
	isJunkVote: 'Is this inappropriate junk or SPAM message?'
  };
  var $JCL = window.JSCC_Translate || function(t) {
		return (window.$JCLTL ? $JCLTL[t] : false) || $JCLT[t] || t;
		}
}

if(!window.JSKitLib) JSKitLib = {};

JSKitLib.getOuterHTML = function(node) {
    var clone = node.cloneNode(true);
    var parent = document.createElement('div');
    parent.appendChild(clone);
    var ihtml = parent.innerHTML;

    // ff converts sp characters inside of href to hex ascii
    var ihtmlHref = ihtml.match(/href\s*=\s*"[^"]*(%7B|%7D)[^"]*"/g) || [];
    for (var i=0; i< ihtmlHref.length; i++) {
      var a = ihtmlHref[i];
      var b = a.replace(/%7B/, '{');
      b = b.replace(/%7D/, '}');
      ihtml = ihtml.replace(a, b);
    }
    return ihtml;
};

JSKitLib.getElementsByClass = function(node, searchClass, tag) {
    var classElements = [];
    node = node || document;
    tag = tag || '*';
    var tagElements = node.getElementsByTagName(tag);
    var regex = new RegExp("(^|\\s)" + searchClass + "(\\s|$)");
    for (var i=0, j=0; i < tagElements.length; i++) {
      if (regex.test(tagElements[i].className)) {
        classElements[j] = tagElements[i];
        j++;
      }
    }
    return classElements;
};

JSKitLib.preloadImg = function(imgURL) { 

  if (!JSKitLib.preloadImgList) JSKitLib.preloadImgList = {};

  if (!JSKitLib.preloadImgList[imgURL]) {
    (new Image()).src = imgURL; 
    JSKitLib.preloadImgList[imgURL] = true;
  }
};

var JSKitGlobal = function() {

  this._appAvailable = {};
  this._appObjects = {};  // Specific objects of an application type 
  this._appObjectActions = {}; // app.object.actions

  this._isAppAvailable = function(app) {
    return (this._appAvailable[app]) ? true : false;
  }
    
  this.isRatingsAppAvailable = function() {
    return this._isAppAvailable('ratings');
  }

  this.isCommentsAppAvailable = function() {
    return this._isAppAvailable('comments');
  }

  this._setAppAvailable = function(app) {
    this._appAvailable[app] = true;
    /* index this app */
    this.indexAppObjects(app);
    /* execute any queued actions */
    this.executeAppObjectActions(app);
  }

  this.setRatingsAppAvailable = function() {
    this._setAppAvailable('ratings');
  }
  this.setCommentsAppAvailable = function() {
    this._setAppAvailable('comments');
  }

  this.indexAppObjects = function(app) {

    if (app == 'ratings') {
      var appArray = $JRA;
    } else if (app == 'comments') {
      var appArray = $JCA;
    } else {
      alert('Attempt to index invalid app type');
      return;
    }

    for (var i=0; i < appArray.length; i++) {
      // Check that it's not standalone
      if (appArray[i].isStandalone()) {
        continue;
      }
      var uniq = appArray[i].uniq;
      if ( ! this._appObjects[uniq] ) {
        this._appObjects[uniq] = {};
      }
      if ( ! this._appObjects[uniq][app]) {
        this._appObjects[uniq][app] = [];
      }
      this._appObjects[uniq][app].push(appArray[i]);
    }
  }

  this.executeAppObjectActions = function(app) {
    if (this._appObjectActions[app]) {
      for (var i=0; i < this._appObjectActions[app].length; i++) {
        var uniq = this._appObjectActions[app][i].uniq;
        if (this._getAppObject(app, uniq)) {
          this._appObjectActions[app][i].action();
        }
      }
    }
  }

  this._getAppObject = function(app, uniq) {
    if (this._appObjects[uniq] && this._appObjects[uniq][app]) {
      return this._appObjects[uniq][app][0];  // Return only the first
    }
    return null;
  }

  this.getCommentsAppObject = function(uniq) {
    return this._getAppObject('comments', uniq);
  }

  /* Returns a Ratings Object */
  this.getRatingsAppObject = function(uniq) {
    return this._getAppObject('ratings', uniq);
  }

  this.copyRatingsAppObject = function(uniq, node) {
    if ( ! this.isRatingsAppAvailable()) {
      return;
    }

    var oldObj = this.getRatingsAppObject(uniq);
    var newObj = oldObj.clone(node, { 'view':'user', 'commentprompt':'no', 'menu':'no'  } );

    return newObj;
  }

  this._tryAppObjectAction = function(app, uniq, action) {
    if (this._isAppAvailable(app)) {
      if (this._getAppObject(app, uniq)) {
        action();
      }
    } else {

      if ( ! this._appObjectActions[app]) {
        this._appObjectActions[app] = [];
      }
      this._appObjectActions[app].push( { 'uniq' : uniq, 'action' : action } );
    }
  }

  this.tryRatingsAppObjectAction = function(uniq, action) {
    this._tryAppObjectAction('ratings', uniq, action);
  }

  this.tryCommentsAppObjectAction = function(uniq, action) {
    this._tryAppObjectAction('comments', uniq, action);
  }

}

/* Singleton-like handler */
JSKitGlobal.getInstance = function() {
  if ( ! window.JSKitGlobalInstance) {
    JSKitGlobalInstance = new JSKitGlobal();
  }
  return JSKitGlobalInstance;
}

/* JSKitGlobal  object */
$JSKitGlobal = JSKitGlobal.getInstance();

/* JavaScript Comment Class */
new JSCC(); 

/* JSKitGlobal : App is ready */
$JSKitGlobal.setCommentsAppAvailable();


function JSCC() {
	/* Find the target DIV for all the blog comments */
	this.jcaIndex = $JCA.length;
	$JCA.push(this);
	this.get = function(id) { return document.getElementById(id); }
	this.cr = function(tag) { return document.createElement(tag); }
	var wl = window.location;
	this.uriDomain = (wl.protocol.substr(0, 4) != 'http' ? 'http:' : '')
				+ '//js-kit.com';
	this.uri = this.uriDomain + '/comment';
	this.fieldDfl = {};
	this.TC = {};
	this.tmpID = 0;
	this.pathOverride = "";
	this.uniq = wl.pathname;
	this.cmtById = {};
	this.objById = {};
	this.utmpl={};
	this.config = {};
	this.gen = 0;
	this.ctag = null;
	this.czidx = 300;

	this.isStandalone = function() {
		return (this.config.standalone == 'yes');
	}

	this.scoringEnabled = function() {
		return (this.config.scoring != 'no');
	}

	var idName = "js-kit-comments";

	var target = arguments.length ? arguments[0] : this.get(idName);

	if(target) {
		this.labelHTML = target.getAttribute("label");
		var path = target.getAttribute("path");
		if(path) {
			path = String(path);
			var ar = path.match(/^https?:\/\/[^\/]+(.*)/);
			if(ar) this.pathOverride = ar[1];
			else this.pathOverride = path.replace(/^([^\/]+)/,
					wl.pathname + "/$1");
			path = this.pathOverride;
		} else { path=wl.pathname; }
		this.uniq = target.getAttribute("uniq") || path;

		var cn = target.childNodes;
		for(var n=0;n < cn.length;n++)
			this.utmpl[cn[n].className] = JSKitLib.getOuterHTML(cn[n]);
		if(cn.length) target.innerHTML = "";
		target.style.display = "block";
		target.style.visibility = "visible";
		var utsc = this.utmpl['js-singleComment'];
		if(utsc) this.dtComment = utsc;

		// Override
		var jovs = window.JSKit$Override;
		if(jovs) {
		  for(var i in jovs) {
			var fName = jovs[i][0];
			var func = jovs[i][1];
			this[fName] = func;
		  }
		}
	} else {
		if(!document.body)
			alert("Enclose the script in a <BODY></BODY> tag!");
		var els = document.body.getElementsByTagName(idName);
		var oWay = false;
		if(els && els.length) oWay = true;
		else els = document.body.getElementsByTagName("div");
		if(els && els.length) {
			$JCA.shift();
			for(var i=0; i < els.length;i++)
			  if(oWay || els[i].className.match(/js-kit-comments/))
				new JSCC(els[i]);
			if($JCA.length) return;
			$JCA.push(this);
		}
		document.write('<div id="'+idName+'"></div>');
		this.idcr = true;
		target = this.get(idName);
	}
	target.className = idName;
	target.id = "";

	// Handling user configuration settings
	this.config = (function() {
		var cf = {};
		for(var i = 0; i < arguments.length; i++) {
			var arg = arguments[i];
			if(typeof(arg) == 'string') arg = [arg];
			var name = arg[0];
			var value = target.getAttribute(name);
			if(arg.length > 1) {
				if(typeof(arg[1]) == 'number') {
				    if(value) {
					var n = parseInt(value);
					if(isNaN(n) || n < 0) {
						if(value == "no")
							value = 0;
						else
							value = arg[1];
					} else {
						value = n;
					}
				    } else
					value = arg[1];
				} else if(typeof(arg[1]) == 'object') {
					for(var j=arg[1].length; j; j--)
						if(arg[1][j-1] == value)
							break;
					if(!j) value = arg[1][j];
				} else {
					if(!value) value = arg[1];
				}
			}
			cf[name] = value;
		}
		return cf;
	})(
		['standalone', 'no'],
		['scoring', 'yes'],
		['paginate', 50],
		['backwards', 'no'],
		['domain', wl.host],
		['sort', ['date','karma','name','status','rating']],
		['thread', ['yes','no']],
		'adminBgColor',
		'moderate'
	);
	this.config.domain = this.config.moderate || this.config.domain;
	if(this.config.paginate <= 0) this.config.paginate = 200;
	if(target.getAttribute('backwards'))
		this.backwards = (this.config.backwards == 'yes');
	else if(target.getAttribute('paginate'))
		this.backwards = !!this.config.paginate;
	else
		this.backwards = false;
	this.preq = {
		srt:this.config.sort,
		ord:(this.backwards?'desc':'asc'),
		thr:this.config.thread,
		sp: 1, pn: 5, ps: this.config.paginate };
	if(!this.preq.ps) this.preq.ps = 100;
	this.preq.pn = Math.round(50 / this.preq.ps);
	if(this.preq.pn < 2) this.preq.pn = 2;

	var self = this;
	self.target = target;
	self.setDefaultField = function(name,value){self.fieldDfl[name]=value;}

	this.server = function(ext, data) {
		var sc = this.cr("script");
		sc.setAttribute("charset", "utf-8");
		sc.src = this.uri + ext + "?ref=" + wl.protocol + '//'
			+ encodeURIComponent(
				this.config.domain
				+ (this.pathOverride || wl.pathname))
			+ (this.config.moderate?'&mod':'')
			+ (this.idcr?'&idcr':'')
			+ "&" + data;

		this.target.appendChild(sc);
		return false;
	}

	this.getpages = function(sp, ap) {
		var preq = self.preq;
		if(!sp) sp = preq.sp;
		self.loading = (new Date()).valueOf();
		self.server("s-data.js", "jx="+self.jcaIndex
			+ "&gen=" + self.gen
			+ "&srt=" + preq.srt
			+ "&ord=" + preq.ord
			+ (preq.thr=='yes'?'':"&prs=flat")
			+ "&sp="+ sp + "&pn="+ preq.pn + "&ps="+ preq.ps
			+ (ap?ap:''));
	}
	this.getpages();
}

document.write('<style type="text/css">'
+ ".js-OldComments { margin-bottom: 1px; }"
+ ".js-LeaveComment { margin: 3pt 0; }"
+ ".js-CreateComment { display: none; }"
+ ".js-CreateCommentBg { margin: 1em; padding: 0.5em; border: solid 1px #c0c0c0; text-align: left; float: left; }"
+ ".js-PageNavTop { margin-bottom: 3px; } "
+ ".js-PageNavBottom { margin-top: 3px; } "
+ ".js-commentFieldSubject { font-weight: bold; margin-bottom: 5px; }"
+ ".js-commentFieldLabel { margin-top: 5px; }"
+ ".js-commentFieldNote { font-family: Verdana; font-size: 7pt; color: #808080; }"
+ ".js-siteAdmin { font-weight: bold; }"
+ ".js-singleComment { font-size: 8pt; font-family: Verdana, Helvetica; border: solid 1px #c0c0c0; text-align: left; margin-bottom: -1px; }"
+ ".js-singleCommentBg { padding: 0.3em; position: relative; }"
+ ".js-singleCommentINFO { color: #808080; float: right; padding: 3px; margin-left: 2em; text-align: right; }"
+ ".js-singleCommentDate { font-size: 7pt; }"
+ ".js-singleCommentKarma, .js-singleCommentOrigin { position: absolute; bottom: 0.3em; font-size: 7pt; color: #808080; }"
+ ".js-singleCommentKarmaScore { display: none; }"
+ ".js-commentControl { float: left; margin-right: 2em; }"
+ ".js-poweredBy { margin-top: 2pt; margin-right: 2pt; color: #808080; font-size: 7pt; font-family: Verdana, Helvetica; }"
+ ".js-poweredBy A { text-decoration: none; color: #8080a0 }"
+ ".js-antispamBy { text-align: right; }"
+ ".js-Progress { position: absolute; visibility: hidden; right: 5px; top: 5px; width: 15px; height: 15px; }"
+ ".js-SettingsWindow { position: absolute; margin: 2px; padding: 0.3em; border: solid 1px #cccccc; color: #404040; }"
+ "</style>");
(function(v){v=parseFloat(v.split("MSIE")[1]);
if(v>=5.5)document.write('<style>.js-singleComment{zoom:1} .js-singleCommentBg{zoom:1}</style>');})
(navigator.appVersion);
if(navigator.userAgent.indexOf("Opera")>=0)
document.write("<style>wbr:after{content:\"\\00200B\"}</style>");
else
document.write("<style>.js-singleCommentTEXT{word-wrap:break-word}</style>");


JSCC.prototype.addChild = function(to, what) {


	if (typeof(to) != 'object')
		return;

	if(arguments.length == 3 && arguments[2])
		to.insertBefore(what, to.firstChild);
	else
		to.appendChild(what);
}
JSCC.prototype.html = function(text) {
	var div = this.cr("div");
	div.innerHTML = text;
	var ch = div.firstChild;
	div = null;
	return ch;
}
JSCC.prototype.a = function() {
	var a = this.cr("a");
	a.href = "javascript:void(0);";
	for(var text = '', i = 0; i < arguments.length; i++)
		text += arguments[i];
	a.innerHTML += text;
	return a;
}

JSCC.prototype.div = function(id) {
	var div = this.cr("div");
	for(var i = 1; i < arguments.length; i++) {
		var arg = arguments[i];
		switch(typeof(arg)) {
		case "string":
			this.addChild(div, document.createTextNode(arg));
			break;
		case "undefined":
			break;
		default:
		case "object":
			if(!arg) break;
			this.addChild(div, arg);
			break;
		}
	}
	if(id) {
		div.className = id;
		var idText = String(id);
		if(idText.charCodeAt(3) < 91)
			this.TC[idText] = div;
	}
	return div;
}

JSCC.prototype.dtComment
 = '<div class="js-singleComment">'
 + '<div class="js-singleCommentBg">'
 + '<div class="js-singleCommentINFO">'
   + '<div class="js-singleCommentName">{Name}</div>'
   + '<div class="js-singleCommentDate">{Date}</div>'
   + '<div class="js-singleCommentControls">'
     + '<span class="js-singleCommentReplyable">[<a class="js-singleCommentReply">{Label:reply}</a>]</span>'
     + '<span class="js-singleCommentDeletable"> [<a class="js-singleCommentDelete">{Label:delete}</a>]</span>'
   + '</div>'
 + '</div>'
 + '<div class="js-singleCommentRating" style="display:none;"></div>'
 + '<div class="js-singleCommentText">{Text}</div>'
 + '<div class="js-singleCommentKarma">{Label:Like this comment?}'
     + ' [<a class="js-singleCommentKarmaY">{Label:yes}</a>]'
     + ' [<a class="js-singleCommentKarmaN">{Label:no}</a>]'
 + ' <span class="js-singleCommentKarmaScore">({Label:Score}:'
 + ' <span class="js-singleCommentKarmaValue">0</span> {Label:byVotes}'
 + ' <span class="js-singleCommentKarmaVoters">0</span>)'
 + '</span>'
 + '</div>'
 + '<br clear="all" />'
 + '</div>'
 + '</div>'
;

JSCC.prototype.dtCreate
 = '<div class="js-CreateComment">'
 + '<div class="js-CreateCommentBg">'
 + '<div class="js-commentFieldSubject">{Label:leaveComment}</div>'
 + '<div class="js-commentFieldLabel">{Label:nameLabel}</div>'
 + '<div><input name="js-CmtName" SIZE=32 /></div>'
 + '<div class="js-commentInputEmail">'
 + '<div class="js-commentFieldLabel">{Label:emailLabel}'
   + '<div class="js-commentFieldNote">{Label:emailNote}</div>'
 + '</div>'
 + '<div><input name="js-CmtEmail" type="email" SIZE=32 /></div>'
 + '</div>'
 + '<div class="js-commentFieldLabel js-commentRatingDisplay">{Label:ratingLabel}</div>'
 + '<div class="js-commentFieldRating js-commentRatingDisplay"></div>'
 + '<div class="js-commentFieldLabel">{Label:commentLabel}</div>'
 + '<div><textarea name="js-CmtText" ROWS=4 COLS=32></textarea></div>'
 + '<div><input type=submit name="js-Cmtsubmit" VALUE="{Label:submit}">'
 + '<input type=reset name="js-Cmtcancel" VALUE="{Label:cancel}"></div>'
 + '<div class="js-poweredBy js-antispamBy">(<a href="http://js-kit.com/comments?wow">Powered by JS-kit</a>)</div>'
 + '<div class="js-poweredBy js-antispamBy">(Spam filtering by <a href="http://akismet.com/">Akismet</a>)</div>'
 + '</div><br clear="all" /></div>'
;

JSCC.prototype.mapComments = function(cmts) {
	var cn = cmts.childNodes;
	if(cn) {
		var clen = cn.length;
		var cmt, id, obj;
		for(var i = 0; i < clen; i++) {
			cmt = cn[i];
			id = cmt.id;
			if(id && (obj = this.objById[id])) {
				this.fixComment(cmt, obj);
			} else {
				this.mapComments(cmt);
			}
		}
	}
}

JSCC.prototype.mapClass2Object = function(ctl, e) {

	var cName = e.className;
	if (cName && (cName.indexOf('js-') != -1)) {
		if(cName.indexOf(' ') > -1) {
			var classes = cName.match(/\S+/g);
			for (var i=0; i < classes.length; i++) { 
				ctl[classes[i]] = e;
			}
		} else {
			ctl[cName] = e;
		}
	}
   
	if(e.name) ctl[e.name] = e;
	var cn = e.childNodes;
	if(cn) {
		var clen = cn.length;
		for(var i = 0; i < clen; i++)
			this.mapClass2Object(ctl, cn[i]);
	}
	return ctl;
}

JSCC.prototype.localDate = function(t) {
	if(!t) return "";
	var d = new Date(t * 1000);
	return d.toLocaleDateString();
}
JSCC.prototype.localTime = function(t) {
	if(!t) return "";
	var d = new Date(t * 1000);
	return d.toLocaleTimeString();
}
JSCC.prototype.gtmpl = function(t, mObj) {
	var lowercase = function(a, m) { return String(m).toLowerCase(); }
	t = t.replace(/^[^<]*(<.*>)[^>]*$/m, "$1");
	t = t.replace(/(<[\/]?[A-Z]+)/g, lowercase);
	if(mObj) t = t.replace(/(<[a-z]+)/, '$1 id="' + mObj.ID + '"');
	t = t.replace(/{Label:([^}]*)}/g,function(a,m){return $JCL(m);});
	return t;
}
JSCC.prototype.tmpl = function(t, obj, dontPutId) {
	var self = this;
	t = self.gtmpl(t, dontPutId ? false : obj);
	t = t.replace(/{Date}/g, self.localDate(obj.TS));
	t = t.replace(/{Time}/g, self.localTime(obj.TS));
	var text = String(obj.Text).replace(/^[ \s]+|[ \s]+$/, '');
	text = text.replace(/\n\n+/g, '\n\n');
	text = text.replace(/\n/g, '&nbsp;<br />');
	if(!this.nwb)
	text = text.replace(/([^&<>\s]{12})([^&<>\s]{12})/g, '$1<wbr></wbr>$2');
	text = text.replace(/{/g, '&#123;');
	t = t.replace(/{Text}/g, text);
	t = t.replace(/{([A-Za-z0-9]+)}/g,function(a,m){return obj[m]||'';});
	return t;
}

JSCC.prototype.FRef = function(cmt, tgt, funcName) {
	if(!cmt || !tgt) return;

	var self = this;
	var args = arguments;

	tgt.href = "";
	tgt.onclick = function() {
	  try {
		var arr = [];
		if(cmt) arr.push(cmt.id);
		for(var i = 3; i < args.length; i++) arr.push(args[i]);
		self[funcName].apply(self, arr);
	  } catch(e) { ; }
		return false;
	}
}

JSCC.prototype.cmtSetSpamStatus = function(cmt, s) {
	cmt.cobj.status = s ? 'S' : 'A';
	if(s) {
		cmt.style.background = '#ffffe0 url(' + this.uriDomain + '/images/bio-hazard.gif) bottom right repeat-x';
		cmt.style.color = '#404040';
	} else {
		cmt.style.backgroundColor = "";
		cmt.style.backgroundImage = "";
		cmt.style.color = '';
	}
	if(cmt.domINFO) cmt.domINFO.style.backgroundColor = s ? '#ffffe0' : "";
}

JSCC.prototype.blockAction = function(action) {
	var s = this;
	var cid = s.ctBlock.forId;
	var cmt = s.cmtById[cid];
	s.hideSettingsWindow('ctBlock');
	switch(action) {
	case "delete":
		s.cmtDelete(cid, 'delete');
		break;
	case "spam":
		s.cmtSetSpamStatus(cmt, true);
		if(s.config.moderate)
			s.pathOverride = cmt.cobj.path;
		s.server('.jnk','id='+cid+"&junk=yes");
		setTimeout(function() { // screen del
			s.cmtDelete(cid, 'ignore');
		}, 1000);
		break;
	case "ip":
	case "user":
		if(s.config.moderate)
			s.pathOverride = cmt.cobj.path;
		s.server('.blk','id='+cid+"&by="+action);
		s.cmtDelete(cid, 'delete');
		break;
	}
}

JSCC.prototype.cmtBlock = function(cid) {}
JSCC.prototype.cmtApprove = function(cid) {
	var cmt = this.cmtById[cid];
	if(cmt.cobj.status == 'S') {
		this.cmtSetSpamStatus(cmt, false);
		cmt.cobj.status = 'S'; // cmtDelete's deal
	}
	this.cmtDelete(cid, 'message');
}
JSCC.prototype.cmtApproveUser = function(cid) {
	var cmt = this.cmtById[cid];
	if(cmt.cobj.status == 'S') {
		this.cmtSetSpamStatus(cmt, false);
		cmt.cobj.status = 'S'; // cmtDelete's deal
	}
	this.cmtDelete(cid, 'user');
}

JSCC.prototype.cmtDelete = function(cid, approvalMode) {
	var cmt = this.cmtById[cid];

	if(arguments.length == 1) approvalMode = 'delete';

	var oldStatus = cmt.cobj.status;
	cmt.cobj.status = 'D';
	cmt.style.display = "none";

	var flushSS = false;

	// Shift the comments one to the left.
	for(var cp = this.curPage - 1; cp < this.pages.length; cp++) {
		var npage = this.pages[cp];
		if((cp + 1) == this.pages.length)
			npage.items--;
		if(cp < this.curPage)
			continue;

		// Propagate "get from the server side" status.
		if(npage.ss) {
			this.pages[cp - 1].ss = true;
			flushSS = true;
			continue;
		} else if(flushSS) {
			npage.ss = true;
			continue;
		}

		npage.rmdiv();

		// Move first comment to the previous page.
		while(npage.oarr[0].status == 'D') {
			npage.oarr.shift();
			npage.harr.shift();
		}
		var nobj = npage.oarr.shift();
		var nhtm = npage.harr.shift();
		var ppage = this.pages[cp - 1];
		ppage.oarr.push(nobj);
		ppage.harr.push(nhtm);
		if(ppage.div) {
			var ncmt = this.createSingleComment(nobj);
			ppage.div.appendChild(ncmt);
		}
	}

	if(this.pages.length > 1 && !this.pages[this.pages.length-1].items) {
		this.pages.pop();
		this.displayPage(this.curPage);
	}

	if(this.config.moderate)
		this.pathOverride = cmt.cobj.path;
	switch(approvalMode) {
	case 'message':
		this.server('.del', 'id=' + cid + '&apr=message'
			+ (oldStatus == 'S' ? '&junk=no' : '')); break;
	case 'user':
		this.server('.del', 'id=' + cid + '&apr=user'
			+ (oldStatus == 'S' ? '&junk=no' : '')); break;
	case 'delete':
		this.server('.del', 'id=' + cid); break;
	case 'ignore':
		/* Just delete from screen */
	default:
	}

	var p = this.pages[this.curPage - 1];
	if(p.ss) {
		this.ctag = null;
		this.czidx = 300;
		var pageNo = this.curPage;
		this.curPage = 0;
		this.displayPage(pageNo);
	}

}

JSCC.prototype.objppc = function() {}
JSCC.prototype.createCommentAsHTML = function(obj) {
	if(obj.status == 'D')
		return '';
	this.objppc(obj);
	return this.tmpl(this.dtComment, obj);
}

JSCC.prototype.createSingleComment = function(obj) {
	if(!obj.ID || !obj.Text) return undefined;

	if(obj.status == 'D') {
		var cmt = this.cr("div");
		cmt.style.display = "none";
	} else {
		var cmt = this.html(this.createCommentAsHTML(obj));
	}

	cmt.id = obj.ID;
	this.fixComment(cmt, obj);

	return cmt;
}

JSCC.prototype.fixComment = function(cmt, obj) {
	var self = this;

	self.cmtById[obj.ID] = cmt;

	if(obj.depth) {
		cmt.style.marginLeft = this.level4margin(obj.depth)
	} else {
		obj.depth = 0;
	}

	var ctls = this.mapClass2Object({}, cmt);
	cmt.ctls = ctls;
	cmt.cobj = obj;
	var jsc = function(t){return ctls['js-singleComment'+t]}

	jsc('').className += " js-singleCommentDepth" + (obj.depth || 0);

	/* Handle if ratings are present */
	if (obj.Rating > 0 && ( ! this.isStandalone()) ) {
		var self = this;
		var action = function() {
			if (jsc('Rating')) {
				jsc('Rating').appendChild(self.createMiniStarObject(obj.Rating, 10));
				jsc('Rating').style.display = '';
				var clear = document.createElement('div');
				clear.style.clear = 'left';
				jsc('Rating').appendChild(clear);
			}
		}
		$JSKitGlobal.tryRatingsAppObjectAction(this.uniq, action);
	} else {
		if (jsc('Rating')) {
			jsc('Rating').style.display = 'none';
		}
	}

	if(obj.isEmbryonic) {
		/* Waiting for permanent ID */
		cmt.domCtls = jsc('Controls');
		if(cmt.domCtls) cmt.domCtls.style.visibility = "hidden";
	}

	if(obj.admin) {
		var sa = jsc("Name");
		if(sa) sa.className = sa.className + " js-siteAdmin";
	}

	if(!this.scoringEnabled()
	|| (obj.yours && !this.adminMode) || !obj.karma) {
		var kA = jsc("Karma");
		if(kA) kA.style.display = "none";
	}
	var kS = jsc("KarmaScore");
	if(kS && obj.karma) {
		var kVal = jsc("KarmaValue");
		var kVot = jsc("KarmaVoters");
		if(obj.karma.votes) {
			kVal.innerHTML = obj.karma.score;
			kVot.innerHTML = obj.karma.votesText;
			kS.style.display = "inline";
		}
		var kY = jsc("KarmaY");
		if(kY) {
			kY.href = "";
			kY.onclick = function() {
				obj.karma.recomputeScore(1);
				kVal.innerHTML = obj.karma.score;
				kVot.innerHTML = obj.karma.votesText;
				kS.style.display = "inline";
				this.blur();
				return false;
			}
		}
		var kN = jsc("KarmaN");
		if(kN) {
			kN.href = "";
			kN.onclick = function() {
				obj.karma.recomputeScore(-1);
				kVal.innerHTML = obj.karma.score;
				kVot.innerHTML = obj.karma.votesText;
				kS.style.display = "inline";
				this.blur();
				return false;
			}
		}
	}

    /* FIXME(?) Lev, this.serverOptions are not defined in moderation mode
       but the result is likely as desired, i.e. admin can still reply */
	if (this.serverOptions.mmode == "pause") {
		var rb = jsc("Replyable");
		if(rb) rb.style.display = "none";
	}

	this.FRef(cmt, jsc("Reply"), "ShowCommentDialog");
	this.FRef(cmt, jsc("Delete"), "cmtDelete");
	this.FRef(cmt, jsc("Block"), "cmtBlock");
	this.FRef(cmt, jsc("Approve"), "cmtApprove");
	this.FRef(cmt, jsc("ApproveUser"), "cmtApproveUser");

	var au = jsc("ApproveUser");
	if (au)
	{
		 au.style.display = (mmode == "onhold") ? "inline" :  "none";
	}

	var hideCtl = jsc("Deletable");
	if(hideCtl && !obj.yours)
		hideCtl.style.display = "none";

	cmt.bg = jsc('Bg');
	if(this.czidx < 10) this.czidx = 300; else this.czidx--;
	cmt.bg.style.zIndex = this.czidx;
	var cinfo = jsc('INFO');
	cmt.domINFO = cinfo;

	if(obj.status == 'S')
		this.cmtSetSpamStatus(cmt, true);

	if(obj.admin && this.config.adminBgColor) {
		cmt.style.backgroundColor = this.config.adminBgColor;
	}

}

JSCC.prototype.level2margin = function(level) {
	if(level < 20) return "10px";
	if(level < 40) return "4px";
	return "0px";
}
JSCC.prototype.level4margin = function(level) {
	if(level <= 20) return (10 * level) + 'px';
	if(level <= 40) return (200 + 4 * level) + 'px';
	return '280px';
}
JSCC.prototype.cmtInDiv = function(div, obj, fincb) {
  var cmt = this.createSingleComment(obj);
  if(!cmt) return null;

  if(obj.ParentID) {
	var prn = this.objById[obj.ParentID];
	var td = prn ? prn.depth : 0;
	if(prn) {
		obj.depth = 1 + td;
		cmt.style.marginLeft = this.level4margin(obj.depth)

		this.replyPlacement(obj.ParentID, true, function(immed, apl) {
			if(apl) apl[0].insertBefore(cmt, apl[1]);
			prn.thread.push([obj, '-']);
			var page = this.pages[this.curPage-1];
			page.oarr.splice(apl[2], 0, obj)
			page.harr.splice(apl[2], 0, this.createCommentAsHTML(obj));
			page.items++;
			try { this.czidx = 300;
				for(var i = 0; i < page.oarr.length; i++) {
				var sbl = this.cmtById[page.oarr[i].ID];
				sbl.bg.style.zIndex = this.czidx--;
				//alert('some zindex['+i+']= ' + sbl.bg.style.zIndex + ' hz='+ sbl.bg.currentStyle.hasLayout);
			}
			//alert('my zindex = ' + cmt.bg.style.zIndex + ' ' + cmt.bg.className + '/' + cmt.bg.currentStyle.hasLayout);
			} catch(e){alert(e);}
			fincb.apply(this, [cmt]);
		});
		return;
	}
  }

  // Fixme! create additional pages as necessary.
  if(this.preq.ord == 'desc') {
	this.displayPage(1, function(immed) {
		var page = this.pages[0];
		page.oarr.unshift(obj);
		page.harr.unshift(this.createCommentAsHTML(obj))
  		this.addChild(page.div, cmt, true);
	  	page.items++;
		fincb.apply(this, [cmt]);
	});
  } else {
	this.displayPage(this.pages.length, function(immed) {
		var page = this.pages[this.curPage - 1];
		page.oarr.push(obj);
		page.harr.push(this.createCommentAsHTML(obj));
  		this.addChild(page.div, cmt, false);
	  	page.items++;
		fincb.apply(this, [cmt]);
	});
  }
}

JSCC.prototype.setOpacity = function(div, val) {
	if(div) {
		div.style.opacity = val;
		div.style.filter = 'alpha(opacity: ' + Math.round(val * 100) + ')';
	} else {
		if(document.body.filters)
			return 'zoom:1;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=' + Math.round(val * 100) + ');';
		else
			return 'opacity: ' + val + ';';
	}
}

JSCC.prototype.flash = function(cmt) {
	if(!cmt) return;

	var self = this;
	var bg = cmt.bg;

	try {
		bg.style.backgroundColor = "#ffff00";
		self.setOpacity(bg, 0);
	} catch(e) { return; }

	cmt.cntDown = 3.14 / 2;
	cmt.cntMode = 0;

	cmt.intvl = setInterval(function() {
		cmt.cntDown -= cmt.cntMode ? 0.5 : 0.3;
		if(cmt.cntDown > 0) {
			if(cmt.cntMode)
				var c = Math.sin(cmt.cntDown);
			else
				var c = Math.cos(cmt.cntDown);
			self.setOpacity(bg, c);
		} else if(cmt.cntMode) {
			clearInterval(cmt.intvl);
			cmt.intvl = null;
			bg.style.backgroundColor = "";
			self.setOpacity(bg, 1);
		} else {
			cmt.cntMode = 1;
			cmt.cntDown = 3.14 / 2;
		}
	}, 100);
}

JSCC.prototype.fields = function (node, cmtObj, tagName) {
	var flds = node.getElementsByTagName(tagName);
	var message = '';
	for(var i = 0; i < flds.length; i++) {
		var field = flds[i];
		var name = String(field.getAttribute("NAME"));
		if(!name.match(/^js-Cmt[A-Za-z0-9]+$/)) continue;
		message += "&" + name + "=" + encodeURIComponent(field.value);
		var shortName = name.replace(/^js-Cmt/, '');
		var text = String(field.value);
		text = text.replace(/([&<>\n])/g, function(a,m) {
			var map = {'&':'&amp;','<':'&lt;','>':'&gt;','\n':'<br />'};
			return map[m];
		});
		cmtObj[shortName] = text;
	}
	return message;
}

JSCC.prototype.cmtInPlace = function(cobj, fincb) {
	var div = this.TC["js-OldComments"];
	this.tmpID++;
	cobj.ID = "jst-" + this.tmpID;
	cobj.isEmbryonic = true;
	cobj.thread = [];
	cobj.depth = 0;
	cobj.jcaIndex = this.jcaIndex;
	cobj.admin = this.adminMode;
	var d = new Date();
	cobj.TS = Math.round(d.valueOf() / 1000);
	this.cmtInDiv(div, cobj, function(cmt) {
		if(cmt) this.flash(cmt);
		fincb.apply(this, [cmt]);
	});
}

JSCC.prototype.pageShiftLastItem = function() {
	// Find the last non-deleted item on this page;
	var page = this.pages[this.curPage - 1];
	var pli;
	for(var li = page.div.lastChild; li; li = pli) {
		pli = li.previousSibling;
		if(li.cobj) {
			li.parentNode.removeChild(li);
			if(li.cobj.status != 'D')
				break;
		}
	}
	this.pageShiftItemDescription(this.curPage);
}

JSCC.prototype.pageShiftItemDescription = function(pageNo, flush) {
	if(pageNo >= this.pages.length) return;
	var page = this.pages[pageNo - 1];
	var npage = this.pages[pageNo];
	npage.rmdiv();
	var obj;

	if(flush) {
		npage.ss = 0;
		npage.rmdiv();
		npage.items = 0;
		return this.pageShiftItemDescription(pageNo + 1, flush);
	}

	while(obj = page.oarr.pop()) {
		var html = page.harr.pop();
		if(npage.ss) {
			flush = true;
		} else {
			npage.harr.unshift(html);
			npage.oarr.unshift(obj);
		}
		if(obj.status != 'D') {
			page.items--;
			if(!flush) npage.items++;
			break;
		}
	}
	this.pageShiftItemDescription(pageNo + 1, flush);
}

// Search for a proper place for a reply
JSCC.prototype.replyPlacement = function(msgId, finalPlace, plcb) {
	if(!msgId) { plcb.apply(this, [true, null]); }
	var pobj = this.objById[msgId];
	if(!pobj) return null;

	var shiftF = function(imm, cmt, arridx, cb) {
		if(this.pages[this.curPage - 1].items >= this.config.paginate) {
			if(this.curPage == this.pages.length)
				this.pages.push(this.pages.empty());
			// Find first non-deleted comment on this page
			for(var ndc = cmt.nextSibling; ndc && ndc.cobj.status == 'D'; ndc = ndc.parentNode);
			if(ndc) {
				// Move the last comment off the page.
				this.pageShiftLastItem();
			} else {
				this.displayPage(this.curPage + 1, function(immed) {
					var pdiv = this.pages[this.curPage - 1].div;
					var r = [pdiv, pdiv.firstChild, 0];
					cb.apply(this, [immed, r]);
				});
				return;
			}
		}
		/* Reply's place is right beside the entry */
		var r = [cmt.parentNode, cmt.nextSibling, arridx];
		cb.apply(this, [imm, r]);
	}

	// Find the last reply to this comment
	var lreplyObj = null;
	while(pobj.thread.length) {
		lreplyObj = pobj.thread[pobj.thread.length-1][0];
		if(lreplyObj && lreplyObj.status == 'D') {
			pobj.thread.pop();
			lreplyObj = null;
		} else break;
	}
	if(!lreplyObj) lreplyObj = pobj;

	// Find the reply object's page
	var idx;
	for(var pdx = this.curPage - 1; pdx < this.pages.length; pdx++) {
		var page = this.pages[pdx];
		var oarr = page.oarr;
		var oal = oarr.length;
		for(idx = 0; idx < oal; idx++)
			if(oarr[idx].ID == lreplyObj.ID) break;
		if(idx < oarr.length) break;
	}
	if(pdx == this.pages.length) {
		var pcmt = this.get(msgId);
		return plcb.apply(this, [true, [pcmt.parentNode, pcmt.nextSibling, 0]]);
	}

	// Show the page corresponding to that page index
	this.displayPage(pdx+1, function(immed) {
		// Insert right before the last recorded reply
		var prevReply = this.get(lreplyObj.ID);
		if(finalPlace)
			shiftF.apply(this, [immed, prevReply, idx + 1, plcb]);
		else
			plcb.apply(this, [immed, [prevReply.parentNode, prevReply.nextSibling]]);
	});
}

JSCC.prototype.ShowCommentDialog = function(msgId) {

	this.forMsg = this.objById[msgId||''];

	var isReply = msgId ? true : false;
	var cct = this.TC["js-LeaveComment"];
	var ccd = this.TC["js-CreateComment"];

	/* Remove dialog from sight */
	this.CommentCancelled();

	this.replyPlacement(msgId, false, function(immediate, apl) {
		if(apl) apl[0].insertBefore(ccd, apl[1]);

		if(this.backwards && msgId)
			cct.style.visibility = "hidden";
		else
			cct.style.display = "none";
		ccd.style.display = "block";
		try {
			var sub = this.TC["js-Cmtsubmit"];
			var email = this.TC["js-CmtEmail"];
			var name = this.TC["js-CmtName"];
			var text = this.TC["js-CmtText"];
	
			/* combined ratings */
			var commentRatingElements = JSKitLib.getElementsByClass(ccd, "js-commentRatingDisplay");
			var commentRatingDisplay = 'none';
			this.submitRating = false;
			if (this.hasRatingsAppObject() && ( ! isReply)) {
				if (this.TC["js-commentFieldRating"]) {
					this.embedRatingsAppObject(this.TC["js-commentFieldRating"]);
					commentRatingDisplay = '';
					this.submitRating = true;
				}
			}
			for (var i=0; i < commentRatingElements.length; i++) {
				commentRatingElements[i].style.display = commentRatingDisplay;
			}

			if(sub) sub.focus();
			if(email && !email.eFilled && this.fieldDfl["Email"]) {
				email.focus();
				email.value = this.fieldDfl["Email"];
			}
			if(name && !name.value.length)
				name.value = this.fieldDfl["Name"];
			if(name && name.value.length) {
				text.focus();
			} else if(name) {
				name.focus();
			}
		} catch(e) { }
	});
	return false;
}

JSCC.prototype.CommentCancelled = function() {
	var cct = this.TC["js-LeaveComment"];
	var ccd = this.TC["js-CreateComment"];
	var car = this.TC["js-CommentsArea"];
	cct.style.visibility = "";
	cct.style.display = "";
	ccd.style.display = "";
	if(car && ccd.parentNode != car) {
		ccd.parentNode.removeChild(ccd);
		this.addChild(car, ccd, this.backwards);
	}
	return false;
}

JSCC.prototype.CommentSubmitted = function() {
	var form = this.TC["js-CreateComment"];
	var cmtObj = {};
	cmtObj.yours = true;
	var message = this.fields(form, cmtObj, "input")
		+ this.fields(form, cmtObj, "textarea");
	message = message.replace(/^&/, '');

	/* combined ratings */
	if (this.submitRating) {
		var rating = this.getRatingsAppObject().userRating;
		message += '&js-CmtRating=' + rating;
		cmtObj.Rating = rating;
	}

	if(!cmtObj.Text || !cmtObj.Text.length) {
		alert($JCL("tooShort"));
		return false;
	}
	if(cmtObj.Text.length > 1700) {
		alert($JCL("tooLong"));
		return false;
	}
	var prn = this.forMsg;
	if(prn) {
		cmtObj.ParentID = prn.ID;
		cmtObj.path = prn.path;
	}
	this.CommentCancelled();
	/* Attach message */
	this.cmtInPlace(cmtObj, function() {
		this.controls.reveal();
		/* Kick in message submission */
		var puturl = message + '&tid=' + cmtObj.ID
		  + (cmtObj.ParentID ? ('&js-CmtParentID=' + cmtObj.ParentID) : '');
		if(this.config.moderate)
			this.pathOverride = this.forMsg.path;
		return this.server('.put', puturl);
	});
}

JSCC.prototype.getRatingsAppObject = function() {
	if (this.isStandalone()) {
		return null;
	} else {
		return $JSKitGlobal.getRatingsAppObject(this.uniq);
	}
}

JSCC.prototype.hasRatingsAppObject = function() {
	return this.getRatingsAppObject() ? true : false;
}

JSCC.prototype.embedRatingsAppObject = function(node) {
	// One time
	if ( ! this.embedRatingsAppObjectCompleted) {
		$JSKitGlobal.copyRatingsAppObject(this.uniq, node);
		this.embedRatingsAppObjectCompleted = true;
	}
}

JSCC.prototype.createMiniStarObject = function(rating, scale) {

	var rao = this.getRatingsAppObject();
	var fullStar = rao.miniFullStar['user'];
	var emptyStar = rao.miniEmptyStar['user'];
	var starWidth = rao.miniStarWidth + 'px';
	var starHeight = rao.miniStarHeight + 'px';

	var setImage = function(star, imageURL) {
		if(star.imageURL == imageURL)
			return; // Already set and we know it

		star.imageURL = imageURL;

		if(document.body.filters) {
			star.runtimeStyle.filter
				= "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"
				+ imageURL + "', sizingMethod='crop')"
		} else {
			star.style.backgroundImage = 'url(' + imageURL + ')';
		}
	}

	var obj = document.createElement('div');

	/* Increment by Full Star Ratings */
	for (var i=2; i <= scale; i += 2) {

		var star = this.cr('div');

		star.style.cssFloat   = 'left';
		star.style.styleFloat = 'left';
		star.style.width    = starWidth;
		star.style.height   = starHeight;

		if (rating >= i) {
			setImage(star, fullStar);
		} else {
			setImage(star, emptyStar);
		}

		obj.appendChild(star);
	}

	return obj;
}

function JSReplyMSGId(tmpid, msgId) {
	try {
		var cmt = document.getElementById(tmpid);
		var self = $JCA[cmt.cobj.jcaIndex];
		delete self.cmtById[cmt.id];
		cmt.id = msgId;
		cmt.cobj.ID = msgId;
		if(arguments.length > 2) {
			var aux = arguments[2];
			if(aux.status == 'H') {
				cmt.cobj.status = aux.status;
				cmt.ctls['js-singleCommentText'].innerHTML += aux.message;
			}
		}
		self.cmtById[msgId] = cmt;
		self.objById[msgId] = cmt.cobj;
		cmt.domCtls.style.visibility = "";
	} catch(e) { ; }
}

function JSCCKarma(kObj, cObj, self) {
	if(!kObj) var kObj = { p: 0, n: 0 };
	this.score = kObj.p - kObj.n;
	this.votes = kObj.p + kObj.n;
	this.cObj = cObj;
	this.self = self;
	this.myNegativeVotesThisSecond = 0;
	this.vote2text();
	return this;
}
JSCCKarma.prototype.vote2text = function() {
	this.votesText = this.votes + ' '
			+ ((this.votes == 1) ? $JCL("vote") : $JCL("votes"));
}

JSCCKarma.prototype.recomputeScore = function(scoreAdjustment) {
	var now = new Date();
	if(this.votedAlready) {
		this.score -= this.myVote;
		if(scoreAdjustment > 0) {	// Positive vote
			this.myVoteTS = now;
			this.myNegativeVotesThisSecond = 0;
		} else if(!this.multiNegativeRecorded) {	// Negative vote
			if((now - this.myVoteTS) > 1000) {
				this.myNegativeVotesThisSecond = 0;
				this.myVoteTS = now;
			} else if(this.myNegativeVotesThisSecond >= 2) {
				var p = confirm($JCL("isJunkVote"));
				this.multiNegativeRecorded = true;
			}
			this.myNegativeVotesThisSecond++;
		}
	} else {
		this.myVoteTS = now;
		this.votes += 1;
		this.votedAlready = true;
		var kObj = this;
		setTimeout(function() {
			kObj.self.server('-karma.prg', 'id=' + kObj.cObj.ID
				+ '&action=' + (kObj.myVote > 0 ? '+' : '-'));
			}, 2000);
	}
	this.score += scoreAdjustment;
	this.myVote = scoreAdjustment;
	this.vote2text();
}

JSCC.prototype.divPages = function(so, items_ho) {
	var srv = so.pages;
	var cpp = this.preq.ps;
	var hitems = items_ho[0];
	var oitems = items_ho[1];
	var replace = (this.ctag != so.tag);
	this.curPage = 0;

	var f = function() {
		var div = this.div;
		if(div) {
			this.div = null;
			if(div.parentNode) div.parentNode.removeChild(div);
		}
	}

	if(this.pages) {
		if(replace) {
			for(var i = this.pages.length; i; i--) {
				var p = this.pages[i-1];
				p.rmdiv();
			}
			this.pages = [];
		}
	} else {
		this.pages = [];
		replace = true;
	}
	this.pages.empty = function(sv) {
		return {ss:(sv?true:false),harr:[],oarr:[],items:0,div:null,rmdiv:f}};

	if(replace) {
		for(var i = srv.sp; i > 1; i--)
			this.pages.push(this.pages.empty(true));
	}
	var i = srv.sp - 1;
	for(var start = 0; start < hitems.length; start += cpp, i++) {
		var cnt = (start + cpp > hitems.length)
				? hitems.length - start : cpp;
		var page = {
			harr: hitems.slice(start, start + cnt),
			oarr: oitems.slice(start, start + cnt),
			items: cnt, div: null, rmdiv: f };
		if(replace) {
			this.pages.push(page);
		} else {
			var p = this.pages[i];
			p.rmdiv();
			p.harr = page.harr;
			p.oarr = page.oarr;
			p.items = page.items;
			p.ss = false;
		}
	}
	var togo = srv.tp - (srv.sp + srv.pn - 1);
	if(replace) {
		for(var i = togo; i > 0; i--)
			this.pages.push(this.pages.empty(true));
	}
	if(this.pages.length == 0)
		this.pages.push(this.pages.empty());
}

JSCC.prototype.htmlPaginate = function(thread) {
	this.icount = 0;	/* Page item count */
	return this.htmlPaginator(thread, [], []);
}

JSCC.prototype.htmlPaginator = function(thread, harr, oarr) {
	var tl = thread.length;
	for(var i = 0; i < tl; i++) {
		var thr = thread[i];
		var obj = thr[0];
		var html = thr[1];
		var present = (obj.status == 'D') ? 0 : 1;
		if(present) {
			oarr.push(obj);
			harr.push(html);
		}
		this.htmlPaginator(obj.thread, harr, oarr);
	}
	return [harr,oarr];
}

JSCC.prototype.rerender = function() {
	var pageToDisplay = this.curPage;
	this.curPage = 0;
	this.pages[pageToDisplay - 1].ss = true;
	this.displayPage(pageToDisplay);
}

JSCC.prototype.displayPage = function(pageNo, cb) {

	if(this.loading && !cb) {
		var nt = (new Date()).valueOf();
		if((nt - this.loading) > 5000) {
			this.gen++;
		} else  {
			return;
		}
	}

	if(pageNo < 1)
		return;

	if(pageNo > this.pages.length)
		pageNo = this.pages.length;

	var immediate = true;

	if(this.curPage != pageNo) {
		try { this.pages[this.curPage - 1].div.style.display = 'none';
		} catch(e) { }
		this.curPage = pageNo;
		var page = this.pages[this.curPage - 1];
		if(page.ss) page.rmdiv();
		if(page.div) {
			page.div.style.display = '';
		} else {
			var oc = this.TC["js-OldComments"];
			page.div = this.cr('div');
			if(page.ss) {
				page.div.innerHTML = $JCL("Loading...");
				immediate = false;
				this.dataLoader = function() {
					this.curPage = 0;
					this.displayPage(pageNo);
					if(cb) cb.apply(this, [immediate]);
				}
				if(this.preq.pn < 10)
					this.preq.pn += 5;
				this.getpages(pageNo - Math.ceil(this.preq.pn / 2));
			} else {
				page.div.innerHTML = page.harr.join('');
				this.mapComments(page.div);
			}
			oc.appendChild(page.div);
		}
	} else {
		var page = this.pages[this.curPage - 1];
	}

	var nav = '';
	if(this.pages.length > 1)
		nav = this.pageNavigator(this.pages.length, this.curPage);
	var nvs = ['Top','Bottom'];
	for(var i = 0; i < nvs.length; i++) {
		var bar = this.TC['js-PageNav' + nvs[i]];
		bar.innerHTML = nav;
		bar.onselectstart = function() { return false; }
		if(i) bar.style.display = (page.items <= 5 ? 'none' : '');
	}
	if(immediate && cb) cb.apply(this, [immediate]);
}

JSCC.prototype.pageNavigator = function(pages, cur) {
	var self = this;
	var arr = [$JCL('Page: ')];
	var f = function(i, txt, cmt, css) {
		return '<a href="#'+cmt+'" onclick="$JCA['+self.jcaIndex+'].displayPage('+i+'); return false;" onmouseover="window.status='+"'"+cmt+"'"+'; return false;" onmouseout="window.status=\'\'; return true;" style="text-decoration:none; ' + (css?css:'') + '">' + txt + '</a> ';
	}
	arr.push(f(cur - 1, '&larr;', $JCL('Previous page'),
		cur == 1 ? this.setOpacity(null, 0.3) : ''));
	for(var i = 1; i <= pages; i++) {
		if((i == 4 || i == 3) && (cur - i) > 3) {
			i = Math.floor((cur - i) / 2 + i);
			arr.push(f(i, '&hellip;', 'Page-' + i));
			if(pages - cur > 3 || cur == pages)
				i = cur - 2;
			else
				i = cur - 1;
		}
		if((i == cur + 3) && (pages - cur) > 4) {
			i = Math.floor((pages - cur) / 2 + cur);
			arr.push(f(i, '&hellip;', 'Page-' + i));
			i = pages - 1;
		}
		if(i == cur) {
			arr.push('<strong>' + i + '</strong> ');
		} else {
			arr.push(f(i, i, 'Page-' + i));
		}
	}
	arr.push(f(cur + 1, '&rarr;', $JCL('Next page'),
		pages == cur ? this.setOpacity(null, 0.3) : ''));
	return arr.join('');
}

JSCC.prototype.hideSettingsWindow = function(wname) {
	if(this[wname]) this.settingsWindow(wname);
}

JSCC.prototype.showProgress = function(wname, on) {
	if(this[wname]) this[wname].showProgress(on);
}

JSCC.prototype.settingsWindow = function(wname, atDiv, html) {
	var s = this;
	if(s[wname]) {
		if(!s.sWHideable) return;
		s[wname].parentNode.removeChild(s[wname]);
		delete s[wname];
		return;
	}
	var nohide = function() {
		s.sWHideable = false;
		if(s.swsHidt) clearTimeout(s.swsHidt);
		s.swsHidt = setTimeout(function(){s.sWHideable=true}, 100);
	}
	var div = this.cr("div");
	div.className = "js-SettingsWindow"
	div.style.background = '#FFFFFF url('+this.uriDomain
				+'/images/bg-header-gray.png) bottom repeat-x';
	div.onclick = nohide;
	div.onselectstart = function() { return false; }
	if(typeof(html) == 'string') {
		div.style.zIndex = 400;
		div.innerHTML = html;
	} else {
		div.style.width = '20em';
		div.appendChild(html);
	}
	var pgr = this.cr('div');
	pgr.className = "js-Progress";
	pgr.style.backgroundImage = 'url('+this.uriDomain+'/images/progress-wg.png)';
	div.appendChild(pgr);
	div.showProgress = function(on) {
		if(!on) {
			if(div.pIntvl) clearInterval(div.pIntvl);
			div.pIntvl = null;
			pgr.style.visibility  = 'hidden';
			return;
		} else if(div.pIntvl) return;
		var f = function() {
			pgr.vison = !pgr.vison;
			pgr.style.visibility = pgr.vison
				? 'visible' : 'hidden';
		}
		f();
		div.pIntvl = setInterval(f, 500);
	}

	s[wname] = div;
	atDiv.appendChild(div);
	nohide();
}

JSCC.prototype.viewControl = function(sel) {
	var s = this;
	switch(sel.name) {
	case "jss-srt":
		var newSortBy = sel.options[sel.selectedIndex].value;
		if(newSortBy == s.preq.srt) return true;
		s.preq.srt = newSortBy;
		break;
	case "jss-rev":
		var newOrder = sel.selectedIndex?'desc':'asc';
		if(s.preq.ord == newOrder) return true;
		s.preq.ord = newOrder;
		break;
	case "jss-prs":
		var newPrs = sel.options[sel.selectedIndex].value;
		if(newPrs == s.preq.thr) return true;
		s.preq.thr = newPrs;
		break;
	default: return false;
	}
	s.showProgress('ctWnd', true);
	s.dataLoader = function() {
		this.showProgress('ctWnd', false);
		this.curPage = 0;
		this.displayPage(1); }
	s.ctag = null;
	s.czidx = 300;
	s.getpages(0, '&usr=yes');
	return true;
}

JSCC.prototype.dataLoader = function(so, nc) {
	var s = this;
	var tc = s.TC;
	var d=function(){return s.div.apply(s,arguments);}

	var cc = s.html(s.gtmpl(s.utmpl['js-CreateComment'] || s.dtCreate));
	s.mapClass2Object(tc, cc);

	var o;
	o = tc['js-Cmtsubmit'];
	if(o) o.onclick = function() { return s.CommentSubmitted(); }
	o = tc['js-Cmtcancel'];
	if(o) o.onclick = function() { return s.CommentCancelled(); }
	o = tc['js-CmtEmail'];
	if(o) {
		o.style.color = '#808080';
		o.value = 'email is ' + (so.adminMode ? 'mandatory for you (admin)' : 'optional');
		o.onfocus = function() { if(o.eFilled) return true; o.value = ''; o.style.color = ''; o.eFilled = true; return true; }
	}

	var labelHTML = s.labelHTML || $JCL('leaveComment');

	if(so.mmode == "pause") {
		var lca = null;
	} else {
		var lca = d('js-commentControl', s.a($JCL(labelHTML)));
		lca.onclick = function() { return s.ShowCommentDialog(); };
	}

	var jmg = d('js-commentControl js-commentTool', s.html('<font face="Webdings">&#64;&nbsp;</font>'), s.a($JCL("Controls")));
	jmg.onclick = function() {
		var srt = ["date", "name"];
		if(!s.config.moderate) srt.push("karma");
		if(s.adminMode) srt.push("status");
		/* s.submitRating check is not good for all the cases */
		if ( $JSKitGlobal.isRatingsAppAvailable() ) srt.push("rating");
		var srtOpts = [];
		for(var i = 0; i < srt.length; i++) {
			srtOpts.push('<option value="'+srt[i] + '"'
				+ (srt[i]==s.preq.srt?" selected":"")+'>'
				+$JCL(srt[i])+'</option>');
		}
		var bkw = ["ascending", "descending"]
		var bkwOpts = [];
		for(var i = 0; i < bkw.length; i++) {
			bkwOpts.push('<option value="'+bkw[i]+'"'
				+ ((!!i) == (s.preq.ord=='desc')?" selected":"")+'>'
				+$JCL(bkw[i])+'</option>');
		}
		var prs = ["on (threaded)", "off (flat)"]
		var prsMap = {'on (threaded)':'yes','off (flat)':'no'}
		var prsOpts = [];
		for(var i = 0; i < prs.length; i++) {
			prsOpts.push('<option value="'+prsMap[prs[i]]+'"'
				+ (prsMap[prs[i]] == s.preq.thr?" selected":"")+'>'
				+$JCL(prs[i])+'</option>');
		}
		s.settingsWindow('ctWnd', this,
			"<table border=0 cellpadding=4>"
			+ "<tr><td align=right>" + $JCL("Sort by") + '</td><td align=left><select name="jss-srt" onchange="$JCA['+s.jcaIndex+'].viewControl(this);return true;">'
			+ srtOpts.join("")
			+ "</select></td></tr>"
			+ "<tr><td align=right>" + $JCL("Order") + '</td><td align=left><select name="jss-rev" onchange="$JCA['+s.jcaIndex+'].viewControl(this);return true;">'
			+ bkwOpts.join("")
			+ "</select></td></tr>"
			+ "<tr><td align=right>" + $JCL("Threading") + '</td><td align=left><select name="jss-prs" onchange="$JCA['+s.jcaIndex+'].viewControl(this);return true;">'
			+ prsOpts.join("")
			+ "</select></td></tr>"
			+ (so.adminMode && !s.config.moderate?('<tr><td align=center colspan=2><a href="'+s.uriDomain+'/moderate/'+s.config.domain+'" onclick="window.location.href = this.href; return true;">Moderate comments</a></td></tr>'):'')
			+ "</table>"
		);
		return false;
	}
	s.controls = jmg;
	if(nc || s.config.moderate) {
		s.controls.reveal = function(){};
	} else {
		s.controls.style.display = 'none';
		s.controls.reveal = function(){s.controls.style.display=''}
	}

	if(so.subs || so.noJunk) {
		var pb = "";
	} else {
		var propLink = s.html('<a href="http://js-kit.com/comments?wow">Powered by JS-Kit</a>');
		var prop = d('', "(", propLink, ")");
		prop.style.position = 'relative';
		var pb = d("js-commentControl js-poweredBy", prop);
	}

	var ca = d("js-CommentsArea",
		d("js-LeaveComment", s.config.moderate?null:lca, jmg, pb,
			s.html('<br clear="all"/>')),
		tc["js-CreateComment"]);
	s.addChild(ca, d(null, d('js-PageNavTop'), d("js-OldComments"), d('js-PageNavBottom')), !s.backwards);
	s.displayPage(so.pages.sp);
	ca.onclick = function() {
		s.hideSettingsWindow('ctWnd');
		s.hideSettingsWindow('ctBlock');
	}
	s.addChild(s.target, ca);
}


/* Must be last to support Opera */
JSCC.prototype.newData = function(arr, so) {
	var s = this;
	s.serverOptions = so;
	s.adminMode = !!so.adminMode;
	s.nwb = !!so.nwb;

	if(so.req) {
		s.preq.srt = so.req.srt;
		s.preq.ord = so.req.ord;
		s.preq.thr = ((so.req.prs == 'flat') ? 'no' : 'yes');
	}

	s.gen++;
	s.loading = false;

	if(s.ctag != so.tag) {
		s.objById = {};
		s.cmtById = {};
	}

	var flat = s.preq.thr != 'yes';

	var ttt = []; // top level thread
	var nc = 0;
	for(var i = 0; i < arr.length; i++) {
		var obj = arr[i];
		if(!obj.ID || !obj.Text) continue;
		if(flat) {
			delete(obj.ParentID);
			delete(obj.depth);
		}
		s.objById[obj.ID] = obj;
		obj.thread = [];
		obj.karma = new JSCCKarma(obj.votes, obj, this);
		if(obj.status != 'D') nc++;
		var cmtHTML = s.createCommentAsHTML(obj);
		var prn = s.objById[obj.ParentID];
		if(prn) {
			prn.thread.push([obj, cmtHTML]);
		} else {
			ttt.push([obj, cmtHTML]);
		}
	}
	s.divPages(so, s.htmlPaginate(ttt));

	s.ctag = so.tag;
	s.dataLoader(so, nc);
}
