You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
461 lines
12 KiB
461 lines
12 KiB
6 years ago
|
/*!
|
||
|
* jQuery Raty - A Star Rating Plugin - http://wbotelhos.com/raty
|
||
|
* -------------------------------------------------------------------
|
||
|
*
|
||
|
* jQuery Raty is a plugin that generates a customizable star rating.
|
||
|
*
|
||
|
* Licensed under The MIT License
|
||
|
*
|
||
|
* @version 2.4.5
|
||
|
* @since 2010.06.11
|
||
|
* @author Washington Botelho
|
||
|
* @documentation wbotelhos.com/raty
|
||
|
* @twitter twitter.com/wbotelhos
|
||
|
*
|
||
|
* Usage:
|
||
|
* -------------------------------------------------------------------
|
||
|
* $('#star').raty();
|
||
|
*
|
||
|
* <div id="star"></div>
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
;(function($) {
|
||
|
|
||
|
var methods = {
|
||
|
init: function(settings) {
|
||
|
return this.each(function() {
|
||
|
var self = this,
|
||
|
$this = $(self).empty();
|
||
|
|
||
|
self.opt = $.extend(true, {}, $.fn.raty.defaults, settings);
|
||
|
|
||
|
$this.data('settings', self.opt);
|
||
|
|
||
|
self.opt.number = methods.between(self.opt.number, 0, 20);
|
||
|
|
||
|
if (self.opt.path.substring(self.opt.path.length - 1, self.opt.path.length) != '/') {
|
||
|
self.opt.path += '/';
|
||
|
}
|
||
|
|
||
|
if (typeof self.opt.score == 'function') {
|
||
|
self.opt.score = self.opt.score.call(self);
|
||
|
}
|
||
|
|
||
|
if (self.opt.score) {
|
||
|
self.opt.score = methods.between(self.opt.score, 0, self.opt.number);
|
||
|
}
|
||
|
|
||
|
for (var i = 1; i <= self.opt.number; i++) {
|
||
|
$('<img />', {
|
||
|
src : self.opt.path + ((!self.opt.score || self.opt.score < i) ? self.opt.starOff : self.opt.starOn),
|
||
|
alt : i,
|
||
|
title : (i <= self.opt.hints.length && self.opt.hints[i - 1] !== null) ? self.opt.hints[i - 1] : i
|
||
|
}).appendTo(self);
|
||
|
|
||
|
if (self.opt.space) {
|
||
|
$this.append((i < self.opt.number) ? ' ' : '');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
self.stars = $this.children('img:not(".raty-cancel")');
|
||
|
self.score = $('<input />', { type: 'hidden', name: self.opt.scoreName }).appendTo(self);
|
||
|
|
||
|
if (self.opt.score && self.opt.score > 0) {
|
||
|
self.score.val(self.opt.score);
|
||
|
methods.roundStar.call(self, self.opt.score);
|
||
|
}
|
||
|
|
||
|
if (self.opt.iconRange) {
|
||
|
methods.fill.call(self, self.opt.score);
|
||
|
}
|
||
|
|
||
|
methods.setTarget.call(self, self.opt.score, self.opt.targetKeep);
|
||
|
|
||
|
var space = self.opt.space ? 4 : 0,
|
||
|
width = self.opt.width || (self.opt.number * self.opt.size + self.opt.number * space);
|
||
|
|
||
|
if (self.opt.cancel) {
|
||
|
self.cancel = $('<img />', { src: self.opt.path + self.opt.cancelOff, alt: 'x', title: self.opt.cancelHint, 'class': 'raty-cancel' });
|
||
|
|
||
|
if (self.opt.cancelPlace == 'left') {
|
||
|
$this.prepend(' ').prepend(self.cancel);
|
||
|
} else {
|
||
|
$this.append(' ').append(self.cancel);
|
||
|
}
|
||
|
|
||
|
width += (self.opt.size + space);
|
||
|
}
|
||
|
|
||
|
if (self.opt.readOnly) {
|
||
|
methods.fixHint.call(self);
|
||
|
|
||
|
if (self.cancel) {
|
||
|
self.cancel.hide();
|
||
|
}
|
||
|
} else {
|
||
|
$this.css('cursor', 'pointer');
|
||
|
|
||
|
methods.bindAction.call(self);
|
||
|
}
|
||
|
|
||
|
$this.css('width', width);
|
||
|
});
|
||
|
}, between: function(value, min, max) {
|
||
|
return Math.min(Math.max(parseFloat(value), min), max);
|
||
|
}, bindAction: function() {
|
||
|
var self = this,
|
||
|
$this = $(self);
|
||
|
|
||
|
$this.mouseleave(function() {
|
||
|
var score = self.score.val() || undefined;
|
||
|
|
||
|
methods.initialize.call(self, score);
|
||
|
methods.setTarget.call(self, score, self.opt.targetKeep);
|
||
|
|
||
|
if (self.opt.mouseover) {
|
||
|
self.opt.mouseover.call(self, score);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
var action = self.opt.half ? 'mousemove' : 'mouseover';
|
||
|
|
||
|
if (self.opt.cancel) {
|
||
|
self.cancel.mouseenter(function() {
|
||
|
$(this).attr('src', self.opt.path + self.opt.cancelOn);
|
||
|
|
||
|
self.stars.attr('src', self.opt.path + self.opt.starOff);
|
||
|
|
||
|
methods.setTarget.call(self, null, true);
|
||
|
|
||
|
if (self.opt.mouseover) {
|
||
|
self.opt.mouseover.call(self, null);
|
||
|
}
|
||
|
}).mouseleave(function() {
|
||
|
$(this).attr('src', self.opt.path + self.opt.cancelOff);
|
||
|
|
||
|
if (self.opt.mouseover) {
|
||
|
self.opt.mouseover.call(self, self.score.val() || null);
|
||
|
}
|
||
|
}).click(function(evt) {
|
||
|
self.score.removeAttr('value');
|
||
|
|
||
|
if (self.opt.click) {
|
||
|
self.opt.click.call(self, null, evt);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
self.stars.bind(action, function(evt) {
|
||
|
var value = parseInt(this.alt, 10);
|
||
|
|
||
|
if (self.opt.half) {
|
||
|
var position = parseFloat((evt.pageX - $(this).offset().left) / self.opt.size),
|
||
|
diff = (position > .5) ? 1 : .5;
|
||
|
|
||
|
value = parseFloat(this.alt) - 1 + diff;
|
||
|
|
||
|
methods.fill.call(self, value);
|
||
|
|
||
|
if (self.opt.precision) {
|
||
|
value = value - diff + position;
|
||
|
}
|
||
|
|
||
|
methods.showHalf.call(self, value);
|
||
|
} else {
|
||
|
methods.fill.call(self, value);
|
||
|
}
|
||
|
|
||
|
$this.data('score', value);
|
||
|
|
||
|
methods.setTarget.call(self, value, true);
|
||
|
|
||
|
if (self.opt.mouseover) {
|
||
|
self.opt.mouseover.call(self, value, evt);
|
||
|
}
|
||
|
}).click(function(evt) {
|
||
|
self.score.val((self.opt.half || self.opt.precision) ? $this.data('score') : this.alt);
|
||
|
|
||
|
if (self.opt.click) {
|
||
|
self.opt.click.call(self, self.score.val(), evt);
|
||
|
}
|
||
|
});
|
||
|
}, cancel: function(isClick) {
|
||
|
return $(this).each(function() {
|
||
|
var self = this,
|
||
|
$this = $(self);
|
||
|
|
||
|
if ($this.data('readonly') === true) {
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
if (isClick) {
|
||
|
methods.click.call(self, null);
|
||
|
} else {
|
||
|
methods.score.call(self, null);
|
||
|
}
|
||
|
|
||
|
self.score.removeAttr('value');
|
||
|
});
|
||
|
}, click: function(score) {
|
||
|
return $(this).each(function() {
|
||
|
if ($(this).data('readonly') === true) {
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
methods.initialize.call(this, score);
|
||
|
|
||
|
if (this.opt.click) {
|
||
|
this.opt.click.call(this, score);
|
||
|
} else {
|
||
|
methods.error.call(this, 'you must add the "click: function(score, evt) { }" callback.');
|
||
|
}
|
||
|
|
||
|
methods.setTarget.call(this, score, true);
|
||
|
});
|
||
|
}, error: function(message) {
|
||
|
$(this).html(message);
|
||
|
|
||
|
$.error(message);
|
||
|
}, fill: function(score) {
|
||
|
var self = this,
|
||
|
number = self.stars.length,
|
||
|
count = 0,
|
||
|
$star ,
|
||
|
star ,
|
||
|
icon ;
|
||
|
|
||
|
for (var i = 1; i <= number; i++) {
|
||
|
$star = self.stars.eq(i - 1);
|
||
|
|
||
|
if (self.opt.iconRange && self.opt.iconRange.length > count) {
|
||
|
star = self.opt.iconRange[count];
|
||
|
|
||
|
if (self.opt.single) {
|
||
|
icon = (i == score) ? (star.on || self.opt.starOn) : (star.off || self.opt.starOff);
|
||
|
} else {
|
||
|
icon = (i <= score) ? (star.on || self.opt.starOn) : (star.off || self.opt.starOff);
|
||
|
}
|
||
|
|
||
|
if (i <= star.range) {
|
||
|
$star.attr('src', self.opt.path + icon);
|
||
|
}
|
||
|
|
||
|
if (i == star.range) {
|
||
|
count++;
|
||
|
}
|
||
|
} else {
|
||
|
if (self.opt.single) {
|
||
|
icon = (i == score) ? self.opt.starOn : self.opt.starOff;
|
||
|
} else {
|
||
|
icon = (i <= score) ? self.opt.starOn : self.opt.starOff;
|
||
|
}
|
||
|
|
||
|
$star.attr('src', self.opt.path + icon);
|
||
|
}
|
||
|
}
|
||
|
}, fixHint: function() {
|
||
|
var $this = $(this),
|
||
|
score = parseInt(this.score.val(), 10),
|
||
|
hint = this.opt.noRatedMsg;
|
||
|
|
||
|
if (!isNaN(score) && score > 0) {
|
||
|
hint = (score <= this.opt.hints.length && this.opt.hints[score - 1] !== null) ? this.opt.hints[score - 1] : score;
|
||
|
}
|
||
|
|
||
|
$this.data('readonly', true).css('cursor', 'default').attr('title', hint);
|
||
|
|
||
|
this.score.attr('readonly', 'readonly');
|
||
|
this.stars.attr('title', hint);
|
||
|
}, getScore: function() {
|
||
|
var score = [],
|
||
|
value ;
|
||
|
|
||
|
$(this).each(function() {
|
||
|
value = this.score.val();
|
||
|
|
||
|
score.push(value ? parseFloat(value) : undefined);
|
||
|
});
|
||
|
|
||
|
return (score.length > 1) ? score : score[0];
|
||
|
}, readOnly: function(isReadOnly) {
|
||
|
return this.each(function() {
|
||
|
var $this = $(this);
|
||
|
|
||
|
if ($this.data('readonly') === isReadOnly) {
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
if (this.cancel) {
|
||
|
if (isReadOnly) {
|
||
|
this.cancel.hide();
|
||
|
} else {
|
||
|
this.cancel.show();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (isReadOnly) {
|
||
|
$this.unbind();
|
||
|
|
||
|
$this.children('img').unbind();
|
||
|
|
||
|
methods.fixHint.call(this);
|
||
|
} else {
|
||
|
methods.bindAction.call(this);
|
||
|
methods.unfixHint.call(this);
|
||
|
}
|
||
|
|
||
|
$this.data('readonly', isReadOnly);
|
||
|
});
|
||
|
}, reload: function() {
|
||
|
return methods.set.call(this, {});
|
||
|
}, roundStar: function(score) {
|
||
|
var diff = (score - Math.floor(score)).toFixed(2);
|
||
|
|
||
|
if (diff > this.opt.round.down) {
|
||
|
var icon = this.opt.starOn; // Full up: [x.76 .. x.99]
|
||
|
|
||
|
if (diff < this.opt.round.up && this.opt.halfShow) { // Half: [x.26 .. x.75]
|
||
|
icon = this.opt.starHalf;
|
||
|
} else if (diff < this.opt.round.full) { // Full down: [x.00 .. x.5]
|
||
|
icon = this.opt.starOff;
|
||
|
}
|
||
|
|
||
|
this.stars.eq(Math.ceil(score) - 1).attr('src', this.opt.path + icon);
|
||
|
} // Full down: [x.00 .. x.25]
|
||
|
}, score: function() {
|
||
|
return arguments.length ? methods.setScore.apply(this, arguments) : methods.getScore.call(this);
|
||
|
}, set: function(settings) {
|
||
|
this.each(function() {
|
||
|
var $this = $(this),
|
||
|
actual = $this.data('settings'),
|
||
|
clone = $this.clone().removeAttr('style').insertBefore($this);
|
||
|
|
||
|
$this.remove();
|
||
|
|
||
|
clone.raty($.extend(actual, settings));
|
||
|
});
|
||
|
|
||
|
return $(this.selector);
|
||
|
}, setScore: function(score) {
|
||
|
return $(this).each(function() {
|
||
|
if ($(this).data('readonly') === true) {
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
methods.initialize.call(this, score);
|
||
|
methods.setTarget.call(this, score, true);
|
||
|
});
|
||
|
}, setTarget: function(value, isKeep) {
|
||
|
if (this.opt.target) {
|
||
|
var $target = $(this.opt.target);
|
||
|
|
||
|
if ($target.length == 0) {
|
||
|
methods.error.call(this, 'target selector invalid or missing!');
|
||
|
}
|
||
|
|
||
|
var score = value;
|
||
|
|
||
|
if (!isKeep || score === undefined) {
|
||
|
score = this.opt.targetText;
|
||
|
} else {
|
||
|
if (this.opt.targetType == 'hint') {
|
||
|
score = (score === null && this.opt.cancel)
|
||
|
? this.opt.cancelHint
|
||
|
: this.opt.hints[Math.ceil(score - 1)];
|
||
|
} else {
|
||
|
score = this.opt.precision
|
||
|
? parseFloat(score).toFixed(1)
|
||
|
: parseInt(score, 10);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (this.opt.targetFormat.indexOf('{score}') < 0) {
|
||
|
methods.error.call(this, 'template "{score}" missing!');
|
||
|
}
|
||
|
|
||
|
if (value !== null) {
|
||
|
score = this.opt.targetFormat.toString().replace('{score}', score);
|
||
|
}
|
||
|
|
||
|
if ($target.is(':input')) {
|
||
|
$target.val(score);
|
||
|
} else {
|
||
|
$target.html(score);
|
||
|
}
|
||
|
}
|
||
|
}, showHalf: function(score) {
|
||
|
var diff = (score - Math.floor(score)).toFixed(1);
|
||
|
|
||
|
if (diff > 0 && diff < .6) {
|
||
|
this.stars.eq(Math.ceil(score) - 1).attr('src', this.opt.path + this.opt.starHalf);
|
||
|
}
|
||
|
}, initialize: function(score) {
|
||
|
score = !score ? 0 : methods.between(score, 0, this.opt.number);
|
||
|
|
||
|
methods.fill.call(this, score);
|
||
|
|
||
|
if (score > 0) {
|
||
|
if (this.opt.halfShow) {
|
||
|
methods.roundStar.call(this, score);
|
||
|
}
|
||
|
|
||
|
this.score.val(score);
|
||
|
}
|
||
|
}, unfixHint: function() {
|
||
|
for (var i = 0; i < this.opt.number; i++) {
|
||
|
this.stars.eq(i).attr('title', (i < this.opt.hints.length && this.opt.hints[i] !== null) ? this.opt.hints[i] : i);
|
||
|
}
|
||
|
|
||
|
$(this).data('readonly', false).css('cursor', 'pointer').removeAttr('title');
|
||
|
|
||
|
this.score.attr('readonly', 'readonly');
|
||
|
}
|
||
|
};
|
||
|
|
||
|
$.fn.raty = function(method) {
|
||
|
if (methods[method]) {
|
||
|
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
||
|
} else if (typeof method === 'object' || !method) {
|
||
|
return methods.init.apply(this, arguments);
|
||
|
} else {
|
||
|
$.error('Method ' + method + ' does not exist!');
|
||
|
}
|
||
|
};
|
||
|
|
||
|
$.fn.raty.defaults = {
|
||
|
cancel : false,
|
||
|
cancelHint : 'cancel this rating!',
|
||
|
cancelOff : 'cancel-off.png',
|
||
|
cancelOn : 'cancel-on.png',
|
||
|
cancelPlace : 'left',
|
||
|
click : undefined,
|
||
|
half : false,
|
||
|
halfShow : true,
|
||
|
hints : ['bad', 'poor', 'regular', 'good', 'gorgeous'],
|
||
|
iconRange : undefined,
|
||
|
mouseover : undefined,
|
||
|
noRatedMsg : 'not rated yet',
|
||
|
number : 5,
|
||
|
path : 'img/',
|
||
|
precision : false,
|
||
|
round : { down: .25, full: .6, up: .76 },
|
||
|
readOnly : false,
|
||
|
score : undefined,
|
||
|
scoreName : 'score',
|
||
|
single : false,
|
||
|
size : 16,
|
||
|
space : true,
|
||
|
starHalf : 'star-half.png',
|
||
|
starOff : 'star-off.png',
|
||
|
starOn : 'star-on.png',
|
||
|
target : undefined,
|
||
|
targetFormat : '{score}',
|
||
|
targetKeep : false,
|
||
|
targetText : '',
|
||
|
targetType : 'hint',
|
||
|
width : undefined
|
||
|
};
|
||
|
|
||
|
})(jQuery);
|