function viewerDragDropMixin(viewer, options) {
  //>>includeStart('debug', pragmas.debug);
  if (!defined(viewer)) {
    throw new DeveloperError("viewer is required.");
  }
  if (viewer.hasOwnProperty("dropTarget")) {
    throw new DeveloperError("dropTarget is already defined by another mixin.");
  }
  if (viewer.hasOwnProperty("dropEnabled")) {
    throw new DeveloperError(
      "dropEnabled is already defined by another mixin.",
    );
  }
  if (viewer.hasOwnProperty("dropError")) {
    throw new DeveloperError("dropError is already defined by another mixin.");
  }
  if (viewer.hasOwnProperty("clearOnDrop")) {
    throw new DeveloperError(
      "clearOnDrop is already defined by another mixin.",
    );
  }
  if (viewer.hasOwnProperty("flyToOnDrop")) {
    throw new DeveloperError(
      "flyToOnDrop is already defined by another mixin.",
    );
  }
  //>>includeEnd('debug');

  options = options ?? Frozen.EMPTY_OBJECT;

  //Local variables to be closed over by defineProperties.
  let dropEnabled = true;
  let flyToOnDrop = options.flyToOnDrop ?? true;
  const dropError = new Event();
  let clearOnDrop = options.clearOnDrop ?? true;
  let dropTarget = options.dropTarget ?? viewer.container;
  let clampToGround = options.clampToGround ?? true;
  let proxy = options.proxy;

  dropTarget = getElement(dropTarget);

  Object.defineProperties(viewer, {
    /**
     * Gets or sets the element to serve as the drop target.
     * @memberof viewerDragDropMixin.prototype
     * @type {Element}
     */
    dropTarget: {
      //TODO See https://github.com/CesiumGS/cesium/issues/832
      get: function () {
        return dropTarget;
      },
      set: function (value) {
        //>>includeStart('debug', pragmas.debug);
        if (!defined(value)) {
          throw new DeveloperError("value is required.");
        }
        //>>includeEnd('debug');

        unsubscribe(dropTarget, handleDrop);
        dropTarget = value;
        subscribe(dropTarget, handleDrop);
      },
    },

    /**
     * Gets or sets a value indicating if drag and drop support is enabled.
     * @memberof viewerDragDropMixin.prototype
     * @type {Element}
     */
    dropEnabled: {
      get: function () {
        return dropEnabled;
      },
      set: function (value) {
        if (value !== dropEnabled) {
          if (value) {
            subscribe(dropTarget, handleDrop);
          } else {
            unsubscribe(dropTarget, handleDrop);
          }
          dropEnabled = value;
        }
      },
    },

    /**
     * Gets the event that will be raised when an error is encountered during drop processing.
     * @memberof viewerDragDropMixin.prototype
     * @type {Event}
     */
    dropError: {
      get: function () {
        return dropError;
      },
    },

    /**
     * Gets or sets a value indicating if existing data sources should be cleared before adding the newly dropped sources.
     * @memberof viewerDragDropMixin.prototype
     * @type {boolean}
     */
    clearOnDrop: {
      get: function () {
        return clearOnDrop;
      },
      set: function (value) {
        clearOnDrop = value;
      },
    },

    /**
     * Gets or sets a value indicating if the camera should fly to the data source after it is loaded.
     * @memberof viewerDragDropMixin.prototype
     * @type {boolean}
     */
    flyToOnDrop: {
      get: function () {
        return flyToOnDrop;
      },
      set: function (value) {
        flyToOnDrop = value;
      },
    },

    /**
     * Gets or sets the proxy to be used for KML.
     * @memberof viewerDragDropMixin.prototype
     * @type {Proxy}
     */
    proxy: {
      get: function () {
        return proxy;
      },
      set: function (value) {
        proxy = value;
      },
    },

    /**
     * Gets or sets a value indicating if the datasources should be clamped to the ground
     * @memberof viewerDragDropMixin.prototype
     * @type {boolean}
     */
    clampToGround: {
      get: function () {
        return clampToGround;
      },
      set: function (value) {
        clampToGround = value;
      },
    },
  });

  function handleDrop(event) {
    stop(event);

    if (clearOnDrop) {
      viewer.entities.removeAll();
      viewer.dataSources.removeAll();
    }

    const files = event.dataTransfer.files;
    const length = files.length;
    for (let i = 0; i < length; i++) {
      const file = files[i];
      const reader = new FileReader();
      reader.onload = createOnLoadCallback(viewer, file, proxy, clampToGround);
      reader.onerror = createDropErrorCallback(viewer, file);
      reader.readAsText(file);
    }
  }

  //Enable drop by default;
  subscribe(dropTarget, handleDrop);

  //Wrap the destroy function to make sure all events are unsubscribed from
  viewer.destroy = wrapFunction(viewer, viewer.destroy, function () {
    viewer.dropEnabled = false;
  });

  //Specs need access to handleDrop
  viewer._handleDrop = handleDrop;
}
A mixin which adds default drag and drop support for CZML files to the Viewer widget.
Rather than being called directly, this function is normally passed as
a parameter to {@link Viewer#extend}, as shown in the example below.
@function viewerDragDropMixin
@param {Viewer} viewer The viewer instance.
@param {object} [options] Object with the following properties:
@param {Element|string} [options.dropTarget=viewer.container] The DOM element which will serve as the drop target.
@param {boolean} [options.clearOnDrop=true] When true, dropping files will clear all existing data sources first, when false, new data sources will be loaded after the existing ones.
@param {boolean} [options.flyToOnDrop=true] When true, dropping files will fly to the data source once it is loaded.
@param {boolean} [options.clampToGround=true] When true, datasources are clamped to the ground.
@param {Proxy} [options.proxy] The proxy to be used for KML network links.

@exception {DeveloperError} Element with id <options.dropTarget> does not exist in the document.
@exception {DeveloperError} dropTarget is already defined by another mixin.
@exception {DeveloperError} dropEnabled is already defined by another mixin.
@exception {DeveloperError} dropError is already defined by another mixin.
@exception {DeveloperError} clearOnDrop is already defined by another mixin.

@example
// Add basic drag and drop support and pop up an alert window on error.
const viewer = new Cesium.Viewer('cesiumContainer');
viewer.extend(Cesium.viewerDragDropMixin);
viewer.dropError.addEventListener(function(viewerArg, source, error) {
    window.alert('Error processing ' + source + ':' + error);
});