Dashboard sipadu mbip
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.

jquery.barrating.js 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. /**
  2. * jQuery Bar Rating Plugin v1.2.2
  3. *
  4. * http://github.com/antennaio/jquery-bar-rating
  5. *
  6. * Copyright (c) 2012-2016 Kazik Pietruszewski
  7. *
  8. * This plugin is available under the MIT license.
  9. * http://www.opensource.org/licenses/mit-license.php
  10. */
  11. (function (factory) {
  12. if (typeof define === 'function' && define.amd) {
  13. // AMD
  14. define(['jquery'], factory);
  15. } else if (typeof module === 'object' && module.exports) {
  16. // Node/CommonJS
  17. module.exports = factory(require('jquery'));
  18. } else {
  19. // browser globals
  20. factory(jQuery);
  21. }
  22. }(function ($) {
  23. var BarRating = (function() {
  24. function BarRating() {
  25. var self = this;
  26. // wrap element in a wrapper div
  27. var wrapElement = function() {
  28. var classes = ['br-wrapper'];
  29. if (self.options.theme !== '') {
  30. classes.push('br-theme-' + self.options.theme);
  31. }
  32. self.$elem.wrap($('<div />', {
  33. 'class': classes.join(' ')
  34. }));
  35. };
  36. // unwrap element
  37. var unwrapElement = function() {
  38. self.$elem.unwrap();
  39. };
  40. // find option by value
  41. var findOption = function(value) {
  42. if ($.isNumeric(value)) {
  43. value = Math.floor(value);
  44. }
  45. return $('option[value="' + value + '"]', self.$elem);
  46. };
  47. // get initial option
  48. var getInitialOption = function() {
  49. var initialRating = self.options.initialRating;
  50. if (!initialRating) {
  51. return $('option:selected', self.$elem);
  52. }
  53. return findOption(initialRating);
  54. };
  55. // get empty option
  56. var getEmptyOption = function() {
  57. var $emptyOpt = self.$elem.find('option[value="' + self.options.emptyValue + '"]');
  58. if (!$emptyOpt.length && self.options.allowEmpty) {
  59. $emptyOpt = $('<option />', { 'value': self.options.emptyValue });
  60. return $emptyOpt.prependTo(self.$elem);
  61. }
  62. return $emptyOpt;
  63. };
  64. // get data
  65. var getData = function(key) {
  66. var data = self.$elem.data('barrating');
  67. if (typeof key !== 'undefined') {
  68. return data[key];
  69. }
  70. return data;
  71. };
  72. // set data
  73. var setData = function(key, value) {
  74. if (value !== null && typeof value === 'object') {
  75. self.$elem.data('barrating', value);
  76. } else {
  77. self.$elem.data('barrating')[key] = value;
  78. }
  79. };
  80. // save data on element
  81. var saveDataOnElement = function() {
  82. var $opt = getInitialOption();
  83. var $emptyOpt = getEmptyOption();
  84. var value = $opt.val();
  85. var text = $opt.data('html') ? $opt.data('html') : $opt.text();
  86. // if the allowEmpty option is not set let's check if empty option exists in the select field
  87. var allowEmpty = (self.options.allowEmpty !== null) ?
  88. self.options.allowEmpty :
  89. !!$emptyOpt.length;
  90. var emptyValue = ($emptyOpt.length) ? $emptyOpt.val() : null;
  91. var emptyText = ($emptyOpt.length) ? $emptyOpt.text() : null;
  92. setData(null, {
  93. userOptions: self.options,
  94. // initial rating based on the OPTION value
  95. ratingValue: value,
  96. ratingText: text,
  97. // rating will be restored by calling clear method
  98. originalRatingValue: value,
  99. originalRatingText: text,
  100. // allow empty ratings?
  101. allowEmpty: allowEmpty,
  102. // rating value and text of the empty OPTION
  103. emptyRatingValue: emptyValue,
  104. emptyRatingText: emptyText,
  105. // read-only state
  106. readOnly: self.options.readonly,
  107. // did the user already select a rating?
  108. ratingMade: false
  109. });
  110. };
  111. // remove data on element
  112. var removeDataOnElement = function() {
  113. self.$elem.removeData('barrating');
  114. };
  115. // return current rating text
  116. var ratingText = function() {
  117. return getData('ratingText');
  118. };
  119. // return current rating value
  120. var ratingValue = function() {
  121. return getData('ratingValue');
  122. };
  123. // build widget and return jQuery element
  124. var buildWidget = function() {
  125. var $w = $('<div />', { 'class': 'br-widget' });
  126. // create A elements that will replace OPTIONs
  127. self.$elem.find('option').each(function() {
  128. var val, text, html, $a;
  129. val = $(this).val();
  130. // create ratings - but only if val is not defined as empty
  131. if (val !== getData('emptyRatingValue')) {
  132. text = $(this).text();
  133. html = $(this).data('html');
  134. if (html) { text = html; }
  135. $a = $('<a />', {
  136. 'href': '#',
  137. 'data-rating-value': val,
  138. 'data-rating-text': text,
  139. 'html': (self.options.showValues) ? text : ''
  140. });
  141. $w.append($a);
  142. }
  143. });
  144. // append .br-current-rating div to the widget
  145. if (self.options.showSelectedRating) {
  146. $w.append($('<div />', { 'text': '', 'class': 'br-current-rating' }));
  147. }
  148. // additional classes for the widget
  149. if (self.options.reverse) {
  150. $w.addClass('br-reverse');
  151. }
  152. if (self.options.readonly) {
  153. $w.addClass('br-readonly');
  154. }
  155. return $w;
  156. };
  157. // return a jQuery function name depending on the 'reverse' setting
  158. var nextAllorPreviousAll = function() {
  159. if (getData('userOptions').reverse) {
  160. return 'nextAll';
  161. } else {
  162. return 'prevAll';
  163. }
  164. };
  165. // set the value of the select field
  166. var setSelectFieldValue = function(value) {
  167. // change selected option
  168. findOption(value).prop('selected', true);
  169. self.$elem.change();
  170. };
  171. // reset select field
  172. var resetSelectField = function() {
  173. $('option', self.$elem).prop('selected', function() {
  174. return this.defaultSelected;
  175. });
  176. self.$elem.change();
  177. };
  178. // display the currently selected rating
  179. var showSelectedRating = function(text) {
  180. // text undefined?
  181. text = text ? text : ratingText();
  182. // special case when the selected rating is defined as empty
  183. if (text == getData('emptyRatingText')) {
  184. text = '';
  185. }
  186. // update .br-current-rating div
  187. if (self.options.showSelectedRating) {
  188. self.$elem.parent().find('.br-current-rating').text(text);
  189. }
  190. };
  191. // return rounded fraction of a value (14.4 -> 40, 0.99 -> 90)
  192. var fraction = function(value) {
  193. return Math.round(((Math.floor(value * 10) / 10) % 1) * 100);
  194. };
  195. // remove all classes from elements
  196. var resetStyle = function() {
  197. // remove all classes starting with br-*
  198. self.$widget.find('a').removeClass(function(index, classes) {
  199. return (classes.match(/(^|\s)br-\S+/g) || []).join(' ');
  200. });
  201. };
  202. // apply style by setting classes on elements
  203. var applyStyle = function() {
  204. var $a = self.$widget.find('a[data-rating-value="' + ratingValue() + '"]');
  205. var initialRating = getData('userOptions').initialRating;
  206. var baseValue = $.isNumeric(ratingValue()) ? ratingValue() : 0;
  207. var f = fraction(initialRating);
  208. var $all, $fractional;
  209. resetStyle();
  210. // add classes
  211. $a.addClass('br-selected br-current')[nextAllorPreviousAll()]()
  212. .addClass('br-selected');
  213. if (!getData('ratingMade') && $.isNumeric(initialRating)) {
  214. if ((initialRating <= baseValue) || !f) {
  215. return;
  216. }
  217. $all = self.$widget.find('a');
  218. $fractional = ($a.length) ?
  219. $a[(getData('userOptions').reverse) ? 'prev' : 'next']() :
  220. $all[(getData('userOptions').reverse) ? 'last' : 'first']();
  221. $fractional.addClass('br-fractional');
  222. $fractional.addClass('br-fractional-' + f);
  223. }
  224. };
  225. // check if the element is deselectable?
  226. var isDeselectable = function($element) {
  227. if (!getData('allowEmpty') || !getData('userOptions').deselectable) {
  228. return false;
  229. }
  230. return (ratingValue() == $element.attr('data-rating-value'));
  231. };
  232. // handle click events
  233. var attachClickHandler = function($elements) {
  234. $elements.on('click.barrating', function(event) {
  235. var $a = $(this),
  236. options = getData('userOptions'),
  237. value,
  238. text;
  239. event.preventDefault();
  240. value = $a.attr('data-rating-value');
  241. text = $a.attr('data-rating-text');
  242. // is current and deselectable?
  243. if (isDeselectable($a)) {
  244. value = getData('emptyRatingValue');
  245. text = getData('emptyRatingText');
  246. }
  247. // remember selected rating
  248. setData('ratingValue', value);
  249. setData('ratingText', text);
  250. setData('ratingMade', true);
  251. setSelectFieldValue(value);
  252. showSelectedRating(text);
  253. applyStyle();
  254. // onSelect callback
  255. options.onSelect.call(
  256. self,
  257. ratingValue(),
  258. ratingText(),
  259. event
  260. );
  261. return false;
  262. });
  263. };
  264. // handle mouseenter events
  265. var attachMouseEnterHandler = function($elements) {
  266. $elements.on('mouseenter.barrating', function() {
  267. var $a = $(this);
  268. resetStyle();
  269. $a.addClass('br-active')[nextAllorPreviousAll()]()
  270. .addClass('br-active');
  271. showSelectedRating($a.attr('data-rating-text'));
  272. });
  273. };
  274. // handle mouseleave events
  275. var attachMouseLeaveHandler = function($elements) {
  276. self.$widget.on('mouseleave.barrating blur.barrating', function() {
  277. showSelectedRating();
  278. applyStyle();
  279. });
  280. };
  281. // somewhat primitive way to remove 300ms click delay on touch devices
  282. // for a more advanced solution consider setting `fastClicks` option to false
  283. // and using a library such as fastclick (https://github.com/ftlabs/fastclick)
  284. var fastClicks = function($elements) {
  285. $elements.on('touchstart.barrating', function(event) {
  286. event.preventDefault();
  287. event.stopPropagation();
  288. $(this).click();
  289. });
  290. };
  291. // disable clicks
  292. var disableClicks = function($elements) {
  293. $elements.on('click.barrating', function(event) {
  294. event.preventDefault();
  295. });
  296. };
  297. var attachHandlers = function($elements) {
  298. // attach click event handler
  299. attachClickHandler($elements);
  300. if (self.options.hoverState) {
  301. // attach mouseenter event handler
  302. attachMouseEnterHandler($elements);
  303. // attach mouseleave event handler
  304. attachMouseLeaveHandler($elements);
  305. }
  306. };
  307. var detachHandlers = function($elements) {
  308. // remove event handlers in the ".barrating" namespace
  309. $elements.off('.barrating');
  310. };
  311. var setupHandlers = function(readonly) {
  312. var $elements = self.$widget.find('a');
  313. if (fastClicks) {
  314. fastClicks($elements);
  315. }
  316. if (readonly) {
  317. detachHandlers($elements);
  318. disableClicks($elements);
  319. } else {
  320. attachHandlers($elements);
  321. }
  322. };
  323. this.show = function() {
  324. // run only once
  325. if (getData()) return;
  326. // wrap element
  327. wrapElement();
  328. // save data
  329. saveDataOnElement();
  330. // build & append widget to the DOM
  331. self.$widget = buildWidget();
  332. self.$widget.insertAfter(self.$elem);
  333. applyStyle();
  334. showSelectedRating();
  335. setupHandlers(self.options.readonly);
  336. // hide the select field
  337. self.$elem.hide();
  338. };
  339. this.readonly = function(state) {
  340. if (typeof state !== 'boolean' || getData('readOnly') == state) return;
  341. setupHandlers(state);
  342. setData('readOnly', state);
  343. self.$widget.toggleClass('br-readonly');
  344. };
  345. this.set = function(value) {
  346. var options = getData('userOptions');
  347. if (self.$elem.find('option[value="' + value + '"]').length === 0) return;
  348. // set data
  349. setData('ratingValue', value);
  350. setData('ratingText', self.$elem.find('option[value="' + value + '"]').text());
  351. setData('ratingMade', true);
  352. setSelectFieldValue(ratingValue());
  353. showSelectedRating(ratingText());
  354. applyStyle();
  355. // onSelect callback
  356. if (!options.silent) {
  357. options.onSelect.call(
  358. this,
  359. ratingValue(),
  360. ratingText()
  361. );
  362. }
  363. };
  364. this.clear = function() {
  365. var options = getData('userOptions');
  366. // restore original data
  367. setData('ratingValue', getData('originalRatingValue'));
  368. setData('ratingText', getData('originalRatingText'));
  369. setData('ratingMade', false);
  370. resetSelectField();
  371. showSelectedRating(ratingText());
  372. applyStyle();
  373. // onClear callback
  374. options.onClear.call(
  375. this,
  376. ratingValue(),
  377. ratingText()
  378. );
  379. };
  380. this.destroy = function() {
  381. var value = ratingValue();
  382. var text = ratingText();
  383. var options = getData('userOptions');
  384. // detach handlers
  385. detachHandlers(self.$widget.find('a'));
  386. // remove widget
  387. self.$widget.remove();
  388. // remove data
  389. removeDataOnElement();
  390. // unwrap the element
  391. unwrapElement();
  392. // show the element
  393. self.$elem.show();
  394. // onDestroy callback
  395. options.onDestroy.call(
  396. this,
  397. value,
  398. text
  399. );
  400. };
  401. }
  402. BarRating.prototype.init = function (options, elem) {
  403. this.$elem = $(elem);
  404. this.options = $.extend({}, $.fn.barrating.defaults, options);
  405. return this.options;
  406. };
  407. return BarRating;
  408. })();
  409. $.fn.barrating = function (method, options) {
  410. return this.each(function () {
  411. var plugin = new BarRating();
  412. // plugin works with select fields
  413. if (!$(this).is('select')) {
  414. $.error('Sorry, this plugin only works with select fields.');
  415. }
  416. // method supplied
  417. if (plugin.hasOwnProperty(method)) {
  418. plugin.init(options, this);
  419. if (method === 'show') {
  420. return plugin.show(options);
  421. } else {
  422. // plugin exists?
  423. if (plugin.$elem.data('barrating')) {
  424. plugin.$widget = $(this).next('.br-widget');
  425. return plugin[method](options);
  426. }
  427. }
  428. // no method supplied or only options supplied
  429. } else if (typeof method === 'object' || !method) {
  430. options = method;
  431. plugin.init(options, this);
  432. return plugin.show();
  433. } else {
  434. $.error('Method ' + method + ' does not exist on jQuery.barrating');
  435. }
  436. });
  437. };
  438. $.fn.barrating.defaults = {
  439. theme:'',
  440. initialRating:null, // initial rating
  441. allowEmpty:null, // allow empty ratings?
  442. emptyValue:'', // this is the expected value of the empty rating
  443. showValues:false, // display rating values on the bars?
  444. showSelectedRating:true, // append a div with a rating to the widget?
  445. deselectable:true, // allow to deselect ratings?
  446. reverse:false, // reverse the rating?
  447. readonly:false, // make the rating ready-only?
  448. fastClicks:true, // remove 300ms click delay on touch devices?
  449. hoverState:true, // change state on hover?
  450. silent:false, // supress callbacks when controlling ratings programatically
  451. onSelect:function (value, text, event) {
  452. }, // callback fired when a rating is selected
  453. onClear:function (value, text) {
  454. }, // callback fired when a rating is cleared
  455. onDestroy:function (value, text) {
  456. } // callback fired when a widget is destroyed
  457. };
  458. $.fn.barrating.BarRating = BarRating;
  459. }));