/*******************************************************************************
 * Copyright 2018 The MIT Internet Trust Consortium
 *
 * Portions copyright 2011-2013 The MITRE Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
var DynRegClient = Backbone.Model.extend({
  idAttribute: "client_id",

  defaults: {
    client_id: null,
    client_secret: null,
    redirect_uris: [],
    client_name: null,
    client_uri: null,
    contacts: [],
    tos_uri: null,
    token_endpoint_auth_method: null,
    scope: null,
    grant_types: [],
    response_types: [],
    policy_uri: null,

    jwks_uri: null,
    jwks: null,
    jwksType: "URI",

    application_type: null,
    sector_identifier_uri: null,
    subject_type: null,

    request_object_signing_alg: null,

    userinfo_signed_response_alg: null,
    userinfo_encrypted_response_alg: null,
    userinfo_encrypted_response_enc: null,

    id_token_signed_response_alg: null,
    id_token_encrypted_response_alg: null,
    id_token_encrypted_response_enc: null,

    default_max_age: null,
    require_auth_time: false,
    default_acr_values: null,

    initiate_login_uri: null,
    post_logout_redirect_uris: null,

    claims_redirect_uris: [],

    request_uris: [],

    software_statement: null,
    software_id: null,
    software_version: null,

    code_challenge_method: null,

    registration_access_token: null,
    registration_client_uri: null,
  },

  sync: function (method, model, options) {
    if (model.get("registration_access_token")) {
      var headers = options.headers ? options.headers : {};
      headers["Authorization"] =
        "Bearer " + model.get("registration_access_token");
      options.headers = headers;
    }

    return this.constructor.__super__.sync(method, model, options);
  },

  urlRoot: "register",
});

var DynRegRootView = Backbone.View.extend({
  tagName: "span",

  initialize: function (options) {
    this.options = options;
  },

  events: {
    "click #newreg": "newReg",
    "click #editreg": "editReg",
  },

  load: function (callback) {
    if (this.options.systemScopeList.isFetched) {
      callback();
      return;
    }

    $("#loadingbox").sheet("show");
    $("#loading").html(
      '<span class="label" id="loading-scopes">' +
        $.t("common.scopes") +
        "</span> ",
    );

    $.when(
      this.options.systemScopeList.fetchIfNeeded({
        success: function (e) {
          $("#loading-scopes").addClass("label-success");
        },
        error: app.errorHandlerView.handleError(),
      }),
    ).done(function () {
      $("#loadingbox").sheet("hide");
      callback();
    });
  },

  render: function () {
    $(this.el).html($("#tmpl-dynreg").html());
    $(this.el).i18n();
    return this;
  },

  newReg: function (e) {
    e.preventDefault();
    this.remove();
    app.navigate("dev/dynreg/new", {
      trigger: true,
    });
  },

  editReg: function (e) {
    e.preventDefault();
    var clientId = $("#clientId").val();
    var token = $("#regtoken").val();

    var client = new DynRegClient({
      client_id: clientId,
      registration_access_token: token,
    });

    var self = this;

    client.fetch({
      success: function () {
        var userInfo = getUserInfo();
        var contacts = client.get("contacts");
        if (
          userInfo != null &&
          userInfo.email != null &&
          !_.contains(contacts, userInfo.email)
        ) {
          contacts.push(userInfo.email);
        }
        client.set(
          {
            contacts: contacts,
          },
          {
            silent: true,
          },
        );

        if (client.get("jwks")) {
          client.set(
            {
              jwksType: "VAL",
            },
            {
              silent: true,
            },
          );
        } else {
          client.set(
            {
              jwksType: "URI",
            },
            {
              silent: true,
            },
          );
        }

        var view = new DynRegEditView({
          model: client,
          systemScopeList: app.systemScopeList,
        });

        view.load(function () {
          $("#content").html(view.render().el);
          view.delegateEvents();
          setPageTitle($.t("dynreg.edit-dynamically-registered"));
          app.navigate("dev/dynreg/edit", {
            trigger: true,
          });
          self.remove();
        });
      },
      error: app.errorHandlerView.handleError({
        message: $.t("dynreg.invalid-access-token"),
      }),
    });
  },
});

var DynRegEditView = Backbone.View.extend({
  tagName: "span",

  initialize: function (options) {
    this.options = options;
    if (!this.template) {
      this.template = _.template($("#tmpl-dynreg-client-form").html());
    }

    this.redirectUrisCollection = new Backbone.Collection();
    this.scopeCollection = new Backbone.Collection();
    this.contactsCollection = new Backbone.Collection();
    this.defaultAcrValuesCollection = new Backbone.Collection();
    this.requestUrisCollection = new Backbone.Collection();
    this.resourceIdsCollection = new Backbone.Collection();
    this.postLogoutRedirectUrisCollection = new Backbone.Collection();
    this.claimsRedirectUrisCollection = new Backbone.Collection();

    this.listWidgetViews = [];
  },

  load: function (callback) {
    if (this.options.systemScopeList.isFetched) {
      callback();
      return;
    }

    $("#loadingbox").sheet("show");
    $("#loading").html(
      '<span class="label" id="loading-scopes">' +
        $.t("common.scopes") +
        "</span> ",
    );

    $.when(
      this.options.systemScopeList.fetchIfNeeded({
        success: function (e) {
          $("#loading-scopes").addClass("label-success");
        },
        error: app.errorHandlerView.handleError(),
      }),
    ).done(function () {
      $("#loadingbox").sheet("hide");
      callback();
    });
  },

  events: {
    "click .btn-save": "saveClient",
    "click .btn-cancel": "cancel",
    "click .btn-delete": "deleteClient",
    "change #tokenEndpointAuthMethod input:radio": "toggleClientCredentials",
    "change #jwkSelector input:radio": "toggleJWKSetType",
  },

  cancel: function (e) {
    e.preventDefault();
    app.navigate("dev/dynreg", {
      trigger: true,
    });
  },

  deleteClient: function (e) {
    e.preventDefault();

    if (confirm($.t("client.client-table.confirm"))) {
      var self = this;

      this.model.destroy({
        dataType: false,
        processData: false,
        success: function () {
          self.remove();
          app.navigate("dev/dynreg", {
            trigger: true,
          });
        },
        error: app.errorHandlerView.handleError({
          log: "An error occurred when deleting a client",
        }),
      });
    }

    return false;
  },

  /**
   * Set up the form based on the current state of the tokenEndpointAuthMethod
   * parameter
   *
   * @param event
   */
  toggleClientCredentials: function () {
    var tokenEndpointAuthMethod = $("#tokenEndpointAuthMethod input", this.el)
      .filter(":checked")
      .val();

    // show or hide the signing algorithm method depending on what's
    // selected
    if (
      tokenEndpointAuthMethod == "private_key_jwt" ||
      tokenEndpointAuthMethod == "client_secret_jwt"
    ) {
      $("#tokenEndpointAuthSigningAlg", this.el).show();
    } else {
      $("#tokenEndpointAuthSigningAlg", this.el).hide();
    }
  },

  /**
   * Set up the form based on the JWK Set selector
   */
  toggleJWKSetType: function () {
    var jwkSelector = $("#jwkSelector input:radio", this.el)
      .filter(":checked")
      .val();

    if (jwkSelector == "URI") {
      $("#jwksUri", this.el).show();
      $("#jwks", this.el).hide();
    } else if (jwkSelector == "VAL") {
      $("#jwksUri", this.el).hide();
      $("#jwks", this.el).show();
    } else {
      $("#jwksUri", this.el).hide();
      $("#jwks", this.el).hide();
    }
  },

  disableUnsupportedJOSEItems: function (serverSupported, query) {
    var supported = ["default"];
    if (serverSupported) {
      supported = _.union(supported, serverSupported);
    }
    $(query, this.$el).each(function (idx) {
      if (_.contains(supported, $(this).val())) {
        $(this).prop("disabled", false);
      } else {
        $(this).prop("disabled", true);
      }
    });
  },

  // returns "null" if given the value "default" as a string,
  // otherwise returns input value. useful for parsing the JOSE
  // algorithm dropdowns
  defaultToNull: function (value) {
    if (value == "default") {
      return null;
    } else {
      return value;
    }
  },

  // returns "null" if the given value is falsy
  emptyToNull: function (value) {
    if (value) {
      return value;
    } else {
      return null;
    }
  },

  // maps from a form-friendly name to the real grant parameter name
  grantMap: {
    authorization_code: "authorization_code",
    password: "password",
    implicit: "implicit",
    client_credentials: "client_credentials",
    redelegate: "urn:ietf:params:oauth:grant_type:redelegate",
    refresh_token: "refresh_token",
  },

  // maps from a form-friendly name to the real response type
  // parameter name
  responseMap: {
    code: "code",
    token: "token",
    idtoken: "id_token",
    "token-idtoken": "token id_token",
    "code-idtoken": "code id_token",
    "code-token": "code token",
    "code-token-idtoken": "code token id_token",
  },

  saveClient: function (e) {
    e.preventDefault();

    $(".control-group").removeClass("error");

    // sync any leftover collection items
    _.each(this.listWidgetViews, function (v) {
      v.addItem($.Event("click"));
    });

    // build the scope object
    var scopes = this.scopeCollection.pluck("item").join(" ");

    // build the grant type object
    var grantTypes = [];
    $.each(this.grantMap, function (index, type) {
      if ($("#grantTypes-" + index).is(":checked")) {
        grantTypes.push(type);
      }
    });

    // build the response type object
    var responseTypes = [];
    $.each(this.responseMap, function (index, type) {
      if ($("#responseTypes-" + index).is(":checked")) {
        responseTypes.push(type);
      }
    });

    var contacts = this.contactsCollection.pluck("item");
    var userInfo = getUserInfo();
    if (userInfo && userInfo.email) {
      if (!_.contains(contacts, userInfo.email)) {
        contacts.push(userInfo.email);
      }
    }

    // make sure that the subject identifier is consistent with the
    // redirect URIs
    var subjectType = $("#subjectType input").filter(":checked").val();
    var redirectUris = this.redirectUrisCollection.pluck("item");
    var sectorIdentifierUri = $("#sectorIdentifierUri input").val();
    if (
      subjectType == "PAIRWISE" &&
      redirectUris.length > 1 &&
      sectorIdentifierUri == ""
    ) {
      // Display an alert with an error message
      app.errorHandlerView.showErrorMessage(
        $.t("client.client-form.error.consistency"),
        $.t("client.client-form.error.pairwise-sector"),
      );
      return false;
    }

    // process the JWKS
    var jwksUri = null;
    var jwks = null;
    var jwkSelector = $("#jwkSelector input:radio", this.el)
      .filter(":checked")
      .val();

    if (jwkSelector == "URI") {
      jwksUri = $("#jwksUri input").val();
      jwks = null;
    } else if (jwkSelector == "VAL") {
      jwksUri = null;
      try {
        jwks = JSON.parse($("#jwks textarea").val());
      } catch (e) {
        console.log("An error occurred when parsing the JWK Set");
        app.errorHandlerView.showErrorMessage(
          $.t("client.client-form.error.jwk-set"),
          $.t("client.client-form.error.jwk-set-parse"),
        );
        return false;
      }
    } else {
      jwksUri = null;
      jwks = null;
    }

    var attrs = {
      client_name: this.emptyToNull($("#clientName input").val()),
      redirect_uris: redirectUris,
      grant_types: grantTypes,
      scope: scopes,
      client_secret: null, // never send a client secret
      tos_uri: this.emptyToNull($("#tosUri input").val()),
      policy_uri: this.emptyToNull($("#policyUri input").val()),
      client_uri: this.emptyToNull($("#clientUri input").val()),
      application_type: $("#applicationType input").filter(":checked").val(),
      jwks_uri: jwksUri,
      jwks: jwks,
      subject_type: subjectType,
      software_statement: this.emptyToNull(
        $("#softwareStatement textarea").val(),
      ),
      softwareId: this.emptyToNull($("#softwareId input").val()),
      softwareVersion: this.emptyToNull($("#softwareVersion input").val()),
      token_endpoint_auth_method: $("#tokenEndpointAuthMethod input")
        .filter(":checked")
        .val(),
      response_types: responseTypes,
      sector_identifier_uri: sectorIdentifierUri,
      initiate_login_uri: this.emptyToNull($("#initiateLoginUri input").val()),
      post_logout_redirect_uris:
        this.postLogoutRedirectUrisCollection.pluck("item"),
      claims_redirect_uris: this.claimsRedirectUrisCollection.pluck("item"),
      require_auth_time: $("#requireAuthTime input").is(":checked"),
      default_max_age: parseInt($("#defaultMaxAge input").val()),
      contacts: contacts,
      request_uris: this.requestUrisCollection.pluck("item"),
      resource_ids: this.resourceIdsCollection.pluck("item"),
      default_acr_values: this.defaultAcrValuesCollection.pluck("item"),
      request_object_signing_alg: this.defaultToNull(
        $("#requestObjectSigningAlg select").val(),
      ),
      userinfo_signed_response_alg: this.defaultToNull(
        $("#userInfoSignedResponseAlg select").val(),
      ),
      userinfo_encrypted_response_alg: this.defaultToNull(
        $("#userInfoEncryptedResponseAlg select").val(),
      ),
      userinfo_encrypted_response_enc: this.defaultToNull(
        $("#userInfoEncryptedResponseEnc select").val(),
      ),
      id_token_signed_response_alg: this.defaultToNull(
        $("#idTokenSignedResponseAlg select").val(),
      ),
      id_token_encrypted_response_alg: this.defaultToNull(
        $("#idTokenEncryptedResponseAlg select").val(),
      ),
      id_token_encrypted_response_enc: this.defaultToNull(
        $("#idTokenEncryptedResponseEnc select").val(),
      ),
      token_endpoint_auth_signing_alg: this.defaultToNull(
        $("#tokenEndpointAuthSigningAlg select").val(),
      ),
      code_challenge_method: this.defaultToNull(
        $("#codeChallengeMethod select").val(),
      ),
    };

    // set all empty strings to nulls
    for (var key in attrs) {
      if (attrs[key] === "") {
        attrs[key] = null;
      }
    }

    var _self = this;
    this.model.save(attrs, {
      success: function () {
        // switch to an "edit" view
        app.navigate("dev/dynreg/edit", {
          trigger: true,
        });
        _self.remove();

        var userInfo = getUserInfo();
        var contacts = _self.model.get("contacts");
        if (
          userInfo != null &&
          userInfo.email != null &&
          !_.contains(contacts, userInfo.email)
        ) {
          contacts.push(userInfo.email);
        }
        _self.model.set(
          {
            contacts: contacts,
          },
          {
            silent: true,
          },
        );

        if (_self.model.get("jwks")) {
          _self.model.set(
            {
              jwksType: "VAL",
            },
            {
              silent: true,
            },
          );
        } else {
          _self.model.set(
            {
              jwksType: "URI",
            },
            {
              silent: true,
            },
          );
        }

        var view = new DynRegEditView({
          model: _self.model,
          systemScopeList: _self.options.systemScopeList,
        });

        view.load(function () {
          // reload
          $("#content").html(view.render().el);
          view.delegateEvents();
        });
      },
      error: app.errorHandlerView.handleError({
        log: "An error occurred when saving a client",
      }),
    });

    return false;
  },

  render: function () {
    var data = {
      client: this.model.toJSON(),
      userInfo: getUserInfo(),
      heartMode: heartMode,
    };
    $(this.el).html(this.template(data));

    this.listWidgetViews = [];

    var _self = this;

    // build and bind registered redirect URI collection and view
    _.each(this.model.get("redirect_uris"), function (redirectUri) {
      _self.redirectUrisCollection.add(
        new URIModel({
          item: redirectUri,
        }),
      );
    });

    var redirectUriView = new ListWidgetView({
      type: "uri",
      placeholder: "https://",
      helpBlockText: $.t("client.client-form.redirect-uris-help"),
      collection: this.redirectUrisCollection,
    });
    $("#redirectUris .controls", this.el).html(redirectUriView.render().el);
    this.listWidgetViews.push(redirectUriView);

    // build and bind scopes
    var scopes = this.model.get("scope");
    var scopeSet = scopes ? scopes.split(" ") : [];
    _.each(scopeSet, function (scope) {
      _self.scopeCollection.add(
        new Backbone.Model({
          item: scope,
        }),
      );
    });

    var scopeView = new ListWidgetView({
      placeholder: $.t("client.client-form.scope-placeholder"),
      autocomplete: _.uniq(
        _.flatten(
          this.options.systemScopeList.unrestrictedScopes().pluck("value"),
        ),
      ),
      helpBlockText: $.t("client.client-form.scope-help"),
      collection: this.scopeCollection,
    });
    $("#scope .controls", this.el).html(scopeView.render().el);
    this.listWidgetViews.push(scopeView);

    // build and bind contacts
    _.each(this.model.get("contacts"), function (contact) {
      _self.contactsCollection.add(
        new Backbone.Model({
          item: contact,
        }),
      );
    });

    var contactView = new ListWidgetView({
      placeholder: $.t("client.client-form.contacts-placeholder"),
      helpBlockText: $.t("client.client-form.contacts-help"),
      collection: this.contactsCollection,
    });
    $("#contacts .controls div", this.el).html(contactView.render().el);
    this.listWidgetViews.push(contactView);

    // build and bind post-logout redirect URIs
    _.each(
      this.model.get("post_logout_redirect_uris"),
      function (postLogoutRedirectUri) {
        _self.postLogoutRedirectUrisCollection.add(
          new URIModel({
            item: postLogoutRedirectUri,
          }),
        );
      },
    );

    var postLogoutRedirectUrisView = new ListWidgetView({
      type: "uri",
      placeholder: "https://",
      helpBlockText: $.t("client.client-form.post-logout-help"),
      collection: this.postLogoutRedirectUrisCollection,
    });
    $("#postLogoutRedirectUris .controls", this.el).html(
      postLogoutRedirectUrisView.render().el,
    );
    this.listWidgetViews.push(postLogoutRedirectUrisView);

    // build and bind claims redirect URIs
    _.each(this.model.get("claimsRedirectUris"), function (claimsRedirectUri) {
      _self.claimsRedirectUrisCollection.add(
        new URIModel({
          item: claimsRedirectUri,
        }),
      );
    });

    var claimsRedirectUrisView = new ListWidgetView({
      type: "uri",
      placeholder: "https://",
      helpBlockText: $.t("client.client-form.claims-redirect-uris-help"),
      collection: this.claimsRedirectUrisCollection,
    });
    $("#claimsRedirectUris .controls", this.el).html(
      claimsRedirectUrisView.render().el,
    );
    this.listWidgetViews.push(claimsRedirectUrisView);

    // build and bind request URIs
    _.each(this.model.get("request_uris"), function (requestUri) {
      _self.requestUrisCollection.add(
        new URIModel({
          item: requestUri,
        }),
      );
    });

    var requestUriView = new ListWidgetView({
      type: "uri",
      placeholder: "https://",
      helpBlockText: $.t("client.client-form.request-uri-help"),
      collection: this.requestUrisCollection,
    });
    $("#requestUris .controls", this.el).html(requestUriView.render().el);
    this.listWidgetViews.push(requestUriView);

    // build and bind resource IDs
    _.each(this.model.get("resource_ids"), function (resource) {
      _self.resourceIdsCollection.add(
        new Backbone.Model({
          item: resource,
        }),
      );
    });

    var resourceIdsView = new ListWidgetView({
      placeholder: $.t("client.client-form.resource-ids-placeholder"),
      helpBlockText: $.t("client.client-form.resource-ids-help"),
      collection: this.resourceIdsCollection,
    });
    $("#resourceIds .controls div", this.el).html(resourceIdsView.render().el);
    this.listWidgetViews.push(resourceIdsView);

    // build and bind default ACR values
    _.each(this.model.get("default_acr_values"), function (defaultAcrValue) {
      _self.defaultAcrValuesCollection.add(
        new Backbone.Model({
          item: defaultAcrValue,
        }),
      );
    });

    var defaultAcrView = new ListWidgetView({
      placeholder: $.t("client.client-form.acr-values-placeholder"),
      // TODO: autocomplete from spec
      helpBlockText: $.t("client.client-form.acr-values-help"),
      collection: this.defaultAcrValuesCollection,
    });
    $("#defaultAcrValues .controls", this.el).html(defaultAcrView.render().el);
    this.listWidgetViews.push(defaultAcrView);

    this.toggleClientCredentials();
    this.toggleJWKSetType();

    // disable unsupported JOSE algorithms
    this.disableUnsupportedJOSEItems(
      app.serverConfiguration.request_object_signing_alg_values_supported,
      "#requestObjectSigningAlg option",
    );
    this.disableUnsupportedJOSEItems(
      app.serverConfiguration.userinfo_signing_alg_values_supported,
      "#userInfoSignedResponseAlg option",
    );
    this.disableUnsupportedJOSEItems(
      app.serverConfiguration.userinfo_encryption_alg_values_supported,
      "#userInfoEncryptedResponseAlg option",
    );
    this.disableUnsupportedJOSEItems(
      app.serverConfiguration.userinfo_encryption_enc_values_supported,
      "#userInfoEncryptedResponseEnc option",
    );
    this.disableUnsupportedJOSEItems(
      app.serverConfiguration.id_token_signing_alg_values_supported,
      "#idTokenSignedResponseAlg option",
    );
    this.disableUnsupportedJOSEItems(
      app.serverConfiguration.id_token_encryption_alg_values_supported,
      "#idTokenEncryptedResponseAlg option",
    );
    this.disableUnsupportedJOSEItems(
      app.serverConfiguration.id_token_encryption_enc_values_supported,
      "#idTokenEncryptedResponseEnc option",
    );
    this.disableUnsupportedJOSEItems(
      app.serverConfiguration.token_endpoint_auth_signing_alg_values_supported,
      "#tokenEndpointAuthSigningAlg option",
    );

    this.$(".nyi").clickover({
      placement: "right",
      title: $.t("common.not-yet-implemented"),
      content: $.t("common.not-yet-implemented-content"),
    });

    $(this.el).i18n();
    return this;
  },
});

ui.routes.push({
  path: "dev/dynreg",
  name: "dynReg",
  callback: function () {
    this.breadCrumbView.collection.reset();
    this.breadCrumbView.collection.add([
      {
        text: $.t("admin.home"),
        href: "",
      },
      {
        text: $.t("admin.self-service-client"),
        href: "manage/#dev/dynreg",
      },
    ]);

    var view = new DynRegRootView({
      systemScopeList: this.systemScopeList,
    });

    this.updateSidebar("dev/dynreg");

    view.load(function () {
      $("#content").html(view.render().el);

      setPageTitle($.t("admin.self-service-client"));
    });
  },
});

ui.routes.push({
  path: "dev/dynreg/new",
  name: "newDynReg",
  callback: function () {
    this.breadCrumbView.collection.reset();
    this.breadCrumbView.collection.add([
      {
        text: $.t("admin.home"),
        href: "",
      },
      {
        text: $.t("admin.self-service-client"),
        href: "manage/#dev/dynreg",
      },
      {
        text: $.t("dynreg.new-client"),
        href: "manage/#dev/dynreg/new",
      },
    ]);

    this.updateSidebar("dev/dynreg");

    var client = new DynRegClient();
    var view = new DynRegEditView({
      model: client,
      systemScopeList: this.systemScopeList,
    });

    view.load(function () {
      var userInfo = getUserInfo();
      var contacts = [];
      if (userInfo != null && userInfo.email != null) {
        contacts.push(userInfo.email);
      }

      if (heartMode) {
        client.set(
          {
            require_auth_time: true,
            default_max_age: 60000,
            scope: _.uniq(
              _.flatten(
                app.systemScopeList.defaultUnrestrictedScopes().pluck("value"),
              ),
            ).join(" "),
            token_endpoint_auth_method: "private_key_jwt",
            grant_types: ["authorization_code"],
            response_types: ["code"],
            subject_type: "public",
            contacts: contacts,
          },
          {
            silent: true,
          },
        );
      } else {
        client.set(
          {
            require_auth_time: true,
            default_max_age: 60000,
            scope: _.uniq(
              _.flatten(
                app.systemScopeList.defaultUnrestrictedScopes().pluck("value"),
              ),
            ).join(" "),
            token_endpoint_auth_method: "client_secret_basic",
            grant_types: ["authorization_code"],
            response_types: ["code"],
            subject_type: "public",
            contacts: contacts,
          },
          {
            silent: true,
          },
        );
      }

      $("#content").html(view.render().el);
      view.delegateEvents();
      setPageTitle($.t("dynreg.new-client"));
    });
  },
});

ui.routes.push({
  path: "dev/dynreg/edit",
  name: "editDynReg",
  callback: function () {
    this.breadCrumbView.collection.reset();
    this.breadCrumbView.collection.add([
      {
        text: $.t("admin.home"),
        href: "",
      },
      {
        text: $.t("admin.self-service-client"),
        href: "manage/#dev/dynreg",
      },
      {
        text: $.t("dynreg.edit-existing"),
        href: "manage/#dev/dynreg/edit",
      },
    ]);

    this.updateSidebar("dev/dynreg");

    setPageTitle($.t("dynreg.edit-existing"));
    // note that this doesn't actually load the client, that's supposed to
    // happen elsewhere...
  },
});

ui.templates.push("resources/template/dynreg.html");
