1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 | ;(function ($, window, document, undefined) {
|
12 |
|
13 | 'use strict';
|
14 |
|
15 | $.isFunction = $.isFunction || function(obj) {
|
16 | return typeof obj === "function" && typeof obj.nodeType !== "number";
|
17 | };
|
18 |
|
19 | window = (typeof window != 'undefined' && window.Math == Math)
|
20 | ? window
|
21 | : (typeof self != 'undefined' && self.Math == Math)
|
22 | ? self
|
23 | : Function('return this')()
|
24 | ;
|
25 |
|
26 | $.fn.rating = function(parameters) {
|
27 | var
|
28 | $allModules = $(this),
|
29 | moduleSelector = $allModules.selector || '',
|
30 |
|
31 | time = new Date().getTime(),
|
32 | performance = [],
|
33 |
|
34 | query = arguments[0],
|
35 | methodInvoked = (typeof query == 'string'),
|
36 | queryArguments = [].slice.call(arguments, 1),
|
37 | returnedValue
|
38 | ;
|
39 | $allModules
|
40 | .each(function() {
|
41 | var
|
42 | settings = ( $.isPlainObject(parameters) )
|
43 | ? $.extend(true, {}, $.fn.rating.settings, parameters)
|
44 | : $.extend({}, $.fn.rating.settings),
|
45 |
|
46 | namespace = settings.namespace,
|
47 | className = settings.className,
|
48 | metadata = settings.metadata,
|
49 | selector = settings.selector,
|
50 |
|
51 | eventNamespace = '.' + namespace,
|
52 | moduleNamespace = 'module-' + namespace,
|
53 |
|
54 | element = this,
|
55 | instance = $(this).data(moduleNamespace),
|
56 |
|
57 | $module = $(this),
|
58 | $icon = $module.find(selector.icon),
|
59 |
|
60 | initialLoad,
|
61 | module
|
62 | ;
|
63 |
|
64 | module = {
|
65 |
|
66 | initialize: function() {
|
67 | module.verbose('Initializing rating module', settings);
|
68 |
|
69 | if($icon.length === 0) {
|
70 | module.setup.layout();
|
71 | }
|
72 |
|
73 | if(settings.interactive && !module.is.disabled()) {
|
74 | module.enable();
|
75 | }
|
76 | else {
|
77 | module.disable();
|
78 | }
|
79 | module.set.initialLoad();
|
80 | module.set.rating( module.get.initialRating() );
|
81 | module.remove.initialLoad();
|
82 | module.instantiate();
|
83 | },
|
84 |
|
85 | instantiate: function() {
|
86 | module.verbose('Instantiating module', settings);
|
87 | instance = module;
|
88 | $module
|
89 | .data(moduleNamespace, module)
|
90 | ;
|
91 | },
|
92 |
|
93 | destroy: function() {
|
94 | module.verbose('Destroying previous instance', instance);
|
95 | module.remove.events();
|
96 | $module
|
97 | .removeData(moduleNamespace)
|
98 | ;
|
99 | },
|
100 |
|
101 | refresh: function() {
|
102 | $icon = $module.find(selector.icon);
|
103 | },
|
104 |
|
105 | setup: {
|
106 | layout: function() {
|
107 | var
|
108 | maxRating = module.get.maxRating(),
|
109 | icon = module.get.icon(),
|
110 | html = $.fn.rating.settings.templates.icon(maxRating, icon)
|
111 | ;
|
112 | module.debug('Generating icon html dynamically');
|
113 | $module
|
114 | .html(html)
|
115 | ;
|
116 | module.refresh();
|
117 | }
|
118 | },
|
119 |
|
120 | event: {
|
121 | mouseenter: function() {
|
122 | var
|
123 | $activeIcon = $(this)
|
124 | ;
|
125 | $activeIcon
|
126 | .nextAll()
|
127 | .removeClass(className.selected)
|
128 | ;
|
129 | $module
|
130 | .addClass(className.selected)
|
131 | ;
|
132 | $activeIcon
|
133 | .addClass(className.selected)
|
134 | .prevAll()
|
135 | .addClass(className.selected)
|
136 | ;
|
137 | },
|
138 | mouseleave: function() {
|
139 | $module
|
140 | .removeClass(className.selected)
|
141 | ;
|
142 | $icon
|
143 | .removeClass(className.selected)
|
144 | ;
|
145 | },
|
146 | click: function() {
|
147 | var
|
148 | $activeIcon = $(this),
|
149 | currentRating = module.get.rating(),
|
150 | rating = $icon.index($activeIcon) + 1,
|
151 | canClear = (settings.clearable == 'auto')
|
152 | ? ($icon.length === 1)
|
153 | : settings.clearable
|
154 | ;
|
155 | if(canClear && currentRating == rating) {
|
156 | module.clearRating();
|
157 | }
|
158 | else {
|
159 | module.set.rating( rating );
|
160 | }
|
161 | }
|
162 | },
|
163 |
|
164 | clearRating: function() {
|
165 | module.debug('Clearing current rating');
|
166 | module.set.rating(0);
|
167 | },
|
168 |
|
169 | bind: {
|
170 | events: function() {
|
171 | module.verbose('Binding events');
|
172 | $module
|
173 | .on('mouseenter' + eventNamespace, selector.icon, module.event.mouseenter)
|
174 | .on('mouseleave' + eventNamespace, selector.icon, module.event.mouseleave)
|
175 | .on('click' + eventNamespace, selector.icon, module.event.click)
|
176 | ;
|
177 | }
|
178 | },
|
179 |
|
180 | remove: {
|
181 | events: function() {
|
182 | module.verbose('Removing events');
|
183 | $module
|
184 | .off(eventNamespace)
|
185 | ;
|
186 | },
|
187 | initialLoad: function() {
|
188 | initialLoad = false;
|
189 | }
|
190 | },
|
191 |
|
192 | enable: function() {
|
193 | module.debug('Setting rating to interactive mode');
|
194 | module.bind.events();
|
195 | $module
|
196 | .removeClass(className.disabled)
|
197 | ;
|
198 | },
|
199 |
|
200 | disable: function() {
|
201 | module.debug('Setting rating to read-only mode');
|
202 | module.remove.events();
|
203 | $module
|
204 | .addClass(className.disabled)
|
205 | ;
|
206 | },
|
207 |
|
208 | is: {
|
209 | initialLoad: function() {
|
210 | return initialLoad;
|
211 | },
|
212 | disabled: function() {
|
213 | return $module.hasClass(className.disabled);
|
214 | }
|
215 | },
|
216 |
|
217 | get: {
|
218 | icon: function(){
|
219 | var icon = $module.data(metadata.icon);
|
220 | if (icon) {
|
221 | $module.removeData(metadata.icon);
|
222 | }
|
223 | return icon || settings.icon;
|
224 | },
|
225 | initialRating: function() {
|
226 | if($module.data(metadata.rating) !== undefined) {
|
227 | $module.removeData(metadata.rating);
|
228 | return $module.data(metadata.rating);
|
229 | }
|
230 | return settings.initialRating;
|
231 | },
|
232 | maxRating: function() {
|
233 | if($module.data(metadata.maxRating) !== undefined) {
|
234 | $module.removeData(metadata.maxRating);
|
235 | return $module.data(metadata.maxRating);
|
236 | }
|
237 | return settings.maxRating;
|
238 | },
|
239 | rating: function() {
|
240 | var
|
241 | currentRating = $icon.filter('.' + className.active).length
|
242 | ;
|
243 | module.verbose('Current rating retrieved', currentRating);
|
244 | return currentRating;
|
245 | }
|
246 | },
|
247 |
|
248 | set: {
|
249 | rating: function(rating) {
|
250 | var
|
251 | ratingIndex = (rating - 1 >= 0)
|
252 | ? (rating - 1)
|
253 | : 0,
|
254 | $activeIcon = $icon.eq(ratingIndex)
|
255 | ;
|
256 | $module
|
257 | .removeClass(className.selected)
|
258 | ;
|
259 | $icon
|
260 | .removeClass(className.selected)
|
261 | .removeClass(className.active)
|
262 | ;
|
263 | if(rating > 0) {
|
264 | module.verbose('Setting current rating to', rating);
|
265 | $activeIcon
|
266 | .prevAll()
|
267 | .addBack()
|
268 | .addClass(className.active)
|
269 | ;
|
270 | }
|
271 | if(!module.is.initialLoad()) {
|
272 | settings.onRate.call(element, rating);
|
273 | }
|
274 | },
|
275 | initialLoad: function() {
|
276 | initialLoad = true;
|
277 | }
|
278 | },
|
279 |
|
280 | setting: function(name, value) {
|
281 | module.debug('Changing setting', name, value);
|
282 | if( $.isPlainObject(name) ) {
|
283 | $.extend(true, settings, name);
|
284 | }
|
285 | else if(value !== undefined) {
|
286 | if($.isPlainObject(settings[name])) {
|
287 | $.extend(true, settings[name], value);
|
288 | }
|
289 | else {
|
290 | settings[name] = value;
|
291 | }
|
292 | }
|
293 | else {
|
294 | return settings[name];
|
295 | }
|
296 | },
|
297 | internal: function(name, value) {
|
298 | if( $.isPlainObject(name) ) {
|
299 | $.extend(true, module, name);
|
300 | }
|
301 | else if(value !== undefined) {
|
302 | module[name] = value;
|
303 | }
|
304 | else {
|
305 | return module[name];
|
306 | }
|
307 | },
|
308 | debug: function() {
|
309 | if(!settings.silent && settings.debug) {
|
310 | if(settings.performance) {
|
311 | module.performance.log(arguments);
|
312 | }
|
313 | else {
|
314 | module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
|
315 | module.debug.apply(console, arguments);
|
316 | }
|
317 | }
|
318 | },
|
319 | verbose: function() {
|
320 | if(!settings.silent && settings.verbose && settings.debug) {
|
321 | if(settings.performance) {
|
322 | module.performance.log(arguments);
|
323 | }
|
324 | else {
|
325 | module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
|
326 | module.verbose.apply(console, arguments);
|
327 | }
|
328 | }
|
329 | },
|
330 | error: function() {
|
331 | if(!settings.silent) {
|
332 | module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
|
333 | module.error.apply(console, arguments);
|
334 | }
|
335 | },
|
336 | performance: {
|
337 | log: function(message) {
|
338 | var
|
339 | currentTime,
|
340 | executionTime,
|
341 | previousTime
|
342 | ;
|
343 | if(settings.performance) {
|
344 | currentTime = new Date().getTime();
|
345 | previousTime = time || currentTime;
|
346 | executionTime = currentTime - previousTime;
|
347 | time = currentTime;
|
348 | performance.push({
|
349 | 'Name' : message[0],
|
350 | 'Arguments' : [].slice.call(message, 1) || '',
|
351 | 'Element' : element,
|
352 | 'Execution Time' : executionTime
|
353 | });
|
354 | }
|
355 | clearTimeout(module.performance.timer);
|
356 | module.performance.timer = setTimeout(module.performance.display, 500);
|
357 | },
|
358 | display: function() {
|
359 | var
|
360 | title = settings.name + ':',
|
361 | totalTime = 0
|
362 | ;
|
363 | time = false;
|
364 | clearTimeout(module.performance.timer);
|
365 | $.each(performance, function(index, data) {
|
366 | totalTime += data['Execution Time'];
|
367 | });
|
368 | title += ' ' + totalTime + 'ms';
|
369 | if(moduleSelector) {
|
370 | title += ' \'' + moduleSelector + '\'';
|
371 | }
|
372 | if($allModules.length > 1) {
|
373 | title += ' ' + '(' + $allModules.length + ')';
|
374 | }
|
375 | if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
|
376 | console.groupCollapsed(title);
|
377 | if(console.table) {
|
378 | console.table(performance);
|
379 | }
|
380 | else {
|
381 | $.each(performance, function(index, data) {
|
382 | console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
|
383 | });
|
384 | }
|
385 | console.groupEnd();
|
386 | }
|
387 | performance = [];
|
388 | }
|
389 | },
|
390 | invoke: function(query, passedArguments, context) {
|
391 | var
|
392 | object = instance,
|
393 | maxDepth,
|
394 | found,
|
395 | response
|
396 | ;
|
397 | passedArguments = passedArguments || queryArguments;
|
398 | context = element || context;
|
399 | if(typeof query == 'string' && object !== undefined) {
|
400 | query = query.split(/[\. ]/);
|
401 | maxDepth = query.length - 1;
|
402 | $.each(query, function(depth, value) {
|
403 | var camelCaseValue = (depth != maxDepth)
|
404 | ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
|
405 | : query
|
406 | ;
|
407 | if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
|
408 | object = object[camelCaseValue];
|
409 | }
|
410 | else if( object[camelCaseValue] !== undefined ) {
|
411 | found = object[camelCaseValue];
|
412 | return false;
|
413 | }
|
414 | else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
|
415 | object = object[value];
|
416 | }
|
417 | else if( object[value] !== undefined ) {
|
418 | found = object[value];
|
419 | return false;
|
420 | }
|
421 | else {
|
422 | return false;
|
423 | }
|
424 | });
|
425 | }
|
426 | if ( $.isFunction( found ) ) {
|
427 | response = found.apply(context, passedArguments);
|
428 | }
|
429 | else if(found !== undefined) {
|
430 | response = found;
|
431 | }
|
432 | if(Array.isArray(returnedValue)) {
|
433 | returnedValue.push(response);
|
434 | }
|
435 | else if(returnedValue !== undefined) {
|
436 | returnedValue = [returnedValue, response];
|
437 | }
|
438 | else if(response !== undefined) {
|
439 | returnedValue = response;
|
440 | }
|
441 | return found;
|
442 | }
|
443 | };
|
444 | if(methodInvoked) {
|
445 | if(instance === undefined) {
|
446 | module.initialize();
|
447 | }
|
448 | module.invoke(query);
|
449 | }
|
450 | else {
|
451 | if(instance !== undefined) {
|
452 | instance.invoke('destroy');
|
453 | }
|
454 | module.initialize();
|
455 | }
|
456 | })
|
457 | ;
|
458 |
|
459 | return (returnedValue !== undefined)
|
460 | ? returnedValue
|
461 | : this
|
462 | ;
|
463 | };
|
464 |
|
465 | $.fn.rating.settings = {
|
466 |
|
467 | name : 'Rating',
|
468 | namespace : 'rating',
|
469 |
|
470 | icon : 'star',
|
471 |
|
472 | silent : false,
|
473 | debug : false,
|
474 | verbose : false,
|
475 | performance : true,
|
476 |
|
477 | initialRating : 0,
|
478 | interactive : true,
|
479 | maxRating : 4,
|
480 | clearable : 'auto',
|
481 |
|
482 | fireOnInit : false,
|
483 |
|
484 | onRate : function(rating){},
|
485 |
|
486 | error : {
|
487 | method : 'The method you called is not defined',
|
488 | noMaximum : 'No maximum rating specified. Cannot generate HTML automatically'
|
489 | },
|
490 |
|
491 |
|
492 | metadata: {
|
493 | rating : 'rating',
|
494 | maxRating : 'maxRating',
|
495 | icon : 'icon'
|
496 | },
|
497 |
|
498 | className : {
|
499 | active : 'active',
|
500 | disabled : 'disabled',
|
501 | selected : 'selected',
|
502 | loading : 'loading'
|
503 | },
|
504 |
|
505 | selector : {
|
506 | icon : '.icon'
|
507 | },
|
508 |
|
509 | templates: {
|
510 | icon: function(maxRating, iconClass) {
|
511 | var
|
512 | icon = 1,
|
513 | html = ''
|
514 | ;
|
515 | while(icon <= maxRating) {
|
516 | html += '<i class="'+iconClass+' icon"></i>';
|
517 | icon++;
|
518 | }
|
519 | return html;
|
520 | }
|
521 | }
|
522 |
|
523 | };
|
524 |
|
525 | })( jQuery, window, document );
|