mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-09-19 11:40:21 +02:00
1119 lines
34 KiB
JavaScript
1119 lines
34 KiB
JavaScript
# -*- Mode: HTML -*-
|
|
# ***** BEGIN LICENSE BLOCK *****
|
|
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
#
|
|
# The contents of this file are subject to the Mozilla Public License Version
|
|
# 1.1 (the "License"); you may not use this file except in compliance with
|
|
# the License. You may obtain a copy of the License at
|
|
# http://www.mozilla.org/MPL/
|
|
#
|
|
# Software distributed under the License is distributed on an "AS IS" basis,
|
|
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
# for the specific language governing rights and limitations under the
|
|
# License.
|
|
#
|
|
# The Original Code is mozilla.org viewsource frontend.
|
|
#
|
|
# The Initial Developer of the Original Code is
|
|
# Netscape Communications Corporation.
|
|
# Portions created by the Initial Developer are Copyright (C) 2003
|
|
# the Initial Developer. All Rights Reserved.
|
|
#
|
|
# Contributor(s):
|
|
# Blake Ross <blake@cs.stanford.edu> (Original Author)
|
|
# Masayuki Nakano <masayuki@d-toybox.com>
|
|
# Ben Basson <contact@cusser.net>
|
|
# Jason Barnabe <jason_barnabe@fastmail.fm>
|
|
#
|
|
# Alternatively, the contents of this file may be used under the terms of
|
|
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
# in which case the provisions of the GPL or the LGPL are applicable instead
|
|
# of those above. If you wish to allow use of your version of this file only
|
|
# under the terms of either the GPL or the LGPL, and not to allow others to
|
|
# use your version of this file under the terms of the MPL, indicate your
|
|
# decision by deleting the provisions above and replace them with the notice
|
|
# and other provisions required by the LGPL or the GPL. If you do not delete
|
|
# the provisions above, a recipient may use your version of this file under
|
|
# the terms of any one of the MPL, the GPL or the LGPL.
|
|
#
|
|
#***** END LICENSE BLOCK ***** -->
|
|
|
|
const FIND_NORMAL = 0;
|
|
const FIND_TYPEAHEAD = 1;
|
|
const FIND_LINKS = 2;
|
|
|
|
const CHAR_CODE_SPACE = " ".charCodeAt(0);
|
|
const CHAR_CODE_SLASH = "/".charCodeAt(0);
|
|
const CHAR_CODE_APOSTROPHE = "'".charCodeAt(0);
|
|
|
|
/*
|
|
* WARNING:
|
|
* Don't define global variables and functions in this file. Because this file
|
|
* is included by some files. So, the global name space is not safety.
|
|
*
|
|
* But we need global functions for the Timer Event Handlers or for the Event
|
|
* Listener Handlers. In this case, we MUST define global functions with
|
|
* "findBar_" prefix.
|
|
*/
|
|
|
|
var gFindBar = {
|
|
SELECTION_CONTROLLER: Components.interfaces.nsISelectionController,
|
|
|
|
mFindEnabled: true,
|
|
mFindMode: FIND_NORMAL,
|
|
mFoundLink: null,
|
|
mFoundEditable: null,
|
|
mCurrentWindow: null,
|
|
mTmpOutline: null,
|
|
mTmpOutlineOffset: "0",
|
|
mDrawOutline: false,
|
|
mQuickFindTimeout: null,
|
|
mQuickFindTimeoutLength: 0,
|
|
mHighlightTimeout: null,
|
|
mUseTypeAheadFind: false,
|
|
mWrappedToTopStr: "",
|
|
mWrappedToBottomStr: "",
|
|
mNotFoundStr: "",
|
|
mCaseSensitiveStr: "",
|
|
mFlashFindBar: 0,
|
|
mFlashFindBarCount: 6,
|
|
mFlashFindBarTimeout: null,
|
|
mLastHighlightString: "",
|
|
mTypeAheadLinksOnly: false,
|
|
mTypeAheadCaseSensitive: -1,
|
|
mIsIMEComposing: false,
|
|
mTextToSubURIService: null,
|
|
|
|
// DOMRange used during highlighting
|
|
mSearchRange: null,
|
|
mStartPt: null,
|
|
mEndPt: null,
|
|
|
|
mTypeAheadFind: {
|
|
useTAFPref: "accessibility.typeaheadfind",
|
|
searchLinksPref: "accessibility.typeaheadfind.linksonly",
|
|
caseSensePref: "accessibility.typeaheadfind.casesensitive",
|
|
|
|
observe: function(aSubject, aTopic, aPrefName)
|
|
{
|
|
if (aTopic != "nsPref:changed" ||
|
|
(aPrefName != this.useTAFPref && aPrefName != this.searchLinksPref &&
|
|
aPrefName != this.caseSensePref))
|
|
return;
|
|
|
|
var prefService =
|
|
Components.classes["@mozilla.org/preferences-service;1"]
|
|
.getService(Components.interfaces.nsIPrefBranch);
|
|
|
|
gFindBar.mUseTypeAheadFind =
|
|
prefService.getBoolPref("accessibility.typeaheadfind");
|
|
gFindBar.mTypeAheadLinksOnly =
|
|
prefService.getBoolPref("accessibility.typeaheadfind.linksonly");
|
|
gFindBar.mTypeAheadCaseSensitive =
|
|
prefService.getIntPref("accessibility.typeaheadfind.casesensitive");
|
|
gFindBar.setCaseSensitivity();
|
|
}
|
|
},
|
|
|
|
initFindBar: function ()
|
|
{
|
|
getBrowser().addEventListener("keypress",
|
|
findBar_OnBrowserKeyPress, false);
|
|
getBrowser().addEventListener("mouseup",
|
|
findBar_OnBrowserMouseUp, false);
|
|
|
|
var prefService =
|
|
Components.classes["@mozilla.org/preferences-service;1"]
|
|
.getService(Components.interfaces.nsIPrefBranch);
|
|
|
|
var pbi = prefService.QueryInterface(Components.interfaces.nsIPrefBranch2);
|
|
|
|
this.mQuickFindTimeoutLength =
|
|
prefService.getIntPref("accessibility.typeaheadfind.timeout");
|
|
this.mFlashFindBar =
|
|
prefService.getIntPref("accessibility.typeaheadfind.flashBar");
|
|
|
|
pbi.addObserver(this.mTypeAheadFind.useTAFPref,
|
|
this.mTypeAheadFind, false);
|
|
pbi.addObserver(this.mTypeAheadFind.searchLinksPref,
|
|
this.mTypeAheadFind, false);
|
|
pbi.addObserver(this.mTypeAheadFind.caseSensePref,
|
|
this.mTypeAheadFind, false);
|
|
this.mUseTypeAheadFind =
|
|
prefService.getBoolPref("accessibility.typeaheadfind");
|
|
this.mTypeAheadLinksOnly =
|
|
prefService.getBoolPref("accessibility.typeaheadfind.linksonly");
|
|
this.mTypeAheadCaseSensitive =
|
|
prefService.getIntPref("accessibility.typeaheadfind.casesensitive");
|
|
|
|
var findField = document.getElementById("find-field");
|
|
findField.addEventListener("dragdrop", findBar_OnDrop, true);
|
|
|
|
this.mTextToSubURIService =
|
|
Components.classes["@mozilla.org/intl/texttosuburi;1"]
|
|
.getService(Components.interfaces.nsITextToSubURI);
|
|
},
|
|
|
|
mFindbarObserver: {
|
|
onDrop: function (aEvent, aXferData, aDragSession)
|
|
{
|
|
var findField = document.getElementById("find-field");
|
|
findField.value = aXferData.data;
|
|
gFindBar.find(aXferData.data);
|
|
},
|
|
getSupportedFlavours: function ()
|
|
{
|
|
var flavourSet = new FlavourSet();
|
|
flavourSet.appendFlavour("text/unicode");
|
|
return flavourSet;
|
|
}
|
|
},
|
|
|
|
uninitFindBar: function ()
|
|
{
|
|
var prefService =
|
|
Components.classes["@mozilla.org/preferences-service;1"]
|
|
.getService(Components.interfaces.nsIPrefBranch);
|
|
|
|
var pbi = prefService.QueryInterface(Components.interfaces.nsIPrefBranch2);
|
|
pbi.removeObserver(this.mTypeAheadFind.useTAFPref,
|
|
this.mTypeAheadFind);
|
|
pbi.removeObserver(this.mTypeAheadFind.searchLinksPref,
|
|
this.mTypeAheadFind);
|
|
pbi.removeObserver(this.mTypeAheadFind.caseSensePref,
|
|
this.mTypeAheadFind);
|
|
|
|
getBrowser().removeEventListener("keypress",
|
|
findBar_OnBrowserKeyPress, false);
|
|
getBrowser().removeEventListener("mouseup",
|
|
findBar_OnBrowserMouseUp, false);
|
|
},
|
|
|
|
/**
|
|
* Turns highlighting on or off.
|
|
*
|
|
* @param aHighlight true to turn highlighting on
|
|
*/
|
|
toggleHighlight: function (aHighlight)
|
|
{
|
|
var word = document.getElementById("find-field").value;
|
|
if (aHighlight) {
|
|
// We have to update the status because we might still have the status
|
|
// of another tab
|
|
if (this.highlightDoc('yellow', 'black', word))
|
|
this.updateStatus(Components.interfaces.nsITypeAheadFind.FIND_FOUND, false);
|
|
else
|
|
this.updateStatus(Components.interfaces.nsITypeAheadFind.FIND_NOTFOUND, false);
|
|
} else {
|
|
this.highlightDoc(null, null, null);
|
|
this.mLastHighlightString = null;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* (Un)highlights each instance of the searched word in the passed window's content.
|
|
*
|
|
* @param highBackColor the background color for the highlight
|
|
* @param highTextColor the text color for the highlight, or null to make it unhighlight
|
|
* @param word the word to search for
|
|
* @param win the window to search in. Passing undefined will search the current window.
|
|
* @return true if the text was found
|
|
*/
|
|
highlightDoc: function (highBackColor, highTextColor, word, win)
|
|
{
|
|
if (!win)
|
|
win = window.content;
|
|
|
|
var textFound = false;
|
|
for (var i = 0; win.frames && i < win.frames.length; i++) {
|
|
if (this.highlightDoc(highBackColor, highTextColor, word, win.frames[i]))
|
|
textFound = true;
|
|
}
|
|
|
|
var doc = win.document;
|
|
if (!doc || !("body" in doc))
|
|
return textFound;
|
|
|
|
var body = doc.body;
|
|
|
|
var count = body.childNodes.length;
|
|
this.mSearchRange = doc.createRange();
|
|
this.mStartPt = doc.createRange();
|
|
this.mEndPt = doc.createRange();
|
|
|
|
this.mSearchRange.setStart(body, 0);
|
|
this.mSearchRange.setEnd(body, count);
|
|
|
|
this.mStartPt.setStart(body, 0);
|
|
this.mStartPt.setEnd(body, 0);
|
|
this.mEndPt.setStart(body, count);
|
|
this.mEndPt.setEnd(body, count);
|
|
|
|
if (!highBackColor) {
|
|
// Remove highlighting. We use the find API again rather than
|
|
// searching for our span elements by id so that we gain access to the
|
|
// anonymous content that nsIFind searches.
|
|
|
|
if (!this.mLastHighlightString)
|
|
return textFound;
|
|
|
|
var retRange = null;
|
|
var finder = Components.classes["@mozilla.org/embedcomp/rangefind;1"]
|
|
.createInstance()
|
|
.QueryInterface(Components.interfaces.nsIFind);
|
|
|
|
while ((retRange = finder.Find(this.mLastHighlightString,
|
|
this.mSearchRange, this.mStartPt,
|
|
this.mEndPt))) {
|
|
var startContainer = retRange.startContainer;
|
|
var elem = null;
|
|
try {
|
|
elem = startContainer.parentNode;
|
|
}
|
|
catch (e) { }
|
|
|
|
if (elem && elem.getAttribute("id") == "__firefox-findbar-search-id") {
|
|
var child = null;
|
|
var docfrag = doc.createDocumentFragment();
|
|
var next = elem.nextSibling;
|
|
var parent = elem.parentNode;
|
|
|
|
while ((child = elem.firstChild)) {
|
|
docfrag.appendChild(child);
|
|
}
|
|
|
|
this.mStartPt = doc.createRange();
|
|
this.mStartPt.setStartAfter(elem);
|
|
|
|
parent.removeChild(elem);
|
|
parent.insertBefore(docfrag, next);
|
|
parent.normalize();
|
|
}
|
|
else {
|
|
// Somehow we didn't highlight this instance; just skip it.
|
|
this.mStartPt = doc.createRange();
|
|
this.mStartPt.setStart(retRange.endContainer, retRange.endOffset);
|
|
}
|
|
|
|
this.mStartPt.collapse(true);
|
|
|
|
textFound = true;
|
|
}
|
|
return textFound;
|
|
}
|
|
|
|
var baseNode = doc.createElement("span");
|
|
baseNode.setAttribute("style", "background-color: " + highBackColor + "; " +
|
|
"color: " + highTextColor + "; " +
|
|
"display: inline; font-size: inherit;" +
|
|
" padding: 0;");
|
|
baseNode.setAttribute("id", "__firefox-findbar-search-id");
|
|
|
|
return this.highlightText(word, baseNode) || textFound;
|
|
},
|
|
|
|
/**
|
|
* Highlights each instance of the searched word in the current range.
|
|
*
|
|
* @param word the word to search for
|
|
* @param baseNode a node to use as a template for what will replace the
|
|
* searched word
|
|
* @return true if the text was found
|
|
*/
|
|
highlightText: function (word, baseNode)
|
|
{
|
|
var retRange = null;
|
|
var finder = Components.classes["@mozilla.org/embedcomp/rangefind;1"]
|
|
.createInstance()
|
|
.QueryInterface(Components.interfaces.nsIFind);
|
|
|
|
finder.caseSensitive = this.shouldBeCaseSensitive(word);
|
|
|
|
var textFound = false;
|
|
while((retRange = finder.Find(word, this.mSearchRange,
|
|
this.mStartPt, this.mEndPt))) {
|
|
// Highlight
|
|
var nodeSurround = baseNode.cloneNode(true);
|
|
var node = this.highlight(retRange, nodeSurround);
|
|
this.mStartPt = node.ownerDocument.createRange();
|
|
this.mStartPt.setStart(node, node.childNodes.length);
|
|
this.mStartPt.setEnd(node, node.childNodes.length);
|
|
|
|
textFound = true;
|
|
}
|
|
|
|
this.mLastHighlightString = word;
|
|
|
|
return textFound;
|
|
},
|
|
|
|
/**
|
|
* Highlights the word in the passed range.
|
|
*
|
|
* @param range the range that contains the word to highlight
|
|
* @param node the node replace the searched word with
|
|
* @return the node that replaced the searched word
|
|
*/
|
|
highlight: function (range, node)
|
|
{
|
|
var startContainer = range.startContainer;
|
|
var startOffset = range.startOffset;
|
|
var endOffset = range.endOffset;
|
|
var docfrag = range.extractContents();
|
|
var before = startContainer.splitText(startOffset);
|
|
var parent = before.parentNode;
|
|
node.appendChild(docfrag);
|
|
parent.insertBefore(node, before);
|
|
return node;
|
|
},
|
|
|
|
setCaseSensitivity: function (val)
|
|
{
|
|
var findToolbar = document.getElementById("FindToolbar");
|
|
if (findToolbar.hidden)
|
|
return;
|
|
|
|
if (!val)
|
|
val = document.getElementById("find-field").value;
|
|
|
|
var matchCaseCheckbox = document.getElementById("find-case-sensitive");
|
|
matchCaseCheckbox.checked = this.shouldBeCaseSensitive(val);
|
|
|
|
var matchCaseText = document.getElementById("match-case-status");
|
|
matchCaseText.value = matchCaseCheckbox.checked ?
|
|
this.mCaseSensitiveStr : "";
|
|
|
|
// Show the checkbox on the full Find bar in non-auto mode. Show the label
|
|
// in all other cases.
|
|
matchCaseCheckbox.hidden = this.mUsingMinimalUI ||
|
|
(this.mTypeAheadCaseSensitive != 0 && this.mTypeAheadCaseSensitive != 1);
|
|
matchCaseText.hidden = !matchCaseCheckbox.hidden;
|
|
|
|
var fastFind = getBrowser().fastFind;
|
|
fastFind.caseSensitive = matchCaseCheckbox.checked;
|
|
},
|
|
|
|
toggleCaseSensitiveCheckbox: function (aCaseSensitive)
|
|
{
|
|
var prefService =
|
|
Components.classes["@mozilla.org/preferences-service;1"]
|
|
.getService(Components.interfaces.nsIPrefBranch);
|
|
|
|
// Just set the pref; our observer will change the find bar behavior
|
|
prefService.setIntPref("accessibility.typeaheadfind.casesensitive",
|
|
aCaseSensitive ? 1 : 0);
|
|
},
|
|
|
|
/**
|
|
* Opens and displays the find bar.
|
|
*
|
|
* @param showMinimalUI
|
|
* true if the find bar is to contain minimalist UI, false (or
|
|
* undefined, as happens if no arguments are provided) if the find
|
|
* bar is to display more generalized search UI
|
|
* @returns bool
|
|
* true if the find bar wasn't previously open, false otherwise
|
|
*/
|
|
openFindBar: function (showMinimalUI)
|
|
{
|
|
// Notify anyone else that might want to handle this event
|
|
var findActivatedEvent = document.createEvent("Events");
|
|
findActivatedEvent.initEvent("find-activated", false, true);
|
|
window.dispatchEvent(findActivatedEvent);
|
|
if (!this.mFindEnabled)
|
|
throw Components.results.NS_OK;
|
|
|
|
if (!this.mNotFoundStr || !this.mWrappedToTopStr ||
|
|
!this.mWrappedToBottomStr || !this.mNormalFindStr ||
|
|
!this.mFastFindStr || !this.mCaseSensitiveStr) {
|
|
var bundle = document.getElementById("bundle_findBar");
|
|
this.mNotFoundStr = bundle.getString("NotFound");
|
|
this.mWrappedToTopStr = bundle.getString("WrappedToTop");
|
|
this.mWrappedToBottomStr = bundle.getString("WrappedToBottom");
|
|
this.mNormalFindStr = bundle.getString("NormalFindLabel");
|
|
this.mFastFindStr = bundle.getString("FastFindLabel");
|
|
this.mCaseSensitiveStr = bundle.getString("CaseSensitive");
|
|
}
|
|
|
|
this.updateFindUI(showMinimalUI);
|
|
var findToolbar = document.getElementById("FindToolbar");
|
|
if (findToolbar.hidden) {
|
|
findToolbar.hidden = false;
|
|
|
|
var findField = document.getElementById("find-field");
|
|
this.setCaseSensitivity(findField.value);
|
|
this.updateStatus(Components.interfaces.nsITypeAheadFind.FIND_FOUND, false);
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
focusFindBar: function ()
|
|
{
|
|
var findField = document.getElementById("find-field");
|
|
findField.focus();
|
|
},
|
|
|
|
selectFindBar: function ()
|
|
{
|
|
var findField = document.getElementById("find-field");
|
|
findField.select();
|
|
},
|
|
|
|
closeFindBar: function ()
|
|
{
|
|
// ensure the dom is ready...
|
|
setTimeout(findBar_DelayedCloseFindBar, 0);
|
|
},
|
|
|
|
fireKeypressEvent: function (target, evt)
|
|
{
|
|
if (!target)
|
|
return;
|
|
var event = document.createEvent("KeyEvents");
|
|
event.initKeyEvent(evt.type, evt.canBubble, evt.cancelable,
|
|
evt.view, evt.ctrlKey, evt.altKey, evt.shiftKey,
|
|
evt.metaKey, evt.keyCode, evt.charCode);
|
|
target.dispatchEvent(event);
|
|
},
|
|
|
|
updateStatusBar: function ()
|
|
{
|
|
var xulBrowserWindow = window.XULBrowserWindow;
|
|
if (!xulBrowserWindow)
|
|
return false;
|
|
|
|
if (!this.mFoundLink || !this.mFoundLink.href ||
|
|
this.mFoundLink.href == "") {
|
|
xulBrowserWindow.setOverLink("");
|
|
return true;
|
|
}
|
|
|
|
var docCharset = "";
|
|
var ownerDoc = this.mFoundLink.ownerDocument;
|
|
if (ownerDoc)
|
|
docCharset = ownerDoc.characterSet;
|
|
|
|
var url =
|
|
this.mTextToSubURIService.unEscapeURIForUI(docCharset,
|
|
this.mFoundLink.href);
|
|
xulBrowserWindow.setOverLink(url);
|
|
|
|
return true;
|
|
},
|
|
|
|
setFoundLink: function (foundLink)
|
|
{
|
|
if (this.mFoundLink == foundLink)
|
|
return;
|
|
|
|
if (this.mFoundLink && this.mDrawOutline) {
|
|
// restore original outline
|
|
this.mFoundLink.style.outline = this.mTmpOutline;
|
|
this.mFoundLink.style.outlineOffset = this.mTmpOutlineOffset;
|
|
}
|
|
this.mDrawOutline = (foundLink && this.mFindMode != FIND_NORMAL);
|
|
if (this.mDrawOutline) {
|
|
// backup original outline
|
|
this.mTmpOutline = foundLink.style.outline;
|
|
this.mTmpOutlineOffset = foundLink.style.outlineOffset;
|
|
// draw pseudo focus rect
|
|
// XXX Should we change the following style for FAYT pseudo focus?
|
|
// XXX Shouldn't we change default design if outline is visible already?
|
|
foundLink.style.outline = "1px dotted invert";
|
|
foundLink.style.outlineOffset = "0";
|
|
}
|
|
|
|
this.mFoundLink = foundLink;
|
|
|
|
// We should update status bar. But we need delay. If the mouse cursor is
|
|
// on the document, the status bar text is changed by mouse event that is
|
|
// fired by scroll event. So, we need to change the status bar text after
|
|
// mouse event.
|
|
if (this.mFindMode != FIND_NORMAL)
|
|
setTimeout(findBar_UpdateStatusBar, 0);
|
|
},
|
|
|
|
finishFAYT: function (aKeypressEvent)
|
|
{
|
|
try {
|
|
if (this.mFoundLink)
|
|
this.mFoundLink.focus();
|
|
else if (this.mFoundEditable) {
|
|
this.mFoundEditable.focus();
|
|
var fastFind = getBrowser().fastFind;
|
|
fastFind.collapseSelection();
|
|
} else if (this.mCurrentWindow)
|
|
this.mCurrentWindow.focus();
|
|
else
|
|
return false;
|
|
}
|
|
catch(e) {
|
|
return false;
|
|
}
|
|
|
|
if (aKeypressEvent)
|
|
aKeypressEvent.preventDefault();
|
|
|
|
this.closeFindBar();
|
|
return true;
|
|
},
|
|
|
|
delayedCloseFindBar: function ()
|
|
{
|
|
var findField = document.getElementById("find-field");
|
|
var findToolbar = document.getElementById("FindToolbar");
|
|
var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
|
|
.getService(Components.interfaces.nsIWindowWatcher);
|
|
|
|
if (window == ww.activeWindow) {
|
|
var focusedElement = document.commandDispatcher.focusedElement;
|
|
if (focusedElement && focusedElement.parentNode &&
|
|
(focusedElement.parentNode == findToolbar ||
|
|
focusedElement.parentNode.parentNode == findField)) {
|
|
// block scrolling on focus since find already scrolls, further
|
|
// scrolling is due to user action, so don't override this
|
|
var suppressedScroll = document.commandDispatcher.suppressFocusScroll;
|
|
document.commandDispatcher.suppressFocusScroll = true;
|
|
// We MUST reset suppressFocusScroll.
|
|
try {
|
|
if (this.mFoundLink)
|
|
this.mFoundLink.focus();
|
|
else if (this.mFoundEditable)
|
|
this.mFoundEditable.focus();
|
|
else if (this.mCurrentWindow)
|
|
this.mCurrentWindow.focus();
|
|
else
|
|
window.content.focus();
|
|
}
|
|
catch(e) {
|
|
// Retry to set focus.
|
|
try {
|
|
window.content.focus();
|
|
}
|
|
catch(e) { /* We lose focused element! */ }
|
|
}
|
|
document.commandDispatcher.suppressFocusScroll = suppressedScroll;
|
|
}
|
|
}
|
|
|
|
findToolbar.hidden = true;
|
|
var fastFind = getBrowser().fastFind;
|
|
fastFind.setSelectionModeAndRepaint(this.SELECTION_CONTROLLER.SELECTION_ON);
|
|
this.setFoundLink(null);
|
|
this.mFoundEditable = null;
|
|
this.mCurrentWindow = null;
|
|
if (this.mQuickFindTimeout) {
|
|
clearTimeout(this.mQuickFindTimeout);
|
|
this.mQuickFindTimeout = null;
|
|
}
|
|
},
|
|
|
|
shouldFastFind: function (evt)
|
|
{
|
|
if (evt.ctrlKey || evt.altKey || evt.metaKey || evt.getPreventDefault())
|
|
return false;
|
|
|
|
var elt = document.commandDispatcher.focusedElement;
|
|
if (elt) {
|
|
if (elt instanceof HTMLInputElement) {
|
|
// block FAYT when an <input> textfield element is focused
|
|
var inputType = elt.type;
|
|
switch (inputType) {
|
|
case "text":
|
|
case "password":
|
|
case "file":
|
|
return false;
|
|
}
|
|
}
|
|
else if (elt instanceof HTMLTextAreaElement ||
|
|
elt instanceof HTMLSelectElement ||
|
|
elt instanceof HTMLIsIndexElement ||
|
|
elt instanceof HTMLObjectElement ||
|
|
elt instanceof HTMLEmbedElement)
|
|
return false;
|
|
}
|
|
|
|
// disable FAYT in about:config and about:blank to prevent FAYT opening
|
|
// unexpectedly - to fix bugs 264562, 267150, 269712
|
|
var url = getBrowser().currentURI.spec;
|
|
if (url == "about:blank" || url == "about:config")
|
|
return false;
|
|
|
|
var win = document.commandDispatcher.focusedWindow;
|
|
if (win && win.document.designMode == "on")
|
|
return false;
|
|
|
|
return true;
|
|
},
|
|
|
|
shouldBeCaseSensitive: function (str)
|
|
{
|
|
if (this.mTypeAheadCaseSensitive == 0)
|
|
return false;
|
|
if (this.mTypeAheadCaseSensitive == 1)
|
|
return true;
|
|
|
|
return (str != str.toLowerCase());
|
|
},
|
|
|
|
onFindBarBlur: function ()
|
|
{
|
|
var fastFind = getBrowser().fastFind;
|
|
if (this.mFoundEditable) {
|
|
fastFind.collapseSelection();
|
|
} else {
|
|
fastFind.setSelectionModeAndRepaint(this.SELECTION_CONTROLLER.SELECTION_ON);
|
|
}
|
|
this.setFoundLink(null);
|
|
this.mFoundEditable = null;
|
|
this.mCurrentWindow = null;
|
|
},
|
|
|
|
onBrowserMouseUp: function (evt)
|
|
{
|
|
var findToolbar = document.getElementById("FindToolbar");
|
|
if (!findToolbar.hidden && this.mFindMode != FIND_NORMAL)
|
|
this.closeFindBar();
|
|
},
|
|
|
|
onBrowserKeyPress: function (evt)
|
|
{
|
|
// Check focused elt
|
|
if (!this.shouldFastFind(evt))
|
|
return;
|
|
|
|
var findField = document.getElementById("find-field");
|
|
if (this.mFindMode != FIND_NORMAL && this.mQuickFindTimeout) {
|
|
if (!evt.charCode)
|
|
return;
|
|
this.selectFindBar();
|
|
this.focusFindBar();
|
|
this.fireKeypressEvent(findField.inputField, evt);
|
|
evt.preventDefault();
|
|
return;
|
|
}
|
|
|
|
if (evt.charCode == CHAR_CODE_APOSTROPHE ||
|
|
evt.charCode == CHAR_CODE_SLASH ||
|
|
(this.mUseTypeAheadFind &&
|
|
evt.charCode && evt.charCode != CHAR_CODE_SPACE)) {
|
|
var findMode =
|
|
(evt.charCode == CHAR_CODE_APOSTROPHE ||
|
|
(this.mTypeAheadLinksOnly && evt.charCode != CHAR_CODE_SLASH)) ?
|
|
FIND_LINKS : FIND_TYPEAHEAD;
|
|
this.setFindMode(findMode);
|
|
|
|
// Clear bar first, so that when openFindBar() calls setCaseSensitivity()
|
|
// it doesn't get confused by a lingering value
|
|
findField.value = "";
|
|
|
|
try {
|
|
var opened = this.openFindBar(true);
|
|
}
|
|
catch (e) {
|
|
return;
|
|
}
|
|
if (opened) {
|
|
this.setFindCloseTimeout();
|
|
this.selectFindBar();
|
|
this.focusFindBar();
|
|
if (this.mUseTypeAheadFind &&
|
|
evt.charCode != CHAR_CODE_APOSTROPHE &&
|
|
evt.charCode != CHAR_CODE_SLASH)
|
|
this.fireKeypressEvent(findField.inputField, evt);
|
|
else
|
|
this.updateStatus(Components.interfaces.nsITypeAheadFind.FIND_FOUND, false);
|
|
evt.preventDefault();
|
|
}
|
|
else {
|
|
this.selectFindBar();
|
|
this.focusFindBar();
|
|
if (this.mFindMode != FIND_NORMAL) {
|
|
if (evt.charCode != CHAR_CODE_APOSTROPHE &&
|
|
evt.charCode != CHAR_CODE_SLASH)
|
|
this.fireKeypressEvent(findField.inputField, evt);
|
|
else
|
|
this.updateStatus(Components.interfaces.nsITypeAheadFind.FIND_FOUND, false);
|
|
evt.preventDefault();
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
onFindBarKeyPress: function (evt)
|
|
{
|
|
if (evt.keyCode == KeyEvent.DOM_VK_RETURN) {
|
|
if (this.mFindMode == FIND_NORMAL) {
|
|
var findString = document.getElementById("find-field");
|
|
if (!findString.value)
|
|
return;
|
|
|
|
#ifdef XP_MACOSX
|
|
if (evt.metaKey) {
|
|
#else
|
|
if (evt.ctrlKey) {
|
|
#endif
|
|
document.getElementById("highlight").click();
|
|
return;
|
|
}
|
|
|
|
if (evt.shiftKey)
|
|
this.findPrevious();
|
|
else
|
|
this.findNext();
|
|
}
|
|
else {
|
|
if (this.mFoundLink) {
|
|
var tmpLink = this.mFoundLink;
|
|
if (this.finishFAYT(evt))
|
|
this.fireKeypressEvent(tmpLink, evt);
|
|
}
|
|
}
|
|
}
|
|
else if (evt.keyCode == KeyEvent.DOM_VK_TAB) {
|
|
var shouldHandle = !evt.altKey && !evt.ctrlKey && !evt.metaKey;
|
|
if (shouldHandle && this.mFindMode != FIND_NORMAL &&
|
|
this.finishFAYT(evt)) {
|
|
if (evt.shiftKey)
|
|
document.commandDispatcher.rewindFocus();
|
|
else
|
|
document.commandDispatcher.advanceFocus();
|
|
}
|
|
}
|
|
else if (evt.keyCode == KeyEvent.DOM_VK_ESCAPE) {
|
|
this.closeFindBar();
|
|
evt.preventDefault();
|
|
}
|
|
else if (evt.keyCode == KeyEvent.DOM_VK_PAGE_UP) {
|
|
window.top._content.scrollByPages(-1);
|
|
evt.preventDefault();
|
|
}
|
|
else if (evt.keyCode == KeyEvent.DOM_VK_PAGE_DOWN) {
|
|
window.top._content.scrollByPages(1);
|
|
evt.preventDefault();
|
|
}
|
|
else if (evt.keyCode == KeyEvent.DOM_VK_UP) {
|
|
window.top._content.scrollByLines(-1);
|
|
evt.preventDefault();
|
|
}
|
|
else if (evt.keyCode == KeyEvent.DOM_VK_DOWN) {
|
|
window.top._content.scrollByLines(1);
|
|
evt.preventDefault();
|
|
}
|
|
},
|
|
|
|
enableFindButtons: function (aEnable)
|
|
{
|
|
var findNext = document.getElementById("find-next");
|
|
var findPrev = document.getElementById("find-previous");
|
|
var highlight = document.getElementById("highlight");
|
|
findNext.disabled = findPrev.disabled = highlight.disabled = !aEnable;
|
|
},
|
|
|
|
/**
|
|
* Determines whether minimalist or general-purpose search UI is to be
|
|
* displayed when the find bar is activated.
|
|
*
|
|
* @param showMinimalUI
|
|
* true if minimalist UI should be used, false if general-purpose UI
|
|
* should be used
|
|
*/
|
|
updateFindUI: function (showMinimalUI)
|
|
{
|
|
this.mUsingMinimalUI = showMinimalUI;
|
|
|
|
var findBar = document.getElementById("FindToolbar");
|
|
for (var i = 0; i < findBar.childNodes.length; i++) {
|
|
var node = findBar.childNodes[i];
|
|
if (node.className == "find-fast")
|
|
continue;
|
|
|
|
node.hidden = showMinimalUI;
|
|
}
|
|
|
|
var findLabel = document.getElementById("find-label");
|
|
if (showMinimalUI)
|
|
findLabel.value = this.mFastFindStr;
|
|
else
|
|
findLabel.value = this.mNormalFindStr;
|
|
},
|
|
|
|
updateFoundLink: function (res)
|
|
{
|
|
var val = document.getElementById("find-field").value;
|
|
if (res == Components.interfaces.nsITypeAheadFind.FIND_NOTFOUND || !val) {
|
|
this.setFoundLink(null);
|
|
this.mFoundEditable = null;
|
|
this.mCurrentWindow = null;
|
|
} else {
|
|
this.setFoundLink(getBrowser().fastFind.foundLink);
|
|
this.mFoundEditable = getBrowser().fastFind.foundEditable;
|
|
this.mCurrentWindow = getBrowser().fastFind.currentWindow;
|
|
}
|
|
},
|
|
|
|
find: function (val)
|
|
{
|
|
if (!val)
|
|
val = document.getElementById("find-field").value;
|
|
|
|
this.enableFindButtons(val);
|
|
|
|
var highlightBtn = document.getElementById("highlight");
|
|
if (highlightBtn.checked)
|
|
this.setHighlightTimeout();
|
|
|
|
this.setCaseSensitivity(val);
|
|
|
|
var fastFind = getBrowser().fastFind;
|
|
var res = fastFind.find(val, this.mFindMode == FIND_LINKS);
|
|
this.updateFoundLink(res);
|
|
this.updateStatus(res, true);
|
|
|
|
if (this.mFindMode != FIND_NORMAL)
|
|
this.setFindCloseTimeout();
|
|
},
|
|
|
|
flashFindBar: function ()
|
|
{
|
|
var findToolbar = document.getElementById("FindToolbar");
|
|
if (this.mFlashFindBarCount-- == 0) {
|
|
clearInterval(this.mFlashFindBarTimeout);
|
|
findToolbar.removeAttribute("flash");
|
|
this.mFlashFindBarCount = 6;
|
|
return;
|
|
}
|
|
|
|
findToolbar.setAttribute("flash",
|
|
(this.mFlashFindBarCount % 2 == 0) ?
|
|
"false" : "true");
|
|
},
|
|
|
|
onFindCmd: function ()
|
|
{
|
|
try {
|
|
this.openFindBar();
|
|
}
|
|
catch (e) {
|
|
}
|
|
this.setFindMode(FIND_NORMAL);
|
|
if (this.mFlashFindBar) {
|
|
this.mFlashFindBarTimeout = setInterval(findBar_FlashFindBar, 500);
|
|
var prefService =
|
|
Components.classes["@mozilla.org/preferences-service;1"]
|
|
.getService(Components.interfaces.nsIPrefBranch);
|
|
|
|
prefService.setIntPref("accessibility.typeaheadfind.flashBar",
|
|
--this.mFlashFindBar);
|
|
}
|
|
this.selectFindBar();
|
|
this.focusFindBar();
|
|
},
|
|
|
|
onFindAgainCmd: function ()
|
|
{
|
|
var findString = getBrowser().findString;
|
|
if (!findString) {
|
|
this.onFindCmd();
|
|
return;
|
|
}
|
|
|
|
var res = this.findNext();
|
|
if (res == Components.interfaces.nsITypeAheadFind.FIND_NOTFOUND) {
|
|
try {
|
|
var opened = this.openFindBar(this.mUsingMinimalUI);
|
|
}
|
|
catch(e) {
|
|
}
|
|
if (opened) {
|
|
this.focusFindBar();
|
|
this.selectFindBar();
|
|
if (this.mFindMode != FIND_NORMAL)
|
|
this.setFindCloseTimeout();
|
|
|
|
this.updateStatus(res, true);
|
|
}
|
|
}
|
|
},
|
|
|
|
onFindPreviousCmd: function ()
|
|
{
|
|
var findString = getBrowser().findString;
|
|
if (!findString) {
|
|
this.onFindCmd();
|
|
return;
|
|
}
|
|
|
|
var res = this.findPrevious();
|
|
if (res == Components.interfaces.nsITypeAheadFind.FIND_NOTFOUND) {
|
|
try {
|
|
var opened = this.openFindBar(this.mUsingMinimalUI);
|
|
}
|
|
catch (e) {
|
|
}
|
|
if (opened) {
|
|
this.focusFindBar();
|
|
this.selectFindBar();
|
|
if (this.mFindMode != FIND_NORMAL)
|
|
this.setFindCloseTimeout();
|
|
|
|
this.updateStatus(res, false);
|
|
}
|
|
}
|
|
},
|
|
|
|
setHighlightTimeout: function ()
|
|
{
|
|
if (this.mHighlightTimeout)
|
|
clearTimeout(this.mHighlightTimeout);
|
|
this.mHighlightTimeout =
|
|
setTimeout(function() {
|
|
gFindBar.toggleHighlight(false);
|
|
gFindBar.toggleHighlight(true);
|
|
}, 500);
|
|
},
|
|
|
|
isFindBarVisible: function ()
|
|
{
|
|
var findBar = document.getElementById("FindToolbar");
|
|
return !findBar.hidden;
|
|
},
|
|
|
|
findNext: function ()
|
|
{
|
|
var fastFind = getBrowser().fastFind;
|
|
var res = fastFind.findNext();
|
|
this.updateFoundLink(res);
|
|
this.updateStatus(res, true);
|
|
|
|
if (this.mFindMode != FIND_NORMAL && this.isFindBarVisible())
|
|
this.setFindCloseTimeout();
|
|
|
|
return res;
|
|
},
|
|
|
|
findPrevious: function ()
|
|
{
|
|
var fastFind = getBrowser().fastFind;
|
|
var res = fastFind.findPrevious();
|
|
this.updateFoundLink(res);
|
|
this.updateStatus(res, false);
|
|
|
|
if (this.mFindMode != FIND_NORMAL && this.isFindBarVisible())
|
|
this.setFindCloseTimeout();
|
|
|
|
return res;
|
|
},
|
|
|
|
updateStatus: function (res, findNext)
|
|
{
|
|
var findBar = document.getElementById("FindToolbar");
|
|
var field = document.getElementById("find-field");
|
|
var statusIcon = document.getElementById("find-status-icon");
|
|
var statusText = document.getElementById("find-status");
|
|
switch(res) {
|
|
case Components.interfaces.nsITypeAheadFind.FIND_WRAPPED:
|
|
statusIcon.setAttribute("status", "wrapped");
|
|
statusText.value =
|
|
findNext ? this.mWrappedToTopStr : this.mWrappedToBottomStr;
|
|
break;
|
|
case Components.interfaces.nsITypeAheadFind.FIND_NOTFOUND:
|
|
statusIcon.setAttribute("status", "notfound");
|
|
statusText.value = this.mNotFoundStr;
|
|
field.setAttribute("status", "notfound");
|
|
break;
|
|
case Components.interfaces.nsITypeAheadFind.FIND_FOUND:
|
|
default:
|
|
statusIcon.removeAttribute("status");
|
|
statusText.value = "";
|
|
field.removeAttribute("status");
|
|
break;
|
|
}
|
|
},
|
|
|
|
setFindCloseTimeout: function ()
|
|
{
|
|
if (this.mQuickFindTimeout)
|
|
clearTimeout(this.mQuickFindTimeout);
|
|
|
|
// Don't close the find toolbar while IME is composing.
|
|
if (this.mIsIMEComposing) {
|
|
this.mQuickFindTimeout = null;
|
|
return;
|
|
}
|
|
|
|
this.mQuickFindTimeout =
|
|
setTimeout(function() {
|
|
if (gFindBar.mFindMode != FIND_NORMAL)
|
|
gFindBar.closeFindBar();
|
|
},
|
|
this.mQuickFindTimeoutLength);
|
|
},
|
|
|
|
findBarOnDrop: function (evt)
|
|
{
|
|
nsDragAndDrop.drop(evt, this.mFindbarObserver);
|
|
},
|
|
|
|
onFindBarCompositionStart: function (evt)
|
|
{
|
|
this.mIsIMEComposing = true;
|
|
// Don't close the find toolbar while IME is composing.
|
|
if (this.mQuickFindTimeout) {
|
|
clearTimeout(this.mQuickFindTimeout);
|
|
this.mQuickFindTimeout = null;
|
|
}
|
|
},
|
|
|
|
onFindBarCompositionEnd: function (evt)
|
|
{
|
|
this.mIsIMEComposing = false;
|
|
if (this.mFindMode != FIND_NORMAL && this.isFindBarVisible())
|
|
this.setFindCloseTimeout();
|
|
},
|
|
|
|
setFindMode: function (mode)
|
|
{
|
|
this.mFindMode = mode;
|
|
}
|
|
};
|
|
|
|
// ================
|
|
// Event Handlers
|
|
// ================
|
|
function findBar_OnDrop(aEvt)
|
|
{
|
|
gFindBar.findBarOnDrop(aEvt);
|
|
}
|
|
|
|
function findBar_OnBrowserKeyPress(aEvt)
|
|
{
|
|
gFindBar.onBrowserKeyPress(aEvt);
|
|
}
|
|
|
|
function findBar_OnBrowserMouseUp(aEvt)
|
|
{
|
|
gFindBar.onBrowserMouseUp(aEvt);
|
|
}
|
|
|
|
// ======================
|
|
// Timer Event Handlers
|
|
// ======================
|
|
function findBar_DelayedCloseFindBar()
|
|
{
|
|
gFindBar.delayedCloseFindBar();
|
|
}
|
|
|
|
function findBar_UpdateStatusBar()
|
|
{
|
|
gFindBar.updateStatusBar();
|
|
}
|
|
|
|
function findBar_FlashFindBar()
|
|
{
|
|
gFindBar.flashFindBar();
|
|
}
|