diff --git a/hypermd/addon/paste-image.js b/hypermd/addon/paste-image.js index bc85fa06..43c1c06a 100644 --- a/hypermd/addon/paste-image.js +++ b/hypermd/addon/paste-image.js @@ -9,16 +9,20 @@ if (typeof exports == "object" && typeof module == "object") // CommonJS mod( require("codemirror/lib/codemirror"), + require("blueimp-canvas-to-blob"), + require("blueimp-load-image"), require("./../hypermd") ); else if (typeof define == "function" && define.amd) // AMD define([ "codemirror/lib/codemirror", + "blueimp-canvas-to-blob", + "blueimp-load-image", "./../hypermd", ], mod); else // Plain browser env - mod(CodeMirror, HyperMD); -})(function (CodeMirror, HyperMD) { + mod(CodeMirror, dataURLtoBlob, loadImage, HyperMD); +})(function (CodeMirror, dataURLtoBlob, loadImage, HyperMD) { "use strict"; /** @@ -63,11 +67,17 @@ this.cm = cm this.enabled = false this.enabledDrop = false + this.enableThumbnail = defaultOption.enableThumbnail + this.thumbnailMaxWidth = defaultOption.thumbnailMaxWidth + this.thumbnailMaxHeight = defaultOption.thumbnailMaxHeight this.uploadTo = 'sm.ms' this.placeholderURL = defaultOption.placeholderURL - + this.placeholderURLNonImage = defaultOption.placeholderURLNonImage + this.updateUploader(this.uploadTo) + this._doInsertWithThumbnailHandle = this.doInsertWithThumbnail.bind(this) + // use FlipFlop to bind/unbind event listeners var _pasteHandle = this.pasteHandle.bind(this) @@ -87,7 +97,8 @@ /** @type {{[name:string]:function(file:File,callback:UploadCallback)}} */ var builtInUploader = { - 'sm.ms': function Upload_SmMs(file, callback) { + 'sm.ms': function Upload_SmMs(file, thumbBlobIfAny, callback) { + // thumbBlobIfAny is ignored for sm.ms -> just upload the large file ajaxUpload( 'https://sm.ms/api/upload', { @@ -116,8 +127,8 @@ if ('function' == type) { if (newUploader.length == 1) { // transform Promise style into callback style. - uploadFunc = function (file, callback) { - newUploader(file) + uploadFunc = function (file, thumbBlob, callback) { + newUploader(file, thumbBlob) .then(function (url) { callback(url, null) }) .catch(function (err) { callback(null, err) }) } @@ -145,35 +156,22 @@ * * PasteImageUploader(aka. PasteImage Upload Function) has two forms: * - * 1. ` ( file:File, callback: (url:string, err:string)=>void ) => void ` + * 1. ` ( file:File, thumbBlob: Blob | null, callback: (url:string, err:string)=>void ) => void ` * - if failed to upload, the `url` shall be `null` and `err` shall be set. - * 2. ` ( file:File ) => Promise ` + * 2. ` ( file:File, thumbBlob: Blob | null ) => Promise ` */ /** * The default upload function shall be overrided! */ - Paste.prototype.uploader = function (file, callback) { + Paste.prototype.uploader = function (file, thumbBlobIfAny, callback) { callback(null, "Uploader is not configured") } - /** - * upload a image and insert at the current cursor. - * - * @param {DataTransfer} data - * @returns {boolean} handled or not - */ - Paste.prototype.doInsert = function (data) { + Paste.prototype.doInsertWithThumbnail = function (blob, thumbBlobIfAny, blobUrlIfAny, isImage) { var self = this, cm = self.cm - if (!data || !data.files || 1 != data.files.length) return false - var file = data.files[0] - - if (!/image\//.test(file.type)) return false - if (!this.uploader) return false - - var placeholderURL = this.placeholderURL - var blobURL = (placeholderURL.indexOf('') !== -1 && typeof URL !== 'undefined') ? URL.createObjectURL(file) : null - if (blobURL) placeholderURL = placeholderURL.replace('', blobURL) + var placeholderURL = isImage ? this.placeholderURL : this.placeholderURLNonImage + if (blobUrlIfAny) placeholderURL = placeholderURL.replace('', blobUrlIfAny) cm.operation(function () { cm.replaceSelection("![](" + placeholderURL + ")") @@ -183,24 +181,29 @@ var bookmark = cm.setBookmark(pos) // start uploading - self.uploader(file, function (url, err) { + self.uploader(blob, thumbBlobIfAny, function (url, err) { pos = bookmark.find() bookmark.clear() + // if a blobURL was created. revoke it + if (blobUrlIfAny) URL.revokeObjectURL(blobUrlIfAny) + // if failed to upload, show message if (!url) { - alert("Failed to upload image\n\n" + err) + alert("Failed to upload file\n\n" + err) cm.setCursor(pos) return } - // if a blobURL was created. revoke it - if (blobURL) URL.revokeObjectURL(blobURL) - // replace `Uploading` with the URL var pos1 = { line: pos.line, ch: pos.ch - 1 - placeholderURL.length }, pos2 = { line: pos.line, ch: pos.ch - 1 } + + if (!isImage) { // if not image, replace the whole placeholder: ![](...) + pos1.ch = pos1.ch - 4 + pos2.ch = pos2.ch + 1 + } cm.replaceRange(url, pos1, pos2) // update `` if the text is folded @@ -218,6 +221,47 @@ return true } + /** + * upload a image and insert at the current cursor. + * + * @param {DataTransfer} data + * @returns {boolean} handled or not + */ + Paste.prototype.doInsert = function (data) { + var self = this, cm = self.cm + + if (!data || !data.files || 1 != data.files.length) return false + var file = data.files[0] + + if (!this.uploader) return false + var isImage = /image\//.test(file.type) + + var useUploadImageAsPlaceholder = (isImage && this.placeholderURL.indexOf('') !== -1 && typeof URL !== 'undefined') + if (useUploadImageAsPlaceholder) { + if (!this.enableThumbnail) { // if use full image + return this._doInsertWithThumbnailHandle(file, null, URL.createObjectURL(file), isImage) + } + + // if thumbnail required + loadImage(file, + function (imageCanvas) { + imageCanvas.toBlob(function (thumbnailBlob) { + self._doInsertWithThumbnailHandle(file, thumbnailBlob, URL.createObjectURL(thumbnailBlob), isImage) + }, "image/jpeg", 0.85) + }, + { + maxWidth: this.thumbnailMaxWidth, + maxHeight: this.thumbnailMaxHeight, + canvas: true + } + ) + return true + } + else { // use user-defined placeholder that is not the current image + return this._doInsertWithThumbnailHandle(file, null, null, isImage); + } + } + /** * 'paste' event handler * @@ -259,9 +303,13 @@ enabled: false, enabledDrop: false, uploadTo: 'sm.ms', + enableThumbnail: false, // true to show image thumbnail as placeholder, false to show full image + thumbnailMaxWidth: 260, + thumbnailMaxHeight: 140, // before image is uploaded, a placeholder is applied. // you may use to display what user just submitted - placeholderURL: '?HyperMD-Uploading', + placeholderURL: '', + placeholderURLNonImage: '/hypermd-image-spin.gif', } CodeMirror.defineOption("hmdPasteImage", false, function (cm, newVal) {