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.

403 lines
11 KiB

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