зеркало из https://github.com/mozilla/pjs.git
Checked in patch for bug 264447: Export into vCalendar format has illegal newlines
This commit is contained in:
Родитель
b3fdf147f9
Коммит
2adec20936
|
@ -797,6 +797,7 @@ function saveEventsToFile( calendarEventArray )
|
||||||
break;
|
break;
|
||||||
case 6 : // vcs
|
case 6 : // vcs
|
||||||
aDataStream = eventArrayToICalString( calendarEventArray, true );
|
aDataStream = eventArrayToICalString( calendarEventArray, true );
|
||||||
|
aDataStream = patchICalStringForVCal( aDataStream );
|
||||||
extension = extensionvCalendar;
|
extension = extensionvCalendar;
|
||||||
charset = "UTF-8";
|
charset = "UTF-8";
|
||||||
break;
|
break;
|
||||||
|
@ -904,6 +905,184 @@ function patchICalStringForExport( sTextiCalendar )
|
||||||
return sTextiCalendar;
|
return sTextiCalendar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* patchICalStringForVCal:
|
||||||
|
* iCal (v2.0) [rfc2445sec4.1] says lines can be broken anywhere
|
||||||
|
* to comply with 75-octet line length (SHOULD NOT be longer)
|
||||||
|
* vCal (v1.0) [vCalendar1.0sec 2.1.3] says lines can only be broken
|
||||||
|
* at places where there may be linear white space, and does not give
|
||||||
|
* a general max line length, though says quoted-printable lines should be
|
||||||
|
* less than 76 characters.
|
||||||
|
*
|
||||||
|
* Quoted-printable lines are continued with an = just before the crlf.
|
||||||
|
*
|
||||||
|
* So approach is to eliminate breaks in lines except quoted-printable ones.
|
||||||
|
* Replace DESCRIPTION:line1\nline2
|
||||||
|
* with DESCRIPTION;ENCODING=QUOTED-PRINTABLE:=
|
||||||
|
* line1=0D=0A=
|
||||||
|
* line2
|
||||||
|
*
|
||||||
|
* Also breaks single long values in aribrary text values
|
||||||
|
* (COMMENT,DESCRIPTION,LOCATION,SUMMARY,CONTACT,RESOURCES)
|
||||||
|
* using quoted-printable line breaks (end line with =), and
|
||||||
|
* replaces \, with , in these values.
|
||||||
|
*
|
||||||
|
* Converts version number to 1.0, as ENCODING=QUOTED-PRINTABLE is not
|
||||||
|
* part of the version 2.0 standard (it defers to transport MIME encoding).
|
||||||
|
*/
|
||||||
|
function patchICalStringForVCal( sTextiCalendar )
|
||||||
|
{
|
||||||
|
// replace "\r\n " or "\r\n\t" with "" except when it follows "=".
|
||||||
|
// i.e., replace [char-other-than-"="] followed by \r\n then [space-or-tab],
|
||||||
|
// with just the captured ([char-other-than-"="])
|
||||||
|
var unfolded = sTextiCalendar.replace(/([^=])\r\n[ \t]/g, "$1");
|
||||||
|
var lines = unfolded.split("\r\n");
|
||||||
|
// Replace "\\n" in TEXT values with "=0D=0A\r\n ":
|
||||||
|
// - look for (property name and params) folowed by colon
|
||||||
|
// followed by (text with 'n' immediately preceded by an odd number of '\').
|
||||||
|
// Assumes params may not contain a colon.
|
||||||
|
// Assumes only text values contain "\\n"
|
||||||
|
var multilinePropertyRegExp = /^([^:]+):((?:.*[^\\](?:[\\][\\])*)?\\n.*)$/;
|
||||||
|
var longTextPropertyRegExp =
|
||||||
|
/^((?:COMMENT|DESCRIPTION|LOCATION|SUMMARY|CONTACT|RESOURCES)[^:]*):(.*)$/;
|
||||||
|
for (var i = 0; i < lines.length; i++)
|
||||||
|
{
|
||||||
|
var matchedMultiLineParts = lines[i].match(multilinePropertyRegExp);
|
||||||
|
if (matchedMultiLineParts)
|
||||||
|
{
|
||||||
|
var propertyAndParams = matchedMultiLineParts[1];
|
||||||
|
propertyAndParams = propertyAndParams+";ENCODING=QUOTED-PRINTABLE";
|
||||||
|
|
||||||
|
var value = matchedMultiLineParts[2];
|
||||||
|
// Replace "\\n" in text values with "=0D=0A\r\n ":
|
||||||
|
value = value.replace(/\\n/g,
|
||||||
|
//replace if odd number of backslashes precede n
|
||||||
|
function(match, offset, whole) {
|
||||||
|
if (hasOddBackslashCount(whole, offset)){
|
||||||
|
return "=0D=0A=\r\n "; // odd, so replace
|
||||||
|
} else {
|
||||||
|
return match; // even, so keep \n
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// trim if ends with "=0D=0A=\r\n ", to "=0D=0A"
|
||||||
|
if (endsWith(value, "=0D=0A=\r\n "))
|
||||||
|
value = value.substring(0, value.length - "=\r\n ".length);
|
||||||
|
// Replace "\\," in text values with ",":
|
||||||
|
value = replaceBackslashComma(value);
|
||||||
|
// limit line length
|
||||||
|
value = breakIntoContinuationLines(value, true);
|
||||||
|
|
||||||
|
lines[i] = propertyAndParams + ":=\r\n " + value;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var matchedLongTextParts = lines[i].match(longTextPropertyRegExp);
|
||||||
|
if (matchedLongTextParts) // maxlen
|
||||||
|
{
|
||||||
|
/*var*/ propertyAndParams = matchedLongTextParts[1];
|
||||||
|
/*var*/ value = matchedLongTextParts[2];
|
||||||
|
|
||||||
|
value = replaceBackslashComma(value);
|
||||||
|
|
||||||
|
if (propertyAndParams.length + ":".length + value.length > 75)
|
||||||
|
value = "\r\n "+breakIntoContinuationLines(value, false);
|
||||||
|
|
||||||
|
lines[i] = propertyAndParams + ":" + value;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lines[i]=="VERSION:2.0")
|
||||||
|
{
|
||||||
|
lines[i] = "VERSION:1.0";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lines.join("\r\n")+"\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Replace \, with , if there is an odd number of backslashes. */
|
||||||
|
function replaceBackslashComma(value)
|
||||||
|
{
|
||||||
|
return value.replace(/\\,/g,
|
||||||
|
//replace if odd number of backslashes precede n
|
||||||
|
function(match, offset, whole) {
|
||||||
|
if (hasOddBackslashCount(whole, offset)){
|
||||||
|
return ","; // odd, so replace
|
||||||
|
} else {
|
||||||
|
return match; // even, so keep \,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** whole is string. Offset is position to look for last backslash.
|
||||||
|
Returns true if there are an odd number of preceding backslashes,
|
||||||
|
ending with the one at offset. Returns false if offset does not
|
||||||
|
point to a backslash, or there are an even number before offset inclusive.
|
||||||
|
**/
|
||||||
|
function hasOddBackslashCount(whole, offset)
|
||||||
|
{
|
||||||
|
var backSlashCount = 0;
|
||||||
|
for (var i = offset; i >= 0; i--) {
|
||||||
|
if (whole.charAt(i) == "\\")
|
||||||
|
++backSlashCount;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return backSlashCount & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Break long lines in string value into shorter continuations.
|
||||||
|
* If useQuotedPrintable, then lines are delimited by "=0D=0A=\r\n ",
|
||||||
|
* else they are delimited by "\r\n ";
|
||||||
|
* If useQUotedPrintable, then continuations are separated with "=\r\n ",
|
||||||
|
* else they are separated with "\r\n ";
|
||||||
|
*/
|
||||||
|
function breakIntoContinuationLines(value, useQuotedPrintable)
|
||||||
|
{
|
||||||
|
var lineDelimiter = (useQuotedPrintable? "=0D=0A=\r\n " : "\r\n ");
|
||||||
|
var lineContinuer = (useQuotedPrintable? "=\r\n " : "\r\n ");
|
||||||
|
// leave room to add "=\r\n " or "\r\n " and still be 75 chars or less.
|
||||||
|
var maxLen = 75 - lineContinuer.length;
|
||||||
|
if (value.length > maxLen) {
|
||||||
|
var lines = value.split(lineDelimiter); // existing split
|
||||||
|
for (var i = 0; i < lines.length; i++) {
|
||||||
|
var line = lines[i];
|
||||||
|
if (line.length > maxLen) {
|
||||||
|
var lineParts = new Array();
|
||||||
|
var start = 0; // start of part of line
|
||||||
|
for (var end = maxLen; end < line.length; end+=maxLen) {
|
||||||
|
// for clarity, try to break on whitespace.
|
||||||
|
for(; end > 0; end--) {
|
||||||
|
var c = line.charAt(end - 1);
|
||||||
|
if (c == " " || c == "\t")
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (end == start) // line was filled with nonbreaks
|
||||||
|
end = start + maxLen;
|
||||||
|
lineParts[lineParts.length] = line.substring(start, end);
|
||||||
|
start = end;
|
||||||
|
}
|
||||||
|
lineParts[lineParts.length] = line.substring(start);
|
||||||
|
lines[i] = lineParts.join(lineContinuer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value = lines.join(lineDelimiter);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function endsWith(whole, part) {
|
||||||
|
if (part.length <= whole.length) {
|
||||||
|
for (var i = 1; i <=part.length; i++)
|
||||||
|
if (part[part.length - i] != whole[whole.length - i])
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a array of events to a block of HTML code
|
* Converts a array of events to a block of HTML code
|
||||||
|
|
Загрузка…
Ссылка в новой задаче