Drag-Drop Image Uploader

Enables image uploading by simply dragging and dropping images onto a fixed div in the bottom right corner of the page. Easily upload and use images on any website with this convenient script.

  1. // ==UserScript==
  2. // @name Drag-Drop Image Uploader
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2.1
  5. // @description Enables image uploading by simply dragging and dropping images onto a fixed div in the bottom right corner of the page. Easily upload and use images on any website with this convenient script.
  6. // @match https://*/*
  7. // @grant none
  8. // @license MIT License
  9. // @author CY Fung
  10. // @icon https://raw.githubusercontent.com/cyfung1031/userscript-supports/main/icons/drag-drop-image-uploader.svg
  11. // @run-at document-start
  12. // ==/UserScript==
  13.  
  14. (function () {
  15. 'use strict';
  16.  
  17. if (location.hostname === 'lihkg.com') {
  18.  
  19. if (location.pathname !== '/robots.txt') return;
  20. if (window.name !== 'pNwjxjQi') return;
  21. if (window === top) return;
  22.  
  23.  
  24. function T(e) {
  25. return new Promise(function (resolve, reject) {
  26. window.history.replaceState(null, '', 'https://lihkg.com');
  27. let formData = new FormData();
  28. formData.append('image', e);
  29.  
  30. fetch('https://api.na.cx/upload', {
  31. method: 'POST',
  32. body: formData,
  33.  
  34. "mode": "cors",
  35. "credentials": "omit",
  36.  
  37. referrerPolicy: "no-referrer",
  38. cache: "default", // no effect on POST request
  39. // cache: "force-cache",
  40. redirect: "error", // there shall be no redirection in this API request
  41. integrity: "",
  42. keepalive: false,
  43.  
  44. "headers": {
  45. "Accept-Encoding": "gzip, deflate, br",
  46. "Accept": "application/json",
  47. },
  48.  
  49. })
  50. .then(function (response) {
  51. if (response.ok) {
  52. return response.json();
  53. } else {
  54. throw new Error('Status is not 200');
  55. }
  56. })
  57. .then(function (response) {
  58. let status = response.status;
  59. let url = response.url;
  60. let error = response.error;
  61.  
  62. if (status === 200) {
  63. resolve(url);
  64. } else {
  65. reject(new Error(error));
  66. }
  67. })
  68. .catch(function (error) {
  69. reject(error);
  70. });
  71. });
  72. }
  73.  
  74.  
  75. let iframe = document;
  76.  
  77. // Function to handle the dragenter event
  78. function handleDragEnter(e) {
  79. top.postMessage('pNwjxjQi-top-dragenter', '*');
  80. // Add a class to visually indicate the drag over the iframe
  81. //iframe.classList.add("drag-over");
  82. }
  83.  
  84. // Function to handle the dragover event
  85. function handleDragOver(e) {
  86. // top.postMessage('pNwjxjQi-top-dragover','*');
  87. e.preventDefault();
  88. e.dataTransfer.dropEffect = 'copy';
  89. }
  90.  
  91. // Function to handle the dragleave event
  92. function handleDragLeave(e) {
  93. top.postMessage('pNwjxjQi-top-dragleave', '*');
  94. // Remove the class when the drag leaves the iframe
  95. //iframe.classList.remove("drag-over");
  96. }
  97.  
  98. async function goUpload(e) {
  99.  
  100. let files = e.dataTransfer.files;
  101. let images = [...files].filter(file => file.type == "image/png" || file.type == "image/jpg" || file.type == "image/jpeg" || file.type == "image/gif")
  102. // console.log(images);
  103.  
  104. for (const image of images) {
  105. await T(image)
  106. .then(function (url) {
  107. // focusElement.focus();
  108. // document.execCommand("insertText", false, url)
  109. top.postMessage({ p: 'pNwjxjQi-top-finish-upload', url: url }, '*')
  110. console.log('Uploaded image URL:', url);
  111. })
  112. .catch(function (error) {
  113. console.error('Upload failed:', error);
  114. });
  115. }
  116.  
  117. }
  118.  
  119. // Function to handle the drop event
  120. function handleDrop(e) {
  121. e.preventDefault();
  122. top.postMessage('pNwjxjQi-top-drop', '*');
  123.  
  124. // Remove the class when the drop occurs
  125. //iframe.classList.remove("drag-over");
  126.  
  127. // Access the dropped files or data
  128.  
  129. goUpload(e);
  130.  
  131. // Process the dropped files or data as needed
  132. // ...
  133. }
  134. // Add event listeners for drag and drop events
  135. iframe.addEventListener("dragenter", handleDragEnter, false);
  136. iframe.addEventListener("dragover", handleDragOver, false);
  137. iframe.addEventListener("dragleave", handleDragLeave, false);
  138. iframe.addEventListener("drop", handleDrop, false);
  139.  
  140.  
  141. top.postMessage('pNwjxjQi-top-uploader-ready', '*');
  142.  
  143.  
  144. } else {
  145.  
  146. function onReady() {
  147.  
  148. let fixedDiv = null;
  149.  
  150. let focusElement = null;
  151.  
  152. let moused = false;
  153.  
  154. let lastDragIn = 0;
  155. let cid = 0;
  156.  
  157.  
  158. function createFixedDiv() {
  159.  
  160.  
  161. // Create the fixed div element
  162. let fixedDiv = document.createElement('div');
  163. fixedDiv.style.position = 'fixed';
  164. fixedDiv.style.zIndex = '8888';
  165. fixedDiv.style.bottom = '10px';
  166. fixedDiv.style.right = '10px';
  167. fixedDiv.style.width = '200px';
  168. fixedDiv.style.height = '200px';
  169. // fixedDiv.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
  170. fixedDiv.style.border = '2px solid rgb(244, 244, 244)';
  171. fixedDiv.style.outline = '2px solid rgb(20, 20, 20)';
  172. fixedDiv.style.borderRadius = '5px';
  173. fixedDiv.style.padding = '0px';
  174. fixedDiv.style.color = 'white';
  175. fixedDiv.style.fontSize = '14px';
  176. fixedDiv.style.textAlign = 'center';
  177. // fixedDiv.style.cursor = 'move';
  178. // fixedDiv.draggable = true;
  179. fixedDiv.style.opacity = '0'; // Set initial opacity to 0 (hidden)
  180. fixedDiv.style.display = 'none';
  181. fixedDiv.id = 'ewIMf5Dw';
  182.  
  183. fixedDiv.style.background = 'url(https://raw.githubusercontent.com/cyfung1031/userscript-supports/main/icons/drag-drop-image-uploader.svg)';
  184. fixedDiv.style.background = 'url(https://raw.githubusercontent.com/cyfung1031/userscript-supports/main/icons/drag-drop-image-uploader.svg), rgba(135,206,250, 0.2)';
  185.  
  186. fixedDiv.style.backgroundPosition = 'center';
  187. fixedDiv.style.backgroundSize = 'cover';
  188. fixedDiv.style.backgroundRepeat = 'no-repeat';
  189.  
  190. fixedDiv.style.pointerEvents = 'none';
  191.  
  192. // Append the div to the document body
  193. document.body.appendChild(fixedDiv);
  194.  
  195. // Create the Intersection Observer
  196. let observer = new IntersectionObserver(function (entries) {
  197. entries.forEach(function (entry) {
  198. if (entry.isIntersecting) {
  199. // When fixedDiv appears, check if it has an iframe inside
  200. let iframe = fixedDiv.querySelector('iframe');
  201. if (!iframe) {
  202. // If no iframe inside, create and append one
  203. iframe = document.createElement('iframe');
  204. setupIframe(iframe);
  205. iframe.src = 'https://lihkg.com/robots.txt';
  206. fixedDiv.appendChild(iframe);
  207. }
  208. }
  209. });
  210. });
  211.  
  212. // Observe the fixedDiv element
  213. observer.observe(fixedDiv);
  214.  
  215. return fixedDiv;
  216. }
  217. function setupIframe(iframe) {
  218. if (!fixedDiv) return;
  219. iframe.name = 'pNwjxjQi';
  220.  
  221. iframe.style.position = 'relative';
  222. iframe.style.width = '100%';
  223. iframe.style.height = '100%';
  224. iframe.style.opacity = '0';
  225. iframe.style.pointerEvents = 'all';
  226. iframe.style.transform = 'translateY(-300vh)';
  227. fixedDiv.style.transform = 'translateY(-300vh)';
  228.  
  229. }
  230.  
  231.  
  232.  
  233.  
  234. document.addEventListener('dragleave', function (event) {
  235. if (!fixedDiv) return;
  236. if (moused) return;
  237.  
  238. if (cid > 0) cid = clearTimeout(cid);
  239. if (event.relatedTarget) return;
  240. // console.log(221);
  241.  
  242. let endTime = Date.now();
  243. cid = setTimeout(() => {
  244. cid = 0;
  245. requestAnimationFrame(() => {
  246. if (lastDragIn > endTime) return;
  247.  
  248. if (fixedDiv.style.display !== 'none' && !moused) {
  249.  
  250. // focusElement = null;
  251. fixedDiv.style.display = 'none';
  252. fixedDiv.style.opacity = '0';
  253. }
  254. });
  255. }, 80)
  256.  
  257. event.preventDefault();
  258.  
  259. });
  260.  
  261. document.addEventListener('dragenter', function (event) {
  262. if (!fixedDiv) fixedDiv = createFixedDiv();
  263. if (moused) return;
  264. if (cid > 0) cid = clearTimeout(cid);
  265. if (event.relatedTarget) return;
  266. // console.log(222);
  267.  
  268.  
  269. lastDragIn = Date.now();
  270.  
  271. let activeNode = document.activeElement || 0;
  272. let activeNodeName = activeNode.nodeName;
  273. if (activeNodeName === 'TEXTAREA' || (activeNodeName === 'INPUT' && (!activeNode.type || activeNode.type == 'text'))) {
  274. if (fixedDiv.style.display === 'none') {
  275. fixedDiv.style.display = 'block';
  276. fixedDiv.style.opacity = '0.4';
  277. focusElement = activeNode;
  278. // console.log(focusElement)
  279. }
  280. }
  281.  
  282. requestAnimationFrame(() => {
  283.  
  284. lastDragIn = Date.now();
  285. });
  286. }, true);
  287.  
  288. document.addEventListener('drop', function (event) {
  289. if (!fixedDiv) return;
  290. moused = false;
  291. if (moused) return;
  292. if (cid > 0) cid = clearTimeout(cid);
  293. // console.log(223);
  294.  
  295. let endTime = Date.now();
  296. cid = setTimeout(() => {
  297. cid = 0;
  298. if (lastDragIn > endTime) return;
  299. if (fixedDiv.style.display !== 'none' && !moused) {
  300. // focusElement = null;
  301.  
  302. fixedDiv.style.display = 'none';
  303. fixedDiv.style.opacity = '0';
  304. }
  305. }, 80)
  306.  
  307.  
  308. }, true);
  309.  
  310.  
  311.  
  312. window.addEventListener('message', event => {
  313. if (!fixedDiv) return;
  314.  
  315. let data = (((event || 0).data || 0));
  316.  
  317.  
  318. if (data === 'pNwjxjQi-top-uploader-ready') {
  319.  
  320. let fixedDiv = document.querySelector('#ewIMf5Dw');
  321. let iframe = fixedDiv.querySelector('iframe');
  322.  
  323. iframe.style.transform = '';
  324. fixedDiv.style.transform = '';
  325.  
  326. }
  327.  
  328. if (data === 'pNwjxjQi-top-dragenter') {
  329. moused = true;
  330. fixedDiv.style.opacity = '1';
  331. }
  332. if (data === 'pNwjxjQi-top-dragleave') {
  333. moused = false;
  334. fixedDiv.style.opacity = '0.4';
  335. }
  336.  
  337. if (data === 'pNwjxjQi-top-dragenter') {
  338.  
  339. if (cid > 0) cid = clearTimeout(cid);
  340. }
  341.  
  342. if (data === 'pNwjxjQi-top-dragleave') {
  343.  
  344. let endTime = Date.now();
  345. if (cid > 0) cid = clearTimeout(cid);
  346. cid = setTimeout(() => {
  347. cid = 0;
  348. requestAnimationFrame(() => {
  349. if (lastDragIn > endTime) return;
  350.  
  351. if (fixedDiv.style.display !== 'none' && !moused) {
  352.  
  353. // focusElement = null;
  354. fixedDiv.style.display = 'none';
  355. fixedDiv.style.opacity = '0';
  356. }
  357. });
  358. }, 80)
  359.  
  360. }
  361.  
  362. if (data.p === 'pNwjxjQi-top-finish-upload') {
  363. let url = event.data.url;
  364. focusElement.focus();
  365. document.execCommand("insertText", false, url)
  366. }
  367.  
  368. if (data === 'pNwjxjQi-top-drop') {
  369. moused = false;
  370.  
  371. let endTime = Date.now();
  372. cid = setTimeout(() => {
  373. cid = 0;
  374. if (lastDragIn > endTime) return;
  375. if (fixedDiv.style.display !== 'none' && !moused) {
  376. // focusElement = null;
  377.  
  378. fixedDiv.style.display = 'none';
  379. fixedDiv.style.opacity = '0';
  380. }
  381. }, 80)
  382. }
  383.  
  384. })
  385.  
  386. }
  387. if (document.readyState !== 'loading') {
  388. onReady();
  389. } else {
  390. document.addEventListener('DOMContentLoaded', onReady, false);
  391. }
  392.  
  393.  
  394.  
  395. }
  396. })();