Last active
December 14, 2019 13:41
-
-
Save Kcko/0513c38707316c51801e47aa02f91dd7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* jQuery Raty - A Star Rating Plugin | |
* | |
* The MIT License | |
* | |
* author: Washington Botelho | |
* github: wbotelhos/raty | |
* version: 2.9.0 | |
* | |
*/ | |
(function($) { | |
'use strict'; | |
var methods = { | |
init: function(options) { | |
return this.each(function() { | |
this.self = $(this); | |
methods.destroy.call(this.self); | |
this.opt = $.extend(true, {}, $.fn.raty.defaults, options, this.self.data()); | |
methods._adjustCallback.call(this); | |
methods._adjustNumber.call(this); | |
methods._adjustHints.call(this); | |
this.opt.score = methods._adjustedScore.call(this, this.opt.score); | |
if (this.opt.starType !== 'img') { | |
methods._adjustStarType.call(this); | |
} | |
methods._adjustPath.call(this); | |
methods._createStars.call(this); | |
if (this.opt.cancel) { | |
methods._createCancel.call(this); | |
} | |
if (this.opt.precision) { | |
methods._adjustPrecision.call(this); | |
} | |
methods._createScore.call(this); | |
methods._apply.call(this, this.opt.score); | |
methods._setTitle.call(this, this.opt.score); | |
methods._target.call(this, this.opt.score); | |
if (this.opt.readOnly) { | |
methods._lock.call(this); | |
} else { | |
this.style.cursor = 'pointer'; | |
methods._binds.call(this); | |
} | |
}); | |
}, | |
_adjustCallback: function() { | |
var options = ['number', 'readOnly', 'score', 'scoreName', 'target', 'path']; | |
for (var i = 0; i < options.length; i++) { | |
if (typeof this.opt[options[i]] === 'function') { | |
this.opt[options[i]] = this.opt[options[i]].call(this); | |
} | |
} | |
}, | |
_adjustedScore: function(score) { | |
if (!score) { | |
return score; | |
} | |
return methods._between(score, 0, this.opt.number); | |
}, | |
_adjustHints: function() { | |
if (!this.opt.hints) { | |
this.opt.hints = []; | |
} | |
if (!this.opt.halfShow && !this.opt.half) { | |
return; | |
} | |
var steps = this.opt.precision ? 10 : 2; | |
for (var i = 0; i < this.opt.number; i++) { | |
var group = this.opt.hints[i]; | |
if (Object.prototype.toString.call(group) !== '[object Array]') { | |
group = [group]; | |
} | |
this.opt.hints[i] = []; | |
for (var j = 0; j < steps; j++) { | |
var | |
hint = group[j], | |
last = group[group.length - 1]; | |
if (last === undefined) { | |
last = null; | |
} | |
this.opt.hints[i][j] = hint === undefined ? last : hint; | |
} | |
} | |
}, | |
_adjustNumber: function() { | |
this.opt.number = methods._between(this.opt.number, 1, this.opt.numberMax); | |
}, | |
_adjustPath: function() { | |
this.opt.path = this.opt.path || ''; | |
if (this.opt.path && this.opt.path.charAt(this.opt.path.length - 1) !== '/') { | |
this.opt.path += '/'; | |
} | |
}, | |
_adjustPrecision: function() { | |
this.opt.half = true; | |
}, | |
_adjustStarType: function() { | |
var replaces = ['cancelOff', 'cancelOn', 'starHalf', 'starOff', 'starOn']; | |
this.opt.path = ''; | |
for (var i = 0; i < replaces.length; i++) { | |
this.opt[replaces[i]] = this.opt[replaces[i]].replace('.', '-'); | |
} | |
}, | |
_apply: function(score) { | |
methods._fill.call(this, score); | |
if (score) { | |
if (score > 0) { | |
this.score.val(score); | |
} | |
methods._roundStars.call(this, score); | |
} | |
}, | |
_between: function(value, min, max) { | |
return Math.min(Math.max(parseFloat(value), min), max); | |
}, | |
_binds: function() { | |
if (this.cancel) { | |
methods._bindOverCancel.call(this); | |
methods._bindClickCancel.call(this); | |
methods._bindOutCancel.call(this); | |
} | |
methods._bindOver.call(this); | |
methods._bindClick.call(this); | |
methods._bindOut.call(this); | |
}, | |
_bindClick: function() { | |
var that = this; | |
that.stars.on('click.raty', function(evt) { | |
var | |
execute = true, | |
score = (that.opt.half || that.opt.precision) ? that.self.data('score') : (this.alt || $(this).data('alt')); | |
if (that.opt.click) { | |
execute = that.opt.click.call(that, +score, evt); | |
} | |
if (execute || execute === undefined) { | |
if (that.opt.half && !that.opt.precision) { | |
score = methods._roundHalfScore.call(that, score); | |
} | |
methods._apply.call(that, score); | |
} | |
}); | |
}, | |
_bindClickCancel: function() { | |
var that = this; | |
that.cancel.on('click.raty', function(evt) { | |
that.score.removeAttr('value'); | |
if (that.opt.click) { | |
that.opt.click.call(that, null, evt); | |
} | |
}); | |
}, | |
_bindOut: function() { | |
var that = this; | |
that.self.on('mouseleave.raty', function(evt) { | |
var score = +that.score.val() || undefined; | |
methods._apply.call(that, score); | |
methods._target.call(that, score, evt); | |
methods._resetTitle.call(that); | |
if (that.opt.mouseout) { | |
that.opt.mouseout.call(that, score, evt); | |
} | |
}); | |
}, | |
_bindOutCancel: function() { | |
var that = this; | |
that.cancel.on('mouseleave.raty', function(evt) { | |
var icon = that.opt.cancelOff; | |
if (that.opt.starType !== 'img') { | |
icon = that.opt.cancelClass + ' ' + icon; | |
} | |
methods._setIcon.call(that, this, icon); | |
if (that.opt.mouseout) { | |
var score = +that.score.val() || undefined; | |
that.opt.mouseout.call(that, score, evt); | |
} | |
}); | |
}, | |
_bindOver: function() { | |
var | |
that = this, | |
action = that.opt.half ? 'mousemove.raty' : 'mouseover.raty'; | |
that.stars.on(action, function(evt) { | |
var score = methods._getScoreByPosition.call(that, evt, this); | |
methods._fill.call(that, score); | |
if (that.opt.half) { | |
methods._roundStars.call(that, score, evt); | |
methods._setTitle.call(that, score, evt); | |
that.self.data('score', score); | |
} | |
methods._target.call(that, score, evt); | |
if (that.opt.mouseover) { | |
that.opt.mouseover.call(that, score, evt); | |
} | |
}); | |
}, | |
_bindOverCancel: function() { | |
var that = this; | |
that.cancel.on('mouseover.raty', function(evt) { | |
var | |
starOff = that.opt.path + that.opt.starOff, | |
icon = that.opt.cancelOn; | |
if (that.opt.starType === 'img') { | |
that.stars.attr('src', starOff); | |
} else { | |
icon = that.opt.cancelClass + ' ' + icon; | |
that.stars.attr('class', starOff); | |
} | |
methods._setIcon.call(that, this, icon); | |
methods._target.call(that, null, evt); | |
if (that.opt.mouseover) { | |
that.opt.mouseover.call(that, null); | |
} | |
}); | |
}, | |
_buildScoreField: function() { | |
return $('<input />', { name: this.opt.scoreName, type: 'hidden' }).appendTo(this); | |
}, | |
_createCancel: function() { | |
var | |
icon = this.opt.path + this.opt.cancelOff, | |
cancel = $('<' + this.opt.starType + ' />', { title: this.opt.cancelHint, 'class': this.opt.cancelClass }); | |
if (this.opt.starType === 'img') { | |
cancel.attr({ src: icon, alt: 'x' }); | |
} else { | |
// TODO: use $.data | |
cancel.attr('data-alt', 'x').addClass(icon); | |
} | |
if (this.opt.cancelPlace === 'left') { | |
this.self.prepend(' ').prepend(cancel); | |
} else { | |
this.self.append(' ').append(cancel); | |
} | |
this.cancel = cancel; | |
}, | |
_createScore: function() { | |
var score = $(this.opt.targetScore); | |
this.score = score.length ? score : methods._buildScoreField.call(this); | |
}, | |
_createStars: function() { | |
for (var i = 1; i <= this.opt.number; i++) { | |
var | |
name = methods._nameForIndex.call(this, i), | |
attrs = { alt: i, src: this.opt.path + this.opt[name] }; | |
if (this.opt.starType !== 'img') { | |
attrs = { 'data-alt': i, 'class': attrs.src }; // TODO: use $.data. | |
} | |
attrs.title = methods._getHint.call(this, i); | |
$('<' + this.opt.starType + ' />', attrs).appendTo(this); | |
if (this.opt.space) { | |
this.self.append(i < this.opt.number ? ' ' : ''); | |
} | |
} | |
this.stars = this.self.children(this.opt.starType); | |
}, | |
_error: function(message) { | |
$(this).text(message); | |
$.error(message); | |
}, | |
_fill: function(score) { | |
var hash = 0; | |
for (var i = 1; i <= this.stars.length; i++) { | |
var | |
icon, | |
star = this.stars[i - 1], | |
turnOn = methods._turnOn.call(this, i, score); | |
if (this.opt.iconRange && this.opt.iconRange.length > hash) { | |
var irange = this.opt.iconRange[hash]; | |
icon = methods._getRangeIcon.call(this, irange, turnOn); | |
if (i <= irange.range) { | |
methods._setIcon.call(this, star, icon); | |
} | |
if (i === irange.range) { | |
hash++; | |
} | |
} else { | |
icon = this.opt[turnOn ? 'starOn' : 'starOff']; | |
methods._setIcon.call(this, star, icon); | |
} | |
} | |
}, | |
_getFirstDecimal: function(number) { | |
var | |
decimal = number.toString().split('.')[1], | |
result = 0; | |
if (decimal) { | |
result = parseInt(decimal.charAt(0), 10); | |
if (decimal.slice(1, 5) === '9999') { | |
result++; | |
} | |
} | |
return result; | |
}, | |
_getRangeIcon: function(irange, turnOn) { | |
return turnOn ? irange.on || this.opt.starOn : irange.off || this.opt.starOff; | |
}, | |
_getScoreByPosition: function(evt, icon) { | |
var score = parseInt(icon.alt || icon.getAttribute('data-alt'), 10); | |
if (this.opt.half) { | |
var | |
size = methods._getWidth.call(this), | |
percent = parseFloat((evt.pageX - $(icon).offset().left) / size); | |
score = score - 1 + percent; | |
} | |
return score; | |
}, | |
_getHint: function(score, evt) { | |
if (score !== 0 && !score) { | |
return this.opt.noRatedMsg; | |
} | |
var | |
decimal = methods._getFirstDecimal.call(this, score), | |
integer = Math.ceil(score), | |
group = this.opt.hints[(integer || 1) - 1], | |
hint = group, | |
set = !evt || this.move; | |
if (this.opt.precision) { | |
if (set) { | |
decimal = decimal === 0 ? 9 : decimal - 1; | |
} | |
hint = group[decimal]; | |
} else if (this.opt.halfShow || this.opt.half) { | |
decimal = set && decimal === 0 ? 1 : decimal > 5 ? 1 : 0; | |
hint = group[decimal]; | |
} | |
return hint === '' ? '' : hint || score; | |
}, | |
_getWidth: function() { | |
var width = this.stars[0].width || parseFloat(this.stars.eq(0).css('font-size')); | |
if (!width) { | |
methods._error.call(this, 'Could not get the icon width!'); | |
} | |
return width; | |
}, | |
_lock: function() { | |
var hint = methods._getHint.call(this, this.score.val()); | |
this.style.cursor = ''; | |
this.title = hint; | |
this.score.prop('readonly', true); | |
this.stars.prop('title', hint); | |
if (this.cancel) { | |
this.cancel.hide(); | |
} | |
this.self.data('readonly', true); | |
}, | |
_nameForIndex: function(i) { | |
return this.opt.score && this.opt.score >= i ? 'starOn' : 'starOff'; | |
}, | |
_resetTitle: function() { | |
for (var i = 0; i < this.opt.number; i++) { | |
this.stars[i].title = methods._getHint.call(this, i + 1); | |
} | |
}, | |
_roundHalfScore: function(score) { | |
var | |
integer = parseInt(score, 10), | |
decimal = methods._getFirstDecimal.call(this, score); | |
if (decimal !== 0) { | |
decimal = decimal > 5 ? 1 : 0.5; | |
} | |
return integer + decimal; | |
}, | |
_roundStars: function(score, evt) { | |
var | |
decimal = (score % 1).toFixed(2), | |
name ; | |
if (evt || this.move) { | |
name = decimal > 0.5 ? 'starOn' : 'starHalf'; | |
} else if (decimal > this.opt.round.down) { // Up: [x.76 .. x.99] | |
name = 'starOn'; | |
if (this.opt.halfShow && decimal < this.opt.round.up) { // Half: [x.26 .. x.75] | |
name = 'starHalf'; | |
} else if (decimal < this.opt.round.full) { // Down: [x.00 .. x.5] | |
name = 'starOff'; | |
} | |
} | |
if (name) { | |
var | |
icon = this.opt[name], | |
star = this.stars[Math.ceil(score) - 1]; | |
methods._setIcon.call(this, star, icon); | |
} // Full down: [x.00 .. x.25] | |
}, | |
_setIcon: function(star, icon) { | |
star[this.opt.starType === 'img' ? 'src' : 'className'] = this.opt.path + icon; | |
}, | |
_setTarget: function(target, score) { | |
if (score) { | |
score = this.opt.targetFormat.toString().replace('{score}', score); | |
} | |
if (target.is(':input')) { | |
target.val(score); | |
} else { | |
target.html(score); | |
} | |
}, | |
_setTitle: function(score, evt) { | |
if (score) { | |
var | |
integer = parseInt(Math.ceil(score), 10), | |
star = this.stars[integer - 1]; | |
star.title = methods._getHint.call(this, score, evt); | |
} | |
}, | |
_target: function(score, evt) { | |
if (this.opt.target) { | |
var target = $(this.opt.target); | |
if (!target.length) { | |
methods._error.call(this, 'Target selector invalid or missing!'); | |
} | |
var mouseover = evt && evt.type === 'mouseover'; | |
if (score === undefined) { | |
score = this.opt.targetText; | |
} else if (score === null) { | |
score = mouseover ? this.opt.cancelHint : this.opt.targetText; | |
} else { | |
if (this.opt.targetType === 'hint') { | |
score = methods._getHint.call(this, score, evt); | |
} else if (this.opt.precision) { | |
score = parseFloat(score).toFixed(1); | |
} | |
var mousemove = evt && evt.type === 'mousemove'; | |
if (!mouseover && !mousemove && !this.opt.targetKeep) { | |
score = this.opt.targetText; | |
} | |
} | |
methods._setTarget.call(this, target, score); | |
} | |
}, | |
_turnOn: function(i, score) { | |
return this.opt.single ? (i === score) : (i <= score); | |
}, | |
_unlock: function() { | |
this.style.cursor = 'pointer'; | |
this.removeAttribute('title'); | |
this.score.removeAttr('readonly'); | |
this.self.data('readonly', false); | |
for (var i = 0; i < this.opt.number; i++) { | |
this.stars[i].title = methods._getHint.call(this, i + 1); | |
} | |
if (this.cancel) { | |
this.cancel.css('display', ''); | |
} | |
}, | |
cancel: function(click) { | |
return this.each(function() { | |
var self = $(this); | |
if (self.data('readonly') !== true) { | |
methods[click ? 'click' : 'score'].call(self, null); | |
this.score.removeAttr('value'); | |
} | |
}); | |
}, | |
click: function(score) { | |
return this.each(function() { | |
if ($(this).data('readonly') !== true) { | |
score = methods._adjustedScore.call(this, score); | |
methods._apply.call(this, score); | |
if (this.opt.click) { | |
this.opt.click.call(this, score, $.Event('click')); | |
} | |
methods._target.call(this, score); | |
} | |
}); | |
}, | |
destroy: function() { | |
return this.each(function() { | |
var | |
self = $(this), | |
raw = self.data('raw'); | |
if (raw) { | |
self.off('.raty').empty().css({ cursor: raw.style.cursor }).removeData('readonly'); | |
} else { | |
self.data('raw', self.clone()[0]); | |
} | |
}); | |
}, | |
getScore: function() { | |
var | |
score = [], | |
value ; | |
this.each(function() { | |
value = this.score.val(); | |
score.push(value ? +value : undefined); | |
}); | |
return (score.length > 1) ? score : score[0]; | |
}, | |
move: function(score) { | |
return this.each(function() { | |
var | |
integer = parseInt(score, 10), | |
decimal = methods._getFirstDecimal.call(this, score); | |
if (integer >= this.opt.number) { | |
integer = this.opt.number - 1; | |
decimal = 10; | |
} | |
var | |
width = methods._getWidth.call(this), | |
steps = width / 10, | |
star = $(this.stars[integer]), | |
percent = star.offset().left + steps * decimal, | |
evt = $.Event('mousemove', { pageX: percent }); | |
this.move = true; | |
star.trigger(evt); | |
this.move = false; | |
}); | |
}, | |
readOnly: function(readonly) { | |
return this.each(function() { | |
var self = $(this); | |
if (self.data('readonly') !== readonly) { | |
if (readonly) { | |
self.off('.raty').children(this.opt.starType).off('.raty'); | |
methods._lock.call(this); | |
} else { | |
methods._binds.call(this); | |
methods._unlock.call(this); | |
} | |
self.data('readonly', readonly); | |
} | |
}); | |
}, | |
reload: function() { | |
return methods.set.call(this, {}); | |
}, | |
score: function() { | |
var self = $(this); | |
return arguments.length ? methods.setScore.apply(self, arguments) : methods.getScore.call(self); | |
}, | |
set: function(options) { | |
return this.each(function() { | |
$(this).raty($.extend({}, this.opt, options)); | |
}); | |
}, | |
setScore: function(score) { | |
return this.each(function() { | |
if ($(this).data('readonly') !== true) { | |
score = methods._adjustedScore.call(this, score); | |
methods._apply.call(this, score); | |
methods._target.call(this, score); | |
} | |
}); | |
} | |
}; | |
$.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, | |
cancelClass: 'raty-cancel', | |
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, | |
mouseout: undefined, | |
mouseover: undefined, | |
noRatedMsg: 'Not rated yet!', | |
number: 5, | |
numberMax: 20, | |
path: undefined, | |
precision: false, | |
readOnly: false, | |
round: { down: 0.25, full: 0.6, up: 0.76 }, | |
score: undefined, | |
scoreName: 'score', | |
single: false, | |
space: true, | |
starHalf: 'star-half.png', | |
starOff: 'star-off.png', | |
starOn: 'star-on.png', | |
starType: 'img', | |
target: undefined, | |
targetFormat: '{score}', | |
targetKeep: false, | |
targetScore: undefined, | |
targetText: '', | |
targetType: 'hint' | |
}; | |
})(jQuery); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment