mirror of
				https://github.com/BookStackApp/BookStack.git
				synced 2025-10-31 03:50:27 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			164 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			164 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import Clipboard from "../services/clipboard";
 | |
| 
 | |
| let wrap;
 | |
| let draggedContentEditable;
 | |
| 
 | |
| function hasTextContent(node) {
 | |
|     return node && !!(node.textContent || node.innerText);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Handle pasting images from clipboard.
 | |
|  * @param {Editor} editor
 | |
|  * @param {WysiwygConfigOptions} options
 | |
|  * @param {ClipboardEvent|DragEvent} event
 | |
|  */
 | |
| function paste(editor, options, event) {
 | |
|     const clipboard = new Clipboard(event.clipboardData || event.dataTransfer);
 | |
| 
 | |
|     // Don't handle the event ourselves if no items exist of contains table-looking data
 | |
|     if (!clipboard.hasItems() || clipboard.containsTabularData()) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     const images = clipboard.getImages();
 | |
|     for (const imageFile of images) {
 | |
| 
 | |
|         const id = "image-" + Math.random().toString(16).slice(2);
 | |
|         const loadingImage = window.baseUrl('/loading.gif');
 | |
|         event.preventDefault();
 | |
| 
 | |
|         setTimeout(() => {
 | |
|             editor.insertContent(`<p><img src="${loadingImage}" id="${id}"></p>`);
 | |
| 
 | |
|             uploadImageFile(imageFile, options.pageId).then(resp => {
 | |
|                 const safeName = resp.name.replace(/"/g, '');
 | |
|                 const newImageHtml = `<img src="${resp.thumbs.display}" alt="${safeName}" />`;
 | |
| 
 | |
|                 const newEl = editor.dom.create('a', {
 | |
|                     target: '_blank',
 | |
|                     href: resp.url,
 | |
|                 }, newImageHtml);
 | |
| 
 | |
|                 editor.dom.replace(newEl, id);
 | |
|             }).catch(err => {
 | |
|                 editor.dom.remove(id);
 | |
|                 window.$events.emit('error', options.translations.imageUploadErrorText);
 | |
|                 console.log(err);
 | |
|             });
 | |
|         }, 10);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Upload an image file to the server
 | |
|  * @param {File} file
 | |
|  * @param {int} pageId
 | |
|  */
 | |
| async function uploadImageFile(file, pageId) {
 | |
|     if (file === null || file.type.indexOf('image') !== 0) {
 | |
|         throw new Error(`Not an image file`);
 | |
|     }
 | |
| 
 | |
|     let ext = 'png';
 | |
|     if (file.name) {
 | |
|         let fileNameMatches = file.name.match(/\.(.+)$/);
 | |
|         if (fileNameMatches.length > 1) ext = fileNameMatches[1];
 | |
|     }
 | |
| 
 | |
|     const remoteFilename = "image-" + Date.now() + "." + ext;
 | |
|     const formData = new FormData();
 | |
|     formData.append('file', file, remoteFilename);
 | |
|     formData.append('uploaded_to', pageId);
 | |
| 
 | |
|     const resp = await window.$http.post(window.baseUrl('/images/gallery'), formData);
 | |
|     return resp.data;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @param {Editor} editor
 | |
|  * @param {WysiwygConfigOptions} options
 | |
|  */
 | |
| function dragStart(editor, options) {
 | |
|     let node = editor.selection.getNode();
 | |
| 
 | |
|     if (node.nodeName === 'IMG') {
 | |
|         wrap = editor.dom.getParent(node, '.mceTemp');
 | |
| 
 | |
|         if (!wrap && node.parentNode.nodeName === 'A' && !hasTextContent(node.parentNode)) {
 | |
|             wrap = node.parentNode;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Track dragged contenteditable blocks
 | |
|     if (node.hasAttribute('contenteditable') && node.getAttribute('contenteditable') === 'false') {
 | |
|         draggedContentEditable = node;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @param {Editor} editor
 | |
|  * @param {WysiwygConfigOptions} options
 | |
|  * @param {DragEvent} event
 | |
|  */
 | |
| function drop(editor, options, event) {
 | |
|     let dom = editor.dom,
 | |
|         rng = tinymce.dom.RangeUtils.getCaretRangeFromPoint(event.clientX, event.clientY, editor.getDoc());
 | |
| 
 | |
|     // Template insertion
 | |
|     const templateId = event.dataTransfer && event.dataTransfer.getData('bookstack/template');
 | |
|     if (templateId) {
 | |
|         event.preventDefault();
 | |
|         window.$http.get(`/templates/${templateId}`).then(resp => {
 | |
|             editor.selection.setRng(rng);
 | |
|             editor.undoManager.transact(function () {
 | |
|                 editor.execCommand('mceInsertContent', false, resp.data.html);
 | |
|             });
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     // Don't allow anything to be dropped in a captioned image.
 | |
|     if (dom.getParent(rng.startContainer, '.mceTemp')) {
 | |
|         event.preventDefault();
 | |
|     } else if (wrap) {
 | |
|         event.preventDefault();
 | |
| 
 | |
|         editor.undoManager.transact(function () {
 | |
|             editor.selection.setRng(rng);
 | |
|             editor.selection.setNode(wrap);
 | |
|             dom.remove(wrap);
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     // Handle contenteditable section drop
 | |
|     if (!event.isDefaultPrevented() && draggedContentEditable) {
 | |
|         event.preventDefault();
 | |
|         editor.undoManager.transact(function () {
 | |
|             const selectedNode = editor.selection.getNode();
 | |
|             const range = editor.selection.getRng();
 | |
|             const selectedNodeRoot = selectedNode.closest('body > *');
 | |
|             if (range.startOffset > (range.startContainer.length / 2)) {
 | |
|                 editor.$(selectedNodeRoot).after(draggedContentEditable);
 | |
|             } else {
 | |
|                 editor.$(selectedNodeRoot).before(draggedContentEditable);
 | |
|             }
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     // Handle image insert
 | |
|     if (!event.isDefaultPrevented()) {
 | |
|         paste(editor, options, event);
 | |
|     }
 | |
| 
 | |
|     wrap = null;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @param {Editor} editor
 | |
|  * @param {WysiwygConfigOptions} options
 | |
|  */
 | |
| export function listenForDragAndPaste(editor, options) {
 | |
|     editor.on('dragstart', () => dragStart(editor, options));
 | |
|     editor.on('drop',  event => drop(editor, options, event));
 | |
|     editor.on('paste', event => paste(editor, options, event));
 | |
| } |