define([
"underscore",
"jquery",
"backbone",
"models/metadata/eml211/EMLParty",
"text!templates/metadata/EMLParty.html",
], function (_, $, Backbone, EMLParty, EMLPartyTemplate) {
/**
@class EMLPartyView
@classdesc The EMLParty renders the content of an EMLParty model
@classcategory Views/Metadata
@extends Backbone.View
*/
var EMLPartyView = Backbone.View.extend(
/** @lends EMLPartyView.prototype */ {
type: "EMLPartyView",
tagName: "div",
className: "row-fluid eml-party",
editTemplate: _.template(EMLPartyTemplate),
initialize: function (options) {
if (!options) var options = {};
this.isNew = options.isNew || (options.model ? false : true);
this.model = options.model || new EMLParty();
this.edit = options.edit || false;
this.$el.data({ model: this.model });
},
events: {
change: "updateModel",
focusout: "showValidation",
"keyup .phone": "formatPhone",
"mouseover .remove": "previewRemove",
"mouseout .remove": "previewRemove",
},
render: function () {
//Format the given names
var name = this.model.get("individualName") || {},
fullGivenName = "";
//Take multiple given names and combine into one given name.
//TODO: Support multiple given names as an array
if (Array.isArray(name.givenName)) {
fullGivenName = _.map(name.givenName, function (name) {
if (typeof name != "undefined" && name) return name.trim();
else return "";
}).join(" ");
} else fullGivenName = name.givenName;
//Get the address object
var address = Array.isArray(this.model.get("address"))
? this.model.get("address")[0] || {}
: this.model.get("address") || {};
//Use the template with the editing elements if this view has the "edit" flag on
if (this.edit) {
//Send all the EMLParty info to the template
this.$el.html(
this.editTemplate({
uniqueId: this.model.cid,
}),
);
//Populate the form with all the EMLParty values
this.$("#" + this.model.cid + "-givenName").val(fullGivenName || "");
this.$("#" + this.model.cid + "-surName").val(name.surName || "");
this.$("#" + this.model.cid + "-position").val(
this.model.get("positionName") || "",
);
this.$("#" + this.model.cid + "-organizationName").val(
this.model.get("organizationName") || "",
);
this.$("#" + this.model.cid + "-email").val(
this.model.get("email").length ? this.model.get("email")[0] : "",
);
this.$("#" + this.model.cid + "-website").val(
this.model.get("onlineUrl").length
? this.model.get("onlineUrl")[0]
: "",
);
this.$("#" + this.model.cid + "-phone").val(
this.model.get("phone").length ? this.model.get("phone")[0] : "",
);
this.$("#" + this.model.cid + "-fax").val(
this.model.get("fax").length ? this.model.get("fax")[0] : "",
);
this.$("#" + this.model.cid + "-orcid").val(
Array.isArray(this.model.get("userId"))
? this.model.get("userId")[0]
: this.model.get("userId") || "",
);
this.$("#" + this.model.cid + "-address").val(
address.deliveryPoint && address.deliveryPoint.length
? address.deliveryPoint[0]
: "",
);
this.$("#" + this.model.cid + "-address2").val(
address.deliveryPoint && address.deliveryPoint.length > 1
? address.deliveryPoint[1]
: "",
);
this.$("#" + this.model.cid + "-city").val(address.city || "");
this.$("#" + this.model.cid + "-state").val(
address.administrativeArea || "",
);
this.$("#" + this.model.cid + "-zip").val(address.postalCode || "");
this.$("#" + this.model.cid + "-country").val(address.country || "");
}
//If this EML Party is new/empty, then add the new class
if (this.isNew) {
this.$el.addClass("new");
}
//Save the view and model on the element
this.$el.data({
model: this.model,
view: this,
});
this.$el.attr("data-category", this.model.get("type"));
return this;
},
updateModel: function (e) {
if (!e) return false;
//Get the attribute that was changed
var changedAttr = $(e.target).attr("data-attribute");
if (!changedAttr) return false;
//Get the current value
var currentValue = this.model.get(changedAttr);
//Addresses and Names have special rules for updating
switch (changedAttr) {
case "deliveryPoint":
this.updateAddress(e);
return;
case "city":
this.updateAddress(e);
return;
case "administrativeArea":
this.updateAddress(e);
return;
case "country":
this.updateAddress(e);
return;
case "postalCode":
this.updateAddress(e);
return;
case "surName":
this.updateName(e);
return;
case "givenName":
this.updateName(e);
return;
case "salutation":
this.updateName(e);
return;
}
//Update the EMLParty model with the new value
if (Array.isArray(currentValue)) {
//Get the position that this new value should go in
var position = this.$("[data-attribute='" + changedAttr + "']").index(
e.target,
);
if ($(e.target).val() == "") {
//Remove the current value from the array if there is no value in the input field
currentValue.splice(position, 1);
} else {
var emlModel = this.model.getParentEML(),
value = $(e.target).val();
if (emlModel) {
value = emlModel.cleanXMLText(value);
}
//Put the new value in the array at the correct position
currentValue[position] = value;
}
this.model.set(changedAttr, currentValue);
this.model.trigger("change:" + changedAttr);
this.model.trigger("change");
} else {
//If the value of the input field is nothing, then reset the field
if ($(e.target).val() == "") {
this.model.set(changedAttr, this.model.defaults()[changedAttr]);
} else {
var emlModel = this.model.getParentEML(),
value = $(e.target).val();
if (emlModel) {
value = emlModel.cleanXMLText(value);
}
this.model.set(changedAttr, value);
}
}
//If this is a new EML Party, add it to the parent EML211 model
if (this.isNew) {
var mergeSuccess = this.model.mergeIntoParent();
//If the merge was sucessfull, mark this as not new
if (mergeSuccess) this.notNew();
}
//If this EMLParty model has been removed from the parent EML model,
//then add it back
if (this.model.get("removed")) {
var position = this.$el
.parent()
.children(".eml-party")
.index(this.$el);
this.model.get("parentModel").addParty(this.model);
this.model.set("removed", false);
}
this.model.trickleUpChange();
},
updateAddress: function (e) {
if (!e) return false;
//Get the address part that was changed
var changedAttr = $(e.target).attr("data-attribute");
if (!changedAttr) return false;
//TODO: Allow multiple addresses - right now we only support editing the first address
var address = this.model.get("address")[0] || {},
currentValue = address[changedAttr];
//Get the parent EML model and the value from the input element
var emlModel = this.model.getParentEML(),
value = $(e.target).val();
//If there is a parent EML model, clean up the text for XML
if (emlModel) {
value = emlModel.cleanXMLText(value);
}
//Update the address
if (Array.isArray(currentValue)) {
//Get the position that this new value should go in
var position = this.$("[data-attribute='" + changedAttr + "']").index(
e.target,
);
//Put the new value in the array at the correct position
currentValue[position] = value;
}
//Make sure delivery points are saved as arrays
else if (changedAttr == "deliveryPoint") {
address[changedAttr] = [value];
} else address[changedAttr] = value;
//Update the model
var allAddresses = this.model.get("address");
allAddresses[0] = address;
this.model.set("address", allAddresses);
//If this is a new EML Party, add it to the parent EML211 model
if (this.isNew) {
var mergeSuccess = this.model.mergeIntoParent();
//If the merge was sucessfull, mark this as not new
if (mergeSuccess) this.notNew();
}
//If this EMLParty model has been removed from the parent EML model,
//then add it back
if (this.model.get("removed")) {
var position = this.$el
.parent()
.children(".eml-party")
.index(this.$el);
this.model.get("parentModel").addParty(this.model);
this.model.set("removed", false);
}
//Manually trigger the change event since it's an object
this.model.trigger("change:address");
this.model.trigger("change");
this.model.trickleUpChange();
},
updateName: function (e) {
if (!e) return false;
//Get the address part that was changed
var changedAttr = $(e.target).attr("data-attribute");
if (!changedAttr) return false;
//TODO: Allow multiple given names - right now we only support editing the first given name
var name = this.model.get("individualName") || {},
currentValue = String.prototype.trim(name[changedAttr]);
//Get the parent EML model and the value from the input element
var emlModel = this.model.getParentEML(),
value = $(e.target).val().trim();
//If there is a parent EML model, clean up the text for XML
if (emlModel) {
value = emlModel.cleanXMLText(value);
}
//Update the name
if (Array.isArray(currentValue)) {
//Get the position that this new value should go in
var position = this.$("[data-attribute='" + changedAttr + "']").index(
e.target,
);
//Put the new value in the array at the correct position
currentValue[position] = value;
} else if (changedAttr == "givenName") {
name.givenName = value;
} else name[changedAttr] = value;
//Update the value on the model
this.model.set("individualName", name);
//If this is a new EML Party, add it to the parent EML211 model
if (this.isNew) {
var mergeSuccess = this.model.mergeIntoParent();
//If the merge was sucessfull, mark this as not new
if (mergeSuccess) this.notNew();
}
//If this EMLParty model has been removed from the parent EML model,
//then add it back
if (this.model.get("removed")) {
var position = this.$el
.parent()
.children(".eml-party")
.index(this.$el);
this.model.get("parentModel").addParty(this.model);
this.model.set("removed", false);
}
//Manually trigger a change on the name attribute
this.model.trigger("change:individualName");
this.model.trigger("change");
this.model.trickleUpChange();
},
/**
* Validates and displays error messages for the persons' name, position
* and organization name.
*
*/
showValidation: function () {
//Remove the error styling
this.$(".notification").empty();
this.$(".error").removeClass("error");
// Check if there are values to validate
if (this.isEmpty()) {
//Remove this EMLParty model from it's parent model instead
//of showing a validation error, since it's completely empty
this.model.removeFromParent();
return;
}
//If the model is valid, exit
else if (this.model.isValid()) {
return;
} else {
//Start the full error message string for all the EMLParty errors
var errorMessages = "";
//Iterate over each field that has a validation error
_.mapObject(
this.model.validationError,
function (errorMsg, attribute) {
//Find the input element for this attribute and add the error styling
this.$("[data-attribute='" + attribute + "']").addClass("error");
//Add this error message to the full error messages string
errorMessages += errorMsg + " ";
},
this,
);
//Add the full error message text to the notification area and add the error styling
this.$(".notification").text(errorMessages).addClass("error");
}
},
/**
* Checks if the user has entered any data in the fields.
*
* @return {bool} True if the user hasn't entered any party info, otherwise returns false
*/
isEmpty: function () {
// If we add any new fields, be sure to add the data-attribute here.
var attributes = [
"country",
"city",
"administrativeArea",
"postalCode",
"deliveryPoint",
"userId",
"fax",
"phone",
"onlineUrl",
"email",
"givenName",
"surName",
"positionName",
"organizationName",
];
for (var i in attributes) {
var attribute = "[data-attribute='" + attributes[i] + "']";
if (this.$(attribute).val() != "") return false;
}
return true;
},
// A function to format text to look like a phone number
formatPhone: function (e) {
// Strip all characters from the input except digits
var input = $(e.target).val().replace(/\D/g, "");
// Trim the remaining input to ten characters, to preserve phone number format
input = input.substring(0, 10);
// Based upon the length of the string, we add formatting as necessary
var size = input.length;
if (size == 0) {
input = input;
} else if (size < 4) {
input = "(" + input;
} else if (size < 7) {
input = "(" + input.substring(0, 3) + ") " + input.substring(3, 6);
} else {
input =
"(" +
input.substring(0, 3) +
") " +
input.substring(3, 6) +
" - " +
input.substring(6, 10);
}
$(e.target).val(input);
},
previewRemove: function () {
this.$("input, img, label").toggleClass("remove-preview");
},
/**
* Changes this view and its model from -new- to -not new-
* "New" means this EMLParty model is not referenced or stored on a
* parent model, and this view is being displayed to the user so they can
* add a new party to their EML (versus edit an existing one).
*/
notNew: function () {
this.isNew = false;
this.$el.removeClass("new");
this.$el.find(".new").removeClass("new");
},
},
);
return EMLPartyView;
});