Files
evolution/data/webkit/e-convert.js
Milan Crha 461c01ad07 Composer: UL/OL not wrapped properly in Plain Text mode
In the Plain Text mode, when changing paragraph format to UL/OL, its width
was not properly set, which drew the long lines unwrapped. Switching to HTML
mode and back did set the expected wrap width.

Similarly, when converting HTML into plain text (on send), the wrap width did
not count width of the UL/OL prefix, making the line longer than it should be.
2020-12-02 13:57:19 +01:00

1035 lines
27 KiB
JavaScript

/* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 2019 Red Hat (www.redhat.com)
*
* This library is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
/* semi-convention: private functions start with lower-case letter,
public functions start with upper-case letter. */
var EvoConvert = {
MIN_PARAGRAPH_WIDTH : 5, // in characters
MIN_OL_WIDTH : 6, // includes ". " at the end
TAB_WIDTH : 8, // in characters
ALIGN_LEFT : 0,
ALIGN_CENTER : 1,
ALIGN_RIGHT : 2,
ALIGN_JUSTIFY : 3,
NOWRAP_CHAR_START : "\x01",
NOWRAP_CHAR_END : "\x02"
};
EvoConvert.GetOLMaxLetters = function(type, levels)
{
if (type && type.toUpperCase() == "I") {
if (levels < 2)
return 1;
if (levels < 3)
return 2;
if (levels < 8)
return 3;
if (levels < 18)
return 4;
if (levels < 28)
return 5;
if (levels < 38)
return 6;
if (levels < 88)
return 7
if (levels < 188)
return 8;
if (levels < 288)
return 9;
if (levels < 388)
return 10;
if (levels < 888)
return 11;
return 12;
} else if (type && type.toUpperCase() == "A") {
if (levels < 27)
return 1;
if (levels < 703)
return 2;
if (levels < 18279)
return 3;
return 4;
} else {
if (levels < 10)
return 1;
if (levels < 100)
return 2;
if (levels < 1000)
return 3;
if (levels < 10000)
return 4;
return 5;
}
}
EvoConvert.getOLIndexValue = function(type, value)
{
var str = "";
if (type == "A" || type == "a") { // alpha
var add = type.charCodeAt(0);
do {
str = String.fromCharCode(((value - 1) % 26) + add) + str;
value = Math.floor((value - 1) / 26);
} while (value);
} else if (type == "I" || type == "i") { // roman
var base = "IVXLCDM";
var b, r, add = 0;
if (value > 3999) {
str = "?";
} else {
if (type == "i")
base = base.toLowerCase();
for (b = 0; value > 0 && b < 7 - 1; b += 2, value = Math.floor(value / 10)) {
r = value % 10;
if (r != 0) {
if (r < 4) {
for (; r; r--)
str = String.fromCharCode(base.charCodeAt(b) + add) + str;
} else if (r == 4) {
str = String.fromCharCode(base.charCodeAt(b + 1) + add) + str;
str = String.fromCharCode(base.charCodeAt(b) + add) + str;
} else if (r == 5) {
str = String.fromCharCode(base.charCodeAt(b + 1) + add) + str;
} else if (r < 9) {
for (; r > 5; r--)
str = String.fromCharCode(base.charCodeAt(b) + add) + str;
str = String.fromCharCode(base.charCodeAt(b + 1) + add) + str;
} else if (r == 9) {
str = String.fromCharCode(base.charCodeAt(b + 2) + add) + str;
str = String.fromCharCode(base.charCodeAt(b) + add) + str;
}
}
}
}
} else { // numeric
str = "" + value;
}
return str;
}
EvoConvert.getComputedOrNodeStyle = function(node)
{
if (!node)
return null;
var parent = node;
while (parent && !(parent === document.body)) {
parent = parent.parentElement;
}
if (parent)
return window.getComputedStyle(node);
return node.style;
}
EvoConvert.replaceList = function(element, tagName, normalDivWidth)
{
var ll, lists, type = null;
if (tagName == "OL")
type = "";
lists = element.getElementsByTagName(tagName);
for (ll = lists.length - 1; ll >= 0; ll--) {
var list;
list = lists.item(ll);
if (!list)
continue;
var style = EvoConvert.getComputedOrNodeStyle(list), ltr, ii, prefixSuffix, indent;
if (!style)
style = list.style;
ltr = !style || style.direction != "rtl";
if (type == null) {
var level = 0, parent = list;
for (parent = list.parentElement; parent; parent = parent.parentElement) {
if (parent.tagName == "UL" || parent.tagName == "OL")
level++;
}
switch (level % 3) {
default:
case 0:
prefixSuffix = " * ";
break;
case 1:
prefixSuffix = " - ";
break;
case 2:
prefixSuffix = " + ";
break;
}
indent = 3;
} else {
type = list.getAttribute("type");
if (!type)
type = "";
var nChildren = 0, child;
for (ii = 0; ii < list.children.length; ii++) {
child = list.children.item(ii);
if (child.tagName == "LI")
nChildren++;
}
prefixSuffix = ltr ? ". " : " .";
indent = EvoConvert.GetOLMaxLetters(type, nChildren) + prefixSuffix.length;
if (indent < EvoConvert.MIN_OL_WIDTH)
indent = EvoConvert.MIN_OL_WIDTH;
}
if (list.hasAttribute("x-evo-extra-indent")) {
var tmp = list.getAttribute("x-evo-extra-indent");
if (tmp) {
tmp = parseInt(tmp);
if (!Number.isInteger(tmp))
tmp = 0;
} else {
tmp = 0;
}
indent += tmp;
}
var liCount = 0;
for (ii = 0; ii < list.children.length; ii++) {
var child = list.children.item(ii), node;
if (!child)
continue;
// nested lists
if (child.tagName == "DIV" && child.hasAttribute("x-evo-extra-indent") && child.hasAttribute("x-evo-li-text")) {
node = child.cloneNode(true);
var tmp = child.getAttribute("x-evo-extra-indent");
if (tmp) {
tmp = parseInt(tmp);
if (!Number.isInteger(tmp))
tmp = 0;
} else {
tmp = 0;
}
node.setAttribute("x-evo-extra-indent", indent + tmp);
tmp = node.getAttribute("x-evo-li-text");
if (ltr)
tmp = " ".repeat(indent) + tmp;
else
tmp = tmp + " ".repeat(indent);
node.setAttribute("x-evo-li-text", tmp);
} else if (child.tagName == "LI") {
liCount++;
node = document.createElement("DIV");
if (list.style.width.endsWith("ch")) {
var width = parseInt(list.style.width.slice(0, -2));
if (Number.isInteger(width))
node.style.width = "" + width + "ch";
} else if (normalDivWidth > 0) {
var width, needs;
if (tagName == "UL") {
needs = 3;
} else {
needs = EvoConvert.GetOLMaxLetters(list.getAttribute("type"), list.children.length) + 2; // length of ". " suffix
if (needs < EvoConvert.MIN_OL_WIDTH)
needs = EvoConvert.MIN_OL_WIDTH;
}
width = normalDivWidth - needs;
if (width < EvoConvert.MIN_PARAGRAPH_WIDTH)
width = EvoConvert.MIN_PARAGRAPH_WIDTH;
node.style.width = "" + width + "ch";
}
node.style.textAlign = list.style.testAlign;
node.style.direction = list.style.direction;
node.style.marginLeft = list.style.marginLeft;
node.style.marginRight = list.style.marginRight;
node.setAttribute("x-evo-extra-indent", indent);
node.innerText = child.innerText;
if (type == null) {
node.setAttribute("x-evo-li-text", prefixSuffix);
} else {
var prefix;
prefix = EvoConvert.getOLIndexValue(type, liCount);
while (prefix.length + 2 /* prefixSuffix.length */ < indent) {
prefix = ltr ? " " + prefix : prefix + " ";
}
node.setAttribute("x-evo-li-text", ltr ? prefix + prefixSuffix : prefixSuffix + prefix);
}
} else {
node = child.cloneNode(true);
if (node.tagName == "UL" || node.tagName == "OL") {
var tmp = child.getAttribute("x-evo-extra-indent");
if (tmp) {
tmp = parseInt(tmp);
if (!Number.isInteger(tmp))
tmp = 0;
} else {
tmp = 0;
}
node.setAttribute("x-evo-extra-indent", indent + tmp);
}
}
list.parentNode.insertBefore(node, list);
}
list.parentNode.removeChild(list);
}
}
EvoConvert.calcLineLetters = function(line)
{
var len;
if (line.search("\t") >= 0) {
var jj;
len = 0;
for (jj = 0; jj < line.length; jj++) {
if (line.charAt(jj) == "\t") {
len = len - (len % EvoConvert.TAB_SIZE) + EvoConvert.TAB_SIZE;
} else {
len++;
}
}
} else {
len = line.length;
}
return len;
}
EvoConvert.getQuotePrefix = function(quoteLevel, ltr)
{
var prefix = "";
if (quoteLevel > 0) {
prefix = ltr ? "> " : " <";
prefix = prefix.repeat(quoteLevel);
}
return prefix;
}
EvoConvert.formatParagraph = function(str, ltr, align, indent, whiteSpace, wrapWidth, extraIndent, liText, quoteLevel)
{
if (!str || str == "")
return liText ? liText : str;
var lines = [], ii;
// wrap the string first
if (wrapWidth > 0) {
var worker = {
collapseWhiteSpace : whiteSpace != "pre" && whiteSpace != "pre-wrap",
collapseEndingWhiteSpace : whiteSpace != "pre",
canWrap : whiteSpace != "nowrap",
charWrap : whiteSpace == "pre",
inAnchor : 0,
ignoreLineLetters : 0, // used for EvoConvert.NOWRAP_CHAR_START and EvoConvert.NOWRAP_CHAR_END, which should be skipped in width calculation
useWrapWidth : wrapWidth,
spacesFrom : -1, // in 'str'
lastWrapableChar : -1, // in this->line
lineLetters : 0,
line : "",
maybeRecalcIgnoreLineLetters : function() {
if (this.ignoreLineLetters) {
var ii, len = this.line.length, chr;
this.ignoreLineLetters = 0;
for (ii = 0; ii < len; ii++) {
chr = this.line[ii];
if (chr == EvoConvert.NOWRAP_CHAR_START || chr == EvoConvert.NOWRAP_CHAR_END)
this.ignoreLineLetters++;
}
}
},
mayConsumeWhitespaceAfterWrap : function(str, ii) {
return ii > 0 && this.line == "" && str[ii - 1] == EvoConvert.NOWRAP_CHAR_END;
},
isInUnwrapPart : function() {
if (this.inAnchor)
return true;
if (this.line[0] == EvoConvert.NOWRAP_CHAR_START)
return this.line.indexOf(EvoConvert.NOWRAP_CHAR_END) < 0;
return false;
},
shouldWrap : function(nextChar) {
return this.canWrap && (!this.collapseWhiteSpace || nextChar != '\n') &&
(!this.isInUnwrapPart() || this.lastWrapableChar != -1) && (this.lineLetters - this.ignoreLineLetters > this.useWrapWidth || (
((!this.charWrap && (nextChar == " " || nextChar == "\t") && this.lineLetters - this.ignoreLineLetters > this.useWrapWidth) ||
((this.charWrap || (nextChar != " " && nextChar != "\t")) && this.lineLetters - this.ignoreLineLetters == this.useWrapWidth)) && (
this.lastWrapableChar == -1/* || this.lastWrapableChar == this.line.length*/)));
},
commitSpaces : function(ii) {
if (this.spacesFrom != -1 && (!this.canWrap || this.line.length - this.ignoreLineLetters <= this.useWrapWidth)) {
var spaces;
spaces = ii - this.spacesFrom;
if (this.canWrap && this.line.length - this.ignoreLineLetters + spaces > this.useWrapWidth)
spaces = this.useWrapWidth - this.line.length + this.ignoreLineLetters;
if (!this.canWrap || (this.line.length - this.ignoreLineLetters + spaces <= this.useWrapWidth) && spaces >= 0) {
if (this.collapseWhiteSpace && (!extraIndent || lines.length))
this.line += " ";
else
this.line += " ".repeat(spaces);
}
this.spacesFrom = -1;
this.lastWrapableChar = this.line.length;
} else if (this.spacesFrom != -1) {
this.lastWrapableChar = this.line.length;
}
},
commit : function(ii, force) {
this.commitSpaces(ii);
var didWrap = false;
if (this.canWrap && this.lastWrapableChar != -1 && this.lineLetters - this.ignoreLineLetters > this.useWrapWidth) {
lines[lines.length] = this.line.substr(0, this.lastWrapableChar);
this.line = this.line.substr(this.lastWrapableChar);
this.maybeRecalcIgnoreLineLetters();
didWrap = true;
} else if (!this.isInUnwrapPart() && this.useWrapWidth != -1 && this.lineLetters - this.ignoreLineLetters > this.useWrapWidth) {
var jj;
if (this.charWrap) {
var subLineLetters = 0, ignoreSubLineLetters = 0, chr;
for(jj = 0; jj < this.line.length; jj++) {
chr = this.line.charAt(jj);
if (chr == "\t") {
subLineLetters = subLineLetters - ((subLineLetters - ignoreSubLineLetters) % EvoConvert.TAB_SIZE) + EvoConvert.TAB_SIZE;
} else {
subLineLetters++;
}
if (chr == EvoConvert.NOWRAP_CHAR_START || chr == EvoConvert.NOWRAP_CHAR_END)
ignoreSubLineLetters++;
if (subLineLetters - ignoreSubLineLetters >= this.useWrapWidth)
break;
}
} else {
jj = this.line.length;
}
lines[lines.length] = this.line.substr(0, jj);
this.line = this.line.substr(jj);
this.maybeRecalcIgnoreLineLetters();
didWrap = true;
} else {
lines[lines.length] = this.line;
this.line = "";
this.ignoreLineLetters = 0;
}
if (this.canWrap && this.collapseEndingWhiteSpace && didWrap && lines[lines.length - 1].endsWith(" ")) {
if (lines[lines.length - 1].length == 1)
lines.length = lines.length - 1;
else
lines[lines.length - 1] = lines[lines.length - 1].substr(0, lines[lines.length - 1].length - 1);
}
if (force && this.line) {
lines[lines.length] = this.line;
this.line = "";
this.ignoreLineLetters = 0;
}
this.lineLetters = this.canWrap ? EvoConvert.calcLineLetters(this.line) : this.line.length;
this.spacesFrom = -1;
this.lastWrapableChar = -1;
}
};
if (worker.useWrapWidth < EvoConvert.MIN_PARAGRAPH_WIDTH)
worker.useWrapWidth = EvoConvert.MIN_PARAGRAPH_WIDTH;
var chr, isHighSurrogate = false;
for (ii = 0; ii < str.length; ii += 1 + (isHighSurrogate ? 1 : 0)) {
// surrogate are two characters "high+low"; high: 0xD800 - 0xDBFF; low: 0xDC00 - 0xDFFF
// and cannot split after the high surrogate, because that would break the character encoding
isHighSurrogate = str.charCodeAt(ii) >= 0xd800 && str.charCodeAt(ii) <= 0xdbff;
if (isHighSurrogate)
chr = str.substr(ii, 2);
else
chr = str.charAt(ii);
if (chr == EvoConvert.NOWRAP_CHAR_START)
worker.inAnchor++;
if (chr == "\n") {
if (!worker.mayConsumeWhitespaceAfterWrap(str, ii))
worker.commit(ii, true);
} else if (!worker.charWrap && !worker.collapseWhiteSpace && chr == "\t") {
if (worker.shouldWrap(str[ii + 1]))
worker.commit(ii, false);
else
worker.commitSpaces(ii);
var add = chr; // this expands the tab, instead of leaving it '\t'... " ".repeat(EvoConvert.TAB_WIDTH - ((worker.lineLetters - worker.ignoreLineLetters) % EvoConvert.TAB_WIDTH));
worker.lineLetters = worker.lineLetters - ((worker.lineLetters - worker.ignoreLineLetters) % EvoConvert.TAB_WIDTH) + EvoConvert.TAB_WIDTH;
if (worker.shouldWrap(str[ii + 1]))
worker.commit(ii, false);
worker.line += add;
worker.lineLetters += add.length;
} else if (!worker.charWrap && (chr == " " || chr == "\t")) {
var setSpacesFrom = false;
if (chr == "\t") {
worker.lineLetters = worker.lineLetters - ((worker.lineLetters - worker.ignoreLineLetters) % EvoConvert.TAB_WIDTH) + EvoConvert.TAB_WIDTH;
setSpacesFrom = true;
} else if ((worker.spacesFrom == -1 && worker.line != "") || (!worker.collapseWhiteSpace && !worker.mayConsumeWhitespaceAfterWrap(str, ii))) {
worker.lineLetters++;
setSpacesFrom = true;
}
// all spaces at the end of paragraph line are ignored
if (setSpacesFrom && worker.spacesFrom == -1)
worker.spacesFrom = ii;
} else {
worker.commitSpaces(ii);
worker.line += chr;
if (chr == "\t")
worker.lineLetters = worker.lineLetters - ((worker.lineLetters - worker.ignoreLineLetters) % EvoConvert.TAB_WIDTH) + EvoConvert.TAB_WIDTH;
else
worker.lineLetters += chr.length;
if (chr == EvoConvert.NOWRAP_CHAR_START || chr == EvoConvert.NOWRAP_CHAR_END)
worker.ignoreLineLetters++;
if (chr == EvoConvert.NOWRAP_CHAR_END && worker.inAnchor)
worker.inAnchor--;
if (worker.shouldWrap(str[ii + 1]))
worker.commit(ii, false);
if (chr == "-" && worker.line.length && !worker.inAnchor)
worker.lastWrapableChar = worker.line.length;
}
}
while (worker.line.length || worker.spacesFrom != -1 || !lines.length) {
worker.commit(ii, false);
}
} else {
if (str.endsWith("\n"))
str = str.substr(0, str.length - 1);
lines = str.split("\n");
}
var extraIndentStr = extraIndent > 0 ? " ".repeat(extraIndent) : null;
if (wrapWidth <= 0) {
for (ii = 0; ii < lines.length; ii++) {
var len = EvoConvert.calcLineLetters(lines[ii]);
if (wrapWidth < len)
wrapWidth = len;
}
}
str = "";
for (ii = 0; ii < lines.length; ii++) {
var line = lines[ii];
if ((!ltr && align == EvoConvert.ALIGN_LEFT) ||
(ltr && align == EvoConvert.ALIGN_RIGHT)) {
var len = EvoConvert.calcLineLetters(line);
if (len < wrapWidth) {
var add = " ".repeat(wrapWidth - len);
if (ltr)
line = add + line;
else
line = line + add;
}
} else if (align == EvoConvert.ALIGN_CENTER) {
var len = EvoConvert.calcLineLetters(line);
if (len < wrapWidth) {
var add = " ".repeat((wrapWidth - len) / 2);
if (ltr)
line = add + line;
else
line = line + add;
}
} else if (align == EvoConvert.ALIGN_JUSTIFY && ii + 1 < lines.length) {
var len = EvoConvert.calcLineLetters(line);
if (len < wrapWidth) {
var words = line.split(" ");
if (words.length > 1) {
var nAdd = (wrapWidth - len);
var add = " ".repeat(nAdd / (words.length - 1) >= 1 ? nAdd / (words.length - 1) : nAdd), jj;
for (jj = 0; jj < words.length - 1 && nAdd > 0; jj++) {
words[jj] = words[jj] + add;
nAdd -= add.length;
if (nAdd > 0 && jj + 2 >= words.length) {
words[jj] = " ".repeat(nAdd) + words[jj];
}
}
line = words[0];
for (jj = 1; jj < words.length; jj++) {
line = line + " " + words[jj];
}
}
}
}
if (!ii && liText) {
if (ltr)
line = liText + line;
else
line = line + liText;
} else if (extraIndentStr && ii > 0) {
if (ltr)
line = extraIndentStr + line;
else
line = line + extraIndentStr;
}
if (indent != "") {
if (ltr && align != EvoConvert.ALIGN_RIGHT)
line = indent + line;
else
line = line + indent;
}
str += line + "\n";
}
return str;
}
EvoConvert.ImgToText = function(img)
{
if (!img)
return "";
var txt;
txt = img.alt;
return txt ? txt : "";
}
EvoConvert.appendNodeText = function(node, str, text)
{
/* This breaks "-- <br>", thus disable it for now. Cannot distinguish from test 70 of /EWebView/ConvertToPlain.
if (node && node.parentElement && text.startsWith('\n') && str.endsWith(" ")) {
var whiteSpace = "normal";
if (node.parentElement)
whiteSpace = window.getComputedStyle(node.parentElement).whiteSpace;
if (!whiteSpace || whiteSpace == "normal") {
return str.substr(0, str.length - 1) + text;
}
} */
return str + text;
}
EvoConvert.extractElemText = function(elem, normalDivWidth, quoteLevel)
{
if (!elem)
return "";
if (!elem.childNodes.length)
return elem.innerText;
var str = "", ii;
for (ii = 0; ii < elem.childNodes.length; ii++) {
var node = elem.childNodes.item(ii);
if (!node)
continue;
str = EvoConvert.appendNodeText(node, str, EvoConvert.processNode(node, normalDivWidth, quoteLevel));
}
return str;
}
EvoConvert.mergeConsecutiveSpaces = function(str)
{
if (str.indexOf(" ") >= 0) {
var words = str.split(" "), ii, word;
str = "";
for (ii = 0; ii < words.length; ii++) {
word = words[ii];
if (word) {
if (ii)
str += " ";
str += word;
}
}
if (!words[words.length - 1])
str += " ";
}
return str;
}
EvoConvert.RemoveInsignificantNewLines = function(node, stripSingleSpace)
{
var str = "";
if (node && node.nodeType == node.TEXT_NODE) {
str = node.nodeValue;
if (str.indexOf("\r") >= 0 ||
str.indexOf("\n") >= 0 ||
str.indexOf("\t") >= 0 ||
str.indexOf(" ") >= 0) {
var whiteSpace = "normal";
if (node.parentElement)
whiteSpace = window.getComputedStyle(node.parentElement).whiteSpace;
if (whiteSpace == "pre-line") {
str = EvoConvert.mergeConsecutiveSpaces(str.replace(/\t/g, " "));
} else if (!whiteSpace || whiteSpace == "normal" || whiteSpace == "nowrap") {
if (str == "\n" || str == "\r" || str == "\r\n") {
str = "";
} else {
if ((!node.previousSibling || node.previousSibling.tagName) && (str[0] == '\r' || str[0] == '\n')) {
var ii;
for (ii = 0; str[ii] == '\n' || str[ii] == '\r'; ii++) {
// do nothing, just skip all leading insignificant new lines
}
str = str.substr(ii);
}
if (str.length > 0 && (!node.nextSibling || node.nextSibling.tagName) && (str[str.length - 1] == '\r' || str[str.length - 1] == '\n')) {
var ii;
for (ii = str.length - 1; ii >= 0 && (str[ii] == '\n' || str[ii] == '\r'); ii--) {
// do nothing, just skip all trailing insignificant new lines
}
str = str.substr(0, ii + 1);
}
str = EvoConvert.mergeConsecutiveSpaces(str.replace(/\t/g, " ").replace(/\r/g, " ").replace(/\n/g, " "));
if ((!whiteSpace || whiteSpace == "normal") && str == " " && (stripSingleSpace || (
!node.nextElementSibling || node.nextElementSibling.tagName == "DIV" || node.nextElementSibling.tagName == "P" || node.nextElementSibling.tagName == "PRE"))) {
str = "";
}
}
}
}
}
return str;
}
EvoConvert.processNode = function(node, normalDivWidth, quoteLevel)
{
var str = "";
if (node.nodeType == node.TEXT_NODE) {
str = EvoConvert.RemoveInsignificantNewLines(node);
} else if (node.nodeType == node.ELEMENT_NODE) {
if (node.hidden ||
node.tagName == "STYLE" ||
node.tagName == "META" ||
(node.tagName == "SPAN" && node.classList.contains("-x-evo-quoted")))
return str;
var style = EvoConvert.getComputedOrNodeStyle(node), ltr, align, indent, whiteSpace;
if (!style)
style = node.style;
ltr = !style || style.direction != "rtl";
align = style ? style.textAlign : "";
if (!align || align == "")
align = node.style.textAlign;
if (align)
align = align.toLowerCase();
if (!align)
align = "";
if (align == "" || align == "start")
align = ltr ? "left" : "right";
if (align == "center")
align = EvoConvert.ALIGN_CENTER;
else if (align == "right")
align = EvoConvert.ALIGN_RIGHT;
else if (align == "justify")
align = EvoConvert.ALIGN_JUSTIFY;
else
align = EvoConvert.ALIGN_LEFT;
// mixed indent and opposite text align does nothing
if ((ltr && align == EvoConvert.ALIGN_RIGHT) ||
(!ltr && align == EvoConvert.ALIGN_LEFT)) {
indent = "";
} else {
// computed style's 'indent' uses pixels, not characters
indent = ltr ? node.style.marginLeft : node.style.marginRight;
}
if (indent && indent.endsWith("ch")) {
indent = parseInt(indent.slice(0, -2));
if (!Number.isInteger(indent) || indent < 0)
indent = 0;
} else {
indent = 0;
}
if (indent > 0)
indent = " ".repeat(indent);
else
indent = "";
whiteSpace = style ? style.whiteSpace.toLowerCase() : "";
if (node.tagName == "DIV" || node.tagName == "P") {
var liText, extraIndent, width, useDefaultWidth = false;
liText = node.getAttribute("x-evo-li-text");
if (!liText)
liText = "";
extraIndent = node.getAttribute("x-evo-extra-indent");
extraIndent = extraIndent ? parseInt(extraIndent, 10) : 0;
if (!Number.isInteger(extraIndent)) {
extraIndent = 0;
}
width = node.style.width;
if (width && width.endsWith("ch")) {
width = parseInt(width.slice(0, -2));
if (!Number.isInteger(width) || width < 0)
useDefaultWidth = true;
} else {
useDefaultWidth = true;
}
if (useDefaultWidth && normalDivWidth > 0) {
width = normalDivWidth - (quoteLevel * 2);
if (width < EvoConvert.MIN_PARAGRAPH_WIDTH)
width = EvoConvert.MIN_PARAGRAPH_WIDTH;
}
str = EvoConvert.formatParagraph(EvoConvert.extractElemText(node, normalDivWidth, quoteLevel), ltr, align, indent, whiteSpace, width, extraIndent, liText, quoteLevel);
if (!liText && node.parentElement && (node.parentElement.tagName == "DIV" || node.parentElement.tagName == "P") &&
style.display == "block" && str != "" && node.previousSibling &&
((node.previousSibling.nodeType == node.ELEMENT_NODE && node.previousSibling.tagName != "DIV" && node.previousSibling.tagName != "P" && node.previousSibling.tagName != "BR") ||
(node.previousSibling.nodeType == node.TEXT_NODE && EvoConvert.RemoveInsignificantNewLines(node.previousSibling, true) != ""))) {
str = "\n" + str;
}
} else if (node.tagName == "PRE") {
str = EvoConvert.formatParagraph(EvoConvert.extractElemText(node, normalDivWidth, quoteLevel), ltr, align, indent, "pre", -1, 0, "", quoteLevel);
} else if (node.tagName == "BR") {
// ignore new-lines added by wrapping, treat them as spaces
if (node.classList.contains("-x-evo-wrap-br"))
str += " ";
else
str = "\n";
} else if (node.tagName == "IMG") {
str = EvoConvert.ImgToText(node);
} else if (node.tagName == "A" && !node.innerText.includes(" ") && !node.innerText.includes("\n")) {
str = EvoConvert.NOWRAP_CHAR_START + node.innerText + EvoConvert.NOWRAP_CHAR_END;
} else {
var isBlockquote = node.tagName == "BLOCKQUOTE";
str = EvoConvert.extractElemText(node, normalDivWidth, quoteLevel + (isBlockquote ? 1 : 0));
if (isBlockquote) {
var ii, lines = str.split("\n"), prefix, suffix;
prefix = ltr ? EvoConvert.getQuotePrefix(1, ltr) : "";
suffix = ltr ? "" : EvoConvert.getQuotePrefix(1, ltr);
str = "";
for (ii = 0; ii < lines.length; ii++) {
if (ii + 1 == lines.length && !lines[ii])
break;
str += prefix + lines[ii] + suffix + "\n";
}
}
if ((!isBlockquote || !str.endsWith("\n")) &&
str != "\n" && ((style && style.display == "block") || node.tagName == "ADDRESS")) {
str += "\n";
}
}
}
return str;
}
/*
* Converts element and its children to plain text. Any <div>,<ul>,<ol>, as an immediate child
* of the element, is wrapped to upto normalDivWidth characters, if it's defined and greater
* than EvoConvert.MIN_PARAGRAPH_WIDTH.
*/
EvoConvert.ToPlainText = function(element, normalDivWidth)
{
if (!element)
return null;
if (element.tagName == "HTML") {
var bodys;
bodys = element.getElementsByTagName("BODY");
if (bodys.length == 1)
element = bodys.item(0);
}
if (!element)
return null;
if (!normalDivWidth)
normalDivWidth = -1;
var disconnectFromHead = false;
if (!element.isConnected) {
// this is needed to be able to use window.getComputedStyle()
document.head.appendChild(element);
disconnectFromHead = true;
}
try {
var uls, ols, str = "", ii;
uls = element.getElementsByTagName("UL");
ols = element.getElementsByTagName("OL");
if (uls.length > 0 || ols.length > 0) {
element = element.cloneNode(true);
if (uls.length)
EvoConvert.replaceList(element, "UL", normalDivWidth);
if (ols.length)
EvoConvert.replaceList(element, "OL", normalDivWidth);
}
for (ii = 0; ii < element.childNodes.length; ii++) {
var node = element.childNodes.item(ii);
if (!node)
continue;
str = EvoConvert.appendNodeText(node, str, EvoConvert.processNode(node, normalDivWidth, 0));
}
} finally {
if (disconnectFromHead)
document.head.removeChild(element);
}
// remove EvoConvert.NOWRAP_CHAR_START and EvoConvert.NOWRAP_CHAR_END from the result
return str.replace(/\x01/g, "").replace(/\x02/g, "");
}