diff --git a/content/bookmarks.js b/content/bookmarks.js index fa09915f..76b7e19b 100644 --- a/content/bookmarks.js +++ b/content/bookmarks.js @@ -43,8 +43,10 @@ liberator.Bookmarks = function () //{{{ const faviconService = Components.classes["@mozilla.org/browser/favicon-service;1"] .getService(Components.interfaces.nsIFaviconService); + const Bookmark = new liberator.util.Struct("url", "title", "icon", "keyword", "tags", "id"); + const Keyword = new liberator.util.Struct("keyword", "title", "icon", "url"); + const storage = liberator.storage; - const properties = { url: 0, title: 1, keyword: 2, tags: 3, id: 4, icon: 5 }; function Cache(name, store, serial) { const rootFolders = [bookmarksService.toolbarFolder, bookmarksService.bookmarksMenuFolder, bookmarksService.unfiledBookmarksFolder]; @@ -57,7 +59,7 @@ liberator.Bookmarks = function () //{{{ this.__defineGetter__("bookmarks", function () { this.load(); return bookmarks; }); this.__defineGetter__("keywords", - function () [[k[2], k[1], k[0], k[5]] for each (k in self.bookmarks) if (k[2])]); + function () [new Keyword(k.keyword, k.title, k.icon, k.url) for each (k in self.bookmarks) if (k.keyword)]); this.__iterator__ = function () (val for each (val in self.bookmarks)); @@ -67,7 +69,7 @@ liberator.Bookmarks = function () //{{{ let keyword = bookmarksService.getKeywordForBookmark(node.itemId); let tags = taggingService.getTagsForURI(uri, {}) || []; let icon = faviconService.getFaviconImageForPage(uri).spec; - return bookmarks.push([node.uri, node.title, keyword, tags, node.itemId, icon]); + return bookmarks.push(new Bookmark(node.uri, node.title, icon, keyword, tags, node.itemId)); } function readBookmark(id) @@ -82,7 +84,7 @@ liberator.Bookmarks = function () //{{{ function deleteBookmark(id) { var length = bookmarks.length; - bookmarks = bookmarks.filter(function (item) item[properties.id] != id); + bookmarks = bookmarks.filter(function (item) item.id != id); return bookmarks.length < length; } @@ -160,13 +162,13 @@ liberator.Bookmarks = function () //{{{ if (isAnnotation) return; // liberator.dump("onItemChanged(" + itemId + ", " + property + ", " + value + ")\n"); - var bookmark = bookmarks.filter(function (item) item[properties.id] == itemId)[0]; + var bookmark = bookmarks.filter(function (item) item.id == itemId)[0]; if (bookmark) { if (property == "tags") - value = taggingService.getTagsForURI(ioService.newURI(bookmark[properties.url], null, null), {}); - if (property in properties) - bookmark[properties[property]] = value; + value = taggingService.getTagsForURI(ioService.newURI(bookmark.url, null, null), {}); + if (property in bookmark) + bookmark[property] = value; storage.fireEvent(name, "change", itemId); } }, @@ -184,12 +186,7 @@ liberator.Bookmarks = function () //{{{ let bookmarkObserver = function (key, event, arg) { if (event == "add") - { - let args = {}; - for (let [k, v] in Iterator(properties)) - args[k] = arg[v]; - liberator.autocommands.trigger("BookmarkAdd", args); - } + liberator.autocommands.trigger("BookmarkAdd", arg); liberator.statusline.updateUrl(); } @@ -512,14 +509,15 @@ liberator.Bookmarks = function () //{{{ } if (openItems) - return liberator.open([i[0] for each (i in items)], liberator.NEW_TAB); + return liberator.open([i.url for each (i in items)], liberator.NEW_TAB); let list = liberator.template.bookmarks("title", ( { - url: item[0], - title: item[1], - extra: [['keyword', item[2], "hl-Keyword"], - ['tags', item[3].join(', '), "hl-Tag"]].filter(function (i) i[1]) + url: item.url, + title: item.title, + extra: [['keyword', item.keyword, "hl-Keyword"], + ['tags', item.tags.join(', '), "hl-Tag"] + ].filter(function (i) i[1]) } for each (item in items))); liberator.commandline.echo(list, liberator.commandline.HL_NORMAL, liberator.commandline.FORCE_MULTILINE); }, @@ -566,7 +564,7 @@ liberator.History = function () //{{{ var node = rootNode.getChild(i); // liberator.dump("History child " + node.itemId + ": " + node.title + " - " + node.type); if (node.type == node.RESULT_TYPE_URI) // just make sure it's a bookmark - history.push([node.uri, node.title || "[No title]", getIcon(node.uri)]); + history.push([node.url, node.title || "[No title]", getIcon(node.uri)]); } // close a container after using it! diff --git a/content/buffer.js b/content/buffer.js index cc7a2897..2614d25b 100644 --- a/content/buffer.js +++ b/content/buffer.js @@ -69,7 +69,7 @@ liberator.Buffer = function () //{{{ const XHTML = "http://www.w3.org/1999/xhtml"; const namespace = "@namespace html url(" + XHTML + ");\n" + "@namespace xul url(http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul);\n"; - const NAME = 0, SITES = 1, CSS = 2, REF = 3; + const Sheet = new util.Struct("name", "sites", "css", "ref"); let cssUri = function (css) "data:text/css," + encodeURI(css); @@ -91,13 +91,13 @@ liberator.Buffer = function () //{{{ if (name && name in names) this.removeSheet(name, null, null, null, system); - let sheet = sheets.filter(function (s) s[SITES].join(",") == filter && s[CSS] == css)[0]; + let sheet = sheets.filter(function (s) s.sites.join(",") == filter && s.css == css)[0]; if (!sheet) - sheet = [name, filter.split(","), css, null]; + sheet = new Sheet(name, filter.split(","), css, null); - if (sheet[REF] == null) // Not registered yet + if (sheet.ref == null) // Not registered yet { - sheet[REF] = []; + sheet.ref = []; try { this.registerSheet(cssUri(wrapCSS(sheet)), !force); @@ -110,7 +110,7 @@ liberator.Buffer = function () //{{{ } if (name) { - sheet[REF].push(name); + sheet.ref.push(name); names[name] = sheet; } return null; @@ -128,9 +128,9 @@ liberator.Buffer = function () //{{{ if (name) matches = matches.filter(function (i) sheets[i] == names[name]); if (css) - matches = matches.filter(function (i) sheets[i][CSS] == css); + matches = matches.filter(function (i) sheets[i].css == css); if (filter) - matches = matches.filter(function (i) sheets[i][SITES].indexOf(filter) >= 0); + matches = matches.filter(function (i) sheets[i].sites.indexOf(filter) >= 0); return matches; }, @@ -156,10 +156,10 @@ liberator.Buffer = function () //{{{ let sheet = sheets[i]; if (name) { - sheet[REF].splice(sheet[REF].indexOf(name)); + sheet.ref.splice(sheet.ref.indexOf(name)); delete names[name]; } - if (!sheet[REF].length) + if (!sheet.ref.length) { sheets.splice(i); this.unregisterSheet(cssUri(wrapCSS(sheet))); @@ -167,7 +167,7 @@ liberator.Buffer = function () //{{{ // Filter out the given site, and re-add if there are any left if (filter) { - let sites = sheet[SITES].filter(function (f) f != filter); + let sites = sheet.sites.filter(function (f) f != filter); if (sites.length) this.addSheet(name, sites.join(","), css, system, true); } @@ -195,8 +195,8 @@ liberator.Buffer = function () //{{{ function wrapCSS(sheet) { - let filter = sheet[SITES]; - let css = sheet[CSS]; + let filter = sheet.sites; + let css = sheet.css; if (filter[0] == "*") return namespace + css; let selectors = filter.map(function (part) (/[*]$/.test(part) ? "url-prefix" : @@ -266,7 +266,7 @@ liberator.Buffer = function () //{{{ } } Styles.prototype = { - get sites() util.uniq(util.flatten([v[1] for ([k, v] in this.userSheets)])), + get sites() util.uniq(util.flatten([v.sites for ([k, v] in this.userSheets)])), }; let styles = liberator.storage.newObject("styles", Styles, false); @@ -829,7 +829,7 @@ liberator.Buffer = function () //{{{ if (!css) { let list = Array.concat([i for (i in styles.userNames)], - [i for (i in styles.userSheets) if (!i[1][3].length)]); + [i for (i in styles.userSheets) if (!i[1].ref.length)]); let str = liberator.template.tabular(["", "Filter", "CSS"], ["padding: 0 1em 0 1ex; vertical-align: top", "padding: 0 1em 0 0; vertical-align: top"], ([k, v[1].join(","), v[2]] @@ -871,8 +871,10 @@ liberator.Buffer = function () //{{{ { argCount: 1, completer: function (filter) [0, liberator.completion.filter( - [[i, <>{s[1].join(",")}: {s[2].replace("\n", "\\n")}] for ([i, s] in styles.userSheets)] - .concat([[s, ""] for each (s in styles.sites)]) + [[i, <>{s.sites.join(",")}: {s.css.replace("\n", "\\n")}] + for ([i, s] in styles.userSheets) + ] + .concat([[s, ""] for each (s in styles.sites)]) , filter)], literal: true, options: [[["-index", "-i"], liberator.commands.OPTION_INT], diff --git a/content/commands.js b/content/commands.js index 7c7c2159..31a05a4c 100644 --- a/content/commands.js +++ b/content/commands.js @@ -186,6 +186,26 @@ liberator.Commands = function () //{{{ return matches; } + function parseBool(arg) + { + if (arg == "true" || arg == "1" || arg == "on") + return true; + if (arg == "false" || arg == "0" || arg == "off") + return false; + return NaN; + } + + const ArgType = new liberator.util.Struct("description", "parse"); + const argTypes = [ + null, + ["no arg", function (arg) !arg], + ["boolean", parseBool], + ["string", function (val) val], + ["int", parseInt], + ["float", parseFloat], + ["list", function (arg) arg && arg.split(/\s*,\s*/)] + ].map(function (x) x && ArgType.apply(null, x));; + /////////////////////////////////////////////////////////////////////////////}}} ////////////////////// PUBLIC SECTION ////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ @@ -528,57 +548,15 @@ liberator.Commands = function () //{{{ if (!invalid) { - switch (opt[1]) // type + let type = argTypes[opt[1]]; + if (type) { - case this.OPTION_NOARG: - if (arg != null) + arg = type.parse(arg); + if (arg == null || arg == NaN) { - liberator.echoerr("No argument allowed for option: " + optname); + liberator.echoerr("Invalid argument for " + type.description + "option: " + optname); return null; } - break; - case this.OPTION_BOOL: - if (arg == "true" || arg == "1" || arg == "on") - arg = true; - else if (arg == "false" || arg == "0" || arg == "off") - arg = false; - else - { - liberator.echoerr("Invalid argument for boolean option: " + optname); - return null; - } - break; - case this.OPTION_STRING: - if (arg == null) - { - liberator.echoerr("Argument required for string option: " + optname); - return null; - } - break; - case this.OPTION_INT: - arg = parseInt(arg, 10); - if (isNaN(arg)) - { - liberator.echoerr("Numeric argument required for integer option: " + optname); - return null; - } - break; - case this.OPTION_FLOAT: - arg = parseFloat(arg); - if (isNaN(arg)) - { - liberator.echoerr("Numeric argument required for float option: " + optname); - return null; - } - break; - case this.OPTION_LIST: - if (arg == null) - { - liberator.echoerr("Argument required for list option: " + optname); - return null; - } - arg = arg.split(/\s*,\s*/); - break; } // we have a validator function diff --git a/content/completion.js b/content/completion.js index d7e1c762..2d03a631 100644 --- a/content/completion.js +++ b/content/completion.js @@ -476,6 +476,11 @@ liberator.Completion = function () //{{{ }; let javascript = new Javascript(); + function filterFavicon(array, want) + { + return want ? array : [a[2] ? a.slice(0, 2) : a for ([i, a] in Iterator(array))]; + } + function buildSubstrings(str, filter) { if (filter == "") @@ -618,7 +623,7 @@ liberator.Completion = function () //{{{ cacheResults[key] = generate(filter); cacheFilter[key] = filter; if (cacheResults[key].length) - return cacheResults[key] = this[method].apply(this, [cacheResults[key], filter].concat(Array.splice(arguments, 4))); + return cacheResults[key] = this[method].apply(this, [cacheResults[key], filter].concat(Array.slice(arguments, 4))); return []; }, @@ -659,7 +664,7 @@ liberator.Completion = function () //{{{ { var url = elem[0] || ""; var title = elem[1] || ""; - var tags = elem[3] || []; + var tags = elem.tags || elem[3] || []; if (ignorecase) { url = url.toLowerCase(); @@ -923,7 +928,7 @@ liberator.Completion = function () //{{{ search: function search(filter) { let [, keyword, args] = filter.match(/^\s*(\S*)\s*(.*)/); - let keywords = liberator.bookmarks.getKeywords().map(function (k) [k[0], k[1], k[3], k[2]]); + let keywords = liberator.bookmarks.getKeywords(); let engines = this.filter(keywords.concat(liberator.bookmarks.getSearchEngines()), filter, false, true); let generate = function () { @@ -1064,7 +1069,7 @@ liberator.Completion = function () //{{{ else if (c == "S") completions.push(this.searchEngineSuggest(filter, suggestEngineAlias)[1]); else if (c == "b") - completions.push(liberator.bookmarks.get(filter).map(function (a) [a[0], a[1], a[5]])); + completions.push(liberator.bookmarks.get(filter)); else if (c == "h") completions.push(liberator.history.get(filter)); else if (c == "l" && completionService) // add completions like Firefox's smart location bar diff --git a/content/util.js b/content/util.js index f6393f21..86433d53 100644 --- a/content/util.js +++ b/content/util.js @@ -422,4 +422,43 @@ liberator.util = { //{{{ }, }; //}}} +liberator.util.Struct = function Struct() +{ + let self = this instanceof Struct ? this : new Struct(); + if (!arguments.length) + return self; + + let args = Array.slice(arguments); + self.__defineGetter__("length", function () args.length); + self.__defineGetter__("members", function () args.slice()); + for (let arg in Iterator(args)) + { + let [i, name] = arg; + self.__defineGetter__(name, function () this[i]); + self.__defineSetter__(name, function (val) { this[i] = val; }); + } + function ConStructor() + { + let self = this instanceof arguments.callee ? this : new arguments.callee(); + for (let [k, v] in Iterator(Array.slice(arguments))) + self[k] = v; + return self; + } + ConStructor.prototype = self; + return self.constructor = ConStructor; +} +liberator.util.Struct.prototype = { + clone: function () + { + return this.constructor.apply(null, this.slice()); + }, + // Iterator over our named members + __iterator__: function () ([v, this[v]] for ([k, v] in this.members)) +} +// Add no-sideeffect array methods. Can't set new Array() as the prototype or +// get length() won't work. +for (let [,k] in Iterator(["concat", "every", "filter", "forEach", "indexOf", "join", "lastIndexOf", + "map", "reduce", "reduceRight", "reverse", "slice", "some", "sort"])) + liberator.util.Struct.prototype[k] = Array.prototype[k]; + // vim: set fdm=marker sw=4 ts=4 et: