michielbdejong/solid-ui

View on GitHub
src/widgets/dragAndDrop.js

Summary

Maintainability
F
3 days
Test Coverage
/* Drag and drop common functionality
 */
const mime = require('mime-types')

/* global FileReader alert */
module.exports = {
  makeDropTarget: makeDropTarget,
  makeDraggable: makeDraggable,
  uploadFiles: uploadFiles
}
// const UI = require('../index.js') // this package

function makeDropTarget (ele, droppedURIHandler, droppedFileHandler) {
  var dragoverListener = function (e) {
    e.preventDefault() // Neeed else drop does not work [sic]
    e.dataTransfer.dropEffect = 'copy'
  }

  var dragenterListener = function (e) {
    console.log('dragenter event dropEffect: ' + e.dataTransfer.dropEffect)
    if (this.style) {
      //  necessary not sure when
      if (!this.savedStyle) {
        this.savedStyle = {}
        this.savedStyle.border = this.style.border
        this.savedStyle.backgroundColor = this.style.backgroundColor
        this.savedStyle.borderRadius = this.style.borderRadius
      }
      this.style.backgroundColor = '#ccc'
      this.style.border = '0.25em dashed black'
      this.style.borderRadius = '0.3em'
    }

    e.dataTransfer.dropEffect = 'link'
    console.log('dragenter event dropEffect 2: ' + e.dataTransfer.dropEffect)
  }
  var dragleaveListener = function (e) {
    console.log('dragleave event dropEffect: ' + e.dataTransfer.dropEffect)
    if (this.savedStyle) {
      this.style.border = this.savedStyle.border
      this.style.backgroundColor = this.savedStyle.backgroundColor
      this.style.borderRadius = this.savedStyle.borderRadius
    } else {
      this.style.backgroundColor = 'white'
      this.style.border = '0em solid black'
    }
  }

  var dropListener = function (e) {
    if (e.preventDefault) e.preventDefault() // stops the browser from redirecting off to the text.
    console.log('Drop event. dropEffect: ' + e.dataTransfer.dropEffect)
    console.log(
      'Drop event. types: ' +
        (e.dataTransfer.types ? e.dataTransfer.types.join(', ') : 'NOPE')
    )

    var uris = null
    var text
    if (e.dataTransfer.types) {
      for (var t = 0; t < e.dataTransfer.types.length; t++) {
        var type = e.dataTransfer.types[t]
        if (type === 'text/uri-list') {
          uris = e.dataTransfer.getData(type).split('\n') // @ ignore those starting with #
          console.log('Dropped text/uri-list: ' + uris)
        } else if (type === 'text/plain') {
          text = e.dataTransfer.getData(type)
        } else if (type === 'Files' && droppedFileHandler) {
          var files = e.dataTransfer.files // FileList object.
          for (let i = 0; files[i]; i++) {
            const f = files[i]
            console.log(
              'Filename: ' +
                f.name +
                ', type: ' +
                (f.type || 'n/a') +
                ' size: ' +
                f.size +
                ' bytes, last modified: ' +
                (f.lastModifiedDate
                  ? f.lastModifiedDate.toLocaleDateString()
                  : 'n/a')
            )
          }
          droppedFileHandler(files)
        }
      }
      if (uris === null && text && text.slice(0, 4) === 'http') {
        uris = text
        console.log("Waring: Poor man's drop: using text for URI") // chrome disables text/uri-list??
      }
    } else {
      // ... however, if we're IE, we don't have the .types property, so we'll just get the Text value
      uris = [e.dataTransfer.getData('Text')]
      console.log('WARNING non-standard drop event: ' + uris[0])
    }
    console.log('Dropped URI list (2): ' + uris)
    if (uris) {
      droppedURIHandler(uris)
    }
    this.style.backgroundColor = 'white' // restore style
    return false
  } // dropListener

  var addTargetListeners = function (ele) {
    if (!ele) {
      console.log('@@@ addTargetListeners: ele ' + ele)
    }
    ele.addEventListener('dragover', dragoverListener)
    ele.addEventListener('dragenter', dragenterListener)
    ele.addEventListener('dragleave', dragleaveListener)
    ele.addEventListener('drop', dropListener)
  }
  addTargetListeners(ele, droppedURIHandler)
} // listen for dropped URIs

// Make an HTML element draggable as a URI-identified thing
//
// Possibly later set the drag image too?
//
function makeDraggable (tr, obj) {
  tr.setAttribute('draggable', 'true') // Stop the image being dragged instead - just the TR

  tr.addEventListener(
    'dragstart',
    function (e) {
      tr.style.fontWeight = 'bold'
      e.dataTransfer.setData('text/uri-list', obj.uri)
      e.dataTransfer.setData('text/plain', obj.uri)
      e.dataTransfer.setData('text/html', tr.outerHTML)
      console.log(
        'Dragstart: ' + tr + ' -> ' + obj + 'de: ' + e.dataTransfer.dropEffect
      )
    },
    false
  )

  tr.addEventListener(
    'drag',
    function (e) {
      e.preventDefault()
      e.stopPropagation()
      // console.log('Drag: dropEffect: ' + e.dataTransfer.dropEffect)
    },
    false
  )

  tr.addEventListener(
    'dragend',
    function (e) {
      tr.style.fontWeight = 'normal'
      console.log('Dragend dropeffect: ' + e.dataTransfer.dropEffect)
      console.log('Dragend: ' + tr + ' -> ' + obj)
    },
    false
  )
}

/* uploadFiles
 **
 **  Generic uploader of local files to the web
 **   typically called from dropped file handler
 ** Params
 **  fetcher   instance of class Fetcher as in kb.fetcher
 **  files      Array of file objects
 **  fileBase   URI of folder in which to put files (except images) (no trailing slash)
 **  imageBase  URI of folder in which to put images
 **  successHandler(file, uploadedURI)    Called after each success upload
 **                              With file object an final URI as params
 */
function uploadFiles (fetcher, files, fileBase, imageBase, successHandler) {
  for (var i = 0; files[i]; i++) {
    const f = files[i]
    console.log(
      ' dropped: Filename: ' +
        f.name +
        ', type: ' +
        (f.type || 'n/a') +
        ' size: ' +
        f.size +
        ' bytes, last modified: ' +
        (f.lastModifiedDate ? f.lastModifiedDate.toLocaleDateString() : 'n/a')
    ) // See e.g. https://www.html5rocks.com/en/tutorials/file/dndfiles/

    // @@ Add: progress bar(s)
    var reader = new FileReader()
    reader.onload = (function (theFile) {
      return function (e) {
        var data = e.target.result
        var suffix = ''
        console.log(' File read byteLength : ' + data.byteLength)
        var contentType = theFile.type
        if (!theFile.type || theFile.type === '') {
          // Not known by browser
          contentType = mime.lookup(theFile.name)
          if (!contentType) {
            const msg =
              'Filename needs to have an extension which gives a type we know: ' +
              theFile.name
            console.log(msg)
            alert(msg)
            throw new Error(msg)
          }
        } else {
          var extension = mime.extension(theFile.type)
          if (theFile.type !== mime.lookup(theFile.name)) {
            suffix = '_.' + extension
            console.log('MIME TYPE MISMATCH -- adding extension: ' + suffix)
          }
        }
        var folderName = theFile.type.startsWith('image/')
          ? imageBase || fileBase
          : fileBase
        var destURI =
          folderName +
          (folderName.endsWith('/') ? '' : '/') +
          encodeURIComponent(theFile.name) +
          suffix

        fetcher
          .webOperation('PUT', destURI, {
            data: data,
            contentType: contentType
          })
          .then(
            _response => {
              console.log(' Upload: put OK: ' + destURI)
              successHandler(theFile, destURI)
            },
            error => {
              const msg = ' Upload: FAIL ' + destURI + ', Error: ' + error
              console.log(msg)
              alert(msg)
              throw new Error(msg)
            }
          )
      }
    })(f)
    reader.readAsArrayBuffer(f)
  }
}