diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..b24fe9f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2021 Frameable Inc
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 24625eb..9b9d018 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
Encode html to its component plain-text and meta style parts
```javascript
-{ encode, decode } = require('html-text-projection');
+{ encode, decode } = require('html-text-weaver');
const html = '
Hey, you! Get out of there!
';
diff --git a/index.js b/index.js
index d386fc6..0bf3b36 100644
--- a/index.js
+++ b/index.js
@@ -10,7 +10,6 @@ const ATTRIBUTES = 3;
const KEY = 0;
const VALUE = 1;
-const { document } = (typeof global === 'undefined' ? eval('this') : eval('global'));
const linkify = require('./linkify');
class Weaver {
@@ -48,7 +47,10 @@ class Weaver {
let attributes = null;
for (const attributeName of this.tagAttributes[tagName] || []) {
attributes = attributes || [];
- attributes.push([ attributeName, child.getAttribute(attributeName) ]);
+ const attrValue = child.getAttribute(attributeName);
+ if (attrValue) {
+ attributes.push([ attributeName, attrValue ]);
+ }
}
const marker = [tagName, text.length, null, attributes];
meta.push(marker);
@@ -70,6 +72,8 @@ class Weaver {
}
decode({ text, meta }) {
+ if (text === undefined) throw new Error('no text passed to decode');
+ if (meta === undefined) throw new Error('no meta passed to decode');
const links = linkify.findLinkOffsets(text);
@@ -82,6 +86,7 @@ class Weaver {
];
const _between = (a, b, c) => a < b && b < c;
+ const _betweenOrEq = (a, b, c) => a <= b && b <= c;
for (const m of meta) {
const startInside = _between(link.offsets[0], m[OPEN_OFFSET], link.offsets[1]);
@@ -92,10 +97,17 @@ class Weaver {
let candidateIndex = null;
for (const [i, m] of meta.entries()) {
- if (m[OPEN_OFFSET] < link.offsets[0])
+ if (m[OPEN_OFFSET] <= link.offsets[0])
candidateIndex = i + 1;
}
candidateIndex = candidateIndex || 0;
+
+ const enclosingMetaATags = meta
+ .filter(m => m[TAG_NAME].toLowerCase() === 'a')
+ .filter(m => _betweenOrEq(m[OPEN_OFFSET], link.offsets[0], m[CLOSE_OFFSET]));
+
+ if (enclosingMetaATags.length) continue LINK;
+
meta.splice(candidateIndex, 0, anchorMeta);
}
diff --git a/package.json b/package.json
index 32b8453..036eedd 100644
--- a/package.json
+++ b/package.json
@@ -1,13 +1,13 @@
{
"name": "html-text-weaver",
- "version": "1.0.2",
+ "version": "1.0.11",
"description": "",
"main": "index.js",
"scripts": {
"test": "node_modules/.bin/mocha -u tdd test"
},
"author": "",
- "license": "ISC",
+ "license": "MIT",
"dependencies": {
"basichtml": "^0.22.1",
"linkifyjs": "^2.1.8"
diff --git a/test.js b/test.js
index 2b50dd1..6e98e0a 100644
--- a/test.js
+++ b/test.js
@@ -16,7 +16,7 @@ suite('encode', () => {
});
test('alert', () => {
- const encodedText = weaver.encode(escapeHTML(''))
+ const encodedText = weaver.encode(escapeHTML(''));
assert.deepEqual(encodedText, {
text: '',
meta: []
@@ -33,6 +33,14 @@ suite('encode', () => {
});
});
+ test('trim the input', () => {
+ const encodedText = weaver.encode(' my padded text \n');
+ assert.deepEqual(encodedText,{
+ text: 'my padded text',
+ meta: [],
+ });
+ });
+
test('links', () => {
const encodedText = weaver.encode('YAHOO');
assert.deepEqual(encodedText, {
@@ -54,7 +62,7 @@ suite('encode', () => {
});
test('stacked tags', () => {
- const encodedText = weaver.encode('she told him that he was the worst!')
+ const encodedText = weaver.encode('she told him that he was the worst!');
assert.deepEqual(encodedText, {
text: 'she told him that he was the worst!',
meta: [
@@ -68,6 +76,13 @@ suite('encode', () => {
suite('decode', () => {
+ test('errors with bad inputs', () => {
+ const noMeta = () => weaver.decode({ text: 'test' });
+ assert.throws(noMeta, Error);
+ const noText = () => weaver.decode({ meta: [] });
+ assert.throws(noText, Error);
+ });
+
test('alert', () => {
const decodedText = weaver.decode({
text: '',
@@ -95,7 +110,7 @@ suite('decode', () => {
['i', 31, 37],
]
});
- assert.equal(decodedText, 'she told him that
he
was the worst!')
+ assert.equal(decodedText, 'she told him that
he
was the worst!');
});
test('stacked tags', () => {
@@ -107,7 +122,7 @@ suite('decode', () => {
['i', 29, 35],
]
});
- assert.equal(decodedText, 'she told him that he was the worst!')
+ assert.equal(decodedText, 'she told him that he was the worst!');
});
});
@@ -127,11 +142,51 @@ suite('linkify', () => {
meta: [ ['b', 0, 5], ['i', 6, 15] ]
});
- assert.equal(html, 'visit yahoo.com and gmx.net today!');
+ assert.equal(html, 'visit yahoo.com and gmx.net today!');
+ });
+
+ test('ignore links with partially overlapping tags', () => {
+ const overlappingAfter = weaver.decode(weaver.encode('yahoooo.com is great'));
+ assert.equal(overlappingAfter, 'yahoooo.com is great');
+ const overlappingBefore = weaver.decode(weaver.encode('yahoooo.com is great'));
+ assert.equal(overlappingBefore, 'yahoooo.com is great');
+ const notOverlapping = weaver.decode(weaver.encode('yahoooo.com is great'));
+ assert.equal(notOverlapping, 'yahoooo.com is great');
+ });
+
+ test('linkify inside style', () => {
+ assert.equal(
+ weaver.decode(weaver.encode('yahoo.com')),
+ 'yahoo.com'
+ );
+ });
+
+ test('round trip without adding extra links', () => {
+ const html = weaver.decode(weaver.encode(weaver.decode({
+ text: 'yahoo.com',
+ meta: []
+ })));
+ assert.equal(html, 'yahoo.com');
+ });
+
+ test("round trip without adding extra links when href doesn't match content", () => {
+ const html = weaver.decode(weaver.encode('yahoo.com'));
+ assert.equal(html, 'yahoo.com');
});
-
+ test("don't linkify when we are inside an a tag", () => {
+ const input = 'A bunch of stuff yadayahoo.com';
+ const html = weaver.decode(weaver.encode(input));
+ assert.equal(html, input);
+ });
+ test('double round trip without adding extra links', () => {
+ const html = weaver.decode(weaver.encode(weaver.decode(weaver.encode(weaver.decode({
+ text: 'yahoo.com',
+ meta: []
+ })))));
+ assert.equal(html, 'yahoo.com');
+ });
});
@@ -158,6 +213,10 @@ suite('encode-decode', () => {
}
});
+ test('works with empty string', () => {
+ const html = weaver.decode(weaver.encode(''));
+ assert.equal(html, '');
+ });
test('newlines', () => {
const inputs = [
@@ -166,7 +225,18 @@ suite('encode-decode', () => {
for (const input of inputs) {
assert.equal(input, weaver.decode(weaver.encode(input)));
}
+ });
- })
+ test('no doubling up', () => {
+ const input = 'visit yahoo.com today!';
+ const decodedHtml = weaver.decode(weaver.encode(input));
+ assert.equal(decodedHtml, input);
+ });
+
+ test('no doubling doubling up', () => {
+ const input = 'visit yahoo.com today!';
+ const decodedHtml = weaver.decode(weaver.encode(weaver.decode(weaver.encode(input))));
+ assert.equal(decodedHtml, input);
+ });
});