/*!
* bootstrap-star-rating v4.0.2
* http://plugins.krajee.com/star-rating
*
* Author: Kartik Visweswaran
* Copyright: 2013 - 2017, Kartik Visweswaran, Krajee.com
*
* Licensed under the BSD 3-Clause
* https://github.com/kartik-v/bootstrap-star-rating/blob/master/LICENSE.md
*/
(function (factory) {
"use strict";
//noinspection JSUnresolvedVariable
if (typeof define === 'function' && define.amd) { // jshint ignore:line
// AMD. Register as an anonymous module.
define(['jquery'], factory); // jshint ignore:line
} else { // noinspection JSUnresolvedVariable
if (typeof module === 'object' && module.exports) { // jshint ignore:line
// Node/CommonJS
// noinspection JSUnresolvedVariable
module.exports = factory(require('jquery')); // jshint ignore:line
} else {
// Browser globals
factory(window.jQuery);
}
}
}(function ($) {
"use strict";
$.fn.ratingLocales = {};
$.fn.ratingThemes = {};
var $h, Rating;
// global helper methods and constants
$h = {
NAMESPACE: '.rating',
DEFAULT_MIN: 0,
DEFAULT_MAX: 5,
DEFAULT_STEP: 0.5,
isEmpty: function (value, trim) {
return value === null || value === undefined || value.length === 0 || (trim && $.trim(value) === '');
},
getCss: function (condition, css) {
return condition ? ' ' + css : '';
},
addCss: function ($el, css) {
$el.removeClass(css).addClass(css);
},
getDecimalPlaces: function (num) {
var m = ('' + num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
return !m ? 0 : Math.max(0, (m[1] ? m[1].length : 0) - (m[2] ? +m[2] : 0));
},
applyPrecision: function (val, precision) {
return parseFloat(val.toFixed(precision));
},
handler: function ($el, event, callback, skipOff, skipNS) {
var ev = skipNS ? event : event.split(' ').join($h.NAMESPACE + ' ') + $h.NAMESPACE;
if (!skipOff) {
$el.off(ev);
}
$el.on(ev, callback);
}
};
// rating constructor
Rating = function (element, options) {
var self = this;
self.$element = $(element);
self._init(options);
};
Rating.prototype = {
constructor: Rating,
_parseAttr: function (vattr, options) {
var self = this, $el = self.$element, elType = $el.attr('type'), finalVal, val, chk, out;
if (elType === 'range' || elType === 'number') {
val = options[vattr] || $el.data(vattr) || $el.attr(vattr);
switch (vattr) {
case 'min':
chk = $h.DEFAULT_MIN;
break;
case 'max':
chk = $h.DEFAULT_MAX;
break;
default:
chk = $h.DEFAULT_STEP;
}
finalVal = $h.isEmpty(val) ? chk : val;
out = parseFloat(finalVal);
} else {
out = parseFloat(options[vattr]);
}
return isNaN(out) ? chk : out;
},
_parseValue: function (val) {
var self = this, v = parseFloat(val);
if (isNaN(v)) {
v = self.clearValue;
}
return (self.zeroAsNull && (v === 0 || v === '0') ? null : v);
},
_setDefault: function (key, val) {
var self = this;
if ($h.isEmpty(self[key])) {
self[key] = val;
}
},
_initSlider: function (options) {
var self = this, v = self.$element.val();
self.initialValue = $h.isEmpty(v) ? 0 : v;
self._setDefault('min', self._parseAttr('min', options));
self._setDefault('max', self._parseAttr('max', options));
self._setDefault('step', self._parseAttr('step', options));
if (isNaN(self.min) || $h.isEmpty(self.min)) {
self.min = $h.DEFAULT_MIN;
}
if (isNaN(self.max) || $h.isEmpty(self.max)) {
self.max = $h.DEFAULT_MAX;
}
if (isNaN(self.step) || $h.isEmpty(self.step) || self.step === 0) {
self.step = $h.DEFAULT_STEP;
}
self.diff = self.max - self.min;
},
_initHighlight: function (v) {
var self = this, w, cap = self._getCaption();
if (!v) {
v = self.$element.val();
}
w = self.getWidthFromValue(v) + '%';
self.$filledStars.width(w);
self.cache = {caption: cap, width: w, val: v};
},
_getContainerCss: function () {
var self = this;
return 'rating-container' +
$h.getCss(self.theme, 'theme-' + self.theme) +
$h.getCss(self.rtl, 'rating-rtl') +
$h.getCss(self.size, 'rating-' + self.size) +
$h.getCss(self.animate, 'rating-animate') +
$h.getCss(self.disabled || self.readonly, 'rating-disabled') +
$h.getCss(self.containerClass, self.containerClass);
},
_checkDisabled: function () {
var self = this, $el = self.$element, opts = self.options;
self.disabled = opts.disabled === undefined ? $el.attr('disabled') || false : opts.disabled;
self.readonly = opts.readonly === undefined ? $el.attr('readonly') || false : opts.readonly;
self.inactive = (self.disabled || self.readonly);
$el.attr({disabled: self.disabled, readonly: self.readonly});
},
_addContent: function (type, content) {
var self = this, $container = self.$container, isClear = type === 'clear';
if (self.rtl) {
return isClear ? $container.append(content) : $container.prepend(content);
} else {
return isClear ? $container.prepend(content) : $container.append(content);
}
},
_generateRating: function () {
var self = this, $el = self.$element, $rating, $container, w;
$container = self.$container = $(document.createElement("div")).insertBefore($el);
$h.addCss($container, self._getContainerCss());
self.$rating = $rating = $(document.createElement("div")).attr('class', 'rating-stars').appendTo($container)
.append(self._getStars('empty')).append(self._getStars('filled'));
self.$emptyStars = $rating.find('.empty-stars');
self.$filledStars = $rating.find('.filled-stars');
self._renderCaption();
self._renderClear();
self._initHighlight();
$container.append($el);
if (self.rtl) {
w = Math.max(self.$emptyStars.outerWidth(), self.$filledStars.outerWidth());
self.$emptyStars.width(w);
}
$el.appendTo($rating);
},
_getCaption: function () {
var self = this;
return self.$caption && self.$caption.length ? self.$caption.html() : self.defaultCaption;
},
_setCaption: function (content) {
var self = this;
if (self.$caption && self.$caption.length) {
self.$caption.html(content);
}
},
_renderCaption: function () {
var self = this, val = self.$element.val(), html, $cap = self.captionElement ? $(self.captionElement) : '';
if (!self.showCaption) {
return;
}
html = self.fetchCaption(val);
if ($cap && $cap.length) {
$h.addCss($cap, 'caption');
$cap.html(html);
self.$caption = $cap;
return;
}
self._addContent('caption', '
' + html + '
');
self.$caption = self.$container.find(".caption");
},
_renderClear: function () {
var self = this, css, $clr = self.clearElement ? $(self.clearElement) : '';
if (!self.showClear) {
return;
}
css = self._getClearClass();
if ($clr.length) {
$h.addCss($clr, css);
$clr.attr({"title": self.clearButtonTitle}).html(self.clearButton);
self.$clear = $clr;
return;
}
self._addContent('clear',
'' + self.clearButton + '
');
self.$clear = self.$container.find('.' + self.clearButtonBaseClass);
},
_getClearClass: function () {
var self = this;
return self.clearButtonBaseClass + ' ' + (self.inactive ? '' : self.clearButtonActiveClass);
},
_toggleHover: function (out) {
var self = this, w, width, caption;
if (!out) {
return;
}
if (self.hoverChangeStars) {
w = self.getWidthFromValue(self.clearValue);
width = out.val <= self.clearValue ? w + '%' : out.width;
self.$filledStars.css('width', width);
}
if (self.hoverChangeCaption) {
caption = out.val <= self.clearValue ? self.fetchCaption(self.clearValue) : out.caption;
if (caption) {
self._setCaption(caption + '');
}
}
},
_init: function (options) {
var self = this, $el = self.$element.addClass('rating-input'), v;
self.options = options;
$.each(options, function (key, value) {
self[key] = value;
});
if (self.rtl || $el.attr('dir') === 'rtl') {
self.rtl = true;
$el.attr('dir', 'rtl');
}
self.starClicked = false;
self.clearClicked = false;
self._initSlider(options);
self._checkDisabled();
if (self.displayOnly) {
self.inactive = true;
self.showClear = false;
self.showCaption = false;
}
self._generateRating();
self._initEvents();
self._listen();
v = self._parseValue($el.val());
$el.val(v);
return $el.removeClass('rating-loading');
},
_initEvents: function () {
var self = this;
self.events = {
_getTouchPosition: function (e) {
var pageX = $h.isEmpty(e.pageX) ? e.originalEvent.touches[0].pageX : e.pageX;
return pageX - self.$rating.offset().left;
},
_listenClick: function (e, callback) {
e.stopPropagation();
e.preventDefault();
if (e.handled !== true) {
callback(e);
e.handled = true;
} else {
return false;
}
},
_noMouseAction: function (e) {
return !self.hoverEnabled || self.inactive || (e && e.isDefaultPrevented());
},
initTouch: function (e) {
//noinspection JSUnresolvedVariable
var ev, touches, pos, out, caption, w, width, params, clrVal = self.clearValue || 0,
isTouchCapable = 'ontouchstart' in window ||
(window.DocumentTouch && document instanceof window.DocumentTouch);
if (!isTouchCapable || self.inactive) {
return;
}
ev = e.originalEvent;
//noinspection JSUnresolvedVariable
touches = !$h.isEmpty(ev.touches) ? ev.touches : ev.changedTouches;
pos = self.events._getTouchPosition(touches[0]);
if (e.type === "touchend") {
self._setStars(pos);
params = [self.$element.val(), self._getCaption()];
self.$element.trigger('change').trigger('rating.change', params);
self.starClicked = true;
} else {
out = self.calculate(pos);
caption = out.val <= clrVal ? self.fetchCaption(clrVal) : out.caption;
w = self.getWidthFromValue(clrVal);
width = out.val <= clrVal ? w + '%' : out.width;
self._setCaption(caption);
self.$filledStars.css('width', width);
}
},
starClick: function (e) {
var pos, params;
self.events._listenClick(e, function (ev) {
if (self.inactive) {
return false;
}
pos = self.events._getTouchPosition(ev);
self._setStars(pos);
params = [self.$element.val(), self._getCaption()];
self.$element.trigger('change').trigger('rating.change', params);
self.starClicked = true;
});
},
clearClick: function (e) {
self.events._listenClick(e, function () {
if (!self.inactive) {
self.clear();
self.clearClicked = true;
}
});
},
starMouseMove: function (e) {
var pos, out;
if (self.events._noMouseAction(e)) {
return;
}
self.starClicked = false;
pos = self.events._getTouchPosition(e);
out = self.calculate(pos);
self._toggleHover(out);
self.$element.trigger('rating.hover', [out.val, out.caption, 'stars']);
},
starMouseLeave: function (e) {
var out;
if (self.events._noMouseAction(e) || self.starClicked) {
return;
}
out = self.cache;
self._toggleHover(out);
self.$element.trigger('rating.hoverleave', ['stars']);
},
clearMouseMove: function (e) {
var caption, val, width, out;
if (self.events._noMouseAction(e) || !self.hoverOnClear) {
return;
}
self.clearClicked = false;
caption = '' + self.clearCaption + '';
val = self.clearValue;
width = self.getWidthFromValue(val) || 0;
out = {caption: caption, width: width, val: val};
self._toggleHover(out);
self.$element.trigger('rating.hover', [val, caption, 'clear']);
},
clearMouseLeave: function (e) {
var out;
if (self.events._noMouseAction(e) || self.clearClicked || !self.hoverOnClear) {
return;
}
out = self.cache;
self._toggleHover(out);
self.$element.trigger('rating.hoverleave', ['clear']);
},
resetForm: function (e) {
if (e && e.isDefaultPrevented()) {
return;
}
if (!self.inactive) {
self.reset();
}
}
};
},
_listen: function () {
var self = this, $el = self.$element, $form = $el.closest('form'), $rating = self.$rating,
$clear = self.$clear, events = self.events;
$h.handler($rating, 'touchstart touchmove touchend', $.proxy(events.initTouch, self));
$h.handler($rating, 'click touchstart', $.proxy(events.starClick, self));
$h.handler($rating, 'mousemove', $.proxy(events.starMouseMove, self));
$h.handler($rating, 'mouseleave', $.proxy(events.starMouseLeave, self));
if (self.showClear && $clear.length) {
$h.handler($clear, 'click touchstart', $.proxy(events.clearClick, self));
$h.handler($clear, 'mousemove', $.proxy(events.clearMouseMove, self));
$h.handler($clear, 'mouseleave', $.proxy(events.clearMouseLeave, self));
}
if ($form.length) {
$h.handler($form, 'reset', $.proxy(events.resetForm, self), true);
}
return $el;
},
_getStars: function (type) {
var self = this, stars = '', i;
for (i = 1; i <= self.stars; i++) {
stars += '' + self[type + 'Star'] + '';
}
return stars + '';
},
_setStars: function (pos) {
var self = this, out = arguments.length ? self.calculate(pos) : self.calculate(), $el = self.$element,
v = self._parseValue(out.val);
$el.val(v);
self.$filledStars.css('width', out.width);
self._setCaption(out.caption);
self.cache = out;
return $el;
},
showStars: function (val) {
var self = this, v = self._parseValue(val);
self.$element.val(v);
return self._setStars();
},
calculate: function (pos) {
var self = this, defaultVal = $h.isEmpty(self.$element.val()) ? 0 : self.$element.val(),
val = arguments.length ? self.getValueFromPosition(pos) : defaultVal,
caption = self.fetchCaption(val), width = self.getWidthFromValue(val);
width += '%';
return {caption: caption, width: width, val: val};
},
getValueFromPosition: function (pos) {
var self = this, precision = $h.getDecimalPlaces(self.step), val, factor, maxWidth = self.$rating.width();
factor = (self.diff * pos) / (maxWidth * self.step);
factor = self.rtl ? Math.floor(factor) : Math.ceil(factor);
val = $h.applyPrecision(parseFloat(self.min + factor * self.step), precision);
val = Math.max(Math.min(val, self.max), self.min);
return self.rtl ? (self.max - val) : val;
},
getWidthFromValue: function (val) {
var self = this, min = self.min, max = self.max, factor, $r = self.$emptyStars, w;
if (!val || val <= min || min === max) {
return 0;
}
w = $r.outerWidth();
factor = w ? $r.width() / w : 1;
if (val >= max) {
return 100;
}
return (val - min) * factor * 100 / (max - min);
},
fetchCaption: function (rating) {
var self = this, val = parseFloat(rating) || self.clearValue, css, cap, capVal, cssVal, caption,
vCap = self.starCaptions, vCss = self.starCaptionClasses;
if (val && val !== self.clearValue) {
val = $h.applyPrecision(val, $h.getDecimalPlaces(self.step));
}
cssVal = typeof vCss === "function" ? vCss(val) : vCss[val];
capVal = typeof vCap === "function" ? vCap(val) : vCap[val];
cap = $h.isEmpty(capVal) ? self.defaultCaption.replace(/\{rating}/g, val) : capVal;
css = $h.isEmpty(cssVal) ? self.clearCaptionClass : cssVal;
caption = (val === self.clearValue) ? self.clearCaption : cap;
return '' + caption + '';
},
destroy: function () {
var self = this, $el = self.$element;
if (!$h.isEmpty(self.$container)) {
self.$container.before($el).remove();
}
$.removeData($el.get(0));
return $el.off('rating').removeClass('rating rating-input');
},
create: function (options) {
var self = this, opts = options || self.options || {};
return self.destroy().rating(opts);
},
clear: function () {
var self = this, title = '' + self.clearCaption + '';
if (!self.inactive) {
self._setCaption(title);
}
return self.showStars(self.clearValue).trigger('change').trigger('rating.clear');
},
reset: function () {
var self = this;
return self.showStars(self.initialValue).trigger('rating.reset');
},
update: function (val) {
var self = this;
return arguments.length ? self.showStars(val) : self.$element;
},
refresh: function (options) {
var self = this, $el = self.$element;
if (!options) {
return $el;
}
return self.destroy().rating($.extend(true, self.options, options)).trigger('rating.refresh');
}
};
$.fn.rating = function (option) {
var args = Array.apply(null, arguments), retvals = [];
args.shift();
this.each(function () {
var self = $(this), data = self.data('rating'), options = typeof option === 'object' && option,
theme = options.theme || self.data('theme'), lang = options.language || self.data('language') || 'en',
thm = {}, loc = {}, opts;
if (!data) {
if (theme) {
thm = $.fn.ratingThemes[theme] || {};
}
if (lang !== 'en' && !$h.isEmpty($.fn.ratingLocales[lang])) {
loc = $.fn.ratingLocales[lang];
}
opts = $.extend(true, {}, $.fn.rating.defaults, thm, $.fn.ratingLocales.en, loc, options, self.data());
data = new Rating(this, opts);
self.data('rating', data);
}
if (typeof option === 'string') {
retvals.push(data[option].apply(data, args));
}
});
switch (retvals.length) {
case 0:
return this;
case 1:
return retvals[0] === undefined ? this : retvals[0];
default:
return retvals;
}
};
$.fn.rating.defaults = {
theme: '',
language: 'en',
stars: 5,
filledStar: '',
emptyStar: '',
containerClass: '',
size: 'md',
animate: true,
displayOnly: false,
rtl: false,
showClear: true,
showCaption: true,
starCaptionClasses: {
0.5: 'label label-danger',
1: 'label label-danger',
1.5: 'label label-warning',
2: 'label label-warning',
2.5: 'label label-info',
3: 'label label-info',
3.5: 'label label-primary',
4: 'label label-primary',
4.5: 'label label-success',
5: 'label label-success'
},
clearButton: '',
clearButtonBaseClass: 'clear-rating',
clearButtonActiveClass: 'clear-rating-active',
clearCaptionClass: 'label label-default',
clearValue: null,
captionElement: null,
clearElement: null,
hoverEnabled: true,
hoverChangeCaption: true,
hoverChangeStars: true,
hoverOnClear: true,
zeroAsNull: true
};
$.fn.ratingLocales.en = {
defaultCaption: '{rating} Stars',
starCaptions: {
0.5: 'Half Star',
1: 'One Star',
1.5: 'One & Half Star',
2: 'Two Stars',
2.5: 'Two & Half Stars',
3: 'Three Stars',
3.5: 'Three & Half Stars',
4: 'Four Stars',
4.5: 'Four & Half Stars',
5: 'Five Stars'
},
clearButtonTitle: 'Clear',
clearCaption: 'Not Rated'
};
$.fn.rating.Constructor = Rating;
/**
* Convert automatically inputs with class 'rating' into Krajee's star rating control.
*/
$(document).ready(function () {
var $input = $('input.rating');
if ($input.length) {
$input.removeClass('rating-loading').addClass('rating-loading').rating();
}
});
}));