diff --git a/lib/helpers.js b/lib/helpers.js
index bf18243..a65dd6c 100644
--- a/lib/helpers.js
+++ b/lib/helpers.js
@@ -1,4 +1,25 @@
+
+function unvantedFirstOrLastNode(node) {
+ if (node.tag === 'br' || (isEmptyText(node) && !isStatement(node))) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+function isExcludedNode(node) {
+ if ((node.tag === 'i' && node.attributes.find(a => a.name === 'class' && a.value?.chars?.indexOf('auditboard-icons') !== -1)) ||
+ (node.tag === 'span' && node.attributes.find(a => a.name === 'class' && a.value?.chars?.indexOf('clearfix') !== -1))
+ ) {
+ return true;
+ }
+ return false;
+}
+
function* iterateChildren(node) {
+ if (isExcludedNode(node)) {
+ return;
+ }
switch (node.type) {
case 'MustacheCommentStatement':
case 'MustacheStatement':
@@ -57,15 +78,32 @@ function replaceChildren(node, newChildren) {
}
}
+// function return true for node if there is no text to translate.
function isEmptyText(node) {
- return node.type === 'TextNode' && node.chars.match(/^\s*$/);
-}
+ // TextNode is empty when there are only white space or   or there are no characters;
+ let isEmptyTextNode = node.type === 'TextNode' && (node.chars.match(/^\s*( )*\s*$/) || node.chars.match(/^[^a-zA-Z]+$/));
-function isNotEmptyText(node) {
- return node.type === 'TextNode' && !node.chars.match(/^\s*$/);
+ if (isEmptyTextNode || node.tag === 'br' || node.type === 'MustacheStatement') {
+ return true;
+ }
+
+ //allow only allowed text elements
+ if (!allowedTextElements.includes(node.tag) ||
+ (node.modifiers && node.modifiers.length)) {// ||
+ return false;
+ }
+
+ //exclude special nodes
+ if (isExcludedNode(node)) {
+ return false;
+ }
+
+ let children = Array.from(iterateChildren(node));
+ let emptyOrStatement = children.every(n => isEmptyText(n));
+ return emptyOrStatement;
}
-const allowedTextElements = ['i', 'em', 'b', 'strong', 'bold', 'span', 'a', 'code', 'br'];
+const allowedTextElements = ['i', 'em', 'b', 'strong', 'bold', 'span', 'a', 'code', 'br', 'sup', 'sub'];
function isStatement(node) {
if (node.type === 'MustacheStatement') {
return true;
@@ -73,75 +111,63 @@ function isStatement(node) {
if (!allowedTextElements.includes(node.tag) ||
(node.modifiers && node.modifiers.length)) {// ||
- // node.attributes.find(a => a.value.params?.length || a.value.parts?.find(p => p.params?.length))) {
+ return false;
+ }
+
+ //exclude special nodes
+ if (isExcludedNode(node)) {
return false;
}
let children = Array.from(iterateChildren(node));
- let everyEmptyOrMustache = children.every(ch => isEmptyText(ch) || ch.type === 'MustacheStatement');
- return everyEmptyOrMustache;
+ let everyTextOrMustache = children.every(ch => isText(ch) || isStatement(ch));
+ let atLeastOneMustache = children.find(ch => isStatement(ch));
+ return everyTextOrMustache && atLeastOneMustache;
}
function isText(node) {
- if ((node.type === 'TextNode' && isNotEmptyText(node)) || node.tag === 'br') {
+ if (node.type === 'TextNode' || node.tag === 'br' || node.type === 'MustacheStatement') {
return true;
}
+ //allow only allowed text elements
if (!allowedTextElements.includes(node.tag) ||
(node.modifiers && node.modifiers.length)) {// ||
- // node.attributes.find(a => a.value.params?.length || a.value.parts?.find(p => p.params?.length))) {
return false;
}
- let children = Array.from(iterateChildren(node));
- let everyText = children.every(n => isText(n) || isStatement(n) || isEmptyText(n));
- let onlyNotEmptys = children.filter(ch => !isEmptyText(ch));
- if (onlyNotEmptys.length) {
- let firstText = isText(onlyNotEmptys[0]);
- let lastText = isText(onlyNotEmptys[onlyNotEmptys.length - 1]);
- return everyText && firstText && lastText;
- } else {
+ //exclude special nodes
+ if (isExcludedNode(node)) {
return false;
}
-}
-function* iteratePureNodes(node, parent, index) {
- if (isText(node) || isStatement(node)) {
- yield { node, parent, index, isText, isStatement };
- } else {
- let i = 0;
- for (let subNode of iterateChildren(node)) {
- for (let res of iteratePureNodes(subNode, node, i)) {
- yield res;
- }
- i++;
- }
- }
+
+ let children = Array.from(iterateChildren(node));
+ let everyText = children.every(n => isText(n));
+ return everyText;
}
function checksFirstLastStatement(group) {
- let thereIsText = group.find(i => isText(i.node));
+ let thereIsText = group.find(i => !isEmptyText(i.node));
if (thereIsText) {
- let notEmptyItems = group.filter(i => !isEmptyText(i.node));
- //remove statements from beginning
- while (group.length && notEmptyItems.length) {
- let isStatementFirst = isStatement(notEmptyItems[0].node);
+ while (group.length) {
+ let isStatementFirst = unvantedFirstOrLastNode(group[0].node);
if (isStatementFirst) {
- notEmptyItems.shift();
+ group.shift();
} else {
break;
}
}
- while (group.length && notEmptyItems.length) {
- let isStatementLast = isStatement(notEmptyItems[notEmptyItems.length - 1].node);
+ while (group.length) {
+ let isStatementLast = unvantedFirstOrLastNode(group[group.length - 1].node);
if (isStatementLast) {
- notEmptyItems.pop();
+ group.pop();
} else {
break;
}
}
- return notEmptyItems;
+ return group;
}
return [];
@@ -150,7 +176,7 @@ function checksFirstLastStatement(group) {
function* iterateGroups(node) {
let group = [];
for (let subNode of iterateChildren(node)) {
- if (isText(subNode) || isEmptyText(subNode) || isStatement(subNode)) {
+ if (isText(subNode) || isEmptyText(subNode)) {
group.push({
node: subNode,
parent: node
@@ -352,7 +378,6 @@ function replace(group, serialized, isInsideStatement) {
}
module.exports = {
- iteratePureNodes,
iterateChildren,
iterateGroups,
serializeGroup,
diff --git a/package.json b/package.json
index 5afdd36..74a193a 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,8 @@
"scripts": {
"start": "node runner.js",
"lint": "eslint .",
- "test": "mocha --recursive test/acceptance test/unit"
+ "test": "mocha --recursive test/acceptance test/unit",
+ "test-watch": "mocha --watch --recursive test/acceptance test/unit"
},
"author": "",
"license": "ISC",
@@ -26,4 +27,4 @@
"recursive-copy": "^2.0.13",
"temp": "^0.9.4"
}
-}
+}
\ No newline at end of file
diff --git a/test/unit/br.js b/test/unit/br.js
new file mode 100644
index 0000000..b08a0e7
--- /dev/null
+++ b/test/unit/br.js
@@ -0,0 +1,69 @@
+const testCase = require('../helpers/test-case');
+
+describe('nested elements', function() {
+ testCase({
+ name: 'Br inside text',
+ input: `
+
+ Hello
+
+ world
+ `,
+ output: `
+
+ {{t "Hello
world" htmlSafe=true}}
+ `
+ });
+
+ testCase({
+ name: 'No br at the end of the translation',
+ input: `
+
+ Hello
+
+ world
+
+ `,
+ output: `
+
+ {{t "Hello
world" htmlSafe=true}}
+
+ `
+ });
+
+ testCase({
+ name: 'No br at the start of the translation',
+ input: `
+
+
+ Hello
+
+ world
+ `,
+ output: `
+
+
+ {{t "Hello
world" htmlSafe=true}}
+ `
+ });
+
+ testCase({
+ name: 'No br at the start/end of the translation',
+ input: `
+
+
+
+ Hello
+
+ world
+
+ `,
+ output: `
+
+
+
+ {{t "Hello
world" htmlSafe=true}}
+
+ `
+ });
+})
diff --git a/test/unit/emptyText.js b/test/unit/emptyText.js
new file mode 100644
index 0000000..8908ab1
--- /dev/null
+++ b/test/unit/emptyText.js
@@ -0,0 +1,31 @@
+const testCase = require('../helpers/test-case');
+
+describe('Empty text', function() {
+ testCase({
+ name: 'Remove empty elements from start and begining of a translation',
+ input: `
+
+
+ This is
+
+ `,
+ output: `
+
+
+ {{t "This is"}}
+
+ `
+ });
+
+ testCase({
+ name: 'Do not remove empty elements inside translation.',
+ input: `
+ This is
+
+ text.
+ `,
+ output: `
+ {{t "This is text." htmlSafe=true}}
+ `
+ });
+});
\ No newline at end of file
diff --git a/test/unit/groupAllowedElements.js b/test/unit/groupAllowedElements.js
new file mode 100644
index 0000000..6dca63f
--- /dev/null
+++ b/test/unit/groupAllowedElements.js
@@ -0,0 +1,32 @@
+
+const testCase = require('../helpers/test-case');
+
+describe('grouping allowed elements', function() {
+ testCase({
+ name: 'merge html elements without element-modifiers',
+ input: `
+
h