text: /* eslint-disable one-var */
  /* jslint newcap: true */
  /* global XMLHttpRequest: false, FormData: false */
  /*
   * Inline Text Attachment
   *
   * Author: Roy van Kaathoven
   * Contact: ik@royvankaathoven.nl
   */
  (function (document, window) {
    'use strict'
  
    var inlineAttachment = function (options, instance) {
      this.settings = inlineAttachment.util.merge(options, inlineAttachment.defaults)
      this.editor = instance
      this.filenameTag = '{filename}'
      this.lastValue = null
    }
  
    /**
     * Will holds the available editors
     *
     * @type {Object}
     */
    inlineAttachment.editors = {}
  
    /**
     * Utility functions
     */
    inlineAttachment.util = {
  
      /**
       * Simple function to merge the given objects
       *
       * @param {Object[]} object Multiple object parameters
       * @returns {Object}
       */
      merge: function () {
        var result = {}
        for (var i = arguments.length - 1; i >= 0; i--) {
          var obj = arguments[i]
          for (var k in obj) {
            if (obj.hasOwnProperty(k)) {
              result[k] = obj[k]
            }
          }
        }
        return result
      },
  
      /**
       * Append a line of text at the bottom, ensuring there aren't unnecessary newlines
       *
       * @param {String} appended Current content
       * @param {String} previous Value which should be appended after the current content
       */
      appendInItsOwnLine: function (previous, appended) {
        return (previous + '\n\n[[D]]' + appended)
          .replace(/(\n{2,})\[\[D\]\]/, '\n\n')
          .replace(/^(\n*)/, '')
      },
  
      /**
       * Inserts the given value at the current cursor position of the textarea element
       *
       * @param  {HtmlElement} el
       * @param  {String} value Text which will be inserted at the cursor position
       */
      insertTextAtCursor: function (el, text) {
        var scrollPos = el.scrollTop,
          strPos = 0,
          browser = false,
          range
  
        if ((el.selectionStart || el.selectionStart === '0')) {
          browser = 'ff'
        } else if (document.selection) {
          browser = 'ie'
        }
  
        if (browser === 'ie') {
          el.focus()
          range = document.selection.createRange()
          range.moveStart('character', -el.value.length)
          strPos = range.text.length
        } else if (browser === 'ff') {
          strPos = el.selectionStart
        }
  
        var front = (el.value).substring(0, strPos)
        var back = (el.value).substring(strPos, el.value.length)
        el.value = front + text + back
        strPos = strPos + text.length
        if (browser === 'ie') {
          el.focus()
          range = document.selection.createRange()
          range.moveStart('character', -el.value.length)
          range.moveStart('character', strPos)
          range.moveEnd('character', 0)
          range.select()
        } else if (browser === 'ff') {
          el.selectionStart = strPos
          el.selectionEnd = strPos
          el.focus()
        }
        el.scrollTop = scrollPos
      }
    }
  
    /**
     * Default configuration options
     *
     * @type {Object}
     */
    inlineAttachment.defaults = {
      /**
       * URL where the file will be send
       */
      uploadUrl: 'upload_attachment.php',
  
      /**
       * Which method will be used to send the file to the upload URL
       */
      uploadMethod: 'POST',
  
      /**
       * Name in which the file will be placed
       */
      uploadFieldName: 'file',
  
      /**
       * Extension which will be used when a file extension could not
       * be detected
       */
      defaultExtension: 'png',
  
      /**
       * JSON field which refers to the uploaded file URL
       */
      jsonFieldName: 'filename',
  
      /**
       * Allowed MIME types
       */
      allowedTypes: [
        'image/jpeg',
        'image/png',
        'image/jpg',
        'image/gif'
      ],
  
      /**
       * Text which will be inserted when dropping or pasting a file.
       * Acts as a placeholder which will be replaced when the file is done with uploading
       */
      progressText: '![Uploading file...]()',
  
      /**
       * When a file has successfully been uploaded the progressText
       * will be replaced by the urlText, the {filename} tag will be replaced
       * by the filename that has been returned by the server
       */
      urlText: '![file]({filename})',
  
      /**
       * Text which will be used when uploading has failed
       */
      errorText: 'Error uploading file',
  
      /**
       * Extra parameters which will be send when uploading a file
       */
      extraParams: {},
  
      /**
       * Extra headers which will be send when uploading a file
       */
      extraHeaders: {},
  
      /**
       * Before the file is send
       */
      beforeFileUpload: function () {
        return true
      },
  
      /**
       * Triggers when a file is dropped or pasted
       */
      onFileReceived: function () {},
  
      /**
       * Custom upload handler
       *
       * @return {Boolean} when false is returned it will prevent default upload behavior
       */
      onFileUploadResponse: function () {
        return true
      },
  
      /**
       * Custom error handler. Runs after removing the placeholder text and before the alert().
       * Return false from this function to prevent the alert dialog.
       *
       * @return {Boolean} when false is returned it will prevent default error behavior
       */
      onFileUploadError: function () {
        return true
      },
  
      /**
       * When a file has succesfully been uploaded
       */
      onFileUploaded: function () {}
    }
  
    /**
     * Uploads the blob
     *
     * @param  {Blob} file blob data received from event.dataTransfer object
     * @return {XMLHttpRequest} request object which sends the file
     */
    inlineAttachment.prototype.uploadFile = function (file) {
      var me = this,
        formData = new FormData(),
        xhr = new XMLHttpRequest(),
        settings = this.settings,
        extension = settings.defaultExtension || settings.defualtExtension
  
      if (typeof settings.setupFormData === 'function') {
        settings.setupFormData(formData, file)
      }
      // Attach the file. If coming from clipboard, add a default filename (only works in Chrome for now)
      // http://stackoverflow.com/questions/6664967/how-to-give-a-blob-uploaded-as-formdata-a-file-name
      if (file.name) {
        var fileNameMatches = file.name.match(/\.(.+)$/)
        if (fileNameMatches) {
          extension = fileNameMatches[1]
        }
      }
  
      var remoteFilename = 'image-' + Date.now() + '.' + extension
      console.log(file)
      if (typeof settings.remoteFilename === 'function') {
        remoteFilename = settings.remoteFilename(file)
      }
  
      formData.append(settings.uploadFieldName, file, remoteFilename)
  
      // Append the extra parameters to the formdata
      if (typeof settings.extraParams === 'object') {
        for (var key in settings.extraParams) {
          if (settings.extraParams.hasOwnProperty(key)) {
            formData.append(key, settings.extraParams[key])
          }
        }
      }
  
      xhr.open('POST', settings.uploadUrl)
  
      // Add any available extra headers
      if (typeof settings.extraHeaders === 'object') {
        for (var header in settings.extraHeaders) {
          if (settings.extraHeaders.hasOwnProperty(header)) {
            xhr.setRequestHeader(header, settings.extraHeaders[header])
          }
        }
      }
  
      xhr.onload = function () {
        // If HTTP status is OK or Created
        if (xhr.status === 200 || xhr.status === 201) {
          me.onFileUploadResponse(xhr)
        } else {
          me.onFileUploadError(xhr)
        }
      }
      if (settings.beforeFileUpload(xhr) !== false) {
        xhr.send(formData)
      }
      return xhr
    }
  
    /**
     * Returns if the given file is allowed to handle
     *
     * @param {File} clipboard data file
     */
    inlineAttachment.prototype.isFileAllowed = function (file) {
      if (file.kind === 'string') { return false }
      if (this.settings.allowedTypes.indexOf('*') === 0) {
        return true
      } else {
        return this.settings.allowedTypes.indexOf(file.type) >= 0
      }
    }
  
    /**
     * Handles upload response
     *
     * @param  {XMLHttpRequest} xhr
     * @return {Void}
     */
    inlineAttachment.prototype.onFileUploadResponse = function (xhr) {
      if (this.settings.onFileUploadResponse.call(this, xhr) !== false) {
        var result = JSON.parse(xhr.responseText),
          filename = result[this.settings.jsonFieldName]
        if (result && filename) {
          var newValue
          if (typeof this.settings.urlText === 'function') {
            newValue = this.settings.urlText.call(this, filename, result)
          } else {
            newValue = this.settings.urlText.replace(this.filenameTag, filename)
          }
          var text = this.editor.getValue().replace(this.lastValue, newValue)
          this.editor.setValue(text)
          this.settings.onFileUploaded.call(this, filename)
        }
      }
    }
  
    /**
     * Called when a file has failed to upload
     *
     * @param  {XMLHttpRequest} xhr
     * @return {Void}
     */
    inlineAttachment.prototype.onFileUploadError = function (xhr) {
      if (this.settings.onFileUploadError.call(this, xhr) !== false) {
        var text = this.editor.getValue().replace(this.lastValue, '')
        this.editor.setValue(text)
      }
    }
  
    /**
     * Called when a file has been inserted, either by drop or paste
     *
     * @param  {File} file
     * @return {Void}
     */
    inlineAttachment.prototype.onFileInserted = function (file) {
      if (this.settings.onFileReceived.call(this, file) !== false) {
        this.lastValue = this.settings.progressText
        this.editor.insertValue(this.lastValue)
      }
    }
  
    /**
     * Called when a paste event occured
     * @param  {Event} e
     * @return {Boolean} if the event was handled
     */
    inlineAttachment.prototype.onPaste = function (e) {
      var result = false,
        clipboardData = e.clipboardData,
        items
      if (typeof clipboardData === 'object') {
        items = clipboardData.items || clipboardData.files || []
  
        for (var i = 0; i < items.length; i++) {
          var item = items[i]
          if (this.isFileAllowed(item)) {
            result = true
            this.onFileInserted(item.getAsFile())
            this.uploadFile(item.getAsFile())
          }
        }
      }
  
      if (result) { e.preventDefault() }
      return result
    }
  
    /**
     * Called when a drop event occures
     * @param  {Event} e
     * @return {Boolean} if the event was handled
     */
    inlineAttachment.prototype.onDrop = function (e) {
      var result = false
      for (var i = 0; i < e.dataTransfer.files.length; i++) {
        var file = e.dataTransfer.files[i]
        if (this.isFileAllowed(file)) {
          result = true
          this.onFileInserted(file)
          this.uploadFile(file)
        }
      }
  
      return result
    }
  
    window.inlineAttachment = inlineAttachment
  })(document, window)
  