FarisaRahmaSari_E31222327/public/plugins/bootstrap-rating/bootstrap-rating.js

248 lines
9.8 KiB
JavaScript

(function ($, undefined) {
'use strict';
var OFFSET = 5;
function Rating(element, options) {
this.$input = $(element);
this.$rating = $('<span></span>').css({
cursor: 'default'
}).insertBefore(this.$input);
// Merge data and parameter options.
// Those provided as parameter prevail over the data ones.
this.options = (function (opts) {
// Sanitize start, stop, step, and fractions.
// All of them start, stop, and step must be integers.
opts.start = parseInt(opts.start, 10);
opts.start = isNaN(opts.start) ? undefined : opts.start;
// In case we don't have a valid stop rate try to get a reasonable
// one based on the existence of a valid start rate.
opts.stop = parseInt(opts.stop, 10);
opts.stop = isNaN(opts.stop) ?
opts.start + OFFSET || undefined : opts.stop;
// 0 step is ignored.
opts.step = parseInt(opts.step, 10) || undefined;
// Symbol fractions and scale (number of significant digits).
// 0 is ignored and negative numbers are turned to positive.
opts.fractions = Math.abs(parseInt(opts.fractions, 10)) || undefined;
opts.scale = Math.abs(parseInt(opts.scale, 10)) || undefined;
// Extend/Override the default options with those provided either as
// data attributes or function parameters.
opts = $.extend({}, $.fn.rating.defaults, opts);
// Inherit default filled if none is defined for the selected symbol.
opts.filledSelected = opts.filledSelected || opts.filled;
return opts;
}($.extend({}, this.$input.data(), options)));
this._init();
};
Rating.prototype = {
_init: function () {
var rating = this,
$input = this.$input,
$rating = this.$rating;
var ifEnabled = function (f) {
return function (e) {
// According to the W3C attribute readonly is not allowed on input
// elements with type hidden.
// Keep readonly prop for legacy but its use should be deprecated.
if (!$input.prop('disabled') && !$input.prop('readonly') &&
$input.data('readonly') === undefined) {
f.call(this, e);
}
}
};
// Build the rating control.
for (var i = 1; i <= this._rateToIndex(this.options.stop); i++) {
// Create the rating symbol container.
var $symbol = $('<div class="rating-symbol"></div>').css({
display: 'inline-block',
position: 'relative'
});
// Add background symbol to the symbol container.
$('<div class="rating-symbol-background ' + this.options.empty + '"></div>')
.appendTo($symbol);
// Add foreground symbol to the symbol container.
// The filled icon is wrapped with a div to allow fractional selection.
$('<div class="rating-symbol-foreground"></div>')
.append('<span class="' + this.options.filled + '"></span>')
.css({
display: 'inline-block',
position: 'absolute',
overflow: 'hidden',
left: 0,
// Overspecify right and left to 0 and let the container direction
// decide which one is going to take precedence according to the
// ltr/rtl direction.
// (https://developer.mozilla.org/en-US/docs/Web/CSS/right)
// When both the right CSS property and the left CSS property are
// defined, the position of the element is overspecified. In that
// case, the left value has precedence when the container is
// left-to-right (that is that the right computed value is set to
// -left), and the right value has precedence when the container is
// right-to-left (that is that the left computed value is set to
// -right).
right: 0,
width: 0
}).appendTo($symbol);
$rating.append($symbol);
this.options.extendSymbol.call($symbol, this._indexToRate(i));
}
// Initialize the rating control with the associated input value rate.
this._updateRate($input.val());
// Keep rating control and its associated input in sync.
$input
.on('change', function () {
rating._updateRate($(this).val());
});
var fractionalIndex = function (e) {
var $symbol = $(e.currentTarget);
// Calculate the distance from the mouse pointer to the origin of the
// symbol. We need to be careful with the CSS direction. If we are
// right-to-left then the symbol starts at the right. So we have to add
// the symbol width to the left offset to get the CSS rigth position.
var x = Math.abs((e.pageX || e.originalEvent.touches[0].pageX) -
(($symbol.css('direction') === 'rtl' && $symbol.width()) +
$symbol.offset().left));
// NOTE: When the mouse pointer is close to the left side of the symbol
// a negative x is returned. Probably some precision error in the
// calculation.
// x should never be less than 0 because this would mean that we are in
// the previous symbol.
x = x > 0 ? x : rating.options.scale * 0.1;
return $symbol.index() + x / $symbol.width();
};
// Keep the current highlighted index (fractional or not).
var index;
$rating
.on('mousedown touchstart', '.rating-symbol', ifEnabled(function (e) {
// Set input 'trigger' the change event.
$input.val(rating._indexToRate(fractionalIndex(e))).change();
}))
.on('mousemove touchmove', '.rating-symbol', ifEnabled(function (e) {
var current = rating._roundToFraction(fractionalIndex(e));
if (current !== index) {
// Trigger pseudo rate leave event if the mouse pointer is not
// leaving from another symbol (mouseleave).
if (index !== undefined) $(this).trigger('rating.rateleave');
// Update index and trigger rate enter event.
index = current;
$(this).trigger('rating.rateenter', [rating._indexToRate(index)]);
}
// Fill the symbols as fractions chunks.
rating._fillUntil(current);
}))
.on('mouseleave touchend', '.rating-symbol', ifEnabled(function () {
// When a symbol is left, reset index and trigger rate leave event.
index = undefined;
$(this).trigger('rating.rateleave');
// Restore on hover out.
rating._fillUntil(rating._rateToIndex(parseFloat($input.val())));
}));
},
// Fill rating symbols until index.
_fillUntil: function (index) {
var $rating = this.$rating;
// Get the index of the last whole symbol.
var i = Math.floor(index);
// Hide completely hidden symbols background.
$rating.find('.rating-symbol-background')
.css('visibility', 'visible')
.slice(0, i).css('visibility', 'hidden');
var $rates = $rating.find('.rating-symbol-foreground');
// Reset foreground
$rates.width(0);
// Fill all the foreground symbols up to the selected one.
$rates.slice(0, i).width('auto')
.find('span').attr('class', this.options.filled);
// Amend selected symbol.
$rates.eq(index % 1 ? i : i - 1)
.find('span').attr('class', this.options.filledSelected);
// Partially fill the fractional one.
$rates.eq(i).width(index % 1 * 100 + '%');
},
// Calculate the rate of an index according the the start and step.
_indexToRate: function (index) {
return this.options.start + Math.floor(index) * this.options.step +
this.options.step * this._roundToFraction(index % 1);
},
// Calculate the corresponding index for a rate.
_rateToIndex: function (rate) {
return (rate - this.options.start) / this.options.step;
},
// Round index to the configured opts.fractions.
_roundToFraction: function (index) {
// Get the closest top fraction.
var fraction = Math.ceil(index % 1 * this.options.fractions) / this.options.fractions;
// Truncate decimal trying to avoid float precission issues.
var p = Math.pow(10, this.options.scale);
return Math.floor(index) + Math.floor(fraction * p) / p;
},
// Check the rate is in the proper range [start..stop].
_contains: function (rate) {
var start = this.options.step > 0 ? this.options.start : this.options.stop;
var stop = this.options.step > 0 ? this.options.stop : this.options.start;
return start <= rate && rate <= stop;
},
// Update empty and filled rating symbols according to a rate.
_updateRate: function (rate) {
var value = parseFloat(rate);
if (this._contains(value)) {
this._fillUntil(this._rateToIndex(value));
this.$input.val(value);
} else if (rate === '') {
this._fillUntil(0);
this.$input.val('');
}
},
rate: function (value) {
if (value === undefined) {
return this.$input.val();
}
this._updateRate(value);
}
};
$.fn.rating = function (options) {
var args = Array.prototype.slice.call(arguments, 1),
result;
this.each(function () {
var $input = $(this);
var rating = $input.data('rating');
if (!rating) {
$input.data('rating', (rating = new Rating(this, options)));
}
// Underscore are used for private methods.
if (typeof options === 'string' && options[0] !== '_') {
result = rating[options].apply(rating, args);
}
});
return result !== undefined ? result : this;
};
// Plugin defaults.
$.fn.rating.defaults = {
filled: 'glyphicon glyphicon-star',
filledSelected: undefined,
empty: 'glyphicon glyphicon-star-empty',
start: 0,
stop: OFFSET,
step: 1,
fractions: 1,
scale: 3,
extendSymbol: function (rate) {},
};
$(function () {
$('input.rating').rating();
});
}(jQuery));