Fix bug 591742 - Import and Export is broken [Exception "Component returned failure code: 0x80570016 (NS_ERROR_XPC_GS_RETURNED_FAILURE) [nsIJSCID.getService]"]. r=simon

This commit is contained in:
Philipp Kewisch 2010-12-10 22:46:47 +01:00
Родитель d3bd453555
Коммит 758b44d6d1
10 изменённых файлов: 1198 добавлений и 1136 удалений

Просмотреть файл

@ -1,5 +1,4 @@
/* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
/* ***** 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
@ -68,7 +67,7 @@ function loadEventsFromFile(aCalendar) {
nsIFilePicker.modeOpen);
fp.defaultExtension = "ics";
// Get a list of exporters
// Get a list of importers
let contractids = new Array();
let catman = Components.classes["@mozilla.org/categorymanager;1"]
.getService(Components.interfaces.nsICategoryManager);
@ -78,11 +77,16 @@ function loadEventsFromFile(aCalendar) {
let entry = catenum.getNext();
entry = entry.QueryInterface(Components.interfaces.nsISupportsCString);
let contractid = catman.getCategoryEntry('cal-importers', entry);
let exporter = Components.classes[contractid]
let importer;
try {
importer = Components.classes[contractid]
.getService(Components.interfaces.calIImporter);
let types = exporter.getFileTypes({});
let type;
for each (type in types) {
} catch (e) {
cal.WARN("Could not initialize importer: " + contractid + "\nError: " + e);
continue;
}
let types = importer.getFileTypes({});
for each (let type in types) {
fp.appendFilter(type.description, type.extensionFilter);
if (type.extensionFilter=="*." + fp.defaultExtension) {
fp.filterIndex = currentListLength;
@ -92,9 +96,10 @@ function loadEventsFromFile(aCalendar) {
}
}
fp.show();
let rv = fp.show();
if (fp.file && fp.file.path && fp.file.path.length > 0) {
if (rv != nsIFilePicker.returnCancel &&
fp.file && fp.file.path && fp.file.path.length > 0) {
let filePath = fp.file.path;
let importer = Components.classes[contractids[fp.filterIndex]]
.getService(Components.interfaces.calIImporter);
@ -260,10 +265,15 @@ function saveEventsToFile(calendarEventArray, aDefaultFileName) {
let entry = catenum.getNext();
entry = entry.QueryInterface(Components.interfaces.nsISupportsCString);
let contractid = catman.getCategoryEntry('cal-exporters', entry);
let exporter = Components.classes[contractid]
let exporter;
try {
exporter = Components.classes[contractid]
.getService(Components.interfaces.calIExporter);
} catch (e) {
cal.WARN("Could not initialize exporter: " + contractid + "\nError: " + e);
continue;
}
let types = exporter.getFileTypes({});
let type;
for each (let type in types) {
fp.appendFilter(type.description, type.extensionFilter);
if (type.extensionFilter=="*." + fp.defaultExtension) {
@ -274,10 +284,11 @@ function saveEventsToFile(calendarEventArray, aDefaultFileName) {
}
}
fp.show();
let rv = fp.show();
// Now find out as what to save, convert the events and save to file.
if (fp.file && fp.file.path.length > 0) {
if (rv != nsIFilePicker.returnCancel &&
fp.file && fp.file.path.length > 0) {
const UTF8 = "UTF-8";
let aDataStream;
let extension;

Просмотреть файл

@ -1,4 +1,3 @@
#
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#

Просмотреть файл

@ -1,5 +1,4 @@
/* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
/* ***** 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
@ -38,173 +37,178 @@
*
* ***** END LICENSE BLOCK ***** */
// Export
Components.utils.import("resource://calendar/modules/calUtils.jsm");
// Export
function calHtmlExporter() {
this.wrappedJSObject = this;
}
calHtmlExporter.prototype.QueryInterface =
function QueryInterface(aIID) {
if (!aIID.equals(Components.interfaces.nsISupports) &&
!aIID.equals(Components.interfaces.calIExporter)) {
throw Components.results.NS_ERROR_NO_INTERFACE;
}
calHtmlExporter.prototype = {
getInterfaces: function (count) {
const ifaces = [
Components.interfaces.nsISupports,
Components.interfaces.nsIClassInfo,
Components.interfaces.calIExporter,
];
count.value = ifaces.length;
return ifaces;
},
return this;
};
getHelperForLanguage: function (language) {
return null;
},
calHtmlExporter.prototype.getFileTypes =
function getFileTypes(aCount) {
aCount.value = 1;
var wildmat = '*.html; *.htm';
var label = calGetString("calendar", 'filterHtml', [wildmat]);
return([{defaultExtension:'html',
extensionFilter: wildmat,
description: label}]);
};
contractID: "@mozilla.org/calendar/export;1?type=html",
classDescription: "Calendar HTML Exporter",
classID: Components.ID("{72d9ab35-9b1b-442a-8cd0-ae49f00b159b}"),
implementationLanguage: Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
flags: 0,
// not prototype.export. export is reserved.
calHtmlExporter.prototype.exportToStream =
function html_exportToStream(aStream, aCount, aItems, aTitle) {
var dateFormatter =
Components.classes["@mozilla.org/calendar/datetime-formatter;1"]
.getService(Components.interfaces.calIDateTimeFormatter);
QueryInterface: function QueryInterface(aIID) {
return cal.doQueryInterface(this, calHtmlExporter.prototype, aIID, null, this);
},
var documentTitle = aTitle;
if (!documentTitle) {
documentTitle = calGetString("calendar", "HTMLTitle");
}
getFileTypes: function getFileTypes(aCount) {
aCount.value = 1;
let wildmat = '*.html; *.htm';
let label = cal.calGetString("calendar", 'filterHtml', [wildmat]);
return [{ defaultExtension:'html',
extensionFilter: wildmat,
description: label }];
},
var html =
<html>
<head>
<title>{documentTitle}</title>
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>
<style type='text/css'/>
</head>
<body>
<!-- Note on the use of the summarykey class: this is a
special class, because in the default style, it is hidden.
The div is still included for those that want a different
style, where the key is visible -->
</body>
</html>;
// XXX The html comment above won't propagate to the resulting html.
// Should fix that, one day.
exportToStream: function html_exportToStream(aStream, aCount, aItems, aTitle) {
let documentTitle = aTitle || cal.calGetString("calendar", "HTMLTitle");
// Using this way to create the styles, because { and } are special chars
// in e4x. They have to be escaped, which doesn't improve readability
html.head.style = ".vevent {border: 1px solid black; padding: 0px; margin-bottom: 10px;}\n";
html.head.style += "div.key {font-style: italic; margin-left: 3px;}\n";
html.head.style += "div.value {margin-left: 20px;}\n";
html.head.style += "abbr {border: none;}\n";
html.head.style += ".summarykey {display: none;}\n";
html.head.style += "div.summary {background: white; font-weight: bold; margin: 0px; padding: 3px;}\n";
let html =
<html>
<head>
<title>{documentTitle}</title>
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>
<style type='text/css'/>
</head>
<body>
<!-- Note on the use of the summarykey class: this is a
special class, because in the default style, it is hidden.
The div is still included for those that want a different
style, where the key is visible -->
</body>
</html>;
// XXX The html comment above won't propagate to the resulting html.
// Should fix that, one day.
// Sort aItems
function sortFunc(a, b) {
var start_a = a[calGetStartDateProp(a)];
if (!start_a) {
return -1;
// Using this way to create the styles, because { and } are special chars
// in e4x. They have to be escaped, which doesn't improve readability
html.head.style = ".vevent {border: 1px solid black; padding: 0px; margin-bottom: 10px;}\n";
html.head.style += "div.key {font-style: italic; margin-left: 3px;}\n";
html.head.style += "div.value {margin-left: 20px;}\n";
html.head.style += "abbr {border: none;}\n";
html.head.style += ".summarykey {display: none;}\n";
html.head.style += "div.summary {background: white; font-weight: bold; margin: 0px; padding: 3px;}\n";
// Sort aItems
function sortFunc(a, b) {
let start_a = a[cal.calGetStartDateProp(a)];
if (!start_a) {
return -1;
}
let start_b = b[cal.calGetStartDateProp(b)];
if (!start_b) {
return 1;
}
return start_a.compare(start_b);
}
var start_b = b[calGetStartDateProp(b)];
if (!start_b) {
return 1;
}
return start_a.compare(start_b);
}
aItems.sort(sortFunc);
aItems.sort(sortFunc);
var prefixTitle = calGetString("calendar", "htmlPrefixTitle");
var prefixWhen = calGetString("calendar", "htmlPrefixWhen");
var prefixLocation = calGetString("calendar", "htmlPrefixLocation");
var prefixDescription = calGetString("calendar", "htmlPrefixDescription");
var defaultTimezone = calendarDefaultTimezone();
let prefixTitle = cal.calGetString("calendar", "htmlPrefixTitle");
let prefixWhen = cal.calGetString("calendar", "htmlPrefixWhen");
let prefixLocation = cal.calGetString("calendar", "htmlPrefixLocation");
let prefixDescription = cal.calGetString("calendar", "htmlPrefixDescription");
let defaultTimezone = cal.calendarDefaultTimezone();
for (var pos = 0; pos < aItems.length; ++pos) {
var item = aItems[pos];
for (let pos = 0; pos < aItems.length; ++pos) {
let item = aItems[pos];
// Put properties of the event in a definition list
// Use hCalendar classes as bonus
var ev = <div class='vevent'/>;
var fmtTaskCompleted = calGetString("calendar",
"htmlTaskCompleted",
[item.title]);
// Put properties of the event in a definition list
// Use hCalendar classes as bonus
let ev = <div class='vevent'/>;
let fmtTaskCompleted = cal.calGetString("calendar",
"htmlTaskCompleted",
[item.title]);
// Title
ev.appendChild(
<div>
<div class='key summarykey'>{prefixTitle}</div>
<div class='value summary'>{item.isCompleted ? fmtTaskCompleted : item.title}</div>
</div>
);
var startDate = item[calGetStartDateProp(item)];
var endDate = item[calGetEndDateProp(item)];
var dateString = dateFormatter.formatItemInterval(item);
if (startDate != null || endDate != null) {
// This is a task with a start or due date, format accordingly
// Title
ev.appendChild(
<div>
<div class='key'>{prefixWhen}</div>
<div class='value'>
<abbr class='dtstart' title={startDate ? startDate.icalString : "none"}>{dateString}</abbr>
<div class='key summarykey'>{prefixTitle}</div>
<div class='value summary'>{item.isCompleted ? fmtTaskCompleted : item.title}</div>
</div>
);
let startDate = item[cal.calGetStartDateProp(item)];
let endDate = item[cal.calGetEndDateProp(item)];
let dateString = cal.getDateFormatter().formatItemInterval(item);
if (startDate != null || endDate != null) {
// This is a task with a start or due date, format accordingly
ev.appendChild(
<div>
<div class='key'>{prefixWhen}</div>
<div class='value'>
<abbr class='dtstart' title={startDate ? startDate.icalString : "none"}>{dateString}</abbr>
</div>
</div>
</div>
);
}
// Location
if (item.getProperty('LOCATION')) {
ev.appendChild(
<div>
<div class='key'>{prefixLocation}</div>
<div class='value location'>{item.getProperty('LOCATION')}</div>
</div>
);
}
);
}
// Location
if (item.getProperty('LOCATION')) {
ev.appendChild(
<div>
<div class='key'>{prefixLocation}</div>
<div class='value location'>{item.getProperty('LOCATION')}</div>
</div>
);
}
// Description, inside a pre to preserve formating when needed.
var desc = item.getProperty('DESCRIPTION');
if (desc && desc.length > 0) {
var usePre = false;
if (desc.indexOf("\n ") >= 0 || desc.indexOf("\n\t") >= 0 ||
desc.indexOf(" ") == 0 || desc.indexOf("\t") == 0)
// (RegExp /^[ \t]/ doesn't work.)
// contains indented preformatted text after beginning or newline
// so preserve indentation with PRE.
usePre = true;
// Description, inside a pre to preserve formating when needed.
let desc = item.getProperty('DESCRIPTION');
if (desc && desc.length > 0) {
let usePre = false;
if (desc.indexOf("\n ") >= 0 || desc.indexOf("\n\t") >= 0 ||
desc.indexOf(" ") == 0 || desc.indexOf("\t") == 0)
// (RegExp /^[ \t]/ doesn't work.)
// contains indented preformatted text after beginning or newline
// so preserve indentation with PRE.
usePre = true;
var descnode =
<div>
<div class='key'>{prefixDescription}</div>
<div class='value'/>
</div>;
let descnode =
<div>
<div class='key'>{prefixDescription}</div>
<div class='value'/>
</div>;
if (usePre) {
descnode.div[1] = <pre class='description'>{desc}</pre>;
} else {
var lines = desc.split('\n');
for (var i in lines) {
descnode.div[1].appendChild(lines[i]);
// Add a new line, except after the last line
if (i != (lines.length-1)) {
descnode.div[1].appendChild(<br/>);
if (usePre) {
descnode.div[1] = <pre class='description'>{desc}</pre>;
} else {
let lines = desc.split('\n');
for (let i in lines) {
descnode.div[1].appendChild(lines[i]);
// Add a new line, except after the last line
if (i != (lines.length-1)) {
descnode.div[1].appendChild(<br/>);
}
}
}
ev.appendChild(descnode);
}
ev.appendChild(descnode);
html.body.appendChild(ev);
}
html.body.appendChild(ev);
// Convert the javascript string to an array of bytes, using the
// utf8 encoder
let convStream = Components.classes["@mozilla.org/intl/converter-output-stream;1"]
.createInstance(Components.interfaces.nsIConverterOutputStream);
convStream.init(aStream, 'UTF-8', 0, 0x0000);
let str = html.toXMLString()
convStream.writeString(str);
}
// Convert the javascript string to an array of bytes, using the
// utf8 encoder
var convStream = Components.classes["@mozilla.org/intl/converter-output-stream;1"]
.createInstance(Components.interfaces.nsIConverterOutputStream);
convStream.init(aStream, 'UTF-8', 0, 0x0000);
var str = html.toXMLString()
convStream.writeString(str);
return;
};

Просмотреть файл

@ -1,5 +1,4 @@
/* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
/* ***** 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
@ -20,6 +19,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Philipp Kewisch <mozilla@kewis.ch>
*
* 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
@ -35,65 +35,92 @@
*
* ***** END LICENSE BLOCK ***** */
// Import
function calIcsImporter() {
this.wrappedJSObject = this;
}
calIcsImporter.prototype.QueryInterface =
function QueryInterface(aIID) {
if (!aIID.equals(Components.interfaces.nsISupports) &&
!aIID.equals(Components.interfaces.calIImporter)) {
throw Components.results.NS_ERROR_NO_INTERFACE;
}
return this;
};
Components.utils.import("resource://calendar/modules/calUtils.jsm");
// Shared functions
function getIcsFileTypes(aCount) {
aCount.value = 1;
var wildmat = '*.ics';
var label = calGetString("calendar", 'filterIcs', [wildmat]);
return([{defaultExtension:'ics',
extensionFilter: wildmat,
description: label}]);
let wildmat = '*.ics';
let label = cal.calGetString("calendar", 'filterIcs', [wildmat]);
return [{ defaultExtension: 'ics',
extensionFilter: wildmat,
description: label }];
}
calIcsImporter.prototype.getFileTypes = getIcsFileTypes;
calIcsImporter.prototype.importFromStream =
function ics_importFromStream(aStream, aCount) {
var parser = Components.classes["@mozilla.org/calendar/ics-parser;1"].
createInstance(Components.interfaces.calIIcsParser);
parser.parseFromStream(aStream, null);
return parser.getItems(aCount);
};
// Export
function calIcsExporter() {
this.wrappedJSObject = this;
// Importer
function calIcsImporter() {
}
calIcsExporter.prototype.QueryInterface =
function QueryInterface(aIID) {
if (!aIID.equals(Components.interfaces.nsISupports) &&
!aIID.equals(Components.interfaces.calIExporter)) {
throw Components.results.NS_ERROR_NO_INTERFACE;
calIcsImporter.prototype = {
getInterfaces: function (count) {
const ifaces = [
Components.interfaces.nsISupports,
Components.interfaces.nsIClassInfo,
Components.interfaces.calIImporter,
];
count.value = ifaces.length;
return ifaces;
},
getHelperForLanguage: function (language) {
return null;
},
contractID: "@mozilla.org/calendar/import;1?type=ics",
classDescription: "Calendar ICS Importer",
classID: Components.ID("{1e3e33dc-445a-49de-b2b6-15b2a050bb9d}"),
implementationLanguage: Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
flags: 0,
QueryInterface: function QueryInterface(aIID) {
return cal.doQueryInterface(this, calIcsImporter.prototype, aIID, null, this);
},
getFileTypes: getIcsFileTypes,
importFromStream: function importFromStream(aStream, aCount) {
let parser = Components.classes["@mozilla.org/calendar/ics-parser;1"]
.createInstance(Components.interfaces.calIIcsParser);
parser.parseFromStream(aStream, null);
return parser.getItems(aCount);
}
return this;
};
calIcsExporter.prototype.getFileTypes = getIcsFileTypes;
// Exporter
function calIcsExporter() {
}
// not prototype.export. export is reserved.
calIcsExporter.prototype.exportToStream =
function ics_exportToStream(aStream, aCount, aItems) {
var serializer = Components.classes["@mozilla.org/calendar/ics-serializer;1"].
createInstance(Components.interfaces.calIIcsSerializer);
serializer.addItems(aItems, aItems.length);
serializer.serializeToStream(aStream);
calIcsExporter.prototype = {
getInterfaces: function (count) {
const ifaces = [
Components.interfaces.nsISupports,
Components.interfaces.nsIClassInfo,
Components.interfaces.calIExporter,
];
count.value = ifaces.length;
return ifaces;
},
getHelperForLanguage: function (language) {
return null;
},
contractID: "@mozilla.org/calendar/export;1?type=ics",
classDescription: "Calendar ICS Exporter",
classID: Components.ID("{a6a524ce-adff-4a0f-bb7d-d1aaad4adc60}"),
implementationLanguage: Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
flags: 0,
QueryInterface: function QueryInterface(aIID) {
return cal.doQueryInterface(this, calIcsExporter.prototype, aIID, null, this);
},
getFileTypes: getIcsFileTypes,
exportToStream: function exportToStream(aStream, aCount, aItems) {
let serializer = Components.classes["@mozilla.org/calendar/ics-serializer;1"]
.createInstance(Components.interfaces.calIIcsSerializer);
serializer.addItems(aItems, aItems.length);
serializer.serializeToStream(aStream);
}
};

Просмотреть файл

@ -1,4 +1,3 @@
/* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -38,6 +37,9 @@
*
* ***** END LICENSE BLOCK ***** */
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
const scriptLoadOrder = [
"calIcsImportExport.js",
"calHtmlExport.js",
@ -48,7 +50,6 @@ const scriptLoadOrder = [
"calWeekPrinter.js"
];
function NSGetFactory(cid) {
if (!this.scriptsLoaded) {
Services.io.getProtocolHandler("resource")
@ -70,5 +71,6 @@ function NSGetFactory(cid) {
calMonthPrinter,
calWeekPrinter
];
return (XPCOMUtils.generateNSGetFactory(components))(cid);
}

Просмотреть файл

@ -1,31 +1,31 @@
component {1e3e33dc-445a-49de-b2b6-15b2a050bb9d} calIcsImportExport.js
component {1e3e33dc-445a-49de-b2b6-15b2a050bb9d} calImportExportModule.js
contract @mozilla.org/calendar/import;1?type=ics {1e3e33dc-445a-49de-b2b6-15b2a050bb9d}
category cal-importers cal-ics-importer @mozilla.org/calendar/import;1?type=ics
component {a6a524ce-adff-4a0f-bb7d-d1aaad4adc60} calIcsImportExport.js
component {a6a524ce-adff-4a0f-bb7d-d1aaad4adc60} calImportExportModule.js
contract @mozilla.org/calendar/export;1?type=ics {a6a524ce-adff-4a0f-bb7d-d1aaad4adc60}
category cal-exporters cal-ics-exporter @mozilla.org/calendar/export;1?type=ics
component {72d9ab35-9b1b-442a-8cd0-ae49f00b159b} calHtmlExport.js
component {72d9ab35-9b1b-442a-8cd0-ae49f00b159b} calImportExportModule.js
contract @mozilla.org/calendar/export;1?type=htmllist {72d9ab35-9b1b-442a-8cd0-ae49f00b159b}
category cal-exporters cal-html-list-exporter @mozilla.org/calendar/export;1?type=htmllist
component {9ae04413-fee3-45b9-8bbb-1eb39a4cbd1b} calListFormatter.js
component {9ae04413-fee3-45b9-8bbb-1eb39a4cbd1b} calImportExportModule.js
contract @mozilla.org/calendar/printformatter;1?type=list {9ae04413-fee3-45b9-8bbb-1eb39a4cbd1b}
category cal-print-formatters cal-list-printformatter @mozilla.org/calendar/printformatter;1?type=list
component {f42d5132-92c4-487b-b5c8-38bf292d74c1} calMonthGridPrinter.js
component {f42d5132-92c4-487b-b5c8-38bf292d74c1} calImportExportModule.js
contract @mozilla.org/calendar/printformatter;1?type=monthgrid {f42d5132-92c4-487b-b5c8-38bf292d74c1}
category cal-print-formatters cal-month-printer @mozilla.org/calendar/printformatter;1?type=monthgrid
component {2d6ec97b-9109-4b92-89c5-d4b4806619ce} calWeekPrinter.js
component {2d6ec97b-9109-4b92-89c5-d4b4806619ce} calImportExportModule.js
contract @mozilla.org/calendar/printformatter;1?type=weekplan {2d6ec97b-9109-4b92-89c5-d4b4806619ce}
category cal-print-formatters cal-week-planner-printer @mozilla.org/calendar/printformatter;1?type=weekplan
component {64a5d17a-0497-48c5-b54f-72b15c9e9a14} calOutlookCSVImportExport.js
component {64a5d17a-0497-48c5-b54f-72b15c9e9a14} calImportExportModule.js
contract @mozilla.org/calendar/import;1?type=csv {64a5d17a-0497-48c5-b54f-72b15c9e9a14}
category cal-importers cal-outlookcsv-importer @mozilla.org/calendar/import;1?type=csv
component {48e6d3a6-b41b-4052-9ed2-40b27800bd4b} calOutlookCSVImportExport.js
component {48e6d3a6-b41b-4052-9ed2-40b27800bd4b} calImportExportModule.js
contract @mozilla.org/calendar/export;1?type=csv {48e6d3a6-b41b-4052-9ed2-40b27800bd4b}
category cal-exporters cal-outlookcsv-exporter @mozilla.org/calendar/export;1?type=csv

Просмотреть файл

@ -1,5 +1,4 @@
/* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
/* ***** 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
@ -20,6 +19,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Philipp Kewisch <mozilla@kewis.ch>
*
* 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
@ -36,33 +36,44 @@
* ***** END LICENSE BLOCK ***** */
/**
* A thin wrapper that is a print formatter, and just calls the html (list)
* A thin wrapper that is a print formatter, and just calls the html (list)
* exporter
*/
function calListFormatter() {
this.wrappedJSObject = this;
}
calListFormatter.prototype.QueryInterface =
function QueryInterface(aIID) {
if (!aIID.equals(Components.interfaces.nsISupports) &&
!aIID.equals(Components.interfaces.calIPrintFormatter)) {
throw Components.results.NS_ERROR_NO_INTERFACE;
calListFormatter.prototype = {
getInterfaces: function (count) {
const ifaces = [
Components.interfaces.nsISupports,
Components.interfaces.nsIClassInfo,
Components.interfaces.calIPrintFormatter,
];
count.value = ifaces.length;
return ifaces;
},
getHelperForLanguage: function (language) {
return null;
},
contractID: "@mozilla.org/calendar/printformatter;1?type=list",
classDescription: "Calendar List Print Formatter",
classID: Components.ID("{9ae04413-fee3-45b9-8bbb-1eb39a4cbd1b}"),
implementationLanguage: Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
flags: 0,
QueryInterface: function QueryInterface(aIID) {
return cal.doQueryInterface(this, calListFormatter.prototype, aIID, null, this);
},
get name() {
return cal.calGetString("calendar", "formatListName");
},
formatToHtml: function list_formatToHtml(aStream, aStart, aEnd, aCount, aItems, aTitle) {
let htmlexporter = Components.classes["@mozilla.org/calendar/export;1?type=htmllist"]
.createInstance(Components.interfaces.calIExporter);
htmlexporter.exportToStream(aStream, aCount, aItems, aTitle);
}
return this;
};
calListFormatter.prototype.getName =
function list_getName() {
return calGetString("calendar", "formatListName");
};
calListFormatter.prototype.__defineGetter__("name", calListFormatter.prototype.getName);
calListFormatter.prototype.formatToHtml =
function list_formatToHtml(aStream, aStart, aEnd, aCount, aItems, aTitle) {
var htmlexporter = Components.classes["@mozilla.org/calendar/export;1?type=htmllist"]
.createInstance(Components.interfaces.calIExporter);
htmlexporter.exportToStream(aStream, aCount, aItems, aTitle);
};

Просмотреть файл

@ -1,5 +1,4 @@
/* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
/* ***** 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
@ -16,15 +15,16 @@
*
* The Initial Developer of the Original Code is
* Joey Minta <jminta@gmail.com>
* Diego Mira David <diegomd86@gmail.com>
* Eduardo Teruo Katayama <eduardo@ime.usp.br>
* Glaucus Augustus Grecco Cardoso <glaucus@ime.usp.br>
*
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Matthew Willis <mattwillis@gmail.com>
* Diego Mira David <diegomd86@gmail.com>
* Eduardo Teruo Katayama <eduardo@ime.usp.br>
* Glaucus Augustus Grecco Cardoso <glaucus@ime.usp.br>
* Philipp Kewisch <mozilla@kewis.ch>
*
* 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
@ -45,315 +45,312 @@ Components.utils.import("resource://calendar/modules/calPrintUtils.jsm");
/**
* Prints a rough month-grid of events/tasks
*/
function calMonthPrinter() {
this.wrappedJSObject = this;
}
calMonthPrinter.prototype.QueryInterface =
function QueryInterface(aIID) {
if (!aIID.equals(Components.interfaces.nsISupports) &&
!aIID.equals(Components.interfaces.calIPrintFormatter)) {
throw Components.results.NS_ERROR_NO_INTERFACE;
}
calMonthPrinter.prototype = {
getInterfaces: function (count) {
const ifaces = [
Components.interfaces.nsISupports,
Components.interfaces.nsIClassInfo,
Components.interfaces.calIPrintFormatter,
];
count.value = ifaces.length;
return ifaces;
},
return this;
};
getHelperForLanguage: function (language) {
return null;
},
calMonthPrinter.prototype.getName =
function monthPrint_getName() {
return calGetString("calendar", "monthPrinterName");
};
calMonthPrinter.prototype.__defineGetter__("name", calMonthPrinter.prototype.getName);
contractID: "@mozilla.org/calendar/printformatter;1?type=monthgrid",
classDescription: "Calendar Month Grid Print Formatter",
classID: Components.ID("{f42d5132-92c4-487b-b5c8-38bf292d74c1}"),
implementationLanguage: Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
flags: 0,
calMonthPrinter.prototype.formatToHtml =
function monthPrint_format(aStream, aStart, aEnd, aCount, aItems, aTitle) {
var html = <html/>
html.appendChild(
<head>
<title>{aTitle}</title>
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>
<link rel='stylesheet' type='text/css' href='chrome://calendar/skin/calendar-printing.css'/>
</head>);
html.head.style = ".main-table { font-size: 26px; font-weight: bold; }\n";
html.head.style += ".day-name { border: 1px solid black; background-color: white; font-size: 12px; font-weight: bold; }\n";
html.head.style += ".day-box { border: 1px solid black; vertical-align: top; }\n";
html.head.style += ".out-of-month { background-color: white !important; }\n";
html.head.style += ".day-off { background-color: white !important; }\n";
QueryInterface: function QueryInterface(aIID) {
return cal.doQueryInterface(this, calMonthPrinter.prototype, aIID, null, this);
},
// If aStart or aEnd weren't passed in, we need to calculate them based on
// aItems data.
get name() {
return cal.calGetString("calendar", "monthPrinterName");
},
var start = aStart;
var end = aEnd;
if (!start || !end) {
for each (var item in aItems) {
var itemStart = item.startDate || item.entryDate;
var itemEnd = item.endDate || item.dueDate;
if (!start || (itemStart && start.compare(itemStart) == 1)) {
start = itemStart;
}
if (!end || (itemEnd && end.compare(itemEnd) == -1)) {
end = itemEnd;
formatToHtml: function monthPrint_format(aStream, aStart, aEnd, aCount, aItems, aTitle) {
let html = <html/>
html.appendChild(
<head>
<title>{aTitle}</title>
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>
<link rel='stylesheet' type='text/css' href='chrome://calendar/skin/calendar-printing.css'/>
</head>);
html.head.style = ".main-table { font-size: 26px; font-weight: bold; }\n";
html.head.style += ".day-name { border: 1px solid black; background-color: white; font-size: 12px; font-weight: bold; }\n";
html.head.style += ".day-box { border: 1px solid black; vertical-align: top; }\n";
html.head.style += ".out-of-month { background-color: white !important; }\n";
html.head.style += ".day-off { background-color: white !important; }\n";
// If aStart or aEnd weren't passed in, we need to calculate them based on
// aItems data.
let start = aStart;
let end = aEnd;
if (!start || !end) {
for each (let item in aItems) {
let itemStart = item[cal.calGetStartDateProp(item)];
let itemEnd = item[cal.calGetEndDateProp(item)];
if (!start || (itemStart && start.compare(itemStart) == 1)) {
start = itemStart;
}
if (!end || (itemEnd && end.compare(itemEnd) == -1)) {
end = itemEnd;
}
}
}
}
// Play around with aStart and aEnd to determine the minimal number of
// months we can show to still technically meet their requirements. This
// is most useful when someone printed 'Current View' in the month view. If
// we take the aStart and aEnd literally, we'll print 3 months (because of
// the extra days at the start/end), but we should avoid that.
//
// Basically, we check whether aStart falls in the same week as the start
// of a month (ie aStart is Jan 29, which often is in the same week as
// Feb 1), and similarly whether aEnd falls in the same week as the end of
// a month.
var weekStart = getPrefSafe("calendar.week.start", 0);
maybeNewStart = start.clone();
maybeNewStart.day = 1;
maybeNewStart.month = start.month+1;
// Play around with aStart and aEnd to determine the minimal number of
// months we can show to still technically meet their requirements. This
// is most useful when someone printed 'Current View' in the month view. If
// we take the aStart and aEnd literally, we'll print 3 months (because of
// the extra days at the start/end), but we should avoid that.
//
// Basically, we check whether aStart falls in the same week as the start
// of a month (ie aStart is Jan 29, which often is in the same week as
// Feb 1), and similarly whether aEnd falls in the same week as the end of
// a month.
let weekStart = cal.getPrefSafe("calendar.week.start", 0);
maybeNewStart = start.clone();
maybeNewStart.day = 1;
maybeNewStart.month = start.month+1;
var date = start.clone();
let dt = start.clone();
// First we have to adjust the end date for comparison, as the
// provided end date is exclusive, i.e. will not be displayed.
// First we have to adjust the end date for comparison, as the
// provided end date is exclusive, i.e. will not be displayed.
var realEnd = end.clone();
realEnd.day -= 1;
let realEnd = end.clone();
realEnd.day -= 1;
if (start.compare(realEnd) <= 0) {
// Only adjust dates if start date is earlier than end date.
if (start.compare(realEnd) <= 0) {
// Only adjust dates if start date is earlier than end date.
if ((start.month != realEnd.month) || (start.year != realEnd.year)) {
// We only need to adjust if start and end are in different months.
if ((start.month != realEnd.month) || (start.year != realEnd.year)) {
// We only need to adjust if start and end are in different months.
// We want to check whether or not the start day is in the same
// week as the beginning of the next month. To do this, we take
// the start date, add seven days and subtract the "day of week"
// value (which has to be corrected in case we do not start on
// Sunday).
var testBegin = start.clone();
var startWeekday = testBegin.weekday;
if (startWeekday < weekStart) {
startWeekday += 7;
// We want to check whether or not the start day is in the same
// week as the beginning of the next month. To do this, we take
// the start date, add seven days and subtract the "day of week"
// value (which has to be corrected in case we do not start on
// Sunday).
let testBegin = start.clone();
let startWeekday = testBegin.weekday;
if (startWeekday < weekStart) {
startWeekday += 7;
}
testBegin.day += 7 + weekStart - startWeekday;
if (testBegin.compare(maybeNewStart) > 0) {
start = maybeNewStart;
dt = start.clone();
}
}
testBegin.day += 7 + weekStart - startWeekday;
if (testBegin.compare(maybeNewStart) > 0) {
start = maybeNewStart;
date = start.clone();
}
}
if ((start.month != realEnd.month) || (start.year != realEnd.year)) {
// We only need to adjust if start and end are in different months.
if ((start.month != realEnd.month) || (start.year != realEnd.year)) {
// We only need to adjust if start and end are in different months.
// Next, we want to check whether or not the end day is in the same
// week as the end of the previous month. So we have to get the
// "day of week" value for the end of the previous month, adjust it
// if necessary (when start of week is not Sunday) and check if the
// end day is in the same week.
// Next, we want to check whether or not the end day is in the same
// week as the end of the previous month. So we have to get the
// "day of week" value for the end of the previous month, adjust it
// if necessary (when start of week is not Sunday) and check if the
// end day is in the same week.
var lastDayOfPreviousMonth = end.clone();
lastDayOfPreviousMonth.day = 0;
var lastDayWeekday = lastDayOfPreviousMonth.weekday;
if (lastDayWeekday < weekStart) {
lastDayWeekday += 7;
}
if (date.month != end.month) {
date.day = 1;
}
if ((lastDayWeekday + end.day - 1) < (7 + weekStart)) {
date.day = end.day;
}
let lastDayOfPreviousMonth = end.clone();
lastDayOfPreviousMonth.day = 0;
let lastDayWeekday = lastDayOfPreviousMonth.weekday;
if (lastDayWeekday < weekStart) {
lastDayWeekday += 7;
}
if (dt.month != end.month) {
dt.day = 1;
}
if ((lastDayWeekday + end.day - 1) < (7 + weekStart)) {
dt.day = end.day;
}
// Finally, we have to check whether we adjusted the dates too
// well so that nothing is printed. That happens if you print just
// one week which has the last day of a month in it.
// Finally, we have to check whether we adjusted the dates too
// well so that nothing is printed. That happens if you print just
// one week which has the last day of a month in it.
if (date.compare(end) >= 0) {
date.day = 1;
if (dt.compare(end) >= 0) {
dt.day = 1;
}
} else {
dt.day = 1;
}
} else {
date.day = 1;
// If start date is after end date, just print empty month.
dt = realEnd.clone();
}
} else {
// If start date is after end date, just print empty month.
date = realEnd.clone();
}
var body = <body/>
let body = <body/>
while (date.compare(end) < 0) {
var monthName = calGetString("dateFormat", "month." + (date.month +1)+ ".name");
monthName += " " + date.year;
body.appendChild(
<table border='0' width='100%' class='main-table'>
<tr>
<td align='center' valign='bottom'>{monthName}</td>
</tr>
</table>);
body.appendChild(this.getStringForMonth(date, aItems));
// Make sure each month gets put on its own page
body.appendChild(<br style="page-break-after:always;"/>);
date.month++;
while (dt.compare(end) < 0) {
let monthName = cal.calGetString("dateFormat", "month." + (dt.month +1) + ".name");
monthName += " " + dt.year;
body.appendChild(
<table border='0' width='100%' class='main-table'>
<tr>
<td align='center' valign='bottom'>{monthName}</td>
</tr>
</table>);
body.appendChild(this.getStringForMonth(dt, aItems));
// Make sure each month gets put on its own page
body.appendChild(<br style="page-break-after:always;"/>);
dt.month++;
}
let tasks = cal.print.getTasksWithoutDueDate(aItems, dt);
body.appendChild(tasks);
html.appendChild(body);
let convStream = Components.classes["@mozilla.org/intl/converter-output-stream;1"]
.createInstance(Components.interfaces.nsIConverterOutputStream);
convStream.init(aStream, 'UTF-8', 0, 0x0000);
convStream.writeString(html.toXMLString());
},
getStringForMonth: function monthPrint_getStringForMonth(aStart, aItems) {
let weekStart = cal.getPrefSafe("calendar.week.start", 0);
let monthTable = <table style='border:1px solid black;' width='100%'/>
let dayNameRow = <tr/>
for (let i = 0; i < 7; i++) {
let dayName = cal.calGetString("dateFormat", "day."+ (((weekStart+i)%7)+1) + ".Mmm");
dayNameRow.appendChild(<td class='day-name' align='center'>{dayName}</td>);
}
monthTable.appendChild(dayNameRow);
// Set up the item-list so it's easy to work with.
function hasUsableDate(item) item.startDate || item.entryDate || item.dueDate;
let filteredItems = aItems.filter(hasUsableDate);
function compareItems(a, b) {
// Sort tasks before events
if (cal.isEvent(a) && cal.isToDo(b)) {
return 1;
}
if (cal.isToDo(a) && cal.isEvent(b)) {
return -1;
}
if (cal.isEvent(a)) {
let startCompare = a.startDate.compare(b.startDate);
if (startCompare != 0) {
return startCompare;
}
return a.endDate.compare(b.endDate);
}
let aDate = a.entryDate || a.dueDate;
let bDate = b.entryDate || b.dueDate;
return aDate.compare(bDate);
}
let sortedList = filteredItems.sort(compareItems);
let firstDate = aStart.startOfMonth.startOfWeek.clone();
firstDate.day += weekStart;
if (aStart.startOfMonth.weekday < weekStart) {
// Go back one week to make sure we display this day
firstDate.day -= 7;
}
let lastDate = aStart.endOfMonth.endOfWeek.clone();
if (aStart.endOfMonth.weekday < weekStart) {
// Go back one week so we don't display any extra days
lastDate.day -= 7;
}
firstDate.isDate = true;
lastDate.isDate = true;
let dt = firstDate.clone();
let itemListIndex = 0;
while (dt.compare(lastDate) != 1) {
monthTable.appendChild(this.makeHTMLWeek(dt, sortedList, aStart.month));
}
return monthTable;
},
makeHTMLWeek: function makeHTMLWeek(dt, sortedList, targetMonth) {
let weekRow = <tr/>;
const weekPrefix = "calendar.week.";
let prefNames = ["d0sundaysoff", "d1mondaysoff", "d2tuesdaysoff",
"d3wednesdaysoff", "d4thursdaysoff", "d5fridaysoff", "d6saturdaysoff"];
let defaults = [true, false, false, false, false, false, true];
let daysOff = [];
for (let i in prefNames) {
if (cal.getPrefSafe(weekPrefix+prefNames[i], defaults[i])) {
daysOff.push(Number(i));
}
}
for (let i = 0; i < 7; i++) {
let myClass = 'day-box';
if (dt.month != targetMonth) {
myClass += ' out-of-month';
} else if (daysOff.some(function(a) { return a == dt.weekday; })) {
myClass += ' day-off';
}
let day = <td align='left' valign='top' class={myClass} height='100' width='100'/>
let innerTable = <table valign='top' style='font-size: 10px;'/>
let dateLabel = <tr valign='top'>
<td valign='top' align='left'>{dt.day}</td>
</tr>
innerTable.appendChild(dateLabel);
let defaultTimezone = calendarDefaultTimezone();
for each (let item in sortedList) {
let sDate = item.startDate || item.entryDate || item.dueDate;
let eDate = item.endDate || item.dueDate || item.entryDate;
if (sDate) {
sDate = sDate.getInTimezone(defaultTimezone);
}
if (eDate) {
eDate = eDate.getInTimezone(defaultTimezone);
}
// end dates are exclusive
if (sDate.isDate) {
eDate = eDate.clone();
eDate.day -= 1;
}
if (!eDate || eDate.compare(dt) == -1) {
continue;
}
itemListIndex = i;
if (!sDate || sDate.compare(dt) == 1) {
break;
}
let time = (!s.isDate ? cal.getDateFormatter().formatTime(sDate) : "");
let calColor = item.calendar.getProperty('color') || "#A8C2E1";
let pb2 = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch2);
let catColor;
for each (let cat in item.getCategories({})) {
try {
catColor = pb2.getCharPref("calendar.category.color." + cat.toLowerCase());
break; // take first matching
} catch (ex) {}
}
let style = 'font-size: 11px; text-align: left;';
style += ' background-color: ' + calColor + ';';
style += ' color: ' + getContrastingTextColor(calColor);
if (catColor) {
style += ' border: solid ' + catColor + ' 2px;';
}
let item = <tr>
<td valign='top' style={style}>{time} {item.title}</td>
</tr>;
innerTable.appendChild(item);
}
day.appendChild(innerTable);
weekRow.appendChild(day);
dt.day++;
}
return weekRow;
}
let tasks = cal.print.getTasksWithoutDueDate(aItems, date);
body.appendChild(tasks);
html.appendChild(body);
let convStream = Components.classes["@mozilla.org/intl/converter-output-stream;1"].createInstance(Components.interfaces.nsIConverterOutputStream);
convStream.init(aStream, 'UTF-8', 0, 0x0000);
convStream.writeString(html.toXMLString());
};
calMonthPrinter.prototype.getStringForMonth =
function monthPrint_getHTML(aStart, aItems) {
var weekStart = getPrefSafe("calendar.week.start", 0);
var monthTable = <table style='border:1px solid black;' width='100%'/>
var dayNameRow = <tr/>
for (var i = 0; i < 7; i++) {
var dayName = calGetString("dateFormat", "day."+ (((weekStart+i)%7)+1) + ".Mmm");
dayNameRow.appendChild(<td class='day-name' align='center'>{dayName}</td>);
}
monthTable.appendChild(dayNameRow);
// Set up the item-list so it's easy to work with.
function hasUsableDate(item) {
return item.startDate || item.entryDate || item.dueDate;
}
var filteredItems = aItems.filter(hasUsableDate);
var calIEvent = Components.interfaces.calIEvent;
var calITodo = Components.interfaces.calITodo
function compareItems(a, b) {
// Sort tasks before events
if (isEvent(a) && isToDo(b)) {
return 1;
}
if (isToDo(a) && isEvent(b)) {
return -1;
}
if (isEvent(a)) {
var startCompare = a.startDate.compare(b.startDate);
if (startCompare != 0) {
return startCompare;
}
return a.endDate.compare(b.endDate);
}
var aDate = a.entryDate || a.dueDate;
var bDate = b.entryDate || b.dueDate;
return aDate.compare(bDate);
}
var sortedList = filteredItems.sort(compareItems);
var firstDate = aStart.startOfMonth.startOfWeek.clone();
firstDate.day += weekStart;
if (aStart.startOfMonth.weekday < weekStart) {
// Go back one week to make sure we display this day
firstDate.day -= 7;
}
var lastDate = aStart.endOfMonth.endOfWeek.clone();
if (aStart.endOfMonth.weekday < weekStart) {
// Go back one week so we don't display any extra days
lastDate.day -= 7;
}
firstDate.isDate = true;
lastDate.isDate = true;
var date = firstDate.clone();
var itemListIndex = 0;
while (date.compare(lastDate) != 1) {
monthTable.appendChild(this.makeHTMLWeek(date, sortedList, aStart.month));
}
return monthTable;
};
calMonthPrinter.prototype.makeHTMLWeek =
function makeHTMLWeek(date, sortedList, targetMonth) {
var weekRow = <tr/>;
const weekPrefix = "calendar.week.";
var prefNames = ["d0sundaysoff", "d1mondaysoff", "d2tuesdaysoff",
"d3wednesdaysoff", "d4thursdaysoff", "d5fridaysoff", "d6saturdaysoff"];
var defaults = [true, false, false, false, false, false, true];
var daysOff = new Array();
for (var i in prefNames) {
if (getPrefSafe(weekPrefix+prefNames[i], defaults[i])) {
daysOff.push(Number(i));
}
}
for (var i = 0; i < 7; i++) {
var myClass = 'day-box';
if (date.month != targetMonth) {
myClass += ' out-of-month';
} else if (daysOff.some(function(a) { return a == date.weekday; })) {
myClass += ' day-off';
}
var day = <td align='left' valign='top' class={myClass} height='100' width='100'/>
var innerTable = <table valign='top' style='font-size: 10px;'/>
var dateLabel = <tr valign='top'>
<td valign='top' align='left'>{date.day}</td>
</tr>
innerTable.appendChild(dateLabel);
var defaultTimezone = calendarDefaultTimezone();
for each (var item in sortedList) {
var sDate = item.startDate || item.entryDate || item.dueDate;
var eDate = item.endDate || item.dueDate || item.entryDate;
if (sDate) {
sDate = sDate.getInTimezone(defaultTimezone);
}
if (eDate) {
eDate = eDate.getInTimezone(defaultTimezone);
}
// end dates are exclusive
if (sDate.isDate) {
eDate = eDate.clone();
eDate.day -= 1;
}
if (!eDate || eDate.compare(date) == -1) {
continue;
}
itemListIndex = i;
if (!sDate || sDate.compare(date) == 1) {
break;
}
var dateFormatter =
Components.classes["@mozilla.org/calendar/datetime-formatter;1"]
.getService(Components.interfaces.calIDateTimeFormatter);
var time = "";
if (!sDate.isDate) {
time = dateFormatter.formatTime(sDate);
}
var calColor = item.calendar.getProperty('color');
if (!calColor) {
calColor = "#A8C2E1";
}
var pb2 = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch2);
var catColor;
for each (var cat in item.getCategories({})) {
try {
catColor = pb2.getCharPref("calendar.category.color." + cat.toLowerCase());
break; // take first matching
} catch(ex) {}
}
var style = 'font-size: 11px; text-align: left;';
style += ' background-color: ' + calColor + ';';
style += ' color: ' + getContrastingTextColor(calColor);
if (catColor) {
style += ' border: solid ' + catColor + ' 2px;';
}
var item = <tr>
<td valign='top' style={style}>{time} {item.title}</td>
</tr>;
innerTable.appendChild(item);
}
day.appendChild(innerTable);
weekRow.appendChild(day);
date.day++;
}
return weekRow;
};

Просмотреть файл

@ -1,5 +1,4 @@
/* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
/* ***** 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
@ -40,33 +39,6 @@
Components.utils.import("resource://calendar/modules/calAlarmUtils.jsm");
// Import
function calOutlookCSVImporter() {
this.wrappedJSObject = this;
}
calOutlookCSVImporter.prototype.QueryInterface =
function QueryInterface(aIID) {
if (!aIID.equals(Components.interfaces.nsISupports) &&
!aIID.equals(Components.interfaces.calIImporter)) {
throw Components.results.NS_ERROR_NO_INTERFACE;
}
return this;
};
function getOutlookCsvFileTypes(aCount) {
aCount.value = 1;
var wildmat = '*.csv';
var label = calGetString("calendar", 'filterOutlookCsv', [wildmat]);
return([{defaultExtension:'csv',
extensionFilter: wildmat,
description: label}]);
}
calOutlookCSVImporter.prototype.getFileTypes = getOutlookCsvFileTypes;
const localeEn = {
headTitle : "Subject",
headStartDate : "Start Date",
@ -84,7 +56,7 @@ const localeEn = {
valueTrue : "True",
valueFalse : "False",
dateRe : /^(\d+)\/(\d+)\/(\d+)$/,
dateDayIndex : 2,
dateMonthIndex : 1,
@ -118,7 +90,7 @@ const localeNl = {
valueTrue : "Waar",
valueFalse : "Onwaar",
dateRe : /^(\d+)-(\d+)-(\d+)$/,
dateDayIndex : 1,
dateMonthIndex : 2,
@ -137,414 +109,451 @@ const locales = [localeEn, localeNl];
// Windows line endings, CSV files with LF only can't be read by Outlook.
const exportLineEnding = "\r\n";
/**
* Takes a text block of Outlook-exported Comma Separated Values and tries to
* parse that into individual events.
*
* First line is field names, all quoted with double quotes. Field names are
* locale dependendent. In English the recognized field names are:
* "Title","Start Date","Start Time","End Date","End Time","All day event",
* "Reminder on/off","Reminder Date","Reminder Time","Categories",
* "Description","Location","Private"
* The fields "Title" and "Start Date" are mandatory. If "Start Time" misses
* the event is set as all day event. If "End Date" or "End Time" miss the
* default durations are set.
*
* The rest of the lines are events, one event per line, with fields in the
* order descibed by the first line. All non-empty values must be quoted.
*
* Returns: an array of parsed calendarEvents.
* If the parse is cancelled, a zero length array is returned.
*/
// Shared functions
function getOutlookCsvFileTypes(aCount) {
aCount.value = 1;
let wildmat = '*.csv';
let label = cal.calGetString("calendar", 'filterOutlookCsv', [wildmat]);
return [{ defaultExtension: 'csv',
extensionFilter: wildmat,
description: label }];
}
calOutlookCSVImporter.prototype.importFromStream =
function csv_importFromStream(aStream, aCount) {
var scriptableInputStream = Components.classes["@mozilla.org/scriptableinputstream;1"]
.createInstance(Components.interfaces.nsIScriptableInputStream);
scriptableInputStream.init(aStream);
var str = scriptableInputStream.read(-1);
// Importer
function calOutlookCSVImporter() {
}
parse: {
// parse header line of quoted comma separated column names.
var trimEndQuotesRegExp = /^"(.*)"$/m;
var trimResults = trimEndQuotesRegExp.exec( str );
var header = trimResults && trimResults[1].split(/","/);
if (header == null)
break parse;
calOutlookCSVImporter.prototype = {
getInterfaces: function (count) {
const ifaces = [
Components.interfaces.nsISupports,
Components.interfaces.nsIClassInfo,
Components.interfaces.calIImporter,
];
count.value = ifaces.length;
return ifaces;
},
//strip header from string
str = str.slice(trimResults[0].length);
getHelperForLanguage: function (language) {
return null;
},
var args = new Object();
//args.fieldList contains the field names from the first row of CSV
args.fieldList = header;
contractID: "@mozilla.org/calendar/import;1?type=csv",
classDescription: "Calendar Outlook CSV Importer",
classID: Components.ID("{64a5d17a-0497-48c5-b54f-72b15c9e9a14}"),
implementationLanguage: Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
flags: 0,
var locale;
var i;
for (i in locales) {
locale = locales[i];
var knownIndxs = 0;
args.titleIndex = 0;
args.startDateIndex = 0;
for (var i = 1; i <= header.length; ++i) {
switch( header[i-1] ) {
case locale.headTitle: args.titleIndex = i; knownIndxs++; break;
case locale.headStartDate: args.startDateIndex = i; knownIndxs++; break;
case locale.headStartTime: args.startTimeIndex = i; knownIndxs++; break;
case locale.headEndDate: args.endDateIndex = i; knownIndxs++; break;
case locale.headEndTime: args.endTimeIndex = i; knownIndxs++; break;
case locale.headAllDayEvent: args.allDayIndex = i; knownIndxs++; break;
case locale.headAlarm: args.alarmIndex = i; knownIndxs++; break;
case locale.headAlarmDate: args.alarmDateIndex = i; knownIndxs++; break;
case locale.headAlarmTime: args.alarmTimeIndex = i; knownIndxs++; break;
case locale.headCategories: args.categoriesIndex = i; knownIndxs++; break;
case locale.headDescription: args.descriptionIndex = i; knownIndxs++; break;
case locale.headLocation: args.locationIndex = i; knownIndxs++; break;
case locale.headPrivate: args.privateIndex = i; knownIndxs++; break;
QueryInterface: function QueryInterface(aIID) {
return cal.doQueryInterface(this, calOutlookCSVImporter.prototype, aIID, null, this);
},
getFileTypes: getOutlookCsvFileTypes,
/**
* Takes a text block of Outlook-exported Comma Separated Values and tries to
* parse that into individual events.
*
* First line is field names, all quoted with double quotes. Field names are
* locale dependendent. In English the recognized field names are:
* "Title","Start Date","Start Time","End Date","End Time","All day event",
* "Reminder on/off","Reminder Date","Reminder Time","Categories",
* "Description","Location","Private"
* The fields "Title" and "Start Date" are mandatory. If "Start Time" misses
* the event is set as all day event. If "End Date" or "End Time" miss the
* default durations are set.
*
* The rest of the lines are events, one event per line, with fields in the
* order descibed by the first line. All non-empty values must be quoted.
*
* Returns: an array of parsed calendarEvents.
* If the parse is cancelled, a zero length array is returned.
*/
importFromStream: function csv_importFromStream(aStream, aCount) {
let scriptableInputStream = Components.classes["@mozilla.org/scriptableinputstream;1"]
.createInstance(Components.interfaces.nsIScriptableInputStream);
scriptableInputStream.init(aStream);
let str = scriptableInputStream.read(-1);
parse: {
// parse header line of quoted comma separated column names.
let trimEndQuotesRegExp = /^"(.*)"$/m;
let trimResults = trimEndQuotesRegExp.exec( str );
let header = trimResults && trimResults[1].split(/","/);
if (header == null) {
break parse;
}
//strip header from string
str = str.slice(trimResults[0].length);
let args = {};
//args.fieldList contains the field names from the first row of CSV
args.fieldList = header;
let locale;
let i;
for (i in locales) {
locale = locales[i];
let knownIndxs = 0;
args.titleIndex = 0;
args.startDateIndex = 0;
for (let i = 1; i <= header.length; ++i) {
switch( header[i-1] ) {
case locale.headTitle: args.titleIndex = i; knownIndxs++; break;
case locale.headStartDate: args.startDateIndex = i; knownIndxs++; break;
case locale.headStartTime: args.startTimeIndex = i; knownIndxs++; break;
case locale.headEndDate: args.endDateIndex = i; knownIndxs++; break;
case locale.headEndTime: args.endTimeIndex = i; knownIndxs++; break;
case locale.headAllDayEvent: args.allDayIndex = i; knownIndxs++; break;
case locale.headAlarm: args.alarmIndex = i; knownIndxs++; break;
case locale.headAlarmDate: args.alarmDateIndex = i; knownIndxs++; break;
case locale.headAlarmTime: args.alarmTimeIndex = i; knownIndxs++; break;
case locale.headCategories: args.categoriesIndex = i; knownIndxs++; break;
case locale.headDescription: args.descriptionIndex = i; knownIndxs++; break;
case locale.headLocation: args.locationIndex = i; knownIndxs++; break;
case locale.headPrivate: args.privateIndex = i; knownIndxs++; break;
}
}
// Were both mandatory fields recognized?
if (args.titleIndex != 0 && args.startDateIndex != 0) {
break;
}
}
// Were both mandatory fields recognized?
if (args.titleIndex != 0 && args.startDateIndex != 0) {
break;
if (knownIndxs == 0 && header.length == 22) {
// set default indexes for a default Outlook2000 CSV file
args.titleIndex = 1;
args.startDateIndex = 2;
args.startTimeIndex = 3;
args.endDateIndex = 4;
args.endTimeIndex = 5;
args.allDayIndex = 6;
args.alarmIndex = 7;
args.alarmDateIndex = 8;
args.alarmTimeIndex = 9;
args.categoriesIndex = 15;
args.descriptionIndex = 16;
args.locationIndex = 17;
args.privateIndex = 20;
}
}
if (knownIndxs == 0 && header.length == 22) {
// set default indexes for a default Outlook2000 CSV file
args.titleIndex = 1;
args.startDateIndex = 2;
args.startTimeIndex = 3;
args.endDateIndex = 4;
args.endTimeIndex = 5;
args.allDayIndex = 6;
args.alarmIndex = 7;
args.alarmDateIndex = 8;
args.alarmTimeIndex = 9;
args.categoriesIndex = 15;
args.descriptionIndex = 16;
args.locationIndex = 17;
args.privateIndex = 20;
}
if (args.titleIndex == 0 || args.startDateIndex == 0) {
dump("Can't import. Life sucks\n")
break parse;
}
if (args.titleIndex == 0 || args.startDateIndex == 0) {
dump("Can't import. Life sucks\n")
break parse;
}
// Construct event regexp according to field indexes. The regexp can
// be made stricter, if it seems this matches too loosely.
let regExpStr = "^";
for (i = 1; i <= header.length; i++) {
if (i > 1)
regExpStr += ",";
regExpStr += "(?:\"((?:[^\"]|\"\")*)\")?";
}
regExpStr += "$";
// Construct event regexp according to field indexes. The regexp can
// be made stricter, if it seems this matches too loosely.
var regExpStr = "^";
for (i = 1; i <= header.length; i++) {
if (i > 1)
regExpStr += ",";
regExpStr += "(?:\"((?:[^\"]|\"\")*)\")?";
}
regExpStr += "$";
// eventRegExp: regexp for reading events (this one'll be constructed on fly)
const eventRegExp = new RegExp(regExpStr, "gm");
// eventRegExp: regexp for reading events (this one'll be constructed on fly)
const eventRegExp = new RegExp(regExpStr, "gm");
// match first line
let eventFields = eventRegExp(str);
// match first line
var eventFields = eventRegExp(str);
if (eventFields == null)
break parse;
if (eventFields == null)
break parse;
args.boolStr = localeEn.valueTrue;
args.boolIsTrue = true;
args.boolStr = localeEn.valueTrue;
args.boolIsTrue = true;
let dateParseConfirmed = false;
let eventArray = new Array();
do {
// At this point eventFields contains following fields. Position
// of fields is in args.[fieldname]Index.
// subject, start date, start time, end date, end time,
// all day, alarm on, alarm date, alarm time,
// Description, Categories, Location, Private
// Unused fields (could maybe be copied to Description):
// Meeting Organizer, Required Attendees, Optional Attendees,
// Meeting Resources, Billing Information, Mileage, Priority,
// Sensitivity, Show time as
var dateParseConfirmed = false;
var eventArray = new Array();
do {
// At this point eventFields contains following fields. Position
// of fields is in args.[fieldname]Index.
// subject, start date, start time, end date, end time,
// all day, alarm on, alarm date, alarm time,
// Description, Categories, Location, Private
// Unused fields (could maybe be copied to Description):
// Meeting Organizer, Required Attendees, Optional Attendees,
// Meeting Resources, Billing Information, Mileage, Priority,
// Sensitivity, Show time as
let title = ("titleIndex" in args
? this.parseTextField(eventFields[args.titleIndex]) : "");
let sDate = this.parseDateTime(eventFields[args.startDateIndex],
eventFields[args.startTimeIndex],
locale);
let eDate = this.parseDateTime(eventFields[args.endDateIndex],
eventFields[args.endTimeIndex],
locale);
// Create an event only if we have a startDate. No more checks
// on sDate needed in the following process.
if (sDate) {
let event = cal.createEvent();
var title = ("titleIndex" in args
? parseTextField(eventFields[args.titleIndex]) : "");
var sDate = parseDateTime(eventFields[args.startDateIndex],
eventFields[args.startTimeIndex],
locale);
var eDate = parseDateTime(eventFields[args.endDateIndex],
eventFields[args.endTimeIndex],
locale);
// Create an event only if we have a startDate. No more checks
// on sDate needed in the following process.
if (sDate) {
var event = createEvent();
// Use column head in brackets if event title misses in data.
if (title) {
event.title = title;
} else {
event.title = "[" + locale.headTitle + "]";
}
// Check data for all day event. Additionally sDate.isDate
// may have been set in parseDateTime() if no time was found
if (eventFields[args.allDayIndex] == locale.valueTrue) {
sDate.isDate = true;
}
if (locale.valueTrue == eventFields[args.privateIndex])
event.privacy = "PRIVATE";
if (!eDate) {
// No endDate was found. All day events last one day and
// timed events last the default length.
eDate = sDate.clone();
if (sDate.isDate) {
// end date is exclusive, so set to next day after start.
eDate.day += 1;
// Use column head in brackets if event title misses in data.
if (title) {
event.title = title;
} else {
eDate.minute += getPrefSafe(
"calendar.event.defaultlength", 60);
event.title = "[" + locale.headTitle + "]";
}
} else {
// An endDate was found.
if (sDate.isDate) {
// A time part for the startDate is missing or was
// not recognized. We have to throw away the endDates
// time part too for obtaining a valid event.
eDate.isDate = true;
// Correct the eDate if duration is less than one day.
if (1 > eDate.subtractDate(sDate).days) {
eDate = sDate.clone();
eDate.day += 1;
}
} else {
// We now have a timed startDate and an endDate. If the
// end time is invalid set it to 23:59:00
if (eDate.isDate) {
eDate.isDate = false;
eDate.hour = 23;
eDate.minute = 59;
}
// Correct the duration to 0 seconds if it is negative.
if (eDate.subtractDate(sDate).isNegative ) {
eDate = sDate.clone();
}
}
}
event.startDate = sDate;
event.endDate = eDate;
// Exists an alarm true/false column?
if ("alarmIndex" in args) {
// Is an alarm wanted for this event?
if (locale.valueTrue == eventFields[args.alarmIndex]) {
let alarmDate =
parseDateTime(eventFields[args.alarmDateIndex],
eventFields[args.alarmTimeIndex],
locale);
// Only set the alarm if a date was parsed
if (alarmDate) {
let alarm = cal.createAlarm();
alarm.related = alarm.ALARM_RELATED_ABSOLUTE;
alarm.alarmDate = alarmDate;
event.addAlarm(alarm);
} else {
// XXX Is this really wanted here?
cal.alarms.setDefaultValues(event);
}
// Check data for all day event. Additionally sDate.isDate
// may have been set in parseDateTime() if no time was found
if (eventFields[args.allDayIndex] == locale.valueTrue) {
sDate.isDate = true;
}
}
// Using the "Private" field only for getting privacy status.
// "Sensitivity" is neglected for now.
if ("privateIndex" in args) {
if (locale.valueTrue == eventFields[args.privateIndex]) {
event.privacy = "PRIVATE";
}
}
// Avoid setting empty properties
var txt = "";
if ("descriptionIndex" in args) {
txt = parseTextField(eventFields[args.descriptionIndex])
if (txt) {
event.setProperty("DESCRIPTION", txt);
if (!eDate) {
// No endDate was found. All day events last one day and
// timed events last the default length.
eDate = sDate.clone();
if (sDate.isDate) {
// end date is exclusive, so set to next day after start.
eDate.day += 1;
} else {
eDate.minute += cal.getPrefSafe("calendar.event.defaultlength", 60);
}
} else {
// An endDate was found.
if (sDate.isDate) {
// A time part for the startDate is missing or was
// not recognized. We have to throw away the endDates
// time part too for obtaining a valid event.
eDate.isDate = true;
// Correct the eDate if duration is less than one day.
if (1 > eDate.subtractDate(sDate).days) {
eDate = sDate.clone();
eDate.day += 1;
}
} else {
// We now have a timed startDate and an endDate. If the
// end time is invalid set it to 23:59:00
if (eDate.isDate) {
eDate.isDate = false;
eDate.hour = 23;
eDate.minute = 59;
}
// Correct the duration to 0 seconds if it is negative.
if (eDate.subtractDate(sDate).isNegative ) {
eDate = sDate.clone();
}
}
}
}
if ("categoriesIndex" in args) {
txt = parseTextField(eventFields[args.categoriesIndex])
if (txt) {
var categories = categoriesStringToArray(txt);
event.setCategories(categories.length, categories);
event.startDate = sDate;
event.endDate = eDate;
// Exists an alarm true/false column?
if ("alarmIndex" in args) {
// Is an alarm wanted for this event?
if (locale.valueTrue == eventFields[args.alarmIndex]) {
let alarmDate =
this.parseDateTime(eventFields[args.alarmDateIndex],
eventFields[args.alarmTimeIndex],
locale);
// Only set the alarm if a date was parsed
if (alarmDate) {
let alarm = cal.createAlarm();
alarm.related = alarm.ALARM_RELATED_ABSOLUTE;
alarm.alarmDate = alarmDate;
event.addAlarm(alarm);
} else {
// XXX Is this really wanted here?
cal.alarms.setDefaultValues(event);
}
}
}
}
if ("locationIndex" in args) {
txt = parseTextField(eventFields[args.locationIndex])
if (txt) {
event.setProperty("LOCATION", txt);
// Using the "Private" field only for getting privacy status.
// "Sensitivity" is neglected for now.
if ("privateIndex" in args) {
if (locale.valueTrue == eventFields[args.privateIndex]) {
event.privacy = "PRIVATE";
}
}
// Avoid setting empty properties
let txt = "";
if ("descriptionIndex" in args) {
txt = this.parseTextField(eventFields[args.descriptionIndex])
if (txt) {
event.setProperty("DESCRIPTION", txt);
}
}
if ("categoriesIndex" in args) {
txt = this.parseTextField(eventFields[args.categoriesIndex])
if (txt) {
let categories = categoriesStringToArray(txt);
event.setCategories(categories.length, categories);
}
}
if ("locationIndex" in args) {
txt = this.parseTextField(eventFields[args.locationIndex])
if (txt) {
event.setProperty("LOCATION", txt);
}
}
//save the event into return array
eventArray.push(event);
}
//save the event into return array
eventArray.push(event);
//get next events fields
eventFields = eventRegExp(str);
} while (eventRegExp.lastIndex != 0);
// return results
aCount.value = eventArray.length;
return eventArray;
} // end parse
// The parse was cancelled, return an empty array of events.
aCount.value = 0;
return [];
},
parseDateTime: function parseDateTime(aDate, aTime, aLocale) {
let dt = cal.createDateTime();
//XXX Can we do better?
dt.timezone = cal.floating();
let rd = aLocale.dateRe.exec(aDate);
let rt = aLocale.timeRe.exec(aTime);
if (!rd || !rt) {
return null;
}
dt.year = rd[aLocale.dateYearIndex];
dt.month = rd[aLocale.dateMonthIndex] - 1;
dt.day = rd[aLocale.dateDayIndex];
if (rt) {
dt.hour = Number(rt[aLocale.timeHourIndex]);
dt.minute = rt[aLocale.timeMinuteIndex];
dt.second = rt[aLocale.timeSecondIndex];
} else {
dt.isDate = true;
}
if (rt && aLocale.timeAmPmIndex &&
rt[aLocale.timeAmPmIndex] != aLocale.timePmString) {
// AM
if (dt.hour == 12) {
dt.hour = 0;
}
} else {
// PM
if (dt.hour < 12) {
dt.hour += 12;
}
}
return dt;
},
//get next events fields
eventFields = eventRegExp(str);
} while (eventRegExp.lastIndex != 0);
// return results
aCount.value = eventArray.length;
return eventArray;
} // end parse
aCount.value = 0;
return new Array();
parseTextField: function parseTextField(aTextField) {
return aTextField ? aTextField.replace(/""/g, "\"") : "";
}
};
function parseDateTime(aDate, aTime, aLocale)
{
var date = Components.classes["@mozilla.org/calendar/datetime;1"]
.createInstance(Components.interfaces.calIDateTime);
//XXX Can we do better?
date.timezone = floating();
var rd = aLocale.dateRe.exec(aDate);
var rt = aLocale.timeRe.exec(aTime);
if (!rd || !rt) {
return null;
}
date.year = rd[aLocale.dateYearIndex];
date.month = rd[aLocale.dateMonthIndex] - 1;
date.day = rd[aLocale.dateDayIndex];
if (rt) {
date.hour = Number(rt[aLocale.timeHourIndex]);
date.minute = rt[aLocale.timeMinuteIndex];
date.second = rt[aLocale.timeSecondIndex];
} else {
date.isDate = true;
}
if (rt && aLocale.timeAmPmIndex)
if (rt[aLocale.timeAmPmIndex] != aLocale.timePmString) {
// AM
if (date.hour == 12)
date.hour = 0;
} else {
// PM
if (date.hour < 12)
date.hour += 12;
}
dump(date+"\n");
return date;
}
function parseTextField(aTextField)
{
return aTextField ? aTextField.replace(/""/g, "\"") : "";
}
// Export
// Exporter
function calOutlookCSVExporter() {
this.wrappedJSObject = this;
}
calOutlookCSVExporter.prototype.QueryInterface =
function QueryInterface(aIID) {
if (!aIID.equals(Components.interfaces.nsISupports) &&
!aIID.equals(Components.interfaces.calIExporter)) {
throw Components.results.NS_ERROR_NO_INTERFACE;
}
calOutlookCSVExporter.prototype = {
getInterfaces: function (count) {
const ifaces = [
Components.interfaces.nsISupports,
Components.interfaces.nsIClassInfo,
Components.interfaces.calIExporter,
];
count.value = ifaces.length;
return ifaces;
},
return this;
};
getHelperForLanguage: function (language) {
return null;
},
calOutlookCSVExporter.prototype.getFileTypes = getOutlookCsvFileTypes;
contractID: "@mozilla.org/calendar/export;1?type=csv",
classDescription: "Calendar Outlook CSV Exporter",
classID: Components.ID("{48e6d3a6-b41b-4052-9ed2-40b27800bd4b}"),
implementationLanguage: Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
flags: 0,
// not prototype.export. export is reserved.
calOutlookCSVExporter.prototype.exportToStream =
function csv_exportToStream(aStream, aCount, aItems) {
var str = "";
var headers = [];
// Not using a loop here, since we need to be sure the order here matches
// with the orders the field data is added later on
headers.push(localeEn['headTitle']);
headers.push(localeEn['headStartDate']);
headers.push(localeEn['headStartTime']);
headers.push(localeEn['headEndDate']);
headers.push(localeEn['headEndTime']);
headers.push(localeEn['headAllDayEvent']);
headers.push(localeEn['headAlarm']);
headers.push(localeEn['headAlarmDate']);
headers.push(localeEn['headAlarmTime']);
headers.push(localeEn['headCategories']);
headers.push(localeEn['headDescription']);
headers.push(localeEn['headLocation']);
headers.push(localeEn['headPrivate']);
headers = headers.map(function(v) {
return '"' + v + '"';
});
str = headers.join(',');
str += exportLineEnding;
aStream.write(str, str.length);
QueryInterface: function QueryInterface(aIID) {
return cal.doQueryInterface(this, calOutlookCSVExporter.prototype, aIID, null, this);
},
for each (item in aItems) {
if (!isEvent(item)) {
// XXX TODO: warn the user (once) that tasks are not supported
// (bug 336175)
continue;
}
var line = [];
line.push(item.title);
line.push(dateString(item.startDate));
line.push(timeString(item.startDate));
line.push(dateString(item.endDate));
line.push(timeString(item.endDate));
line.push(item.startDate.isDate ? localeEn.valueTrue : localeEn.valueFalse);
let alarmDate;
let alarms = item.getAlarms({});
if (alarms.length) {
alarmDate = cal.alarms.calculateAlarmDate(item, alarms[0]);
}
line.push(alarmDate ? localeEn.valueTrue : localeEn.valueFalse);
line.push(alarmDate ? dateString(alarmDate) : "");
line.push(alarmDate ? timeString(alarmDate) : "");
line.push(txtString(categoriesArrayToString(item.getCategories({})))); // xxx todo: what's the correct way to encode ',' in csv?, how are multi-values expressed?
line.push(txtString(item.getProperty("DESCRIPTION")));
line.push(txtString(item.getProperty("LOCATION")));
line.push((item.privacy=="PRIVATE") ? localeEn.valueTrue : localeEn.valueFalse);
getFileTypes: getOutlookCsvFileTypes,
line = line.map(function(v) {
v = String(v).replace(/"/g,'""');
return '"'+v+'"';
})
str = line.join(',') + exportLineEnding;
exportToStream: function csv_exportToStream(aStream, aCount, aItems) {
// Helper functions
function dateString(aDateTime) aDateTime.jsDate.toLocaleFormat(localeEn.dateFormat);
function timeString(aDateTime) aDateTime.jsDate.toLocaleFormat(localeEn.timeFormat);
function txtString(aString) aString || "";
let str = "";
let headers = [];
// Not using a loop here, since we need to be sure the order here matches
// with the orders the field data is added later on
headers.push(localeEn['headTitle']);
headers.push(localeEn['headStartDate']);
headers.push(localeEn['headStartTime']);
headers.push(localeEn['headEndDate']);
headers.push(localeEn['headEndTime']);
headers.push(localeEn['headAllDayEvent']);
headers.push(localeEn['headAlarm']);
headers.push(localeEn['headAlarmDate']);
headers.push(localeEn['headAlarmTime']);
headers.push(localeEn['headCategories']);
headers.push(localeEn['headDescription']);
headers.push(localeEn['headLocation']);
headers.push(localeEn['headPrivate']);
headers = headers.map(function(v) {
return '"' + v + '"';
});
str = headers.join(',');
str += exportLineEnding;
aStream.write(str, str.length);
}
return;
for each (item in aItems) {
if (!cal.isEvent(item)) {
// XXX TODO: warn the user (once) that tasks are not supported
// (bug 336175)
continue;
}
let line = [];
line.push(item.title);
line.push(dateString(item.startDate));
line.push(timeString(item.startDate));
line.push(dateString(item.endDate));
line.push(timeString(item.endDate));
line.push(item.startDate.isDate ? localeEn.valueTrue : localeEn.valueFalse);
let alarmDate;
let alarms = item.getAlarms({});
if (alarms.length) {
alarmDate = cal.alarms.calculateAlarmDate(item, alarms[0]);
}
line.push(alarmDate ? localeEn.valueTrue : localeEn.valueFalse);
line.push(alarmDate ? dateString(alarmDate) : "");
line.push(alarmDate ? timeString(alarmDate) : "");
line.push(txtString(categoriesArrayToString(item.getCategories({})))); // xxx todo: what's the correct way to encode ',' in csv?, how are multi-values expressed?
line.push(txtString(item.getProperty("DESCRIPTION")));
line.push(txtString(item.getProperty("LOCATION")));
line.push((item.privacy=="PRIVATE") ? localeEn.valueTrue : localeEn.valueFalse);
line = line.map(function(v) {
v = String(v).replace(/"/g,'""');
return '"'+v+'"';
})
str = line.join(',') + exportLineEnding;
aStream.write(str, str.length);
}
}
};
function dateString(aDateTime) {
return aDateTime.jsDate.toLocaleFormat(localeEn.dateFormat);
}
function timeString(aDateTime) {
return aDateTime.jsDate.toLocaleFormat(localeEn.timeFormat);
}
function txtString(aString) {
if (aString) {
return aString;
} else {
return "";
}
}

Просмотреть файл

@ -1,5 +1,4 @@
/* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
/* ***** 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
@ -24,6 +23,7 @@
* Diego Mira David <diegomd86@gmail.com>
* Eduardo Teruo Katayama <eduardo@ime.usp.br>
* Glaucus Augustus Grecco Cardoso <glaucus@ime.usp.br>
* Philipp Kewisch <mozilla@kewis.ch>
*
* 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
@ -42,247 +42,249 @@
/**
* Prints a two column view of a week of events, much like a paper day-planner
*/
Components.utils.import("resource://calendar/modules/calPrintUtils.jsm");
function calWeekPrinter() {
this.wrappedJSObject = this;
}
calWeekPrinter.prototype.QueryInterface =
function QueryInterface(aIID) {
if (!aIID.equals(Components.interfaces.nsISupports) &&
!aIID.equals(Components.interfaces.calIPrintFormatter)) {
throw Components.results.NS_ERROR_NO_INTERFACE;
}
calWeekPrinter.prototype = {
getInterfaces: function (count) {
const ifaces = [
Components.interfaces.nsISupports,
Components.interfaces.nsIClassInfo,
Components.interfaces.calIPrintFormatter,
];
count.value = ifaces.length;
return ifaces;
},
return this;
};
getHelperForLanguage: function (language) {
return null;
},
calWeekPrinter.prototype.getName =
function weekPrint_getName() {
return calGetString("calendar", "weekPrinterName");
};
calWeekPrinter.prototype.__defineGetter__("name", calWeekPrinter.prototype.getName);
contractID: "@mozilla.org/calendar/printformatter;1?type=weekplan",
classDescription: "Calendar Week Print Formatter",
classID: Components.ID("{2d6ec97b-9109-4b92-89c5-d4b4806619ce}"),
implementationLanguage: Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
flags: 0,
calWeekPrinter.prototype.formatToHtml =
function weekPrint_format(aStream, aStart, aEnd, aCount, aItems, aTitle) {
// Create the e4x framework of the HTML document
var html = <html/>;
html.appendChild(
<head>
<title>{aTitle}</title>
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>
<link rel='stylesheet' type='text/css' href='chrome://calendar/skin/calendar-printing.css'/>
</head>);
QueryInterface: function QueryInterface(aIID) {
return cal.doQueryInterface(this, calWeekPrinter.prototype, aIID, null, this);
},
var body = <body/>;
get name() {
return cal.calGetString("calendar", "weekPrinterName");
},
// helper: returns the passed item's startDate, entryDate or dueDate, in
// that order. If the item doesn't have one of those dates, this
// doesn't return.
function hasUsableDate(item) {
return item.startDate || item.entryDate || item.dueDate;
}
formatToHtml: function weekPrint_format(aStream, aStart, aEnd, aCount, aItems, aTitle) {
// Create the e4x framework of the HTML document
let html = <html/>;
html.appendChild(
<head>
<title>{aTitle}</title>
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>
<link rel='stylesheet' type='text/css' href='chrome://calendar/skin/calendar-printing.css'/>
</head>);
// Clean out the item list so it only contains items we will want to
// include in the printout.
var filteredItems = aItems.filter(hasUsableDate);
let body = <body/>;
var calIEvent = Components.interfaces.calIEvent;
var calITodo = Components.interfaces.calITodo
function compareItems(a, b) {
// Sort tasks before events
if (isEvent(a) && isToDo(b)) {
return 1;
}
if (isToDo(a) && isEvent(b)) {
return -1;
}
if (isEvent(a)) {
var startCompare = a.startDate.compare(b.startDate);
if (startCompare != 0) {
return startCompare;
// helper: returns the passed item's startDate, entryDate or dueDate, in
// that order. If the item doesn't have one of those dates, this
// doesn't return.
function hasUsableDate(item) item.startDate || item.entryDate || item.dueDate;
// Clean out the item list so it only contains items we will want to
// include in the printout.
let filteredItems = aItems.filter(hasUsableDate);
function compareItems(a, b) {
// Sort tasks before events
if (cal.isEvent(a) && cal.isToDo(b)) {
return 1;
}
return a.endDate.compare(b.endDate);
if (cal.isToDo(a) && cal.isEvent(b)) {
return -1;
}
if (cal.isEvent(a)) {
let startCompare = a.startDate.compare(b.startDate);
if (startCompare != 0) {
return startCompare;
}
return a.endDate.compare(b.endDate);
}
let dateA = a.entryDate || a.dueDate;
let dateB = b.entryDate || b.dueDate;
return dateA.compare(dateB);
}
var dateA = a.entryDate || a.dueDate;
var dateB = b.entryDate || b.dueDate;
return dateA.compare(dateB);
}
var sortedList = filteredItems.sort(compareItems);
let sortedList = filteredItems.sort(compareItems);
var weekInfo = getWeekInfoService();
let weekInfo = cal.getWeekInfoService();
// Start at the beginning of the week that aStart is in, and loop until
// we're at aEnd. In the loop we build the HTML table for each day, and
// get the day's items using getDayTd().
var start = aStart || sortedList[0].startDate || sortedList[0].entryDate ||
sortList[0].dueDate;
ASSERT(start, "can't find a good starting date to print");
// Start at the beginning of the week that aStart is in, and loop until
// we're at aEnd. In the loop we build the HTML table for each day, and
// get the day's items using getDayTd().
let start = aStart || sortedList[0].startDate || sortedList[0].entryDate ||
sortList[0].dueDate;
ASSERT(start, "can't find a good starting date to print");
var lastItem = sortedList[sortedList.length-1];
var end = aEnd || lastItem.startDate || lastItem.entryDate ||
lastItem.dueDate;
ASSERT(end, "can't find a good ending date to print");
let lastItem = sortedList[sortedList.length-1];
let end = aEnd || lastItem.startDate || lastItem.entryDate ||
lastItem.dueDate;
ASSERT(end, "can't find a good ending date to print");
var date = start.startOfWeek;
var startOfWeek = getPrefSafe("calendar.week.start", 0);
date.day += startOfWeek;
// Make sure we didn't go too far ahead
if (date.compare(start) == 1) {
date.day -= 7;
}
while(date.compare(end) == -1) {
var weekno = weekInfo.getWeekTitle(date);
var weekTitle = calGetString("calendar", 'WeekTitle', [weekno]);
body.appendChild(
<table border='0' width='100%' class='main-table'>
<tr>
<td align='center' valign='bottom'>{weekTitle}</td>
</tr>
</table>);
var mainWeek = <table width='100%' height="90%" border='solid 1px;'/>
// Create the <td> for each day, and put it into an array.
var dayTds = new Array();
for (var i = 0; i < 7 ; i++) {
dayTds[date.weekday] = this.getDayTd(date, sortedList);
date.day += 1;
let dt = start.startOfWeek;
let startOfWeek = cal.getPrefSafe("calendar.week.start", 0);
dt.day += startOfWeek;
// Make sure we didn't go too far ahead
if (dt.compare(start) == 1) {
dt.day -= 7;
}
var monRow = <tr height="33%"/>;
monRow.appendChild(dayTds[1]); // Monday
monRow.appendChild(dayTds[4]); // Thursday
mainWeek.appendChild(monRow);
while (dt.compare(end) == -1) {
let weekno = weekInfo.getWeekTitle(dt);
let weekTitle = cal.calGetString("calendar", 'WeekTitle', [weekno]);
body.appendChild(
<table border='0' width='100%' class='main-table'>
<tr>
<td align='center' valign='bottom'>{weekTitle}</td>
</tr>
</table>);
let mainWeek = <table width='100%' height="90%" border='solid 1px;'/>
var tueRow = <tr height="33%"/>;
tueRow.appendChild(dayTds[2]); // Tuesday
tueRow.appendChild(dayTds[5]); // Friday
mainWeek.appendChild(tueRow);
// Create the <td> for each day, and put it into an array.
let dayTds = [];
for (let i = 0; i < 7 ; i++) {
dayTds[dt.weekday] = this.getDayTd(dt, sortedList);
dt.day += 1;
}
var wedRow = <tr height="33%"/>;
wedRow.appendChild(dayTds[3]); // Wednesday
let monRow = <tr height="33%"/>;
monRow.appendChild(dayTds[1]); // Monday
monRow.appendChild(dayTds[4]); // Thursday
mainWeek.appendChild(monRow);
// Saturday and Sunday are half-size
var satSunTd = <td height="33%"/>;
var weekendTable = <table border="1" width="100%" height="100%"/>;
let tueRow = <tr height="33%"/>;
tueRow.appendChild(dayTds[2]); // Tuesday
tueRow.appendChild(dayTds[5]); // Friday
mainWeek.appendChild(tueRow);
var satRow = <tr valign='top'/>;
satRow.appendChild(dayTds[6]); // Saturday
weekendTable.appendChild(satRow);
let wedRow = <tr height="33%"/>;
wedRow.appendChild(dayTds[3]); // Wednesday
var sunRow = <tr valign='top'/>;
sunRow.appendChild(dayTds[0]); // Sunday
weekendTable.appendChild(sunRow);
// Saturday and Sunday are half-size
let satSunTd = <td height="33%"/>;
let weekendTable = <table border="1" width="100%" height="100%"/>;
satSunTd.appendChild(weekendTable);
wedRow.appendChild(satSunTd);
mainWeek.appendChild(wedRow);
let satRow = <tr valign='top'/>;
satRow.appendChild(dayTds[6]); // Saturday
weekendTable.appendChild(satRow);
body.appendChild(mainWeek);
// Make sure each month gets put on its own page
body.appendChild(<br style="page-break-after: always;"/>);
}
let tasks = cal.print.getTasksWithoutDueDate(aItems, date);
body.appendChild(tasks);
html.appendChild(body);
let sunRow = <tr valign='top'/>;
sunRow.appendChild(dayTds[0]); // Sunday
weekendTable.appendChild(sunRow);
// Stream out the resulting HTML
var convStream = Components.classes["@mozilla.org/intl/converter-output-stream;1"]
.createInstance(Components.interfaces.nsIConverterOutputStream);
convStream.init(aStream, 'UTF-8', 0, 0x0000);
convStream.writeString(html.toXMLString());
};
satSunTd.appendChild(weekendTable);
wedRow.appendChild(satSunTd);
mainWeek.appendChild(wedRow);
calWeekPrinter.prototype.getDayTd =
/**
* Given a calIDateTime and an array of items, this function creates an HTML
* table containing the items, using the appropriate formatting and colours.
*/
function weekPrint_getDayTable(aDate, aItems) {
// mainTd is the <td> element from the parent HTML table that will hold
// the child HTML tables containing the date string and this day's items.
var mainTd = <td border='1px solid black;' width="50%" valign='top'/>
var dateFormatter = Components.classes["@mozilla.org/calendar/datetime-formatter;1"]
.getService(Components.interfaces.calIDateTimeFormatter);
var defaultTimezone = calendarDefaultTimezone();
var dateString = dateFormatter.formatDateLong(aDate.getInTimezone(defaultTimezone));
body.appendChild(mainWeek);
// Make sure each month gets put on its own page
body.appendChild(<br style="page-break-after: always;"/>);
}
let tasks = cal.print.getTasksWithoutDueDate(aItems, dt);
body.appendChild(tasks);
html.appendChild(body);
// Add the formatted date string (in its own child HTML table)
mainTd.appendChild(
<table class='day-name' width='100%' style='background-color:white; border: 1px solid black;'>
<tr>
// Stream out the resulting HTML
let convStream = Components.classes["@mozilla.org/intl/converter-output-stream;1"]
.createInstance(Components.interfaces.nsIConverterOutputStream);
convStream.init(aStream, 'UTF-8', 0, 0x0000);
convStream.writeString(html.toXMLString());
},
/**
* Given a calIDateTime and an array of items, this function creates an HTML
* table containing the items, using the appropriate formatting and colours.
*/
getDayTd: function weekPrint_getDayTable(aDate, aItems) {
// mainTd is the <td> element from the parent HTML table that will hold
// the child HTML tables containing the date string and this day's items.
let mainTd = <td border='1px solid black;' width="50%" valign='top'/>
let dateFormatter = cal.getDateFormatter();
let defaultTimezone = cal.calendarDefaultTimezone();
let dateString = dateFormatter.formatDateLong(aDate.getInTimezone(defaultTimezone));
// Add the formatted date string (in its own child HTML table)
mainTd.appendChild(
<table class='day-name' width='100%' style='background-color:white; border: 1px solid black;'>
<tr>
<td align='center' valign='bottom'>{dateString}</td>
</tr>
</table>);
</tr>
</table>);
// Add the formatted items (in their child HTML table)
var innerTable = <table valign='top' style='font-size: 10px;'/>;
for each (var item in aItems) {
var sDate = item.startDate || item.entryDate || item.dueDate;
var eDate = item.endDate || item.dueDate || item.entryDate;
if (sDate) {
sDate = sDate.getInTimezone(defaultTimezone);
}
if (eDate) {
eDate = eDate.getInTimezone(defaultTimezone);
}
// Add the formatted items (in their child HTML table)
let innerTable = <table valign='top' style='font-size: 10px;'/>;
for each (let item in aItems) {
let sDate = item.startDate || item.entryDate || item.dueDate;
let eDate = item.endDate || item.dueDate || item.entryDate;
if (sDate) {
sDate = sDate.getInTimezone(defaultTimezone);
}
if (eDate) {
eDate = eDate.getInTimezone(defaultTimezone);
}
// End dates are exclusive. Adjust the eDate accordingly.
if (sDate && sDate.isDate && eDate) {
eDate = eDate.clone();
eDate.day -= 1;
}
// End dates are exclusive. Adjust the eDate accordingly.
if (sDate && sDate.isDate && eDate) {
eDate = eDate.clone();
eDate.day -= 1;
}
// If the item has no end date, or if the item's end date is aDate or
// is before aDate, skip to the next item.
if (!eDate || (eDate.compare(aDate) < 0)) {
continue;
}
// If the item has no end date, or if the item's end date is aDate or
// is before aDate, skip to the next item.
if (!eDate || (eDate.compare(aDate) < 0)) {
continue;
}
// No start date or a start date that's after the date we want is bad.
if (!sDate || (sDate.compare(aDate) > 0)) {
break;
}
// No start date or a start date that's after the date we want is bad.
if (!sDate || (sDate.compare(aDate) > 0)) {
break;
}
var time = "";
if (sDate && eDate && !sDate.isDate) {
time = dateFormatter.formatTime(sDate) + '-' + dateFormatter.formatTime(eDate);
} else if (sDate && !sDate.isDate) {
time = dateFormatter.formatTime(sDate);
} else if (eDate && !eDate.isDate) {
time = dateFormatter.formatTime(eDate);
}
let time = "";
if (sDate && eDate && !sDate.isDate) {
time = dateFormatter.formatTime(sDate) + '-' + dateFormatter.formatTime(eDate);
} else if (sDate && !sDate.isDate) {
time = dateFormatter.formatTime(sDate);
} else if (eDate && !eDate.isDate) {
time = dateFormatter.formatTime(eDate);
}
// Get calendar and category colours and apply them to the item's
// table cell.
var calColor = item.calendar.getProperty('color');
if (!calColor) {
calColor = "#A8C2E1";
}
var pb2 = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch2);
var catColor;
for each (var cat in item.getCategories({})) {
try {
catColor = pb2.getCharPref("calendar.category.color." + cat.toLowerCase());
break; // take first matching
} catch(ex) {}
}
// Get calendar and category colours and apply them to the item's
// table cell.
let calColor = item.calendar.getProperty('color') || "#A8C2E1";
let pb2 = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch2);
let catColor;
for each (let cat in item.getCategories({})) {
try {
catColor = pb2.getCharPref("calendar.category.color." + cat.toLowerCase());
break; // take first matching
} catch (ex) {}
}
var style = 'font-size: 11px; background-color: ' + calColor + ';';
style += ' color: ' + getContrastingTextColor(calColor);
if (catColor) {
style += ' border: solid ' + catColor + ' 2px;';
let style = 'font-size: 11px; background-color: ' + calColor + ';';
style += ' color: ' + getContrastingTextColor(calColor);
if (catColor) {
style += ' border: solid ' + catColor + ' 2px;';
}
let item = <tr>
<td valign='top' align='left' style={style}>{time} {item.title}</td>
</tr>;
innerTable.appendChild(item);
}
var item = <tr>
<td valign='top' align='left' style={style}>{time} {item.title}</td>
</tr>;
innerTable.appendChild(item);
innerTable.appendChild(<p> </p>);
mainTd.appendChild(innerTable);
return mainTd;
}
innerTable.appendChild(<p> </p>);
mainTd.appendChild(innerTable);
return mainTd;
};