508 строки
14 KiB
JavaScript
508 строки
14 KiB
JavaScript
/* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 ; js-indent-level: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* 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 of items in this file require:
|
|
*
|
|
* getSelectedDirectoryURI()
|
|
* returns the URI of the selected directory
|
|
* AbResultsPaneDoubleClick(card)
|
|
* Is called when the results pane is double-clicked, with the clicked card.
|
|
* AbEditCard(card)
|
|
* Is called when a card is to be edited, with the card as the parameter.
|
|
*
|
|
* The following function is only required if ResultsPaneController is used:
|
|
*
|
|
* goSetMenuValue()
|
|
* Core function in globalOverlay.js
|
|
*/
|
|
|
|
// List/card selections in the results pane.
|
|
var kNothingSelected = 0;
|
|
var kListsAndCards = 1;
|
|
var kMultipleListsOnly = 2;
|
|
var kSingleListOnly = 3;
|
|
var kCardsOnly = 4;
|
|
|
|
// Global Variables
|
|
|
|
// gAbView holds an object with an nsIAbView interface
|
|
var gAbView = null;
|
|
// Holds a reference to the "abResultsTree" document element. Initially
|
|
// set up by SetAbView.
|
|
var gAbResultsTree = null;
|
|
|
|
function SetAbView(aURI)
|
|
{
|
|
// If we don't have a URI, just clear the view and leave everything else
|
|
// alone.
|
|
if (!aURI) {
|
|
gAbView.clearView();
|
|
return;
|
|
}
|
|
|
|
// If we do have a URI, we want to allow updating the review even if the
|
|
// URI is the same, as the search results may be different.
|
|
|
|
var sortColumn = kDefaultSortColumn;
|
|
var sortDirection = kDefaultAscending;
|
|
|
|
if (!gAbResultsTree) {
|
|
gAbResultsTree = document.getElementById("abResultsTree");
|
|
gAbResultsTree.controllers.appendController(ResultsPaneController);
|
|
}
|
|
|
|
if (gAbView) {
|
|
sortColumn = gAbView.sortColumn;
|
|
sortDirection = gAbView.sortDirection;
|
|
}
|
|
else {
|
|
if (gAbResultsTree.hasAttribute("sortCol"))
|
|
sortColumn = gAbResultsTree.getAttribute("sortCol");
|
|
var sortColumnNode = document.getElementById(sortColumn);
|
|
if (sortColumnNode && sortColumnNode.hasAttribute("sortDirection"))
|
|
sortDirection = sortColumnNode.getAttribute("sortDirection");
|
|
}
|
|
|
|
var directory = GetDirectoryFromURI(aURI);
|
|
|
|
if (!gAbView)
|
|
gAbView = Cc["@mozilla.org/addressbook/abview;1"]
|
|
.createInstance(Ci.nsIAbView);
|
|
|
|
var actualSortColumn = gAbView.setView(directory, GetAbViewListener(),
|
|
sortColumn, sortDirection);
|
|
|
|
gAbResultsTree.treeBoxObject.view =
|
|
gAbView.QueryInterface(Ci.nsITreeView);
|
|
|
|
UpdateSortIndicators(actualSortColumn, sortDirection);
|
|
|
|
// If the selected address book is LDAP and the search box is empty,
|
|
// inform the user of the empty results pane.
|
|
let abResultsTree = document.getElementById("abResultsTree");
|
|
let cardViewOuterBox = document.getElementById("CardViewOuterBox");
|
|
let blankResultsPaneMessageBox = document.getElementById("blankResultsPaneMessageBox");
|
|
if (aURI.startsWith("moz-abldapdirectory://") && !aURI.includes("?")) {
|
|
if (abResultsTree)
|
|
abResultsTree.hidden = true;
|
|
if (cardViewOuterBox)
|
|
cardViewOuterBox.hidden = true;
|
|
if (blankResultsPaneMessageBox)
|
|
blankResultsPaneMessageBox.hidden = false;
|
|
} else {
|
|
if (abResultsTree)
|
|
abResultsTree.hidden = false;
|
|
if (cardViewOuterBox)
|
|
cardViewOuterBox.hidden = false;
|
|
if (blankResultsPaneMessageBox)
|
|
blankResultsPaneMessageBox.hidden = true;
|
|
}
|
|
}
|
|
|
|
function CloseAbView()
|
|
{
|
|
if (gAbView)
|
|
gAbView.clearView();
|
|
}
|
|
|
|
function GetOneOrMoreCardsSelected()
|
|
{
|
|
return (gAbView && (gAbView.selection.getRangeCount() > 0));
|
|
}
|
|
|
|
function GetSelectedAddresses()
|
|
{
|
|
return GetAddressesForCards(GetSelectedAbCards());
|
|
}
|
|
|
|
function GetNumSelectedCards()
|
|
{
|
|
try {
|
|
return gAbView.selection.count;
|
|
}
|
|
catch (ex) {
|
|
}
|
|
|
|
// if something went wrong, return 0 for the count.
|
|
return 0;
|
|
}
|
|
|
|
function GetSelectedCardTypes()
|
|
{
|
|
var cards = GetSelectedAbCards();
|
|
if (!cards) {
|
|
Cu.reportError("ERROR: GetSelectedCardTypes: |cards| is null.");
|
|
return kNothingSelected; // no view
|
|
}
|
|
var count = cards.length;
|
|
if (count == 0)
|
|
return kNothingSelected; // nothing selected
|
|
|
|
var mailingListCnt = 0;
|
|
var cardCnt = 0;
|
|
for (let i = 0; i < count; i++) {
|
|
// We can assume no values from GetSelectedAbCards will be null.
|
|
if (cards[i].isMailList)
|
|
mailingListCnt++;
|
|
else
|
|
cardCnt++;
|
|
}
|
|
|
|
return (mailingListCnt == 0) ? kCardsOnly :
|
|
(cardCnt > 0) ? kListsAndCards :
|
|
(mailingListCnt == 1) ? kSingleListOnly :
|
|
kMultipleListsOnly;
|
|
}
|
|
|
|
// NOTE, will return -1 if more than one card selected, or no cards selected.
|
|
function GetSelectedCardIndex()
|
|
{
|
|
if (!gAbView)
|
|
return -1;
|
|
|
|
var treeSelection = gAbView.selection;
|
|
if (treeSelection.getRangeCount() == 1) {
|
|
var start = new Object;
|
|
var end = new Object;
|
|
treeSelection.getRangeAt(0, start, end);
|
|
if (start.value == end.value)
|
|
return start.value;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// NOTE, returns the card if exactly one card is selected, null otherwise
|
|
function GetSelectedCard()
|
|
{
|
|
var index = GetSelectedCardIndex();
|
|
return (index == -1) ? null : gAbView.getCardFromRow(index);
|
|
}
|
|
|
|
/**
|
|
* Return a (possibly empty) list of cards
|
|
*
|
|
* It pushes only non-null/empty element, if any, into the returned list.
|
|
*/
|
|
function GetSelectedAbCards()
|
|
{
|
|
var abView = gAbView;
|
|
|
|
// if sidebar is open, and addressbook panel is open and focused,
|
|
// then use the ab view from sidebar (gCurFrame is from sidebarOverlay.js)
|
|
if (document.getElementById("sidebar-box")) {
|
|
const abPanelUrl =
|
|
"chrome://messenger/content/addressbook/addressbook-panel.xul";
|
|
if (gCurFrame &&
|
|
gCurFrame.getAttribute("src") == abPanelUrl &&
|
|
document.commandDispatcher.focusedWindow == gCurFrame.contentDocument.defaultView)
|
|
abView = gCurFrame.contentDocument.defaultView.gAbView;
|
|
}
|
|
|
|
if (!abView)
|
|
return [];
|
|
|
|
let cards = [];
|
|
var count = abView.selection.getRangeCount();
|
|
var current = 0;
|
|
for (let i = 0; i < count; ++i) {
|
|
let start = {};
|
|
let end = {};
|
|
|
|
abView.selection.getRangeAt(i, start, end);
|
|
|
|
for (let j = start.value; j <= end.value; ++j) {
|
|
// avoid inserting null element into the list. GetRangeAt() may be buggy.
|
|
let tmp = abView.getCardFromRow(j);
|
|
if (tmp) {
|
|
cards.push(tmp);
|
|
}
|
|
}
|
|
}
|
|
return cards;
|
|
}
|
|
|
|
// XXX todo
|
|
// an optimization might be to make this return
|
|
// the selected ranges, which would be faster
|
|
// when the user does large selections, but for now, let's keep it simple.
|
|
function GetSelectedRows()
|
|
{
|
|
var selectedRows = "";
|
|
|
|
if (!gAbView)
|
|
return selectedRows;
|
|
|
|
var rangeCount = gAbView.selection.getRangeCount();
|
|
for (let i = 0; i < rangeCount; ++i) {
|
|
var start = new Object;
|
|
var end = new Object;
|
|
gAbView.selection.getRangeAt(i, start, end);
|
|
for (let j = start.value;j <= end.value; ++j) {
|
|
if (selectedRows)
|
|
selectedRows += ",";
|
|
selectedRows += j;
|
|
}
|
|
}
|
|
|
|
return selectedRows;
|
|
}
|
|
|
|
function AbSwapFirstNameLastName()
|
|
{
|
|
if (gAbView)
|
|
gAbView.swapFirstNameLastName();
|
|
}
|
|
|
|
function AbEditSelectedCard()
|
|
{
|
|
AbEditCard(GetSelectedCard());
|
|
}
|
|
|
|
function AbResultsPaneOnClick(event)
|
|
{
|
|
// we only care about button 0 (left click) events
|
|
if (event.button != 0) return;
|
|
|
|
// all we need to worry about here is double clicks
|
|
// and column header clicks.
|
|
//
|
|
// we get in here for clicks on the "treecol" (headers)
|
|
// and the "scrollbarbutton" (scrollbar buttons)
|
|
// we don't want those events to cause a "double click"
|
|
|
|
var t = event.originalTarget;
|
|
|
|
if (t.localName == "treecol") {
|
|
var sortDirection;
|
|
var currentDirection = t.getAttribute("sortDirection");
|
|
|
|
// Revert the sort order. If none is set, use Ascending.
|
|
sortDirection = currentDirection == kDefaultAscending ?
|
|
kDefaultDescending : kDefaultAscending;
|
|
|
|
SortAndUpdateIndicators(t.id, sortDirection);
|
|
}
|
|
else if (t.localName == "treechildren") {
|
|
// figure out what row the click was in
|
|
var row = gAbResultsTree.treeBoxObject.getRowAt(event.clientX,
|
|
event.clientY);
|
|
if (row == -1)
|
|
return;
|
|
|
|
if (event.detail == 2)
|
|
AbResultsPaneDoubleClick(gAbView.getCardFromRow(row));
|
|
}
|
|
}
|
|
|
|
function AbSortAscending()
|
|
{
|
|
var sortColumn = gAbResultsTree.getAttribute("sortCol");
|
|
SortAndUpdateIndicators(sortColumn, kDefaultAscending);
|
|
}
|
|
|
|
function AbSortDescending()
|
|
{
|
|
var sortColumn = gAbResultsTree.getAttribute("sortCol");
|
|
SortAndUpdateIndicators(sortColumn, kDefaultDescending);
|
|
}
|
|
|
|
function SortResultPane(sortColumn)
|
|
{
|
|
var sortDirection = kDefaultAscending;
|
|
if (gAbView)
|
|
sortDirection = gAbView.sortDirection;
|
|
|
|
SortAndUpdateIndicators(sortColumn, sortDirection);
|
|
}
|
|
|
|
function SortAndUpdateIndicators(sortColumn, sortDirection)
|
|
{
|
|
UpdateSortIndicators(sortColumn, sortDirection);
|
|
|
|
if (gAbView)
|
|
gAbView.sortBy(sortColumn, sortDirection);
|
|
}
|
|
|
|
function UpdateSortIndicators(colID, sortDirection)
|
|
{
|
|
var sortedColumn = null;
|
|
|
|
// set the sort indicator on the column we are sorted by
|
|
if (colID) {
|
|
sortedColumn = document.getElementById(colID);
|
|
if (sortedColumn) {
|
|
sortedColumn.setAttribute("sortDirection",sortDirection);
|
|
gAbResultsTree.setAttribute("sortCol", colID);
|
|
}
|
|
}
|
|
|
|
// remove the sort indicator from all the columns
|
|
// except the one we are sorted by
|
|
var currCol = gAbResultsTree.firstChild.firstChild;
|
|
while (currCol) {
|
|
if (currCol != sortedColumn && currCol.localName == "treecol")
|
|
currCol.removeAttribute("sortDirection");
|
|
currCol = currCol.nextSibling;
|
|
}
|
|
}
|
|
|
|
function InvalidateResultsPane()
|
|
{
|
|
if (gAbResultsTree)
|
|
gAbResultsTree.treeBoxObject.invalidate();
|
|
}
|
|
|
|
// Controller object for Results Pane
|
|
var ResultsPaneController =
|
|
{
|
|
supportsCommand: function(command)
|
|
{
|
|
switch (command) {
|
|
case "cmd_selectAll":
|
|
case "cmd_delete":
|
|
case "button_delete":
|
|
case "cmd_printcardpreview":
|
|
case "cmd_printcard":
|
|
case "cmd_properties":
|
|
case "cmd_newlist":
|
|
case "cmd_newCard":
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
},
|
|
|
|
isCommandEnabled: function(command)
|
|
{
|
|
switch (command) {
|
|
case "cmd_selectAll":
|
|
return true;
|
|
case "cmd_delete":
|
|
case "button_delete":
|
|
var numSelected;
|
|
var enabled = false;
|
|
if (gAbView && gAbView.selection) {
|
|
if (gAbView.directory)
|
|
enabled = !gAbView.directory.readOnly;
|
|
numSelected = gAbView.selection.count;
|
|
}
|
|
else
|
|
numSelected = 0;
|
|
|
|
// fix me, don't update on isCommandEnabled
|
|
if (command == "cmd_delete") {
|
|
switch (GetSelectedCardTypes()) {
|
|
case kSingleListOnly:
|
|
goSetMenuValue(command, "valueList");
|
|
break;
|
|
case kMultipleListsOnly:
|
|
goSetMenuValue(command, "valueLists");
|
|
break;
|
|
case kListsAndCards:
|
|
goSetMenuValue(command, "valueItems");
|
|
break;
|
|
case kCardsOnly:
|
|
default:
|
|
if (numSelected < 2)
|
|
goSetMenuValue(command, "valueCard");
|
|
else
|
|
goSetMenuValue(command, "valueCards");
|
|
break;
|
|
}
|
|
}
|
|
return (enabled && (numSelected > 0));
|
|
case "cmd_printcardpreview":
|
|
case "cmd_printcard":
|
|
return (GetNumSelectedCards() > 0);
|
|
case "cmd_properties":
|
|
// Temporary fix for SeaMonkey (see bug 1318852).
|
|
// goSetLabelAccesskeyTooltiptext() is only defined in mail/.
|
|
// This will be removed in due course and therefore the
|
|
// block wasn't indented.
|
|
if (typeof goSetLabelAccesskeyTooltiptext == "function") {
|
|
let labelAttr = "valueGeneric";
|
|
let accKeyAttr = "valueGenericAccessKey";
|
|
let tooltipTextAttr = "valueGenericTooltipText";
|
|
switch (GetSelectedCardTypes()) {
|
|
// Set cmd_properties UI according to the type of the selected item(s),
|
|
// even with multiple selections for which cmd_properties is
|
|
// not yet available and hence disabled.
|
|
case kMultipleListsOnly:
|
|
case kSingleListOnly:
|
|
labelAttr = "valueMailingList";
|
|
accKeyAttr = "valueMailingListAccessKey";
|
|
tooltipTextAttr = "valueMailingListTooltipText";
|
|
break;
|
|
case kCardsOnly:
|
|
labelAttr = "valueContact";
|
|
accKeyAttr = "valueContactAccessKey";
|
|
tooltipTextAttr = "valueContactTooltipText";
|
|
break;
|
|
case kListsAndCards:
|
|
default:
|
|
//use generic set of attributes declared above
|
|
break;
|
|
}
|
|
// This code is shared between main AB and composition's contacts sidebar.
|
|
// Note that in composition, there's no cmd_properties-button (yet);
|
|
// the resulting dump() should be ignored.
|
|
goSetLabelAccesskeyTooltiptext("cmd_properties-button", null, null,
|
|
tooltipTextAttr);
|
|
goSetLabelAccesskeyTooltiptext("cmd_properties-contextMenu",
|
|
labelAttr, accKeyAttr);
|
|
goSetLabelAccesskeyTooltiptext("cmd_properties-menu",
|
|
labelAttr, accKeyAttr);
|
|
}
|
|
// While "Edit Contact" dialogue is still modal (bug 115904, bug 135126),
|
|
// only enable "Properties" button for single selection; then fix bug 119999.
|
|
return (GetNumSelectedCards() == 1);
|
|
case "cmd_newlist":
|
|
case "cmd_newCard":
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
},
|
|
|
|
doCommand: function(command)
|
|
{
|
|
switch (command) {
|
|
case "cmd_selectAll":
|
|
if (gAbView)
|
|
gAbView.selectAll();
|
|
break;
|
|
case "cmd_delete":
|
|
case "button_delete":
|
|
AbDelete();
|
|
break;
|
|
case "cmd_properties":
|
|
AbEditSelectedCard();
|
|
break;
|
|
case "cmd_newlist":
|
|
AbNewList();
|
|
break;
|
|
case "cmd_newCard":
|
|
AbNewCard();
|
|
break;
|
|
}
|
|
},
|
|
|
|
onEvent: function(event)
|
|
{
|
|
// on blur events set the menu item texts back to the normal values
|
|
if (event == "blur")
|
|
goSetMenuValue("cmd_delete", "valueDefault");
|
|
}
|
|
};
|
|
|
|
function SelectFirstCard()
|
|
{
|
|
if (gAbView && gAbView.selection && (gAbView.selection.count > 0))
|
|
gAbView.selection.select(0);
|
|
}
|