Bug 1886417 - remove HashedArray. r=leftmostcat
Differential Revision: https://phabricator.services.mozilla.com/D205206 --HG-- extra : rebase_source : f205ecf13436076a9c409ab1c87d26b38ef530ba
This commit is contained in:
Родитель
319ed56ad7
Коммит
7dc5369bf3
|
@ -7,10 +7,12 @@
|
|||
/* import-globals-from item-editing/calendar-item-editing.js */
|
||||
/* import-globals-from widgets/mouseoverPreviews.js */
|
||||
|
||||
/* globals cal */
|
||||
var { cal } = ChromeUtils.importESModule("resource:///modules/calendar/calUtils.sys.mjs");
|
||||
|
||||
/**
|
||||
* The tree view for a CalendarTaskTree.
|
||||
*
|
||||
* @implements {nsITreeView}
|
||||
*/
|
||||
class CalendarTaskTreeView {
|
||||
/**
|
||||
|
@ -80,12 +82,12 @@ class CalendarTaskTreeView {
|
|||
* Removes an array of old items from the list, and adds an array of new items if
|
||||
* they match the currently applied filter.
|
||||
*
|
||||
* @param {object[]} newItems - An array of new items to add.
|
||||
* @param {object[]} oldItems - An array of old items to remove.
|
||||
* @param {boolean} [doNotSort] - Whether to re-sort the list after modifying it.
|
||||
* @param {boolean} [selectNew] - Whether to select the new tasks.
|
||||
* @param {calIItemBase[]} newItems - An array of new items to add.
|
||||
* @param {calIItemBase[]} oldItems - An array of old items to remove.
|
||||
* @param {boolean} [doNotSort=false] - Whether to re-sort the list after modifying it.
|
||||
* @param {boolean} [selectNew=false] - Whether to select the new tasks.
|
||||
*/
|
||||
modifyItems(newItems = [], oldItems = [], doNotSort, selectNew) {
|
||||
modifyItems(newItems, oldItems, doNotSort = false, selectNew = false) {
|
||||
const selItem = this.tree.currentTask;
|
||||
let selIndex = this.tree.currentIndex;
|
||||
let firstHash = null;
|
||||
|
@ -93,16 +95,10 @@ class CalendarTaskTreeView {
|
|||
|
||||
this.tree.beginUpdateBatch();
|
||||
|
||||
const idiff = new cal.item.ItemDiff();
|
||||
idiff.load(oldItems);
|
||||
idiff.difference(newItems);
|
||||
idiff.complete();
|
||||
const delItems = idiff.deletedItems;
|
||||
const addItems = idiff.addedItems;
|
||||
const modItems = idiff.modifiedItems;
|
||||
const { deletedItems, addedItems, modifiedItems } = cal.item.interDiff(oldItems, newItems);
|
||||
|
||||
// Find the indexes of the old items that need to be removed.
|
||||
for (const item of delItems.mArray) {
|
||||
for (const item of deletedItems) {
|
||||
if (item.hashId in this.tree.mHash2Index) {
|
||||
// The old item needs to be removed.
|
||||
remIndexes.push(this.tree.mHash2Index[item.hashId]);
|
||||
|
@ -111,7 +107,7 @@ class CalendarTaskTreeView {
|
|||
}
|
||||
|
||||
// Modified items need to be updated.
|
||||
for (const item of modItems.mArray) {
|
||||
for (const item of modifiedItems) {
|
||||
if (item.hashId in this.tree.mHash2Index) {
|
||||
// Make sure we're using the new version of a modified item.
|
||||
this.tree.mTaskArray[this.tree.mHash2Index[item.hashId]] = item;
|
||||
|
@ -127,7 +123,7 @@ class CalendarTaskTreeView {
|
|||
});
|
||||
|
||||
// Add the new items.
|
||||
for (const item of addItems.mArray) {
|
||||
for (const item of addedItems) {
|
||||
if (!(item.hashId in this.tree.mHash2Index)) {
|
||||
const index = this.tree.mTaskArray.length;
|
||||
this.tree.mTaskArray.push(item);
|
||||
|
@ -194,7 +190,7 @@ class CalendarTaskTreeView {
|
|||
* @param {Event} event - An event.
|
||||
* @param {object} [col] - A column object.
|
||||
* @param {object} [row] - A row object.
|
||||
* @returns {object | false} The task object related to the event or false if none found.
|
||||
* @returns {?calITodo} the task object related to the event, if any.
|
||||
*/
|
||||
getItemFromEvent(event, col, row) {
|
||||
const { col: eventColumn, row: eventRow } = this.tree.getCellAt(event.clientX, event.clientY);
|
||||
|
@ -204,7 +200,7 @@ class CalendarTaskTreeView {
|
|||
if (row) {
|
||||
row.value = eventRow;
|
||||
}
|
||||
return eventRow > -1 && this.tree.mTaskArray[eventRow];
|
||||
return eventRow > -1 ? this.tree.mTaskArray[eventRow] : null;
|
||||
}
|
||||
|
||||
// nsITreeView Methods and Properties
|
||||
|
@ -353,9 +349,10 @@ class CalendarTaskTreeView {
|
|||
}
|
||||
|
||||
/**
|
||||
* Called to link the task tree to the tree view. A null argument un-sets/un-links the tree.
|
||||
* Called to link the task tree to the tree view.
|
||||
* A null argument un-sets/un-links the tree.
|
||||
*
|
||||
* @param {object | null} tree
|
||||
* @param {?XULTreeElement} tree
|
||||
*/
|
||||
setTree(tree) {
|
||||
const hasOldTree = this.tree != null;
|
||||
|
@ -484,8 +481,8 @@ class CalendarTaskTreeView {
|
|||
/**
|
||||
* Format a datetime object for display.
|
||||
*
|
||||
* @param {object} dateTime - From a todo object, not a JavaScript date.
|
||||
* @returns {string} Formatted string version of the datetime ("" if invalid).
|
||||
* @param {calIDateTime} dateTime - Datetime, from a calITodo object.
|
||||
* @returns {string} a formatted string version of the datetime ("" if invalid).
|
||||
*/
|
||||
_formatDateTime(dateTime) {
|
||||
return dateTime && dateTime.isValid
|
||||
|
|
|
@ -463,7 +463,7 @@
|
|||
* Return the task object related to a given event.
|
||||
*
|
||||
* @param {Event} event - The event.
|
||||
* @returns {object | false} The task object related to the event or false if none found.
|
||||
* @returns {?calITodo} the task object related to the event, if any.
|
||||
*/
|
||||
getTaskFromEvent(event) {
|
||||
return this.mTreeView.getItemFromEvent(event);
|
||||
|
|
|
@ -1,258 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
import { data } from "resource:///modules/calendar/utils/calDataUtils.sys.mjs";
|
||||
|
||||
/**
|
||||
* An unsorted array of hashable items with some extra functions to quickly
|
||||
* retrieve the item by its hash id.
|
||||
*
|
||||
* Performance Considerations:
|
||||
* - Accessing items is fast
|
||||
* - Adding items is fast (they are added to the end)
|
||||
* - Deleting items is O(n)
|
||||
* - Modifying items is fast.
|
||||
*/
|
||||
export const HashedArray = function () {
|
||||
this.clear();
|
||||
}; // even though it's defined in calUtils.sys.mjs, import needs this
|
||||
|
||||
HashedArray.prototype = {
|
||||
mArray: null,
|
||||
mHash: null,
|
||||
|
||||
mBatch: 0,
|
||||
mFirstDirty: -1,
|
||||
|
||||
/**
|
||||
* Returns a copy of the internal array. Note this is a shallow copy.
|
||||
*/
|
||||
get arrayCopy() {
|
||||
return this.mArray.concat([]);
|
||||
},
|
||||
|
||||
/**
|
||||
* The function to retrieve the hashId given the item. This function can be
|
||||
* overridden by implementations, in case the added items are not instances
|
||||
* of calIItemBase.
|
||||
*
|
||||
* @param item The item to get the hashId for
|
||||
* @returns The hashId of the item
|
||||
*/
|
||||
hashAccessor(item) {
|
||||
return item.hashId;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the item, given its index in the array
|
||||
*
|
||||
* @param index The index of the item to retrieve.
|
||||
* @returns The retrieved item.
|
||||
*/
|
||||
itemByIndex(index) {
|
||||
return this.mArray[index];
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the item, given its hashId
|
||||
*
|
||||
* @param id The hashId of the item to retrieve.
|
||||
* @returns The retrieved item.
|
||||
*/
|
||||
itemById(id) {
|
||||
if (this.mBatch > 0) {
|
||||
throw new Error("Accessing Array by ID not supported in batch mode");
|
||||
}
|
||||
return id in this.mHash ? this.mArray[this.mHash[id]] : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the index of the given item. This function is cheap performance
|
||||
* wise, since it uses the hash
|
||||
*
|
||||
* @param item The item to search for.
|
||||
* @returns The index of the item.
|
||||
*/
|
||||
indexOf(item) {
|
||||
if (this.mBatch > 0) {
|
||||
throw new Error("Accessing Array Indexes not supported in batch mode");
|
||||
}
|
||||
const hashId = this.hashAccessor(item);
|
||||
return hashId in this.mHash ? this.mHash[hashId] : -1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove the item with the given hashId.
|
||||
*
|
||||
* @param id The id of the item to be removed
|
||||
*/
|
||||
removeById(id) {
|
||||
if (this.mBatch > 0) {
|
||||
throw new Error("Remvoing by ID in batch mode is not supported"); /* TODO */
|
||||
}
|
||||
const index = this.mHash[id];
|
||||
delete this.mHash[id];
|
||||
this.mArray.splice(index, 1);
|
||||
this.reindex(index);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove the item at the given index.
|
||||
*
|
||||
* @param index The index of the item to remove.
|
||||
*/
|
||||
removeByIndex(index) {
|
||||
delete this.mHash[this.hashAccessor(this.mArray[index])];
|
||||
this.mArray.splice(index, 1);
|
||||
this.reindex(index);
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear the whole array, removing all items. This also resets batch mode.
|
||||
*/
|
||||
clear() {
|
||||
this.mHash = {};
|
||||
this.mArray = [];
|
||||
this.mFirstDirty = -1;
|
||||
this.mBatch = 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add the item to the array
|
||||
*
|
||||
* @param item The item to add.
|
||||
* @returns The index of the added item.
|
||||
*/
|
||||
addItem(item) {
|
||||
const index = this.mArray.length;
|
||||
this.mArray.push(item);
|
||||
this.reindex(index);
|
||||
return index;
|
||||
},
|
||||
|
||||
/**
|
||||
* Modifies the item in the array. If the item is already in the array, then
|
||||
* it is replaced by the passed item. Otherwise, the item is added to the
|
||||
* array.
|
||||
*
|
||||
* @param item The item to modify.
|
||||
* @returns The (new) index.
|
||||
*/
|
||||
modifyItem(item) {
|
||||
const hashId = this.hashAccessor(item);
|
||||
if (hashId in this.mHash) {
|
||||
const index = this.mHash[this.hashAccessor(item)];
|
||||
this.mArray[index] = item;
|
||||
return index;
|
||||
}
|
||||
return this.addItem(item);
|
||||
},
|
||||
|
||||
/**
|
||||
* Reindexes the items in the array. This function is mostly used
|
||||
* internally. All parameters are inclusive. The ranges are automatically
|
||||
* swapped if from > to.
|
||||
*
|
||||
* @param from (optional) The index to start indexing from. If left
|
||||
* out, defaults to 0.
|
||||
* @param to (optional) The index to end indexing on. If left out,
|
||||
* defaults to the array length.
|
||||
*/
|
||||
reindex(from, to) {
|
||||
if (this.mArray.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
from = from === undefined ? 0 : from;
|
||||
to = to === undefined ? this.mArray.length - 1 : to;
|
||||
|
||||
from = Math.min(this.mArray.length - 1, Math.max(0, from));
|
||||
to = Math.min(this.mArray.length - 1, Math.max(0, to));
|
||||
|
||||
if (from > to) {
|
||||
const tmp = from;
|
||||
from = to;
|
||||
to = tmp;
|
||||
}
|
||||
|
||||
if (this.mBatch > 0) {
|
||||
// No indexing in batch mode, but remember from where to index.
|
||||
this.mFirstDirty = Math.min(Math.max(0, this.mFirstDirty), from);
|
||||
return;
|
||||
}
|
||||
|
||||
for (let idx = from; idx <= to; idx++) {
|
||||
this.mHash[this.hashAccessor(this.mArray[idx])] = idx;
|
||||
}
|
||||
},
|
||||
|
||||
startBatch() {
|
||||
this.mBatch++;
|
||||
},
|
||||
|
||||
endBatch() {
|
||||
this.mBatch = Math.max(0, this.mBatch - 1);
|
||||
|
||||
if (this.mBatch == 0 && this.mFirstDirty > -1) {
|
||||
this.reindex(this.mFirstDirty);
|
||||
this.mFirstDirty = -1;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Iterator to allow iterating the hashed array object.
|
||||
*/
|
||||
*[Symbol.iterator]() {
|
||||
yield* this.mArray;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* Sorted hashed array. The array always stays sorted.
|
||||
* TODO: unused outside of tests. Remove?
|
||||
*
|
||||
* Performance Considerations:
|
||||
* - Accessing items is fast
|
||||
* - Adding and deleting items is O(n)
|
||||
* - Modifying items is fast.
|
||||
*/
|
||||
export const SortedHashedArray = function (comparator) {
|
||||
HashedArray.apply(this, arguments);
|
||||
if (!comparator) {
|
||||
throw new Error("Sorted Hashed Array needs a comparator");
|
||||
}
|
||||
this.mCompFunc = comparator;
|
||||
};
|
||||
|
||||
SortedHashedArray.prototype = {
|
||||
__proto__: HashedArray.prototype,
|
||||
|
||||
mCompFunc: null,
|
||||
|
||||
addItem(item) {
|
||||
const newIndex = data.binaryInsert(this.mArray, item, this.mCompFunc, false);
|
||||
this.reindex(newIndex);
|
||||
return newIndex;
|
||||
},
|
||||
|
||||
modifyItem(item) {
|
||||
const hashId = this.hashAccessor(item);
|
||||
if (hashId in this.mHash) {
|
||||
const cmp = this.mCompFunc(item, this.mArray[this.mHash[hashId]]);
|
||||
if (cmp == 0) {
|
||||
// The item will be at the same index, we just need to replace it
|
||||
this.mArray[this.mHash[hashId]] = item;
|
||||
return this.mHash[hashId];
|
||||
}
|
||||
const oldIndex = this.mHash[hashId];
|
||||
|
||||
const newIndex = data.binaryInsert(this.mArray, item, this.mCompFunc, false);
|
||||
this.mArray.splice(oldIndex, 1);
|
||||
this.reindex(oldIndex, newIndex);
|
||||
return newIndex;
|
||||
}
|
||||
return this.addItem(item);
|
||||
},
|
||||
};
|
|
@ -29,7 +29,6 @@ EXTRA_JS_MODULES.calendar.utils += [
|
|||
EXTRA_JS_MODULES.calendar += [
|
||||
"calCalendarDeactivator.sys.mjs",
|
||||
"calExtract.sys.mjs",
|
||||
"calHashedArray.sys.mjs",
|
||||
"calRecurrenceUtils.sys.mjs",
|
||||
"calUtils.sys.mjs",
|
||||
"Ical.sys.mjs",
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
* 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/. */
|
||||
|
||||
import { HashedArray } from "resource:///modules/calendar/calHashedArray.sys.mjs";
|
||||
import { iterate } from "resource:///modules/calendar/utils/calIteratorUtils.sys.mjs";
|
||||
import { data } from "resource:///modules/calendar/utils/calDataUtils.sys.mjs";
|
||||
import { dtz } from "resource:///modules/calendar/utils/calDateTimeUtils.sys.mjs";
|
||||
|
@ -15,174 +14,39 @@ import { dtz } from "resource:///modules/calendar/utils/calDateTimeUtils.sys.mjs
|
|||
// including calUtils.sys.mjs under the cal.item namespace.
|
||||
|
||||
export var item = {
|
||||
ItemDiff: (function () {
|
||||
/**
|
||||
* Given two sets of items, find out which items were added, changed or
|
||||
* removed.
|
||||
*
|
||||
* The general flow is to first use load method to load the engine with
|
||||
* the first set of items, then use difference to load the set of
|
||||
* items to diff against. Afterwards, call the complete method to tell the
|
||||
* engine that no more items are coming.
|
||||
*
|
||||
* You can then access the mAddedItems/mModifiedItems/mDeletedItems attributes to
|
||||
* get the items that were changed during the process.
|
||||
*/
|
||||
function ItemDiff() {
|
||||
this.reset();
|
||||
/**
|
||||
* Calculcate difference between items.
|
||||
*
|
||||
* @param {calIItemBase[]} oldItems - Old items.
|
||||
* @param {calIItemBase[]} newItems - New items.
|
||||
* @returns {object} interdiff
|
||||
* @returns {calIItemBase[]} interdiff.deletedItems
|
||||
* @returns {calIItemBase[]} interdiff.addedItems
|
||||
* @returns {calIItemBase[]} interdiff.modifiedItems
|
||||
*/
|
||||
interDiff(oldItems, newItems) {
|
||||
const addedItems = [];
|
||||
const modifiedItems = [];
|
||||
|
||||
const initialItems = new Map(oldItems.map(item => [item.hashId, item]));
|
||||
for (const item of newItems) {
|
||||
if (initialItems.has(item.hashId)) {
|
||||
modifiedItems.push(item);
|
||||
} else {
|
||||
addedItems.push(item);
|
||||
}
|
||||
initialItems.delete(item.hashId);
|
||||
}
|
||||
|
||||
ItemDiff.prototype = {
|
||||
STATE_INITIAL: 1,
|
||||
STATE_LOADING: 2,
|
||||
STATE_DIFFERING: 4,
|
||||
STATE_COMPLETED: 8,
|
||||
|
||||
state: 1,
|
||||
mInitialItems: null,
|
||||
|
||||
mModifiedItems: null,
|
||||
mModifiedOldItems: null,
|
||||
mAddedItems: null,
|
||||
mDeletedItems: null,
|
||||
|
||||
/**
|
||||
* Expect the difference engine to be in the given state.
|
||||
*
|
||||
* @param aState The state to be in
|
||||
* @param aMethod The method name expecting the state
|
||||
*/
|
||||
_expectState(aState, aMethod) {
|
||||
if ((this.state & aState) == 0) {
|
||||
throw new Error(
|
||||
"ItemDiff method " + aMethod + " called while in unexpected state " + this.state
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads an array of items. This step cannot be executed
|
||||
* after calling the difference methods.
|
||||
*
|
||||
* @param items The array of items to load
|
||||
*/
|
||||
load(items) {
|
||||
this._expectState(this.STATE_INITIAL | this.STATE_LOADING, "load");
|
||||
|
||||
for (const calendarItem of items) {
|
||||
this.mInitialItems[calendarItem.hashId] = calendarItem;
|
||||
}
|
||||
|
||||
this.state = this.STATE_LOADING;
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculate the difference for the array of items. This method should be
|
||||
* called after all load methods and before the complete method.
|
||||
*
|
||||
* @param items The array of items to calculate difference with
|
||||
*/
|
||||
difference(items) {
|
||||
this._expectState(
|
||||
this.STATE_INITIAL | this.STATE_LOADING | this.STATE_DIFFERING,
|
||||
"difference"
|
||||
);
|
||||
|
||||
this.mModifiedOldItems.startBatch();
|
||||
this.mModifiedItems.startBatch();
|
||||
this.mAddedItems.startBatch();
|
||||
|
||||
for (const calendarItem of items) {
|
||||
if (calendarItem.hashId in this.mInitialItems) {
|
||||
const oldItem = this.mInitialItems[calendarItem.hashId];
|
||||
this.mModifiedOldItems.addItem(oldItem);
|
||||
this.mModifiedItems.addItem(calendarItem);
|
||||
} else {
|
||||
this.mAddedItems.addItem(calendarItem);
|
||||
}
|
||||
delete this.mInitialItems[calendarItem.hashId];
|
||||
}
|
||||
|
||||
this.mModifiedOldItems.endBatch();
|
||||
this.mModifiedItems.endBatch();
|
||||
this.mAddedItems.endBatch();
|
||||
|
||||
this.state = this.STATE_DIFFERING;
|
||||
},
|
||||
|
||||
/**
|
||||
* Tell the engine that all load and difference calls have been made, this
|
||||
* makes sure that all item states are correctly returned.
|
||||
*/
|
||||
complete() {
|
||||
this._expectState(
|
||||
this.STATE_INITIAL | this.STATE_LOADING | this.STATE_DIFFERING,
|
||||
"complete"
|
||||
);
|
||||
|
||||
this.mDeletedItems.startBatch();
|
||||
|
||||
for (const hashId in this.mInitialItems) {
|
||||
const calendarItem = this.mInitialItems[hashId];
|
||||
this.mDeletedItems.addItem(calendarItem);
|
||||
}
|
||||
|
||||
this.mDeletedItems.endBatch();
|
||||
this.mInitialItems = {};
|
||||
|
||||
this.state = this.STATE_COMPLETED;
|
||||
},
|
||||
|
||||
/** @returns a HashedArray containing the new version of the modified items */
|
||||
get modifiedItems() {
|
||||
this._expectState(this.STATE_COMPLETED, "get modifiedItems");
|
||||
return this.mModifiedItems;
|
||||
},
|
||||
|
||||
/** @returns a HashedArray containing the old version of the modified items */
|
||||
get modifiedOldItems() {
|
||||
this._expectState(this.STATE_COMPLETED, "get modifiedOldItems");
|
||||
return this.mModifiedOldItems;
|
||||
},
|
||||
|
||||
/** @returns a HashedArray containing added items */
|
||||
get addedItems() {
|
||||
this._expectState(this.STATE_COMPLETED, "get addedItems");
|
||||
return this.mAddedItems;
|
||||
},
|
||||
|
||||
/** @returns a HashedArray containing deleted items */
|
||||
get deletedItems() {
|
||||
this._expectState(this.STATE_COMPLETED, "get deletedItems");
|
||||
return this.mDeletedItems;
|
||||
},
|
||||
|
||||
/** @returns the number of loaded items */
|
||||
get count() {
|
||||
return Object.keys(this.mInitialItems).length;
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets the difference engine to its initial state.
|
||||
*/
|
||||
reset() {
|
||||
this.mInitialItems = {};
|
||||
this.mModifiedItems = new HashedArray();
|
||||
this.mModifiedOldItems = new HashedArray();
|
||||
this.mAddedItems = new HashedArray();
|
||||
this.mDeletedItems = new HashedArray();
|
||||
this.state = this.STATE_INITIAL;
|
||||
},
|
||||
};
|
||||
return ItemDiff;
|
||||
})(),
|
||||
const deletedItems = [...initialItems.values()];
|
||||
return { deletedItems, addedItems, modifiedItems };
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if an item is supported by a Calendar.
|
||||
*
|
||||
* @param aCalendar the calendar
|
||||
* @param aItem the item either a task or an event
|
||||
* @returns true or false
|
||||
* @param {calICalendar} aCalendar - The calendar to check.
|
||||
* @param {calIItemBase} aItem - The item; either a task or an event.
|
||||
* @returns {boolean} true if supported.
|
||||
*/
|
||||
isItemSupported(aItem, aCalendar) {
|
||||
if (aItem.isTodo()) {
|
||||
|
@ -196,7 +60,7 @@ export var item = {
|
|||
/*
|
||||
* Checks whether a calendar supports events
|
||||
*
|
||||
* @param aCalendar
|
||||
* @param {calICalendar} aCalendar
|
||||
*/
|
||||
isEventCalendar(aCalendar) {
|
||||
return aCalendar.getProperty("capabilities.events.supported") !== false;
|
||||
|
|
|
@ -1,209 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
var { cal } = ChromeUtils.importESModule("resource:///modules/calendar/calUtils.sys.mjs");
|
||||
ChromeUtils.defineESModuleGetters(this, {
|
||||
CalEvent: "resource:///modules/CalEvent.sys.mjs",
|
||||
HashedArray: "resource:///modules/calendar/calHashedArray.sys.mjs",
|
||||
SortedHashedArray: "resource:///modules/calendar/calHashedArray.sys.mjs",
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
test_array_base();
|
||||
test_array_sorted();
|
||||
test_hashAccessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create an item that has a sensible hash id, with the given
|
||||
* title identification.
|
||||
*
|
||||
* @param ident The title to identify the item.
|
||||
* @returns The created item.
|
||||
*/
|
||||
function hashedCreateItem(ident) {
|
||||
const item = new CalEvent();
|
||||
item.calendar = { id: "test" };
|
||||
item.id = cal.getUUID();
|
||||
item.title = ident;
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparator function to sort the items by their title
|
||||
*
|
||||
* @param a Object to compare.
|
||||
* @param b Object to compare with.
|
||||
* @returns 0, -1, or 1 (usual comptor meanings)
|
||||
*/
|
||||
function titleComptor(a, b) {
|
||||
if (a.title > b.title) {
|
||||
return 1;
|
||||
} else if (a.title < b.title) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the hashed array accessor functions work for the status of the
|
||||
* items array.
|
||||
*
|
||||
* @param har The Hashed Array
|
||||
* @param testItems The array of test items
|
||||
* @param itemAccessor The accessor func to retrieve the items
|
||||
* @throws Exception If the arrays are not the same.
|
||||
*/
|
||||
function checkConsistancy(har, testItems, itemAccessor) {
|
||||
itemAccessor =
|
||||
itemAccessor ||
|
||||
function (item) {
|
||||
return item;
|
||||
};
|
||||
for (const idx in testItems) {
|
||||
const testItem = itemAccessor(testItems[idx]);
|
||||
equal(itemAccessor(har.itemByIndex(idx)).title, testItem.title);
|
||||
equal(itemAccessor(har.itemById(testItem.hashId)).title, testItem.title);
|
||||
equal(har.indexOf(testItems[idx]), idx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Man, this function is really hard to keep general enough, I'm almost tempted
|
||||
* to duplicate the code. It checks if the remove and modify operations work for
|
||||
* the given hashed array.
|
||||
*
|
||||
* @param har The Hashed Array
|
||||
* @param testItems The js array with the items
|
||||
* @param postprocessFunc (optional) The function to call after each
|
||||
* operation, but before checking consistency.
|
||||
* @param itemAccessor (optional) The function to access the item for an
|
||||
* array element.
|
||||
* @param itemCreator (optional) Function to create a new item for the
|
||||
* array.
|
||||
*/
|
||||
function testRemoveModify(har, testItems, postprocessFunc, itemAccessor, itemCreator) {
|
||||
postprocessFunc =
|
||||
postprocessFunc ||
|
||||
function (a, b) {
|
||||
return [a, b];
|
||||
};
|
||||
itemCreator = itemCreator || (title => hashedCreateItem(title));
|
||||
itemAccessor =
|
||||
itemAccessor ||
|
||||
function (item) {
|
||||
return item;
|
||||
};
|
||||
|
||||
// Now, delete the second item and check again
|
||||
har.removeById(itemAccessor(testItems[1]).hashId);
|
||||
testItems.splice(1, 1);
|
||||
[har, testItems] = postprocessFunc(har, testItems);
|
||||
|
||||
checkConsistancy(har, testItems, itemAccessor);
|
||||
|
||||
// Try the same by index
|
||||
har.removeByIndex(2);
|
||||
testItems.splice(2, 1);
|
||||
[har, testItems] = postprocessFunc(har, testItems);
|
||||
checkConsistancy(har, testItems, itemAccessor);
|
||||
|
||||
// Try modifying an item
|
||||
const newInstance = itemCreator("z-changed");
|
||||
itemAccessor(newInstance).id = itemAccessor(testItems[0]).id;
|
||||
testItems[0] = newInstance;
|
||||
har.modifyItem(newInstance);
|
||||
[har, testItems] = postprocessFunc(har, testItems);
|
||||
checkConsistancy(har, testItems, itemAccessor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the basic HashedArray
|
||||
*/
|
||||
function test_array_base() {
|
||||
let har, testItems;
|
||||
|
||||
// Test normal additions
|
||||
har = new HashedArray();
|
||||
testItems = ["a", "b", "c", "d"].map(hashedCreateItem);
|
||||
|
||||
testItems.forEach(har.addItem, har);
|
||||
checkConsistancy(har, testItems);
|
||||
testRemoveModify(har, testItems);
|
||||
|
||||
// Test adding in batch mode
|
||||
har = new HashedArray();
|
||||
testItems = ["e", "f", "g", "h"].map(hashedCreateItem);
|
||||
har.startBatch();
|
||||
testItems.forEach(har.addItem, har);
|
||||
har.endBatch();
|
||||
checkConsistancy(har, testItems);
|
||||
testRemoveModify(har, testItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the sorted SortedHashedArray
|
||||
*/
|
||||
function test_array_sorted() {
|
||||
let har, testItems, testItemsSorted;
|
||||
|
||||
function sortedPostProcess(harParam, tiParam) {
|
||||
tiParam = tiParam.sort(titleComptor);
|
||||
return [harParam, tiParam];
|
||||
}
|
||||
|
||||
// Test normal additions
|
||||
har = new SortedHashedArray(titleComptor);
|
||||
testItems = ["d", "c", "a", "b"].map(hashedCreateItem);
|
||||
testItemsSorted = testItems.sort(titleComptor);
|
||||
|
||||
testItems.forEach(har.addItem, har);
|
||||
checkConsistancy(har, testItemsSorted);
|
||||
testRemoveModify(har, testItemsSorted, sortedPostProcess);
|
||||
|
||||
// Test adding in batch mode
|
||||
har = new SortedHashedArray(titleComptor);
|
||||
testItems = ["e", "f", "g", "h"].map(hashedCreateItem);
|
||||
testItemsSorted = testItems.sort(titleComptor);
|
||||
har.startBatch();
|
||||
testItems.forEach(har.addItem, har);
|
||||
har.endBatch();
|
||||
checkConsistancy(har, testItemsSorted);
|
||||
testRemoveModify(har, testItemsSorted, sortedPostProcess);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests SortedHashedArray with a custom hashAccessor.
|
||||
*/
|
||||
function test_hashAccessor() {
|
||||
const comptor = (a, b) => titleComptor(a.item, b.item);
|
||||
|
||||
const har = new SortedHashedArray(comptor);
|
||||
har.hashAccessor = function (obj) {
|
||||
return obj.item.hashId;
|
||||
};
|
||||
|
||||
function itemAccessor(obj) {
|
||||
if (!obj) {
|
||||
do_throw("WTF?");
|
||||
}
|
||||
return obj.item;
|
||||
}
|
||||
|
||||
function itemCreator(title) {
|
||||
return { item: hashedCreateItem(title) };
|
||||
}
|
||||
|
||||
function sortedPostProcess(harParam, tiParam) {
|
||||
tiParam = tiParam.sort(comptor);
|
||||
return [harParam, tiParam];
|
||||
}
|
||||
|
||||
const testItems = ["d", "c", "a", "b"].map(itemCreator);
|
||||
|
||||
const testItemsSorted = testItems.sort(comptor);
|
||||
testItems.forEach(har.addItem, har);
|
||||
checkConsistancy(har, testItemsSorted, itemAccessor);
|
||||
testRemoveModify(har, testItemsSorted, sortedPostProcess, itemAccessor, itemCreator);
|
||||
}
|
|
@ -52,7 +52,6 @@ tags = oauth
|
|||
[test_filter_tree_view.js]
|
||||
[test_freebusy.js]
|
||||
[test_freebusy_service.js]
|
||||
[test_hashedarray.js]
|
||||
[test_ics.js]
|
||||
[test_ics_parser.js]
|
||||
[test_ics_service.js]
|
||||
|
|
|
@ -282,7 +282,6 @@
|
|||
"resource:///modules/calendar/Ical.jsm": "comm/calendar/base/modules/Ical.jsm",
|
||||
"resource:///modules/calendar/calCalendarDeactivator.jsm": "comm/calendar/base/modules/calCalendarDeactivator.jsm",
|
||||
"resource:///modules/calendar/calExtract.jsm": "comm/calendar/base/modules/calExtract.jsm",
|
||||
"resource:///modules/calendar/calHashedArray.jsm": "comm/calendar/base/modules/calHashedArray.jsm",
|
||||
"resource:///modules/calendar/calRecurrenceUtils.jsm": "comm/calendar/base/modules/calRecurrenceUtils.jsm",
|
||||
"resource:///modules/calendar/calStorageHelpers.jsm": "comm/calendar/providers/storage/calStorageHelpers.jsm",
|
||||
"resource:///modules/calendar/calStorageUpgrade.jsm": "comm/calendar/providers/storage/calStorageUpgrade.jsm",
|
||||
|
|
Загрузка…
Ссылка в новой задаче