X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=common%2Fcontent%2Fmarks.js;h=7f6e256e245c6414d43a0a449e5292076a6f234b;hb=247daf849abc85f4cfb10fa358c62c8daf8db95b;hp=aedbfc06b88737379737083f47ec5fe11900f5c6;hpb=eeed0be1a8abf7e3c97f43b63c1d595e940fef21;p=dactyl.git diff --git a/common/content/marks.js b/common/content/marks.js index aedbfc0..7f6e256 100644 --- a/common/content/marks.js +++ b/common/content/marks.js @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2011 by Kris Maglione +// Copyright (c) 2008-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -30,9 +30,24 @@ var Marks = Module("marks", { */ get all() iter(this._localMarks.get(this.localURI) || {}, this._urlMarks - ).sort(function (a, b) String.localeCompare(a[0], b[0])), + ).sort((a, b) => String.localeCompare(a[0], b[0])), - get localURI() buffer.focusedFrame.document.documentURI, + get localURI() buffer.focusedFrame.document.documentURI.replace(/#.*/, ""), + + Mark: function Mark(params={}) { + let win = buffer.focusedFrame; + let doc = win.document; + + params.location = doc.documentURI.replace(/#.*/, ""), + params.offset = buffer.scrollPosition; + params.path = DOM(buffer.findScrollable(0, false)).xpath; + params.timestamp = Date.now() * 1000; + params.equals = function (m) this.location == m.location + && this.offset.x == m.offset.x + && this.offset.y == m.offset.y + && this.path == m.path; + return params; + }, /** * Add a named mark for the current buffer, at its current position. @@ -41,29 +56,90 @@ var Marks = Module("marks", { * selected from. If it matches [a-z], it's a local mark, and can * only be recalled from a buffer with a matching URL. * - * @param {string} mark The mark name. + * @param {string} name The mark name. * @param {boolean} silent Whether to output error messages. */ - add: function (mark, silent) { - let win = buffer.focusedFrame; - let doc = win.document; - - let position = { x: buffer.scrollXPercent / 100, y: buffer.scrollYPercent / 100 }; - - if (Marks.isURLMark(mark)) { - let res = this._urlMarks.set(mark, { location: doc.documentURI, position: position, tab: Cu.getWeakReference(tabs.getTab()), timestamp: Date.now()*1000 }); - if (!silent) - dactyl.log("Adding URL mark: " + Marks.markToString(mark, res), 5); + add: function (name, silent) { + let mark = this.Mark(); + + if (Marks.isURLMark(name)) { + // FIXME: Disabled due to cross-compartment magic. + // mark.tab = util.weakReference(tabs.getTab()); + this._urlMarks.set(name, mark); + var message = "mark.addURL"; } - else if (Marks.isLocalMark(mark)) { - let marks = this._localMarks.get(doc.documentURI, {}); - marks[mark] = { location: doc.documentURI, position: position, timestamp: Date.now()*1000 }; + else if (Marks.isLocalMark(name)) { + this._localMarks.get(mark.location, {})[name] = mark; this._localMarks.changed(); - if (!silent) - dactyl.log("Adding local mark: " + Marks.markToString(mark, marks[mark]), 5); + message = "mark.addLocal"; + } + + if (!silent) + dactyl.log(_(message, Marks.markToString(name, mark)), 5); + return mark; + }, + + /** + * Push the current buffer position onto the jump stack. + * + * @param {string} reason The reason for this scroll event. Multiple + * scroll events for the same reason are coalesced. @optional + */ + push: function push(reason) { + let store = buffer.localStore; + let jump = store.jumps[store.jumpsIndex]; + + if (reason && jump && jump.reason == reason) + return; + + let mark = this.add("'"); + if (jump && mark.equals(jump.mark)) + return; + + if (!this.jumping) { + store.jumps[++store.jumpsIndex] = { mark: mark, reason: reason }; + store.jumps.length = store.jumpsIndex + 1; + + if (store.jumps.length > this.maxJumps) { + store.jumps = store.jumps.slice(-this.maxJumps); + store.jumpsIndex = store.jumps.length - 1; + } } }, + maxJumps: 200, + + /** + * Jump to the given offset in the jump stack. + * + * @param {number} offset The offset from the current position in + * the jump stack to jump to. + * @returns {number} The actual change in offset. + */ + jump: function jump(offset) { + let store = buffer.localStore; + if (offset < 0 && store.jumpsIndex == store.jumps.length - 1) + this.push(); + + return this.withSavedValues(["jumping"], function _jump() { + this.jumping = true; + let idx = Math.constrain(store.jumpsIndex + offset, 0, store.jumps.length - 1); + let orig = store.jumpsIndex; + + if (idx in store.jumps && !dactyl.trapErrors("_scrollTo", this, store.jumps[idx].mark)) + store.jumpsIndex = idx; + return store.jumpsIndex - orig; + }); + }, + + get jumps() { + let store = buffer.localStore; + return { + index: store.jumpsIndex, + locations: store.jumps.map(j => j.mark) + }; + }, + /** * Remove all marks matching *filter*. If *special* is given, removes all * local marks. @@ -106,7 +182,7 @@ var Marks = Module("marks", { let tab = mark.tab && mark.tab.get(); if (!tab || !tab.linkedBrowser || tabs.allTabs.indexOf(tab) == -1) for ([, tab] in iter(tabs.visibleTabs, tabs.allTabs)) { - if (tab.linkedBrowser.contentDocument.documentURI === mark.location) + if (tab.linkedBrowser.contentDocument.documentURI.replace(/#.*/, "") === mark.location) break; tab = null; } @@ -114,9 +190,9 @@ var Marks = Module("marks", { if (tab) { tabs.select(tab); let doc = tab.linkedBrowser.contentDocument; - if (doc.documentURI == mark.location) { - dactyl.log("Jumping to URL mark: " + Marks.markToString(char, mark), 5); - buffer.scrollToPercent(mark.position.x * 100, mark.position.y * 100); + if (doc.documentURI.replace(/#.*/, "") == mark.location) { + dactyl.log(_("mark.jumpingToURL", Marks.markToString(char, mark)), 5); + this._scrollTo(mark); } else { this._pendingJumps.push(mark); @@ -146,14 +222,31 @@ var Marks = Module("marks", { let mark = (this._localMarks.get(this.localURI) || {})[char]; dactyl.assert(mark, _("mark.unset", char)); - dactyl.log("Jumping to local mark: " + Marks.markToString(char, mark), 5); - buffer.scrollToPercent(mark.position.x * 100, mark.position.y * 100); + dactyl.log(_("mark.jumpingToLocal", Marks.markToString(char, mark)), 5); + this._scrollTo(mark); } else dactyl.echoerr(_("mark.invalid")); }, + _scrollTo: function _scrollTo(mark) { + if (!mark.path) + var node = buffer.findScrollable(0, (mark.offset || mark.position).x); + else + for (node in DOM.XPath(mark.path, buffer.focusedFrame.document)) + break; + + util.assert(node); + if (node instanceof Element) + DOM(node).scrollIntoView(); + + if (mark.offset) + Buffer.scrollToPosition(node, mark.offset.x, mark.offset.y); + else if (mark.position) + Buffer.scrollToPercent(node, mark.position.x * 100, mark.position.y * 100); + }, + /** * List all marks matching *filter*. * @@ -166,7 +259,7 @@ var Marks = Module("marks", { if (filter.length > 0) { let pattern = util.charListToRegexp(filter, "a-zA-Z"); - marks = marks.filter(function ([k, ]) pattern.test(k)); + marks = marks.filter(([k]) => (pattern.test(k))); dactyl.assert(marks.length > 0, _("mark.noMatching", filter.quote())); } @@ -174,18 +267,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.offset ? Math.round(mark.offset.x) + : Math.round(mark.position.x * 100) + "%", + mark.offset ? Math.round(mark.offset.y) + : Math.round(mark.position.y * 100) + "%", + 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,20 +289,29 @@ 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.offset) + 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(", "); + + 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(", "); }, - isLocalMark: function isLocalMark(mark) /^[a-z`']$/.test(mark), + isLocalMark: bind("test", /^[a-z`']$/), - isURLMark: function isURLMark(mark) /^[A-Z]$/.test(mark) + isURLMark: bind("test", /^[A-Z]$/) }, { events: function () { let appContent = document.getElementById("appcontent"); if (appContent) - events.listen(appContent, "load", marks.closure._onPageLoad, true); + events.listen(appContent, "load", marks.bound._onPageLoad, true); }, mappings: function () { var myModes = config.browserModes; @@ -226,7 +330,7 @@ var Marks = Module("marks", { { arg: true }); }, - commands: function () { + commands: function initCommands() { commands.add(["delm[arks]"], "Delete the specified marks", function (args) { @@ -249,7 +353,7 @@ var Marks = Module("marks", { "Mark current location within the web page", function (args) { let mark = args[0] || ""; - dactyl.assert(mark.length <= 1, _("error.trailing")); + dactyl.assert(mark.length <= 1, _("error.trailingCharacters")); dactyl.assert(/[a-zA-Z]/.test(mark), _("mark.invalid")); marks.add(mark); @@ -266,17 +370,18 @@ var Marks = Module("marks", { }); }, - completion: function () { + completion: function initCompletion() { completion.mark = function mark(context) { function percent(i) Math.round(i * 100); - // FIXME: Line/Column doesn't make sense with % context.title = ["Mark", "HPos VPos File"]; - context.keys.description = function ([, m]) percent(m.position.x) + "% " + percent(m.position.y) + "% " + m.location; + context.keys.description = ([, m]) => (m.offset ? Math.round(m.offset.x) + " " + Math.round(m.offset.y) + : percent(m.position.x) + "% " + percent(m.position.y) + "%" + ) + " " + m.location; context.completions = marks.all; }; }, - sanitizer: function () { + sanitizer: function initSanitizer() { sanitizer.addItem("marks", { description: "Local and URL marks", persistent: true, @@ -301,4 +406,4 @@ var Marks = Module("marks", { } }); -// vim: set fdm=marker sw=4 ts=4 et: +// vim: set fdm=marker sw=4 sts=4 ts=8 et: