/*
 * Copyright (c) 2007 JS-Kit.com. All rights reserved.
 * $Date: 2007-11-28 22:48:33 -0800 (Wed, 28 Nov 2007) $
 */

if ( ! window.$JRA) {
  /* Global JS Ratings Array */
  var $JRA = [];
  var $JRH = {};

  var $JRLT = {
    yourRatingTitleCase: 'Tu puntuación',
    yourRating: 'Tu puntuación',
    vote: 'voto',
    votes: 'votos',
    unrated: 'No votado',
    rateThis: 'Vota este',
    avgRating: 'Puntuación media',
    poweredBy: 'Powered by',
    youHaveNotRatedYet: 'Aún no has votado',
    addACommentToYourRating: 'Agraga un comentario',
    noVotesReceivedYet: 'Aún no ha recibido votos',
    beTheFirstToRate: 'Vota primero!',
    ratingsDisabled: 'Los votos han sido deshabilitados',
    thankYou: 'Gracias!'
  };

  var $JRL = function(t) {
    return $JRLT[t] || t;
  };
}

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

JSKitLib.vars = {}; // Global JSKitLib variables

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.isIE = function() {
	if (document.body.filters && navigator.appVersion.match(/MSIE/))
		return true;
}

JSKitLib.isPreIE7 = function() {
	if (document.body.filters && parseInt(navigator.appVersion.split("MSIE")[1]) < 7)
		return true;
}

JSKitLib.addPNG = function(node, imageURL) {
	if (JSKitLib.isPreIE7()) {
		node.runtimeStyle.filter
			= "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"
			+ imageURL + "', sizingMethod='crop')"
	} else {
		node.style.backgroundImage = 'url(' + imageURL + ')';
		node.style.backgroundRepeat = 'no-repeat';        
	}
	return node;
}

JSKitLib.preloadImg = function(imgURL) { 

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

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


JSKitLib.visible = function(element) {
  return element.style.display != 'none';
}

JSKitLib.show = function(element) {
  element.style.display = '';
}

JSKitLib.hide = function(element) {
  element.style.display = 'none';
}

JSKitLib.toggle = function(element) {
  (element.style.display == 'none') ? JSKitLib.show(element) :  JSKitLib.hide(element);
}

JSKitLib.addStyle = function(element, style) {
  if (typeof element.style.cssText != "undefined") {
    element.style.cssText = style;
  } else {
    element.setAttribute("style", style);
  }
}

JSKitLib.findPos = function(obj) {
    var origObj = obj;
    var curleft = curtop = curright = curbottom = 0;
    if (obj.offsetParent) {
        curleft = obj.offsetLeft;
        curtop = obj.offsetTop;
        while (obj = obj.offsetParent) {
            curleft += obj.offsetLeft;
            curtop += obj.offsetTop;
        }
    }
    curright = curleft + origObj.offsetWidth;
    curbottom = curtop + origObj.offsetHeight;
    return [curleft,curtop,curright,curbottom];
}

// rounds number to x decimal places
JSKitLib.round = function(number, x) {
    x = (!x ? 2 : x);
    return Math.round(number*Math.pow(10,x))/Math.pow(10,x);
}

JSKitLib.zeroPad = function(number, x) {
  number = JSKitLib.round(number, x);
  var text = new String(number);
  var matches = text.match(/(\d*)(\.(\d*))?/) || [];
  var decimal = matches[3] || '';
  if (!decimal) {
    text += '.';
  }
  var count = x - decimal.length;
  for (var i=0; i<count; i++) {
    text += '0';
  }
  return text;
}

// Used when we don't want events to bubble up
JSKitLib.stopEventPropagation = function(e) {
  if (!e) var e = window.event;
  e.cancelBubble = true;
  if (e.stopPropagation) e.stopPropagation();
}

JSKitLib.addLoadEvent = function(newLoadEvent) {
  var origLoadEvent = window.onload;
  if (typeof origLoadEvent == "function") {
    window.onload = function() { 
      origLoadEvent();
      newLoadEvent();
    }
  } else {
    window.onload = newLoadEvent;
  }
}

// Detect wether window.onload has fired
if ( ! JSKitLib.vars.windowOnLoadFired) JSKitLib.vars.windowOnLoadFired = false;
JSKitLib.addLoadEvent(function() { JSKitLib.vars.windowOnLoadFired = true; });

JSKitLib.deferCallIfIE = function(func) {
  if (JSKitLib.isIE() && ! JSKitLib.vars.windowOnLoadFired) {
    JSKitLib.addLoadEvent(func);
  } else {
    func();
  }
}

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;
}

/* JS Rating Class */
function JSRC() {

  this.jraIndex = $JRA.length;
  $JRA.push(this);
  var self = this;

  this.cr = function(tag) { return document.createElement(tag) };

  this.pathOverride = '';
  this.config = {};
  this.raterInc = 2;  // Increment ratio of rateable v. displayable
  this.scale    = 10; // Points on rating scale

  this.onRate = []; // Callbacks for post rating processing

  this.isStandalone = function() {
    return (this.config.standalone == 'yes') ? true : false;
  }

  this.starWidth       = 16;
  this.starHeight      = 15;
  this.miniStarWidth   = 9;
  this.miniStarHeight  = 9;
  this.totalWidth; //The total width of the visible widget

  var wl = window.location;

  var idName = 'js-kit-rating';

  var target = arguments.length ? arguments[0] : document.getElementById(idName);
  var options = arguments[1] || {};

  if (target) {

    this.target = target;

    // Handling user configuration settings
    this.config.path = options.path || target.getAttribute('path');
    this.config.uniq = options.uniq || target.getAttribute('uniq') || '';
    this.config.standalone = options.standalone || target.getAttribute('standalone') || 'no';
    this.config.view = options.view || target.getAttribute('view') || 'combo';
    this.config.commentprompt = options.commentprompt || target.getAttribute('commentprompt') || true;
    this.config.starcolor = options.starcolor || (target.getAttribute('starcolor')||"").toLowerCase();
    this.config.usercolor = options.usercolor || (target.getAttribute('usercolor')||"").toLowerCase();
    this.config.imageurl  = options.imageurl  || target.getAttribute('imageurl');
    this.config.imagesize = options.imagesize || (target.getAttribute('imagesize')||"");
    this.config.title = options.title || target.getAttribute('title') || '';
    this.config.notop = options.notop || target.getAttribute('notop') || '';
    this.config.permalink = options.permalink || target.getAttribute('permalink') || '';
    this.config.freeze = options.freeze || target.getAttribute('freeze') || 'no';
    this.config.menu = options.menu || target.getAttribute('menu') || 'yes';
    this.config.subtext = options.subtext || target.getAttribute('subtext') || 'yes';
    this.config.property = options.property || target.getAttribute('property');

    // Special menu handling for particular sites
    if (wl.host.match(/icanhascheezburger.com/i)) { this.config.menu = 'no'; }

    if(this.config.imageurl && this.config.imagesize) {
	var dim = self.config.imagesize.match(/(\d+)([^\d]+(\d+))?/);
	if(dim) {
		self.starWidth = dim[1];
		self.starHeight = dim[3] || self.starWidth;
	}
    }
    this.ratingBarWidth  = this.scale / this.raterInc * this.starWidth; 
    this.ratingBarHeight = this.starHeight;

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

    this.path = this.pathOverride || wl.pathname;
    this.uniq = this.config.uniq || this.path;
    if ( ! $JRH[this.uniq]) {
      $JRH[this.uniq] = [];
    }
    $JRH[this.uniq].push(this);

    this.defineIcons();
    JSKitLib.preloadImg(JSRC.WINDOW_BG_IMG);
    JSKitLib.preloadImg(JSRC.INFO_IMG);

    if (options.newRating) {
        //TODO
        this.newRating({ Sum: options.newRating.objSum, Num: options.newRating.objNum, Votes: options.newRating.objVotes }, { Sum: options.newRating.userRating});
    }

    this.myref = function() {
	if(arguments.length) return self.path;
	return encodeURIComponent(wl.protocol + "//" + wl.host
          + (self.pathOverride.length ? "/" : wl.pathname));
    }

    if(wl.host.match(/icanhascheezburger.com/i)) {
	JSRC.URIS['get'] = 'http://b.js-kit.com/rating-data.js';
    }

    this.server = function(method, data) {
      var sc = self.cr("script");
      sc.setAttribute("charset", "utf-8");
      sc.src = JSRC.URIS[method] + self.pathOverride
		+ "?ref=" + self.myref() + "&" + data;
      self.target.appendChild(sc);
      return false;
    }
    if(options.autorequest) {
	var mr = this.myref('path');
	this.server('get', 'p[0]=' + encodeURIComponent(mr)
		+ ((mr == this.uniq) ? ''
			: ('&u[0]=' + encodeURIComponent(this.uniq)))
		+ (this.config.property ? '&pr[0]=' + encodeURIComponent(this.config.property) : '')
		+ '&jx[0]=' + this.jraIndex);
    }
  } else {

    /* Iterate and find all rating divs */
    var els = document.getElementsByTagName('div');
    if (els && els.length) {
      $JRA.shift();
      var multiQ = '';
      var multiI = 0;
      var reqMulti = function(atext) {
        if(!atext.length) return;
	var sc = self.cr("script");
        sc.src = JSRC.URIS['get'] + "?ref="
	  + encodeURIComponent(wl.protocol + "//" + wl.host + wl.pathname)
	  + atext;

        $JRA[0].target.appendChild(sc);

      }
      for (var i=0; i < els.length; i++) {
        if (els[i].className.match(/js-kit-rating/)) {
          var r = new JSRC(els[i]);
	  var mr = r.myref('path');
	  multiQ += "&p["+multiI+"]=" + encodeURIComponent(mr)
			+ ((mr == r.uniq) ? ''
			    : ("&u["+multiI+"]=" + encodeURIComponent(r.uniq)))
			+ (r.config.property ? "&pr["+multiI+"]=" + encodeURIComponent(r.config.property) : '')
			+ "&jx[" + multiI + "]=" + r.jraIndex;
	  if(multiQ.length > 700) {
		reqMulti(multiQ);
		multiQ = '';
		multiI = 0;
	  } else {
		multiI ++;
	  }
        }
      }
      reqMulti(multiQ);
      if ($JRA.length) {
        return;
      }
    }
    /* Handling for single line entry */
  }

}

/* Constants */
JSRC.DOMAIN = (window.location.protocol.substr(0, 4) != 'http' ? 'http:' : '')
              + '//js-kit.com';
JSRC.URIS = { 'put': JSRC.DOMAIN + '/rating.put',
	      'get': JSRC.DOMAIN + '/rating-data.js' };
JSRC.BASE_STAR_URI = JSRC.DOMAIN + '/images/stars/';
JSRC.WINDOW_BG_IMG = JSRC.DOMAIN + '/images/1px90pct.png';
JSRC.INFO_IMG = JSRC.DOMAIN + '/images/i-wg.png';
JSRC.INFO_IMG_WIDTH = 15;
JSRC.INFO_IMG_OFFSET = 7;


JSRC.prototype.defineIcons = function() {

  var self = this;
  this.fullStar  = [];
  this.halfStar  = [];
  this.emptyStar = [];
  this.miniFullStar  = [];
  this.miniEmptyStar = [];

  var genstar = function(confColor, defColor, type) {
	var acceptedColors = { blue:1, yellow:1, gold:1, golden:1,
			green:1, violet:1, emerald:1, indigo:1, red:1, ruby:1 };
	var color = (confColor && acceptedColors[confColor])
			? confColor : defColor;
	var starURI = JSRC.BASE_STAR_URI;
	if(self.config.imageurl) {
		starURI = self.config.imageurl + '/';
		color = type;
	}
	var size = '';

	self.fullStar[type]  = starURI + color + size + '.png';
	self.halfStar[type]  = starURI + color + size + '-half.png';
	self.emptyStar[type] = starURI + size + 'gray.png';

	if ( ! self.config.imageurl) {
		self.miniFullStar[type]  = starURI + color + '-tiny.png';
		self.miniEmptyStar[type] = starURI + 'gray-tiny.png';
		self.miniStarWidth = 9;
		self.miniStarHeight = 9;
	} else {
		self.miniFullStar[type]  = self.fullStar[type];
		self.miniEmptyStar[type] = self.emptyStar[type];
		self.miniStarWidth = self.starWidth;
		self.miniStarHeight = self.starHeight;
	}

	JSKitLib.preloadImg(self.fullStar[type]);
	JSKitLib.preloadImg(self.halfStar[type]);
	JSKitLib.preloadImg(self.emptyStar[type]);
	JSKitLib.preloadImg(self.miniFullStar[type]);
	JSKitLib.preloadImg(self.miniEmptyStar[type]);

  }

  genstar(this.config.starcolor, 'ruby', 'star');
  genstar(this.config.usercolor, 'gold', 'user');
}

/* Create our global JSKit object */
$JSKitGlobal = JSKitGlobal.getInstance();

/* Init a single JSRC object */
if ( ! $JRA.length) {
  new JSRC();
  $JSKitGlobal.setRatingsAppAvailable();
}


/* CSS Stylings */
document.write('<style type="text/css">'
  + '.js-rating-labelText { padding-top: 2px; font-size: 11px; text-align: center; cursor: default; -moz-user-select: none; }'
  + '.js-rating-splitObjectRating { margin-right: 10px; }'
  + '.js-rating-afterRating { width: 100px; font-size: 12px; text-align: center; padding: .3em;}'
  + '.js-rating-windowWrapper { border: 1px solid #ccc; }'
  + '.js-rating-window {  background: #ffc; border: none; filter: alpha(opacity=90); opacity: 0.9; padding: .3em; }'
  + '.js-rating-menuArrow { width:15px; height:15px; margin-left:' + JSRC.INFO_IMG_OFFSET + 'px; cursor:pointer; float: left; }'
  + '.js-rating-infoBox { color: black; padding: .3em; text-align:left; -moz-user-select: none; }'
  + '.js-rating-infoBoxStats { padding-bottom: 1em; line-height: 12pt;  }'
  + '.js-rating-infoBoxText { font-size: 9pt; }'
  + '.js-rating-infoBoxTextEm { font-size: 9pt; color: #a00; }'
  + '</style>'
)

JSRC.prototype.html = function(text) {
	var div = this.cr("div");
	div.innerHTML = text;
	var ch = div.firstChild;
	div = null;
	return ch;
}

/* Will add a callback for post rating processing */
JSRC.prototype.addOnRate = function(action) {
  this.onRate.push(action);
}

JSRC.prototype.processOnRate = function() {
  for (var i=0; i < this.onRate.length; i++) {
    this.onRate[i]();
  }
}

JSRC.prototype.table = function(content) {
  var self = this;
  var a = function(n, w) {var o=self.cr(n);o.appendChild(w);return o;}
  var t = a('table', a('tbody', a('tr', a('td', content))));
  var z = function(a) {t.setAttribute(a, '0')}
  z('cellSpacing');
  z('cellPadding');
  z('border');
  return t;
}

JSRC.prototype.display = function() {

  var self = this;

  // wrapper for our floated elements
  var wrapper = this.cr('div');
  wrapper.style.margin = '3px';
  wrapper.style.position = 'relative';
  wrapper.onselectstart = function() { return false; }

  var actionable = (this.config.freeze == "yes") ? false : true;

  if (this.config.view.match(/(combo|user)/) && this.config.freeze == "yes") {
      this.userRatingBar = this.initRating(this.objEffRating, 'star', false);
  } else {
      this.userRatingBar = this.initRating(this.userRating, 'user', actionable);
  }

  this.userRatingDiv = this.cr('div');
  this.userRatingDiv.appendChild(this.userRatingBar);

if (this.config.subtext != 'no') {
  this.textTotal = this.cr('div');
  this.textTotal.className = 'js-rating-labelText';
  this.refreshTextTotal();
}

  if (this.config.view.match(/split/)) {
    // split view : community and user ratings
    this.defaultView = 'user';

    var starRatingBar = this.initRating(this.objEffRating, 'star', false);
    var starRatingDiv = this.cr('div');
        starRatingDiv.className = 'js-rating-splitObjectRating';
        starRatingDiv.style.cssFloat = 'left';
        starRatingDiv.style.styleFloat = 'left';
        starRatingDiv.style.width = this.ratingBarWidth + 'px';
        starRatingDiv.appendChild(starRatingBar);
        starRatingDiv.appendChild(this.textTotal);

    wrapper.appendChild(starRatingDiv);

    this.userRatingDiv.style.cssFloat = 'left';
    this.userRatingDiv.style.styleFloat = 'left';
    this.userRatingDiv.style.width = this.ratingBarWidth + 'px';

if (this.config.subtext != 'no') {
    this.textRating = this.cr('div');
    this.textRating.className = 'js-rating-labelText';
    this.refreshTextRating();
    this.userRatingDiv.appendChild(this.textRating);

    this.activeText = this.textRating;
}
    this.totalWidth = this.ratingBarWidth + this.ratingBarWidth + 10;

    wrapper.appendChild(this.userRatingDiv);

  } else if (this.config.view.match(/user/)) {
    // single star set, only shows current user's rating
    this.defaultView = 'user';

    this.userRatingDiv.style.cssFloat = 'left';
    this.userRatingDiv.style.styleFloat = 'left';

if (this.config.subtext != 'no') {
    this.textRating = this.cr('div');
    this.textRating.className = 'js-rating-labelText';
    this.refreshTextRating();
    this.userRatingDiv.appendChild(this.textRating);

    this.activeText = this.textRating;
}

    this.totalWidth = this.ratingBarWidth;
    wrapper.appendChild(this.userRatingDiv);

  } else {
    // single star set, defaults to community rating
    this.defaultView = 'star';

    this.userRatingDiv.style.cssFloat = 'left';
    this.userRatingDiv.style.styleFloat = 'left'; 

if (this.config.subtext != 'no') {
    this.userRatingDiv.appendChild(this.textTotal);
    this.activeText = this.textTotal;
}

    this.totalWidth = this.ratingBarWidth;

    wrapper.appendChild(this.userRatingDiv);
  }

  // Set our total width
  wrapper.style.width = this.totalWidth + 'px';

  /* Rating Menu */
  if (this.config.menu != 'no') {
    wrapper.style.width = (this.totalWidth + 10 + JSRC.INFO_IMG_WIDTH) + 'px';
    var menuArrow = this.createMenuArrow();
    this.prepMenu(); // 'i' and infobox
    wrapper.appendChild(menuArrow);

  }

  // Set the target width
  var targetMinWidth = parseInt(wrapper.style.width) + 6; // 3px margin
  var targetWidth = this.target.style.width || targetMinWidth;
  if (parseInt(targetWidth) <= targetMinWidth) {
    this.target.style.width = targetMinWidth + 'px';
  }

  if (( ! this.isStandalone()) && this.config.commentprompt != 'no') {

    var addCommentPrompt = function() {

      var afterRatingA = document.createElement('a');
      afterRatingA.appendChild(document.createTextNode($JRL('addACommentToYourRating')));
      afterRatingA.onclick = function() { 
        self.getCommentsAppObject().ShowCommentDialog(null);
        return false;
      };
      afterRatingA.href = 'javascript:void(0);';
  
      var afterRatingDiv = document.createElement('div');
      afterRatingDiv.appendChild(afterRatingA);
      afterRatingDiv.className = 'js-rating-afterRating';

      var afterRating = self.createWindow(afterRatingDiv);
      afterRating.style.position ='absolute';
      afterRating.style.left = (self.totalWidth + 5) + 'px';
      afterRating.style.top = '-4px';
      afterRating.style.zIndex = '110'; // above menuArrow
      JSKitLib.hide(afterRating);

      self.addOnRate(function() { 
        JSKitLib.show(afterRating);
        setTimeout(function() { JSKitLib.hide(afterRating); }, 5000);
      });

  
      wrapper.appendChild(afterRating);
    }
    $JSKitGlobal.tryCommentsAppObjectAction(this.uniq, addCommentPrompt); 
  }

  this.target.appendChild(this.table(wrapper)); // stars

  if ( ! this.config.view.match(/split/)) {
    this.refreshRating();  
  }

}

// generic jskit body tag fror absolutely position elements
JSRC.prototype.createBodyElement = function() {
  if ( ! document.getElementById('js-kit-body-element')) {
    var be = this.cr('div');
    be.id = "js-kit-body-element";
    document.body.appendChild(be);
  }
}

// Adds the 'i' button and infobox
JSRC.prototype.prepMenu = function() {
  var self = this;
  var prepMenu = function() {
    self.createBodyElement();

    var infoBox = self.cr('div');
    self.infoBox = infoBox;

    document.getElementById('js-kit-body-element').appendChild(infoBox);

    var infobox1Show = infobox2Show = false;

    var infoBoxMouseover = function() {
      clearTimeout(self.ratingMenuTimer);
    }

    self.target.onmouseover = function() { 
      infobox1Show = true; 
      infoBoxMouseover(); 
      JSKitLib.show(self.menuArrow);
    }
    self.infoBox.onmouseover = function() { infobox2Show = true; infoBoxMouseover(); }

    var infoBoxMouseout = function() {

      if (infobox1Show || infobox2Show) 
        return;

      self.ratingMenuTimer = setTimeout(function() {
        self.ratingMenuTimer = null;
          self.hideInfoBox();
          JSKitLib.hide(self.menuArrow);
      }, 1500);
    }

    self.target.onmouseout = function() { infobox1Show = false;  infoBoxMouseout(); }
    self.infoBox.onmouseout = function() { infobox2Show = false; infoBoxMouseout(); } 

    self.infoBox.onclick = function(e) { 
      self.toggleInfoBox(); 
      JSKitLib.stopEventPropagation(e);
    }
  };

  // document.body.append functionality can only happen after window.onload in IE
  JSKitLib.deferCallIfIE(prepMenu);

}

JSRC.prototype.createMenuArrow = function() {
  this.menuArrow = document.createElement('div');
  this.menuArrow.className = 'js-rating-menuArrow';
  JSKitLib.hide(this.menuArrow);
  JSKitLib.addPNG(this.menuArrow, JSRC.INFO_IMG);

  this.infoBoxImg = this.menuArrow;
  var self = this;
  this.menuArrow.onclick = function() { 
    self.toggleInfoBox();
  }

  return this.menuArrow;;
}

JSRC.prototype.showInfoBox = function() {
  var infoBox = this.createInfoBox();
  this.infoBox.appendChild(infoBox);
}

JSRC.prototype.hideInfoBox = function() {
  if (this.infoBox) {
    while (this.infoBox.hasChildNodes()) {
      this.infoBox.removeChild(this.infoBox.firstChild);
    }
  }
}

JSRC.prototype.toggleInfoBox = function() {
  if (this.infoBox && this.infoBox.hasChildNodes()) {
    this.hideInfoBox();
  } else {
    this.showInfoBox();
  }
}

JSRC.prototype.refreshInfoBox = function() {
  if (this.infoBox && this.infoBox.hasChildNodes()) {
    this.hideInfoBox();
    this.showInfoBox();
  }
}

JSRC.prototype.createWindow = function(content, opts) {

  if (typeof opts != 'object') opts = {};

  var wrapper = document.createElement('div');
  wrapper.className = 'js-rating-windowWrapper';

  var box = document.createElement('div');
  box.className = 'js-rating-window';
  
  if (typeof content == 'string') {
    box.appendChild(this.html(content));

  } else {
    box.appendChild(content);
  }

  wrapper.appendChild(box);


  return wrapper;

}

JSRC.prototype.createInfoBox = function() {

  var INFOBOX_WINDOW_WIDTH = 170;

  var pos = JSKitLib.findPos(this.target);
  var html = '<div class="js-rating-infoBox" onselectstart="return false">';

  html += '<div class="js-rating-infoBoxStats js-rating-infoBoxText">';

  if (this.config.freeze == 'yes') {
    html += '<span class="js-rating-infoBoxTextEm">' + $JRL('ratingsDisabled') + '</span><br>';
  }

  if (this.objNum) {
    html += this.getTextForTotalVotes(this.objNum);
    html += ' <span style="white-space: nowrap">(' + JSKitLib.zeroPad(this.objAvgStarRating, 2) + '&nbsp;' + $JRL('avgRating') + ')</span><br>';
  } else {
    if (this.config.freeze != 'yes') {
      html += $JRL('noVotesReceivedYet') + '<br>';
    }
  }

  if (this.userRating) {
    html += $JRL('Tu voto') + ': ';
    html += (this.userRating / this.raterInc);
  } else {
    if (this.config.freeze != 'yes') {
      if (this.objNum) {
        html += $JRL('youHaveNotRatedYet');
      } else {
        html += $JRL('beTheFirstToRate');
      }
    }
  }
  html += '</div>';

  html += '<span class="js-rating-infoBoxText">' + $JRL('poweredBy') + ' <a href="http://js-kit.com/ratings/?wow" class="js-rating-infoBoxText" target="_blank">JS-Kit</a></span>';
  html += '</div>';
  var self = this;
  var closeInfoBox = function() {
    self.hideInfoBox();
  }
  var node = this.createWindow(html);
  node.style.position = 'absolute';

  node.style.top = pos[3] + 'px';

  // If rating widget is too close to left side, show on the right side
  if (pos[0] > INFOBOX_WINDOW_WIDTH || this.totalWidth >= INFOBOX_WINDOW_WIDTH) {
    node.style.left = (pos[2] - INFOBOX_WINDOW_WIDTH - 6) + 'px'; // 3px margin
  } else {
    node.style.left = pos[0] + 'px'; // 3px margin
  }
  
  node.style.width = INFOBOX_WINDOW_WIDTH + 'px';

  node.style.zIndex = '1000';
  return node;
}

/* Process all rating objects with the same ID */
JSRC.prototype.processSiblings = function(handler) {
  for (var i=0; i < $JRH[this.uniq].length; i++) {
    // property must match as well
    if (this.config.property || $JRH[this.uniq][i].config.property) {
      if ($JRH[this.uniq][i].config.property == this.config.property) {
        handler($JRH[this.uniq][i]);
      }
    } else {
        handler($JRH[this.uniq][i]);
    }
  }
}

JSRC.prototype.rate = function(givenRating) {
  var oldRating = this.userRating;
  this.setUserRating(givenRating);
  var objSum = this.objSum;
  var objNum = this.objNum;
  var objVotes = this.objVotes;
  if(oldRating) {
    objSum -= oldRating;
    objNum --;
  }
  this.setTmpText($JRL('thankYou'));

  // Update all ratings for this ID
  this.processSiblings(function(sibling) {
    //TODO: determine if current user increments objVotes count
    sibling.newRating({ Sum: objSum + givenRating, Num: objNum + 1, Votes: objVotes }, { Sum: givenRating });
  });

  // Refresh the InfoBox 
  if (this.config.menu != 'no') {
    this.refreshInfoBox();
  }

  // Handle any callbacks
  this.processOnRate();

  // TODO: parametric rating
  if (window.$J$PRA && typeof $J$PRA == 'object') {
    for (var i=0; i < $J$PRA.length; i++) {
      if ($J$PRA[i].path == this.path) {
        $J$PRA[i].onRate();
      }
    }
  }

  var title = this.config.title || "";
  this.server('put', "rating=" + givenRating
    + (this.config.property ? "&property=" + this.config.property : "")
	+ (title ? ("&title=" + encodeURIComponent(title)) : "")
	+ (this.config.notop ? "&notop=true" : "")
	+ (this.config.permalink ? "&permalink=" + encodeURIComponent(this.config.permalink) : "")
	);
}

JSRC.prototype.setUserRating = function(rating) {
  this.userRating = rating;
}

// Returns: an array of actionable rating icons 
JSRC.prototype.getRatingIcons = function() {

  if (this._ratingIcons && this._ratingIcons.length > 0) {
    return this._ratingIcons;
  }

  this._ratingIcons = this._getIcons('js-kit-rater');
  return this._ratingIcons;
}

JSRC.prototype.getObjIcons = function() {

  if (this._objIcons && this._objIcons.length > 0) {
    return this._objIcons;
  }

  this._objIcons = this._getIcons('js-kit-objIcon');
  return this._objIcons;
}

JSRC.prototype._getIcons = function(iconClass) {

  var divs = this.target.getElementsByTagName('div');
  var icons = [];
  for (var i=0; i < divs.length; i++) {
    if (divs[i].className && divs[i].className.indexOf(iconClass) >= 0) {
      icons.push(divs[i]);
    }
  }
  return icons;
}

JSRC.prototype.getTextForTotalVotes = function(votes) {
  var text;
  switch(votes) {
    case  1: text = votes + ' ' + $JRL('vote');  break;
    default: text = votes + ' ' + $JRL('votes'); break;
  }
  return text;
}

JSRC.prototype.getTextForUserRating = function(rating) {
  var text = $JRL('yourRatingTitleCase') + ': ' + rating;
  return text;
}

JSRC.prototype.refreshTextTotal = function() {
  var text = (this.objNum) ? this.getTextForTotalVotes(this.objNum) : $JRL('unrated');
  this.setTextTotal(text);
}

JSRC.prototype.refreshTextRating = function(text) {
  if (this.userRating) {
    var text = this.getTextForUserRating(this.userRating / this.raterInc);
  } else { 
    var text = $JRL('yourRatingTitleCase');
  }
  this.setTextRating(text);
}

JSRC.prototype.setTextRating = function(text) {
  this._setText(this.textRating, text);
}

JSRC.prototype.setTextTotal = function(text) {
  this.lastSetText = text;
  if(this.tmpTextTimer)
	return;
  this._setText(this.textTotal, text);
}

JSRC.prototype.setActiveText = function(text) {
  this._setText(this.activeText, text);
}

JSRC.prototype.setTmpText = function(text) {
  var self = this;
  if(this.tmpTextTimer)
    clearTimeout(this.tmpTextTimer);
  this.tmpTextTimer = setTimeout(function() {
	self.tmpTextTimer = null;
	self.setTextTotal(self.lastSetText);
    }, 1000);
  this._setText(this.textTotal, text);
}

JSRC.prototype._setText = function(node, text) {
  if ( ! node) {
    return;
  }
  while (node.hasChildNodes()) {
    node.removeChild(node.firstChild);
  }
  node.appendChild(document.createTextNode(text));
}

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

	JSKitLib.addPNG(star, imageURL);    
}

// Handles the hover state for the actionable stars
JSRC.prototype.hover = function(index) {

  if(this.tmpTextTimer) return;

  // The text which is under the hover state
  this.setActiveText($JRL('rateThis') + ': ' + (index / this.raterInc));

  var icons = this.getRatingIcons();
  for (var i=0; i < icons.length; i++) {
    if (index > (i * this.raterInc)) {
	this.setImage(icons[i], this.fullStar['user']);
    } else {
	this.setImage(icons[i], this.emptyStar['user']);
    }
  }

}

JSRC.prototype.refreshObjRating = function() {
  var icons = this.getObjIcons();
  this._refreshRating('star', this.objEffRating, icons);
}

// Resets the user rating view to their actual rating
JSRC.prototype.refreshRating = function() {

  if (this.defaultView == 'star') {
    var type = 'star';
    var comparison = this.objEffRating;
  } else {
    var type = 'user';
    var comparison = this.userRating;
  }

  var icons = this.getRatingIcons();

  this._refreshRating(type, comparison, icons);

  if (this.defaultView == 'star') {
    this.refreshTextTotal();
  } else {
    this.refreshTextRating();
  }
}

JSRC.prototype._refreshRating = function(type, comparison, icons) {

  for (var i=0; i < icons.length; i++) {
    if (comparison > (i * this.raterInc)) {
      if (i * this.raterInc + (this.raterInc / 2) == comparison) {
        this.setImage(icons[i], this.halfStar[type]);
      } else {
        this.setImage(icons[i], this.fullStar[type]);
      }
    } else {
      this.setImage(icons[i], this.emptyStar[type]);
    }
  }
}  

JSRC.prototype.initRating = function(rating, type, actionable) {
  var self = this;
  var node = this.cr('div');
  node.style.width = this.ratingBarWidth + 'px';
  node.style.height = this.ratingBarHeight + 'px';

  var inf = function() {
	if(self.refreshScheduled)
		clearTimeout(self.refreshScheduled);
  }
  var outf = function() {
	if(self.refreshScheduled)
		clearTimeout(self.refreshScheduled);
	self.refreshScheduled = setTimeout(
		function(){self.refreshScheduled=null;
		self.refreshRating()}, 300);
  }

  node.onmouseover = function() {
			if(self.refreshScheduled)
				clearTimeout(self.refreshScheduled);
		}
  node.onmouseout = outf;

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

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

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

    if (rating + this.raterInc > i) {
      if (rating + this.raterInc - i >=  this.raterInc) {
	this.setImage(star, this.fullStar[type]);
      } else {
	this.setImage(star, this.halfStar[type]);
      }
    } else {
      this.setImage(star, this.emptyStar[type]);
    }

    if (actionable) {
     (function(i) {
      star.className += ' js-kit-rater';
      star.onmouseover = function() { inf(); self.hover(i); }
      star.onmouseout  = outf;
      star.onclick     = function() { self.rate(i); }
     })(i);
    } else {
      star.className += ' js-kit-objIcon';
    }
    node.appendChild(star);
  }

  if (actionable) {
    node.style.cursor = 'pointer';
  }

  return node;
}


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

JSRC.prototype.hasCommentsAppObject = function() {
  return this.getCommentsAppObject() ? true : false;
}

JSRC.prototype.clone = function(node, options) {
  if ( ! options) {
    options = {};
  }

  var clone = new JSRC(node, {
    'newRating' : {
      'objSum' : this.objSum,
      'objNum' : this.objNum,
      'userRating' : this.userRating
    },
    'path' : options.path || this.config.path,
    'uniq' : options.uniq || this.config.uniq,
    'view' : options.view || this.config.view,
    'notop' : options.notop || this.config.notop,
    'commentprompt' : options.commentprompt || this.config.commentprompt,
    'starcolor' : options.starcolor || this.config.starcolor,
    'usercolor' : options.usercolor || this.config.usercolor,
    'imageurl' : options.imageurl || this.config.imageurl,
    'imagesize' : options.imagesize || this.config.imagesize,
    'menu' : options.menu || this.config.menu

  });

  return clone;
}

JSRC.prototype.newRating = function() {
  var args = arguments;
  if(typeof args[0] != 'object')
    args = [ args[3], args[4] || {} ];
  var community = args[0];
  var user = args[1] || { Sum: 0 };

  if(user.frozen) this.config.freeze = "yes";

  this.objSum = community.Sum;
  this.objNum = community.Num;
  this.objVotes = community.Votes || community.Num;
  this.userRating = user.Sum;
  this.objAvgStarRating = JSKitLib.round((this.objSum / this.objNum) / this.raterInc, 1);
  this.objEffRating = Math.round(this.objSum / this.objNum) || 0;  // Used for star display purposes

  if(this.refreshScheduled) {
	clearTimeout(this.refreshScheduled);
	this.refreshScheduled = null;
  }

  if (this.constructed) {
    this.refreshTextTotal();
    this.refreshObjRating();
    this.refreshRating();
  } else {
    this.constructed = true;
    this.display();
  }

  // TODO: use JSKitGlobal
  if (window.$J$PRA && typeof $J$PRA == 'object') {
    for (var i=0; i < $J$PRA.length; i++) {
        $J$PRA[i].updateComposite();
    }
  }


}

