'use strict'; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * Licensed Materials - Property of IBM * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2019, 2020 * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp. * */ define(['../lib/@waca/dashboard-common/dist/core/APIFactory', '../lib/@waca/core-client/js/core-client/utils/ClassFactory', './FeatureLoaderAPI', 'underscore'], function (APIFactory, ClassFactory, FeatureLoaderAPI, _) { 'use strict'; /** * Create the feature dependency map that will be passed to a given feature in the constructor * We define the getter function where we check if the feature can access the feature at init time or not. * * @param {String} featureName - Feature name * @param {Object[]} dependencyList List of dependencies that the feature requires. The list contains a list of objects that describe a feature * @param {String} dependency.name dependency name * @param {boolean} dependency.init True if the dependency is an init time dependency * @param {FeatureLoader} dependency.loader The feature loader used to access the feature * @param {Object} [loadingState] the loading state of a given feature. Used to check we are in the init phase or not * @param {Bboolean} [loadingState.loaded] Indicates if the feature is done loading or not */ var createFeatureDependencies = function createFeatureDependencies(featureName, dependencyList, loadingState) { var map = {}; if (dependencyList) { var _loop = function _loop(name) { var dep = dependencyList[name]; Object.defineProperty(map, name, { get: function get() { if (!dep.init && !loadingState.loaded) { throw new Error('The required feature "' + dep.name + '" cannot be accessed at init time by the feature"' + featureName + '". The feature "' + dep.name + '" must be added to the initDependencies of feature "' + +featureName + '" if it is needed at init time.'); } return dep.loader.getFeature(dep.name); } }); }; for (var name in dependencyList) { _loop(name); } } return map; }; var MODULE_LOADING_ERROR = {}; var FeatureLoader = function () { /** * * @param {Object} options * @param {Object} options.dashboardAPI * @param {Object} options.featureParams - parameters to be passed to the feautre when it is created * @param {String} options.collectionName - collection name that contains the features to be loaderd * @param {Object[]} options.inlineFeatures - array of inline feature to be created * @param {Object} options.lifeCycleManager - lifecyclemanager */ function FeatureLoader(options) { _classCallCheck(this, FeatureLoader); this.externalFeatureLoader = options.externalFeatures; this.featureNamePrefix = options.featureNamePrefix; this.featureParams = options.featureParams; this.features = {}; this.lifeCycleManager = options.lifeCycleManager; this.loadingOrder = []; this.errors = {}; this.logger = options.logger || console; this.types = options.types || []; this.parentAPI = options.parentAPI; // todo - remove the featureSet -- it is not needed this.featureSet = options.featureSet; } FeatureLoader.prototype.getAPI = function getAPI() { if (!this._api) { this._api = APIFactory.createAPI(this, [FeatureLoaderAPI]); } return this._api; }; FeatureLoader.prototype._getFeatureListThatInitDependsOn = function _getFeatureListThatInitDependsOn(featureName) { var list = []; for (var name in this.features) { if (this._getInitDependencies(name).indexOf(featureName) !== -1) { list.push(name); } } return list; }; FeatureLoader.prototype._getFeatureModule = function _getFeatureModule(feature) { return feature.module.default && _.isFunction(feature.module.default) ? feature.module.default : feature.module; }; FeatureLoader.prototype._createFeature = function _createFeature(feature) { feature.isCreated = true; var params = feature.params; var module = this._getFeatureModule(feature); feature.instance = new module(params); return feature.instance; }; /** * Get the feature instance. This function will create the feature if it is hasn't been created. * We will also create any feature that has init depenency on this feature.. These dependent features might want to register themselves with this feature * This is typically when we have a "provider" type feature with the purpose of only registering itself in some other feature * These features are not typically accessed directly, so they won't get created if we don't do this trick here * * * @param {String} name */ FeatureLoader.prototype._getFeatureInstance = function _getFeatureInstance(name) { var _this = this; var instance = void 0; if (this.features[name]) { if (!this.features[name].instance && !this._getError(name)) { try { //delayed creation of the feature var _instance = this._createFeature(this.features[name]); this.setParentChildRelation(name, _instance); // Create any feature that has init depenency on this feature. See comments above // TODO - fix this function to also find features that depend on thi given feature when using a feature modifier/type // example : 3 feature: A, B , C // A depends on C // B depends on C.modifier // When C is created, we want to create A and B .. but the following function only finds A this._getFeatureListThatInitDependsOn(name).forEach(function (depName) { if (!_this.features[depName].isCreated) { _this._getFeatureInstance(depName); } }); } catch (e) { this._setError(name, 'Error while creating the feature:' + name, e, this.features); } } instance = this.features[name].instance; } return instance; }; FeatureLoader.prototype._getFeatureTags = function _getFeatureTags(name) { return this.features[name] && this.features[name].spec && this.features[name].spec.tags || []; }; FeatureLoader.prototype._getOptions = function _getOptions(name) { var features = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.features; return features[name] && features[name].spec && features[name].spec.options; }; FeatureLoader.prototype._getInitDependencies = function _getInitDependencies(name) { var features = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.features; var initDep = features[name] && features[name].spec && features[name].spec.dependencies || []; return Array.isArray(initDep) ? initDep : Object.keys(initDep); }; FeatureLoader.prototype._getRuntimeDependencies = function _getRuntimeDependencies(name) { var features = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.features; var runtimeDep = features[name] && features[name].spec && features[name].spec.runtimeDependencies || []; return Array.isArray(runtimeDep) ? runtimeDep : Object.keys(runtimeDep); }; FeatureLoader.prototype._getDependencies = function _getDependencies(name) { var features = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.features; return this._getInitDependencies(name, features).concat(this._getRuntimeDependencies(name, features)); }; // Return the feature alias name if one is defined in the dependency FeatureLoader.prototype._getDependenciesPreferredName = function _getDependenciesPreferredName(depName, name) { var features = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.features; var preferredName = depName; // check init depdendencies var initDep = features[name] && features[name].spec && features[name].spec.dependencies; var runtimeDep = features[name] && features[name].spec && features[name].spec.runtimeDependencies; if (initDep && !Array.isArray(initDep) && initDep[depName]) { preferredName = initDep[depName]; } else if (runtimeDep && !Array.isArray(runtimeDep) && runtimeDep[depName]) { preferredName = runtimeDep[depName]; } return preferredName; }; FeatureLoader.prototype._getError = function _getError(name) { var features = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.features; return features[name] && features[name].error; }; FeatureLoader.prototype.setParentChildRelation = function setParentChildRelation(name, instance) { if (this.parentAPI) { APIFactory.setParentChildRelation(this.parentAPI, instance, { propagationInfo: { namespace: name.toLowerCase(), info: { featureName: name }, callStack: { name: 'getFeature', params: [name] } } }); } }; /** * A function to manually register a service. Could be used by the dashboard framework to register low level features. * @param {String} name * @param {Object} instance * @param {Object} spec */ FeatureLoader.prototype.registerFeature = function registerFeature(name, instance, spec, skipInitialization) { var _this2 = this; this.features[name] = { instance: instance, spec: spec || {}, preparePromise: Promise.resolve(name) }; if (this.featureNamePrefix) { this.features[this.featureNamePrefix + '.' + name] = this.features[name]; } var promise = void 0; if (skipInitialization) { promise = Promise.resolve(); } else { promise = this._initializeFeature(name); } return promise.then(function () { // Hook up API events after the feature is initialized _this2.setParentChildRelation(name, instance); }); }; /** * Load the features from a collection json * * @param {Object[]} features * @param {Object} [featureSpecs] Feature specfications provided as paramaters to features * */ FeatureLoader.prototype.loadFeaturesFromArray = function loadFeaturesFromArray(collectionItems, featureSpecs) { var _this3 = this; if (featureSpecs && Object.keys(featureSpecs).length > 0) { this.featureParams = this.featureParams ? this.featureParams : {}; this.featureParams.featureSpecs = Object.assign({}, this.featureParams.featureSpecs, featureSpecs); } var promises = []; var features = {}; var loadingState = { loaded: false, readyPromises: [] }; collectionItems.forEach(function (feature) { var identifier = feature.name || feature.id; // If the feature has types make it matches the type associated with the feature loader var skipFeature = false; if (_this3.types.length > 0 && feature.types && feature.types.length > 0) { var matchedType = _this3.types.find(function (type) { return feature.types.indexOf(type) !== -1; }); skipFeature = !matchedType; } if (!skipFeature && (!_this3.featureSet || _this3.featureSet.indexOf(identifier) !== -1)) { features[identifier] = { spec: feature }; if (MODULE_LOADING_ERROR[feature.class]) { // We already failed while loading this class. don't try again. Rquire.j will not hang the second time _this3._setError(identifier, 'Failed to require feature', MODULE_LOADING_ERROR[feature.class].causedBy, features); } else { promises.push(ClassFactory.loadModule(feature.class).then(function (module) { features[identifier].module = module; }).catch(function (e) { console.log(e); _this3._setError(identifier, 'Failed to require feature', e.causedBy, features); MODULE_LOADING_ERROR[feature.class] = e; })); } } }); // Create the features based on the dependencies list and initialize them return Promise.all(promises).then(function () { //Validate the dependencies _this3._validateDependencies(features); var name = void 0; for (name in features) { _this3._prepareFeature(name, features, loadingState); } return Promise.all(loadingState.readyPromises).then(function () { loadingState.loaded = true; }); }); }; /** * Return the order the features were loaded in * Used for testing to verify the order of loading */ FeatureLoader.prototype.getFeatureLoadingOrder = function getFeatureLoadingOrder() { return this.loadingOrder; }; FeatureLoader.prototype._setError = function _setError(featureName, message) { var details = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; var features = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : this.features; if (!features[featureName]) { features[featureName] = {}; } if (details instanceof ErrorEvent) { details = { message: details.message, lineno: details.lineno, filename: details.filename }; } features[featureName].error = { message: message, details: details }; // move the failed feature to the main list of features this.features[featureName] = features[featureName]; this.logger.log('Feature loader:' + message, featureName, details); }; FeatureLoader.prototype.getFeatureErrors = function getFeatureErrors() { var errors = {}; var name = void 0; for (name in this.features) { if (this.features[name].error) { errors[name] = this.features[name].error; } } return errors; }; FeatureLoader.prototype._prepareFeature = function _prepareFeature(name) { var _this4 = this; var features = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.features; var loadingState = arguments[2]; // If the feature is not part of the features list that are being currently loaded, // then we check the main feature list (this.features) that contains already loaded features // this could happen when a feature that loaded a part of a collection is trying to load one of its dependencies that // was previously loaded as part of another colletion. if (!features[name] && this.features[name]) { // If not found in the local feature map, then we try to global that has the loaded features. // If we find the feature in the global feature, we will switch to it. features = this.features; } // try #2 - we could be using a feature name with modifier (e.g. state.internal) // let us check if we have a modifier in the feature name . //this could happen when a feature is attempting to create a dependency and it is using a modifier in the feature name (e.g. state.internal) if (!features[name]) { var resolved = this._resolvePartialFeatureName(name, features); if (resolved) { name = resolved.name; } else { resolved = this._resolvePartialFeatureName(name, this.features); if (resolved) { name = resolved.name; features = this.features; } } } if (!features[name]) { this._setError(name, 'Attempting to load an undefined feature:' + name, '', features); } if (this._getError(name, features)) { features[name].preparePromise = Promise.resolve(name); } var preparePromise = features[name].preparePromise; if (!preparePromise) { var requiredFeatures = {}; var initDepdendents = []; // Go over the init dependencies and create them first this._getInitDependencies(name, features).forEach(function (feature) { // if we depend on a feature that is not is part of the feature collection, // then check the external feature, if (_this4.isFeature(feature, features) || _this4.isFeature(feature)) { requiredFeatures[_this4._getDependenciesPreferredName(feature, name, features)] = { loader: _this4, init: true, name: feature }; initDepdendents.push(_this4._prepareFeature(feature, features, loadingState)); } else if (_this4.externalFeatureLoader && _this4.externalFeatureLoader.isFeature(feature)) { requiredFeatures[_this4._getDependenciesPreferredName(feature, name, features)] = { loader: _this4.externalFeatureLoader, init: true, name: feature }; } else { _this4._setError(name, 'Attempting to load an undefined dependency:' + feature, '', features); } }); // Go over the runtime dependencies and create them this._getRuntimeDependencies(name, features).forEach(function (feature) { // if we depend on a feature that is not is part of the feature collection, // then check the external feature, if (_this4.isFeature(feature, features) || _this4.isFeature(feature)) { requiredFeatures[_this4._getDependenciesPreferredName(feature, name, features)] = { loader: _this4, init: false, name: feature }; } else if (_this4.externalFeatureLoader && _this4.externalFeatureLoader.isFeature(feature)) { requiredFeatures[_this4._getDependenciesPreferredName(feature, name, features)] = { loader: _this4.externalFeatureLoader, init: false, name: feature }; } else { // Could not find the runtime dependency.. let us not consider it an error yet, maybe it is not register requiredFeatures[_this4._getDependenciesPreferredName(feature, name, features)] = { loader: _this4, init: false, name: feature }; } }); preparePromise = Promise.all(initDepdendents).then(function () { // Make sure that all dependencies loaded without an error var depErrors = []; _this4._getDependencies(name, features).forEach(function (featureName) { var error = _this4._getError(featureName, features); if (error) { depErrors.push(error); } }); // TODO -- improve this loop and the one above var canDelayCreation = true; // If we already created the init time dependency, then we will not delay the creation of this feature. // This is because the delayed creating will happen when either: // 1 - the feature is accessed through the getter // 2 - one if its init depdendencies was created // TODO -- Possibly at one point, we want to also check external dependencies. // This might be needed when we have content feature that registers itself in a dashboard level feature in the initializer. _this4._getInitDependencies(name, features).forEach(function (featureName) { if (canDelayCreation) { canDelayCreation = !(_this4._isCreated(featureName, features) || _this4._isCreated(featureName, _this4.features)); } }); if (depErrors.length === 0) { // All good, create and initialize this feature if needed, otherwise we will lazy create it when requested _this4.loadingOrder.push(name); try { var params = _.extend({}, _this4.featureParams); var options = _this4._getOptions(name, features); if (options) { params.options = options; } params.features = createFeatureDependencies(name, requiredFeatures, loadingState); features[name].params = params; if (params.featureSpecs) { params.spec = params.featureSpecs[name]; } // Don't delay the create if the feature has an initialize // Or if it depends on a feature that was already created -- (in case the feature is not referenced and simply registering itself in one of the dependencies) var module = _this4._getFeatureModule(features[name]); if (!canDelayCreation || module.prototype.initialize || module.prototype.getLifeCycleHandlers) { // we have to create and initialize because this feature needs some async initilize var instance = _this4._createFeature(features[name]); return _this4._initializeFeature(name, features).then(function () { // move the feature to the main list of features once initialized _this4.features[name] = features[name]; if (_this4.featureNamePrefix) { _this4.features[_this4.featureNamePrefix + '.' + name] = features[name]; } _this4.setParentChildRelation(name, instance); }); } else { _this4.features[name] = features[name]; if (_this4.featureNamePrefix) { _this4.features[_this4.featureNamePrefix + '.' + name] = features[name]; } } } catch (e) { _this4._setError(name, 'Error while creating the feature:' + name, e, features); } } else { _this4._setError(name, 'Dependency failed to load', depErrors, features); } }); features[name].preparePromise = preparePromise; // This is uesd by the main feature loading to tell when all features are loaded loadingState.readyPromises.push(preparePromise); } return preparePromise; }; FeatureLoader.prototype._initializeFeature = function _initializeFeature(name) { var _this5 = this; var features = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.features; var feature = features[name]; var promise = null; try { var instance = feature.instance; if (typeof instance.initialize === 'function') { promise = instance.initialize(); } if (promise && promise.then) { promise = promise.then(function () { _this5._postFeatureInitialize(feature); }); } else { this._postFeatureInitialize(feature); } } catch (e) { this._setError(name, 'Failed to initialize feature:' + name, e, features); } if (!promise) { promise = Promise.resolve(); } return promise.catch(function (e) { _this5._setError(name, 'Failed to initialize feature: ' + name, e, features); }); }; FeatureLoader.prototype._deregisterLifeCycleHandlers = function _deregisterLifeCycleHandlers(feature) { if (feature) { if (feature.registeredHandlers) { feature.registeredHandlers.forEach(function (handler) { return handler.remove(); }); } feature.registeredHandlers = []; } }; // TODO - the lifecycle handlers should not be part of the feature loader // The lifeCycle manager is exposed as a feature.. so features can depend on it and register handlers directly with it. FeatureLoader.prototype._postFeatureInitialize = function _postFeatureInitialize(feature) { var _this6 = this; var instance = feature.instance; this._deregisterLifeCycleHandlers(feature); if (this.lifeCycleManager && typeof instance.getLifeCycleHandlers === 'function') { var handlers = instance.getLifeCycleHandlers(); if (!Array.isArray(handlers)) { throw new Error('serviceExtension getLifeCycleHandlers method is expected to return an array'); } handlers.forEach(function (handler) { if (!handler.name || !handler.action) { throw new Error('serviceExtension handlers are expected to have a name and action property'); } feature.registeredHandlers.push(_this6.lifeCycleManager.registerHandler(handler.name, handler.action, handler.priority)); }); } }; FeatureLoader.prototype._validateDependencies = function _validateDependencies() { var features = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.features; // Validate the dependencies for each step var name = void 0; for (name in features) { this._validateFeatureDependencies(name, null, features); } }; FeatureLoader.prototype._validateFeatureDependencies = function _validateFeatureDependencies(featureId, currentDeps) { var _this7 = this; var features = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.features; var dependencies = currentDeps || []; if (dependencies.indexOf(featureId) !== -1) { throw new Error('The feature with id "' + featureId + '" has a a circular dependency: ' + currentDeps); } dependencies.push(featureId); this._getDependencies(featureId, features).forEach(function (depItem) { if (!_this7._getFeatureSpec(depItem, features)) { throw new Error('The feature with id "' + featureId + '" has a missing dependency "' + depItem + '"'); } var clonedDepArray = dependencies.concat([]); _this7._validateFeatureDependencies(depItem, clonedDepArray, features); }); }; FeatureLoader.prototype._getFeatureSpec = function _getFeatureSpec(name) { var features = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.features; return features[name] && features[name].spec || {}; }; FeatureLoader.prototype.setFeatureEnabled = function setFeatureEnabled(name, isEnabled) { if (this.features[name]) { if (isEnabled) { this.features[name].disabled = false; this._postFeatureInitialize(this.features[name]); } else { this.features[name].disabled = true; this._deregisterLifeCycleHandlers(this.features[name]); } } }; FeatureLoader.prototype.destroy = function destroy() { var _this8 = this; _.each(_.keys(this.features), function (featureName) { // TODO -- we should have destroyFeature if (_this8.featureNamePrefix && featureName.startsWith(_this8.featureNamePrefix + '.')) { delete _this8.features[featureName]; } else { if (_this8.features[featureName].instance) { _this8._deregisterLifeCycleHandlers(_this8.features[featureName]); var instance = _this8._getFeatureInstance(featureName); if (instance && instance.destroy) { try { instance.destroy(); } catch (e) { _this8._setError(featureName, 'Error while destroying the feature:' + featureName, e); } } } delete _this8.features[featureName]; } }); for (var prop in this) { // clear everything referenced by this object in case someone is still holding on to it. if (Object.prototype.hasOwnProperty.call(this, prop)) { delete this[prop]; } } }; /** * Get a feature. If a feature is disabled or does not implement the getAPI function, then null will be returned. * * If the name is of the form 'myFeature.xyz', We will first try to find a feature that matches the full name, * if we can't find one, then we will look for a feature with name 'myFeature', * and we will pass the value 'xyz' to the getAPI() when we get the API object (i.e. myFeature.getAPI('xyz')) * * @param {String} name */ FeatureLoader.prototype.getFeature = function getFeature(name) { var api; var instance = this._getFeatureInstance(name); var apiType = void 0; if (!instance && name) { var resolved = this._resolvePartialFeatureName(name); if (resolved) { name = resolved.name; apiType = resolved.apiType; instance = this._getFeatureInstance(name); } } if (this.isFeatureEnabled(name) && !this._getError(name) && instance.getAPI) { api = instance.getAPI(apiType); } return api; }; FeatureLoader.prototype.isFeature = function isFeature(name) { var features = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.features; var exist = features[name]; if (!exist) { var resolved = this._resolvePartialFeatureName(name, features); if (resolved) { exist = features[resolved.name]; } } return !!exist; }; FeatureLoader.prototype._isCreated = function _isCreated(name) { var features = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.features; var exist = features[name]; if (!exist) { var resolved = this._resolvePartialFeatureName(name, features); if (resolved) { exist = features[resolved.name]; } } return exist && exist.instance; }; FeatureLoader.prototype.getLoadedFeatures = function getLoadedFeatures() { var _this9 = this; // TODO - this is used for serialization .. it will end up creating all features even ones that were not referenced // WE can optimize to only use the feature that were created.. var loadedFeatures = {}; Object.keys(this.features).forEach(function (featureName) { if (!featureName.startsWith(_this9.featureNamePrefix + '.')) { var featureInstance = _this9._getFeatureInstance(featureName); if (featureInstance) { loadedFeatures[featureName] = featureInstance; } } }); return loadedFeatures; }; FeatureLoader.prototype._resolvePartialFeatureName = function _resolvePartialFeatureName(name) { var featuresMap = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.features; // We support the 2 cases: // "a.b.c" where "a" is the feature and "b.c" is the api type // or // "a.b.c" where "a.b" is the feature and "c" is the api type var parts = name.split('.'); if (parts.length > 1) { var firstPart = parts[0]; var lastPart = parts[parts.length - 1]; var middlePart = parts.splice(1, parts.length - 2).join('.'); name = middlePart ? firstPart + '.' + middlePart : firstPart; if (featuresMap[name]) { return { name: name, apiType: lastPart }; } else { name = firstPart; if (featuresMap[name]) { return { name: name, apiType: lastPart ? middlePart + '.' + lastPart : middlePart }; } } } return null; }; /** * Indicate if the feature is enabled or not. * @param {boolean} name */ FeatureLoader.prototype.isFeatureEnabled = function isFeatureEnabled(name) { var instance = this._getFeatureInstance(name); return instance && !this.features[name].disabled && (!instance.isEnabled || instance.isEnabled()); }; /** * @param tagToMatch - a tag that any feature must include in its getTags() api. * @returns any features that match the tag passed in as an array (empty array is returned if no features match). */ FeatureLoader.prototype.getMatchingFeatures = function getMatchingFeatures(tagToMatch) { var _this10 = this; var matchingFeatures = []; _.each(_.keys(this.features), function (featureName) { if (_this10.isFeatureEnabled(featureName) && _this10._getFeatureTags(featureName).indexOf(tagToMatch) >= 0) { var featureAPI = _this10.getFeature(featureName); if (featureAPI) { matchingFeatures.push(featureAPI); } } }); return matchingFeatures; }; /** * Enable or disable a feature that matches the supplied tag * @param tagToMatch - a tag that any feature must include in its getTags() api. * @param isEnabled */ FeatureLoader.prototype.setMatchingFeaturesEnabled = function setMatchingFeaturesEnabled(tagToMatch, isEnabled) { var _this11 = this; _.each(_.keys(this.features), function (featureName) { var feature = _this11.features[featureName]; if (feature && _this11._getFeatureTags(featureName).indexOf(tagToMatch) >= 0) { _this11.setFeatureEnabled(featureName, isEnabled); } }); }; return FeatureLoader; }(); return FeatureLoader; }); //# sourceMappingURL=FeatureLoader.js.map