/*! * @copyright © Kartik Visweswaran, Krajee.com, 2014 * @version 2.5.0 * * A simple yet powerful JQuery star rating plugin that allows rendering * fractional star ratings and supports Right to Left (RTL) input. * * For more JQuery plugins visit http://plugins.krajee.com * For more Yii related demos visit http://demos.krajee.com */ (function ($) { var DEFAULT_MIN = 0; var DEFAULT_MAX = 5; var DEFAULT_STEP = 0.5; var isEmpty = function (value, trim) { return typeof value === 'undefined' || value === null || value === undefined || value == [] || value === '' || trim && $.trim(value) === ''; }; var validateAttr = function ($input, vattr, options) { var chk = isEmpty($input.data(vattr)) ? $input.attr(vattr) : $input.data(vattr); if (chk) { return chk; } return options[vattr]; }; var getDecimalPlaces = function (num) { var match = ('' + num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/); if (!match) { return 0; } return Math.max(0, (match[1] ? match[1].length : 0) - (match[2] ? +match[2] : 0)); }; var applyPrecision = function (val, precision) { return parseFloat(val.toFixed(precision)); }; // Rating public class definition var Rating = function (element, options) { this.$element = $(element); this.init(options); }; Rating.prototype = { constructor: Rating, _parseAttr: function (vattr, options) { var self = this, $input = self.$element; if ($input.attr('type') === 'range' || $input.attr('type') === 'number') { var val = validateAttr($input, vattr, options); var chk = DEFAULT_STEP; if (vattr === 'min') { chk = DEFAULT_MIN; } else if (vattr === 'max') { chk = DEFAULT_MAX; } else if (vattr === 'step') { chk = DEFAULT_STEP; } var final = isEmpty(val) ? chk : val; return parseFloat(final); } return parseFloat(options[vattr]); }, /** * Listens to events */ listen: function () { var self = this; self.$rating.on("click", function (e) { if (!self.inactive) { w = e.pageX - self.$rating.offset().left; self.setStars(w); self.$element.trigger('change'); self.$element.trigger('rating.change', [self.$element.val(), self.$caption.html()]); } }); self.$clear.on("click", function (e) { if (!self.inactive) { self.clear(); } }); $(self.$element[0].form).on("reset", function (e) { if (!self.inactive) { self.reset(); } }); }, initSlider: function (options) { var self = this; if (isEmpty(self.$element.val())) { self.$element.val(0); } self.initialValue = self.$element.val(); self.min = (typeof options.min !== 'undefined') ? options.min : self._parseAttr('min', options); self.max = (typeof options.max !== 'undefined') ? options.max : self._parseAttr('max', options); self.step = (typeof options.step !== 'undefined') ? options.step : self._parseAttr('step', options); if (isNaN(self.min) || isEmpty(self.min)) { self.min = DEFAULT_MIN; } if (isNaN(self.max) || isEmpty(self.max)) { self.max = DEFAULT_MAX; } if (isNaN(self.step) || isEmpty(self.step) || self.step == 0) { self.step = DEFAULT_STEP; } self.diff = self.max - self.min; }, /** * Initializes the plugin */ init: function (options) { var self = this; self.options = options; self.initSlider(options); self.checkDisabled(); $element = self.$element; self.containerClass = options.containerClass; self.glyphicon = options.glyphicon; var defaultStar = (self.glyphicon) ? '\ue006' : '\u2605'; self.symbol = isEmpty(options.symbol) ? defaultStar : options.symbol; self.rtl = options.rtl || self.$element.attr('dir'); if (self.rtl) { self.$element.attr('dir', 'rtl'); } self.showClear = options.showClear; self.showCaption = options.showCaption; self.size = options.size; self.stars = options.stars; self.defaultCaption = options.defaultCaption; self.starCaptions = options.starCaptions; self.starCaptionClasses = options.starCaptionClasses; self.clearButton = options.clearButton; self.clearButtonTitle = options.clearButtonTitle; self.clearButtonBaseClass = !isEmpty(options.clearButtonBaseClass) ? options.clearButtonBaseClass : 'clear-rating'; self.clearButtonActiveClass = !isEmpty(options.clearButtonActiveClass) ? options.clearButtonActiveClass : 'clear-rating-active'; self.clearCaption = options.clearCaption; self.clearCaptionClass = options.clearCaptionClass; self.clearValue = options.clearValue; self.$element.removeClass('form-control').addClass('form-control'); self.$clearElement = isEmpty(options.clearElement) ? null : $(options.clearElement); self.$captionElement = isEmpty(options.captionElement) ? null : $(options.captionElement); if (typeof self.$rating == 'undefined' && typeof self.$container == 'undefined') { self.$rating = $(document.createElement("div")).html('
'); self.$container = $(document.createElement("div")); self.$container.before(self.$rating); self.$container.append(self.$rating); self.$element.before(self.$container).appendTo(self.$rating); } self.$stars = self.$rating.find('.rating-stars'); self.generateRating(); self.$clear = !isEmpty(self.$clearElement) ? self.$clearElement : self.$container.find('.' + self.clearButtonBaseClass); self.$caption = !isEmpty(self.$captionElement) ? self.$captionElement : self.$container.find(".caption"); self.setStars(); self.$element.hide(); self.listen(); if (self.showClear) { self.$clear.attr({"class": self.getClearClass()}); } }, checkDisabled: function () { var self = this; self.disabled = validateAttr(self.$element, 'disabled', self.options); self.readonly = validateAttr(self.$element, 'readonly', self.options); self.inactive = (self.disabled || self.readonly); }, getClearClass: function () { return this.clearButtonBaseClass + ' ' + ((this.inactive) ? '' : this.clearButtonActiveClass); }, generateRating: function () { var self = this, clear = self.renderClear(), caption = self.renderCaption(), css = (self.rtl) ? 'rating-container-rtl' : 'rating-container', stars = self.getStars(); css += (self.glyphicon) ? ((self.symbol == '\ue006') ? ' rating-gly-star' : ' rating-gly') : ' rating-uni'; self.$rating.attr('class', css); self.$rating.attr('data-content', stars); self.$stars.attr('data-content', stars); var css = self.rtl ? 'star-rating-rtl' : 'star-rating'; self.$container.attr('class', css + ' rating-' + self.size); if (self.inactive) { self.$container.removeClass('rating-active').addClass('rating-disabled'); } else { self.$container.removeClass('rating-disabled').addClass('rating-active'); } if (typeof self.$caption == 'undefined' && typeof self.$clear == 'undefined') { if (self.rtl) { self.$container.prepend(caption).append(clear); } else { self.$container.prepend(clear).append(caption); } } if (!isEmpty(self.containerClass)) { self.$container.removeClass(self.containerClass).addClass(self.containerClass); } }, getStars: function () { var self = this, numStars = self.stars, stars = ''; for (var i = 1; i <= numStars; i++) { stars += self.symbol; } return stars; }, renderClear: function () { var self = this; if (!self.showClear) { return ''; } var css = self.getClearClass(); if (!isEmpty(self.$clearElement)) { self.$clearElement.removeClass(css).addClass(css).attr({"title": self.clearButtonTitle}); self.$clearElement.html(self.clearButton); return ''; } return '
' + self.clearButton + '
'; }, renderCaption: function () { var self = this, val = self.$element.val(); if (!self.showCaption) { return ''; } var html = self.fetchCaption(val); if (!isEmpty(self.$captionElement)) { self.$captionElement.removeClass('caption').addClass('caption').attr({"title": self.clearCaption}); self.$captionElement.html(html); return ''; } return '
' + html + '
'; }, fetchCaption: function (rating) { var self = this; var val = parseFloat(rating); var css = isEmpty(self.starCaptionClasses[val]) ? self.clearCaptionClass : self.starCaptionClasses[val]; var cap = !isEmpty(self.starCaptions[val]) ? self.starCaptions[val] : self.defaultCaption.replace(/\{rating\}/g, val); var caption = (val == self.clearValue) ? self.clearCaption : cap; return '' + caption + ''; }, getValueFromPosition: function (pos) { var self = this, precision = getDecimalPlaces(self.step), percentage, val, maxWidth = self.$rating.width(); percentage = (pos / maxWidth); val = (self.min + Math.ceil(self.diff * percentage / self.step) * self.step); if (val < self.min) { val = self.min; } else if (val > self.max) { val = self.max; } val = applyPrecision(parseFloat(val), precision); if (self.rtl) { val = self.max - val; } return val; }, setStars: function (pos) { var self = this, min = self.min, max = self.max, step = self.step, val = arguments.length ? self.getValueFromPosition(pos) : (isEmpty(self.$element.val()) ? 0 : self.$element.val()), width = 0, maxWidth = self.$rating.width(), caption = self.fetchCaption(val); width = (val - min) / max * 100; if (self.rtl) { width = 100 - width; } self.$element.val(val); width += '%'; self.$stars.css('width', width); self.$caption.html(caption); }, clear: function () { var self = this; var title = '' + self.clearCaption + ''; self.$stars.removeClass('rated'); if (!self.inactive) { self.$caption.html(title); } self.$element.val(self.clearValue); self.setStars(); self.$element.trigger('rating.clear'); }, reset: function () { var self = this; self.$element.val(self.initialValue); self.setStars(); self.$element.trigger('rating.reset'); }, update: function (val) { if (arguments.length > 0) { var self = this; self.$element.val(val); self.setStars(); } }, refresh: function (options) { var self = this; if (arguments.length) { var cap = ''; self.init($.extend(self.options, options)); if (self.showClear) { self.$clear.show(); } else { self.$clear.hide(); } if (self.showCaption) { self.$caption.show(); } else { self.$caption.hide(); } } } }; //Star rating plugin definition $.fn.rating = function (option) { var args = Array.apply(null, arguments); args.shift(); return this.each(function () { var $this = $(this), data = $this.data('rating'), options = typeof option === 'object' && option; if (!data) { $this.data('rating', (data = new Rating(this, $.extend({}, $.fn.rating.defaults, options, $(this).data())))); } if (typeof option === 'string') { data[option].apply(data, args); } }); }; $.fn.rating.defaults = { stars: 5, glyphicon: true, symbol: null, disabled: false, readonly: false, rtl: false, size: 'md', showClear: true, showCaption: true, defaultCaption: '{rating} Stars', starCaptions: { 0.5: '差到极点了', 1: '非常差', 1.5: '不太好', 2: '不太好', 2.5: '感觉一般般', 3: '感觉还凑合', 3.5: '感觉还凑合', 4: '我挺满意的', 4.5: '总体不错', 5: '超级棒棒棒' }, starCaptionClasses: { 0.5: 'label label-default', 1: 'label label-default', 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-danger', 5: 'label label-danger' }, clearButton: '', clearButtonTitle: 'Clear', clearButtonBaseClass: 'clear-rating', clearButtonActiveClass: 'clear-rating-active', clearCaption: '没有选择评分', clearCaptionClass: 'label label-default', clearValue: 0, captionElement: null, clearElement: null, containerClass: null }; /** * Convert automatically number inputs with class 'rating' * into the star rating control. */ $(document).ready(function () { var $input = $('input.rating'), count = Object.keys($input).length; if (count > 0) { $input.rating(); } }); }(jQuery));