michael@0: // -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- 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: /* We don't support zooming yet, disable Animated zoom by clamping it to the default zoom. */ michael@0: const kBrowserFindZoomLevelMin = 1; michael@0: const kBrowserFindZoomLevelMax = 1; michael@0: michael@0: var FindHelperUI = { michael@0: type: "find", michael@0: commands: { michael@0: next: "cmd_findNext", michael@0: previous: "cmd_findPrevious", michael@0: close: "cmd_findClose" michael@0: }, michael@0: michael@0: _finder: null, michael@0: _open: false, michael@0: _status: null, michael@0: _searchString: "", michael@0: michael@0: /* michael@0: * Properties michael@0: */ michael@0: michael@0: get isActive() { michael@0: return this._open; michael@0: }, michael@0: michael@0: get status() { michael@0: return this._status; michael@0: }, michael@0: michael@0: set status(val) { michael@0: if (val != this._status) { michael@0: this._status = val; michael@0: if (!val) michael@0: this._textbox.removeAttribute("status"); michael@0: else michael@0: this._textbox.setAttribute("status", val); michael@0: this.updateCommands(this._textbox.value); michael@0: } michael@0: }, michael@0: michael@0: init: function findHelperInit() { michael@0: this._textbox = document.getElementById("findbar-textbox"); michael@0: this._container = Elements.findbar; michael@0: michael@0: this._cmdPrevious = document.getElementById(this.commands.previous); michael@0: this._cmdNext = document.getElementById(this.commands.next); michael@0: michael@0: this._textbox.addEventListener("keydown", this); michael@0: michael@0: // Listen for events where form assistant should be closed michael@0: Elements.tabList.addEventListener("TabSelect", this, true); michael@0: Elements.browsers.addEventListener("URLChanged", this, true); michael@0: window.addEventListener("MozAppbarShowing", this); michael@0: window.addEventListener("MozFlyoutPanelShowing", this, false); michael@0: }, michael@0: michael@0: handleEvent: function findHelperHandleEvent(aEvent) { michael@0: switch (aEvent.type) { michael@0: case "TabSelect": michael@0: this.hide(); michael@0: break; michael@0: michael@0: case "URLChanged": michael@0: if (aEvent.detail && aEvent.target == getBrowser()) michael@0: this.hide(); michael@0: break; michael@0: michael@0: case "keydown": michael@0: if (aEvent.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_RETURN) { michael@0: let backwardsSearch = aEvent.shiftKey; michael@0: this.searchAgain(this._searchString, backwardsSearch); michael@0: } michael@0: break; michael@0: michael@0: case "MozAppbarShowing": michael@0: case "MozFlyoutPanelShowing": michael@0: if (aEvent.target != this._container) { michael@0: this.hide(); michael@0: } michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: show: function findHelperShow() { michael@0: if (BrowserUI.isStartTabVisible) { michael@0: return; michael@0: } michael@0: if (this._open) { michael@0: setTimeout(() => { michael@0: this._textbox.select(); michael@0: this._textbox.focus(); michael@0: }, 0); michael@0: return; michael@0: } michael@0: michael@0: // Hide any menus michael@0: ContextUI.dismiss(); michael@0: michael@0: // Shutdown selection related ui michael@0: SelectionHelperUI.closeEditSession(); michael@0: michael@0: let findbar = this._container; michael@0: setTimeout(() => { michael@0: findbar.show(); michael@0: this.search(this._textbox.value); michael@0: this._textbox.select(); michael@0: this._textbox.focus(); michael@0: michael@0: this._open = true; michael@0: }, 0); michael@0: michael@0: // Prevent the view to scroll automatically while searching michael@0: Browser.selectedBrowser.scrollSync = false; michael@0: }, michael@0: michael@0: hide: function findHelperHide() { michael@0: if (!this._open) michael@0: return; michael@0: michael@0: ContentAreaObserver.shiftBrowserDeck(0); michael@0: michael@0: let onTransitionEnd = () => { michael@0: this._container.removeEventListener("transitionend", onTransitionEnd, true); michael@0: this._textbox.value = ""; michael@0: this.status = null; michael@0: this._open = false; michael@0: if (this._finder) { michael@0: this._finder.removeResultListener(this); michael@0: this._finder = null michael@0: } michael@0: // Restore the scroll synchronisation michael@0: Browser.selectedBrowser.scrollSync = true; michael@0: }; michael@0: michael@0: this._textbox.blur(); michael@0: this._container.addEventListener("transitionend", onTransitionEnd, true); michael@0: this._container.dismiss(); michael@0: }, michael@0: michael@0: search: function findHelperSearch(aValue) { michael@0: if (!this._finder) { michael@0: this._finder = Browser.selectedBrowser.finder; michael@0: this._finder.addResultListener(this); michael@0: } michael@0: this._searchString = aValue; michael@0: if (aValue != "") { michael@0: this._finder.fastFind(aValue, false, false); michael@0: } else { michael@0: this.updateCommands(); michael@0: } michael@0: }, michael@0: michael@0: searchAgain: function findHelperSearchAgain(aValue, aFindBackwards) { michael@0: // This can happen if the user taps next/previous after re-opening the search bar michael@0: if (!this._finder) { michael@0: this.search(aValue); michael@0: return; michael@0: } michael@0: michael@0: this._finder.findAgain(aFindBackwards, false, false); michael@0: }, michael@0: michael@0: goToPrevious: function findHelperGoToPrevious() { michael@0: this.searchAgain(this._searchString, true); michael@0: }, michael@0: michael@0: goToNext: function findHelperGoToNext() { michael@0: this.searchAgain(this._searchString, false); michael@0: }, michael@0: michael@0: onFindResult: function(aData) { michael@0: this._status = aData.result; michael@0: if (aData.rect) { michael@0: this._zoom(aData.rect, Browser.selectedBrowser.contentDocumentHeight); michael@0: } michael@0: this.updateCommands(); michael@0: }, michael@0: michael@0: updateCommands: function findHelperUpdateCommands() { michael@0: let disabled = (this._status == Ci.nsITypeAheadFind.FIND_NOTFOUND) || (this._searchString == ""); michael@0: this._cmdPrevious.setAttribute("disabled", disabled); michael@0: this._cmdNext.setAttribute("disabled", disabled); michael@0: }, michael@0: michael@0: _zoom: function _findHelperZoom(aElementRect, aContentHeight) { michael@0: // The rect we get here is the content rect including scroll offset michael@0: // in the page. michael@0: michael@0: // If the text falls below the find bar and keyboard shift content up. michael@0: let browserShift = 0; michael@0: // aElementRect.y is the top left origin of the selection rect. michael@0: if ((aElementRect.y + aElementRect.height) > michael@0: (aContentHeight - this._container.boxObject.height)) { michael@0: browserShift += this._container.boxObject.height; michael@0: } michael@0: browserShift += Services.metro.keyboardHeight; michael@0: michael@0: // If the rect top of the selection is above the view, don't shift content michael@0: // (or if it's already shifted, shift it back down). michael@0: if (aElementRect.y < browserShift) { michael@0: browserShift = 0; michael@0: } michael@0: michael@0: // Shift the deck so that the selection is within the visible view. michael@0: ContentAreaObserver.shiftBrowserDeck(browserShift); michael@0: michael@0: // Adjust for keyboad display and position the text selection rect in michael@0: // the middle of the viewable area. michael@0: let xPos = aElementRect.x; michael@0: let yPos = aElementRect.y; michael@0: let scrollAdjust = ((ContentAreaObserver.height - Services.metro.keyboardHeight) * .5) + michael@0: Services.metro.keyboardHeight; michael@0: yPos -= scrollAdjust; michael@0: if (yPos < 0) { michael@0: yPos = 0; michael@0: } michael@0: michael@0: // TODO zoom via apzc, right now all we support is scroll michael@0: // positioning. michael@0: Browser.selectedBrowser.contentWindow.scrollTo(xPos, yPos); michael@0: } michael@0: };