1/** 2 * Modified Star Rating - jQuery plugin 3 * 4 * Copyright (c) 2006 Wil Stuckey 5 * 6 * Original source available: http://sandbox.wilstuckey.com/jquery-ratings/ 7 * Extensively modified by Lullabot: http://www.lullabot.com 8 * 9 * Dual licensed under the MIT and GPL licenses: 10 * http://www.opensource.org/licenses/mit-license.php 11 * http://www.gnu.org/licenses/gpl.html 12 */ 13 14/** 15 * Create a degradeable star rating interface out of a simple form structure. 16 * Returns a modified jQuery object containing the new interface. 17 * 18 * @example jQuery('form.rating').fivestar(); 19 * @cat plugin 20 * @type jQuery 21 * 22 */ 23(function($){ // Create local scope. 24 /** 25 * Takes the form element, builds the rating interface and attaches the proper events. 26 * @param {Object} $obj 27 */ 28 var buildRating = function($obj){ 29 var $widget = buildInterface($obj), 30 $stars = $('.star', $widget), 31 $cancel = $('.cancel', $widget), 32 $summary = $('.fivestar-summary', $obj), 33 feedbackTimerId = 0, 34 summaryText = $summary.html(), 35 summaryHover = $obj.is('.fivestar-labels-hover'), 36 currentValue = $("select", $obj).val(), 37 cancelTitle = $('label', $obj).html(), 38 voteTitle = cancelTitle != Drupal.settings.fivestar.titleAverage ? cancelTitle : Drupal.settings.fivestar.titleUser, 39 voteChanged = false; 40 41 // Record star display. 42 if ($obj.is('.fivestar-user-stars')) { 43 var starDisplay = 'user'; 44 } 45 else if ($obj.is('.fivestar-average-stars')) { 46 var starDisplay = 'average'; 47 currentValue = $("input[name=vote_average]", $obj).val(); 48 } 49 else if ($obj.is('.fivestar-combo-stars')) { 50 var starDisplay = 'combo'; 51 } 52 else { 53 var starDisplay = 'none'; 54 } 55 56 // Smart is intentionally separate, so the average will be set if necessary. 57 if ($obj.is('.fivestar-smart-stars')) { 58 var starDisplay = 'smart'; 59 } 60 61 // Record text display. 62 if ($summary.size()) { 63 var textDisplay = $summary.attr('class').replace(/.*?fivestar-summary-([^ ]+).*/, '$1').replace(/-/g, '_'); 64 } 65 else { 66 var textDisplay = 'none'; 67 } 68 69 // Add hover and focus events. 70 $stars 71 .mouseover(function(){ 72 event.drain(); 73 event.fill(this); 74 }) 75 .mouseout(function(){ 76 event.drain(); 77 event.reset(); 78 }); 79 $stars.children() 80 .focus(function(){ 81 event.drain(); 82 event.fill(this.parentNode) 83 }) 84 .blur(function(){ 85 event.drain(); 86 event.reset(); 87 }).end(); 88 89 // Cancel button events. 90 $cancel 91 .mouseover(function(){ 92 event.drain(); 93 $(this).addClass('on') 94 }) 95 .mouseout(function(){ 96 event.reset(); 97 $(this).removeClass('on') 98 }); 99 $cancel.children() 100 .focus(function(){ 101 event.drain(); 102 $(this.parentNode).addClass('on') 103 }) 104 .blur(function(){ 105 event.reset(); 106 $(this.parentNode).removeClass('on') 107 }).end(); 108 109 // Click events. 110 $cancel.click(function(){ 111 currentValue = 0; 112 event.reset(); 113 voteChanged = false; 114 // Inform a user that his vote is being processed 115 if ($("input.fivestar-path", $obj).size() && $summary.is('.fivestar-feedback-enabled')) { 116 setFeedbackText(Drupal.settings.fivestar.feedbackDeletingVote); 117 } 118 // Save the currentValue in a hidden field. 119 $("select", $obj).val(0); 120 // Update the title. 121 cancelTitle = starDisplay != 'smart' ? cancelTitle : Drupal.settings.fivestar.titleAverage; 122 $('label', $obj).html(cancelTitle); 123 // Update the smart classes on the widget if needed. 124 if ($obj.is('.fivestar-smart-text')) { 125 $obj.removeClass('fivestar-user-text').addClass('fivestar-average-text'); 126 $summary[0].className = $summary[0].className.replace(/-user/, '-average'); 127 textDisplay = $summary.attr('class').replace(/.*?fivestar-summary-([^ ]+).*/, '$1').replace(/-/g, '_'); 128 } 129 if ($obj.is('.fivestar-smart-stars')) { 130 $obj.removeClass('fivestar-user-stars').addClass('fivestar-average-stars'); 131 } 132 // Submit the form if needed. 133 $("input.fivestar-path", $obj).each(function() { 134 var token = $("input.fivestar-token", $obj).val(); 135 $.ajax({ 136 type: 'GET', 137 data: { token: token }, 138 dataType: 'xml', 139 url: this.value + '/' + 0, 140 success: voteHook 141 }); 142 }); 143 return false;
144 }); 145 $stars.click(function(){ 146 currentValue = $('select option', $obj).get($stars.index(this) + $cancel.size() + 1).value; 147 // Save the currentValue to the hidden select field. 148 $("select", $obj).val(currentValue); 149 // Update the display of the stars. 150 voteChanged = true; 151 event.reset(); 152 // Inform a user that his vote is being processed. 153 if ($("input.fivestar-path", $obj).size() && $summary.is('.fivestar-feedback-enabled')) { 154 setFeedbackText(Drupal.settings.fivestar.feedbackSavingVote); 155 } 156 // Update the smart classes on the widget if needed. 157 if ($obj.is('.fivestar-smart-text')) { 158 $obj.removeClass('fivestar-average-text').addClass('fivestar-user-text'); 159 $summary[0].className = $summary[0].className.replace(/-average/, '-user'); 160 textDisplay = $summary.attr('class').replace(/.*?fivestar-summary-([^ ]+).*/, '$1').replace(/-/g, '_'); 161 } 162 if ($obj.is('.fivestar-smart-stars')) { 163 $obj.removeClass('fivestar-average-stars').addClass('fivestar-user-stars'); 164 } 165 // Submit the form if needed. 166 $("input.fivestar-path", $obj).each(function () { 167 var token = $("input.fivestar-token", $obj).val(); 168 $.ajax({ 169 type: 'GET', 170 data: { token: token }, 171 dataType: 'xml', 172 url: this.value + '/' + currentValue, 173 success: voteHook 174 }); 175 }); 176 return false; 177 }); 178 179 var event = { 180 fill: function(el){ 181 // Fill to the current mouse position. 182 var index = $stars.index(el) + 1; 183 $stars 184 .children('a').css('width', '100%').end() 185 .filter(':lt(' + index + ')').addClass('hover').end(); 186 // Update the description text and label. 187 if (summaryHover && !feedbackTimerId) { 188 var summary = $("select option", $obj)[index + $cancel.size()].text; 189 var value = $("select option", $obj)[index + $cancel.size()].value; 190 $summary.html(summary != index + 1 ? summary : ' '); 191 $('label', $obj).html(voteTitle); 192 } 193 }, 194 drain: function() { 195 // Drain all the stars. 196 $stars 197 .filter('.on').removeClass('on').end() 198 .filter('.hover').removeClass('hover').end(); 199 // Update the description text. 200 if (summaryHover && !feedbackTimerId) { 201 var cancelText = $("select option", $obj)[1].text; 202 $summary.html(($cancel.size() && cancelText != 0) ? cancelText : ' '); 203 if (!voteChanged) { 204 $('label', $obj).html(cancelTitle); 205 } 206 } 207 }, 208 reset: function(){ 209 // Reset the stars to the default index. 210 var starValue = currentValue/100 * $stars.size(); 211 var percent = (starValue - Math.floor(starValue)) * 100; 212 $stars.filter(':lt(' + Math.floor(starValue) + ')').addClass('on').end(); 213 if (percent > 0) { 214 $stars.eq(Math.floor(starValue)).addClass('on').children('a').css('width', percent + "%").end().end(); 215 } 216 // Restore the summary text and original title. 217 if (summaryHover && !feedbackTimerId) { 218 $summary.html(summaryText ? summaryText : ' '); 219 } 220 if (voteChanged) { 221 $('label', $obj).html(voteTitle); 222 } 223 else { 224 $('label', $obj).html(cancelTitle); 225 } 226 } 227 }; 228 229 var setFeedbackText = function(text) { 230 // Kill previous timer if it isn't finished yet so that the text we 231 // are about to set will not get cleared too early. 232 feedbackTimerId = 1; 233 $summary.html(text); 234 }; 235 236 /** 237 * Checks for the presence of a javascript hook 'fivestarResult' to be 238 * called upon completion of a AJAX vote request. 239 */ 240 var voteHook = function(data) { 241 var returnObj = { 242 result: { 243 count: $("result > count", data).text(), 244 average: $("result > average", data).text(), 245 summary: { 246 average: $("summary average", data).text(), 247 average_count: $("summary average_count", data).text(), 248 user: $("summary user", data).text(), 249 user_count: $("summary user_count", data).text(), 250 combo: $("summary combo", data).text(), 251 count: $("summary count", data).text() 252 } 253 }, 254 vote: { 255 id: $("vote id", data).text(), 256 tag: $("vote tag", data).text(), 257 type: $("vote type", data).text(), 258 value: $("vote value", data).text() 259 }, 260 display: { 261 stars: starDisplay, 262 text: textDisplay 263 } 264 }; 265 // Check for a custom callback. 266 if (window.fivestarResult) { 267 fivestarResult(returnObj); 268 } 269 // Use the default. 270 else { 271 fivestarDefaultResult(returnObj); 272 } 273 // Update the summary text. 274 summaryText = returnObj.result.summary[returnObj.display.text]; 275 if ($(returnObj.result.summary.average).is('.fivestar-feedback-enabled')) { 276 // Inform user that his/her vote has been processed. 277 if (returnObj.vote.value != 0) { // check if vote has been saved or deleted 278 setFeedbackText(Drupal.settings.fivestar.feedbackVoteSaved); 279 } 280 else { 281 setFeedbackText(Drupal.settings.fivestar.feedbackVoteDeleted); 282 } 283 // Setup a timer to clear the feedback text after 3 seconds. 284 feedbackTimerId = setTimeout(function() { clearTimeout(feedbackTimerId); feedbackTimerId = 0; $summary.html(returnObj.result.summary[returnObj.display.text]); }, 2000); 285 } 286 // Update the current star currentValue to the previous average. 287 if (returnObj.vote.value == 0 && (starDisplay == 'average' || starDisplay == 'smart')) { 288 currentValue = returnObj.result.average; 289 event.reset(); 290 } 291 }; 292 293 event.reset(); 294 return $widget; 295 }; 296 297 /**
298 * Accepts jQuery object containing a single fivestar widget. 299 * Returns the proper div structure for the star interface. 300 * 301 * @return jQuery 302 * @param {Object} $widget 303 * 304 */ 305 var buildInterface = function($widget){ 306 var $container = $('<div class="fivestar-widget clear-block"></div>'); 307 var $options = $("select option", $widget); 308 var size = $('option', $widget).size() - 1; 309 var cancel = 1; 310 for (var i = 1, option; option = $options[i]; i++){ 311 if (option.value == "0") { 312 cancel = 0; 313 $div = $('<div class="cancel"><a href="#0" title="' + option.text + '">' + option.text + '</a></div>'); 314 } 315 else { 316 var zebra = (i + cancel - 1) % 2 == 0 ? 'even' : 'odd'; 317 var count = i + cancel - 1; 318 var first = count == 1 ? ' star-first' : ''; 319 var last = count == size + cancel - 1 ? ' star-last' : ''; 320 $div = $('<div class="star star-' + count + ' star-' + zebra + first + last + '"><a href="#' + option.value + '" title="' + option.text + '">' + option.text + '</a></div>'); 321 } 322 $container.append($div[0]); 323 } 324 $container.addClass('fivestar-widget-' + (size + cancel - 1)); 325 // Attach the new widget and hide the existing widget. 326 $('select', $widget).after($container).css('display', 'none'); 327 return $container; 328 }; 329 330 /** 331 * Standard handler to update the average rating when a user changes their 332 * vote. This behavior can be overridden by implementing a fivestarResult 333 * function in your own module or theme. 334 * @param object voteResult 335 * Object containing the following properties from the vote result: 336 * voteResult.result.count The current number of votes for this item. 337 * voteResult.result.average The current average of all votes for this item. 338 * voteResult.result.summary.average The textual description of the average. 339 * voteResult.result.summary.user The textual description of the user's current vote. 340 * voteResult.vote.id The id of the item the vote was placed on (such as the nid) 341 * voteResult.vote.type The type of the item the vote was placed on (such as 'node') 342 * voteResult.vote.tag The multi-axis tag the vote was placed on (such as 'vote') 343 * voteResult.vote.average The average of the new vote saved 344 * voteResult.display.stars The type of star display we're using. Either 'average', 'user', or 'combo'. 345 * voteResult.display.text The type of text display we're using. Either 'average', 'user', or 'combo'. 346 */ 347 function fivestarDefaultResult(voteResult) { 348 // Update the summary text. 349 $('div.fivestar-summary-'+voteResult.vote.tag+'-'+voteResult.vote.id).html(voteResult.result.summary[voteResult.display.text]); 350 // If this is a combo display, update the average star display. 351 if (voteResult.display.stars == 'combo') { 352 $('div.fivestar-form-'+voteResult.vote.id).each(function() { 353 // Update stars. 354 var $stars = $('.fivestar-widget-static .star span', this); 355 var average = voteResult.result.average/100 * $stars.size(); 356 var index = Math.floor(average); 357 $stars.removeClass('on').addClass('off').css('width', 'auto'); 358 $stars.filter(':lt(' + (index + 1) + ')').removeClass('off').addClass('on'); 359 $stars.eq(index).css('width', ((average - index) * 100) + "%"); 360 // Update summary. 361 var $summary = $('.fivestar-static-form-item .fivestar-summary', this); 362 if ($summary.size()) { 363 var textDisplay = $summary.attr('class').replace(/.*?fivestar-summary-([^ ]+).*/, '$1').replace(/-/g, '_'); 364 $summary.html(voteResult.result.summary[textDisplay]); 365 } 366 }); 367 } 368 }; 369 370 /** 371 * Set up the plugin 372 */ 373 $.fn.fivestar = function() { 374 var stack = []; 375 this.each(function() { 376 var ret = buildRating($(this)); 377 stack.push(ret); 378 }); 379 return stack; 380 }; 381 382 // Fix ie6 background flicker problem. 383 if ($.browser.msie == true) { 384 try {
385 document.execCommand('BackgroundImageCache', false, true); 386 } catch(err) {} 387 } 388 389 Drupal.behaviors.fivestar = function(context) { 390 $('div.fivestar-form-item:not(.fivestar-processed)', context).addClass('fivestar-processed').fivestar(); 391 $('input.fivestar-submit', context).css('display', 'none'); 392 } 393 394})(jQuery);
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.