jquery.rateyo.js 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264
  1. import $$1 from 'jquery';
  2. function _typeof(obj) {
  3. "@babel/helpers - typeof";
  4. if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
  5. _typeof = function (obj) {
  6. return typeof obj;
  7. };
  8. } else {
  9. _typeof = function (obj) {
  10. return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
  11. };
  12. }
  13. return _typeof(obj);
  14. }
  15. function _typeof$1(obj) {
  16. "@babel/helpers - typeof";
  17. if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
  18. _typeof$1 = function (obj) {
  19. return typeof obj;
  20. };
  21. } else {
  22. _typeof$1 = function (obj) {
  23. return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
  24. };
  25. }
  26. return _typeof$1(obj);
  27. }
  28. function _defineProperty(obj, key, value) {
  29. if (key in obj) {
  30. Object.defineProperty(obj, key, {
  31. value: value,
  32. enumerable: true,
  33. configurable: true,
  34. writable: true
  35. });
  36. } else {
  37. obj[key] = value;
  38. }
  39. return obj;
  40. }
  41. function ownKeys(object, enumerableOnly) {
  42. var keys = Object.keys(object);
  43. if (Object.getOwnPropertySymbols) {
  44. var symbols = Object.getOwnPropertySymbols(object);
  45. if (enumerableOnly) symbols = symbols.filter(function (sym) {
  46. return Object.getOwnPropertyDescriptor(object, sym).enumerable;
  47. });
  48. keys.push.apply(keys, symbols);
  49. }
  50. return keys;
  51. }
  52. function _objectSpread2(target) {
  53. for (var i = 1; i < arguments.length; i++) {
  54. var source = arguments[i] != null ? arguments[i] : {};
  55. if (i % 2) {
  56. ownKeys(Object(source), true).forEach(function (key) {
  57. _defineProperty(target, key, source[key]);
  58. });
  59. } else if (Object.getOwnPropertyDescriptors) {
  60. Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
  61. } else {
  62. ownKeys(Object(source)).forEach(function (key) {
  63. Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
  64. });
  65. }
  66. }
  67. return target;
  68. }
  69. // The basic svg string required to generate stars
  70. var BASICSTAR = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<svg version=\"1.1\"" + "xmlns=\"http://www.w3.org/2000/svg\"" + "viewBox=\"0 12.705 512 486.59\"" + "x=\"0px\" y=\"0px\"" + "xml:space=\"preserve\">" + "<polygon " + "points=\"256.814,12.705 317.205,198.566" + " 512.631,198.566 354.529,313.435 " + "414.918,499.295 256.814,384.427 " + "98.713,499.295 159.102,313.435 " + "1,198.566 196.426,198.566 \"/>" + "</svg>"; // The Default values of different options available in the Plugin
  71. var DEFAULTS = {
  72. starWidth: "32px",
  73. normalFill: "gray",
  74. ratedFill: "#f39c12",
  75. numStars: 5,
  76. maxValue: 5,
  77. precision: 1,
  78. rating: 0,
  79. fullStar: false,
  80. halfStar: false,
  81. hover: true,
  82. readOnly: false,
  83. spacing: "0px",
  84. rtl: false,
  85. multiColor: null,
  86. onInit: null,
  87. onChange: null,
  88. onSet: null,
  89. starSvg: null
  90. }; //Default colors for multi-color rating
  91. var MULTICOLOR_OPTIONS = {
  92. startColor: "#c0392b",
  93. //red
  94. endColor: "#f1c40f" //yellow
  95. };
  96. // http://stackoverflow.com/questions/11381673/detecting-a-mobile-browser
  97. function isMobileBrowser() {
  98. var check = false;
  99. (function (a) {
  100. if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true;
  101. })(navigator.userAgent || navigator.vendor || window.opera);
  102. return check;
  103. }
  104. function checkPrecision(value, minValue, maxValue) {
  105. /*
  106. * This function removes the unnecessary precision, at Min and Max Values
  107. */
  108. // Its like comparing 0.0 with 0, which is true
  109. if (value === minValue) {
  110. value = minValue;
  111. } else if (value === maxValue) {
  112. value = maxValue;
  113. }
  114. return value;
  115. }
  116. function checkBounds(value, minValue, maxValue) {
  117. /*
  118. * Check if the value is between min and max values, if not, throw an error
  119. */
  120. var isValid = value >= minValue && value <= maxValue;
  121. if (!isValid) {
  122. throw Error("Invalid Rating, expected value between " + minValue + " and " + maxValue);
  123. }
  124. return value;
  125. }
  126. function isType(value, type) {
  127. return _typeof$1(value) === type;
  128. }
  129. function isDefined(value) {
  130. // Better way to check if a variable is defined or not
  131. return typeof value !== "undefined";
  132. }
  133. var isNumber = function isNumber(input) {
  134. return isType(input, "number");
  135. };
  136. var isString = function isString(input) {
  137. return isType(input, "string");
  138. };
  139. var isFunction = function isFunction(input) {
  140. return isType(input, "function");
  141. };
  142. var hexRegex = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i;
  143. function hexToRGB(hex) {
  144. /*
  145. * Extracts and returns the Red, Blue, Green Channel values,
  146. * in the form of decimals
  147. */
  148. if (!hexRegex.test(hex)) {
  149. return null;
  150. }
  151. var hexValues = hexRegex.exec(hex),
  152. r = parseInt(hexValues[1], 16),
  153. g = parseInt(hexValues[2], 16),
  154. b = parseInt(hexValues[3], 16);
  155. return {
  156. r: r,
  157. g: g,
  158. b: b
  159. };
  160. }
  161. function getChannelValue(startVal, endVal, percent) {
  162. /*
  163. * Returns a value between `startVal` and `endVal` based on the percent
  164. */
  165. var newVal = (endVal - startVal) * (percent / 100);
  166. newVal = Math.round(startVal + newVal).toString(16);
  167. if (newVal.length === 1) {
  168. newVal = "0" + newVal;
  169. }
  170. return newVal;
  171. }
  172. function getColor(startColor, endColor, percent) {
  173. /*
  174. * Given the percentage( `percent` ) of `endColor` to be mixed
  175. * with the `startColor`, returns the mixed color.
  176. * Colors should be only in Hex Format
  177. */
  178. if (!startColor || !endColor) {
  179. return null;
  180. }
  181. percent = isDefined(percent) ? percent : 0;
  182. startColor = hexToRGB(startColor);
  183. endColor = hexToRGB(endColor);
  184. var r = getChannelValue(startColor.r, endColor.r, percent),
  185. b = getChannelValue(startColor.b, endColor.b, percent),
  186. g = getChannelValue(startColor.g, endColor.g, percent);
  187. return "#" + r + g + b;
  188. }
  189. var eventObjectMap = {};
  190. function getEventObject(event) {
  191. return eventObjectMap[event] || (eventObjectMap[event] = new String(event));
  192. }
  193. var handlerProxyMap = new WeakMap();
  194. function proxy(node, fn, event) {
  195. event = getEventObject(event);
  196. var eventHandlerMap = handlerProxyMap.get(node);
  197. if (!eventHandlerMap) {
  198. handlerProxyMap.set(node, eventHandlerMap = new WeakMap());
  199. }
  200. var handlerMap = eventHandlerMap.get(event);
  201. if (!handlerMap) {
  202. eventHandlerMap.set(event, handlerMap = new Map());
  203. }
  204. var handler = handlerMap.get(fn);
  205. if (handler) {
  206. return handler;
  207. }
  208. function proxy(e) {
  209. var data = e.detail;
  210. fn.call(node, e, data);
  211. }
  212. handlerMap.set(fn, proxy);
  213. return proxy;
  214. }
  215. proxy.get = function getOriginalFunction(node, fn, event) {
  216. event = getEventObject(event);
  217. var eventHandlerMap = handlerProxyMap.get(node);
  218. if (!eventHandlerMap) {
  219. return fn;
  220. }
  221. var handlerMap = eventHandlerMap.get(event);
  222. if (!handlerMap) {
  223. return fn;
  224. }
  225. return handlerMap.get(fn) || fn;
  226. };
  227. var Event = isFunction(window.Event) ? window.Event : function Event(event) {
  228. var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  229. var _params$bubbles = params.bubbles,
  230. bubbles = _params$bubbles === void 0 ? false : _params$bubbles,
  231. _params$cancelable = params.cancelable,
  232. cancelable = _params$cancelable === void 0 ? false : _params$cancelable;
  233. var evt = document.createEvent("Event");
  234. evt.initEvent(event, bubbles, cancelable);
  235. return evt;
  236. };
  237. var CustomEvent = isFunction(window.CustomEvent) ? window.CustomEvent : (CustomEvent.prototype = Object.create(Event.prototype), CustomEvent);
  238. var events = {
  239. trigger: function trigger(event, detail) {
  240. var eventProps = {
  241. bubbles: true
  242. };
  243. if (!isDefined(detail)) {
  244. this.node.dispatchEvent(new Event(event, eventProps));
  245. } else {
  246. this.node.dispatchEvent(new CustomEvent(event, _objectSpread2({
  247. detail: detail
  248. }, eventProps)));
  249. }
  250. return this;
  251. },
  252. on: function on(event, handler) {
  253. this.node.addEventListener(event, proxy(this.node, handler, event));
  254. return this;
  255. },
  256. off: function off(event, handler) {
  257. this.node.removeEventListener(event, proxy.get(this.node, handler, event));
  258. return this;
  259. }
  260. };
  261. var rateyoAttrRegex = /^rateyo(.+)$/;
  262. function classList(node, operation, input) {
  263. var className = node.className.trim();
  264. var classes = className && className.split(/\s/) || [],
  265. classMap = {};
  266. classes = classes.reduce(function (result, item, index) {
  267. if (!classMap.hasOwnProperty(item)) {
  268. result.push(item);
  269. classMap[item] = index;
  270. }
  271. return result;
  272. }, []);
  273. if (operation === classList.add) {
  274. if (classMap.hasOwnProperty(input)) {
  275. return;
  276. }
  277. classes.push(input);
  278. } else if (operation === classList.remove) {
  279. if (!classMap.hasOwnProperty(input)) {
  280. return;
  281. }
  282. classes.splice(classMap[input], 1);
  283. }
  284. node.className = classes.join(" ");
  285. }
  286. classList.add = "add";
  287. classList.remove = "remove";
  288. function El(node) {
  289. this.node = node;
  290. }
  291. El.prototype = {
  292. empty: function empty() {
  293. this.node.innerHTML = "";
  294. return this;
  295. },
  296. addClass: function addClass(className) {
  297. classList(this.node, classList.add, className);
  298. return this;
  299. },
  300. removeClass: function removeClass(className) {
  301. classList(this.node, classList.remove, className);
  302. return this;
  303. },
  304. appendTo: function appendTo(parent) {
  305. (El.isEl(parent) ? parent.node : parent).appendChild(this.node);
  306. return this;
  307. },
  308. css: function css(styleAttribute, value) {
  309. this.node.style[styleAttribute] = value;
  310. return this;
  311. },
  312. width: function width(_width) {
  313. if (!isDefined(_width)) {
  314. return this.node.getBoundingClientRect().width;
  315. }
  316. this.css("width", _width + (isNumber(_width) ? "px" : ""));
  317. },
  318. find: function find(selector) {
  319. return $(this.node.querySelectorAll(selector));
  320. },
  321. attr: function attr(attrObj) {
  322. for (var attrName in attrObj) {
  323. if (attrObj.hasOwnProperty(attrName)) {
  324. this.node.setAttribute(attrName, attrObj[attrName]);
  325. }
  326. }
  327. return this;
  328. },
  329. removeAttr: function removeAttr(attributeName) {
  330. this.node.removeAttribute(attributeName);
  331. return this;
  332. },
  333. children: function children() {
  334. return $(this.node.childNodes);
  335. },
  336. remove: function remove() {
  337. return this.node.remove();
  338. },
  339. offset: function offset() {
  340. var _this$node$getBoundin = this.node.getBoundingClientRect(),
  341. top = _this$node$getBoundin.top,
  342. left = _this$node$getBoundin.left,
  343. bottom = _this$node$getBoundin.bottom,
  344. right = _this$node$getBoundin.right;
  345. return {
  346. top: top,
  347. left: left,
  348. bottom: bottom,
  349. right: right
  350. };
  351. },
  352. dataAttrOptions: function dataAttrOptions() {
  353. var dataset = this.node.dataset;
  354. return Object.keys(dataset).reduce(function (options, attr) {
  355. var match = attr.match(rateyoAttrRegex);
  356. if (!match) {
  357. return options;
  358. }
  359. var rateyoAttr = match[1],
  360. option = rateyoAttr[0].toLowerCase() + rateyoAttr.slice(1);
  361. options[option] = dataset[attr];
  362. return options;
  363. }, {});
  364. }
  365. };
  366. El.prototype = _objectSpread2(_objectSpread2({}, El.prototype), events);
  367. El.isEl = function (node) {
  368. return node instanceof El;
  369. };
  370. function Collection(nodeList) {
  371. var _this = this;
  372. this.collection = [];
  373. Array.prototype.forEach.call(nodeList, function (node) {
  374. _this.collection.push(new El(node));
  375. });
  376. }
  377. Collection.isCollection = function (input) {
  378. return input instanceof Collection;
  379. };
  380. Collection.prototype = {};
  381. var _loop = function _loop(method) {
  382. if (!El.prototype.hasOwnProperty(method)) {
  383. return "continue";
  384. }
  385. function proxy() {
  386. for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
  387. args[_key] = arguments[_key];
  388. }
  389. this.collection.forEach(function (el) {
  390. return El.isEl(el) && el[method].apply(el, args);
  391. });
  392. return this;
  393. }
  394. Collection.prototype[method] = proxy;
  395. };
  396. for (var method in El.prototype) {
  397. var _ret = _loop(method);
  398. if (_ret === "continue") continue;
  399. }
  400. var parser = new DOMParser();
  401. function parseHTML(html) {
  402. return parser.parseFromString(html.trim(), "text/html").body.childNodes;
  403. }
  404. function $(node) {
  405. node = isString(node) && parseHTML(node) || node;
  406. if (El.isEl(node) || Collection.isCollection(node)) {
  407. return node;
  408. }
  409. if (node instanceof NodeList) {
  410. return new Collection(node);
  411. }
  412. return new El(node);
  413. }
  414. $.El = El;
  415. var instanceMap = new WeakMap();
  416. function RateYo(node) {
  417. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  418. if (!(this instanceof RateYo)) {
  419. return new RateYo(node, options);
  420. }
  421. if (instanceMap.has(node)) {
  422. return instanceMap.get(node);
  423. }
  424. var that = this;
  425. this.node = node;
  426. var $node = $(node);
  427. options = _objectSpread2(_objectSpread2(_objectSpread2({}, DEFAULTS), options), $node.dataAttrOptions()); // Remove any stuff that is present inside the container, and add the plugin class
  428. $node.empty().addClass("jq-ry-container");
  429. /*
  430. * Basically the plugin displays the rating using two rows of stars lying one above
  431. * the other, the row that is on the top represents the actual rating, and the one
  432. * behind acts just like a background.
  433. *
  434. * `$groupWrapper`: is an element that wraps both the rows
  435. * `$normalGroup`: is the container for row of stars thats behind and
  436. * acts as background
  437. * `$ratedGroup`: is the container for row of stars that display the actual rating.
  438. *
  439. * The rating is displayed by adjusting the width of `$ratedGroup`
  440. */
  441. var $groupWrapper = $(document.createElement("div")).addClass("jq-ry-group-wrapper").appendTo($node);
  442. var $normalGroup = $(document.createElement("div")).addClass("jq-ry-normal-group").addClass("jq-ry-group").appendTo($groupWrapper);
  443. var $ratedGroup = $(document.createElement("div")).addClass("jq-ry-rated-group").addClass("jq-ry-group").appendTo($groupWrapper);
  444. /*
  445. * Variable `step`: store the value of the rating for each star
  446. * eg: if `maxValue` is 5 and `numStars` is 5, value of each star
  447. * is 1.
  448. * Variable `starWidth`: stores the decimal value of width of star in units of px
  449. * Variable `percentOfStar`: stores the percentage of width each star takes w.r.t
  450. * the container
  451. * Variable `spacing`: stores the decimal value of the spacing between stars
  452. * in the units of px
  453. * Variable `percentOfSpacing`: stores the percentage of width of the spacing
  454. * between stars w.r.t the container
  455. */
  456. var step,
  457. starWidth,
  458. percentOfStar,
  459. spacing,
  460. percentOfSpacing,
  461. containerWidth,
  462. minValue = 0;
  463. /*
  464. * `currentRating` contains rating that is being displayed at the latest point of
  465. * time.
  466. *
  467. * When ever you hover over the plugin UI, the rating value changes
  468. * according to the place where you point the cursor, currentRating contains
  469. * the current value of rating that is being shown in the UI
  470. */
  471. var currentRating = options.rating; // A flag to store if the plugin is already being displayed in the UI
  472. var isInitialized = false;
  473. function showRating(ratingVal) {
  474. /*
  475. * The function is responsible for displaying the rating by changing
  476. * the width of `$ratedGroup`
  477. */
  478. if (!isDefined(ratingVal)) {
  479. ratingVal = options.rating;
  480. } // Storing the value that is being shown in `currentRating`.
  481. currentRating = ratingVal;
  482. var numStarsToShow = ratingVal / step; // calculating the percentage of width of $ratedGroup with respect to its parent
  483. var percent = numStarsToShow * percentOfStar;
  484. if (numStarsToShow > 1) {
  485. // adding the percentage of space that is taken by the gap the stars
  486. percent += (Math.ceil(numStarsToShow) - 1) * percentOfSpacing;
  487. }
  488. setRatedFill(options.ratedFill);
  489. percent = options.rtl ? 100 - percent : percent;
  490. if (percent < 0) {
  491. percent = 0;
  492. } else if (percent > 100) {
  493. percent = 100;
  494. }
  495. $ratedGroup.css("width", percent + "%");
  496. }
  497. function setContainerWidth() {
  498. /*
  499. * Set the width of the `this.node` based on the width of each star and
  500. * the space between them
  501. */
  502. containerWidth = starWidth * options.numStars + spacing * (options.numStars - 1);
  503. percentOfStar = starWidth / containerWidth * 100;
  504. percentOfSpacing = spacing / containerWidth * 100;
  505. $node.width(containerWidth);
  506. showRating();
  507. }
  508. function setStarWidth(newWidth) {
  509. /*
  510. * Set the width and height of each SVG star, called whenever one changes the
  511. * `starWidth` option
  512. */
  513. // The width and height of the star should be the same
  514. var starHeight = options.starWidth = newWidth;
  515. starWidth = window.parseFloat(options.starWidth.replace("px", ""));
  516. $normalGroup.find("svg").attr({
  517. width: options.starWidth,
  518. height: starHeight
  519. });
  520. $ratedGroup.find("svg").attr({
  521. width: options.starWidth,
  522. height: starHeight
  523. });
  524. setContainerWidth();
  525. return $node;
  526. }
  527. function setSpacing(newSpacing) {
  528. /*
  529. * Set spacing between the SVG stars, called whenever one changes
  530. * the `spacing` option
  531. */
  532. options.spacing = newSpacing;
  533. spacing = parseFloat(options.spacing.replace("px", ""));
  534. $normalGroup.find("svg:not(:first-child)").css("margin-left", newSpacing);
  535. $ratedGroup.find("svg:not(:first-child)").css("margin-left", newSpacing);
  536. setContainerWidth();
  537. return $node;
  538. }
  539. function setNormalFill(newFill) {
  540. /*
  541. * Set the background fill of the Stars, called whenever one changes the
  542. * `normalFill` option
  543. */
  544. options.normalFill = newFill;
  545. var $svgs = (options.rtl ? $ratedGroup : $normalGroup).find("svg");
  546. $svgs.attr({
  547. fill: options.normalFill
  548. });
  549. return $node;
  550. }
  551. /*
  552. * Store the recent `ratedFill` option in a variable
  553. * so that if multiColor is unset, we can use the perviously set `ratedFill`
  554. * from this variable
  555. */
  556. var ratedFill = options.ratedFill;
  557. function setRatedFill(newFill) {
  558. /*
  559. * Set ratedFill of the stars, called when one changes the `ratedFill` option
  560. */
  561. /*
  562. * If `multiColor` option is set, `newFill` variable is dynamically set
  563. * based on the rating, what ever set as parameter will be discarded
  564. */
  565. if (options.multiColor) {
  566. var ratingDiff = currentRating - minValue,
  567. percentCovered = ratingDiff / options.maxValue * 100;
  568. var colorOpts = options.multiColor || {},
  569. startColor = colorOpts.startColor || MULTICOLOR_OPTIONS.startColor,
  570. endColor = colorOpts.endColor || MULTICOLOR_OPTIONS.endColor;
  571. newFill = getColor(startColor, endColor, percentCovered);
  572. } else {
  573. ratedFill = newFill;
  574. }
  575. options.ratedFill = newFill;
  576. var $svgs = (options.rtl ? $normalGroup : $ratedGroup).find("svg");
  577. $svgs.attr({
  578. fill: options.ratedFill
  579. });
  580. return $node;
  581. }
  582. function setRtl(newValue) {
  583. newValue = !!newValue;
  584. options.rtl = newValue;
  585. setNormalFill(options.normalFill);
  586. showRating();
  587. }
  588. function setMultiColor(colorOptions) {
  589. /*
  590. * called whenever one changes the `multiColor` option
  591. */
  592. options.multiColor = colorOptions; // set the recently set `ratedFill` option, if multiColor Options are unset
  593. setRatedFill(colorOptions ? colorOptions : ratedFill);
  594. }
  595. function setNumStars(newValue) {
  596. /*
  597. * Set the number of stars to use to display the rating, called whenever one
  598. * changes the `numStars` option
  599. */
  600. options.numStars = newValue;
  601. step = options.maxValue / options.numStars;
  602. $normalGroup.empty();
  603. $ratedGroup.empty();
  604. for (var i = 0; i < options.numStars; i++) {
  605. $(options.starSvg || BASICSTAR).appendTo($normalGroup);
  606. $(options.starSvg || BASICSTAR).appendTo($ratedGroup);
  607. }
  608. setStarWidth(options.starWidth);
  609. setNormalFill(options.normalFill);
  610. setSpacing(options.spacing);
  611. showRating();
  612. return $node;
  613. }
  614. function setMaxValue(newValue) {
  615. /*
  616. * set the Maximum Value of rating to be allowed, called whenever
  617. * one changes the `maxValue` option
  618. */
  619. options.maxValue = newValue;
  620. step = options.maxValue / options.numStars;
  621. if (options.rating > newValue) {
  622. setRating(newValue);
  623. }
  624. showRating();
  625. return $node;
  626. }
  627. function setPrecision(newValue) {
  628. /*
  629. * Set the precision of the rating value, called if one changes the
  630. * `precision` option
  631. */
  632. options.precision = newValue;
  633. setRating(options.rating);
  634. return $node;
  635. }
  636. function setHalfStar(newValue) {
  637. /*
  638. * This function will be called if one changes the `halfStar` option
  639. */
  640. options.halfStar = newValue;
  641. return $node;
  642. }
  643. function setFullStar(newValue) {
  644. /*
  645. * This function will be called if one changes the `fullStar` option
  646. */
  647. options.fullStar = newValue;
  648. return $node;
  649. }
  650. function round(value) {
  651. /*
  652. * Rounds the value of rating if `halfStar` or `fullStar` options are chosen
  653. */
  654. var remainder = value % step,
  655. halfStep = step / 2,
  656. isHalfStar = options.halfStar,
  657. isFullStar = options.fullStar;
  658. if (!isFullStar && !isHalfStar) {
  659. return value;
  660. }
  661. if (isFullStar || isHalfStar && remainder > halfStep) {
  662. value += step - remainder;
  663. } else {
  664. value = value - remainder;
  665. if (remainder > 0) {
  666. value += halfStep;
  667. }
  668. }
  669. return value;
  670. }
  671. function calculateRating(e) {
  672. /*
  673. * Calculates and returns the rating based on the position of cursor w.r.t the
  674. * plugin container
  675. */
  676. var position = $normalGroup.offset(),
  677. nodeStartX = position.left,
  678. nodeEndX = nodeStartX + $normalGroup.width();
  679. var maxValue = options.maxValue; // The x-coordinate(position) of the mouse pointer w.r.t page
  680. var pageX = e.pageX;
  681. var calculatedRating = 0; // If the mouse pointer is to the left of the container
  682. if (pageX < nodeStartX) {
  683. calculatedRating = minValue;
  684. } else if (pageX > nodeEndX) {
  685. // If the mouse pointer is right of the container
  686. calculatedRating = maxValue;
  687. } else {
  688. // If the mouse pointer is inside the continer
  689. /*
  690. * The fraction of width covered by the pointer w.r.t to the total width
  691. * of the container.
  692. */
  693. var calcPrcnt = (pageX - nodeStartX) / (nodeEndX - nodeStartX);
  694. if (spacing > 0) {
  695. /*
  696. * If there is spacing between stars, take the percentage of width covered
  697. * and subtract the percentage of width covered by stars and spacing, to find
  698. * how many stars are covered, the number of stars covered is the rating
  699. *
  700. * TODO: I strongly feel that this logic can be improved!, Please help!
  701. */
  702. calcPrcnt *= 100;
  703. var remPrcnt = calcPrcnt;
  704. while (remPrcnt > 0) {
  705. if (remPrcnt > percentOfStar) {
  706. calculatedRating += step;
  707. remPrcnt -= percentOfStar + percentOfSpacing;
  708. } else {
  709. calculatedRating += remPrcnt / percentOfStar * step;
  710. remPrcnt = 0;
  711. }
  712. }
  713. } else {
  714. /*
  715. * If there is not spacing between stars, the fraction of width covered per
  716. * `maxValue` is the rating
  717. */
  718. calculatedRating = calcPrcnt * options.maxValue;
  719. } // Round the rating if `halfStar` or `fullStar` options are chosen
  720. calculatedRating = round(calculatedRating);
  721. }
  722. if (options.rtl) {
  723. calculatedRating = maxValue - calculatedRating;
  724. }
  725. return parseFloat(calculatedRating);
  726. }
  727. function setReadOnly(newValue) {
  728. /*
  729. * UnBinds mouse event handlers, called when whenever one changes the
  730. * `readOnly` option
  731. */
  732. options.readOnly = newValue;
  733. $node.attr({
  734. "readonly": true
  735. });
  736. unbindEvents();
  737. if (!newValue) {
  738. $node.removeAttr("readonly");
  739. bindEvents();
  740. }
  741. return $node;
  742. }
  743. function setRating(newValue) {
  744. /*
  745. * Sets the rating of the Plugin, Called when option `rating` is changed
  746. * or, when `rating` method is called
  747. */
  748. var rating = newValue;
  749. var maxValue = options.maxValue;
  750. if (typeof rating === "string") {
  751. // If rating is given in percentage, maxValue should be 100
  752. if (rating[rating.length - 1] === "%") {
  753. rating = rating.substr(0, rating.length - 1);
  754. maxValue = 100;
  755. setMaxValue(maxValue);
  756. }
  757. rating = parseFloat(rating);
  758. }
  759. checkBounds(rating, minValue, maxValue);
  760. rating = parseFloat(rating.toFixed(options.precision));
  761. checkPrecision(parseFloat(rating), minValue, maxValue);
  762. options.rating = rating;
  763. showRating();
  764. if (isInitialized) {
  765. $node.trigger("rateyo.set", {
  766. rating: rating
  767. });
  768. }
  769. return $node;
  770. }
  771. function setOnInit(method) {
  772. /*
  773. * set what method to be called on Initialization
  774. */
  775. options.onInit = method;
  776. return $node;
  777. }
  778. function setOnSet(method) {
  779. /*
  780. * set what method to be called when rating is set
  781. */
  782. options.onSet = method;
  783. return $node;
  784. }
  785. function setOnChange(method) {
  786. /*
  787. * set what method to be called rating in the UI is changed
  788. */
  789. options.onChange = method;
  790. return $node;
  791. }
  792. this.rating = function (newValue) {
  793. /*
  794. * rating getter/setter
  795. */
  796. if (!isDefined(newValue)) {
  797. return options.rating;
  798. }
  799. setRating(newValue);
  800. return $node;
  801. };
  802. this.destroy = function () {
  803. /*
  804. * Removes the Rating UI by clearing the content, and removing the custom classes
  805. */
  806. if (!options.readOnly) {
  807. unbindEvents();
  808. }
  809. instanceMap["delete"](node);
  810. $node.removeClass("jq-ry-container").children().remove();
  811. return $node;
  812. };
  813. this.method = function (methodName) {
  814. /*
  815. * Method to call the methods of RateYo Instance
  816. */
  817. if (!methodName) {
  818. throw Error("Method name not specified!");
  819. }
  820. if (!isDefined(this[methodName])) {
  821. throw Error("Method " + methodName + " doesn't exist!");
  822. }
  823. var args = Array.prototype.slice.apply(arguments, []),
  824. params = args.slice(1),
  825. method = this[methodName];
  826. return method.apply(this, params);
  827. };
  828. this.option = function (optionName, param) {
  829. /*
  830. * Method to get/set Options
  831. */
  832. if (!isDefined(optionName)) {
  833. return options;
  834. }
  835. var method;
  836. switch (optionName) {
  837. case "starWidth":
  838. method = setStarWidth;
  839. break;
  840. case "numStars":
  841. method = setNumStars;
  842. break;
  843. case "normalFill":
  844. method = setNormalFill;
  845. break;
  846. case "ratedFill":
  847. method = setRatedFill;
  848. break;
  849. case "multiColor":
  850. method = setMultiColor;
  851. break;
  852. case "maxValue":
  853. method = setMaxValue;
  854. break;
  855. case "precision":
  856. method = setPrecision;
  857. break;
  858. case "rating":
  859. method = setRating;
  860. break;
  861. case "halfStar":
  862. method = setHalfStar;
  863. break;
  864. case "fullStar":
  865. method = setFullStar;
  866. break;
  867. case "readOnly":
  868. method = setReadOnly;
  869. break;
  870. case "spacing":
  871. method = setSpacing;
  872. break;
  873. case "rtl":
  874. method = setRtl;
  875. break;
  876. case "onInit":
  877. method = setOnInit;
  878. break;
  879. case "onSet":
  880. method = setOnSet;
  881. break;
  882. case "onChange":
  883. method = setOnChange;
  884. break;
  885. default:
  886. throw Error("No such option as " + optionName);
  887. }
  888. return isDefined(param) ? method(param) : options[optionName];
  889. };
  890. function onMouseEnter(e) {
  891. if (!options.hover) {
  892. return;
  893. }
  894. /*
  895. * If the Mouse Pointer is inside the container, calculate and show the rating
  896. * in UI
  897. */
  898. var rating = calculateRating(e).toFixed(options.precision);
  899. var maxValue = options.maxValue;
  900. rating = checkPrecision(parseFloat(rating), minValue, maxValue);
  901. showRating(rating);
  902. $node.trigger("rateyo.change", {
  903. rating: rating
  904. });
  905. }
  906. function onMouseLeave() {
  907. if (isMobileBrowser() || !options.hover) {
  908. return;
  909. }
  910. /*
  911. * If mouse leaves, revert the rating in UI to previously set rating,
  912. * when empty value is passed to showRating, it will take the previously set
  913. * rating
  914. */
  915. showRating();
  916. $node.trigger("rateyo.change", {
  917. rating: options.rating
  918. });
  919. }
  920. function onMouseClick(e) {
  921. /*
  922. * On clicking the mouse inside the container, calculate and set the rating
  923. */
  924. var resultantRating = calculateRating(e).toFixed(options.precision);
  925. resultantRating = parseFloat(resultantRating);
  926. that.rating(resultantRating);
  927. }
  928. function onInit(e, data) {
  929. if (options.onInit && typeof options.onInit === "function") {
  930. options.onInit.apply(this, [data.rating, that]);
  931. }
  932. }
  933. function onChange(e, data) {
  934. if (options.onChange && typeof options.onChange === "function") {
  935. options.onChange.apply(this, [data.rating, that]);
  936. }
  937. }
  938. function onSet(e, data) {
  939. if (options.onSet && typeof options.onSet === "function") {
  940. options.onSet.apply(this, [data.rating, that]);
  941. }
  942. }
  943. function bindEvents() {
  944. $node.on("mousemove", onMouseEnter).on("mouseenter", onMouseEnter).on("mouseleave", onMouseLeave).on("click", onMouseClick).on("rateyo.init", onInit).on("rateyo.change", onChange).on("rateyo.set", onSet);
  945. }
  946. function unbindEvents() {
  947. $node.off("mousemove", onMouseEnter).off("mouseenter", onMouseEnter).off("mouseleave", onMouseLeave).off("click", onMouseClick).off("rateyo.init", onInit).off("rateyo.change", onChange).off("rateyo.set", onSet);
  948. }
  949. setNumStars(options.numStars);
  950. setReadOnly(options.readOnly);
  951. if (options.rtl) {
  952. setRtl(options.rtl);
  953. }
  954. instanceMap.set(node, this);
  955. this.rating(options.rating, true);
  956. isInitialized = true;
  957. $node.trigger("rateyo.init", {
  958. rating: options.rating
  959. });
  960. }
  961. Object.defineProperty(RateYo.prototype, "on", {
  962. value: function on(eventName, handler) {
  963. $(this.node).on(eventName, handler);
  964. return this;
  965. }
  966. });
  967. Object.defineProperty(RateYo.prototype, "off", {
  968. value: function off(eventName, handler) {
  969. $(this.node).off(eventName, handler);
  970. return this;
  971. }
  972. });
  973. Object.defineProperty(RateYo, "has", {
  974. value: function has(node) {
  975. return instanceMap.has(node);
  976. }
  977. });
  978. Object.defineProperty(RateYo, "get", {
  979. value: function get(node) {
  980. return instanceMap.get(node);
  981. }
  982. });
  983. Object.defineProperty(RateYo, "_$", {
  984. get: function get() {
  985. return $;
  986. }
  987. });
  988. var dollar = RateYo._$;
  989. var eventsTrigger = dollar.El.prototype.trigger;
  990. dollar.El.prototype.trigger = function overrideTrigger() {
  991. var _$;
  992. for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
  993. args[_key] = arguments[_key];
  994. }
  995. eventsTrigger.apply(this, args); // seems like jquery .on() does not handle custom events
  996. // also handlers registered using .addEventListener will not
  997. // be called when event is triggered using .trigger()
  998. // triggering with jquery .trigger() again for those handlers
  999. // registered using
  1000. (_$ = $$1(this.node)).trigger.apply(_$, args);
  1001. };
  1002. function _rateYo(options) {
  1003. /* jshint validthis:true */
  1004. var $nodes = $$1(this);
  1005. if ($nodes.length === 0) {
  1006. return $nodes;
  1007. }
  1008. var args = Array.prototype.slice.apply(arguments, []);
  1009. if (args.length === 0) {
  1010. //If args length is 0, Initialize the UI with default settings
  1011. options = args[0] = {};
  1012. } else if (args.length === 1 && _typeof(args[0]) === "object") {
  1013. //If an Object is specified as first argument, it is considered as options
  1014. options = args[0];
  1015. } else if (args.length >= 1 && typeof args[0] === "string") {
  1016. /*
  1017. * if there is only one argument, and if its a string, it is supposed to be a
  1018. * method name, if more than one argument is specified, the remaining arguments
  1019. * except the first argument, will be passed as a params to the specified method
  1020. */
  1021. var methodName = args[0],
  1022. params = args.slice(1);
  1023. var result = [];
  1024. $$1.each($nodes, function (i, node) {
  1025. var existingInstance = RateYo.get(node);
  1026. if (!existingInstance) {
  1027. throw Error("Trying to set options before even initialization");
  1028. }
  1029. var method = existingInstance[methodName];
  1030. if (!method) {
  1031. throw Error("Method " + methodName + " does not exist!");
  1032. }
  1033. var returnVal = method.apply(existingInstance, params);
  1034. result.push(returnVal);
  1035. });
  1036. /*
  1037. * If the plugin in being called on only one jQuery Element, return only the
  1038. * first value, to support chaining.
  1039. */
  1040. result = result.length === 1 ? result[0] : result;
  1041. return result;
  1042. } else {
  1043. throw Error("Invalid Arguments");
  1044. }
  1045. return $$1.each($nodes, function () {
  1046. return new RateYo(this, $$1.extend({}, options));
  1047. });
  1048. }
  1049. function rateYo() {
  1050. /* jshint validthis:true */
  1051. return _rateYo.apply(this, Array.prototype.slice.apply(arguments, []));
  1052. }
  1053. $$1.fn.rateYo = rateYo;