mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2026-02-05 11:15:46 +01:00
Merge with testing.
This commit is contained in:
@@ -11,6 +11,8 @@ syntax: glob
|
||||
*/locale/*/*.html
|
||||
*/chrome
|
||||
*/contrib/vim/*.vba
|
||||
*/bak/*
|
||||
downloads/*
|
||||
|
||||
## Editor backup and swap files
|
||||
*~
|
||||
|
||||
@@ -99,8 +99,8 @@ dist: $(XPI)
|
||||
|
||||
$(RDF): $(RDF_IN) Makefile
|
||||
@echo "Preparing release..."
|
||||
$(SED) -e "s,###VERSION###,$(VERSION),g" \
|
||||
-e "s,###DATE###,$(BUILD_DATE),g" \
|
||||
$(SED) -e "s,@VERSION@,$(VERSION),g" \
|
||||
-e "s,@DATE@,$(BUILD_DATE),g" \
|
||||
< $< > $@
|
||||
@echo "SUCCESS: $@"
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
|
||||
"use strict";
|
||||
|
||||
/* Adds support for data: URIs with chrome privileges
|
||||
* and fragment identifiers.
|
||||
@@ -27,7 +27,7 @@ let channel = Components.classesByID["{61ba33c0-3031-11d3-8cd0-0060b0fc14a3}"]
|
||||
.QueryInterface(Ci.nsIRequest);
|
||||
const systemPrincipal = channel.owner;
|
||||
channel.cancel(NS_BINDING_ABORTED);
|
||||
delete channel;
|
||||
channel = null;
|
||||
|
||||
function dataURL(type, data) "data:" + (type || "application/xml;encoding=UTF-8") + "," + escape(data);
|
||||
function makeChannel(url, orig) {
|
||||
@@ -152,8 +152,9 @@ Liberator.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
var components = [ChromeData, Liberator];
|
||||
|
||||
function NSGetModule(compMgr, fileSpec) XPCOMUtils.generateModule(components)
|
||||
if (XPCOMUtils.generateNSGetFactory)
|
||||
const NSGetFactory = XPCOMUtils.generateNSGetFactory([ChromeData, Liberator]);
|
||||
else
|
||||
const NSGetModule = XPCOMUtils.generateNSGetModule([ChromeData, Liberator]);
|
||||
|
||||
// vim: set fdm=marker sw=4 ts=4 et:
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
@@ -274,9 +276,9 @@ const AutoCommands = Module("autocommands", {
|
||||
completer: function () config.autocommands.concat([["all", "All events"]])
|
||||
});
|
||||
|
||||
options.add(["focuscontent", "fc"],
|
||||
"Try to stay in normal mode after loading a web page",
|
||||
"boolean", false);
|
||||
options.add(["strictfocus", "sf"],
|
||||
"Prevent scripts from focusing input elements without user intervention",
|
||||
"boolean", true);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
|
||||
"use strict";
|
||||
|
||||
const DEFAULT_FAVICON = "chrome://mozapps/skin/places/defaultFavicon.png";
|
||||
|
||||
@@ -364,7 +366,6 @@ const Bookmarks = Module("bookmarks", {
|
||||
|
||||
// ripped from Firefox
|
||||
function getShortcutOrURI(url) {
|
||||
var shortcutURL = null;
|
||||
var keyword = url;
|
||||
var param = "";
|
||||
var offset = url.indexOf(" ");
|
||||
@@ -379,7 +380,7 @@ const Bookmarks = Module("bookmarks", {
|
||||
return [submission.uri.spec, submission.postData];
|
||||
}
|
||||
|
||||
[shortcutURL, postData] = PlacesUtils.getURLAndPostDataForKeyword(keyword);
|
||||
let [shortcutURL, postData] = PlacesUtils.getURLAndPostDataForKeyword(keyword);
|
||||
if (!shortcutURL)
|
||||
return [url, null];
|
||||
|
||||
@@ -598,6 +599,18 @@ const Bookmarks = Module("bookmarks", {
|
||||
context.completions = [["", "Don't perform searches by default"]].concat(context.completions);
|
||||
}
|
||||
});
|
||||
|
||||
options.add(["suggestengines"],
|
||||
"Engine Alias which has a feature of suggest",
|
||||
"stringlist", "google",
|
||||
{
|
||||
completer: function completer(value) {
|
||||
let engines = services.get("browserSearch").getEngines({})
|
||||
.filter(function (engine) engine.supportsResponseType("application/x-suggestions+json"));
|
||||
|
||||
return engines.map(function (engine) [engine.alias, engine.description]);
|
||||
}
|
||||
});
|
||||
},
|
||||
completion: function () {
|
||||
completion.bookmark = function bookmark(context, tags, extra) {
|
||||
@@ -643,8 +656,11 @@ const Bookmarks = Module("bookmarks", {
|
||||
let rest = item.url.length - end.length;
|
||||
let query = item.url.substring(begin.length, rest);
|
||||
if (item.url.substr(rest) == end && query.indexOf("&") == -1) {
|
||||
item.url = decodeURIComponent(query.replace(/#.*/, ""));
|
||||
return item;
|
||||
try {
|
||||
item.url = decodeURIComponent(query.replace(/#.*/, ""));
|
||||
return item;
|
||||
}
|
||||
catch (e) {}
|
||||
}
|
||||
return null;
|
||||
}).filter(util.identity);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k at Gmail>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k at Gmail>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
@@ -19,9 +19,10 @@ const Point = Struct("x", "y");
|
||||
* @instance buffer
|
||||
*/
|
||||
const Buffer = Module("buffer", {
|
||||
requires: ["config"],
|
||||
requires: ["config", "util"],
|
||||
|
||||
init: function () {
|
||||
this.evaluateXPath = util.evaluateXPath;
|
||||
this.pageInfo = {};
|
||||
|
||||
this.addPageInfoSection("f", "Feeds", function (verbose) {
|
||||
@@ -168,7 +169,7 @@ const Buffer = Module("buffer", {
|
||||
// called when the active document is scrolled
|
||||
_updateBufferPosition: function _updateBufferPosition() {
|
||||
statusline.updateBufferPosition();
|
||||
modes.show();
|
||||
modes.show(); // Clear the status line.
|
||||
},
|
||||
|
||||
onDOMContentLoaded: function onDOMContentLoaded(event) {
|
||||
@@ -199,19 +200,7 @@ const Buffer = Module("buffer", {
|
||||
// any buffer, even in a background tab
|
||||
doc.pageIsFullyLoaded = 1;
|
||||
|
||||
// code which is only relevant if the page load is the current tab goes here:
|
||||
if (doc == config.browser.contentDocument) {
|
||||
// we want to stay in command mode after a page has loaded
|
||||
// TODO: move somewhere else, as focusing can already happen earlier than on "load"
|
||||
if (options["focuscontent"]) {
|
||||
setTimeout(function () {
|
||||
let focused = liberator.focus;
|
||||
if (focused && (focused.value != null) && focused.value.length == 0)
|
||||
focused.blur();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
else // background tab
|
||||
if (doc != config.browser.contentDocument)
|
||||
liberator.echomsg("Background tab loaded: " + doc.title || doc.location.href, 3);
|
||||
|
||||
this._triggerLoadAutocmd("PageLoad", doc);
|
||||
@@ -279,7 +268,11 @@ const Buffer = Module("buffer", {
|
||||
autocommands.trigger("LocationChange", { url: buffer.URL });
|
||||
|
||||
// if this is not delayed we get the position of the old buffer
|
||||
setTimeout(function () { statusline.updateBufferPosition(); }, 500);
|
||||
setTimeout(function () {
|
||||
statusline.updateBufferPosition();
|
||||
statusline.updateZoomLevel();
|
||||
modes.show(); // Clear the status line.
|
||||
}, 500);
|
||||
},
|
||||
// called at the very end of a page load
|
||||
asyncUpdateUI: function () {
|
||||
@@ -384,19 +377,18 @@ const Buffer = Module("buffer", {
|
||||
get pageHeight() window.content.innerHeight,
|
||||
|
||||
/**
|
||||
* @property {number} The current browser's text zoom level, as a
|
||||
* percentage with 100 as 'normal'. Only affects text size.
|
||||
* @property {number} The current browser's zoom level, as a
|
||||
* percentage with 100 as 'normal'.
|
||||
*/
|
||||
get textZoom() config.browser.markupDocumentViewer.textZoom * 100,
|
||||
set textZoom(value) { Buffer.setZoom(value, false); },
|
||||
get zoomLevel() config.browser.markupDocumentViewer[this.fullZoom ? "textZoom" : "fullZoom"] * 100,
|
||||
set zoomLevel(value) { Buffer.setZoom(value, this.fullZoom); },
|
||||
|
||||
/**
|
||||
* @property {number} The current browser's text zoom level, as a
|
||||
* percentage with 100 as 'normal'. Affects text size, as well as
|
||||
* image size and block size.
|
||||
* @property {boolean} Whether the current browser is using full
|
||||
* zoom, as opposed to text zoom.
|
||||
*/
|
||||
get fullZoom() config.browser.markupDocumentViewer.fullZoom * 100,
|
||||
set fullZoom(value) { Buffer.setZoom(value, true); },
|
||||
get fullZoom() ZoomManager.useFullZoom,
|
||||
set fullZoom(value) { Buffer.setZoom(this.zoomLevel, value); },
|
||||
|
||||
/**
|
||||
* @property {string} The current document's title.
|
||||
@@ -470,6 +462,18 @@ const Buffer = Module("buffer", {
|
||||
return String(selection);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if a scripts are allowed to focus the given input
|
||||
* element or input elements in the given window.
|
||||
*
|
||||
* @param {Node|Window}
|
||||
* @returns {boolean}
|
||||
*/
|
||||
focusAllowed: function (elem) {
|
||||
let win = elem.ownerDocument && elem.ownerDocument.defaultView || elem;
|
||||
return !options["strictfocus"] || elem.liberatorFocusAllowed;
|
||||
},
|
||||
|
||||
/**
|
||||
* Focuses the given element. In contrast to a simple
|
||||
* elem.focus() call, this function works for iframes and
|
||||
@@ -479,7 +483,10 @@ const Buffer = Module("buffer", {
|
||||
*/
|
||||
focusElement: function (elem) {
|
||||
let doc = window.content.document;
|
||||
if (elem instanceof HTMLFrameElement || elem instanceof HTMLIFrameElement)
|
||||
let win = elem.ownerDocument && elem.ownerDocument.defaultView || elem;
|
||||
win.liberatorFocusAllowed = true;
|
||||
|
||||
if (isinstance(elem, [HTMLFrameElement, HTMLIFrameElement]))
|
||||
elem.contentWindow.focus();
|
||||
else if (elem instanceof HTMLInputElement && elem.type == "file") {
|
||||
Buffer.openUploadPrompt(elem);
|
||||
@@ -960,14 +967,20 @@ const Buffer = Module("buffer", {
|
||||
liberator.assert(value >= Buffer.ZOOM_MIN || value <= Buffer.ZOOM_MAX,
|
||||
"Zoom value out of range (" + Buffer.ZOOM_MIN + " - " + Buffer.ZOOM_MAX + "%)");
|
||||
|
||||
ZoomManager.useFullZoom = fullZoom;
|
||||
if (fullZoom !== undefined)
|
||||
ZoomManager.useFullZoom = fullZoom;
|
||||
ZoomManager.zoom = value / 100;
|
||||
|
||||
if ("FullZoom" in window)
|
||||
FullZoom._applySettingToPref();
|
||||
liberator.echomsg((fullZoom ? "Full" : "Text") + " zoom: " + value + "%");
|
||||
|
||||
statusline.updateZoomLevel(value, ZoomManager.useFullZoom);
|
||||
},
|
||||
|
||||
bumpZoomLevel: function bumpZoomLevel(steps, fullZoom) {
|
||||
if (fullZoom === undefined)
|
||||
fullZoom = ZoomManager.useFullZoom;
|
||||
|
||||
let values = ZoomManager.zoomValues;
|
||||
let cur = values.indexOf(ZoomManager.snap(ZoomManager.zoom));
|
||||
let i = util.Math.constrain(cur + steps, 0, values.length - 1);
|
||||
@@ -1138,8 +1151,8 @@ const Buffer = Module("buffer", {
|
||||
},
|
||||
{
|
||||
argCount: "?",
|
||||
literal: 0,
|
||||
bang: true
|
||||
bang: true,
|
||||
literal: 0
|
||||
});
|
||||
|
||||
commands.add(["pa[geinfo]"],
|
||||
@@ -1178,8 +1191,8 @@ const Buffer = Module("buffer", {
|
||||
"Reload the current web page",
|
||||
function (args) { tabs.reload(config.browser.mCurrentTab, args.bang); },
|
||||
{
|
||||
bang: true,
|
||||
argCount: "0"
|
||||
argCount: "0",
|
||||
bang: true
|
||||
});
|
||||
|
||||
// TODO: we're prompted if download.useDownloadDir isn't set and no arg specified - intentional?
|
||||
@@ -1470,16 +1483,19 @@ const Buffer = Module("buffer", {
|
||||
if (count < 1 && buffer.lastInputField)
|
||||
buffer.focusElement(buffer.lastInputField);
|
||||
else {
|
||||
let xpath = ["input[not(@type) or @type='text' or @type='password' or @type='file']",
|
||||
"textarea[not(@disabled) and not(@readonly)]"];
|
||||
let xpath = ["input", "textarea[not(@disabled) and not(@readonly)]"];
|
||||
|
||||
let elements = [m for (m in util.evaluateXPath(xpath))].filter(function (match) {
|
||||
let computedStyle = util.computedStyle(match);
|
||||
let elements = [m for (m in util.evaluateXPath(xpath))].filter(function (elem) {
|
||||
if (elem.readOnly || elem instanceof HTMLInputElement && ["file", "search", "text", "password"].indexOf(elem.type) < 0)
|
||||
return false;
|
||||
let computedStyle = util.computedStyle(elem);
|
||||
return computedStyle.visibility != "hidden" && computedStyle.display != "none";
|
||||
});
|
||||
|
||||
liberator.assert(elements.length > 0);
|
||||
buffer.focusElement(elements[util.Math.constrain(count, 1, elements.length) - 1]);
|
||||
let elem = elements[util.Math.constrain(count, 1, elements.length) - 1];
|
||||
buffer.focusElement(elem);
|
||||
util.scrollIntoView(elem);
|
||||
}
|
||||
},
|
||||
{ count: true });
|
||||
@@ -1504,7 +1520,7 @@ const Buffer = Module("buffer", {
|
||||
function () {
|
||||
let url = util.readFromClipboard();
|
||||
liberator.assert(url);
|
||||
liberator.open(url, { from: "activate", where: liberator.NEW_TAB });
|
||||
liberator.open(url, { from: "paste", where: liberator.NEW_TAB });
|
||||
});
|
||||
|
||||
// reloading
|
||||
@@ -1551,27 +1567,27 @@ const Buffer = Module("buffer", {
|
||||
function (count) { buffer.textZoom = count > 1 ? count : 100; },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["zI"],
|
||||
mappings.add(myModes, ["ZI", "zI"],
|
||||
"Enlarge full zoom of current web page",
|
||||
function (count) { buffer.zoomIn(Math.max(count, 1), true); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["zM"],
|
||||
mappings.add(myModes, ["ZM", "zM"],
|
||||
"Enlarge full zoom of current web page by a larger amount",
|
||||
function (count) { buffer.zoomIn(Math.max(count, 1) * 3, true); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["zO"],
|
||||
mappings.add(myModes, ["ZO", "zO"],
|
||||
"Reduce full zoom of current web page",
|
||||
function (count) { buffer.zoomOut(Math.max(count, 1), true); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["zR"],
|
||||
mappings.add(myModes, ["ZR", "zR"],
|
||||
"Reduce full zoom of current web page by a larger amount",
|
||||
function (count) { buffer.zoomOut(Math.max(count, 1) * 3, true); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["zZ"],
|
||||
mappings.add(myModes, ["ZZ", "zZ"],
|
||||
"Set full zoom value of current web page",
|
||||
function (count) { buffer.fullZoom = count > 1 ? count : 100; },
|
||||
{ count: true });
|
||||
@@ -1599,7 +1615,7 @@ const Buffer = Module("buffer", {
|
||||
"Desired info in the :pageinfo output",
|
||||
"charlist", "gfm",
|
||||
{
|
||||
completer: function (context) [[k, v[1]] for ([k, v] in Iterator(this.pageInfo))]
|
||||
completer: function (context) [[k, v[1]] for ([k, v] in Iterator(buffer.pageInfo))]
|
||||
});
|
||||
|
||||
options.add(["scroll", "scr"],
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
@@ -102,7 +105,7 @@ const CommandLine = Module("commandline", {
|
||||
});
|
||||
|
||||
this._autocompleteTimer = new Timer(200, 500, function autocompleteTell(tabPressed) {
|
||||
if (!events.feedingKeys && self._completions && options.get("wildoptions").has("auto")) {
|
||||
if (!events.feedingKeys && self._completions && options.get("autocomplete").values.length) {
|
||||
self._completions.complete(true, false);
|
||||
self._completions.itemList.show();
|
||||
}
|
||||
@@ -329,10 +332,11 @@ const CommandLine = Module("commandline", {
|
||||
|
||||
FORCE_MULTILINE : 1 << 0,
|
||||
FORCE_SINGLELINE : 1 << 1,
|
||||
DISALLOW_MULTILINE : 1 << 2, // if an echo() should try to use the single line
|
||||
DISALLOW_MULTILINE : 1 << 2, // If an echo() should try to use the single line
|
||||
// but output nothing when the MOW is open; when also
|
||||
// FORCE_MULTILINE is given, FORCE_MULTILINE takes precedence
|
||||
APPEND_TO_MESSAGES : 1 << 3, // add the string to the message this._history
|
||||
APPEND_TO_MESSAGES : 1 << 3, // Add the string to the message this._history.
|
||||
ACTIVE_WINDOW : 1 << 4, // Only echo in active window.
|
||||
|
||||
get completionContext() this._completions.context,
|
||||
|
||||
@@ -499,6 +503,10 @@ const CommandLine = Module("commandline", {
|
||||
|
||||
if (flags & this.APPEND_TO_MESSAGES)
|
||||
this._messageHistory.add({ str: str, highlight: highlightGroup });
|
||||
if ((flags & this.ACTIVE_WINDOW) &&
|
||||
window != services.get("windowWatcher").activeWindow &&
|
||||
services.get("windowWatcher").activeWindow.liberator)
|
||||
return;
|
||||
|
||||
// The DOM isn't threadsafe. It must only be accessed from the main thread.
|
||||
liberator.callInMainThread(function () {
|
||||
@@ -754,12 +762,12 @@ const CommandLine = Module("commandline", {
|
||||
case "<MiddleMouse>":
|
||||
case "<C-LeftMouse>":
|
||||
case "<C-M-LeftMouse>":
|
||||
openLink(liberator.NEW_BACKGROUND_TAB);
|
||||
openLink({ where: liberator.NEW_TAB, background: true });
|
||||
break;
|
||||
case "<S-MiddleMouse>":
|
||||
case "<C-S-LeftMouse>":
|
||||
case "<C-M-S-LeftMouse>":
|
||||
openLink(liberator.NEW_TAB);
|
||||
openLink({ where: liberator.NEW_TAB, background: false });
|
||||
break;
|
||||
case "<S-LeftMouse>":
|
||||
openLink(liberator.NEW_WINDOW);
|
||||
@@ -964,7 +972,7 @@ const CommandLine = Module("commandline", {
|
||||
|
||||
let doc = this._multilineOutputWidget.contentDocument;
|
||||
|
||||
availableHeight = config.outputHeight;
|
||||
let availableHeight = config.outputHeight;
|
||||
if (!this._outputContainer.collapsed)
|
||||
availableHeight += parseFloat(this._outputContainer.height);
|
||||
doc.body.style.minWidth = this._commandlineWidget.scrollWidth + "px";
|
||||
@@ -1032,7 +1040,7 @@ const CommandLine = Module("commandline", {
|
||||
sanitize: function (timespan) {
|
||||
let range = [0, Number.MAX_VALUE];
|
||||
if (liberator.has("sanitizer") && (timespan || options["sanitizetimespan"]))
|
||||
range = sanitizer.getClearRange(timespan || options["sanitizetimespan"]);
|
||||
range = Sanitizer.getClearRange(timespan || options["sanitizetimespan"]);
|
||||
|
||||
const self = this;
|
||||
this.store.mutate("filter", function (item) {
|
||||
@@ -1107,7 +1115,7 @@ const CommandLine = Module("commandline", {
|
||||
*/
|
||||
Completions: Class("Completions", {
|
||||
init: function (input) {
|
||||
this.context = CompletionContext(input.editor);
|
||||
this.context = CompletionContext(input.QueryInterface(Ci.nsIDOMNSEditableElement).editor);
|
||||
this.context.onUpdate = this.closure._reset;
|
||||
this.editor = input.editor;
|
||||
this.selected = null;
|
||||
@@ -1127,7 +1135,7 @@ const CommandLine = Module("commandline", {
|
||||
let str = commandline.command;
|
||||
return str.substring(this.prefix.length, str.length - this.suffix.length);
|
||||
},
|
||||
set completion set_completion(completion) {
|
||||
set completion(completion) {
|
||||
this.previewClear();
|
||||
|
||||
// Change the completion text.
|
||||
@@ -1516,84 +1524,6 @@ const CommandLine = Module("commandline", {
|
||||
options.add(["showmode", "smd"],
|
||||
"Show the current mode in the command line",
|
||||
"boolean", true);
|
||||
|
||||
options.add(["suggestengines"],
|
||||
"Engine Alias which has a feature of suggest",
|
||||
"stringlist", "google",
|
||||
{
|
||||
completer: function completer(value) {
|
||||
let engines = services.get("browserSearch").getEngines({})
|
||||
.filter(function (engine) engine.supportsResponseType("application/x-suggestions+json"));
|
||||
|
||||
return engines.map(function (engine) [engine.alias, engine.description]);
|
||||
}
|
||||
});
|
||||
|
||||
options.add(["complete", "cpt"],
|
||||
"Items which are completed at the :open prompts",
|
||||
"charlist", typeof(config.defaults["complete"]) == "string" ? config.defaults["complete"] : "slf",
|
||||
{
|
||||
completer: function (context) array(values(completion.urlCompleters))
|
||||
});
|
||||
|
||||
options.add(["wildcase", "wic"],
|
||||
"Completion case matching mode",
|
||||
"string", "smart",
|
||||
{
|
||||
completer: function () [
|
||||
["smart", "Case is significant when capital letters are typed"],
|
||||
["match", "Case is always significant"],
|
||||
["ignore", "Case is never significant"]
|
||||
]
|
||||
});
|
||||
|
||||
options.add(["wildignore", "wig"],
|
||||
"List of file patterns to ignore when completing files",
|
||||
"stringlist", "",
|
||||
{
|
||||
validator: function validator(values) {
|
||||
// TODO: allow for escaping the ","
|
||||
try {
|
||||
RegExp("^(" + values.join("|") + ")$");
|
||||
return true;
|
||||
}
|
||||
catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
options.add(["wildmode", "wim"],
|
||||
"Define how command line completion works",
|
||||
"stringlist", "list:full",
|
||||
{
|
||||
completer: function (context) [
|
||||
// Why do we need ""?
|
||||
["", "Complete only the first match"],
|
||||
["full", "Complete the next full match"],
|
||||
["longest", "Complete to longest common string"],
|
||||
["list", "If more than one match, list all matches"],
|
||||
["list:full", "List all and complete first match"],
|
||||
["list:longest", "List all and complete common string"]
|
||||
],
|
||||
checkHas: function (value, val) {
|
||||
let [first, second] = value.split(":", 2);
|
||||
return first == val || second == val;
|
||||
}
|
||||
});
|
||||
|
||||
options.add(["wildoptions", "wop"],
|
||||
"Change how command line completion is done",
|
||||
"stringlist", "",
|
||||
{
|
||||
completer: function completer(value) {
|
||||
return [
|
||||
["", "Default completion that won't show or sort the results"],
|
||||
["auto", "Automatically show this._completions while you are typing"],
|
||||
["sort", "Always sort the completion list"]
|
||||
];
|
||||
}
|
||||
});
|
||||
},
|
||||
styles: function () {
|
||||
let fontSize = util.computedStyle(document.getElementById(config.mainWindowId)).fontSize;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k at Gmail>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
@@ -73,9 +73,9 @@ const Command = Class("Command", {
|
||||
modifiers = modifiers || {};
|
||||
|
||||
let self = this;
|
||||
function exec(args) {
|
||||
function exec(command) {
|
||||
// FIXME: Move to parseCommand?
|
||||
args = self.parseArgs(args);
|
||||
args = self.parseArgs(command);
|
||||
if (!args)
|
||||
return;
|
||||
args.count = count;
|
||||
@@ -237,6 +237,7 @@ const ArgType = Struct("description", "parse");
|
||||
const Commands = Module("commands", {
|
||||
init: function () {
|
||||
this._exCommands = [];
|
||||
this._exMap = {};
|
||||
},
|
||||
|
||||
// FIXME: remove later, when our option handler is better
|
||||
@@ -304,7 +305,7 @@ const Commands = Module("commands", {
|
||||
repeat: null,
|
||||
|
||||
_addCommand: function (command, replace) {
|
||||
if (this._exCommands.some(function (c) c.hasName(command.name))) {
|
||||
if (command.name in this._exMap) {
|
||||
if (command.user && replace)
|
||||
commands.removeUserCommand(command.name);
|
||||
else {
|
||||
@@ -314,6 +315,8 @@ const Commands = Module("commands", {
|
||||
}
|
||||
|
||||
this._exCommands.push(command);
|
||||
for(let [,name] in Iterator(command.names))
|
||||
this._exMap[name] = command;
|
||||
|
||||
return true;
|
||||
},
|
||||
@@ -387,7 +390,7 @@ const Commands = Module("commands", {
|
||||
* @returns {Command}
|
||||
*/
|
||||
get: function (name) {
|
||||
return this._exCommands.filter(function (cmd) cmd.hasName(name))[0] || null;
|
||||
return this._exMap[name] || this._exCommands.filter(function (cmd) cmd.hasName(name))[0] || null;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -762,6 +765,10 @@ const Commands = Module("commands", {
|
||||
* any of the command's names.
|
||||
*/
|
||||
removeUserCommand: function (name) {
|
||||
for(let [,cmd] in Iterator(this._exCommands))
|
||||
if(cmd.user && cmd.hasName(name))
|
||||
for(let [,name] in Iterator(cmd.names))
|
||||
delete this._exMap[name];
|
||||
this._exCommands = this._exCommands.filter(function (cmd) !(cmd.user && cmd.hasName(name)));
|
||||
},
|
||||
|
||||
@@ -894,10 +901,10 @@ const Commands = Module("commands", {
|
||||
}
|
||||
|
||||
[prefix] = context.filter.match(/^(?:\w*[\s!]|!)\s*/);
|
||||
let cmdContext = context.fork(cmd, prefix.length);
|
||||
let cmdContext = context.fork(command.name, prefix.length);
|
||||
let argContext = context.fork("args", prefix.length);
|
||||
args = command.parseArgs(cmdContext.filter, argContext, { count: count, bang: bang });
|
||||
if (args) {
|
||||
if (args && !cmdContext.waitingForTab) {
|
||||
// FIXME: Move to parseCommand
|
||||
args.count = count;
|
||||
args.bang = bang;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k at Gmail>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
@@ -34,6 +35,11 @@ const CompletionContext = Class("CompletionContext", {
|
||||
if (editor instanceof this.constructor) {
|
||||
let parent = editor;
|
||||
name = parent.name + "/" + name;
|
||||
|
||||
this.autoComplete = options.get("autocomplete").getKey(name);
|
||||
this.sortResults = options.get("wildsort").getKey(name);
|
||||
this.wildcase = options.get("wildcase").getKey(name);
|
||||
|
||||
this.contexts = parent.contexts;
|
||||
if (name in this.contexts)
|
||||
self = this.contexts[name];
|
||||
@@ -146,6 +152,8 @@ const CompletionContext = Class("CompletionContext", {
|
||||
this.top = this;
|
||||
this.__defineGetter__("incomplete", function () this.contextList.some(function (c) c.parent && c.incomplete));
|
||||
this.__defineGetter__("waitingForTab", function () this.contextList.some(function (c) c.parent && c.waitingForTab));
|
||||
this.__defineSetter__("incomplete", function (val) {});
|
||||
this.__defineSetter__("waitingForTab", function (val) {});
|
||||
this.reset();
|
||||
}
|
||||
/**
|
||||
@@ -243,7 +251,7 @@ const CompletionContext = Class("CompletionContext", {
|
||||
get completions() this._completions || [],
|
||||
set completions(items) {
|
||||
// Accept a generator
|
||||
if ({}.toString.call(items) != '[object Array]')
|
||||
if (!isarray(items))
|
||||
items = [x for (x in Iterator(items))];
|
||||
delete this.cache.filtered;
|
||||
delete this.cache.filter;
|
||||
@@ -333,7 +341,7 @@ const CompletionContext = Class("CompletionContext", {
|
||||
get ignoreCase() {
|
||||
if ("_ignoreCase" in this)
|
||||
return this._ignoreCase;
|
||||
let mode = options["wildcase"];
|
||||
let mode = this.wildcase;
|
||||
if (mode == "match")
|
||||
return this._ignoreCase = false;
|
||||
if (mode == "ignore")
|
||||
@@ -367,7 +375,7 @@ const CompletionContext = Class("CompletionContext", {
|
||||
if (this.maxItems)
|
||||
filtered = filtered.slice(0, this.maxItems);
|
||||
|
||||
if (options.get("wildoptions").has("sort") && this.compare)
|
||||
if (this.sortResults && this.compare)
|
||||
filtered.sort(this.compare);
|
||||
let quote = this.quote;
|
||||
if (quote)
|
||||
@@ -404,12 +412,12 @@ const CompletionContext = Class("CompletionContext", {
|
||||
let filter = fixCase(this.filter);
|
||||
if (this.anchored) {
|
||||
var compare = function compare(text, s) text.substr(0, s.length) == s;
|
||||
substrings = util.map(util.range(filter.length, text.length + 1),
|
||||
var substrings = util.map(util.range(filter.length, text.length + 1),
|
||||
function (end) text.substring(0, end));
|
||||
}
|
||||
else {
|
||||
var compare = function compare(text, s) text.indexOf(s) >= 0;
|
||||
substrings = [];
|
||||
var substrings = [];
|
||||
let start = 0;
|
||||
let idx;
|
||||
let length = filter.length;
|
||||
@@ -498,8 +506,14 @@ const CompletionContext = Class("CompletionContext", {
|
||||
completer = self[completer];
|
||||
let context = CompletionContext(this, name, offset);
|
||||
this.contextList.push(context);
|
||||
if (completer)
|
||||
|
||||
if (!context.autoComplete && !context.tabPressed && context.editor)
|
||||
context.waitingForTab = true;
|
||||
else if (completer)
|
||||
return completer.apply(self || this, [context].concat(Array.slice(arguments, arguments.callee.length)));
|
||||
|
||||
if (completer)
|
||||
return null;
|
||||
return context;
|
||||
},
|
||||
|
||||
@@ -682,7 +696,7 @@ const Completion = Module("completion", {
|
||||
if (skip)
|
||||
context.advance(skip[0].length);
|
||||
|
||||
if (typeof complete === "undefined")
|
||||
if (complete == null)
|
||||
complete = options["complete"];
|
||||
|
||||
// Will, and should, throw an error if !(c in opts)
|
||||
@@ -744,6 +758,80 @@ const Completion = Module("completion", {
|
||||
//}}}
|
||||
}, {
|
||||
UrlCompleter: Struct("name", "description", "completer")
|
||||
}, {
|
||||
commands: function () {
|
||||
commands.add(["contexts"],
|
||||
"List the completion contexts used during completion of an ex command",
|
||||
function (args) {
|
||||
commandline.echo(template.commandOutput(
|
||||
<div highlight="Completions">
|
||||
{ template.completionRow(["Context", "Title"], "CompTitle") }
|
||||
{ template.map(completion.contextList || [], function (item) template.completionRow(item, "CompItem")) }
|
||||
</div>),
|
||||
null, commandline.FORCE_MULTILINE);
|
||||
|
||||
},
|
||||
{
|
||||
argCount: "1",
|
||||
completer: function (context, args) {
|
||||
let PREFIX = "/ex/contexts";
|
||||
context.fork("ex", 0, completion, "ex");
|
||||
completion.contextList = [[k.substr(PREFIX.length), v.title[0]] for ([k, v] in iter(context.contexts)) if (k.substr(0, PREFIX.length) == PREFIX)];
|
||||
},
|
||||
literal: 0
|
||||
});
|
||||
},
|
||||
options: function () {
|
||||
options.add(["autocomplete", "au"],
|
||||
"Automatically update the completion list on any key press",
|
||||
"regexlist", ".*");
|
||||
|
||||
options.add(["complete", "cpt"],
|
||||
"Items which are completed at the :open prompts",
|
||||
"charlist", typeof(config.defaults["complete"]) == "string" ? config.defaults["complete"] : "slf",
|
||||
{
|
||||
completer: function (context) array(values(completion.urlCompleters))
|
||||
});
|
||||
|
||||
options.add(["wildcase", "wic"],
|
||||
"Completion case matching mode",
|
||||
"regexmap", "smart",
|
||||
{
|
||||
completer: function () [
|
||||
["smart", "Case is significant when capital letters are typed"],
|
||||
["match", "Case is always significant"],
|
||||
["ignore", "Case is never significant"]
|
||||
]
|
||||
});
|
||||
|
||||
options.add(["wildmode", "wim"],
|
||||
"Define how command line completion works",
|
||||
"stringlist", "list:full",
|
||||
{
|
||||
completer: function (context) [
|
||||
// Why do we need ""?
|
||||
// Because its description is useful during completion. --Kris
|
||||
["", "Complete only the first match"],
|
||||
["full", "Complete the next full match"],
|
||||
["longest", "Complete to longest common string"],
|
||||
["list", "If more than one match, list all matches"],
|
||||
["list:full", "List all and complete first match"],
|
||||
["list:longest", "List all and complete common string"]
|
||||
],
|
||||
checkHas: function (value, val) {
|
||||
let [first, second] = value.split(":", 2);
|
||||
return first == val || second == val;
|
||||
},
|
||||
has: function () {
|
||||
test = function (val) this.values.some(function (value) this.checkHas(value, val), this);
|
||||
return Array.some(arguments, test, this);
|
||||
}
|
||||
});
|
||||
|
||||
options.add(["wildsort", "wis"],
|
||||
"Regexp list of which contexts to sort",
|
||||
"regexlist", ".*");
|
||||
}
|
||||
});
|
||||
|
||||
// vim: set fdm=marker sw=4 ts=4 et:
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
"use strict";
|
||||
|
||||
const ConfigBase = Class(ModuleBase, {
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k at Gmail>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
@@ -103,32 +103,16 @@ const Events = Module("events", {
|
||||
}
|
||||
}, 100);
|
||||
|
||||
function wrapListener(method) {
|
||||
return function (event) {
|
||||
try {
|
||||
self[method](event);
|
||||
}
|
||||
catch (e) {
|
||||
if (e.message == "Interrupted")
|
||||
liberator.echoerr("Interrupted");
|
||||
else
|
||||
liberator.echoerr("Processing " + event.type + " event: " + (e.echoerr || e));
|
||||
liberator.reportError(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
this._wrappedOnKeyPress = wrapListener("onKeyPress");
|
||||
this._wrappedOnKeyUpOrDown = wrapListener("onKeyUpOrDown");
|
||||
this.addSessionListener(window, "keypress", this.closure._wrappedOnKeyPress, true);
|
||||
this.addSessionListener(window, "keydown", this.closure._wrappedOnKeyUpOrDown, true);
|
||||
this.addSessionListener(window, "keyup", this.closure._wrappedOnKeyUpOrDown, true);
|
||||
|
||||
this._activeMenubar = false;
|
||||
this.addSessionListener(window, "popupshown", this.closure.onPopupShown, true);
|
||||
this.addSessionListener(window, "popuphidden", this.closure.onPopupHidden, true);
|
||||
this.addSessionListener(window, "DOMMenuBarActive", this.closure.onDOMMenuBarActive, true);
|
||||
this.addSessionListener(window, "DOMMenuBarInactive", this.closure.onDOMMenuBarInactive, true);
|
||||
this.addSessionListener(window, "focus", this.wrapListener(this.closure.onFocus), true);
|
||||
this.addSessionListener(window, "keydown", this.wrapListener(this.closure.onKeyUpOrDown), true);
|
||||
this.addSessionListener(window, "keypress", this.wrapListener(this.closure.onKeyPress), true);
|
||||
this.addSessionListener(window, "keyup", this.wrapListener(this.closure.onKeyUpOrDown), true);
|
||||
this.addSessionListener(window, "mousedown", this.wrapListener(this.closure.onMouseDown), true);
|
||||
this.addSessionListener(window, "popuphidden", this.closure.onPopupHidden, true);
|
||||
this.addSessionListener(window, "popupshown", this.closure.onPopupShown, true);
|
||||
this.addSessionListener(window, "resize", this.closure.onResize, true);
|
||||
|
||||
},
|
||||
@@ -151,10 +135,28 @@ const Events = Module("events", {
|
||||
*/
|
||||
addSessionListener: function (target, event, callback, capture) {
|
||||
let args = Array.slice(arguments, 0);
|
||||
target.addEventListener.apply(target, args.slice(1));
|
||||
target.addEventListener.apply(args[0], args.slice(1));
|
||||
this.sessionListeners.push(args);
|
||||
},
|
||||
|
||||
/**
|
||||
* Wraps an event listener to ensure that errors are reported.
|
||||
*/
|
||||
wrapListener: function wrapListener(method, self) {
|
||||
return function (event) {
|
||||
try {
|
||||
method.apply(self, arguments);
|
||||
}
|
||||
catch (e) {
|
||||
if (e.message == "Interrupted")
|
||||
liberator.echoerr("Interrupted");
|
||||
else
|
||||
liberator.echoerr("Processing " + event.type + " event: " + (e.echoerr || e));
|
||||
liberator.reportError(e);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* @property {boolean} Whether synthetic key events are currently being
|
||||
* processed.
|
||||
@@ -651,89 +653,14 @@ const Events = Module("events", {
|
||||
return ret;
|
||||
},
|
||||
|
||||
// argument "event" is deliberately not used, as i don't seem to have
|
||||
// access to the real focus target
|
||||
// Huh? --djk
|
||||
onFocusChange: function (event) {
|
||||
// command line has it's own focus change handler
|
||||
if (liberator.mode == modes.COMMAND_LINE)
|
||||
return;
|
||||
|
||||
function hasHTMLDocument(win) win && win.document && win.document instanceof HTMLDocument
|
||||
|
||||
let win = window.document.commandDispatcher.focusedWindow;
|
||||
let elem = window.document.commandDispatcher.focusedElement;
|
||||
|
||||
if (win && win.top == content && liberator.has("tabs"))
|
||||
tabs.localStore.focusedFrame = win;
|
||||
|
||||
try {
|
||||
if (elem && elem.readOnly)
|
||||
return;
|
||||
|
||||
if ((elem instanceof HTMLInputElement && /^(text|password)$/.test(elem.type)) ||
|
||||
(elem instanceof HTMLSelectElement)) {
|
||||
liberator.mode = modes.INSERT;
|
||||
if (hasHTMLDocument(win))
|
||||
buffer.lastInputField = elem;
|
||||
return;
|
||||
}
|
||||
if (elem instanceof HTMLEmbedElement || elem instanceof HTMLObjectElement) {
|
||||
liberator.mode = modes.EMBED;
|
||||
return;
|
||||
}
|
||||
|
||||
if (elem instanceof HTMLTextAreaElement || (elem && elem.contentEditable == "true")) {
|
||||
if (options["insertmode"])
|
||||
modes.set(modes.INSERT);
|
||||
else if (elem.selectionEnd - elem.selectionStart > 0)
|
||||
modes.set(modes.VISUAL, modes.TEXTAREA);
|
||||
else
|
||||
modes.main = modes.TEXTAREA;
|
||||
if (hasHTMLDocument(win))
|
||||
buffer.lastInputField = elem;
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.focusChange) {
|
||||
config.focusChange(win);
|
||||
return;
|
||||
}
|
||||
|
||||
let urlbar = document.getElementById("urlbar");
|
||||
if (elem == null && urlbar && urlbar.inputField == this._lastFocus)
|
||||
liberator.threadYield(true);
|
||||
|
||||
if (liberator.mode & (modes.EMBED | modes.INSERT | modes.TEXTAREA | modes.VISUAL))
|
||||
modes.reset();
|
||||
}
|
||||
finally {
|
||||
this._lastFocus = elem;
|
||||
}
|
||||
onDOMMenuBarActive: function () {
|
||||
this._activeMenubar = true;
|
||||
modes.add(modes.MENU);
|
||||
},
|
||||
|
||||
onSelectionChange: function (event) {
|
||||
let couldCopy = false;
|
||||
let controller = document.commandDispatcher.getControllerForCommand("cmd_copy");
|
||||
if (controller && controller.isCommandEnabled("cmd_copy"))
|
||||
couldCopy = true;
|
||||
|
||||
if (liberator.mode != modes.VISUAL) {
|
||||
if (couldCopy) {
|
||||
if ((liberator.mode == modes.TEXTAREA ||
|
||||
(modes.extended & modes.TEXTAREA))
|
||||
&& !options["insertmode"])
|
||||
modes.set(modes.VISUAL, modes.TEXTAREA);
|
||||
else if (liberator.mode == modes.CARET)
|
||||
modes.set(modes.VISUAL, modes.CARET);
|
||||
}
|
||||
}
|
||||
// XXX: disabled, as i think automatically starting visual caret mode does more harm than help
|
||||
// else
|
||||
// {
|
||||
// if (!couldCopy && modes.extended & modes.CARET)
|
||||
// liberator.mode = modes.CARET;
|
||||
// }
|
||||
onDOMMenuBarInactive: function () {
|
||||
this._activeMenubar = false;
|
||||
modes.remove(modes.MENU);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -808,6 +735,78 @@ const Events = Module("events", {
|
||||
}
|
||||
},
|
||||
|
||||
onFocus: function (event) {
|
||||
function hasHTMLDocument(win) win && win.document && win.document instanceof HTMLDocument
|
||||
|
||||
let elem = event.originalTarget;
|
||||
let win = elem.ownerDocument && elem.ownerDocument.defaultView || elem;
|
||||
|
||||
if (hasHTMLDocument(win) && !buffer.focusAllowed(win))
|
||||
elem.blur();
|
||||
},
|
||||
|
||||
// argument "event" is deliberately not used, as i don't seem to have
|
||||
// access to the real focus target
|
||||
// Huh? --djk
|
||||
onFocusChange: function (event) {
|
||||
// command line has it's own focus change handler
|
||||
if (liberator.mode == modes.COMMAND_LINE)
|
||||
return;
|
||||
|
||||
function hasHTMLDocument(win) win && win.document && win.document instanceof HTMLDocument
|
||||
|
||||
let win = window.document.commandDispatcher.focusedWindow;
|
||||
let elem = window.document.commandDispatcher.focusedElement;
|
||||
|
||||
if (win && win.top == content && liberator.has("tabs"))
|
||||
tabs.localStore.focusedFrame = win;
|
||||
|
||||
try {
|
||||
if (elem && elem.readOnly)
|
||||
return;
|
||||
|
||||
if ((elem instanceof HTMLInputElement && /^(search|text|password)$/.test(elem.type)) ||
|
||||
(elem instanceof HTMLSelectElement)) {
|
||||
liberator.mode = modes.INSERT;
|
||||
if (hasHTMLDocument(win))
|
||||
buffer.lastInputField = elem;
|
||||
return;
|
||||
}
|
||||
|
||||
if(isinstance(elem, [HTMLEmbedElement, HTMLEmbedElement])) {
|
||||
liberator.mode = modes.EMBED;
|
||||
return;
|
||||
}
|
||||
|
||||
if (elem instanceof HTMLTextAreaElement || (elem && elem.contentEditable == "true")) {
|
||||
if (options["insertmode"])
|
||||
modes.set(modes.INSERT);
|
||||
else if (elem.selectionEnd - elem.selectionStart > 0)
|
||||
modes.set(modes.VISUAL, modes.TEXTAREA);
|
||||
else
|
||||
modes.main = modes.TEXTAREA;
|
||||
if (hasHTMLDocument(win))
|
||||
buffer.lastInputField = elem;
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.focusChange) {
|
||||
config.focusChange(win);
|
||||
return;
|
||||
}
|
||||
|
||||
let urlbar = document.getElementById("urlbar");
|
||||
if (elem == null && urlbar && urlbar.inputField == this._lastFocus)
|
||||
liberator.threadYield(true);
|
||||
|
||||
if (liberator.mode & (modes.EMBED | modes.INSERT | modes.TEXTAREA | modes.VISUAL))
|
||||
modes.reset();
|
||||
}
|
||||
finally {
|
||||
this._lastFocus = elem;
|
||||
}
|
||||
},
|
||||
|
||||
// this keypress handler gets always called first, even if e.g.
|
||||
// the commandline has focus
|
||||
// TODO: ...help me...please...
|
||||
@@ -1022,7 +1021,9 @@ const Events = Module("events", {
|
||||
|
||||
if (liberator.mode == modes.COMMAND_LINE) {
|
||||
if (!(modes.extended & modes.INPUT_MULTILINE))
|
||||
commandline.onEvent(event); // reroute event in command line mode
|
||||
liberator.trapErrors(function () {
|
||||
commandline.onEvent(event); // reroute event in command line mode
|
||||
});
|
||||
}
|
||||
else if (!modes.mainMode.input)
|
||||
liberator.beep();
|
||||
@@ -1050,6 +1051,13 @@ const Events = Module("events", {
|
||||
event.stopPropagation();
|
||||
},
|
||||
|
||||
onMouseDown: function (event) {
|
||||
let elem = event.target;
|
||||
let win = elem.ownerDocument && elem.ownerDocument.defaultView || elem;
|
||||
for(; win; win = win != win.parent && win.parent)
|
||||
win.liberatorFocusAllowed = true;
|
||||
},
|
||||
|
||||
onPopupShown: function (event) {
|
||||
if (event.originalTarget.localName == "tooltip" || event.originalTarget.id == "liberator-visualbell")
|
||||
return;
|
||||
@@ -1062,22 +1070,36 @@ const Events = Module("events", {
|
||||
modes.remove(modes.MENU);
|
||||
},
|
||||
|
||||
onDOMMenuBarActive: function () {
|
||||
this._activeMenubar = true;
|
||||
modes.add(modes.MENU);
|
||||
},
|
||||
|
||||
onDOMMenuBarInactive: function () {
|
||||
this._activeMenubar = false;
|
||||
modes.remove(modes.MENU);
|
||||
},
|
||||
|
||||
onResize: function (event) {
|
||||
if (window.fullScreen != this._fullscreen) {
|
||||
this._fullscreen = window.fullScreen;
|
||||
liberator.triggerObserver("fullscreen", this._fullscreen);
|
||||
autocommands.trigger("Fullscreen", { state: this._fullscreen });
|
||||
}
|
||||
},
|
||||
|
||||
onSelectionChange: function (event) {
|
||||
let couldCopy = false;
|
||||
let controller = document.commandDispatcher.getControllerForCommand("cmd_copy");
|
||||
if (controller && controller.isCommandEnabled("cmd_copy"))
|
||||
couldCopy = true;
|
||||
|
||||
if (liberator.mode != modes.VISUAL) {
|
||||
if (couldCopy) {
|
||||
if ((liberator.mode == modes.TEXTAREA ||
|
||||
(modes.extended & modes.TEXTAREA))
|
||||
&& !options["insertmode"])
|
||||
modes.set(modes.VISUAL, modes.TEXTAREA);
|
||||
else if (liberator.mode == modes.CARET)
|
||||
modes.set(modes.VISUAL, modes.CARET);
|
||||
}
|
||||
}
|
||||
// XXX: disabled, as i think automatically starting visual caret mode does more harm than help
|
||||
// else
|
||||
// {
|
||||
// if (!couldCopy && modes.extended & modes.CARET)
|
||||
// liberator.mode = modes.CARET;
|
||||
// }
|
||||
}
|
||||
}, {
|
||||
isInputElemFocused: function () {
|
||||
|
||||
@@ -1,465 +1,12 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2008-2010 by Kris Maglione <maglione.k@gmail.com>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
// TODO: proper backwards search - implement our own component?
|
||||
// : implement our own highlighter?
|
||||
// : <ESC> should cancel search highlighting in 'incsearch' mode and jump
|
||||
// back to the presearch page location - can probably use the same
|
||||
// solution as marks
|
||||
// : 'linksearch' searches should highlight link matches only
|
||||
// : changing any search settings should also update the search state including highlighting
|
||||
// : incremental searches shouldn't permanently update search modifiers
|
||||
//
|
||||
// TODO: Clean up this rat's nest. --Kris
|
||||
|
||||
/**
|
||||
* @instance finder
|
||||
*/
|
||||
const Finder = Module("finder", {
|
||||
requires: ["config"],
|
||||
|
||||
init: function () {
|
||||
const self = this;
|
||||
|
||||
this._found = false; // true if the last search was successful
|
||||
this._backwards = false; // currently searching backwards
|
||||
this._searchString = ""; // current search string (without modifiers)
|
||||
this._searchPattern = ""; // current search string (includes modifiers)
|
||||
this._lastSearchPattern = ""; // the last searched pattern (includes modifiers)
|
||||
this._lastSearchString = ""; // the last searched string (without modifiers)
|
||||
this._lastSearchBackwards = false; // like "backwards", but for the last search, so if you cancel a search with <esc> this is not set
|
||||
this._caseSensitive = false; // search string is case sensitive
|
||||
this._linksOnly = false; // search is limited to link text only
|
||||
|
||||
/* 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>
|
||||
*/
|
||||
this._highlighter = {
|
||||
|
||||
doc: null,
|
||||
|
||||
spans: [],
|
||||
|
||||
search: function (aWord, matchCase) {
|
||||
var finder = services.create("find");
|
||||
if (matchCase !== undefined)
|
||||
self._caseSensitive = matchCase;
|
||||
|
||||
var range;
|
||||
while ((range = finder.Find(aWord, this.searchRange, this.startPt, this.endPt)))
|
||||
yield range;
|
||||
},
|
||||
|
||||
highlightDoc: function highlightDoc(win, aWord) {
|
||||
this.doc = content.document; // XXX
|
||||
Array.forEach(win.frames, function (frame) this.highlightDoc(frame, aWord), this);
|
||||
|
||||
var doc = win.document;
|
||||
if (!doc || !(doc instanceof HTMLDocument))
|
||||
return;
|
||||
|
||||
if (!aWord) {
|
||||
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(aWord, 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(aRange, aNode) {
|
||||
var startContainer = aRange.startContainer;
|
||||
var startOffset = aRange.startOffset;
|
||||
var endOffset = aRange.endOffset;
|
||||
var docfrag = aRange.extractContents();
|
||||
var before = startContainer.splitText(startOffset);
|
||||
var parent = before.parentNode;
|
||||
aNode.appendChild(docfrag);
|
||||
parent.insertBefore(aNode, before);
|
||||
this.spans.push(aNode);
|
||||
return aNode;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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
|
||||
};
|
||||
},
|
||||
|
||||
// set searchString, searchPattern, caseSensitive, linksOnly
|
||||
_processUserPattern: function (pattern) {
|
||||
//// strip off pattern terminator and offset
|
||||
//if (backwards)
|
||||
// pattern = pattern.replace(/\?.*/, "");
|
||||
//else
|
||||
// pattern = pattern.replace(/\/.*/, "");
|
||||
|
||||
this._searchPattern = pattern;
|
||||
|
||||
// links only search - \l wins if both modifiers specified
|
||||
if (/\\l/.test(pattern))
|
||||
this._linksOnly = true;
|
||||
else if (/\L/.test(pattern))
|
||||
this._linksOnly = false;
|
||||
else if (options["linksearch"])
|
||||
this._linksOnly = true;
|
||||
else
|
||||
this._linksOnly = false;
|
||||
|
||||
// strip links-only modifiers
|
||||
pattern = pattern.replace(/(\\)?\\[lL]/g, function ($0, $1) { return $1 ? $0 : ""; });
|
||||
|
||||
// case sensitivity - \c wins if both modifiers specified
|
||||
if (/\c/.test(pattern))
|
||||
this._caseSensitive = false;
|
||||
else if (/\C/.test(pattern))
|
||||
this._caseSensitive = true;
|
||||
else if (options["ignorecase"] && options["smartcase"] && /[A-Z]/.test(pattern))
|
||||
this._caseSensitive = true;
|
||||
else if (options["ignorecase"])
|
||||
this._caseSensitive = false;
|
||||
else
|
||||
this._caseSensitive = true;
|
||||
|
||||
// strip case-sensitive modifiers
|
||||
pattern = pattern.replace(/(\\)?\\[cC]/g, function ($0, $1) { return $1 ? $0 : ""; });
|
||||
|
||||
// remove any modifier escape \
|
||||
pattern = pattern.replace(/\\(\\[cClL])/g, "$1");
|
||||
|
||||
this._searchString = pattern;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the search dialog is requested.
|
||||
*
|
||||
* @param {number} mode The search mode, either modes.SEARCH_FORWARD or
|
||||
* modes.SEARCH_BACKWARD.
|
||||
* @default modes.SEARCH_FORWARD
|
||||
*/
|
||||
openPrompt: function (mode) {
|
||||
this._backwards = mode == modes.SEARCH_BACKWARD;
|
||||
commandline.open(this._backwards ? "?" : "/", "", mode);
|
||||
// TODO: focus the top of the currently visible screen
|
||||
},
|
||||
|
||||
// TODO: backwards seems impossible i fear
|
||||
/**
|
||||
* Searches the current buffer for <b>str</b>.
|
||||
*
|
||||
* @param {string} str The string to find.
|
||||
*/
|
||||
find: function (str) {
|
||||
let fastFind = config.browser.fastFind;
|
||||
|
||||
this._processUserPattern(str);
|
||||
fastFind.caseSensitive = this._caseSensitive;
|
||||
this._found = fastFind.find(this._searchString, this._linksOnly) != Ci.nsITypeAheadFind.FIND_NOTFOUND;
|
||||
|
||||
if (!this._found)
|
||||
this.setTimeout(function () liberator.echoerr("E486: Pattern not found: " + this._searchPattern, commandline.FORCE_SINGLELINE), 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Searches the current buffer again for the most recently used search
|
||||
* string.
|
||||
*
|
||||
* @param {boolean} reverse Whether to search forwards or backwards.
|
||||
* @default false
|
||||
*/
|
||||
findAgain: function (reverse) {
|
||||
// This hack is needed to make n/N work with the correct string, if
|
||||
// we typed /foo<esc> after the original search. Since searchString is
|
||||
// readonly we have to call find() again to update it.
|
||||
if (config.browser.fastFind.searchString != this._lastSearchString)
|
||||
this.find(this._lastSearchString);
|
||||
|
||||
let up = reverse ? !this._lastSearchBackwards : this._lastSearchBackwards;
|
||||
let result = config.browser.fastFind.findAgain(up, this._linksOnly);
|
||||
|
||||
if (result == Ci.nsITypeAheadFind.FIND_NOTFOUND)
|
||||
liberator.echoerr("E486: Pattern not found: " + this._lastSearchPattern, commandline.FORCE_SINGLELINE);
|
||||
else if (result == Ci.nsITypeAheadFind.FIND_WRAPPED) {
|
||||
// hack needed, because wrapping causes a "scroll" event which clears
|
||||
// our command line
|
||||
setTimeout(function () {
|
||||
let msg = up ? "search hit TOP, continuing at BOTTOM" : "search hit BOTTOM, continuing at TOP";
|
||||
commandline.echo(msg, commandline.HL_WARNINGMSG, commandline.APPEND_TO_MESSAGES | commandline.FORCE_SINGLELINE);
|
||||
}, 0);
|
||||
}
|
||||
else {
|
||||
commandline.echo((up ? "?" : "/") + this._lastSearchPattern, null, commandline.FORCE_SINGLELINE);
|
||||
|
||||
if (options["hlsearch"])
|
||||
this.highlight(this._lastSearchString);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the user types a key in the search dialog. Triggers a
|
||||
* search attempt if 'incsearch' is set.
|
||||
*
|
||||
* @param {string} str The search string.
|
||||
*/
|
||||
onKeyPress: function (str) {
|
||||
if (options["incsearch"])
|
||||
this.find(str);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the <Enter> key is pressed to trigger a search.
|
||||
*
|
||||
* @param {string} str The search string.
|
||||
* @param {boolean} forcedBackward Whether to search forwards or
|
||||
* backwards. This overrides the direction set in
|
||||
* (@link #openPrompt).
|
||||
* @default false
|
||||
*/
|
||||
onSubmit: function (str, forcedBackward) {
|
||||
if (typeof forcedBackward === "boolean")
|
||||
this._backwards = forcedBackward;
|
||||
|
||||
if (str)
|
||||
var pattern = str;
|
||||
else {
|
||||
liberator.assert(this._lastSearchPattern, "E35: No previous search pattern");
|
||||
pattern = this._lastSearchPattern;
|
||||
}
|
||||
|
||||
this.clear();
|
||||
|
||||
if (!options["incsearch"] || !str || !this._found) {
|
||||
// prevent any current match from matching again
|
||||
if (!window.content.getSelection().isCollapsed)
|
||||
window.content.getSelection().getRangeAt(0).collapse(this._backwards);
|
||||
|
||||
this.find(pattern);
|
||||
}
|
||||
|
||||
this._lastSearchBackwards = this._backwards;
|
||||
//lastSearchPattern = pattern.replace(backwards ? /\?.*/ : /\/.*/, ""); // XXX
|
||||
this._lastSearchPattern = pattern;
|
||||
this._lastSearchString = this._searchString;
|
||||
|
||||
// TODO: move to find() when reverse incremental searching is kludged in
|
||||
// need to find again for reverse searching
|
||||
if (this._backwards)
|
||||
this.setTimeout(function () { this.findAgain(false); }, 0);
|
||||
|
||||
if (options["hlsearch"])
|
||||
this.highlight(this._searchString);
|
||||
|
||||
modes.reset();
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the search is canceled. For example, if someone presses
|
||||
* <Esc> while typing a search.
|
||||
*/
|
||||
onCancel: function () {
|
||||
// TODO: code to reposition the document to the place before search started
|
||||
},
|
||||
|
||||
/**
|
||||
* Highlights all occurances of <b>str</b> in the buffer.
|
||||
*
|
||||
* @param {string} str The string to highlight.
|
||||
*/
|
||||
highlight: function (str) {
|
||||
// FIXME: Thunderbird incompatible
|
||||
if (config.name == "Muttator")
|
||||
return;
|
||||
|
||||
if (this._highlighter.isHighlighted(content.document))
|
||||
return;
|
||||
|
||||
if (!str)
|
||||
str = this._lastSearchString;
|
||||
|
||||
this._highlighter.highlightDoc(window.content, str);
|
||||
|
||||
// recreate selection since highlightDoc collapses the selection
|
||||
if (window.content.getSelection().isCollapsed)
|
||||
config.browser.fastFind.findAgain(this._backwards, this._linksOnly);
|
||||
|
||||
// TODO: remove highlighting from non-link matches (HTML - A/AREA with href attribute; XML - Xlink [type="simple"])
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears all search highlighting.
|
||||
*/
|
||||
clear: function () {
|
||||
this._highlighter.clear();
|
||||
}
|
||||
}, {
|
||||
}, {
|
||||
commandline: function () {
|
||||
// Event handlers for search - closure is needed
|
||||
commandline.registerCallback("change", modes.SEARCH_FORWARD, this.closure.onKeyPress);
|
||||
commandline.registerCallback("submit", modes.SEARCH_FORWARD, this.closure.onSubmit);
|
||||
commandline.registerCallback("cancel", modes.SEARCH_FORWARD, this.closure.onCancel);
|
||||
// TODO: allow advanced myModes in register/triggerCallback
|
||||
commandline.registerCallback("change", modes.SEARCH_BACKWARD, this.closure.onKeyPress);
|
||||
commandline.registerCallback("submit", modes.SEARCH_BACKWARD, this.closure.onSubmit);
|
||||
commandline.registerCallback("cancel", modes.SEARCH_BACKWARD, this.closure.onCancel);
|
||||
|
||||
},
|
||||
commands: function () {
|
||||
commands.add(["noh[lsearch]"],
|
||||
"Remove the search highlighting",
|
||||
function () { finder.clear(); },
|
||||
{ argCount: "0" });
|
||||
},
|
||||
mappings: function () {
|
||||
var myModes = config.browserModes;
|
||||
myModes = myModes.concat([modes.CARET]);
|
||||
|
||||
mappings.add(myModes,
|
||||
["/"], "Search forward for a pattern",
|
||||
function () { finder.openPrompt(modes.SEARCH_FORWARD); });
|
||||
|
||||
mappings.add(myModes,
|
||||
["?"], "Search backwards for a pattern",
|
||||
function () { finder.openPrompt(modes.SEARCH_BACKWARD); });
|
||||
|
||||
mappings.add(myModes,
|
||||
["n"], "Find next",
|
||||
function () { finder.findAgain(false); });
|
||||
|
||||
mappings.add(myModes,
|
||||
["N"], "Find previous",
|
||||
function () { finder.findAgain(true); });
|
||||
|
||||
mappings.add(myModes.concat([modes.CARET, modes.TEXTAREA]), ["*"],
|
||||
"Find word under cursor",
|
||||
function () {
|
||||
this._found = false;
|
||||
finder.onSubmit(buffer.getCurrentWord(), false);
|
||||
});
|
||||
|
||||
mappings.add(myModes.concat([modes.CARET, modes.TEXTAREA]), ["#"],
|
||||
"Find word under cursor backwards",
|
||||
function () {
|
||||
this._found = false;
|
||||
finder.onSubmit(buffer.getCurrentWord(), true);
|
||||
});
|
||||
},
|
||||
options: function () {
|
||||
options.add(["hlsearch", "hls"],
|
||||
"Highlight previous search pattern matches",
|
||||
"boolean", "false", {
|
||||
setter: function (value) {
|
||||
try {
|
||||
if (value)
|
||||
finder.highlight();
|
||||
else
|
||||
finder.clear();
|
||||
}
|
||||
catch (e) {}
|
||||
|
||||
return value;
|
||||
}
|
||||
});
|
||||
|
||||
options.add(["ignorecase", "ic"],
|
||||
"Ignore case in search patterns",
|
||||
"boolean", true);
|
||||
|
||||
options.add(["incsearch", "is"],
|
||||
"Show where the search pattern matches as it is typed",
|
||||
"boolean", true);
|
||||
|
||||
options.add(["linksearch", "lks"],
|
||||
"Limit the search to hyperlink text",
|
||||
"boolean", false);
|
||||
|
||||
options.add(["smartcase", "scs"],
|
||||
"Override the 'ignorecase' option if the pattern contains uppercase characters",
|
||||
"boolean", true);
|
||||
}
|
||||
});
|
||||
|
||||
/** @instance rangefinder */
|
||||
const RangeFinder = Module("rangefinder", {
|
||||
requires: ["config"],
|
||||
|
||||
@@ -471,6 +18,7 @@ const RangeFinder = Module("rangefinder", {
|
||||
let backwards = mode == modes.FIND_BACKWARD;
|
||||
commandline.open(backwards ? "?" : "/", "", mode);
|
||||
|
||||
this.rangeFind = null;
|
||||
this.find("", backwards);
|
||||
},
|
||||
|
||||
@@ -537,6 +85,7 @@ const RangeFinder = Module("rangefinder", {
|
||||
|
||||
if (options["hlsearch"])
|
||||
this.highlight();
|
||||
this.rangeFind.focus();
|
||||
},
|
||||
|
||||
// Called when the user types a key in the search dialog. Triggers a find attempt if 'incsearch' is set
|
||||
@@ -557,6 +106,7 @@ const RangeFinder = Module("rangefinder", {
|
||||
|
||||
if (options["hlsearch"])
|
||||
this.highlight();
|
||||
this.rangeFind.focus();
|
||||
|
||||
modes.reset();
|
||||
},
|
||||
@@ -603,48 +153,106 @@ const RangeFinder = Module("rangefinder", {
|
||||
|
||||
},
|
||||
commands: function () {
|
||||
commands.add(["noh[lsearch]"],
|
||||
"Remove the search highlighting",
|
||||
function () { rangefinder.clear(); },
|
||||
{ argCount: "0" });
|
||||
},
|
||||
mappings: function () {
|
||||
var myModes = config.browserModes.concat([modes.CARET]);
|
||||
|
||||
mappings.add(myModes,
|
||||
["g/"], "Search forward for a pattern",
|
||||
["/"], "Search forward for a pattern",
|
||||
function () { rangefinder.openPrompt(modes.FIND_FORWARD); });
|
||||
|
||||
mappings.add(myModes,
|
||||
["g?"], "Search backwards for a pattern",
|
||||
["?"], "Search backwards for a pattern",
|
||||
function () { rangefinder.openPrompt(modes.FIND_BACKWARD); });
|
||||
|
||||
mappings.add(myModes,
|
||||
["g."], "Find next",
|
||||
["n"], "Find next",
|
||||
function () { rangefinder.findAgain(false); });
|
||||
|
||||
mappings.add(myModes,
|
||||
["g,"], "Find previous",
|
||||
["N"], "Find previous",
|
||||
function () { rangefinder.findAgain(true); });
|
||||
|
||||
mappings.add(myModes.concat([modes.CARET, modes.TEXTAREA]), ["g*"],
|
||||
mappings.add(myModes.concat([modes.CARET, modes.TEXTAREA]), ["*"],
|
||||
"Find word under cursor",
|
||||
function () {
|
||||
rangefinder._found = false;
|
||||
rangefinder.onSubmit(buffer.getCurrentWord(), false);
|
||||
});
|
||||
|
||||
mappings.add(myModes.concat([modes.CARET, modes.TEXTAREA]), ["g#"],
|
||||
mappings.add(myModes.concat([modes.CARET, modes.TEXTAREA]), ["#"],
|
||||
"Find word under cursor backwards",
|
||||
function () {
|
||||
rangefinder._found = false;
|
||||
rangefinder.onSubmit(buffer.getCurrentWord(), true);
|
||||
});
|
||||
|
||||
},
|
||||
modes: function () {
|
||||
modes.addMode("FIND_FORWARD", true);
|
||||
modes.addMode("FIND_BACKWARD", true);
|
||||
},
|
||||
options: function () {
|
||||
options.add(["hlsearch", "hls"],
|
||||
"Highlight previous search pattern matches",
|
||||
"boolean", "false", {
|
||||
setter: function (value) {
|
||||
try {
|
||||
if (value)
|
||||
rangefinder.highlight();
|
||||
else
|
||||
rangefinder.clear();
|
||||
}
|
||||
catch (e) {}
|
||||
|
||||
return value;
|
||||
}
|
||||
});
|
||||
|
||||
options.add(["ignorecase", "ic"],
|
||||
"Ignore case in search patterns",
|
||||
"boolean", true);
|
||||
|
||||
options.add(["incsearch", "is"],
|
||||
"Show where the search pattern matches as it is typed",
|
||||
"boolean", true);
|
||||
|
||||
options.add(["linksearch", "lks"],
|
||||
"Limit the search to hyperlink text",
|
||||
"boolean", false);
|
||||
|
||||
options.add(["smartcase", "scs"],
|
||||
"Override the 'ignorecase' option if the pattern contains uppercase characters",
|
||||
"boolean", true);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @class RangeFind
|
||||
*
|
||||
* A fairly sophisticated typeahead-find replacement. It supports
|
||||
* incremental search very much as the builtin component.
|
||||
* Additionally, it supports several features impossible to
|
||||
* implement using the standard component. Incremental searching
|
||||
* works both forwards and backwards. Erasing characters during an
|
||||
* incremental search moves the selection back to the first
|
||||
* available match for the shorter term. The selection and viewport
|
||||
* are restored when the search is canceled.
|
||||
*
|
||||
* Also, in addition to full support for frames and iframes, this
|
||||
* implementation will begin searching from the position of the
|
||||
* caret in the last active frame. This is contrary to the behavior
|
||||
* of the builtin component, which always starts a search from the
|
||||
* begining of the first frame in the case of frameset documents,
|
||||
* and cycles through all frames from begining to end. This makes it
|
||||
* impossible to choose the starting point of a search for such
|
||||
* documents, and represents a major detriment to productivity where
|
||||
* large amounts of data are concerned (e.g., for API documents).
|
||||
*/
|
||||
const RangeFind = Class("RangeFind", {
|
||||
init: function (matchCase, backward, elementPath) {
|
||||
this.window = Cu.getWeakReference(window);
|
||||
@@ -655,20 +263,28 @@ const RangeFind = Class("RangeFind", {
|
||||
this.finder.caseSensitive = this.matchCase;
|
||||
|
||||
this.ranges = this.makeFrameList(content);
|
||||
this.range = RangeFind.Range(tabs.localStore.focusedFrame || content);
|
||||
|
||||
this.startRange = (this.range.selection.rangeCount ? this.range.selection.getRangeAt(0) : this.ranges[0].range).cloneRange();
|
||||
this.startRange.collapse(!backward);
|
||||
this.range = this.findRange(this.startRange);
|
||||
this.ranges.first = this.range;
|
||||
this.reset();
|
||||
|
||||
this.highlighted = null;
|
||||
this.lastString = "";
|
||||
this.lastRange = null;
|
||||
this.forward = null;
|
||||
this.found = false;
|
||||
},
|
||||
|
||||
get selectedRange() {
|
||||
let range = RangeFind.Range(tabs.localStore.focusedFrame || content);
|
||||
return (range.selection.rangeCount ? range.selection.getRangeAt(0) : this.ranges[0].range).cloneRange();
|
||||
},
|
||||
|
||||
reset: function () {
|
||||
this.startRange = this.selectedRange;
|
||||
this.startRange.collapse(!this.reverse);
|
||||
this.lastRange = this.selectedRange;
|
||||
this.range = this.findRange(this.startRange);
|
||||
this.ranges.first = this.range;
|
||||
},
|
||||
|
||||
sameDocument: function (r1, r2) r1 && r2 && r1.endContainer.ownerDocument == r2.endContainer.ownerDocument,
|
||||
|
||||
compareRanges: function (r1, r2)
|
||||
@@ -699,6 +315,16 @@ const RangeFind = Class("RangeFind", {
|
||||
}
|
||||
},
|
||||
|
||||
focus: function() {
|
||||
if(this.lastRange)
|
||||
var node = util.evaluateXPath(RangeFind.selectNodePath, this.range.document,
|
||||
this.lastRange.commonAncestorContainer).snapshotItem(0);
|
||||
if(node) {
|
||||
node.focus();
|
||||
this.search(null, false); // Rehighlight collapsed range
|
||||
}
|
||||
},
|
||||
|
||||
makeFrameList: function (win) {
|
||||
const self = this;
|
||||
win = win.top;
|
||||
@@ -706,15 +332,21 @@ const RangeFind = Class("RangeFind", {
|
||||
let backup = null;
|
||||
|
||||
function pushRange(start, end) {
|
||||
function push(r) {
|
||||
r = RangeFind.Range(r, frames.length);
|
||||
if (r)
|
||||
frames.push(r);
|
||||
}
|
||||
|
||||
let range = start.startContainer.ownerDocument.createRange();
|
||||
range.setStart(start.startContainer, start.startOffset);
|
||||
range.setEnd(end.startContainer, end.startOffset);
|
||||
|
||||
if (!self.elementPath)
|
||||
frames.push(RangeFind.Range(range, frames.length));
|
||||
push(range);
|
||||
else
|
||||
for (let r in self.findSubRanges(range))
|
||||
frames.push(RangeFind.Range(r, frames.length));
|
||||
push(r);
|
||||
}
|
||||
function rec(win) {
|
||||
let doc = win.document;
|
||||
@@ -741,8 +373,8 @@ const RangeFind = Class("RangeFind", {
|
||||
|
||||
// This doesn't work yet.
|
||||
resetCaret: function () {
|
||||
let equal = function (r1, r2) !r1.compareBoundaryPoints(Range.START_TO_START, r2) && !r1.compareBoundaryPoints(Range.END_TO_END, r2);
|
||||
letselection = this.win.getSelection();
|
||||
let equal = RangeFind.equal;
|
||||
let selection = this.win.getSelection();
|
||||
if (selection.rangeCount == 0)
|
||||
selection.addRange(this.pageStart);
|
||||
function getLines() {
|
||||
@@ -792,6 +424,9 @@ const RangeFind = Class("RangeFind", {
|
||||
},
|
||||
|
||||
search: function (word, reverse, private_) {
|
||||
if (!private_ && this.lastRange && !RangeFind.equal(this.selectedRange, this.lastRange))
|
||||
this.reset();
|
||||
|
||||
this.wrapped = false;
|
||||
this.finder.findBackwards = reverse ? !this.reverse : this.reverse;
|
||||
let again = word == null;
|
||||
@@ -886,6 +521,7 @@ const RangeFind = Class("RangeFind", {
|
||||
parent.insertBefore(node, before);
|
||||
range.selectNode(node);
|
||||
}
|
||||
|
||||
function unhighlight(range) {
|
||||
let elem = range.startContainer;
|
||||
while (!(elem instanceof Element) && elem.parentNode)
|
||||
@@ -913,7 +549,7 @@ const RangeFind = Class("RangeFind", {
|
||||
else {
|
||||
this.highlighted = this.lastString;
|
||||
this.addListeners();
|
||||
this.search(null, false);
|
||||
this.search(null, false); // Rehighlight collapsed range
|
||||
}
|
||||
},
|
||||
|
||||
@@ -952,6 +588,9 @@ const RangeFind = Class("RangeFind", {
|
||||
this.window = this.document.defaultView;
|
||||
this.range = range;
|
||||
|
||||
if (this.selection == null)
|
||||
return false;
|
||||
|
||||
this.save();
|
||||
},
|
||||
|
||||
@@ -989,12 +628,27 @@ const RangeFind = Class("RangeFind", {
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsISelectionDisplay)
|
||||
.QueryInterface(Ci.nsISelectionController),
|
||||
get selection() this.selectionController.getSelection(Ci.nsISelectionController.SELECTION_NORMAL)
|
||||
get selection() {
|
||||
try {
|
||||
return this.selectionController.getSelection(Ci.nsISelectionController.SELECTION_NORMAL)
|
||||
} catch (e) {
|
||||
return null;
|
||||
}}
|
||||
|
||||
}),
|
||||
selectNodePath: ["ancestor-or-self::" + s for ([i, s] in Iterator(
|
||||
["a", "xhtml:a", "*[@onclick]"]))].join(" | "),
|
||||
endpoint: function (range, before) {
|
||||
range = range.cloneRange();
|
||||
range.collapse(before);
|
||||
return range;
|
||||
},
|
||||
equal: function (r1, r2) {
|
||||
try {
|
||||
return !r1.compareBoundaryPoints(Range.START_TO_START, r2) && !r1.compareBoundaryPoints(Range.END_TO_END, r2)
|
||||
}
|
||||
catch (e) {}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
|
||||
"use strict";
|
||||
|
||||
function checkFragment() {
|
||||
document.title = document.getElementsByTagNameNS("http://www.w3.org/1999/xhtml", "title")[0].textContent;
|
||||
|
||||
@@ -202,7 +202,7 @@
|
||||
<xsl:variable name="type" select="preceding-sibling::liberator:type[1] | following-sibling::liberator:type[1]"/>
|
||||
<span liberator:highlight="HelpDefault">(default:<xsl:text> </xsl:text>
|
||||
<xsl:choose>
|
||||
<xsl:when test="starts-with($type, 'string')">
|
||||
<xsl:when test="starts-with($type, 'string') or starts-with($type, 'regex')">
|
||||
<span liberator:highlight="HelpString"><xsl:apply-templates/></span>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
@@ -351,9 +351,13 @@
|
||||
|
||||
<xsl:template match="liberator:ex" mode="pass-2">
|
||||
<span liberator:highlight="HelpEx">
|
||||
<xsl:call-template name="linkify-tag">
|
||||
<xsl:with-param name="contents" select="."/>
|
||||
</xsl:call-template>
|
||||
<xsl:variable name="tag" select="str:tokenize(text(), ' [!')[1]"/>
|
||||
<a href="liberator://help-tag/{$tag}" style="color: inherit;">
|
||||
<xsl:if test="contains($tags, concat(' ', $tag, ' '))">
|
||||
<xsl:attribute name="href">#<xsl:value-of select="$tag"/></xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:apply-templates/>
|
||||
</a>
|
||||
</span>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
/** @instance hints */
|
||||
@@ -40,12 +42,12 @@ const Hints = Module("hints", {
|
||||
"?": Mode("Show information for hint", function (elem) buffer.showElementInfo(elem), extended),
|
||||
s: Mode("Save hint", function (elem) buffer.saveLink(elem, true)),
|
||||
a: Mode("Save hint with prompt", function (elem) buffer.saveLink(elem, false)),
|
||||
f: Mode("Focus frame", function (elem) elem.ownerDocument.defaultView.focus(), function () util.makeXPath(["body"])),
|
||||
f: Mode("Focus frame", function (elem) elem.ownerDocument.defaultView.focus(), function () ["body"]),
|
||||
o: Mode("Follow hint", function (elem) buffer.followLink(elem, liberator.CURRENT_TAB)),
|
||||
t: Mode("Follow hint in a new tab", function (elem) buffer.followLink(elem, liberator.NEW_TAB)),
|
||||
b: Mode("Follow hint in a background tab", function (elem) buffer.followLink(elem, liberator.NEW_BACKGROUND_TAB)),
|
||||
w: Mode("Follow hint in a new window", function (elem) buffer.followLink(elem, liberator.NEW_WINDOW), extended),
|
||||
F: Mode("Open multiple hints in tabs", followAndReshow),
|
||||
F: Mode("Open multiple hints in tabs", function (elem) { buffer.followLink(elem, liberator.NEW_BACKGROUND_TAB); hints.show("F") }),
|
||||
O: Mode("Generate an ':open URL' using hint", function (elem, loc) commandline.open(":", "open " + loc, modes.EX)),
|
||||
T: Mode("Generate a ':tabopen URL' using hint", function (elem, loc) commandline.open(":", "tabopen " + loc, modes.EX)),
|
||||
W: Mode("Generate a ':winopen URL' using hint", function (elem, loc) commandline.open(":", "winopen " + loc, modes.EX)),
|
||||
@@ -57,21 +59,6 @@ const Hints = Module("hints", {
|
||||
i: Mode("Show image", function (elem) liberator.open(elem.src), images),
|
||||
I: Mode("Show image in a new tab", function (elem) liberator.open(elem.src, liberator.NEW_TAB), images)
|
||||
};
|
||||
|
||||
/**
|
||||
* Follows the specified hint and then reshows all hints. Used to open
|
||||
* multiple hints in succession.
|
||||
*
|
||||
* @param {Node} elem The selected hint.
|
||||
*/
|
||||
function followAndReshow(elem) {
|
||||
buffer.followLink(elem, liberator.NEW_BACKGROUND_TAB);
|
||||
|
||||
// TODO: Maybe we find a *simple* way to keep the hints displayed rather than
|
||||
// showing them again, or is this short flash actually needed as a "usability
|
||||
// feature"? --mst
|
||||
hints.show("F");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -127,7 +114,7 @@ const Hints = Module("hints", {
|
||||
|
||||
let type = elem.type;
|
||||
|
||||
if (elem instanceof HTMLInputElement && /(submit|button|this._reset)/.test(type))
|
||||
if (elem instanceof HTMLInputElement && /(submit|button|reset)/.test(type))
|
||||
return [elem.value, false];
|
||||
else {
|
||||
for (let [, option] in Iterator(options["hintinputs"].split(","))) {
|
||||
@@ -266,14 +253,10 @@ const Hints = Module("hints", {
|
||||
let hint = { elem: elem, showText: false };
|
||||
|
||||
// TODO: for iframes, this calculation is wrong
|
||||
rect = elem.getBoundingClientRect();
|
||||
let rect = elem.getBoundingClientRect();
|
||||
if (!rect || rect.top > height || rect.bottom < 0 || rect.left > width || rect.right < 0)
|
||||
continue;
|
||||
|
||||
rect = elem.getClientRects()[0];
|
||||
if (!rect)
|
||||
continue;
|
||||
|
||||
let computedStyle = doc.defaultView.getComputedStyle(elem, null);
|
||||
if (computedStyle.getPropertyValue("visibility") != "visible" || computedStyle.getPropertyValue("display") == "none")
|
||||
continue;
|
||||
@@ -370,7 +353,7 @@ const Hints = Module("hints", {
|
||||
|
||||
if (hint.text == "" && hint.elem.firstChild && hint.elem.firstChild instanceof HTMLImageElement) {
|
||||
if (!hint.imgSpan) {
|
||||
rect = hint.elem.firstChild.getBoundingClientRect();
|
||||
var rect = hint.elem.firstChild.getBoundingClientRect();
|
||||
if (!rect)
|
||||
continue;
|
||||
|
||||
@@ -394,11 +377,11 @@ const Hints = Module("hints", {
|
||||
}
|
||||
}
|
||||
|
||||
if (config.browser.markupDocumentViewer.authorStyleDisabled) {
|
||||
if (options["usermode"]) {
|
||||
let css = [];
|
||||
// FIXME: Broken for imgspans.
|
||||
for (let [, { doc: doc }] in Iterator(this._docs)) {
|
||||
for (let elem in util.evaluateXPath(" {//*[@liberator:highlight and @number]", doc)) {
|
||||
for (let elem in util.evaluateXPath("//*[@liberator:highlight and @number]", doc)) {
|
||||
let group = elem.getAttributeNS(NS.uri, "highlight");
|
||||
css.push(highlight.selector(group) + "[number=" + elem.getAttribute("number").quote() + "] { " + elem.style.cssText + " }");
|
||||
}
|
||||
@@ -1058,8 +1041,8 @@ const Hints = Module("hints", {
|
||||
},
|
||||
options: function () {
|
||||
const DEFAULT_HINTTAGS =
|
||||
util.makeXPath(["input[not(@type='hidden')]", "a", "area", "iframe", "textarea", "button", "select"])
|
||||
+ " | //*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @role='link']";
|
||||
util.makeXPath(["input[not(@type='hidden')]", "a", "area", "iframe", "textarea", "button", "select",
|
||||
"*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @role='link']"]);
|
||||
|
||||
function checkXPath(val) {
|
||||
try {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
"use strict";
|
||||
|
||||
const History = Module("history", {
|
||||
requires: ["config"],
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
|
||||
// Some code based on Venkman
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
@@ -160,7 +162,7 @@ const File = Class("File", {
|
||||
mode = File.MODE_WRONLY | File.MODE_CREATE | File.MODE_TRUNCATE;
|
||||
|
||||
if (!perms)
|
||||
perms = 0644;
|
||||
perms = parseInt('0644', 8);
|
||||
|
||||
ofstream.init(this, mode, perms, 0);
|
||||
let ocstream = getStream(0);
|
||||
@@ -240,7 +242,7 @@ const File = Class("File", {
|
||||
*/
|
||||
MODE_EXCL: 0x80,
|
||||
|
||||
expandPathList: function (list) list.split(",").map(this.expandPath).join(","),
|
||||
expandPathList: function (list) list.map(this.expandPath),
|
||||
|
||||
expandPath: function (path, relative) {
|
||||
|
||||
@@ -338,7 +340,7 @@ const IO = Module("io", {
|
||||
let file = download.targetFile.path;
|
||||
let size = download.size;
|
||||
|
||||
liberator.echomsg("Download of " + title + " to " + file + " finished", 1);
|
||||
liberator.echomsg("Download of " + title + " to " + file + " finished", 1, commandline.ACTIVE_WINDOW);
|
||||
autocommands.trigger("DownloadPost", { url: url, title: title, file: file, size: size });
|
||||
}
|
||||
},
|
||||
@@ -485,7 +487,7 @@ const IO = Module("io", {
|
||||
let file = services.get("directory").get("TmpD", Ci.nsIFile);
|
||||
|
||||
file.append(config.tempFile);
|
||||
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
|
||||
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt('0600', 8));
|
||||
|
||||
return File(file);
|
||||
},
|
||||
@@ -595,6 +597,8 @@ lookup:
|
||||
*/
|
||||
source: function (filename, silent) {
|
||||
let wasSourcing = this.sourcing;
|
||||
liberator.dump("sourcing " + filename);
|
||||
let time = Date.now();
|
||||
try {
|
||||
var file = File(filename);
|
||||
this.sourcing = {
|
||||
@@ -624,8 +628,7 @@ lookup:
|
||||
if (/\.js$/.test(filename)) {
|
||||
try {
|
||||
liberator.loadScript(uri.spec, Script(file));
|
||||
if (liberator.initialized)
|
||||
liberator.initHelp();
|
||||
liberator.helpInitialized = false;
|
||||
}
|
||||
catch (e) {
|
||||
let err = new Error();
|
||||
@@ -713,6 +716,7 @@ lookup:
|
||||
liberator.echoerr(message);
|
||||
}
|
||||
finally {
|
||||
liberator.dump("done sourcing " + filename + ": " + (Date.now() - time) + "ms");
|
||||
this.sourcing = wasSourcing;
|
||||
}
|
||||
},
|
||||
@@ -1028,8 +1032,8 @@ lookup:
|
||||
b.isdir - a.isdir || String.localeCompare(a.text, b.text);
|
||||
|
||||
if (options["wildignore"]) {
|
||||
let wigRegexp = RegExp("(^" + options.get("wildignore").values.join("|") + ")$");
|
||||
context.filters.push(function ({item: f}) f.isDirectory() || !wigRegexp.test(f.leafName));
|
||||
let wig = options.get("wildignore");
|
||||
context.filters.push(function ({item: f}) f.isDirectory() || !wig.getKey(this.name));
|
||||
}
|
||||
|
||||
// context.background = true;
|
||||
@@ -1061,7 +1065,10 @@ lookup:
|
||||
};
|
||||
};
|
||||
|
||||
completion.addUrlCompleter("f", "Local files", completion.file);
|
||||
completion.addUrlCompleter("f", "Local files", function (context, full) {
|
||||
if (!/^\.?\//.test(context.filter))
|
||||
completion.file(context, full);
|
||||
});
|
||||
},
|
||||
options: function () {
|
||||
var shell, shellcmdflag;
|
||||
@@ -1099,6 +1106,10 @@ lookup:
|
||||
options.add(["shellcmdflag", "shcf"],
|
||||
"Flag passed to shell when executing :! and :run commands",
|
||||
"string", shellcmdflag);
|
||||
|
||||
options.add(["wildignore", "wig"],
|
||||
"List of file patterns to ignore when completing files",
|
||||
"regexlist", "");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
"use strict";
|
||||
|
||||
// TODO: Clean this up.
|
||||
|
||||
@@ -33,17 +34,18 @@ const JavaScript = Module("javascript", {
|
||||
},
|
||||
|
||||
iter: function iter(obj, toplevel) {
|
||||
"use strict";
|
||||
toplevel = !!toplevel;
|
||||
let seen = {};
|
||||
let ret = {};
|
||||
|
||||
try {
|
||||
if(obj == null)
|
||||
return;
|
||||
|
||||
if(options["jsdebugger"]) {
|
||||
let orig = obj;
|
||||
let top = services.get("debugger").wrapValue(obj);
|
||||
|
||||
if (!toplevel)
|
||||
obj = obj.__proto__;
|
||||
|
||||
for (; obj; obj = !toplevel && obj.__proto__) {
|
||||
services.get("debugger").wrapValue(obj).getProperties(ret, {});
|
||||
for (let prop in values(ret.value)) {
|
||||
@@ -51,18 +53,27 @@ const JavaScript = Module("javascript", {
|
||||
if (name in seen)
|
||||
continue;
|
||||
seen[name] = 1;
|
||||
yield [prop.name.stringValue, top.getProperty(prop.name.stringValue).value.getWrappedValue()]
|
||||
if (toplevel || obj !== orig)
|
||||
yield [prop.name.stringValue, top.getProperty(prop.name.stringValue).value.getWrappedValue()]
|
||||
}
|
||||
}
|
||||
// The debugger doesn't list some properties. I can't guess why.
|
||||
for (let k in orig)
|
||||
if (k in orig && !('|' + k in seen) && obj.hasOwnProperty(k) == toplevel)
|
||||
yield [k, this.getKey(orig, k)]
|
||||
// This only lists ENUMERABLE properties.
|
||||
try {
|
||||
for (let k in orig)
|
||||
if (k in orig && !('|' + k in seen)
|
||||
&& Object.hasOwnProperty(orig, k) == toplevel)
|
||||
yield [k, this.getKey(orig, k)]
|
||||
}
|
||||
catch(e) {}
|
||||
}
|
||||
catch(e) {
|
||||
for (k in allkeys(obj))
|
||||
if (obj.hasOwnProperty(k) == toplevel)
|
||||
yield [k, this.getKey(obj, k)];
|
||||
else {
|
||||
for (let k in allkeys(obj))
|
||||
try {
|
||||
if (Object.hasOwnProperty(obj, k) == toplevel)
|
||||
yield [k, this.getKey(obj, k)];
|
||||
}
|
||||
catch (e) {}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -101,7 +112,7 @@ const JavaScript = Module("javascript", {
|
||||
return completions;
|
||||
},
|
||||
|
||||
eval: function eval(arg, key, tmp) {
|
||||
eval: function evalstr(arg, key, tmp) {
|
||||
let cache = this.context.cache.eval;
|
||||
let context = this.context.cache.evalContext;
|
||||
|
||||
@@ -230,8 +241,6 @@ const JavaScript = Module("javascript", {
|
||||
case "'":
|
||||
case "/":
|
||||
case "{":
|
||||
this._push(this._c);
|
||||
break;
|
||||
case "[":
|
||||
this._push(this._c);
|
||||
break;
|
||||
@@ -341,6 +350,10 @@ const JavaScript = Module("javascript", {
|
||||
|
||||
_complete: function (objects, key, compl, string, last) {
|
||||
const self = this;
|
||||
|
||||
if(!options["jsdebugger"] && !this.context.message)
|
||||
this.context.message = "For better completion data, please enable the JavaScript debugger (:set jsdebugger)";
|
||||
|
||||
let orig = compl;
|
||||
if (!compl) {
|
||||
compl = function (context, obj, recurse) {
|
||||
@@ -427,7 +440,8 @@ const JavaScript = Module("javascript", {
|
||||
// Okay, have parse stack. Figure out what we're completing.
|
||||
|
||||
// Find any complete statements that we can eval before we eval our object.
|
||||
// This allows for things like: let doc = window.content.document; let elem = doc.createElement...; elem.<Tab>
|
||||
// This allows for things like:
|
||||
// let doc = window.content.document; let elem = doc.createEle<Tab> ...
|
||||
let prev = 0;
|
||||
for (let [, v] in Iterator(this._get(0).fullStatements)) {
|
||||
let key = this._str.substring(prev, v + 1);
|
||||
@@ -437,14 +451,13 @@ const JavaScript = Module("javascript", {
|
||||
prev = v + 1;
|
||||
}
|
||||
|
||||
// In a string. Check if we're dereferencing an object.
|
||||
// Otherwise, do nothing.
|
||||
// In a string. Check if we're dereferencing an object or
|
||||
// completing a function argument. Otherwise, do nothing.
|
||||
if (this._last == "'" || this._last == '"') {
|
||||
//
|
||||
|
||||
// str = "foo[bar + 'baz"
|
||||
// obj = "foo"
|
||||
// key = "bar + ''"
|
||||
//
|
||||
|
||||
// The top of the stack is the sting we're completing.
|
||||
// Wrap it in its delimiters and eval it to process escape sequences.
|
||||
@@ -497,15 +510,15 @@ const JavaScript = Module("javascript", {
|
||||
// Split up the arguments
|
||||
let prev = this._get(-2).offset;
|
||||
let args = [];
|
||||
for (let [, idx] in Iterator(this._get(-2).comma)) {
|
||||
for (let [i, idx] in Iterator(this._get(-2).comma)) {
|
||||
let arg = this._str.substring(prev + 1, idx);
|
||||
prev = idx;
|
||||
util.memoize(args, this._i, function () self.eval(arg));
|
||||
util.memoize(args, i, function () self.eval(arg));
|
||||
}
|
||||
let key = this._getKey();
|
||||
args.push(key + string);
|
||||
|
||||
compl = function (context, obj) {
|
||||
let compl = function (context, obj) {
|
||||
let res = completer.call(self, context, func, obj, args);
|
||||
if (res)
|
||||
context.completions = res;
|
||||
@@ -520,7 +533,7 @@ const JavaScript = Module("javascript", {
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// str = "foo.bar.baz"
|
||||
// obj = "foo.bar"
|
||||
// key = "baz"
|
||||
@@ -528,11 +541,11 @@ const JavaScript = Module("javascript", {
|
||||
// str = "foo"
|
||||
// obj = [modules, window]
|
||||
// key = "foo"
|
||||
//
|
||||
|
||||
|
||||
let [offset, obj, key] = this._getObjKey(-1);
|
||||
|
||||
// Wait for a keypress before completing the default objects.
|
||||
// Wait for a keypress before completing when there's no key
|
||||
if (!this.context.tabPressed && key == "" && obj.length > 1) {
|
||||
this.context.waitingForTab = true;
|
||||
this.context.message = "Waiting for key press";
|
||||
@@ -589,7 +602,7 @@ const JavaScript = Module("javascript", {
|
||||
let completer = completers[args.length - 1];
|
||||
if (!completer)
|
||||
return [];
|
||||
return completer.call(this, context, obj, args);
|
||||
return completer.call(obj, context, obj, args);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright (c) 2008-2009 Kris Maglione <maglione.k at Gmail>
|
||||
// Copyright (c) 2008-2008 Kris Maglione <maglione.k at Gmail>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
"use strict";
|
||||
|
||||
(function () {
|
||||
const modules = {};
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
@@ -134,7 +136,7 @@ const Liberator = Module("liberator", {
|
||||
forceNewWindow: false,
|
||||
|
||||
/** @property {string} The Liberator version string. */
|
||||
version: "###VERSION### (created: ###DATE###)", // these VERSION and DATE tokens are replaced by the Makefile
|
||||
version: "@VERSION@ (created: @DATE@)", // these VERSION and DATE tokens are replaced by the Makefile
|
||||
|
||||
/**
|
||||
* @property {Object} The map of command-line options. These are
|
||||
@@ -268,7 +270,7 @@ const Liberator = Module("liberator", {
|
||||
let stack = Error().stack.replace(/(?:.*\n){2}/, "");
|
||||
if (frames != null)
|
||||
[stack] = stack.match(RegExp("(?:.*\n){0," + frames + "}"));
|
||||
liberator.dump((msg || "Stack") + "\n" + stack);
|
||||
liberator.dump((msg || "Stack") + "\n" + stack + "\n");
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -329,7 +331,7 @@ const Liberator = Module("liberator", {
|
||||
// you don't like them you can set verbose=0, or use :silent when
|
||||
// someone adds it. I reckon another flag and 'class' of messages
|
||||
// is just going to unnecessarily complicate things. --djk
|
||||
flags |= commandline.APPEND_TO_MESSAGES | commandline.DISALLOW_MULTILINE;
|
||||
flags |= commandline.APPEND_TO_MESSAGES; // | commandline.DISALLOW_MULTILINE;
|
||||
|
||||
if (verbosity == null)
|
||||
verbosity = 0; // verbosity level is exclusionary
|
||||
@@ -542,85 +544,88 @@ const Liberator = Module("liberator", {
|
||||
* Initialize the help system.
|
||||
*/
|
||||
initHelp: function () {
|
||||
let namespaces = [config.name.toLowerCase(), "liberator"];
|
||||
services.get("liberator:").init({});
|
||||
if(!this.helpInitialized) {
|
||||
let namespaces = [config.name.toLowerCase(), "liberator"];
|
||||
services.get("liberator:").init({});
|
||||
|
||||
let tagMap = services.get("liberator:").HELP_TAGS;
|
||||
let fileMap = services.get("liberator:").FILE_MAP;
|
||||
let overlayMap = services.get("liberator:").OVERLAY_MAP;
|
||||
let tagMap = services.get("liberator:").HELP_TAGS;
|
||||
let fileMap = services.get("liberator:").FILE_MAP;
|
||||
let overlayMap = services.get("liberator:").OVERLAY_MAP;
|
||||
|
||||
// Left as an XPCOM instantiation so it can easilly be moved
|
||||
// into XPCOM code.
|
||||
function XSLTProcessor(sheet) {
|
||||
let xslt = Cc["@mozilla.org/document-transformer;1?type=xslt"].createInstance(Ci.nsIXSLTProcessor);
|
||||
xslt.importStylesheet(util.httpGet(sheet).responseXML);
|
||||
return xslt;
|
||||
}
|
||||
|
||||
// Find help and overlay files with the given name.
|
||||
function findHelpFile(file) {
|
||||
let result = [];
|
||||
for (let [, namespace] in Iterator(namespaces)) {
|
||||
let url = ["chrome://", namespace, "/locale/", file, ".xml"].join("");
|
||||
let res = util.httpGet(url);
|
||||
if (res) {
|
||||
if (res.responseXML.documentElement.localName == "document")
|
||||
fileMap[file] = url;
|
||||
if (res.responseXML.documentElement.localName == "overlay")
|
||||
overlayMap[file] = url;
|
||||
result.push(res.responseXML);
|
||||
}
|
||||
// Left as an XPCOM instantiation so it can easilly be moved
|
||||
// into XPCOM code.
|
||||
function XSLTProcessor(sheet) {
|
||||
let xslt = Cc["@mozilla.org/document-transformer;1?type=xslt"].createInstance(Ci.nsIXSLTProcessor);
|
||||
xslt.importStylesheet(util.httpGet(sheet).responseXML);
|
||||
return xslt;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// Find the tags in the document.
|
||||
function addTags(file, doc) {
|
||||
doc = XSLT.transformToDocument(doc);
|
||||
for (let elem in util.evaluateXPath("//xhtml:a/@id", doc))
|
||||
tagMap[elem.value] = file;
|
||||
}
|
||||
|
||||
const XSLT = XSLTProcessor("chrome://liberator/content/help-single.xsl");
|
||||
// Find help and overlay files with the given name.
|
||||
function findHelpFile(file) {
|
||||
let result = [];
|
||||
for (let [, namespace] in Iterator(namespaces)) {
|
||||
let url = ["chrome://", namespace, "/locale/", file, ".xml"].join("");
|
||||
let res = util.httpGet(url);
|
||||
if (res) {
|
||||
if (res.responseXML.documentElement.localName == "document")
|
||||
fileMap[file] = url;
|
||||
if (res.responseXML.documentElement.localName == "overlay")
|
||||
overlayMap[file] = url;
|
||||
result.push(res.responseXML);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// Find the tags in the document.
|
||||
function addTags(file, doc) {
|
||||
doc = XSLT.transformToDocument(doc);
|
||||
for (let elem in util.evaluateXPath("//xhtml:a/@id", doc))
|
||||
tagMap[elem.value] = file;
|
||||
}
|
||||
|
||||
// Scrape the list of help files from all.xml
|
||||
// Always process main and overlay files, since XSLTProcessor and
|
||||
// XMLHttpRequest don't allow access to chrome documents.
|
||||
tagMap.all = "all";
|
||||
let files = findHelpFile("all").map(function (doc)
|
||||
[f.value for (f in util.evaluateXPath(
|
||||
"//liberator:include/@href", doc))]);
|
||||
const XSLT = XSLTProcessor("chrome://liberator/content/help-single.xsl");
|
||||
|
||||
// Scrape the tags from the rest of the help files.
|
||||
util.Array.flatten(files).forEach(function (file) {
|
||||
findHelpFile(file).forEach(function (doc) {
|
||||
addTags(file, doc);
|
||||
// Scrape the list of help files from all.xml
|
||||
// Always process main and overlay files, since XSLTProcessor and
|
||||
// XMLHttpRequest don't allow access to chrome documents.
|
||||
tagMap.all = "all";
|
||||
let files = findHelpFile("all").map(function (doc)
|
||||
[f.value for (f in util.evaluateXPath(
|
||||
"//liberator:include/@href", doc))]);
|
||||
|
||||
// Scrape the tags from the rest of the help files.
|
||||
util.Array.flatten(files).forEach(function (file) {
|
||||
findHelpFile(file).forEach(function (doc) {
|
||||
addTags(file, doc);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Process plugin help entries.
|
||||
XML.ignoreWhiteSpace = false;
|
||||
XML.prettyPrinting = false;
|
||||
XML.prettyPrinting = true; // Should be false, but ignoreWhiteSpace=false doesn't work correctly. This is the lesser evil.
|
||||
XML.prettyIndent = 4;
|
||||
// Process plugin help entries.
|
||||
XML.ignoreWhiteSpace = false;
|
||||
XML.prettyPrinting = false;
|
||||
XML.prettyPrinting = true; // Should be false, but ignoreWhiteSpace=false doesn't work correctly. This is the lesser evil.
|
||||
XML.prettyIndent = 4;
|
||||
|
||||
let body = XML();
|
||||
for (let [, context] in Iterator(plugins.contexts))
|
||||
if (context.INFO instanceof XML)
|
||||
body += <h2 xmlns={NS.uri} tag={context.INFO.@name + '-plugin'}>{context.INFO.@summary}</h2> +
|
||||
context.INFO;
|
||||
let body = XML();
|
||||
for (let [, context] in Iterator(plugins.contexts))
|
||||
if (context.INFO instanceof XML)
|
||||
body += <h2 xmlns={NS.uri} tag={context.INFO.@name + '-plugin'}>{context.INFO.@summary}</h2> +
|
||||
context.INFO;
|
||||
|
||||
let help = '<?xml version="1.0"?>\n' +
|
||||
'<?xml-stylesheet type="text/xsl" href="chrome://liberator/content/help.xsl"?>\n' +
|
||||
'<!DOCTYPE document SYSTEM "chrome://liberator/content/liberator.dtd">' +
|
||||
<document xmlns={NS}
|
||||
name="plugins" title={config.name + " Plugins"}>
|
||||
<h1 tag="using-plugins">Using Plugins</h1>
|
||||
let help = '<?xml version="1.0"?>\n' +
|
||||
'<?xml-stylesheet type="text/xsl" href="chrome://liberator/content/help.xsl"?>\n' +
|
||||
'<!DOCTYPE document SYSTEM "chrome://liberator/content/liberator.dtd">' +
|
||||
<document xmlns={NS}
|
||||
name="plugins" title={config.name + " Plugins"}>
|
||||
<h1 tag="using-plugins">Using Plugins</h1>
|
||||
|
||||
{body}
|
||||
</document>.toXMLString();
|
||||
fileMap["plugins"] = function () ['text/xml;charset=UTF-8', help];
|
||||
{body}
|
||||
</document>.toXMLString();
|
||||
fileMap["plugins"] = function () ['text/xml;charset=UTF-8', help];
|
||||
|
||||
addTags("plugins", util.httpGet("liberator://help/plugins").responseXML);
|
||||
addTags("plugins", util.httpGet("liberator://help/plugins").responseXML);
|
||||
this.helpInitialized = true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -632,6 +637,7 @@ const Liberator = Module("liberator", {
|
||||
* @returns {string}
|
||||
*/
|
||||
help: function (topic, unchunked) {
|
||||
liberator.initHelp();
|
||||
if (!topic) {
|
||||
let helpFile = unchunked ? "all" : options["helpfile"];
|
||||
if (helpFile in services.get("liberator:").FILE_MAP)
|
||||
@@ -737,22 +743,12 @@ const Liberator = Module("liberator", {
|
||||
*/
|
||||
open: function (urls, params, force) {
|
||||
// convert the string to an array of converted URLs
|
||||
// -> see util.stringToURLArray for more details
|
||||
// -> see liberator.stringToURLArray for more details
|
||||
//
|
||||
// This is strange. And counterintuitive. Is it really
|
||||
// necessary? --Kris
|
||||
if (typeof urls == "string") {
|
||||
// rather switch to the tab instead of opening a new url in case of "12: Tab Title" like "urls"
|
||||
if (liberator.has("tabs")) {
|
||||
let matches = urls.match(/^(\d+):/);
|
||||
if (matches) {
|
||||
tabs.select(parseInt(matches[1], 10) - 1, false); // make it zero-based
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
urls = util.stringToURLArray(urls);
|
||||
}
|
||||
if (typeof urls == "string")
|
||||
urls = liberator.stringToURLArray(urls);
|
||||
|
||||
if (urls.length > 20 && !force) {
|
||||
commandline.input("This will open " + urls.length + " new tabs. Would you like to continue? (yes/[no]) ",
|
||||
@@ -765,7 +761,7 @@ const Liberator = Module("liberator", {
|
||||
|
||||
let flags = 0;
|
||||
params = params || {};
|
||||
if (params instanceof Array)
|
||||
if (isarray(params))
|
||||
params = { where: params };
|
||||
|
||||
for (let [opt, flag] in Iterator({ replace: "REPLACE_HISTORY", hide: "BYPASS_HISTORY" }))
|
||||
@@ -773,15 +769,11 @@ const Liberator = Module("liberator", {
|
||||
flags |= Ci.nsIWebNavigation["LOAD_FLAGS_" + flag];
|
||||
|
||||
let where = params.where || liberator.CURRENT_TAB;
|
||||
let background = ("background" in params) ? params.background : params.where == liberator.NEW_BACKGROUND_TAB;
|
||||
if ("from" in params && liberator.has("tabs")) {
|
||||
if (!('where' in params) && options.get("newtab").has("all", params.from))
|
||||
where = liberator.NEW_BACKGROUND_TAB;
|
||||
if (options.get("activate").has("all", params.from)) {
|
||||
if (where == liberator.NEW_TAB)
|
||||
where = liberator.NEW_BACKGROUND_TAB;
|
||||
else if (where == liberator.NEW_BACKGROUND_TAB)
|
||||
where = liberator.NEW_TAB;
|
||||
}
|
||||
where = liberator.NEW_TAB;
|
||||
background = !options.get("activate").has("all", params.from);
|
||||
}
|
||||
|
||||
if (urls.length == 0)
|
||||
@@ -799,7 +791,6 @@ const Liberator = Module("liberator", {
|
||||
browser.loadURIWithFlags(url, flags, null, null, postdata);
|
||||
break;
|
||||
|
||||
case liberator.NEW_BACKGROUND_TAB:
|
||||
case liberator.NEW_TAB:
|
||||
if (!liberator.has("tabs")) {
|
||||
open(urls, liberator.NEW_WINDOW);
|
||||
@@ -808,7 +799,7 @@ const Liberator = Module("liberator", {
|
||||
|
||||
options.withContext(function () {
|
||||
options.setPref("browser.tabs.loadInBackground", true);
|
||||
browser.loadOneTab(url, null, null, postdata, where == liberator.NEW_BACKGROUND_TAB);
|
||||
browser.loadOneTab(url, null, null, postdata, background);
|
||||
});
|
||||
break;
|
||||
|
||||
@@ -832,7 +823,7 @@ const Liberator = Module("liberator", {
|
||||
|
||||
for (let [, url] in Iterator(urls)) {
|
||||
open(url, where);
|
||||
where = liberator.NEW_BACKGROUND_TAB;
|
||||
background = true;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -865,6 +856,64 @@ const Liberator = Module("liberator", {
|
||||
window.goQuitApplication();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an array of URLs parsed from <b>str</b>.
|
||||
*
|
||||
* Given a string like 'google bla, www.osnews.com' return an array
|
||||
* ['www.google.com/search?q=bla', 'www.osnews.com']
|
||||
*
|
||||
* @param {string} str
|
||||
* @returns {string[]}
|
||||
*/
|
||||
stringToURLArray: function stringToURLArray(str) {
|
||||
let urls;
|
||||
|
||||
if (options["urlseparator"])
|
||||
urls = util.splitLiteral(str, RegExp("\\s*" + options["urlseparator"] + "\\s*"));
|
||||
else
|
||||
urls = [str];
|
||||
|
||||
return urls.map(function (url) {
|
||||
if (/^\.?\//.test(url)) {
|
||||
try {
|
||||
// Try to find a matching file.
|
||||
let file = io.File(url);
|
||||
if (file.exists() && file.isReadable())
|
||||
return services.get("io").newFileURI(file).spec;
|
||||
}
|
||||
catch (e) {}
|
||||
}
|
||||
|
||||
// strip each 'URL' - makes things simpler later on
|
||||
url = url.replace(/^\s+|\s+$/, "");
|
||||
|
||||
// Look for a valid protocol
|
||||
let proto = url.match(/^([-\w]+):/);
|
||||
if (proto && Cc["@mozilla.org/network/protocol;1?name=" + proto[1]])
|
||||
// Handle as URL, but remove spaces. Useful for copied/'p'asted URLs.
|
||||
return url.replace(/\s*\n+\s*/g, "");
|
||||
|
||||
// Ok, not a valid proto. If it looks like URL-ish (foo.com/bar),
|
||||
// let Gecko figure it out.
|
||||
if (/^[a-zA-Z0-9-.]+(?:\/|$)/.test(url) && /[.\/]/.test(url) && !/\s/.test(url) || /^[a-zA-Z0-9-.]+:\d+(?:\/|$)/.test(url))
|
||||
return url;
|
||||
|
||||
// TODO: it would be clearer if the appropriate call to
|
||||
// getSearchURL was made based on whether or not the first word was
|
||||
// indeed an SE alias rather than seeing if getSearchURL can
|
||||
// process the call usefully and trying again if it fails
|
||||
|
||||
// check for a search engine match in the string, then try to
|
||||
// search for the whole string in the default engine
|
||||
let searchURL = bookmarks.getSearchURL(url, false) || bookmarks.getSearchURL(url, true);
|
||||
if (searchURL)
|
||||
return searchURL;
|
||||
|
||||
// Hmm. No defsearch? Let the host app deal with it, then.
|
||||
return url;
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
* Tests a condition and throws a FailedAssertion error on
|
||||
* failure.
|
||||
@@ -1271,17 +1320,8 @@ const Liberator = Module("liberator", {
|
||||
let arg = args[0];
|
||||
|
||||
try {
|
||||
// TODO: why are these sorts of properties arrays? --djk
|
||||
let dialogs = config.dialogs;
|
||||
|
||||
for (let [, dialog] in Iterator(dialogs)) {
|
||||
if (util.compareIgnoreCase(arg, dialog[0]) == 0) {
|
||||
dialog[2]();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
liberator.echoerr("E475: Invalid argument: " + arg);
|
||||
liberator.assert(args[0] in config.dialogs, "E475: Invalid argument: " + arg);
|
||||
config.dialogs[args[0]][1]();
|
||||
}
|
||||
catch (e) {
|
||||
liberator.echoerr("Error opening " + arg.quote() + ": " + e);
|
||||
@@ -1331,19 +1371,89 @@ const Liberator = Module("liberator", {
|
||||
}
|
||||
});
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
if (typeof AddonManager == "undefined") {
|
||||
modules.AddonManager = {
|
||||
getInstallForFile: function (file, callback, mimetype) {
|
||||
callback({
|
||||
install: function () {
|
||||
services.get("extensionManager").installItemFromFile(file, "app-profile");
|
||||
}
|
||||
});
|
||||
},
|
||||
getAddonById: function (id, callback) {
|
||||
let addon = id;
|
||||
if (!isobject(addon))
|
||||
addon = services.get("extensionManager").getItemForID(id);
|
||||
if (!addon)
|
||||
return callback(null);
|
||||
|
||||
function getRdfProperty(item, property) {
|
||||
let resource = services.get("rdf").GetResource("urn:mozilla:item:" + item.id);
|
||||
let value = "";
|
||||
|
||||
if (resource) {
|
||||
let target = services.get("extensionManager").datasource.GetTarget(resource,
|
||||
services.get("rdf").GetResource("http://www.mozilla.org/2004/em-rdf#" + property), true);
|
||||
if (target && target instanceof Ci.nsIRDFLiteral)
|
||||
value = target.Value;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
["aboutURL", "creator", "description", "developers",
|
||||
"homepageURL", "iconURL", "installDate", "name",
|
||||
"optionsURL", "releaseNotesURI", "updateDate"].forEach(function (item) {
|
||||
addon[item] = getRdfProperty(addon, item);
|
||||
});
|
||||
addon.isActive = getRdfProperty(addon, "isDisabled") != "true";
|
||||
|
||||
addon.uninstall = function () {
|
||||
services.get("extensionManager").uninstallItem(this.id);
|
||||
};
|
||||
addon.appDisabled = false;
|
||||
addon.__defineGetter("userDisabled", function() getRdfProperty("userDisabled") == "true");
|
||||
addon.__defineSetter__("userDisabled", function(val) {
|
||||
services.get("extensionManager")[val ? "enableItem" : "disableItem"](this.id);
|
||||
});
|
||||
|
||||
return callback(addon);
|
||||
},
|
||||
getAddonsByTypes: function (types, callback) {
|
||||
let res = [];
|
||||
for (let [,type] in Iterator(types))
|
||||
for (let [,item] in Iterator(services.get("extensionManager")
|
||||
.getItemList(Ci.nsIUpdateItem["TYPE_" + type.toUpperCase()], {})))
|
||||
res.append(this.getAddonById(item));
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function callResult(method) {
|
||||
let args = Array.slice(arguments, 1);
|
||||
return function (result) { result[method].apply(result, args) };
|
||||
}
|
||||
|
||||
commands.add(["exta[dd]"],
|
||||
"Install an extension",
|
||||
function (args) {
|
||||
let file = io.File(args[0]);
|
||||
|
||||
if (file.exists() && file.isReadable() && file.isFile())
|
||||
services.get("extensionManager").installItemFromFile(file, "app-profile");
|
||||
else {
|
||||
if (file.exists() && file.isDirectory())
|
||||
liberator.echomsg("Cannot install a directory: \"" + file.path + "\"", 0);
|
||||
let url = args[0];
|
||||
let file = io.File(url);
|
||||
|
||||
if (!file.exists())
|
||||
AddonManager.getInstallForURL(url, callResult("install"), "application/x-xpinstall");
|
||||
else if (file.isReadable() && file.isFile())
|
||||
AddonManager.getInstallForFile(file, callResult("install"), "application/x-xpinstall");
|
||||
else if (file.isDirectory())
|
||||
liberator.echomsg("Cannot install a directory: \"" + file.path + "\"", 0);
|
||||
else
|
||||
liberator.echoerr("E484: Can't open file " + file.path);
|
||||
}
|
||||
}, {
|
||||
argCount: "1",
|
||||
completer: function (context) {
|
||||
@@ -1357,38 +1467,35 @@ const Liberator = Module("liberator", {
|
||||
{
|
||||
name: "extde[lete]",
|
||||
description: "Uninstall an extension",
|
||||
action: "uninstallItem"
|
||||
action: callResult("uninstall")
|
||||
},
|
||||
{
|
||||
name: "exte[nable]",
|
||||
description: "Enable an extension",
|
||||
action: "enableItem",
|
||||
filter: function ({ item: e }) !e.enabled
|
||||
action: function (addon) addon.userDisabled = false,
|
||||
filter: function ({ item: e }) e.userDisabled
|
||||
},
|
||||
{
|
||||
name: "extd[isable]",
|
||||
description: "Disable an extension",
|
||||
action: "disableItem",
|
||||
filter: function ({ item: e }) e.enabled
|
||||
action: function (addon) addon.userDisabled = true,
|
||||
filter: function ({ item: e }) !e.userDisabled
|
||||
}
|
||||
].forEach(function (command) {
|
||||
commands.add([command.name],
|
||||
command.description,
|
||||
function (args) {
|
||||
let name = args[0];
|
||||
function action(e) { services.get("extensionManager")[command.action](e.id); };
|
||||
|
||||
if (args.bang)
|
||||
liberator.extensions.forEach(function (e) { action(e); });
|
||||
else {
|
||||
liberator.assert(name, "E471: Argument required"); // XXX
|
||||
liberator.assert(!name, "E488: Trailing characters");
|
||||
else
|
||||
liberator.assert(name, "E471: Argument required");
|
||||
|
||||
let extension = liberator.getExtension(name);
|
||||
if (extension)
|
||||
action(extension);
|
||||
else
|
||||
liberator.echoerr("E474: Invalid argument");
|
||||
}
|
||||
AddonManager.getAddonsByTypes(["extension"], function (list) {
|
||||
if (!args.bang)
|
||||
list = list.filter(function (extension) extension.name == name);
|
||||
list.forEach(command.action);
|
||||
});
|
||||
}, {
|
||||
argCount: "?", // FIXME: should be "1"
|
||||
bang: true,
|
||||
@@ -1404,51 +1511,64 @@ const Liberator = Module("liberator", {
|
||||
commands.add(["exto[ptions]", "extp[references]"],
|
||||
"Open an extension's preference dialog",
|
||||
function (args) {
|
||||
let extension = liberator.getExtension(args[0]);
|
||||
liberator.assert(extension && extension.options,
|
||||
"E474: Invalid argument");
|
||||
if (args.bang)
|
||||
window.openDialog(extension.options, "_blank", "chrome");
|
||||
else
|
||||
liberator.open(extension.options, { from: "extoptions" });
|
||||
AddonManager.getAddonsByTypes(["extension"], function (list) {
|
||||
list = list.filter(function (extension) extension.name == args[0]);
|
||||
if (!list.length || !list[0].optionsURL)
|
||||
liberator.echoerr("E474: Invalid argument");
|
||||
else if (args.bang)
|
||||
window.openDialog(list[0].optionsURL, "_blank", "chrome");
|
||||
else
|
||||
liberator.open(list[0].optionsURL, { from: "extoptions" });
|
||||
});
|
||||
}, {
|
||||
argCount: "1",
|
||||
bang: true,
|
||||
completer: function (context) {
|
||||
completion.extension(context);
|
||||
context.filters.push(function ({ item: e }) e.options);
|
||||
context.filters.push(function ({ item: e }) e.isActive && e.optionsURL);
|
||||
},
|
||||
literal: 0
|
||||
});
|
||||
|
||||
// TODO: maybe indicate pending status too?
|
||||
commands.add(["extens[ions]"],
|
||||
commands.add(["extens[ions]", "exts"],
|
||||
"List available extensions",
|
||||
function (args) {
|
||||
let filter = args[0] || "";
|
||||
let extensions = liberator.extensions.filter(function (e) e.name.indexOf(filter) >= 0);
|
||||
AddonManager.getAddonsByTypes(["extension"], function (extensions) {
|
||||
if (args[0])
|
||||
extensions = extensions.filter(function (extension) extension.name.indexOf(args[0]) >= 0);
|
||||
extensions.sort(function (a, b) String.localeCompare(a.name, b.name));
|
||||
|
||||
if (extensions.length > 0) {
|
||||
let list = template.tabular(
|
||||
["Name", "Version", "Status", "Description"], [],
|
||||
([template.icon(e, e.name),
|
||||
e.version,
|
||||
e.enabled ? <span highlight="Enabled">enabled</span>
|
||||
: <span highlight="Disabled">disabled</span>,
|
||||
e.description] for ([, e] in Iterator(extensions)))
|
||||
);
|
||||
if (extensions.length > 0) {
|
||||
let list = template.tabular(
|
||||
["Name", "Version", "Status", "Description"], [],
|
||||
([template.icon({ icon: e.iconURL }, e.name),
|
||||
e.version,
|
||||
(e.isActive ? <span highlight="Enabled">enabled</span>
|
||||
: <span highlight="Disabled">disabled</span>) +
|
||||
((e.userDisabled || e.appDisabled) == !e.isActive ? XML() :
|
||||
<> ({e.userDisabled || e.appDisabled
|
||||
? <span highlight="Disabled">disabled</span>
|
||||
: <span highlight="Enabled">enabled</span>}
|
||||
on restart)
|
||||
</>),
|
||||
e.description] for ([, e] in Iterator(extensions)))
|
||||
);
|
||||
|
||||
commandline.echo(list, commandline.HL_NORMAL, commandline.FORCE_MULTILINE);
|
||||
}
|
||||
else {
|
||||
if (filter)
|
||||
liberator.echoerr("Exxx: No extension matching \"" + filter + "\"");
|
||||
else
|
||||
liberator.echoerr("No extensions installed");
|
||||
}
|
||||
commandline.echo(list, commandline.HL_NORMAL, commandline.FORCE_MULTILINE);
|
||||
}
|
||||
else {
|
||||
if (filter)
|
||||
liberator.echoerr("Exxx: No extension matching \"" + filter + "\"");
|
||||
else
|
||||
liberator.echoerr("No extensions installed");
|
||||
}
|
||||
});
|
||||
},
|
||||
{ argCount: "?" });
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
commands.add(["exu[sage]"],
|
||||
"List all Ex commands with a short description",
|
||||
function (args) { Liberator.showHelpIndex("ex-cmd-index", commands, args.bang); }, {
|
||||
@@ -1704,17 +1824,22 @@ const Liberator = Module("liberator", {
|
||||
completion: function () {
|
||||
completion.dialog = function dialog(context) {
|
||||
context.title = ["Dialog"];
|
||||
context.completions = config.dialogs;
|
||||
context.completions = [[k, v[0]] for ([k, v] in Iterator(config.dialogs))];
|
||||
};
|
||||
|
||||
completion.extension = function extension(context) {
|
||||
context.title = ["Extension"];
|
||||
context.anchored = false;
|
||||
context.keys = { text: "name", description: "description", icon: "icon" },
|
||||
context.completions = liberator.extensions;
|
||||
context.keys = { text: "name", description: "description", icon: "iconURL" },
|
||||
context.incomplete = true;
|
||||
AddonManager.getAddonsByTypes(["extension"], function (addons) {
|
||||
context.incomplete = false;
|
||||
context.completions = addons;
|
||||
});
|
||||
};
|
||||
|
||||
completion.help = function help(context, unchunked) {
|
||||
liberator.initHelp();
|
||||
context.title = ["Help"];
|
||||
context.anchored = false;
|
||||
context.completions = services.get("liberator:").HELP_TAGS;
|
||||
@@ -1729,6 +1854,7 @@ const Liberator = Module("liberator", {
|
||||
context.completions = liberator.menuItems;
|
||||
};
|
||||
|
||||
var toolbox = document.getElementById("navigator-toolbox");
|
||||
completion.toolbar = function toolbar(context) {
|
||||
context.title = ["Toolbar"];
|
||||
context.keys = { text: function (item) item.getAttribute("toolbarname"), description: function () "" };
|
||||
@@ -1817,8 +1943,6 @@ const Liberator = Module("liberator", {
|
||||
if (options["loadplugins"])
|
||||
liberator.loadPlugins();
|
||||
|
||||
liberator.initHelp();
|
||||
|
||||
// after sourcing the initialization files, this function will set
|
||||
// all gui options to their default values, if they have not been
|
||||
// set before by any RC file
|
||||
@@ -1841,6 +1965,7 @@ const Liberator = Module("liberator", {
|
||||
|
||||
statusline.update();
|
||||
liberator.log(config.name + " fully initialized", 0);
|
||||
liberator.initialized = true;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
<textbox class="plain" id="liberator-message" flex="1" readonly="true" liberator:highlight="Normal"/>
|
||||
<hbox id="liberator-commandline" hidden="false" collapsed="true" class="liberator-container" liberator:highlight="Normal">
|
||||
<label class="plain" id="liberator-commandline-prompt" flex="0" crop="end" value="" collapsed="true"/>
|
||||
<textbox class="plain" id="liberator-commandline-command" flex="1" type="timed" timeout="100"
|
||||
<textbox class="plain" id="liberator-commandline-command" flex="1" type="search" timeout="100"
|
||||
oninput="window.liberator ∧ liberator.modules.commandline.onEvent(event);"
|
||||
onkeyup="window.liberator ∧ liberator.modules.commandline.onEvent(event);"
|
||||
onfocus="window.liberator ∧ liberator.modules.commandline.onEvent(event);"
|
||||
@@ -86,12 +86,13 @@
|
||||
|
||||
<statusbar id="status-bar" liberator:highlight="StatusLine">
|
||||
<hbox insertbefore="&liberator.statusBefore;" insertafter="&liberator.statusAfter;"
|
||||
id="liberator-statusline" flex="1" hidden="false" align="center">
|
||||
id="liberator-statusline-field-status" flex="1" hidden="false" align="center">
|
||||
<textbox class="plain" id="liberator-statusline-field-url" readonly="false" flex="1" crop="end"/>
|
||||
<label class="plain" id="liberator-statusline-field-inputbuffer" flex="0"/>
|
||||
<label class="plain" id="liberator-statusline-field-progress" flex="0"/>
|
||||
<label class="plain" id="liberator-statusline-field-tabcount" flex="0"/>
|
||||
<label class="plain" id="liberator-statusline-field-bufferposition" flex="0"/>
|
||||
<label class="plain" id="liberator-statusline-field-zoomlevel" flex="0"/>
|
||||
</hbox>
|
||||
<!-- just hide them since other elements expect them -->
|
||||
<statusbarpanel id="statusbar-display" hidden="true"/>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k at Gmail>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* @scope modules
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* @class ModuleBase
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
@@ -22,7 +25,6 @@
|
||||
* getter - see {@link Option#getter}
|
||||
* completer - see {@link Option#completer}
|
||||
* valdator - see {@link Option#validator}
|
||||
* checkHas - see {@link Option#checkHas}
|
||||
* @optional
|
||||
* @private
|
||||
*/
|
||||
@@ -33,6 +35,17 @@ const Option = Class("Option", {
|
||||
this.type = type;
|
||||
this.description = description;
|
||||
|
||||
if (this.type in Option.getKey)
|
||||
this.getKey = Option.getKey[this.type];
|
||||
|
||||
if (this.type in Option.parseValues)
|
||||
this.parseValues = Option.parseValues[this.type];
|
||||
|
||||
if (this.type in Option.joinValues)
|
||||
this.joinValues = Option.joinValues[this.type];
|
||||
|
||||
this._op = Option.ops[this.type];
|
||||
|
||||
if (arguments.length > 3)
|
||||
this.defaultValue = defaultValue;
|
||||
|
||||
@@ -44,7 +57,7 @@ const Option = Class("Option", {
|
||||
this.names = array([name, "no" + name] for (name in values(names))).flatten().__proto__;
|
||||
|
||||
if (this.globalValue == undefined)
|
||||
this.globalValue = this.defaultValue;
|
||||
this.globalValue = this.parseValues(this.defaultValue);
|
||||
},
|
||||
|
||||
/** @property {value} The option's global value. @see #scope */
|
||||
@@ -58,13 +71,7 @@ const Option = Class("Option", {
|
||||
* @param {value} value The option value.
|
||||
* @returns {value|string[]}
|
||||
*/
|
||||
parseValues: function (value) {
|
||||
if (this.type == "stringlist")
|
||||
return (value === "") ? [] : value.split(",");
|
||||
if (this.type == "charlist")
|
||||
return Array.slice(value);
|
||||
return value;
|
||||
},
|
||||
parseValues: function (value) value,
|
||||
|
||||
/**
|
||||
* Returns <b>values</b> packed in the appropriate format for the option
|
||||
@@ -73,16 +80,10 @@ const Option = Class("Option", {
|
||||
* @param {value|string[]} values The option value.
|
||||
* @returns {value}
|
||||
*/
|
||||
joinValues: function (values) {
|
||||
if (this.type == "stringlist")
|
||||
return values.join(",");
|
||||
if (this.type == "charlist")
|
||||
return values.join("");
|
||||
return values;
|
||||
},
|
||||
joinValues: function (vals) vals,
|
||||
|
||||
/** @property {value|string[]} The option value or array of values. */
|
||||
get values() this.parseValues(this.value),
|
||||
get values() this.getValues(this.scope),
|
||||
set values(values) this.setValues(values, this.scope),
|
||||
|
||||
/**
|
||||
@@ -93,7 +94,26 @@ const Option = Class("Option", {
|
||||
* {@link Option#scope}).
|
||||
* @returns {value|string[]}
|
||||
*/
|
||||
getValues: function (scope) this.parseValues(this.get(scope)),
|
||||
getValues: function (scope) {
|
||||
if (scope) {
|
||||
if ((scope & this.scope) == 0) // option doesn't exist in this scope
|
||||
return null;
|
||||
}
|
||||
else
|
||||
scope = this.scope;
|
||||
|
||||
let values;
|
||||
|
||||
if (liberator.has("tabs") && (scope & Option.SCOPE_LOCAL))
|
||||
values = tabs.options[this.name];
|
||||
if ((scope & Option.SCOPE_GLOBAL) && (values == undefined))
|
||||
values = this.globalValue;
|
||||
|
||||
if (this.getter)
|
||||
return liberator.trapErrors(this.getter, this, values);
|
||||
|
||||
return values;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the option's value from an array of values if the option type is
|
||||
@@ -102,8 +122,22 @@ const Option = Class("Option", {
|
||||
* @param {number} scope The scope to apply these values to (see
|
||||
* {@link Option#scope}).
|
||||
*/
|
||||
setValues: function (values, scope) {
|
||||
this.set(this.joinValues(values), scope || this.scope);
|
||||
setValues: function (newValues, scope, skipGlobal) {
|
||||
scope = scope || this.scope;
|
||||
if ((scope & this.scope) == 0) // option doesn't exist in this scope
|
||||
return;
|
||||
|
||||
if (this.setter)
|
||||
newValues = liberator.trapErrors(this.setter, this, newValues);
|
||||
if (newValues === undefined)
|
||||
return;
|
||||
|
||||
if (liberator.has("tabs") && (scope & Option.SCOPE_LOCAL))
|
||||
tabs.options[this.name] = newValues;
|
||||
if ((scope & Option.SCOPE_GLOBAL) && !skipGlobal)
|
||||
this.globalValue = newValues;
|
||||
|
||||
this.hasChanged = true;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -115,26 +149,7 @@ const Option = Class("Option", {
|
||||
* {@link Option#scope}).
|
||||
* @returns {value}
|
||||
*/
|
||||
get: function (scope) {
|
||||
if (scope) {
|
||||
if ((scope & this.scope) == 0) // option doesn't exist in this scope
|
||||
return null;
|
||||
}
|
||||
else
|
||||
scope = this.scope;
|
||||
|
||||
let value;
|
||||
|
||||
if (liberator.has("tabs") && (scope & Option.SCOPE_LOCAL))
|
||||
value = tabs.options[this.name];
|
||||
if ((scope & Option.SCOPE_GLOBAL) && (value == undefined))
|
||||
value = this.globalValue;
|
||||
|
||||
if (this.getter)
|
||||
return liberator.trapErrors(this.getter, this, value);
|
||||
|
||||
return value;
|
||||
},
|
||||
get: function (scope) this.joinValues(this.getValues(scope)),
|
||||
|
||||
/**
|
||||
* Sets the option value to <b>newValue</b> for the specified <b>scope</b>.
|
||||
@@ -145,21 +160,7 @@ const Option = Class("Option", {
|
||||
* @param {number} scope The scope to apply this value to (see
|
||||
* {@link Option#scope}).
|
||||
*/
|
||||
set: function (newValue, scope) {
|
||||
scope = scope || this.scope;
|
||||
if ((scope & this.scope) == 0) // option doesn't exist in this scope
|
||||
return;
|
||||
|
||||
if (this.setter)
|
||||
newValue = liberator.trapErrors(this.setter, this, newValue);
|
||||
|
||||
if (liberator.has("tabs") && (scope & Option.SCOPE_LOCAL))
|
||||
tabs.options[this.name] = newValue;
|
||||
if ((scope & Option.SCOPE_GLOBAL) && newValue != this.globalValue)
|
||||
this.globalValue = newValue;
|
||||
|
||||
this.hasChanged = true;
|
||||
},
|
||||
set: function (newValue, scope) this.setValues(this.parseValues(newValue), scope),
|
||||
|
||||
/**
|
||||
* @property {value} The option's current value. The option's local value,
|
||||
@@ -169,21 +170,15 @@ const Option = Class("Option", {
|
||||
get value() this.get(),
|
||||
set value(val) this.set(val),
|
||||
|
||||
getKey: function (key) undefined,
|
||||
|
||||
/**
|
||||
* Returns whether the option value contains one or more of the specified
|
||||
* arguments.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
has: function () {
|
||||
let self = this;
|
||||
let test = function (val) values.indexOf(val) >= 0;
|
||||
if (this.checkHas)
|
||||
test = function (val) values.some(function (value) self.checkHas(value, val));
|
||||
let values = this.values;
|
||||
// return whether some argument matches
|
||||
return Array.some(arguments, function (val) test(val));
|
||||
},
|
||||
has: function () Array.some(arguments, function (val) this.values.indexOf(val) >= 0, this),
|
||||
|
||||
/**
|
||||
* Returns whether this option is identified by <b>name</b>.
|
||||
@@ -216,97 +211,16 @@ const Option = Class("Option", {
|
||||
* @param {boolean} invert Whether this is an invert boolean operation.
|
||||
*/
|
||||
op: function (operator, values, scope, invert) {
|
||||
let newValue = null;
|
||||
let self = this;
|
||||
|
||||
switch (this.type) {
|
||||
case "boolean":
|
||||
if (operator != "=")
|
||||
break;
|
||||
let newValues = this._op(operator, values, scope, invert);
|
||||
|
||||
if (invert)
|
||||
newValue = !this.value;
|
||||
else
|
||||
newValue = values;
|
||||
break;
|
||||
|
||||
case "number":
|
||||
// TODO: support floats? Validators need updating.
|
||||
if (!/^[+-]?(?:0x[0-9a-f]+|0[0-7]+|0|[1-9]\d*)$/i.test(values))
|
||||
return "E521: Number required after := " + this.name + "=" + values;
|
||||
|
||||
let value = parseInt(values/* deduce radix */);
|
||||
|
||||
switch (operator) {
|
||||
case "+":
|
||||
newValue = this.value + value;
|
||||
break;
|
||||
case "-":
|
||||
newValue = this.value - value;
|
||||
break;
|
||||
case "^":
|
||||
newValue = this.value * value;
|
||||
break;
|
||||
case "=":
|
||||
newValue = value;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "charlist":
|
||||
case "stringlist":
|
||||
values = Array.concat(values);
|
||||
switch (operator) {
|
||||
case "+":
|
||||
newValue = util.Array.uniq(Array.concat(this.values, values), true);
|
||||
break;
|
||||
case "^":
|
||||
// NOTE: Vim doesn't prepend if there's a match in the current value
|
||||
newValue = util.Array.uniq(Array.concat(values, this.values), true);
|
||||
break;
|
||||
case "-":
|
||||
newValue = this.values.filter(function (item) values.indexOf(item) == -1);
|
||||
break;
|
||||
case "=":
|
||||
newValue = values;
|
||||
if (invert) {
|
||||
let keepValues = this.values.filter(function (item) values.indexOf(item) == -1);
|
||||
let addValues = values.filter(function (item) self.values.indexOf(item) == -1);
|
||||
newValue = addValues.concat(keepValues);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "string":
|
||||
switch (operator) {
|
||||
case "+":
|
||||
newValue = this.value + values;
|
||||
break;
|
||||
case "-":
|
||||
newValue = this.value.replace(values, "");
|
||||
break;
|
||||
case "^":
|
||||
newValue = values + this.value;
|
||||
break;
|
||||
case "=":
|
||||
newValue = values;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return "E685: Internal error: option type `" + this.type + "' not supported";
|
||||
}
|
||||
|
||||
if (newValue == null)
|
||||
if (newValues == null)
|
||||
return "Operator " + operator + " not supported for option type " + this.type;
|
||||
if (!this.isValidValue(newValue))
|
||||
|
||||
if (!this.isValidValue(newValues))
|
||||
return "E474: Invalid argument: " + values;
|
||||
this.setValues(newValue, scope);
|
||||
|
||||
this.setValues(newValues, scope);
|
||||
return null;
|
||||
},
|
||||
|
||||
@@ -319,11 +233,13 @@ const Option = Class("Option", {
|
||||
|
||||
/**
|
||||
* @property {string} The option's data type. One of:
|
||||
* "boolean" - Boolean E.g. true
|
||||
* "number" - Integer E.g. 1
|
||||
* "string" - String E.g. "Vimperator"
|
||||
* "charlist" - Character list E.g. "rb"
|
||||
* "stringlist" - String list E.g. "homepage,quickmark,tabopen,paste"
|
||||
* "boolean" - Boolean, e.g., true
|
||||
* "number" - Integer, e.g., 1
|
||||
* "string" - String, e.g., "Vimperator"
|
||||
* "charlist" - Character list, e.g., "rb"
|
||||
* "regexlist" - Regex list, e.g., "^foo,bar$"
|
||||
* "stringmap" - String map, e.g., "key=v,foo=bar"
|
||||
* "regexmap" - Regex map, e.g., "^key=v,foo$=bar"
|
||||
*/
|
||||
type: null,
|
||||
|
||||
@@ -370,12 +286,6 @@ const Option = Class("Option", {
|
||||
return Option.validateCompleter.apply(this, arguments);
|
||||
return true;
|
||||
},
|
||||
/**
|
||||
* @property The function called to determine whether the option already
|
||||
* contains a specified value.
|
||||
* @see #has
|
||||
*/
|
||||
checkHas: null,
|
||||
|
||||
/**
|
||||
* @property {boolean} Set to true whenever the option is first set. This
|
||||
@@ -410,6 +320,132 @@ const Option = Class("Option", {
|
||||
*/
|
||||
SCOPE_BOTH: 3,
|
||||
|
||||
parseRegex: function (val, result) {
|
||||
let [, bang, val] = /^(!?)(.*)/.exec(val);
|
||||
let re = RegExp(val);
|
||||
re.bang = bang;
|
||||
re.result = arguments.length == 2 ? result : !bang;
|
||||
return re;
|
||||
},
|
||||
unparseRegex: function (re) re.bang + re.source + (typeof re.result == "string" ? "=" + re.result : ""),
|
||||
|
||||
getKey: {
|
||||
stringlist: function (k) this.values.indexOf(k) >= 0,
|
||||
regexlist: function (k) {
|
||||
for (let re in values(this.values))
|
||||
if (re.test(k))
|
||||
return re.result;
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
joinValues: {
|
||||
charlist: function (vals) vals.join(""),
|
||||
stringlist: function (vals) vals.join(","),
|
||||
stringmap: function (vals) [k + "=" + v for ([k, v] in Iterator(vals))].join(","),
|
||||
regexlist: function (vals) vals.map(Option.unparseRegex).join(","),
|
||||
},
|
||||
|
||||
parseValues: {
|
||||
number: function (value) Number(value),
|
||||
boolean: function (value) value == "true" || value == true ? true : false,
|
||||
charlist: function (value) Array.slice(value),
|
||||
stringlist: function (value) (value === "") ? [] : value.split(","),
|
||||
stringmap: function (value) array(v.split("=") for (v in values(value.split(",")))).toObject(),
|
||||
regexlist: function (value) (value === "") ? [] : value.split(",").map(Option.parseRegex),
|
||||
regexmap: function (value) value.split(",").map(function (v) v.split("="))
|
||||
.map(function ([k, v]) v != null ? Option.parseRegex(k, v) : Option.parseRegex('.?', k))
|
||||
},
|
||||
|
||||
ops: {
|
||||
boolean: function (operator, values, scope, invert) {
|
||||
if (operator != "=")
|
||||
return null;
|
||||
if (invert)
|
||||
return !this.value;
|
||||
return values;
|
||||
},
|
||||
|
||||
number: function (operator, values, scope, invert) {
|
||||
// TODO: support floats? Validators need updating.
|
||||
if (!/^[+-]?(?:0x[0-9a-f]+|0[0-7]*|[1-9]+)$/i.test(values))
|
||||
return "E521: Number required after := " + this.name + "=" + values;
|
||||
|
||||
let value = parseInt(values);
|
||||
|
||||
switch (operator) {
|
||||
case "+":
|
||||
return this.value + value;
|
||||
case "-":
|
||||
return this.value - value;
|
||||
case "^":
|
||||
return this.value * value;
|
||||
case "=":
|
||||
return value;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
stringmap: function (operator, values, scope, invert) {
|
||||
values = Array.concat(values);
|
||||
orig = [k + "=" + v for ([k, v] in Iterator(this.values))];
|
||||
|
||||
switch (operator) {
|
||||
case "+":
|
||||
return util.Array.uniq(Array.concat(orig, values), true);
|
||||
case "^":
|
||||
// NOTE: Vim doesn't prepend if there's a match in the current value
|
||||
return util.Array.uniq(Array.concat(values, orig), true);
|
||||
case "-":
|
||||
return orig.filter(function (item) values.indexOf(item) == -1);
|
||||
case "=":
|
||||
if (invert) {
|
||||
let keepValues = orig.filter(function (item) values.indexOf(item) == -1);
|
||||
let addValues = values.filter(function (item) self.values.indexOf(item) == -1);
|
||||
return addValues.concat(keepValues);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
stringlist: function (operator, values, scope, invert) {
|
||||
const self = this;
|
||||
values = Array.concat(values);
|
||||
switch (operator) {
|
||||
case "+":
|
||||
return util.Array.uniq(Array.concat(this.values, values), true);
|
||||
case "^":
|
||||
// NOTE: Vim doesn't prepend if there's a match in the current value
|
||||
return util.Array.uniq(Array.concat(values, this.values), true);
|
||||
case "-":
|
||||
return this.values.filter(function (item) values.indexOf(item) == -1);
|
||||
case "=":
|
||||
if (invert) {
|
||||
let keepValues = this.values.filter(function (item) values.indexOf(item) == -1);
|
||||
let addValues = values.filter(function (item) self.values.indexOf(item) == -1);
|
||||
return addValues.concat(keepValues);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
string: function (operator, values, scope, invert) {
|
||||
switch (operator) {
|
||||
case "+":
|
||||
return this.value + values;
|
||||
case "-":
|
||||
return this.value.replace(values, "");
|
||||
case "^":
|
||||
return values + this.value;
|
||||
case "=":
|
||||
return values;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: Run this by default?
|
||||
/**
|
||||
* Validates the specified <b>values</b> against values generated by the
|
||||
@@ -423,10 +459,21 @@ const Option = Class("Option", {
|
||||
let res = context.fork("", 0, this, this.completer);
|
||||
if (!res)
|
||||
res = context.allItems.items.map(function (item) [item.text]);
|
||||
if (this.type == "regexmap")
|
||||
return Array.concat(values).every(function (re) res.some(function (item) item[0] == re.result));
|
||||
return Array.concat(values).every(function (value) res.some(function (item) item[0] == value));
|
||||
}
|
||||
});
|
||||
|
||||
Option.joinValues["regexmap"] = Option.joinValues["regexlist"];
|
||||
|
||||
Option.getKey["charlist"] = Option.getKey["stringlist"];
|
||||
Option.getKey["regexmap"] = Option.getKey["regexlist"];
|
||||
|
||||
Option.ops["charlist"] = Option.ops["stringlist"];
|
||||
Option.ops["regexlist"] = Option.ops["stringlist"];
|
||||
Option.ops["regexmap"] = Option.ops["stringlist"];
|
||||
|
||||
/**
|
||||
* @instance options
|
||||
*/
|
||||
@@ -462,7 +509,7 @@ const Options = Module("options", {
|
||||
// Trigger any setters.
|
||||
let opt = options.get(option);
|
||||
if (event == "change" && opt)
|
||||
opt.set(opt.value, Option.SCOPE_GLOBAL);
|
||||
opt.setValues(opt.globalValue, Option.SCOPE_GLOBAL, true);
|
||||
}
|
||||
|
||||
storage.newMap("options", { store: false });
|
||||
@@ -1008,8 +1055,6 @@ const Options = Module("options", {
|
||||
}
|
||||
}
|
||||
// write access
|
||||
// NOTE: the behavior is generally Vim compatible but could be
|
||||
// improved. i.e. Vim's behavior is pretty sloppy to no real benefit
|
||||
else {
|
||||
option.setFrom = modifiers.setFrom || null;
|
||||
|
||||
@@ -1017,7 +1062,12 @@ const Options = Module("options", {
|
||||
liberator.assert(!opt.valueGiven, "E474: Invalid argument: " + arg);
|
||||
opt.values = !opt.unsetBoolean;
|
||||
}
|
||||
let res = opt.option.op(opt.operator || "=", opt.values, opt.scope, opt.invert);
|
||||
try {
|
||||
var res = opt.option.op(opt.operator || "=", opt.values, opt.scope, opt.invert);
|
||||
}
|
||||
catch (e) {
|
||||
res = e;
|
||||
}
|
||||
if (res)
|
||||
liberator.echoerr(res);
|
||||
}
|
||||
@@ -1244,8 +1294,15 @@ const Options = Module("options", {
|
||||
if (!completer)
|
||||
return;
|
||||
|
||||
let curValues = curValue != null ? opt.parseValues(curValue) : opt.values;
|
||||
let newValues = opt.parseValues(context.filter);
|
||||
try {
|
||||
var curValues = curValue != null ? opt.parseValues(curValue) : opt.values;
|
||||
var newValues = opt.parseValues(context.filter);
|
||||
}
|
||||
catch (e) {
|
||||
context.message = "Error: " + e;
|
||||
context.completions = [];
|
||||
return;
|
||||
}
|
||||
|
||||
let len = context.filter.length;
|
||||
switch (opt.type) {
|
||||
@@ -1253,9 +1310,18 @@ const Options = Module("options", {
|
||||
if (!completer)
|
||||
completer = function () [["true", ""], ["false", ""]];
|
||||
break;
|
||||
case "regexlist":
|
||||
newValues = context.filter.split(",");
|
||||
// Fallthrough
|
||||
case "stringlist":
|
||||
let target = newValues.pop();
|
||||
len = target ? target.length : 0;
|
||||
let target = newValues.pop() || "";
|
||||
len = target.length;
|
||||
break;
|
||||
case "stringmap":
|
||||
case "regexmap":
|
||||
let vals = context.filter.split(",");
|
||||
target = vals.pop() || "";
|
||||
len = target.length - (target.indexOf("=") + 1);
|
||||
break;
|
||||
case "charlist":
|
||||
len = 0;
|
||||
@@ -1268,9 +1334,10 @@ const Options = Module("options", {
|
||||
let completions = completer(context);
|
||||
if (!completions)
|
||||
return;
|
||||
|
||||
// Not Vim compatible, but is a significant enough improvement
|
||||
// that it's worth breaking compatibility.
|
||||
if (newValues instanceof Array) {
|
||||
if (isarray(newValues)) {
|
||||
completions = completions.filter(function (val) newValues.indexOf(val[0]) == -1);
|
||||
switch (op) {
|
||||
case "+":
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
"use strict";
|
||||
|
||||
// TODO:
|
||||
// - fix Sanitize autocommand
|
||||
@@ -19,6 +20,7 @@ const Sanitizer = Module("sanitizer", {
|
||||
init: function () {
|
||||
const self = this;
|
||||
liberator.loadScript("chrome://browser/content/sanitize.js", Sanitizer);
|
||||
Sanitizer.getClearRange = Sanitizer.Sanitizer.getClearRange;
|
||||
this.__proto__.__proto__ = new Sanitizer.Sanitizer; // Good enough.
|
||||
|
||||
// TODO: remove this version test
|
||||
@@ -213,10 +215,9 @@ const Sanitizer = Module("sanitizer", {
|
||||
{
|
||||
setter: function (values) {
|
||||
for (let [, pref] in Iterator(sanitizer.prefNames)) {
|
||||
continue;
|
||||
options.setPref(pref, false);
|
||||
|
||||
for (let [, value] in Iterator(this.parseValues(values))) {
|
||||
for (let [, value] in Iterator(values)) {
|
||||
if (Sanitizer.prefToArg(pref) == value) {
|
||||
options.setPref(pref, true);
|
||||
break;
|
||||
@@ -226,7 +227,7 @@ const Sanitizer = Module("sanitizer", {
|
||||
|
||||
return values;
|
||||
},
|
||||
getter: function () sanitizer.prefNames.filter(function (pref) options.getPref(pref)).map(Sanitizer.prefToArg).join(","),
|
||||
getter: function () sanitizer.prefNames.filter(function (pref) options.getPref(pref)).map(Sanitizer.prefToArg),
|
||||
completer: function (value) [
|
||||
["cache", "Cache"],
|
||||
["commandline", "Command-line history"],
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
@@ -47,6 +48,10 @@ const Services = Module("services", {
|
||||
this.addClass("file:", "@mozilla.org/network/protocol;1?name=file", Ci.nsIFileProtocolHandler);
|
||||
this.addClass("find", "@mozilla.org/embedcomp/rangefind;1", Ci.nsIFind);
|
||||
this.addClass("process", "@mozilla.org/process/util;1", Ci.nsIProcess);
|
||||
this.addClass("zipWriter", "@mozilla.org/zipwriter;1", Ci.nsIZipWriter);
|
||||
|
||||
if (!this.get("extensionManager"))
|
||||
Components.utils.import("resource://gre/modules/AddonManager.jsm", modules);
|
||||
},
|
||||
|
||||
_create: function (classes, ifaces, meth) {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
@@ -11,12 +14,8 @@ const StatusLine = Module("statusline", {
|
||||
this._statusBar.collapsed = true; // it is later restored unless the user sets laststatus=0
|
||||
|
||||
// our status bar fields
|
||||
this._statuslineWidget = document.getElementById("liberator-statusline");
|
||||
this._urlWidget = document.getElementById("liberator-statusline-field-url");
|
||||
this._inputBufferWidget = document.getElementById("liberator-statusline-field-inputbuffer");
|
||||
this._progressWidget = document.getElementById("liberator-statusline-field-progress");
|
||||
this._tabCountWidget = document.getElementById("liberator-statusline-field-tabcount");
|
||||
this._bufferPositionWidget = document.getElementById("liberator-statusline-field-bufferposition");
|
||||
this.widgets = dict(["status", "url", "inputbuffer", "progress", "tabcount", "bufferposition", "zoomlevel"].map(
|
||||
function (field) [field, document.getElementById("liberator-statusline-field-" + field)]));
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -47,6 +46,7 @@ const StatusLine = Module("statusline", {
|
||||
this.updateProgress();
|
||||
this.updateTabCount();
|
||||
this.updateBufferPosition();
|
||||
this.updateZoomLevel();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -61,20 +61,17 @@ const StatusLine = Module("statusline", {
|
||||
updateUrl: function updateUrl(url) {
|
||||
// ripped from Firefox; modified
|
||||
function losslessDecodeURI(url) {
|
||||
// Countermeasure for "Error: malformed URI sequence".
|
||||
// This error occurs when URL is encoded by not UTF-8 encoding.
|
||||
function _decodeURI(url) {
|
||||
try {
|
||||
return decodeURI(url);
|
||||
}
|
||||
catch (e) {
|
||||
return url;
|
||||
}
|
||||
};
|
||||
|
||||
// 1. decodeURI decodes %25 to %, which creates unintended
|
||||
// encoding sequences.
|
||||
url = url.split("%25").map(_decodeURI).join("%25");
|
||||
url = url.split("%25").map(function (url) {
|
||||
// Non-UTF-8 complient URLs cause "malformed URI sequence" errors.
|
||||
try {
|
||||
return decodeURI(url);
|
||||
}
|
||||
catch (e) {
|
||||
return url;
|
||||
}
|
||||
}).join("%25");
|
||||
// 2. Re-encode whitespace so that it doesn't get eaten away
|
||||
// by the location bar (bug 410726).
|
||||
url = url.replace(/[\r\n\t]/g, encodeURIComponent);
|
||||
@@ -125,7 +122,7 @@ const StatusLine = Module("statusline", {
|
||||
if (modified)
|
||||
url += " [" + modified + "]";
|
||||
|
||||
this._urlWidget.value = url;
|
||||
this.widgets.url.value = url;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -140,7 +137,7 @@ const StatusLine = Module("statusline", {
|
||||
if (!buffer || typeof buffer != "string")
|
||||
buffer = "";
|
||||
|
||||
this._inputBufferWidget.value = buffer;
|
||||
this.widgets.inputbuffer.value = buffer;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -157,7 +154,7 @@ const StatusLine = Module("statusline", {
|
||||
progress = "";
|
||||
|
||||
if (typeof progress == "string")
|
||||
this._progressWidget.value = progress;
|
||||
this.widgets.progress.value = progress;
|
||||
else if (typeof progress == "number") {
|
||||
let progressStr = "";
|
||||
if (progress <= 0)
|
||||
@@ -170,7 +167,7 @@ const StatusLine = Module("statusline", {
|
||||
+ " ".substr(0, 19 - progress)
|
||||
+ "]";
|
||||
}
|
||||
this._progressWidget.value = progressStr;
|
||||
this.widgets.progress.value = progressStr;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -194,7 +191,7 @@ const StatusLine = Module("statusline", {
|
||||
for (let [i, tab] in util.Array.iteritems(config.browser.mTabs))
|
||||
tab.setAttribute("ordinal", i + 1);
|
||||
|
||||
this._tabCountWidget.value = "[" + (tabs.index() + 1) + "/" + tabs.count + "]";
|
||||
this.widgets.tabcount.value = "[" + (tabs.index() + 1) + "/" + tabs.count + "]";
|
||||
}
|
||||
},
|
||||
|
||||
@@ -205,7 +202,7 @@ const StatusLine = Module("statusline", {
|
||||
* @param {number} percent The position, as a percentage. @optional
|
||||
*/
|
||||
updateBufferPosition: function updateBufferPosition(percent) {
|
||||
if (!percent || typeof percent != "number") {
|
||||
if (typeof percent != "number") {
|
||||
let win = document.commandDispatcher.focusedWindow;
|
||||
if (!win)
|
||||
return;
|
||||
@@ -218,14 +215,35 @@ const StatusLine = Module("statusline", {
|
||||
bufferPositionStr = "All";
|
||||
else if (percent == 0)
|
||||
bufferPositionStr = "Top";
|
||||
else if (percent < 10)
|
||||
bufferPositionStr = " " + percent + "%";
|
||||
else if (percent >= 100)
|
||||
bufferPositionStr = "Bot";
|
||||
else if (percent < 10)
|
||||
bufferPositionStr = " " + percent + "%";
|
||||
else
|
||||
bufferPositionStr = percent + "%";
|
||||
|
||||
this._bufferPositionWidget.value = bufferPositionStr;
|
||||
this.widgets.bufferposition.value = bufferPositionStr;
|
||||
},
|
||||
|
||||
/**
|
||||
* Display the main content's zoom level.
|
||||
*
|
||||
* @param {number} percent The zoom level, as a percentage. @optional
|
||||
* @param {boolean} full True if full zoom is in operation. @optional
|
||||
*/
|
||||
updateZoomLevel: function updateZoomLevel(percent, full) {
|
||||
if (arguments.length == 0)
|
||||
[percent, full] = [buffer.zoomLevel, buffer.fullZoom];
|
||||
|
||||
if (percent == 100)
|
||||
this.widgets.zoomlevel.value = "";
|
||||
else {
|
||||
percent = (" " + percent).substr(-3);
|
||||
if (full)
|
||||
this.widgets.zoomlevel.value = " [" + percent + "%]";
|
||||
else
|
||||
this.widgets.zoomlevel.value = " (" + percent + "%)";
|
||||
}
|
||||
}
|
||||
|
||||
}, {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
@@ -167,6 +168,8 @@ Highlights.prototype.CSS = <![CDATA[
|
||||
HelpString display: inline-block; color: green; font-weight: normal; vertical-align: text-top;
|
||||
HelpString::before content: '"';
|
||||
HelpString::after content: '"';
|
||||
HelpString[delim]::before content: attr(delim);
|
||||
HelpString[delim]::after content: attr(delim);
|
||||
|
||||
HelpHead,html|h1,liberator://help/* {
|
||||
display: block;
|
||||
@@ -699,10 +702,10 @@ Module("styles", {
|
||||
JavaScript.setCompleter(["get", "addSheet", "removeSheet", "findSheets"].map(function (m) styles[m]),
|
||||
[ // Prototype: (system, name, filter, css, index)
|
||||
null,
|
||||
function (context, obj, args) args[0] ? styles.systemNames : styles.userNames,
|
||||
function (context, obj, args) styles.completeSite(context, content),
|
||||
function (context, obj, args) args[0] ? this.systemNames : this.userNames,
|
||||
function (context, obj, args) this.completeSite(context, content),
|
||||
null,
|
||||
function (context, obj, args) args[0] ? styles.systemSheets : styles.userSheets
|
||||
function (context, obj, args) args[0] ? this.systemSheets : this.userSheets
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k at Gmail>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
@@ -797,14 +797,7 @@ const Tabs = Module("tabs", {
|
||||
commands.add(["tabopen", "t[open]", "tabnew"],
|
||||
"Open one or more URLs in a new tab",
|
||||
function (args) {
|
||||
let special = args.bang;
|
||||
args = args.string;
|
||||
|
||||
let where = special ? liberator.NEW_TAB : liberator.NEW_BACKGROUND_TAB;
|
||||
if (args)
|
||||
liberator.open(args, { from: "tabopen", where: where });
|
||||
else
|
||||
liberator.open("about:blank", { from: "tabopen", where: where });
|
||||
liberator.open(args.string || "about:blank", { from: "tabopen", where: liberator.NEW_TAB, background: args.bang });
|
||||
}, {
|
||||
bang: true,
|
||||
completer: function (context) completion.url(context),
|
||||
@@ -1094,9 +1087,9 @@ const Tabs = Module("tabs", {
|
||||
"Where to show requested popup windows",
|
||||
"stringlist", "tab",
|
||||
{
|
||||
setter: function (value) {
|
||||
setter: function (values) {
|
||||
let [open, restriction] = [1, 0];
|
||||
for (let [, opt] in Iterator(this.parseValues(value))) {
|
||||
for (let [, opt] in Iterator(values)) {
|
||||
if (opt == "tab")
|
||||
open = 3;
|
||||
else if (opt == "window")
|
||||
@@ -1107,7 +1100,7 @@ const Tabs = Module("tabs", {
|
||||
|
||||
options.safeSetPref("browser.link.open_newwindow", open, "See 'popups' option.");
|
||||
options.safeSetPref("browser.link.open_newwindow.restriction", restriction, "See 'popups' option.");
|
||||
return value;
|
||||
return values;
|
||||
},
|
||||
completer: function (context) [
|
||||
["tab", "Open popups in a new tab"],
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright (c) 2006-2009 by Kris Maglione <maglione.k at Gmail>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k at Gmail>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
@@ -194,8 +194,6 @@ const Template = Module("template", {
|
||||
return <>:{commandline.command}<br/>{xml}</>;
|
||||
},
|
||||
|
||||
// every item must have a .xml property which defines how to draw itself
|
||||
// @param headers is an array of strings, the text for the header columns
|
||||
genericTable: function genericTable(items, format) {
|
||||
completion.listCompleter(function (context) {
|
||||
context.filterFunc = null;
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
// Copyright (c) 2007-2009 by Doug Kearns <dougkearns@gmail.com>
|
||||
// Copyright (c) 2008-2009 by Kris Maglione <maglione.k@gmail.com>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
"use strict";
|
||||
|
||||
/** @scope modules */
|
||||
|
||||
@@ -91,6 +94,55 @@ const Util = Module("util", {
|
||||
return fixup.createFixupURI(str, fixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP);
|
||||
},
|
||||
|
||||
/**
|
||||
* Expands brace globbing patterns in a string.
|
||||
*
|
||||
* Example:
|
||||
* "a{b,c}d" => ["abd", "acd"]
|
||||
*
|
||||
* @param {string} pattern The pattern to deglob.
|
||||
* @returns [string] The resulting strings.
|
||||
*/
|
||||
debrace: function deglobBrace(pattern) {
|
||||
function split(pattern, re, fn, dequote) {
|
||||
let end = 0, match, res = [];
|
||||
while (match = re.exec(pattern)) {
|
||||
end = match.index + match[0].length;
|
||||
res.push(match[1]);
|
||||
if (fn)
|
||||
fn(match);
|
||||
}
|
||||
res.push(pattern.substr(end));
|
||||
return res.map(function (s) util.dequote(s, dequote));
|
||||
}
|
||||
let patterns = [], res = [];
|
||||
let substrings = split(pattern, /((?:[^\\{]|\\.)*)\{((?:[^\\}]|\\.)*)\}/gy,
|
||||
function (match) {
|
||||
patterns.push(split(match[2], /((?:[^\\,]|\\.)*),/gy,
|
||||
null, ",{}"));
|
||||
}, "{}");
|
||||
function rec(acc) {
|
||||
if (acc.length == patterns.length)
|
||||
res.push(util.Array.zip(substrings, acc).join(""));
|
||||
else
|
||||
for (let [, pattern] in Iterator(patterns[acc.length]))
|
||||
rec(acc.concat(pattern));
|
||||
}
|
||||
rec([]);
|
||||
return res;
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes certain backslash-quoted characters while leaving other
|
||||
* backslash-quoting sequences untouched.
|
||||
*
|
||||
* @param {string} pattern The string to unquote.
|
||||
* @param {string} chars The characters to unquote.
|
||||
* @returns {string}
|
||||
*/
|
||||
dequote: function dequote(pattern, chars)
|
||||
pattern.replace(/\\(.)/, function (m0, m1) chars.indexOf(m1) >= 0 ? m1 : m0),
|
||||
|
||||
/**
|
||||
* Converts HTML special characters in <b>str</b> to the equivalent HTML
|
||||
* entities.
|
||||
@@ -158,7 +210,8 @@ const Util = Module("util", {
|
||||
* @returns {string}
|
||||
*/
|
||||
makeXPath: function makeXPath(nodes) {
|
||||
return util.Array(nodes).map(function (node) [node, "xhtml:" + node]).flatten()
|
||||
return util.Array(nodes).map(util.debrace).flatten()
|
||||
.map(function (node) [node, "xhtml:" + node]).flatten()
|
||||
.map(function (node) "//" + node).join(" | ");
|
||||
},
|
||||
|
||||
@@ -257,8 +310,9 @@ const Util = Module("util", {
|
||||
const PATH = FILE.leafName.replace(/\..*/, "") + "/";
|
||||
const TIME = Date.now();
|
||||
|
||||
liberator.initHelp();
|
||||
let zip = services.create("zipWriter");
|
||||
zip.open(FILE, io.MODE_CREATE | io.MODE_WRONLY | io.MODE_TRUNCATE);
|
||||
zip.open(FILE, File.MODE_CREATE | File.MODE_WRONLY | File.MODE_TRUNCATE);
|
||||
function addURIEntry(file, uri)
|
||||
zip.addEntryChannel(PATH + file, TIME, 9,
|
||||
services.get("io").newChannel(uri, null, null), false);
|
||||
@@ -407,11 +461,12 @@ const Util = Module("util", {
|
||||
null
|
||||
);
|
||||
|
||||
result.__iterator__ = asIterator
|
||||
return {
|
||||
__proto__: result,
|
||||
__iterator__: asIterator
|
||||
? function () { let elem; while ((elem = this.iterateNext())) yield elem; }
|
||||
: function () { for (let i = 0; i < this.snapshotLength; i++) yield this.snapshotItem(i); };
|
||||
|
||||
return result;
|
||||
: function () { for (let i = 0; i < this.snapshotLength; i++) yield this.snapshotItem(i); }
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -664,61 +719,16 @@ const Util = Module("util", {
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an array of URLs parsed from <b>str</b>.
|
||||
* Scrolls an element into view if and only if it's not already
|
||||
* fully visible.
|
||||
*
|
||||
* Given a string like 'google bla, www.osnews.com' return an array
|
||||
* ['www.google.com/search?q=bla', 'www.osnews.com']
|
||||
*
|
||||
* @param {string} str
|
||||
* @returns {string[]}
|
||||
* @param {Node} elem The element to make visible.
|
||||
*/
|
||||
stringToURLArray: function stringToURLArray(str) {
|
||||
let urls;
|
||||
|
||||
if (options["urlseparator"])
|
||||
urls = util.splitLiteral(str, RegExp("\\s*" + options["urlseparator"] + "\\s*"));
|
||||
else
|
||||
urls = [str];
|
||||
|
||||
return urls.map(function (url) {
|
||||
if (url.substr(0, 5) != "file:") {
|
||||
try {
|
||||
// Try to find a matching file.
|
||||
let file = io.File(url);
|
||||
if (file.exists() && file.isReadable())
|
||||
return services.get("io").newFileURI(file).spec;
|
||||
}
|
||||
catch (e) {}
|
||||
}
|
||||
|
||||
// strip each 'URL' - makes things simpler later on
|
||||
url = url.replace(/^\s+|\s+$/, "");
|
||||
|
||||
// Look for a valid protocol
|
||||
let proto = url.match(/^([-\w]+):/);
|
||||
if (proto && Cc["@mozilla.org/network/protocol;1?name=" + proto[1]])
|
||||
// Handle as URL, but remove spaces. Useful for copied/'p'asted URLs.
|
||||
return url.replace(/\s*\n+\s*/g, "");
|
||||
|
||||
// Ok, not a valid proto. If it looks like URL-ish (foo.com/bar),
|
||||
// let Gecko figure it out.
|
||||
if (/[.\/]/.test(url) && !/\s/.test(url) || /^[\w-.]+:\d+(?:\/|$)/.test(url))
|
||||
return url;
|
||||
|
||||
// TODO: it would be clearer if the appropriate call to
|
||||
// getSearchURL was made based on whether or not the first word was
|
||||
// indeed an SE alias rather than seeing if getSearchURL can
|
||||
// process the call usefully and trying again if it fails
|
||||
|
||||
// check for a search engine match in the string, then try to
|
||||
// search for the whole string in the default engine
|
||||
let searchURL = bookmarks.getSearchURL(url, false) || bookmarks.getSearchURL(url, true);
|
||||
if (searchURL)
|
||||
return searchURL;
|
||||
|
||||
// Hmm. No defsearch? Let the host app deal with it, then.
|
||||
return url;
|
||||
});
|
||||
scrollIntoView: function scrollIntoView(elem) {
|
||||
let win = elem.ownerDocument.defaultView;
|
||||
let rect = elem.getBoundingClientRect();
|
||||
if (!(rect && rect.top < win.innerHeight && rect.bottom >= 0 && rect.left < win.innerWidth && rect.right >= 0))
|
||||
elem.scrollIntoView();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -740,7 +750,7 @@ const Util = Module("util", {
|
||||
}
|
||||
switch (node.nodeKind()) {
|
||||
case "text":
|
||||
return doc.createTextNode(node);
|
||||
return doc.createTextNode(String(node));
|
||||
case "element":
|
||||
let domnode = doc.createElementNS(node.namespace(), node.localName());
|
||||
for each (let attr in node.@*)
|
||||
@@ -814,7 +824,7 @@ const Util = Module("util", {
|
||||
* @param {Array} ary
|
||||
* @returns {Array}
|
||||
*/
|
||||
flatten: function flatten(ary) Array.concat.apply([], ary),
|
||||
flatten: function flatten(ary) ary.length ? Array.concat.apply([], ary) : [],
|
||||
|
||||
/**
|
||||
* Returns an Iterator for an array's values.
|
||||
@@ -864,6 +874,22 @@ const Util = Module("util", {
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
|
||||
/**
|
||||
* Zips the contents of two arrays. The resulting array is twice the
|
||||
* length of ary1, with any shortcomings of ary2 replaced with null
|
||||
* strings.
|
||||
*
|
||||
* @param {Array} ary1
|
||||
* @param {Array} ary2
|
||||
* @returns {Array}
|
||||
*/
|
||||
zip: function zip(ary1, ary2) {
|
||||
let res = []
|
||||
for(let [i, item] in Iterator(ary1))
|
||||
res.push(item, i in ary2 ? ary2[i] : "");
|
||||
return res;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -417,8 +417,8 @@ preference.
|
||||
|
||||
|
||||
<item>
|
||||
<tags>zI</tags>
|
||||
<spec><oa>count</oa>zI</spec>
|
||||
<tags>ZI zI</tags>
|
||||
<spec><oa>count</oa>ZI</spec>
|
||||
<description>
|
||||
<p>Enlarge full zoom of current web page. Mnemonic: zoom in.</p>
|
||||
</description>
|
||||
@@ -426,8 +426,8 @@ preference.
|
||||
|
||||
|
||||
<item>
|
||||
<tags>zM</tags>
|
||||
<spec><oa>count</oa>zM</spec>
|
||||
<tags>ZM zM</tags>
|
||||
<spec><oa>count</oa>ZM</spec>
|
||||
<description>
|
||||
<p>Enlarge full zoom of current web page by a larger amount. Mnemonic: zoom more.</p>
|
||||
</description>
|
||||
@@ -435,8 +435,8 @@ preference.
|
||||
|
||||
|
||||
<item>
|
||||
<tags>zO</tags>
|
||||
<spec><oa>count</oa>zO</spec>
|
||||
<tags>ZO zO</tags>
|
||||
<spec><oa>count</oa>ZO</spec>
|
||||
<description>
|
||||
<p>Reduce full zoom of current web page. Mnemonic: zoom out.</p>
|
||||
</description>
|
||||
@@ -444,8 +444,8 @@ preference.
|
||||
|
||||
|
||||
<item>
|
||||
<tags>zR</tags>
|
||||
<spec><oa>count</oa>zR</spec>
|
||||
<tags>ZR zR</tags>
|
||||
<spec><oa>count</oa>ZR</spec>
|
||||
<description>
|
||||
<p>Reduce full zoom of current web page by a larger amount. Mnemonic: zoom reduce.</p>
|
||||
</description>
|
||||
@@ -453,8 +453,8 @@ preference.
|
||||
|
||||
|
||||
<item>
|
||||
<tags>zZ</tags>
|
||||
<spec><oa>count</oa>zZ</spec>
|
||||
<tags>ZZ zZ</tags>
|
||||
<spec><oa>count</oa>ZZ</spec>
|
||||
<description>
|
||||
<p>
|
||||
Set full zoom value of current web page. Zoom value can be between 30 and
|
||||
|
||||
@@ -179,7 +179,7 @@
|
||||
</p>
|
||||
|
||||
<code><![CDATA[
|
||||
var INFO =
|
||||
var INFO =
|
||||
<plugin name="flashblock" version="1.0"
|
||||
href="http://ticket.vimperator.org/9"
|
||||
summary="Flash Blocker"
|
||||
|
||||
@@ -151,8 +151,9 @@
|
||||
|
||||
|
||||
<item>
|
||||
<tags>:extens :extensions</tags>
|
||||
<tags>:exts :extens :extensions</tags>
|
||||
<spec>:extens<oa>ions</oa></spec>
|
||||
<spec>:exts</spec>
|
||||
<description>
|
||||
<p>List all installed extensions.</p>
|
||||
</description>
|
||||
|
||||
@@ -393,7 +393,6 @@ This file contains a list of all available commands, mappings and options.
|
||||
<dt><o>exrc</o></dt> <dd>Allow reading of an RC file in the current directory</dd>
|
||||
<dt><o>extendedhinttags</o></dt> <dd>XPath string of hintable elements activated by <k>;</k></dd>
|
||||
<dt><o>fileencoding</o></dt> <dd>Changes the character encoding that &liberator.appname; uses to read and write files</dd>
|
||||
<dt><o>focuscontent</o></dt> <dd>Try to stay in Normal mode after loading a web page</dd>
|
||||
<dt><o>followhints</o></dt> <dd>Change the behaviour of <k name="Return"/> in Hints mode</dd>
|
||||
<dt><o>fullscreen</o></dt> <dd>Show the current window fullscreen</dd>
|
||||
<dt><o>guioptions</o></dt> <dd>Show or hide certain GUI elements like the menu or toolbar</dd>
|
||||
@@ -430,6 +429,7 @@ This file contains a list of all available commands, mappings and options.
|
||||
<dt><o>showstatuslinks</o></dt> <dd>Show the destination of the link under the cursor in the status bar</dd>
|
||||
<dt><o>showtabline</o></dt> <dd>Control when to show the tab bar of opened web pages</dd>
|
||||
<dt><o>smartcase</o></dt> <dd>Override the <o>ignorecase</o> option if the pattern contains uppercase characters</dd>
|
||||
<dt><o>strictfocus</o></dt> <dd>Prevent scripts from focusing input elements without user intervention</dd>
|
||||
<dt><o>suggestengines</o></dt> <dd>Engine Alias which has a feature of suggest</dd>
|
||||
<dt><o>titlestring</o></dt> <dd>Change the title of the window</dd>
|
||||
<dt><o>urlseparator</o></dt> <dd>Set the separator regex used to separate multiple URL args</dd>
|
||||
|
||||
@@ -17,11 +17,27 @@
|
||||
</p>
|
||||
|
||||
<dl>
|
||||
<dt>boolean</dt> <dd>can only be on or off</dd>
|
||||
<dt>number</dt> <dd>has a numeric value</dd>
|
||||
<dt>string</dt> <dd>has a string value</dd>
|
||||
<dt>charlist</dt> <dd>like a string but with unique characters</dd>
|
||||
<dt>stringlist</dt> <dd>a comma-separated list of strings</dd>
|
||||
<dt>boolean</dt> <dd>Can only be on or off</dd>
|
||||
<dt>number</dt> <dd>A numeric value</dd>
|
||||
<dt>string</dt> <dd>A string value</dd>
|
||||
<dt>charlist</dt> <dd>A string containing a discrete set of distinct characters</dd>
|
||||
<dt>stringlist</dt> <dd>A comma-separated list of strings</dd>
|
||||
<dt>stringmap</dt> <dd>A comma-separated list of key-value pairs, e.g., <str>key=val,foo=bar</str></dd>
|
||||
<dt>regexlist</dt>
|
||||
<dd>
|
||||
A comma-separated list of regular expressions. Expressions may be
|
||||
prefixed with a <em>!</em>, in which case the match will be negated. A
|
||||
literal <em>!</em> at the begining of the expression may be matched with
|
||||
<em>[!]</em>. Generally, the first matching regular expression is used.
|
||||
</dd>
|
||||
<dt>regexmap</dt>
|
||||
<dd>
|
||||
A combination of a <em>stringmap</em> and a <em>regexlist</em>. Each key
|
||||
in the <a>key</a>=<a>value</a> pair is a regexp. If the regexp begins with a
|
||||
<em>!</em>, the sense match is negated, such that a non-matching
|
||||
expression will be considered a match and <html:i>vice versa</html:i>.
|
||||
The first <a>key</a> to match yields value.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<h2 tag="set-option E764">Setting options</h2>
|
||||
@@ -293,6 +309,40 @@
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<tags>'au' 'autocomplete'</tags>
|
||||
<spec>'autocomplete' 'au'</spec>
|
||||
<type>regexlist</type>
|
||||
<default>.*</default>
|
||||
<description>
|
||||
<p>
|
||||
A list of regexps defining which completion contexts should be
|
||||
autocompleted. When the value is non-empty, the completion list is
|
||||
automatically opened along with the commandline. Thereafter, any key
|
||||
press triggers a completion update for the matching contexts.
|
||||
Non-matching contexts will only be updated when the tab key is
|
||||
pressed. This option is useful for disabling autocompletion for
|
||||
computationally intense contexts that don't perform well on your
|
||||
system under load.
|
||||
</p>
|
||||
|
||||
<example>
|
||||
To enable autocompletion for everything but <ex>:history</ex> or
|
||||
<ex>:bmarks</ex>, you would choose a value such as,
|
||||
<str delim="'">!/ex/bmarks,.?</str>
|
||||
</example>
|
||||
|
||||
<note>
|
||||
Completion contexts have names very much like Unix path names. This
|
||||
denotes the tree in which they're called. A completer will never be
|
||||
called unless every completer preceding it in the tree was also
|
||||
called. For example, if your completer excludes <str>/ex/</str>, it
|
||||
will also exclude <str>/ex/bmarks</str>, and so on.
|
||||
</note>
|
||||
|
||||
<p>See also <ex>:contexts</ex></p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<tags>$CDPATH</tags>
|
||||
@@ -469,19 +519,14 @@
|
||||
|
||||
|
||||
<item>
|
||||
<tags>'nofc' 'nofocuscontent'</tags>
|
||||
<tags>'fc' 'focuscontent'</tags>
|
||||
<spec>'focuscontent' 'fc'</spec>
|
||||
<tags>'nosf' 'nostrictfocus'</tags>
|
||||
<tags>'sf' 'strictfocus'</tags>
|
||||
<spec>'strictfocus' 'sf'</spec>
|
||||
<type>boolean</type>
|
||||
<default>off</default>
|
||||
<default>on</default>
|
||||
<description>
|
||||
<p>
|
||||
Focus the content after a page has loaded. This is useful if you
|
||||
always want to stay in Normal mode when browsing between web sites.
|
||||
When <str>on</str>, it blurs any textbox which often is
|
||||
automatically focused on page load. If you usually like
|
||||
<o>focuscontent</o> but sometimes you'd like to focus the first
|
||||
input field, you can use <k>gi</k> to jump to it.
|
||||
Prevent scripts from focusing input elements without user intervention.
|
||||
</p>
|
||||
</description>
|
||||
</item>
|
||||
@@ -1249,41 +1294,50 @@
|
||||
</description>
|
||||
</item>
|
||||
|
||||
|
||||
<item>
|
||||
<tags>'wildcase' 'wic'</tags>
|
||||
<tags>'wic' 'wildcase'</tags>
|
||||
<spec>'wildcase' 'wic'</spec>
|
||||
<type>string</type>
|
||||
<type>regexmap</type>
|
||||
<default>smart</default>
|
||||
<description>
|
||||
<p>Defines how completions are matched with regard to character case. Possible values:</p>
|
||||
<p>
|
||||
Defines how completions are matched for a given completion context
|
||||
with regard to character case.
|
||||
</p>
|
||||
|
||||
<p>Possible values:</p>
|
||||
|
||||
<dl>
|
||||
<dt><str>smart</str></dt> <dd>Case is significant when capital letters are typed</dd>
|
||||
<dt><str>match</str></dt> <dd>Case is always significant</dd>
|
||||
<dt><str>ignore</str></dt> <dd>Case is never significant</dd>
|
||||
</dl>
|
||||
|
||||
<p>See also <ex>:contexts</ex></p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
|
||||
<item>
|
||||
<tags>'wildignore' 'wig'</tags>
|
||||
<spec>'wildignore' 'wig'</spec>
|
||||
<type>stringlist</type>
|
||||
<type>regexlist</type>
|
||||
<default></default>
|
||||
<description>
|
||||
<p>
|
||||
List of file patterns to ignore when completing files. E.g., to ignore object
|
||||
files and Vim swap files
|
||||
<ex>:set wildignore=<str>.<em>\\.o,\\..</em>\\.s[a-z]\\<a>2</a></str></ex>
|
||||
</p>
|
||||
|
||||
<code><ex>:set wildignore=<str delim="'">\.o$</str>,<str delim="'">^\..*\.s[a-z]<a>2</a>$</str></ex></code>
|
||||
|
||||
<note>Unlike Vim each pattern is a regex rather than a glob.</note>
|
||||
<note>
|
||||
The only way to include a literal comma in a pattern is with the
|
||||
escape <str>\u0044</str>.
|
||||
</note>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
|
||||
<item>
|
||||
<tags>'wim' 'wildmode'</tags>
|
||||
<spec>'wildmode' 'wim'</spec>
|
||||
@@ -1318,25 +1372,23 @@
|
||||
</description>
|
||||
</item>
|
||||
|
||||
|
||||
<item>
|
||||
<tags>'wop' 'wildoptions'</tags>
|
||||
<spec>'wildoptions' 'wop'</spec>
|
||||
<type>stringlist</type>
|
||||
<default></default>
|
||||
<tags>'wis' 'wildsort'</tags>
|
||||
<spec>'wildsort' 'wis'</spec>
|
||||
<type>regexlist</type>
|
||||
<default>.*</default>
|
||||
<description>
|
||||
<p>A list of words that change how command-line completion is done.</p>
|
||||
<p>
|
||||
A list of regexps defining which completion contexts
|
||||
should be sorted. The main purpose of this option is to
|
||||
prevent sorting of certain completion lists that don't
|
||||
perform well under load.
|
||||
</p>
|
||||
|
||||
<p>Possible words:</p>
|
||||
|
||||
<dl>
|
||||
<dt>auto</dt> <dd>Automatically show completions while you are typing.</dd>
|
||||
<dt>sort</dt> <dd>Always sort the completion list, overriding the <o>complete</o> option.</dd>
|
||||
</dl>
|
||||
<p>See also <ex>:contexts</ex></p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
|
||||
<item>
|
||||
<tags>'wsp' 'wordseparators'</tags>
|
||||
<spec>'wordseparators' 'wsp'</spec>
|
||||
|
||||
@@ -17,16 +17,27 @@
|
||||
<tags>:beep</tags>
|
||||
<spec>:beep</spec>
|
||||
<description>
|
||||
<p>Play a system beep.</p>
|
||||
<p>
|
||||
Play a system beep. This should not be used for any purpose other
|
||||
than testing the visual bell.
|
||||
</p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
|
||||
<item>
|
||||
<tags><![CDATA[<C-l> CTRL-L :redr :redraw]]></tags>
|
||||
<spec>:redr<oa>aw</oa></spec>
|
||||
<tags>:contexts</tags>
|
||||
<spec>:contexts <a>ex-command</a></spec>
|
||||
<description>
|
||||
<p>Redraws the screen. Useful to update the screen halfway executing a script or function.</p>
|
||||
<p>
|
||||
Lists the completion contexts used during the completion of its
|
||||
arguments. These context names are used in options such as
|
||||
<o>autocomplete</o> and <o>wildcase</o>. Note that completion must
|
||||
be triggered in order for this command to be effective, so if
|
||||
autocompletion is not active, you'll need to press the
|
||||
<k name="Tab"/> key at least once. You should also be aware that
|
||||
this command is only useful from the commandline.
|
||||
</p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
@@ -46,6 +57,15 @@
|
||||
</item>
|
||||
|
||||
|
||||
<item>
|
||||
<tags><![CDATA[<C-l> CTRL-L :redr :redraw]]></tags>
|
||||
<spec>:redr<oa>aw</oa></spec>
|
||||
<description>
|
||||
<p>Redraws the screen. Useful to update the screen halfway executing a script or function.</p>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
|
||||
<item>
|
||||
<tags>:run :! :!cmd</tags>
|
||||
<spec>:!<a>cmd</a></spec>
|
||||
|
||||
@@ -18,8 +18,8 @@ getfiles () {
|
||||
find "$@" -not -path '*\.hg*' 2>/dev/null | grep -E "$filter" || true
|
||||
}
|
||||
copytext () {
|
||||
sed -e "s,###VERSION###,$VERSION,g" \
|
||||
-e "s,###DATE###,$BUILD_DATE,g" \
|
||||
sed -e "s,@VERSION@,$VERSION,g" \
|
||||
-e "s,@DATE@,$BUILD_DATE,g" \
|
||||
<"$1" >"$2"
|
||||
cmp -s "$1" "$2" ||
|
||||
( echo "modified: $1"; diff -u "$1" "$2" | grep '^[-+][^-+]' )
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
}}} ***** END LICENSE BLOCK *****/
|
||||
"use strict";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["storage", "Timer"];
|
||||
|
||||
@@ -102,12 +103,12 @@ function readFile(file) {
|
||||
|
||||
function writeFile(file, data) {
|
||||
if (!file.exists())
|
||||
file.create(file.NORMAL_FILE_TYPE, 0600);
|
||||
file.create(file.NORMAL_FILE_TYPE, parseInt('0600', 8));
|
||||
|
||||
let fileStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
|
||||
let stream = Cc["@mozilla.org/intl/converter-output-stream;1"].createInstance(Ci.nsIConverterOutputStream);
|
||||
|
||||
fileStream.init(file, 0x20 | 0x08 | 0x02, 0600, 0); // PR_TRUNCATE | PR_CREATE | PR_WRITE
|
||||
fileStream.init(file, 0x20 | 0x08 | 0x02, parseInt('0600', 8), 0); // PR_TRUNCATE | PR_CREATE | PR_WRITE
|
||||
stream.init(fileStream, "UTF-8", 0, 0);
|
||||
|
||||
stream.writeString(data);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@namespace liberator url("http://vimperator.org/namespaces/liberator");
|
||||
@namespace html url("http://www.w3.org/1999/xhtml");
|
||||
|
||||
/* Applied to all content */
|
||||
[liberator|activeframe] {
|
||||
@@ -122,6 +123,12 @@ statusbarpanel {
|
||||
color: inherit;
|
||||
margin: 0px;
|
||||
}
|
||||
#liberator-commandline-command html|*:focus {
|
||||
outline-width: 0px !important
|
||||
}
|
||||
#liberator-commandline-command .textbox-search-icons {
|
||||
visibility: collapse !important;
|
||||
}
|
||||
#liberator-message {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
2009-XX-XX:
|
||||
* version 2.3a1pre
|
||||
* add basic plugin authorship documentation
|
||||
* plugins may now provide full-fleged ':help' documentation
|
||||
* asciidoc is no longer required to build Vimperator
|
||||
* the help system is newly modularized
|
||||
* remove [c]:edit[c], [c]:tabedit[c], and [c]:winedit[c]
|
||||
* add 'jsdebugger' option - switch on/off javascript debugger service
|
||||
* add "addons", "downloads", "extoptions" and "help" to the 'activate' option.
|
||||
* Replaced 'focuscontent' with 'strictfocus'
|
||||
* Replaced previous incremental search implementation
|
||||
* :open now only opens files begining with / or ./
|
||||
* Page zoom information is now shown in the status bar
|
||||
* Added ZO, ZI, ZM, and ZR as aliases for zO, zI, zM, and zR
|
||||
* Add basic plugin authorship documentation
|
||||
* Plugins may now provide full-fleged ':help' documentation
|
||||
* The help system is newly modularized
|
||||
* Asciidoc is no longer for building
|
||||
* Remove [c]:edit[c], [c]:tabedit[c], and [c]:winedit[c]
|
||||
* Add 'jsdebugger' option - switch on/off javascript debugger service
|
||||
* Add "addons", "downloads", "extoptions" and "help" to the 'activate' option.
|
||||
|
||||
2009-10-28:
|
||||
* version 2.2
|
||||
@@ -27,8 +31,6 @@
|
||||
...................................
|
||||
* IMPORTANT: shifted key notation now matches Vim's behaviour. E.g. <C-a>
|
||||
and <C-A> are equivalent, to map the uppercase character use <C-S-A>.
|
||||
(this might change again, as this is REALLY inconsistent, and i don't
|
||||
know if I like copying bugs)
|
||||
* IMPORTANT: 'popups' now takes a stringlist rather than a number.
|
||||
|
||||
* add [c]:winonly[c]
|
||||
|
||||
@@ -42,7 +42,6 @@ BUGS:
|
||||
|
||||
FEATURES:
|
||||
8 Document Textarea, Caret and Visual modes.
|
||||
8 Incremental searches should retreat to their starting position on <Backspace>
|
||||
8 Replace config.name tests in liberator with more specific feature
|
||||
tests or overridable APIs where at all feasible.
|
||||
8 change the extension ID to vimperator@vimperator.org rather than
|
||||
@@ -65,25 +64,11 @@ FEATURES:
|
||||
8 :redir and 'verbosefile'
|
||||
8 middleclick in content == p, and if command line is open, paste there the clipboard buffer
|
||||
8 all search commands should start searching from the top of the visible viewport
|
||||
8 :addsearch wikpedia http://en.wikipedia.org/wiki/Special:Search?search=%s to allow saving of
|
||||
quick searches in the RC file.
|
||||
Why not just add a bookmark? --Kris
|
||||
This would require performance tests, how fast it is to add 20 keywords that way, as we need
|
||||
to search all existing bookmarks to see if the keyword is already defined, and then just update
|
||||
that bookmark. --mst
|
||||
|
||||
Wah? I don't see how that's especially relevant, since they only
|
||||
need to be added once, but, if you insist:
|
||||
:100time bookmarks.getKeywords().some(function(k) k.keyword == "wikipedia")
|
||||
Code execution summary
|
||||
Executed: 100 times
|
||||
Average time: 2.48 msec
|
||||
Total time: 0.25 sec
|
||||
--Kris
|
||||
|
||||
8 allow for multiple ex commands separated with | (see #24)
|
||||
8 <C-o>/<C-i> should work as in vim (i.e., save page positions as well as
|
||||
locations in the history list).
|
||||
8 jump to the next heading with ]h, next image ]i, previous textbox [t and so on
|
||||
8 pipe selected text/link/website to an external command
|
||||
7 use ctrl-n/p in insert mode for word completion
|
||||
7 implement QuickFix window based on ItemList
|
||||
7 wherever possible: get rid of dialogs and ask console-like dialog questions
|
||||
@@ -93,7 +78,7 @@ FEATURES:
|
||||
opera's fast forward does something like this
|
||||
7 make an option to disable session saving by default when you close Firefox
|
||||
7 The output of the pageinfo-command should contain the security-information of ssl-encrypted sites
|
||||
7 Add :every command
|
||||
7 :grep support (needs location list)
|
||||
6 :mksession
|
||||
6 add [count] support to :b* and :tab* commands where missing
|
||||
6 registers
|
||||
@@ -101,12 +86,10 @@ FEATURES:
|
||||
always be the default register. --Ted
|
||||
6 check/correct spellings in insert mode with some mappings
|
||||
6 add more autocommands (TabClose, TabOpen, TabChanged etc)
|
||||
6 jump to the next heading with ]h, next image ]i, previous textbox [t and so on
|
||||
6 :grep support (needs location list)
|
||||
6 pipe selected text/link/website to an external command
|
||||
6 Use ctrl-w+j/k/w to switch between sidebar, content, preview window
|
||||
6 Command :tags for getting a list of used tags
|
||||
6 ;?<hint> should show more information
|
||||
6 Add information to liberator/HACKING file about testing and optimization
|
||||
5 when looking at a zoomed out image (because it's large), zi should zoom in
|
||||
maybe with this? : http://mxr.mozilla.org/seamonkey/source/content/html/document/public/nsIImageDocument.idl
|
||||
5 make a command to search within google search results
|
||||
@@ -117,8 +100,5 @@ FEATURES:
|
||||
3 add a command-line window (:help cmdline-window in Vim).
|
||||
3 Splitting Windows with [:sp :vsp ctrl-w,s ctrl-w,v] and closing with [ctrl-w,q], moving with [ctrl-w,w or tab]
|
||||
have a look into the split browser extension
|
||||
1 Add information to liberator/HACKING file about testing and optimization
|
||||
1 Document remote branches in liberator/HACKING
|
||||
1 Reformat liberator/HACKING so that git diff can find sections and report changes @ somewhere
|
||||
- many other ideas are listed in the wiki
|
||||
|
||||
|
||||
@@ -1,16 +1,28 @@
|
||||
# Firefox
|
||||
content vimperator content/
|
||||
skin vimperator classic/1.0 skin/
|
||||
locale vimperator en-US locale/en-US/
|
||||
locale liberator en-US ../common/locale/en-US/
|
||||
locale vimperator en-US locale/en-US/
|
||||
locale liberator en-US ../common/locale/en-US/
|
||||
|
||||
content liberator ../common/content/
|
||||
resource liberator ../common/modules/
|
||||
skin liberator classic/1.0 ../common/skin/
|
||||
|
||||
override chrome://liberator/content/liberator.dtd chrome://vimperator/content/liberator.dtd
|
||||
override chrome://liberator/content/config.js chrome://vimperator/content/config.js
|
||||
override chrome://liberator/content/liberator.dtd chrome://vimperator/content/liberator.dtd
|
||||
override chrome://liberator/content/config.js chrome://vimperator/content/config.js
|
||||
|
||||
overlay chrome://browser/content/browser.xul chrome://liberator/content/liberator.xul
|
||||
overlay chrome://browser/content/browser.xul chrome://vimperator/content/vimperator.xul
|
||||
|
||||
component {81495d80-89ee-4c36-a88d-ea7c4e5ac63f} components/about-handler.js
|
||||
contract @mozilla.org/network/protocol/about;1?what=vimperator {81495d80-89ee-4c36-a88d-ea7c4e5ac63f}
|
||||
|
||||
component {16dc34f7-6d22-4aa4-a67f-2921fb5dcb69} components/commandline-handler.js
|
||||
contract @mozilla.org/commandlinehandler/general-startup;1?type=vimperator {16dc34f7-6d22-4aa4-a67f-2921fb5dcb69}
|
||||
category command-line-handler m-vimperator @mozilla.org/commandlinehandler/general-startup;1?type=vimperator
|
||||
|
||||
component {c1b67a07-18f7-4e13-b361-2edcc35a5a0d} components/protocols.js
|
||||
contract @mozilla.org/network/protocol;1?name=chrome-data {c1b67a07-18f7-4e13-b361-2edcc35a5a0d}
|
||||
component {9c8f2530-51c8-4d41-b356-319e0b155c44} components/protocols.js
|
||||
contract @mozilla.org/network/protocol;1?name=liberator {9c8f2530-51c8-4d41-b356-319e0b155c44}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Header:
|
||||
"use strict";
|
||||
const Name = "Vimperator";
|
||||
/*
|
||||
* We can't load our modules here, so the following code is sadly
|
||||
@@ -38,6 +39,9 @@ AboutHandler.prototype = {
|
||||
getURIFlags: function (uri) Ci.nsIAboutModule.ALLOW_SCRIPT,
|
||||
};
|
||||
|
||||
function NSGetModule(compMgr, fileSpec) XPCOMUtils.generateModule([AboutHandler])
|
||||
if (XPCOMUtils.generateNSGetFactory)
|
||||
const NSGetFactory = XPCOMUtils.generateNSGetFactory([AboutHandler]);
|
||||
else
|
||||
const NSGetModule = XPCOMUtils.generateNSGetModule([AboutHandler]);
|
||||
|
||||
// vim: set fdm=marker sw=4 ts=4 et:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Header:
|
||||
"use strict";
|
||||
const Name = "Vimperator";
|
||||
/*
|
||||
* We can't load our modules here, so the following code is sadly
|
||||
@@ -43,6 +44,9 @@ CommandLineHandler.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
function NSGetModule(compMgr, fileSpec) XPCOMUtils.generateModule([CommandLineHandler])
|
||||
if (XPCOMUtils.generateNSGetFactory)
|
||||
const NSGetFactory = XPCOMUtils.generateNSGetFactory([CommandLineHandler]);
|
||||
else
|
||||
const NSGetModule = XPCOMUtils.generateNSGetModule([CommandLineHandler]);
|
||||
|
||||
// vim: set ft=javascript fdm=marker sw=4 ts=4 et:
|
||||
|
||||
@@ -46,58 +46,58 @@ const Config = Module("config", ConfigBase, {
|
||||
["VimperatorLeavePre", "Triggered before exiting Firefox, just before destroying each module"],
|
||||
["VimperatorLeave", "Triggered before exiting Firefox"]],
|
||||
|
||||
dialogs: [
|
||||
["about", "About Firefox",
|
||||
dialogs: {
|
||||
about: ["About Firefox",
|
||||
function () { window.openDialog("chrome://browser/content/aboutDialog.xul", "_blank", "chrome,dialog,modal,centerscreen"); }],
|
||||
["addbookmark", "Add bookmark for the current page",
|
||||
addbookmark: ["Add bookmark for the current page",
|
||||
function () { PlacesCommandHook.bookmarkCurrentPage(true, PlacesUtils.bookmarksRootId); }],
|
||||
["addons", "Manage Add-ons",
|
||||
addons: ["Manage Add-ons",
|
||||
function () { window.BrowserOpenAddonsMgr(); }],
|
||||
["bookmarks", "List your bookmarks",
|
||||
bookmarks: ["List your bookmarks",
|
||||
function () { window.openDialog("chrome://browser/content/bookmarks/bookmarksPanel.xul", "Bookmarks", "dialog,centerscreen,width=600,height=600"); }],
|
||||
["checkupdates", "Check for updates",
|
||||
checkupdates: ["Check for updates",
|
||||
function () { window.checkForUpdates(); }],
|
||||
["cleardata", "Clear private data",
|
||||
cleardata: ["Clear private data",
|
||||
function () { Cc[GLUE_CID].getService(Ci.nsIBrowserGlue).sanitize(window || null); }],
|
||||
["cookies", "List your cookies",
|
||||
cookies: ["List your cookies",
|
||||
function () { window.toOpenWindowByType("Browser:Cookies", "chrome://browser/content/preferences/cookies.xul", "chrome,dialog=no,resizable"); }],
|
||||
["console", "JavaScript console",
|
||||
console: ["JavaScript console",
|
||||
function () { window.toJavaScriptConsole(); }],
|
||||
["customizetoolbar", "Customize the Toolbar",
|
||||
customizetoolbar: ["Customize the Toolbar",
|
||||
function () { window.BrowserCustomizeToolbar(); }],
|
||||
["dominspector", "DOM Inspector",
|
||||
dominspector: ["DOM Inspector",
|
||||
function () { try { window.inspectDOMDocument(content.document); } catch (e) { liberator.echoerr("DOM Inspector extension not installed"); } }],
|
||||
["downloads", "Manage Downloads",
|
||||
downloads: ["Manage Downloads",
|
||||
function () { window.toOpenWindowByType("Download:Manager", "chrome://mozapps/content/downloads/downloads.xul", "chrome,dialog=no,resizable"); }],
|
||||
["history", "List your history",
|
||||
history: ["List your history",
|
||||
function () { window.openDialog("chrome://browser/content/history/history-panel.xul", "History", "dialog,centerscreen,width=600,height=600"); }],
|
||||
["import", "Import Preferences, Bookmarks, History, etc. from other browsers",
|
||||
import: ["Import Preferences, Bookmarks, History, etc. from other browsers",
|
||||
function () { window.BrowserImport(); }],
|
||||
["openfile", "Open the file selector dialog",
|
||||
openfile: ["Open the file selector dialog",
|
||||
function () { window.BrowserOpenFileWindow(); }],
|
||||
["pageinfo", "Show information about the current page",
|
||||
pageinfo: ["Show information about the current page",
|
||||
function () { window.BrowserPageInfo(); }],
|
||||
["pagesource", "View page source",
|
||||
pagesource: ["View page source",
|
||||
function () { window.BrowserViewSourceOfDocument(content.document); }],
|
||||
["places", "Places Organizer: Manage your bookmarks and history",
|
||||
places: ["Places Organizer: Manage your bookmarks and history",
|
||||
function () { PlacesCommandHook.showPlacesOrganizer(ORGANIZER_ROOT_BOOKMARKS); }],
|
||||
["preferences", "Show Firefox preferences dialog",
|
||||
preferences: ["Show Firefox preferences dialog",
|
||||
function () { window.openPreferences(); }],
|
||||
["printpreview", "Preview the page before printing",
|
||||
printpreview: ["Preview the page before printing",
|
||||
function () { PrintUtils.printPreview(onEnterPrintPreview, onExitPrintPreview); }],
|
||||
["printsetup", "Setup the page size and orientation before printing",
|
||||
printsetup: ["Setup the page size and orientation before printing",
|
||||
function () { PrintUtils.showPageSetup(); }],
|
||||
["print", "Show print dialog",
|
||||
print: ["Show print dialog",
|
||||
function () { PrintUtils.print(); }],
|
||||
["saveframe", "Save frame to disk",
|
||||
saveframe: ["Save frame to disk",
|
||||
function () { window.saveFrameDocument(); }],
|
||||
["savepage", "Save page to disk",
|
||||
savepage: ["Save page to disk",
|
||||
function () { window.saveDocument(window.content.document); }],
|
||||
["searchengines", "Manage installed search engines",
|
||||
searchengines: ["Manage installed search engines",
|
||||
function () { window.openDialog("chrome://browser/content/search/engineManager.xul", "_blank", "chrome,dialog,modal,centerscreen"); }],
|
||||
["selectionsource", "View selection source",
|
||||
selectionsource: ["View selection source",
|
||||
function () { buffer.viewSelectionSource(); }]
|
||||
],
|
||||
},
|
||||
|
||||
hasTabbrowser: true,
|
||||
|
||||
@@ -232,13 +232,20 @@ const Config = Module("config", ConfigBase, {
|
||||
return;
|
||||
|
||||
context.anchored = false;
|
||||
context.title = ["Smart Completions"];
|
||||
context.keys.icon = 2;
|
||||
context.incomplete = true;
|
||||
context.hasItems = context.completions.length > 0; // XXX
|
||||
context.filterFunc = null;
|
||||
context.cancel = function () { if (searchRunning) { services.get("autoCompleteSearch").stopSearch(); searchRunning = false; } };
|
||||
context.compare = CompletionContext.Sort.unsorted;
|
||||
context.filterFunc = null;
|
||||
context.hasItems = context.completions.length > 0; // XXX
|
||||
context.incomplete = true;
|
||||
context.keys.icon = 2;
|
||||
context.title = ["Smart Completions"];
|
||||
context.cancel = function () {
|
||||
if (searchRunning) {
|
||||
services.get("autoCompleteSearch").stopSearch();
|
||||
searchRunning = false;
|
||||
}
|
||||
};
|
||||
if (searchRunning)
|
||||
services.get("autoCompleteSearch").stopSearch();
|
||||
let timer = new Timer(50, 100, function (result) {
|
||||
context.incomplete = result.searchResult >= result.RESULT_NOMATCH_ONGOING;
|
||||
context.completions = [
|
||||
@@ -246,9 +253,6 @@ const Config = Module("config", ConfigBase, {
|
||||
for (i in util.range(0, result.matchCount))
|
||||
];
|
||||
});
|
||||
if (searchRunning)
|
||||
services.get("autoCompleteSearch").stopSearch();
|
||||
searchRunning = true;
|
||||
services.get("autoCompleteSearch").startSearch(context.filter, "", context.result, {
|
||||
onSearchResult: function onSearchResult(search, result) {
|
||||
timer.tell(result);
|
||||
@@ -258,6 +262,7 @@ const Config = Module("config", ConfigBase, {
|
||||
}
|
||||
}
|
||||
});
|
||||
searchRunning = true;
|
||||
};
|
||||
|
||||
completion.sidebar = function sidebar(context) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>vimperator@mozdev.org</em:id>
|
||||
<em:name>Vimperator</em:name>
|
||||
<em:version>###VERSION###</em:version>
|
||||
<em:version>@VERSION@</em:version>
|
||||
<em:description>Make Firefox behave like Vim</em:description>
|
||||
<em:creator>Martin Stubenschrott</em:creator>
|
||||
<em:homepageURL>http://vimperator.org</em:homepageURL>
|
||||
@@ -19,7 +19,7 @@
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>3.5</em:minVersion>
|
||||
<em:maxVersion>3.6b3</em:maxVersion>
|
||||
<em:maxVersion>4.0</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
|
||||
Reference in New Issue
Block a user