1137 lines
29 KiB
JavaScript
1137 lines
29 KiB
JavaScript
/*****
|
|
* rateYo - v2.3.2
|
|
* http://prrashi.github.io/rateyo/
|
|
* Copyright (c) 2014 Prashanth Pamidi; Licensed MIT
|
|
*****/
|
|
|
|
;(function ($) {
|
|
"use strict";
|
|
|
|
// The basic svg string required to generate stars
|
|
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
|
|
var DEFAULTS = {
|
|
|
|
starWidth : "32px",
|
|
normalFill: "gray",
|
|
ratedFill : "#f39c12",
|
|
numStars : 5,
|
|
maxValue : 5,
|
|
precision : 1,
|
|
rating : 0,
|
|
fullStar : false,
|
|
halfStar : false,
|
|
readOnly : false,
|
|
spacing : "0px",
|
|
rtl : false,
|
|
multiColor: null,
|
|
onInit : null,
|
|
onChange : null,
|
|
onSet : null,
|
|
starSvg : null
|
|
};
|
|
|
|
//Default colors for multi-color rating
|
|
var MULTICOLOR_OPTIONS = {
|
|
|
|
startColor: "#c0392b", //red
|
|
endColor : "#f1c40f" //yellow
|
|
};
|
|
|
|
// http://stackoverflow.com/questions/11381673/detecting-a-mobile-browser
|
|
function isMobileBrowser () {
|
|
var check = false;
|
|
/* jshint ignore:start */
|
|
(function(a){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})(navigator.userAgent||navigator.vendor||window.opera);
|
|
/* jshint ignore:end */
|
|
return check;
|
|
}
|
|
|
|
function checkPrecision (value, minValue, maxValue) {
|
|
|
|
/*
|
|
* This function removes the unnecessary precision, at Min and Max Values
|
|
*/
|
|
|
|
// Its like comparing 0.0 with 0, which is true
|
|
if (value === minValue) {
|
|
|
|
value = minValue;
|
|
}
|
|
else if(value === maxValue) {
|
|
|
|
value = maxValue;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
function checkBounds (value, minValue, maxValue) {
|
|
|
|
/*
|
|
* Check if the value is between min and max values, if not, throw an error
|
|
*/
|
|
|
|
var isValid = value >= minValue && value <= maxValue;
|
|
|
|
if(!isValid){
|
|
|
|
throw Error("Invalid Rating, expected value between "+ minValue +
|
|
" and " + maxValue);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
function isDefined(value) {
|
|
|
|
// Better way to check if a variable is defined or not
|
|
return typeof value !== "undefined";
|
|
}
|
|
|
|
// Regex to match Colors in Hex Format like #FF00FF
|
|
var hexRegex = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i;
|
|
|
|
var hexToRGB = function (hex) {
|
|
|
|
/*
|
|
* Extracts and returns the Red, Blue, Green Channel values,
|
|
* in the form of decimals
|
|
*/
|
|
|
|
if (!hexRegex.test(hex)) {
|
|
|
|
return null;
|
|
}
|
|
|
|
var hexValues = hexRegex.exec(hex),
|
|
r = parseInt(hexValues[1], 16),
|
|
g = parseInt(hexValues[2], 16),
|
|
b = parseInt(hexValues[3], 16);
|
|
|
|
return {r:r, g:g, b:b};
|
|
};
|
|
|
|
function getChannelValue(startVal, endVal, percent) {
|
|
|
|
/*
|
|
* Returns a value between `startVal` and `endVal` based on the percent
|
|
*/
|
|
|
|
var newVal = (endVal - startVal)*(percent/100);
|
|
|
|
newVal = Math.round(startVal + newVal).toString(16);
|
|
|
|
if (newVal.length === 1) {
|
|
|
|
newVal = "0" + newVal;
|
|
}
|
|
|
|
return newVal;
|
|
}
|
|
|
|
function getColor (startColor, endColor, percent) {
|
|
|
|
/*
|
|
* Given the percentage( `percent` ) of `endColor` to be mixed
|
|
* with the `startColor`, returns the mixed color.
|
|
* Colors should be only in Hex Format
|
|
*/
|
|
|
|
if (!startColor || !endColor) {
|
|
|
|
return null;
|
|
}
|
|
|
|
percent = isDefined(percent)? percent : 0;
|
|
|
|
startColor = hexToRGB(startColor);
|
|
endColor = hexToRGB(endColor);
|
|
|
|
var r = getChannelValue(startColor.r, endColor.r, percent),
|
|
b = getChannelValue(startColor.b, endColor.b, percent),
|
|
g = getChannelValue(startColor.g, endColor.g, percent);
|
|
|
|
return "#" + r + g + b;
|
|
}
|
|
|
|
function RateYo ($node, options) {
|
|
|
|
/*
|
|
* The Contructor, whose instances are used by plugin itself
|
|
*/
|
|
|
|
// Storing the HTML element as a property, for future access
|
|
this.node = $node.get(0);
|
|
|
|
var that = this;
|
|
|
|
// Remove any stuff that is present inside the container, and add the plugin class
|
|
$node.empty().addClass("jq-ry-container");
|
|
|
|
/*
|
|
* Basically the plugin displays the rating using two rows of stars lying one above
|
|
* the other, the row that is on the top represents the actual rating, and the one
|
|
* behind acts just like a background.
|
|
*
|
|
* `$groupWrapper`: is an element that wraps both the rows
|
|
* `$normalGroup`: is the container for row of stars thats behind and
|
|
* acts as background
|
|
* `$ratedGroup`: is the container for row of stars that display the actual rating.
|
|
*
|
|
* The rating is displayed by adjusting the width of `$ratedGroup`
|
|
*/
|
|
var $groupWrapper = $("<div/>").addClass("jq-ry-group-wrapper")
|
|
.appendTo($node);
|
|
|
|
var $normalGroup = $("<div/>").addClass("jq-ry-normal-group")
|
|
.addClass("jq-ry-group")
|
|
.appendTo($groupWrapper);
|
|
|
|
var $ratedGroup = $("<div/>").addClass("jq-ry-rated-group")
|
|
.addClass("jq-ry-group")
|
|
.appendTo($groupWrapper);
|
|
|
|
/*
|
|
* Variable `step`: store the value of the rating for each star
|
|
* eg: if `maxValue` is 5 and `numStars` is 5, value of each star
|
|
* is 1.
|
|
* Variable `starWidth`: stores the decimal value of width of star in units of px
|
|
* Variable `percentOfStar`: stores the percentage of width each star takes w.r.t
|
|
* the container
|
|
* Variable `spacing`: stores the decimal value of the spacing between stars
|
|
* in the units of px
|
|
* Variable `percentOfSpacing`: stores the percentage of width of the spacing
|
|
* between stars w.r.t the container
|
|
*/
|
|
var step, starWidth, percentOfStar, spacing,
|
|
percentOfSpacing, containerWidth, minValue = 0;
|
|
|
|
/*
|
|
* `currentRating` contains rating that is being displayed at the latest point of
|
|
* time.
|
|
*
|
|
* When ever you hover over the plugin UI, the rating value changes
|
|
* according to the place where you point the cursor, currentRating contains
|
|
* the current value of rating that is being shown in the UI
|
|
*/
|
|
var currentRating = options.rating;
|
|
|
|
// A flag to store if the plugin is already being displayed in the UI
|
|
var isInitialized = false;
|
|
|
|
function showRating (ratingVal) {
|
|
|
|
/*
|
|
* The function is responsible for displaying the rating by changing
|
|
* the width of `$ratedGroup`
|
|
*/
|
|
|
|
if (!isDefined(ratingVal)) {
|
|
|
|
ratingVal = options.rating;
|
|
}
|
|
|
|
// Storing the value that is being shown in `currentRating`.
|
|
currentRating = ratingVal;
|
|
|
|
var numStarsToShow = ratingVal/step;
|
|
|
|
// calculating the percentage of width of $ratedGroup with respect to its parent
|
|
var percent = numStarsToShow*percentOfStar;
|
|
|
|
if (numStarsToShow > 1) {
|
|
|
|
// adding the percentage of space that is taken by the gap the stars
|
|
percent += (Math.ceil(numStarsToShow) - 1)*percentOfSpacing;
|
|
}
|
|
|
|
setRatedFill(options.ratedFill);
|
|
|
|
percent = options.rtl ? 100 - percent : percent;
|
|
|
|
if (percent < 0) {
|
|
|
|
percent = 0;
|
|
} else if (percent > 100) {
|
|
|
|
percent = 100;
|
|
}
|
|
|
|
$ratedGroup.css("width", percent + "%");
|
|
}
|
|
|
|
function setContainerWidth () {
|
|
|
|
/*
|
|
* Set the width of the `this.node` based on the width of each star and
|
|
* the space between them
|
|
*/
|
|
|
|
containerWidth = starWidth*options.numStars + spacing*(options.numStars - 1);
|
|
|
|
percentOfStar = (starWidth/containerWidth)*100;
|
|
|
|
percentOfSpacing = (spacing/containerWidth)*100;
|
|
|
|
$node.width(containerWidth);
|
|
|
|
showRating();
|
|
}
|
|
|
|
function setStarWidth (newWidth) {
|
|
|
|
/*
|
|
* Set the width and height of each SVG star, called whenever one changes the
|
|
* `starWidth` option
|
|
*/
|
|
|
|
// The width and height of the star should be the same
|
|
var starHeight = options.starWidth = newWidth;
|
|
|
|
starWidth = window.parseFloat(options.starWidth.replace("px", ""));
|
|
|
|
$normalGroup.find("svg")
|
|
.attr({width : options.starWidth,
|
|
height: starHeight});
|
|
|
|
$ratedGroup.find("svg")
|
|
.attr({width : options.starWidth,
|
|
height: starHeight});
|
|
|
|
setContainerWidth();
|
|
|
|
return $node;
|
|
}
|
|
|
|
function setSpacing (newSpacing) {
|
|
|
|
/*
|
|
* Set spacing between the SVG stars, called whenever one changes
|
|
* the `spacing` option
|
|
*/
|
|
|
|
options.spacing = newSpacing;
|
|
|
|
spacing = parseFloat(options.spacing.replace("px", ""));
|
|
|
|
$normalGroup.find("svg:not(:first-child)")
|
|
.css({"margin-left": newSpacing});
|
|
|
|
$ratedGroup.find("svg:not(:first-child)")
|
|
.css({"margin-left": newSpacing});
|
|
|
|
setContainerWidth();
|
|
|
|
return $node;
|
|
}
|
|
|
|
function setNormalFill (newFill) {
|
|
|
|
/*
|
|
* Set the background fill of the Stars, called whenever one changes the
|
|
* `normalFill` option
|
|
*/
|
|
|
|
options.normalFill = newFill;
|
|
|
|
var $svgs = (options.rtl ? $ratedGroup : $normalGroup).find("svg");
|
|
|
|
$svgs.attr({fill: options.normalFill});
|
|
|
|
return $node;
|
|
}
|
|
|
|
/*
|
|
* Store the recent `ratedFill` option in a variable
|
|
* so that if multiColor is unset, we can use the perviously set `ratedFill`
|
|
* from this variable
|
|
*/
|
|
var ratedFill = options.ratedFill;
|
|
|
|
function setRatedFill (newFill) {
|
|
|
|
/*
|
|
* Set ratedFill of the stars, called when one changes the `ratedFill` option
|
|
*/
|
|
|
|
/*
|
|
* If `multiColor` option is set, `newFill` variable is dynamically set
|
|
* based on the rating, what ever set as parameter will be discarded
|
|
*/
|
|
if (options.multiColor) {
|
|
|
|
var ratingDiff = currentRating - minValue,
|
|
percentCovered = (ratingDiff/options.maxValue)*100;
|
|
|
|
var colorOpts = options.multiColor || {},
|
|
startColor = colorOpts.startColor || MULTICOLOR_OPTIONS.startColor,
|
|
endColor = colorOpts.endColor || MULTICOLOR_OPTIONS.endColor;
|
|
|
|
newFill = getColor(startColor, endColor, percentCovered);
|
|
} else {
|
|
|
|
ratedFill = newFill;
|
|
}
|
|
|
|
options.ratedFill = newFill;
|
|
|
|
var $svgs = (options.rtl ? $normalGroup : $ratedGroup).find("svg");
|
|
|
|
$svgs.attr({fill: options.ratedFill});
|
|
|
|
return $node;
|
|
}
|
|
|
|
function setRtl (newValue) {
|
|
|
|
newValue = !!newValue;
|
|
|
|
options.rtl = newValue;
|
|
|
|
setNormalFill(options.normalFill);
|
|
showRating();
|
|
}
|
|
|
|
function setMultiColor (colorOptions) {
|
|
|
|
/*
|
|
* called whenever one changes the `multiColor` option
|
|
*/
|
|
|
|
options.multiColor = colorOptions;
|
|
|
|
// set the recently set `ratedFill` option, if multiColor Options are unset
|
|
setRatedFill(colorOptions ? colorOptions : ratedFill);
|
|
}
|
|
|
|
function setNumStars (newValue) {
|
|
|
|
/*
|
|
* Set the number of stars to use to display the rating, called whenever one
|
|
* changes the `numStars` option
|
|
*/
|
|
|
|
options.numStars = newValue;
|
|
|
|
step = options.maxValue/options.numStars;
|
|
|
|
$normalGroup.empty();
|
|
$ratedGroup.empty();
|
|
|
|
for (var i=0; i<options.numStars; i++) {
|
|
|
|
$normalGroup.append($(options.starSvg || BASICSTAR));
|
|
$ratedGroup.append($(options.starSvg || BASICSTAR));
|
|
}
|
|
|
|
setStarWidth(options.starWidth);
|
|
setNormalFill(options.normalFill);
|
|
setSpacing(options.spacing);
|
|
|
|
showRating();
|
|
|
|
return $node;
|
|
}
|
|
|
|
function setMaxValue (newValue) {
|
|
|
|
/*
|
|
* set the Maximum Value of rating to be allowed, called whenever
|
|
* one changes the `maxValue` option
|
|
*/
|
|
|
|
options.maxValue = newValue;
|
|
|
|
step = options.maxValue/options.numStars;
|
|
|
|
if (options.rating > newValue) {
|
|
|
|
setRating(newValue);
|
|
}
|
|
|
|
showRating();
|
|
|
|
return $node;
|
|
}
|
|
|
|
function setPrecision (newValue) {
|
|
|
|
/*
|
|
* Set the precision of the rating value, called if one changes the
|
|
* `precision` option
|
|
*/
|
|
|
|
options.precision = newValue;
|
|
|
|
setRating(options.rating);
|
|
|
|
return $node;
|
|
}
|
|
|
|
function setHalfStar (newValue) {
|
|
|
|
/*
|
|
* This function will be called if one changes the `halfStar` option
|
|
*/
|
|
|
|
options.halfStar = newValue;
|
|
|
|
return $node;
|
|
}
|
|
|
|
function setFullStar (newValue) {
|
|
|
|
/*
|
|
* This function will be called if one changes the `fullStar` option
|
|
*/
|
|
|
|
options.fullStar = newValue;
|
|
|
|
return $node;
|
|
}
|
|
|
|
function round (value) {
|
|
|
|
/*
|
|
* Rounds the value of rating if `halfStar` or `fullStar` options are chosen
|
|
*/
|
|
|
|
var remainder = value%step,
|
|
halfStep = step/2,
|
|
isHalfStar = options.halfStar,
|
|
isFullStar = options.fullStar;
|
|
|
|
if (!isFullStar && !isHalfStar) {
|
|
|
|
return value;
|
|
}
|
|
|
|
if (isFullStar || (isHalfStar && remainder > halfStep)) {
|
|
|
|
value += step - remainder;
|
|
} else {
|
|
|
|
value = value - remainder;
|
|
|
|
if (remainder > 0) {
|
|
|
|
value += halfStep;
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
function calculateRating (e) {
|
|
|
|
/*
|
|
* Calculates and returns the rating based on the position of cursor w.r.t the
|
|
* plugin container
|
|
*/
|
|
|
|
var position = $normalGroup.offset(),
|
|
nodeStartX = position.left,
|
|
nodeEndX = nodeStartX + $normalGroup.width();
|
|
|
|
var maxValue = options.maxValue;
|
|
|
|
// The x-coordinate(position) of the mouse pointer w.r.t page
|
|
var pageX = e.pageX;
|
|
|
|
var calculatedRating = 0;
|
|
|
|
// If the mouse pointer is to the left of the container
|
|
if(pageX < nodeStartX) {
|
|
|
|
calculatedRating = minValue;
|
|
}else if (pageX > nodeEndX) { // If the mouse pointer is right of the container
|
|
|
|
calculatedRating = maxValue;
|
|
}else { // If the mouse pointer is inside the continer
|
|
|
|
/*
|
|
* The fraction of width covered by the pointer w.r.t to the total width
|
|
* of the container.
|
|
*/
|
|
var calcPrcnt = ((pageX - nodeStartX)/(nodeEndX - nodeStartX));
|
|
|
|
if (spacing > 0) {
|
|
|
|
/*
|
|
* If there is spacing between stars, take the percentage of width covered
|
|
* and subtract the percentage of width covered by stars and spacing, to find
|
|
* how many stars are covered, the number of stars covered is the rating
|
|
*
|
|
* TODO: I strongly feel that this logic can be improved!, Please help!
|
|
*/
|
|
calcPrcnt *= 100;
|
|
|
|
var remPrcnt = calcPrcnt;
|
|
|
|
while (remPrcnt > 0) {
|
|
|
|
if (remPrcnt > percentOfStar) {
|
|
|
|
calculatedRating += step;
|
|
remPrcnt -= (percentOfStar + percentOfSpacing);
|
|
} else {
|
|
|
|
calculatedRating += remPrcnt/percentOfStar*step;
|
|
remPrcnt = 0;
|
|
}
|
|
}
|
|
} else {
|
|
|
|
/*
|
|
* If there is not spacing between stars, the fraction of width covered per
|
|
* `maxValue` is the rating
|
|
*/
|
|
calculatedRating = calcPrcnt * (options.maxValue);
|
|
}
|
|
|
|
// Round the rating if `halfStar` or `fullStar` options are chosen
|
|
calculatedRating = round(calculatedRating);
|
|
}
|
|
|
|
if (options.rtl) {
|
|
|
|
calculatedRating = maxValue - calculatedRating;
|
|
}
|
|
|
|
return parseFloat(calculatedRating);
|
|
}
|
|
|
|
function setReadOnly (newValue) {
|
|
|
|
/*
|
|
* UnBinds mouse event handlers, called when whenever one changes the
|
|
* `readOnly` option
|
|
*/
|
|
|
|
options.readOnly = newValue;
|
|
|
|
$node.attr("readonly", true);
|
|
|
|
unbindEvents();
|
|
|
|
if (!newValue) {
|
|
|
|
$node.removeAttr("readonly");
|
|
|
|
bindEvents();
|
|
}
|
|
|
|
return $node;
|
|
}
|
|
|
|
function setRating (newValue) {
|
|
|
|
/*
|
|
* Sets the rating of the Plugin, Called when option `rating` is changed
|
|
* or, when `rating` method is called
|
|
*/
|
|
|
|
var rating = newValue;
|
|
|
|
var maxValue = options.maxValue;
|
|
|
|
if (typeof rating === "string") {
|
|
|
|
// If rating is given in percentage, maxValue should be 100
|
|
if (rating[rating.length - 1] === "%") {
|
|
|
|
rating = rating.substr(0, rating.length - 1);
|
|
maxValue = 100;
|
|
|
|
setMaxValue(maxValue);
|
|
}
|
|
|
|
rating = parseFloat(rating);
|
|
}
|
|
|
|
checkBounds(rating, minValue, maxValue);
|
|
|
|
rating = parseFloat(rating.toFixed(options.precision));
|
|
|
|
checkPrecision(parseFloat(rating), minValue, maxValue);
|
|
|
|
options.rating = rating;
|
|
|
|
showRating();
|
|
|
|
if (isInitialized) {
|
|
|
|
$node.trigger("rateyo.set", {rating: rating});
|
|
}
|
|
|
|
return $node;
|
|
}
|
|
|
|
function setOnInit (method) {
|
|
|
|
/*
|
|
* set what method to be called on Initialization
|
|
*/
|
|
|
|
options.onInit = method;
|
|
|
|
return $node;
|
|
}
|
|
|
|
function setOnSet (method) {
|
|
|
|
/*
|
|
* set what method to be called when rating is set
|
|
*/
|
|
|
|
options.onSet = method;
|
|
|
|
return $node;
|
|
}
|
|
|
|
function setOnChange (method) {
|
|
|
|
/*
|
|
* set what method to be called rating in the UI is changed
|
|
*/
|
|
|
|
options.onChange = method;
|
|
|
|
return $node;
|
|
}
|
|
|
|
this.rating = function (newValue) {
|
|
|
|
/*
|
|
* rating getter/setter
|
|
*/
|
|
|
|
if (!isDefined(newValue)) {
|
|
|
|
return options.rating;
|
|
}
|
|
|
|
setRating(newValue);
|
|
|
|
return $node;
|
|
};
|
|
|
|
this.destroy = function () {
|
|
|
|
/*
|
|
* Removes the Rating UI by clearing the content, and removing the custom classes
|
|
*/
|
|
|
|
if (!options.readOnly) {
|
|
|
|
unbindEvents();
|
|
}
|
|
|
|
RateYo.prototype.collection = deleteInstance($node.get(0),
|
|
this.collection);
|
|
|
|
$node.removeClass("jq-ry-container").children().remove();
|
|
|
|
return $node;
|
|
};
|
|
|
|
this.method = function (methodName) {
|
|
|
|
/*
|
|
* Method to call the methods of RateYo Instance
|
|
*/
|
|
|
|
if (!methodName) {
|
|
|
|
throw Error("Method name not specified!");
|
|
}
|
|
|
|
if (!isDefined(this[methodName])) {
|
|
|
|
throw Error("Method " + methodName + " doesn't exist!");
|
|
}
|
|
|
|
var args = Array.prototype.slice.apply(arguments, []),
|
|
params = args.slice(1),
|
|
method = this[methodName];
|
|
|
|
return method.apply(this, params);
|
|
};
|
|
|
|
this.option = function (optionName, param) {
|
|
|
|
/*
|
|
* Method to get/set Options
|
|
*/
|
|
|
|
if (!isDefined(optionName)) {
|
|
|
|
return options;
|
|
}
|
|
|
|
var method;
|
|
|
|
switch (optionName) {
|
|
|
|
case "starWidth":
|
|
|
|
method = setStarWidth;
|
|
break;
|
|
case "numStars":
|
|
|
|
method = setNumStars;
|
|
break;
|
|
case "normalFill":
|
|
|
|
method = setNormalFill;
|
|
break;
|
|
case "ratedFill":
|
|
|
|
method = setRatedFill;
|
|
break;
|
|
case "multiColor":
|
|
|
|
method = setMultiColor;
|
|
break;
|
|
case "maxValue":
|
|
|
|
method = setMaxValue;
|
|
break;
|
|
case "precision":
|
|
|
|
method = setPrecision;
|
|
break;
|
|
case "rating":
|
|
|
|
method = setRating;
|
|
break;
|
|
case "halfStar":
|
|
|
|
method = setHalfStar;
|
|
break;
|
|
case "fullStar":
|
|
|
|
method = setFullStar;
|
|
break;
|
|
case "readOnly":
|
|
|
|
method = setReadOnly;
|
|
break;
|
|
case "spacing":
|
|
|
|
method = setSpacing;
|
|
break;
|
|
case "rtl":
|
|
|
|
method = setRtl;
|
|
break;
|
|
case "onInit":
|
|
|
|
method = setOnInit;
|
|
break;
|
|
case "onSet":
|
|
|
|
method = setOnSet;
|
|
break;
|
|
case "onChange":
|
|
|
|
method = setOnChange;
|
|
break;
|
|
default:
|
|
|
|
throw Error("No such option as " + optionName);
|
|
}
|
|
|
|
return isDefined(param) ? method(param) : options[optionName];
|
|
};
|
|
|
|
function onMouseEnter (e) {
|
|
|
|
/*
|
|
* If the Mouse Pointer is inside the container, calculate and show the rating
|
|
* in UI
|
|
*/
|
|
|
|
var rating = calculateRating(e).toFixed(options.precision);
|
|
|
|
var maxValue = options.maxValue;
|
|
|
|
rating = checkPrecision(parseFloat(rating), minValue, maxValue);
|
|
|
|
showRating(rating);
|
|
|
|
$node.trigger("rateyo.change", {rating: rating});
|
|
}
|
|
|
|
function onMouseLeave () {
|
|
if (isMobileBrowser()) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If mouse leaves, revert the rating in UI to previously set rating,
|
|
* when empty value is passed to showRating, it will take the previously set
|
|
* rating
|
|
*/
|
|
|
|
showRating();
|
|
|
|
$node.trigger("rateyo.change", {rating: options.rating});
|
|
}
|
|
|
|
function onMouseClick (e) {
|
|
|
|
/*
|
|
* On clicking the mouse inside the container, calculate and set the rating
|
|
*/
|
|
|
|
var resultantRating = calculateRating(e).toFixed(options.precision);
|
|
resultantRating = parseFloat(resultantRating);
|
|
|
|
that.rating(resultantRating);
|
|
}
|
|
|
|
function onInit(e, data) {
|
|
|
|
if(options.onInit && typeof options.onInit === "function") {
|
|
|
|
/* jshint validthis:true */
|
|
options.onInit.apply(this, [data.rating, that]);
|
|
}
|
|
}
|
|
|
|
function onChange (e, data) {
|
|
|
|
if(options.onChange && typeof options.onChange === "function") {
|
|
|
|
/* jshint validthis:true */
|
|
options.onChange.apply(this, [data.rating, that]);
|
|
}
|
|
}
|
|
|
|
function onSet (e, data) {
|
|
|
|
if(options.onSet && typeof options.onSet === "function") {
|
|
|
|
/* jshint validthis:true */
|
|
options.onSet.apply(this, [data.rating, that]);
|
|
}
|
|
}
|
|
|
|
function bindEvents () {
|
|
|
|
$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);
|
|
}
|
|
|
|
function unbindEvents () {
|
|
|
|
$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);
|
|
}
|
|
|
|
setNumStars(options.numStars);
|
|
setReadOnly(options.readOnly);
|
|
|
|
if (options.rtl) {
|
|
|
|
setRtl(options.rtl);
|
|
}
|
|
|
|
this.collection.push(this);
|
|
this.rating(options.rating, true);
|
|
|
|
isInitialized = true;
|
|
$node.trigger("rateyo.init", {rating: options.rating});
|
|
}
|
|
|
|
RateYo.prototype.collection = [];
|
|
|
|
function getInstance (node, collection) {
|
|
|
|
/*
|
|
* Given a HTML element (node) and a collection of RateYo instances,
|
|
* this function will search through the collection and return the matched
|
|
* instance having the node
|
|
*/
|
|
|
|
var instance;
|
|
|
|
$.each(collection, function () {
|
|
|
|
if(node === this.node){
|
|
|
|
instance = this;
|
|
return false;
|
|
}
|
|
});
|
|
|
|
return instance;
|
|
}
|
|
|
|
function deleteInstance (node, collection) {
|
|
|
|
/*
|
|
* Given a HTML element (node) and a collection of RateYo instances,
|
|
* this function will search through the collection and delete the
|
|
* instance having the node, and return the modified collection
|
|
*/
|
|
|
|
$.each(collection, function (index) {
|
|
|
|
if (node === this.node) {
|
|
|
|
var firstPart = collection.slice(0, index),
|
|
secondPart = collection.slice(index+1, collection.length);
|
|
|
|
collection = firstPart.concat(secondPart);
|
|
|
|
return false;
|
|
}
|
|
});
|
|
|
|
return collection;
|
|
}
|
|
|
|
function _rateYo (options) {
|
|
|
|
var rateYoInstances = RateYo.prototype.collection;
|
|
|
|
/* jshint validthis:true */
|
|
var $nodes = $(this);
|
|
|
|
if($nodes.length === 0) {
|
|
|
|
return $nodes;
|
|
}
|
|
|
|
var args = Array.prototype.slice.apply(arguments, []);
|
|
|
|
if (args.length === 0) {
|
|
|
|
//If args length is 0, Initialize the UI with default settings
|
|
options = args[0] = {};
|
|
}else if (args.length === 1 && typeof args[0] === "object") {
|
|
|
|
//If an Object is specified as first argument, it is considered as options
|
|
options = args[0];
|
|
}else if (args.length >= 1 && typeof args[0] === "string") {
|
|
|
|
/*
|
|
* if there is only one argument, and if its a string, it is supposed to be a
|
|
* method name, if more than one argument is specified, the remaining arguments
|
|
* except the first argument, will be passed as a params to the specified method
|
|
*/
|
|
|
|
var methodName = args[0],
|
|
params = args.slice(1);
|
|
|
|
var result = [];
|
|
|
|
$.each($nodes, function (i, node) {
|
|
|
|
var existingInstance = getInstance(node, rateYoInstances);
|
|
|
|
if(!existingInstance) {
|
|
|
|
throw Error("Trying to set options before even initialization");
|
|
}
|
|
|
|
var method = existingInstance[methodName];
|
|
|
|
if (!method) {
|
|
|
|
throw Error("Method " + methodName + " does not exist!");
|
|
}
|
|
|
|
var returnVal = method.apply(existingInstance, params);
|
|
|
|
result.push(returnVal);
|
|
});
|
|
|
|
/*
|
|
* If the plugin in being called on only one jQuery Element, return only the
|
|
* first value, to support chaining.
|
|
*/
|
|
result = result.length === 1? result[0]: result;
|
|
|
|
return result;
|
|
}else {
|
|
|
|
throw Error("Invalid Arguments");
|
|
}
|
|
|
|
/*
|
|
* if only options are passed, extend default options, and if the plugin is not
|
|
* initialized on a particular jQuery element, initalize RateYo on it
|
|
*/
|
|
options = $.extend({}, DEFAULTS, options);
|
|
|
|
return $.each($nodes, function () {
|
|
|
|
var existingInstance = getInstance(this, rateYoInstances);
|
|
|
|
if (existingInstance) {
|
|
|
|
return existingInstance;
|
|
}
|
|
|
|
var $node = $(this),
|
|
dataAttrs = {},
|
|
optionsCopy = $.extend({}, options);
|
|
|
|
$.each($node.data(), function (key, value) {
|
|
|
|
if (key.indexOf("rateyo") !== 0) {
|
|
|
|
return;
|
|
}
|
|
|
|
var optionName = key.replace(/^rateyo/, "");
|
|
|
|
optionName = optionName[0].toLowerCase() + optionName.slice(1);
|
|
|
|
dataAttrs[optionName] = value;
|
|
|
|
delete optionsCopy[optionName];
|
|
});
|
|
|
|
return new RateYo($(this), $.extend({}, dataAttrs, optionsCopy));
|
|
});
|
|
}
|
|
|
|
function rateYo () {
|
|
|
|
/* jshint validthis:true */
|
|
return _rateYo.apply(this, Array.prototype.slice.apply(arguments, []));
|
|
}
|
|
|
|
window.RateYo = RateYo;
|
|
$.fn.rateYo = rateYo;
|
|
|
|
}(window.jQuery));
|