function()

in kahuna/public/js/usage-rights/usage-rights-editor.js [29:276]


    function($q, $rootScope, $scope, inject$, observe$, editsService, editsApi, imageList) {

    var ctrl = this;
    ctrl.$onInit = () => {

      const multiCat = { name: 'Multiple categories', value: 'multi-cat', properties: [] };

      ctrl.usageRightsHelpLink = window._clientConfig.usageRightsHelpLink;

      // @return Stream.<Array.<UsageRights>>
      const usageRights$ = observe$($scope, () => ctrl.usageRights);

      // @return Stream.<Array.<Category>>
      const allCategories$ = Rx.Observable.fromPromise(editsApi.getUsageRightsCategories());
      const filteredCategories$ = Rx.Observable.fromPromise(editsApi.getFilteredUsageRightsCategories());
      const categories$ = usageRights$.combineLatest(filteredCategories$, allCategories$, (urs, filCats, allCats) => {
          const uniqueCats = getUniqueCats(urs);
          if (uniqueCats.length === 1) {
              if (allCats.length === filCats.length) {
                return allCats;
              }
              const mtchCats = filCats.filter(c => c.value === uniqueCats[0]);
              const extraCats = allCats.filter(c => c.value === uniqueCats[0]);
              if (mtchCats.length === 0 && extraCats.length === 1) {
                return extraCats.concat(filCats);
              } else {
                return filCats;
              }
          } else {
              return filCats;
          }
      });

      // @return Stream.<Array.<Category>>
      const displayCategories$ = usageRights$.combineLatest(categories$, (urs, cats) => {
          const uniqueCats = getUniqueCats(urs);
          if (uniqueCats.length === 1) {
              return cats;
          } else {
              return [multiCat].concat(cats);
          }
      });

      // @return Stream.<Category>
      // FIXME: This is not longer the canonical category as we aren't taking the user interaction
      // into account so this goes stale.
      const category$ = usageRights$.combineLatest(categories$, (urs, cats) => {
          const uniqueCats = getUniqueCats(urs);
          if (uniqueCats.length === 1) {
              const uniqeCat = uniqueCats[0] || '';
              return cats.find(cat => cat.value === uniqeCat);
          } else {
              return multiCat;
          }
      });

      // @return Stream.<Category>
      // The filter is used here to stop the initial setting of `undefined` being published.
      const categoryFromUserChange$ = observe$($scope, () => ctrl.category).filter(cat => !!cat);

      const categoryInvalid$ = categoryFromUserChange$.map((c) => c.value === '');

      // @return Stream.<Category>
      const categoryWithUserChange$ =
          category$.merge(categoryFromUserChange$).distinctUntilChanged();

      const model$ = usageRights$.map(urs => {
          const usageRightsData = (urs.map(ur => ur.data));

          // Get a Map(property, Set(values));
          const objs = imageList.getSetOfProperties(new List(usageRightsData));

          // Return an object with the value, iif there is 1 value
          return objs.filter(obj => obj.size === 1).map(obj => obj.first()).toJS();
      });

      // Stream.<Boolean>
      const savingDisabled$ = categoryWithUserChange$.map(cat => cat === multiCat);

      // Stream.<Boolean>
      const forceRestrictions$ = model$.combineLatest(categoryWithUserChange$, (model, cat) => {
          const defaultRestrictions =
              cat.properties.find(prop => prop.name === 'defaultRestrictions');
          const restrictedProp =
              cat.properties.find(prop => prop.name === 'restrictions');
          return defaultRestrictions || (restrictedProp && restrictedProp.required);
      });

      // Stream.<Boolean>
      const modelHasRestrictions$ = model$.map(model => angular.isDefined(model.restrictions));

      // Stream.<Boolean>
      const showRestrictions$ = forceRestrictions$.combineLatest(modelHasRestrictions$, usageRights$,
          (forceRestrictions, showRestrictions, usageRights) => {
          const [urs] = usageRights;
          if (forceRestrictions) {
              return true;
          } else if (angular.isDefined(urs.data.restrictions)) {
              return true;
          } else {
              return showRestrictions;
          }
      });

      inject$($scope, displayCategories$, ctrl, 'categories');
      inject$($scope, category$, ctrl, 'category');
      inject$($scope, model$, ctrl, 'model');
      inject$($scope, savingDisabled$, ctrl, 'savingDisabled');
      inject$($scope, forceRestrictions$, ctrl, 'forceRestrictions');
      inject$($scope, showRestrictions$, ctrl, 'showRestrictions');
      inject$($scope, categoryInvalid$, ctrl, 'categoryInvalid');

      // TODO: Some of these could be streams
      ctrl.saving = false;
      ctrl.getOptionsFor = property => {
          const options = property.options.map(option => ({ key: option, value: option }));
          if (property.required) {
              return options;
          } else {
              return [{key: 'Other', value: null}].concat(options);
          }
      };
      ctrl.getOptionsMapFor = property => {
          const key = ctrl.category
                          .properties
                          .find(prop => prop.name === property.optionsMapKey)
                          .name;

          const val = ctrl.model[key];
          return property.optionsMap[val] || [];
      };

      $scope.$watch('ctrl.usageRights', (newUsageRights) => {
        const [usageRights] = newUsageRights;

        if (usageRights.data.category) {
          if (ctrl.categories) {
            ctrl.category = ctrl.categories.find(cat => cat.value === usageRights.data.category);
          }

          ctrl.model = usageRights.data;
        } else if (ctrl.categories) {
          ctrl.category = ctrl.categories.find(cat => cat.value === "");
        }
      }, true);

      ctrl.save = () => {
          ctrl.saving = true;
          // we save as `{}` if category isn't defined.
          const data = ctrl.category.value ?
              angular.extend({}, ctrl.model, { category: ctrl.category.value }) : {};

          // unchecking restrictions will remove restriction on save
          if (! ctrl.showRestrictions && data.restrictions) {
              delete data.restrictions;
          }

          save(data).
              catch(uiError).
              finally(saveComplete);
      };

      ctrl.reset = () => {
          ctrl.model = {restrictions: ctrl.model.restrictions};
          ctrl.showRestrictions = undefined;
      };

      ctrl.cancel = () => ctrl.onCancel();

      ctrl.isOtherValue = (property) =>
        angular.isDefined(ctrl.model[property.name]) ? !(ctrl.getOptionsMapFor(property).includes(ctrl.model[property.name])) : undefined;

      function save(data) {
        return trackAll($q, $rootScope, "rights", ctrl.usageRights, [
          ({ image }) => {
            const resource = image.data.userMetadata.data.usageRights;
            return editsService.update(resource, data, image, true);
          },
          ({ image }) => {
            const prevRights = (0 < ctrl.usageRights.size) ? ctrl.usageRights.first().data.category : "";
            return setLeasesFromUsageRights(image, prevRights);
          },
          ({ image }) => setMetadataFromUsageRights(image, true),
          ({ image }) => image.get()
        ],'images-updated');
      }

      function saveComplete() {
          ctrl.onSave();
          ctrl.saving = false;
      }

      function getUniqueCats(usageRights) {
          return unique(usageRights.map(ur => ur.data.category));
      }

      function unique(arr) {
          return arr.reduce((prev, curr) =>
              prev.indexOf(curr) !== -1 ? prev : prev.concat(curr), []);
      }

      function uiError(error) {
          // ♫ Very superstitious ♫
          ctrl.error = error && error.body && error.body.errorMessage ||
              'Unexpected error';
      }

      function setLeasesFromUsageRights(image, prevRights) {
        if (ctrl.category.leases.length === 0) {
          // possibility of removal only
          const removeLeases = removeCategoryLeases(ctrl.categories, image, prevRights);
          if (removeLeases && removeLeases.length > 0) {
            $rootScope.$broadcast('events:rights-category:delete-leases', {
              catLeases: removeLeases,
              batch: false
            });
          }
          return;
        }
        const catLeases = createCategoryLeases(ctrl.category.leases, image);
        if (catLeases.length === 0) {
          // possibility of removal only - missing tx date etc.
          const removeLeases = removeCategoryLeases(ctrl.categories, image, prevRights);
          if (removeLeases && removeLeases.length > 0) {
            $rootScope.$broadcast('events:rights-category:delete-leases', {
              catLeases: removeLeases,
              batch: false
            });
          }
          return;
        }
        $rootScope.$broadcast('events:rights-category:add-leases', {
          catLeases: catLeases,
          batch: false
        });
      }

      // HACK: This should probably live somewhere else, but it's the least intrusive
      // here. This updates the metadata based on the usage rights to stop users having
      // to enter content twice.
      // ALSO: inBatch determines whether the function chain should eventually emit an angular message
      // as emitting multiple times is very performance heavy
      // ideally this should be refactored out.
      function setMetadataFromUsageRights(image, inBatch = false) {
          return editsService.updateMetadataFromUsageRights(image, inBatch);
      }
    };
}]);