added a couple of supporting methods and made representation a bit more compact

This commit is contained in:
or-else
2017-08-18 11:01:51 -07:00
parent c88c821002
commit 5c74be339a
+111 -55
View File
@@ -14,111 +14,131 @@
/* /*
Text: Text:
this is *bold*, `code`, _italic_ and ~deleted~.
this is *bold*, `code` and _italic_ ~strike~ eol nested styles: *just bold _bold-italic_*
none *bold _italic_* eol couple of urls: http://www.example.com/path?a=b%20c#fragment, and bolded *www.tinode.co*
this is a url: http://www.example.com/path?a=b%20c#fragment, and another www.tinode.co
this is a @mention and a #hashtag in a string this is a @mention and a #hashtag in a string
plain unformatted text the _#hashtag_ used again
Sample JSON format of the text above: Sample JSON representation of the text above:
{ {
"blocks":[ "blocks":[
{ {
"text":"this is bold, code and italic strike eol", "txt":"this is bold, code, italic and deleted.",
"styles":[ "fmt":[
{ {
"offset":8, "at":8,
"len":4, "len":4,
"style":"BO" "tp":"BO"
}, },
{ {
"offset":14, "at":14,
"len":4, "len":4,
"style":"CO" "tp":"CO"
}, },
{ {
"offset":23, "at":20,
"len":6, "len":6,
"style":"IT" "tp":"IT"
}, },
{ {
"offset":30, "at":31,
"len":6, "len":7,
"style":"ST" "tp":"ST"
} }
] ]
}, },
{ {
"text":"none bold italic eol", "txt":"nested styles: just bold bold-italic",
"styles":[ "fmt":[
{ {
"offset":10, "at":25,
"len":6,
"style":"IT"
},
{
"offset":5,
"len":11, "len":11,
"style":"BO" "tp":"IT"
},
{
"at":15,
"len":21,
"tp":"BO"
} }
] ]
}, },
{ {
"text":"this is a url: http://www.example.com/path?a=b%20c#fragment, and another www.tinode.co", "txt":"couple of urls: http://www.example.com/path?a=b%20c#fragment, and bolded www.tinode.co",
"fmt":[
{
"at":73,
"len":13,
"tp":"BO"
}
],
"ent":[ "ent":[
{ {
"offset":15, "at":16,
"len":44, "len":44,
"key":0 "key":0
}, },
{ {
"offset":73, "at":73,
"len":13, "len":13,
"key":1 "key":1
} }
] ]
}, },
{ {
"text":"this is a @mention and a #hashtag in a string", "txt":"this is a @mention and a #hashtag in a string",
"ent":[ "ent":[
{ {
"offset":10, "at":10,
"len":8, "len":8,
"key":2 "key":2
}, },
{ {
"offset":25, "at":25,
"len":8, "len":8,
"key":3 "key":3
} }
] ]
}, },
{ {
"text":"plain unformatted text" "txt":"the #hashtag used again",
"fmt":[
{
"at":4,
"len":8,
"tp":"IT"
}
],
"ent":[
{
"at":4,
"len":8,
"key":3
}
]
} }
], ],
"refs":[ "refs":[
{ {
"type":"LN", "tp":"LN",
"data":{ "data":{
"url":"http://www.example.com/path?a=b%20c#fragment" "url":"http://www.example.com/path?a=b%20c#fragment"
} }
}, },
{ {
"type":"LN", "tp":"LN",
"data":{ "data":{
"url":"http://www.tinode.co" "url":"http://www.tinode.co"
} }
}, },
{ {
"type":"MN", "tp":"MN",
"data":{ "data":{
"val":"mention" "val":"mention"
} }
}, },
{ {
"type":"HT", "tp":"HT",
"data":{ "data":{
"val":"hashtag" "val":"hashtag"
} }
@@ -358,17 +378,17 @@ Sample JSON format of the text above:
var chunk = chunks[i]; var chunk = chunks[i];
if (!chunk.text) { if (!chunk.text) {
var drafty = toDrafty(chunk.children, plain.length + startAt); var drafty = toDrafty(chunk.children, plain.length + startAt);
chunk.text = drafty.text; chunk.text = drafty.txt;
ranges = ranges.concat(drafty.ranges); ranges = ranges.concat(drafty.fmt);
} }
if (chunk.type) { if (chunk.type) {
ranges.push({offset: plain.length + startAt, len: chunk.text.length, style: chunk.type}); ranges.push({at: plain.length + startAt, len: chunk.text.length, tp: chunk.type});
} }
plain += chunk.text; plain += chunk.text;
} }
return {text: plain, ranges: ranges}; return {txt: plain, fmt: ranges};
} }
// Splice two strings: insert second string into the first one at the given index // Splice two strings: insert second string into the first one at the given index
@@ -377,6 +397,9 @@ Sample JSON format of the text above:
} }
return { return {
/**
* Parse text into structured representation.
*/
parse: function(content) { parse: function(content) {
// Split text into lines. It makes formatting easier // Split text into lines. It makes formatting easier
var lines = content.split('\n'); var lines = content.split('\n');
@@ -399,7 +422,7 @@ Sample JSON format of the text above:
var block; var block;
if (spans.length == 0) { if (spans.length == 0) {
block = {text: line}; block = {txt: line};
} else { } else {
// Sort spans by style occurence early -> late // Sort spans by style occurence early -> late
spans.sort(function(a,b) { spans.sort(function(a,b) {
@@ -414,11 +437,11 @@ Sample JSON format of the text above:
var drafty = toDrafty(chunks, 0); var drafty = toDrafty(chunks, 0);
block = {text: drafty.text, styles: drafty.ranges}; block = {txt: drafty.txt, fmt: drafty.fmt};
} }
// Extract entities from the cleaned up string. // Extract entities from the cleaned up string.
entities = extractEntities(block.text); entities = extractEntities(block.txt);
var ranges = []; var ranges = [];
if (entities.length > 0) { if (entities.length > 0) {
for (var i in entities) { for (var i in entities) {
@@ -428,9 +451,9 @@ Sample JSON format of the text above:
if (!index) { if (!index) {
index = entityMap.length; index = entityMap.length;
entityIndex[ent.unique] = index; entityIndex[ent.unique] = index;
entityMap.push({type: ent.type, data: ent.data}); entityMap.push({tp: ent.type, data: ent.data});
} }
ranges.push({offset: ent.offset, len: ent.len, key: index}); ranges.push({at: ent.offset, len: ent.len, key: index});
} }
block.ent = ranges; block.ent = ranges;
} }
@@ -445,19 +468,22 @@ Sample JSON format of the text above:
return result; return result;
}, },
format: function(content) { /**
* Given structured representation of rich text, convert it to HTML.
*/
toHTML: function(content) {
var {blocks} = content; var {blocks} = content;
var {refs} = content; var {refs} = content;
var text = ""; var text = "";
for (var i in blocks) { for (var i in blocks) {
var line = blocks[i].text; var line = blocks[i].txt;
var markup = []; var markup = [];
if (blocks[i].styles) { if (blocks[i].fmt) {
for (var j in blocks[i].styles) { for (var j in blocks[i].fmt) {
var range = blocks[i].styles[j]; var range = blocks[i].fmt[j];
markup.push({idx: range.offset, what: STYLE_DECOR[range.style][0]}); markup.push({idx: range.at, what: STYLE_DECOR[range.tp][0]});
markup.push({idx: range.offset + range.len, what: STYLE_DECOR[range.style][1]}); markup.push({idx: range.at + range.len, what: STYLE_DECOR[range.tp][1]});
} }
} }
@@ -465,8 +491,8 @@ Sample JSON format of the text above:
for (var j in blocks[i].ent) { for (var j in blocks[i].ent) {
var range = blocks[i].ent[j]; var range = blocks[i].ent[j];
var entity = refs[range.key]; var entity = refs[range.key];
markup.push({idx: range.offset, what: ENTITY_DECOR[entity.type].open(entity.data)}); markup.push({idx: range.at, what: ENTITY_DECOR[entity.tp].open(entity.data)});
markup.push({idx: range.offset + range.len, what: ENTITY_DECOR[entity.type].close(entity.data)}); markup.push({idx: range.at + range.len, what: ENTITY_DECOR[entity.tp].close(entity.data)});
} }
} }
@@ -482,7 +508,37 @@ Sample JSON format of the text above:
text += line + "<br>"; text += line + "<br>";
} }
return text; return text;
},
/*
* Given structured representation of rich text, convert it to plain text.
* The only structure preserved is line breaks as \n.
*/
toPlainText: function(content) {
var {blocks} = content;
var text = blocks[0].text;
for (var i = 1; i < blocks.length; i++) {
text += "\n" + blocks[i].txt;
} }
return text;
},
/**
* Returns true if content has no markup and no entities.
*/
isPlain: function(content) {
if (content.refs) {
return false;
}
var {blocks} = content;
for (var i in blocks) {
if (blocks[i].fmt) {
return false;
}
}
return true;
}
}; };
}); });