BetterDiscordAddons/Plugins/ImageUtilities/ImageUtilities.plugin.js

2111 lines
110 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @name ImageUtilities
* @author DevilBro
* @authorId 278543574059057154
* @version 5.1.0
* @description Adds several Utilities for Images/Videos (Gallery, Download, Reverse Search, Zoom, Copy, etc.)
* @invite Jx3TjNS
* @donate https://www.paypal.me/MircoWittrien
* @patreon https://www.patreon.com/MircoWittrien
* @website https://mwittrien.github.io/
* @source https://github.com/mwittrien/BetterDiscordAddons/tree/master/Plugins/ImageUtilities/
* @updateUrl https://mwittrien.github.io/BetterDiscordAddons/Plugins/ImageUtilities/ImageUtilities.plugin.js
*/
module.exports = (_ => {
const changeLog = {
};
return !window.BDFDB_Global || (!window.BDFDB_Global.loaded && !window.BDFDB_Global.started) ? class {
constructor (meta) {for (let key in meta) this[key] = meta[key];}
getName () {return this.name;}
getAuthor () {return this.author;}
getVersion () {return this.version;}
getDescription () {return `The Library Plugin needed for ${this.name} is missing. Open the Plugin Settings to download it. \n\n${this.description}`;}
downloadLibrary () {
require("request").get("https://mwittrien.github.io/BetterDiscordAddons/Library/0BDFDB.plugin.js", (e, r, b) => {
if (!e && b && r.statusCode == 200) require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0BDFDB.plugin.js"), b, _ => BdApi.showToast("Finished downloading BDFDB Library", {type: "success"}));
else BdApi.alert("Error", "Could not download BDFDB Library Plugin. Try again later or download it manually from GitHub: https://mwittrien.github.io/downloader/?library");
});
}
load () {
if (!window.BDFDB_Global || !Array.isArray(window.BDFDB_Global.pluginQueue)) window.BDFDB_Global = Object.assign({}, window.BDFDB_Global, {pluginQueue: []});
if (!window.BDFDB_Global.downloadModal) {
window.BDFDB_Global.downloadModal = true;
BdApi.showConfirmationModal("Library Missing", `The Library Plugin needed for ${this.name} is missing. Please click "Download Now" to install it.`, {
confirmText: "Download Now",
cancelText: "Cancel",
onCancel: _ => {delete window.BDFDB_Global.downloadModal;},
onConfirm: _ => {
delete window.BDFDB_Global.downloadModal;
this.downloadLibrary();
}
});
}
if (!window.BDFDB_Global.pluginQueue.includes(this.name)) window.BDFDB_Global.pluginQueue.push(this.name);
}
start () {this.load();}
stop () {}
getSettingsPanel () {
let template = document.createElement("template");
template.innerHTML = `<div style="color: var(--header-primary); font-size: 16px; font-weight: 300; white-space: pre; line-height: 22px;">The Library Plugin needed for ${this.name} is missing.\nPlease click <a style="font-weight: 500;">Download Now</a> to install it.</div>`;
template.content.firstElementChild.querySelector("a").addEventListener("click", this.downloadLibrary);
return template.content.firstElementChild;
}
} : (([Plugin, BDFDB]) => {
var _this;
var firedEvents = [];
var ownLocations = {}, downloadsFolder;
var firstViewedImage, viewedImage, viewedImageTimeout;
var switchedImageProps;
var cachedImages;
var eventTypes = {};
const imgUrlReplaceString = "DEVILBRO_BD_REVERSEIMAGESEARCH_REPLACE_IMAGEURL";
const rescaleOptions = {
NONE: "No Resize",
ORIGINAL: "Resize to Original Size",
WINDOW: "Resize to Window Size"
};
const fileTypes = {
"3gp": {copyable: false, searchable: false, video: true, signs: [[0x66, 0x74, 0x79, 0x70, 0x33, 0x67]]},
"avi": {copyable: false, searchable: false, video: true, signs: [[0x41, 0x56, 0x49, 0x20]]},
"flv": {copyable: false, searchable: false, video: true, signs: [[0x46, 0x4C, 0x56]]},
"jpeg": {copyable: true, searchable: true, video: false, signs: [[0xFF, 0xD8, 0xFF, 0xEE]]},
"jpg": {copyable: true, searchable: true, video: false, signs: [[0xFF, 0xD8, 0xFF, 0xDB], [0xFF, 0xD8, 0xFF, 0xE0], [0xFF, 0xD8, 0xFF, 0xE1]]},
"gif": {copyable: false, searchable: true, video: false, signs: [[0x47, 0x49, 0x46, 0x38, 0x37, 0x61], [0x47, 0x49, 0x46, 0x38, 0x39, 0x61]]},
"mov": {copyable: false, searchable: false, video: true, signs: [[null, null, null, null, 0x6D, 0x6F, 0x6F, 0x76], [null, null, null, null, 0x66, 0x74, 0x79, 0x70, 0x71, 0x74, 0x20, 0x20]]},
"mp4": {copyable: false, searchable: false, video: true, signs: [[null, null, null, null, 0x66, 0x74, 0x79, 0x70]]},
"mpeg-1": {copyable: false, searchable: false, video: true, signs: [[0x00, 0x00, 0x01, 0xBA]]},
"mpeg-2": {copyable: false, searchable: false, video: true, signs: [[0x00, 0x00, 0x01, 0xB3]]},
"ogg": {copyable: false, searchable: false, video: true, signs: [[0x4F, 0x67, 0x67, 0x53]]},
"png": {copyable: true, searchable: true, video: false, signs: [[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]]},
"svg": {copyable: false, searchable: false, video: false, signs: [[0x3C]]},
"webm": {copyable: false, searchable: false, video: true, signs: [[0x1A, 0x45, 0xDF, 0xA3]]},
"webp": {copyable: false, searchable: true, video: false, signs: [[0x52, 0x49, 0x46, 0x46, null, null, null, null, 0x57, 0x45, 0x42, 0x50]]},
"wmv": {copyable: false, searchable: false, video: true, signs: [[0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11], [0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C]]}
};
const LazyImageSiblingComponent = class LazyImageSibling extends BdApi.React.Component {
render() {
if (!this.props.loadedImage) {
const instance = this;
const imageThrowaway = document.createElement("img");
imageThrowaway.addEventListener("load", function() {
let aRects = BDFDB.DOMUtils.getRects(document.querySelector(BDFDB.dotCN.appmount));
let resizeX = (aRects.width/this.width) * 0.8, resizeY = (aRects.height/this.height) * 0.65
let ratio = resizeX < resizeY ? resizeX : resizeY;
instance.props.loadedImage = BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.LazyImage, {
src: imageThrowaway.src,
width: this.width,
height: this.height,
maxWidth: this.width * ratio,
maxHeight: this.height * ratio
});
BDFDB.ReactUtils.forceUpdate(instance);
});
imageThrowaway.src = !_this.isValid(this.props.url, "video") ? this.props.url : _this.getPosterUrl(this.props.url);
}
return BDFDB.ReactUtils.createElement("div", {
className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN._imageutilitiessibling, this.props.className),
onClick: event => {
BDFDB.ListenerUtils.stopEvent(event);
_this.switchImages(this.props.offset);
},
children: [
this.props.loadedImage || BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SpinnerComponents.Spinner, {
type: BDFDB.LibraryComponents.SpinnerComponents.Types.SPINNING_CIRCLE
}),
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
className: BDFDB.disCNS._imageutilitiesswitchicon + BDFDB.disCN.svgicon,
name: this.props.svgIcon
})
]
});
}
};
const ImageDetailsComponent = class ImageDetails extends BdApi.React.Component {
componentDidMount() {
this.props.attachment = BDFDB.ReactUtils.findValue(BDFDB.ObjectUtils.get(this, `${BDFDB.ReactUtils.instanceKey}.return`), "attachment", {up: true});
BDFDB.ReactUtils.forceUpdate(this);
}
componentDidUpdate() {
if ((!this.props.attachment || !this.props.attachment.size) && !this.props.loaded) {
this.props.loaded = true;
this.props.attachment = BDFDB.ReactUtils.findValue(BDFDB.ObjectUtils.get(this, `${BDFDB.ReactUtils.instanceKey}.return`), "attachment", {up: true});
BDFDB.ReactUtils.forceUpdate(this);
}
}
render() {
return !this.props.attachment ? null : BDFDB.ReactUtils.createElement("span", {
className: BDFDB.disCN._imageutilitiesimagedetails,
children: [
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Anchor, {
title: this.props.original,
href: this.props.original,
children: this.props.attachment.filename,
onClick: event => {
BDFDB.ListenerUtils.stopEvent(event);
BDFDB.DiscordUtils.openLink(this.props.original);
}
}),
BDFDB.ReactUtils.createElement("span", {
children: BDFDB.NumberUtils.formatBytes(this.props.attachment.size)
}),
BDFDB.ReactUtils.createElement("span", {
children: `${this.props.attachment.width}x${this.props.attachment.height}px`
}),
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TooltipContainer, {
text: BDFDB.LanguageUtils.LibraryStrings.download,
children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, {
className: BDFDB.disCN.cursorpointer,
name: BDFDB.LibraryComponents.SvgIcon.Names.DOWNLOAD,
width: 16,
height: 16,
onClick: event => {
BDFDB.ListenerUtils.stopEvent(event);
_this.downloadFile({url: this.props.attachment.proxy_url || this.props.original});
},
onContextMenu: event => {
let locations = Object.keys(ownLocations).filter(n => ownLocations[n].enabled);
if (locations.length) BDFDB.ContextMenuUtils.open(_this, event, BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuGroup, {
children: locations.map((name, i) => BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
id: BDFDB.ContextMenuUtils.createItemId(_this.name, "download", name, i),
label: name,
action: _ => _this.downloadFile({url: this.props.attachment.proxy_url || this.props.original}, ownLocations[name].location)
}))
}));
}
})
})
]
});
}
};
return class ImageUtilities extends Plugin {
onLoad () {
_this = this;
firedEvents = [];
firstViewedImage = null;
viewedImage = null;
cachedImages = null;
this.defaults = {
general: {
nsfwMode: {value: false, description: "Blurs Media that is posted in NSFW Channels"}
},
viewerSettings: {
zoomMode: {value: true, description: "Enables Zoom Mode to zoom into Images while holding down your Mouse"},
galleryMode: {value: true, description: "Enables Gallery Mode to quick-switch between Images"},
details: {value: true, description: "Adds Image Details (Name, Size, Amount)"},
copyImage: {value: true, description: "Adds a 'Copy Image' Option"},
saveImage: {value: true, description: "Adds a 'Save Image as' Option"},
jumpTo: {value: true, description: "Adds a 'Jump to Message' Option in Gallery Mode"}
},
galleryFilter: {},
zoomSettings: {
pixelMode: {value: false, label: "Uses Pixel Lens instead of a Blur Lens"},
zoomLevel: {value: 2, digits: 1, minValue: 1, maxValue: 20, unit: "x", label: "ACCESSIBILITY_ZOOM_LEVEL_LABEL"},
lensSize: {value: 200, digits: 0, minValue: 50, maxValue: 5000, unit: "px", label: "context_lenssize"}
},
rescaleSettings: {
messages: {value: "NONE", description: "Messages"},
imageViewer: {value: "NONE", description: "Image Viewer"}
},
detailsSettings: {
footnote: {value: true, description: "in the Image Description"},
tooltip: {value: false, description: "as a Hover Tooltip"},
tooltipDelay: {value: 0, min: 0, description: "Image Tooltip Delay (in ms)"}
},
places: {
userAvatars: {value: true, description: "User Avatars"},
groupIcons: {value: true, description: "Group Icons"},
guildIcons: {value: true, description: "Server Icons"},
emojis: {value: true, description: "Custom Emojis/Emotes"}
},
engines: {
_all: {value: true, name: BDFDB.LanguageUtils.LanguageStrings.FORM_LABEL_ALL, url: null},
Baidu: {value: true, name: "Baidu", url: "http://image.baidu.com/pcdutu?queryImageUrl=" + imgUrlReplaceString},
Bing: {value: true, name: "Bing", url: "https://www.bing.com/images/search?q=imgurl:" + imgUrlReplaceString + "&view=detailv2&iss=sbi&FORM=IRSBIQ"},
Google: {value: true, name: "Google", url: "https://www.google.com/searchbyimage?sbisrc=1&image_url=" + imgUrlReplaceString},
GoogleLens: {value: true, name: "Google Lens", url: "https://lens.google.com/uploadbyurl?url=" + imgUrlReplaceString},
ImgOps: {value: true, name: "ImgOps", raw: true, url: "https://imgops.com/specialized+reverse/" + imgUrlReplaceString},
IQDB: {value: true, name: "IQDB", url: "https://iqdb.org/?url=" + imgUrlReplaceString},
Reddit: {value: true, name: "Reddit", url: "http://karmadecay.com/search?q=" + imgUrlReplaceString},
SauceNAO: {value: true, name: "SauceNAO", url: "https://saucenao.com/search.php?db=999&url=" + imgUrlReplaceString},
Sogou: {value: true, name: "Sogou", url: "http://pic.sogou.com/ris?flag=1&drag=0&query=" + imgUrlReplaceString + "&flag=1"},
TinEye: {value: true, name: "TinEye", url: "https://tineye.com/search?url=" + imgUrlReplaceString},
WhatAnime: {value: true, name: "WhatAnime", url: "https://trace.moe/?url=" + imgUrlReplaceString},
Yandex: {value: true, name: "Yandex", url: "https://yandex.com/images/search?url=" + imgUrlReplaceString + "&rpt=imageview"}
}
};
for (let fileType in fileTypes) this.defaults.galleryFilter[fileType] = {value: true};
this.modulePatches = {
before: [
"ImageModal",
"MessageAccessories",
"Spoiler"
],
after: [
"ImageModal",
"LazyImage",
"LazyImageZoomable",
"ModalCarousel",
"Spoiler",
"UserBanner"
],
componentDidMount: [
"LazyImage"
],
componentDidUpdate: [
"LazyImage"
],
componentWillUnmount: [
"LazyImage"
]
};
this.css = `
${BDFDB.dotCN._imageutilitiesimagedetails} {
display: inline-flex;
font-weight: 500;
color: var(--text-muted);
font-size: 12px;
margin: .25rem 0 .75rem;
line-height: 16px;
}
${BDFDB.dotCNS.spoilerhidden + BDFDB.dotCN._imageutilitiesimagedetails} {
visibility: hidden;
}
span + ${BDFDB.dotCN._imageutilitiesimagedetails} {
margin-left: 12px;
}
${BDFDB.dotCN._imageutilitiesimagedetails} > * {
display: inline-block;
margin-right: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
${BDFDB.dotCN._imageutilitiesimagedetails} > a {
max-width: 300px;
}
span + ${BDFDB.dotCN._imageutilitiesimagedetails} > a {
max-width: 200px;
}
${BDFDB.dotCN._imageutilitiesimagedetails} > span {
max-width: 100px;
}
${BDFDB.dotCN._imageutilitiesgallery},
${BDFDB.dotCN._imageutilitiesdetailsadded} {
transform: unset !important;
filter: unset !important;
backdrop-filter: unset !important;
}
${BDFDB.dotCNS.imagemodal + BDFDB.notCN._imageutilitiessibling} > ${BDFDB.dotCN.imagewrapper} {
display: flex;
justify-content: center;
align-items: center;
min-width: 500px;
}
${BDFDB.dotCNS.imagemodal + BDFDB.notCN._imageutilitiessibling} > ${BDFDB.dotCN.imagewrapper} img {
object-fit: contain;
width: unset;
}
${BDFDB.dotCN.imagemodalnavbutton} {
background: rgba(0, 0, 0, 0.3);
border-radius: 100%;
}
${BDFDB.dotCN.imagemodalnavbutton}:hover {
background: rgba(0, 0, 0, 0.5);
}
${BDFDB.dotCN._imageutilitiessibling} {
display: flex;
align-items: center;
position: fixed;
top: 50%;
bottom: 50%;
cursor: pointer;
}
${BDFDB.dotCN._imageutilitiesprevious} {
justify-content: flex-end;
right: 90%;
}
${BDFDB.dotCN._imageutilitiesnext} {
justify-content: flex-start;
left: 90%;
}
${BDFDB.dotCN._imageutilitiesswitchicon} {
position: absolute;
background: rgba(0, 0, 0, 0.3);
border-radius: 50%;
padding: 15px;
transition: all 0.3s ease;
}
${BDFDB.dotCNS._imageutilitiesprevious + BDFDB.dotCN._imageutilitiesswitchicon} {
right: 10px;
}
${BDFDB.dotCNS._imageutilitiesnext + BDFDB.dotCN._imageutilitiesswitchicon} {
left: 10px;
}
${BDFDB.dotCNS._imageutilitiessibling + BDFDB.dotCN.spinner} {
position: absolute;
}
${BDFDB.dotCNS._imageutilitiesprevious + BDFDB.dotCN.spinner} {
right: 21px;
}
${BDFDB.dotCNS._imageutilitiesnext + BDFDB.dotCN.spinner} {
left: 21px;
}
${BDFDB.dotCN._imageutilitiessibling}:hover ${BDFDB.dotCN._imageutilitiesswitchicon} {
background: rgba(0, 0, 0, 0.5);
}
${BDFDB.dotCN._imageutilitiesdetailswrapper} {
position: fixed;
bottom: 10px;
left: 15px;
right: 15px;
pointer-events: none;
}
${BDFDB.dotCN._imageutilitiesdetails} {
color: #dcddde;
margin-top: 5px;
font-size: 14px;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
${BDFDB.dotCN._imageutilitiesdetailslabel} {
display: inline-block;
width: 80px;
font-weight: 600;
}
${BDFDB.dotCN._imageutilitieslense} {
border: 2px solid var(--bdfdb-blurple);
}
${BDFDB.dotCN._imageutilitiesoperations} {
position: absolute;
display: flex;
}
${BDFDB.dotCNS._imageutilitiesoperations + BDFDB.dotCN.downloadlink} {
position: relative !important;
white-space: nowrap !important;
}
${BDFDB.dotCNS._imageutilitiesoperations + BDFDB.dotCN.anchor + BDFDB.dotCN.downloadlink} {
margin: 0 !important;
}
`;
}
onStart () {
BDFDB.ListenerUtils.add(this, document.body, "click", BDFDB.dotCNS.message + BDFDB.dotCNS.imagewrapper + BDFDB.dotCNC.imageoriginallink + BDFDB.dotCNS.message + BDFDB.dotCNS.imagewrapper + "img", e => this.cacheClickedImage(e.target));
this.forceUpdateAll();
}
onStop () {
this.cleanupListeners("Gallery");
this.cleanupListeners("Zoom");
this.forceUpdateAll();
}
getSettingsPanel (collapseStates = {}) {
let settingsPanel;
return settingsPanel = BDFDB.PluginUtils.createSettingsPanel(this, {
collapseStates: collapseStates,
children: _ => {
let settingsItems = [];
settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.CollapseContainer, {
title: "General",
collapseStates: collapseStates,
children: Object.keys(this.defaults.general).map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
type: "Switch",
plugin: this,
keys: ["general", key],
label: this.defaults.general[key].description,
value: this.settings.general[key]
}))
}));
settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.CollapseContainer, {
title: "Image Viewer Settings",
collapseStates: collapseStates,
children: Object.keys(this.defaults.viewerSettings).map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
type: "Switch",
plugin: this,
keys: ["viewerSettings", key],
label: this.defaults.viewerSettings[key].description,
value: this.settings.viewerSettings[key]
}))
}));
settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.CollapseContainer, {
title: "Gallery Filter Settings",
collapseStates: collapseStates,
children: Object.keys(this.defaults.galleryFilter).map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
type: "Switch",
plugin: this,
keys: ["galleryFilter", key],
label: key,
value: this.settings.galleryFilter[key]
}))
}));
settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.CollapseContainer, {
title: "Resize Settings",
collapseStates: collapseStates,
children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsPanelList, {
title: "Automatically Resize Images in: ",
children: Object.keys(this.defaults.rescaleSettings).map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
type: "Select",
plugin: this,
keys: ["rescaleSettings", key],
label: this.defaults.rescaleSettings[key].description,
basis: "50%",
options: Object.keys(rescaleOptions).map(n => ({value: n, label: rescaleOptions[n]})),
value: this.settings.rescaleSettings[key]
}))
})
}));
settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.CollapseContainer, {
title: "Image Details Settings",
collapseStates: collapseStates,
children: [BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsPanelList, {
title: "Show Image Details",
children: Object.keys(this.defaults.detailsSettings).filter(key => typeof this.defaults.detailsSettings[key].value == "boolean").map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
type: "Switch",
plugin: this,
keys: ["detailsSettings", key],
label: this.defaults.detailsSettings[key].description,
value: this.settings.detailsSettings[key]
}))
})].concat(Object.keys(this.defaults.detailsSettings).filter(key => typeof this.defaults.detailsSettings[key].value != "boolean").map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
type: "TextInput",
plugin: this,
keys: ["detailsSettings", key],
label: this.defaults.detailsSettings[key].description,
value: this.settings.detailsSettings[key],
basis: "50%",
childProps: {type: "number"},
min: this.defaults.detailsSettings[key].min,
max: this.defaults.detailsSettings[key].max,
})))
}));
const locationInputs = {name: "", location: ""};
settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.CollapseContainer, {
title: "Download Locations",
collapseStates: collapseStates,
children: [
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormTitle, {
className: BDFDB.disCN.marginbottom4,
tag: BDFDB.LibraryComponents.FormComponents.FormTags.H3,
children: "Add additional Download Locations"
}),
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, {
className: BDFDB.disCN.marginbottom8,
align: BDFDB.LibraryComponents.Flex.Align.END,
children: [
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, {
children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, {
title: "Name:",
children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextInput, {
value: locationInputs.name,
placeholder: "Name",
onChange: value => locationInputs.name = value
})
})
}),
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, {
children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.FormComponents.FormItem, {
title: "Location:",
children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextInput, {
value: locationInputs.location,
placeholder: "Location",
onChange: value => locationInputs.location = value
})
})
}),
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Button, {
style: {marginBottom: 1},
onClick: _ => {
for (let key in locationInputs) if (!locationInputs[key] || !locationInputs[key].trim()) return BDFDB.NotificationUtils.toast("Fill out all fields to add a new Location", {type: "danger"});
let name = locationInputs.name.trim();
let location = locationInputs.location.trim();
if (ownLocations[name] || name == "Downloads") return BDFDB.NotificationUtils.toast("A Location with the choosen Name already exists, please choose another Name", {type: "danger"});
else if (!BDFDB.LibraryRequires.fs.existsSync(location)) return BDFDB.NotificationUtils.toast("The choosen download Location is not a valid Path to a Folder", {type: "danger"});
else {
ownLocations[name] = {enabled: true, location: location};
BDFDB.DataUtils.save(ownLocations, this, "ownLocations");
BDFDB.PluginUtils.refreshSettingsPanel(this, settingsPanel, collapseStates);
}
},
children: BDFDB.LanguageUtils.LanguageStrings.ADD
})
]
})
].concat(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsPanelList, {
title: "Your own Download Locations",
dividerTop: true,
children: Object.keys(ownLocations).map(name => {
let locationName = name;
let editable = name != "Downloads";
return BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Card, {
horizontal: true,
children: [
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, {
grow: 0,
basis: "180px",
children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextInput, {
value: locationName,
placeholder: locationName,
size: BDFDB.LibraryComponents.TextInput.Sizes.MINI,
maxLength: 100000000000000000000,
disabled: !editable,
onChange: !editable ? null : value => {
ownLocations[value] = ownLocations[locationName];
delete ownLocations[locationName];
locationName = value;
BDFDB.DataUtils.save(ownLocations, this, "ownLocations");
}
})
}),
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, {
children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextInput, {
value: ownLocations[locationName].location,
placeholder: ownLocations[locationName].location,
size: BDFDB.LibraryComponents.TextInput.Sizes.MINI,
maxLength: 100000000000000000000,
onChange: value => {
ownLocations[locationName].location = value;
BDFDB.DataUtils.save(ownLocations, this, "ownLocations");
}
})
}),
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, {
children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Switch, {
value: ownLocations[locationName].enabled,
size: BDFDB.LibraryComponents.Switch.Sizes.MINI,
onChange: value => {
ownLocations[locationName].enabled = value;
BDFDB.DataUtils.save(ownLocations, this, "ownLocations");
}
})
})
],
noRemove: !editable,
onRemove: !editable ? null : _ => {
delete ownLocations[locationName];
BDFDB.DataUtils.save(ownLocations, this, "ownLocations");
BDFDB.PluginUtils.refreshSettingsPanel(this, settingsPanel);
}
});
})
})).filter(n => n)
}));
settingsItems.push(BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.CollapseContainer, {
title: "Context Menu Settings",
collapseStates: collapseStates,
children: [
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsPanelList, {
title: "Add additional Context Menu Entry for",
children: Object.keys(this.defaults.places).map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
type: "Switch",
plugin: this,
keys: ["places", key],
label: this.defaults.places[key].description,
value: this.settings.places[key]
}))
}),
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsPanelList, {
title: "Reverse Image Search Engines",
children: Object.keys(this.defaults.engines).filter(key => key != "_all").map(key => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SettingsSaveItem, {
type: "Switch",
plugin: this,
keys: ["engines", key],
label: this.defaults.engines[key].name,
value: this.settings.engines[key]
}))
})
]
}));
return settingsItems;
}
});
}
onSettingsClosed () {
if (this.SettingsUpdated) {
delete this.SettingsUpdated;
this.forceUpdateAll();
}
}
forceUpdateAll () {
const loadedLocations = BDFDB.DataUtils.load(this, "ownLocations");
ownLocations = Object.assign(!loadedLocations || !loadedLocations.Downloads ? {"Downloads": {enabled:true, location: this.getDownloadLocation()}} : {}, loadedLocations);
BDFDB.PatchUtils.forceAllUpdates(this);
BDFDB.MessageUtils.rerenderAll();
}
onGuildContextMenu (e) {
if (!this.settings.places.guildIcons || !e.instance.props.guild) return;
if (BDFDB.DOMUtils.getParent(BDFDB.dotCN.guildheader, e.instance.props.target) || BDFDB.DOMUtils.getParent(BDFDB.dotCN.guildchannels, e.instance.props.target) && !e.instance.props.target.className && e.instance.props.target.parentElement.firstElementChild == e.instance.props.target) {
let banner = BDFDB.GuildUtils.getBanner(e.instance.props.guild.id);
if (banner) this.injectItem(e, [banner.replace(/\.webp|\.gif/, ".png"), e.instance.props.guild.banner && BDFDB.LibraryModules.IconUtils.isAnimatedIconHash(e.instance.props.guild.banner), banner], BDFDB.LanguageUtils.LibraryStrings.guildbanner);
}
else if (!BDFDB.DOMUtils.getParent(BDFDB.dotCN.channels, e.instance.props.target)) this.injectItem(e, [(e.instance.props.guild.getIconURL(4096) || "").replace(/\.webp|\.gif/, ".png"), e.instance.props.guild.icon && BDFDB.LibraryModules.IconUtils.isAnimatedIconHash(e.instance.props.guild.icon) && e.instance.props.guild.getIconURL(4096, true)], BDFDB.LanguageUtils.LibraryStrings.guildicon);
}
onUserContextMenu (e) {
if (!this.settings.places.userAvatars || !e.instance.props.user) return;
const guildId = BDFDB.LibraryStores.SelectedGuildStore.getGuildId();
const member = BDFDB.LibraryStores.GuildMemberStore.getMember(guildId, e.instance.props.user.id);
this.injectItem(e, [(e.instance.props.user.getAvatarURL(null, 4096) || "").replace(/\.webp|\.gif/, ".png"), BDFDB.LibraryModules.IconUtils.isAnimatedIconHash(e.instance.props.user.avatar) && e.instance.props.user.getAvatarURL(null, 4096, true), (e.instance.props.user.getAvatarURL(guildId, 4096) || "").replace(/\.webp|\.gif/, ".png"), member && member.avatar && BDFDB.LibraryModules.IconUtils.isAnimatedIconHash(member.avatar) && e.instance.props.user.getAvatarURL(guildId, 4096, true)]);
}
onGroupDMContextMenu (e) {
if (!this.settings.places.groupIcons || !e.instance.props.channel || !e.instance.props.channel.isGroupDM()) return;
this.injectItem(e, [(BDFDB.DMUtils.getIcon(e.instance.props.channel.id) || "").replace(/\.webp|\.gif/, ".png")]);
}
onImageContextMenu (e) {
if (!e.instance.props.href && !e.instance.props.src) return;
this.injectItem(e, [e.instance.props.href || e.instance.props.src]);
}
onMessageContextMenu (e) {
if (!e.instance.props.message || !e.instance.props.channel || !e.instance.props.target) return;
if (e.instance.props.attachment) this.injectItem(e, [e.instance.props.attachment.url], null, true);
else {
const target = e.instance.props.target.tagName == "A" && BDFDB.DOMUtils.containsClass(e.instance.props.target, BDFDB.disCN.imageoriginallink) && e.instance.props.target.parentElement.querySelector("img, video") || e.instance.props.target;
if (target.tagName == "A" && e.instance.props.message.embeds && e.instance.props.message.embeds[0] && (e.instance.props.message.embeds[0].type == "image" || e.instance.props.message.embeds[0].type == "video" || e.instance.props.message.embeds[0].type == "gifv")) this.injectItem(e, [target.href], null, true);
else if (target.tagName == "IMG" && target.complete && target.naturalHeight) {
if (BDFDB.DOMUtils.getParent(BDFDB.dotCN.imagewrapper, target) || BDFDB.DOMUtils.containsClass(target, BDFDB.disCN.imagesticker)) this.injectItem(e, [{file: target.src, original: this.getTargetLink(e.instance.props.target) || this.getTargetLink(target)}], null, true);
else if (BDFDB.DOMUtils.containsClass(target, BDFDB.disCN.embedauthoricon) && this.settings.places.userAvatars) this.injectItem(e, [target.src], null, true);
else if (BDFDB.DOMUtils.containsClass(target, BDFDB.disCN.emojiold, "emote", false) && this.settings.places.emojis) this.injectItem(e, [{file: target.src, alternativeName: target.getAttribute("data-name")}], null, true);
}
else if (target.tagName == "VIDEO") {
if (BDFDB.DOMUtils.containsClass(target, BDFDB.disCN.embedvideo) || BDFDB.DOMUtils.getParent(BDFDB.dotCN.attachmentvideo, target)) this.injectItem(e, [{file: target.src, original: this.getTargetLink(e.instance.props.target) || this.getTargetLink(target)}], null, true);
}
else {
const reaction = BDFDB.DOMUtils.getParent(BDFDB.dotCN.messagereaction, target);
if (reaction && this.settings.places.emojis) {
const emoji = reaction.querySelector(BDFDB.dotCN.emojiold);
if (emoji) this.injectItem(e, [{file: emoji.src, alternativeName: emoji.getAttribute("data-name")}], null, true);
}
}
}
}
getTargetLink (target) {
let ele = target;
let src = "", href = "";
while (ele instanceof Node) ele instanceof HTMLImageElement && null != ele.src && (src = ele.src), ele instanceof HTMLAnchorElement && null != ele.href && (href = ele.href), ele = ele.parentNode;
return href || src;
}
injectItem (e, urls, prefix, isNative = false) {
let validUrls = this.filterUrls(...urls);
if (!validUrls.length) return;
let [nativeParent, nativeIndex] = BDFDB.ContextMenuUtils.findItem(e.returnvalue, {id: "copy-native-link", group: true});
if (nativeIndex > -1) {
if (validUrls.length == 1) isNative = true;
nativeParent.splice(nativeIndex, 1);
nativeIndex -= 1;
}
for (let id of ["open-native-link", "copy-image", "save-image"]) {
let [removeParent, removeIndex] = BDFDB.ContextMenuUtils.findItem(e.returnvalue, {id: id, group: true});
if (removeIndex > -1) removeParent.splice(removeIndex, 1);
}
let subMenu = this.createSubMenus({
instance: e.instance,
urls: validUrls,
prefix: prefix,
target: e.instance.props.target
});
let [children, index] = isNative && nativeIndex > -1 ? [nativeParent, nativeIndex] : BDFDB.ContextMenuUtils.findItem(e.returnvalue, {id: "devmode-copy-id", group: true});
children.splice(index > -1 ? index : children.length, 0, isNative ? subMenu : BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuGroup, {
children: BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
label: this.isValid(validUrls[0].file, "video") ? this.labels.context_videoactions : this.labels.context_imageactions,
id: BDFDB.ContextMenuUtils.createItemId(this.name, "main-subitem"),
children: subMenu
})
}));
}
filterUrls (...urls) {
let addedUrls = [];
return urls.filter(n => this.isValid(n && n.file || n)).map(n => {
let srcUrl = (n.file || n).replace(/^url\(|\)$|"|'/g, "").replace(/\?size\=\d+$/, "?size=4096").replace(/\?size\=\d+&/, "?size=4096&").replace(/[\?\&](height|width)=\d+/g, "").split("%3A")[0];
if (srcUrl.startsWith("https://cdn.discordapp.com/") && !srcUrl.endsWith("?size=4096") && srcUrl.indexOf("?size=4096&") == -1) srcUrl += "?size=4096";
let originalUrl = (n.original || n.file || n).replace(/^url\(|\)$|"|'/g, "").replace(/\?size\=\d+$/, "?size=4096").replace(/\?size\=\d+&/, "?size=4096&").replace(/[\?\&](height|width)=\d+/g, "").split("%3A")[0];
if (originalUrl.startsWith("https://cdn.discordapp.com/") && !originalUrl.endsWith("?size=4096") && originalUrl.indexOf("?size=4096&") == -1) originalUrl += "?size=4096";
let fileUrl = srcUrl;
if (fileUrl.indexOf("https://images-ext-1.discordapp.net/external/") > -1 || fileUrl.indexOf("https://images-ext-2.discordapp.net/external/") > -1) {
if (fileUrl.split("/https/").length > 1) fileUrl = "https://" + fileUrl.split("/https/").pop();
else if (url.split("/http/").length > 1) fileUrl = "http://" + fileUrl.split("/http/").pop();
}
const file = fileUrl && (BDFDB.LibraryModules.URLParser.parse(fileUrl).pathname || "").toLowerCase();
const fileType = file && (file.split(".").pop() || "");
return fileUrl && fileType && !addedUrls.includes(srcUrl) && addedUrls.push(srcUrl) && {file: fileUrl, src: srcUrl, original: originalUrl, isGuildSpecific: /^https:\/\/cdn\.discordapp\.com\/guilds\/\d+\/users\/\d+/.test(srcUrl), fileType, alternativeName: escape((n.alternativeName || "").replace(/:/g, ""))};
}).filter(n => n);
}
isValid (url, type) {
if (!url) return false;
const file = url && (BDFDB.LibraryModules.URLParser.parse(url).pathname || "").split("%3A")[0].toLowerCase();
return file && (!type && (url.indexOf("discord.com/streams/guild:") > -1 || url.indexOf("discordapp.com/streams/guild:") > -1 || url.indexOf("discordapp.net/streams/guild:") > -1 || url.startsWith("https://images-ext-1.discordapp.net/") || url.startsWith("https://images-ext-2.discordapp.net/") || Object.keys(fileTypes).some(t => file.endsWith(`/${t}`) || file.endsWith(`.${t}`))) || type && Object.keys(fileTypes).filter(t => fileTypes[t][type]).some(t => file.endsWith(`/${t}`) || file.endsWith(`.${t}`)));
}
getPosterUrl (url) {
return (url || "").replace("https://cdn.discordapp.com", "https://media.discordapp.net").split("?size=")[0] + "?format=jpeg";
}
createSubMenus (data) {
return data.urls.length == 1 ? this.createUrlMenu(data.instance, data.urls[0], data.target) : data.urls.map((urlData, i) => BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
label: [urlData.isGuildSpecific && BDFDB.LanguageUtils.LanguageStrings.CHANGE_IDENTITY_SERVER_PROFILE, data.prefix, urlData.fileType.toUpperCase()].filter(n => n).join(" "),
id: BDFDB.ContextMenuUtils.createItemId(this.name, "subitem", i),
children: this.createUrlMenu(data.instance, urlData, data.target)
}));
}
createUrlMenu (instance, urlData, target) {
let enabledEngines = BDFDB.ObjectUtils.filter(this.settings.engines, n => n);
let enginesWithoutAll = BDFDB.ObjectUtils.filter(enabledEngines, n => n != "_all", true);
let engineKeys = Object.keys(enginesWithoutAll);
let locations = Object.keys(ownLocations).filter(n => ownLocations[n].enabled);
let isVideo = this.isValid(urlData.file, "video");
let type = isVideo ? BDFDB.LanguageUtils.LanguageStrings.VIDEO : BDFDB.LanguageUtils.LanguageStrings.IMAGE;
return BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuGroup, {
children: [
BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
label: BDFDB.LanguageUtils.LanguageStrings.COPY_LINK,
id: BDFDB.ContextMenuUtils.createItemId(this.name, "copy-link"),
action: _ => {
BDFDB.LibraryModules.WindowUtils.copy(urlData.original.split("?size")[0]);
BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LanguageStrings.LINK_COPIED, {type: "success"});
}
}),
urlData.file != urlData.original && BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
label: BDFDB.LanguageUtils.LanguageStrings.COPY_MEDIA_LINK,
id: BDFDB.ContextMenuUtils.createItemId(this.name, "copy-media-link"),
action: _ => {
BDFDB.LibraryModules.WindowUtils.copy(urlData.file.split("?size")[0]);
BDFDB.NotificationUtils.toast(BDFDB.LanguageUtils.LanguageStrings.LINK_COPIED, {type: "success"});
}
}),
BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
label: BDFDB.LanguageUtils.LanguageStrings.OPEN_LINK,
id: BDFDB.ContextMenuUtils.createItemId(this.name, "open-link"),
action: _ => BDFDB.DiscordUtils.openLink(urlData.original)
}),
!this.isValid(urlData.file, "copyable") ? null : BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
label: this.labels.context_copy.replace("{{var0}}", type),
id: BDFDB.ContextMenuUtils.createItemId(this.name, "copy-file"),
action: _ => this.copyFile(urlData.src)
}),
!document.querySelector(BDFDB.dotCN.imagemodal) && BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
label: this.labels.context_view.replace("{{var0}}", type),
id: BDFDB.ContextMenuUtils.createItemId(this.name, "view-file"),
action: _ => {
const imageThrowaway = document.createElement(isVideo ? "video" : "img");
imageThrowaway.addEventListener(isVideo ? "loadedmetadata" : "load", function() {
BDFDB.LibraryModules.ModalUtils.openModal(modalData => {
_this.cacheClickedImage(target);
return BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ModalComponents.ModalRoot, Object.assign({
className: BDFDB.disCN.imagemodal
}, modalData, {
size: BDFDB.LibraryComponents.ModalComponents.ModalSize.DYNAMIC,
"aria-label": BDFDB.LanguageUtils.LanguageStrings.IMAGE,
children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ImageModal, {
animated: !!isVideo,
src: imageThrowaway.src,
original: urlData.original,
width: isVideo ? this.videoWidth : this.width,
height: isVideo ? this.videoHeight : this.height,
className: BDFDB.disCN.imagemodalimage,
shouldAnimate: true,
renderLinkComponent: props => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Anchor, props),
children: !isVideo ? null : (videoData => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Video, {
ignoreMaxSize: true,
poster: _this.getPosterUrl(urlData.src || urlData.file),
src: urlData.src || urlData.file,
width: videoData.size.width,
height: videoData.size.height,
naturalWidth: this.videoWidth,
naturalHeight: this.videoHeight,
play: true,
playOnHover: !!BDFDB.LibraryStores.AccessibilityStore.useReducedMotion
}))
})
}), true);
});
});
imageThrowaway.src = urlData.src || urlData.file;
}
}),
BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
label: this.labels.context_saveas.replace("{{var0}}", type),
id: BDFDB.ContextMenuUtils.createItemId(this.name, "download-file-as"),
action: _ => this.downloadFile({url: urlData.src, fallbackUrl: urlData.file || urlData.original}, null, urlData.alternativeName),
children: locations.length && BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuGroup, {
children: locations.map((name, i) => BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
id: BDFDB.ContextMenuUtils.createItemId(this.name, "download", name, i),
label: name,
action: _ => this.downloadFile({url: urlData.src, fallbackUrl: urlData.file || urlData.original}, ownLocations[name].location, urlData.alternativeName)
}))
})
}),
!this.isValid(urlData.original, "searchable") || !engineKeys.length ? null : engineKeys.length == 1 ? BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
label: this.labels.context_searchwith.replace("{{var0}}", type).replace("...", this.defaults.engines[engineKeys[0]].name),
id: BDFDB.ContextMenuUtils.createItemId(this.name, "single-search"),
persisting: true,
action: event => {
if (!event.shiftKey) BDFDB.ContextMenuUtils.close(instance);
BDFDB.DiscordUtils.openLink(this.defaults.engines[engineKeys[0]].url.replace(imgUrlReplaceString, encodeURIComponent(urlData.original)), {
minimized: event.shiftKey
});
}
}) : BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
label: this.labels.context_searchwith.replace("{{var0}}", type),
id: BDFDB.ContextMenuUtils.createItemId(this.name, "submenu-search"),
children: !engineKeys.length ? BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
label: this.labels.submenu_disabled,
id: BDFDB.ContextMenuUtils.createItemId(this.name, "disabled"),
disabled: true
}) : Object.keys(enabledEngines).map(key => BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
label: this.defaults.engines[key].name,
id: BDFDB.ContextMenuUtils.createItemId(this.name, "search", key),
color: key == "_all" ? BDFDB.DiscordConstants.MenuItemColors.DANGER : BDFDB.DiscordConstants.MenuItemColors.DEFAULT,
persisting: true,
action: event => {
const open = (url, k) => BDFDB.DiscordUtils.openLink(this.defaults.engines[k].url.replace(imgUrlReplaceString, this.defaults.engines[k].raw ? url : encodeURIComponent(url)), {minimized: event.shiftKey});
if (!event.shiftKey) BDFDB.ContextMenuUtils.close(instance);
if (key == "_all") {
for (let key2 in enginesWithoutAll) open(urlData.original, key2);
}
else open(urlData.original, key);
}
}))
})
].filter(n => n)
});
}
processImageModal (e) {
if (!e.returnvalue) {
if (switchedImageProps) {
e.instance.props = Object.assign(e.instance.props, switchedImageProps);
switchedImageProps = null;
}
}
else {
let url = this.getImageSrc(viewedImage && viewedImage.proxy_url || typeof e.instance.props.children == "function" && e.instance.props.children(Object.assign({}, e.instance.props, {size: e.instance.props})).props.src || e.instance.props.src);
let isVideo = this.isValid(url, "video");
let [children, index] = BDFDB.ReactUtils.findParent(e.returnvalue, {props: [["className", BDFDB.disCN.downloadlink]]});
if (index > -1) {
let type = isVideo ? BDFDB.LanguageUtils.LanguageStrings.VIDEO : BDFDB.LanguageUtils.LanguageStrings.IMAGE;
let openContext = event => BDFDB.ContextMenuUtils.open(this, event, BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuGroup, {
children: Object.keys(this.defaults.zoomSettings).map(type => {
let isBoolean = typeof this.defaults.zoomSettings[type].value == "boolean";
return BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems[isBoolean ? "MenuCheckboxItem" : "MenuSliderItem"], Object.assign({
id: BDFDB.ContextMenuUtils.createItemId(this.name, type)
}, isBoolean ? {
checked: this.settings.zoomSettings[type],
action: value => {
this.settings.zoomSettings[type] = value;
BDFDB.DataUtils.save(this.settings.zoomSettings, this, "zoomSettings");
}
} : {
value: this.settings.zoomSettings[type],
renderLabel: (value, instance) => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex, {
align: BDFDB.LibraryComponents.Flex.Align.CENTER,
children: [
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Flex.Child, {
children: `${this.labels[this.defaults.zoomSettings[type].label] || BDFDB.LanguageUtils.LanguageStrings[this.defaults.zoomSettings[type].label]}:`
}),
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextInput, {
type: "number",
size: BDFDB.LibraryComponents.TextInput.Sizes.MINI,
style: {width: 70},
min: 1,
max: this.defaults.zoomSettings[type].maxValue,
value: this.settings.zoomSettings[type],
onChange: value => value && value >= this.defaults.zoomSettings[type].minValue && instance.handleValueChange(BDFDB.NumberUtils.mapRange([this.defaults.zoomSettings[type].minValue, this.defaults.zoomSettings[type].maxValue], [0, 100], value))
}),
BDFDB.ReactUtils.createElement("span", {
style: {width: 20},
children: this.defaults.zoomSettings[type].unit
})
]
}),
onValueRender: value => `${value}${this.defaults.zoomSettings[type].unit}`,
onValueChange: value => {
this.settings.zoomSettings[type] = value;
BDFDB.DataUtils.save(this.settings.zoomSettings, this, "zoomSettings");
}
}, BDFDB.ObjectUtils.extract(this.defaults.zoomSettings[type], isBoolean ? ["label"] : ["digits", "minValue", "maxValue"])));
})
}));
children[index] = BDFDB.ReactUtils.createElement("span", {
className: BDFDB.disCN._imageutilitiesoperations,
children: [
children[index],
this.settings.viewerSettings.saveImage && [
BDFDB.ReactUtils.createElement("span", {
className: BDFDB.disCN.downloadlink,
children: "|",
style: {margin: "0 5px"}
}),
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Anchor, {
className: BDFDB.disCN.downloadlink,
children: this.labels.context_saveas.replace("{{var0}}", type),
onClick: event => {
BDFDB.ListenerUtils.stopEvent(event);
this.downloadFile({url: url});
},
onContextMenu: event => {
let locations = Object.keys(ownLocations).filter(n => ownLocations[n].enabled);
if (locations.length) BDFDB.ContextMenuUtils.open(this, event, BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuGroup, {
children: locations.map((name, i) => BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
id: BDFDB.ContextMenuUtils.createItemId(this.name, "download", name, i),
label: name,
action: _ => this.downloadFile({url: url}, ownLocations[name].location)
}))
}));
}
})
],
this.settings.viewerSettings.copyImage && this.isValid(url, "copyable") && [
BDFDB.ReactUtils.createElement("span", {
className: BDFDB.disCN.downloadlink,
children: "|",
style: {margin: "0 5px"}
}),
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Anchor, {
className: BDFDB.disCN.downloadlink,
children: this.labels.context_copy.replace("{{var0}}", type),
onClick: event => {
BDFDB.ListenerUtils.stopEvent(event);
this.copyFile(url);
}
})
],
this.settings.viewerSettings.galleryMode && viewedImage && this.settings.viewerSettings.jumpTo && [
BDFDB.ReactUtils.createElement("span", {
className: BDFDB.disCN.downloadlink,
children: "|",
style: {margin: "0 5px"}
}),
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Anchor, {
className: BDFDB.disCN.downloadlink,
children: BDFDB.LanguageUtils.LanguageStrings.JUMP,
onClick: event => {
let layerContainer = !event.shiftKey && BDFDB.DOMUtils.getParent(BDFDB.dotCN.itemlayercontainer, event.currentTarget)
let backdrop = layerContainer && layerContainer.querySelector(BDFDB.dotCN.backdrop);
if (backdrop) backdrop.click();
let channel = BDFDB.LibraryStores.ChannelStore.getChannel(viewedImage.channelId);
if (channel) BDFDB.LibraryModules.HistoryUtils.transitionTo(BDFDB.DiscordConstants.Routes.CHANNEL(channel.guild_id, channel.id, viewedImage.messageId));
}
})
],
this.settings.viewerSettings.zoomMode && !isVideo && [
BDFDB.ReactUtils.createElement("span", {
className: BDFDB.disCN.downloadlink,
children: "|",
style: {margin: "0 5px"}
}),
BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Anchor, {
className: BDFDB.disCN.downloadlink,
children: `Zoom ${BDFDB.LanguageUtils.LanguageStrings.SETTINGS}`,
onClick: openContext,
onContextMenu: openContext
})
]
].flat(10).filter(n => n)
});
if (this.settings.viewerSettings.details) {
e.returnvalue.props.children.push(BDFDB.ReactUtils.createElement("div", {
className: BDFDB.disCN._imageutilitiesdetailswrapper,
children: [
e.instance.props.alt && {label: "Alt", text: e.instance.props.alt},
{label: "Source", text: url},
{label: "Size", text: `${e.instance.props.width}x${e.instance.props.height}px`},
cachedImages && cachedImages.amount && cachedImages.amount > 1 && {label: "Image", text: `${cachedImages.index + 1 || 1} of ${cachedImages.amount}`}
].filter(n => n).map(data => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextElement, {
className: BDFDB.disCN._imageutilitiesdetails,
children: [
BDFDB.ReactUtils.createElement("div", {
className: BDFDB.disCN._imageutilitiesdetailslabel,
children: data.label + ":"
}),
data.text
]
}))
}));
}
}
if (this.settings.viewerSettings.galleryMode && viewedImage) {
if (!cachedImages || cachedImages.channelId != viewedImage.channelId || cachedImages.amount && this.getImageIndex(cachedImages.all, viewedImage) == -1) {
BDFDB.TimeUtils.clear(viewedImageTimeout);
let channel = BDFDB.LibraryStores.ChannelStore.getChannel(viewedImage.channelId);
BDFDB.LibraryModules.APIUtils.get({
url: BDFDB.DiscordConstants.Endpoints.MESSAGES(channel.id),
query: BDFDB.LibraryModules.APIEncodeUtils.stringify({
channel_id: channel && channel.guild_id ? (BDFDB.ChannelUtils.isThread(channel) && channel.parent_id || channel.id) : null,
has: "image",
include_nsfw: true,
limit: 100,
around: viewedImage.messageId
})
}).catch(err => {
cachedImages = {
channelId: viewedImage.channelId,
firstReached: null,
oldestId: null,
all: [],
index: -1,
amount: 0,
newestId: null,
lastReached: null
};
this.updateImageModal();
}).then(result => {
if (!viewedImage) return;
let messages = [], index = -1;
if (result) {
messages = result.body.flat(10).reverse();
cachedImages = {all: this.filterMessagesForImages(messages, viewedImage)};
index = this.getImageIndex(cachedImages.all, viewedImage);
}
if (index > -1) cachedImages = Object.assign(cachedImages, {
channelId: viewedImage.channelId,
firstReached: index == 0,
oldestId: messages[0] ? messages[0].id : null,
index: index,
amount: cachedImages.all.length,
newestId: messages[messages.length-1] ? messages[messages.length-1].id : null,
lastReached: index == (cachedImages.all.length - 1)
});
else cachedImages = {
channelId: viewedImage.channelId,
firstReached: null,
oldestId: null,
all: [],
index: -1,
amount: 0,
newestId: null,
lastReached: null
};
this.updateImageModal();
});
}
else {
if (cachedImages.all[cachedImages.index - 1]) e.returnvalue.props.children.push(BDFDB.ReactUtils.createElement(LazyImageSiblingComponent, {
className: BDFDB.disCN._imageutilitiesprevious,
url: this.getImageSrc(cachedImages.all[cachedImages.index - 1].thumbnail || cachedImages.all[cachedImages.index - 1]),
offset: -1,
svgIcon: BDFDB.LibraryComponents.SvgIcon.Names.LEFT_CARET
}));
if (cachedImages.all[cachedImages.index + 1]) e.returnvalue.props.children.push(BDFDB.ReactUtils.createElement(LazyImageSiblingComponent, {
className: BDFDB.disCN._imageutilitiesnext,
url: this.getImageSrc(cachedImages.all[cachedImages.index + 1].thumbnail || cachedImages.all[cachedImages.index + 1]),
offset: 1,
svgIcon: BDFDB.LibraryComponents.SvgIcon.Names.RIGHT_CARET
}));
if (cachedImages.all[cachedImages.index - 1] || cachedImages.all[cachedImages.index + 1]) {
this.addListener("keydown", "Gallery", event => {
if (!firedEvents.includes("Gallery")) {
firedEvents.push("Gallery");
if (event.keyCode == 37) this.switchImages(-1);
else if (event.keyCode == 39) this.switchImages(1);
}
});
this.addListener("keyup", "Gallery", _ => BDFDB.ArrayUtils.remove(firedEvents, "Gallery", true));
}
}
}
}
}
processModalCarousel (e) {
if (!this.settings.viewerSettings.galleryMode || !BDFDB.ReactUtils.findParent(e.returnvalue, {name: "ImageModal"})) return;
e.returnvalue.props.className = "";
e.returnvalue.props.children[0] = null;
e.returnvalue.props.children[2] = null;
}
processLazyImage (e) {
if (e.node) {
if (e.instance.props.resized) {
for (let selector of ["embedfull", "embedinlinemedia", "embedgridcontainer"]) {
let parent = BDFDB.DOMUtils.getParent(BDFDB.dotCN[selector], e.node);
if (parent) parent.style.setProperty("max-width", "unset", "important");
}
for (let ele of [e.node.style.getPropertyValue("width") && e.node, ...e.node.querySelectorAll("[style*='width:']")].filter(n => n)) {
ele.style.setProperty("width", e.instance.props.width + "px");
ele.style.setProperty("max-width", e.instance.props.width + "px");
ele.style.setProperty("height", e.instance.props.height + "px");
ele.style.setProperty("max-height", e.instance.props.height + "px");
}
for (let ele of [e.node.src && e.node, ...e.node.querySelectorAll("[src]")].filter(n => n)) ele.src = ele.src.split("?width")[0].split("?height")[0].split("?size")[0];
if (e.instance.state.readyState != BDFDB.LibraryComponents.ImageComponents.ImageReadyStates.READY) {
e.instance.state.readyState = BDFDB.LibraryComponents.ImageComponents.ImageReadyStates.READY;
BDFDB.ReactUtils.forceUpdate(e.instance);
}
}
if (e.methodname == "componentWillUnmount" && BDFDB.DOMUtils.getParent(BDFDB.dotCN.imagemodal, e.node)) {
firstViewedImage = null;
viewedImage = null;
this.cleanupListeners("Gallery");
}
if (e.methodname == "componentDidMount" && BDFDB.DOMUtils.getParent(BDFDB.dotCNC.imagemodal + BDFDB.dotCN.modalcarouselmodal, e.node)) {
BDFDB.TimeUtils.clear(viewedImageTimeout);
let modal = BDFDB.DOMUtils.getParent(BDFDB.dotCN.modal, e.node);
if (modal) {
modal.className = BDFDB.DOMUtils.formatClassName(modal.className, this.settings.viewerSettings.galleryMode && BDFDB.disCN._imageutilitiesgallery, this.settings.viewerSettings.details && BDFDB.disCN._imageutilitiesdetailsadded);
if (this.settings.viewerSettings.galleryMode) {
BDFDB.DOMUtils.addClass(modal, BDFDB.disCN.imagemodal);
BDFDB.DOMUtils.removeClass(modal, BDFDB.disCN.modalcarouselmodal, BDFDB.disCN.modalcarouselmodalzoomed);
}
}
let isVideo = typeof e.instance.props.children == "function";
if (isVideo && !BDFDB.LibraryStores.AccessibilityStore.useReducedMotion) e.node.style.setProperty("pointer-events", "none");
if (this.settings.viewerSettings.zoomMode && !isVideo && !BDFDB.DOMUtils.containsClass(e.node.parentElement, BDFDB.disCN._imageutilitiessibling)) {
e.node.style.setProperty("cursor", "zoom-in");
e.node.addEventListener("mousedown", event => {
if (event.which != 1 || e.node.querySelector("video")) return;
BDFDB.ListenerUtils.stopEvent(event);
let vanishObserver;
let imgRects = BDFDB.DOMUtils.getRects(e.node.firstElementChild);
let lens = BDFDB.DOMUtils.create(`<div class="${BDFDB.disCN._imageutilitieslense}" style="border-radius: 50% !important; pointer-events: none !important; z-index: 10000 !important; width: ${this.settings.zoomSettings.lensSize}px !important; height: ${this.settings.zoomSettings.lensSize}px !important; position: fixed !important;"><div style="position: absolute !important; top: 0 !important; right: 0 !important; bottom: 0 !important; left: 0 !important;"><${e.node.firstElementChild.tagName} src="${!this.isValid(e.instance.props.src, "video") ? e.instance.props.src : this.getPosterUrl(e.instance.props.src)}" style="width: ${imgRects.width * this.settings.zoomSettings.zoomLevel}px; height: ${imgRects.height * this.settings.zoomSettings.zoomLevel}px; position: fixed !important;${this.settings.zoomSettings.pixelMode ? " image-rendering: pixelated !important;" : ""}"${e.node.firstElementChild.tagName == "VIDEO" ? " loop autoplay" : ""}></${e.node.firstElementChild.tagName}></div></div>`);
let pane = lens.firstElementChild.firstElementChild;
let backdrop = BDFDB.DOMUtils.create(`<div class="${BDFDB.disCN._imageutilitieslensebackdrop}" style="background: rgba(0, 0, 0, 0.3) !important; position: absolute !important; top: 0 !important; right: 0 !important; bottom: 0 !important; left: 0 !important; pointer-events: none !important; z-index: 8000 !important;"></div>`);
let appMount = document.querySelector(BDFDB.dotCN.appmount);
appMount.appendChild(lens);
appMount.appendChild(backdrop);
let lensRects = BDFDB.DOMUtils.getRects(lens);
let halfW = lensRects.width / 2, halfH = lensRects.height / 2;
let minX = imgRects.left, maxX = minX + imgRects.width;
let minY = imgRects.top, maxY = minY + imgRects.height;
lens.update = _ => {
let x = event.clientX > maxX ? maxX - halfW : event.clientX < minX ? minX - halfW : event.clientX - halfW;
let y = event.clientY > maxY ? maxY - halfH : event.clientY < minY ? minY - halfH : event.clientY - halfH;
lens.style.setProperty("left", x + "px", "important");
lens.style.setProperty("top", y + "px", "important");
lens.style.setProperty("width", this.settings.zoomSettings.lensSize + "px", "important");
lens.style.setProperty("height", this.settings.zoomSettings.lensSize + "px", "important");
lens.style.setProperty("clip-path", `circle(${(this.settings.zoomSettings.lensSize/2) + 2}px at center)`, "important");
lens.firstElementChild.style.setProperty("clip-path", `circle(${this.settings.zoomSettings.lensSize/2}px at center)`, "important");
pane.style.setProperty("left", imgRects.left + ((this.settings.zoomSettings.zoomLevel - 1) * (imgRects.left - x - halfW)) + "px", "important");
pane.style.setProperty("top", imgRects.top + ((this.settings.zoomSettings.zoomLevel - 1) * (imgRects.top - y - halfH)) + "px", "important");
pane.style.setProperty("width", imgRects.width * this.settings.zoomSettings.zoomLevel + "px", "important");
pane.style.setProperty("height", imgRects.height * this.settings.zoomSettings.zoomLevel + "px", "important");
};
lens.update();
for (let ele of [e.node, document.querySelector(BDFDB.dotCN.modalcarouselwrapper)]) if (ele) ele.style.setProperty("pointer-events", "none", "important");
let dragging = event2 => {
event = event2;
lens.update();
};
let releasing = event2 => {
BDFDB.ListenerUtils.stopEvent(event2);
for (let ele of [e.node, document.querySelector(BDFDB.dotCN.modalcarouselwrapper)]) if (ele) ele.style.removeProperty("pointer-events");
this.cleanupListeners("Zoom");
document.removeEventListener("mousemove", dragging);
document.removeEventListener("mouseup", releasing);
if (vanishObserver) vanishObserver.disconnect();
BDFDB.DOMUtils.remove(lens, backdrop);
BDFDB.DataUtils.save(this.settings.zoomSettings, this, "zoomSettings");
};
document.addEventListener("mousemove", dragging);
document.addEventListener("mouseup", releasing);
this.cleanupListeners("Zoom");
this.addListener("wheel", "Zoom", event2 => {
if (!document.contains(e.node)) this.cleanupListeners("Zoom");
else {
if (event2.deltaY < 0 && (this.settings.zoomSettings.zoomLevel + 0.1) <= this.defaults.zoomSettings.zoomLevel.maxValue) {
this.settings.zoomSettings.zoomLevel += 0.1;
lens.update();
}
else if (event2.deltaY > 0 && (this.settings.zoomSettings.zoomLevel - 0.1) >= this.defaults.zoomSettings.zoomLevel.minValue) {
this.settings.zoomSettings.zoomLevel -= 0.1;
lens.update();
}
}
});
this.addListener("keydown", "Zoom", event2 => {
if (!document.contains(e.node)) this.cleanupListeners("Zoom");
else if (!firedEvents.includes("Zoom")) {
firedEvents.push("Zoom");
if (event2.keyCode == 187 && (this.settings.zoomSettings.zoomLevel + 0.5) <= this.defaults.zoomSettings.zoomLevel.maxValue) {
this.settings.zoomSettings.zoomLevel += 0.5;
lens.update();
}
else if (event2.keyCode == 189 && (this.settings.zoomSettings.zoomLevel - 0.5) >= this.defaults.zoomSettings.zoomLevel.minValue) {
this.settings.zoomSettings.zoomLevel -= 0.5;
lens.update();
}
}
});
this.addListener("keyup", "Zoom", _ => {
BDFDB.ArrayUtils.remove(firedEvents, "Zoom", true);
if (!document.contains(e.node)) this.cleanupListeners("Zoom");
});
vanishObserver = new MutationObserver(changes => {if (!document.contains(e.node)) releasing();});
vanishObserver.observe(appMount, {childList: true, subtree: true});
});
}
}
}
else {
let reactInstance = BDFDB.ObjectUtils.get(e, `instance.${BDFDB.ReactUtils.instanceKey}`);
if (this.settings.rescaleSettings.imageViewer != "NONE" && BDFDB.ReactUtils.findOwner(reactInstance, {name: "ImageModal", up: true})) {
let aRects = BDFDB.DOMUtils.getRects(document.querySelector(BDFDB.dotCN.appmount));
let ratio = Math.min((aRects.width * (this.settings.viewerSettings.galleryMode ? 0.8 : 1) - 20) / e.instance.props.width, (aRects.height - (this.settings.viewerSettings.details ? 280 : 100)) / e.instance.props.height);
ratio = this.settings.rescaleSettings.imageViewer == "ORIGINAL" && ratio > 1 ? 1 : ratio;
let width = Math.round(ratio * e.instance.props.width);
let height = Math.round(ratio * e.instance.props.height);
if (e.instance.props.width != width || e.instance.props.maxWidth != width || e.instance.props.height != height || e.instance.props.maxHeight != height) {
e.instance.props.width = width;
e.instance.props.maxWidth = width;
e.instance.props.height = height;
e.instance.props.maxHeight = height;
e.instance.props.src = e.instance.props.src.replace(/width=\d+/, `width=${width}`).replace(/height=\d+/, `height=${height}`);
e.instance.props.resized = true;
}
}
if (this.settings.rescaleSettings.messages != "NONE" && (!e.instance.props.className || e.instance.props.className.indexOf(BDFDB.disCN.embedthumbnail) == -1) && (!e.instance.props.containerClassName || e.instance.props.containerClassName.indexOf(BDFDB.disCN.embedthumbnail) == -1 && e.instance.props.containerClassName.indexOf(BDFDB.disCN.embedvideoimagecomponent) == -1) && BDFDB.ReactUtils.findOwner(reactInstance, {name: "LazyImageZoomable", up: true})) {
let aRects = BDFDB.DOMUtils.getRects(document.querySelector(BDFDB.dotCN.appmount));
let mRects = BDFDB.DOMUtils.getRects(document.querySelector(BDFDB.dotCNC.messageaccessory + BDFDB.dotCN.messagecontents));
let mwRects = BDFDB.DOMUtils.getRects(document.querySelector(BDFDB.dotCN.messagewrapper));
if (mRects.width || mwRects.width) {
let embed = BDFDB.ReactUtils.findValue(reactInstance, "embed", {up: true});
let ratio = ((mRects.width || (mwRects.width - 120)) - (embed && embed.color ? 100 : 0)) / e.instance.props.width;
ratio = this.settings.rescaleSettings.messages == "ORIGINAL" && ratio > 1 ? 1 : ratio;
let width = Math.round(ratio * e.instance.props.width);
let height = Math.round(ratio * e.instance.props.height);
if (height > (aRects.height * 0.66)) {
let newHeight = Math.round(aRects.height * 0.66);
width = (newHeight/height) * width;
height = newHeight;
}
if (e.instance.props.width != width || e.instance.props.maxWidth != width || e.instance.props.height != height || e.instance.props.maxHeight != height) {
e.instance.props.width = width;
e.instance.props.maxWidth = width;
e.instance.props.height = height;
e.instance.props.maxHeight = height;
e.instance.props.src = e.instance.props.src.replace(/width=\d+/, `width=${width}`).replace(/height=\d+/, `height=${height}`);
e.instance.props.resized = true;
}
}
}
}
}
processLazyImageZoomable (e) {
if (!e.instance.props.original || e.instance.props.src.indexOf("https://media.discordapp.net/attachments") != 0) return;
if (this.settings.detailsSettings.tooltip) {
const attachment = BDFDB.ReactUtils.findValue(e.instance, "attachment", {up: true});
if (attachment) {
const onMouseEnter = e.returnvalue.props.onMouseEnter;
e.returnvalue.props.onMouseEnter = BDFDB.TimeUtils.suppress((...args) => {
BDFDB.TooltipUtils.create(args[0].target, [
attachment.filename,
BDFDB.NumberUtils.formatBytes(attachment.size),
`${attachment.width}x${attachment.height}px`
].map(l => BDFDB.ReactUtils.createElement("div", {style: {padding: "2px 0"}, children: l})), {
type: "right",
delay: this.settings.detailsSettings.tooltipDelay
});
return onMouseEnter(...args);
}, "Error in onMouseEnter of LazyImageZoomable!");
}
}
if (this.settings.detailsSettings.footnote && (e.instance.props.className || "").indexOf(BDFDB.disCN.embedmedia) == -1 && (e.instance.props.className || "").indexOf(BDFDB.disCN.embedthumbnail) == -1) {
e.returnvalue = BDFDB.ReactUtils.createElement("div", {
children: [
e.returnvalue,
BDFDB.ReactUtils.createElement(ImageDetailsComponent, {
original: e.instance.props.original,
attachment: {
height: 0,
width: 0,
filename: "unknown.png"
}
})
]
});
}
}
processMessageAccessories (e) {
if (this.settings.general.nsfwMode && e.instance.props.channel.nsfw) {
e.instance.props.message = new BDFDB.DiscordObjects.Message(e.instance.props.message);
e.instance.props.message.attachments = [].concat(e.instance.props.message.attachments);
for (let i in e.instance.props.message.attachments) if (e.instance.props.message.attachments[i].spoiler != undefined) {
e.instance.props.message.attachments[i] = Object.assign({}, e.instance.props.message.attachments[i], {spoiler: true, nsfw: !e.instance.props.message.attachments[i].spoiler});
}
}
}
processSpoiler (e) {
if (!e.returnvalue) {
if (this.settings.rescaleSettings.messages != "NONE" && !e.instance.props.inline && e.instance.props.type == "attachment" && e.instance.props.containerStyles) e.instance.props.containerStyles.maxWidth = "100%";
}
else {
if (this.settings.general.nsfwMode && typeof e.returnvalue.props.children == "function") {
let childrenRender = e.returnvalue.props.children;
e.returnvalue.props.children = BDFDB.TimeUtils.suppress((...args) => {
let returnedChildren = childrenRender(...args);
let attachment = BDFDB.ReactUtils.findValue(returnedChildren, "attachment");
if (attachment && attachment.nsfw) {
let [children, index] = BDFDB.ReactUtils.findParent(returnedChildren, {name: "SpoilerWarning"});
if (index > -1) children[index] = BDFDB.ReactUtils.createElement("div", {
className: BDFDB.disCN.spoilerwarning,
children: "NSFW"
});
}
return returnedChildren;
}, "Error in Children Render of Spoiler!");
}
}
}
processUserBanner (e) {
if (!this.settings.places.userAvatars || !e.instance.props.displayProfile || !e.instance.props.displayProfile.banner) return;
let div = BDFDB.ReactUtils.findChild(e.returnvalue, {type: "div"});
if (div) div.props.onContextMenu = event => {
let validUrls = this.filterUrls(BDFDB.UserUtils.getBanner(e.instance.props.user.id, null, false), BDFDB.LibraryModules.IconUtils.isAnimatedIconHash(e.instance.props.displayProfile._userProfile.banner) && BDFDB.UserUtils.getBanner(e.instance.props.user.id, null, true), e.instance.props.displayProfile._guildMemberProfile.banner && BDFDB.UserUtils.getBanner(e.instance.props.user.id, e.instance.props.guildId, false), e.instance.props.displayProfile._guildMemberProfile.banner && BDFDB.LibraryModules.IconUtils.isAnimatedIconHash(e.instance.props.displayProfile._guildMemberProfile.banner) && BDFDB.UserUtils.getBanner(e.instance.props.user.id, e.instance.props.guildId, true));
if (validUrls.length) BDFDB.ContextMenuUtils.open(this, event, BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuGroup, {
children: validUrls.length == 1 ? this.createSubMenus({
instance: {},
urls: validUrls,
prefix: BDFDB.LanguageUtils.LanguageStrings.USER_SETTINGS_PROFILE_BANNER
}) : BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, {
label: this.labels.context_imageactions,
id: BDFDB.ContextMenuUtils.createItemId(this.name, "main-subitem"),
children: this.createSubMenus({
instance: {},
urls: validUrls,
prefix: BDFDB.LanguageUtils.LanguageStrings.USER_SETTINGS_PROFILE_BANNER
})
})
}));
};
}
cacheClickedImage (target) {
if (!target) return;
const image = (BDFDB.DOMUtils.getParent(BDFDB.dotCN.imagewrapper, target) || target).querySelector("img") || target;
if (!image) return;
const message = BDFDB.ReactUtils.findValue(image, "message", {up: true});
if (!message) return;
firstViewedImage = {messageId: message.id, channelId: message.channel_id, proxy_url: image.src};
viewedImage = firstViewedImage;
if (cachedImages) cachedImages.index = this.getImageIndex(cachedImages.all, viewedImage);
viewedImageTimeout = BDFDB.TimeUtils.timeout(_ => {
firstViewedImage = null;
viewedImage = null;
}, 1000);
}
downloadFile (urls, path, alternativeName, fallbackToRequest) {
if (!urls) return BDFDB.NotificationUtils.toast(this.labels.toast_save_failed.replace("{{var0}}", BDFDB.LanguageUtils.LanguageStrings.IMAGE).replace("{{var1}}", path || "PC"), {type: "danger"});
let url = urls.url.startsWith("/assets") ? (window.location.origin + urls.url) : urls.url;
if (!fallbackToRequest) BDFDB.DiscordUtils.requestFileData(url, {timeout: 3000}, (error, buffer) => {
if (error || !buffer) {
if (urls.fallbackUrl && urls.url != urls.fallbackUrl) this.downloadFile({url: urls.fallbackUrl, oldUrl: urls.url}, path, alternativeName);
else this.downloadFile({url: urls.oldUrl || urls.url, fallbackUrl: urls.oldUrl ? urls.url : undefined}, path, alternativeName, true);
}
else {
let extension = this.getFileExtension(new Uint8Array(buffer));
if (!extension) BDFDB.NotificationUtils.toast(this.labels.toast_save_failed.replace("{{var0}}", BDFDB.LanguageUtils.LanguageStrings.IMAGE).replace("{{var1}}", path || "PC"), {type: "danger"});
else {
let type = fileTypes[extension].video ? BDFDB.LanguageUtils.LanguageStrings.VIDEO : BDFDB.LanguageUtils.LanguageStrings.IMAGE;
if (path) BDFDB.LibraryRequires.fs.writeFile(this.getFileName(path, (alternativeName || url.split("/").pop().split(".").slice(0, -1).join(".") || "unknown").slice(0, 35), extension, 0), Buffer.from(buffer), error => {
if (error) BDFDB.NotificationUtils.toast(this.labels.toast_save_failed.replace("{{var0}}", type).replace("{{var1}}", path), {type: "danger"});
else BDFDB.NotificationUtils.toast(this.labels.toast_save_success.replace("{{var0}}", type).replace("{{var1}}", path), {type: "success"});
});
else {
let hrefURL = window.URL.createObjectURL(new Blob([buffer], {type: this.getMimeType(extension)}));
let tempLink = document.createElement("a");
tempLink.href = hrefURL;
tempLink.download = `${(alternativeName || url.split("/").pop().split(".").slice(0, -1).join(".") || "unknown").slice(0, 35)}.${extension}`;
tempLink.click();
window.URL.revokeObjectURL(hrefURL);
}
}
}
});
else BDFDB.LibraryRequires.request(url, {agentOptions: {rejectUnauthorized: false}, headers: {"Content-Type": "application/json"}}, (error, response, buffer) => {
if (error || response.statusCode != 200 || response.headers["content-type"].indexOf("text/html") > -1) {
if (urls.fallbackUrl && urls.url != urls.fallbackUrl) this.downloadFile({url: urls.fallbackUrl}, path, alternativeName, true);
else BDFDB.NotificationUtils.toast(this.labels.toast_save_failed.replace("{{var0}}", BDFDB.LanguageUtils.LanguageStrings.IMAGE).replace("{{var1}}", path || "PC"), {type: "danger"});
}
else {
let extension = this.getFileExtension(new Uint8Array(buffer));
if (!extension) BDFDB.NotificationUtils.toast(this.labels.toast_save_failed.replace("{{var0}}", BDFDB.LanguageUtils.LanguageStrings.IMAGE).replace("{{var1}}", path || "PC"), {type: "danger"});
else {
let type = fileTypes[extension].video ? BDFDB.LanguageUtils.LanguageStrings.VIDEO : BDFDB.LanguageUtils.LanguageStrings.IMAGE;
if (path) BDFDB.LibraryRequires.fs.writeFile(this.getFileName(path, (alternativeName || url.split("/").pop().split(".").slice(0, -1).join(".") || "unknown").slice(0, 35), extension, 0), Buffer.from(buffer), error => {
if (error) BDFDB.NotificationUtils.toast(this.labels.toast_save_failed.replace("{{var0}}", type).replace("{{var1}}", path), {type: "danger"});
else BDFDB.NotificationUtils.toast(this.labels.toast_save_success.replace("{{var0}}", type).replace("{{var1}}", path), {type: "success"});
});
else {
let hrefURL = window.URL.createObjectURL(new Blob([buffer], {type: this.getMimeType(extension)}));
let tempLink = document.createElement("a");
tempLink.href = hrefURL;
tempLink.download = `${(alternativeName || url.split("/").pop().split(".").slice(0, -1).join(".") || "unknown").slice(0, 35)}.${extension}`;
tempLink.click();
window.URL.revokeObjectURL(hrefURL);
}
}
}
});
}
copyFile (url) {
let type = this.isValid(url, "video") ? BDFDB.LanguageUtils.LanguageStrings.VIDEO : BDFDB.LanguageUtils.LanguageStrings.IMAGE;
BDFDB.LibraryModules.WindowUtils.copyImage(url);
BDFDB.NotificationUtils.toast(this.labels.toast_copy_success.replace("{{var0}}", type), {type: "success"});
}
getDownloadLocation () {
if (downloadsFolder && BDFDB.LibraryRequires.fs.existsSync(downloadsFolder)) return downloadsFolder;
let homePath = BDFDB.LibraryRequires.process.env.USERPROFILE || BDFDB.LibraryRequires.process.env.HOMEPATH || BDFDB.LibraryRequires.process.env.HOME;
let downloadPath = homePath && BDFDB.LibraryRequires.path.join(homePath, "Downloads");
if (downloadPath && BDFDB.LibraryRequires.fs.existsSync(downloadPath)) return downloadsFolder = downloadPath;
else {
downloadsFolder = BDFDB.LibraryRequires.path.join(BDFDB.BDUtils.getPluginsFolder(), "downloads");
if (!BDFDB.LibraryRequires.fs.existsSync(downloadsFolder)) BDFDB.LibraryRequires.fs.mkdirSync(downloadsFolder);
return downloadsFolder;
}
}
getFileName (path, fileName, extension, i) {
fileName = fileName.split("?")[0];
let wholePath = BDFDB.LibraryRequires.path.join(path, i ? `${fileName} (${i}).${extension}` : `${fileName}.${extension}`);
if (BDFDB.LibraryRequires.fs.existsSync(wholePath)) return this.getFileName(path, fileName, extension, i + 1);
else return wholePath;
}
getFileExtension (intArray) {
for (let fileType in fileTypes) if (fileTypes[fileType].signs.some(signs => signs.every((hex, i) => hex === null || hex == intArray[i]))) return fileType;
return "";
}
getMimeType (fileType) {
if (fileTypes[fileType]) return `${fileTypes[fileType].video ? "video" : "image"}/${fileType == "svg" ? "svg+xml" : fileType}`;
return "";
}
getImageSrc (img) {
if (!img) return null;
return (typeof img == "string" ? img : (img.proxy_url || img.src || (typeof img.querySelector == "function" && img.querySelector("canvas") ? img.querySelector("canvas").src : ""))).split("?width=")[0];
}
getImageIndex (messages, img) {
return messages.findIndex(i => i.messageId == img.messageId && (messages.filter(n => n.messageId == i.messageId).length < 2 || i.url && img.proxy_url.indexOf(i.url) > -1 || i.proxy_url && img.proxy_url.indexOf(i.proxy_url) > -1));
}
filterMessagesForImages (messages, img) {
return messages.filter(m => m && m.channel_id == img.channelId && !BDFDB.LibraryStores.RelationshipStore.isBlocked(m.author.id) && (firstViewedImage && m.id == firstViewedImage.messageId || m.id == img.messageId || m.embeds.filter(e => e.image || e.thumbnail || e.video).length || m.attachments.filter(a => !a.filename.startsWith("SPOILER_")).length)).map(m => [m.attachments, m.embeds].flat(10).filter(n => n).map(i => Object.assign({messageId: m.id, channelId: img.channelId}, i, i.image, i.thumbnail, i.video))).flat(10).filter(n => {
if (!n) return false;
if (!n.content_type || img.proxy_url == n.proxy_url || img.proxy_url == n.url || img.proxy_url == n.href) return true;
let extension = (n.content_type.split("/")[1] || "").split("+")[0] || "";
if (extension && this.settings.galleryFilter[extension] === false) return false;
return true;
});
}
switchImages (offset) {
const newIndex = parseInt(cachedImages.index) + parseInt(offset);
if (newIndex < 0 || newIndex > (cachedImages.amount - 1)) return;
cachedImages.index = newIndex;
const oldImage = viewedImage;
viewedImage = cachedImages.all[cachedImages.index];
if (offset > 0 && !cachedImages.lastReached && cachedImages.index == (cachedImages.amount - 1)) {
let channel = BDFDB.LibraryStores.ChannelStore.getChannel(viewedImage.channelId);
BDFDB.LibraryModules.APIUtils.get({
url: BDFDB.DiscordConstants.Endpoints.MESSAGES(channel.id),
query: BDFDB.LibraryModules.APIEncodeUtils.stringify({
channel_id: channel && channel.guild_id ? (BDFDB.ChannelUtils.isThread(channel) && channel.parent_id || channel.id) : null,
has: "image",
include_nsfw: true,
limit: 100,
after: (BigInt(cachedImages.newestId) - BigInt(1)).toString()
})
}).then(result => {
if (result && viewedImage) {
const messages = result.body.flat(10).reverse();
Object.assign(cachedImages, {all: this.filterForCopies([].concat(cachedImages.all, this.filterMessagesForImages(messages, viewedImage)))});
const index = this.getImageIndex(cachedImages.all, viewedImage);
cachedImages = Object.assign(cachedImages, {
channelId: viewedImage.channelId,
index: index,
amount: cachedImages.all.length,
newestId: messages[messages.length-1] ? messages[messages.length-1].id : null,
lastReached: index == (cachedImages.all.length - 1)
});
this.updateImageModal();
}
});
}
if (offset < 0 && !cachedImages.firstReached && cachedImages.index == 0) {
let channel = BDFDB.LibraryStores.ChannelStore.getChannel(viewedImage.channelId);
BDFDB.LibraryModules.APIUtils.get({
url: BDFDB.DiscordConstants.Endpoints.MESSAGES(channel.id),
query: BDFDB.LibraryModules.APIEncodeUtils.stringify({
channel_id: channel && channel.guild_id ? (BDFDB.ChannelUtils.isThread(channel) && channel.parent_id || channel.id) : null,
has: "image",
include_nsfw: true,
limit: 100,
before: (BigInt(cachedImages.oldestId) + BigInt(1)).toString()
})
}).then(result => {
if (result && viewedImage) {
const messages = result.body.flat(10).reverse();
Object.assign(cachedImages, {all: this.filterForCopies([].concat(this.filterMessagesForImages(messages, viewedImage), cachedImages.all))});
const index = this.getImageIndex(cachedImages.all, viewedImage);
cachedImages = Object.assign(cachedImages, {
channelId: viewedImage.channelId,
firstReached: index == 0,
oldestId: messages[0] ? messages[0].id : null,
index: index,
amount: cachedImages.all.length
});
this.updateImageModal();
}
});
}
let isVideo = this.isValid(viewedImage.proxy_url, "video");
let thisViewedImage = viewedImage;
switchedImageProps = {
animated: !!isVideo,
original: thisViewedImage.proxy_url,
placeholder: isVideo && (thisViewedImage.thumbnail && thisViewedImage.thumbnail.proxy_url || thisViewedImage.proxy_url),
src: thisViewedImage.proxy_url,
width: thisViewedImage.width,
height: thisViewedImage.height,
children: !isVideo ? null : (videoData => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Video, {
ignoreMaxSize: true,
poster: thisViewedImage.proxy_url.replace("https://cdn.discordapp.com", "https://media.discordapp.net").split("?size=")[0] + "?format=jpeg",
src: thisViewedImage.proxy_url,
width: videoData.size.width,
height: videoData.size.height,
naturalWidth: thisViewedImage.width,
naturalHeight: thisViewedImage.height,
play: true,
playOnHover: !!BDFDB.LibraryStores.AccessibilityStore.useReducedMotion
}))
};
this.updateImageModal();
}
updateImageModal () {
BDFDB.ReactUtils.forceUpdate(BDFDB.ReactUtils.findOwner(document.querySelector(BDFDB.dotCN.imagemodal), {up: true, filter: n => n && n.stateNode && n.stateNode.props && n.stateNode.props.isTopModal && n.stateNode.props.modalKey}));
}
filterForCopies (messages) {
let filtered = [];
for (let message of messages) if (!filtered.find(n => n.messageId == message.messageId)) filtered.push(message);
return filtered;
}
addListener (eventType, type, callback) {
if (!type || !eventType || typeof callback != "function") return;
if (!eventTypes[type]) eventTypes[type] = [];
if (!eventTypes[type].includes(eventType)) eventTypes[type].push(eventType);
document.removeEventListener(eventType, document[`${eventType}${this.name}${type}Listener`]);
delete document[`${eventType}${this.name}${type}Listener`];
document[`${eventType}${this.name}${type}Listener`] = callback;
document.addEventListener(eventType, document[`${eventType}${this.name}${type}Listener`]);
}
cleanupListeners (type) {
if (!type || !eventTypes[type]) return;
for (let eventType of eventTypes[type]) {
document.removeEventListener(eventType, document[`${eventType}${this.name}${type}Listener`]);
delete document[`${eventType}${this.name}${type}Listener`];
}
}
setLabelsByLanguage () {
switch (BDFDB.LanguageUtils.getLanguage().id) {
case "bg": // Bulgarian
return {
context_copy: "Копирайте {{var0}}",
context_imageactions: "Действия с изображения",
context_lenssize: "Размер на обектива",
context_saveas: "Запазете {{var0}} като ...",
context_searchwith: "Търсете {{var0}} с ...",
context_videoactions: "Видео действия",
context_view: "Преглед {{var0}}",
submenu_disabled: "Всички инвалиди",
toast_copy_failed: "{{var0}} не можа да бъде копиран в клипборда",
toast_copy_success: "{{var0}} беше копиран в клипборда",
toast_save_failed: "{{var0}} не можа да бъде запазен в '{{var1}}'",
toast_save_success: "{{var0}} бе запазено в '{{var1}}'"
};
case "cs": // Czech
return {
context_copy: "Zkopírovat {{var0}}",
context_imageactions: "Akce s obrázky",
context_lenssize: "Velikost lupy",
context_saveas: "Uložit {{var0}} jako...",
context_searchwith: "Hledat {{var0}} pomocí...",
context_videoactions: "Video akce",
context_view: "Zobrazit {{var0}}",
submenu_disabled: "Vše zakázáno",
toast_copy_failed: "{{var0}} nemohl být zkopírován do schránky",
toast_copy_success: "{{var0}} byl zkopírován do schránky",
toast_save_failed: "{{var0}} nemohl být uložen do '{{var1}}'",
toast_save_success: "{{var0}} bylo uložen do '{{var1}}'"
};
case "da": // Danish
return {
context_copy: "Kopiér {{var0}}",
context_imageactions: "Billedhandlinger",
context_lenssize: "Objektivstørrelse",
context_saveas: "Gem {{var0}} som ...",
context_searchwith: "Søg i {{var0}} med ...",
context_videoactions: "Videohandlinger",
context_view: "Se {{var0}}",
submenu_disabled: "Alle handicappede",
toast_copy_failed: "{{var0}} kunne ikke kopieres til udklipsholderen",
toast_copy_success: "{{var0}} blev kopieret til udklipsholderen",
toast_save_failed: "{{var0}} kunne ikke gemmes i '{{var1}}'",
toast_save_success: "{{var0}} blev gemt i '{{var1}}'"
};
case "de": // German
return {
context_copy: "{{var0}} kopieren",
context_imageactions: "Bildaktionen",
context_lenssize: "Linsengröße",
context_saveas: "{{var0}} speichern als ...",
context_searchwith: "{{var0}} suchen mit ...",
context_videoactions: "Videoaktionen",
context_view: "{{var0}} ansehen",
submenu_disabled: "Alle deaktiviert",
toast_copy_failed: "{{var0}} konnte nicht in die Zwischenablage kopiert werden",
toast_copy_success: "{{var0}} wurde in die Zwischenablage kopiert",
toast_save_failed: "{{var0}} konnte nicht in '{{var1}}' gespeichert werden",
toast_save_success: "{{var0}} wurde in '{{var1}}' gespeichert"
};
case "el": // Greek
return {
context_copy: "Αντιγραφή {{var0}}",
context_imageactions: "Ενέργειες εικόνας",
context_lenssize: "Μέγεθος φακού",
context_saveas: "Αποθήκευση {{var0}} ως ...",
context_searchwith: "Αναζήτηση {{var0}} με ...",
context_videoactions: "Ενέργειες βίντεο",
context_view: "Προβολή {{var0}}",
submenu_disabled: "Όλα τα άτομα με ειδικές ανάγκες",
toast_copy_failed: "Δεν ήταν δυνατή η αντιγραφή του {{var0}} στο πρόχειρο",
toast_copy_success: "Το {{var0}} αντιγράφηκε στο πρόχειρο",
toast_save_failed: "Δεν ήταν δυνατή η αποθήκευση του {{var0}} στο '{{var1}}'",
toast_save_success: "Το {{var0}} αποθηκεύτηκε στο '{{var1}}'"
};
case "es": // Spanish
return {
context_copy: "Copiar {{var0}}",
context_imageactions: "Acciones de imagen",
context_lenssize: "Tamaño de la lente",
context_saveas: "Guardar {{var0}} como ...",
context_searchwith: "Buscar {{var0}} con ...",
context_videoactions: "Acciones de vídeo",
context_view: "Ver {{var0}}",
submenu_disabled: "Todos discapacitados",
toast_copy_failed: "{{var0}} no se pudo copiar al portapapeles",
toast_copy_success: "{{var0}} se copió en el portapapeles",
toast_save_failed: "{{var0}} no se pudo guardar en '{{var1}}'",
toast_save_success: "{{var0}} se guardó en '{{var1}}'"
};
case "fi": // Finnish
return {
context_copy: "Kopioi {{var0}}",
context_imageactions: "Kuvatoiminnot",
context_lenssize: "Linssin koko",
context_saveas: "Tallenna {{var0}} nimellä ...",
context_searchwith: "Tee haku {{var0}} ...",
context_videoactions: "Videotoiminnot",
context_view: "Näytä {{var0}}",
submenu_disabled: "Kaikki vammaiset",
toast_copy_failed: "Kohdetta {{var0}} ei voitu kopioida leikepöydälle",
toast_copy_success: "{{var0}} kopioitiin leikepöydälle",
toast_save_failed: "Kohdetta {{var0}} ei voitu tallentaa kansioon '{{var1}}'",
toast_save_success: "{{var0}} tallennettiin kansioon '{{var1}}'"
};
case "fr": // French
return {
context_copy: "Copier {{var0}}",
context_imageactions: "Actions sur les images",
context_lenssize: "Taille de l'objectif",
context_saveas: "Enregistrer {{var0}} sous ...",
context_searchwith: "Rechercher {{var0}} avec ...",
context_videoactions: "Actions vidéo",
context_view: "Afficher {{var0}}",
submenu_disabled: "Tout désactivé",
toast_copy_failed: "{{var0}} n'a pas pu être copié dans le presse-papiers",
toast_copy_success: "{{var0}} a été copié dans le presse-papiers",
toast_save_failed: "{{var0}} n'a pas pu être enregistré dans '{{var1}}'",
toast_save_success: "{{var0}} a été enregistré dans '{{var1}}'"
};
case "hi": // Hindi
return {
context_copy: "कॉपी {{var0}}",
context_imageactions: "छवि क्रियाएँ",
context_lenssize: "लेंस का आकार",
context_saveas: "{{var0}} को इस रूप में सेव करें...",
context_searchwith: "इसके साथ {{var0}} खोजें ...",
context_videoactions: "वीडियो क्रिया",
context_view: "देखें {{var0}}",
submenu_disabled: "सभी अक्षम",
toast_copy_failed: "{{var0}} को क्लिपबोर्ड पर कॉपी नहीं किया जा सका",
toast_copy_success: "{{var0}} को क्लिपबोर्ड पर कॉपी किया गया था",
toast_save_failed: "{{var0}} '{{var1}}' में सहेजा नहीं जा सका",
toast_save_success: "{{var0}} '{{var1}}' में सहेजा गया था"
};
case "hr": // Croatian
return {
context_copy: "Kopiraj {{var0}}",
context_imageactions: "Radnje slike",
context_lenssize: "Veličina leće",
context_saveas: "Spremi {{var0}} kao ...",
context_searchwith: "Traži {{var0}} sa ...",
context_videoactions: "Video radnje",
context_view: "Pogledajte {{var0}}",
submenu_disabled: "Svi invalidi",
toast_copy_failed: "{{var0}} nije moguće kopirati u međuspremnik",
toast_copy_success: "{{var0}} je kopirano u međuspremnik",
toast_save_failed: "{{var0}} nije moguće spremiti u '{{var1}}'",
toast_save_success: "{{var0}} spremljeno je u '{{var1}}'"
};
case "hu": // Hungarian
return {
context_copy: "{{var0}} másolása",
context_imageactions: "Képműveletek",
context_lenssize: "Lencse mérete",
context_saveas: "{{var0}} mentése másként ...",
context_searchwith: "Keresés a következőben: {{var0}} a következővel:",
context_videoactions: "Videóműveletek",
context_view: "Megtekintés: {{var0}}",
submenu_disabled: "Minden fogyatékkal él",
toast_copy_failed: "A {{var0}} fájl nem másolható a vágólapra",
toast_copy_success: "A {{var0}} elemet a vágólapra másolta",
toast_save_failed: "A {{var0}} fájl mentése nem sikerült a '{{var1}}' mappába",
toast_save_success: "{{var0}} mentve a '{{var1}}' mappába"
};
case "it": // Italian
return {
context_copy: "Copia {{var0}}",
context_imageactions: "Azioni immagine",
context_lenssize: "Dimensione della lente",
context_saveas: "Salva {{var0}} come ...",
context_searchwith: "Cerca {{var0}} con ...",
context_videoactions: "Azioni video",
context_view: "Visualizza {{var0}}",
submenu_disabled: "Tutti disabilitati",
toast_copy_failed: "{{var0}} non può essere copiato negli appunti",
toast_copy_success: "{{var0}} è stato copiato negli appunti",
toast_save_failed: "Impossibile salvare {{var0}} in '{{var1}}'",
toast_save_success: "{{var0}} è stato salvato in '{{var1}}'"
};
case "ja": // Japanese
return {
context_copy: "{{var0}} をコピーします",
context_imageactions: "画像アクション",
context_lenssize: "レンズサイズ",
context_saveas: "{{var0}} を...として保存します",
context_searchwith: "{{var0}} を...で検索",
context_videoactions: "ビデオ アクション",
context_view: "{{var0}} を表示",
submenu_disabled: "すべて無効",
toast_copy_failed: "{{var0}} をクリップボードにコピーできませんでした",
toast_copy_success: "{{var0}} がクリップボードにコピーされました",
toast_save_failed: "{{var0}} を「'{{var1}}'」に保存できませんでした",
toast_save_success: "{{var0}} は「'{{var1}}'」に保存されました"
};
case "ko": // Korean
return {
context_copy: "{{var0}} 복사",
context_imageactions: "이미지 작업",
context_lenssize: "렌즈 크기",
context_saveas: "{{var0}} 을 다른 이름으로 저장 ...",
context_searchwith: "{{var0}} 검색 ...",
context_videoactions: "비디오 작업",
context_view: "{{var0}} 보기",
submenu_disabled: "모두 비활성화 됨",
toast_copy_failed: "{{var0}} 을 클립 보드에 복사 할 수 없습니다.",
toast_copy_success: "{{var0}} 이 클립 보드에 복사되었습니다.",
toast_save_failed: "{{var0}} 을 '{{var1}}'에 저장할 수 없습니다.",
toast_save_success: "{{var0}} 이 '{{var1}}'에 저장되었습니다."
};
case "lt": // Lithuanian
return {
context_copy: "Kopijuoti {{var0}}",
context_imageactions: "Vaizdo veiksmai",
context_lenssize: "Objektyvo dydis",
context_saveas: "Išsaugoti '{{var0}}' kaip ...",
context_searchwith: "Ieškoti {{var0}} naudojant ...",
context_videoactions: "Vaizdo įrašų veiksmai",
context_view: "Žiūrėti {{var0}}",
submenu_disabled: "Visi neįgalūs",
toast_copy_failed: "{{var0}} nepavyko nukopijuoti į mainų sritį",
toast_copy_success: "{{var0}} buvo nukopijuota į mainų sritį",
toast_save_failed: "Nepavyko išsaugoti {{var0}} aplanke '{{var1}}'",
toast_save_success: "{{var0}} išsaugotas aplanke '{{var1}}'"
};
case "nl": // Dutch
return {
context_copy: "Kopieer {{var0}}",
context_imageactions: "Afbeeldingsacties",
context_lenssize: "Lens Maat",
context_saveas: "Bewaar {{var0}} als ...",
context_searchwith: "Zoek {{var0}} met ...",
context_videoactions: "Video-acties",
context_view: "Bekijk {{var0}}",
submenu_disabled: "Allemaal uitgeschakeld",
toast_copy_failed: "{{var0}} kan niet naar het klembord worden gekopieerd",
toast_copy_success: "{{var0}} is naar het klembord gekopieerd",
toast_save_failed: "{{var0}} kan niet worden opgeslagen in '{{var1}}'",
toast_save_success: "{{var0}} is opgeslagen in '{{var1}}'"
};
case "no": // Norwegian
return {
context_copy: "Kopier {{var0}}",
context_imageactions: "Bildehandlinger",
context_lenssize: "Linsestørrelse",
context_saveas: "Lagre {{var0}} som ...",
context_searchwith: "Søk på {{var0}} med ...",
context_videoactions: "Videohandlinger",
context_view: "Vis {{var0}}",
submenu_disabled: "Alle funksjonshemmede",
toast_copy_failed: "{{var0}} kunne ikke kopieres til utklippstavlen",
toast_copy_success: "{{var0}} ble kopiert til utklippstavlen",
toast_save_failed: "{{var0}} kunne ikke lagres i '{{var1}}'",
toast_save_success: "{{var0}} ble lagret i '{{var1}}'"
};
case "pl": // Polish
return {
context_copy: "Kopiuj {{var0}}",
context_imageactions: "Działania związane z obrazem",
context_lenssize: "Rozmiar soczewki",
context_saveas: "Zapisz {{var0}} jako ...",
context_searchwith: "Wyszukaj {{var0}} za pomocą ...",
context_videoactions: "Akcje wideo",
context_view: "Wyświetl {{var0}}",
submenu_disabled: "Wszystkie wyłączone",
toast_copy_failed: "Nie można skopiować {{var0}} do schowka",
toast_copy_success: "{{var0}} został skopiowany do schowka",
toast_save_failed: "Nie można zapisać {{var0}} w '{{var1}}'",
toast_save_success: "{{var0}} został zapisany w '{{var1}}'"
};
case "pt-BR": // Portuguese (Brazil)
return {
context_copy: "Copiar {{var0}}",
context_imageactions: "Ações de imagem",
context_lenssize: "Tamanho da lente",
context_saveas: "Salvar {{var0}} como ...",
context_searchwith: "Pesquisar {{var0}} com ...",
context_videoactions: "Ações de vídeo",
context_view: "Visualizar {{var0}}",
submenu_disabled: "Todos desativados",
toast_copy_failed: "{{var0}} não pôde ser copiado para a área de transferência",
toast_copy_success: "{{var0}} foi copiado para a área de transferência",
toast_save_failed: "{{var0}} não pôde ser salvo em '{{var1}}'",
toast_save_success: "{{var0}} foi salvo em '{{var1}}'"
};
case "ro": // Romanian
return {
context_copy: "Copiați {{var0}}",
context_imageactions: "Acțiuni de imagine",
context_lenssize: "Dimensiunea obiectivului",
context_saveas: "Salvați {{var0}} ca ...",
context_searchwith: "Căutați {{var0}} cu ...",
context_videoactions: "Acțiuni video",
context_view: "Vizualizați {{var0}}",
submenu_disabled: "Toate sunt dezactivate",
toast_copy_failed: "{{var0}} nu a putut fi copiat în clipboard",
toast_copy_success: "{{var0}} a fost copiat în clipboard",
toast_save_failed: "{{var0}} nu a putut fi salvat în '{{var1}}'",
toast_save_success: "{{var0}} a fost salvat în '{{var1}}'"
};
case "ru": // Russian
return {
context_copy: "Скопируйте {{var0}}",
context_imageactions: "Действия с изображением",
context_lenssize: "Размер линзы",
context_saveas: "Сохранить {{var0}} как ...",
context_searchwith: "Искать {{var0}} с помощью ...",
context_videoactions: "Действия с видео",
context_view: "Посмотреть {{var0}}",
submenu_disabled: "Все отключены",
toast_copy_failed: "{{var0}} не удалось скопировать в буфер обмена",
toast_copy_success: "{{var0}} скопирован в буфер обмена",
toast_save_failed: "{{var0}} не удалось сохранить в '{{var1}}'",
toast_save_success: "{{var0}} был сохранен в '{{var1}}'"
};
case "sv": // Swedish
return {
context_copy: "Kopiera {{var0}}",
context_imageactions: "Bildåtgärder",
context_lenssize: "Linsstorlek",
context_saveas: "Spara {{var0}} som ...",
context_searchwith: "Sök {{var0}} med ...",
context_videoactions: "Videoåtgärder",
context_view: "Visa {{var0}}",
submenu_disabled: "Alla funktionshindrade",
toast_copy_failed: "{{var0}} kunde inte kopieras till Urklipp",
toast_copy_success: "{{var0}} kopierades till Urklipp",
toast_save_failed: "{{var0}} kunde inte sparas i '{{var1}}'",
toast_save_success: "{{var0}} sparades i '{{var1}}'"
};
case "th": // Thai
return {
context_copy: "คัดลอก{{var0}}",
context_imageactions: "การทำงานของรูปภาพ",
context_lenssize: "ขนาดเลนส์",
context_saveas: "บันทึก{{var0}}เป็น ...",
context_searchwith: "ค้นหา{{var0}} ้วย ...",
context_videoactions: "การกระทำของวิดีโอ",
context_view: "ดู{{var0}}",
submenu_disabled: "ปิดใช้งานทั้งหมด",
toast_copy_failed: "ไม่สามารถคัดลอก{{var0}}ไปยังคลิปบอร์ดได้",
toast_copy_success: "คัดลอก{{var0}}ไปยังคลิปบอร์ดแล้ว",
toast_save_failed: "ไม่สามารถบันทึก{{var0}}ใน '{{var1}}'",
toast_save_success: "{{var0}} ูกบันทึกใน '{{var1}}'"
};
case "tr": // Turkish
return {
context_copy: "{{var0}} kopyala",
context_imageactions: "Görüntü Eylemleri",
context_lenssize: "Lens Boyutu",
context_saveas: "{{var0}} farklı kaydet ...",
context_searchwith: "{{var0}} şununla ara ...",
context_videoactions: "Video Eylemleri",
context_view: "{{var0}} görüntüle",
submenu_disabled: "Hepsi devre dışı",
toast_copy_failed: "{{var0}} panoya kopyalanamadı",
toast_copy_success: "{{var0}} panoya kopyalandı",
toast_save_failed: "{{var0}}, '{{var1}}' konumuna kaydedilemedi",
toast_save_success: "{{var0}}, '{{var1}}' konumuna kaydedildi"
};
case "uk": // Ukrainian
return {
context_copy: "Копіювати {{var0}}",
context_imageactions: "Дії із зображеннями",
context_lenssize: "Розмір лінзи",
context_saveas: "Збережіть {{var0}} як ...",
context_searchwith: "Шукати {{var0}} за допомогою ...",
context_videoactions: "Відео дії",
context_view: "Переглянути {{var0}}",
submenu_disabled: "Всі інваліди",
toast_copy_failed: "Не вдалося скопіювати {{var0}} у буфер обміну",
toast_copy_success: "{{var0}} скопійовано в буфер обміну",
toast_save_failed: "Не вдалося зберегти {{var0}} у '{{var1}}'",
toast_save_success: "{{var0}} було збережено у '{{var1}}'"
};
case "vi": // Vietnamese
return {
context_copy: "Sao chép {{var0}}",
context_imageactions: "Hành động hình ảnh",
context_lenssize: "Kích thước ống kính",
context_saveas: "Lưu {{var0}} dưới dạng ...",
context_searchwith: "Tìm kiếm {{var0}} bằng ...",
context_videoactions: "Hành động video",
context_view: "Xem {{var0}}",
submenu_disabled: "Tất cả đã bị vô hiệu hóa",
toast_copy_failed: "Không thể sao chép {{var0}} vào khay nhớ tạm",
toast_copy_success: "{{var0}} đã được sao chép vào khay nhớ tạm",
toast_save_failed: "Không thể lưu {{var0}} trong '{{var1}}'",
toast_save_success: "{{var0}} đã được lưu trong '{{var1}}'"
};
case "zh-CN": // Chinese (China)
return {
context_copy: "复制 {{var0}}",
context_imageactions: "图像动作",
context_lenssize: "缩放尺寸",
context_saveas: "将 {{var0}} 另存到...",
context_searchwith: "搜索 {{var0}} 使用...",
context_videoactions: "视频动作",
context_view: "查看 {{var0}}",
submenu_disabled: "全部禁用",
toast_copy_failed: "{{var0}} 无法复制到剪贴板",
toast_copy_success: "{{var0}} 已复制到剪贴板",
toast_save_failed: "{{var0}} 无法保存到'{{var1}}'",
toast_save_success: "{{var0}} 已保存到'{{var1}}'"
};
case "zh-TW": // Chinese (Taiwan)
return {
context_copy: "複製 {{var0}}",
context_imageactions: "圖像動作",
context_lenssize: "縮放尺寸",
context_saveas: "將 {{var0}} 另存到...",
context_searchwith: "搜尋 {{var0}} 使用...",
context_videoactions: "視頻動作",
context_view: "預覽 {{var0}}",
submenu_disabled: "全部關閉",
toast_copy_failed: "{{var0}} 無法複製到剪貼簿",
toast_copy_success: "{{var0}} 已複製到剪貼簿",
toast_save_failed: "{{var0}} 無法儲存到 '{{var1}}'",
toast_save_success: "{{var0}} 已儲存到 '{{var1}}'"
};
default: // English
return {
context_copy: "Copy {{var0}}",
context_imageactions: "Image Actions",
context_lenssize: "Lens Size",
context_saveas: "Save {{var0}} as ...",
context_searchwith: "Search {{var0}} with ...",
context_videoactions: "Video Actions",
context_view: "View {{var0}}",
submenu_disabled: "All disabled",
toast_copy_failed: "{{var0}} could not be copied to the Clipboard",
toast_copy_success: "{{var0}} was copied to the Clipboard",
toast_save_failed: "{{var0}} could not be saved in '{{var1}}'",
toast_save_success: "{{var0}} was saved in '{{var1}}'"
};
}
}
};
})(window.BDFDB_Global.PluginUtils.buildPlugin(changeLog));
})();