--- a/calendar/base/content/calendar-task-tree.xml
+++ b/calendar/base/content/calendar-task-tree.xml
@@ -140,17 +140,17 @@
<implementation>
<field name="mLoadCount">0</field>
<field name="mTaskArray">[]</field>
<field name="mHash2Index"><![CDATA[({})]]></field>
<field name="mRefreshQueue">[]</field>
<field name="mPendingRefresh">null</field>
<field name="mShowCompletedTasks">true</field>
- <field name="mFilterFunction">null</field>
+ <field name="mFilter">null</field>
<field name="mStartDate">null</field>
<field name="mEndDate">null</field>
<property name="currentIndex">
<getter><![CDATA[
var tree = document.getAnonymousElementByAttribute(
this, "anonid", "calendar-task-tree");
return tree.currentIndex;
@@ -193,44 +193,16 @@
<getter><![CDATA[
return this.mShowCompletedTasks;
]]></getter>
<setter><![CDATA[
this.mShowCompletedTasks = val;
return val;
]]></setter>
</property>
-
- <property name="filterFunction">
- <setter><![CDATA[
- this.mFilterFunction = val;
- return val;
- ]]></setter>
- </property>
-
- <property name="startDate">
- <getter><![CDATA[
- return this.mStartDate;
- ]]></getter>
- <setter><![CDATA[
- this.mStartDate = val;
- return val;
- ]]></setter>
- </property>
-
- <property name="endDate">
- <getter><![CDATA[
- return this.mEndDate;
- ]]></getter>
- <setter><![CDATA[
- this.mEndDate = val;
- return val;
- ]]></setter>
- </property>
-
<method name="duration">
<parameter name="aTask"/>
<body><![CDATA[
if (aTask && aTask.dueDate && aTask.dueDate.isValid){
var dur = aTask.dueDate.subtractDate(now());
if (!dur.isNegative) {
// TODO MOZILLA_1_8_BRANCH We should use Pluralform.jsm on
// trunk here to provide better localization. Use different
@@ -351,18 +323,17 @@
modifyItem: function tTV_modifyItem(aNewItem, aOldItem, aDontSort) {
var index = this.binding.mHash2Index[aOldItem.hashId];
if (index != undefined) {
// if a filter is installed we need to make sure that
// the item still belongs to the set of valid items before
// moving forward. if the filter cuts this item off, we
// need to act accordingly.
- if (this.binding.mFilterFunction &&
- !this.binding.mFilterFunction(aNewItem)) {
+ if (!this.binding.mFilter.isItemInFilters(aNewItem)) {
this.removeItem(aNewItem);
return;
}
// same holds true for the completed filter, which is
// currently modeled as an explicit boolean.
if (aNewItem.isCompleted != aOldItem.isCompleted) {
if (aNewItem.isCompleted && !this.binding.showCompleted) {
this.removeItem(aNewItem);
@@ -750,18 +721,20 @@
onLoad: function tTO_onLoad() {
if (!this.mInBatch) {
this.binding.refresh();
}
},
onAddItem: function tTO_onAddItem(aItem) {
- // XXX: we have to use a filter here
- if (isToDo(aItem) && !this.mInBatch) {
+ // XXX: We have to filter here
+ if (isToDo(aItem) &&
+ !this.mInBatch &&
+ this.binding.mFilter.isItemInFilters(aItem)) {
this.binding.mTreeView.addItem(aItem);
}
},
onModifyItem: function tTO_onModifyItem(aNewItem, aOldItem) {
if ((isToDo(aNewItem) || isToDo(aOldItem)) &&
!this.mInBatch) {
@@ -866,16 +839,19 @@
}
}
]]></constructor>
<method name="onLoad">
<body><![CDATA[
if (!this.mLoadCount++) {
+ // set up the tree filter
+ this.mFilter = new calFilter();
+
// set up the custom tree view
var tree = document.getAnonymousElementByAttribute(this, "anonid", "calendar-task-tree");
this.mTreeView.tree = tree;
tree.view = this.mTreeView;
// set up our calendar event observer
var composite = getCompositeCalendar();
composite.addObserver(this.mTaskTreeObserver);
@@ -921,46 +897,50 @@
}
]]></body>
</method>
<!-- Called by event observers to update the display -->
<method name="refresh">
<parameter name="aFilter"/>
<body><![CDATA[
+ // XXX: why do I need this ?
+ if(!this.mFilter) {
+ this.mFilter = new calFilter();
+ }
var savedThis = this;
var refreshListener = {
mTaskArray: [],
onOperationComplete: function(aCalendar, aStatus, aOperationType, aId, aDateTime) {
- savedThis.mTaskArray = this.mTaskArray;
+ savedThis.mTaskArray = this.mTaskArray;
savedThis.onOperationComplete();
},
onGetResult: function(aCalendar, aStatus, aItemType, aDetail, aCount, aItems) {
- for (var i=0; i<aCount; i++) {
- if (!savedThis.mFilterFunction ||
- savedThis.mFilterFunction(aItems[i])) {
+ for (var i=0; i<aCount; i++) {
+ if (savedThis.mFilter.isItemInFilters(aItems[i])) {
refreshListener.mTaskArray.push(aItems[i]);
}
}
}
};
var refreshJob = {
execute: function() {
var composite = getCompositeCalendar();
var filter = aFilter || !savedThis.mShowCompletedTasks ?
composite.ITEM_FILTER_COMPLETED_NO :
composite.ITEM_FILTER_COMPLETED_ALL;
filter |= composite.ITEM_FILTER_TYPE_TODO;
- if (savedThis.startDate && savedThis.endDate) {
+
+ if (savedThis.mFilter.startDate && savedThis.mFilter.endDate) {
filter |= composite.ITEM_FILTER_CLASS_OCCURRENCES;
}
- composite.getItems(filter, 0, savedThis.startDate, savedThis.endDate, refreshListener);
+ composite.getItems(filter, 0, savedThis.mFilter.startDate, savedThis.mFilter.endDate, refreshListener);
}
};
this.mRefreshQueue.push(refreshJob);
this.popRefreshQueue();
]]></body>
</method>
<method name="onCalendarAdded">
@@ -970,35 +950,34 @@
var savedThis = this;
var refreshListener = {
onOperationComplete: function (aCalendar, aStatus, aOperationType, aId, aDateTime) {
savedThis.onOperationComplete();
},
onGetResult: function (aCalendar, aStatus, aItemType, aDetail, aCount, aItems) {
for (var i=0; i<aCount; i++) {
- if (!savedThis.mFilterFunction ||
- savedThis.mFilterFunction(aItems[i])) {
+ if (savedThis.mFilter.isItemInFilters(aItems[i])) {
savedThis.mTaskArray.push(aItems[i]);
}
}
}
};
var refreshJob = {
execute: function() {
var composite = getCompositeCalendar();
var filter = aFilter || !savedThis.mShowCompletedTasks ?
composite.ITEM_FILTER_COMPLETED_NO :
composite.ITEM_FILTER_COMPLETED_ALL;
filter |= composite.ITEM_FILTER_TYPE_TODO;
- if (savedThis.startDate && savedThis.endDate) {
+ if (savedThis.mFilter.startDate && savedThis.mFilter.endDate) {
filter |= composite.ITEM_FILTER_CLASS_OCCURRENCES;
}
- aCalendar.getItems(filter, 0, savedThis.startDate, savedThis.endDate, refreshListener);
+ aCalendar.getItems(filter, 0, savedThis.mFilter.startDate, savedThis.mFilter.endDate, refreshListener);
}
};
this.mRefreshQueue.push(refreshJob);
this.popRefreshQueue();
]]></body>
</method>
<method name="onCalendarRemoved">
--- a/calendar/base/content/calendar-task-view.js
+++ b/calendar/base/content/calendar-task-view.js
@@ -16,16 +16,17 @@
* The Initial Developer of the Original Code is Sun Microsystems.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Michael Buettner <michael.buettner@sun.com>
* Philipp Kewisch <mozilla@kewis.ch>
* Berend Cornelius <berend.cornelius@sun.com>
+ * Fred Jendrzejewski <fred.jen@web.de>
*
* 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
@@ -160,79 +161,23 @@ var taskDetailsView = {
urlLabel.setAttribute("tooltiptext", gURL);
}
}
}
};
function taskViewUpdate(filter) {
document.getElementById("filterBroadcaster").setAttribute("value", filter);
- var percentCompleted = function(item) {
- var percent = 0;
- var property = item.getProperty("PERCENT-COMPLETE");
- if (property != null) {
- var percent = parseInt(property);
- }
- return percent;
- }
-
- var filterFunctions = {
- notstarted: function filterNotStarted(item) {
- return (percentCompleted(item) <= 0);
- },
- overdue: function filterOverdue(item) {
- // in case the item has no due date
- // it can't be overdue by definition
- if (item.dueDate == null) {
- return false;
- }
- return (percentCompleted(item) < 100) &&
- !(item.dueDate.compare(now()) > 0);
- },
- open: function filterCompleted(item) {
- return (percentCompleted(item) < 100);
- },
- completed: function filterCompleted(item) {
- return (percentCompleted(item) >= 100);
- }
- }
var tree = document.getElementById("calendar-task-tree");
- tree.filterFunction = filterFunctions[filter] || null;
- var todayDate = new Date();
- var startDate = new Date(todayDate.getFullYear(),
- todayDate.getMonth(),
- todayDate.getDate(),
- 0, 0, 0);
-
- var rangeFunctions = {
- today: function rangeToday() {
- tree.startDate = jsDateToDateTime(startDate)
- .getInTimezone(calendarDefaultTimezone());
- tree.endDate = jsDateToDateTime(
- new Date(startDate.getTime() + (1000 * 60 * 60 * 24) - 1))
- .getInTimezone(calendarDefaultTimezone());
- },
- next7days: function rangeNext7Days() {
- tree.startDate = jsDateToDateTime(startDate)
- .getInTimezone(calendarDefaultTimezone());
- tree.endDate = jsDateToDateTime(
- new Date(startDate.getTime() + (1000 * 60 * 60 * 24 * 8)))
- .getInTimezone(calendarDefaultTimezone());
- }
- }
+ // set the filters
+ tree.mFilter.propertyFilter = filter;
+ tree.mFilter.setDateFilter(filter);
- if (rangeFunctions[filter]) {
- rangeFunctions[filter]();
- } else {
- tree.startDate = null;
- tree.endDate = null;
- }
-
tree.refresh();
}
function sendMailToOrganizer() {
var item = document.getElementById("calendar-task-tree").currentTask;
if (item != null) {
var organizer = item.organizer;
if (organizer) {
--- a/calendar/base/content/calendar-task-view.xul
+++ b/calendar/base/content/calendar-task-view.xul
@@ -46,16 +46,17 @@
<!ENTITY % dtd2 SYSTEM "chrome://calendar/locale/sun-calendar-event-dialog.dtd" > %dtd2;
]>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://calendar/content/calendar-task-tree.js"/>
<script type="application/javascript" src="chrome://calendar/content/calendar-task-view.js"/>
<script type="application/javascript" src="chrome://calendar/content/calendar-dialog-utils.js"/>
<script type="application/javascript" src="chrome://calendar/content/calApplicationUtils.js"/>
+ <script type="application/javascript" src="chrome://calendar/content/calFilter.js"/>
<script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
<vbox id="calendarDisplayDeck">
<vbox id="calendar-task-box" flex="1"
onselect="taskDetailsView.onSelect(event);">
<textbox id="view-task-edit-field"
class="task-edit-field"
onfocus="taskEdit.onFocus(event)"
--- a/calendar/base/content/calendar-unifinder-todo.xul
+++ b/calendar/base/content/calendar-unifinder-todo.xul
@@ -46,17 +46,17 @@
]>
<?xml-stylesheet type="text/css" href="chrome://calendar/content/calendar-bindings.css"?>
<?xml-stylesheet type="text/css" href="chrome://calendar/skin/calendar-task-view.css"?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://calendar/content/calendar-task-tree.js"/>
- <script type="application/javascript" src="chrome://calendar/content/calendar-unifinder-todo.js"/>
+ <script type="application/javascript" src="chrome://calendar/content/calFilter.js"/> <script type="application/javascript" src="chrome://calendar/content/calendar-unifinder-todo.js"/>
<script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
<vbox id="todo-tab-panel" persist="height,collapsed" flex="1">
<box id="todo-label" align="left" collapsed="true">
<label flex="1" crop="end" style="font-weight: bold" value="&calendar.unifinder.todoitems.label;" control="unifinder-todo-tree"/>
</box>
<box align="center">
<checkbox id="show-completed-checkbox"
--- a/calendar/base/content/calendar-unifinder.js
+++ b/calendar/base/content/calendar-unifinder.js
@@ -26,16 +26,17 @@
* ArentJan Banck <ajbanck@planet.nl>
* Eric Belhaire <eric.belhaire@ief.u-psud.fr>
* Matthew Willis <mattwillis@gmail.com>
* Michiel van Leeuwen <mvl@exedo.nl>
* Joey Minta <jminta@gmail.com>
* Dan Mosedale <dan.mosedale@oracle.com>
* Michael Buettner <michael.buettner@sun.com>
* Philipp Kewisch <mozilla@kewis.ch>
+ * Fred Jendrzejewski <fred.jen@web.de>
*
* 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
@@ -56,36 +57,24 @@
Components.utils.import("resource://calendar/modules/calUtils.jsm");
// Set this to true when the calendar event tree is clicked to allow for
// multiple selection
var gCalendarEventTreeClicked = false;
// Store the start and enddate, because the providers can't be trusted when
// dealing with all-day events. So we need to filter later. See bug 306157
-var gStartDate;
-var gEndDate;
var kDefaultTimezone;
var gUnifinderNeedsRefresh = true;
function isUnifinderHidden() {
return document.getElementById("bottom-events-box").hidden;
}
-// Extra check to see if the events are in the daterange. Some providers
-// are broken when looking at all-day events.
-function fixAlldayDates(aItem) {
- // Using .compare on the views start and end, not on the events dates,
- // because .compare uses the timezone of the datetime it is called on.
- // The view's timezone is what is important here.
- return ((!gEndDate || gEndDate.compare(aItem.startDate) >= 0) &&
- (!gStartDate || gStartDate.compare(aItem.endDate) < 0));
-}
-
function getCurrentUnifinderFilter() {
return document.getElementById("event-filter-menulist").selectedItem.value;
}
/**
* Observer for the calendar event data source. This keeps the unifinder
* display up to date when the calendar event data is changed
*/
@@ -126,17 +115,18 @@ var unifinderObserver = {
refreshEventTree();
}
},
onAddItem: function uO_onAddItem(aItem) {
if (isEvent(aItem) &&
!this.mInBatch &&
!gUnifinderNeedsRefresh &&
- isItemInFilter(aItem)) {
+ unifinderTreeView.mFilter.isItemInFilters(aItem)
+ ) {
this.addItemToTree(aItem);
}
},
onModifyItem: function uO_onModifyItem(aNewItem, aOldItem) {
this.onDeleteItem(aOldItem);
this.onAddItem(aNewItem);
},
@@ -146,31 +136,35 @@ var unifinderObserver = {
this.removeItemFromTree(aDeletedItem);
}
},
// It is safe to call these for any event. The functions will determine
// whether or not anything actually needs to be done to the tree
addItemToTree: function uO_addItemToTree(aItem) {
var items;
- if (gStartDate && gEndDate) {
- items = aItem.getOccurrencesBetween(gStartDate, gEndDate, {});
+ var filter = unifinderTreeView.mFilter;
+
+ if (filter.startDate && filter.endDate) {
+ items = aItem.getOccurrencesBetween(filter.startDate, filter.endDate, {});
} else {
items = [aItem];
}
- unifinderTreeView.addItems(items.filter(fixAlldayDates));
+ unifinderTreeView.addItems(items.filter(filter.isItemInFilters, filter));
},
removeItemFromTree: function uO_removeItemFromTree(aItem) {
var items;
- if (gStartDate && gEndDate && (aItem.parentItem == aItem)) {
- items = aItem.getOccurrencesBetween(gStartDate, gEndDate, {});
+ var filter = unifinderTreeView.mFilter;
+ if (filter.startDate && filter.endDate && (aItem.parentItem == aItem)) {
+ items = aItem.getOccurrencesBetween(filter.startDate, filter.endDate, {});
} else {
items = [aItem];
}
- unifinderTreeView.removeItems(items.filter(fixAlldayDates));
+ // XXX: do we really still need this, we are always checking it in the refreshInternal
+ unifinderTreeView.removeItems(items.filter(filter.isItemInFilters, filter));
},
onError: function uO_onError(aCalendar, aErrNo, aMessage) {},
onPropertyChanged: function uO_onPropertyChanged(aCalendar, aName, aValue, aOldValue) {
switch (aName) {
case "disabled":
refreshEventTree();
@@ -210,16 +204,20 @@ function prepareCalendarUnifinder() {
// Check if this is not the hidden window, which has no UI elements
if (unifinderTree) {
// set up our calendar event observer
var ccalendar = getCompositeCalendar();
ccalendar.addObserver(unifinderObserver);
kDefaultTimezone = calendarDefaultTimezone();
+ // Set up the filter
+ unifinderTreeView.mFilter = new calFilter();
+ unifinderTreeView.mFilter.propertyFilter = "unifinder-search-field";
+
// Set up the unifinder views.
unifinderTreeView.treeElement = unifinderTree;
unifinderTree.view = unifinderTreeView;
// Listen for changes in the selected day, so we can update if need be
var viewDeck = getViewDeck();
viewDeck.addEventListener("dayselect", unifinderDaySelect, false);
viewDeck.addEventListener("itemselect", unifinderItemSelect, true);
@@ -400,16 +398,18 @@ function unifinderKeyPress(aEvent) {
/**
* Tree controller for unifinder search results
*/
var unifinderTreeView = {
tree: null,
treeElement: null,
doingSelection: false,
+ mFilter: null,
+
mSelectedColumn: null,
sortDirection: null,
get selectedColumn uTV_getSelectedColumn() {
return this.mSelectedColumn;
},
@@ -788,124 +788,48 @@ function refreshEventTree() {
var ccalendar = getCompositeCalendar();
var filter = 0;
filter |= ccalendar.ITEM_FILTER_TYPE_EVENT;
// Not all xul might be there yet...
- if (!document.getElementById("event-filter-menulist")) {
+ if (!document.getElementById(unifinderTreeView.mFilter.textFilterField)) {
return;
}
- var [StartDate, EndDate] = getDatesForFilter(getCurrentUnifinderFilter());
+ unifinderTreeView.mFilter.setDateFilter(getCurrentUnifinderFilter());
- gStartDate = StartDate ? jsDateToDateTime(StartDate, calendarDefaultTimezone()) : null;
- gEndDate = EndDate ? jsDateToDateTime(EndDate, calendarDefaultTimezone()) : null;
- if (StartDate && EndDate) {
+ if (unifinderTreeView.mFilter.startDate && unifinderTreeView.mFilter.endDate) {
filter |= ccalendar.ITEM_FILTER_CLASS_OCCURRENCES;
}
- ccalendar.getItems(filter, 0, gStartDate, gEndDate, refreshListener);
+ ccalendar.getItems(filter, 0, unifinderTreeView.mFilter.startDate, unifinderTreeView.mFilter.endDate, refreshListener);
}
/**
* Get the dates for a certain filter. This function makes it easy to extend the
* unifinder. To add a new view, just overwrite this function with your own. Be
* sure to call this function afterwards though.
*/
-function getDatesForFilter(aFilter) {
- var Today = new Date();
- // Do this to allow all day events to show up all day long.
- var StartDate = new Date(Today.getFullYear(),
- Today.getMonth(),
- Today.getDate(),
- 0, 0, 0);
- var EndDate;
- switch (aFilter) {
- case "all":
- StartDate = null;
- EndDate = null;
- break;
-
- case "today":
- EndDate = new Date(StartDate.getTime() + (1000 * 60 * 60 * 24) - 1);
- break;
-
- case "next7Days":
- EndDate = new Date(StartDate.getTime() + (1000 * 60 * 60 * 24 * 8));
- break;
-
- case "next14Days":
- EndDate = new Date(StartDate.getTime() + (1000 * 60 * 60 * 24 * 15));
- break;
-
- case "next31Days":
- EndDate = new Date(StartDate.getTime() + (1000 * 60 * 60 * 24 * 32));
- break;
-
- case "thisCalendarMonth":
- // midnight on first day of this month
- var startOfMonth = new Date(Today.getFullYear(), Today.getMonth(), 1, 0, 0, 0);
- // midnight on first day of next month
- var startOfNextMonth = new Date(Today.getFullYear(), (Today.getMonth() + 1), 1, 0, 0, 0);
- // 23:59:59 on last day of this month
- EndDate = new Date(startOfNextMonth.getTime() - 1000);
- StartDate = startOfMonth;
- break;
-
- case "future":
- EndDate = null;
- break;
-
- case "current":
- var SelectedDate = currentView().selectedDay.jsDate;
- StartDate = new Date(SelectedDate.getFullYear(), SelectedDate.getMonth(), SelectedDate.getDate(), 0, 0, 0);
- EndDate = new Date(StartDate.getTime() + (1000 * 60 * 60 * 24) - 1000);
- break;
- }
- return [StartDate, EndDate];
-}
function refreshEventTreeInternal(eventArray) {
- var searchText = document.getElementById("unifinder-search-field").value;
- unifinderTreeView.setItems(eventArray.filter(isItemInFilter));
+ unifinderTreeView.setItems(eventArray.filter(unifinderTreeView
+ .mFilter
+ .isItemInFilters
+ , unifinderTreeView
+ .mFilter
+ ));
// Select selected events in the tree. Not passing the argument gets the
// items from the view.
unifinderTreeView.setSelectedItems();
}
-function isItemInFilter(aItem) {
- var searchText = document.getElementById("unifinder-search-field")
- .value.toLowerCase();
-
- if (!searchText.length || searchText.match(/^\s*$/)) {
- return true;
- }
-
- const fieldsToSearch = ["SUMMARY", "DESCRIPTION", "LOCATION", "URL"];
- if (!fixAlldayDates(aItem)) {
- return false;
- }
-
- for each (var field in fieldsToSearch) {
- var val = aItem.getProperty(field);
- if (val && val.toLowerCase().indexOf(searchText) != -1) {
- return true;
- }
- }
-
- return aItem.getCategories({}).some(
- function someFunc(cat) {
- return (cat.toLowerCase().indexOf(searchText) != -1);
- });
-}
-
function focusSearch() {
document.getElementById("unifinder-search-field").focus();
}
function toggleUnifinder() {
// Toggle the elements
goToggleToolbar('bottom-events-box', 'calendar_show_unifinder_command');
goToggleToolbar('calendar-view-splitter');
--- a/calendar/base/content/calendar-unifinder.xul
+++ b/calendar/base/content/calendar-unifinder.xul
@@ -55,33 +55,33 @@
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://calendar/content/calendar-unifinder.js"/>
<script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
<vbox id="calendar-view-box">
<vbox id="bottom-events-box" insertbefore="view-deck" persist="height">
<hbox id="unifinder-searchBox" persist="collapsed">
<box align="center">
- <menulist id="event-filter-menulist" value="next7Days" persist="value">
+ <menulist id="event-filter-menulist" value="next7days" persist="value">
<menupopup id="event-filter-menupopup" oncommand="refreshEventTree()">
<menuitem id="event-filter-all"
label="&calendar.events.filter.all.label;"
value="all"/>
<menuitem id="event-filter-today"
label="&calendar.events.filter.today.label;"
value="today"/>
- <menuitem id="event-filter-next7Days"
+ <menuitem id="event-filter-next7days"
label="&calendar.events.filter.next7Days.label;"
- value="next7Days"/>
+ value="next7days"/>
<menuitem id="event-filter-next14Days"
label="&calendar.events.filter.next14Days.label;"
- value="next14Days"/>
+ value="next14days"/>
<menuitem id="event-filter-next31Days"
label="&calendar.events.filter.next31Days.label;"
- value="next31Days"/>
+ value="next31days"/>
<menuitem id="event-filter-thisCalendarMonth"
label="&calendar.events.filter.thisCalendarMonth.label;"
value="thisCalendarMonth"/>
<menuitem id="event-filter-future"
label="&calendar.events.filter.future.label;"
value="future"/>
<menuitem id="event-filter-current"
label="&calendar.events.filter.current.label;"
--- a/calendar/base/jar.mn
+++ b/calendar/base/jar.mn
@@ -55,16 +55,17 @@ calendar.jar:
content/calendar/widgets/minimonth.xml (content/widgets/minimonth.xml)
content/calendar/widgets/calendar-widget-bindings.css (content/widgets/calendar-widget-bindings.css)
content/calendar/calendar-minimonth-busy.js (content/calendar-minimonth-busy.js)
content/calendar/calendar-view-bindings.css (content/calendar-view-bindings.css)
content/calendar/calendar-view-core.xml (content/calendar-view-core.xml)
content/calendar/calendar-views.js (content/calendar-views.js)
* content/calendar/calApplicationUtils.js (src/calApplicationUtils.js)
content/calendar/calUtils.js (src/calUtils.js)
+ content/calendar/calFilter.js (src/calFilter.js)
content/calendar/Windows98ToZoneInfoTZId.properties (src/Windows98ToZoneInfoTZId.properties)
content/calendar/WindowsNTToZoneInfoTZId.properties (src/WindowsNTToZoneInfoTZId.properties)
content/calendar/calErrorPrompt.xul (content/calErrorPrompt.xul)
content/calendar/chooseCalendarDialog.xul (content/chooseCalendarDialog.xul)
content/calendar/import-export.js (content/import-export.js)
* content/calendar/migration.js (content/migration.js)
content/calendar/migration.xul (content/migration.xul)
content/calendar/widgets/calendar-widgets.xml (content/widgets/calendar-widgets.xml)
--- a/calendar/base/src/Makefile.in
+++ b/calendar/base/src/Makefile.in
@@ -84,16 +84,17 @@ EXTRA_SCRIPTS = \
calAlarmService.js \
calAlarmMonitor.js \
calAttachment.js \
calAttendee.js \
calCalendarManager.js \
calCachedCalendar.js \
calDateTimeFormatter.js \
calEvent.js \
+ calFilter.js \
calIcsParser.js \
calIcsSerializer.js \
calItemBase.js \
calItipItem.js \
calItipProcessor.js \
calProtocolHandler.js \
calRecurrenceInfo.js \
calRelation.js \
new file mode 100644
--- /dev/null
+++ b/calendar/base/src/calFilter.js
@@ -0,0 +1,342 @@
+/* ***** 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 Mozilla Calendar code.
+ *
+ * The Initial Developer of the Original Code is
+ * Fred Jendrzejewski <fred.jen@web.de>
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * 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 ***** */
+
+/**
+ * This object gives you the possibility to filter items for its properties.
+ * It has the following properties:
+ *
+ *
+ * private Attributes:
+ * mStartDate
+ * mEndDate - the limits for the item to be in the filter
+ * mPropertyFilter - a boolean function to filter items in a costumized way
+ * mPropertyFilterBag - an object with a number of predefined filterFunctions
+ * mTextFilterField - the id of a text - field that is associated with the filter
+ *
+ * setting up the dateFilter:
+ * setDateFilter - this function takes a string and sets the start and endDate of
+ * the filter by using the getDatesForFilter function, please look
+ * on the documentation for this function for further informations
+ *
+ * setting up the propertyFilter:
+ * you can associate a textFilter, a built-in filter or a custom function with the
+ * propertyFilter. It is always set up by using calFilter.propertyFilter = aFilter
+ * 1.) Use a built-in filter:
+ * - aFilter is a string
+ * - the string has to be the name of one of the members of mPropertyFilterBag
+ * the propertyFilter is set to this built-in function
+ * 2.) Use a text filter:
+ * - aFilter is a string
+ * - the string is a id of a text-field
+ * the propertyFilter will always filter out by regular expression items whose
+ * properties contain the value of the text-field
+ * 3.) Use a costumized filter:
+ * - aFilter is a function
+ * propertyFilter is set this function
+ *
+ * using the filter:
+ * isItemInFilters - this takes an item and returns the result of
+ * checkIfInRange and propertyFilter
+ */
+
+function calFilter() {
+ this.wrappedJSObject = this;
+}
+
+calFilter.prototype = {
+ mStartDate: null,
+ mEndDate: null,
+ mTextFilterField: null,
+ mPropertyFilter: filterAll,
+
+ // a number of prefined Filters for properties
+ mPropertyFilterBag: {
+ all: filterAll,
+ notstarted: function cF_filterNotStarted(item) {
+ return (percentCompleted(item) <= 0);
+ },
+ overdue: function cF_filterOverdue(item) {
+ // in case the item has no due date
+ // it can't be overdue by definition
+ if (item.dueDate == null) {
+ return false;
+ }
+ return (percentCompleted(item) < 100) &&
+ !(item.dueDate.compare(now()) > 0);
+ },
+ open: function cF_filterCompleted(item) {
+ return (percentCompleted(item) < 100);
+ },
+ completed: function cF_filterCompleted(item) {
+ return (percentCompleted(item) >= 100);
+ }
+ },
+
+ get startDate cF_get_startDate() {
+ return this.mStartDate;
+ },
+
+ set startDate cF_set_startDate(aStartDate) {
+ return (this.mStartDate = aStartDate);
+ },
+
+ get endDate cF_get_endDate() {
+ return this.mEndDate;
+ },
+
+ set endDate cF_set_endDate(aEndDate) {
+ return (this.mEndDate = aEndDate);
+ },
+
+ set textFilterField cF_setTextFilterField(aId) {
+ return (this.mTextFilterField = aId);
+ },
+
+ get textFilterField cF_getTextFilterField() {
+ return this.mTextFilterField;
+ },
+
+ // checks if the item contains the text of mTextFilterField
+ textFilter: function cF_filterByText(aItem) {
+ filterByText.mTextFilterField = this.mTextFilterField;
+ var inIt = filterByText(aItem);
+ return inIt;
+ },
+
+ get propertyFilter cF_get_propertyFilter() {
+ if(!this.mPropertyFilter) {
+ this.mPropertyFilter = filterAll;
+ }
+
+ return this.mPropertyFilter;
+ },
+
+ /*
+ * @param aFilter if - aFilter is a string, propertyFilter is textFilter
+ * if aFilter has to be the id of the textFilterField then
+ * if aFilter is the number of a number of predefined
+ * propertyFilters, propertyFilters is set to be this
+ * - aFilter is a function, the propertyFilter is set to be this
+ * function
+ */
+ set propertyFilter cF_set_propertyFilter(aFilter) {
+ if (typeof(aFilter) == "string") {
+ // check if it is one of the build in filters
+ if (this.mPropertyFilterBag[aFilter]) {
+ return (this.mPropertyFilter = this.mPropertyFilterBag[aFilter]);
+ }
+ // check if the aFilter is the id of an item, otherwise
+ // return set the filter to all
+ if (document.getElementById(aFilter)) {
+ this.mTextFilterField = aFilter;
+ return (this.mPropertyFilter = this.textFilter);
+ }
+ } else if (typeof(aFilter) == "function") {
+ return (this.mPropertyFilter = aFilter);
+ }
+
+ return (this.mPropertyFilter = filterAll);
+ },
+
+ // set's the startDate and the endDate by using getDatesForFilter
+ setDateFilter: function cF_setDateFilter(aFilter) {
+ var [startDate, endDate] = getDatesForFilter(aFilter);
+ this.mStartDate = startDate;
+ this.mEndDate = endDate;
+ return [this.mStartdate, this.mEndDate];
+ },
+
+ // checks if the item is between startDate and endDate
+ isItemWithinRange: function cF_isDateWithinRange(aItem) {
+ return checkIfInRange(aItem, this.mStartDate, this.mEndDate);
+ },
+
+ // checks if the item is between startDate and endDate and its properties
+ isItemInFilters: function cF_isItemInFilters(aItem) {
+ return (this.isItemWithinRange(aItem) && this.propertyFilter(aItem));
+ }
+};
+
+/**
+ * @param aFilter a String describing the filter, it should met a regEx to call
+ * duration from filter otherwise a costumized filter is called
+ * @return [startDate, endDate]
+ */
+
+function getDatesForFilter(aFilter) {
+ var EndDate = createDateTime();
+ var StartDate = createDateTime();
+ var Duration = createDuration();
+ var oneDay = createDuration();
+ oneDay.days = 1;
+
+ var durFilterReg = /next|last\d+\D+$/
+ if (durFilterReg.exec(aFilter)) {
+ Duration = durationFromFilter(aFilter);
+ if (!Duration) {
+ EndDate = null;
+ StartDate = null;
+ } else {
+ StartDate = now();
+ EndDate = now();
+ EndDate.addDuration(Duration);
+ }
+ } else {
+ switch (aFilter) {
+ case "all":
+ StartDate = null;
+ EndDate = null;
+ break;
+
+ case "today":
+ StartDate = now();
+ StartDate.isDate = true;
+ EndDate = now();
+ EndDate.isDate = true;
+ EndDate.addDuration(oneDay);
+ break;
+
+ case "thisCalendarMonth":
+ StartDate = now().startOfMonth;
+ EndDate = now().endOfMonth;
+ EndDate.addDuration(oneDay);
+ break;
+
+ case "future":
+ StartDate = now();
+ EndDate = null;
+ break;
+
+ case "current":
+ var SelectedDate = currentView().selectedDay;
+ StartDate = SelectedDate.clone();
+ StartDate.isDate = true;
+ EndDate = StartDate.clone();
+ EndDate.addDuration(oneDay);
+ break;
+ default:
+ StartDate = null;
+ EndDate = null;
+ break;
+ }
+ }
+ return [StartDate, EndDate];
+}
+
+/**
+ * Functions to create a duration based on a filter string.
+ *
+ * @param aFilter the filter string, it has has to match the pattern
+ * [last|next] Period [UnitOfThePeriod]
+ * @return the duration or null
+ */
+function durationFromFilter(aFilter) {
+ var durReg = /(last|next)/
+ var periodReg = /(\d+)/
+ var unitReg = /\d+(\D+)$/
+ try {
+ // Create the direction of the duration
+ var modifier = (durReg.exec(aFilter)[1] == "next") ? 1 : -1;
+
+ // Get the numerical value
+ var period = periodReg.exec(aFilter)[1];
+
+ //Get the unit
+ var duration = createDuration();
+ switch( unitReg.exec(aFilter)[1]) {
+ case "weeks":
+ duration.weeks = modifier*period;
+ break;
+ case "days":
+ duration.days = modifier*period;
+ break;
+ case "hours":
+ duration.hours = modifier*period;
+ break;
+ default:
+ return null;
+ }
+ return duration;
+ } catch(e) {
+ dump(e);
+ return null;
+ }
+}
+
+/**
+ * @param aItem Is a normal calIItemBase
+ * @param mTextFilterField Has to be set from the outside of the function
+ * @return Filters if the item is contains the searchText
+ */
+function filterByText(aItem) {
+ var searchText = document.getElementById(filterByText.mTextFilterField)
+ .value.toLowerCase();
+
+ if (!searchText.length || searchText.match(/^\s*$/)) {
+ return true;
+ }
+
+ const fieldsToSearch = ["SUMMARY", "DESCRIPTION", "LOCATION", "URL"];
+ if (!fixAlldayDates(aItem)) {
+ return false;
+ }
+
+ for each (var field in fieldsToSearch) {
+ var val = aItem.getProperty(field);
+ if (val && val.toLowerCase().indexOf(searchText) != -1) {
+ return true;
+ }
+ }
+
+ return aItem.getCategories({}).some(
+ function someFunc(cat) {
+ return (cat.toLowerCase().indexOf(searchText) != -1);
+ });
+}
+
+function percentCompleted(item) {
+ var percent = 0;
+ var property = item.getProperty("PERCENT-COMPLETE");
+ if (property != null) {
+ var percent = parseInt(property);
+ }
+ return percent;
+}
+
+function filterAll(aItem) {
+ return true;
+}
+