You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

451 lines
12 KiB

10 years ago
  1. /*
  2. * # Semantic - Rating
  3. * http://github.com/semantic-org/semantic-ui/
  4. *
  5. *
  6. * Copyright 2014 Contributor
  7. * Released under the MIT license
  8. * http://opensource.org/licenses/MIT
  9. *
  10. */
  11. ;(function ($, window, document, undefined) {
  12. "use strict";
  13. $.fn.rating = function(parameters) {
  14. var
  15. $allModules = $(this),
  16. moduleSelector = $allModules.selector || '',
  17. time = new Date().getTime(),
  18. performance = [],
  19. query = arguments[0],
  20. methodInvoked = (typeof query == 'string'),
  21. queryArguments = [].slice.call(arguments, 1),
  22. returnedValue
  23. ;
  24. $allModules
  25. .each(function() {
  26. var
  27. settings = ( $.isPlainObject(parameters) )
  28. ? $.extend(true, {}, $.fn.rating.settings, parameters)
  29. : $.extend({}, $.fn.rating.settings),
  30. namespace = settings.namespace,
  31. className = settings.className,
  32. metadata = settings.metadata,
  33. selector = settings.selector,
  34. error = settings.error,
  35. eventNamespace = '.' + namespace,
  36. moduleNamespace = 'module-' + namespace,
  37. element = this,
  38. instance = $(this).data(moduleNamespace),
  39. $module = $(this),
  40. $icon = $module.find(selector.icon),
  41. module
  42. ;
  43. module = {
  44. initialize: function() {
  45. module.verbose('Initializing rating module', settings);
  46. if($icon.size() === 0) {
  47. module.setup.layout();
  48. }
  49. if(settings.interactive) {
  50. module.enable();
  51. }
  52. else {
  53. module.disable();
  54. }
  55. if(settings.initialRating) {
  56. module.debug('Setting initial rating');
  57. module.setRating(settings.initialRating);
  58. }
  59. if( $module.data(metadata.rating) ) {
  60. module.debug('Rating found in metadata');
  61. module.setRating( $module.data(metadata.rating) );
  62. }
  63. module.instantiate();
  64. },
  65. instantiate: function() {
  66. module.verbose('Instantiating module', settings);
  67. instance = module;
  68. $module
  69. .data(moduleNamespace, module)
  70. ;
  71. },
  72. destroy: function() {
  73. module.verbose('Destroying previous instance', instance);
  74. $module
  75. .removeData(moduleNamespace)
  76. ;
  77. $icon
  78. .off(eventNamespace)
  79. ;
  80. },
  81. refresh: function() {
  82. $icon = $module.find(selector.icon);
  83. },
  84. setup: {
  85. layout: function() {
  86. var
  87. maxRating = $module.data(metadata.maxRating) || settings.maxRating
  88. ;
  89. module.debug('Generating icon html dynamically');
  90. $module
  91. .html($.fn.rating.settings.templates.icon(maxRating))
  92. ;
  93. module.refresh();
  94. }
  95. },
  96. event: {
  97. mouseenter: function() {
  98. var
  99. $activeIcon = $(this)
  100. ;
  101. $activeIcon
  102. .nextAll()
  103. .removeClass(className.selected)
  104. ;
  105. $module
  106. .addClass(className.selected)
  107. ;
  108. $activeIcon
  109. .addClass(className.selected)
  110. .prevAll()
  111. .addClass(className.selected)
  112. ;
  113. },
  114. mouseleave: function() {
  115. $module
  116. .removeClass(className.selected)
  117. ;
  118. $icon
  119. .removeClass(className.selected)
  120. ;
  121. },
  122. click: function() {
  123. var
  124. $activeIcon = $(this),
  125. currentRating = module.getRating(),
  126. rating = $icon.index($activeIcon) + 1,
  127. canClear = (settings.clearable == 'auto')
  128. ? ($icon.size() === 1)
  129. : settings.clearable
  130. ;
  131. if(canClear && currentRating == rating) {
  132. module.clearRating();
  133. }
  134. else {
  135. module.setRating( rating );
  136. }
  137. }
  138. },
  139. clearRating: function() {
  140. module.debug('Clearing current rating');
  141. module.setRating(0);
  142. },
  143. getRating: function() {
  144. var
  145. currentRating = $icon.filter('.' + className.active).size()
  146. ;
  147. module.verbose('Current rating retrieved', currentRating);
  148. return currentRating;
  149. },
  150. enable: function() {
  151. module.debug('Setting rating to interactive mode');
  152. $icon
  153. .on('mouseenter' + eventNamespace, module.event.mouseenter)
  154. .on('mouseleave' + eventNamespace, module.event.mouseleave)
  155. .on('click' + eventNamespace, module.event.click)
  156. ;
  157. $module
  158. .removeClass(className.disabled)
  159. ;
  160. },
  161. disable: function() {
  162. module.debug('Setting rating to read-only mode');
  163. $icon
  164. .off(eventNamespace)
  165. ;
  166. $module
  167. .addClass(className.disabled)
  168. ;
  169. },
  170. setRating: function(rating) {
  171. var
  172. ratingIndex = (rating - 1 >= 0)
  173. ? (rating - 1)
  174. : 0,
  175. $activeIcon = $icon.eq(ratingIndex)
  176. ;
  177. $module
  178. .removeClass(className.selected)
  179. ;
  180. $icon
  181. .removeClass(className.selected)
  182. .removeClass(className.active)
  183. ;
  184. if(rating > 0) {
  185. module.verbose('Setting current rating to', rating);
  186. $activeIcon
  187. .prevAll()
  188. .andSelf()
  189. .addClass(className.active)
  190. ;
  191. }
  192. $.proxy(settings.onRate, element)(rating);
  193. },
  194. setting: function(name, value) {
  195. module.debug('Changing setting', name, value);
  196. if( $.isPlainObject(name) ) {
  197. $.extend(true, settings, name);
  198. }
  199. else if(value !== undefined) {
  200. settings[name] = value;
  201. }
  202. else {
  203. return settings[name];
  204. }
  205. },
  206. internal: function(name, value) {
  207. if( $.isPlainObject(name) ) {
  208. $.extend(true, module, name);
  209. }
  210. else if(value !== undefined) {
  211. module[name] = value;
  212. }
  213. else {
  214. return module[name];
  215. }
  216. },
  217. debug: function() {
  218. if(settings.debug) {
  219. if(settings.performance) {
  220. module.performance.log(arguments);
  221. }
  222. else {
  223. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  224. module.debug.apply(console, arguments);
  225. }
  226. }
  227. },
  228. verbose: function() {
  229. if(settings.verbose && settings.debug) {
  230. if(settings.performance) {
  231. module.performance.log(arguments);
  232. }
  233. else {
  234. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  235. module.verbose.apply(console, arguments);
  236. }
  237. }
  238. },
  239. error: function() {
  240. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  241. module.error.apply(console, arguments);
  242. },
  243. performance: {
  244. log: function(message) {
  245. var
  246. currentTime,
  247. executionTime,
  248. previousTime
  249. ;
  250. if(settings.performance) {
  251. currentTime = new Date().getTime();
  252. previousTime = time || currentTime;
  253. executionTime = currentTime - previousTime;
  254. time = currentTime;
  255. performance.push({
  256. 'Name' : message[0],
  257. 'Arguments' : [].slice.call(message, 1) || '',
  258. 'Element' : element,
  259. 'Execution Time' : executionTime
  260. });
  261. }
  262. clearTimeout(module.performance.timer);
  263. module.performance.timer = setTimeout(module.performance.display, 100);
  264. },
  265. display: function() {
  266. var
  267. title = settings.name + ':',
  268. totalTime = 0
  269. ;
  270. time = false;
  271. clearTimeout(module.performance.timer);
  272. $.each(performance, function(index, data) {
  273. totalTime += data['Execution Time'];
  274. });
  275. title += ' ' + totalTime + 'ms';
  276. if(moduleSelector) {
  277. title += ' \'' + moduleSelector + '\'';
  278. }
  279. if($allModules.size() > 1) {
  280. title += ' ' + '(' + $allModules.size() + ')';
  281. }
  282. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  283. console.groupCollapsed(title);
  284. if(console.table) {
  285. console.table(performance);
  286. }
  287. else {
  288. $.each(performance, function(index, data) {
  289. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  290. });
  291. }
  292. console.groupEnd();
  293. }
  294. performance = [];
  295. }
  296. },
  297. invoke: function(query, passedArguments, context) {
  298. var
  299. object = instance,
  300. maxDepth,
  301. found,
  302. response
  303. ;
  304. passedArguments = passedArguments || queryArguments;
  305. context = element || context;
  306. if(typeof query == 'string' && object !== undefined) {
  307. query = query.split(/[\. ]/);
  308. maxDepth = query.length - 1;
  309. $.each(query, function(depth, value) {
  310. var camelCaseValue = (depth != maxDepth)
  311. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  312. : query
  313. ;
  314. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  315. object = object[camelCaseValue];
  316. }
  317. else if( object[camelCaseValue] !== undefined ) {
  318. found = object[camelCaseValue];
  319. return false;
  320. }
  321. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  322. object = object[value];
  323. }
  324. else if( object[value] !== undefined ) {
  325. found = object[value];
  326. return false;
  327. }
  328. else {
  329. return false;
  330. }
  331. });
  332. }
  333. if ( $.isFunction( found ) ) {
  334. response = found.apply(context, passedArguments);
  335. }
  336. else if(found !== undefined) {
  337. response = found;
  338. }
  339. if($.isArray(returnedValue)) {
  340. returnedValue.push(response);
  341. }
  342. else if(returnedValue !== undefined) {
  343. returnedValue = [returnedValue, response];
  344. }
  345. else if(response !== undefined) {
  346. returnedValue = response;
  347. }
  348. return found;
  349. }
  350. };
  351. if(methodInvoked) {
  352. if(instance === undefined) {
  353. module.initialize();
  354. }
  355. module.invoke(query);
  356. }
  357. else {
  358. if(instance !== undefined) {
  359. module.destroy();
  360. }
  361. module.initialize();
  362. }
  363. })
  364. ;
  365. return (returnedValue !== undefined)
  366. ? returnedValue
  367. : this
  368. ;
  369. };
  370. $.fn.rating.settings = {
  371. name : 'Rating',
  372. namespace : 'rating',
  373. debug : false,
  374. verbose : true,
  375. performance : true,
  376. initialRating : 0,
  377. interactive : true,
  378. maxRating : 4,
  379. clearable : 'auto',
  380. onRate : function(rating){},
  381. error : {
  382. method : 'The method you called is not defined',
  383. noMaximum : 'No maximum rating specified. Cannot generate HTML automatically'
  384. },
  385. metadata: {
  386. rating : 'rating',
  387. maxRating : 'maxRating'
  388. },
  389. className : {
  390. active : 'active',
  391. disabled : 'disabled',
  392. selected : 'selected',
  393. loading : 'loading'
  394. },
  395. selector : {
  396. icon : '.icon'
  397. },
  398. templates: {
  399. icon: function(maxRating) {
  400. var
  401. icon = 1,
  402. html = ''
  403. ;
  404. while(icon <= maxRating) {
  405. html += '<i class="icon"></i>';
  406. icon++;
  407. }
  408. return html;
  409. }
  410. }
  411. };
  412. })( jQuery, window , document );