[6512] | 1 | /*!
|
---|
| 2 | * jQuery Raty - A Star Rating Plugin - http://wbotelhos.com/raty
|
---|
| 3 | * -------------------------------------------------------------------
|
---|
| 4 | *
|
---|
| 5 | * jQuery Raty is a plugin that generates a customizable star rating.
|
---|
| 6 | *
|
---|
| 7 | * Licensed under The MIT License
|
---|
| 8 | *
|
---|
| 9 | * @version 2.4.5
|
---|
| 10 | * @since 2010.06.11
|
---|
| 11 | * @author Washington Botelho
|
---|
| 12 | * @documentation wbotelhos.com/raty
|
---|
| 13 | * @twitter twitter.com/wbotelhos
|
---|
| 14 | *
|
---|
| 15 | * Usage:
|
---|
| 16 | * -------------------------------------------------------------------
|
---|
| 17 | * $('#star').raty();
|
---|
| 18 | *
|
---|
| 19 | * <div id="star"></div>
|
---|
| 20 | *
|
---|
| 21 | */
|
---|
| 22 |
|
---|
| 23 | ;(function($) {
|
---|
| 24 |
|
---|
| 25 | var methods = {
|
---|
| 26 | init: function(settings) {
|
---|
| 27 | return this.each(function() {
|
---|
| 28 | var self = this,
|
---|
| 29 | $this = $(self).empty();
|
---|
| 30 |
|
---|
| 31 | self.opt = $.extend(true, {}, $.fn.raty.defaults, settings);
|
---|
| 32 |
|
---|
| 33 | $this.data('settings', self.opt);
|
---|
| 34 |
|
---|
| 35 | self.opt.number = methods.between(self.opt.number, 0, 20);
|
---|
| 36 |
|
---|
| 37 | if (self.opt.path.substring(self.opt.path.length - 1, self.opt.path.length) != '/') {
|
---|
| 38 | self.opt.path += '/';
|
---|
| 39 | }
|
---|
| 40 |
|
---|
| 41 | if (typeof self.opt.score == 'function') {
|
---|
| 42 | self.opt.score = self.opt.score.call(self);
|
---|
| 43 | }
|
---|
| 44 |
|
---|
| 45 | if (self.opt.score) {
|
---|
| 46 | self.opt.score = methods.between(self.opt.score, 0, self.opt.number);
|
---|
| 47 | }
|
---|
| 48 |
|
---|
| 49 | for (var i = 1; i <= self.opt.number; i++) {
|
---|
| 50 | $('<img />', {
|
---|
| 51 | src : self.opt.path + ((!self.opt.score || self.opt.score < i) ? self.opt.starOff : self.opt.starOn),
|
---|
| 52 | alt : i,
|
---|
| 53 | title : (i <= self.opt.hints.length && self.opt.hints[i - 1] !== null) ? self.opt.hints[i - 1] : i
|
---|
| 54 | }).appendTo(self);
|
---|
| 55 |
|
---|
| 56 | if (self.opt.space) {
|
---|
| 57 | $this.append((i < self.opt.number) ? ' ' : '');
|
---|
| 58 | }
|
---|
| 59 | }
|
---|
| 60 |
|
---|
| 61 | self.stars = $this.children('img:not(".raty-cancel")');
|
---|
| 62 | self.score = $('<input />', { type: 'hidden', name: self.opt.scoreName }).appendTo(self);
|
---|
| 63 |
|
---|
| 64 | if (self.opt.score && self.opt.score > 0) {
|
---|
| 65 | self.score.val(self.opt.score);
|
---|
| 66 | methods.roundStar.call(self, self.opt.score);
|
---|
| 67 | }
|
---|
| 68 |
|
---|
| 69 | if (self.opt.iconRange) {
|
---|
| 70 | methods.fill.call(self, self.opt.score);
|
---|
| 71 | }
|
---|
| 72 |
|
---|
| 73 | methods.setTarget.call(self, self.opt.score, self.opt.targetKeep);
|
---|
| 74 |
|
---|
| 75 | var space = self.opt.space ? 4 : 0,
|
---|
| 76 | width = self.opt.width || (self.opt.number * self.opt.size + self.opt.number * space);
|
---|
| 77 |
|
---|
| 78 | if (self.opt.cancel) {
|
---|
| 79 | self.cancel = $('<img />', { src: self.opt.path + self.opt.cancelOff, alt: 'x', title: self.opt.cancelHint, 'class': 'raty-cancel' });
|
---|
| 80 |
|
---|
| 81 | if (self.opt.cancelPlace == 'left') {
|
---|
| 82 | $this.prepend(' ').prepend(self.cancel);
|
---|
| 83 | } else {
|
---|
| 84 | $this.append(' ').append(self.cancel);
|
---|
| 85 | }
|
---|
| 86 |
|
---|
| 87 | width += (self.opt.size + space);
|
---|
| 88 | }
|
---|
| 89 |
|
---|
| 90 | if (self.opt.readOnly) {
|
---|
| 91 | methods.fixHint.call(self);
|
---|
| 92 |
|
---|
| 93 | if (self.cancel) {
|
---|
| 94 | self.cancel.hide();
|
---|
| 95 | }
|
---|
| 96 | } else {
|
---|
| 97 | $this.css('cursor', 'pointer');
|
---|
| 98 |
|
---|
| 99 | methods.bindAction.call(self);
|
---|
| 100 | }
|
---|
| 101 |
|
---|
| 102 | $this.css('width', width);
|
---|
| 103 | });
|
---|
| 104 | }, between: function(value, min, max) {
|
---|
| 105 | return Math.min(Math.max(parseFloat(value), min), max);
|
---|
| 106 | }, bindAction: function() {
|
---|
| 107 | var self = this,
|
---|
| 108 | $this = $(self);
|
---|
| 109 |
|
---|
| 110 | $this.mouseleave(function() {
|
---|
| 111 | var score = self.score.val() || undefined;
|
---|
| 112 |
|
---|
| 113 | methods.initialize.call(self, score);
|
---|
| 114 | methods.setTarget.call(self, score, self.opt.targetKeep);
|
---|
| 115 |
|
---|
| 116 | if (self.opt.mouseover) {
|
---|
| 117 | self.opt.mouseover.call(self, score);
|
---|
| 118 | }
|
---|
| 119 | });
|
---|
| 120 |
|
---|
| 121 | var action = self.opt.half ? 'mousemove' : 'mouseover';
|
---|
| 122 |
|
---|
| 123 | if (self.opt.cancel) {
|
---|
| 124 | self.cancel.mouseenter(function() {
|
---|
| 125 | $(this).attr('src', self.opt.path + self.opt.cancelOn);
|
---|
| 126 |
|
---|
| 127 | self.stars.attr('src', self.opt.path + self.opt.starOff);
|
---|
| 128 |
|
---|
| 129 | methods.setTarget.call(self, null, true);
|
---|
| 130 |
|
---|
| 131 | if (self.opt.mouseover) {
|
---|
| 132 | self.opt.mouseover.call(self, null);
|
---|
| 133 | }
|
---|
| 134 | }).mouseleave(function() {
|
---|
| 135 | $(this).attr('src', self.opt.path + self.opt.cancelOff);
|
---|
| 136 |
|
---|
| 137 | if (self.opt.mouseover) {
|
---|
| 138 | self.opt.mouseover.call(self, self.score.val() || null);
|
---|
| 139 | }
|
---|
| 140 | }).click(function(evt) {
|
---|
| 141 | self.score.removeAttr('value');
|
---|
| 142 |
|
---|
| 143 | if (self.opt.click) {
|
---|
| 144 | self.opt.click.call(self, null, evt);
|
---|
| 145 | }
|
---|
| 146 | });
|
---|
| 147 | }
|
---|
| 148 |
|
---|
| 149 | self.stars.bind(action, function(evt) {
|
---|
| 150 | var value = parseInt(this.alt, 10);
|
---|
| 151 |
|
---|
| 152 | if (self.opt.half) {
|
---|
| 153 | var position = parseFloat((evt.pageX - $(this).offset().left) / self.opt.size),
|
---|
| 154 | diff = (position > .5) ? 1 : .5;
|
---|
| 155 |
|
---|
| 156 | value = parseFloat(this.alt) - 1 + diff;
|
---|
| 157 |
|
---|
| 158 | methods.fill.call(self, value);
|
---|
| 159 |
|
---|
| 160 | if (self.opt.precision) {
|
---|
| 161 | value = value - diff + position;
|
---|
| 162 | }
|
---|
| 163 |
|
---|
| 164 | methods.showHalf.call(self, value);
|
---|
| 165 | } else {
|
---|
| 166 | methods.fill.call(self, value);
|
---|
| 167 | }
|
---|
| 168 |
|
---|
| 169 | $this.data('score', value);
|
---|
| 170 |
|
---|
| 171 | methods.setTarget.call(self, value, true);
|
---|
| 172 |
|
---|
| 173 | if (self.opt.mouseover) {
|
---|
| 174 | self.opt.mouseover.call(self, value, evt);
|
---|
| 175 | }
|
---|
| 176 | }).click(function(evt) {
|
---|
| 177 | self.score.val((self.opt.half || self.opt.precision) ? $this.data('score') : this.alt);
|
---|
| 178 |
|
---|
| 179 | if (self.opt.click) {
|
---|
| 180 | self.opt.click.call(self, self.score.val(), evt);
|
---|
| 181 | }
|
---|
| 182 | });
|
---|
| 183 | }, cancel: function(isClick) {
|
---|
| 184 | return $(this).each(function() {
|
---|
| 185 | var self = this,
|
---|
| 186 | $this = $(self);
|
---|
| 187 |
|
---|
| 188 | if ($this.data('readonly') === true) {
|
---|
| 189 | return this;
|
---|
| 190 | }
|
---|
| 191 |
|
---|
| 192 | if (isClick) {
|
---|
| 193 | methods.click.call(self, null);
|
---|
| 194 | } else {
|
---|
| 195 | methods.score.call(self, null);
|
---|
| 196 | }
|
---|
| 197 |
|
---|
| 198 | self.score.removeAttr('value');
|
---|
| 199 | });
|
---|
| 200 | }, click: function(score) {
|
---|
| 201 | return $(this).each(function() {
|
---|
| 202 | if ($(this).data('readonly') === true) {
|
---|
| 203 | return this;
|
---|
| 204 | }
|
---|
| 205 |
|
---|
| 206 | methods.initialize.call(this, score);
|
---|
| 207 |
|
---|
| 208 | if (this.opt.click) {
|
---|
| 209 | this.opt.click.call(this, score);
|
---|
| 210 | } else {
|
---|
| 211 | methods.error.call(this, 'you must add the "click: function(score, evt) { }" callback.');
|
---|
| 212 | }
|
---|
| 213 |
|
---|
| 214 | methods.setTarget.call(this, score, true);
|
---|
| 215 | });
|
---|
| 216 | }, error: function(message) {
|
---|
| 217 | $(this).html(message);
|
---|
| 218 |
|
---|
| 219 | $.error(message);
|
---|
| 220 | }, fill: function(score) {
|
---|
| 221 | var self = this,
|
---|
| 222 | number = self.stars.length,
|
---|
| 223 | count = 0,
|
---|
| 224 | $star ,
|
---|
| 225 | star ,
|
---|
| 226 | icon ;
|
---|
| 227 |
|
---|
| 228 | for (var i = 1; i <= number; i++) {
|
---|
| 229 | $star = self.stars.eq(i - 1);
|
---|
| 230 |
|
---|
| 231 | if (self.opt.iconRange && self.opt.iconRange.length > count) {
|
---|
| 232 | star = self.opt.iconRange[count];
|
---|
| 233 |
|
---|
| 234 | if (self.opt.single) {
|
---|
| 235 | icon = (i == score) ? (star.on || self.opt.starOn) : (star.off || self.opt.starOff);
|
---|
| 236 | } else {
|
---|
| 237 | icon = (i <= score) ? (star.on || self.opt.starOn) : (star.off || self.opt.starOff);
|
---|
| 238 | }
|
---|
| 239 |
|
---|
| 240 | if (i <= star.range) {
|
---|
| 241 | $star.attr('src', self.opt.path + icon);
|
---|
| 242 | }
|
---|
| 243 |
|
---|
| 244 | if (i == star.range) {
|
---|
| 245 | count++;
|
---|
| 246 | }
|
---|
| 247 | } else {
|
---|
| 248 | if (self.opt.single) {
|
---|
| 249 | icon = (i == score) ? self.opt.starOn : self.opt.starOff;
|
---|
| 250 | } else {
|
---|
| 251 | icon = (i <= score) ? self.opt.starOn : self.opt.starOff;
|
---|
| 252 | }
|
---|
| 253 |
|
---|
| 254 | $star.attr('src', self.opt.path + icon);
|
---|
| 255 | }
|
---|
| 256 | }
|
---|
| 257 | }, fixHint: function() {
|
---|
| 258 | var $this = $(this),
|
---|
| 259 | score = parseInt(this.score.val(), 10),
|
---|
| 260 | hint = this.opt.noRatedMsg;
|
---|
| 261 |
|
---|
| 262 | if (!isNaN(score) && score > 0) {
|
---|
| 263 | hint = (score <= this.opt.hints.length && this.opt.hints[score - 1] !== null) ? this.opt.hints[score - 1] : score;
|
---|
| 264 | }
|
---|
| 265 |
|
---|
| 266 | $this.data('readonly', true).css('cursor', 'default').attr('title', hint);
|
---|
| 267 |
|
---|
| 268 | this.score.attr('readonly', 'readonly');
|
---|
| 269 | this.stars.attr('title', hint);
|
---|
| 270 | }, getScore: function() {
|
---|
| 271 | var score = [],
|
---|
| 272 | value ;
|
---|
| 273 |
|
---|
| 274 | $(this).each(function() {
|
---|
| 275 | value = this.score.val();
|
---|
| 276 |
|
---|
| 277 | score.push(value ? parseFloat(value) : undefined);
|
---|
| 278 | });
|
---|
| 279 |
|
---|
| 280 | return (score.length > 1) ? score : score[0];
|
---|
| 281 | }, readOnly: function(isReadOnly) {
|
---|
| 282 | return this.each(function() {
|
---|
| 283 | var $this = $(this);
|
---|
| 284 |
|
---|
| 285 | if ($this.data('readonly') === isReadOnly) {
|
---|
| 286 | return this;
|
---|
| 287 | }
|
---|
| 288 |
|
---|
| 289 | if (this.cancel) {
|
---|
| 290 | if (isReadOnly) {
|
---|
| 291 | this.cancel.hide();
|
---|
| 292 | } else {
|
---|
| 293 | this.cancel.show();
|
---|
| 294 | }
|
---|
| 295 | }
|
---|
| 296 |
|
---|
| 297 | if (isReadOnly) {
|
---|
| 298 | $this.unbind();
|
---|
| 299 |
|
---|
| 300 | $this.children('img').unbind();
|
---|
| 301 |
|
---|
| 302 | methods.fixHint.call(this);
|
---|
| 303 | } else {
|
---|
| 304 | methods.bindAction.call(this);
|
---|
| 305 | methods.unfixHint.call(this);
|
---|
| 306 | }
|
---|
| 307 |
|
---|
| 308 | $this.data('readonly', isReadOnly);
|
---|
| 309 | });
|
---|
| 310 | }, reload: function() {
|
---|
| 311 | return methods.set.call(this, {});
|
---|
| 312 | }, roundStar: function(score) {
|
---|
| 313 | var diff = (score - Math.floor(score)).toFixed(2);
|
---|
| 314 |
|
---|
| 315 | if (diff > this.opt.round.down) {
|
---|
| 316 | var icon = this.opt.starOn; // Full up: [x.76 .. x.99]
|
---|
| 317 |
|
---|
| 318 | if (diff < this.opt.round.up && this.opt.halfShow) { // Half: [x.26 .. x.75]
|
---|
| 319 | icon = this.opt.starHalf;
|
---|
| 320 | } else if (diff < this.opt.round.full) { // Full down: [x.00 .. x.5]
|
---|
| 321 | icon = this.opt.starOff;
|
---|
| 322 | }
|
---|
| 323 |
|
---|
| 324 | this.stars.eq(Math.ceil(score) - 1).attr('src', this.opt.path + icon);
|
---|
| 325 | } // Full down: [x.00 .. x.25]
|
---|
| 326 | }, score: function() {
|
---|
| 327 | return arguments.length ? methods.setScore.apply(this, arguments) : methods.getScore.call(this);
|
---|
| 328 | }, set: function(settings) {
|
---|
| 329 | this.each(function() {
|
---|
| 330 | var $this = $(this),
|
---|
| 331 | actual = $this.data('settings'),
|
---|
| 332 | clone = $this.clone().removeAttr('style').insertBefore($this);
|
---|
| 333 |
|
---|
| 334 | $this.remove();
|
---|
| 335 |
|
---|
| 336 | clone.raty($.extend(actual, settings));
|
---|
| 337 | });
|
---|
| 338 |
|
---|
| 339 | return $(this.selector);
|
---|
| 340 | }, setScore: function(score) {
|
---|
| 341 | return $(this).each(function() {
|
---|
| 342 | if ($(this).data('readonly') === true) {
|
---|
| 343 | return this;
|
---|
| 344 | }
|
---|
| 345 |
|
---|
| 346 | methods.initialize.call(this, score);
|
---|
| 347 | methods.setTarget.call(this, score, true);
|
---|
| 348 | });
|
---|
| 349 | }, setTarget: function(value, isKeep) {
|
---|
| 350 | if (this.opt.target) {
|
---|
| 351 | var $target = $(this.opt.target);
|
---|
| 352 |
|
---|
| 353 | if ($target.length == 0) {
|
---|
| 354 | methods.error.call(this, 'target selector invalid or missing!');
|
---|
| 355 | }
|
---|
| 356 |
|
---|
| 357 | var score = value;
|
---|
| 358 |
|
---|
| 359 | if (!isKeep || score === undefined) {
|
---|
| 360 | score = this.opt.targetText;
|
---|
| 361 | } else {
|
---|
| 362 | if (this.opt.targetType == 'hint') {
|
---|
| 363 | score = (score === null && this.opt.cancel)
|
---|
| 364 | ? this.opt.cancelHint
|
---|
| 365 | : this.opt.hints[Math.ceil(score - 1)];
|
---|
| 366 | } else {
|
---|
| 367 | score = this.opt.precision
|
---|
| 368 | ? parseFloat(score).toFixed(1)
|
---|
| 369 | : parseInt(score, 10);
|
---|
| 370 | }
|
---|
| 371 | }
|
---|
| 372 |
|
---|
| 373 | if (this.opt.targetFormat.indexOf('{score}') < 0) {
|
---|
| 374 | methods.error.call(this, 'template "{score}" missing!');
|
---|
| 375 | }
|
---|
| 376 |
|
---|
| 377 | if (value !== null) {
|
---|
| 378 | score = this.opt.targetFormat.toString().replace('{score}', score);
|
---|
| 379 | }
|
---|
| 380 |
|
---|
| 381 | if ($target.is(':input')) {
|
---|
| 382 | $target.val(score);
|
---|
| 383 | } else {
|
---|
| 384 | $target.html(score);
|
---|
| 385 | }
|
---|
| 386 | }
|
---|
| 387 | }, showHalf: function(score) {
|
---|
| 388 | var diff = (score - Math.floor(score)).toFixed(1);
|
---|
| 389 |
|
---|
| 390 | if (diff > 0 && diff < .6) {
|
---|
| 391 | this.stars.eq(Math.ceil(score) - 1).attr('src', this.opt.path + this.opt.starHalf);
|
---|
| 392 | }
|
---|
| 393 | }, initialize: function(score) {
|
---|
| 394 | score = !score ? 0 : methods.between(score, 0, this.opt.number);
|
---|
| 395 |
|
---|
| 396 | methods.fill.call(this, score);
|
---|
| 397 |
|
---|
| 398 | if (score > 0) {
|
---|
| 399 | if (this.opt.halfShow) {
|
---|
| 400 | methods.roundStar.call(this, score);
|
---|
| 401 | }
|
---|
| 402 |
|
---|
| 403 | this.score.val(score);
|
---|
| 404 | }
|
---|
| 405 | }, unfixHint: function() {
|
---|
| 406 | for (var i = 0; i < this.opt.number; i++) {
|
---|
| 407 | this.stars.eq(i).attr('title', (i < this.opt.hints.length && this.opt.hints[i] !== null) ? this.opt.hints[i] : i);
|
---|
| 408 | }
|
---|
| 409 |
|
---|
| 410 | $(this).data('readonly', false).css('cursor', 'pointer').removeAttr('title');
|
---|
| 411 |
|
---|
| 412 | this.score.attr('readonly', 'readonly');
|
---|
| 413 | }
|
---|
| 414 | };
|
---|
| 415 |
|
---|
| 416 | $.fn.raty = function(method) {
|
---|
| 417 | if (methods[method]) {
|
---|
| 418 | return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
---|
| 419 | } else if (typeof method === 'object' || !method) {
|
---|
| 420 | return methods.init.apply(this, arguments);
|
---|
| 421 | } else {
|
---|
| 422 | $.error('Method ' + method + ' does not exist!');
|
---|
| 423 | }
|
---|
| 424 | };
|
---|
| 425 |
|
---|
| 426 | $.fn.raty.defaults = {
|
---|
| 427 | cancel : false,
|
---|
| 428 | cancelHint : 'cancel this rating!',
|
---|
| 429 | cancelOff : 'cancel-off.png',
|
---|
| 430 | cancelOn : 'cancel-on.png',
|
---|
| 431 | cancelPlace : 'left',
|
---|
| 432 | click : undefined,
|
---|
| 433 | half : false,
|
---|
| 434 | halfShow : true,
|
---|
| 435 | hints : ['bad', 'poor', 'regular', 'good', 'gorgeous'],
|
---|
| 436 | iconRange : undefined,
|
---|
| 437 | mouseover : undefined,
|
---|
| 438 | noRatedMsg : 'not rated yet',
|
---|
| 439 | number : 5,
|
---|
| 440 | path : 'img/',
|
---|
| 441 | precision : false,
|
---|
| 442 | round : { down: .25, full: .6, up: .76 },
|
---|
| 443 | readOnly : false,
|
---|
| 444 | score : undefined,
|
---|
| 445 | scoreName : 'score',
|
---|
| 446 | single : false,
|
---|
| 447 | size : 16,
|
---|
| 448 | space : true,
|
---|
| 449 | starHalf : 'star-half.png',
|
---|
| 450 | starOff : 'star-off.png',
|
---|
| 451 | starOn : 'star-on.png',
|
---|
| 452 | target : undefined,
|
---|
| 453 | targetFormat : '{score}',
|
---|
| 454 | targetKeep : false,
|
---|
| 455 | targetText : '',
|
---|
| 456 | targetType : 'hint',
|
---|
| 457 | width : undefined
|
---|
| 458 | };
|
---|
| 459 |
|
---|
| 460 | })(jQuery);
|
---|