added a couple of supporting methods and made representation a bit more compact
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user