adding rich formatting to messags
This commit is contained in:
@@ -825,6 +825,10 @@ table.permission-editor td.checkbox {
|
|||||||
height: 4rem;
|
height: 4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#sendMessage {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
/* A static panel shown when no topic is selected */
|
/* A static panel shown when no topic is selected */
|
||||||
#dummy-view {
|
#dummy-view {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ Sample JSON representation of the text above:
|
|||||||
{
|
{
|
||||||
"at":8,
|
"at":8,
|
||||||
"len":4,
|
"len":4,
|
||||||
"tp":"BO"
|
"tp":"ST"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"at":14,
|
"at":14,
|
||||||
@@ -39,12 +39,12 @@ Sample JSON representation of the text above:
|
|||||||
{
|
{
|
||||||
"at":20,
|
"at":20,
|
||||||
"len":6,
|
"len":6,
|
||||||
"tp":"IT"
|
"tp":"EM"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"at":31,
|
"at":31,
|
||||||
"len":7,
|
"len":7,
|
||||||
"tp":"ST"
|
"tp":"DL"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -54,12 +54,12 @@ Sample JSON representation of the text above:
|
|||||||
{
|
{
|
||||||
"at":25,
|
"at":25,
|
||||||
"len":11,
|
"len":11,
|
||||||
"tp":"IT"
|
"tp":"EM"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"at":15,
|
"at":15,
|
||||||
"len":21,
|
"len":21,
|
||||||
"tp":"BO"
|
"tp":"ST"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -69,7 +69,7 @@ Sample JSON representation of the text above:
|
|||||||
{
|
{
|
||||||
"at":73,
|
"at":73,
|
||||||
"len":13,
|
"len":13,
|
||||||
"tp":"BO"
|
"tp":"ST"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ent":[
|
"ent":[
|
||||||
@@ -106,7 +106,7 @@ Sample JSON representation of the text above:
|
|||||||
{
|
{
|
||||||
"at":4,
|
"at":4,
|
||||||
"len":8,
|
"len":8,
|
||||||
"tp":"IT"
|
"tp":"EM"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ent":[
|
"ent":[
|
||||||
@@ -153,12 +153,12 @@ Sample JSON representation of the text above:
|
|||||||
// Regular expressions for parsing inline formats. Javascript does not support lookbehind,
|
// Regular expressions for parsing inline formats. Javascript does not support lookbehind,
|
||||||
// so it's a bit messy.
|
// so it's a bit messy.
|
||||||
var INLINE_STYLES = [
|
var INLINE_STYLES = [
|
||||||
// Bold, *bold text*
|
// Strong = bold, *bold text*
|
||||||
{name: "BO", start: /(?:^|\W)(\*)[^\s*]/, end: /[^\s*](\*)(?=$|\W)/},
|
{name: "ST", start: /(?:^|\W)(\*)[^\s*]/, end: /[^\s*](\*)(?=$|\W)/},
|
||||||
// Italic, _italic text_
|
// Emphesized = italic, _italic text_
|
||||||
{name: "IT", start: /(?:^|[\W_])(_)[^\s_]/, end: /[^\s_](_)(?=$|[\W_])/},
|
{name: "EM", start: /(?:^|[\W_])(_)[^\s_]/, end: /[^\s_](_)(?=$|[\W_])/},
|
||||||
// Strikethough, ~strike this though~
|
// Deleted, ~strike this though~
|
||||||
{name: "ST", start: /(?:^|\W)(~)[^\s~]/g, end: /[^\s~](~)(?=$|\W)/},
|
{name: "DL", start: /(?:^|\W)(~)[^\s~]/g, end: /[^\s~](~)(?=$|\W)/},
|
||||||
// Code block `this is monospace`
|
// Code block `this is monospace`
|
||||||
{name: "CO", start: /(?:^|\W)(`)[^`]/, end: /[^`](`)(?=$|\W)/}
|
{name: "CO", start: /(?:^|\W)(`)[^`]/, end: /[^`](`)(?=$|\W)/}
|
||||||
];
|
];
|
||||||
@@ -168,7 +168,7 @@ Sample JSON representation of the text above:
|
|||||||
// URLs
|
// URLs
|
||||||
{name: "LN", dataName: "url",
|
{name: "LN", dataName: "url",
|
||||||
pack: function(val) {
|
pack: function(val) {
|
||||||
// Check if the protocol is pecified, if not use http
|
// Check if the protocol is specified, if not use http
|
||||||
if (!/^[a-z]+:\/\//i.test(val)) {
|
if (!/^[a-z]+:\/\//i.test(val)) {
|
||||||
val = 'http://' + val;
|
val = 'http://' + val;
|
||||||
}
|
}
|
||||||
@@ -182,14 +182,14 @@ Sample JSON representation of the text above:
|
|||||||
// Hashtags #hashtag
|
// Hashtags #hashtag
|
||||||
{name: "HT", dataName: "val",
|
{name: "HT", dataName: "val",
|
||||||
pack: function(val) { return {val: val.slice(1)}; },
|
pack: function(val) { return {val: val.slice(1)}; },
|
||||||
re: /\B#(\w\w+)/g}
|
re: /\b#(\w\w+)/g}
|
||||||
];
|
];
|
||||||
|
|
||||||
// Formatting HTML tags
|
// Formatting HTML tags
|
||||||
var STYLE_DECOR = {
|
var STYLE_DECOR = {
|
||||||
BO: ['<b>', '</b>'],
|
ST: ['<b>', '</b>'],
|
||||||
IT: ['<i>', '</i>'],
|
EM: ['<i>', '</i>'],
|
||||||
ST: ['<del>', '</del>'],
|
DL: ['<del>', '</del>'],
|
||||||
CO: ['<tt>', '</tt>']
|
CO: ['<tt>', '</tt>']
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -253,7 +253,7 @@ Sample JSON representation of the text above:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Detect starts and ends of formatting spans. Unformatted spans are
|
// Detect starts and ends of formatting spans. Unformatted spans are
|
||||||
// ignored at this time.
|
// ignored at this stage.
|
||||||
function spannify(original, re_start, re_end, type) {
|
function spannify(original, re_start, re_end, type) {
|
||||||
var result = [];
|
var result = [];
|
||||||
var index = 0;
|
var index = 0;
|
||||||
@@ -269,8 +269,8 @@ Sample JSON representation of the text above:
|
|||||||
if (start == null) {
|
if (start == null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Because javascript RegExp does not support lookbehind, the actual offset may not be at the beginning of
|
// Because javascript RegExp does not support lookbehind, the actual offset may not point
|
||||||
// the match. Find it in the matched string.
|
// at the markup character. Find it in the matched string.
|
||||||
var start_offset = start['index'] + start[0].lastIndexOf(start[1]);
|
var start_offset = start['index'] + start[0].lastIndexOf(start[1]);
|
||||||
// Clip the processed part of the string.
|
// Clip the processed part of the string.
|
||||||
line = line.slice(start_offset + 1);
|
line = line.slice(start_offset + 1);
|
||||||
@@ -337,11 +337,11 @@ Sample JSON representation of the text above:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get a list of entities from a text.
|
// Get a list of entities from a text.
|
||||||
function extractEntities(line, re, name) {
|
function extractEntities(line) {
|
||||||
var match;
|
var match;
|
||||||
var extracted = [];
|
var extracted = [];
|
||||||
ENTITY_TYPES.map(function(ent) {
|
ENTITY_TYPES.map(function(ent) {
|
||||||
while((match = ent.re.exec(line)) !== null) {
|
while ((match = ent.re.exec(line)) !== null) {
|
||||||
extracted.push({
|
extracted.push({
|
||||||
offset: match['index'],
|
offset: match['index'],
|
||||||
len: match[0].length,
|
len: match[0].length,
|
||||||
@@ -371,13 +371,13 @@ Sample JSON representation of the text above:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert the chunks into format suitable for serialization.
|
// Convert the chunks into format suitable for serialization.
|
||||||
function toDrafty(chunks, startAt) {
|
function draftify(chunks, startAt) {
|
||||||
var plain = "";
|
var plain = "";
|
||||||
var ranges = [];
|
var ranges = [];
|
||||||
for (var i in chunks) {
|
for (var i in chunks) {
|
||||||
var chunk = chunks[i];
|
var chunk = chunks[i];
|
||||||
if (!chunk.text) {
|
if (!chunk.text) {
|
||||||
var drafty = toDrafty(chunk.children, plain.length + startAt);
|
var drafty = draftify(chunk.children, plain.length + startAt);
|
||||||
chunk.text = drafty.txt;
|
chunk.text = drafty.txt;
|
||||||
ranges = ranges.concat(drafty.fmt);
|
ranges = ranges.concat(drafty.fmt);
|
||||||
}
|
}
|
||||||
@@ -401,8 +401,8 @@ Sample JSON representation of the text above:
|
|||||||
* Parse text into structured representation.
|
* 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 further processing easier.
|
||||||
var lines = content.split('\n');
|
var lines = content.split(/\r?\n/);
|
||||||
// Holds formatted lines
|
// Holds formatted lines
|
||||||
var blocks = [];
|
var blocks = [];
|
||||||
// Holds entities referenced from text
|
// Holds entities referenced from text
|
||||||
@@ -432,10 +432,11 @@ Sample JSON representation of the text above:
|
|||||||
// Convert an array of possibly overlapping spans into a tree
|
// Convert an array of possibly overlapping spans into a tree
|
||||||
spans = toTree(spans);
|
spans = toTree(spans);
|
||||||
|
|
||||||
// Build tree representation of the format.
|
// Build a tree representation of the entire string, not
|
||||||
|
// just the formatted parts.
|
||||||
var chunks = chunkify(line, 0, line.length, spans);
|
var chunks = chunkify(line, 0, line.length, spans);
|
||||||
|
|
||||||
var drafty = toDrafty(chunks, 0);
|
var drafty = draftify(chunks, 0);
|
||||||
|
|
||||||
block = {txt: drafty.txt, fmt: drafty.fmt};
|
block = {txt: drafty.txt, fmt: drafty.fmt};
|
||||||
}
|
}
|
||||||
@@ -475,7 +476,7 @@ Sample JSON representation of the text above:
|
|||||||
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].txt;
|
var line = blocks[i].txt;
|
||||||
var markup = [];
|
var markup = [];
|
||||||
@@ -505,9 +506,9 @@ Sample JSON representation of the text above:
|
|||||||
line = splice(line, markup[j].idx, markup[j].what);
|
line = splice(line, markup[j].idx, markup[j].what);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
text += line + "<br>";
|
text.push(line);
|
||||||
}
|
}
|
||||||
return text;
|
return text.join("<br/>");
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
+19
-4
@@ -19,6 +19,7 @@
|
|||||||
<script src="https://unpkg.com/react-dom@15.4/dist/react-dom.js"></script>
|
<script src="https://unpkg.com/react-dom@15.4/dist/react-dom.js"></script>
|
||||||
<script src="https://unpkg.com/babel-standalone@6.24.0/babel.min.js"></script>
|
<script src="https://unpkg.com/babel-standalone@6.24.0/babel.min.js"></script>
|
||||||
<script src="tinode.js"></script>
|
<script src="tinode.js"></script>
|
||||||
|
<script src="drafty.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="mountPoint"></div>
|
<div id="mountPoint"></div>
|
||||||
@@ -2318,9 +2319,15 @@
|
|||||||
},
|
},
|
||||||
/* Send on Enter key */
|
/* Send on Enter key */
|
||||||
handleKeyPress: function(e) {
|
handleKeyPress: function(e) {
|
||||||
/* Remove this if you don't want Enter to trigger send */
|
// Remove this if you don't want Enter to trigger send
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
this.handleSend();
|
// Have Shift-Enter insert a line break instead
|
||||||
|
if (!e.shiftKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
this.handleSend();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleMessageTyping: function(e) {
|
handleMessageTyping: function(e) {
|
||||||
@@ -2338,7 +2345,7 @@
|
|||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<div id="send-message-panel">
|
<div id="send-message-panel">
|
||||||
<input id="sendMessage" type="text" placeholder="New message"
|
<textarea id="sendMessage" placeholder="New message"
|
||||||
value={this.state.message} onChange={this.handleMessageTyping} onKeyPress={this.handleKeyPress} />
|
value={this.state.message} onChange={this.handleMessageTyping} onKeyPress={this.handleKeyPress} />
|
||||||
<a href="#" onClick={this.handleSend}>
|
<a href="#" onClick={this.handleSend}>
|
||||||
<i className="material-icons">send</i>
|
<i className="material-icons">send</i>
|
||||||
@@ -2613,7 +2620,15 @@
|
|||||||
// User clicked on a message send button.
|
// User clicked on a message send button.
|
||||||
handleSendMessage: function(msg) {
|
handleSendMessage: function(msg) {
|
||||||
var topic = Tinode.getTopic(this.state.topicSelected);
|
var topic = Tinode.getTopic(this.state.topicSelected);
|
||||||
topic.publish(msg);
|
// Check if the message is formatted.
|
||||||
|
var dft = Drafty.parse(msg);
|
||||||
|
if (Drafty.isPlain(dft)) {
|
||||||
|
// The message is simple text. Sned if as text/plain.
|
||||||
|
topic.publish(msg);
|
||||||
|
} else {
|
||||||
|
// Formatted message. Send it as a Drafty object.
|
||||||
|
topic.publish(dft, undefined, "text/x-drafty+json");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// User chose a Sign Up menu item.
|
// User chose a Sign Up menu item.
|
||||||
handleNewAccount: function() {
|
handleNewAccount: function() {
|
||||||
|
|||||||
Reference in New Issue
Block a user