📄 utils.js
字号:
break; default: } // set generic properties if (id != -1) { this.bookmarks.setItemDateAdded(id, aData.dateAdded); this.bookmarks.setItemLastModified(id, aData.lastModified); if (aData.annos) this.setAnnotationsForItem(id, aData.annos); } return [folderIdMap, searchIds]; }, /** * Replaces imported folder ids with their local counterparts in a place: URI. * * @param aURI * A place: URI with folder ids. * @param aFolderIdMap * An array mapping old folder id to new folder ids. * @returns the fixed up URI if all matched. If some matched, it returns * the URI with only the matching folders included. If none matched it * returns the input URI unchanged. */ _fixupQuery: function PU__fixupQuery(aQueryURI, aFolderIdMap) { var queries = {}; var options = {}; this.history.queryStringToQueries(aQueryURI.spec, queries, {}, options); var fixedQueries = []; queries.value.forEach(function(aQuery) { var folders = aQuery.getFolders({}); var newFolders = []; aFolderIdMap.forEach(function(aMapping) { if (folders.indexOf(aMapping[0]) != -1) newFolders.push(aMapping[1]); }); if (newFolders.length) aQuery.setFolders(newFolders, newFolders.length); fixedQueries.push(aQuery); }); var stringURI = this.history.queriesToQueryString(fixedQueries, fixedQueries.length, options.value); return this._uri(stringURI); }, /** * Serializes the given node (and all it's descendents) as JSON * and writes the serialization to the given output stream. * * @param aNode * An nsINavHistoryResultNode * @param aStream * An nsIOutputStream. NOTE: it only uses the write(str, len) * method of nsIOutputStream. The caller is responsible for * closing the stream. * @param aIsUICommand * Boolean - If true, modifies serialization so that each node self-contained. * For Example, tags are serialized inline with each bookmark. * @param aResolveShortcuts * Converts folder shortcuts into actual folders. * @param aExcludeItems * An array of item ids that should not be written to the backup. */ serializeNodeAsJSONToOutputStream: function PU_serializeNodeAsJSONToOutputStream(aNode, aStream, aIsUICommand, aResolveShortcuts, aExcludeItems) { var self = this; function addGenericProperties(aPlacesNode, aJSNode) { aJSNode.title = aPlacesNode.title; var id = aPlacesNode.itemId; if (id != -1) { aJSNode.id = id; var parent = aPlacesNode.parent; if (parent) aJSNode.parent = parent.itemId; var dateAdded = aPlacesNode.dateAdded; if (dateAdded) aJSNode.dateAdded = dateAdded; var lastModified = aPlacesNode.lastModified; if (lastModified) aJSNode.lastModified = lastModified; // XXX need a hasAnnos api var annos = []; try { annos = self.getAnnotationsForItem(id).filter(function(anno) { // XXX should whitelist this instead, w/ a pref for // backup/restore of non-whitelisted annos // XXX causes JSON encoding errors, so utf-8 encode //anno.value = unescape(encodeURIComponent(anno.value)); if (anno.name == LMANNO_FEEDURI) aJSNode.livemark = 1; if (anno.name == READ_ONLY_ANNO && aResolveShortcuts) { // When copying a read-only node, remove the read-only annotation. return false; } return true; }); } catch(ex) { LOG(ex); } if (annos.length != 0) aJSNode.annos = annos; } // XXXdietrich - store annos for non-bookmark items } function addURIProperties(aPlacesNode, aJSNode) { aJSNode.type = self.TYPE_X_MOZ_PLACE; aJSNode.uri = aPlacesNode.uri; if (aJSNode.id && aJSNode.id != -1) { // harvest bookmark-specific properties var keyword = self.bookmarks.getKeywordForBookmark(aJSNode.id); if (keyword) aJSNode.keyword = keyword; } var tags = aIsUICommand ? aPlacesNode.tags : null; if (tags) aJSNode.tags = tags; // last character-set var uri = self._uri(aPlacesNode.uri); var lastCharset = self.history.getCharsetForURI(uri); if (lastCharset) aJSNode.charset = lastCharset; } function addSeparatorProperties(aPlacesNode, aJSNode) { aJSNode.type = self.TYPE_X_MOZ_PLACE_SEPARATOR; } function addContainerProperties(aPlacesNode, aJSNode) { // saved queries var concreteId = PlacesUtils.getConcreteItemId(aPlacesNode); if (aJSNode.id != -1 && (PlacesUtils.nodeIsQuery(aPlacesNode) || (concreteId != aPlacesNode.itemId && !aResolveShortcuts))) { aJSNode.type = self.TYPE_X_MOZ_PLACE; aJSNode.uri = aPlacesNode.uri; // folder shortcut if (aIsUICommand) aJSNode.concreteId = concreteId; return; } else if (aJSNode.id != -1) { // bookmark folder if (concreteId != aPlacesNode.itemId) aJSNode.type = self.TYPE_X_MOZ_PLACE; aJSNode.type = self.TYPE_X_MOZ_PLACE_CONTAINER; // mark special folders if (aJSNode.id == self.bookmarks.placesRoot) aJSNode.root = "placesRoot"; else if (aJSNode.id == self.bookmarks.bookmarksMenuFolder) aJSNode.root = "bookmarksMenuFolder"; else if (aJSNode.id == self.bookmarks.tagsFolder) aJSNode.root = "tagsFolder"; else if (aJSNode.id == self.bookmarks.unfiledBookmarksFolder) aJSNode.root = "unfiledBookmarksFolder"; else if (aJSNode.id == self.bookmarks.toolbarFolder) aJSNode.root = "toolbarFolder"; } } function writeScalarNode(aStream, aNode) { // serialize to json var jstr = self.toJSONString(aNode); // write to stream aStream.write(jstr, jstr.length); } function writeComplexNode(aStream, aNode, aSourceNode) { var escJSONStringRegExp = /(["\\])/g; // write prefix var properties = []; for (let [name, value] in Iterator(aNode)) { if (name == "annos") value = self.toJSONString(value); else if (typeof value == "string") value = "\"" + value.replace(escJSONStringRegExp, '\\$1') + "\""; properties.push("\"" + name.replace(escJSONStringRegExp, '\\$1') + "\":" + value); } var jStr = "{" + properties.join(",") + ",\"children\":["; aStream.write(jStr, jStr.length); // write child nodes if (!aNode.livemark) { asContainer(aSourceNode); var wasOpen = aSourceNode.containerOpen; if (!wasOpen) aSourceNode.containerOpen = true; var cc = aSourceNode.childCount; for (var i = 0; i < cc; ++i) { var childNode = aSourceNode.getChild(i); if (aExcludeItems && aExcludeItems.indexOf(childNode.itemId) != -1) continue; if (i != 0) aStream.write(",", 1); serializeNodeToJSONStream(aSourceNode.getChild(i), i); } if (!wasOpen) aSourceNode.containerOpen = false; } // write suffix aStream.write("]}", 2); } function serializeNodeToJSONStream(bNode, aIndex) { var node = {}; // set index in order received // XXX handy shortcut, but are there cases where we don't want // to export using the sorting provided by the query? if (aIndex) node.index = aIndex; addGenericProperties(bNode, node); if (self.nodeIsURI(bNode)) addURIProperties(bNode, node); else if (self.nodeIsContainer(bNode)) addContainerProperties(bNode, node); else if (self.nodeIsSeparator(bNode)) addSeparatorProperties(bNode, node); if (!node.feedURI && node.type == self.TYPE_X_MOZ_PLACE_CONTAINER) writeComplexNode(aStream, node, bNode); else writeScalarNode(aStream, node); } // serialize to stream serializeNodeToJSONStream(aNode, null); }, // XXX testing serializers toJSONString: function PU_toJSONString(aObj) { var JSON = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON); return JSON.encode(aObj); }, /** * Serializes bookmarks using JSON, and writes to the supplied file. */ backupBookmarksToFile: function PU_backupBookmarksToFile(aFile, aExcludeItems) { if (aFile.exists() && !aFile.isWritable()) return; // XXX // init stream var stream = Cc["@mozilla.org/network/file-output-stream;1"]. createInstance(Ci.nsIFileOutputStream); stream.init(aFile, 0x02 | 0x08 | 0x20, 0600, 0); // utf-8 converter stream var converter = Cc["@mozilla.org/intl/converter-output-stream;1"]. createInstance(Ci.nsIConverterOutputStream); converter.init(stream, "UTF-8", 0, 0x0000); // weep over stream interface variance var streamProxy = { converter: converter, write: function(aData, aLen) { this.converter.writeString(aData); } }; // query places root var options = this.history.getNewQueryOptions(); options.expandQueries = false; var query = this.history.getNewQuery(); query.setFolders([this.bookmarks.placesRoot], 1); var result = this.history.executeQuery(query, options); result.root.containerOpen = true; // serialize as JSON, write to stream this.serializeNodeAsJSONToOutputStream(result.root, streamProxy, false, false, aExcludeItems); result.root.containerOpen = false; // close converter and stream converter.close(); stream.close(); }, /** * ArchiveBookmarksFile() * * Creates a dated backup once a day in <profile>/bookmarkbackups. * Stores the bookmarks using JSON. * * @param int aNumberOfBackups - the maximum number of backups to keep * * @param bool aForceArchive - forces creating an archive even if one was * already created that day (overwrites) */ archiveBookmarksFile: function PU_archiveBookmarksFile(aNumberOfBackups, aForceArchive) { // get/create backups directory var dirService = Cc["@mozilla.org/file/directory_service;1"]. getService(Ci.nsIProperties); var bookmarksBackupDir = dirService.get("ProfD", Ci.nsILocalFile); bookmarksBackupDir.append("bookmarkbackups"); if (!bookmarksBackupDir.exists()) { bookmarksBackupDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0700); if (!bookmarksBackupDir.exists()) return; // unable to create directory! } // construct the new leafname // Use YYYY-MM-DD (ISO 8601) as it doesn't contain illegal characters // and makes the alphabetical order of multiple backup files more useful. var date = new Date().toLocaleFormat("%Y-%m-%d"); var backupFilename = this.getFormattedString("bookmarksArchiveFilename", [date]); var backupFile = null; if (!aForceArchive) { var backupFileNames = []; var backupFilenamePrefix = backupFilename.substr(0, backupFilename.indexOf("-")); var entries = bookmarksBackupDir.directoryEntries; while (entries.hasMoreElements()) { var entry = entries.getNext().QueryInterface(Ci.nsIFile); var backupName = entry.leafName; if (backupName.substr(0, backupFilenamePrefix.length) == backupFilenamePrefix) { if (backupName == backupFilename) backupFile = entry; backupFileNames.push(backupName); } } var numberOfBackupsToDelete = 0; if (aNumberOfBackups > -1) numberOfBackupsToDelete = backupFileNames.length - aNumberOfBackups; if (numberOfBackupsToDelete > 0) { // If we don't have today's backup, remove one more so that // the total backups after this operation does not exceed the // number specified in the pref. if (!backupFile) numberOfBackupsToDelete++; backupFileNames.sort(); while (numberOfBackupsToDelete--) { let backupFile = bookmarksBackupDir.clone(); backupFile.append(backupFileNames[0]); backupFile.remove(false); backupFileNames.shift(); } } // do nothing if we either have today's backup already // or the user has set the pref to zero. if (backupFile || aNumberOfBackups == 0) return; } backupFile = bookmarksBackupDir.clone(); backupFile.append(backupFilename); if (aForceArchive && backupFile.exists()) backupFile.remove(false); if (!backupFile.exists()) backupFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0600); this.backupBookmarksToFile(backupFile); }, /** * Get the most recent backup file. * @returns nsIFile backup file */ getMostRecentBackup: function PU_getMostRecentBackup() { var dirService = Cc["@mozilla.org/file/directory_service;1"]. getService(Ci.nsIProperties); var bookmarksBackupDir = dirService.get("ProfD", Ci.nsILocalFile); bookmarksBackupDir.append("bookmarkbackups"); if (!bookmarksBackupDir.exists()) return null; var backups = []; var entries = bookmarksBackupDir.directoryEntries; while (entries.hasMoreElements()) { var entry = entries.getNext().QueryInterface(Ci.nsIFile); if (!entry.isHidden() && entry.leafName.match(/^bookmarks-.+(html|json)?$/)) backups.push(entry.leafName); } if (backups.length == 0) return null; backups.sort(); var filename = backups.pop(); var backupFile = bookmarksBackupDir.clone(); backupFile.append(filename); return backupFile; }};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -