/* global define */
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;
});