//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Microsummarizer. * * The Initial Developer of the Original Code is Mozilla. * Portions created by the Initial Developer are Copyright (C) 2006 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Myk Melez * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // XXX Synchronize these values with the VARIANT constants in Places code? const ADD_BOOKMARK_MODE = 0; const EDIT_BOOKMARK_MODE = 1; const Cc = Components.classes; const Ci = Components.interfaces; var MicrosummaryPicker = { // The microsummary service. __mss: null, get _mss() { if (!this.__mss) this.__mss = Cc["@mozilla.org/microsummary/service;1"]. getService(Ci.nsIMicrosummaryService); return this.__mss; }, // The IO service. __ios: null, get _ios() { if (!this.__ios) this.__ios = Cc["@mozilla.org/network/io-service;1"]. getService(Ci.nsIIOService); return this.__ios; }, // The URI of the page being bookmarked. get _pageURI() { if (this._mode == ADD_BOOKMARK_MODE) return this._ios.newURI(gArg.url, null, null); else if (this._mode == EDIT_BOOKMARK_MODE) { var uriResource = BMDS.GetTarget(gResource, gProperties[1], true); if (!uriResource) return null; // The URI spec will be the empty string if the user opened the bookmark // properties dialog via the "New Bookmark" command. var uriSpec = uriResource.QueryInterface(Ci.nsIRDFLiteral).Value; if (!uriSpec) return null; // The IO Service will throw an exception if it can't convert the spec // into an nsIURI object. var uri; try { uri = this._ios.newURI(uriSpec, null, null); } catch(e) { return null; } return uri; } return null; }, // The unique identifier for the bookmark. If we're adding a bookmark, // this may not exist yet. get _bookmarkID() { if (this._mode == ADD_BOOKMARK_MODE && typeof gResource == "undefined") return null; return gResource; }, _microsummaries: null, get _mode() { if ("gArg" in window) return ADD_BOOKMARK_MODE; else if ("gProperties" in window) return EDIT_BOOKMARK_MODE; return null; }, /* * Whether or not the microsummary picker is enabled for this bookmark. * The picker is enabled for regular pages. It's disabled for livemarks, * separators, folders, or when bookmarking multiple tabs. * * @returns boolean true if the picker is enabled; false otherwise * */ get enabled() { if (this._mode == ADD_BOOKMARK_MODE) { // If we're adding a bookmark, we only have to worry about livemarks // and bookmarking multiple tabs. if ("feedURL" in gArg || gArg.bBookmarkAllTabs) return false; } else if (this._mode == EDIT_BOOKMARK_MODE) { // If we're modifying a bookmark, it could be a livemark, separator, // folder, or regular page. The picker is only enabled for regular pages. var isLivemark = BookmarksUtils.resolveType(gResource) == "Livemark"; var isSeparator = BookmarksUtils.resolveType(gResource) == "BookmarkSeparator"; var isContainer = RDFCU.IsContainer(BMDS, gResource); if (isLivemark || isSeparator || isContainer) return false; } else { // We should never get to this point, since we're only being used // in the Add Bookmark and Bookmark Properties dialogs, but if we're here // for some reason, be conservative and assume the picker is disabled. return false; } // We haven't found a reason to disable the picker, so say it's enabled. return true; }, init: function MSP_init() { // Set the label of the menu item representing the user-entered name. this._updateUserEnteredNameItem(); if (this._pageURI) { this._microsummaries = this._mss.getMicrosummaries(this._pageURI, this._bookmarkID); this._microsummaries.addObserver(this._observer); this.rebuild(); } }, destroy: function MSP_destroy() { if (this._pageURI && this._microsummaries) this._microsummaries.removeObserver(this._observer); }, rebuild: function MSP_rebuild() { var microsummaryMenuList = document.getElementById("name"); var microsummaryMenuPopup = document.getElementById("microsummaryMenuPopup"); // Save a reference to the active item (i.e. the item the user is hovering // over), if any, so we can reactivate it after the rebuild. var activeMicrosummary; if (microsummaryMenuList.menuBoxObject.activeChild) activeMicrosummary = microsummaryMenuList.menuBoxObject.activeChild.microsummary; // Remove old items from the menu, except for the first item, which holds // the user-entered name, and the second item, which separates and labels // the microsummaries below it. while (microsummaryMenuPopup.childNodes.length > 2) microsummaryMenuPopup.removeChild(microsummaryMenuPopup.lastChild); var enumerator = this._microsummaries.Enumerate(); // Show the drop marker if there are microsummaries; otherwise hide it. // We do this via a microsummary picker-specific "droppable" attribute // that, when set to "false", activates a "display: none" CSS rule // on the drop marker. if (enumerator.hasMoreElements()) microsummaryMenuList.setAttribute("droppable", "true"); else microsummaryMenuList.setAttribute("droppable", "false"); while (enumerator.hasMoreElements()) { var microsummary = enumerator.getNext().QueryInterface(Ci.nsIMicrosummary); var menuItem = document.createElement("menuitem"); // Store a reference to the microsummary in the menu item, so we know // which microsummary this menu item represents when it's time to save // changes to the datastore. menuItem.microsummary = microsummary; // Content may have to be generated asynchronously; we don't necessarily // have it now. If we do, great; otherwise, fall back to the generator // name, then the URI, and we trigger a microsummary content update. // Once the update completes, the microsummary will notify our observer // to rebuild the menu. // XXX Instead of just showing the generator name or (heaven forbid) // its URI when we don't have content, we should tell the user that we're // loading the microsummary, perhaps with some throbbing to let her know // it's in progress. if (microsummary.content != null) menuItem.setAttribute("label", microsummary.content); else { menuItem.setAttribute("label", microsummary.generator.name ? microsummary.generator.name : microsummary.generator.uri.spec); microsummary.update(); } microsummaryMenuPopup.appendChild(menuItem); // Select the item if this is the current microsummary for the bookmark. if (this._bookmarkID && this._mss.isMicrosummary(this._bookmarkID, microsummary)) microsummaryMenuList.selectedItem = menuItem; // Activate the item if it was active before the rebuild (i.e. the user // was hovering over it). if (activeMicrosummary && microsummary == activeMicrosummary) microsummaryMenuList.menuBoxObject.activeChild = menuItem; } }, _observer: { interfaces: [Ci.nsIMicrosummaryObserver, Ci.nsISupports], QueryInterface: function (iid) { //if (!this.interfaces.some( function(v) { return iid.equals(v) } )) if (!iid.equals(Ci.nsIMicrosummaryObserver) && !iid.equals(Ci.nsISupports)) throw Components.results.NS_ERROR_NO_INTERFACE; return this; }, onContentLoaded: function(microsummary) { MicrosummaryPicker.rebuild(); }, onElementAppended: function(microsummary) { MicrosummaryPicker.rebuild(); } }, /** * Called when the user types in the microsummary picker's text box. * * @param event * the event object representing the input event * */ onInput: function MSP_onInput(event) { this._updateUserEnteredNameItem(); }, /** * Update the menu item representing the user-entered name when the user * changes the name by typing in the text box. * */ _updateUserEnteredNameItem: function MSP__updateUserEnteredNameItem() { var nameField = document.getElementById("name"); var nameItem = document.getElementById("userEnteredNameItem"); nameItem.label = nameField.value; }, commit: function MSP_commit() { var changed = false; var menuList = document.getElementById("name"); // Something should always be selected in the microsummary menu, // but if nothing is selected, then conservatively assume we should // just display the bookmark title. if (menuList.selectedIndex == -1) menuList.selectedIndex = 0; // This will set microsummary == undefined if the user selected // the "don't display a microsummary" item. var newMicrosummary = menuList.selectedItem.microsummary; if (newMicrosummary == null && this._mss.hasMicrosummary(this._bookmarkID)) { this._mss.removeMicrosummary(this._bookmarkID); changed = true; } else if (newMicrosummary != null && !this._mss.isMicrosummary(this._bookmarkID, newMicrosummary)) { this._mss.setMicrosummary(this._bookmarkID, newMicrosummary); changed = true; } return changed; } }; function debug(str) { dump(str + "\n"); }