Source: deliteful/Switch.js

/** @module deliteful/Switch */
define([
	"requirejs-dplugins/has",
	"requirejs-dplugins/jquery!attributes/classes",
	"dpointer/events",
	"delite/register",
	"deliteful/Checkbox",
	"delite/handlebars!./Switch/Switch.html",
	"requirejs-dplugins/has!bidi?./Switch/bidi/Switch",
	"delite/theme!./Switch/themes/{{theme}}/Switch.css"
], function (has, $, pointer, register, Checkbox, template, BidiSwitch) {

	/**
	 * A form-aware switch widget that represents a toggle switch with a sliding knob.
	 * @example
	 * <d-switch checkedLabel="ON" uncheckedLabel="OFF" checked="true"></d-switch>
	 * @class module:deliteful/Switch
	 * @augments module:deliteful/Checkbox
	 */
	return register("d-switch", has("bidi") ? [HTMLElement, Checkbox, BidiSwitch] :
		[HTMLElement, Checkbox], /** @lends module:deliteful/Switch# */ {

		/**
		 * The label corresponding to the checked state.
		 * @member {string}
		 * @default ""
		 */
		checkedLabel: "",

		/**
		 * The label corresponding to the unchecked state.
		 * @member {string}
		 * @default ""
		 */
		uncheckedLabel: "",

		/**
		 * The component css base class.
		 * @member {string}
		 * @default "d-switch"
		 */
		baseClass: "d-switch",

		template: template,

		postRender: function () {
			this.on("pointerdown", this._pointerDownHandler.bind(this), this._knobGlassNode);
			this.on("click", this._clickPreventer.bind(this), this._knobGlassNode);
		},

		destroy: function () {
			this._cleanHandlers();
		},

		_clickPreventer: function (e) {
			e.preventDefault();
			e.stopPropagation();
		},

		_pointerDownHandler: function (e) {
			if (!this.disabled) {
				this._startX = this._curX = e.clientX;
				pointer.setPointerCapture(this._knobGlassNode, e.pointerId);
				if (!this._pHandlers) {
					this._pHandlers = [
						{e: "pointermove", l: this._pointerMoveHandler.bind(this)},
						{e: "pointerup", l: this._pointerUpHandler.bind(this)},
						{e: "lostpointercapture", l: this._lostPointerCaptureHandler.bind(this)}
					];
				}
				this._pHandlers.forEach(function (h) { this._knobGlassNode.addEventListener(h.e, h.l); }.bind(this));
				e.preventDefault();
				e.stopPropagation();
			}
		},

		_pointerMoveHandler: function (e) {
			var dx = e.clientX - this._curX,
				cs = window.getComputedStyle(this._pushNode),
				w = parseInt(cs.width, 10);
			if (!this._drag && Math.abs(e.clientX - this._startX) > 4) {
				this._drag = true;
				$(this._innerNode).removeClass("-d-switch-transition");
				$(this._pushNode).removeClass("-d-switch-transition");
				$(this._innerWrapperNode).removeClass("-d-switch-transition");
			}
			this._curX = e.clientX;
			if (this._drag) {
				// knobWidth and switchWidth are sometimes wrong if computed in 
				// attachedCallback on Chrome so do it here
				this._knobWidth = parseInt(window.getComputedStyle(this._knobNode).width, 10);
				this._switchWidth = parseInt(window.getComputedStyle(this).width, 10);
				var nw = this.effectiveDir === "ltr" ? w + dx : w - dx,
					max = this.checked ? this._switchWidth : this._switchWidth - this._knobWidth,
					min = this.checked ? this._knobWidth : 0;
				nw = Math.max(min, Math.min(max, nw));
				this._pushNode.style.width = nw + "px";
			}
			e.preventDefault();
			e.stopPropagation();
		},

		_pointerUpHandler: function (e) {
			var oldCheckedValue = this.checked;
			if (!this._drag) {
				this.checked = !this.checked;
			} else {
				this._drag = false;
				var cs = parseInt(window.getComputedStyle(this._pushNode).width, 10);
				var m = parseInt(window.getComputedStyle(this._pushNode).marginLeft, 10);
				this.checked = cs + m + this._knobWidth / 2 >= this._switchWidth / 2;
			}
			if (this.checked !== oldCheckedValue) {
				this.emit("change");
			}
			e.preventDefault();
			e.stopPropagation();
		},

		_lostPointerCaptureHandler: function () {
			this._cleanHandlers();
			this._drag = false;
			this._pushNode.style.width = "";
			this._innerNode.style.transform = "none";
			$(this._innerNode).addClass("-d-switch-transition");
			$(this._pushNode).addClass("-d-switch-transition");
			$(this._innerWrapperNode).addClass("-d-switch-transition");
		},

		_cleanHandlers: function () {
			this._pHandlers.forEach(function (h) { this._knobGlassNode.removeEventListener(h.e, h.l); }.bind(this));
		}
	});
});