mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
466 lines
14 KiB
JavaScript
466 lines
14 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* Utility singleton for manipulating bookmarks.
|
|
*/
|
|
var Bookmarks = {
|
|
get metroRoot() {
|
|
return PlacesUtils.annotations.getItemsWithAnnotation('metro/bookmarksRoot', {})[0];
|
|
},
|
|
|
|
logging: false,
|
|
log: function(msg) {
|
|
if (this.logging) {
|
|
Services.console.logStringMessage(msg);
|
|
}
|
|
},
|
|
|
|
addForURI: function bh_addForURI(aURI, aTitle, callback) {
|
|
this.isURIBookmarked(aURI, function (isBookmarked) {
|
|
if (isBookmarked)
|
|
return;
|
|
|
|
let bookmarkTitle = aTitle || aURI.spec;
|
|
let bookmarkService = PlacesUtils.bookmarks;
|
|
let bookmarkId = bookmarkService.insertBookmark(Bookmarks.metroRoot,
|
|
aURI,
|
|
bookmarkService.DEFAULT_INDEX,
|
|
bookmarkTitle);
|
|
|
|
// XXX Used for browser-chrome tests
|
|
let event = document.createEvent("Events");
|
|
event.initEvent("BookmarkCreated", true, false);
|
|
window.dispatchEvent(event);
|
|
|
|
if (callback)
|
|
callback(bookmarkId);
|
|
});
|
|
},
|
|
|
|
isURIBookmarked: function bh_isURIBookmarked(aURI, callback) {
|
|
if (!callback)
|
|
return;
|
|
PlacesUtils.asyncGetBookmarkIds(aURI, function(aItemIds) {
|
|
callback(aItemIds && aItemIds.length > 0);
|
|
}, this);
|
|
},
|
|
|
|
removeForURI: function bh_removeForURI(aURI, callback) {
|
|
// XXX blargle xpconnect! might not matter, but a method on
|
|
// nsINavBookmarksService that takes an array of items to
|
|
// delete would be faster. better yet, a method that takes a URI!
|
|
PlacesUtils.asyncGetBookmarkIds(aURI, function(aItemIds) {
|
|
aItemIds.forEach(PlacesUtils.bookmarks.removeItem);
|
|
if (callback)
|
|
callback(aURI, aItemIds);
|
|
|
|
// XXX Used for browser-chrome tests
|
|
let event = document.createEvent("Events");
|
|
event.initEvent("BookmarkRemoved", true, false);
|
|
window.dispatchEvent(event);
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Wraps a list/grid control implementing nsIDOMXULSelectControlElement and
|
|
* fills it with the user's bookmarks.
|
|
*
|
|
* @param aSet Control implementing nsIDOMXULSelectControlElement.
|
|
* @param {Number} aLimit Maximum number of items to show in the view.
|
|
* @param aRoot Bookmark root to show in the view.
|
|
*/
|
|
function BookmarksView(aSet, aLimit, aRoot, aFilterUnpinned) {
|
|
this._set = aSet;
|
|
this._set.controller = this;
|
|
this._inBatch = false; // batch up grid updates to avoid redundant arrangeItems calls
|
|
|
|
this._limit = aLimit;
|
|
this._filterUnpinned = aFilterUnpinned;
|
|
this._bookmarkService = PlacesUtils.bookmarks;
|
|
this._navHistoryService = gHistSvc;
|
|
|
|
this._changes = new BookmarkChangeListener(this);
|
|
this._pinHelper = new ItemPinHelper("metro.bookmarks.unpinned");
|
|
this._bookmarkService.addObserver(this._changes, false);
|
|
window.addEventListener('MozAppbarDismissing', this, false);
|
|
window.addEventListener('BookmarksNeedsRefresh', this, false);
|
|
|
|
this.root = aRoot;
|
|
}
|
|
|
|
BookmarksView.prototype = {
|
|
_limit: null,
|
|
_set: null,
|
|
_changes: null,
|
|
_root: null,
|
|
_sort: 0, // Natural bookmark order.
|
|
_toRemove: null,
|
|
|
|
get sort() {
|
|
return this._sort;
|
|
},
|
|
|
|
set sort(aSort) {
|
|
this._sort = aSort;
|
|
this.clearBookmarks();
|
|
this.getBookmarks();
|
|
},
|
|
|
|
get root() {
|
|
return this._root;
|
|
},
|
|
|
|
set root(aRoot) {
|
|
this._root = aRoot;
|
|
},
|
|
|
|
handleItemClick: function bv_handleItemClick(aItem) {
|
|
let url = aItem.getAttribute("value");
|
|
BrowserUI.goToURI(url);
|
|
},
|
|
|
|
_getItemForBookmarkId: function bv__getItemForBookmark(aBookmarkId) {
|
|
return this._set.querySelector("richgriditem[bookmarkId='" + aBookmarkId + "']");
|
|
},
|
|
|
|
_getBookmarkIdForItem: function bv__getBookmarkForItem(aItem) {
|
|
return +aItem.getAttribute("bookmarkId");
|
|
},
|
|
|
|
_updateItemWithAttrs: function dv__updateItemWithAttrs(anItem, aAttrs) {
|
|
for (let name in aAttrs)
|
|
anItem.setAttribute(name, aAttrs[name]);
|
|
},
|
|
|
|
getBookmarks: function bv_getBookmarks(aRefresh) {
|
|
let options = this._navHistoryService.getNewQueryOptions();
|
|
options.queryType = options.QUERY_TYPE_BOOKMARKS;
|
|
options.excludeQueries = true; // Don't include "smart folders"
|
|
options.sortingMode = this._sort;
|
|
|
|
let limit = this._limit || Infinity;
|
|
|
|
let query = this._navHistoryService.getNewQuery();
|
|
query.setFolders([Bookmarks.metroRoot], 1);
|
|
|
|
let result = this._navHistoryService.executeQuery(query, options);
|
|
let rootNode = result.root;
|
|
rootNode.containerOpen = true;
|
|
let childCount = rootNode.childCount;
|
|
|
|
this._inBatch = true; // batch up grid updates to avoid redundant arrangeItems calls
|
|
|
|
for (let i = 0, addedCount = 0; i < childCount && addedCount < limit; i++) {
|
|
let node = rootNode.getChild(i);
|
|
|
|
// Ignore folders, separators, undefined item types, etc.
|
|
if (node.type != node.RESULT_TYPE_URI)
|
|
continue;
|
|
|
|
// If item is marked for deletion, skip it.
|
|
if (this._toRemove && this._toRemove.indexOf(node.itemId) !== -1)
|
|
continue;
|
|
|
|
let item = this._getItemForBookmarkId(node.itemId);
|
|
|
|
// Item has been unpinned.
|
|
if (this._filterUnpinned && !this._pinHelper.isPinned(node.itemId)) {
|
|
if (item)
|
|
this.removeBookmark(node.itemId);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (!aRefresh || !item) {
|
|
// If we're not refreshing or the item is not in the grid, add it.
|
|
this.addBookmark(node.itemId, addedCount);
|
|
} else if (aRefresh && item) {
|
|
// Update context action in case it changed in another view.
|
|
this._setContextActions(item);
|
|
}
|
|
|
|
addedCount++;
|
|
}
|
|
|
|
// Remove extra items in case a refresh added more than the limit.
|
|
// This can happen when undoing a delete.
|
|
if (aRefresh) {
|
|
while (this._set.itemCount > limit)
|
|
this._set.removeItemAt(this._set.itemCount - 1, true);
|
|
}
|
|
this._set.arrangeItems();
|
|
this._inBatch = false;
|
|
rootNode.containerOpen = false;
|
|
},
|
|
|
|
inCurrentView: function bv_inCurrentView(aParentId, aItemId) {
|
|
if (this._root && aParentId != this._root)
|
|
return false;
|
|
|
|
return !!this._getItemForBookmarkId(aItemId);
|
|
},
|
|
|
|
clearBookmarks: function bv_clearBookmarks() {
|
|
while (this._set.itemCount > 0)
|
|
this._set.removeItemAt(0, true);
|
|
this._set.arrangeItems();
|
|
},
|
|
|
|
addBookmark: function bv_addBookmark(aBookmarkId, aPos) {
|
|
let index = this._bookmarkService.getItemIndex(aBookmarkId);
|
|
let uri = this._bookmarkService.getBookmarkURI(aBookmarkId);
|
|
let title = this._bookmarkService.getItemTitle(aBookmarkId) || uri.spec;
|
|
let item = this._set.insertItemAt(aPos || index, title, uri.spec, this._inBatch);
|
|
item.setAttribute("bookmarkId", aBookmarkId);
|
|
this._setContextActions(item);
|
|
this._updateFavicon(aBookmarkId, item, uri);
|
|
},
|
|
|
|
_setContextActions: function bv__setContextActions(aItem) {
|
|
let itemId = this._getBookmarkIdForItem(aItem);
|
|
aItem.setAttribute("data-contextactions", "delete," + (this._pinHelper.isPinned(itemId) ? "unpin" : "pin"));
|
|
if (aItem.refresh) aItem.refresh();
|
|
},
|
|
|
|
_updateFavicon: function bv__updateFavicon(aBookmarkId, aItem, aUri) {
|
|
PlacesUtils.favicons.getFaviconURLForPage(aUri, this._gotIcon.bind(this, aBookmarkId, aItem));
|
|
},
|
|
|
|
_gotIcon: function bv__gotIcon(aBookmarkId, aItem, aIconUri) {
|
|
aItem.setAttribute("iconURI", aIconUri ? aIconUri.spec : "");
|
|
if (!aIconUri) {
|
|
return;
|
|
}
|
|
ColorUtils.getForegroundAndBackgroundIconColors(aIconUri, function(foregroundColor, backgroundColor) {
|
|
aItem.style.color = foregroundColor; //color text
|
|
aItem.setAttribute("customColor", backgroundColor); //set background
|
|
if (aItem.refresh) {
|
|
aItem.refresh();
|
|
}
|
|
});
|
|
},
|
|
|
|
_sendNeedsRefresh: function bv__sendNeedsRefresh(){
|
|
// Event sent when all view instances need to refresh.
|
|
let event = document.createEvent("Events");
|
|
event.initEvent("BookmarksNeedsRefresh", true, false);
|
|
window.dispatchEvent(event);
|
|
},
|
|
|
|
updateBookmark: function bv_updateBookmark(aBookmarkId) {
|
|
let item = this._getItemForBookmarkId(aBookmarkId);
|
|
|
|
if (!item)
|
|
return;
|
|
|
|
let oldIndex = this._set.getIndexOfItem(item);
|
|
let index = this._bookmarkService.getItemIndex(aBookmarkId);
|
|
|
|
if (oldIndex != index) {
|
|
this.removeBookmark(aBookmarkId);
|
|
this.addBookmark(aBookmarkId);
|
|
return;
|
|
}
|
|
|
|
let uri = this._bookmarkService.getBookmarkURI(aBookmarkId);
|
|
let title = this._bookmarkService.getItemTitle(aBookmarkId) || uri.spec;
|
|
|
|
item.setAttribute("value", uri.spec);
|
|
item.setAttribute("label", title);
|
|
|
|
this._updateFavicon(aBookmarkId, item, uri);
|
|
},
|
|
|
|
removeBookmark: function bv_removeBookmark(aBookmarkId) {
|
|
let item = this._getItemForBookmarkId(aBookmarkId);
|
|
let index = this._set.getIndexOfItem(item);
|
|
this._set.removeItemAt(index, this._inBatch);
|
|
},
|
|
|
|
destruct: function bv_destruct() {
|
|
this._bookmarkService.removeObserver(this._changes);
|
|
window.removeEventListener('MozAppbarDismissing', this, false);
|
|
window.removeEventListener('BookmarksNeedsRefresh', this, false);
|
|
},
|
|
|
|
doActionOnSelectedTiles: function bv_doActionOnSelectedTiles(aActionName, aEvent) {
|
|
let tileGroup = this._set;
|
|
let selectedTiles = tileGroup.selectedItems;
|
|
|
|
switch (aActionName){
|
|
case "delete":
|
|
Array.forEach(selectedTiles, function(aNode) {
|
|
if (!this._toRemove) {
|
|
this._toRemove = [];
|
|
}
|
|
|
|
let itemId = this._getBookmarkIdForItem(aNode);
|
|
|
|
this._toRemove.push(itemId);
|
|
this.removeBookmark(itemId);
|
|
}, this);
|
|
|
|
// stop the appbar from dismissing
|
|
aEvent.preventDefault();
|
|
|
|
// at next tick, re-populate the context appbar.
|
|
setTimeout(function(){
|
|
// fire a MozContextActionsChange event to update the context appbar
|
|
let event = document.createEvent("Events");
|
|
// we need the restore button to show (the tile node will go away though)
|
|
event.actions = ["restore"];
|
|
event.initEvent("MozContextActionsChange", true, false);
|
|
tileGroup.dispatchEvent(event);
|
|
}, 0);
|
|
break;
|
|
|
|
case "restore":
|
|
// clear toRemove and let _sendNeedsRefresh update the items.
|
|
this._toRemove = null;
|
|
break;
|
|
|
|
case "unpin":
|
|
Array.forEach(selectedTiles, function(aNode) {
|
|
let itemId = this._getBookmarkIdForItem(aNode);
|
|
|
|
if (this._filterUnpinned)
|
|
this.removeBookmark(itemId);
|
|
|
|
this._pinHelper.setUnpinned(itemId);
|
|
}, this);
|
|
break;
|
|
|
|
case "pin":
|
|
Array.forEach(selectedTiles, function(aNode) {
|
|
let itemId = this._getBookmarkIdForItem(aNode);
|
|
|
|
this._pinHelper.setPinned(itemId);
|
|
}, this);
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
// Send refresh event so all view are in sync.
|
|
this._sendNeedsRefresh();
|
|
},
|
|
|
|
handleEvent: function bv_handleEvent(aEvent) {
|
|
switch (aEvent.type){
|
|
case "MozAppbarDismissing":
|
|
// If undo wasn't pressed, time to do definitive actions.
|
|
if (this._toRemove) {
|
|
for (let bookmarkId of this._toRemove) {
|
|
this._bookmarkService.removeItem(bookmarkId);
|
|
}
|
|
this._toRemove = null;
|
|
}
|
|
break;
|
|
|
|
case "BookmarksNeedsRefresh":
|
|
this.getBookmarks(true);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
var BookmarksStartView = {
|
|
_view: null,
|
|
get _grid() { return document.getElementById("start-bookmarks-grid"); },
|
|
|
|
init: function init() {
|
|
this._view = new BookmarksView(this._grid, StartUI.maxResultsPerSection, Bookmarks.metroRoot, true);
|
|
this._view.getBookmarks();
|
|
},
|
|
|
|
uninit: function uninit() {
|
|
this._view.destruct();
|
|
},
|
|
|
|
show: function show() {
|
|
this._grid.arrangeItems();
|
|
}
|
|
};
|
|
|
|
var BookmarksPanelView = {
|
|
_view: null,
|
|
|
|
get _grid() { return document.getElementById("bookmarks-list"); },
|
|
get visible() { return PanelUI.isPaneVisible("bookmarks-container"); },
|
|
|
|
init: function init() {
|
|
this._view = new BookmarksView(this._grid, null, Bookmarks.metroRoot);
|
|
},
|
|
|
|
show: function show() {
|
|
this._view.getBookmarks(true);
|
|
this._grid.arrangeItems();
|
|
},
|
|
|
|
uninit: function uninit() {
|
|
this._view.destruct();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Observes bookmark changes and keeps a linked BookmarksView updated.
|
|
*
|
|
* @param aView An instance of BookmarksView.
|
|
*/
|
|
function BookmarkChangeListener(aView) {
|
|
this._view = aView;
|
|
}
|
|
|
|
BookmarkChangeListener.prototype = {
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//// nsINavBookmarkObserver
|
|
onBeginUpdateBatch: function () { },
|
|
onEndUpdateBatch: function () { },
|
|
|
|
onItemAdded: function bCL_onItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle, aDateAdded, aGUID, aParentGUID) {
|
|
this._view.getBookmarks(true);
|
|
},
|
|
|
|
onItemChanged: function bCL_onItemChanged(aItemId, aProperty, aIsAnnotationProperty, aNewValue, aLastModified, aItemType, aParentId, aGUID, aParentGUID) {
|
|
let itemIndex = PlacesUtils.bookmarks.getItemIndex(aItemId);
|
|
if (!this._view.inCurrentView(aParentId, aItemId))
|
|
return;
|
|
|
|
this._view.updateBookmark(aItemId);
|
|
},
|
|
|
|
onItemMoved: function bCL_onItemMoved(aItemId, aOldParentId, aOldIndex, aNewParentId, aNewIndex, aItemType, aGUID, aOldParentGUID, aNewParentGUID) {
|
|
let wasInView = this._view.inCurrentView(aOldParentId, aItemId);
|
|
let nowInView = this._view.inCurrentView(aNewParentId, aItemId);
|
|
|
|
if (!wasInView && nowInView)
|
|
this._view.addBookmark(aItemId);
|
|
|
|
if (wasInView && !nowInView)
|
|
this._view.removeBookmark(aItemId);
|
|
|
|
this._view.getBookmarks(true);
|
|
},
|
|
|
|
onBeforeItemRemoved: function (aItemId, aItemType, aParentId, aGUID, aParentGUID) { },
|
|
onItemRemoved: function bCL_onItemRemoved(aItemId, aParentId, aIndex, aItemType, aURI, aGUID, aParentGUID) {
|
|
if (!this._view.inCurrentView(aParentId, aItemId))
|
|
return;
|
|
|
|
this._view.removeBookmark(aItemId);
|
|
this._view.getBookmarks(true);
|
|
},
|
|
|
|
onItemVisited: function(aItemId, aVisitId, aTime, aTransitionType, aURI, aParentId, aGUID, aParentGUID) { },
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//// nsISupports
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
|
|
};
|