/*!
 *
 * carbon-angular v0.0.7 | i18n.service.js
 *
 * Copyright 2014, 2019 IBM
 *
 * 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.
 */


import { Injectable } from "@angular/core";
import { BehaviorSubject, isObservable, iif } from "rxjs";
import { map } from "rxjs/operators";
import { merge } from "../utils/object";
var EN = require("./en.json");
/**
 * Takes the `Observable` returned from `i18n.get` and an object of variables to replace.
 *
 * The keys specify the variable name in the string.
 *
 * Example:
 * ```typescript
 * service.set({ "TEST": "{{foo}} {{bar}}" });
 *
 * service.replace(service.get("TEST"), { foo: "test", bar: "asdf" })
 * ```
 *
 * Produces: `"test asdf"`
 *
 * @param subject the translation to replace variables on
 * @param variables object of variables to replace
 */
export var replace = function (subject, variables) { return subject.pipe(map(function (str) {
    var keys = Object.keys(variables);
    for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
        var key = keys_1[_i];
        var value = variables[key];
        str = str.replace(new RegExp("{{\\s*" + key + "\\s*}}", "g"), value);
    }
    return str;
})); };
/**
 * Represents an "overridable" translation value.
 *
 * Largely an internal usecase. There are situations where we want an `Observable` that
 * can emit events from a centralized source **OR** an `Observable` that will emit events
 * from a component local source. The key example being on/off text in a `Toggle` - In some cases
 * we want the `Toggle` to use `I18n`s global translations, but in others we'd prefer to use a local
 * override. We don't ever need to return to a non-overridden state, but we do need the ability to
 * switch _to_ an overridden sate.
 */
var Overridable = /** @class */ (function () {
    function Overridable(path, i18n) {
        this.path = path;
        this.i18n = i18n;
        /**
         * Our base non-overridden translation.
         */
        this.baseTranslation = this.i18n.get(this.path);
        /**
         * A boolean to flip between overridden and non-overridden states.
         */
        this.isOverridden = false;
        /**
         * ensure `$override` is initialized with the correct default value
         * in some cases `_value` can get changed for an `Observable` before `$override` is created
         */
        var value = this.i18n.getValueFromPath(this.path);
        this.$override = new BehaviorSubject(value);
        this._value = value;
    }
    Object.defineProperty(Overridable.prototype, "value", {
        /**
         * The raw value of the translation. Defaults to the string value, but will return the value passed to `override`
         *
         * @readonly
         */
        get: function () {
            return this._value;
        },
        set: function (v) {
            this.override(v);
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Overridable.prototype, "subject", {
        /**
         * The translation subject. Returns either a stream of overridden values, or our base translation values.
         *
         * @readonly
         */
        get: function () {
            var _this = this;
            /**
             * since inputs are bound on template instantiation (and thusly will always have _some_ value)
             * We can use a simple boolean and the `iif` function to determine which subject to return on subscription
             */
            return iif(function () { return _this.isOverridden; }, this.$override, this.baseTranslation);
        },
        enumerable: true,
        configurable: true
    });
    /**
     * Takes a string or an `Observable` that emits strings.
     * Overrides the value provided by the `I18n` service.
     */
    Overridable.prototype.override = function (value) {
        var _this = this;
        this.isOverridden = true;
        this._value = value;
        if (isObservable(value)) {
            value.subscribe(function (v) {
                _this.$override.next(v);
            });
        }
        else {
            this.$override.next(value);
        }
    };
    return Overridable;
}());
export { Overridable };
/**
 * The I18n service is a minimal internal singleton service used to supply our components with translated strings.
 *
 * All the components that support I18n also support directly passed strings.
 * Usage of I18n is optional, and it is not recommended for application use (libraries like ngx-translate
 * are a better choice)
 *
 */
var I18n = /** @class */ (function () {
    function I18n() {
        this.translationStrings = EN;
        this.translations = new Map();
    }
    /**
     * Set/update the translations from an object. Also notifies all participating components of the update.
     *
     * @param strings an object of strings, should follow the same format as src/i18n/en.json
     */
    I18n.prototype.set = function (strings) {
        this.translationStrings = merge({}, EN, strings);
        // iterate over all our tracked translations and update each observable
        var translations = Array.from(this.translations);
        for (var _i = 0, translations_1 = translations; _i < translations_1.length; _i++) {
            var _a = translations_1[_i], path = _a[0], subject = _a[1];
            subject.next(this.getValueFromPath(path));
        }
    };
    /**
     * When a path is specified returns an observable that will resolve to the translation string value.
     *
     * Returns the full translations object if path is not specified.
     *
     * @param path optional, looks like `"NOTIFICATION.CLOSE_BUTTON"`
     */
    I18n.prototype.get = function (path) {
        if (!path) {
            return this.translationStrings;
        }
        return this.getSubject(path);
    };
    /**
     * Returns all descendents of some path fragment as an object.
     *
     * @param partialPath a path fragment, for example `"NOTIFICATION"`
     */
    I18n.prototype.getMultiple = function (partialPath) {
        var values = this.getValueFromPath(partialPath);
        var subjects = {};
        for (var _i = 0, _a = Object.keys(values); _i < _a.length; _i++) {
            var key = _a[_i];
            if (values[key] === Object(values[key])) {
                subjects[key] = this.getMultiple(partialPath + "." + key);
            }
            else {
                subjects[key] = this.getSubject(partialPath + "." + key);
            }
        }
        return subjects;
    };
    /**
     * Returns an instance of `Overridable` that can be used to optionally override the value provided by `I18n`
     * @param path looks like `"NOTIFICATION.CLOSE_BUTTON"`
     */
    I18n.prototype.getOverridable = function (path) {
        return new Overridable(path, this);
    };
    /**
     * Takes the `Observable` returned from `i18n.get` and an object of variables to replace.
     *
     * The keys specify the variable name in the string.
     *
     * Example:
     * ```
     * service.set({ "TEST": "{{foo}} {{bar}}" });
     *
     * service.replace(service.get("TEST"), { foo: "test", bar: "asdf" })
     * ```
     *
     * Produces: `"test asdf"`
     *
     * @param subject the translation to replace variables on
     * @param variables object of variables to replace
     */
    I18n.prototype.replace = function (subject, variables) {
        return replace(subject, variables);
    };
    /**
     * Trys to resolve a value from the provided path.
     *
     * @param path looks like `"NOTIFICATION.CLOSE_BUTTON"`
     */
    I18n.prototype.getValueFromPath = function (path) {
        var value = this.translationStrings;
        for (var _i = 0, _a = path.split("."); _i < _a.length; _i++) {
            var segment = _a[_i];
            if (value[segment]) {
                value = value[segment];
            }
            else {
                throw new Error("no key " + segment + " at " + path);
            }
        }
        return value;
    };
    /**
     * Helper method that returns an observable from the internal cache based on the path
     *
     * @param path looks like `"NOTIFICATION.CLOSE_BUTTON"`
     */
    I18n.prototype.getSubject = function (path) {
        try {
            // we run this here to validate the path exists before adding it to the translation map
            var value = this.getValueFromPath(path);
            if (this.translations.has(path)) {
                return this.translations.get(path);
            }
            var translation = new BehaviorSubject(value);
            this.translations.set(path, translation);
            return translation;
        }
        catch (error) {
            console.error(error);
        }
    };
    I18n.decorators = [
        { type: Injectable },
    ];
    return I18n;
}());
export { I18n };
//# sourceMappingURL=i18n.service.js.map