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

define(["jquery", "underscore", "backbone"],
  function ($, _, Backbone) {

    /**
     * @class EMLAnnotation
     * @classdesc Stores EML SemanticAnnotation elements.
     * @classcategory Models/Metadata/EML211
     * @see https://eml.ecoinformatics.org/eml-2.2.0/eml-semantics.xsd
     * @extends Backbone.Model
     */
    var EMLAnnotation = Backbone.Model.extend(
          /** @lends EMLAnnotation.prototype */{

        type: "EMLAnnotation",

        defaults: function () {
          return {
            isNew: true,
            propertyLabel: null,
            propertyURI: null,
            valueLabel: null,
            valueURI: null,
            objectDOM: null,
            objectXML: null
          }
        },

        initialize: function (attributes, opions) {
          this.on("change", this.trickleUpChange);
        },

        parse: function (attributes, options) {
          // If parsing, this is an existing annotation so it's not isNew
          attributes.isNew = false;

          var propertyURI = $(attributes.objectDOM).find("propertyuri");
          var valueURI = $(attributes.objectDOM).find("valueuri");

          if (propertyURI.length !== 1 || valueURI.length !== 1) {
            return;
          }

          attributes.propertyURI = $(propertyURI).text().trim();

          attributes.valueURI = $(valueURI).text().trim();

          var propertyLabel = $(propertyURI).attr("label");
          var valueLabel = $(valueURI).attr("label");

          if (!propertyLabel || !valueLabel) {
            return;
          }

          attributes.propertyLabel = propertyLabel.trim();
          attributes.valueLabel = valueLabel.trim();

          return attributes;
        },

        validate: function () {
          var errors = [];

          if (this.isEmpty()) {
            this.trigger("valid");

            return;
          }

          var propertyURI = this.get("propertyURI");

          if (!propertyURI || propertyURI.length <= 0) {
            errors.push({ category: "propertyURI", message: "Property URI must be set." });
          } else if (propertyURI.match(/http[s]?:\/\/.+/) === null) {
            errors.push({ category: "propertyURI", message: "Property URI should be an HTTP(S) URI." });
          }

          var propertyLabel = this.get("propertyLabel");

          if (!propertyLabel || propertyLabel.length <= 0) {
            errors.push({ category: "propertyLabel", message: "Property Label must be set." });
          }

          var valueURI = this.get("valueURI");

          if (!valueURI || valueURI.length <= 0) {
            errors.push({ category: "valueURI", message: "Value URI must be set." });
          } else if (valueURI.match(/http[s]?:\/\/.+/) === null) {
            errors.push({ category: "valueURI", message: "Value URI should be an HTTP(S) URI." });
          }

          var valueLabel = this.get("valueLabel");

          if (!valueLabel || valueLabel.length <= 0) {
            errors.push({ category: "valueLabel", message: "Value Label must be set." });
          }

          if (errors.length === 0) {
            this.trigger("valid");

            return;
          }

          return errors;
        },

        updateDOM: function (objectDOM) {
          objectDOM = document.createElement("annotation");

          if (this.get("propertyURI")) {
            var propertyURIEl = document.createElement("propertyuri");
            $(propertyURIEl).html(this.get("propertyURI"));

            if (this.get("propertyLabel")) {
              $(propertyURIEl).attr("label", this.get("propertyLabel"));
            }

            $(objectDOM).append(propertyURIEl);
          }

          if (this.get("valueURI")) {
            var valueURIEl = document.createElement("valueuri");
            $(valueURIEl).html(this.get("valueURI"));

            if (this.get("valueLabel")) {
              $(valueURIEl).attr("label", this.get("valueLabel"));
            }

            $(objectDOM).append(valueURIEl);
          }

          return objectDOM;
        },

        formatXML: function (xmlString) {
          return DataONEObject.prototype.formatXML.call(this, xmlString);
        },

        /**
         * isEmpty
         *
         * Check whether the model's properties are all empty for the purpose
         * of skipping the model during serialization to avoid invalid EML
         * documents.
         *
         * @return {boolean} - Returns true if all child elements have no
         * content
         */
        isEmpty: function () {
          return (typeof this.get("propertyLabel") !== "string" || this.get("propertyLabel").length <= 0) &&
            (typeof this.get("propertyURI") !== "string" || this.get("propertyURI").length <= 0) &&
            (typeof this.get("valueLabel") !== "string" || this.get("valueLabel").length <= 0) &&
            (typeof this.get("valueURI") !== "string" || this.get("valueURI").length <= 0)
        },

        /* Let the top level package know of attribute changes from this object */
        trickleUpChange: function () {
          MetacatUI.rootDataPackage.packageModel.set("changed", true);
        }
      });

    return EMLAnnotation;
  }
);