{"version":3,"file":"filedrop.mjs","sources":["../lib/filedrop.ts"],"sourcesContent":["// tslint:disable-next-line:max-line-length\nfunction getMatchingItems(list: DataTransferItemList, acceptVal: string, multiple: boolean): DataTransferItem[] {\n  const dataItems = Array.from(list);\n  let results: DataTransferItem[];\n\n  // Return the first item (or undefined) if our filter is for all files\n  if (acceptVal === '') {\n    results = dataItems.filter(item => item.kind === 'file');\n    return (multiple) ? results : [results[0]];\n  }\n\n  // Split accepts values by ',' then by '/'. Trim everything & lowercase.\n  const accepts = acceptVal.toLowerCase().split(',').map((accept) => {\n    return accept.split('/').map(part => part.trim());\n  }).filter(acceptParts => acceptParts.length === 2); // Filter invalid values\n\n  const predicate = (item:DataTransferItem) => {\n    if (item.kind !== 'file') return false;\n\n    // 'Parse' the type.\n    const [typeMain, typeSub] = item.type.toLowerCase().split('/').map(s => s.trim());\n\n    for (const [acceptMain, acceptSub] of accepts) {\n      // Look for an exact match, or a partial match if * is accepted, eg image/*.\n      if (typeMain === acceptMain && (acceptSub === '*' || typeSub === acceptSub)) {\n        return true;\n      }\n    }\n    return false;\n  };\n\n  results = results = dataItems.filter(predicate);\n  if (multiple === false) {\n    results = [results[0]];\n  }\n\n  return results;\n}\n\nfunction getFileData(data: DataTransfer, accept: string, multiple: boolean): File[] {\n  const dragDataItems = getMatchingItems(data.items, accept, multiple);\n  const files: File[] = [];\n\n  // This is because Map doesn't like the null type returned by getAsFile\n  dragDataItems.forEach((item) => {\n    const file = item.getAsFile();\n    if (file === null) return;\n    files.push(file);\n  });\n\n  return files;\n}\n\n// Safari and Edge don't quite support extending Event, this works around it.\nfunction fixExtendedEvent(instance: Event, type: Function) {\n  if (!(instance instanceof type)) {\n    Object.setPrototypeOf(instance, type.prototype);\n  }\n}\n\ninterface FileDropEventInit extends EventInit {\n  action: FileDropAccept;\n  files: File[];\n}\n\ntype FileDropAccept = 'drop' | 'paste';\n\nexport class FileDropEvent extends Event {\n  private _action: FileDropAccept;\n  private _files: File[];\n  constructor(typeArg: string, eventInitDict: FileDropEventInit) {\n    super(typeArg, eventInitDict);\n    fixExtendedEvent(this, FileDropEvent);\n    this._files = eventInitDict.files;\n    this._action = eventInitDict.action;\n  }\n\n  get action() {\n    return this._action;\n  }\n\n  get files() {\n    return this._files;\n  }\n}\n\n/*\n  Example Usage.\n  <file-drop\n    accept='image/*'\n    multiple | undefined\n    class='drop-valid|drop-invalid'\n  >\n  [everything in here is a drop target.]\n  </file-drop>\n\n  dropElement.addEventListener('filedrop', (event) => console.log(event.detail))\n*/\nexport class FileDropElement extends HTMLElement {\n\n  private _dragEnterCount = 0;\n\n  constructor() {\n    super();\n\n    // Bind\n    this._onDragEnter = this._onDragEnter.bind(this);\n    this._onDragLeave = this._onDragLeave.bind(this);\n    this._onDrop = this._onDrop.bind(this);\n    this._onPaste = this._onPaste.bind(this);\n\n    this.addEventListener('dragover', event => event.preventDefault());\n    this.addEventListener('drop', this._onDrop);\n    this.addEventListener('dragenter', this._onDragEnter);\n    this.addEventListener('dragend', () => this._reset());\n    this.addEventListener('dragleave', this._onDragLeave);\n    this.addEventListener('paste', this._onPaste);\n  }\n\n  get accept() {\n    return this.getAttribute('accept') || '';\n  }\n\n  set accept(val: string) {\n    this.setAttribute('accept', val);\n  }\n\n  get multiple() : string | null {\n    return this.getAttribute('multiple');\n  }\n\n  set multiple(val: string | null) {\n    this.setAttribute('multiple', val || '');\n  }\n\n  private _onDragEnter(event: DragEvent) {\n    this._dragEnterCount += 1;\n    if (this._dragEnterCount > 1) return;\n    if (event.dataTransfer === null) {\n      this.classList.add('drop-invalid');\n      return;\n    }\n\n    // We don't have data, attempt to get it and if it matches, set the correct state.\n    const items = event.dataTransfer.items;\n    const matchingFiles = getMatchingItems(items, this.accept, (this.multiple !== null));\n    const validDrop: boolean = event.dataTransfer && event.dataTransfer.items.length ?\n      (matchingFiles[0] !== undefined) :\n      // Safari doesn't give file information on drag enter, so the best we\n      // can do is return valid.\n      true;\n\n    if (validDrop) {\n      this.classList.add('drop-valid');\n    } else {\n      this.classList.add('drop-invalid');\n    }\n  }\n\n  private _onDragLeave() {\n    this._dragEnterCount -= 1;\n    if (this._dragEnterCount === 0) {\n      this._reset();\n    }\n  }\n\n  private _onDrop(event: DragEvent) {\n    event.preventDefault();\n    if (event.dataTransfer === null) return;\n    this._reset();\n    const action = 'drop';\n    const files = getFileData(event.dataTransfer, this.accept, (this.multiple !== null));\n    if (files === undefined) return;\n\n    this.dispatchEvent(new FileDropEvent('filedrop', { action, files }));\n  }\n\n  private _onPaste(event: ClipboardEvent) {\n    const action = 'paste';\n    if(!event.clipboardData) return;\n    const files = getFileData(event.clipboardData, this.accept, (this.multiple !== undefined));\n    if (files === undefined) return;\n\n    this.dispatchEvent(new FileDropEvent('filedrop', { action, files }));\n  }\n\n  private _reset() {\n    this._dragEnterCount = 0;\n    this.classList.remove('drop-valid');\n    this.classList.remove('drop-invalid');\n  }\n}\n\ncustomElements.define('file-drop', FileDropElement);\n"],"names":["list","acceptVal","multiple","dataItems","Array","from","results","filter","item","kind","accepts","toLowerCase","split","map","accept","part","trim","acceptParts","length","typeMain","typeSub","type","s","acceptMain","acceptSub","data","files","getMatchingItems","items","forEach","file","getAsFile","push","Event","[object Object]","typeArg","eventInitDict","instance","super","this","FileDropEvent","Object","setPrototypeOf","prototype","_files","_action","action","HTMLElement","_onDragEnter","bind","_onDragLeave","_onDrop","_onPaste","addEventListener","event","preventDefault","_reset","getAttribute","val","setAttribute","_dragEnterCount","dataTransfer","classList","add","matchingFiles","undefined","getFileData","dispatchEvent","clipboardData","remove","customElements","define","FileDropElement"],"mappings":"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"}