/* * Copyright (C) 2016 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ class Slider extends LayoutNode { constructor(cssClassName = "") { super(`
`); this._container = new LayoutNode(`
`); this._track = new LayoutNode(`
`); this._primaryFill = new LayoutNode(`
`); this._secondaryFill = new LayoutNode(`
`); this._knob = new LayoutNode(`
`); this._container.children = [this._track, this._primaryFill, this._secondaryFill, this._knob]; this._input = new LayoutNode(``); this._input.element.addEventListener(GestureRecognizer.SupportsTouches ? "touchstart" : "mousedown", this); this._input.element.addEventListener("input", this); this._input.element.addEventListener("change", this); this.value = 0; this.height = 16; this.enabled = true; this.isActive = false; this._secondaryValue = 0; this._disabled = false; this.children = [this._container, this._input]; } // Public set inputAccessibleLabel(timeValue) { this._input.element.setAttribute("aria-valuetext", formattedStringForDuration(timeValue)); } get disabled() { return this._disabled; } set disabled(flag) { if (this._disabled === flag) return; this._disabled = flag; this.markDirtyProperty("disabled"); } get value() { if (this._value !== undefined) return this._value; return parseFloat(this._input.element.value); } set value(value) { if (this.isActive) return; this._value = value; this.markDirtyProperty("value"); this.needsLayout = true; } get secondaryValue() { return this._secondaryValue; } set secondaryValue(secondaryValue) { if (this._secondaryValue === secondaryValue) return; this._secondaryValue = secondaryValue; this.needsLayout = true; } // Protected handleEvent(event) { switch (event.type) { case "mousedown": this._handleMousedownEvent(); break; case "touchstart": this._handleTouchstartEvent(event); break; case "mouseup": this._handleMouseupEvent(); break; case "touchend": this._handleTouchendEvent(event); break; case "change": case "input": this._valueDidChange(); break; } } commitProperty(propertyName) { switch (propertyName) { case "value": this._input.element.value = this._value; delete this._value; break; case "disabled": this.element.classList.toggle("disabled", this._disabled); break; default : super.commitProperty(propertyName); break; } } commit() { super.commit(); const scrubberRadius = 4.5; const scrubberCenterX = scrubberRadius + Math.round((this.width - (scrubberRadius * 2)) * this.value); this._primaryFill.element.style.width = `${scrubberCenterX}px`; this._secondaryFill.element.style.left = `${scrubberCenterX}px`; this._secondaryFill.element.style.right = `${(1 - this._secondaryValue) * 100}%`; this._knob.element.style.left = `${scrubberCenterX}px`; } // Private _handleMousedownEvent() { this._mouseupTarget = this._interactionEndTarget(); this._mouseupTarget.addEventListener("mouseup", this, true); this._valueWillStartChanging(); } _interactionEndTarget() { const mediaControls = this.parentOfType(MediaControls); return (!mediaControls || mediaControls instanceof MacOSInlineMediaControls) ? window : mediaControls.element; } _handleTouchstartEvent(event) { // We're only interested in the very first touch on the . if (event.touches.length !== 1) return; this._initialTouchIdentifier = event.touches[0].identifier; this._touchendTarget = this._interactionEndTarget(); this._touchendTarget.addEventListener("touchend", this, true); this._valueWillStartChanging(); } _valueWillStartChanging() { // We should no longer cache the value since we'll be interacting with the // so the value should be read back from it dynamically. delete this._value; if (this.uiDelegate && typeof this.uiDelegate.controlValueWillStartChanging === "function") this.uiDelegate.controlValueWillStartChanging(this); this.isActive = true; this.needsLayout = true; } _valueDidChange() { if (this.uiDelegate && typeof this.uiDelegate.controlValueDidChange === "function") this.uiDelegate.controlValueDidChange(this); this.needsLayout = true; } _valueDidStopChanging() { this.isActive = false; if (this.uiDelegate && typeof this.uiDelegate.controlValueDidStopChanging === "function") this.uiDelegate.controlValueDidStopChanging(this); this.needsLayout = true; } _handleMouseupEvent() { this._mouseupTarget.removeEventListener("mouseup", this, true); delete this._mouseupTarget; this._valueDidStopChanging(); } _handleTouchendEvent(event) { if (!Array.from(event.changedTouches).find(touch => touch.identifier === this._initialTouchIdentifier)) return; this._touchendTarget.removeEventListener("touchend", this, true); delete this._touchendTarget; delete this._initialTouchIdentifier; this._valueDidStopChanging(); } }