1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
|
/**
* (C) Copyright 2009 Shawn Betts
* (C) Copyright 2009 John J. Foerch <jjfoerch@earthlink.net>
*
* Use, modification, and distribution are subject to the terms specified in the
* COPYING file.
**/
in_module(null);
define_variable("headings_xpath",
"//h1 | //h2 | //h3 | //h4 | //h5 | //h6 | //xhtml:h1 | "+
"//xhtml:h2 | //xhtml:h3 | //xhtml:h4 | //xhtml:h5 | //xhtml:h6",
"The xpath expression used by next-heading and previous-heading to find "+
"headings. Users will rarely need to change the value of this, but it "+
"exists especially for page-modes to override with a site-specific "+
"xpath expression.");
define_variable("scroll_to_heading_wrap", true,
"If true, will wrap to the topmost heading when the viewport is at the "+
"bottom of the document and the user tries to access the next heading. "+
"Does the equivalent thing for \"previous heading\" as well.");
define_browser_object_class("next-heading", null,
function (I) {
let xpr = I.buffer.document.evaluate(
I.local.headings_xpath, I.buffer.document, xpath_lookup_namespace,
Ci.nsIDOMXPathResult.ORDERED_NODE_ITERATOR_TYPE, null),
heading, found = null, foundtop = null,
first = null, firsttop = null;
while ((heading = xpr.iterateNext())) {
let rect = heading.getBoundingClientRect();
if (rect.bottom - rect.top < 2)
continue;
if (! first || rect.top < firsttop) {
first = heading;
firsttop = rect.top;
}
if (rect.top > 2 && (! found || rect.top < foundtop)) {
found = heading;
foundtop = rect.top;
}
}
// scrollY can exceed scrollMaxY
let eod = I.buffer.scrollY - I.buffer.scrollMaxY >= 0;
if ((!found || eod) && scroll_to_heading_wrap)
found = first;
yield co_return(found);
});
define_browser_object_class("previous-heading", null,
function (I) {
let xpr = I.buffer.document.evaluate(
I.local.headings_xpath, I.buffer.document, xpath_lookup_namespace,
Ci.nsIDOMXPathResult.ORDERED_NODE_ITERATOR_TYPE, null),
heading, found = null, foundtop = null,
last = null, lasttop = null;
while ((heading = xpr.iterateNext())) {
let rect = heading.getBoundingClientRect();
if (rect.bottom - rect.top < 2)
continue;
if (rect.top < -1 && (! found || rect.top > foundtop)) {
found = heading;
foundtop = rect.top;
}
if (! last || rect.top > lasttop) {
last = heading;
lasttop = rect.top;
}
}
if (! found && scroll_to_heading_wrap)
found = last;
yield co_return(found);
});
function scroll (I) {
var element = yield read_browser_object(I);
// no scrolling and no error if we failed to get an object.
if (! element)
return;
element.scrollIntoView();
I.window.minibuffer.message(element.textContent);
}
interactive("scroll",
"Generalized scroll command. The amount of scrolling is determined by "+
"the object passed to the command as a browser-object. If the object "+
"is a DOM node, that node will be scrolled to the top of the viewport "+
"if possible.",
scroll);
provide("scroll");
|