# Source: src/js/views/UserView.js

    /*global define */
    define(['jquery', 'underscore', 'backbone', 'clipboard',
            'collections/UserGroup',
        		'models/UserModel',
            "models/Stats",
    		'views/SignInView', 'views/StatsView', 'views/DataCatalogView',
    		'views/UserGroupView',
            'text!templates/userProfile.html', 'text!templates/alert.html', 'text!templates/loading.html',
            'text!templates/userProfileMenu.html', 'text!templates/userSettings.html', 'text!templates/noResults.html'],
    	function($, _, Backbone, Clipboard,
        UserGroup,
        UserModel, Stats,
        SignInView, StatsView, DataCatalogView, UserGroupView,
        userProfileTemplate, AlertTemplate, LoadingTemplate,
        ProfileMenuTemplate, SettingsTemplate, NoResultsTemplate) {
    	'use strict';

    	/**
    	 * @class UserView
    	 * @classdesc A major view that displays a public profile for the user and a settings page for the logged-in user
    	 * to manage their account info, groups, identities, and API tokens.
       * @classcategory Views
       * @screenshot views/UserView.png
    	 */
    	var UserView = Backbone.View.extend(
        /** @lends UserView.prototype */{

    		el: '#Content',

    		//Templates
    		profileTemplate:  _.template(userProfileTemplate),
    		alertTemplate:    _.template(AlertTemplate),
    		loadingTemplate:  _.template(LoadingTemplate),
    		settingsTemplate: _.template(SettingsTemplate),
    		menuTemplate:     _.template(ProfileMenuTemplate),
    		noResultsTemplate: _.template(NoResultsTemplate),

        /**
        * A jQuery selector for the element that the PortalListView should be inserted into
        * @type {string}
        */
        portalListContainer: ".my-portals-container",

    		events: {
    			"click .section-link"          : "switchToSection",
    			"click .subsection-link"       : "switchToSubSection",
    			"click .token-generator"       : "getToken",
    			"click #mod-save-btn"		   : "saveUser",
    			"click #map-request-btn"	   : "sendMapRequest",
    			"click .remove-identity-btn"   : "removeMap",
    			"click .confirm-request-btn"   : "confirmMapRequest",
    			"click .reject-request-btn"	   : "rejectMapRequest",
    			"click [highlight-subsection]" : "highlightSubSection",
    			"keypress #add-group-name"     : "preventSubmit",
    			"click .token-tab" 			   : "switchTabs"
    		},

    		initialize: function(){
    			this.subviews = new Array();
    		},

    		//------------------------------------------ Rendering the main parts of the view ------------------------------------------------//
    		render: function (options) {
    			//Don't render anything if the user profiles are turned off
    			if( MetacatUI.appModel.get("enableUserProfiles") === false ){
    				return;
    			}

    			this.stopListening();
    			if(this.model) this.model.stopListening();

          //Create a Stats model
          this.statsModel = new Stats();

    			this.activeSection = (options && options.section)? options.section : "profile";
    			this.activeSubSection = (options && options.subsection)? options.subsection : "";
    			this.username = (options && options.username)? options.username : undefined;

    			//Add the container element for our profile sections
    			this.sectionHolder = $(document.createElement("section")).addClass("user-view-section");
    			this.$el.html(this.sectionHolder);

    			//Show the loading sign first
    			//$(this.sectionHolder).html(this.loadingTemplate());
    			this.$el.show();

    			// set the header type
    			MetacatUI.appModel.set('headerType', 'default');

    			//Render the user profile only after the app user's info has been checked
    			//This prevents the app from rendering the profile before the login process has completed - which would
    			//cause this profile to render twice (first before the user is logged in then again after they log in)
    			if(MetacatUI.appUserModel.get("checked")) this.renderUser();
    			else MetacatUI.appUserModel.on("change:checked", this.renderUser, this);

    			return this;
    		},

    		/**
    		 * Update the window location path to route to /portals path
    		 * @param {string} username - Short identifier for the member node
    		*/
    		forwardToPortals: function(username){

    			var pathName      = decodeURIComponent(window.location.pathname)
    								.substring(MetacatUI.root.length)
    								// remove trailing forward slash if one exists in path
    								.replace(/\/$/, "");

    			// Routes the /profile/{node-id} to /portals/{node-id}
    			var pathRE = new RegExp("\\/profile(\\/[^\\/]*)?$", "i");
    			var newPathName = pathName.replace(pathRE, "") + "/" +
    							MetacatUI.appModel.get("portalTermPlural") + "/" + username;

    			// Update the window location
    			MetacatUI.uiRouter.navigate( newPathName, { trigger: true, replace: true } );
    			return;
    		},

    		renderUser: function(){

    			this.model = MetacatUI.appUserModel;

    			var username = MetacatUI.appModel.get("profileUsername") || view.username,
    				currentUser = MetacatUI.appUserModel.get("username") || "";

    			if(username.toUpperCase() == currentUser.toUpperCase()){ //Case-insensitive matching of usernames
    				this.model = MetacatUI.appUserModel;
    				this.model.set("type", "user");

    				//If the user is logged in, display the settings options
    				if(this.model.get("loggedIn")){
    					this.insertMenu();
    					this.renderProfile();
    					this.renderSettings();
    					this.resetSections();
    				}
    			}

    			//If this isn't the currently-logged in user, then let's find out more info about this account
    			else{
    				//Create a UserModel with the username given
    				this.model = new UserModel({
    					username: username
    				});

    				//Is this a member node?
    				if(MetacatUI.nodeModel.get("checked") && this.model.isNode()){
    					this.model.saveAsNode();
    					this.model.set("nodeInfo", _.find(MetacatUI.nodeModel.get("members"), function(nodeModel) {
    						return nodeModel.identifier.toLowerCase() == "urn:node:" + username.toLowerCase();
    					  }));
    					this.forwardToPortals(username);
    					return;
    				}
    				//If the node model hasn't been checked yet
    				else if(!MetacatUI.nodeModel.get("checked")){
    					var user = this.model,
    						view = this;
    					this.listenTo(MetacatUI.nodeModel, "change:checked", function(){
    						if(user.isNode())
    							view.render();
    					});
    				}

    				//When we get the infomration about this account, then crender the profile
    				this.model.once("change:checked", this.renderProfile, this);
    				this.model.once("change:checked", this.resetSections, this);
    				//Get the info
    				this.model.getInfo();
    			}

    			//When the model is reset, refresh the page
    			this.listenTo(this.model, "reset", this.render);

    		},

    		renderProfile: function(){

    			//Insert the template first
    			var profileEl = $.parseHTML(this.profileTemplate({
    				type: this.model.get("type"),
    				logo: this.model.get("logo") || "",
    				description: this.model.get("description") || "",
    				user: this.model.toJSON()
    			}).trim());

    			//If the profile is being redrawn, then replace it
    			if(this.$profile && this.$profile.length){
    				//If the profile section is currently hidden, make sure we hide our new profile rendering too
    				if(!this.$profile.is(":visible"))
    					$(profileEl).hide();

    				this.$profile.replaceWith(profileEl);
    			}
    			//If this is a fresh rendering, then append it to the page and save it
    			else
    				this.sectionHolder.append(profileEl);

    			this.$profile = $(profileEl);

    			//If this user hasn't uploaded anything yet, display so
    			this.listenTo(this.statsModel, "change:totalCount", function(){
    				if(!this.statsModel.get("totalCount"))
    					this.noActivity();
    			});

    			//Insert the user data statistics
    			this.insertStats();

    			//Insert the user's basic information
    			this.listenTo(this.model, "change:fullName", this.insertUserInfo);
    			this.insertUserInfo();

    			var view = this;
    			//Listen to changes in the user's search terms
    			this.listenTo(this.model, "change:searchModel", this.renderProfile);

    			//Insert this user's data content
    			this.insertContent();

    			// create the UserGroupView to generate the membership list
    			// this is the first call to UserGroupView so we instantiate it here
    			var groupView = new UserGroupView({model: this.model});
    			this.subviews.push(groupView);
    			this.renderMembershipList();
    		},

    			renderMembershipList: function() {
    				//List the groups this user is in by creating usergroupview subview
    				//List the groups this user is in by creating usergroupview subview
    				var groupView = _.where(this.subviews, {type: "UserGroupView"}).at(0);

    				if(this.model.get("type") == "group"){
    					//Create the User Group collection
    					var options = {
    						name: this.model.get("fullName"),
    						groupId: this.model.get("username"),
    						rawData: this.model.get("rawData") || null
    					}
    					var userGroup = new UserGroup([], options);
    					//Create the group list and add it to the page
    					var viewOptions = { collapsable: false, showGroupName: false }
    					var groupList = groupView.createGroupList(userGroup, viewOptions);
    					this.$("#user-membership-container").html(groupList);
    				}
    				else{
    					var groups = _.sortBy(this.model.get("isMemberOf"), "name");
    					if(!groups.length){
    						this.$("#user-membership-header").hide();
    						return;
    					}
    					this.sectionHolder.append(groupView.insertMembership(groups, this.$("#user-membership-container")).html());
    				}
    			},

    			renderGroupsSection: function() {
    				var groupView = _.where(this.subviews, {type: "UserGroupView"}).at(0);
    				var container = this.$('#groups-container');
    				container.append(groupView.render().el)
    			},

    			renderSettings: function(){
    					//Don't render anything if the user profile settings are turned off
    		if( MetacatUI.appModel.get("enableUserProfileSettings") === false ){
    			return;
    		}

    		//Insert the template first
    		this.sectionHolder.append(this.settingsTemplate(this.model.toJSON()));
    		this.$settings = this.$("[data-section='settings']");

    		//Draw the group list
    		this.renderGroupsSection();

    		//Listen for the identity list
    		this.listenTo(this.model, "change:identities", this.insertIdentityList);
    		this.insertIdentityList();

    		//Listen for the pending list
    		this.listenTo(this.model, "change:pending", this.insertPendingList);
    		this.model.getPendingIdentities();

    		//Render the portals subsection
    		this.renderMyPortals();

    			//Listen for updates to person details
    			this.listenTo(this.model, "change:lastName change:firstName change:email change:registered", this.updateModForm);
    			this.updateModForm();

    			// init autocomplete fields
    			this.setUpAutocomplete();

    			//Get the token right away
    			this.getToken();
    		},

    		/*
    		 * Displays a menu for the user to switch between different views of the user profile
    		 */
    		insertMenu: function(){

    			//If the user is not logged in, then remove the menu
    			if(!MetacatUI.appUserModel.get("loggedIn")){
    				this.$(".nav").remove();
    				return;
    			}

    			//Otherwise, insert the menu
    			var menu = this.menuTemplate({
    				username: this.model.get("username")
    			});

    			this.$el.prepend(menu);
    		},

    		//------------------------------------------ Navigating sections of view ------------------------------------------------//
    		switchToSection: function(e, sectionName){

    			if(e) e.preventDefault();

    			//Hide all the sections first
    			$(this.sectionHolder).children().slideUp().removeClass(".active");

    			//Get the section name
    			if(!sectionName)
    				var sectionName = $(e.target).attr("data-section");

    			//Display the specified section
    			var activeSection = this.$(".section[data-section='" + sectionName + "']");
    			if(!activeSection.length) activeSection = this.$(".section[data-section='profile']");
    			$(activeSection).addClass("active").slideDown();

    			//Change the navigation tabs
    			this.$(".nav-tab").removeClass("active");
    			$(".nav-tab[data-section='" + sectionName + "']").addClass("active");

    			//Find all the subsections, if there are any
    			if($(activeSection).find(".subsection").length > 0){
    				//Find any item classified as "active"
    				var activeItem = $(activeSection).find(".active");
    				if(activeItem.length > 0){
    					//Find the data section this active item is referring to
    					if($(activeItem).children("[data-subsection]").length > 0){
    						//Get the section name
    						var subsectionName = $(activeItem).find("[data-subsection]").first().attr("data-subsection");
    						//If we found a section name, find the subsection element and display it
    						if(subsectionName) this.switchToSubSection(null, subsectionName);
    					}
    					else
    						this.switchToSubSection(null, $(activeSection).children("[data-section]").first().attr("data-section"));
    				}
    			}
    		},

    		switchToSubSection: function(e, subsectionName){
    			if(e){
    				e.preventDefault();
    			    var subsectionName = $(e.target).attr("data-section");
              if( !subsectionName ){
                subsectionName = $(e.target).parents("[data-section]").first().attr("data-section");
              }
    			}

    			//Mark its links as active
    			$(".section.active").find(".subsection-link").removeClass("active");
    			$(".section.active").find(".subsection-link[data-section='" + subsectionName + "']").addClass("active");

    			//Hide all the other sections
    			$(".section.active").find(".subsection").hide();
    			$(".section.active").find(".subsection[data-section='" + subsectionName + "']").show();
    		},

    		resetSections: function(){
    			//Hide all the sections first, then display the section specified in the URL (or the default)
    			this.$(".subsection, .section").hide();
    			this.switchToSection(null, this.activeSection);

    			//Show the subsection
    			if(this.activeSubSection)
    				this.switchToSubSection(null, this.activeSubSection);
    		},

    		highlightSubSection: function(e, subsectionName){
    			if(e) e.preventDefault();

    			if(!subsectionName && e){
    				//Get the subsection name
    				var subsectionName = $(e.target).attr("highlight-subsection");
    				if(!subsectionName) return;
    			}
    			else if(!subsectionName && !e) return false;

    			//Find the subsection
    			var subsection = this.$(".subsection[data-section='" + subsectionName + "']");
    			if(!subsection.length) subsection = this.$("[data-subsection='add-account']");
    			if(!subsection.length) return;

    			//Visually highlight the subsection
    			subsection.addClass("highlight");
    			MetacatUI.appView.scrollTo(subsection);
    			//Wait about a second and then remove the highlight style
    			window.setTimeout(function(){ subsection.removeClass("highlight"); }, 1500);
    		},

    		//------------------------------------------ Inserting public profile UI elements ------------------------------------------------//
    		insertStats: function(){
    			if(this.model.noActivity && this.statsView){
    				this.statsView.$el.addClass("no-activity");
    				this.$("#total-download-wrapper, section.downloads").hide();
    				return;
    			}

    			var username = this.model.get("username"),
    				view = this;

    			//Insert a couple stats into the profile
    			this.listenToOnce(this.statsModel, "change:firstUpload", this.insertFirstUpload);

    			this.listenToOnce(this.statsModel, "change:totalCount", function(){
    				view.$("#total-upload-container").text(MetacatUI.appView.commaSeparateNumber(view.statsModel.get("totalCount")));
    			});

    			//Create a base query for the statistics
    			var statsSearchModel = this.model.get("searchModel").clone();
    			statsSearchModel.set("exclude", [], {silent: true}).set("formatType", [], {silent: true});
    			this.statsModel.set("query", statsSearchModel.getQuery());
          this.statsModel.set("isSystemMetadataQuery", true);
    			this.statsModel.set("searchModel", statsSearchModel);

    			//Create the description for this profile
    			var description;

    			switch(this.model.get("type")){
    				case "node":
    					description = "A summary of all datasets from the " + this.model.get("fullName") + " repository";
    					break;
    				case "group":
    					description = "A summary of all datasets from the " + this.model.get("fullName") + " group";
    					break;
    				case "user":
    					description = "A summary of all datasets from " + this.model.get("fullName");
    					break;
    				default:
    					description = "";
    					break;
    			}

    			//Render the Stats View for this person
    			this.statsView = new StatsView({
    				title: "Statistics and Figures",
    				description: description,
    				userType: "user",
    				el: this.$("#user-stats"),
    				model: this.statsModel
    			});
    			this.subviews.push(this.statsView);
    			this.statsView.render();
    			if(this.model.noActivity)
    				this.statsView.$el.addClass("no-activity");

    		},

    		/*
    		 * Insert the name of the user
    		 */
    		insertUserInfo: function(){

    			//Don't try to insert anything if we haven't gotten all the user info yet
    			if(!this.model.get("fullName")) return;

    			//Insert the name into this page
    			var usernameLink = $(document.createElement("a")).attr("href", MetacatUI.root + "/profile/" + this.model.get("username")).text(this.model.get("fullName"));
    			this.$(".insert-fullname").append(usernameLink);

    			//Insert the username
    			if(this.model.get("type") != "node"){
    				if(!this.model.get("usernameReadable")) this.model.createReadableUsername();
    				this.$(".insert-username").text(this.model.get("usernameReadable"));
    			}
    			else{
    				$("#username-wrapper").hide();
    			}

    			//Show or hide ORCID logo
    			if(this.model.isOrcid())
    				this.$(".show-orcid").show();
    			else
    				this.$(".show-orcid").hide();

    			//Show the email
    			if(this.model.get("email")){
    				this.$(".email-wrapper").show();
    				var parts = this.model.get("email").split("@");
    				this.$(".email-container").attr("data-user", parts[0]);
    				this.$(".email-container").attr("data-domain", parts[1]);
    			}
    			else
    				this.$(".email-wrapper").hide();

    		},

    		// Creates an HTML element to display in front of the user identity/subject.
    		// Only used for the ORCID logo right now
    		createIdPrefix: function(){
    			if(this.model.isOrcid())
    				return $(document.createElement("img")).attr("src", MetacatUI.root + "/img/orcid_64x64.png").addClass("orcid-logo");
    			else
    				return "";
    		},

    		/*
    		 * Insert the first year of contribution for this user
    		 */
    		insertFirstUpload: function(){
    			if(this.model.noActivity || !this.statsModel.get("firstUpload")){
    				this.$("#first-upload-container, #first-upload-year-container").hide();
    				return;
    			}

    			// Get the first upload or first operational date
    			if(this.model.get("type") == "node"){

    				//Get the member node object
    				var node = _.findWhere(MetacatUI.nodeModel.get("members"), {identifier: "urn:node:" + this.model.get("username") });

    				//If there is no memberSince date, then hide this statistic and exit
    				if( !node.memberSince ){
    					this.$("#first-upload-container, #first-upload-year-container").hide();
    					return;
    				}
    				else{
    					var firstUpload = node.memberSince? new Date(node.memberSince.substring(0, node.memberSince.indexOf("T"))) : new Date();
    				}

    			}
    			else{
    				var	firstUpload = new Date(this.statsModel.get("firstUpload"));
    			}

    			// Construct the first upload date sentence
    			var	monthNames = [ "January", "February", "March", "April", "May", "June",
    				                 "July", "August", "September", "October", "November", "December" ],
    				m = monthNames[firstUpload.getUTCMonth()],
    				y = firstUpload.getUTCFullYear(),
    				d = firstUpload.getUTCDate();

    			//For Member Nodes, start all dates at July 2012, the beginning of DataONE
    			if(this.model.get("type") == "node"){
    				this.$("#first-upload-container").text("DataONE Member Node since " + y);
    			}
    			else
    				this.$("#first-upload-container").text("Contributor since " + m + " " + d + ", " + y);

    			//Construct the time-elapsed sentence
    			var now = new Date(),
    				msElapsed = now - firstUpload,
    				years = msElapsed / 31556952000,
    				months = msElapsed / 2629746000,
    				weeks = msElapsed / 604800000,
    				days = msElapsed / 86400000,
    				time = "";

    			//If one week or less, express in days
    			if(weeks <= 1){
    				time = (Math.round(days) || 1) + " day";
    				if(days > 1.5) time += "s";
    			}
    			//If one month or less, express in weeks
    			else if(months < 1){
    				time = (Math.round(weeks) || 1) + " week";
    				if(weeks > 1.5) time += "s";
    			}
    			//If less than 12 months, express in months
    			else if(months <= 11.5){
    				time = (Math.round(months) || 1) + " month";
    				if(months > 1.5) time += "s";
    			}
    			//If one year or more, express in years and months
    			else{
    				var yearsOnly = (Math.floor(years) || 1),
    					monthsOnly = Math.round(years % 1 * 12);

    				if(monthsOnly == 12){
    					yearsOnly += 1;
    					monthsOnly = 0;
    				}

    				time = yearsOnly + " year";
    				if(yearsOnly > 1) time += "s";

    				if(monthsOnly)
    					time += ", " + monthsOnly + " month";
    				if(monthsOnly > 1) time += "s";
    			}

    			this.$("#first-upload-year-container").text(time);
    		},

    		/*
    		 * Insert a list of this user's content
    		 */
    		insertContent: function(){
    			if(this.model.noActivity){
    				this.$("#data-list").html(this.noResultsTemplate({
    					fullName: this.model.get("fullName"),
    					username: ((this.model == MetacatUI.appUserModel) && MetacatUI.appUserModel.get("loggedIn"))? this.model.get("username") : null
    				}));
    				return;
    			}

    			var view = new DataCatalogView({
    				el            : this.$("#data-list")[0],
    				searchModel   : this.model.get("searchModel"),
    				searchResults : this.model.get("searchResults"),
    				mode          : "list",
    				isSubView     : true,
    				filters       : false
    			});
    			this.subviews.push(view);
    			view.render();
    			view.$el.addClass("list-only");
    			view.$(".auto-height").removeClass("auto-height").css("height", "auto");
    			$("#metacatui-app").removeClass("DataCatalog mapMode");
    		},

    		/*
    		 * When this user has not uploaded any content, render the profile differently
    		 */
    		noActivity: function(){
    			this.model.noActivity = true;
    			this.insertContent();
    			this.insertFirstUpload();
    			this.insertStats();
    		},

    		//------------------------------------------------ Identities/Accounts -------------------------------------------------------//
    			/*
                 * Sends a new identity map request and displays notifications about the result
                 */
    		sendMapRequest: function(e) {
    			e.preventDefault();

    			//Get the identity entered into the input
    			var equivalentIdentity = this.$("#map-request-field").val();
    			if (!equivalentIdentity || equivalentIdentity.length < 1) {
    				return;
    			}
    			//Clear the text input
    			this.$("#map-request-field").val("");

    			//Show notifications after the identity map request is a success or failure
    			var viewRef = this,
    				success = function(){
    					var message = "An account map request has been sent to <a href=" + MetacatUI.root + "'/profile/" + equivalentIdentity + "'>" + equivalentIdentity + "</a>" +
    					  "<h4>Next step:</h4><p>Sign In with this other account and approve this request.</p>"
    					viewRef.showAlert(message, null, "#request-alert-container");
    				},
    				error = function(xhr){
    					var errorMessage = xhr.responseText;

    					if( xhr.responseText.indexOf("Request already issued") > -1 ){
    							viewRef.showAlert("<p>You have already sent a request to map this account to " + equivalentIdentity +
    							".</p> <h4>Next Step:</h4><p> Sign In with your " + equivalentIdentity + " account and approve the request.</p>",
    							'alert-info', "#request-alert-container");
    					}
    					else{

    						//Make a more understandable error message when the account isn't found
    						if(xhr.responseText.indexOf("LDAP: error code 32 - No Such Object") > -1){
    							xhr.responseText = "The username " + equivalentIdentity + " does not exist in our system."
    						}

    					viewRef.showAlert(xhr.responseText, 'alert-error', "#request-alert-container");
    					}
    				};

    			//Send it
    			this.model.addMap(equivalentIdentity, success, error);
    		},

    		/*
    		 * Removes a confirmed identity map request and displays notifications about the result
    		 */
    		removeMap: function(e) {
    			e.preventDefault();

    			var equivalentIdentity = $(e.target).parents("a").attr("data-identity");
    			if(!equivalentIdentity) return;

    			var viewRef = this,
    				success = function(){
    					viewRef.showAlert("Success! Your account is no longer associated with the user " + equivalentIdentity, "alert-success", "#identity-alert-container");
    				},
    				error = function(xhr, textStatus, error){
    					viewRef.showAlert("Something went wrong: " + xhr.responseText, 'alert-error', "#identity-alert-container");
    				};

    			this.model.removeMap(equivalentIdentity, success, error);
    		},

    		/*
    		 * Confirms an identity map request that was initiated from another user, and displays notifications about the result
    		 */
    		confirmMapRequest: function(e) {
    			var model = this.model;

    			e.preventDefault();
    			var otherUsername = $(e.target).parents("a").attr("data-identity"),
    				mapRequestEl = $(e.target).parents(".pending.identity");

    			var viewRef = this;

    			var success = function(data, textStatus, xhr) {
    				viewRef.showAlert("Success! Your account is now linked with the username " + otherUsername, "alert-success", "#pending-alert-container");

    				mapRequestEl.remove();
    			}
    			var error = function(xhr, textStatus, error) {
    				viewRef.showAlert(xhr.responseText, 'alert-error', "#pending-alert-container");
    			}

    			//Confirm this map request
    			this.model.confirmMapRequest(otherUsername, success, error);
    		},

    		/*
    		 * Rejects an identity map request that was initiated by another user, and displays notifications about the result
    		 */
    		rejectMapRequest: function(e) {
    			e.preventDefault();

    			var equivalentIdentity = $(e.target).parents("a").attr("data-identity"),
    				mapRequestEl = $(e.target).parents(".pending.identity");

    			if(!equivalentIdentity) return;

    			var viewRef = this,
    				success = function(data){
    					viewRef.showAlert("Removed mapping request for " + equivalentIdentity, "alert-success", "#pending-alert-container");
    					$(mapRequestEl).remove();
    				},
    				error = function(xhr, textStatus, error){
    					viewRef.showAlert(xhr.responseText, 'alert-error', "#pending-alert-container");
    				};

    			this.model.denyMapRequest(equivalentIdentity, success, error);
    		},

    		insertIdentityList: function(){
    			var identities = this.model.get("identities");

    			//Remove the equivalentIdentities list if it was drawn already so we don't do it twice
    			this.$("#identity-list-container").empty();

    			if(!identities) return;

    			//Create the list element
    			if(identities.length < 1){
    				var identityList = $(document.createElement("p")).text("You haven't linked to another account yet. Send a request below.");
    			}
    			else
    				var identityList = $(document.createElement("ul")).addClass("list-identity").attr("id", "identity-list");

    			var view = this;
    			//Create a list item for each identity
    			_.each(identities, function(identity, i){
    				var listItem = view.createUserListItem(identity, { confirmed: true });

    				//When/if the info from the equivalent identities is retrieved, update the item
    				view.listenToOnce(identity, "change:fullName", function(identity){
    					var newListItem = view.createUserListItem(identity, {confirmed: true});
    					listItem.replaceWith(newListItem);
    				});

    				$(identityList).append(listItem);
    			});

    			//Add to the page
    			//$(identityList).find(".collapsed").hide();
    			this.$("#identity-list-container").append(identityList);
    		},

    		insertPendingList: function(){
    			var pending = this.model.get("pending");

    			//Remove the equivalentIdentities list if it was drawn already so we don't do it twice
    			this.$("#pending-list-container").empty();

    			//Create the list element
    			if (pending.length < 1){
    				this.$("[data-subsection='pending-accounts']").hide();
    				return;
    			}
    			else{
    				this.$("[data-subsection='pending-accounts']").show();
    				this.$("#pending-list-container").prepend($(document.createElement("p")).text("You have " + pending.length + " new request to map accounts. If these requests are from you, accept them below. If you do not recognize a username, reject the request."));
    				var pendingList = $(document.createElement("ul")).addClass("list-identity").attr("id", "pending-list");
    				var pendingCount = $(document.createElement("span")).addClass("badge").attr("id", "pending-count").text(pending.length);
    				this.$("#pending-list-heading").append(pendingCount);
    			}

    			//Create a list item for each pending id
    			var view = this;
    			_.each(pending, function(pendingUser, i){
    				var listItem = view.createUserListItem(pendingUser, {pending: true});
    				$(pendingList).append(listItem);

    				if(pendingUser.isOrcid()){
    					view.listenToOnce(pendingUser, "change:fullName", function(pendingUser){
    						var newListItem = view.createUserListItem(pendingUser, {pending: true});
    						listItem.replaceWith(newListItem);
    					});
    				}
    			});

    			//Add to the page
    			this.$("#pending-list-container").append(pendingList);
    		},

    		createUserListItem: function(user, options){
    			var pending = false,
    				confirmed = false;

    			if(options && options.pending)
    				pending = true;
    			if(options && options.confirmed)
    				confirmed = true;

    			var username = user.get("username"),
    			    fullName = user.get("fullName") || username;

    			var listItem = $(document.createElement("li")).addClass("list-group-item identity"),
    				link     = $(document.createElement("a")).attr("href", MetacatUI.root + "/profile/" + username).attr("data-identity", username).text(fullName),
    				details  = $(document.createElement("span")).addClass("subtle details").text(username);

    			listItem.append(link, details);

    			if(pending){
    				var acceptIcon = $(document.createElement("i")).addClass("icon icon-ok icon-large icon-positive tooltip-this").attr("data-title", "Accept Request").attr("data-trigger", "hover").attr("data-placement", "top"),
    					rejectIcon = $(document.createElement("i")).addClass("icon icon-remove icon-large icon-negative tooltip-this").attr("data-title", "Reject Request").attr("data-trigger", "hover").attr("data-placement", "top"),
    					confirm = $(document.createElement("a")).attr("href", "#").addClass('confirm-request-btn').attr("data-identity", username).append(acceptIcon),
    					reject = $(document.createElement("a")).attr("href", "#").addClass("reject-request-btn").attr("data-identity", username).append(rejectIcon);

    				listItem.prepend(confirm, reject).addClass("pending");
    			}
    			else if(confirmed){
    				var removeIcon = $(document.createElement("i")).addClass("icon icon-remove icon-large icon-negative"),
    					remove = $(document.createElement("a")).attr("href", "#").addClass("remove-identity-btn").attr("data-identity", username).append(removeIcon);
    				$(remove).tooltip({
    					trigger: "hover",
    					placement: "top",
    					title: "Remove equivalent account"
    				});
    				listItem.prepend(remove.append(removeIcon));
    			}

    			if(user.isOrcid()){
    				details.prepend(this.createIdPrefix(), " ORCID: ");
    			}
    			else
    				details.prepend(" Username: ");

    			return listItem;
    		},

    		updateModForm: function() {
    			this.$("#mod-givenName").val(this.model.get("firstName"));
    			this.$("#mod-familyName").val(this.model.get("lastName"));
    			this.$("#mod-email").val(this.model.get("email"));

    			if(!this.model.get("email")){
    				this.$("#mod-email").parent(".form-group").addClass("has-warning");
    				this.$("#mod-email").parent(".form-group").find(".help-block").text("Please provide an email address.");
    			}
    			else{
    				this.$("#mod-email").parent(".form-group").removeClass("has-warning");
    				this.$("#mod-email").parent(".form-group").find(".help-block").text("");
    			}

    			if (this.model.get("registered")) {
    				this.$("#registered-user-container").show();
    			} else {
    				this.$("#registered-user-container").hide();
    			}
    		},

    		/*
    		 * Gets the user account settings, updates the UserModel and saves this new info to the server
    		 */
    		saveUser: function(e) {

    			e.preventDefault();

    			var view = this,
    				container = this.$('[data-subsection="edit-account"] .content') || $(e.target).parent();

    			var success = function(data){
    				$(container).find(".loading").detach();
    				$(container).children().show();
    				view.showAlert("Success! Your profile has been updated.", 'alert-success', container);
    			}
    			var error = function(data){
    				$(container).find(".loading").detach();
    				$(container).children().show();
    				var msg = (data && data.responseText) ? data.responseText : "Sorry, updating your profile failed. Please try again.";
    				if(!data.responseText)
    					view.showAlert(msg, 'alert-error', container);
    			}

    			//Get info entered into form
    			var givenName = this.$("#mod-givenName").val();
    			var familyName = this.$("#mod-familyName").val();
    			var email = this.$("#mod-email").val();

    			//Update the model
    			this.model.set("firstName", givenName);
    			this.model.set("lastName", familyName);
    			this.model.set("email", email);

    			//Loading icon
    			$(container).children().hide();
    			$(container).prepend(this.loadingTemplate());

    			//Send the update
    			this.model.update(success, error);

    		},

    		//---------------------------------- Token -----------------------------------------//
    		getToken: function(){
    			var model = this.model;

    			//Show loading sign
    			this.$("#token-generator-container").html(this.loadingTemplate());

    			//When the token is retrieved, then show it
    			this.listenToOnce(this.model, "change:token", this.showToken);

    			//Get the token from the CN
    			this.model.getToken(function(data, textStatus, xhr){
    				model.getTokenExpiration();
    				model.set("token", data);
    				model.trigger("change:token");
    			});
    		},

    		showToken: function(){
    			var token = this.model.get("token");

    			if(!token || !this.model.get("loggedIn"))
    				return;

    			var expires    = this.model.get("expires"),
    				rTokenName = (MetacatUI.appModel.get("d1CNBaseUrl").indexOf("cn.dataone.org") > -1)? "dataone_token" : "dataone_test_token",
    				rToken = 'options(' + rTokenName +' = "' + token + '")',
    				matlabToken = "import org.dataone.client.run.RunManager; mgr = RunManager.getInstance(); mgr.configuration.authentication_token = '" + token + "';",
    				tokenInput = $(document.createElement("textarea")).attr("type", "text").attr("rows", "5").addClass("token copy").text(token),
    				copyButton = $(document.createElement("a")).addClass("btn btn-primary copy").text("Copy").attr("data-clipboard-text", token),
    				copyRButton = $(document.createElement("a")).addClass("btn btn-primary copy").text("Copy").attr("data-clipboard-text", rToken),
    				copyMatlabButton = $(document.createElement("a")).addClass("btn btn-primary copy").text("Copy").attr("data-clipboard-text", matlabToken),
    				successIcon = $(document.createElement("i")).addClass("icon icon-ok"),
    		  		copySuccess = $(document.createElement("div")).addClass("notification success copy-success hidden").append(successIcon, " Copied!"),
    		  		expirationMsg = expires? "<strong>Note:</strong> Your authentication token expires on " + expires.toLocaleDateString() + " at " + expires.toLocaleTimeString() : "",
    		  		usernameMsg = "<div class='footnote'>Your user identity: ",
    		  		usernamePrefix = this.createIdPrefix(),
    		  		tabs = $(document.createElement("ul")).addClass("nav nav-tabs")
    		  				.append($(document.createElement("li")).addClass("active")
    		  						.append( $(document.createElement("a")).attr("href", "#token-code-panel").addClass("token-tab").text("Token") ))
    		  				.append($(document.createElement("li"))
    		  						.append( $(document.createElement("a")).attr("href", "#r-token-code-panel").addClass("token-tab").text("Token for DataONE R") ))
    		  				.append($(document.createElement("li"))
    		  						.append( $(document.createElement("a")).attr("href", "#matlab-token-code-panel").addClass("token-tab").text("Token for Matlab DataONE Toolbox") )),
    		  		tokenRInput = $(document.createElement("textarea")).attr("type", "text").attr("rows", "5").addClass("token copy").text(rToken),
    		  		tokenRText = $(document.createElement("p")).text("Copy this code snippet to use your token with the DataONE R package."),
    		  		tokenMatlabInput = $(document.createElement("textarea")).attr("type", "text").attr("rows", "5").addClass("token copy").text(matlabToken),
    		  		tokenMatlabText = $(document.createElement("p")).text("Copy this code snippet to use your token with the Matlab DataONE toolbox."),
    		  		tokenInputContain = $(document.createElement("div")).attr("id", "token-code-panel").addClass("tab-panel active").append(tokenInput, copyButton, copySuccess),
    		  		rTokenInputContain = $(document.createElement("div")).attr("id", "r-token-code-panel").addClass("tab-panel").append(tokenRText, 	tokenRInput, copyRButton, copySuccess.clone()).addClass("hidden"),
    		  		matlabTokenInputContain = $(document.createElement("div")).attr("id", "matlab-token-code-panel").addClass("tab-panel").append(tokenMatlabText, 	tokenMatlabInput, copyMatlabButton, copySuccess.clone()).addClass("hidden");

    			if(typeof usernamePrefix == "object")
    				usernameMsg += usernamePrefix[0].outerHTML;
    			else if(typeof usernamePrefix == "string")
    				usernameMsg += usernamePrefix;

    			usernameMsg += this.model.get("username") + "</div>";

    			var	successMessage = $.parseHTML(this.alertTemplate({
    					msg: 'Copy your authentication token: <br/> ' + expirationMsg + usernameMsg,
    					classes: "alert-success",
    					containerClasses: "well"
    				}));
    			$(successMessage).append(tabs, tokenInputContain, rTokenInputContain, matlabTokenInputContain);
    			this.$("#token-generator-container").html(successMessage);

    			$(".token-tab").tab();

    			//Create clickable "Copy" buttons to copy text (e.g. token) to the user's clipboard
    			var clipboard = new Clipboard(".copy");

    				clipboard.on("success", function(e){
    				$(".copy-success").show().delay(3000).fadeOut();
    				});

    				clipboard.on("error", function(e){
    					var textarea = $(e.trigger).parent().children("textarea.token");
    					textarea.trigger("focus");
    					textarea.tooltip({
    						title: "Press Ctrl+c to copy",
    						placement: "bottom"
    					});
    					textarea.tooltip("show");

    				});
    		},

    		setUpAutocomplete: function() {
    			var input = this.$(".account-autocomplete");
    			if(!input || !input.length) return;

    			// look up registered identities
    			$(input).hoverAutocomplete({
    				source: function (request, response) {
    		            var term = $.ui.autocomplete.escapeRegex(request.term);

    		            var list = [];

    		            //Ids/Usernames that we want to ignore in the autocompelte
    		            var ignoreEquivIds = ($(this.element).attr("id") == "map-request-field"),
    		            	ignoreIds = ignoreEquivIds? MetacatUI.appUserModel.get("identitiesUsernames") : [];
    		            ignoreIds.push(MetacatUI.appUserModel.get("username").toLowerCase());

    		            var url = MetacatUI.appModel.get("accountsUrl") + "?query=" + encodeURIComponent(term);
    					var requestSettings = {
    						url: url,
    						success: function(data, textStatus, xhr) {
    							_.each($(data).find("person"), function(person, i){
    								var item = {};
    								item.value = $(person).find("subject").text();

    								//Don't display yourself in the autocomplete dropdown (prevents users from adding themselves as an equivalent identity or group member)
    								//Also don't display your equivalent identities in the autocomplete
    								if(_.contains(ignoreIds, item.value.toLowerCase())) return;

    								item.label = $(person).find("fullName").text() || ($(person).find("givenName").text() + " " + $(person).find("familyName").text());
    								list.push(item);
    							});

    				            response(list);

    						}
    					}
    					$.ajax(_.extend(requestSettings, MetacatUI.appUserModel.createAjaxSettings()));

    					//Send an ORCID search when the search string gets long enough
    					if(request.term.length > 3)
    						MetacatUI.appLookupModel.orcidSearch(request, response, false, ignoreIds);
    		        },
    				select: function(e, ui) {
    					e.preventDefault();

    					// set the text field
    					$(e.target).val(ui.item.value);
    					$(e.target).parents("form").find("input[name='fullName']").val(ui.item.label);
    				},
    				position: {
    					my: "left top",
    					at: "left bottom",
    					collision: "none"
    				}
    			});

    		},

        /**
        * Renders a list of portals that this user is an owner of.
        */
        renderMyPortals: function(){

          //If my portals has been disabled, don't render the list
          if( MetacatUI.appModel.get("showMyPortals") === false ){
            return;
          }

          var view = this;

          //If Bookkeeper services are enabled, render the Portals via a PortalUsagesView,
          // which queries Bookkeeper for portal Usages
          if( MetacatUI.appModel.get("enableBookkeeperServices") ){
            require(['views/portals/PortalUsagesView'], function(PortalUsagesView){
              var portalListView = new PortalUsagesView();
              //Render the Portal list view and insert it in the page
              portalListView.render();
              view.$(view.portalListContainer)
                  .html(portalListView.el);
            });
          }
          //If Bookkeeper services are disabled, render the Portals via a PortalListView,
          // which queries Solr for portal docs
          else{
            require(['views/portals/PortalListView'], function(PortalListView){
              //Create a PortalListView
              var portalListView = new PortalListView();
              //Render the Portal list view and insert it in the page
              portalListView.render();
              view.$(view.portalListContainer)
                  .html(portalListView.el);
            });
          }

        },

    		//---------------------------------- Misc. and Utilities -----------------------------------------//

    		showAlert: function(msg, classes, container) {
    			if(!classes)
    				var classes = 'alert-success';
    			if(!container || !$(container).length)
    				var container = this.$el;

    			//Remove any alerts that are already in this container
    			if($(container).children(".alert-container").length > 0)
    				$(container).children(".alert-container").remove();

    			$(container).prepend(
    					this.alertTemplate({
    						msg: msg,
    						classes: classes
    					})
    			);
    		},

    		switchTabs: function(e){
    			e.preventDefault();
    			$(e.target).tab('show');
    			this.$(".tab-panel").hide();
    			this.$(".tab-panel" + $(e.target).attr("href")).show();
    			this.$("#token-generator-container .copy-button").attr("data-clipboard-text")

    		},

    		preventSubmit: function(e){
    			if(e.keyCode != 13) return;

    			e.preventDefault();
    		},

    		onClose: function () {
    			//Clear the template
    			this.$el.html("");

    			//Reset the active section and subsection
    			this.activeSection = "profile";
    			this.activeSubSection = "";

          //Reset the model
          if( this.model ){
    			  this.model.noActivity = null;
            this.stopListening(this.model);
          }

    			//Remove saved elements
    			this.$profile = null;

    			//Stop listening to changes in models
    			this.stopListening(this.statsModel);
    			this.stopListening(MetacatUI.appUserModel);

    			//Close the subviews
    			_.each(this.subviews, function(view){
    				view.onClose();
    			});
    			this.subviews = new Array();
    		}

    	});

    	return UserView;
    });