/* global define */ define(['jquery', 'underscore', 'backbone', 'models/DataONEObject', 'models/metadata/eml211/EMLText'], function($, _, Backbone, DataONEObject, EMLText) { var EMLMethods = Backbone.Model.extend({ defaults: function(){ return { objectXML: null, objectDOM: null, methodStepDescription: [], studyExtentDescription: null, samplingDescription: null } }, initialize: function(attributes){ attributes = attributes || {}; if(attributes.objectDOM) this.parse(attributes.objectDOM); //specific attributes to listen to this.on("change:methodStepDescription change:studyExtentDescription change:samplingDescription", this.trickleUpChange); }, /* * Maps the lower-case EML node names (valid in HTML DOM) to the camel-cased EML node names (valid in EML). * Used during parse() and serialize() */ nodeNameMap: function(){ return { "methodstep" : "methodStep", "substep" : "subStep", "datasource" : "dataSource", "studyextent" : "studyExtent", "samplingdescription" : "samplingDescription", "spatialsamplingunits" : "spatialSamplingUnits", "referencedentityid" : "referencedEntityId", "qualitycontrol" : "qualityControl" } }, parse: function(objectDOM) { var modelJSON = {}; if (!objectDOM) var objectDOM = this.get("objectDOM"); var model = this; this.set('methodStepDescription', _.map($(objectDOM).find('methodstep description'), function(el, i) { return new EMLText({ objectDOM: el, type: 'description', parentModel: model }); })); if ($(objectDOM).find('sampling studyextent description').length > 0) { this.set('studyExtentDescription', new EMLText({ objectDOM: $(objectDOM).find('sampling studyextent description').get(0), type: 'description', parentModel: model })); } if ($(objectDOM).find('sampling samplingdescription').length > 0) { this.set('samplingDescription', new EMLText({ objectDOM: $(objectDOM).find('sampling samplingdescription').get(0), type: 'samplingDescription', parentModel: model })); } return modelJSON; }, serialize: function(){ var objectDOM = this.updateDOM(); if(!objectDOM) return ""; var xmlString = objectDOM.outerHTML; //Camel-case the XML xmlString = this.formatXML(xmlString); return xmlString; }, /* * Makes a copy of the original XML DOM and updates it with the new values from the model. */ updateDOM: function(){ var objectDOM; if (this.get("objectDOM")) { objectDOM = this.get("objectDOM").cloneNode(true); } else { objectDOM = $(document.createElement("methods")); } objectDOM = $(objectDOM); var methodStepsFromModel = this.get('methodStepDescription'), methodStepsFromDOM = $(objectDOM).find("methodstep"), numMethods = methodStepsFromModel.length; //If there are no method steps or they are all empty... if ( methodStepsFromModel.length == 0 || _.every(methodStepsFromModel, function(step){ return step.isEmpty(); }) ){ //Remove all existing method steps from the EML methodStepsFromDOM.remove(); // If there are no method steps but there is sampling metadata, then insert an empty method step if( (this.get('samplingDescription') && !this.get('samplingDescription').isEmpty()) || (this.get('studyExtentDescription') && !this.get('studyExtentDescription').isEmpty()) ){ objectDOM.prepend("No method step description provided."); numMethods = 0; } } else{ //Update the method step descriptions _.each(methodStepsFromModel, function(stepDescription, i) { //If there is a method step node in the DOM at this position, then update it if( methodStepsFromDOM[i] ){ if( stepDescription.isEmpty() || stepDescription.get("text") == "No method step description provided." ){ $(methodStepsFromDOM[i]).remove(); } else{ $(methodStepsFromDOM[i]).children("description") .replaceWith( stepDescription.updateDOM() ); } } //Otherwise, create a new method step node else if( !stepDescription.isEmpty() ){ var lastMethodStep = objectDOM.find("methodstep").last(); //If there is already one method step, insert this one after it if( lastMethodStep.length ){ lastMethodStep.after( $(document.createElement('methodStep')) .append( stepDescription.updateDOM() ) ); } //Otherwise if there is no existing method step, append it to the parent DOM element else{ objectDOM.append( $(document.createElement('methodStep')) .append( stepDescription.updateDOM() ) ); } } }); } // Update the sampling metadata if (this.get('samplingDescription') || this.get('studyExtentDescription')) { var samplingEl = $(document.createElement('sampling')), studyExtentEl = $(document.createElement('studyExtent')), missingStudyExtent = false, missingDescription = false; //If there is a study extent description, then create a DOM element for it and append it to the parent node if (this.get('studyExtentDescription') && !this.get('studyExtentDescription').isEmpty()) { $(studyExtentEl).append(this.get('studyExtentDescription').updateDOM()); //If the text matches the default "filler" text, then mark it as missing if( this.get('studyExtentDescription').get("text")[0] == "No study extent description provided."){ missingStudyExtent = true; } } //If there isn't a study extent description, then mark it as missing and append the default "filler" text else { missingStudyExtent = true; $(studyExtentEl).append($(document.createElement('description')).html("No study extent description provided.")); } //Add the study extent element to the sampling element $(samplingEl).append(studyExtentEl); //If there is a sampling description, then create a DOM element for it and append it to the parent node if (this.get('samplingDescription') && !this.get('samplingDescription').isEmpty()) { $(samplingEl).append(this.get('samplingDescription').updateDOM()); //If the text matches the default "filler" text, then mark it as missing if( this.get('samplingDescription').get("text")[0] == "No sampling description provided."){ missingDescription = true; } } //If there isn't a study extent description, then mark it as missing and append the default "filler" text else { missingDescription = true; $(samplingEl).append($(document.createElement('samplingDescription')).html("No sampling description provided.")); } //Find the existing element var existingSampling = objectDOM.find("sampling"); //Remove all the sampling nodes if there is no study extent and no description if(missingStudyExtent && missingDescription){ existingSampling.remove(); //If there are no method steps either, make sure their DOM elements are removed if(numMethods == 0){ objectDOM.find("methodstep").remove(); } } //Replace the existing sampling element, if it exists else if( existingSampling.length > 0 ){ existingSampling.replaceWith(samplingEl); } //Or append a new one else{ objectDOM.append(samplingEl); } } // Remove empty (zero-length or whitespace-only) nodes objectDOM.find("*").filter(function() { return $.trim(this.innerHTML) === ""; } ).remove(); //Check if all the content is filler content. This means there are no method steps, no sampling description, and // no study extent description. if( objectDOM.find("methodstep").length == 1 && objectDOM.find("methodstep description para").text() == "No method step description provided." && objectDOM.find("samplingdescription").length == 1 && objectDOM.find("samplingdescription para").text() == "No sampling description provided." && objectDOM.find("studyextent").length == 1 && objectDOM.find("studyextent description para").text() == "No study extent description provided." ){ //If it is all empty / filler content, then totally remove the methods return ""; } //If there are sampling nodes listed before methodStep nodes, then reorder them if( objectDOM.children().index(objectDOM.find("methodstep").last()) > objectDOM.children().index(objectDOM.find("sampling").last()) ){ //Detach all the sampling nodes and append them to the parent node objectDOM.append( objectDOM.children("sampling").detach() ); } //If there is more than one method step, remove any that have the default filler text if( objectDOM.find("methodstep").length > 1 ){ objectDOM.find("methodstep:contains('No method step description provided.')").remove(); } //If there are sampling nodes but no method nodes, make method nodes if( objectDOM.find("samplingdescription").length > 0 && objectDOM.find("studyextent").length > 0 && objectDOM.find("methodstep").length == 0){ objectDOM.prepend("No method step description provided."); } return objectDOM; }, /* * function isEmpty() - Will check if there are any values set on this model * that are different than the default values and would be serialized to the EML. * * @return {boolean} - Returns true is this model is empty, false if not */ isEmpty: function(){ var methodsStepsEmpty = false, studyExtentEmpty = false, samplingEmpty = false; if( !this.get("methodStepDescription").length || !this.get("methodStepDescription") || (this.get("methodStepDescription").length == 1 && this.get("methodStepDescription")[0].get("text").length == 1 && this.get("methodStepDescription")[0].get("text")[0] == "No method step description provided.") || (this.get("methodStepDescription").length && _.every(this.get("methodStepDescription"), function(emlText){ if(emlText.isEmpty()){ return true; } else if(_.every(emlText.get("text"), function(text){ return (text.trim() == "No method step description provided." || text.trim().length == 0); })){ return true; } })) ){ methodsStepsEmpty = true; } if( this.get("studyExtentDescription") == this.defaults().studyExtentDescription || !this.get("studyExtentDescription") || (this.get("studyExtentDescription").isEmpty && this.get("studyExtentDescription").isEmpty()) || (Array.isArray(this.get("studyExtentDescription")) && !this.get("studyExtentDescription").length ) || (Array.isArray(this.get("studyExtentDescription")) && this.get("studyExtentDescription").length == 1 && this.get("studyExtentDescription")[0].get("text").length == 1 && this.get("studyExtentDescription")[0].get("text")[0] == "No study extent description provided.") ){ studyExtentEmpty = true; } if( this.get("samplingDescription") == this.defaults().samplingDescription || !this.get("samplingDescription") || (this.get("samplingDescription").isEmpty && this.get("samplingDescription").isEmpty()) || (Array.isArray(this.get("samplingDescription")) && !this.get("samplingDescription").length ) || (Array.isArray(this.get("samplingDescription")) && this.get("samplingDescription").length == 1 && this.get("samplingDescription")[0].get("text").length == 1 && this.get("samplingDescription")[0].get("text")[0] == "No sampling description provided.") ){ samplingEmpty = true; } if( methodsStepsEmpty && studyExtentEmpty && samplingEmpty ) return true; }, /* * Climbs up the model heirarchy until it finds the EML model * * @return {EML211 or false} - Returns the EML 211 Model or false if not found */ getParentEML: function(){ var emlModel = this.get("parentModel"), tries = 0; while (emlModel.type !== "EML" && tries < 6){ emlModel = emlModel.get("parentModel"); tries++; } if( emlModel && emlModel.type == "EML") return emlModel; else return false; }, trickleUpChange: function(){ MetacatUI.rootDataPackage.packageModel.set("changed", true); }, formatXML: function(xmlString){ return DataONEObject.prototype.formatXML.call(this, xmlString); } }); return EMLMethods; });