Source: src/js/views/DataPackageView.js

define([
  "jquery",
  "underscore",
  "backbone",
  "localforage",
  "collections/DataPackage",
  "models/DataONEObject",
  "models/PackageModel",
  "models/metadata/ScienceMetadata",
  "models/metadata/eml211/EML211",
  "models/PackageModel",
  "views/DataItemView",
  "views/DownloadButtonView",
  "text!templates/dataPackage.html",
  "text!templates/dataPackageStart.html",
  "text!templates/dataPackageHeader.html",
], function (
  $,
  _,
  Backbone,
  LocalForage,
  DataPackage,
  DataONEObject,
  PackageModel,
  ScienceMetadata,
  EML211,
  Package,
  DataItemView,
  DownloadButtonView,
  DataPackageTemplate,
  DataPackageStartTemplate,
  DataPackageHeaderTemplate,
) {
  "use strict";

  /**
   * @class DataPackageView
   * @classdesc The main view of a Data Package in MetacatUI.  The view is
   *  a file/folder browser
   * @classcategory Views
   * @screenshot views/DataPackageView.png
   * @extends Backbone.View
   */
  var DataPackageView = Backbone.View.extend(
    /** @lends DataPackageView.prototype */ {
      type: "DataPackage",

      tagName: "table",

      className: "table table-striped table-hover",

      id: "data-package-table",

      events: {
        "click .toggle-rows": "toggleRows", // Show/hide rows associated with event's metadata row
        "click .message-row .addFiles": "handleAddFiles",
        "click .expand-control": "expand",
        "click .collapse-control": "collapse",
        "click .d1package-expand": "expandAll",
        "click .d1package-collapse": "collapseAll",
      },

      subviews: {},

      /**
       * A reference to the parent EditorView that contains this view
       * @type EditorView
       * @since 2.15.0
       */
      parentEditorView: null,

      template: _.template(DataPackageTemplate),
      startMessageTemplate: _.template(DataPackageStartTemplate),
      dataPackageHeaderTemplate: _.template(DataPackageHeaderTemplate),

      // Models waiting for their parent folder to be rendered, hashed by parent id:
      // {'parentid': [model1, model2, ...]}
      delayedModels: {},

      /* Flag indicating the open or closed state of the package rows */
      isOpen: true,

      initialize: function (options) {
        if (options === undefined || !options) var options = {};

        if (!options.edit) {
          //The edit option will allow the user to edit the table
          this.edit = options.edit || false;
          this.mode = "view";
          this.packageId = options.packageId || null;
          this.memberId = options.memberId || null;
          this.attributes = options.attributes || null;
          this.dataPackage = options.dataPackage || new DataPackage();
          this.dataEntities = options.dataEntities || new Array();
          this.disablePackageDownloads =
            options.disablePackageDownloads || false;
          this.currentlyViewing = options.currentlyViewing || null;
          this.parentEditorView = options.parentView || null;
          this.title = options.title || "";
          this.packageTitle = options.packageTitle || "";
          this.nested =
            typeof options.nested === "undefined" ? false : options.nested;
          this.metricsModel = options.metricsModel;

          // set the package model
          this.packageModel = this.dataPackage.packageModel;

          this.listenTo(this.packageModel, "changeAll", this.render);
        } else {
          //Get the options sent to this view
          if (typeof options == "object") {
            //The edit option will allow the user to edit the table
            this.edit = options.edit || false;
            this.mode = "edit";

            //The data package to render
            this.dataPackage = options.dataPackage || new DataPackage();

            this.parentEditorView = options.parentEditorView || null;
          }
          //Create a new DataPackage collection if one wasn't sent
          else if (!this.dataPackage) {
            this.dataPackage = new DataPackage();
          }

          return this;
        }
      },

      /**
       *  Render the DataPackage HTML
       */
      render: function () {
        this.$el.addClass("download-contents table-condensed");
        this.$el.append(
          this.template({
            edit: this.edit,
            dataPackageFiltering:
              MetacatUI.appModel.get("dataPackageFiltering") || false,
            dataPackageSorting:
              MetacatUI.appModel.get("dataPackageSorting") || false,
            loading: MetacatUI.appView.loadingTemplate({
              msg: "Loading files table... ",
            }),
            id: this.dataPackage.get("id"),
            title: this.title || "Files in this dataset",
            classes: "download-contents table-striped table-condensed table",
          }),
        );

        if (this.edit) {
          // Listen for  add events because models are being merged
          this.listenTo(this.dataPackage, "add", this.addOne);
          this.listenTo(this.dataPackage, "fileAdded", this.addOne);
        }

        // Render the current set of models in the DataPackage
        this.addAll();

        if (this.edit) {
          //If this is a new data package, then display a message and button
          if (
            (this.dataPackage.length == 1 &&
              this.dataPackage.models[0].isNew()) ||
            !this.dataPackage.length
          ) {
            var messageRow = this.startMessageTemplate();

            this.$("tbody").append(messageRow);

            this.listenTo(this.dataPackage, "add", function () {
              this.$(".message-row").remove();
            });
          }

          //Render the Share control(s)
          this.renderShareControl();
        } else {
          // check for nessted datasets

          if (this.nested) {
            this.getNestedPackages();
          }
        }

        return this;
      },

      /**
       * Add a single DataItemView row to the DataPackageView
       */
      addOne: function (item, dataPackage) {
        if (!item) return false;

        //Don't add duplicate rows
        if (this.$(".data-package-item[data-id='" + item.id + "']").length)
          return;

        // Don't add data package
        if (
          item.get("formatType") == "RESOURCE" ||
          item.get("type") == "DataPackage"
        ) {
          return;
        }

        var dataItemView, scimetaParent, parentRow, delayed_models;

        if (_.contains(Object.keys(this.subviews), item.id)) {
          return false; // Don't double render
        }

        var itemPath = null,
          view = this;
        if (!_.isEmpty(this.atLocationObj)) {
          itemPath = this.atLocationObj[item.get("id")];
          if (itemPath[0] != "/") {
            itemPath = "/" + itemPath;
          }
        }

        // get the data package id
        if (typeof dataPackage !== "undefined") {
          var dataPackageId = dataPackage.id;
        }
        if (typeof dataPackageId === "undefined")
          dataPackageId = this.dataPackage.id;

        var insertInfoIcon = this.edit
          ? false
          : view.dataEntities.includes(item.id);

        dataItemView = new DataItemView({
          model: item,
          metricsModel: this.metricsModel,
          itemPath: itemPath,
          insertInfoIcon: insertInfoIcon,
          currentlyViewing: this.currentlyViewing,
          mode: this.mode,
          parentEditorView: this.parentEditorView,
          dataPackageId: dataPackageId,
        });
        this.subviews[item.id] = dataItemView; // keep track of all views

        if (this.edit) {
          //Get the science metadata that documents this item
          scimetaParent = item.get("isDocumentedBy");

          //If this item is not documented by a science metadata object,
          // and there is only one science metadata doc in the package, then assume it is
          // documented by that science metadata doc
          if (typeof scimetaParent == "undefined" || !scimetaParent) {
            //Get the science metadata models
            var metadataIds = this.dataPackage.sciMetaPids;

            //If there is only one science metadata model in the package, then use it
            if (metadataIds.length == 1) scimetaParent = metadataIds[0];
          }
          //Otherwise, get the first science metadata doc that documents this object
          else {
            scimetaParent = scimetaParent[0];
          }

          if (
            scimetaParent == item.get("id") ||
            (!scimetaParent && item.get("type") == "Metadata")
          ) {
            // This is a metadata folder row, append it to the table
            this.$el.append(dataItemView.render().el);

            // Render any delayed models if this is the parent
            if (_.contains(Object.keys(this.delayedModels), dataItemView.id)) {
              delayed_models = this.delayedModels[dataItemView.id];
              _.each(delayed_models, this.addOne, this);
            }
          } else {
            // Find the parent row by it's id, stored in a custom attribute
            if (scimetaParent)
              parentRow = this.$("[data-id='" + scimetaParent + "']");

            if (typeof parentRow !== "undefined" && parentRow.length) {
              // This is a data row, insert below it's metadata parent folder
              parentRow.after(dataItemView.render().el);

              // Remove it from the delayedModels list if necessary
              if (_.contains(Object.keys(this.delayedModels), scimetaParent)) {
                delayed_models = this.delayedModels[scimetaParent];
                var index = _.indexOf(delayed_models, item);
                delayed_models = delayed_models.splice(index, 1);

                // Put the shortened array back if delayed models remains
                if (delayed_models.length > 0) {
                  this.delayedModels[scimetaParent] = delayed_models;
                } else {
                  this.delayedModels[scimetaParent] = undefined;
                }
              }

              this.trigger("addOne");
            } else {
              console.warn(
                "Couldn't render " +
                  item.id +
                  ". Delayed until parent is rendered.",
              );
              // Postpone the data row until the parent is rendered
              delayed_models = this.delayedModels[scimetaParent];

              // Delay the model rendering if it isn't already delayed
              if (typeof delayed_models !== "undefined") {
                if (!_.contains(delayed_models, item)) {
                  delayed_models.push(item);
                  this.delayedModels[scimetaParent] = delayed_models;
                }
              } else {
                delayed_models = [];
                delayed_models.push(item);
                this.delayedModels[scimetaParent] = delayed_models;
              }
            }
          }
        } else {
          // This is a metadata folder row, append it to the table
          this.$el.append(dataItemView.render().el);

          this.trigger("addOne");
        }
      },

      /**
       * Render the Data Package View and insert it into this view
       */
      renderDataPackage: function () {
        var view = this;

        if (MetacatUI.rootDataPackage.packageModel.isNew()) {
          view.renderMember(this.model);
        }

        // As the root collection is updated with models, render the UI
        this.listenTo(MetacatUI.rootDataPackage, "add", function (model) {
          if (!model.get("synced") && model.get("id"))
            this.listenTo(model, "sync", view.renderMember);
          else if (model.get("synced")) view.renderMember(model);

          //Listen for changes on this member
          model.on("change:fileName", model.addToUploadQueue);
        });

        //Render the Data Package view
        this.dataPackageView = new DataPackageView({
          edit: true,
          dataPackage: MetacatUI.rootDataPackage,
          parentEditorView: this,
        });

        //Render the view
        var $packageTableContainer = this.$("#data-package-container");
        $packageTableContainer.html(this.dataPackageView.render().el);

        //Make the view resizable on the bottom
        var handle = $(document.createElement("div"))
          .addClass("ui-resizable-handle ui-resizable-s")
          .attr("title", "Drag to resize")
          .append(
            $(document.createElement("i")).addClass("icon icon-caret-down"),
          );
        $packageTableContainer.after(handle);
        $packageTableContainer.resizable({
          handles: { s: handle },
          minHeight: 100,
          maxHeight: 900,
          resize: function () {
            view.emlView.resizeTOC();
          },
        });

        var tableHeight = ($(window).height() - $("#Navbar").height()) * 0.4;
        $packageTableContainer.css("height", tableHeight + "px");

        var table = this.dataPackageView.$el;
        this.listenTo(this.dataPackageView, "addOne", function () {
          if (
            table.outerHeight() > $packageTableContainer.outerHeight() &&
            table.outerHeight() < 220
          ) {
            $packageTableContainer.css(
              "height",
              table.outerHeight() + handle.outerHeight(),
            );
            if (this.emlView) this.emlView.resizeTOC();
          }
        });

        if (this.emlView) this.emlView.resizeTOC();

        //Save the view as a subview
        this.subviews.push(this.dataPackageView);

        this.listenTo(
          MetacatUI.rootDataPackage.packageModel,
          "change:childPackages",
          this.renderChildren,
        );
      },

      /**
       * Add all rows to the DataPackageView
       */
      addAll: function () {
        this.$el.find("#data-package-table-body").html(""); // clear the table first
        this.dataPackage.sort();

        if (!this.edit) {
          var atLocationObj = this.dataPackage.getAtLocation();
          this.atLocationObj = atLocationObj;

          // form path to D1 object dictionary
          if (
            this.atLocationObj !== undefined &&
            !_.isEmpty(this.atLocationObj)
          ) {
            var filePathObj = new Object();

            this.dataPackage.each(function (item) {
              if (!Object.keys(this.atLocationObj).includes(item.id)) {
                this.atLocationObj[item.id] = "/";
              }
            }, this);

            for (let key of Object.keys(this.atLocationObj)) {
              var path = this.atLocationObj[key];
              var pathArray = path.split("/");
              pathArray.pop();
              var parentPath = pathArray.join("/");
              if (filePathObj.hasOwnProperty(parentPath)) {
                filePathObj[parentPath].push(key);
              } else {
                filePathObj[parentPath] = new Array();
                filePathObj[parentPath].push(key);
              }
            }
          }

          // add top level data package row to the package table
          var tableRow = null,
            view = this,
            title = this.packageTitle,
            packageUrl = null;

          if (title === "") {
            let metadataObj = _.filter(this.dataPackage.models, function (m) {
              return m.get("id") == view.currentlyViewing;
            });

            if (metadataObj.length > 0) {
              title = metadataObj[0].get("title");
              let metaId = metadataObj[0].get("id");
              this.metaId = metaId;
            } else {
              title = this.dataPackage.get("id");
            }
          }

          let titleTooltip = title;
          title =
            title.length > 150
              ? title.slice(0, 75) +
                "..." +
                title.slice(title.length - 75, title.length)
              : title;

          // set the package URL
          if (MetacatUI.appModel.get("packageServiceUrl"))
            packageUrl =
              MetacatUI.appModel.get("packageServiceUrl") +
              encodeURIComponent(view.dataPackage.id);

          var disablePackageDownloads = this.disablePackageDownloads;
          tableRow = this.dataPackageHeaderTemplate({
            id: view.dataPackage.id,
            title: title,
            titleTooltip: titleTooltip,
            downloadUrl: packageUrl,
            disablePackageDownloads: disablePackageDownloads,
          });

          this.$el.append(tableRow);

          if (this.atLocationObj !== undefined && filePathObj !== undefined) {
            // sort the filePath by length
            var sortedFilePathObj = Object.keys(filePathObj)
              .sort()
              .reduce((obj, key) => {
                obj[key] = filePathObj[key];
                return obj;
              }, {});
            this.sortedFilePathObj = sortedFilePathObj;

            this.addFilesAndFolders(sortedFilePathObj);
          } else {
            this.dataPackage.each(this.addOne, this, this.dataPackage);
          }
        } else {
          this.dataPackage.each(this.addOne, this);
        }
      },

      /**
       * Add all the files and folders
       */
      addFilesAndFolders: function (sortedFilePathObj) {
        if (!sortedFilePathObj) return false;
        var insertedPath = new Array();
        let pathMap = new Object();
        pathMap[""] = "";

        for (let key of Object.keys(sortedFilePathObj)) {
          // add folder
          var pathArray = key.split("/");
          //skip the first empty value
          for (let i = 0; i < pathArray.length; i++) {
            if (pathArray[i].length < 1) continue;

            if (!(pathArray[i] in pathMap)) {
              // insert path
              var dataItemView, itemPath;

              // root
              if (i == 0) {
                itemPath = "";
              } else {
                itemPath = pathMap[pathArray[i - 1]];
              }

              dataItemView = new DataItemView({
                mode: this.mode,
                itemName: pathArray[i],
                itemPath: itemPath,
                itemType: "folder",
                parentEditorView: this.parentEditorView,
                dataPackageId: this.dataPackage.id,
              });

              this.subviews[pathArray[i]] = dataItemView; // keep track of all views

              this.$el.append(dataItemView.render().el);

              this.trigger("addOne");

              pathMap[pathArray[i]] = itemPath + "/" + pathArray[i];
            }
          }

          // add files in the folder
          var itemArray = sortedFilePathObj[key];

          // Add metadata object at the top of the file table
          if (
            key == "" &&
            this.metaId !== "undefined" &&
            itemArray.includes(this.metaId)
          ) {
            let item = this.metaId;
            this.addOne(this.dataPackage.get(item));
          }

          for (let i = 0; i < itemArray.length; i++) {
            let item = itemArray[i];
            this.addOne(this.dataPackage.get(item));
          }
        }
      },

      /**
                Remove the subview represented by the given model item.

                @param item The model representing the sub view to be removed
            */
      removeOne: function (item) {
        if (_.contains(Object.keys(this.subviews), item.id)) {
          // Remove the view and the its reference in the subviews list
          this.subviews[item.id].remove();
          delete this.subviews[item.id];
        }
      },

      handleAddFiles: function (e) {
        //Pass this on to the DataItemView for the root data package
        this.$(".data-package-item.folder")
          .first()
          .data("view")
          .handleAddFiles(e);
      },

      /**
       * Renders a control that opens the AccessPolicyView for editing permissions on this package
       * @since 2.15.0
       */
      renderShareControl: function () {
        if (
          this.parentEditorView &&
          !this.parentEditorView.isAccessPolicyEditEnabled()
        ) {
          this.$("#data-package-table-share").remove();
        }
      },

      /**
       * Close subviews as needed
       */
      onClose: function () {
        // Close each subview
        _.each(
          Object.keys(this.subviews),
          function (id) {
            var subview = this.subviews[id];
            subview.onClose();
          },
          this,
        );

        //Reset the subviews from the view completely (by removing it from the prototype)
        this.__proto__.subviews = {};
      },

      /**
             Show or hide the data rows associated with the event row science metadata
             */
      toggleRows: function (event) {
        if (this.isOpen) {
          // Get the DataItemView associated with each id
          _.each(
            Object.keys(this.subviews),
            function (id) {
              var subview = this.subviews[id];

              if (subview.model.get("type") === "Data" && subview.remove) {
                // Remove the view from the DOM
                subview.remove();
                // And from the subviews list
                delete this.subviews[id];
              }
            },
            this,
          );

          // And then close the folder
          this.$el
            .find(".open")
            .removeClass("open")
            .addClass("closed")
            .removeClass("icon-chevron-down")
            .addClass("icon-chevron-right");

          this.$el
            .find(".icon-folder-open")
            .removeClass("icon-folder-open")
            .addClass("icon-folder-close");

          this.isOpen = false;
        } else {
          // Add sub rows to the view
          var dataModels = this.dataPackage.where({ type: "Data" });
          _.each(
            dataModels,
            function (model) {
              this.addOne(model);
            },
            this,
          );

          // And then open the folder
          this.$el
            .find(".closed")
            .removeClass("closed")
            .addClass("open")
            .removeClass("icon-folder-close")
            .addClass("icon-chevron-down");

          this.$el
            .find(".icon-folder-close")
            .removeClass("icon-folder-close")
            .addClass("icon-folder-open");

          this.isOpen = true;
        }

        event.stopPropagation();
        event.preventDefault();
      },

      /**
       * Expand function to show hidden rows when a user clicks on an expand control.
       * @param {Event} e - The event object.
       * @since 2.28.0
       */
      expand: function (e) {
        // Don't do anything...
        e.preventDefault();

        var view = this;
        var eventEl = $(e.target).parents("td");
        var rowEl = $(e.target).parents("tr");

        var parentId = rowEl.data("id");
        var children = "tr[data-parent='" + parentId + "']";

        this.$(children).fadeIn();

        this.$(eventEl)
          .children()
          .children(".expand-control")
          .fadeOut(function () {
            view
              .$(eventEl)
              .children()
              .children(".collapse-control")
              .fadeIn("fast");
            view.$(".tooltip-this").tooltip();
          });

        this.$(children)
          .children()
          .children()
          .children(".collapse-control")
          .fadeOut(function () {
            view
              .$(children)
              .children()
              .children()
              .children(".expand-control")
              .fadeIn("fast");
          });
      },

      /**
       * Collapse function to hide rows when a user clicks on a collapse control.
       * @param {Event} e - The event object.
       *
       * @since 2.28.0
       */
      collapse: function (e) {
        // Don't do anything...
        e.preventDefault();

        var view = this;
        var eventEl = $(e.target).parents("td");
        var rowEl = $(e.target).parents("tr");

        var parentId = rowEl.data("id");
        var children = "tr[data-parent^='" + parentId + "']";
        this.$(children).fadeOut();

        this.$(eventEl)
          .children()
          .children(".collapse-control")
          .fadeOut(function () {
            view.$(eventEl).children().children(".expand-control").fadeIn();
            view.$(".tooltip-this").tooltip();
          });
      },

      /**
       * Expand all function to show all child rows when a user clicks on an expand-all control.
       * @param {Event} e - The event object.
       *
       * @since 2.28.0
       */
      expandAll: function (e) {
        // Don't do anything...
        e.preventDefault();

        var view = this;
        var eventEl = $(e.target).parents("td");
        var rowEl = $(e.target).parents("tr");

        var parentId = rowEl.data("id");
        var children = "tr[data-packageid='" + parentId + "']";

        this.$(children).fadeIn();

        this.$(eventEl)
          .children(".d1package-expand")
          .fadeOut(function () {
            view.$(eventEl).children(".d1package-collapse").fadeIn("fast");
            view.$(".tooltip-this").tooltip();
          });

        this.$(children)
          .children()
          .children()
          .children(".collapse-control")
          .fadeOut(function () {
            view
              .$(children)
              .children()
              .children()
              .children(".expand-control")
              .fadeIn("fast");
          });
      },

      /**
       * Collapse all function to hide all child rows when a user clicks on a collapse-all control.
       * @param {Event} e - The event object.
       *
       * @since 2.28.0
       */
      collapseAll: function (e) {
        // Don't do anything...
        e.preventDefault();

        var view = this;
        var eventEl = $(e.target).parents("td");
        var rowEl = $(e.target).parents("tr");

        var parentId = rowEl.data("id");
        var children = "tr[data-packageid='" + parentId + "']";

        this.$(children).each(function () {
          $(this).fadeOut();
          let childId = $(this).data("id");
          let grandchildren = "tr[data-parent^='" + childId + "']";

          $(grandchildren).fadeOut();
        });

        this.$(eventEl)
          .children(".d1package-collapse")
          .fadeOut(function () {
            view.$(eventEl).children(".d1package-expand").fadeIn();
            view.$(".tooltip-this").tooltip();
          });
      },

      /**
       * Check for private members and disable download buttons if necessary.
       *
       * @since 2.28.0
       */
      checkForPrivateMembers: function () {
        try {
          var packageModel = this.model,
            packageCollection = this.dataPackage;

          if (!packageModel || !packageCollection) {
            return;
          }

          var numMembersFromSolr = packageModel.get("members").length,
            numMembersFromRDF = packageCollection.length;

          if (numMembersFromRDF > numMembersFromSolr) {
            var downloadButtons = this.$(".btn.download");

            for (var i = 0; i < downloadButtons.length; i++) {
              var btn = downloadButtons[i];
              var downloadURL = $(btn).attr("href");

              if (
                downloadURL.indexOf(packageModel.get("id")) > -1 ||
                downloadURL.indexOf(
                  encodeURIComponent(packageModel.get("id")),
                ) > -1
              ) {
                $(btn)
                  .attr("disabled", "disabled")
                  .addClass("disabled")
                  .attr("href", "")
                  .tooltip({
                    trigger: "hover",
                    placement: "top",
                    delay: 500,
                    title:
                      "This dataset may contain private data, so each data file should be downloaded individually.",
                  });

                i = downloadButtons.length;
              }
            }
          }
        } catch (e) {
          console.error(e);
        }
      },

      /**
       * Retrieves and processes nested packages for the current package.
       *
       * @since 2.28.0
       */
      getNestedPackages: function () {
        var nestedPackages = new Array();
        var nestedPackageIds = new Array();
        this.nestedPackages = nestedPackages;

        // get all the child packages for this resource map
        var childPackages = this.dataPackage.filter(function (m) {
          return m.get("formatType") === "RESOURCE";
        });

        // iterate over the list of child packages and add their members
        for (var ite in childPackages) {
          var childPkg = childPackages[ite];
          if (!nestedPackageIds.includes(childPkg.get("id"))) {
            var nestedPackage = new PackageModel();
            nestedPackage.set("id", childPkg.get("id"));
            nestedPackage.setURL();
            nestedPackage.getMembers();
            nestedPackages.push(nestedPackage);
            nestedPackageIds.push(childPkg.get("id"));

            this.listenToOnce(
              nestedPackage,
              "change:members",
              this.addNestedPackages,
              nestedPackage,
            );
          }
        }
      },

      /**
       * Adds a nested data package to the package table.
       *
       * @param {Object} dataPackage - The data package to be added.
       * @since 2.28.0
       */
      addNestedPackages: function (dataPackage) {
        /**
         * Generates the table row for the data package header.
         *
         * @type {null|Element}
         */
        var tableRow = null,
          /**
           * Reference to the current view.
           *
           * @type {Object}
           */
          view = this,
          /**
           * The title of the data package.
           *
           * @type {null|string}
           */
          title = null,
          /**
           * The URL of the data package.
           *
           * @type {null|string}
           */
          packageUrl = null;

        /**
         * The members of the data package.
         *
         * @type {Array}
         */
        var members = dataPackage.get("members");
        /**
         * Filters out metadata objects from the members.
         *
         * @type {Array}
         */
        let metadataObj = _.filter(members, function (m) {
          return m.get("type") == "Metadata" || m.get("type") == "metadata";
        });

        title = metadataObj[0].get("title");

        /**
         * The tooltip for the title (used for long titles).
         *
         * @type {string}
         */
        let titleTooltip = title;
        title =
          title.length > 150
            ? title.slice(0, 75) +
              "..." +
              title.slice(title.length - 75, title.length)
            : title;

        // Set the package URL
        if (MetacatUI.appModel.get("packageServiceUrl"))
          packageUrl =
            MetacatUI.appModel.get("packageServiceUrl") +
            encodeURIComponent(dataPackage.id);

        /**
         * The HTML content for the data package header.
         *
         * @type {string}
         */
        tableRow = this.dataPackageHeaderTemplate({
          id: dataPackage.id,
          title: title,
          titleTooltip: titleTooltip,
          disablePackageDownloads: false,
          downloadUrl: packageUrl,
        });
        this.$el.append(tableRow);

        // Create an instance of DownloadButtonView to handle package downloads
        this.downloadButtonView = new DownloadButtonView({
          model: dataPackage,
          view: "actionsView",
          nested: true,
        });

        // Render
        this.downloadButtonView.render();

        // Add the downloadButtonView el to the span
        this.$el.find(".downloadAction").html(this.downloadButtonView.el);

        // Filter out the packages from the member list
        members = _.filter(members, function (m) {
          return m.type != "Package";
        });

        // Add each member to the package table view
        var view = this;
        _.each(members, function (m) {
          // Update the size to bytes format
          m.set({ size: m.bytesToSize(m.get("size")) });

          // Add each item of this nested package to the package table view
          view.addOne(m, dataPackage);
        });
      },

      /*showDownloadProgress: function(e){
                e.preventDefault();

                var button = $(e.target);
                button.addClass("in-progress");
                button.html("Downloading... <i class='icon icon-on-right icon-spinner icon-spin'></i>");

                return true;

            }*/
    },
  );
  return DataPackageView;
});