Merge branch 'master' into keyboard-trap

This commit is contained in:
El RIDO 2025-01-04 08:48:08 +01:00
commit 45b38770db
No known key found for this signature in database
GPG key ID: 0F5C940A6BD81F92
71 changed files with 1395 additions and 30 deletions

View file

@ -757,14 +757,14 @@ jQuery.PrivateBin = (function($, RawDeflate) {
args[0] = translations[messageId];
}
// messageID may contain links, but should be from a trusted source (code or translation JSON files)
let containsLinks = args[0].indexOf('<a') !== -1;
// messageID may contain HTML, but should be from a trusted source (code or translation JSON files)
let containsHtml = isStringContainsHtml(args[0]);
// prevent double encoding, when we insert into a text node
if (containsLinks || $element === null) {
if (containsHtml || $element === null) {
for (let i = 0; i < args.length; ++i) {
// parameters (i > 0) may never contain HTML as they may come from untrusted parties
if ((containsLinks ? i > 1 : i > 0) || !containsLinks) {
if ((containsHtml ? i > 1 : i > 0) || !containsHtml) {
args[i] = Helper.htmlEntities(args[i]);
}
}
@ -772,11 +772,11 @@ jQuery.PrivateBin = (function($, RawDeflate) {
// format string
let output = Helper.sprintf.apply(this, args);
if (containsLinks) {
if (containsHtml) {
// only allow tags/attributes we actually use in translations
output = DOMPurify.sanitize(
output, {
ALLOWED_TAGS: ['a', 'i', 'span'],
ALLOWED_TAGS: ['a', 'i', 'span', 'kbd'],
ALLOWED_ATTR: ['href', 'id']
}
);
@ -784,7 +784,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
// if $element is given, insert translation
if ($element !== null) {
if (containsLinks) {
if (containsHtml) {
$element.html(output);
} else {
// text node takes care of entity encoding
@ -914,6 +914,25 @@ jQuery.PrivateBin = (function($, RawDeflate) {
translations = mockTranslations || {};
};
/**
* Check if string contains valid HTML code
*
* @name I18n.isStringContainsHtml
* @function
* @private
* @param {string} messageId
* @returns {boolean}
*/
function isStringContainsHtml(messageId) {
// An integer which specifies the type of the node. An Element node like <p> or <div>.
const elementNodeType = 1;
const div = document.createElement('div');
div.innerHTML = messageId;
return Array.from(div.childNodes).some(node => node.nodeType === elementNodeType);
}
return me;
})();
@ -4958,6 +4977,9 @@ jQuery.PrivateBin = (function($, RawDeflate) {
TopNav.showViewButtons();
CopyToClipboard.setUrl(url);
CopyToClipboard.showKeyboardShortcutHint();
// this cannot be grouped with showViewButtons due to remaining time calculation
TopNav.showEmailButton();
@ -5394,6 +5416,8 @@ jQuery.PrivateBin = (function($, RawDeflate) {
// shows the remaining time (until) deletion
PasteStatus.showRemainingTime(paste);
CopyToClipboard.showKeyboardShortcutHint();
Promise.all(decryptionPromises)
.then(() => {
Alert.hideLoading();
@ -5423,6 +5447,181 @@ jQuery.PrivateBin = (function($, RawDeflate) {
return me;
})();
/**
*
* @name CopyToClipboard
* @class
*/
const CopyToClipboard = (function () {
const me = {};
let copyButton = $('#prettyMessageCopyBtn'),
copyLinkButton = $('#copyLink'),
copyIcon = $('#copyIcon'),
successIcon = $('#copySuccessIcon'),
shortcutHint = $('#copyShortcutHintText'),
url;
/**
* Handle copy to clipboard button click
*
* @name CopyToClipboard.handleCopyButtonClick
* @private
* @function
*/
function handleCopyButtonClick() {
$(copyButton).click(function() {
const text = PasteViewer.getText();
saveToClipboard(text);
toggleSuccessIcon();
showAlertMessage('Paste copied to clipboard');
});
};
/**
* Handle copy link to clipboard button click
*
* @name CopyToClipboard.handleCopyLinkButtonClick
* @private
* @function
*/
function handleCopyLinkButtonClick() {
$(copyLinkButton).click(function () {
saveToClipboard(url);
showAlertMessage('Link copied to clipboard');
});
}
/**
* Handle CTRL+C/CMD+C keyboard shortcut
*
* @name CopyToClipboard.handleKeyboardShortcut
* @private
* @function
*/
function handleKeyboardShortcut() {
$(document).bind('copy', function () {
if (!isUserSelectedTextToCopy()) {
const text = PasteViewer.getText();
saveToClipboard(text);
showAlertMessage('Paste copied to clipboard');
}
});
};
/**
* Check if user selected some text on the page to copy it
*
* @name CopyToClipboard.isUserSelectedTextToCopy
* @private
* @function
* @returns {boolean}
*/
function isUserSelectedTextToCopy() {
let text = '';
if (window.getSelection) {
text = window.getSelection().toString();
} else if (document.selection && document.selection.type != 'Control') {
text = document.selection.createRange().text;
}
return text.length > 0;
};
/**
* Save text to the clipboard
*
* @name CopyToClipboard.saveToClipboard
* @private
* @param {string} text
* @function
*/
function saveToClipboard(text) {
navigator.clipboard.writeText(text);
};
/**
* Show alert message after text copy
*
* @name CopyToClipboard.showAlertMessage
* @private
* @param {string} message
* @function
*/
function showAlertMessage(message) {
Alert.showStatus(message);
};
/**
* Toogle success icon after copy
*
* @name CopyToClipboard.toggleSuccessIcon
* @private
* @function
*/
function toggleSuccessIcon() {
$(copyIcon).css('display', 'none');
$(successIcon).css('display', 'block');
setTimeout(function() {
$(copyIcon).css('display', 'block');
$(successIcon).css('display', 'none');
}, 1000);
};
/**
* Show keyboard shortcut hint
*
* @name CopyToClipboard.showKeyboardShortcutHint
* @function
*/
me.showKeyboardShortcutHint = function () {
I18n._(
shortcutHint,
'To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>'
);
};
/**
* Hide keyboard shortcut hint
*
* @name CopyToClipboard.showKeyboardShortcutHint
* @function
*/
me.hideKeyboardShortcutHint = function () {
$(shortcutHint).html('');
};
/**
* Set paste url
*
* @name CopyToClipboard.setUrl
* @param {string} newUrl
* @function
*/
me.setUrl = function (newUrl) {
url = newUrl;
};
/**
* Initialize
*
* @name CopyToClipboard.init
* @function
*/
me.init = function() {
handleCopyButtonClick();
handleCopyLinkButtonClick();
handleKeyboardShortcut();
};
return me;
})();
/**
* (controller) main PrivateBin logic
*
@ -5444,6 +5643,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
{
PasteStatus.hideMessages();
Alert.hideMessages();
CopyToClipboard.hideKeyboardShortcutHint();
};
/**
@ -5669,6 +5869,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
Prompt.init();
TopNav.init();
UiHelper.init();
CopyToClipboard.init();
// check for legacy browsers before going any further
if (!Legacy.Check.getInit()) {
@ -5728,6 +5929,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
ServerInteraction: ServerInteraction,
PasteEncrypter: PasteEncrypter,
PasteDecrypter: PasteDecrypter,
CopyToClipboard: CopyToClipboard,
Controller: Controller
};
})(jQuery, RawDeflate);