Source: d:/Users/cjolif/sdk/sdk-utils/deliteful/StarRating.js

/** @module deliteful/StarRating */
define([
	"requirejs-dplugins/has",
	"dpointer/events",
	"dojo/keys",
	"dojo/dom-class",
	"delite/register",
	"delite/FormValueWidget",
	"requirejs-dplugins/has!bidi?./StarRating/bidi/StarRating",
	"requirejs-dplugins/i18n!./StarRating/nls/StarRating",
	"delite/uacss", // to use dedicated CSS styles in IE9
	"delite/theme!./StarRating/themes/{{theme}}/StarRating.css",
	"requirejs-dplugins/has!bidi?delite/theme!./StarRating/themes/{{theme}}/StarRating_rtl.css"
], function (has, pointer, keys, domClass,
			register, FormValueWidget, BidiStarRating, messages) {

	/**
	 * A widget that displays a rating, usually with stars, and that allows setting a different rating value
	 * by touching the stars.
	 * Its custom element tag is `d-star-rating`.
	 * 
	 * See the {@link https://github.com/ibm-js/deliteful/tree/master/docs/StarRating.md user documentation}
	 * for more details.
	 * 
	 * @class module:deliteful/StarRating
	 * @augments delite/FormValueWidget
	 */
	var StarRating = register.dcl([FormValueWidget], /** @lends module:deliteful/StarRating# */ {
		/**
		 * The name of the CSS class of this widget.
		 * @member {string}
		 */
		baseClass: "d-star-rating",

		/**
		 * The maximum rating, that is also the number of stars to show.
		 * @member {number}
		 */
		max: 5,

		/**
		 * The current value of the Rating.
		 * @member {number}
		 */
		value: 0,

		/**
		 * Indicates whether the user is allowed to edit half values (0.5, 1.5, ...) or not.
		 * Ignored if readOnly is set to false.
		 * @member {boolean}
		 */
		editHalfValues: false,

		/**
		 * Indicates whether the user is allowed to set the value to zero or not.
		 * @member {boolean}
		 */
		allowZero: true,

		/* internal properties */

		/*=====
		_zeroAreaWidth: 1,
		_enterValue: null,
		_hoveredValue: null,
		_startHandles: null,
		_keyDownHandle: null,
		=====*/
		_hovering: false,
		_otherEventsHandles: [],
		_incrementKeyCodes: [keys.RIGHT_ARROW, keys.UP_ARROW, keys.NUMPAD_PLUS], // keys to press to increment value
		_decrementKeyCodes: [keys.LEFT_ARROW, keys.DOWN_ARROW, keys.NUMPAD_MINUS], // keys to press to decrement value

		buildRendering: function () {
			this.focusNode = this.ownerDocument.createElement("div");
			this.appendChild(this.focusNode);
			pointer.setTouchAction(this, "none");
			// init WAI-ARIA attributes
			this.focusNode.setAttribute("role", "slider");
			this.focusNode.setAttribute("aria-valuemin", 0);
		},

		createdCallback: register.after(function () {
			var inputs = this.getElementsByTagName("INPUT");
			if (inputs.length) {
				this.valueNode = inputs[0];
				if (!isNaN(parseFloat(this.valueNode.value))) {
					this.value = this.valueNode.value;
				}
				this.valueNode.style.display = "none";
			} else {
				this.valueNode = this.ownerDocument.createElement("input");
				this.valueNode.style.display = "none";
				this.appendChild(this.valueNode);
			}
			["disabled", "max", "value", "name", "readOnly", "allowZero"].forEach(function (prop) {
				this.notifyCurrentValue(prop);
			}, this);
		}),

		/* jshint maxcomplexity: 13 */
		refreshRendering: function (props) {
			if ("disabled" in props) {
				domClass.toggle(this, this.baseClass + "-disabled", this.disabled);
			}
			if ("max" in props) {
				this.focusNode.setAttribute("aria-valuemax", this.max);
			}
			if ("max" in props || "value" in props) {
				this._refreshStarsRendering();
			}
			if ("value" in props) {
				this.focusNode.setAttribute("aria-valuenow", this.value);
				this.focusNode.setAttribute("aria-valuetext",
						messages["aria-valuetext"].replace("${value}", this.value));
				this.valueNode.value = this.value;
			}
			if ("name" in props && this.name) {
				this.valueNode.name = this.name;
			}
			if ("readOnly" in props || "disabled" in props) {
				this._refreshEditionEventHandlers();
			}
			if ("readOnly" in props || "disabled" in props || "allowZero" in props) {
				this._updateZeroArea();
			}
		},
		/* jshint maxcomplexity: 10 */

		_refreshStarsRendering: function () {
			var createChildren = this.focusNode.children.length - 1 !== 2 * this.max;
			if (createChildren) {
				this.focusNode.innerHTML = "";
			}
			this._updateStars(this.value, createChildren);
		},

		_refreshEditionEventHandlers: function () {
			var passive = this.disabled || this.readOnly;
			if (!passive && !this._keyDownHandle) {
				this._keyDownHandle = this.on("keydown", this._keyDownHandler.bind(this));
			} else if (passive && this._keyDownHandle) {
				this._keyDownHandle.remove();
				this._keyDownHandle = null;
			}
			if (!passive && !this._startHandles) {
				this._startHandles = [this.on("pointerover", this._pointerOverHandler.bind(this)),
									  this.on("pointerdown", this._wireHandlers.bind(this))];
			} else if (passive && this._startHandles) {
				while (this._startHandles.length) {
					this._startHandles.pop().remove();
				}
				this._startHandles = null;
			}
		},

		_removeEventsHandlers: function () {
			while (this._otherEventsHandles.length) {
				this._otherEventsHandles.pop().remove();
			}
		},

		_wireHandlers: function () {
			if (!this._otherEventsHandles.length) {
				this._otherEventsHandles.push(this.on("pointerup", this._pointerUpHandler.bind(this)));
				this._otherEventsHandles.push(this.on("pointerleave", this._pointerLeaveHandler.bind(this)));
				this._otherEventsHandles.push(this.on("pointercancel", this._pointerLeaveHandler.bind(this)));
			}
		},

		_pointerOverHandler: function (/*Event*/ event) {
			this._wireHandlers();
			if (!this._hovering && event.pointerType === "mouse") {
				this._hovering = true;
				domClass.add(this, this.baseClass + "-hovered");
			}
			var newValue = event.target.value;
			if (newValue !== undefined) {
				if (this._hovering) {
					if (newValue !== this._hoveredValue) {
						domClass.add(this, this.baseClass + "-hovered");
						this._updateStars(newValue, false);
						this._hoveredValue = newValue;
					}
				} else {
					// Set the previous value here, as this handler is called before _onFocus
					this._previousOnChangeValue = this.value;
					this.handleOnChange(newValue);
				}
			}
		},

		_pointerUpHandler: function (/*Event*/ event) {
			var value = event.target.value;
			if (value !== undefined) {
				this.handleOnChange(value);
			}
			if (!this._hovering) {
				this._removeEventsHandlers();
			} else {
				domClass.remove(this, this.baseClass + "-hovered");
			}
		},

		/*jshint unused:vars */
		_pointerLeaveHandler: function (/*Event*/ event) {
			if (this._hovering) {
				this._hovering = false;
				this._hoveredValue = null;
				domClass.remove(this, this.baseClass + "-hovered");
				this._updateStars(this.value, false);
			}
			this._removeEventsHandlers();
		},

		_keyDownHandler: function (/*Event*/ event) {
			if (this._incrementKeyCodes.indexOf(event.keyCode) !== -1) {
				event.preventDefault();
				this._incrementValue();
			} else if (this._decrementKeyCodes.indexOf(event.keyCode) !== -1) {
				event.preventDefault();
				this._decrementValue();
			}
		},

		_incrementValue: function () {
			if (this.value < this.max) {
				this.value = this.value + (this.editHalfValues ? 0.5 : 1);
			}
		},

		_decrementValue: function () {
			if (this.value > (this.allowZero ? 0 : (this.editHalfValues ? 0.5 : 1))) {
				this.value = this.value - (this.editHalfValues ? 0.5 : 1);
			}
		},

		_updateStars: function (/*Number*/value, /*Boolean*/create) {
			var stars = this.focusNode.querySelectorAll("div");
			if (create) {
				this._zeroSettingArea = this.ownerDocument.createElement("div");
				this._zeroSettingArea.className = this.baseClass + "-zero";
				this._zeroSettingArea.value = 0;
				this.focusNode.appendChild(this._zeroSettingArea);
				this._updateZeroArea();
			}
			for (var i = 0; i < 2 * this.max; i++) {
				var starClass = this.baseClass + (i % 2 ? "-end " : "-start ");
				if ((i + 1) * 0.5 <= value) {
					starClass += this.baseClass + "-full";
				} else {
					starClass += this.baseClass + "-empty";
				}
				if (create) {
					var parent = this.ownerDocument.createElement("div");
					parent.value = this.editHalfValues ? (i + 1) / 2 : Math.ceil((i + 1) / 2);
					this.focusNode.appendChild(parent);
				} else {
					parent = stars[i + 1];
				}
				parent.className = this.baseClass + "-star-icon " + starClass;
			}
		},

		_updateZeroArea: function () {
			if (this.readOnly || !this.allowZero) {
				domClass.add(this._zeroSettingArea, "d-hidden");
				this._zeroAreaWidth = 0;
			} else {
				domClass.remove(this._zeroSettingArea, "d-hidden");
				delete this._zeroAreaWidth;
			}
		}
	});

	return register("d-star-rating",
			has("bidi") ? [HTMLElement, StarRating, BidiStarRating] : [HTMLElement, StarRating]);
});