{"version":3,"sources":["jsdelivr-header.js","/npm/quill2-image-drop-and-paste@0.1.3/src/blots/image.ts","/npm/quill2-image-drop-and-paste@0.1.3/src/utils/index.ts","/npm/quill2-image-drop-and-paste@0.1.3/src/interfaces.ts","/npm/quill2-image-drop-and-paste@0.1.3/src/index.ts"],"names":[],"mappings":"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","file":"/npm/quill2-image-drop-and-paste@0.1.3/dist/index.umd.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/quill2-image-drop-and-paste@0.1.3/dist/index.umd.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","// import parchment from 'parchment';\n\nimport Quill from 'quill';\n\nconst parchment = Quill.import('parchment');\n// const Embed = Quill.import('blots/embed');\n// const InlineBlot = Quill.import('blots/inline');\n\nclass LoadingImage extends parchment.EmbedBlot {\n  static create(src: string) {\n    const node = super.create(src);\n\n    const image = document.createElement('img');\n    image.setAttribute('src', src);\n    node.appendChild(image);\n    node.setAttribute('data-src', src);\n    return node;\n  }\n\n  static value(domNode: HTMLParagraphElement) {\n    return domNode.getAttribute('data-src') || '';\n  }\n}\n\nLoadingImage.blotName = 'loading-iamge';\nLoadingImage.className = 'ql-image-uploading';\nLoadingImage.tagName = 'SECTION';\n\nexport default LoadingImage;\n","import { Op } from '../interfaces';\n\nexport const url2Img = (url: string): Promise<HTMLImageElement> => new Promise(\n  (resolve, reject) => {\n    const img = new Image();\n    img.crossOrigin = 'anonymous';\n    img.src = url;\n    img.onload = (): void => {\n      resolve(img);\n    };\n    img.onerror = reject;\n  },\n);\n\n/**\n * 使用 Canvas 将图片 url 转为带 水印的 Blob\n * @param img Image 实例\n * @param watermarkOptions 水印选项\n * @param watermarkOptions text 水印文本内容\n * @param watermarkOptions x x轴位置\n * @param watermarkOptions y y轴位置\n * @param watermarkOptions font 水印文本字体\n * @param watermarkOptions fillStyle 水印文本样式\n * @param watermarkOptions imgAlpha 图片透明度 0~1\n * @param watermarkOptions textAlpha 水印文本透明度 0~1\n * @param watermarkOptions textAlign 水印文本对齐方式\n */\nexport const img2Blob = (img: HTMLImageElement, {\n  text = '',\n  x = 0,\n  y = 0,\n  font = '24px serif',\n  fillStyle = 'red',\n  imgAlpha = 1,\n  textAlpha = 1,\n  textAlign = 'center' as CanvasTextAlign,\n}): Promise<Blob> => new Promise(\n  (resolve, reject) => {\n    if (!img) {\n      reject();\n    }\n    const canvas = document.createElement('canvas');\n    canvas.width = img.width;\n    canvas.height = img.height;\n    const ctx = canvas.getContext('2d');\n    if (!ctx) {\n      return;\n    }\n    ctx.globalAlpha = imgAlpha;\n    ctx.drawImage(img, 0, 0, canvas.width, canvas.height);\n    ctx.save();\n    if (text) {\n      ctx.globalAlpha = textAlpha;\n      ctx.font = font;\n      ctx.fillStyle = fillStyle;\n      ctx.textAlign = textAlign;\n      ctx.fillText(text, x || canvas.width / 2, y || canvas.height / 2);\n    }\n    canvas.toBlob((blob) => resolve(blob as Blob));\n  },\n);\n\nexport const getLoadedImage = (image: HTMLImageElement): Promise<Blob> => new Promise(\n  (resolve, reject) => {\n    const img = image;\n    if (!img.complete) {\n      img.onload = () => {\n        resolve(img2Blob(img, {}));\n      };\n      img.onerror = (err) => reject(err);\n      return;\n    }\n    img2Blob(img, {}).then(resolve, reject);\n  },\n);\n\nexport const convertURLtoFile = async (url: string) => {\n  const response = await fetch(url);\n  const data = await response.blob();\n  const filename = url.split('/').pop(); // url 구조에 맞게 수정할 것\n  const metadata = { type: 'image/jpeg' };\n  return new File([data], filename!, metadata);\n};\n\nexport const isImageOp = (op: Op): boolean => {\n  if (op.insert === 'undefined' || typeof op.insert === 'string') {\n    return false;\n  }\n\n  const insert = op.insert as { image?: string, ['loading-iamge']?: string };\n  return typeof (insert.image || insert['loading-iamge']) === 'string';\n};\n\nexport const getImageUrlOfOp = (op: Op): string => {\n  if (typeof op.insert === 'object') {\n    const insert = op.insert as { image?: string, ['loading-iamge']?: string };\n    return insert.image || insert['loading-iamge'] || '';\n  }\n  return '';\n};\n\nexport const isDataURL = (s: string): boolean => /^data:image\\/.+;base64/.test(s);\n","import Quill from 'quill';\n\nexport interface Options {\n  upload(file: Blob, originalUrl?: string): Promise<string>;\n  imageDomainAllowList?: string[];\n  imageAllowMatch?: (url: string) => boolean;\n}\n\nexport abstract class QuillImageDropAndPaste {\n  public quill;\n\n  public options: Options;\n\n  public constructor(quill: Quill, options: Options) {\n    this.quill = quill;\n    this.options = options;\n  }\n\n  protected abstract handleDrop(e: DragEvent): void;\n\n  protected abstract handlePaste(e: ClipboardEvent): void;\n}\n\nexport interface Op {\n  // only one property out of {insert, delete, retain} will be present\n  insert?: string | object;\n  delete?: number;\n  retain?: number;\n  attributes?: AttributeMap;\n}\n\nexport interface AttributeMap {\n  [key: string]: any;\n}\n\nexport enum ImageStatus {\n  LOADING = 'ql-image-loading',\n  SUCCESS = 'ql-image-success',\n  ERROR = 'ql-image-error',\n}\n","import Quill, { RangeStatic } from 'quill';\nimport LoadingImage from './blots/image';\nimport {\n  img2Blob, isImageOp, getImageUrlOfOp, isDataURL, url2Img,\n} from './utils';\nimport {\n  QuillImageDropAndPaste, Options, Op, ImageStatus,\n} from './interfaces';\n\nconst Delta = Quill.import('delta');\n\n// TODO: this is an invalid usage\nQuill.register({ 'formats/loading-image': LoadingImage }, true);\n\nconst defaultOptions: Required<Options> = {\n  imageAllowMatch: () => false,\n  imageDomainAllowList: [],\n  async upload(file: Blob, originalUrl: string) {\n    return originalUrl;\n  },\n};\n\nclass ImageDropAndPaste extends QuillImageDropAndPaste {\n  quill: Quill;\n\n  options: Required<Options>;\n\n  private altMap = new Map<string, string>();\n\n  private urlMap = new Map<string, string>();\n\n  constructor(quill: Quill, options: Options) {\n    super(quill, options);\n    this.quill = quill;\n\n    this.options = {\n      ...defaultOptions,\n      ...options,\n    };\n\n    if (!options) {\n      return;\n    }\n\n    this.options.imageDomainAllowList?.push(window.location.hostname);\n    this.handleDrop = this.handleDrop.bind(this);\n    this.handlePaste = this.handlePaste.bind(this);\n    this.quill.root.addEventListener('drop', this.handleDrop, false);\n    this.quill.root.addEventListener('paste', this.handlePaste, false);\n    this.injectUploaderHandler();\n  }\n\n  /**\n   * With quill2 editor, you should only set options.handler of uploader module.\n   * It will automatically replace images from dataurl to url via your custom image handler.\n   * Related issue: https://github.com/quilljs/quill/issues/863#issuecomment-240579430\n   *\n   * Source code for handling paste event: https://github.com/quilljs/quill/blob/d2bd71d758dd0bb707801ca3e07b9919ece748ca/modules/uploader.js#L8\n   * Source code for handling drop event: https://github.com/quilljs/quill/blob/d2bd71d758dd0bb707801ca3e07b9919ece748ca/modules/clipboard.js#L156\n   */\n  injectUploaderHandler() {\n    const imageHandler = (range: RangeStatic, files: Blob[]) => {\n      const promises = files.map((file: Blob) => this.options.upload(file));\n      Promise.all(promises).then((images) => {\n        const update = images.reduce(\n          (delta, image) => delta.insert({ image }),\n          new Delta().retain(range.index).delete(range.length),\n        );\n        this.quill.updateContents(update, 'user');\n        this.quill.setSelection(\n          range.index + images.length,\n          0,\n          'silent',\n        );\n      });\n    };\n    const uploader = this.quill\n      .getModule('uploader');\n    uploader.options.handler = imageHandler;\n  }\n\n  // eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars\n  handleDrop(e: DragEvent): void {\n    // TODO: elegant implements for paste or drop images / videos\n  }\n\n  handlePaste(e: ClipboardEvent): void {\n    if (!e.clipboardData) {\n      return;\n    }\n    if (e.clipboardData.types.includes('text/html') && !e.clipboardData.types.includes('Files')) {\n      this.restoreImages();\n    }\n  }\n\n  shouldImageRestore(url: string) {\n    const allowList = this.options.imageDomainAllowList;\n    const match = this.options.imageAllowMatch;\n    if (isDataURL(url)) {\n      return true;\n    }\n    const { hostname } = new URL(url);\n    if (allowList.includes(hostname)) {\n      return false;\n    }\n    return !match(hostname);\n  }\n\n  async restoreImage(op: Op): Promise<void> {\n    const originalUrl = getImageUrlOfOp(op);\n    if (!this.shouldImageRestore(originalUrl)) {\n      return;\n    }\n    try {\n      // const imageElement = !isDataURL(originalUrl)\n      //   ? this.quill.root.querySelector(`img[src=\"${originalUrl}\"]`)\n      //   : url2Img(originalUrl);\n      const imageElement = await url2Img(originalUrl);\n      if (!imageElement) {\n        console.warn('Can not read img element of url: %s', originalUrl);\n        return;\n      }\n      const { attributes } = op;\n      const cachedUrl = this.urlMap.get(originalUrl);\n      if (typeof cachedUrl === 'string') {\n        this.modifyImageDelta(originalUrl, cachedUrl, ImageStatus.SUCCESS);\n        return;\n      }\n      if (attributes?.alt === ImageStatus.LOADING) {\n        // If the image matches loading status, do nothing to avoid repeated loading.\n        return;\n      }\n      // TODO: base64 encode image has a too large string length\n      this.modifyImageDelta(originalUrl, '', ImageStatus.LOADING);\n      if (!this.altMap.has(originalUrl)) {\n        this.altMap.set(originalUrl, attributes?.alt || '');\n        const file = await img2Blob(imageElement as HTMLImageElement, {});\n        // const file = await getLoadedImage(imageElement as HTMLImageElement);\n        // const file = await convertURLtoFile(originalUrl);\n        const targetUrl = await this.options.upload(file, originalUrl);\n        this.urlMap.set(originalUrl, targetUrl);\n        this.modifyImageDelta(originalUrl, targetUrl, ImageStatus.SUCCESS);\n      }\n    } catch (error) {\n      console.warn(error);\n      this.modifyImageDelta(originalUrl, '', ImageStatus.ERROR);\n    }\n  }\n\n  restoreImages() {\n    const delta = this.quill.getContents();\n    for (let i = 0; i < delta.ops.length; i += 1) {\n      const op = delta.ops[i];\n      // Replace original url to target url\n      if (isImageOp(op)) {\n        this.restoreImage(op);\n      }\n    }\n  }\n\n  async modifyImageDelta(originalUrl: string, targetUrl: string, status: ImageStatus) {\n    const delta = this.quill.getContents();\n    const newOps = delta.ops.map((op) => {\n      const matchedImage = isImageOp(op) && getImageUrlOfOp(op) === originalUrl;\n      if (!matchedImage) {\n        return op;\n      }\n      const { attributes } = op;\n      const finalStatus = targetUrl\n        ? ImageStatus.SUCCESS\n        : status;\n      const alt = finalStatus === ImageStatus.SUCCESS\n        ? this.altMap.get(originalUrl)\n        : status;\n      const finalUrl = targetUrl || originalUrl;\n      const newOp: Op = {\n        attributes: {\n          ...(attributes || {}),\n          alt,\n        },\n        insert: {\n          image: finalUrl,\n        },\n        // insert: status !== ImageStatus.LOADING\n        //   ? {\n        //     image: targetUrl || originalUrl,\n        //   }\n        //   : {\n        //     [LoadingImage.blotName]: originalUrl,\n        //   },\n      };\n      return newOp;\n    });\n    const newDelta = new Delta(newOps);\n    this.quill.setContents(newDelta, 'silent');\n  }\n}\n\nexport default ImageDropAndPaste;\n\nexport {\n  LoadingImage,\n};\n"]}