jquery.rating.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. /*# AVOID COLLISIONS #*/
  2. ;if(window.jQuery) (function($){
  3. /*# AVOID COLLISIONS #*/
  4. // IE6 Background Image Fix
  5. if ($.browser.msie) try { document.execCommand("BackgroundImageCache", false, true)} catch(e) { };
  6. // Thanks to http://www.visualjquery.com/rating/rating_redux.html
  7. // plugin initialization
  8. $.fn.rating = function(options){
  9. if(this.length==0) return this; // quick fail
  10. // Handle API methods
  11. if(typeof arguments[0]=='string'){
  12. // Perform API methods on individual elements
  13. if(this.length>1){
  14. var args = arguments;
  15. return this.each(function(){
  16. $.fn.rating.apply($(this), args);
  17. });
  18. };
  19. // Invoke API method handler
  20. $.fn.rating[arguments[0]].apply(this, $.makeArray(arguments).slice(1) || []);
  21. // Quick exit...
  22. return this;
  23. };
  24. // Initialize options for this call
  25. var options = $.extend(
  26. {}/* new object */,
  27. $.fn.rating.options/* default options */,
  28. options || {} /* just-in-time options */
  29. );
  30. // Allow multiple controls with the same name by making each call unique
  31. $.fn.rating.calls++;
  32. // loop through each matched element
  33. this
  34. .not('.star-rating-applied')
  35. .addClass('star-rating-applied')
  36. .each(function(){
  37. // Load control parameters / find context / etc
  38. var control, input = $(this);
  39. var eid = (this.name || 'unnamed-rating').replace(/\[|\]/g, '_').replace(/^\_+|\_+$/g,'');
  40. var context = $(this.form || document.body);
  41. // FIX: http://code.google.com/p/jquery-star-rating-plugin/issues/detail?id=23
  42. var raters = context.data('rating');
  43. if(!raters || raters.call!=$.fn.rating.calls) raters = { count:0, call:$.fn.rating.calls };
  44. var rater = raters[eid];
  45. // if rater is available, verify that the control still exists
  46. if(rater) control = rater.data('rating');
  47. if(rater && control)//{// save a byte!
  48. // add star to control if rater is available and the same control still exists
  49. control.count++;
  50. //}// save a byte!
  51. else{
  52. // create new control if first star or control element was removed/replaced
  53. // Initialize options for this rater
  54. control = $.extend(
  55. {}/* new object */,
  56. options || {} /* current call options */,
  57. ($.metadata? input.metadata(): ($.meta?input.data():null)) || {}, /* metadata options */
  58. { count:0, stars: [], inputs: [] }
  59. );
  60. // increment number of rating controls
  61. control.serial = raters.count++;
  62. // create rating element
  63. rater = $('<span class="star-rating-control"/>');
  64. input.before(rater);
  65. // Mark element for initialization (once all stars are ready)
  66. rater.addClass('rating-to-be-drawn');
  67. // Accept readOnly setting from 'disabled' property
  68. if(input.attr('disabled') || input.hasClass('disabled')) control.readOnly = true;
  69. // Accept required setting from class property (class='required')
  70. if(input.hasClass('required')) control.required = true;
  71. // Create 'cancel' button
  72. rater.append(
  73. control.cancel = $('<div class="rating-cancel"><a title="' + control.cancel + '">' + control.cancelValue + '</a></div>')
  74. .mouseover(function(){
  75. $(this).rating('drain');
  76. $(this).addClass('star-rating-hover');
  77. //$(this).rating('focus');
  78. })
  79. .mouseout(function(){
  80. $(this).rating('draw');
  81. $(this).removeClass('star-rating-hover');
  82. //$(this).rating('blur');
  83. })
  84. .click(function(){
  85. $(this).rating('select');
  86. })
  87. .data('rating', control)
  88. );
  89. }; // first element of group
  90. // insert rating star
  91. var star = $('<div class="star-rating rater-'+ control.serial +'"><a title="' + (this.title || this.value) + '">' + this.value + '</a></div>');
  92. rater.append(star);
  93. // inherit attributes from input element
  94. if(this.id) star.attr('id', this.id);
  95. if(this.className) star.addClass(this.className);
  96. // Half-stars?
  97. if(control.half) control.split = 2;
  98. // Prepare division control
  99. if(typeof control.split=='number' && control.split>0){
  100. var stw = ($.fn.width ? star.width() : 0) || control.starWidth;
  101. var spi = (control.count % control.split), spw = Math.floor(stw/control.split);
  102. star
  103. // restrict star's width and hide overflow (already in CSS)
  104. .width(spw)
  105. // move the star left by using a negative margin
  106. // this is work-around to IE's stupid box model (position:relative doesn't work)
  107. .find('a').css({ 'margin-left':'-'+ (spi*spw) +'px' })
  108. };
  109. // readOnly?
  110. if(control.readOnly)//{ //save a byte!
  111. // Mark star as readOnly so user can customize display
  112. star.addClass('star-rating-readonly');
  113. //} //save a byte!
  114. else//{ //save a byte!
  115. // Enable hover css effects
  116. star.addClass('star-rating-live')
  117. // Attach mouse events
  118. .mouseover(function(){
  119. $(this).rating('fill');
  120. $(this).rating('focus');
  121. })
  122. .mouseout(function(){
  123. $(this).rating('draw');
  124. $(this).rating('blur');
  125. })
  126. .click(function(){
  127. $(this).rating('select');
  128. })
  129. ;
  130. //}; //save a byte!
  131. // set current selection
  132. if(this.checked) control.current = star;
  133. // set current select for links
  134. if(this.nodeName=="A"){
  135. if($(this).hasClass('selected'))
  136. control.current = star;
  137. };
  138. // hide input element
  139. input.hide();
  140. // backward compatibility, form element to plugin
  141. input.change(function(){
  142. $(this).rating('select');
  143. });
  144. // attach reference to star to input element and vice-versa
  145. star.data('rating.input', input.data('rating.star', star));
  146. // store control information in form (or body when form not available)
  147. control.stars[control.stars.length] = star[0];
  148. control.inputs[control.inputs.length] = input[0];
  149. control.rater = raters[eid] = rater;
  150. control.context = context;
  151. input.data('rating', control);
  152. rater.data('rating', control);
  153. star.data('rating', control);
  154. context.data('rating', raters);
  155. }); // each element
  156. // Initialize ratings (first draw)
  157. $('.rating-to-be-drawn').rating('draw').removeClass('rating-to-be-drawn');
  158. return this; // don't break the chain...
  159. };
  160. /*--------------------------------------------------------*/
  161. /*
  162. ### Core functionality and API ###
  163. */
  164. $.extend($.fn.rating, {
  165. // Used to append a unique serial number to internal control ID
  166. // each time the plugin is invoked so same name controls can co-exist
  167. calls: 0,
  168. focus: function(){
  169. var control = this.data('rating'); if(!control) return this;
  170. if(!control.focus) return this; // quick fail if not required
  171. // find data for event
  172. var input = $(this).data('rating.input') || $( this.tagName=='INPUT' ? this : null );
  173. // focus handler, as requested by focusdigital.co.uk
  174. if(control.focus) control.focus.apply(input[0], [input.val(), $('a', input.data('rating.star'))[0]]);
  175. }, // $.fn.rating.focus
  176. blur: function(){
  177. var control = this.data('rating'); if(!control) return this;
  178. if(!control.blur) return this; // quick fail if not required
  179. // find data for event
  180. var input = $(this).data('rating.input') || $( this.tagName=='INPUT' ? this : null );
  181. // blur handler, as requested by focusdigital.co.uk
  182. if(control.blur) control.blur.apply(input[0], [input.val(), $('a', input.data('rating.star'))[0]]);
  183. }, // $.fn.rating.blur
  184. fill: function(){ // fill to the current mouse position.
  185. var control = this.data('rating'); if(!control) return this;
  186. // do not execute when control is in read-only mode
  187. if(control.readOnly) return;
  188. // Reset all stars and highlight them up to this element
  189. this.rating('drain');
  190. this.prevAll().andSelf().filter('.rater-'+ control.serial).addClass('star-rating-hover');
  191. },// $.fn.rating.fill
  192. drain: function() { // drain all the stars.
  193. var control = this.data('rating'); if(!control) return this;
  194. // do not execute when control is in read-only mode
  195. if(control.readOnly) return;
  196. // Reset all stars
  197. control.rater.children().filter('.rater-'+ control.serial).removeClass('star-rating-on').removeClass('star-rating-hover');
  198. },// $.fn.rating.drain
  199. draw: function(){ // set value and stars to reflect current selection
  200. var control = this.data('rating'); if(!control) return this;
  201. // Clear all stars
  202. this.rating('drain');
  203. // Set control value
  204. if(control.current){
  205. control.current.data('rating.input').attr('checked','checked');
  206. control.current.prevAll().andSelf().filter('.rater-'+ control.serial).addClass('star-rating-on');
  207. }
  208. else
  209. $(control.inputs).removeAttr('checked');
  210. // Show/hide 'cancel' button
  211. control.cancel[control.readOnly || control.required?'hide':'hide']();
  212. // Add/remove read-only classes to remove hand pointer
  213. this.siblings()[control.readOnly?'addClass':'removeClass']('star-rating-readonly');
  214. },// $.fn.rating.draw
  215. select: function(value,wantCallBack){ // select a value
  216. var control = this.data('rating'); if(!control) return this;
  217. // do not execute when control is in read-only mode
  218. if(control.readOnly) return;
  219. // clear selection
  220. control.current = null;
  221. // programmatically (based on user input)
  222. if(typeof value!='undefined'){
  223. // select by index (0 based)
  224. if(typeof value=='number')
  225. return $(control.stars[value]).rating('select',undefined,wantCallBack);
  226. // select by literal value (must be passed as a string
  227. if(typeof value=='string')
  228. //return
  229. $.each(control.stars, function(){
  230. if($(this).data('rating.input').val()==value) $(this).rating('select',undefined,wantCallBack);
  231. });
  232. }
  233. else
  234. control.current = this[0].tagName=='INPUT' ?
  235. this.data('rating.star') :
  236. (this.is('.rater-'+ control.serial) ? this : null);
  237. // Update rating control state
  238. this.data('rating', control);
  239. // Update display
  240. this.rating('draw');
  241. // find data for event
  242. var input = $( control.current ? control.current.data('rating.input') : null );
  243. // click callback, as requested here: http://plugins.jquery.com/node/1655
  244. if((wantCallBack ||wantCallBack == undefined) && control.callback) control.callback.apply(input[0], [input.val(), $('a', control.current)[0]]);// callback event
  245. //to ensure retro-compatibility, wantCallBack must be considered as true by default
  246. },// $.fn.rating.select
  247. readOnly: function(toggle, disable){ // make the control read-only (still submits value)
  248. var control = this.data('rating'); if(!control) return this;
  249. // setread-only status
  250. control.readOnly = toggle || toggle==undefined ? true : false;
  251. // enable/disable control value submission
  252. if(disable) $(control.inputs).attr("disabled", "disabled");
  253. else $(control.inputs).removeAttr("disabled");
  254. // Update rating control state
  255. this.data('rating', control);
  256. // Update display
  257. this.rating('draw');
  258. },// $.fn.rating.readOnly
  259. disable: function(){ // make read-only and never submit value
  260. this.rating('readOnly', true, true);
  261. },// $.fn.rating.disable
  262. enable: function(){ // make read/write and submit value
  263. this.rating('readOnly', false, false);
  264. }// $.fn.rating.select
  265. });
  266. /*--------------------------------------------------------*/
  267. $.fn.rating.options = { //$.extend($.fn.rating, { options: {
  268. cancel: 'Cancel Rating', // advisory title for the 'cancel' link
  269. cancelValue: '', // value to submit when user click the 'cancel' link
  270. split: 0, // split the star into how many parts?
  271. // Width of star image in case the plugin can't work it out. This can happen if
  272. // the jQuery.dimensions plugin is not available OR the image is hidden at installation
  273. starWidth: 16//,
  274. //NB.: These don't need to be pre-defined (can be undefined/null) so let's save some code!
  275. //half: false, // just a shortcut to control.split = 2
  276. //required: false, // disables the 'cancel' button so user can only select one of the specified values
  277. //readOnly: false, // disable rating plugin interaction/ values cannot be changed
  278. //focus: function(){}, // executed when stars are focused
  279. //blur: function(){}, // executed when stars are focused
  280. //callback: function(){}, // executed when a star is clicked
  281. }; //} });
  282. /*--------------------------------------------------------*/
  283. $(function(){
  284. });
  285. /*# AVOID COLLISIONS #*/
  286. })(jQuery);
  287. /*# AVOID COLLISIONS #*/