1/*! 2 * Raty - A Star Rating Plugin 3 * 4 * The MIT License 5 * 6 * author: Washington Botelho 7 * github: wbotelhos/raty 8 * version: 3.0.0 9 * 10 */ 11 12(function (root, factory) { 13 if (typeof define === 'function' && define.amd) { 14 define(['jquery'], factory); 15 } else if (typeof module === 'object' && module.exports) { 16 module.exports = factory(require('jquery')); 17 } else { 18 factory(root.jQuery); 19 } 20}(this, function ($) { 21 'use strict'; 22 23 $.raty = { 24 cancelButton: false, 25 cancelClass: 'raty-cancel', 26 cancelHint: 'Cancel this rating!', 27 cancelOff: 'cancel-off.png', 28 cancelOn: 'cancel-on.png', 29 cancelPlace: 'left', 30 click: undefined, 31 half: false, 32 halfShow: true, 33 hints: ['bad', 'poor', 'average', 'good', 'great'], 34 iconRange: undefined, 35 mouseout: undefined, 36 mouseover: undefined, 37 noRatedMsg: 'Not rated yet!', 38 number: 5, 39 numberMax: 20, 40 path: undefined, 41 precision: false, 42 readOnly: false, 43 round: { down: 0.25, full: 0.6, up: 0.76 }, 44 score: undefined, 45 scoreName: 'score', 46 single: false, 47 space: true, 48 starHalf: 'dealer/1/upload/star-half.png', 49 starOff: 'star-off-yellow.png', 50 starOn: 'star-on-yellow.png', 51 starType: 'img', 52 target: undefined, 53 targetFormat: '{score}', 54 targetKeep: false, 55 targetScore: undefined, 56 targetText: '', 57 targetType: 'hint' 58 }; 59 60 $.fn.raty = function (options) { 61 return this.each(function () { 62 var instance = new $.raty.Raty(this, options); 63 64 return instance._create(); 65 }); 66 }; 67 68 $.raty.Raty = (function () { 69 var Raty = function (element, options) { 70 this.element = element; 71 this.self = $(element); 72 this.opt = $.extend(true, {}, $.raty, options, this.self.data()); 73 }; 74 75 Raty.prototype = { 76 _create: function () { 77 this._executeCallbacks(); 78 this._adjustNumber(); 79 this._adjustHints(); 80 81 this.opt.score = this._adjustedScore(this.opt.score); 82 83 if (this.opt.starType !== 'img') { 84 this._adjustStarName(); 85 } 86 87 this._setPath(); 88 this._createStars(); 89 90 if (this.opt.cancelButton) { 91 this._createCancel(); 92 } 93 94 if (this.opt.precision) { 95 this._adjustPrecision(); 96 } 97 98 this._createScore(); 99 100 this._apply(this.opt.score); 101 this._setTitle(this.opt.score); 102 this._target(this.opt.score); 103 104 if (this.opt.readOnly) { 105 this._lock(); 106 } else { 107 this.element.style.cursor = 'pointer'; 108 109 this._binds(); 110 } 111 112 this.self.data('raty', this); 113 }, 114 115 // TODO: model spec 116 _adjustedScore: function (score) { 117 if (score || score === 0) { 118 return this._between(score, 0, this.opt.number); 119 } 120 }, 121 122 _adjustHints: function () { 123 if (!this.opt.hints) { 124 this.opt.hints = []; 125 } 126 127 if (!this.opt.halfShow && !this.opt.half) { 128 return; 129 } 130 131 var steps = this.opt.precision ? 10 : 2; 132 133 for (var i = 0; i < this.opt.number; i++) { 134 var group = this.opt.hints[i]; 135 136 if (Object.prototype.toString.call(group) !== '[object Array]') { 137 group = [group]; 138 } 139 140 this.opt.hints[i] = []; 141 142 for (var j = 0; j < steps; j++) { 143 var hint = group[j]; 144 var last = group[group.length - 1]; 145 146 if (last === undefined) { 147 last = null; 148 } 149 150 this.opt.hints[i][j] = hint === undefined ? last : hint; 151 } 152 } 153 }, 154 155 _adjustNumber: function () { 156 this.opt.number = this._between(this.opt.number, 1, this.opt.numberMax); 157 }, 158 159 _adjustPrecision: function () { 160 this.opt.half = true; 161 }, 162 163 _adjustStarName: function () { 164 var replaces = ['cancelOff', 'cancelOn', 'starHalf', 'starOff', 'starOn']; 165 166 this.opt.path = ''; 167 168 for (var i = 0; i < replaces.length; i++) { 169 this.opt[replaces[i]] = this.opt[replaces[i]].replace('.', '-'); 170 } 171 }, 172 173 // TODO: model spec 174 _apply: function (score) { 175 this._fill(score); 176 177 if (score) { 178 if (score > 0) { 179 this.scoreField.val(score); 180 } 181 182 this._roundStars(score); 183 } 184 }, 185 186 _attributesForIndex: function (i) { 187 var name = this._nameForIndex(i); 188 var attributes = { alt: i, src: this.opt.path + this.opt[name] }; 189
190 if (this.opt.starType !== 'img') { 191 attributes = { 'data-alt': i, 'class': this.opt[name] }; 192 } 193 194 attributes.title = this._getHint(i); 195 196 return attributes; 197 }, 198 199 _between: function (value, min, max) { 200 return Math.min(Math.max(parseFloat(value), min), max); 201 }, 202 203 // TODO: model spec 204 _binds: function () { 205 if (this.cancelButton) { 206 this._bindOverCancel(); 207 this._bindClickCancel(); 208 this._bindOutCancel(); 209 } 210 211 this._bindOver(); 212 this._bindClick(); 213 this._bindOut(); 214 }, 215 216 // TODO: model spec 217 _bindClick: function () { 218 var that = this; 219 220 this.stars.on('click.raty', function (evt) { 221 if (that.self.data('readonly')) { 222 return; 223 } 224 225 var execute = true; 226 var score = (that.opt.half || that.opt.precision) ? that.self.data('score') : (this.alt || $(this).data('alt')); 227 228 if (that.opt.half && !that.opt.precision) { 229 score = that._roundHalfScore(score); 230 } 231 232 if (that.opt.click) { 233 execute = that.opt.click.call(that.element, +score, evt); 234 } 235 236 if (execute || execute === undefined) { 237 that._apply(+score); 238 } 239 }); 240 }, 241 242 // TODO: model spec 243 _bindClickCancel: function () { 244 this.cancelButton.on('click.raty', function (evt) { 245 this.scoreField.removeAttr('value'); 246 247 if (this.opt.click) { 248 this.opt.click.call(this.element, null, evt); 249 } 250 }.bind(this)); 251 }, 252 253 // TODO: model spec 254 _bindOut: function () { 255 this.self.on('mouseleave.raty', function (evt) { 256 var score = +this.scoreField.val() || undefined; 257 258 this._apply(score); 259 this._target(score, evt); 260 this._resetTitle(); 261 262 if (this.opt.mouseout) { 263 this.opt.mouseout.call(this.element, score, evt); 264 } 265 }.bind(this)); 266 }, 267 268 // TODO: model spec 269 _bindOutCancel: function () { 270 var that = this; 271 272 this.cancelButton.on('mouseleave.raty', function (evt) { 273 var icon = that.opt.cancelOff; 274 275 if (that.opt.starType !== 'img') { 276 icon = that.opt.cancelClass + ' ' + icon; 277 } 278 279 that._setIcon(this, icon); 280 281 if (that.opt.mouseout) { 282 var score = +that.scoreField.val() || undefined; 283 284 that.opt.mouseout.call(that.element, score, evt); 285 } 286 }); 287 }, 288 289 // TODO: model spec 290 _bindOver: function () { 291 var that = this; 292 var action = that.opt.half ? 'mousemove.raty' : 'mouseover.raty'; 293 294 this.stars.on(action, function (evt) { 295 var score = that._getScoreByPosition(evt, this); 296 297 that._fill(score); 298 299 if (that.opt.half) { 300 that._roundStars(score, evt); 301 that._setTitle(score, evt); 302 303 that.self.data('score', score); 304 } 305 306 that._target(score, evt); 307 308 if (that.opt.mouseover) { 309 that.opt.mouseover.call(that.element, score, evt); 310 } 311 }); 312 }, 313 314 // TODO: model spec 315 _bindOverCancel: function () { 316 var that = this; 317 318 this.cancelButton.on('mouseover.raty', function (evt) { 319 if (that.self.data('readonly')) { 320 return; 321 } 322 323 var starOff = that.opt.path + that.opt.starOff; 324 var icon = that.opt.cancelOn; 325 326 if (that.opt.starType === 'img') { 327 that.stars.attr('src', starOff); 328 } else { 329 icon = that.opt.cancelClass + ' ' + icon; 330 331 that.stars.attr('class', starOff); 332 } 333 334 that._setIcon(this, icon); 335 that._target(null, evt); 336 337 if (that.opt.mouseover) { 338 that.opt.mouseover.call(that.element, null); 339 } 340 }); 341 }, 342 343 // TODO: model spec 344 _buildScoreField: function () { 345 return $('<input />', { name: this.opt.scoreName, type: 'hidden' }).appendTo(this.self); 346 }, 347 348 // TODO: model spec 349 _createCancel: function () { 350 var icon = this.opt.path + this.opt.cancelOff; 351 var button = $('<' + this.opt.starType + ' />', { title: this.opt.cancelHint, 'class': this.opt.cancelClass }); 352 353 if (this.opt.starType === 'img') { 354 button.attr({ src: icon, alt: 'x' });
355 } else { 356 // TODO: use $.data 357 button.attr('data-alt', 'x').addClass(icon); 358 } 359 360 if (this.opt.cancelPlace === 'left') { 361 this.self.prepend(' ').prepend(button); 362 } else { 363 this.self.append(' ').append(button); 364 } 365 366 this.cancelButton = button; 367 }, 368 369 // TODO: model spec 370 _createScore: function () { 371 var score = $(this.opt.targetScore); 372 373 this.scoreField = score.length ? score : this._buildScoreField(); 374 }, 375 376 _createStars: function () { 377 for (var i = 1; i <= this.opt.number; i++) { 378 var attributes = this._attributesForIndex(i); 379 380 $('<' + this.opt.starType + ' />', attributes).appendTo(this.element); 381 382 if (this.opt.space && i < this.opt.number) { 383 this.self.append(' '); 384 } 385 } 386 387 this.stars = this.self.children(this.opt.starType); 388 }, 389 390 // TODO: model spec 391 _error: function (message) { 392 $(this).text(message); 393 394 $.error(message); 395 }, 396 397 _executeCallbacks: function () { 398 var options = ['number', 'readOnly', 'score', 'scoreName', 'target', 'path']; 399 400 for (var i = 0; i < options.length; i++) { 401 if (typeof this.opt[options[i]] === 'function') { 402 var value = this.opt[options[i]].call(this.element); 403 404 if (value) { 405 this.opt[options[i]] = value; 406 } else { 407 delete this.opt[options[i]]; 408 } 409 } 410 } 411 }, 412 413 // TODO: model spec 414 _fill: function (score) { 415 var hash = 0; 416 417 for (var i = 1; i <= this.stars.length; i++) { 418 var icon; 419 var star = this.stars[i - 1]; 420 var turnOn = this._turnOn(i, score); 421 422 if (this.opt.iconRange && this.opt.iconRange.length > hash) { 423 var irange = this.opt.iconRange[hash]; 424 425 icon = this._getRangeIcon(irange, turnOn); 426 427 if (i <= irange.range) { 428 this._setIcon(star, icon); 429 } 430 431 if (i === irange.range) { 432 hash++; 433 } 434 } else { 435 icon = this.opt[turnOn ? 'starOn' : 'starOff']; 436 437 this._setIcon(star, icon); 438 } 439 } 440 }, 441 442 _getDecimal: function (number, fractions) { 443 var decimal = number.toString().split('.')[1]; 444 var result = 0; 445 446 if (decimal) { 447 result = parseInt(decimal.slice(0, fractions), 10); 448 449 if (decimal.slice(1, 5) === '9999') { 450 result++; 451 } 452 } 453 454 return result; 455 }, 456 457 // TODO: model spec 458 _getRangeIcon: function (irange, turnOn) { 459 return turnOn ? irange.on || this.opt.starOn : irange.off || this.opt.starOff; 460 }, 461 462 // TODO: model spec 463 _getScoreByPosition: function (evt, icon) { 464 var score = parseInt(icon.alt || icon.getAttribute('data-alt'), 10); 465 466 if (this.opt.half) { 467 var size = this._getWidth(); 468 var percent = parseFloat((evt.pageX - $(icon).offset().left) / size); 469 470 score = score - 1 + percent; 471 } 472 473 return score; 474 }, 475 476 // TODO: model spec 477 _getHint: function (score, evt) { 478 if (score !== 0 && !score) { 479 return this.opt.noRatedMsg; 480 } 481 482 var decimal = this._getDecimal(score, 1);
483 var integer = Math.ceil(score); 484 var group = this.opt.hints[(integer || 1) - 1]; 485 var hint = group; 486 var set = !evt || this.isMove; 487 488 if (this.opt.precision) { 489 if (set) { 490 decimal = decimal === 0 ? 9 : decimal - 1; 491 } 492 493 hint = group[decimal]; 494 } else if (this.opt.halfShow || this.opt.half) { 495 decimal = set && decimal === 0 ? 1 : decimal > 5 ? 1 : 0; 496 497 hint = group[decimal]; 498 } 499 500 return hint === '' ? '' : hint || score; 501 }, 502 503 // TODO: model spec 504 _getWidth: function () { 505 var width = this.stars[0].width || parseFloat(this.stars.eq(0).css('font-size')); 506 507 if (!width) { 508 this._error('Could not get the icon width!'); 509 } 510 511 return width; 512 }, 513 514 // TODO: model spec 515 _lock: function () { 516 var hint = this._getHint(this.scoreField.val()); 517 518 this.element.style.cursor = ''; 519 this.element.title = hint; 520 521 this.scoreField.prop('readonly', true); 522 this.stars.prop('title', hint); 523 524 if (this.cancelButton) { 525 this.cancelButton.hide(); 526 } 527 528 this.self.data('readonly', true); 529 }, 530 531 _nameForIndex: function (i) { 532 return this.opt.score && this.opt.score >= i ? 'starOn' : 'starOff'; 533 }, 534 535 // TODO: model spec 536 _resetTitle: function () { 537 for (var i = 0; i < this.opt.number; i++) { 538 this.stars[i].title = this._getHint(i + 1); 539 } 540 }, 541 542 // TODO: model spec 543 _roundHalfScore: function (score) { 544 var integer = parseInt(score, 10); 545 var decimal = this._getDecimal(score, 1); 546 547 if (decimal !== 0) { 548 decimal = decimal > 5 ? 1 : 0.5; 549 } 550 551 return integer + decimal; 552 }, 553 554 // TODO: model spec 555 _roundStars: function (score, evt) { 556 var name = this._starName(score, evt); 557 558 if (name) { 559 var icon = this.opt[name]; 560 var star = this.stars[Math.ceil(score) - 1]; 561 562 this._setIcon(star, icon); 563 } // Full down: [x.00 .. x.25] 564 }, 565 566 // TODO: model spec 567 _setIcon: function (star, icon) { 568 star[this.opt.starType === 'img' ? 'src' : 'className'] = this.opt.path + icon; 569 }, 570 571 _setPath: function () { 572 this.opt.path = this.opt.path || ''; 573 574 if (this.opt.path && this.opt.path.slice(-1)[0] !== '/') { 575 this.opt.path += '/'; 576 } 577 }, 578 579 // TODO: model spec 580 _setTarget: function (target, score) { 581 if (score) { 582 score = this.opt.targetFormat.toString().replace('{score}', score); 583 } 584 585 if (target.is(':input')) { 586 target.val(score); 587 } else { 588 target.html(score); 589 } 590 }, 591 592 // TODO: model spec 593 _setTitle: function (score, evt) { 594 if (score) { 595 var integer = parseInt(Math.ceil(score), 10); 596 var star = this.stars[integer - 1]; 597 598 star.title = this._getHint(score, evt); 599 } 600 }, 601 602 _starName: function (score, evt) { 603 var decimal = +(score % 1).toFixed(2); 604 605 if (evt || this.isMove) { 606 return decimal > 0.5 ? 'starOn' : 'starHalf'; 607 } 608 609 if (decimal <= this.opt.round.down) { // Down: [x.00 ... x.25] 610 return; 611 } 612 613 if (this.opt.halfShow && decimal < this.opt.round.up) { // Half: [x.26 ... x.75] 614 return 'starHalf'; 615 } 616 617 if (decimal < this.opt.round.full) { // Off: [x.26 .. x.6] 618 return 'starOff'; 619 } 620 621 return 'starOn'; // Up: [x.26 ...] || [x.6 ...] 622 }, 623 624 // TODO: model spec 625 _target: function (score, evt) { 626 if (this.opt.target) { 627 var target = $(this.opt.target); 628 629 if (!target.length) { 630 this._error('Target selector invalid or missing!'); 631 } 632 633 var mouseover = evt && evt.type === 'mouseover'; 634 635 if (score === undefined) { 636 score = this.opt.targetText; 637 } else if (score === null) { 638 score = mouseover ? this.opt.cancelHint : this.opt.targetText; 639 } else { 640 if (this.opt.targetType === 'hint') { 641 score = this._getHint(score, evt); 642 } else if (this.opt.precision) { 643 score = parseFloat(score).toFixed(1); 644 } 645 646 var mousemove = evt && evt.type === 'mousemove'; 647 648 if (!mouseover && !mousemove && !this.opt.targetKeep) { 649 score = this.opt.targetText; 650 } 651 } 652 653 this._setTarget(target, score); 654 } 655 }, 656 657 // TODO: model spec 658 _turnOn: function (i, score) { 659 return this.opt.single ? (i === score) : (i <= score); 660 }, 661 662 // TODO: model spec 663 _unlock: function () { 664 this.element.style.cursor = 'pointer'; 665 this.element.removeAttribute('title'); 666 667 this.scoreField.removeAttr('readonly'); 668 669 this.self.data('readonly', false); 670 671 this._resetTitle(); 672 673 if (this.cancelButton) { 674 this.cancelButton.css('display', ''); 675 } 676 }, 677 678 // TODO: model spec 679 cancel: function (click) { 680 if (this.self.data('readonly') !== true) { 681 this[click ? 'click' : 'score'].call(this, null); 682 683 this.scoreField.removeAttr('value'); 684 } 685 }, 686 687 // TODO: model spec 688 click: function (score) { 689 if (this.self.data('readonly') !== true) { 690 score = this._adjustedScore(score); 691 692 this._apply(score); 693 694 if (this.opt.click) { 695 this.opt.click.call(this.element, score, $.Event('click')); 696 } 697 698 this._target(score); 699 } 700 }, 701 702 // TODO: model spec 703 getScore: function () { 704 var score = []; 705 var value; 706 707 value = this.scoreField.val(); 708 709 score.push(value ? +value : undefined); 710 711 return (score.length > 1) ? score : score[0]; 712 }, 713 714 // TODO: model spec 715 move: function (score) { 716 var integer = parseInt(score, 10); 717 var decimal = this._getDecimal(score, 1); 718 719 if (integer >= this.opt.number) { 720 integer = this.opt.number - 1; 721 decimal = 10; 722 } 723 724 var width = this._getWidth(); 725 var steps = width / 10; 726 var star = $(this.stars[integer]); 727 var percent = star.offset().left + steps * decimal; 728 var evt = $.Event('mousemove', { pageX: percent }); 729 730 this.isMove = true; 731 732 star.trigger(evt); 733 734 this.isMove = false; 735 }, 736 737 // TODO: model spec 738 readOnly: function (readonly) { 739 if (this.self.data('readonly') !== readonly) { 740 if (readonly) { 741 this.self.off('.raty').children(this.opt.starType).off('.raty'); 742 743 this._lock(); 744 } else { 745 this._binds(); 746 this._unlock(); 747 } 748 749 this.self.data('readonly', readonly); 750 } 751 }, 752 753 // TODO: model spec 754 score: function () { 755 return arguments.length ? this.setScore.apply(this, arguments) : this.get
755Score(); 756 }, 757 758 setScore: function (score) { 759 if (this.self.data('readonly') !== true) { 760 score = this._adjustedScore(score); 761 762 this._apply(score); 763 this._target(score); 764 } 765 } 766 }; 767 768 return Raty; 769 })(); 770}));
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.