mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2025-12-23 11:58:00 +01:00
Resurrect my range finder, Part III: Search highlighting.
This commit is contained in:
@@ -8,6 +8,19 @@ const Ci = Components.interfaces;
|
|||||||
const Cr = Components.results;
|
const Cr = Components.results;
|
||||||
const Cu = Components.utils;
|
const Cu = Components.utils;
|
||||||
|
|
||||||
|
// TODO: Move to liberator.
|
||||||
|
function setTimeout(callback, timeout, self) {
|
||||||
|
function target() {
|
||||||
|
try {
|
||||||
|
callback.call(self);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
liberator.reportError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return window.setTimeout(target, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
function array(obj) {
|
function array(obj) {
|
||||||
if (isgenerator(obj))
|
if (isgenerator(obj))
|
||||||
obj = [k for (k in obj)];
|
obj = [k for (k in obj)];
|
||||||
@@ -230,8 +243,9 @@ function Class() {
|
|||||||
if (callable(args[0]))
|
if (callable(args[0]))
|
||||||
superclass = args.shift();
|
superclass = args.shift();
|
||||||
|
|
||||||
var Constructor = eval("(function " + (name || superclass.name) +
|
var Constructor = eval("(function " + (name || superclass.name).replace(/\W/g, "_") +
|
||||||
String.substr(constructor, 20) + ")");
|
String.substr(constructor, 20) + ")");
|
||||||
|
Constructor.name = name || superclass.name;
|
||||||
|
|
||||||
if (!('init' in superclass.prototype)) {
|
if (!('init' in superclass.prototype)) {
|
||||||
var superc = superclass;
|
var superc = superclass;
|
||||||
|
|||||||
@@ -430,10 +430,13 @@ const Finder = Module("finder", {
|
|||||||
"Highlight previous search pattern matches",
|
"Highlight previous search pattern matches",
|
||||||
"boolean", "false", {
|
"boolean", "false", {
|
||||||
setter: function (value) {
|
setter: function (value) {
|
||||||
if (value)
|
try {
|
||||||
finder.highlight();
|
if (value)
|
||||||
else
|
finder.highlight();
|
||||||
finder.clear();
|
else
|
||||||
|
finder.clear();
|
||||||
|
}
|
||||||
|
catch (e) {}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -464,45 +467,36 @@ const RangeFinder = Module("rangefinder", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
openPrompt: function (mode) {
|
openPrompt: function (mode) {
|
||||||
let backwards;
|
let backwards = mode == modes.FIND_BACKWARD;
|
||||||
if (mode == modes.FIND_BACKWARD) {
|
commandline.open(backwards ? "?" : "/", "", mode);
|
||||||
commandline.open("?", "", modes.FIND_BACKWARD);
|
|
||||||
backwards = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
commandline.open("/", "", modes.FIND_FORWARD);
|
|
||||||
backwards = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.find("", backwards);
|
this.find("", backwards);
|
||||||
// TODO: focus the top of the currently visible screen
|
|
||||||
},
|
},
|
||||||
|
|
||||||
find: function (str, backwards) {
|
find: function (str, backwards) {
|
||||||
try {
|
|
||||||
let caseSensitive = false;
|
let caseSensitive = false;
|
||||||
|
if (this.rangeFind)
|
||||||
|
this.clear();
|
||||||
this.rangeFind = RangeFind(caseSensitive, backwards);
|
this.rangeFind = RangeFind(caseSensitive, backwards);
|
||||||
|
|
||||||
if (!this.rangeFind.search(str))
|
if (!this.rangeFind.search(str))
|
||||||
setTimeout(function () { liberator.echoerr("E486: Pattern not found: " + str); }, 0);
|
setTimeout(function () { liberator.echoerr("E486: Pattern not found: " + str); }, 0);
|
||||||
|
|
||||||
return this.rangeFind.found;
|
return this.rangeFind.found;
|
||||||
} catch(e) { liberator.reportError(e) }
|
|
||||||
},
|
},
|
||||||
|
|
||||||
findAgain: function (reverse) {
|
findAgain: function (reverse) {
|
||||||
if (!this.rangeFind || !this.rangeFind.search(null, reverse))
|
if (!this.rangeFind)
|
||||||
|
this.find(this._lastSearchString);
|
||||||
|
else if (!this.rangeFind.search(null, reverse))
|
||||||
liberator.echoerr("E486: Pattern not found: " + lastSearchPattern);
|
liberator.echoerr("E486: Pattern not found: " + lastSearchPattern);
|
||||||
else if (this.rangeFind.wrapped) {
|
else if (this.rangeFind.wrapped) {
|
||||||
// hack needed, because wrapping causes a "scroll" event which clears
|
// hack needed, because wrapping causes a "scroll" event which clears
|
||||||
// our command line
|
// our command line
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
if (rangfinder.rangeFind.backward)
|
let msg = rangfinder.rangeFind.backward ? "search hit TOP, continuing at BOTTOM"
|
||||||
commandline.echo("search hit TOP, continuing at BOTTOM",
|
: "search hit BOTTOM, continuing at TOP";
|
||||||
commandline.HL_WARNINGMSG, commandline.APPEND_TO_MESSAGES);
|
commandline.echo(msg, commandline.HL_WARNINGMSG, commandline.APPEND_TO_MESSAGES);
|
||||||
else
|
|
||||||
commandline.echo("search hit BOTTOM, continuing at TOP",
|
|
||||||
commandline.HL_WARNINGMSG, commandline.APPEND_TO_MESSAGES);
|
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -515,20 +509,14 @@ try {
|
|||||||
|
|
||||||
// Called when the user types a key in the search dialog. Triggers a find attempt if 'incsearch' is set
|
// Called when the user types a key in the search dialog. Triggers a find attempt if 'incsearch' is set
|
||||||
onKeyPress: function (command) {
|
onKeyPress: function (command) {
|
||||||
try {
|
|
||||||
if (options["incsearch"] && this.rangeFind)
|
if (options["incsearch"] && this.rangeFind)
|
||||||
this.rangeFind.search(command);
|
this.rangeFind.search(command);
|
||||||
} catch(e) { liberator.reportError(e); }
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onSubmit: function (command) {
|
onSubmit: function (command) {
|
||||||
// use the last pattern if none specified
|
if (!options["incsearch"] || !this.rangeFind || !this.rangeFind.found) {
|
||||||
if (!command)
|
|
||||||
command = lastSearchPattern;
|
|
||||||
|
|
||||||
if (!options["incsearch"] || !this.rangeFind.found) {
|
|
||||||
this.clear();
|
this.clear();
|
||||||
this.find(command, this.rangeFind.backwards);
|
this.find(command || this._lastSearchString, this._lastSearchBackwards);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._lastSearchBackwards = this.rangeFind.backwards;
|
this._lastSearchBackwards = this.rangeFind.backwards;
|
||||||
@@ -536,7 +524,7 @@ try {
|
|||||||
this._lastSearchString = command;
|
this._lastSearchString = command;
|
||||||
|
|
||||||
if (options["hlsearch"])
|
if (options["hlsearch"])
|
||||||
this.highlight(this.rangeFind.searchString);
|
this.highlight();
|
||||||
|
|
||||||
modes.reset();
|
modes.reset();
|
||||||
},
|
},
|
||||||
@@ -558,15 +546,17 @@ try {
|
|||||||
*
|
*
|
||||||
* @param {string} str The string to highlight.
|
* @param {string} str The string to highlight.
|
||||||
*/
|
*/
|
||||||
highlight: function (str) {
|
highlight: function () {
|
||||||
return;
|
if (this.rangeFind)
|
||||||
|
this.rangeFind.highlight();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears all search highlighting.
|
* Clears all search highlighting.
|
||||||
*/
|
*/
|
||||||
clear: function () {
|
clear: function () {
|
||||||
return;
|
if (this.rangeFind)
|
||||||
|
this.rangeFind.highlight(true);
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
}, {
|
}, {
|
||||||
@@ -632,9 +622,9 @@ const RangeFind = Class("RangeFind", {
|
|||||||
this._backward = backward;
|
this._backward = backward;
|
||||||
|
|
||||||
this.ranges = this.makeFrameList(content);
|
this.ranges = this.makeFrameList(content);
|
||||||
this.range = { document: (tabs.localStore.focusedFrame || content).document };
|
this.range = RangeFind.Range(tabs.localStore.focusedFrame || content);
|
||||||
|
|
||||||
this.startRange = (this.selection.rangeCount ? this.selection.getRangeAt(0) : this.ranges[0].range).cloneRange();
|
this.startRange = (this.range.selection.rangeCount ? this.range.selection.getRangeAt(0) : this.ranges[0].range).cloneRange();
|
||||||
this.startRange.collapse(!backward);
|
this.startRange.collapse(!backward);
|
||||||
this.range = this.findRange(this.startRange);
|
this.range = this.findRange(this.startRange);
|
||||||
this.ranges.first = this.range;
|
this.ranges.first = this.range;
|
||||||
@@ -645,17 +635,6 @@ const RangeFind = Class("RangeFind", {
|
|||||||
this.found = false;
|
this.found = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
get docShell() {
|
|
||||||
for (let shell in iter(getBrowser().docShell.getDocShellEnumerator(Ci.nsIDocShellTreeItem.typeAll, Ci.nsIDocShell.ENUMERATE_FORWARDS)))
|
|
||||||
if (shell.QueryInterface(nsIWebNavigation).document == this.range.document)
|
|
||||||
return shell;
|
|
||||||
},
|
|
||||||
get selectionController() this.docShell
|
|
||||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
||||||
.getInterface(Ci.nsISelectionDisplay)
|
|
||||||
.QueryInterface(Ci.nsISelectionController),
|
|
||||||
get selection() this.selectionController.getSelection(Ci.nsISelectionController.SELECTION_NORMAL),
|
|
||||||
|
|
||||||
sameDocument: function (r1, r2) r1 && r2 && r1.endContainer.ownerDocument == r2.endContainer.ownerDocument,
|
sameDocument: function (r1, r2) r1 && r2 && r1.endContainer.ownerDocument == r2.endContainer.ownerDocument,
|
||||||
|
|
||||||
compareRanges: function (r1, r2)
|
compareRanges: function (r1, r2)
|
||||||
@@ -679,34 +658,25 @@ const RangeFind = Class("RangeFind", {
|
|||||||
win = win.top;
|
win = win.top;
|
||||||
let frames = [];
|
let frames = [];
|
||||||
|
|
||||||
function endpoint(range, before) {
|
function pushRange(start, end) {
|
||||||
range = range.cloneRange();
|
frames.push(RangeFind.Range(start, end, frames.length));
|
||||||
range.collapse(before);
|
|
||||||
return range;
|
|
||||||
}
|
|
||||||
function pushRange(start, end, win) {
|
|
||||||
let scroll = Point(win.pageXOffset, win.pageYOffset);
|
|
||||||
let range = win.document.createRange();
|
|
||||||
range.setStart(start.startContainer, start.startOffset);
|
|
||||||
range.setEnd(end.startContainer, end.startOffset);
|
|
||||||
frames.push({ range: range, index: frames.length, window: win, document: win.document, scroll: scroll });
|
|
||||||
}
|
}
|
||||||
function rec(win) {
|
function rec(win) {
|
||||||
let doc = win.document;
|
let doc = win.document;
|
||||||
let pageRange = doc.createRange();
|
let pageRange = doc.createRange();
|
||||||
pageRange.setStartBefore(doc.body || doc.documentElement.lastChild);
|
pageRange.setStartBefore(doc.body || doc.documentElement.lastChild);
|
||||||
pageRange.setEndAfter(doc.body || doc.documentElement.lastChild);
|
pageRange.setEndAfter(doc.body || doc.documentElement.lastChild);
|
||||||
let pageStart = endpoint(pageRange, true);
|
let pageStart = RangeFind.endpoint(pageRange, true);
|
||||||
let pageEnd = endpoint(pageRange, false);
|
let pageEnd = RangeFind.endpoint(pageRange, false);
|
||||||
|
|
||||||
for (let frame in util.Array.itervalues(win.frames)) {
|
for (let frame in util.Array.itervalues(win.frames)) {
|
||||||
let range = doc.createRange();
|
let range = doc.createRange();
|
||||||
range.selectNode(frame.frameElement);
|
range.selectNode(frame.frameElement);
|
||||||
pushRange(pageStart, endpoint(range, true), win);
|
pushRange(pageStart, RangeFind.endpoint(range, true));
|
||||||
pageStart = endpoint(range, false);
|
pageStart = RangeFind.endpoint(range, false);
|
||||||
rec(frame);
|
rec(frame);
|
||||||
}
|
}
|
||||||
pushRange(pageStart, pageEnd, win);
|
pushRange(pageStart, pageEnd);
|
||||||
}
|
}
|
||||||
rec(win);
|
rec(win);
|
||||||
return frames;
|
return frames;
|
||||||
@@ -749,7 +719,23 @@ const RangeFind = Class("RangeFind", {
|
|||||||
get searchString() this.lastString,
|
get searchString() this.lastString,
|
||||||
get backward() this.finder.findBackwards,
|
get backward() this.finder.findBackwards,
|
||||||
|
|
||||||
search: function (word, reverse) {
|
__iterator__: function () {
|
||||||
|
let range = this.range;
|
||||||
|
let lastRange = this.lastRange
|
||||||
|
try {
|
||||||
|
this.range = this.ranges[0];
|
||||||
|
this.lastRange = null;
|
||||||
|
var res;
|
||||||
|
while (res = this.search(null, this._backward, true))
|
||||||
|
yield res;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
this.range = range;
|
||||||
|
this.lastRange = lastRange;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
search: function (word, reverse, private) {
|
||||||
this.finder.findBackwards = reverse ? !this._backward : this._backward;
|
this.finder.findBackwards = reverse ? !this._backward : this._backward;
|
||||||
let again = word == null;
|
let again = word == null;
|
||||||
if (again)
|
if (again)
|
||||||
@@ -758,9 +744,10 @@ const RangeFind = Class("RangeFind", {
|
|||||||
word = word.toLowerCase();
|
word = word.toLowerCase();
|
||||||
|
|
||||||
if (!again && (word == "" || word.indexOf(this.lastString) != 0 || this.backward)) {
|
if (!again && (word == "" || word.indexOf(this.lastString) != 0 || this.backward)) {
|
||||||
this.unhighlight();
|
if (!private)
|
||||||
|
this.range.deselect();
|
||||||
if (word == "")
|
if (word == "")
|
||||||
this.deScroll(this.range);
|
this.range.descroll()
|
||||||
this.lastRange = this.startRange;
|
this.lastRange = this.startRange;
|
||||||
this.range = this.ranges.first;
|
this.range = this.ranges.first;
|
||||||
}
|
}
|
||||||
@@ -772,6 +759,8 @@ const RangeFind = Class("RangeFind", {
|
|||||||
let idx = this.range.index;
|
let idx = this.range.index;
|
||||||
for (let i in this.backward ? util.range(idx + 1, -1, -1) : util.range(idx, this.ranges.length))
|
for (let i in this.backward ? util.range(idx + 1, -1, -1) : util.range(idx, this.ranges.length))
|
||||||
yield i;
|
yield i;
|
||||||
|
if (private)
|
||||||
|
return;
|
||||||
this.wrapped = true;
|
this.wrapped = true;
|
||||||
for (let i in this.backward ? util.range(this.ranges.length, idx, -1) : util.range(0, idx))
|
for (let i in this.backward ? util.range(this.ranges.length, idx, -1) : util.range(0, idx))
|
||||||
yield i;
|
yield i;
|
||||||
@@ -779,16 +768,24 @@ const RangeFind = Class("RangeFind", {
|
|||||||
for (let i in indices.call(this)) {
|
for (let i in indices.call(this)) {
|
||||||
this.range = this.ranges[i];
|
this.range = this.ranges[i];
|
||||||
let start = this.sameDocument(this.lastRange, this.range.range) ?
|
let start = this.sameDocument(this.lastRange, this.range.range) ?
|
||||||
this.lastRange : this.range.range;
|
RangeFind.endpoint(this.lastRange, this.backward) :
|
||||||
|
RangeFind.endpoint(this.range.range, !this.backward);;
|
||||||
|
|
||||||
var range = this.finder.Find(word, this.range.range, start, this.range.range);
|
var range = this.finder.Find(word, this.range.range, start, this.range.range);
|
||||||
if (range && this.compareRanges(range, this.range.range) <= 0)
|
if (range)
|
||||||
break;
|
break;
|
||||||
this.deScroll(this.range);
|
if (!private) {
|
||||||
this.unhighlight();
|
this.range.descroll();
|
||||||
|
this.range.deselect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (range)
|
||||||
|
this.lastRange = range.cloneRange();
|
||||||
|
if (private)
|
||||||
|
return range;
|
||||||
|
|
||||||
this.lastString = word;
|
this.lastString = word;
|
||||||
if (range == null) {
|
if (range == null) {
|
||||||
this.cancel();
|
this.cancel();
|
||||||
@@ -796,159 +793,121 @@ const RangeFind = Class("RangeFind", {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
this.wrapped = false;
|
this.wrapped = false;
|
||||||
this.selection.removeAllRanges();
|
this.range.selection.removeAllRanges();
|
||||||
this.selection.addRange(range);
|
this.range.selection.addRange(range);
|
||||||
this.selectionController.scrollSelectionIntoView(this.selectionController.SELECTION_NORMAL, 0, false);
|
this.range.selectionController.scrollSelectionIntoView(
|
||||||
this.lastRange = range.cloneRange();
|
this.range.selectionController.SELECTION_NORMAL, 0, false);
|
||||||
this.found = true;
|
this.found = true;
|
||||||
return range;
|
return range;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
highlight: function (clear) {
|
||||||
|
if (!this.lastString)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!clear)
|
||||||
|
for (let range in values(this.ranges))
|
||||||
|
if (util.evaluateXPath("//@liberator:highlight[1][.='Search']").snapshotLength)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let span = util.xmlToDom(<span highlight="Search"/>, this.range.document);
|
||||||
|
|
||||||
|
function highlight(range) {
|
||||||
|
let startContainer = range.startContainer;
|
||||||
|
let startOffset = range.startOffset;
|
||||||
|
let node = startContainer.ownerDocument.importNode(span, true);
|
||||||
|
|
||||||
|
let docfrag = range.extractContents();
|
||||||
|
let before = startContainer.splitText(startOffset);
|
||||||
|
let parent = before.parentNode;
|
||||||
|
node.appendChild(docfrag);
|
||||||
|
parent.insertBefore(node, before);
|
||||||
|
range.selectNode(node);
|
||||||
|
}
|
||||||
|
function unhighlight(range) {
|
||||||
|
let elem = range.startContainer;
|
||||||
|
while (!(elem instanceof Element) && elem.parentNode)
|
||||||
|
elem = elem.parentNode;
|
||||||
|
if (elem.getAttributeNS(NS.uri, "highlight") != "Search")
|
||||||
|
return;
|
||||||
|
|
||||||
|
let docfrag = range.extractContents();
|
||||||
|
|
||||||
|
let parent = elem.parentNode;
|
||||||
|
parent.replaceChild(docfrag, elem);
|
||||||
|
parent.normalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.range.save();
|
||||||
|
liberator.dump(String.quote(this.range.initialSelection));
|
||||||
|
let action = clear ? unhighlight : highlight;
|
||||||
|
for (let r in this) {
|
||||||
|
action(r);
|
||||||
|
this.lastRange = r;
|
||||||
|
}
|
||||||
|
this.range.deselect();
|
||||||
|
if (!clear)
|
||||||
|
this.search(null, false);
|
||||||
|
},
|
||||||
|
|
||||||
cancel: function () {
|
cancel: function () {
|
||||||
if (false) // Later.
|
this.range.deselect();
|
||||||
this.selection.addRange(this.startRange);
|
this.range.descroll()
|
||||||
this.unhighlight();
|
|
||||||
this.deScroll(this.range);
|
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
Range: Class("RangeFind.Range", {
|
||||||
|
init: function (start, end, index) {
|
||||||
|
if (start instanceof Ci.nsIDOMWindow) { // Kludge
|
||||||
|
this.document = start.document;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
unhighlight: function () {
|
this.index = index;
|
||||||
this.selection.removeAllRanges();
|
|
||||||
},
|
|
||||||
|
|
||||||
deScroll: function (range) {
|
this.document = start.startContainer.ownerDocument;
|
||||||
range.window.scrollTo(range.scroll.x, range.scroll.y);
|
this.window = this.document.defaultView;
|
||||||
|
|
||||||
|
this.range = this.document.createRange();
|
||||||
|
this.range.setStart(start.startContainer, start.startOffset);
|
||||||
|
this.range.setEnd(end.startContainer, end.startOffset);
|
||||||
|
|
||||||
|
this.save();
|
||||||
|
},
|
||||||
|
|
||||||
|
save: function () {
|
||||||
|
this.scroll = Point(this.window.pageXOffset, this.window.pageYOffset);
|
||||||
|
|
||||||
|
this.initialSelection = null;
|
||||||
|
if (this.selection.rangeCount)
|
||||||
|
this.initialSelection = this.selection.getRangeAt(0);
|
||||||
|
},
|
||||||
|
|
||||||
|
descroll: function (range) {
|
||||||
|
this.window.scrollTo(this.scroll.x, this.scroll.y);
|
||||||
|
},
|
||||||
|
|
||||||
|
deselect: function () {
|
||||||
|
this.selection.removeAllRanges();
|
||||||
|
if (this.initialSelection)
|
||||||
|
this.selection.addRange(this.initialSelection);
|
||||||
|
},
|
||||||
|
|
||||||
|
get docShell() {
|
||||||
|
for (let shell in iter(getBrowser().docShell.getDocShellEnumerator(Ci.nsIDocShellTreeItem.typeAll, Ci.nsIDocShell.ENUMERATE_FORWARDS)))
|
||||||
|
if (shell.QueryInterface(nsIWebNavigation).document == this.document)
|
||||||
|
return shell;
|
||||||
|
},
|
||||||
|
get selectionController() this.docShell
|
||||||
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Ci.nsISelectionDisplay)
|
||||||
|
.QueryInterface(Ci.nsISelectionController),
|
||||||
|
get selection() this.selectionController.getSelection(Ci.nsISelectionController.SELECTION_NORMAL),
|
||||||
|
}),
|
||||||
|
endpoint: function (range, before) {
|
||||||
|
range = range.cloneRange();
|
||||||
|
range.collapse(before);
|
||||||
|
return range;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/* Stolen from toolkit.jar in Firefox, for the time being. The private
|
|
||||||
* methods were unstable, and changed. The new version is not remotely
|
|
||||||
* compatible with what we do.
|
|
||||||
* The following only applies to this object, and may not be
|
|
||||||
* necessary, or accurate, but, just in case:
|
|
||||||
* 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
|
|
||||||
* by 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>
|
|
||||||
* Asaf Romano <mano@mozilla.com>
|
|
||||||
* Ehsan Akhgari <ehsan.akhgari@gmail.com>
|
|
||||||
* Graeme McCutcheon <graememcc_firefox@graeme-online.co.uk>
|
|
||||||
*/
|
|
||||||
const Highlighter = Class("Highlighter", {
|
|
||||||
init: function (doc) {
|
|
||||||
this.doc = doc;
|
|
||||||
},
|
|
||||||
|
|
||||||
doc: null,
|
|
||||||
|
|
||||||
spans: [],
|
|
||||||
|
|
||||||
search: function (word, matchCase) {
|
|
||||||
var finder = services.create("find");
|
|
||||||
|
|
||||||
var range;
|
|
||||||
while ((range = finder.Find(word, this.searchRange, this.startPt, this.endPt)))
|
|
||||||
yield range;
|
|
||||||
},
|
|
||||||
|
|
||||||
highlightDoc: function highlightDoc(win, word) {
|
|
||||||
Array.forEach(win.frames, function (frame) this.highlightDoc(frame, word), this);
|
|
||||||
|
|
||||||
var doc = win.document;
|
|
||||||
if (!doc || !(doc instanceof HTMLDocument))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!word) {
|
|
||||||
let elems = this._highlighter.spans;
|
|
||||||
for (let i = elems.length; --i >= 0;) {
|
|
||||||
let elem = elems[i];
|
|
||||||
let docfrag = doc.createDocumentFragment();
|
|
||||||
let next = elem.nextSibling;
|
|
||||||
let parent = elem.parentNode;
|
|
||||||
|
|
||||||
let child;
|
|
||||||
while (child = elem.firstChild)
|
|
||||||
docfrag.appendChild(child);
|
|
||||||
|
|
||||||
parent.removeChild(elem);
|
|
||||||
parent.insertBefore(docfrag, next);
|
|
||||||
parent.normalize();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var baseNode = <span highlight="Search"/>;
|
|
||||||
baseNode = util.xmlToDom(baseNode, window.content.document);
|
|
||||||
|
|
||||||
var body = doc.body;
|
|
||||||
var count = body.childNodes.length;
|
|
||||||
this.searchRange = doc.createRange();
|
|
||||||
this.startPt = doc.createRange();
|
|
||||||
this.endPt = doc.createRange();
|
|
||||||
|
|
||||||
this.searchRange.setStart(body, 0);
|
|
||||||
this.searchRange.setEnd(body, count);
|
|
||||||
|
|
||||||
this.startPt.setStart(body, 0);
|
|
||||||
this.startPt.setEnd(body, 0);
|
|
||||||
this.endPt.setStart(body, count);
|
|
||||||
this.endPt.setEnd(body, count);
|
|
||||||
|
|
||||||
liberator.interrupted = false;
|
|
||||||
let n = 0;
|
|
||||||
for (let retRange in this.search(word, this._caseSensitive)) {
|
|
||||||
// Highlight
|
|
||||||
var nodeSurround = baseNode.cloneNode(true);
|
|
||||||
var node = this.highlight(retRange, nodeSurround);
|
|
||||||
this.startPt = node.ownerDocument.createRange();
|
|
||||||
this.startPt.setStart(node, node.childNodes.length);
|
|
||||||
this.startPt.setEnd(node, node.childNodes.length);
|
|
||||||
if (n++ % 20 == 0)
|
|
||||||
liberator.threadYield(true);
|
|
||||||
if (liberator.interrupted)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
highlight: function highlight(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);
|
|
||||||
this.spans.push(node);
|
|
||||||
return node;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears all search highlighting.
|
|
||||||
*/
|
|
||||||
clear: function () {
|
|
||||||
this.spans.forEach(function (span) {
|
|
||||||
if (span.parentNode) {
|
|
||||||
let el = span.firstChild;
|
|
||||||
while (el) {
|
|
||||||
span.removeChild(el);
|
|
||||||
span.parentNode.insertBefore(el, span);
|
|
||||||
el = span.firstChild;
|
|
||||||
}
|
|
||||||
span.parentNode.removeChild(span);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.spans = [];
|
|
||||||
},
|
|
||||||
|
|
||||||
isHighlighted: function (doc) this.doc == doc && this.spans.length > 0
|
|
||||||
});
|
|
||||||
|
|
||||||
// vim: set fdm=marker sw=4 ts=4 et:
|
// vim: set fdm=marker sw=4 ts=4 et:
|
||||||
|
|||||||
@@ -190,17 +190,17 @@ const Option = Class("Option", {
|
|||||||
else
|
else
|
||||||
scope = this.scope;
|
scope = this.scope;
|
||||||
|
|
||||||
let aValue;
|
let value;
|
||||||
|
|
||||||
if (liberator.has("tabs") && (scope & options.OPTION_SCOPE_LOCAL))
|
if (liberator.has("tabs") && (scope & options.OPTION_SCOPE_LOCAL))
|
||||||
aValue = tabs.options[this.name];
|
value = tabs.options[this.name];
|
||||||
if ((scope & options.OPTION_SCOPE_GLOBAL) && (aValue == undefined))
|
if ((scope & options.OPTION_SCOPE_GLOBAL) && (value == undefined))
|
||||||
aValue = this.globalValue;
|
value = this.globalValue;
|
||||||
|
|
||||||
if (this.getter)
|
if (this.getter)
|
||||||
return liberator.trapErrors(this.getter, this, aValue);
|
return liberator.trapErrors(this.getter, this, value);
|
||||||
|
|
||||||
return aValue;
|
return value;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user