Commit 72e6fb5e authored by JC Brand's avatar JC Brand

Refactor rendering of OOB urls and images

- limit the number of instantiations of URI
- Handle try/catch of URI invoking in one place (`getURI`)
- Reduce exposed interface of utils/html.js by making some methods internal functions
parent 13652f6a
...@@ -199,10 +199,7 @@ converse.plugins.add('converse-message-view', { ...@@ -199,10 +199,7 @@ converse.plugins.add('converse-message-view', {
}, },
transformOOBURL (url) { transformOOBURL (url) {
url = u.renderFileURL(_converse, url); return u.getOOBURLMarkup(_converse, url);
url = u.renderMovieURL(_converse, url);
url = u.renderAudioURL(_converse, url);
return u.renderImageURL(_converse, url);
}, },
async transformBodyText (text) { async transformBodyText (text) {
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
import URI from "urijs"; import URI from "urijs";
import _ from "../headless/lodash.noconflict"; import _ from "../headless/lodash.noconflict";
import log from '@converse/headless/log';
import sizzle from "sizzle"; import sizzle from "sizzle";
import tpl_audio from "../templates/audio.html"; import tpl_audio from "../templates/audio.html";
import tpl_file from "../templates/file.html"; import tpl_file from "../templates/file.html";
...@@ -32,13 +33,6 @@ function getAutoCompleteProperty (name, options) { ...@@ -32,13 +33,6 @@ function getAutoCompleteProperty (name, options) {
}[name]; }[name];
} }
const logger = _.assign({
'debug': _.get(console, 'log') ? console.log.bind(console) : function noop () {},
'error': _.get(console, 'log') ? console.log.bind(console) : function noop () {},
'info': _.get(console, 'log') ? console.log.bind(console) : function noop () {},
'warn': _.get(console, 'log') ? console.log.bind(console) : function noop () {}
}, console);
const XFORM_TYPE_MAP = { const XFORM_TYPE_MAP = {
'text-private': 'password', 'text-private': 'password',
'text-single': 'text', 'text-single': 'text',
...@@ -58,8 +52,7 @@ function slideOutWrapup (el) { ...@@ -58,8 +52,7 @@ function slideOutWrapup (el) {
el.style.height = ""; el.style.height = "";
} }
function isImage (url) {
const isImage = function (url) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
var img = new Image(); var img = new Image();
var timer = window.setTimeout(function () { var timer = window.setTimeout(function () {
...@@ -76,112 +69,93 @@ const isImage = function (url) { ...@@ -76,112 +69,93 @@ const isImage = function (url) {
}; };
img.src = url; img.src = url;
}); });
};
u.isAudioURL = function (url) {
if (!(url instanceof URI)) {
try {
url = new URI(url);
} catch (error) {
return false;
}
}
const filename = url.filename().toLowerCase();
if (url.protocol().toLowerCase() !== "https") {
return false;
}
return filename.endsWith('.ogg') || filename.endsWith('.mp3') || filename.endsWith('.m4a');
} }
function getURI (url) {
u.isImageURL = function (url) { try {
if (!(url instanceof URI)) { return (url instanceof URI) ? url : (new URI(url));
try { } catch (error) {
url = new URI(url); log.debug(error);
} catch (error) { return null;
return false;
}
}
const filename = url.filename().toLowerCase();
if (window.location.protocol === 'https:' && url.protocol().toLowerCase() !== "https") {
return false;
} }
return filename.endsWith('.jpg') || filename.endsWith('.jpeg') || }
filename.endsWith('.png') || filename.endsWith('.gif') ||
filename.endsWith('.bmp') || filename.endsWith('.tiff') ||
filename.endsWith('.svg');
};
function checkTLS (uri) {
return window.location.protocol === 'http:' ||
window.location.protocol === 'https:' && uri.protocol().toLowerCase() === "https";
}
u.isVideoURL = function (url) { function checkFileTypes (types, url) {
if (!(url instanceof URI)) { const uri = getURI(url);
try { if (uri === null || !checkTLS(uri)) {
url = new URI(url);
} catch (error) {
return false;
}
}
const filename = url.filename().toLowerCase();
if (url.protocol().toLowerCase() !== "https") {
return false; return false;
} }
return filename.endsWith('.mp4') || filename.endsWith('.webm'); const filename = uri.filename().toLowerCase();
return !!types.filter(ext => filename.endsWith(ext)).length;
} }
const isAudioURL = url => checkFileTypes(['.ogg', '.mp3', '.m4a'], url);
const isImageURL = url => checkFileTypes(['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.svg'], url);
const isVideoURL = url => checkFileTypes(['.mp4', '.webm'], url);
u.renderAudioURL = function (_converse, url) { function getFileName (uri) {
try { try {
const uri = new URI(url); return decodeURI(uri.filename());
if (u.isAudioURL(uri)) {
const { __ } = _converse;
return tpl_audio({
'url': url,
'label_download': __('Download audio file "%1$s"', decodeURI(uri.filename()))
})
}
} catch (error) { } catch (error) {
// decodeURI may throw error in case of malformed URIs log.debug(error);
return uri.filename();
} }
return url; }
};
function renderAudioURL (_converse, uri) {
const { __ } = _converse;
return tpl_audio({
'url': uri.toString(),
'label_download': __('Download audio file "%1$s"', getFileName(uri))
})
}
u.renderFileURL = function (_converse, url) { function renderImageURL (_converse, uri) {
try { if (!_converse.show_images_inline) {
const uri = new URI(url); return u.convertToHyperlink(uri);
if (u.isImageURL(uri) || u.isVideoURL(uri) || u.isAudioURL(uri)) {
return url;
}
const { __ } = _converse,
filename = uri.filename();
return tpl_file({
'url': url,
'label_download': __('Download file "%1$s"', decodeURI(filename))
})
} catch (error) {
return url;
} }
}; const { __ } = _converse;
return tpl_image({
'url': uri.toString(),
'label_download': __('Download image "%1$s"', getFileName(uri))
})
}
u.renderImageURL = function (_converse, url) { function renderFileURL (_converse, uri) {
if (!_converse.show_images_inline) { const { __ } = _converse;
return u.addHyperlinks(url); return tpl_file({
'url': uri.toString(),
'label_download': __('Download file "%1$s"', getFileName(uri))
})
}
/**
* Returns the markup for a URL that points to a downloadable asset
* (such as a video, image or audio file).
* @method u#getOOBURLMarkup
* @param { String } url
* @returns { String }
*/
u.getOOBURLMarkup = function (_converse, url) {
const uri = getURI(url);
if (uri === null) {
return url;
} }
try { if (isVideoURL(uri)) {
const uri = new URI(url); return tpl_video({url})
if (u.isImageURL(uri)) { } else if (isAudioURL(uri)) {
const { __ } = _converse; return renderAudioURL(_converse, uri);
return tpl_image({ } else if (isImageURL(uri)) {
'url': url, return renderImageURL(_converse, uri);
'label_download': __('Download image "%1$s"', decodeURI(uri.filename())) } else {
}) return renderFileURL(_converse, uri);
}
} catch (error) {
// decodeURI may throw error in case of malformed URIs
} }
return url; }
};
/** /**
...@@ -223,7 +197,7 @@ u.renderImageURLs = function (_converse, el) { ...@@ -223,7 +197,7 @@ u.renderImageURLs = function (_converse, el) {
return Promise.all( return Promise.all(
list.map(url => list.map(url =>
new Promise((resolve) => { new Promise((resolve) => {
if (u.isImageURL(url)) { if (isImageURL(url)) {
return isImage(url).then(img => { return isImage(url).then(img => {
const i = new Image(); const i = new Image();
i.src = img.src; i.src = img.src;
...@@ -244,19 +218,6 @@ u.renderImageURLs = function (_converse, el) { ...@@ -244,19 +218,6 @@ u.renderImageURLs = function (_converse, el) {
}; };
u.renderMovieURL = function (_converse, url) {
try {
const uri = new URI(url);
if (u.isVideoURL(uri)) {
return tpl_video({url});
}
} catch (error) {
// decodeURI may throw error in case of malformed URIs
}
return url;
};
u.renderNewLines = function (text) { u.renderNewLines = function (text) {
return text.replace(/\n\n+/g, '<br/><br/>').replace(/\n/g, '<br/>'); return text.replace(/\n\n+/g, '<br/><br/>').replace(/\n/g, '<br/>');
}; };
...@@ -411,25 +372,28 @@ u.addMentionsMarkup = function (text, references, chatbox) { ...@@ -411,25 +372,28 @@ u.addMentionsMarkup = function (text, references, chatbox) {
}; };
u.convertToHyperlink = function (url) {
const uri = getURI(url);
if (uri === null) {
return url;
}
url = uri.normalize()._string;
const pretty_url = uri._parts.urn ? url : uri.readable();
if (!uri._parts.protocol && !url.startsWith('http://') && !url.startsWith('https://')) {
url = 'http://' + url;
}
if (uri._parts.protocol === 'xmpp' && uri._parts.query === 'join') {
return `<a target="_blank" rel="noopener" class="open-chatroom" href="${url}">${u.escapeHTML(pretty_url)}</a>`;
}
return `<a target="_blank" rel="noopener" href="${url}">${u.escapeHTML(pretty_url)}</a>`;
}
u.addHyperlinks = function (text) { u.addHyperlinks = function (text) {
return URI.withinString(text, url => { const parse_options = {
try {
const uri = new URI(url);
url = uri.normalize()._string;
const pretty_url = uri._parts.urn ? url : uri.readable();
if (!uri._parts.protocol && !url.startsWith('http://') && !url.startsWith('https://')) {
url = 'http://' + url;
}
if (uri._parts.protocol === 'xmpp' && uri._parts.query === 'join') {
return `<a target="_blank" rel="noopener" class="open-chatroom" href="${url}">${u.escapeHTML(pretty_url)}</a>`;
}
return `<a target="_blank" rel="noopener" href="${url}">${u.escapeHTML(pretty_url)}</a>`;
} catch (error) {
return url;
}
}, {
'start': /\b(?:([a-z][a-z0-9.+-]*:\/\/)|xmpp:|mailto:|www\.)/gi 'start': /\b(?:([a-z][a-z0-9.+-]*:\/\/)|xmpp:|mailto:|www\.)/gi
}); };
return URI.withinString(text, url => u.convertToHyperlink(url), parse_options);
}; };
...@@ -462,7 +426,7 @@ u.slideOut = function (el, duration=200) { ...@@ -462,7 +426,7 @@ u.slideOut = function (el, duration=200) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!el) { if (!el) {
const err = "An element needs to be passed in to slideOut" const err = "An element needs to be passed in to slideOut"
logger.warn(err); log.warn(err);
reject(new Error(err)); reject(new Error(err));
return; return;
} }
...@@ -521,7 +485,7 @@ u.slideIn = function (el, duration=200) { ...@@ -521,7 +485,7 @@ u.slideIn = function (el, duration=200) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!el) { if (!el) {
const err = "An element needs to be passed in to slideIn"; const err = "An element needs to be passed in to slideIn";
logger.warn(err); log.warn(err);
return reject(new Error(err)); return reject(new Error(err));
} else if (_.includes(el.classList, 'collapsed')) { } else if (_.includes(el.classList, 'collapsed')) {
return resolve(el); return resolve(el);
...@@ -588,7 +552,7 @@ u.isVisible = function (el) { ...@@ -588,7 +552,7 @@ u.isVisible = function (el) {
u.fadeIn = function (el, callback) { u.fadeIn = function (el, callback) {
if (!el) { if (!el) {
logger.warn("An element needs to be passed in to fadeIn"); log.warn("An element needs to be passed in to fadeIn");
} }
if (window.converse_disable_effects) { if (window.converse_disable_effects) {
el.classList.remove('hidden'); el.classList.remove('hidden');
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment