JavaScript Extension Development API for Oracle Visual Builder Cloud Service - Classic Applications

Source: translations.dt/js/api/EditableTranslatable.js

/**
 * Copyright (c) 2016, Oracle and/or its affiliates.
 * All rights reserved.
 */
define(['translations/js/api/Translatable'], function (Translatable) {

    'use strict';

    /**
     * Constructs an EditableTranslatable. Calling the constructor with
     * no arguments will create an empty-string translatable.
     *
     * <p>
     * To avoid using this constructor directly (to not have have a DT
     * dependency for example) use the factory method
     * {@link module:translations/js/api/I18n.createTranslatable I18n.createTranslatable()} .
     * </p>
     *
     * @AbcsExtension stable
     * @version 16.3.1
     * @exports translations.dt/js/api/EditableTranslatable
     * @constructor
     * @extends {translations/js/api/Translatable}
     * @see {@link module:translations/js/api/I18n.createTranslatable I18n.createTranslatable()}
     * @param {String} [i18nKey] - the i18n key of the translatable
     * @param {String} [value] - the value of the translatable
     * @param {Boolean} [autoEscape=true] - whether special chars ${}[] should
     *                  be treated as string literals by default
     *
     */
    var EditableTranslatable = function (i18nKey, value, autoEscape) {
        AbcsLib.checkThis(this);
        if (arguments.length === 0 || (arguments.length > 0 && AbcsLib.isString(i18nKey))) {
            Translatable.apply(this, i18nKey ? [i18nKey] : []);
            this._autoEscape = arguments.length > 2 ? autoEscape : true;
            if (value) {
                this.store(value);
            }
        } else {
            throw new Error('Invalid i18n key, \'' + i18nKey + '\'');
        }
    };

    AbcsLib.extend(EditableTranslatable, Translatable);

    /**
     * Checks if there exists a translation for the key.
     *
     * @version 16.3.1
     * @returns {Boolean} true, if there exists a translation for the key
     */
    EditableTranslatable.prototype.hasTranslation = function () {
        return (this.i18n !== Translatable.EMPTY_STRING && this.bind() !== this.key());
    };

    /**
     * Generates a string to be used to represent the translatable in the generated DOM. This method
     * would typically be called during view generation. It would return
     *
     * <ol>
     *   <li>A language-independent JS expression that will get bound at run-time</li>
     *   <li>the empty string '' if there is no value stored for the translatable's i18n key</li>
     * </ol>
     *
     * @AbcsExtension stable
     * @version 16.3.1
     * @returns {String} a String to be used to represent the Translatable
     */
    EditableTranslatable.prototype.toTranslatableString = function () {
        if (this.i18n !== Translatable.EMPTY_STRING && this.hasTranslation()) {
            if (arguments.length === 0) {
                // No parameters
                return 'AbcsLib.i18n(\'' + this.i18n + '\')';
            } else {
                var params = this.__buildAbcsLib_i18nArgsArray(arguments);
                var buffer = 'AbcsLib.i18n.apply(AbcsLib,[\'' + params[0] + '\'';
                // Parameters by name
                if (params.length === 2 && typeof params[1] === 'object' &&
                    typeof params[1].toTranslatableString !== 'function') {
                    buffer += ',{';
                    var msgParametersObject = params[1];
                    var objectBuffer = '';
                    Object.getOwnPropertyNames(msgParametersObject).forEach(function(key) {
                        if (objectBuffer.length > 0) {
                            objectBuffer += ',';
                        }
                        objectBuffer += '\'' + key + '\':' + (typeof msgParametersObject[key].toTranslatableString === 'function' ?
                                                              msgParametersObject[key].toTranslatableString() :
                                                              '\'' + msgParametersObject[key] + '\'');
                    });
                    buffer += objectBuffer;
                    buffer += '}';
                } else { // Parameters by position
                    params.splice(0, 1);
                    params.forEach(function (param) {
                        buffer += ',';
                        if (typeof param.toTranslatableString === 'function') {
                            buffer += param.toTranslatableString();
                        } else {
                            buffer += '\'' + param + '\'';
                        }
                    });
                }
                // Wrap it all up
                buffer += '])';
                return buffer;
            }
        } else {
            return '\'\'';
        }
    };

    /**
     * Returns a string representation to be used when storing the translatable.
     * This function will be called by JSON.stringify() to get the string to be
     * stored to represent the object in metadata storage. This method should
     * never have to be called outside of JSON.stringify() contexts.
     *
     * @private
     * @version 16.3.1
     * @returns {String} a string to be used when serializing/ stringifying the translatable
     */
    EditableTranslatable.prototype.toJSON = function () {
        return 'i18n:' + this.i18n;
    };

    /**
     * Stores a new string value for this Translatable.  The value must be a non-empty string.
     *
     * @AbcsExtension stable
     * @version 16.3.1
     * @param {String} value the value to be stored for the translation key
     * @throws {Error} if the value is undefined, null, empty string, or not a string
     * @returns {EditableTranslatable} <b>this</b>, to allow for chained calls
     */
    EditableTranslatable.prototype.store = function (value) {
        AbcsLib.checkDefined(this.i18n, 'translation key');
        this._checkIfWriteable('store');
        if (!value) {
            throw new Error('translatable value cannot be undefined, null, or the empty string');
        }
        AbcsLib.checkDataType(value, AbcsLib.Type.STRING);
        AbcsLib.Translations.putTranslatedString(this.i18n, value, this._autoEscape);
        return this;
    };

    /**
     * Deletes the string value stored for this translation key.
     *
     * @AbcsExtension stable
     * @version 16.3.1
     * @returns {EditableTranslatable} <b>this</b>, to allow for chained calls
     */
    EditableTranslatable.prototype.delete = function () {
        AbcsLib.checkDefined(this.i18n, 'translation key');
        this._checkIfWriteable('delete');
        AbcsLib.Translations.deleteTranslatedString(this.i18n);
        return this;
    };

    EditableTranslatable.prototype._checkIfWriteable = function (operation) {
        if (this.i18n === Translatable.EMPTY_STRING) {
           throw new Error('Calling \'' + operation + '\' on an empty-string Translatable, which is read-only. Call ' +
                'EditableTranslatable.key() with a valid key to make it writeable or ' +
                'just create a new Translatable.');
        }
    };

    /**
     * If <b>i18nKey</b> parameter is provided, changes the translation key to
     * that, preserves the stored value and returns the present translatable to
     * allow for chained calls.
     *
     * <p>
     * If called without args, acts as a getter for the current i18n key.
     * </p>
     *
     * <p>
     * The method is a no-op if the new value is 'undefined' or is the same as
     * the old key.
     * </p>
     *
     * @version 16.3.1
     * @param {String} i18nKey - the new key
     */
    EditableTranslatable.prototype.key = function (i18nKey) {
        if (arguments.length === 0) {
            return this.i18n;
        } else {
            AbcsLib.checkDefined(i18nKey, 'new translation key');
            if (this.i18n !== i18nKey) {
                var value;
                if (this.i18n && this.hasTranslation()) {
                    value = this.bind();
                    AbcsLib.Translations.deleteTranslatedString(this.i18n);
                }
                this.i18n = i18nKey;
                if (value) {
                    AbcsLib.Translations.putTranslatedString(this.i18n, value, this._autoEscape);
                }
            }
            return this;
        }
    };

    /**
     * Configures whether auto-escape should be on.
     *
     * <p>
     * If set to <b>true</b> all
     * future updates to the translated value will be auto-escaped; in other
     * words, the special chars $[]{}, will be treated as string literals.
     * </p>
     *
     * <p>
     * If set to <b>false</b> all future updates to the translated value will
     * not be auto-escaped. $[]{} will not be treated as string literals so the
     * stored string will be able to contain parameters such as {0}. {1} or
     * {groupName}. Then it will be up to the caller to escape special
     * characters as literals whenever they are not part of a parameter.
     * </p>
     *
     * <p>
     * If running with autoEscape set to <b>false</b>, you may find it useful
     * to escape just bits of a string manually. To escape parts of a passed-in
     * string manually, see {@link module:translations.dt/js/api/EditableTranslations.escapeSpecialCharsOfValue}
     * </p>
     *
     * @version 16.3.1
     * @param {Boolean} autoescape - whether stored value should be auto-escaped
     */
    EditableTranslatable.prototype.setAutoEscape = function (autoEscape) {
        this._autoEscape = autoEscape;
    };

    /**
     * Checks if auto-escape is on.
     *
     * @version 16.3.1
     * @see {@link module:translations.dt/js/api/EditableTranslatable#setAutoEscape}
     * @returns {Boolean} <b>true</b> if auto-escape is on
     */
    EditableTranslatable.prototype.isAutoEscaped = function () {
        return this._autoEscape;
    };

    return EditableTranslatable;
});