Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit be41462

Browse files
committed
merge
2 parents 0a3d9fc + cd5a1ab commit be41462

35 files changed

+622
-276
lines changed

.eslintrc.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,13 @@ module.exports = {
5353
* things that are errors in the js-sdk config that the current
5454
* code does not adhere to, turned down to warn
5555
*/
56-
"max-len": ["warn"],
56+
"max-len": ["warn", {
57+
// apparently people believe the length limit shouldn't apply
58+
// to JSX.
59+
ignorePattern: '^\\s*<',
60+
ignoreComments: true,
61+
code: 90,
62+
}],
5763
"valid-jsdoc": ["warn"],
5864
"new-cap": ["warn"],
5965
"key-spacing": ["warn"],

karma.conf.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,14 @@ module.exports = function (config) {
165165
},
166166
devtool: 'inline-source-map',
167167
},
168+
169+
webpackMiddleware: {
170+
stats: {
171+
// don't fill the console up with a mahoosive list of modules
172+
chunks: false,
173+
},
174+
},
175+
168176
browserNoActivityTimeout: 15000,
169177
});
170178
};

src/Markdown.js

Lines changed: 95 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -15,110 +15,143 @@ limitations under the License.
1515
*/
1616

1717
import commonmark from 'commonmark';
18+
import escape from 'lodash/escape';
19+
20+
const ALLOWED_HTML_TAGS = ['del'];
21+
22+
// These types of node are definitely text
23+
const TEXT_NODES = ['text', 'softbreak', 'linebreak', 'paragraph', 'document'];
24+
25+
function is_allowed_html_tag(node) {
26+
// Regex won't work for tags with attrs, but we only
27+
// allow <del> anyway.
28+
const matches = /^<\/?(.*)>$/.exec(node.literal);
29+
if (matches && matches.length == 2) {
30+
const tag = matches[1];
31+
return ALLOWED_HTML_TAGS.indexOf(tag) > -1;
32+
}
33+
return false;
34+
}
35+
36+
function html_if_tag_allowed(node) {
37+
if (is_allowed_html_tag(node)) {
38+
this.lit(node.literal);
39+
return;
40+
} else {
41+
this.lit(escape(node.literal));
42+
}
43+
}
44+
45+
/*
46+
* Returns true if the parse output containing the node
47+
* comprises multiple block level elements (ie. lines),
48+
* or false if it is only a single line.
49+
*/
50+
function is_multi_line(node) {
51+
var par = node;
52+
while (par.parent) {
53+
par = par.parent;
54+
}
55+
return par.firstChild != par.lastChild;
56+
}
1857

1958
/**
20-
* Class that wraps marked, adding the ability to see whether
59+
* Class that wraps commonmark, adding the ability to see whether
2160
* a given message actually uses any markdown syntax or whether
2261
* it's plain text.
2362
*/
2463
export default class Markdown {
2564
constructor(input) {
2665
this.input = input;
27-
this.parser = new commonmark.Parser();
28-
this.renderer = new commonmark.HtmlRenderer({safe: false});
66+
67+
const parser = new commonmark.Parser();
68+
this.parsed = parser.parse(this.input);
2969
}
3070

3171
isPlainText() {
32-
// we determine if the message requires markdown by
33-
// running the parser on the tokens with a dummy
34-
// rendered and seeing if any of the renderer's
35-
// functions are called other than those noted below.
36-
// In case you were wondering, no we can't just examine
37-
// the tokens because the tokens we have are only the
38-
// output of the *first* tokenizer: any line-based
39-
// markdown is processed by marked within Parser by
40-
// the 'inline lexer'...
41-
let is_plain = true;
42-
43-
function setNotPlain() {
44-
is_plain = false;
45-
}
46-
47-
const dummy_renderer = new commonmark.HtmlRenderer();
48-
for (const k of Object.keys(commonmark.HtmlRenderer.prototype)) {
49-
dummy_renderer[k] = setNotPlain;
72+
const walker = this.parsed.walker();
73+
74+
let ev;
75+
while ( (ev = walker.next()) ) {
76+
const node = ev.node;
77+
if (TEXT_NODES.indexOf(node.type) > -1) {
78+
// definitely text
79+
continue;
80+
} else if (node.type == 'html_inline' || node.type == 'html_block') {
81+
// if it's an allowed html tag, we need to render it and therefore
82+
// we will need to use HTML. If it's not allowed, it's not HTML since
83+
// we'll just be treating it as text.
84+
if (is_allowed_html_tag(node)) {
85+
return false;
86+
}
87+
} else {
88+
return false;
89+
}
5090
}
51-
// text and paragraph are just text
52-
dummy_renderer.text = function(t) { return t; };
53-
dummy_renderer.softbreak = function(t) { return t; };
54-
dummy_renderer.paragraph = function(t) { return t; };
55-
56-
const dummy_parser = new commonmark.Parser();
57-
dummy_renderer.render(dummy_parser.parse(this.input));
58-
59-
return is_plain;
91+
return true;
6092
}
6193

6294
toHTML() {
63-
const real_paragraph = this.renderer.paragraph;
95+
const renderer = new commonmark.HtmlRenderer({safe: false});
96+
const real_paragraph = renderer.paragraph;
6497

65-
this.renderer.paragraph = function(node, entering) {
98+
renderer.paragraph = function(node, entering) {
6699
// If there is only one top level node, just return the
67100
// bare text: it's a single line of text and so should be
68101
// 'inline', rather than unnecessarily wrapped in its own
69102
// p tag. If, however, we have multiple nodes, each gets
70103
// its own p tag to keep them as separate paragraphs.
71-
var par = node;
72-
while (par.parent) {
73-
par = par.parent;
74-
}
75-
if (par.firstChild != par.lastChild) {
104+
if (is_multi_line(node)) {
76105
real_paragraph.call(this, node, entering);
77106
}
78107
};
79108

80-
var parsed = this.parser.parse(this.input);
81-
var rendered = this.renderer.render(parsed);
109+
renderer.html_inline = html_if_tag_allowed;
110+
renderer.html_block = function(node) {
111+
// as with `paragraph`, we only insert line breaks
112+
// if there are multiple lines in the markdown.
113+
const isMultiLine = is_multi_line(node);
82114

83-
this.renderer.paragraph = real_paragraph;
115+
if (isMultiLine) this.cr();
116+
html_if_tag_allowed.call(this, node);
117+
if (isMultiLine) this.cr();
118+
}
84119

85-
return rendered;
120+
return renderer.render(this.parsed);
86121
}
87122

123+
/*
124+
* Render the markdown message to plain text. That is, essentially
125+
* just remove any backslashes escaping what would otherwise be
126+
* markdown syntax
127+
* (to fix https://github.com/vector-im/riot-web/issues/2870)
128+
*/
88129
toPlaintext() {
89-
const real_paragraph = this.renderer.paragraph;
130+
const renderer = new commonmark.HtmlRenderer({safe: false});
131+
const real_paragraph = renderer.paragraph;
90132

91133
// The default `out` function only sends the input through an XML
92134
// escaping function, which causes messages to be entity encoded,
93135
// which we don't want in this case.
94-
this.renderer.out = function(s) {
136+
renderer.out = function(s) {
95137
// The `lit` function adds a string literal to the output buffer.
96138
this.lit(s);
97139
};
98140

99-
this.renderer.paragraph = function(node, entering) {
100-
// If there is only one top level node, just return the
101-
// bare text: it's a single line of text and so should be
102-
// 'inline', rather than unnecessarily wrapped in its own
103-
// p tag. If, however, we have multiple nodes, each gets
104-
// its own p tag to keep them as separate paragraphs.
105-
var par = node;
106-
while (par.parent) {
107-
node = par;
108-
par = par.parent;
109-
}
110-
if (node != par.lastChild) {
111-
if (!entering) {
141+
renderer.paragraph = function(node, entering) {
142+
// as with toHTML, only append lines to paragraphs if there are
143+
// multiple paragraphs
144+
if (is_multi_line(node)) {
145+
if (!entering && node.next) {
112146
this.lit('\n\n');
113147
}
114148
}
115149
};
150+
renderer.html_block = function(node) {
151+
this.lit(node.literal);
152+
if (is_multi_line(node) && node.next) this.lit('\n\n');
153+
}
116154

117-
var parsed = this.parser.parse(this.input);
118-
var rendered = this.renderer.render(parsed);
119-
120-
this.renderer.paragraph = real_paragraph;
121-
122-
return rendered;
155+
return renderer.render(this.parsed);
123156
}
124157
}

src/Modal.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ class ModalManager {
177177

178178
var modal = this._modals[0];
179179
var dialog = (
180-
<div className={"mx_Dialog_wrapper " + modal.className}>
180+
<div className={"mx_Dialog_wrapper " + (modal.className ? modal.className : '') }>
181181
<div className="mx_Dialog">
182182
{modal.elem}
183183
</div>

src/Resend.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ module.exports = {
3434
Modal.createDialog(UnknownDeviceDialog, {
3535
devices: err.devices,
3636
room: MatrixClientPeg.get().getRoom(event.getRoomId()),
37-
});
37+
}, "mx_Dialog_unknownDevice");
3838
}
3939

4040
dis.dispatch({

src/RtsClient.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import 'whatwg-fetch';
2+
3+
function checkStatus(response) {
4+
if (!response.ok) {
5+
return response.text().then((text) => {
6+
throw new Error(text);
7+
});
8+
}
9+
return response;
10+
}
11+
12+
function parseJson(response) {
13+
return response.json();
14+
}
15+
16+
function encodeQueryParams(params) {
17+
return '?' + Object.keys(params).map((k) => {
18+
return k + '=' + encodeURIComponent(params[k]);
19+
}).join('&');
20+
}
21+
22+
const request = (url, opts) => {
23+
if (opts && opts.qs) {
24+
url += encodeQueryParams(opts.qs);
25+
delete opts.qs;
26+
}
27+
if (opts && opts.body) {
28+
if (!opts.headers) {
29+
opts.headers = {};
30+
}
31+
opts.body = JSON.stringify(opts.body);
32+
opts.headers['Content-Type'] = 'application/json';
33+
}
34+
return fetch(url, opts)
35+
.then(checkStatus)
36+
.then(parseJson);
37+
};
38+
39+
40+
export default class RtsClient {
41+
constructor(url) {
42+
this._url = url;
43+
}
44+
45+
getTeamsConfig() {
46+
return request(this._url + '/teams');
47+
}
48+
49+
/**
50+
* Track a referral with the Riot Team Server. This should be called once a referred
51+
* user has been successfully registered.
52+
* @param {string} referrer the user ID of one who referred the user to Riot.
53+
* @param {string} userId the user ID of the user being referred.
54+
* @param {string} userEmail the email address linked to `userId`.
55+
* @returns {Promise} a promise that resolves to { team_token: 'sometoken' } upon
56+
* success.
57+
*/
58+
trackReferral(referrer, userId, userEmail) {
59+
return request(this._url + '/register',
60+
{
61+
body: {
62+
referrer: referrer,
63+
user_id: userId,
64+
user_email: userEmail,
65+
},
66+
method: 'POST',
67+
}
68+
);
69+
}
70+
71+
getTeam(teamToken) {
72+
return request(this._url + '/teamConfiguration',
73+
{
74+
qs: {
75+
team_token: teamToken,
76+
},
77+
}
78+
);
79+
}
80+
}

src/Velociraptor.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,11 @@ module.exports = React.createClass({
6262
oldNode.style.visibility = c.props.style.visibility;
6363
}
6464
});
65-
if (oldNode.style.visibility == 'hidden' && c.props.style.visibility == 'visible') {
66-
oldNode.style.visibility = c.props.style.visibility;
67-
}
6865
//console.log("translation: "+oldNode.style.left+" -> "+c.props.style.left);
6966
}
67+
if (oldNode.style.visibility == 'hidden' && c.props.style.visibility == 'visible') {
68+
oldNode.style.visibility = c.props.style.visibility;
69+
}
7070
self.children[c.key] = old;
7171
} else {
7272
// new element. If we have a startStyle, use that as the style and go through

src/WhoIsTyping.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
/*
2+
Copyright 2017 Vector Creations Ltd
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
117
var MatrixClientPeg = require("./MatrixClientPeg");
218

319
module.exports = {

0 commit comments

Comments
 (0)