315 lines
9.3 KiB
JavaScript
315 lines
9.3 KiB
JavaScript
import { escape } from 'pretty-text/sanitizer';
|
|
|
|
export function translateSize(size) {
|
|
switch (size) {
|
|
case 'tiny': return 20;
|
|
case 'small': return 25;
|
|
case 'medium': return 32;
|
|
case 'large': return 45;
|
|
case 'extra_large': return 60;
|
|
case 'huge': return 120;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
export function escapeExpression(string) {
|
|
// don't escape SafeStrings, since they're already safe
|
|
if (string instanceof Handlebars.SafeString) {
|
|
return string.toString();
|
|
}
|
|
|
|
return escape(string);
|
|
}
|
|
|
|
export function avatarUrl(template, size) {
|
|
if (!template) { return ""; }
|
|
const rawSize = getRawSize(translateSize(size));
|
|
return template.replace(/\{size\}/g, rawSize);
|
|
}
|
|
|
|
export function getRawSize(size) {
|
|
const pixelRatio = window.devicePixelRatio || 1;
|
|
return size * Math.min(3, Math.max(1, Math.round(pixelRatio)));
|
|
}
|
|
|
|
export function avatarImg(options, getURL) {
|
|
getURL = getURL || Discourse.getURLWithCDN;
|
|
|
|
const size = translateSize(options.size);
|
|
const url = avatarUrl(options.avatarTemplate, size);
|
|
|
|
// We won't render an invalid url
|
|
if (!url || url.length === 0) { return ""; }
|
|
|
|
const classes = "avatar" + (options.extraClasses ? " " + options.extraClasses : "");
|
|
const title = (options.title) ? " title='" + escapeExpression(options.title || "") + "'" : "";
|
|
|
|
return "<img alt='' width='" + size + "' height='" + size + "' src='" + getURL(url) + "' class='" + classes + "'" + title + ">";
|
|
}
|
|
|
|
export function tinyAvatar(avatarTemplate, options) {
|
|
return avatarImg(_.merge({avatarTemplate: avatarTemplate, size: 'tiny' }, options));
|
|
}
|
|
|
|
export function postUrl(slug, topicId, postNumber) {
|
|
var url = Discourse.getURL("/t/");
|
|
if (slug) {
|
|
url += slug + "/";
|
|
} else {
|
|
url += 'topic/';
|
|
}
|
|
url += topicId;
|
|
if (postNumber > 1) {
|
|
url += "/" + postNumber;
|
|
}
|
|
return url;
|
|
}
|
|
|
|
export function userUrl(username) {
|
|
return Discourse.getURL("/users/" + username.toLowerCase());
|
|
}
|
|
|
|
export function emailValid(email) {
|
|
// see: http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
|
|
const re = /^[a-zA-Z0-9!#$%&'*+\/=?\^_`{|}~\-]+(?:\.[a-zA-Z0-9!#$%&'\*+\/=?\^_`{|}~\-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?$/;
|
|
return re.test(email);
|
|
}
|
|
|
|
export function extractDomainFromUrl(url) {
|
|
if (url.indexOf("://") > -1) {
|
|
url = url.split('/')[2];
|
|
} else {
|
|
url = url.split('/')[0];
|
|
}
|
|
return url.split(':')[0];
|
|
}
|
|
|
|
export function selectedText() {
|
|
var html = '';
|
|
|
|
if (typeof window.getSelection !== "undefined") {
|
|
var sel = window.getSelection();
|
|
if (sel.rangeCount) {
|
|
var container = document.createElement("div");
|
|
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
|
|
container.appendChild(sel.getRangeAt(i).cloneContents());
|
|
}
|
|
html = container.innerHTML;
|
|
}
|
|
} else if (typeof document.selection !== "undefined") {
|
|
if (document.selection.type === "Text") {
|
|
html = document.selection.createRange().htmlText;
|
|
}
|
|
}
|
|
|
|
html = html.replace(/<br>/g, "\n");
|
|
|
|
// Strip out any .click elements from the HTML before converting it to text
|
|
const div = document.createElement('div');
|
|
div.innerHTML = html;
|
|
|
|
const $div = $(div);
|
|
|
|
// Find all emojis and replace with its title attribute.
|
|
$div.find('img.emoji').replaceWith(function() { return this.title; });
|
|
$('.clicks', $div).remove();
|
|
const text = div.textContent || div.innerText || "";
|
|
|
|
return String(text).trim();
|
|
}
|
|
|
|
// Determine the row and col of the caret in an element
|
|
export function caretRowCol(el) {
|
|
var cp = caretPosition(el);
|
|
var rows = el.value.slice(0, cp).split("\n");
|
|
var rowNum = rows.length;
|
|
|
|
var colNum = cp - rows.splice(0, rowNum - 1).reduce(function(sum, row) {
|
|
return sum + row.length + 1;
|
|
}, 0);
|
|
|
|
return { rowNum: rowNum, colNum: colNum};
|
|
}
|
|
|
|
// Determine the position of the caret in an element
|
|
export function caretPosition(el) {
|
|
var r, rc, re;
|
|
if (el.selectionStart) {
|
|
return el.selectionStart;
|
|
}
|
|
if (document.selection) {
|
|
el.focus();
|
|
r = document.selection.createRange();
|
|
if (!r) return 0;
|
|
|
|
re = el.createTextRange();
|
|
rc = re.duplicate();
|
|
re.moveToBookmark(r.getBookmark());
|
|
rc.setEndPoint('EndToStart', re);
|
|
return rc.text.length;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Set the caret's position
|
|
export function setCaretPosition(ctrl, pos) {
|
|
var range;
|
|
if (ctrl.setSelectionRange) {
|
|
ctrl.focus();
|
|
ctrl.setSelectionRange(pos, pos);
|
|
return;
|
|
}
|
|
if (ctrl.createTextRange) {
|
|
range = ctrl.createTextRange();
|
|
range.collapse(true);
|
|
range.moveEnd('character', pos);
|
|
range.moveStart('character', pos);
|
|
return range.select();
|
|
}
|
|
}
|
|
|
|
export function validateUploadedFiles(files, bypassNewUserRestriction) {
|
|
if (!files || files.length === 0) { return false; }
|
|
|
|
if (files.length > 1) {
|
|
bootbox.alert(I18n.t('post.errors.too_many_uploads'));
|
|
return false;
|
|
}
|
|
|
|
var upload = files[0];
|
|
|
|
// CHROME ONLY: if the image was pasted, sets its name to a default one
|
|
if (typeof Blob !== "undefined" && typeof File !== "undefined") {
|
|
if (upload instanceof Blob && !(upload instanceof File) && upload.type === "image/png") { upload.name = "blob.png"; }
|
|
}
|
|
|
|
var type = uploadTypeFromFileName(upload.name);
|
|
|
|
return validateUploadedFile(upload, type, bypassNewUserRestriction);
|
|
}
|
|
|
|
export function validateUploadedFile(file, type, bypassNewUserRestriction) {
|
|
// check that the uploaded file is authorized
|
|
if (!authorizesAllExtensions() && !isAuthorizedUpload(file)) {
|
|
bootbox.alert(I18n.t('post.errors.upload_not_authorized', { authorized_extensions: authorizedExtensions() }));
|
|
return false;
|
|
}
|
|
|
|
if (!bypassNewUserRestriction) {
|
|
// ensures that new users can upload a file
|
|
if (!Discourse.User.current().isAllowedToUploadAFile(type)) {
|
|
bootbox.alert(I18n.t('post.errors.' + type + '_upload_not_allowed_for_new_user'));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// everything went fine
|
|
return true;
|
|
}
|
|
|
|
export function uploadTypeFromFileName(fileName) {
|
|
return isAnImage(fileName) ? 'image' : 'attachment';
|
|
}
|
|
|
|
export function authorizesAllExtensions() {
|
|
return Discourse.SiteSettings.authorized_extensions.indexOf("*") >= 0;
|
|
}
|
|
|
|
function extensions() {
|
|
return Discourse.SiteSettings.authorized_extensions
|
|
.toLowerCase()
|
|
.replace(/[\s\.]+/g, "")
|
|
.split("|")
|
|
.filter(ext => ext.indexOf("*") === -1);
|
|
}
|
|
|
|
function extensionsRegex() {
|
|
return new RegExp("\\.(" + extensions().join("|") + ")$", "i");
|
|
}
|
|
|
|
export function isAuthorizedUpload(file) {
|
|
return file && file.name && extensionsRegex().test(file.name);
|
|
}
|
|
|
|
export function authorizedExtensions() {
|
|
return extensions().join(", ");
|
|
}
|
|
|
|
export function uploadLocation(url) {
|
|
if (Discourse.CDN) {
|
|
url = Discourse.getURLWithCDN(url);
|
|
return /^\/\//.test(url) ? 'http:' + url : url;
|
|
} else if (Discourse.SiteSettings.enable_s3_uploads) {
|
|
return 'https:' + url;
|
|
} else {
|
|
var protocol = window.location.protocol + '//',
|
|
hostname = window.location.hostname,
|
|
port = ':' + window.location.port;
|
|
return protocol + hostname + port + url;
|
|
}
|
|
}
|
|
|
|
export function getUploadMarkdown(upload) {
|
|
if (isAnImage(upload.original_filename)) {
|
|
return '<img src="' + upload.url + '" width="' + upload.width + '" height="' + upload.height + '">';
|
|
} else if (!Discourse.SiteSettings.prevent_anons_from_downloading_files && (/\.(mov|mp4|webm|ogv|mp3|ogg|wav|m4a)$/i).test(upload.original_filename)) {
|
|
// is Audio/Video
|
|
return uploadLocation(upload.url);
|
|
} else {
|
|
return '<a class="attachment" href="' + upload.url + '">' + upload.original_filename + '</a> (' + I18n.toHumanSize(upload.filesize) + ')\n';
|
|
}
|
|
}
|
|
|
|
export function isAnImage(path) {
|
|
return (/\.(png|jpe?g|gif|bmp|tiff?|svg|webp|ico)$/i).test(path);
|
|
}
|
|
|
|
export function allowsImages() {
|
|
return authorizesAllExtensions() ||
|
|
(/(png|jpe?g|gif|bmp|tiff?|svg|webp|ico)/i).test(authorizedExtensions());
|
|
}
|
|
|
|
export function allowsAttachments() {
|
|
return authorizesAllExtensions() ||
|
|
!/^((png|jpe?g|gif|bmp|tiff?|svg|webp|ico)(,\s)?)+$/i.test(authorizedExtensions());
|
|
}
|
|
|
|
export function displayErrorForUpload(data) {
|
|
// deal with meaningful errors first
|
|
if (data.jqXHR) {
|
|
switch (data.jqXHR.status) {
|
|
// cancelled by the user
|
|
case 0: return;
|
|
|
|
// entity too large, usually returned from the web server
|
|
case 413:
|
|
var type = uploadTypeFromFileName(data.files[0].name);
|
|
var maxSizeKB = Discourse.SiteSettings['max_' + type + '_size_kb'];
|
|
bootbox.alert(I18n.t('post.errors.file_too_large', { max_size_kb: maxSizeKB }));
|
|
return;
|
|
|
|
// the error message is provided by the server
|
|
case 422:
|
|
if (data.jqXHR.responseJSON.message) {
|
|
bootbox.alert(data.jqXHR.responseJSON.message);
|
|
} else {
|
|
bootbox.alert(data.jqXHR.responseJSON.join("\n"));
|
|
}
|
|
return;
|
|
}
|
|
} else if (data.errors && data.errors.length > 0) {
|
|
bootbox.alert(data.errors.join("\n"));
|
|
return;
|
|
}
|
|
// otherwise, display a generic error message
|
|
bootbox.alert(I18n.t('post.errors.upload'));
|
|
}
|
|
|
|
export function defaultHomepage() {
|
|
// the homepage is the first item of the 'top_menu' site setting
|
|
return Discourse.SiteSettings.top_menu.split("|")[0].split(",")[0];
|
|
}
|
|
|
|
// This prevents a mini racer crash
|
|
export default {};
|