Source: src/js/models/metadata/eml211/EMLDataTable.js

define(["jquery", "underscore", "backbone", "models/metadata/eml211/EMLEntity"],
    function($, _, Backbone, EMLEntity) {

        /**
        * @class EMLDataTable
         * @classdesc EMLDataTable represents a tabular data entity, corresponding
         * with the EML dataTable module.
         * @classcategory Models/Metadata/EML211
         * @see https://eml.ecoinformatics.org/schema/eml-datatable_xsd
         */
        var EMLDataTable = EMLEntity.extend(
          /** @lends EMLDataTable.prototype */{

            //The class name for this model
            type: "EMLDataTable",

            /* Attributes of any entity */
            defaults: function(){
                return    _.extend({

                        /* Attributes from EML */
                        caseSensitive: null, // The case sensitivity of the table records
                        numberOfRecords: null, // the number of records in the table
                        type: "dataTable",

                        /* Attributes not from EML */
                        nodeOrder: [ // The order of the top level XML element nodes
                            "caseSensitive",
                            "numberOfRecords",
                            "references"
                        ],

                    }, EMLEntity.prototype.defaults());
            },

            /*
             * The map of lower case to camel case node names
             * needed to deal with parsing issues with $.parseHTML().
             * Use this until we can figure out issues with $.parseXML().
             */
            nodeNameMap: _.extend({
                "casesensitive" : "caseSensitive",
                "numberofrecords": "numberOfRecords"

            }, EMLEntity.prototype.nodeNameMap),

            /* Initialize an EMLDataTable object */
            initialize: function(attributes) {

                // if options.parse = true, Backbone will call parse()

                // Call super() first
                this.constructor.__super__.initialize.apply(this, [attributes]);

                // EMLDataTable-specific work
                this.set("type", "dataTable", {silent: true});

                // Register change events
                this.on( "change:caseSensitive change:numberOfRecords", EMLEntity.trickleUpChange);

            },

            /*
             * Parse the incoming other entity's XML elements
             */
            parse: function(attributes, options) {

                var attributes = attributes || {};

                // Call super() first
                attributes = this.constructor.__super__.parse.apply(this, [attributes, options]);

                // EMLDataTable-specific work
                var objectXML  = attributes.objectXML; // The dataTable XML fragment
                var objectDOM; // The W3C DOM of the object XML fragment
                var $objectDOM; // The JQuery object of the XML fragment

                // Use the updated objectDOM if we have it
                if ( attributes.objectDOM ) {
                    $objectDOM = $(attributes.objectDOM);
                } else {
                    // Hmm, oddly not there, start from scratch =/
                    $objectDOM = $(objectXML);
                }

                // Add the caseSensitive
                attributes.caseSensitive = $objectDOM.children("caseSensitive").text();

                // Add the numberOfRecords
                attributes.numberOfRecords = $objectDOM.children("numberOfRecords").text();

                // Add the references value
                attributes.references = $objectDOM.children("references").text();

                return attributes;
            },

            /* Copy the original XML and update fields in a DOM object */
            updateDOM: function(objectDOM) {
                var nodeToInsertAfter;
                var type = this.get("type") || "dataTable";
                if ( ! objectDOM ) {
                    objectDOM = this.get("objectDOM");
                }
                var objectXML = this.get("objectXML");

                // If present, use the cached DOM
                if ( objectDOM ) {
                    objectDOM = objectDOM.cloneNode(true);

                // otherwise, use the cached XML
                } else if ( objectXML ){
                    objectDOM = $(objectXML)[0].cloneNode(true);

                // This is new, create it
                } else {
                    objectDOM = document.createElement(type);

                }

                // Now call the superclass
                objectDOM = this.constructor.__super__.updateDOM.apply(this, [objectDOM]);

                // And then update the EMLDataTable-specific fields
                // Update the caseSensitive field
                if ( this.get("caseSensitive") ) {
                    if ( $(objectDOM).find("caseSensitive").length ) {
                        $(objectDOM).find("caseSensitive").text(this.get("caseSensitive"));

                    } else {
                        nodeToInsertAfter = this.getEMLPosition(objectDOM, "caseSensitive");

                        if ( ! nodeToInsertAfter ) {
                            $(objectDOM).append($(document.createElement("casesensitive"))
                                .text(this.get("caseSensitive"))[0]);
                        } else {
                            $(nodeToInsertAfter).after($(document.createElement("casesensitive"))
                                .text(this.get("caseSensitive"))[0]);
                        }
                    }
                }

                // Update the numberOfRecords field
                if ( this.get("numberOfRecords") ) {
                    if ( $(objectDOM).find("numberOfRecords").length ) {
                        $(objectDOM).find("numberOfRecords").text(this.get("numberOfRecords"));

                    } else {
                        nodeToInsertAfter = this.getEMLPosition(objectDOM, "numberOfRecords");

                        if ( ! nodeToInsertAfter ) {
                            $(objectDOM).append($(document.createElement("numberofrecords"))
                                .text(this.get("numberOfRecords"))[0]);
                        } else {
                            $(nodeToInsertAfter).after($(document.createElement("numberofrecords"))
                                .text(this.get("numberOfRecords"))[0]);
                        }
                    }
                }

                return objectDOM;
            },

            /* Serialize the EML DOM to XML */
            serialize: function() {

                var xmlString = "";

                // Update the superclass fields in the objectDOM first
                var objectDOM = this.constructor.__super__.updateDOM.apply(this, []);

                // Then update the subclass fields in the objectDOM
                // TODO


                this.set("objectXML", xmlString);

                return xmlString;
            },

            /* Validate the datable's required fields */
            validate: function(){

              var errors = {};

              // Require the entity name
              if( !this.get("entityName") ) {
                  errors.entityName = "Please specify an data table name.";
              }

              //Validate the attributes
              var attributeErrors = this.validateAttributes();
              if(attributeErrors.length)
                errors.attributeList = errors;

              // Require the attribute list
              /*if( !this.get("attributeList").length ) {
                  errors.attributeList = "Please describe the table attributes (columns).";
              }*/

              if( Object.keys(errors).length ){
                return errors;
              }
              else{
                return false;
              }
            },

            /*
            * 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;

            }

        });

        return EMLDataTable;
    }
);