michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * Handles nav overlay button positioning. michael@0: */ michael@0: michael@0: // minimum amount of movement using the mouse after which we cancel the button click handlers michael@0: const kOnClickMargin = 3; michael@0: michael@0: const kNavButtonPref = "browser.display.overlaynavbuttons"; michael@0: michael@0: var NavButtonSlider = { michael@0: _back: document.getElementById("overlay-back"), michael@0: _plus: document.getElementById("overlay-plus"), michael@0: _mouseMoveStarted: false, michael@0: _mouseDown: false, michael@0: _yPos: -1, michael@0: michael@0: get back() { michael@0: return this._back; michael@0: }, michael@0: michael@0: get plus() { michael@0: return this._plus; michael@0: }, michael@0: michael@0: /* michael@0: * custom dragger, see input.js michael@0: */ michael@0: michael@0: freeDrag: function freeDrag() { michael@0: return true; michael@0: }, michael@0: michael@0: isDraggable: function isDraggable(aTarget, aContent) { michael@0: return { x: false, y: true }; michael@0: }, michael@0: michael@0: dragStart: function dragStart(aX, aY, aTarget, aScroller) { michael@0: return true; michael@0: }, michael@0: michael@0: dragStop: function dragStop(aDx, aDy, aScroller) { michael@0: return true; michael@0: }, michael@0: michael@0: dragMove: function dragMove(aDx, aDy, aScroller, aIsKenetic, aClientX, aClientY) { michael@0: // Note if aIsKenetic is true this is synthetic movement, michael@0: // we don't want that so return false. michael@0: if (aIsKenetic) { michael@0: return false; michael@0: } michael@0: michael@0: this._updatePosition(aClientY); michael@0: michael@0: // return true if we moved, false otherwise. The result michael@0: // is used in deciding if we should repaint between drags. michael@0: return true; michael@0: }, michael@0: michael@0: /* michael@0: * logic michael@0: */ michael@0: michael@0: init: function init() { michael@0: // Touch drag support provided by input.js michael@0: this._back.customDragger = this; michael@0: this._plus.customDragger = this; michael@0: Elements.browsers.addEventListener("ContentSizeChanged", this, true); michael@0: let events = ["mousedown", "mouseup", "mousemove", "click", "touchstart", "touchmove", "touchend"]; michael@0: events.forEach(function (value) { michael@0: this._back.addEventListener(value, this, true); michael@0: this._plus.addEventListener(value, this, true); michael@0: }, this); michael@0: michael@0: this._updateStops(); michael@0: this._updateVisibility(); michael@0: Services.prefs.addObserver(kNavButtonPref, this, false); michael@0: }, michael@0: michael@0: observe: function (aSubject, aTopic, aData) { michael@0: if (aTopic == "nsPref:changed" && aData == kNavButtonPref) { michael@0: this._updateVisibility(); michael@0: } michael@0: }, michael@0: michael@0: _updateVisibility: function () { michael@0: if (Services.prefs.getBoolPref(kNavButtonPref)) { michael@0: this._back.removeAttribute("hidden"); michael@0: this._plus.removeAttribute("hidden"); michael@0: } else { michael@0: this._back.setAttribute("hidden", true); michael@0: this._plus.setAttribute("hidden", true); michael@0: } michael@0: }, michael@0: michael@0: _updateStops: function () { michael@0: this._contentHeight = ContentAreaObserver.contentHeight; michael@0: this._imageHeight = 118; michael@0: this._topStop = this._imageHeight * .7; michael@0: this._bottomStop = this._contentHeight - (this._imageHeight * .7); michael@0: michael@0: // Check to see if we need to move the slider into view michael@0: if (this._yPos != -1 && michael@0: (this._topStop > this._yPos || this._bottomStop < this._yPos)) { michael@0: this._back.style.top = "50%"; michael@0: this._plus.style.top = "50%"; michael@0: } michael@0: }, michael@0: michael@0: _getPosition: function _getPosition() { michael@0: this._yPos = parseInt(getComputedStyle(this._back).top); michael@0: }, michael@0: michael@0: _setPosition: function _setPosition() { michael@0: this._back.style.top = this._yPos + "px"; michael@0: this._plus.style.top = this._yPos + "px"; michael@0: }, michael@0: michael@0: _updatePosition: function (aClientY) { michael@0: if (this._topStop > aClientY || this._bottomStop < aClientY) michael@0: return; michael@0: this._yPos = aClientY; michael@0: this._setPosition(); michael@0: }, michael@0: michael@0: _updateOffset: function (aOffset) { michael@0: let newPos = this._yPos + aOffset; michael@0: if (this._topStop > newPos || this._bottomStop < newPos) michael@0: return; michael@0: this._yPos = newPos; michael@0: this._setPosition(); michael@0: }, michael@0: michael@0: /* michael@0: * Events michael@0: */ michael@0: michael@0: handleEvent: function handleEvent(aEvent) { michael@0: switch (aEvent.type) { michael@0: case "ContentSizeChanged": michael@0: this._updateStops(); michael@0: break; michael@0: michael@0: case "touchstart": michael@0: if (aEvent.touches.length != 1) michael@0: break; michael@0: aEvent.preventDefault(); michael@0: aEvent = aEvent.touches[0]; michael@0: case "mousedown": michael@0: this._getPosition(); michael@0: this._mouseDown = true; michael@0: this._mouseMoveStarted = false; michael@0: this._mouseY = aEvent.clientY; michael@0: if (aEvent.originalTarget) michael@0: aEvent.originalTarget.setCapture(); michael@0: this._back.setAttribute("mousedrag", true); michael@0: this._plus.setAttribute("mousedrag", true); michael@0: break; michael@0: michael@0: case "touchend": michael@0: if (aEvent.touches.length != 0) michael@0: break; michael@0: this._mouseDown = false; michael@0: this._back.removeAttribute("mousedrag"); michael@0: this._plus.removeAttribute("mousedrag"); michael@0: if (!this._mouseMoveStarted) { michael@0: if (aEvent.originalTarget == this._back) { michael@0: CommandUpdater.doCommand('cmd_back'); michael@0: } else { michael@0: CommandUpdater.doCommand('cmd_newTab'); michael@0: } michael@0: } michael@0: break; michael@0: case "mouseup": michael@0: this._mouseDown = false; michael@0: this._back.removeAttribute("mousedrag"); michael@0: this._plus.removeAttribute("mousedrag"); michael@0: break; michael@0: michael@0: case "touchmove": michael@0: if (aEvent.touches.length != 1) michael@0: break; michael@0: aEvent = aEvent.touches[0]; michael@0: case "mousemove": michael@0: // Check to be sure this is a drag operation michael@0: if (!this._mouseDown) { michael@0: return; michael@0: } michael@0: // Don't start a drag until we've passed a threshold michael@0: let dy = aEvent.clientY - this._mouseY; michael@0: if (!this._mouseMoveStarted && Math.abs(dy) < kOnClickMargin) { michael@0: return; michael@0: } michael@0: // Start dragging via the mouse michael@0: this._mouseMoveStarted = true; michael@0: this._mouseY = aEvent.clientY; michael@0: this._updateOffset(dy); michael@0: break; michael@0: case "click": michael@0: // Don't invoke the click action if we've moved the buttons via the mouse. michael@0: if (this._mouseMoveStarted) { michael@0: return; michael@0: } michael@0: if (aEvent.originalTarget == this._back) { michael@0: CommandUpdater.doCommand('cmd_back'); michael@0: } else { michael@0: CommandUpdater.doCommand('cmd_newTab'); michael@0: } michael@0: break; michael@0: } michael@0: }, michael@0: }; michael@0: michael@0: