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.

475 lines
13 KiB

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