/**
* 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;
});