Bug 5668 - Star ratings in the opac
[koha.git] / koha-tmpl / opac-tmpl / prog / en / lib / jquery / plugins / jquery.rating.js
1 /*
2  ### jQuery Star Rating Plugin v3.14 - 2012-01-26 ###
3  * Home: http://www.fyneworks.com/jquery/star-rating/
4  * Code: http://code.google.com/p/jquery-star-rating-plugin/
5  *
6     * Dual licensed under the MIT and GPL licenses:
7  *   http://www.opensource.org/licenses/mit-license.php
8  *   http://www.gnu.org/licenses/gpl.html
9  ###
10 */
11
12 /*# AVOID COLLISIONS #*/
13 ;if(window.jQuery) (function($){
14 /*# AVOID COLLISIONS #*/
15
16     // IE6 Background Image Fix
17     if ($.browser.msie) try { document.execCommand("BackgroundImageCache", false, true)} catch(e) { };
18     // Thanks to http://www.visualjquery.com/rating/rating_redux.html
19
20     // plugin initialization
21     $.fn.rating = function(options){
22         if(this.length==0) return this; // quick fail
23
24         // Handle API methods
25         if(typeof arguments[0]=='string'){
26             // Perform API methods on individual elements
27             if(this.length>1){
28                 var args = arguments;
29                 return this.each(function(){
30                     $.fn.rating.apply($(this), args);
31     });
32             };
33             // Invoke API method handler
34             $.fn.rating[arguments[0]].apply(this, $.makeArray(arguments).slice(1) || []);
35             // Quick exit...
36             return this;
37         };
38
39         // Initialize options for this call
40         var options = $.extend(
41             {}/* new object */,
42             $.fn.rating.options/* default options */,
43             options || {} /* just-in-time options */
44         );
45
46         // Allow multiple controls with the same name by making each call unique
47         $.fn.rating.calls++;
48
49         // loop through each matched element
50         this
51          .not('.star-rating-applied')
52             .addClass('star-rating-applied')
53         .each(function(){
54
55             // Load control parameters / find context / etc
56             var control, input = $(this);
57             var eid = (this.name || 'unnamed-rating').replace(/\[|\]/g, '_').replace(/^\_+|\_+$/g,'');
58             var context = $(this.form || document.body);
59
60             // FIX: http://code.google.com/p/jquery-star-rating-plugin/issues/detail?id=23
61             var raters = context.data('rating');
62             if(!raters || raters.call!=$.fn.rating.calls) raters = { count:0, call:$.fn.rating.calls };
63             var rater = raters[eid];
64
65             // if rater is available, verify that the control still exists
66             if(rater) control = rater.data('rating');
67
68             if(rater && control)//{// save a byte!
69                 // add star to control if rater is available and the same control still exists
70                 control.count++;
71
72             //}// save a byte!
73             else{
74                 // create new control if first star or control element was removed/replaced
75
76                 // Initialize options for this rater
77                 control = $.extend(
78                     {}/* new object */,
79                     options || {} /* current call options */,
80                     ($.metadata? input.metadata(): ($.meta?input.data():null)) || {}, /* metadata options */
81                     { count:0, stars: [], inputs: [] }
82                 );
83
84                 // increment number of rating controls
85                 control.serial = raters.count++;
86
87                 // create rating element
88                 rater = $('<span class="star-rating-control"/>');
89                 input.before(rater);
90
91                 // Mark element for initialization (once all stars are ready)
92                 rater.addClass('rating-to-be-drawn');
93
94                 // Accept readOnly setting from 'disabled' property
95                 if(input.attr('disabled') || input.hasClass('disabled')) control.readOnly = true;
96
97                 // Accept required setting from class property (class='required')
98                 if(input.hasClass('required')) control.required = true;
99
100                 // Create 'cancel' button
101                 rater.append(
102                     control.cancel = $('<div class="rating-cancel"><a title="' + control.cancel + '">' + control.cancelValue + '</a></div>')
103                     .mouseover(function(){
104                         $(this).rating('drain');
105                         $(this).addClass('star-rating-hover');
106                         //$(this).rating('focus');
107                     })
108                     .mouseout(function(){
109                         $(this).rating('draw');
110                         $(this).removeClass('star-rating-hover');
111                         //$(this).rating('blur');
112                     })
113                     .click(function(){
114                      $(this).rating('select');
115                     })
116                     .data('rating', control)
117                 );
118
119             }; // first element of group
120
121             // insert rating star
122             var star = $('<div class="star-rating rater-'+ control.serial +'"><a title="' + (this.title || this.value) + '">' + this.value + '</a></div>');
123             rater.append(star);
124
125             // inherit attributes from input element
126             if(this.id) star.attr('id', this.id);
127             if(this.className) star.addClass(this.className);
128
129             // Half-stars?
130             if(control.half) control.split = 2;
131
132             // Prepare division control
133             if(typeof control.split=='number' && control.split>0){
134                 var stw = ($.fn.width ? star.width() : 0) || control.starWidth;
135                 var spi = (control.count % control.split), spw = Math.floor(stw/control.split);
136                 star
137                 // restrict star's width and hide overflow (already in CSS)
138                 .width(spw)
139                 // move the star left by using a negative margin
140                 // this is work-around to IE's stupid box model (position:relative doesn't work)
141                 .find('a').css({ 'margin-left':'-'+ (spi*spw) +'px' })
142             };
143
144             // readOnly?
145             if(control.readOnly)//{ //save a byte!
146                 // Mark star as readOnly so user can customize display
147                 star.addClass('star-rating-readonly');
148             //}  //save a byte!
149             else//{ //save a byte!
150              // Enable hover css effects
151                 star.addClass('star-rating-live')
152                  // Attach mouse events
153                     .mouseover(function(){
154                         $(this).rating('fill');
155                         $(this).rating('focus');
156                     })
157                     .mouseout(function(){
158                         $(this).rating('draw');
159                         $(this).rating('blur');
160                     })
161                     .click(function(){
162                         $(this).rating('select');
163                     })
164                 ;
165             //}; //save a byte!
166
167             // set current selection
168             if(this.checked)    control.current = star;
169
170             // set current select for links
171             if(this.nodeName=="A"){
172     if($(this).hasClass('selected'))
173      control.current = star;
174    };
175
176             // hide input element
177             input.hide();
178
179             // backward compatibility, form element to plugin
180             input.change(function(){
181     $(this).rating('select');
182    });
183
184             // attach reference to star to input element and vice-versa
185             star.data('rating.input', input.data('rating.star', star));
186
187             // store control information in form (or body when form not available)
188             control.stars[control.stars.length] = star[0];
189             control.inputs[control.inputs.length] = input[0];
190             control.rater = raters[eid] = rater;
191             control.context = context;
192
193             input.data('rating', control);
194             rater.data('rating', control);
195             star.data('rating', control);
196             context.data('rating', raters);
197   }); // each element
198
199         // Initialize ratings (first draw)
200         $('.rating-to-be-drawn').rating('draw').removeClass('rating-to-be-drawn');
201
202         return this; // don't break the chain...
203     };
204
205     /*--------------------------------------------------------*/
206
207     /*
208         ### Core functionality and API ###
209     */
210     $.extend($.fn.rating, {
211         // Used to append a unique serial number to internal control ID
212         // each time the plugin is invoked so same name controls can co-exist
213         calls: 0,
214
215         focus: function(){
216             var control = this.data('rating'); if(!control) return this;
217             if(!control.focus) return this; // quick fail if not required
218             // find data for event
219             var input = $(this).data('rating.input') || $( this.tagName=='INPUT' ? this : null );
220    // focus handler, as requested by focusdigital.co.uk
221             if(control.focus) control.focus.apply(input[0], [input.val(), $('a', input.data('rating.star'))[0]]);
222         }, // $.fn.rating.focus
223
224         blur: function(){
225             var control = this.data('rating'); if(!control) return this;
226             if(!control.blur) return this; // quick fail if not required
227             // find data for event
228             var input = $(this).data('rating.input') || $( this.tagName=='INPUT' ? this : null );
229    // blur handler, as requested by focusdigital.co.uk
230             if(control.blur) control.blur.apply(input[0], [input.val(), $('a', input.data('rating.star'))[0]]);
231         }, // $.fn.rating.blur
232
233         fill: function(){ // fill to the current mouse position.
234             var control = this.data('rating'); if(!control) return this;
235             // do not execute when control is in read-only mode
236             if(control.readOnly) return;
237             // Reset all stars and highlight them up to this element
238             this.rating('drain');
239             this.prevAll().andSelf().filter('.rater-'+ control.serial).addClass('star-rating-hover');
240         },// $.fn.rating.fill
241
242         drain: function() { // drain all the stars.
243             var control = this.data('rating'); if(!control) return this;
244             // do not execute when control is in read-only mode
245             if(control.readOnly) return;
246             // Reset all stars
247             control.rater.children().filter('.rater-'+ control.serial).removeClass('star-rating-on').removeClass('star-rating-hover');
248         },// $.fn.rating.drain
249
250         draw: function(){ // set value and stars to reflect current selection
251             var control = this.data('rating'); if(!control) return this;
252             // Clear all stars
253             this.rating('drain');
254             // Set control value
255             if(control.current){
256                 control.current.data('rating.input').attr('checked','checked');
257                 control.current.prevAll().andSelf().filter('.rater-'+ control.serial).addClass('star-rating-on');
258             }
259             else
260              $(control.inputs).removeAttr('checked');
261             // Show/hide 'cancel' button
262             control.cancel[control.readOnly || control.required?'hide':'show']();
263             // Add/remove read-only classes to remove hand pointer
264             this.siblings()[control.readOnly?'addClass':'removeClass']('star-rating-readonly');
265         },// $.fn.rating.draw
266
267
268
269
270
271         select: function(value,wantCallBack){ // select a value
272
273                     // ***** MODIFICATION *****
274                     // Thanks to faivre.thomas - http://code.google.com/p/jquery-star-rating-plugin/issues/detail?id=27
275                     //
276                     // ***** LIST OF MODIFICATION *****
277                     // ***** added Parameter wantCallBack : false if you don't want a callback. true or undefined if you want postback to be performed at the end of this method'
278                     // ***** recursive calls to this method were like : ... .rating('select') it's now like .rating('select',undefined,wantCallBack); (parameters are set.)
279                     // ***** line which is calling callback
280                     // ***** /LIST OF MODIFICATION *****
281
282             var control = this.data('rating'); if(!control) return this;
283             // do not execute when control is in read-only mode
284             if(control.readOnly) return;
285             // clear selection
286             control.current = null;
287             // programmatically (based on user input)
288             if(typeof value!='undefined'){
289              // select by index (0 based)
290                 if(typeof value=='number')
291                          return $(control.stars[value]).rating('select',undefined,wantCallBack);
292                 // select by literal value (must be passed as a string
293                 if(typeof value=='string')
294                     //return
295                     $.each(control.stars, function(){
296                         if($(this).data('rating.input').val()==value) $(this).rating('select',undefined,wantCallBack);
297                     });
298             }
299             else
300                 control.current = this[0].tagName=='INPUT' ?
301                  this.data('rating.star') :
302                     (this.is('.rater-'+ control.serial) ? this : null);
303
304             // Update rating control state
305             this.data('rating', control);
306             // Update display
307             this.rating('draw');
308             // find data for event
309             var input = $( control.current ? control.current.data('rating.input') : null );
310             // click callback, as requested here: http://plugins.jquery.com/node/1655
311
312                     // **** MODIFICATION *****
313                     // Thanks to faivre.thomas - http://code.google.com/p/jquery-star-rating-plugin/issues/detail?id=27
314                     //
315                     //old line doing the callback :
316                     //if(control.callback) control.callback.apply(input[0], [input.val(), $('a', control.current)[0]]);// callback event
317                     //
318                     //new line doing the callback (if i want :)
319                     if((wantCallBack ||wantCallBack == undefined) && control.callback) control.callback.apply(input[0], [input.val(), $('a', control.current)[0]]);// callback event
320                     //to ensure retro-compatibility, wantCallBack must be considered as true by default
321                     // **** /MODIFICATION *****
322
323   },// $.fn.rating.select
324
325
326
327
328
329         readOnly: function(toggle, disable){ // make the control read-only (still submits value)
330             var control = this.data('rating'); if(!control) return this;
331             // setread-only status
332             control.readOnly = toggle || toggle==undefined ? true : false;
333             // enable/disable control value submission
334             if(disable) $(control.inputs).attr("disabled", "disabled");
335             else                        $(control.inputs).removeAttr("disabled");
336             // Update rating control state
337             this.data('rating', control);
338             // Update display
339             this.rating('draw');
340         },// $.fn.rating.readOnly
341
342         disable: function(){ // make read-only and never submit value
343             this.rating('readOnly', true, true);
344         },// $.fn.rating.disable
345
346         enable: function(){ // make read/write and submit value
347             this.rating('readOnly', false, false);
348         }// $.fn.rating.select
349
350  });
351
352     /*--------------------------------------------------------*/
353
354     /*
355         ### Default Settings ###
356         eg.: You can override default control like this:
357         $.fn.rating.options.cancel = 'Clear';
358     */
359     $.fn.rating.options = { //$.extend($.fn.rating, { options: {
360             cancel: 'Cancel Rating',   // advisory title for the 'cancel' link
361             cancelValue: '',           // value to submit when user click the 'cancel' link
362             split: 0,                  // split the star into how many parts?
363
364             // Width of star image in case the plugin can't work it out. This can happen if
365             // the jQuery.dimensions plugin is not available OR the image is hidden at installation
366             starWidth: 16//,
367
368             //NB.: These don't need to be pre-defined (can be undefined/null) so let's save some code!
369             //half:     false,         // just a shortcut to control.split = 2
370             //required: false,         // disables the 'cancel' button so user can only select one of the specified values
371             //readOnly: false,         // disable rating plugin interaction/ values cannot be changed
372             //focus:    function(){},  // executed when stars are focused
373             //blur:     function(){},  // executed when stars are focused
374             //callback: function(){},  // executed when a star is clicked
375  }; //} });
376
377     /*--------------------------------------------------------*/
378
379     /*
380         ### Default implementation ###
381         The plugin will attach itself to file inputs
382         with the class 'multi' when the page loads
383     */
384     $(function(){
385      $('input[type=radio].star').rating();
386     });
387
388
389
390 /*# AVOID COLLISIONS #*/
391 })(jQuery);
392 /*# AVOID COLLISIONS #*/