//
// This work is licensed for reuse under an MIT license. Details are
// given in the LICENSE.txt file included with this file.
-"use strict";
-
-var DEFAULT_FAVICON = "chrome://mozapps/skin/places/defaultFavicon.png";
+/* use strict */
// also includes methods for dealing with keywords and search engines
var Bookmarks = Module("bookmarks", {
get format() ({
anchored: false,
title: ["URL", "Info"],
- keys: { text: "url", description: "title", icon: "icon", extra: "extra", tags: "tags" },
+ keys: { text: "url", description: "title", icon: "icon", extra: "extra", tags: "tags", isURI: function () true },
process: [template.icon, template.bookmarkDescription]
}),
* Otherwise, if a bookmark for the given URL exists it is
* updated instead.
* @optional
- * @returns {boolean} True if the bookmark was added or updated
- * successfully.
+ * @returns {boolean} True if the bookmark was updated, false if a
+ * new bookmark was added.
*/
add: function add(unfiled, title, url, keyword, tags, force) {
// FIXME
if (isObject(unfiled))
- var { unfiled, title, url, keyword, tags, post, charset, force } = unfiled;
-
- try {
- let uri = util.createURI(url);
- if (!force && bookmarkcache.isBookmarked(uri))
- for (var bmark in bookmarkcache)
- if (bmark.url == uri.spec) {
- if (title)
- bmark.title = title;
+ var { id, unfiled, title, url, keyword, tags, post, charset, force } = unfiled;
+
+ let uri = util.createURI(url);
+ if (id != null)
+ var bmark = bookmarkcache.bookmarks[id];
+ else if (!force) {
+ if (keyword && Set.has(bookmarkcache.keywords, keyword))
+ bmark = bookmarkcache.keywords[keyword];
+ else if (bookmarkcache.isBookmarked(uri))
+ for (bmark in bookmarkcache)
+ if (bmark.url == uri.spec)
break;
- }
+ }
- if (tags) {
- PlacesUtils.tagging.untagURI(uri, null);
- PlacesUtils.tagging.tagURI(uri, tags);
- }
- if (bmark == undefined)
- bmark = bookmarkcache.bookmarks[
- services.bookmarks.insertBookmark(
- services.bookmarks[unfiled ? "unfiledBookmarksFolder" : "bookmarksMenuFolder"],
- uri, -1, title || url)];
- if (!bmark)
- return false;
-
- if (charset !== undefined)
- bmark.charset = charset;
- if (post !== undefined)
- bmark.post = post;
- if (keyword)
- bmark.keyword = keyword;
+ if (tags) {
+ PlacesUtils.tagging.untagURI(uri, null);
+ PlacesUtils.tagging.tagURI(uri, tags);
}
- catch (e) {
- util.reportError(e);
- return false;
+
+ let updated = !!bmark;
+ if (bmark == undefined)
+ bmark = bookmarkcache.bookmarks[
+ services.bookmarks.insertBookmark(
+ services.bookmarks[unfiled ? "unfiledBookmarksFolder" : "bookmarksMenuFolder"],
+ uri, -1, title || url)];
+ else {
+ if (title)
+ bmark.title = title;
+ if (!uri.equals(bmark.uri))
+ bmark.uri = uri;
}
- return true;
+ util.assert(bmark);
+
+ if (charset !== undefined)
+ bmark.charset = charset;
+ if (post !== undefined)
+ bmark.post = post;
+ if (keyword)
+ bmark.keyword = keyword;
+
+ return updated;
},
/**
*/
addSearchKeyword: function addSearchKeyword(elem) {
if (elem instanceof HTMLFormElement || elem.form)
- var [url, post, charset] = util.parseForm(elem);
+ var { url, postData, charset } = DOM(elem).formData;
else
- var [url, post, charset] = [elem.href || elem.src, null, elem.ownerDocument.characterSet];
+ var [url, postData, charset] = [elem.href || elem.src, null, elem.ownerDocument.characterSet];
let options = { "-title": "Search " + elem.ownerDocument.title };
- if (post != null)
- options["-post"] = post;
+ if (postData != null)
+ options["-post"] = postData;
if (charset != null && charset !== "UTF-8")
options["-charset"] = charset;
let extra = "";
if (title != url)
extra = " (" + title + ")";
+
this.add({ unfiled: true, title: title, url: url });
dactyl.echomsg({ domains: [util.getHost(url)], message: _("bookmark.added", url + extra) });
}
getSuggestions: function getSuggestions(engineName, query, callback) {
const responseType = "application/x-suggestions+json";
+ if (Set.has(this.suggestionProviders, engineName))
+ return this.suggestionProviders[engineName](query, callback);
+
let engine = Set.has(this.searchEngines, engineName) && this.searchEngines[engineName];
if (engine && engine.supportsResponseType(responseType))
var queryURI = engine.getSubmission(query, responseType).uri.spec;
if (!queryURI)
return (callback || util.identity)([]);
+ function parse(req) JSON.parse(req.responseText)[1].filter(isString);
+ return this.makeSuggestions(queryURI, parse, callback);
+ },
+
+ /**
+ * Given a query URL, response parser, and optionally a callback,
+ * fetch and parse search query results for {@link getSuggestions}.
+ *
+ * @param {string} url The URL to fetch.
+ * @param {function(XMLHttpRequest):[string]} parser The function which
+ * parses the response.
+ */
+ makeSuggestions: function makeSuggestions(url, parser, callback) {
function process(req) {
let results = [];
try {
- results = JSON.parse(req.responseText)[1].filter(isString);
+ results = parser(req);
+ }
+ catch (e) {
+ util.reportError(e);
}
- catch (e) {}
if (callback)
return callback(results);
return results;
}
- let req = util.httpGet(queryURI, callback && process);
+ let req = util.httpGet(url, callback && process);
if (callback)
return req;
return process(req);
},
+ suggestionProviders: {},
+
/**
* Returns an array containing a search URL and POST data for the
* given search string. If *useDefsearch* is true, the string is
}, {
}, {
commands: function () {
- commands.add(["ju[mps]"],
- "Show jumplist",
- function () {
- let sh = history.session;
- commandline.commandOutput(template.jumps(sh.index, sh));
- },
- { argCount: "0" });
-
// TODO: Clean this up.
const tags = {
names: ["-tags", "-T"],
return bookmarks.get(args.join(" "), args["-tags"], null, { keyword: context.filter, title: args["-title"] });
},
type: CommandOption.STRING,
- validator: function (arg) /^\S+$/.test(arg)
+ validator: bind("test", /^\S+$/)
};
commands.add(["bma[rk]"],
"Add a bookmark",
function (args) {
+ dactyl.assert(!args.bang || args["-id"] == null,
+ _("bookmark.bangOrID"));
+
let opts = {
force: args.bang,
unfiled: false,
+ id: args["-id"],
keyword: args["-keyword"] || null,
charset: args["-charset"],
post: args["-post"],
url: args.length === 0 ? buffer.uri.spec : args[0]
};
- if (bookmarks.add(opts)) {
- let extra = (opts.title == opts.url) ? "" : " (" + opts.title + ")";
- dactyl.echomsg({ domains: [util.getHost(opts.url)], message: _("bookmark.added", opts.url + extra) },
- 1, commandline.FORCE_SINGLELINE);
- }
- else
- dactyl.echoerr(_("bookmark.cantAdd", opts.title.quote()));
+ let updated = bookmarks.add(opts);
+ let action = updated ? "updated" : "added";
+
+ let extra = (opts.title == opts.url) ? "" : " (" + opts.title + ")";
+
+ dactyl.echomsg({ domains: [util.getHost(opts.url)], message: _("bookmark." + action, opts.url + extra) },
+ 1, commandline.FORCE_SINGLELINE);
}, {
argCount: "?",
bang: true,
type: CommandOption.STRING,
completer: function (context) completion.charset(context),
validator: Option.validateCompleter
+ },
+ {
+ names: ["-id"],
+ description: "The ID of the bookmark to update",
+ type: CommandOption.INT
}
]
});
if (bmarks.length == 1) {
let bmark = bmarks[0];
+ options["-id"] = bmark.id;
options["-title"] = bmark.title;
if (bmark.charset)
options["-charset"] = bmark.charset;
context.fork("keyword/" + keyword, keyword.length + space.length, null, function (context) {
context.format = history.format;
context.title = [/*L*/keyword + " Quick Search"];
+ context.keys = { text: "url", description: "title", icon: "icon" };
// context.background = true;
context.compare = CompletionContext.Sort.unsorted;
context.generate = function () {
let engineList = (engineAliases || options["suggestengines"].join(",") || "google").split(",");
engineList.forEach(function (name) {
+ var desc = name;
let engine = bookmarks.searchEngines[name];
- if (!engine)
+ if (engine)
+ var desc = engine.description;
+ else if (!Set.has(bookmarks.suggestionProviders, name))
return;
+
let [, word] = /^\s*(\S+)/.exec(context.filter) || [];
if (!kludge && word == name) // FIXME: Check for matching keywords
return;
let ctxt = context.fork(name, 0);
- ctxt.title = [/*L*/engine.description + " Suggestions"];
+ ctxt.title = [/*L*/desc + " Suggestions"];
ctxt.keys = { text: util.identity, description: function () "" };
ctxt.compare = CompletionContext.Sort.unsorted;
ctxt.filterFunc = null;
});
};
- completion.addUrlCompleter("S", "Suggest engines", completion.searchEngineSuggest);
- completion.addUrlCompleter("b", "Bookmarks", completion.bookmark);
- completion.addUrlCompleter("s", "Search engines and keyword URLs", completion.search);
+ completion.addUrlCompleter("suggestion", "Search engine suggestions", completion.searchEngineSuggest);
+ completion.addUrlCompleter("bookmark", "Bookmarks", completion.bookmark);
+ completion.addUrlCompleter("search", "Search engines and keyword URLs", completion.search);
}
});