summaryrefslogtreecommitdiff
path: root/static/js/pichat.js
diff options
context:
space:
mode:
Diffstat (limited to 'static/js/pichat.js')
-rwxr-xr-xstatic/js/pichat.js106
1 files changed, 90 insertions, 16 deletions
diff --git a/static/js/pichat.js b/static/js/pichat.js
index 2283baf..ad50fae 100755
--- a/static/js/pichat.js
+++ b/static/js/pichat.js
@@ -2114,21 +2114,74 @@ function escapeHtml(txt) {
URLRegex = /((\b(http\:\/\/|https\:\/\/|ftp\:\/\/)|(www\.))+(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?)/gi;
-PicRegex = /\.(jpg|jpeg|png|gif|bmp|svg|fid)$/i;
+PicRegex = /\.(jpg|jpeg|png|gif|bmp|svg|webp|fid)$/i;
+VideoRegex = /\.(mp4|webm|mov|m4v|gifv)$/i;
RecipRegex = /(^|\s)@\w+/g;
TopicRegex = /(^|\s)#\w+/g;
+function splitTrailingPunctuation(url) {
+ if (!url) return { "url": url, "suffix": "" };
+ // Common punctuation that follows pasted links in chat.
+ var match = url.match(/[)\]\}\.,!\?;:'"]+$/);
+ if (!match) return { "url": url, "suffix": "" };
+ return { "url": url.slice(0, -match[0].length), "suffix": match[0] };
+}
+
+function imgurIdFromUri(uri) {
+ var host = (uri.host || "").toLowerCase();
+ if (!host || !host.match(/(^|\.)imgur\.com$/i)) return null;
+ if (!uri.file) return null;
+
+ var fileLower = uri.file.toLowerCase();
+ if (PicRegex.test(fileLower) || VideoRegex.test(fileLower)) return null;
+
+ if (!uri.file.match(/^[A-Za-z0-9]+$/)) return null;
+ return uri.file;
+}
+
+function imgurCandidateUrls(id) {
+ var base = "https://i.imgur.com/" + id;
+ return [base + ".jpg", base + ".png", base + ".gif", base + ".jpeg"];
+}
+
+function imgurHotlinkFallback(img) {
+ try {
+ var candidates = (img.getAttribute("data-imgur-candidates") || "").split("|");
+ if (!candidates.length || !candidates[0]) return;
+
+ var idx = parseInt(img.getAttribute("data-imgur-idx") || "0", 10);
+ var next = idx + 1;
+ if (next >= candidates.length) {
+ var link = img.parentNode;
+ if (link && link.tagName == "A") {
+ var href = link.getAttribute("href") || "";
+ while (link.firstChild) link.removeChild(link.firstChild);
+ link.appendChild(document.createTextNode(href));
+ }
+ return;
+ }
+
+ img.setAttribute("data-imgur-idx", "" + next);
+ img.src = candidates[next];
+ } catch (e) {}
+}
+
function getImagesAsArray(text) {
var imgs = []
var urls = text.match(URLRegex)
if (urls === null) return imgs
for (var i = 0; i<urls.length; i++){
- var url = urls[i]
- var normalized = normalizeUrl(url);
- var urlWithoutParams = normalized.replace(/[?#].*$/i, "");
- if (PicRegex.test(urlWithoutParams))
- imgs.push(normalized)
+ var split = splitTrailingPunctuation(urls[i]);
+ var normalized = normalizeUrl(split.url);
+ var uri = parseUri(normalized);
+
+ // Try to turn common "page" links into direct image URLs (best-effort).
+ var imgurId = imgurIdFromUri(uri);
+ var candidate = imgurId ? imgurCandidateUrls(imgurId)[0] : normalized;
+
+ var urlWithoutParams = candidate.replace(/[?#].*$/i, "");
+ if (PicRegex.test(urlWithoutParams)) imgs.push(candidate)
}
return imgs
}
@@ -2204,25 +2257,43 @@ function imgClickHandler() {
// durty hack to use a global to check this... but otherwise i'd have to rewrite the String.replace function? :/
var LastMsgContainsImage = false
function linkReplace(url) {
- linkUrl = normalizeUrl(url);
+ var split = splitTrailingPunctuation(url);
+ var hrefUrl = normalizeUrl(split.url);
+ var uri = parseUri(hrefUrl);
+
+ // Best-effort support for Imgur page links (e.g. https://imgur.com/<id>).
+ var imgurId = imgurIdFromUri(uri);
+ if (imgurId) {
+ LastMsgContainsImage = true;
+ var candidates = imgurCandidateUrls(imgurId);
+ var first = candidates[0];
+ return "<a target=\"_blank\" href=\"" + hrefUrl + "\" class=\"img-wrapper\" onclick=\"return imgClickHandler()\">"
+ + "<img src=\"" + first + "\" class=\"unbound imgur-hotlink\" data-imgur-idx=\"0\" data-imgur-candidates=\"" + candidates.join("|") + "\" onerror=\"imgurHotlinkFallback(this)\">"
+ + "</a>" + split.suffix;
+ }
+
+ linkUrl = hrefUrl;
- var uri = parseUri(url)
var type = getUriType(uri)
if (type == 'image') {
LastMsgContainsImage = true;
- return "<a target='_blank' href='" + linkUrl + "' class='img-wrapper' onclick='return imgClickHandler()'><img src='" + linkUrl + "' class='unbound'></a>";
+ return "<a target=\"_blank\" href=\"" + linkUrl + "\" class=\"img-wrapper\" onclick=\"return imgClickHandler()\"><img src=\"" + linkUrl + "\" class=\"unbound\"></a>" + split.suffix;
+ } else if (type == 'video') {
+ LastMsgContainsImage = true;
+ var videoUrl = linkUrl.replace(/\.gifv([?#].*)?$/i, ".mp4$1");
+ return "<a target=\"_blank\" href=\"" + linkUrl + "\" class=\"video-wrapper\" onclick=\"return imgClickHandler()\"><video src=\"" + videoUrl + "\" autoplay loop muted controls playsinline></video></a>" + split.suffix;
} else if (type == 'youtube') {
Youtube.startAnimation();
- return "<a target='_blank' class='youtube' href='" + linkUrl + "'>" +
- "<img class='youtube-thumb' width='130' height='97' src='"+Youtube.nextThumbUrl(uri.queryKey.v)+"'>" +
- "<img class='youtube-controls' src='/static/img/youtube.controls.png'></a>"
+ return "<a target=\"_blank\" class=\"youtube\" href=\"" + linkUrl + "\">" +
+ "<img class=\"youtube-thumb\" width=\"130\" height=\"97\" src=\"" + Youtube.nextThumbUrl(uri.queryKey.v) + "\">" +
+ "<img class=\"youtube-controls\" src=\"/static/img/youtube.controls.png\"></a>" + split.suffix;
} else if (type == 'midi') {
- return '<embed src="'+linkUrl+'" autostart="false" loop="false" volume="80" width="150" height="20" style="vertical-align:bottom"> <a href="'+linkUrl+'">'+uri.file+'</a>'
+ return '<embed src="' + linkUrl + '" autostart="false" loop="false" volume="80" width="150" height="20" style="vertical-align:bottom"> <a href="' + linkUrl + '">' + uri.file + '</a>' + split.suffix;
} else if (type == 'wav') {
- return '<audio src="'+linkUrl+'" controls volume="80" width="150" height="20" style="vertical-align:bottom"></audio> <a href="'+linkUrl+'">'+uri.file+'</a>'
+ return '<audio src="' + linkUrl + '" controls volume="80" width="150" height="20" style="vertical-align:bottom"></audio> <a href="' + linkUrl + '">' + uri.file + '</a>' + split.suffix;
} else
- return "<a target='_blank' href='" + linkUrl + "'>" + url + "</a>";
+ return "<a target=\"_blank\" href=\"" + linkUrl + "\">" + split.url + "</a>" + split.suffix;
}
@@ -2230,6 +2301,9 @@ function linkReplace(url) {
function getUriType(uri){
if (PicRegex.test(uri.file.toLowerCase()))
return "image";
+
+ if (VideoRegex.test(uri.file.toLowerCase()))
+ return "video";
var domain = parseDomain(uri.host)
@@ -2443,7 +2517,7 @@ Youtube = {
var img = $(this);
// yt thumb url example: https://i.ytimg.com/vi/0123456789A/1.jpg
var src = img.attr("src") || ""
- var match = src.match(/\\/vi\\/([^/]{11})\\/(\\d)\\.jpg/i)
+ var match = src.match(/\/vi\/([^/]{11})\/(\d)\.jpg/i)
if (!match) return
var v = match[1]
var num = match[2]