rateyo.js 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165
  1. function _typeof(obj) {
  2. "@babel/helpers - typeof";
  3. if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
  4. _typeof = function (obj) {
  5. return typeof obj;
  6. };
  7. } else {
  8. _typeof = function (obj) {
  9. return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
  10. };
  11. }
  12. return _typeof(obj);
  13. }
  14. function _defineProperty(obj, key, value) {
  15. if (key in obj) {
  16. Object.defineProperty(obj, key, {
  17. value: value,
  18. enumerable: true,
  19. configurable: true,
  20. writable: true
  21. });
  22. } else {
  23. obj[key] = value;
  24. }
  25. return obj;
  26. }
  27. function ownKeys(object, enumerableOnly) {
  28. var keys = Object.keys(object);
  29. if (Object.getOwnPropertySymbols) {
  30. var symbols = Object.getOwnPropertySymbols(object);
  31. if (enumerableOnly) symbols = symbols.filter(function (sym) {
  32. return Object.getOwnPropertyDescriptor(object, sym).enumerable;
  33. });
  34. keys.push.apply(keys, symbols);
  35. }
  36. return keys;
  37. }
  38. function _objectSpread2(target) {
  39. for (var i = 1; i < arguments.length; i++) {
  40. var source = arguments[i] != null ? arguments[i] : {};
  41. if (i % 2) {
  42. ownKeys(Object(source), true).forEach(function (key) {
  43. _defineProperty(target, key, source[key]);
  44. });
  45. } else if (Object.getOwnPropertyDescriptors) {
  46. Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
  47. } else {
  48. ownKeys(Object(source)).forEach(function (key) {
  49. Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
  50. });
  51. }
  52. }
  53. return target;
  54. }
  55. // The basic svg string required to generate stars
  56. 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
  57. var DEFAULTS = {
  58. starWidth: "32px",
  59. normalFill: "gray",
  60. ratedFill: "#f39c12",
  61. numStars: 5,
  62. maxValue: 5,
  63. precision: 1,
  64. rating: 0,
  65. fullStar: false,
  66. halfStar: false,
  67. hover: true,
  68. readOnly: false,
  69. spacing: "0px",
  70. rtl: false,
  71. multiColor: null,
  72. onInit: null,
  73. onChange: null,
  74. onSet: null,
  75. starSvg: null
  76. }; //Default colors for multi-color rating
  77. var MULTICOLOR_OPTIONS = {
  78. startColor: "#c0392b",
  79. //red
  80. endColor: "#f1c40f" //yellow
  81. };
  82. // http://stackoverflow.com/questions/11381673/detecting-a-mobile-browser
  83. function isMobileBrowser() {
  84. var check = false;
  85. (function (a) {
  86. 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;
  87. })(navigator.userAgent || navigator.vendor || window.opera);
  88. return check;
  89. }
  90. function checkPrecision(value, minValue, maxValue) {
  91. /*
  92. * This function removes the unnecessary precision, at Min and Max Values
  93. */
  94. // Its like comparing 0.0 with 0, which is true
  95. if (value === minValue) {
  96. value = minValue;
  97. } else if (value === maxValue) {
  98. value = maxValue;
  99. }
  100. return value;
  101. }
  102. function checkBounds(value, minValue, maxValue) {
  103. /*
  104. * Check if the value is between min and max values, if not, throw an error
  105. */
  106. var isValid = value >= minValue && value <= maxValue;
  107. if (!isValid) {
  108. throw Error("Invalid Rating, expected value between " + minValue + " and " + maxValue);
  109. }
  110. return value;
  111. }
  112. function isType(value, type) {
  113. return _typeof(value) === type;
  114. }
  115. function isDefined(value) {
  116. // Better way to check if a variable is defined or not
  117. return typeof value !== "undefined";
  118. }
  119. var isNumber = function isNumber(input) {
  120. return isType(input, "number");
  121. };
  122. var isString = function isString(input) {
  123. return isType(input, "string");
  124. };
  125. var isFunction = function isFunction(input) {
  126. return isType(input, "function");
  127. };
  128. var hexRegex = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i;
  129. function hexToRGB(hex) {
  130. /*
  131. * Extracts and returns the Red, Blue, Green Channel values,
  132. * in the form of decimals
  133. */
  134. if (!hexRegex.test(hex)) {
  135. return null;
  136. }
  137. var hexValues = hexRegex.exec(hex),
  138. r = parseInt(hexValues[1], 16),
  139. g = parseInt(hexValues[2], 16),
  140. b = parseInt(hexValues[3], 16);
  141. return {
  142. r: r,
  143. g: g,
  144. b: b
  145. };
  146. }
  147. function getChannelValue(startVal, endVal, percent) {
  148. /*
  149. * Returns a value between `startVal` and `endVal` based on the percent
  150. */
  151. var newVal = (endVal - startVal) * (percent / 100);
  152. newVal = Math.round(startVal + newVal).toString(16);
  153. if (newVal.length === 1) {
  154. newVal = "0" + newVal;
  155. }
  156. return newVal;
  157. }
  158. function getColor(startColor, endColor, percent) {
  159. /*
  160. * Given the percentage( `percent` ) of `endColor` to be mixed
  161. * with the `startColor`, returns the mixed color.
  162. * Colors should be only in Hex Format
  163. */
  164. if (!startColor || !endColor) {
  165. return null;
  166. }
  167. percent = isDefined(percent) ? percent : 0;
  168. startColor = hexToRGB(startColor);
  169. endColor = hexToRGB(endColor);
  170. var r = getChannelValue(startColor.r, endColor.r, percent),
  171. b = getChannelValue(startColor.b, endColor.b, percent),
  172. g = getChannelValue(startColor.g, endColor.g, percent);
  173. return "#" + r + g + b;
  174. }
  175. var eventObjectMap = {};
  176. function getEventObject(event) {
  177. return eventObjectMap[event] || (eventObjectMap[event] = new String(event));
  178. }
  179. var handlerProxyMap = new WeakMap();
  180. function proxy(node, fn, event) {
  181. event = getEventObject(event);
  182. var eventHandlerMap = handlerProxyMap.get(node);
  183. if (!eventHandlerMap) {
  184. handlerProxyMap.set(node, eventHandlerMap = new WeakMap());
  185. }
  186. var handlerMap = eventHandlerMap.get(event);
  187. if (!handlerMap) {
  188. eventHandlerMap.set(event, handlerMap = new Map());
  189. }
  190. var handler = handlerMap.get(fn);
  191. if (handler) {
  192. return handler;
  193. }
  194. function proxy(e) {
  195. var data = e.detail;
  196. fn.call(node, e, data);
  197. }
  198. handlerMap.set(fn, proxy);
  199. return proxy;
  200. }
  201. proxy.get = function getOriginalFunction(node, fn, event) {
  202. event = getEventObject(event);
  203. var eventHandlerMap = handlerProxyMap.get(node);
  204. if (!eventHandlerMap) {
  205. return fn;
  206. }
  207. var handlerMap = eventHandlerMap.get(event);
  208. if (!handlerMap) {
  209. return fn;
  210. }
  211. return handlerMap.get(fn) || fn;
  212. };
  213. var Event = isFunction(window.Event) ? window.Event : function Event(event) {
  214. var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  215. var _params$bubbles = params.bubbles,
  216. bubbles = _params$bubbles === void 0 ? false : _params$bubbles,
  217. _params$cancelable = params.cancelable,
  218. cancelable = _params$cancelable === void 0 ? false : _params$cancelable;
  219. var evt = document.createEvent("Event");
  220. evt.initEvent(event, bubbles, cancelable);
  221. return evt;
  222. };
  223. var CustomEvent = isFunction(window.CustomEvent) ? window.CustomEvent : (CustomEvent.prototype = Object.create(Event.prototype), CustomEvent);
  224. var events = {
  225. trigger: function trigger(event, detail) {
  226. var eventProps = {
  227. bubbles: true
  228. };
  229. if (!isDefined(detail)) {
  230. this.node.dispatchEvent(new Event(event, eventProps));
  231. } else {
  232. this.node.dispatchEvent(new CustomEvent(event, _objectSpread2({
  233. detail: detail
  234. }, eventProps)));
  235. }
  236. return this;
  237. },
  238. on: function on(event, handler) {
  239. this.node.addEventListener(event, proxy(this.node, handler, event));
  240. return this;
  241. },
  242. off: function off(event, handler) {
  243. this.node.removeEventListener(event, proxy.get(this.node, handler, event));
  244. return this;
  245. }
  246. };
  247. var rateyoAttrRegex = /^rateyo(.+)$/;
  248. function classList(node, operation, input) {
  249. var className = node.className.trim();
  250. var classes = className && className.split(/\s/) || [],
  251. classMap = {};
  252. classes = classes.reduce(function (result, item, index) {
  253. if (!classMap.hasOwnProperty(item)) {
  254. result.push(item);
  255. classMap[item] = index;
  256. }
  257. return result;
  258. }, []);
  259. if (operation === classList.add) {
  260. if (classMap.hasOwnProperty(input)) {
  261. return;
  262. }
  263. classes.push(input);
  264. } else if (operation === classList.remove) {
  265. if (!classMap.hasOwnProperty(input)) {
  266. return;
  267. }
  268. classes.splice(classMap[input], 1);
  269. }
  270. node.className = classes.join(" ");
  271. }
  272. classList.add = "add";
  273. classList.remove = "remove";
  274. function El(node) {
  275. this.node = node;
  276. }
  277. El.prototype = {
  278. empty: function empty() {
  279. this.node.innerHTML = "";
  280. return this;
  281. },
  282. addClass: function addClass(className) {
  283. classList(this.node, classList.add, className);
  284. return this;
  285. },
  286. removeClass: function removeClass(className) {
  287. classList(this.node, classList.remove, className);
  288. return this;
  289. },
  290. appendTo: function appendTo(parent) {
  291. (El.isEl(parent) ? parent.node : parent).appendChild(this.node);
  292. return this;
  293. },
  294. css: function css(styleAttribute, value) {
  295. this.node.style[styleAttribute] = value;
  296. return this;
  297. },
  298. width: function width(_width) {
  299. if (!isDefined(_width)) {
  300. return this.node.getBoundingClientRect().width;
  301. }
  302. this.css("width", _width + (isNumber(_width) ? "px" : ""));
  303. },
  304. find: function find(selector) {
  305. return $(this.node.querySelectorAll(selector));
  306. },
  307. attr: function attr(attrObj) {
  308. for (var attrName in attrObj) {
  309. if (attrObj.hasOwnProperty(attrName)) {
  310. this.node.setAttribute(attrName, attrObj[attrName]);
  311. }
  312. }
  313. return this;
  314. },
  315. removeAttr: function removeAttr(attributeName) {
  316. this.node.removeAttribute(attributeName);
  317. return this;
  318. },
  319. children: function children() {
  320. return $(this.node.childNodes);
  321. },
  322. remove: function remove() {
  323. return this.node.remove();
  324. },
  325. offset: function offset() {
  326. var _this$node$getBoundin = this.node.getBoundingClientRect(),
  327. top = _this$node$getBoundin.top,
  328. left = _this$node$getBoundin.left,
  329. bottom = _this$node$getBoundin.bottom,
  330. right = _this$node$getBoundin.right;
  331. return {
  332. top: top,
  333. left: left,
  334. bottom: bottom,
  335. right: right
  336. };
  337. },
  338. dataAttrOptions: function dataAttrOptions() {
  339. var dataset = this.node.dataset;
  340. return Object.keys(dataset).reduce(function (options, attr) {
  341. var match = attr.match(rateyoAttrRegex);
  342. if (!match) {
  343. return options;
  344. }
  345. var rateyoAttr = match[1],
  346. option = rateyoAttr[0].toLowerCase() + rateyoAttr.slice(1);
  347. options[option] = dataset[attr];
  348. return options;
  349. }, {});
  350. }
  351. };
  352. El.prototype = _objectSpread2(_objectSpread2({}, El.prototype), events);
  353. El.isEl = function (node) {
  354. return node instanceof El;
  355. };
  356. function Collection(nodeList) {
  357. var _this = this;
  358. this.collection = [];
  359. Array.prototype.forEach.call(nodeList, function (node) {
  360. _this.collection.push(new El(node));
  361. });
  362. }
  363. Collection.isCollection = function (input) {
  364. return input instanceof Collection;
  365. };
  366. Collection.prototype = {};
  367. var _loop = function _loop(method) {
  368. if (!El.prototype.hasOwnProperty(method)) {
  369. return "continue";
  370. }
  371. function proxy() {
  372. for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
  373. args[_key] = arguments[_key];
  374. }
  375. this.collection.forEach(function (el) {
  376. return El.isEl(el) && el[method].apply(el, args);
  377. });
  378. return this;
  379. }
  380. Collection.prototype[method] = proxy;
  381. };
  382. for (var method in El.prototype) {
  383. var _ret = _loop(method);
  384. if (_ret === "continue") continue;
  385. }
  386. var parser = new DOMParser();
  387. function parseHTML(html) {
  388. return parser.parseFromString(html.trim(), "text/html").body.childNodes;
  389. }
  390. function $(node) {
  391. node = isString(node) && parseHTML(node) || node;
  392. if (El.isEl(node) || Collection.isCollection(node)) {
  393. return node;
  394. }
  395. if (node instanceof NodeList) {
  396. return new Collection(node);
  397. }
  398. return new El(node);
  399. }
  400. $.El = El;
  401. var instanceMap = new WeakMap();
  402. function RateYo(node) {
  403. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  404. if (!(this instanceof RateYo)) {
  405. return new RateYo(node, options);
  406. }
  407. if (instanceMap.has(node)) {
  408. return instanceMap.get(node);
  409. }
  410. var that = this;
  411. this.node = node;
  412. var $node = $(node);
  413. options = _objectSpread2(_objectSpread2(_objectSpread2({}, DEFAULTS), options), $node.dataAttrOptions()); // Remove any stuff that is present inside the container, and add the plugin class
  414. $node.empty().addClass("jq-ry-container");
  415. /*
  416. * Basically the plugin displays the rating using two rows of stars lying one above
  417. * the other, the row that is on the top represents the actual rating, and the one
  418. * behind acts just like a background.
  419. *
  420. * `$groupWrapper`: is an element that wraps both the rows
  421. * `$normalGroup`: is the container for row of stars thats behind and
  422. * acts as background
  423. * `$ratedGroup`: is the container for row of stars that display the actual rating.
  424. *
  425. * The rating is displayed by adjusting the width of `$ratedGroup`
  426. */
  427. var $groupWrapper = $(document.createElement("div")).addClass("jq-ry-group-wrapper").appendTo($node);
  428. var $normalGroup = $(document.createElement("div")).addClass("jq-ry-normal-group").addClass("jq-ry-group").appendTo($groupWrapper);
  429. var $ratedGroup = $(document.createElement("div")).addClass("jq-ry-rated-group").addClass("jq-ry-group").appendTo($groupWrapper);
  430. /*
  431. * Variable `step`: store the value of the rating for each star
  432. * eg: if `maxValue` is 5 and `numStars` is 5, value of each star
  433. * is 1.
  434. * Variable `starWidth`: stores the decimal value of width of star in units of px
  435. * Variable `percentOfStar`: stores the percentage of width each star takes w.r.t
  436. * the container
  437. * Variable `spacing`: stores the decimal value of the spacing between stars
  438. * in the units of px
  439. * Variable `percentOfSpacing`: stores the percentage of width of the spacing
  440. * between stars w.r.t the container
  441. */
  442. var step,
  443. starWidth,
  444. percentOfStar,
  445. spacing,
  446. percentOfSpacing,
  447. containerWidth,
  448. minValue = 0;
  449. /*
  450. * `currentRating` contains rating that is being displayed at the latest point of
  451. * time.
  452. *
  453. * When ever you hover over the plugin UI, the rating value changes
  454. * according to the place where you point the cursor, currentRating contains
  455. * the current value of rating that is being shown in the UI
  456. */
  457. var currentRating = options.rating; // A flag to store if the plugin is already being displayed in the UI
  458. var isInitialized = false;
  459. function showRating(ratingVal) {
  460. /*
  461. * The function is responsible for displaying the rating by changing
  462. * the width of `$ratedGroup`
  463. */
  464. if (!isDefined(ratingVal)) {
  465. ratingVal = options.rating;
  466. } // Storing the value that is being shown in `currentRating`.
  467. currentRating = ratingVal;
  468. var numStarsToShow = ratingVal / step; // calculating the percentage of width of $ratedGroup with respect to its parent
  469. var percent = numStarsToShow * percentOfStar;
  470. if (numStarsToShow > 1) {
  471. // adding the percentage of space that is taken by the gap the stars
  472. percent += (Math.ceil(numStarsToShow) - 1) * percentOfSpacing;
  473. }
  474. setRatedFill(options.ratedFill);
  475. percent = options.rtl ? 100 - percent : percent;
  476. if (percent < 0) {
  477. percent = 0;
  478. } else if (percent > 100) {
  479. percent = 100;
  480. }
  481. $ratedGroup.css("width", percent + "%");
  482. }
  483. function setContainerWidth() {
  484. /*
  485. * Set the width of the `this.node` based on the width of each star and
  486. * the space between them
  487. */
  488. containerWidth = starWidth * options.numStars + spacing * (options.numStars - 1);
  489. percentOfStar = starWidth / containerWidth * 100;
  490. percentOfSpacing = spacing / containerWidth * 100;
  491. $node.width(containerWidth);
  492. showRating();
  493. }
  494. function setStarWidth(newWidth) {
  495. /*
  496. * Set the width and height of each SVG star, called whenever one changes the
  497. * `starWidth` option
  498. */
  499. // The width and height of the star should be the same
  500. var starHeight = options.starWidth = newWidth;
  501. starWidth = window.parseFloat(options.starWidth.replace("px", ""));
  502. $normalGroup.find("svg").attr({
  503. width: options.starWidth,
  504. height: starHeight
  505. });
  506. $ratedGroup.find("svg").attr({
  507. width: options.starWidth,
  508. height: starHeight
  509. });
  510. setContainerWidth();
  511. return $node;
  512. }
  513. function setSpacing(newSpacing) {
  514. /*
  515. * Set spacing between the SVG stars, called whenever one changes
  516. * the `spacing` option
  517. */
  518. options.spacing = newSpacing;
  519. spacing = parseFloat(options.spacing.replace("px", ""));
  520. $normalGroup.find("svg:not(:first-child)").css("margin-left", newSpacing);
  521. $ratedGroup.find("svg:not(:first-child)").css("margin-left", newSpacing);
  522. setContainerWidth();
  523. return $node;
  524. }
  525. function setNormalFill(newFill) {
  526. /*
  527. * Set the background fill of the Stars, called whenever one changes the
  528. * `normalFill` option
  529. */
  530. options.normalFill = newFill;
  531. var $svgs = (options.rtl ? $ratedGroup : $normalGroup).find("svg");
  532. $svgs.attr({
  533. fill: options.normalFill
  534. });
  535. return $node;
  536. }
  537. /*
  538. * Store the recent `ratedFill` option in a variable
  539. * so that if multiColor is unset, we can use the perviously set `ratedFill`
  540. * from this variable
  541. */
  542. var ratedFill = options.ratedFill;
  543. function setRatedFill(newFill) {
  544. /*
  545. * Set ratedFill of the stars, called when one changes the `ratedFill` option
  546. */
  547. /*
  548. * If `multiColor` option is set, `newFill` variable is dynamically set
  549. * based on the rating, what ever set as parameter will be discarded
  550. */
  551. if (options.multiColor) {
  552. var ratingDiff = currentRating - minValue,
  553. percentCovered = ratingDiff / options.maxValue * 100;
  554. var colorOpts = options.multiColor || {},
  555. startColor = colorOpts.startColor || MULTICOLOR_OPTIONS.startColor,
  556. endColor = colorOpts.endColor || MULTICOLOR_OPTIONS.endColor;
  557. newFill = getColor(startColor, endColor, percentCovered);
  558. } else {
  559. ratedFill = newFill;
  560. }
  561. options.ratedFill = newFill;
  562. var $svgs = (options.rtl ? $normalGroup : $ratedGroup).find("svg");
  563. $svgs.attr({
  564. fill: options.ratedFill
  565. });
  566. return $node;
  567. }
  568. function setRtl(newValue) {
  569. newValue = !!newValue;
  570. options.rtl = newValue;
  571. setNormalFill(options.normalFill);
  572. showRating();
  573. }
  574. function setMultiColor(colorOptions) {
  575. /*
  576. * called whenever one changes the `multiColor` option
  577. */
  578. options.multiColor = colorOptions; // set the recently set `ratedFill` option, if multiColor Options are unset
  579. setRatedFill(colorOptions ? colorOptions : ratedFill);
  580. }
  581. function setNumStars(newValue) {
  582. /*
  583. * Set the number of stars to use to display the rating, called whenever one
  584. * changes the `numStars` option
  585. */
  586. options.numStars = newValue;
  587. step = options.maxValue / options.numStars;
  588. $normalGroup.empty();
  589. $ratedGroup.empty();
  590. for (var i = 0; i < options.numStars; i++) {
  591. $(options.starSvg || BASICSTAR).appendTo($normalGroup);
  592. $(options.starSvg || BASICSTAR).appendTo($ratedGroup);
  593. }
  594. setStarWidth(options.starWidth);
  595. setNormalFill(options.normalFill);
  596. setSpacing(options.spacing);
  597. showRating();
  598. return $node;
  599. }
  600. function setMaxValue(newValue) {
  601. /*
  602. * set the Maximum Value of rating to be allowed, called whenever
  603. * one changes the `maxValue` option
  604. */
  605. options.maxValue = newValue;
  606. step = options.maxValue / options.numStars;
  607. if (options.rating > newValue) {
  608. setRating(newValue);
  609. }
  610. showRating();
  611. return $node;
  612. }
  613. function setPrecision(newValue) {
  614. /*
  615. * Set the precision of the rating value, called if one changes the
  616. * `precision` option
  617. */
  618. options.precision = newValue;
  619. setRating(options.rating);
  620. return $node;
  621. }
  622. function setHalfStar(newValue) {
  623. /*
  624. * This function will be called if one changes the `halfStar` option
  625. */
  626. options.halfStar = newValue;
  627. return $node;
  628. }
  629. function setFullStar(newValue) {
  630. /*
  631. * This function will be called if one changes the `fullStar` option
  632. */
  633. options.fullStar = newValue;
  634. return $node;
  635. }
  636. function round(value) {
  637. /*
  638. * Rounds the value of rating if `halfStar` or `fullStar` options are chosen
  639. */
  640. var remainder = value % step,
  641. halfStep = step / 2,
  642. isHalfStar = options.halfStar,
  643. isFullStar = options.fullStar;
  644. if (!isFullStar && !isHalfStar) {
  645. return value;
  646. }
  647. if (isFullStar || isHalfStar && remainder > halfStep) {
  648. value += step - remainder;
  649. } else {
  650. value = value - remainder;
  651. if (remainder > 0) {
  652. value += halfStep;
  653. }
  654. }
  655. return value;
  656. }
  657. function calculateRating(e) {
  658. /*
  659. * Calculates and returns the rating based on the position of cursor w.r.t the
  660. * plugin container
  661. */
  662. var position = $normalGroup.offset(),
  663. nodeStartX = position.left,
  664. nodeEndX = nodeStartX + $normalGroup.width();
  665. var maxValue = options.maxValue; // The x-coordinate(position) of the mouse pointer w.r.t page
  666. var pageX = e.pageX;
  667. var calculatedRating = 0; // If the mouse pointer is to the left of the container
  668. if (pageX < nodeStartX) {
  669. calculatedRating = minValue;
  670. } else if (pageX > nodeEndX) {
  671. // If the mouse pointer is right of the container
  672. calculatedRating = maxValue;
  673. } else {
  674. // If the mouse pointer is inside the continer
  675. /*
  676. * The fraction of width covered by the pointer w.r.t to the total width
  677. * of the container.
  678. */
  679. var calcPrcnt = (pageX - nodeStartX) / (nodeEndX - nodeStartX);
  680. if (spacing > 0) {
  681. /*
  682. * If there is spacing between stars, take the percentage of width covered
  683. * and subtract the percentage of width covered by stars and spacing, to find
  684. * how many stars are covered, the number of stars covered is the rating
  685. *
  686. * TODO: I strongly feel that this logic can be improved!, Please help!
  687. */
  688. calcPrcnt *= 100;
  689. var remPrcnt = calcPrcnt;
  690. while (remPrcnt > 0) {
  691. if (remPrcnt > percentOfStar) {
  692. calculatedRating += step;
  693. remPrcnt -= percentOfStar + percentOfSpacing;
  694. } else {
  695. calculatedRating += remPrcnt / percentOfStar * step;
  696. remPrcnt = 0;
  697. }
  698. }
  699. } else {
  700. /*
  701. * If there is not spacing between stars, the fraction of width covered per
  702. * `maxValue` is the rating
  703. */
  704. calculatedRating = calcPrcnt * options.maxValue;
  705. } // Round the rating if `halfStar` or `fullStar` options are chosen
  706. calculatedRating = round(calculatedRating);
  707. }
  708. if (options.rtl) {
  709. calculatedRating = maxValue - calculatedRating;
  710. }
  711. return parseFloat(calculatedRating);
  712. }
  713. function setReadOnly(newValue) {
  714. /*
  715. * UnBinds mouse event handlers, called when whenever one changes the
  716. * `readOnly` option
  717. */
  718. options.readOnly = newValue;
  719. $node.attr({
  720. "readonly": true
  721. });
  722. unbindEvents();
  723. if (!newValue) {
  724. $node.removeAttr("readonly");
  725. bindEvents();
  726. }
  727. return $node;
  728. }
  729. function setRating(newValue) {
  730. /*
  731. * Sets the rating of the Plugin, Called when option `rating` is changed
  732. * or, when `rating` method is called
  733. */
  734. var rating = newValue;
  735. var maxValue = options.maxValue;
  736. if (typeof rating === "string") {
  737. // If rating is given in percentage, maxValue should be 100
  738. if (rating[rating.length - 1] === "%") {
  739. rating = rating.substr(0, rating.length - 1);
  740. maxValue = 100;
  741. setMaxValue(maxValue);
  742. }
  743. rating = parseFloat(rating);
  744. }
  745. checkBounds(rating, minValue, maxValue);
  746. rating = parseFloat(rating.toFixed(options.precision));
  747. checkPrecision(parseFloat(rating), minValue, maxValue);
  748. options.rating = rating;
  749. showRating();
  750. if (isInitialized) {
  751. $node.trigger("rateyo.set", {
  752. rating: rating
  753. });
  754. }
  755. return $node;
  756. }
  757. function setOnInit(method) {
  758. /*
  759. * set what method to be called on Initialization
  760. */
  761. options.onInit = method;
  762. return $node;
  763. }
  764. function setOnSet(method) {
  765. /*
  766. * set what method to be called when rating is set
  767. */
  768. options.onSet = method;
  769. return $node;
  770. }
  771. function setOnChange(method) {
  772. /*
  773. * set what method to be called rating in the UI is changed
  774. */
  775. options.onChange = method;
  776. return $node;
  777. }
  778. this.rating = function (newValue) {
  779. /*
  780. * rating getter/setter
  781. */
  782. if (!isDefined(newValue)) {
  783. return options.rating;
  784. }
  785. setRating(newValue);
  786. return $node;
  787. };
  788. this.destroy = function () {
  789. /*
  790. * Removes the Rating UI by clearing the content, and removing the custom classes
  791. */
  792. if (!options.readOnly) {
  793. unbindEvents();
  794. }
  795. instanceMap["delete"](node);
  796. $node.removeClass("jq-ry-container").children().remove();
  797. return $node;
  798. };
  799. this.method = function (methodName) {
  800. /*
  801. * Method to call the methods of RateYo Instance
  802. */
  803. if (!methodName) {
  804. throw Error("Method name not specified!");
  805. }
  806. if (!isDefined(this[methodName])) {
  807. throw Error("Method " + methodName + " doesn't exist!");
  808. }
  809. var args = Array.prototype.slice.apply(arguments, []),
  810. params = args.slice(1),
  811. method = this[methodName];
  812. return method.apply(this, params);
  813. };
  814. this.option = function (optionName, param) {
  815. /*
  816. * Method to get/set Options
  817. */
  818. if (!isDefined(optionName)) {
  819. return options;
  820. }
  821. var method;
  822. switch (optionName) {
  823. case "starWidth":
  824. method = setStarWidth;
  825. break;
  826. case "numStars":
  827. method = setNumStars;
  828. break;
  829. case "normalFill":
  830. method = setNormalFill;
  831. break;
  832. case "ratedFill":
  833. method = setRatedFill;
  834. break;
  835. case "multiColor":
  836. method = setMultiColor;
  837. break;
  838. case "maxValue":
  839. method = setMaxValue;
  840. break;
  841. case "precision":
  842. method = setPrecision;
  843. break;
  844. case "rating":
  845. method = setRating;
  846. break;
  847. case "halfStar":
  848. method = setHalfStar;
  849. break;
  850. case "fullStar":
  851. method = setFullStar;
  852. break;
  853. case "readOnly":
  854. method = setReadOnly;
  855. break;
  856. case "spacing":
  857. method = setSpacing;
  858. break;
  859. case "rtl":
  860. method = setRtl;
  861. break;
  862. case "onInit":
  863. method = setOnInit;
  864. break;
  865. case "onSet":
  866. method = setOnSet;
  867. break;
  868. case "onChange":
  869. method = setOnChange;
  870. break;
  871. default:
  872. throw Error("No such option as " + optionName);
  873. }
  874. return isDefined(param) ? method(param) : options[optionName];
  875. };
  876. function onMouseEnter(e) {
  877. if (!options.hover) {
  878. return;
  879. }
  880. /*
  881. * If the Mouse Pointer is inside the container, calculate and show the rating
  882. * in UI
  883. */
  884. var rating = calculateRating(e).toFixed(options.precision);
  885. var maxValue = options.maxValue;
  886. rating = checkPrecision(parseFloat(rating), minValue, maxValue);
  887. showRating(rating);
  888. $node.trigger("rateyo.change", {
  889. rating: rating
  890. });
  891. }
  892. function onMouseLeave() {
  893. if (isMobileBrowser() || !options.hover) {
  894. return;
  895. }
  896. /*
  897. * If mouse leaves, revert the rating in UI to previously set rating,
  898. * when empty value is passed to showRating, it will take the previously set
  899. * rating
  900. */
  901. showRating();
  902. $node.trigger("rateyo.change", {
  903. rating: options.rating
  904. });
  905. }
  906. function onMouseClick(e) {
  907. /*
  908. * On clicking the mouse inside the container, calculate and set the rating
  909. */
  910. var resultantRating = calculateRating(e).toFixed(options.precision);
  911. resultantRating = parseFloat(resultantRating);
  912. that.rating(resultantRating);
  913. }
  914. function onInit(e, data) {
  915. if (options.onInit && typeof options.onInit === "function") {
  916. options.onInit.apply(this, [data.rating, that]);
  917. }
  918. }
  919. function onChange(e, data) {
  920. if (options.onChange && typeof options.onChange === "function") {
  921. options.onChange.apply(this, [data.rating, that]);
  922. }
  923. }
  924. function onSet(e, data) {
  925. if (options.onSet && typeof options.onSet === "function") {
  926. options.onSet.apply(this, [data.rating, that]);
  927. }
  928. }
  929. function bindEvents() {
  930. $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);
  931. }
  932. function unbindEvents() {
  933. $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);
  934. }
  935. setNumStars(options.numStars);
  936. setReadOnly(options.readOnly);
  937. if (options.rtl) {
  938. setRtl(options.rtl);
  939. }
  940. instanceMap.set(node, this);
  941. this.rating(options.rating, true);
  942. isInitialized = true;
  943. $node.trigger("rateyo.init", {
  944. rating: options.rating
  945. });
  946. }
  947. Object.defineProperty(RateYo.prototype, "on", {
  948. value: function on(eventName, handler) {
  949. $(this.node).on(eventName, handler);
  950. return this;
  951. }
  952. });
  953. Object.defineProperty(RateYo.prototype, "off", {
  954. value: function off(eventName, handler) {
  955. $(this.node).off(eventName, handler);
  956. return this;
  957. }
  958. });
  959. Object.defineProperty(RateYo, "has", {
  960. value: function has(node) {
  961. return instanceMap.has(node);
  962. }
  963. });
  964. Object.defineProperty(RateYo, "get", {
  965. value: function get(node) {
  966. return instanceMap.get(node);
  967. }
  968. });
  969. Object.defineProperty(RateYo, "_$", {
  970. get: function get() {
  971. return $;
  972. }
  973. });
  974. export default RateYo;