Subversion Repositories Integrator Subversion

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 espaco 1
/*! RateIt | v1.0.18 / 12/22/2013 | https://rateit.codeplex.com/license
2
    http://rateit.codeplex.com | Twitter: @gjunge
3
*/
4
(function ($) {
5
    $.rateit = {
6
        aria : {
7
            resetLabel: 'reset rating',
8
            ratingLabel: 'rating'
9
        }
10
    }
11
 
12
    $.fn.rateit = function (p1, p2) {
13
        //quick way out.
14
        var index = 1;
15
        var options = {}; var mode = 'init';
16
        var capitaliseFirstLetter = function (string) {
17
            return string.charAt(0).toUpperCase() + string.substr(1);
18
        };
19
 
20
        if (this.length == 0) return this;
21
 
22
 
23
        var tp1 = $.type(p1);
24
        if (tp1 == 'object' || p1 === undefined || p1 == null) {
25
            options = $.extend({}, $.fn.rateit.defaults, p1); //wants to init new rateit plugin(s).
26
        }
27
        else if (tp1 == 'string' && p1 !== 'reset' && p2 === undefined) {
28
            return this.data('rateit' + capitaliseFirstLetter(p1)); //wants to get a value.
29
        }
30
        else if (tp1 == 'string') {
31
            mode = 'setvalue'
32
        }
33
 
34
        return this.each(function () {
35
            var item = $(this);
36
 
37
 
38
            //shorten all the item.data('rateit-XXX'), will save space in closure compiler, will be like item.data('XXX') will become x('XXX')
39
            var itemdata = function (key, value) {
40
 
41
                if (value != null) {
42
                    //update aria values
43
                    var ariakey = 'aria-value' + ((key == 'value') ? 'now' : key);
44
                    var range = item.find('.rateit-range');
45
                    if (range.attr(ariakey) != undefined) {
46
                        range.attr(ariakey, value);
47
                    }
48
 
49
                }
50
 
51
                arguments[0] = 'rateit' + capitaliseFirstLetter(key);
52
                return item.data.apply(item, arguments); ////Fix for WI: 523
53
            };
54
 
55
            //handle programmatic reset
56
            if (p1 == 'reset') {
57
              var setup = itemdata('init'); //get initial value
58
              for (var prop in setup) {
59
                item.data(prop, setup[prop]);
60
              }
61
 
62
              if (itemdata('backingfld')) { //reset also backingfield
63
                var fld = $(itemdata('backingfld'));
64
                fld.val(itemdata('value'));
65
                if (fld[0].min) fld[0].min = itemdata('min');
66
                if (fld[0].max) fld[0].max = itemdata('max');
67
                if (fld[0].step) fld[0].step = itemdata('step');
68
              }
69
              item.trigger('reset');
70
            }
71
 
72
            //add the rate it class.
73
            if (!item.hasClass('rateit')) item.addClass('rateit');
74
 
75
            var ltr = item.css('direction') != 'rtl';
76
 
77
            // set value mode
78
            if (mode == 'setvalue') {
79
                if (!itemdata('init')) throw 'Can\'t set value before init';
80
 
81
 
82
                //if readonly now and it wasn't readonly, remove the eventhandlers.
83
                if (p1 == 'readonly' && p2 == true && !itemdata('readonly')) {
84
                    item.find('.rateit-range').unbind();
85
                    itemdata('wired', false);
86
                }
87
                //when we receive a null value, reset the score to its min value.
88
                if (p1 == 'value')
89
                    p2 = (p2 == null) ? itemdata('min') : Math.max(itemdata('min'), Math.min(itemdata('max'), p2));
90
                if (itemdata('backingfld')) {
91
                    //if we have a backing field, check which fields we should update. 
92
                    //In case of input[type=range], although we did read its attributes even in browsers that don't support it (using fld.attr())
93
                    //we only update it in browser that support it (&& fld[0].min only works in supporting browsers), not only does it save us from checking if it is range input type, it also is unnecessary.
94
                    var fld = $(itemdata('backingfld'));
95
                    if (p1 == 'value') fld.val(p2);
96
                    if (p1 == 'min' && fld[0].min) fld[0].min = p2;
97
                    if (p1 == 'max' && fld[0].max) fld[0].max = p2;
98
                    if (p1 == 'step' && fld[0].step) fld[0].step = p2;
99
                }
100
 
101
                itemdata(p1, p2);
102
            }
103
 
104
            //init rateit plugin
105
            if (!itemdata('init')) {
106
 
107
                //get our values, either from the data-* html5 attribute or from the options.
108
                itemdata('min', isNaN(itemdata('min')) ? options.min : itemdata('min'));
109
                itemdata('max', isNaN(itemdata('max')) ? options.max : itemdata('max'));
110
                itemdata('step', itemdata('step') || options.step);
111
                itemdata('readonly', itemdata('readonly') !== undefined ? itemdata('readonly') : options.readonly);
112
                itemdata('resetable', itemdata('resetable') !== undefined ? itemdata('resetable') : options.resetable);
113
                itemdata('backingfld', itemdata('backingfld') || options.backingfld);
114
                itemdata('starwidth', itemdata('starwidth') || options.starwidth);
115
                itemdata('starheight', itemdata('starheight') || options.starheight);
116
                itemdata('value', Math.max(itemdata('min'), Math.min(itemdata('max'), (!isNaN(itemdata('value')) ? itemdata('value') : (!isNaN(options.value) ? options.value : options.min) ))));
117
                itemdata('ispreset', itemdata('ispreset') !== undefined ? itemdata('ispreset') : options.ispreset);
118
                //are we LTR or RTL?
119
 
120
                if (itemdata('backingfld')) {
121
                    //if we have a backing field, hide it, and get its value, and override defaults if range.
122
                    var fld = $(itemdata('backingfld'));
123
                    itemdata('value', fld.hide().val());
124
 
125
                    if (fld.attr('disabled') || fld.attr('readonly'))
126
                        itemdata('readonly', true); //http://rateit.codeplex.com/discussions/362055 , if a backing field is disabled or readonly at instantiation, make rateit readonly.
127
 
128
 
129
                    if (fld[0].nodeName == 'INPUT') {
130
                        if (fld[0].type == 'range' || fld[0].type == 'text') { //in browsers not support the range type, it defaults to text
131
 
132
                            itemdata('min', parseInt(fld.attr('min')) || itemdata('min')); //if we would have done fld[0].min it wouldn't have worked in browsers not supporting the range type.
133
                            itemdata('max', parseInt(fld.attr('max')) || itemdata('max'));
134
                            itemdata('step', parseInt(fld.attr('step')) || itemdata('step'));
135
                        }
136
                    }
137
                    if (fld[0].nodeName == 'SELECT' && fld[0].options.length > 1) {
138
                        itemdata('min', Number(fld[0].options[0].value));
139
                        itemdata('max', Number(fld[0].options[fld[0].length - 1].value));
140
                        itemdata('step', Number(fld[0].options[1].value) - Number(fld[0].options[0].value));
141
                    }
142
                }
143
 
144
                //Create the necessary tags. For ARIA purposes we need to give the items an ID. So we use an internal index to create unique ids
145
                var element = item[0].nodeName == 'DIV' ? 'div' : 'span';
146
                index++;
147
                var html = '<button id="rateit-reset-{{index}}" data-role="none" class="rateit-reset" aria-label="' + $.rateit.aria.resetLabel + '" aria-controls="rateit-range-{{index}}"></button><{{element}} id="rateit-range-{{index}}" class="rateit-range" tabindex="0" role="slider" aria-label="' + $.rateit.aria.ratingLabel + '" aria-owns="rateit-reset-{{index}}" aria-valuemin="' + itemdata('min') + '" aria-valuemax="' + itemdata('max') + '" aria-valuenow="' + itemdata('value') + '"><{{element}} class="rateit-selected" style="height:' + itemdata('starheight') + 'px"></{{element}}><{{element}} class="rateit-hover" style="height:' + itemdata('starheight') + 'px"></{{element}}></{{element}}>';
148
                item.append(html.replace(/{{index}}/gi, index).replace(/{{element}}/gi, element));
149
 
150
                //if we are in RTL mode, we have to change the float of the "reset button"
151
                if (!ltr) {
152
                    item.find('.rateit-reset').css('float', 'right');
153
                    item.find('.rateit-selected').addClass('rateit-selected-rtl');
154
                    item.find('.rateit-hover').addClass('rateit-hover-rtl');
155
                }
156
 
157
                itemdata('init', JSON.parse(JSON.stringify(item.data()))); //cheap way to create a clone
158
            }
159
            //resize the height of all elements, 
160
            item.find('.rateit-selected, .rateit-hover').height(itemdata('starheight'));
161
 
162
            //set the range element to fit all the stars.
163
            var range = item.find('.rateit-range');
164
            range.width(itemdata('starwidth') * (itemdata('max') - itemdata('min'))).height(itemdata('starheight'));
165
 
166
 
167
            //add/remove the preset class
168
            var presetclass = 'rateit-preset' + ((ltr) ? '' : '-rtl');
169
            if (itemdata('ispreset'))
170
                item.find('.rateit-selected').addClass(presetclass);
171
            else
172
                item.find('.rateit-selected').removeClass(presetclass);
173
 
174
            //set the value if we have it.
175
            if (itemdata('value') != null) {
176
                var score = (itemdata('value') - itemdata('min')) * itemdata('starwidth');
177
                item.find('.rateit-selected').width(score);
178
            }
179
 
180
            //setup the reset button
181
            var resetbtn = item.find('.rateit-reset');
182
            if (resetbtn.data('wired') !== true) {
183
                resetbtn.bind('click', function (e) {
184
                    e.preventDefault();
185
                    resetbtn.blur();
186
                    item.rateit('value', null);
187
                    item.trigger('reset');
188
                }).data('wired', true);
189
 
190
            }
191
 
192
            //this function calculates the score based on the current position of the mouse.
193
            var calcRawScore = function (element, event) {
194
                var pageX = (event.changedTouches) ? event.changedTouches[0].pageX : event.pageX;
195
 
196
                var offsetx = pageX - $(element).offset().left;
197
                if (!ltr) offsetx = range.width() - offsetx;
198
                if (offsetx > range.width()) offsetx = range.width();
199
                if (offsetx < 0) offsetx = 0;
200
 
201
                return score = Math.ceil(offsetx / itemdata('starwidth') * (1 / itemdata('step')));
202
            };
203
 
204
            //sets the hover element based on the score.
205
            var setHover = function (score) {
206
                var w = score * itemdata('starwidth') * itemdata('step');
207
                var h = range.find('.rateit-hover');
208
                if (h.data('width') != w) {
209
                    range.find('.rateit-selected').hide();
210
                    h.width(w).show().data('width', w);
211
                    var data = [(score * itemdata('step')) + itemdata('min')];
212
                    item.trigger('hover', data).trigger('over', data);
213
                }
214
            };
215
 
216
            var setSelection = function (value) {
217
                itemdata('value', value);
218
                if (itemdata('backingfld')) {
219
                    $(itemdata('backingfld')).val(value);
220
                }
221
                if (itemdata('ispreset')) { //if it was a preset value, unset that.
222
                    range.find('.rateit-selected').removeClass(presetclass);
223
                    itemdata('ispreset', false);
224
                }
225
                range.find('.rateit-hover').hide();
226
                range.find('.rateit-selected').width(value * itemdata('starwidth') - (itemdata('min') * itemdata('starwidth'))).show();
227
                item.trigger('hover', [null]).trigger('over', [null]).trigger('rated', [value]);
228
            };
229
 
230
            if (!itemdata('readonly')) {
231
                //if we are not read only, add all the events
232
 
233
                //if we have a reset button, set the event handler.
234
                if (!itemdata('resetable'))
235
                    resetbtn.hide();
236
 
237
                //when the mouse goes over the range element, we set the "hover" stars.
238
                if (!itemdata('wired')) {
239
                    range.bind('touchmove touchend', touchHandler); //bind touch events
240
                    range.mousemove(function (e) {
241
                        var score = calcRawScore(this, e);
242
                        setHover(score);
243
                    });
244
                    //when the mouse leaves the range, we have to hide the hover stars, and show the current value.
245
                    range.mouseleave(function (e) {
246
                        range.find('.rateit-hover').hide().width(0).data('width', '');
247
                        item.trigger('hover', [null]).trigger('over', [null]);
248
                        range.find('.rateit-selected').show();
249
                    });
250
                    //when we click on the range, we have to set the value, hide the hover.
251
                    range.mouseup(function (e) {
252
                        var score = calcRawScore(this, e);
253
                        var value = (score * itemdata('step')) + itemdata('min');
254
                        setSelection(value);
255
                        range.blur();
256
                    });
257
 
258
                    //support key nav
259
                    range.keyup( function (e) {
260
                        if (e.which == 38 || e.which == (ltr ? 39 : 37)) {
261
                            setSelection(Math.min(itemdata('value') + itemdata('step'), itemdata('max')));
262
                        }
263
                        if (e.which == 40 || e.which == (ltr ? 37 : 39)) {
264
                            setSelection(Math.max(itemdata('value') - itemdata('step'), itemdata('min')));
265
                        }
266
                    });
267
 
268
                    itemdata('wired', true);
269
                }
270
                if (itemdata('resetable')) {
271
                    resetbtn.show();
272
                }
273
            }
274
            else {
275
                resetbtn.hide();
276
            }
277
 
278
            range.attr('aria-readonly', itemdata('readonly'));
279
        });
280
    };
281
 
282
    //touch converter http://ross.posterous.com/2008/08/19/iphone-touch-events-in-javascript/
283
    function touchHandler(event) {
284
 
285
        var touches = event.originalEvent.changedTouches,
286
                first = touches[0],
287
                type = "";
288
        switch (event.type) {
289
            case "touchmove": type = "mousemove"; break;
290
            case "touchend": type = "mouseup"; break;
291
            default: return;
292
        }
293
 
294
        var simulatedEvent = document.createEvent("MouseEvent");
295
        simulatedEvent.initMouseEvent(type, true, true, window, 1,
296
                              first.screenX, first.screenY,
297
                              first.clientX, first.clientY, false,
298
                              false, false, false, 0/*left*/, null);
299
 
300
        first.target.dispatchEvent(simulatedEvent);
301
        event.preventDefault();
302
    };
303
 
304
    //some default values.
305
    $.fn.rateit.defaults = { min: 0, max: 5, step: 0.5, starwidth: 16, starheight: 16, readonly: false, resetable: true, ispreset: false};
306
 
307
    //invoke it on all .rateit elements. This could be removed if not wanted.
308
    $(function () { $('div.rateit, span.rateit').rateit(); });
309
 
310
})(jQuery);