diff --git a/common/content/buffer.js b/common/content/buffer.js index 8912ad0a..25c8cfed 100644 --- a/common/content/buffer.js +++ b/common/content/buffer.js @@ -352,6 +352,12 @@ var Buffer = Module("buffer", { return elem.scrollTop * 100 / (elem.scrollHeight - elem.clientHeight); }, + /** + * @property {{ x: number, y: number }} The buffer's current scroll position + * as reported by {@link Buffer.getScrollPosition}. + */ + get scrollPosition() Buffer.getScrollPosition(this.findScrollable(0, false)), + /** * Adds a new section to the page information output. * @@ -705,6 +711,14 @@ var Buffer = Module("buffer", { scrollToPercent: function scrollToPercent(horizontal, vertical) Buffer.scrollToPercent(this.findScrollable(0, vertical == null), horizontal, vertical), + /** + * Scrolls the currently active element to the given horizontal and + * vertical positions. See {@link Buffer.scrollToPosition} for + * parameters. + */ + scrollToPosition: function scrollToPosition(horizontal, vertical) + Buffer.scrollToPosition(this.findScrollable(0, vertical == null), horizontal, vertical), + _scrollByScrollSize: function _scrollByScrollSize(count, direction) { if (count > 0) options["scroll"] = count; @@ -1248,6 +1262,7 @@ var Buffer = Module("buffer", { // Temporary hack. Should be done better. if (elem.ownerDocument == buffer.focusedFrame.document) marks.add("'"); + if (left != null) elem.scrollLeft = left; if (top != null) @@ -1301,7 +1316,7 @@ var Buffer = Module("buffer", { * given direction. */ scrollVertical: function scrollVertical(elem, increment, number) { - let fontSize = parseInt(util.computedStyle(elem).fontSize); + let fontSize = parseInt(util.computedStyle(elem).lineHeight); if (increment == "lines") increment = fontSize; else if (increment == "pages") @@ -1337,6 +1352,50 @@ var Buffer = Module("buffer", { : (elem.scrollHeight - elem.clientHeight) * (vertical / 100)); }, + /** + * Scrolls the currently active element to the given horizontal and + * vertical position. + * + * @param {Element} elem The element to scroll. + * @param {number|null} horizontal The possibly fractional + * line ordinal to scroll to. + * @param {number|null} vertical The possibly fractional + * column ordinal to scroll to. + */ + scrollToPosition: function scrollToPosition(elem, horizontal, vertical) { + let style = util.computedStyle(elem); + Buffer.scrollTo(elem, + horizontal == null ? null : + horizontal == 0 ? 0 : this._exWidth(elem) * horizontal, + vertical == null ? null : parseFloat(style.lineHeight) * vertical); + }, + + /** + * Returns the current scroll position as understood by + * {@link #scrollToPosition}. + * + * @param {Element} elem The element to scroll. + */ + getScrollPosition: function getPosition(elem) { + let style = util.computedStyle(elem); + return { + x: elem.scrollLeft && elem.scrollLeft / this._exWidth(elem), + y: elem.scrollTop / parseFloat(style.lineHeight) + } + }, + + _exWidth: function _exWidth(elem) { + let div = elem.appendChild( + util.xmlToDom(, + elem.ownerDocument)); + try { + return parseFloat(util.computedStyle(div).width); + } + finally { + div.parentNode.removeChild(div); + } + }, + openUploadPrompt: function openUploadPrompt(elem) { io.CommandFileMode(_("buffer.prompt.uploadFile") + " ", { onSubmit: function onSubmit(path) { @@ -2010,12 +2069,12 @@ var Buffer = Module("buffer", { options.add(["nextpattern"], "Patterns to use when guessing the next page in a document sequence", - "regexplist", UTF8("'\\bnext\\b',^>$,^(>>|»)$,^(>|»),(>|»)$,'\\bmore\\b'"), + "regexplist", UTF8(/'\bnext\b',^>$,^(>>|»)$,^(>|»),(>|»)$,'\bmore\b'/.source), { regexpFlags: "i" }); options.add(["previouspattern"], "Patterns to use when guessing the previous page in a document sequence", - "regexplist", UTF8("'\\bprev|previous\\b',^<$,^(<<|«)$,^(<|«),(<|«)$"), + "regexplist", UTF8(/'\bprev|previous\b',^<$,^(<<|«)$,^(<|«),(<|«)$/.source), { regexpFlags: "i" }); options.add(["pageinfo", "pa"], diff --git a/common/content/marks.js b/common/content/marks.js index 8e563283..5af61cc9 100644 --- a/common/content/marks.js +++ b/common/content/marks.js @@ -48,16 +48,16 @@ var Marks = Module("marks", { let win = buffer.focusedFrame; let doc = win.document; - let position = { x: buffer.scrollXPercent / 100, y: buffer.scrollYPercent / 100 }; + let position = buffer.scrollPosition; if (Marks.isURLMark(mark)) { - let res = this._urlMarks.set(mark, { location: doc.documentURI, position: position, tab: Cu.getWeakReference(tabs.getTab()), timestamp: Date.now()*1000 }); + let res = this._urlMarks.set(mark, { location: doc.documentURI, offset: position, tab: Cu.getWeakReference(tabs.getTab()), timestamp: Date.now()*1000 }); if (!silent) dactyl.log(_("mark.addURL", Marks.markToString(mark, res)), 5); } else if (Marks.isLocalMark(mark)) { let marks = this._localMarks.get(doc.documentURI, {}); - marks[mark] = { location: doc.documentURI, position: position, timestamp: Date.now()*1000 }; + marks[mark] = { location: doc.documentURI, offset: position, timestamp: Date.now()*1000 }; this._localMarks.changed(); if (!silent) dactyl.log(_("mark.addLocal", Marks.markToString(mark, marks[mark])), 5); @@ -116,7 +116,7 @@ var Marks = Module("marks", { let doc = tab.linkedBrowser.contentDocument; if (doc.documentURI == mark.location) { dactyl.log(_("mark.jumpingToURL", Marks.markToString(char, mark)), 5); - buffer.scrollToPercent(mark.position.x * 100, mark.position.y * 100); + this._scrollTo(mark); } else { this._pendingJumps.push(mark); @@ -147,13 +147,20 @@ var Marks = Module("marks", { dactyl.assert(mark, _("mark.unset", char)); dactyl.log(_("mark.jumpingToLocal", Marks.markToString(char, mark)), 5); - buffer.scrollToPercent(mark.position.x * 100, mark.position.y * 100); + this._scrollTo(mark); } else dactyl.echoerr(_("mark.invalid")); }, + _scrollTo: function _scrollTo(mark) { + if (mark.position) + buffer.scrollToPercent(mark.position.x * 100, mark.position.y * 100); + else if (mark.offset) + buffer.scrollToPosition(mark.offset.x, mark.offset.y); + }, + /** * List all marks matching *filter*. * @@ -174,18 +181,20 @@ var Marks = Module("marks", { template.tabular( ["Mark", "HPos", "VPos", "File"], ["", "text-align: right", "text-align: right", "color: green"], - ([mark[0], - Math.round(mark[1].position.x * 100) + "%", - Math.round(mark[1].position.y * 100) + "%", - mark[1].location] - for ([, mark] in Iterator(marks))))); + ([name, + mark.position ? Math.round(mark.position.x * 100) + "%" + : Math.round(mark.offset.x), + mark.position ? Math.round(mark.position.y * 100) + "%" + : Math.round(mark.offset.y), + mark.location] + for ([, [name, mark]] in Iterator(marks))))); }, _onPageLoad: function _onPageLoad(event) { let win = event.originalTarget.defaultView; for (let [i, mark] in Iterator(this._pendingJumps)) { if (win && win.location.href == mark.location) { - buffer.scrollToPercent(mark.position.x * 100, mark.position.y * 100); + this._scrollTo(mark); this._pendingJumps.splice(i, 1); return; } @@ -194,10 +203,18 @@ var Marks = Module("marks", { }, { markToString: function markToString(name, mark) { let tab = mark.tab && mark.tab.get(); - return name + ", " + mark.location + - ", (" + Math.round(mark.position.x * 100) + - "%, " + Math.round(mark.position.y * 100) + "%)" + - (tab ? ", tab: " + tabs.index(tab) : ""); + if (mark.position) + return [name, mark.location, + "(" + Math.round(mark.position.x * 100) + "%", + Math.round(mark.position.y * 100) + "%)", + (tab && "tab: " + tabs.index(tab)) + ].filter(util.identity).join(", "); + + return [name, mark.location, + "(" + Math.round(mark.offset.x * 100), + Math.round(mark.offset.y * 100) + ")", + (tab && "tab: " + tabs.index(tab)) + ].filter(util.identity).join(", "); }, isLocalMark: function isLocalMark(mark) /^[a-z`']$/.test(mark), diff --git a/pentadactyl/NEWS b/pentadactyl/NEWS index 7c32c645..db357ee8 100644 --- a/pentadactyl/NEWS +++ b/pentadactyl/NEWS @@ -71,6 +71,7 @@ editing input fields with i_. See :h 'editor'. [b4] • Improved [macro-string] support, including automatic elision of optional elements, and array subscripts. [b4][b7] + • Marks are now stored as line and column ordinals rather than percentages. [b8] • Mapping changes: - It's now possible to map keys in many more modes, including Hint, Multi-line Output, and Menu. [b4]