gecko/mobile/chrome/content/bindings.xml

1122 lines
38 KiB
XML
Raw Normal View History

<?xml version="1.0"?>
<!DOCTYPE bindings [
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
]>
<bindings
xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="autocomplete-aligned" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
<implementation>
<method name="openPopup">
<body><![CDATA[
this.popup.openAutocompletePopup(this, null);
]]></body>
</method>
<method name="closePopup">
<body><![CDATA[
// hack! we want to revert to the "all results" popup when the
// controller would otherwise close us because of an empty search
// string.
if (this.value == "")
this.showHistoryPopup();
]]></body>
</method>
</implementation>
<handlers>
<handler event="keypress" keycode="VK_RETURN" phase="capturing">
<![CDATA[
if (this.popup.allBookmarksItemSelected) {
this.popup.closePopup();
CommandUpdater.doCommand("cmd_bookmarks");
}
]]>
</handler>
</handlers>
</binding>
<binding id="popup_autocomplete_result">
<content orient="vertical">
<xul:hbox class="autocomplete-item-label" align="center" xbl:inherits="tags, favorite" mousethrough="always">
<xul:image xbl:inherits="src"/>
<xul:label flex="1" crop="center" xbl:inherits="value"/>
</xul:hbox>
<xul:label class="autocomplete-item-url" xbl:inherits="value=url" crop="center" mousethrough="always"/>
</content>
</binding>
<binding id="popup_autocomplete">
<content>
<xul:vbox class="autocomplete-box" flex="1">
<!-- 12 child items, to match browser.urlbar.maxRichResults -->
<xul:scrollbox orient="vertical"
class="autocomplete-items"
anonid="autocomplete-items"
flex="1000">
<xul:autocompleteresult anonid="allbookmarks"
value="&allBookmarks.label;"
class="allbookmarks"/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
</xul:scrollbox>
<children/>
</xul:vbox>
</content>
<implementation implements="nsIAutoCompletePopup">
<!-- Used by the chrome input handler -->
<property name="boxObject"
readonly="true"
onget="return this._items.boxObject;"/>
<field name="_scrollBoxObject">
this.boxObject.QueryInterface(Components.interfaces.nsIScrollBoxObject);
</field>
<!-- nsIAutocompleteInput -->
<property name="overrideValue"
readonly="true"
onget="return null;"/>
<field name="_input"/>
<property name="input"
readonly="true"
onget="return this._input;"/>
<field name="_selectedIndex">-1</field>
<field name="_selectedItem"/>
<property name="selectedIndex"
onget="return this._allBookmarksItem._hidden ? this._selectedIndex : this._selectedIndex - 1;">
<setter><![CDATA[
// Ignore invalid indices
if (val < -1 ||
val > this._matchCount - 1)
return val;
if (this._selectedItem)
this._styleItem(this._selectedItem, false);
// highlight the selected item
let item = this._items.childNodes.item(val);
if (item) {
this._selectedItem = item;
this._styleItem(this._selectedItem, true);
this._scrollBoxObject.ensureElementIsVisible(this._selectedItem);
}
return this._selectedIndex = val;
]]></setter>
</property>
<field name="_popupOpen">false</field>
<property name="popupOpen"
readonly="true"
onget="return this._popupOpen;"/>
<method name="openAutocompletePopup">
<parameter name="aInput"/>
<parameter name="aElement"/>
<body><![CDATA[
if (this._popupOpen)
return;
BrowserUI.pushDialog(this);
this._selectedItem = null;
this._input = aInput;
this._input.select();
if (this.hidden)
this.hidden = false;
this.collapsed = false;
this._popupOpen = true;
this.invalidate();
]]></body>
</method>
<method name="closePopup">
<body><![CDATA[
if (!this._popupOpen)
return;
this.selectedIndex = -1;
this.input.controller.stopSearch();
this.collapsed = true;
this._popupOpen = false;
// Scroll to the top left for the next open (only if necessary).
// Doing this now rather than in open() turns out to be faster,
// possibly because it avoids scrolling too soon after we uncollapse
// ourselves
if (this._items.scrollTop || this._items.scrollLeft)
this._scrollBoxObject.scrollTo(0, 0);
BrowserUI.showToolbar(false);
BrowserUI.popDialog();
]]></body>
</method>
<!-- Helper used by active dialog system -->
<method name="close">
<body><![CDATA[
this.input.reset();
this.input.blur();
this.closePopup();
]]></body>
</method>
<field name="_XULNS">("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul")</field>
<method name="invalidate">
<body><![CDATA[
// Don't bother doing work if we're not even open
if (!this.popupOpen)
return;
let controller = this.input.controller;
let searchString = controller.searchString;
let items = this._items;
// Remove the allBookmarksItem if present, before populating the list.
if (!this._allBookmarksItem._hidden) {
items.removeChild(this._allBookmarksItem);
this._allBookmarksItem._hidden = true;
}
// Need to iterate over all our existing entries at a minimum, to make
// sure they're either updated or cleared out. We might also have to
// add extra items.
let matchCount = this._matchCount;
let children = items.childNodes;
let iterCount = Math.max(children.length, matchCount);
for (let i = 0; i < iterCount; ++i) {
let item = children.item(i);
// Create an item if needed
if (!item) {
item = document.createElementNS(this._XULNS, "xul:autocompleteresult");
items.appendChild(item);
}
item._index = i;
// Check whether there's an entry to fill
if (i > matchCount - 1) {
// Just clear out the old item's value. CSS takes care of hiding
// everything else based on value="" (star, tags, etc.)
item.setAttribute("value", "");
item._empty = true;
continue;
}
item._empty = false;
// Assign the values
let type = controller.getStyleAt(i);
let title = controller.getCommentAt(i);
let tags = '';
if (type == "tag")
[, title, tags] = title.match(/^(.+) \u2013 (.+)$/);
item.setAttribute("tags", tags);
let url = controller.getValueAt(i);
item.setAttribute("value", title || url);
item.setAttribute("url", url);
let isBookmark = ((type == "bookmark") || (type == "tag"));
item.setAttribute("favorite", isBookmark);
item.setAttribute("src", controller.getImageAt(i));
}
// Show the "no results" or "all bookmarks" entries as needed
this._updateNoResultsItem(matchCount);
if (searchString == "") {
items.insertBefore(this._allBookmarksItem, items.firstChild);
this._allBookmarksItem._hidden = false;
}
]]></body>
</method>
<method name="_updateNoResultsItem">
<parameter name="isResults" />
<body><![CDATA[
let noResultsItem = this._items.childNodes.item(1);
if (isResults) {
noResultsItem.className = "";
}
else {
noResultsItem.className = "noresults";
noResultsItem.setAttribute("value", "]]>&noResults.label;<![CDATA[");
noResultsItem.removeAttribute("favorite");
noResultsItem.removeAttribute("url");
noResultsItem.removeAttribute("src");
noResultsItem.removeAttribute("tags");
}
]]></body>
</method>
<field name="_allBookmarksItem">document.getAnonymousElementByAttribute(this, "anonid", "allbookmarks");</field>
<property name="allBookmarksItemSelected" readonly="true"
onget="return this._selectedItem == this._allBookmarksItem;"/>
<method name="selectBy">
<parameter name="aReverse"/>
<parameter name="aPage"/>
<body><![CDATA[
let newIndex;
let lastIndex = this._matchCount - 1;
if (this._selectedIndex == -1)
newIndex = aReverse ? lastIndex : 0;
else
newIndex = this._selectedIndex + (aReverse ? -1 : 1);
// Deal with rollover
if (newIndex > lastIndex)
newIndex = 0;
else if (newIndex < 0)
newIndex = lastIndex;
this.selectedIndex = newIndex;
]]></body>
</method>
<!-- Helpers -->
<field name="_items">
document.getAnonymousElementByAttribute(this,
"anonid", "autocomplete-items");
</field>
<property name="_matchCount"
readonly="true">
<getter><![CDATA[
let matchCount = this.input.controller.matchCount;
return matchCount + (this._allBookmarksItem._hidden ? 0 : 1);
]]></getter>
</property>
<method name="_styleItem">
<parameter name="aItem"/>
<parameter name="aAddStyle"/>
<body><![CDATA[
if (aAddStyle)
aItem.className += " autocompleteresult-selected";
else
aItem.className = aItem.className.replace(/\s*autocompleteresult-selected/, "");
]]></body>
</method>
</implementation>
<handlers>
<handler event="click" button="0">
<![CDATA[
let target = event.originalTarget;
if (target == this._allBookmarksItem) {
this._selectedIndex = 0;
this.close();
CommandUpdater.doCommand("cmd_bookmarks");
}
else if (target.localName == "autocompleteresult" && !target._empty) {
let offset = this._allBookmarksItem._hidden ? 0 : 1;
this._selectedIndex = target._index + offset;
this.input.controller.handleEnter(true);
}
]]>
</handler>
</handlers>
</binding>
<binding id="place-base">
<content/>
<implementation>
<field name="_uri">null</field>
<field name="_control">null</field>
<field name="_isEditing">false</field>
<field name="_ioService">
Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
</field>
<field name="_nameField">
document.getAnonymousElementByAttribute(this, "anonid", "name");
</field>
<field name="_uriField">
document.getAnonymousElementByAttribute(this, "anonid", "uri");
</field>
<field name="_tagsField">
document.getAnonymousElementByAttribute(this, "anonid", "tags");
</field>
<property name="itemId" onget="return this.getAttribute('itemid');"/>
<property name="type" onget="return this.getAttribute('type');"/>
<property name="uri">
<getter><![CDATA[
if (!this._uri && this.getAttribute("uri"))
this._uri = this._ioService.newURI(this.getAttribute("uri"), null, null);
return this._uri;
]]></getter>
</property>
<property name="name" onget="return this._nameField.value"
onset="this._nameField.value = val; return val;"/>
<property name="spec" onget="return this._uriField.value"
onset="this._uriField.value = val; return val;"/>
<property name="tags" onget="return this._tagsField.value"
onset="this._tagsField.value = val; return val;"/>
<property name="tagsAsArray" readonly="true">
<getter>
<![CDATA[
// we don't require the leading space (after each comma)
var tags = this.tags.split(",");
for (var i = 0; i < tags.length; i++) {
// remove trailing and leading spaces
tags[i] = tags[i].trim();
// remove empty entries from the array.
if (tags[i] == "") {
tags.splice(i, 1);
i--;
}
}
return tags;
]]>
</getter>
</property>
<property name="isEditing" readonly="true" onget="return this._isEditing;"/>
<property name="control" readonly="true">
<getter>
<![CDATA[
if (this._control)
return this._control;
let parent = this.parentNode;
while (parent) {
if (parent.localName == "placelist") {
this._control = parent;
return this._control;
}
parent = parent.parentNode;
}
return null;
]]>
</getter>
</property>
<method name="startEditing">
<parameter name="autoSelect"/>
<body>
<![CDATA[
if (!this.itemId)
return;
this._isEditing = true;
if (this.control) {
this.setAttribute("selected", "true");
this.control.scrollBoxObject.ensureElementIsVisible(this);
this.control.activeItem = this;
}
this.updateFields();
this._nameField.focus();
if (autoSelect)
this._nameField.select();
]]>
</body>
</method>
<method name="stopEditing">
<parameter name="shouldSave"/>
<body>
<![CDATA[
if (shouldSave)
this.save();
this._isEditing = false;
if (this.control) {
this.control.activeItem.removeAttribute("selected");
this.control.activeItem = null;
}
this.updateFields();
let focusedElement = document.commandDispatcher.focusedElement;
if (focusedElement)
focusedElement.blur();
let event = document.createEvent("Events");
event.initEvent("close", true, false);
this.dispatchEvent(event);
]]>
</body>
</method>
<method name="save">
<body>
<![CDATA[
// Update the tags
if (this.uri && this.type != "folder") {
let currentTags = PlacesUtils.tagging.getTagsForURI(this.uri, {});
let tags = this.tagsAsArray;
if (tags.length > 0 || currentTags.length > 0) {
let tagsToRemove = [];
let tagsToAdd = [];
for (let i = 0; i < currentTags.length; i++) {
if (tags.indexOf(currentTags[i]) == -1)
tagsToRemove.push(currentTags[i]);
}
for (let i = 0; i < tags.length; i++) {
if (currentTags.indexOf(tags[i]) == -1)
tagsToAdd.push(tags[i]);
}
if (tagsToAdd.length > 0)
PlacesUtils.tagging.tagURI(this.uri, tagsToAdd);
if (tagsToRemove.length > 0)
PlacesUtils.tagging.untagURI(this.uri, tagsToRemove);
}
this.setAttribute('tags', this.tags);
// If the URI was updated change it in the bookmark, but don't
// allow a blank URI. Revert to previous URI if blank.
let spec = this.spec;
if (spec && this.uri.spec != spec) {
try {
let oldURI = this._uri;
this._uri = this._ioService.newURI(spec, null, null);
PlacesUtils.bookmarks.changeBookmarkURI(this.itemId, this.uri);
this.setAttribute('uri', this.spec);
// move tags from old URI to new URI
let tags = this.tagsAsArray;
if (tags.length != 0) {
// only untag the old URI if this is the only bookmark
if (PlacesUtils.getBookmarksForURI(oldURI, {}).length == 0)
PlacesUtils.tagging.untagURI(oldURI, tags);
PlacesUtils.tagging.tagURI(this._uri, tags);
}
}
catch (e) { }
}
if (spec != this.uri.spec)
this.spec = this.uri.spec;
}
// Update the name and use the URI if name is blank
this.name = this.name || this.spec;
this.setAttribute('title', this.name);
PlacesUtils.bookmarks.setItemTitle(this.itemId, this.name);
]]>
</body>
</method>
<method name="remove">
<body>
<![CDATA[
PlacesUtils.bookmarks.removeItem(this.itemId);
// If this was the last bookmark (excluding tag-items and livemark
// children, see getMostRecentBookmarkForURI) for the bookmark's url,
// remove the url from tag containers as well.
if (this.uri && this.type != "folder") {
if (PlacesUtils.getMostRecentBookmarkForURI(this.uri) == -1) {
var tags = PlacesUtils.tagging.getTagsForURI(this.uri, {});
PlacesUtils.tagging.untagURI(this.uri, tags);
}
}
this.stopEditing(false);
let event = document.createEvent("Events");
event.initEvent("BookmarkRemove", true, false);
this.dispatchEvent(event);
if (this.control)
this.control.removeItem(this);
]]>
</body>
</method>
<method name="updateFields">
<body>
<![CDATA[
// implemented by sub classes
]]>
</body>
</method>
</implementation>
</binding>
<binding id="place-item" extends="chrome://browser/content/bindings.xml#place-base">
<content orient="vertical">
<xul:hbox anonid="bookmark-item" class="bookmark-item-label" align="center" flex="1" xbl:inherits="tags" mousethrough="always">
<xul:image xbl:inherits="src"/>
<xul:label flex="1" crop="center" xbl:inherits="value=title"/>
</xul:hbox>
<xul:label anonid="bookmark-url" class="bookmark-item-url" xbl:inherits="value=uri" crop="center" mousethrough="always"/>
<xul:hbox anonid="bookmark-manage" class="bookmark-manage" hidden="true" flex="1">
<xul:image xbl:inherits="src"/>
<xul:vbox flex="1">
<xul:vbox flex="1">
<xul:textbox anonid="name" xbl:inherits="value=title"/>
<xul:textbox anonid="uri" xbl:inherits="value=uri"/>
<xul:textbox anonid="tags" xbl:inherits="value=tags" emptytext="&editBookmarkTags.label;"/>
</xul:vbox>
<xul:hbox class="bookmark-controls" align="center">
<xul:button anonid="remove-button" class="bookmark-remove" label="&editBookmarkRemove.label;"
oncommand="document.getBindingParent(this).remove()"/>
<xul:spacer flex="1"/>
<xul:button anonid="done-button" class="bookmark-done" label="&editBookmarkDone.label;"
oncommand="document.getBindingParent(this).stopEditing(true)"/>
</xul:hbox>
</xul:vbox>
</xul:hbox>
</content>
<implementation>
<method name="updateFields">
<body>
<![CDATA[
document.getAnonymousElementByAttribute(this, "anonid", "bookmark-item").hidden = this._isEditing;
document.getAnonymousElementByAttribute(this, "anonid", "bookmark-url").hidden = this._isEditing;
document.getAnonymousElementByAttribute(this, "anonid", "bookmark-manage").hidden = !this._isEditing;
]]>
</body>
</method>
</implementation>
</binding>
<binding id="place-folder" extends="chrome://browser/content/bindings.xml#place-item">
<implementation>
<method name="updateFields">
<body>
<![CDATA[
document.getAnonymousElementByAttribute(this, "anonid", "bookmark-item").hidden = this._isEditing;
document.getAnonymousElementByAttribute(this, "anonid", "bookmark-url").hidden = this._isEditing;
this._uriField.hidden = true;
this._tagsField.hidden = true;
document.getAnonymousElementByAttribute(this, "anonid", "bookmark-manage").hidden = !this._isEditing;
]]>
</body>
</method>
</implementation>
</binding>
<binding id="place-label" extends="chrome://browser/content/bindings.xml#place-base">
<content align="center">
<xul:spacer xbl:inherits="width=indent"/>
<xul:image anonid="favicon" class="bookmark-folder-image"/>
<xul:label anonid="name" crop="end" flex="1" xbl:inherits="value=title"/>
</content>
</binding>
<binding id="place-list">
<content orient="vertical" flex="1">
<xul:vbox anonid="parent-items" class="place-list-parents" />
<xul:richlistbox anonid="child-items" class="place-list-children" flex="1"/>
</content>
<implementation>
<constructor>
<![CDATA[
this._type = this.getAttribute("type");
this._mode = this.getAttribute("mode");
this._folderParents = {};
this._folderParents[this._fakeDesktopFolderId] = this.mobileRoot;
this._folderParents[PlacesUtils.bookmarks.unfiledBookmarksFolder] = this._fakeDesktopFolderId;
this._folderParents[PlacesUtils.bookmarksMenuFolderId] = this._fakeDesktopFolderId;
this._folderParents[PlacesUtils.toolbarFolderId] = this._fakeDesktopFolderId;
]]>
</constructor>
<field name="_bundle">
Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService)
.createBundle("chrome://browser/locale/browser.properties");
</field>
<field name="_fakeDesktopFolderId">-1000</field>
<field name="_desktopFolder"><![CDATA[
({
itemId: this._fakeDesktopFolderId, tags: "", uri: "",
title: Elements.browserBundle.getString("bookmarkList.desktop"),
type: Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER
})
]]></field>
<field name="_desktopChildren"><![CDATA[
[
{
itemId: PlacesUtils.bookmarks.unfiledBookmarksFolder, tags: "", uri: "",
title: PlacesUtils.bookmarks.getItemTitle(PlacesUtils.bookmarks.unfiledBookmarksFolder),
type: Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER
},
{
itemId: PlacesUtils.bookmarksMenuFolderId, tags: "", uri: "",
title: PlacesUtils.bookmarks.getItemTitle(PlacesUtils.bookmarksMenuFolderId),
type: Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER
},
{
itemId: PlacesUtils.toolbarFolderId, tags: "", uri: "",
title: PlacesUtils.bookmarks.getItemTitle(PlacesUtils.toolbarFolderId),
type: Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER
}
];
]]></field>
<field name="_type"/>
<field name="_mode"/>
<field name="_activeItem">null</field>
<field name="_ignoreEditing">false</field>
<field name="_manageUI">false</field>
<field name="_parents">
document.getAnonymousElementByAttribute(this, "anonid", "parent-items");
</field>
<field name="_children">
document.getAnonymousElementByAttribute(this, "anonid", "child-items");
</field>
<field name="scrollBoxObject">this._children.scrollBoxObject</field>
<!-- This won't be updated when the window size changes, but that's OK,
since we don't need an exact value - we just use it to get an
approximate scroll position for deciding whether to append additional
items (see batchSize/_insertItems()).
-->
<field name="_childrenHeight">this.scrollBoxObject.height;</field>
<property name="items" readonly="true" onget="return this._children.childNodes"/>
<field name="mobileRoot"><![CDATA[
PlacesUtils.annotations.getItemsWithAnnotation("mobile/bookmarksRoot", {})[0];
]]></field>
<property name="isRootFolder" readonly="true">
<getter>
<![CDATA[
let currentFolderId = this._parents.lastChild.getAttribute("itemid");
return currentFolderId == this.mobileRoot;
]]>
</getter>
</property>
<property name="activeItem">
<getter>
<![CDATA[
return this._activeItem;
]]>
</getter>
<setter>
<![CDATA[
if (!this._ignoreEditing) {
if (this._activeItem && this._activeItem.isEditing) {
this._ignoreEditing = true;
this._activeItem.stopEditing(false);
this._ignoreEditing = false;
}
this._activeItem = val;
}
return val;
]]>
</setter>
</property>
<property name="manageUI">
<getter>
<![CDATA[
return this._manageUI;
]]>
</getter>
<setter>
<![CDATA[
if (this._manageUI != val) {
this._manageUI = val;
if (this._manageUI) {
this.setAttribute("ui", "manage");
if (this.getAttribute("autoedit") == "true" && this._children.hasChildNodes()) {
let first = this._getFirstVisibleChild();
if (first)
first.startEditing();
}
}
else {
if (this._activeItem && this._activeItem.isEditing)
this._activeItem.stopEditing();
this.removeAttribute("ui");
}
}
return val;
]]>
</setter>
</property>
<method name="isDesktopFolderEmpty">
<body>
<![CDATA[
let options = PlacesUtils.history.getNewQueryOptions();
options.queryType = options.QUERY_TYPE_BOOKMARKS;
let query = PlacesUtils.history.getNewQuery();
let folders = [ PlacesUtils.bookmarks.unfiledBookmarksFolder,
PlacesUtils.bookmarksMenuFolderId,
PlacesUtils.toolbarFolderId
];
query.setFolders(folders, 3);
let result = PlacesUtils.history.executeQuery(query, options);
let rootNode = result.root;
rootNode.containerOpen = true;
let isEmpty = !rootNode.childCount;
rootNode.containerOpen = false;
return isEmpty;
]]>
</body>
</method>
<method name="_getFirstVisibleChild">
<body>
<![CDATA[
let children = this._children.childNodes;
for (let i = 0; i < children.length; i++) {
let rect = children[i].getBoundingClientRect();
let position = rect.top - rect.height;
if (position > 0)
return children[i];
}
return null;
]]>
</body>
</method>
<method name="_getChildren">
<parameter name="aFolder"/>
<body>
<![CDATA[
let items = [];
let options = PlacesUtils.history.getNewQueryOptions();
options.queryType = (this._type == "bookmarks" ? options.QUERY_TYPE_BOOKMARKS : options.QUERY_TYPE_HISTORY);
// Exclude "query" items (e.g. "smart folders")
options.excludeQueries = true;
let query = PlacesUtils.history.getNewQuery();
if (aFolder)
query.setFolders([aFolder], 1);
let result = PlacesUtils.history.executeQuery(query, options);
let rootNode = result.root;
rootNode.containerOpen = true;
let cc = rootNode.childCount;
for (var i=0; i<cc; ++i) {
let node = rootNode.getChild(i);
// Ignore separators
if (node.type == node.RESULT_TYPE_SEPARATOR)
continue;
if (this._mode == "folders" && node.type == node.RESULT_TYPE_FOLDER) {
items.push(node);
}
else if (this._mode == "") {
items.push(node);
}
}
rootNode.containerOpen = false;
return items;
]]>
</body>
</method>
<!-- Number of elements to add to the list initially. If there are more
than this many bookmarks to display, only add them to the list once
the user has scrolled towards them. This is a performance
optimization to avoid locking up while attempting to append hundreds
of bookmarks to our richlistbox.
-->
<field name="batchSize">25</field>
<method name="openFolder">
<parameter name="aRootFolder"/>
<body>
<![CDATA[
aRootFolder = aRootFolder || this.mobileRoot;
this._activeItem = null;
let parents = this._parents;
while (parents.firstChild)
parents.removeChild(parents.firstChild);
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
var self = this;
let folderId = aRootFolder;
do {
let title;
if (folderId == this._fakeDesktopFolderId)
title = this._desktopFolder.title;
else
title = PlacesUtils.bookmarks.getItemTitle(folderId);
let parent = document.createElementNS(XULNS, "placelabel");
parent.setAttribute("class", "bookmark-folder");
parent.setAttribute("itemid", folderId);
parent.setAttribute("indent", 0);
parent.setAttribute("title", title);
parents.insertBefore(parent, parents.firstChild);
// XXX Fix me - use <handler>?
parent.addEventListener("click", function(e) { self.openFolder(e.target.previousSibling.itemId); }, false);
folderId = this._folderParents[folderId] || PlacesUtils.bookmarks.getFolderIdForItem(folderId);
} while (folderId != PlacesUtils.bookmarks.placesRoot)
let children = this._children;
while (children.firstChild)
children.removeChild(children.firstChild);
children.scrollBoxObject.scrollTo(0, 0);
this._childItems = (aRootFolder == this._fakeDesktopFolderId) ? this._desktopChildren.concat()
: this._getChildren(aRootFolder);
if (aRootFolder == this.mobileRoot && !this.isDesktopFolderEmpty())
this._childItems.unshift(this._desktopFolder);
this._insertItems();
]]>
</body>
</method>
<method name="close">
<body>
<![CDATA[
// Clear out references to child items, and remove event listener
// if needed
this._childItems = null;
if (this._scrollListenerAdded) {
this._children.removeEventListener("scroll", this._scrollListener, false);
this._scrollListenerAdded = false;
}
]]>
</body>
</method>
<method name="_insertItems">
<body>
<![CDATA[
let items = this._childItems.splice(0, this.batchSize);
if (!items.length)
return; // no items to insert
let children = this._children;
let itemsRemaining = this._childItems.length > 0;
if (itemsRemaining && !this._scrollListenerAdded) {
// We're not going to insert all items, so add a scroll listener
// to know when to add them.
this._children.addEventListener("scroll", this._scrollListener, false);
this._scrollListenerAdded = true;
}
if (!itemsRemaining && this._scrollListenerAdded) {
// Can get rid of the scroll listener now that all items are added.
this._children.removeEventListener("scroll", this._scrollListener, false);
this._scrollListenerAdded = false;
}
let fragment = document.createDocumentFragment();
let count = items.length;
for (let i=0; i<count; i++)
fragment.appendChild(this.createItem(items[i]));
children.appendChild(fragment);
// make sure we recalculate the scrollHeight of the children
this._childrenScrollHeight = -1;
]]>
</body>
</method>
<field name="_scrollListener"><![CDATA[
({
self: this,
handleEvent: function () {
this.self._checkForInsert();
}
})
]]></field>
<method name="_checkForInsert">
<body>
<![CDATA[
if (this._childrenScrollHeight == -1) {
// Need to force a reflow to get the right value here since we may
// have just added children.
Browser.forceChromeReflow();
let scrollheight = {};
this.scrollBoxObject.getScrolledSize({}, scrollheight);
this._childrenScrollHeight = scrollheight.value;
}
let y = {};
this.scrollBoxObject.getPosition({}, y);
let scrollRatio = (y.value + this._childrenHeight) / this._childrenScrollHeight;
// If we're scrolled 80% to the bottom of the list, append the next
// set of items
if (scrollRatio > 0.8)
this._insertItems();
]]>
</body>
</method>
<method name="createItem">
<parameter name="aItem"/>
<body>
<![CDATA[
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let child = document.createElementNS(XULNS, "placeitem");
child.setAttribute("itemid", aItem.itemId);
child.setAttribute("class", "bookmark-item");
child.setAttribute("title", aItem.title);
child.setAttribute("uri", aItem.uri);
child.setAttribute("tags", aItem.tags);
if (aItem.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER)
child.setAttribute("type", "folder");
else
child.setAttribute("src", aItem.icon);
// XXX make a <handler>
let self = this;
child.addEventListener("click", function(e) { self._fireOpen(e, child); }, false);
return child;
]]>
</body>
</method>
<method name="removeItem">
<parameter name="aItem"/>
<body>
<![CDATA[
this._children.removeChild(aItem);
]]>
</body>
</method>
<method name="_fireOpen">
<parameter name="aEvent"/>
<parameter name="aItem"/>
<body>
<![CDATA[
if (aEvent.originalTarget.localName == "button" || this._activeItem == aItem)
return;
if (this._manageUI) {
aItem.startEditing();
return;
}
if (aItem.type == "folder") {
this.openFolder(aItem.itemId);
}
else {
// Force the item to be active
this._activeItem = aItem;
// This is a callback used to forward information to some
// external code [we fire an event & a pseudo attribute event]
let event = document.createEvent("Events");
event.initEvent("BookmarkOpen", true, false);
this.dispatchEvent(event);
let func = new Function("event", this.getAttribute("onopen"));
func.call(this, event);
}
]]>
</body>
</method>
</implementation>
</binding>
<binding id="richlistitem" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
<handlers>
<handler event="mousedown" phase="capturing">
<![CDATA[
event.stopPropagation();
]]>
</handler>
</handlers>
</binding>
<binding id="menulist" display="xul:box" extends="chrome://global/content/bindings/menulist.xml#menulist">
<handlers>
<handler event="mousedown" phase="capturing">
<![CDATA[
// Stop the normal menupopup from appearing
event.stopPropagation();
]]>
</handler>
<handler event="click" button="0">
<![CDATA[
if (this.disabled || this.itemCount == 0)
return;
this.focus();
SelectHelper.show(this);
]]>
</handler>
</handlers>
</binding>
<binding id="chrome-select-option">
<content orient="horizontal" flex="1">
<xul:image anonid="check"/>
<xul:label anonid="label" xbl:inherits="value=label"/>
</content>
<implementation>
<property name="selected">
<getter>
<![CDATA[
return this.hasAttribute("selected");
]]>
</getter>
<setter>
<![CDATA[
if (val)
this.setAttribute("selected", "true");
else
this.removeAttribute("selected");
return val;
]]>
</setter>
</property>
</implementation>
</binding>
</bindings>