/* global define */
define(['underscore', 'jquery', 'backbone', 'models/DataONEObject',
        'models/metadata/eml211/EML211', 'models/metadata/eml211/EMLOtherEntity',
        'text!templates/dataItem.html'],
    function(_, $, Backbone, DataONEObject, EML, EMLOtherEntity, DataItemTemplate){
        /**
        * @class DataItemView
        * @classdesc    A DataItemView represents a single data item in a data package as a single row of
            a nested table.  An item may represent a metadata object (as a folder), or a data
            object described by the metadata (as a file).  Every metadata DataItemView has a
            resource map associated with it that describes the relationships between the
            aggregated metadata and data objects.
        * @classcategory Views
        * @constructor
        */
        var DataItemView = Backbone.View.extend(
          /** @lends DataItemView.prototype */{
            tagName: "tr",
            className: "data-package-item",
            id: null,
            /* The HTML template for a data item */
            template: _.template(DataItemTemplate),
            /* Events this view listens to */
            events: {
                "focusout .name"       : "updateName",
                "click    .name"       : "emptyName",
                "click .duplicate"     : "duplicate",         // Edit dropdown, duplicate scimeta/rdf
                "click .addFolder"     : "handleAddFolder",   // Edit dropdown, add nested scimeta/rdf
                "click .addFiles"      : "handleAddFiles",    // Edit dropdown, open file picker dialog
                "change .file-upload"  : "addFiles",          // Adds the files into the collection
                "change .file-replace" : "replaceFile",       // Replace a file in the collection
                "dragover"             : "showDropzone",      // Drag & drop, show the dropzone for this row
                "dragend"              : "hideDropzone",      // Drag & drop, hide the dropzone for this row
                "dragleave"            : "hideDropzone",      // Drag & drop, hide the dropzone for this row
                "drop"                 : "addFiles",          // Drag & drop, adds the files into the collection
                "click .replaceFile"   : "handleReplace",     // Replace dropdown, data in collection
                "click .removeFiles"   : "handleRemove",      // Edit dropdown, remove sci{data,meta} from collection
                "click .cancel"        : "handleCancel",      // Cancel a file load
                "change: percentLoaded": "updateLoadProgress", // Update the file read progress bar
                "mouseover .remove"    : "previewRemove",
                "mouseout  .remove"    : "previewRemove",
                "change .private"      : "changeAccessPolicy"
            },
            /* Initialize the object - post constructor */
            initialize: function(options) {
            	if(typeof options == "undefined") var options = {};
                this.model = options.model || new DataONEObject();
                this.id = this.model.get("id");
                this.canReplace = false; // Default. Updated in render()
            },
            /* Render the template into the DOM */
            render: function(model) {
            	//Prevent duplicate listeners
            	this.stopListening();
              // Set the data-id for identifying events to model ids
              this.$el.attr("data-id", this.model.get("id"));
              this.$el.attr("data-category", "entities-" + this.model.get("id"));
                //Destroy the old tooltip
                this.$(".status .icon, .status .progress").tooltip("hide").tooltip("destroy");
                var attributes = this.model.toJSON();
                //Format the title
                if(Array.isArray(attributes.title))
                	attributes.title  = attributes.title[0];
                //Set some defaults
                attributes.numAttributes = 0;
                attributes.entityIsValid = true;
                attributes.hasInvalidAttribute = false;
                // Restrict item replacement depending on access
                //
                // Note: .canReplace is set here (at render) instead of at init
                // because render will get called a few times during page load
                // as the app updates what it knows about the object
                this.canReplace = this.model.get("accessPolicy") &&
                    this.model.get("accessPolicy").isAuthorized("write");
                attributes.canReplace = this.canReplace; // Copy to template
                //Get the number of attributes for this item
                if(this.model.type != "EML"){
	                //Get the parent EML model
                	if( this.parentEML ){
                		var parentEML = this.parentEML;
                	}
                	else{
                		var parentEML = MetacatUI.rootDataPackage.where({
	                    	id: Array.isArray(this.model.get("isDocumentedBy")) ?
	                    			this.model.get("isDocumentedBy")[0] : null
	                	});
                	}
	                if( Array.isArray(parentEML) )
	                	parentEML = parentEML[0];
	                //If we found a parent EML model
	                if(parentEML && parentEML.type == "EML"){
	                	this.parentEML = parentEML;
	                	//Find the EMLEntity model for this data item
	                	var entity = this.model.get("metadataEntity") || parentEML.getEntity(this.model);
	                	//If we found an EMLEntity model
	                	if(entity){
	                		this.entity = entity;
	                		//Get the file name from the metadata if it is not in the model
	                		if( !this.model.get("fileName") ){
	                			var fileName = "";
	                			if( entity.get("physicalObjectName") )
	                				fileName = entity.get("physicalObjectName");
	                			else if( entity.get("entityName") )
	                				fileName = entity.get("entityName");
	                			if( fileName )
	                				attributes.fileName = fileName;
	                				this.model.set("fileName", fileName);
	                		}
	                		//Get the number of attributes for this entity
	                		attributes.numAttributes = entity.get("attributeList").length;
	                		//Determine if the entity model is valid
	                		attributes.entityIsValid = entity.isValid();
                      //Listen to changes to certain attributes of this EMLEntity model
                      // to re-render this view
                      this.stopListening(entity);
                      this.listenTo(entity, "change:entityType, change:entityName", this.render);
	                		//Check if there are any invalid attribute models
	                		//Also listen to each attribute model
                			_.each( entity.get("attributeList"), function(attr){
                				var isValid = attr.isValid();
                				//Mark that this entity has at least one invalid attribute
	                			if( !attributes.hasInvalidAttribute && !isValid )
	                				attributes.hasInvalidAttribute = true;
	                			this.stopListening(attr);
	                			//Listen to when the validation status changes and rerender
	                			if(isValid)
	                				this.listenTo( attr, "invalid", this.render);
	                			else
	                				this.listenTo( attr, "valid",   this.render);
	                		}, this);
	                		//If there are no attributes now, rerender when one is added
	                		this.listenTo(entity, "change:attributeList", this.render);
	                	}
	                	else{
	                		//Rerender when an entity is added
	                		this.listenTo(this.model, "change:entities", this.render);
	                	}
	                }
	                else{
	                	//When the package is complete, rerender
	                	this.listenTo(MetacatUI.rootDataPackage, "add:EML", this.render);
	                }
                }
                this.$el.html( this.template(attributes) );
                //Initialize dropdowns
                this.$el.find(".dropdown-toggle").dropdown();
                if(this.model.get("type") == "Metadata"){
                  //Add the title data-attribute attribute to the name cell
                	this.$el.find(".name").attr("data-attribute", "title");
                	this.$el.addClass("folder");
                }
                else{
                	this.$el.addClass("data");
                  //Get the AccessPolicy for this object
                  var accessPolicy = this.model.get("accessPolicy"),
                      checkbox = this.$(".sharing input");
                  //Check the public/private toggle if this object is private
                  if( accessPolicy && !accessPolicy.isPublic() ){
                    checkbox.prop("checked", true);
                  }
                  //If the user is not authorized to change the permissions of
                  // this object, then disable the checkbox
                  if( !accessPolicy.isAuthorized("changePermission") ){
                    checkbox.prop("disabled", "disabled")
                            .addClass("disabled");
                    this.$(".sharing").tooltip({
                      title: "You are not authorized to edit the privacy of this data file",
                      placement: "top",
                      container: this.el,
                      trigger: "hover",
                      delay: { show: 800 }
                    });
                  }
                  else{
                    checkbox.tooltip({
                      title: "Check to make this data file private",
                      placement: "top",
                      trigger: "hover",
                      delay: { show: 800 }
                    });
                  }
                }
                // Add tooltip to a disabled Replace link
                $(this.$el).find(".replace.disabled").tooltip({
                    title: "You don't have sufficient privileges to replace this item.",
                    placement: "left",
                    trigger: "hover",
                    delay: { show: 400 },
                    container: "body"
                  });
                //Check if the data package is in progress of being uploaded
                this.toggleSaving();
                //Create tooltips based on the upload status
                if(this.model.get("uploadStatus") == "e" && this.model.get("errorMessage")){
                	var errorMsg = this.model.get("errorMessage");
                	this.$(".status .icon").tooltip({
                		placement: "top",
                		trigger: "hover",
                		html: true,
                		title: "
",
                		container: "body"
                	});
                	this.$el.removeClass("loading");
                }
                else if (( !this.model.get("uploadStatus") || this.model.get("uploadStatus") == "c" || this.model.get("uploadStatus") == "q") && attributes.numAttributes == 0){
                	this.$(".status .icon").tooltip({
                		placement: "top",
                		trigger: "hover",
                		html: true,
                		title: "This file needs to be described - Click 'Describe'
",
                		container: "body"
                	});
            		this.$el.removeClass("loading");
            	}
                else if( attributes.hasInvalidAttribute || !attributes.entityIsValid ){
                	this.$(".status .icon").tooltip({
                		placement: "top",
                		trigger: "hover",
                		html: true,
                		title: "There is missing information about this file. Click 'Describe'
",
                		container: "body"
                	});
                	this.$el.removeClass("loading");
                }
                else if(this.model.get("uploadStatus") == "c"){
            		this.$(".status .icon").tooltip({
                		placement: "top",
                		trigger: "hover",
                		html: true,
                		title: "Complete
",
                		container: "body"
                	});
                	this.$el.removeClass("loading");
                }
                else if(this.model.get("uploadStatus") == "l"){
                	this.$(".status .icon").tooltip({
                		placement: "top",
                		trigger: "hover",
                		html: true,
                		title: "Reading file...
",
                		container: "body"
                	});
                	this.$el.addClass("loading");
                }
                else if(this.model.get("uploadStatus") == "p"){
                	var model = this.model;
                	this.$(".status .progress").tooltip({
                		placement: "top",
                		trigger: "hover",
                		html: true,
                		title: function(){
                			if(model.get("numSaveAttempts") > 0){
                				return "Something went wrong during upload. 
 Trying again... (attempt " + (model.get("numSaveAttempts") + 1) + " of 3)
";
                			}
                			else if(model.get("uploadProgress")){
                				var percentDone = model.get("uploadProgress").toString();
                				if(percentDone.indexOf(".") > -1)
                					percentDone = percentDone.substring(0, percentDone.indexOf("."));
                			}
                			else
                				var percentDone = "0";
                			return "Uploading: " + percentDone + "%
";
                		},
                		container: "body"
                	});
                	this.$el.addClass("loading");
                }
                else{
                	this.$el.removeClass("loading");
                }
                //Listen to changes to the upload progress of this object
                this.listenTo(this.model, "change:uploadProgress", this.showUploadProgress);
                //Listen to changes to the upload status of the entire package
                this.listenTo(MetacatUI.rootDataPackage.packageModel, "change:uploadStatus", this.toggleSaving);
                //listen for changes to rerender the view
                this.listenTo(this.model, "change:fileName change:title change:id change:formatType " +
                		"change:formatId change:type change:resourceMap change:documents change:isDocumentedBy " +
                		"change:size change:nodeLevel change:uploadStatus", this.render); // render changes to the item
                var view = this;
                this.listenTo(this.model, "replace", function(newModel){
                	view.model = newModel;
                	view.render();
                });
                this.$el.data({
                	view: this,
                	model: this.model
                	});
                return this;
            },
            /* Close the view and remove it from the DOM */
            onClose: function(){
                this.remove(); // remove for the DOM, stop listening
                this.off();    // remove callbacks, prevent zombies
            },
            /*
              Generate a unique id for each data item in the table
              TODO: This could be replaced with the DataONE identifier
            */
            generateId: function() {
                var idStr = ''; // the id to return
                var length = 30; // the length of the generated string
                var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'.split('');
                for (var i = 0; i < length; i++) {
                    idStr += chars[Math.floor(Math.random() * chars.length)];
                }
                return idStr;
            },
            /**
             * Update the folder name based on the scimeta title
             *
             * @param e The event triggering this method
             */
            updateName: function(e) {
                var enteredText = this.cleanInput($(e.target).text().trim());
                // Set the title if this item is metadata or set the file name
                // if its not
            	if(this.model.get("type") == "Metadata") {
            		var title = this.model.get("title");
                    // Get the current title which is either an array of titles
                    // or a single string. When it's an array of strings, we
                    // use the first as the canonical title
                    var currentTitle = Array.isArray(title) ? title[0] : title;
                    // Don't set the title if it hasn't changed or is empty
                    if (enteredText !== "" &&
                        currentTitle !== enteredText &&
                        enteredText !== "Untitled dataset") {
                        // Set the new title, upgrading any title attributes
                        // that aren't Arrays into Arrays
                        if ((Array.isArray(title) && title.length < 2) || typeof title == "string") {
                            this.model.set("title", [ enteredText ]);
                        } else {
                            title[0] = enteredText;
                        }
                    }
                } else {
                    this.model.set("fileName", enteredText);
                    // Reset sysMetaUploadStatus only if this item doesn't
                    // have content changes. This is here because replaceFile
                    // sets sysMetaUploadStatus to "c" to prevent the editor
                    // from updating sysmeta after the update call
                    if (!this.model.get("hasContentChanges")) {
                        this.model.set("sysMetaUploadStatus", null);
                    }
                }
            },
            /* Duplicate a file or folder */
            duplicate: function(event) {
            },
            /* Add a sub folder */
            addFolder: function(event) {
            },
            /*
                Handle the add file event, showing the file picker dialog
                Multiple files are allowed using the shift and or option/alt key
            */
            handleAddFiles: function(event) {
                event.stopPropagation();
                var fileUploadElement = this.$(".file-upload");
                fileUploadElement.val("");
                if ( fileUploadElement ) {
                    fileUploadElement.click();
                }
                event.preventDefault();
            },
            /*
                With a file list from the file picker or drag and drop,
                add the files to the collection
            */
            addFiles: function(event) {
                var fileList,            // The list of chosen files
                    parentDataPackage,   // The id of the first resource of this row's scimeta
                    self = this;         // A reference to this view
                event.stopPropagation();
                event.preventDefault();
                // handle drag and drop files
                if ( typeof event.originalEvent.dataTransfer !== "undefined" ) {
                    fileList = event.originalEvent.dataTransfer.files;
                // handle file picker files
                } else {
                    if ( event.target ) {
                        fileList = event.target.files;
                    }
                }
                this.$el.removeClass("droppable");
                // Find the correct collection to add to. Use JQuery's delegateTarget
                // attribute corresponding to the element where the event handler was attached
                if ( typeof event.delegateTarget.dataset.id !== "undefined" ) {
                    this.parentSciMeta = this.getParentScienceMetadata(event);
                    this.collection = this.getParentDataPackage(event);
                    // Read each file, and make a DataONEObject
                    _.each(fileList, function(file) {
                        var uploadStatus = "l",
                            errorMessage = "";
                        if( file.size == 0 ){
                          uploadStatus = "e";
                          errorMessage = "This is an empty file. It won't be included in the dataset.";
                        }
                        var dataONEObject = new DataONEObject({
                            synced: true,
                            type: "Data",
                            fileName: file.name,
                            size: file.size,
                            mediaType: file.type,
                            uploadFile: file,
                            uploadStatus: uploadStatus,
                            errorMessage: errorMessage,
                            isDocumentedBy: [this.parentSciMeta.id],
                            resourceMap: [this.collection.packageModel.id]
                        });
                        // Add it to the parent collection
                        this.collection.add(dataONEObject);
                        // Asychronously calculate the checksum
                        if ( dataONEObject.get("uploadFile") && ! dataONEObject.get("checksum") ) {
                            dataONEObject.stopListening(dataONEObject, "checksumCalculated");
                            dataONEObject.listenToOnce(dataONEObject, "checksumCalculated", dataONEObject.save);
                            try {
                                dataONEObject.calculateChecksum();
                            } catch (exception) {
                                // TODO: Fail gracefully here for the user
                            }
                        }
                    }, this);
                }
            },
            /* During file reading, update the progress bar */
            updateLoadProgress: function(event) {
                // TODO: Update the progress bar
            },
            /* Show the drop zone for this row in the table */
            showDropzone: function() {
                if ( this.model.get("type") !== "Metadata" ) return;
                this.$el.addClass("droppable");
            },
            /* Hide the drop zone for this row in the table */
            hideDropzone: function(event) {
                if ( this.model.get("type") !== "Metadata" ) return;
                this.$el.removeClass("droppable");
            },
            /**
             * Handle the user's click of the Replace item in the DataItemView
             * dropdown. Triggers replaceFile after some basic validation.
             *
             * Called indirectly via the "click" event on elements with the
             * class .replaceFile. See this View's events map.
             *
             * @param {MouseEvent} event: Browser Click event
             */
            handleReplace: function(event) {
                event.stopPropagation();
                // Stop immediately if we know the user doesn't have privs
                if (!this.canReplace) {
                    event.preventDefault();
                    return;
                }
                var fileReplaceElement = $(event.target)
                    .parents(".dropdown-menu")
                    .children(".file-replace")
                if (!fileReplaceElement) {
                    console.log("Unable to find Replace file picker.");
                    return;
                }
                fileReplaceElement.val("");
                fileReplaceElement.trigger("click");
                event.preventDefault();
            },
            /**
             * Replace a file (DataONEObject) in the collection with another one
             * from a file picker. Maintains attributes on the original
             * DataONEObject and maintains the entity information in the parent
             * collection's metadata record (i.e., keeps your attributes, etc.).
             *
             * Called indirectly via the "change" event on elements with the
             * class .file-upload. See this View's events map.
             *
             * The bulk of the work is done in a try-catch block to catch
             * mistakes that would cause the editor to get into a broken state.
             * On error, we attempt to return the editor back to its pre-replace
             * state.
             *
             * @param {Event}
             */
            replaceFile: function(event) {
                event.stopPropagation();
                event.preventDefault();
                if (!this.canReplace) {
                    return;
                }
                var fileList = event.target.files;
                // Pre-check fileList value to make sure we can work with it
                if (fileList.length != 1) {
                    // TODO: Show error, find out how to do this
                    return;
                }
                if (typeof event.delegateTarget.dataset.id === "undefined") {
                    // TODO: Show error, find out how to do this
                    return;
                }
                // Save uploadStatus for reverting if need to
                var oldUploadStatus = this.model.get("uploadStatus");
                var file = fileList[0],
                    uploadStatus = "q",
                    errorMessage = "";
                if (file.size == 0 ) {
                    uploadStatus = "e";
                    errorMessage = "This is an empty file. It won't be included in the dataset.";
                }
				if (!this.model) {
                    console.log("Couldn't find model we're supposed to be replacing. Stopping.");
					return;
				}
                // Copy model attributes aside for reverting on error
                var newAttributes = {
                    synced: false,
                    fileName: file.name,
                    size: file.size,
                    mediaType: file.type,
                    uploadFile: file,
                    hasContentChanges: true,
                    checksum: null,
                    uploadStatus: uploadStatus,
                    sysMetaUploadStatus: "c", // I set this so DataPackage::save
                    // wouldn't try to update the sysmeta after the update
                    errorMessage: errorMessage
                };
                // Save a copy of the attributes we're changing so we can revert
                // later if we encounter an exception
                var oldAttributes = {};
                _.each(Object.keys(newAttributes), function(k) {
                    oldAttributes[k] = _.clone(this.model.get(k));
                }, this);
                oldAttributes["uploadStatus"] = oldUploadStatus;
                try {
                    this.model.set(newAttributes);
                    // Attempt the formatId. Defaults to app/octet-stream
                    this.model.set("formatId", this.model.getFormatId());
                    // Grab a reference to the entity in the EML for the object
                    // we're replacing
                    this.parentSciMeta = this.getParentScienceMetadata(event);
                    var entity = null;
                    if (this.parentSciMeta) {
                        entity = this.parentSciMeta.getEntity(this.model);
                    }
                    // Eagerly update the PID for this object so we can update
                    // the matching EML entity
                    this.model.updateID();
                    // Update the EML entity with the new id
                    if (entity) {
                        entity.set("xmlID", this.model.getXMLSafeID());
                    }
                    this.render();
                    if (this.model.get("uploadFile") && !this.model.get("checksum")) {
                        try {
                            this.model.calculateChecksum();
                        } catch (exception) {
                            // TODO: Fail gracefully here for the user
                        }
                    }
                    MetacatUI.rootDataPackage.packageModel.set("changed", true);
                    // Last, provided a visual indication the replace was completed
                    var describeButton = this.$el
                        .children(".controls")
                        .children(".btn-group")
                        .children("button.edit")
                        .first();
                    if (describeButton.length != 1) {
                        return;
                    }
                    var oldText = describeButton.html();
                    describeButton.html(' Replaced');
                    var previousBtnClasses = describeButton.attr("class");
                    describeButton.removeClass("warning error").addClass("message");
                    window.setTimeout(function() {
                        describeButton.html(oldText);
                        describeButton.addClass(previousBtnClasses).removeClass("message");
                    }, 3000);
                } catch (error) {
                    console.log("Error replacing: ", error);
                    // Revert changes to the attributes
                    this.model.set(oldAttributes);
                    this.model.set("formatId", this.model.getFormatId());
                    this.model.set("sysMetaUploadStatus", "c"); // Prevents a sysmeta update
                    this.model.resetID();
                    this.render();
                }
                return;
            },
            /* Handle remove events for this row in the data package table */
            handleRemove: function(event) {
                var eventId,         // The id of the row of this event
                    removalIds = [], // The list of target ids to remove
                    dataONEObject,   // The model represented by this row
                    documents;       // The list of ids documented by this row (if meta)
                event.stopPropagation();
                event.preventDefault();
                // Get the row id, add it to the remove list
                if ( typeof event.delegateTarget.dataset.id !== "undefined" ) {
                    eventId = event.delegateTarget.dataset.id;
                    removalIds.push(eventId);
                }
                this.parentSciMeta = this.getParentScienceMetadata(event);
                if(!this.parentSciMeta){
                	this.$(".status .icon, .status .progress").tooltip("hide").tooltip("destroy");
                	// Remove the row
                    this.remove();
                    return;
                }
                this.collection = this.getParentDataPackage(event);
                // Get the corresponding model
                if ( typeof eventId !== "undefined" ) {
                    dataONEObject = this.collection.get(eventId);
                }
                // Is it nested science metadata?
                if ( dataONEObject && dataONEObject.get("type") == "Metadata" ) {
                    // We also remove the data documented by these metadata
                    documents = dataONEObject.get("documents");
                    if ( documents.length > 0 ) {
                        _.each(documents, removalIds.push());
                    }
                }
                //Data objects may need to be removed from the EML model entities list
                else if(dataONEObject && this.parentSciMeta.type == "EML"){
                	var matchingEntity = this.parentSciMeta.getEntity(dataONEObject);
                	if(matchingEntity)
                		this.parentSciMeta.removeEntity(matchingEntity);
                }
                // Remove the id from the documents array in the science metadata
                _.each(removalIds, function(id) {
                    var documents = this.parentSciMeta.get("documents");
                    var index = documents.indexOf(id);
                    if ( index > -1 ) {
                        this.parentSciMeta.get("documents").splice(index, 1);
                    }
                }, this);
                // Remove each object from the collection
                this.collection.remove(removalIds);
                this.$(".status .icon, .status .progress").tooltip("hide").tooltip("destroy");
                // Remove the row
                this.remove();
                MetacatUI.rootDataPackage.packageModel.set("changed", true);
            },
            /*
             * Return the parent science metadata model associated with the
             * data or metadata row of the UI event
             */
            getParentScienceMetadata: function(event) {
                var parentMetadata,  // The parent metadata array in the collection
                    eventModels,     // The models associated with the event's table row
                    eventModel,      // The model associated with the event's table row
                    parentSciMeta;   // The parent science metadata for the event model
                if ( typeof event.delegateTarget.dataset.id !== "undefined" ) {
                    eventModels = MetacatUI.rootDataPackage.where({
                        id: event.delegateTarget.dataset.id
                    });
                    if ( eventModels.length > 0 ) {
                        eventModel = eventModels[0];
                    } else {
                        return;
                    }
                    // Is this a Data or Metadata model?
                    if ( eventModel.get && eventModel.get("type") === "Metadata" ) {
                        return eventModel;
                    } else {
                        // It's data, get the parent scimeta
                        parentMetadata = MetacatUI.rootDataPackage.where({
                            id: Array.isArray(eventModel.get("isDocumentedBy"))? eventModel.get("isDocumentedBy")[0] : null
                        });
                        if ( parentMetadata.length > 0 ) {
                            parentSciMeta = parentMetadata[0];
                            return parentSciMeta;
                        } else {
                        	//If there is only one metadata model in the root data package, then use that metadata model
                        	var metadataModels = MetacatUI.rootDataPackage.where({
                                type: "Metadata"
                            });
                        	if(metadataModels.length == 1)
                        		return metadataModels[0];
                        }
                    }
                }
            },
            /*
             * Return the parent data package collection associated with the
             * data or metadata row of the UI event
             */
            getParentDataPackage: function(event) {
                var parentSciMeta,
                    parenResourceMaps,
                    parentResourceMapId;
                if ( typeof event.delegateTarget.dataset.id !== "undefined" ) {
                    parentSciMeta = this.getParentScienceMetadata(event);
                    if ( parentSciMeta.get && parentSciMeta.get("resourceMap").length > 0 ) {
                        parentResourceMaps = parentSciMeta.get("resourceMap");
                        if ( ! MetacatUI.rootDataPackage.packageModel.get("latestVersion") ) {
                            // Decide how to handle this by calling model.findLatestVersion()
                            // and listen for the result, setting getParentDataPackage() as the callback?
                        } else {
                            parentResourceMapId = MetacatUI.rootDataPackage.packageModel.get("latestVersion");
                        }
                    } else {
                        console.log("There is no resource map associated with the science metadata.");
                    }
                    // Is this the root package or a nested package?
                    if ( MetacatUI.rootDataPackage.packageModel.id === parentResourceMapId ) {
                        return MetacatUI.rootDataPackage;
                    // A nested package
                    } else {
                        return MetacatUI.rootDataPackage.where({id: parentResourceMapId})[0];
                    }
                }
            },
            cleanInput: function(input){
            	// 1. remove line breaks / Mso classes
      				var stringStripper = /(\n|\r| class=(")?Mso[a-zA-Z]+(")?)/g;
      				var output = input.replace(stringStripper, ' ');
      				// 2. strip Word generated HTML comments
      				var commentSripper = new RegExp('','g');
      				output = output.replace(commentSripper, '');
      				var tagStripper = new RegExp('<(/)*(meta|link|span|\\?xml:|st1:|o:|font)(.*?)>','gi');
      				// 3. remove tags leave content if any
      				output = output.replace(tagStripper, '');
      				// 4. Remove everything in between and including tags '