PageSourceSearch

https://members.malcp.org/js/plugins/star-rating/star-rating.js

js malcp.org collected 2026-10-04 13:13:35 UTC 25,379 bytes, 599 lines download raw bytes

1/*!
2 * bootstrap-star-rating v4.0.3
3 * http://plugins.krajee.com/star-rating
4 *
5 * Author: Kartik Visweswaran
6 * Copyright: 2013 - 2017, Kartik Visweswaran, Krajee.com
7 *
8 * Licensed under the BSD 3-Clause
9 * https://github.com/kartik-v/bootstrap-star-rating/blob/master/LICENSE.md
10 */
11(function (factory) {
12    "use strict";
13    //noinspection JSUnresolvedVariable
14    if (typeof define === 'function' && define.amd) { // jshint ignore:line
15        // AMD. Register as an anonymous module.
16        define(['jquery'], factory); // jshint ignore:line
17    } else { // noinspection JSUnresolvedVariable
18        if (typeof module === 'object' && module.exports) { // jshint ignore:line
19            // Node/CommonJS
20            // noinspection JSUnresolvedVariable
21            module.exports = factory(require('jquery')); // jshint ignore:line
22        } else {
23            // Browser globals
24            factory(window.jQuery);
25        }
26    }
27}(function ($) {
28    "use strict";
29
30    $.fn.ratingLocales = {};
31    $.fn.ratingThemes = {};
32
33    var $h, Rating;
34
35    // global helper methods and constants
36    $h = {
37        NAMESPACE: '.rating',
38        DEFAULT_MIN: 0,
39        DEFAULT_MAX: 5,
40        DEFAULT_STEP: 0.5,
41        isEmpty: function (value, trim) {
42            return value === null || value === undefined || value.length === 0 || (trim && $.trim(value) === '');
43        },
44        getCss: function (condition, css) {
45            return condition ? ' ' + css : '';
46        },
47        addCss: function ($el, css) {
48            $el.removeClass(css).addClass(css);
49        },
50        getDecimalPlaces: function (num) {
51            var m = ('' + num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
52            return !m ? 0 : Math.max(0, (m[1] ? m[1].length : 0) - (m[2] ? +m[2] : 0));
53        },
54        applyPrecision: function (val, precision) {
55            return parseFloat(val.toFixed(precision));
56        },
57        handler: function ($el, event, callback, skipOff, skipNS) {
58            var ev = skipNS ? event : event.split(' ').join($h.NAMESPACE + ' ') + $h.NAMESPACE;
59            if (!skipOff) {
60                $el.off(ev);
61            }
62            $el.on(ev, callback);
63        }
64    };
65
66    // rating constructor
67    Rating = function (element, options) {
68        var self = this;
69        self.$element = $(element);
70        self._init(options);
71    };
72    Rating.prototype = {
73        constructor: Rating,
74        _parseAttr: function (vattr, options) {
75            var self = this, $el = self.$element, elType = $el.attr('type'), finalVal, val, chk, out;
76            if (elType === 'range' || elType === 'number') {
77                val = options[vattr] || $el.data(vattr) || $el.attr(vattr);
78                switch (vattr) {
79                    case 'min':
80                        chk = $h.DEFAULT_MIN;
81                        break;
82                    case 'max':
83                        chk = $h.DEFAULT_MAX;
84                        break;
85                    default:
86                        chk = $h.DEFAULT_STEP;
87                }
88                finalVal = $h.isEmpty(val) ? chk : val;
89                out = parseFloat(finalVal);
90            } else {
91                out = parseFloat(options[vattr]);
92            }
93            return isNaN(out) ? chk : out;
94        },
95        _parseValue: function (val) {
96            var self = this, v = parseFloat(val);
97            if (isNaN(v)) {
98                v = self.clearValue;
99            }
100            return (self.zeroAsNull && (v === 0 || v === '0') ? null : v);
101        },
102        _setDefault: function (key, val) {
103            var self = this;
104            if ($h.isEmpty(self[key])) {
105                self[key] = val;
106            }
107        },
108        _initSlider: function (options) {
109            var self = this, v = self.$element.val();
110            self.initialValue = $h.isEmpty(v) ? 0 : v;
111            self._setDefault('min', self._parseAttr('min', options));
112            self._setDefault('max', self._parseAttr('max', options));
113            self._setDefault('step', self._parseAttr('step', options));
114            if (isNaN(self.min) || $h.isEmpty(self.min)) {
115                self.min = $h.DEFAULT_MIN;
116            }
117            if (isNaN(self.max) || $h.isEmpty(self.max)) {
118                self.max = $h.DEFAULT_MAX;
119            }
120            if (isNaN(self.step) || $h.isEmpty(self.step) || self.step === 0) {
121                self.step = $h.DEFAULT_STEP;
122            }
123            self.diff = self.max - self.min;
124        },
125        _initHighlight: function (v) {
126            var self = this, w, cap = self._getCaption();
127            if (!v) {
128                v = self.$element.val();
129            }
130            w = self.getWidthFromValue(v) + '%';
131            self.$filledStars.width(w);
132            self.cache = {caption: cap, width: w, val: v};
133        },
134        _getContainerCss: function () {
135            var self = this;
136            return 'rating-container' +
137                $h.getCss(self.theme, 'theme-' + self.theme) +
138                $h.getCss(self.rtl, 'rating-rtl') +
139                $h.getCss(self.size, 'rating-' + self.size) +
140                $h.getCss(self.animate, 'rating-animate') +
141                $h.getCss(self.disabled || self.readonly, 'rating-disabled') +
142                $h.getCss(self.containerClass, self.containerClass);
143        },
144        _checkDisabled: function () {
145            var self = this, $el = self.$element, opts = self.options;
146            self.disabled = opts.disabled === undefined ? $el.attr('disabled') || false : opts.disabled;
147            self.readonly = opts.readonly === undefined ? $el.attr('readonly') || false : opts.readonly;
148            self.inactive = (self.disabled || self.readonly);
149            $el.attr({disabled: self.disabled, readonly: self.readonly});
150        },
151        _addContent: function (type, content) {
152            var self = this, $container = self.$container, isClear = type === 'clear';
153            if (self.rtl) {
154                return isClear ? $container.append(content) : $container.prepend(content);
155            } else {
156                return isClear ? $container.prepend(content) : $container.append(content);
157            }
158        },
159        _generateRating: function () {
160            var self = this, $el = self.$element, $rating, $container, w;
161            $container = self.$container = $(document.createElement("div")).insertBefore($el);
162            $h.addCss($container, self._getContainerCss());
163            self.$rating = $rating = $(document.createElement("div")).attr('class', 'rating-stars').appendTo($container)
164                .append(self._getStars('empty')).append(self._getStars('filled'));
165            self.$emptyStars = $rating.find('.empty-stars');
166            self.$filledStars = $rating.find('.filled-stars');
167            self._renderCaption();
168            self._renderClear();
169            self._initHighlight();
170            $container.append($el);
171            if (self.rtl) {
172                w = Math.max(self.$emptyStars.outerWidth(), self.$filledStars.outerWidth());
173                self.$emptyStars.width(w);
174            }
175            $el.appendTo($rating);
176        },
177        _getCaption: function () {
178            var self = this;
179            return self.$caption && self.$caption.length ? self.$caption.html() : self.defaultCaption;
180        },
181        _setCaption: function (content) {
182            var self = this;
183            if (self.$caption && self.$caption.length) {
184                self.$caption.html(content);
185            }
186        },
187        _renderCaption: function () {
188            var self = this, val = self.$element.val(), html, $cap = self.captionElement ? $(self.captionElement) : '';
189            if (!self.showCaption) {
190                return;
191            }
192            html = self.fetchCaption(val);
193            if ($cap && $cap.length) {
194                $h.addCss($cap, 'caption');
195                $cap.html(html);
196                self.$caption = $cap;
197                return;
198            }
199            self._addContent('caption', '<div class="caption">' + html + '</div>');
200            self.$caption = self.$container.find(".caption");
201        },
202        _renderClear: function () {
203            var self = this, css, $clr = self.clearElement ? $(self.clearElement) : '';
204            if (!self.showClear) {
205                return;
206            }
207            css = self._getClearClass();
208            if ($clr.length) {
209                $h.addCss($clr, css);
210                $clr.attr({"title": self.clearButtonTitle}).html(self.clearButton);
211                self.$clear = $clr;
212                return;
213            }
214            self._addContent('clear',
215                '<div class="' + css + '" title="' + self.clearButtonTitle + '">' + self.clearButton + '</div>');
216            self.$clear = self.$container.find('.' + self.clearButtonBaseClass);
217        },
218        _getClearClass: function () {
219            var self = this;
220            return self.clearButtonBaseClass + ' ' + (self.inactive ? '' : self.clearButtonActiveClass);
221        },
222        _toggleHover: function (out) {
223            var self = this, w, width, caption;
224            if (!out) {
225                return;
226            }
227            if (self.hoverChangeStars) {
228                w = self.getWidthFromValue(self.clearValue);
229                width = out.val <= self.clearValue ? w + '%' : out.width;
230                self.$filledStars.css('width', width);
231            }
232            if (self.hoverChangeCaption) {
233                caption = out.val <= self.clearValue ? self.fetchCaption(self.clearValue) : out.caption;
234                if (caption) {
235                    self._setCaption(caption + '');
236                }
237            }
238        },
239        _init: function (options) {
240            var self = this, $el = self.$element.addClass('rating-input'), v;
241            self.options = options;
242            $.each(options, function (key, value) {
243                self[key] = value;
244            });
245            if (self.rtl || $el.attr('dir') === 'rtl') {
246                self.rtl = true;
247                $el.attr('dir', 'rtl');
248            }
249            self.starClicked = false;
250            self.clearClicked = false;
251            self._initSlider(options);
252            self._checkDisabled();
253            if (self.displayOnly) {
254                self.inactive = true;
255                self.showClear = false;
256                self.showCaption = false;
257            }
258            self._generateRating();
259            self._initEvents();
260            self._listen();
261            v = self._parseValue($el.val());
262            $el.val(v);
263            return $el.removeClass('rating-loading');
264        },
265        _initEvents: function () {
266            var self = this;
267            self.events = {
268                _getTouchPosition: function (e) {
269                    var pageX = $h.isEmpty(e.pageX) ? e.originalEvent.touches[0].pageX : e.pageX;
270                    return pageX - self.$rating.offset().left;
271                },
272                _listenClick: function (e, callback) {
273                    e.stopPropagation();
274                    e.preventDefault();
275                    if (e.handled !== true) {
276                        callback(e);
277                        e.handled = true;
278                    } else {
279                        return false;
280                    }
281                },
282                _noMouseAction: function (e) {
283                    return !self.hoverEnabled || self.inactive || (e && e.isDefaultPrevented());
284                },
285                initTouch: function (e) {
286                    //noinspection JSUnresolvedVariable
287                    var ev, touches, pos, out, caption, w, width, params, clrVal = self.clearValue || 0,
288                        isTouchCapable = 'ontouchstart' in window ||
289                            (window.DocumentTouch && document instanceof window.DocumentTouch);
290                    if (!isTouchCapable || self.inactive) {
291                        return;
292                    }
293                    ev = e.originalEvent;
294                    //noinspection JSUnresolvedVariable
295                    touches = !$h.isEmpty(ev.touches) ? ev.touches : ev.changedTouches;
296                    pos = self.events._getTouchPosition(touches[0]);
297                    if (e.type === "touchend") {
298                        self._setStars(pos);
299                        params = [self.$element.val(), self._getCaption()];
300                        self.$element.trigger('change').trigger('rating:change', params);
301                        self.starClicked = true;
302                    } else {
303                        out = self.calculate(pos);
304                        caption = out.val <= clrVal ? self.fetchCaption(clrVal) : out.caption;
305                        w = self.getWidthFromValue(clrVal);
306                        width = out.val <= clrVal ? w + '%' : out.width;
307                        self._setCaption(caption);
308                        self.$filledStars.css('width', width);
309                    }
310                },
311                starClick: function (e) {
312                    var pos, params;
313                    self.events._listenClick(e, function (ev) {
314                        if (self.inactive) {
315                            return false;
316                        }
317                        pos = self.events._getTouchPosition(ev);
318                        self._setStars(pos);
319                        params = [self.$element.val(), self._getCaption()];
320                        self.$element.trigger('change').trigger('rating:change', params);
321                        self.starClicked = true;
322                    });
323                },
324                clearClick: function (e) {
325                    self.events._listenClick(e, function () {
326                        if (!self.inactive) {
327                            self.clear();
328                            self.clearClicked = true;
329                        }
330                    });
331                },
332                starMouseMove: function (e) {
333                    var pos, out;
334                    if (self.events._noMouseAction(e)) {
335                        return;
336                    }
337                    self.starClicked = false;
338                    pos = self.events._getTouchPosition(e);
339                    out = self.calculate(pos);
340                    self._toggleHover(out);
341                    self.$element.trigger('rating:hover', [out.val, out.caption, 'stars']);
342                },
343                starMouseLeave: function (e) {
344                    var out;
345                    if (self.events._noMouseAction(e) || self.starClicked) {
346                        return;
347                    }
348                    out = self.cache;
349                    self._toggleHover(out);
350                    self.$element.trigger('rating:hoverleave', ['stars']);
351                },
352                clearMouseMove: function (e) {
353                    var caption, val, width, out;
354                    if (self.events._noMouseAction(e) || !self.hoverOnClear) {
355                        return;
356                    }
357                    self.clearClicked = false;
358                    caption = '<span class="' + self.clearCaptionClass + '">' + self.clearCaption + '</span>';
359                    val = self.clearValue;
360                    width = self.getWidthFromValue(val) || 0;
361                    out = {caption: caption, width: width, val: val};
362                    self._toggleHover(out);
363                    self.$element.trigger('rating:hover', [val, caption, 'clear']);
364                },
365                clearMouseLeave: function (e) {
366                    var out;
367                    if (self.events._noMouseAction(e) || self.clearClicked || !self.hoverOnClear) {
368                        return;
369                    }
370                    out = self.cache;
371                    self._toggleHover(out);
372                    self.$element.trigger('rating:hoverleave', ['clear']);
373                },
374                resetForm: function (e) {
375                    if (e && e.isDefaultPrevented()) {
376                        return;
377                    }
378                    if (!self.inactive) {
379                        self.reset();
380                    }
381                }
382            };
383        },
384        _listen: function () {
385            var self = this, $el = self.$element, $form = $el.closest('form'), $rating = self.$rating,
386                $clear = self.$clear, events = self.events;
387            $h.handler($rating, 'touchstart touchmove touchend', $.proxy(events.initTouch, self));
388            $h.handler($rating, 'click touchstart', $.proxy(events.starClick, self));
389            $h.handler($rating, 'mousemove', $.proxy(events.starMouseMove, self));
390            $h.handler($rating, 'mouseleave', $.proxy(events.starMouseLeave, self));
391            if (self.showClear && $clear.length) {
392                $h.handler($clear, 'click touchstart', $.proxy(events.clearClick, self));
393                $h.handler($clear, 'mousemove', $.proxy(events.clearMouseMove, self));
394                $h.handler($clear, 'mouseleave', $.proxy(events.clearMouseLeave, self));
395            }
396            if ($form.length) {
397                $h.handler($form, 'reset', $.proxy(events.resetForm, self), true);
398            }
399            return $el;
400        },
401        _getStars: function (type) {
402            var self = this, stars = '<span class="' + type + '-stars">', i;
403            for (i = 1; i <= self.stars; i++) {
404                stars += '<span class="star">' + self[type + 'Star'] + '</span>';
405            }
406            return stars + '</span>';
407        },
408        _setStars: function (pos) {
409            var self = this, out = arguments.length ? self.calculate(pos) : self.calculate(), $el = self.$element,
410                v = self._parseValue(out.val);
411            $el.val(v);
412            self.$filledStars.css('width', out.width);
413            self._setCaption(out.caption);
414            self.cache = out;
415            return $el;
416        },
417        showStars: function (val) {
418            var self = this, v = self._parseValue(val);
419            self.$element.val(v);
420            return self._setStars();
421        },
422        calculate: function (pos) {
423            var self = this, defaultVal = $h.isEmpty(self.$element.val()) ? 0 : self.$element.val(),
424                val = arguments.length ? self.getValueFromPosition(pos) : defaultVal,
425                caption = self.fetchCaption(val), width = self.getWidthFromValue(val);
426            width += '%';
427            return {caption: caption, width: width, val: val};
428        },
429        getValueFromPosition: function (pos) {
430            var self = this, precision = $h.getDecimalPlaces(self.step), val, factor, maxWidth = self.$rating.width();
431            factor = (self.diff * pos) / (maxWidth * self.step);
432            factor = self.rtl ? Math.floor(factor) : Math.ceil(factor);
433            val = $h.applyPrecision(parseFloat(self.min + factor * self.step), precision);
434            val = Math.max(Math.min(val, self.max), self.min);
435            return self.rtl ? (self.max - val) : val;
436        },
437        getWidthFromValue: function (val) {
438            var self = this, min = self.min, max = self.max, factor, $r = self.$emptyStars, w;
439            if (!val || val <= min || min === max) {
440                return 0;
441            }
442            w = $r.outerWidth();
443            factor = w ? $r.width() / w : 1;
444            if (val >= max) {
445                return 100;
446            }
447            return (val - min) * factor * 100 / (max - min);
448        },
449        fetchCaption: function (rating) {
450            var self = this, val = parseFloat(rating) || self.clearValue, css, cap, capVal, cssVal, caption,
451                vCap = self.starCaptions, vCss = self.starCaptionClasses;
452            if (val && val !== self.clearValue) {
453                val = $h.applyPrecision(val, $h.getDecimalPlaces(self.step));
454            }
455            cssVal = typeof vCss === "function" ? vCss(val) : vCss[val];
456            capVal = typeof vCap === "function" ? vCap(val) : vCap[val];
457            cap = $h.isEmpty(capVal) ? self.defaultCaption.replace(/\{rating}/g, val) : capVal;
458            css = $h.isEmpty(cssVal) ? self.clearCaptionClass : cssVal;
459            caption = (val === self.clearValue) ? self.clearCaption : cap;
460            return '<span class="' + css + '">' + caption + '</span>';
461        },
462        destroy: function () {
463            var self = this, $el = self.$element;
464            if (!$h.isEmpty(self.$container)) {
465                self.$container.before($el).remove();
466            }
467            $.removeData($el.get(0));
468            return $el.off('rating').removeClass('rating rating-input');
469        },
470        create: function (options) {
471            var self = this, opts = options || self.options || {};
472            return self.destroy().rating(opts);
473        },
474        clear: function () {
475            var self = this, title = '<span class="' + self.clearCaptionClass + '">' + self.clearCaption + '</span>';
476            if (!self.inactive) {
477                self._setCaption(title);
478            }
479            return self.showStars(self.clearValue).trigger('change').trigger('rating:clear');
480        },
481        reset: function () {
482            var self = this;
483            return self.showStars(self.initialValue).trigger('rating:reset');
484        },
485        update: function (val) {
486            var self = this;
487            return arguments.length ? self.showStars(val) : self.$element;
488        },
489        refresh: function (options) {
490            var self = this, $el = self.$element;
491            if (!options) {
492                return $el;
493            }
494            return self.destroy().rating($.extend(true, self.options, options)).trigger('rating:refresh');
495        }
496    };
497
498    $.fn.rating = function (option) {
499        var args = Array.apply(null, arguments), retvals = [];
500        args.shift();
501        this.each(function () {
502            var self = $(this), data = self.data('rating'), options = typeof option === 'object' && option,
503                theme = options.theme || self.data('theme'), lang = options.language || self.data('language') || 'en',
504                thm = {}, loc = {}, opts;
505            if (!data) {
506                if (theme) {
507                    thm = $.fn.ratingThemes[theme] || {};
508                }
509                if (lang !== 'en' && !$h.isEmpty($.fn.ratingLocales[lang])) {
510                    loc = $.fn.ratingLocales[lang];
511                }
512                opts = $.extend(true, {}, $.fn.rating.defaults, thm, $.fn.ratingLocales.en, loc, options, self.data());
513                data = new Rating(this, opts);
514                self.data('rating', data);
515            }
516
517            if (typeof option === 'string') {
518                retvals.push(data[option].apply(data, args));
519            }
520        });
521        switch (retvals.length) {
522            case 0:
523                return this;
524            case 1:
525                return retvals[0] === undefined ? this : retvals[0];
526            default:
527                return retvals;
528        }
529    };
530
531    $.fn.rating.defaults = {
532        theme: '',
533        language: 'en',
534        stars: 5,
535        filledStar: '<i class="glyphicon glyphicon-star"></i>',
536        emptyStar: '<i class="glyphicon glyphicon-star-empty"></i>',
537        containerClass: '',
538        size: 'md',
539        animate: true,
540        displayOnly: false,
541        rtl: false,
542        showClear: true,
543        showCaption: true,
544        starCaptionClasses: {
545            0.5: 'label label-danger badge-danger',
546            1: 'label label-danger badge-danger',
547            1.5: 'label label-warning badge-warning',
548            2: 'label label-warning badge-warning',
549            2.5: 'label label-info badge-info',
550            3: 'label label-info badge-info',
551            3.5: 'label label-primary badge-primary',
552            4: 'label label-primary badge-primary',
553            4.5: 'label label-success badge-success',
554            5: 'label label-success badge-success'
555        },
556        clearButton: '<i class="glyphicon glyphicon-minus-sign"></i>',
557        clearButtonBaseClass: 'clear-rating',
558        clearButtonActiveClass: 'clear-rating-active',
559        clearCaptionClass: 'label label-default',
560        clearValue: null,
561        captionElement: null,
562        clearElement: null,
563        hoverEnabled: true,
564        hoverChangeCaption: true,
565        hoverChangeStars: true,
566        hoverOnClear: true,
567        zeroAsNull: true
568    };
569
570    $.fn.ratingLocales.en = {
571        defaultCaption: '{rating} Stars',
572        starCaptions: {
573            0.5: 'Half Star',
574            1: 'One Star',
575            1.5: 'One & Half Star',
576            2: 'Two Stars',
577            2.5: 'Two & Half Stars',
578            3: 'Three Stars',
579            3.5: 'Three & Half Stars',
580            4: 'Four Stars',
581            4.5: 'Four & Half Stars',
582            5: 'Five Stars'
583        },
584        clearButtonTitle: 'Clear',
585        clearCaption: 'Not Rated'
586    };
587
588    $.fn.rating.Constructor = Rating;
589
590    /**
591     * Convert automatically inputs with class 'rating' into Krajee's star rating control.
592     */
593    $(document).ready(function () {
594        var $input = $('input.rating');
595        if ($input.length) {
596            $input.removeClass('rating-loading').addClass('rating-loading').rating();
597        }
598    });
599}));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.