smoosic
Version:
<sub>[Github site](https://github.com/Smoosic/smoosic) | [source documentation](https://smoosic.github.io/Smoosic/release/docs/modules.html) | [change notes](https://aarondavidnewman.github.io/Smoosic/changes.html) | [application](https://smoosic.github.i
368 lines (350 loc) • 16.9 kB
text/typescript
// [Smoosic](https://github.com/AaronDavidNewman/Smoosic)
// Copyright (c) Aaron David Newman 2021.
import { buildDom, createTopDomContainer } from "../../common/htmlHelpers";
import { SmoLanguage, SmoTranslator, TranslationStrings } from "./language";
import { RibbonButtons } from "../buttons/ribbon";
import { ButtonLabel } from "../buttons/button";
import { DialogTranslation, DialogDefinition } from "../dialogs/dialog";
import { DialogDefinitionElement, DialogDefinitionOption } from "../dialogs/components/baseComponent";
import { SmoNamespace } from "../../smo/data/common";
declare var $: any;
// ## SmoTranslationEditor
// Create a somewhat user-friendly editor DOM to translate SMO
// dialogs and menus, and any subset, into other languages.
export class SmoTranslationEditor {
// ### _getHtmlTextInput
// All the editable text elements contain: the code label or value from the
// UI element, the En string, and the translated string, or a copy of the
// EN string if the string has not been translated.
static _getHtmlTextInput(dbLabel: string, enLabel: string, langLabel: string, labelType: string, labelId: string) {
var b = buildDom;
const compHtml = b('div').classes('dialog-element-container')
.attr('data-' + labelType, labelId).append(
b('div').classes('dialog-component-label').append(
b('span').classes('trans-label').append(
b('span').classes('trans-db-text').text(dbLabel)
).append(
b('span').classes('trans-en-text').text(enLabel)
).append(
b('input').classes('trans-label-input')
).append(
b('span').classes('plaintext-translate hide').text(langLabel)
)
)
).dom();
return compHtml;
}
// ### _getMenuTextDialogHtml
// Get all the menu item labels for translation
static _getMenuTextDialogHtml(menuCtor: string, enStrings: TranslationStrings, langStrings: TranslationStrings) {
const menuClass = eval(`${SmoNamespace}.${menuCtor}`);
const menuItems = menuClass['defaults'].menuItems;
var enMenu = enStrings.menus.find((mn: any) => mn.ctor === menuCtor);
// Get the JSON EN menu, or copy the DB strings if it doesn't exist
if (!enMenu) {
enMenu = JSON.parse(JSON.stringify(menuClass['defaults']));
enMenu!.ctor = menuCtor;
}
// Get the JSON language menu strings, or copy the EN strings if it doesn't exist
var langMenu = langStrings.menus.find((mn: any) => mn.ctor === menuCtor);
if (!langMenu) {
langMenu = JSON.parse(JSON.stringify(menuClass['defaults']));
langMenu!.ctor = menuCtor;
}
// create the DOM menu container
var b = buildDom;
const container = b('div').classes('menu-translate-container')
.attr('data-menucontainer', menuCtor).append(
b('button').classes('icon-plus trans-expander')).append(
b('span').classes('menu-translate-title').text(menuCtor)
).dom();
const menuItemsDom = b('div').classes('menu-element-container').dom();
// create the label editor
const menuLabel = SmoTranslationEditor._getHtmlTextInput(menuClass['defaults'].label, enMenu!.label, langMenu!.label,
'menulabel', menuCtor);
$(menuItemsDom).append(menuLabel);
$(container).append(menuItemsDom);
// create the editor for each item
menuItems.forEach((item: any) => {
var enItem = enMenu!.menuItems.find((mi: any) => mi.value === item.value);
if (!enItem) {
enItem = JSON.parse(JSON.stringify(item));
}
var langItem = langMenu!.menuItems!.find((mi: any) => mi.value === item.value);
if (!langItem) {
langItem = JSON.parse(JSON.stringify(item));
}
const menuItemDom = b('div').classes('menu-item-container').dom();
const itemEditDom = SmoTranslationEditor._getHtmlTextInput(
item.value, enItem!.text, langItem!.text,
'itemtext', item.value);
$(menuItemDom).append(itemEditDom);
$(menuItemsDom).append(menuItemDom);
});
return container;
}
static getButtonTranslateHtml(enStrings: TranslationStrings, langStrings: TranslationStrings, transContainer: HTMLElement) {
var b = buildDom;
var buttonDom: HTMLElement = b('div').classes('ribbon-translate-container')
.attr('data-ribbon-translate', 'buttons').append(
b('button').classes('icon-plus trans-expander')).append(
b('span').classes('ribbon-translate-title').text('Button Text')
).dom() as HTMLElement;
var enKeys: ButtonLabel[] = enStrings.buttonText;
if (!enKeys) {
enKeys = JSON.parse(JSON.stringify(RibbonButtons.translateButtons));
}
var langKeys = langStrings.buttonText;
if (!langKeys) {
langKeys = JSON.parse(JSON.stringify(RibbonButtons.translateButtons));
}
enKeys.forEach((button: ButtonLabel) => {
const langObj = langKeys.find((langText: any) => langText.buttonId === button.buttonId);
const langString = langObj ? langObj.buttonText : button.buttonText;
var buttonContainer: HTMLElement = b('div').classes('ribbon-button-container')
.attr('data-buttoncontainer', button.buttonId).dom();
$(buttonContainer).append(
SmoTranslationEditor._getHtmlTextInput(button.buttonId, button.buttonText, langString, 'ribbon-button', button.buttonId)
);
$(buttonDom).append(buttonContainer);
});
$(transContainer).append(buttonDom);
}
// ### _getStaticTextDialogHtml
// create DOM for the static text section of the dialogs.
static _getStaticTextDialogHtml(elements: DialogDefinition, enDb: Record<string, string>, langDb: Record<string, string>, htmlContainer: HTMLElement) {
var b = buildDom;
const keys = Object.keys(elements.staticText);
const nodeContainer = b('div')
.classes('dialog-element-container')
.attr('data-component', 'staticText')
.dom();
elements.staticText.forEach((nv: any) => {
const name = Object.keys(nv)[0];
const value = nv[name];
var enVal = enDb[name] ? enDb[name] : value;
var langVal = langDb[name] ? langDb[name] : enDb[name];
const translateElement = SmoTranslationEditor._getHtmlTextInput(
name, enVal, langVal, 'statictext', name);
$(nodeContainer).append(translateElement);
});
$(htmlContainer).append(nodeContainer);
}
static _getDialogComponentHtml(element: DialogDefinitionElement, enDb: DialogTranslation, langDb: DialogTranslation, container: HTMLElement) {
var b = buildDom;
var label = element.label;
var smoName = element.smoName;
if (typeof (enDb.dialogElements.find) !== 'function') {
console.warn('no ENDB!');
}
var enComponent = enDb.dialogElements.find((st: any) => st.id === smoName);
if (!enComponent) {
enComponent = JSON.parse(JSON.stringify(element))
}
var langComponent = langDb.dialogElements.find((st: any) => st.id === smoName);
if (!langComponent) {
langComponent = JSON.parse(JSON.stringify(element));
}
const enLabel = enComponent!.label ? enComponent!.label : label;
const langLabel = langComponent!.label ? langComponent!.label : label;
const compHtml = SmoTranslationEditor._getHtmlTextInput(
label, enLabel, langLabel, 'component', smoName);
if (element.options) {
const optionsHtml = b('div').classes('dialog-component-options').dom();
$(compHtml).append(optionsHtml);
if (!enComponent!.options) {
enComponent!.options = JSON.parse(JSON.stringify(element.options));
}
if (!langComponent!.options) {
langComponent!.options = JSON.parse(JSON.stringify(element.options));
}
element.options.forEach((option: DialogDefinitionOption) => {
var enOption = enComponent!.options!.find((op: any) => op.value === option.value);
var langOption = langComponent!.options!.find((op: any) => op.value === option.value);
if (!enOption || !enOption.label) {
enOption = JSON.parse(JSON.stringify(option));
}
if (!langOption || !langOption.label) {
langOption = JSON.parse(JSON.stringify(option));
}
const optionHtml = SmoTranslationEditor._getHtmlTextInput(
option.value.toString(), enOption!.label, langOption!.label, 'component-option', option!.value.toString());
$(optionsHtml).append(optionHtml)
});
}
$(container).append(compHtml);
}
static getDialogTranslationHtml(dialogCtor: string, enStrings: TranslationStrings, langStrings: TranslationStrings) {
var b = buildDom;
var container: HTMLElement = b('div').classes('db-translate-container').attr('data-dbcontainer', dialogCtor)
.append(b('button').classes('icon-plus trans-expander'))
.append(b('span').classes('db-translate-title').text(dialogCtor)).dom() as HTMLElement;
var ctor = eval(`${SmoNamespace.value}.${dialogCtor}`);
if (!ctor) {
console.warn('Bad dialog in translate: ' + dialogCtor);
return;
}
var elements = ctor.dialogElements as DialogDefinition;
var enDb = enStrings.dialogs.find((dbStr: DialogTranslation) => dbStr.ctor === dialogCtor);
if (!enDb) {
enDb = JSON.parse(JSON.stringify({
ctor: dialogCtor, label: elements.label, dialogElements: elements.elements, staticText: elements.staticText
}
));
}
var langDb = langStrings.dialogs.find((dbStr: DialogTranslation) => dbStr.ctor === dialogCtor);
if (!langDb) {
langDb = JSON.parse(JSON.stringify({
ctor: dialogCtor, label: elements.label, dialogElements: elements.elements, staticText: elements.staticText
}));
}
const htmlText = SmoTranslationEditor._getHtmlTextInput(dialogCtor, enDb!.label, langDb!.label, 'dialog-label', dialogCtor);
$(container).append(htmlText);
if (elements.staticText) {
SmoTranslationEditor._getStaticTextDialogHtml(elements, enDb!.staticText, langDb!.staticText, container);
}
elements.elements.forEach((element: DialogDefinitionElement) => {
if (element.smoName && element.label) {
SmoTranslationEditor._getDialogComponentHtml(element, enDb!, langDb!, container);
}
});
return container;
}
static getAllTranslationHtml(lang: string) {
const enStr: TranslationStrings = SmoLanguage.en.strings;
const langStr: TranslationStrings = (SmoLanguage as any)[lang].strings;
var b = buildDom;
var container: HTMLElement = b('div').classes('top-translate-container')
.attr('dir', (SmoLanguage as any)[lang].dir).dom() as HTMLElement;
SmoTranslator.allDialogs.forEach((dialog) => {
const htmlDom: HTMLElement | undefined = SmoTranslationEditor.getDialogTranslationHtml(dialog, enStr, langStr);
if (htmlDom) {
$(container).append(htmlDom);
}
});
SmoTranslator.allMenus.forEach((menu) => {
$(container).append(SmoTranslationEditor._getMenuTextDialogHtml(menu, enStr, langStr));
});
SmoTranslationEditor.getButtonTranslateHtml(enStr, langStr, container);
var resultDom = b('div').classes('translation-json-container').append(
b('textarea').classes('translation-json-text')).append(
b('div').append(
b('button').classes('translate-submit-button').text('Submit')
)
).dom() as HTMLElement;
$(container).append(resultDom);
return container;
}
static parseDom() {
var json = [];
// $('.top-translate-container .db-translate-container[data-dbcontainer] [data-component="staticText"]')
$('.top-translate-container .db-translate-container[data-dbcontainer]').each((ix: number, dbEl: any) => {
var db = $(dbEl).attr('data-dbcontainer');
var obj: any = { ctor: db };
var elements: any[] = [];
var domComponents = $(dbEl).find('[data-component]');
$(domComponents).each(function (ix: number, domComponent: any) {
const compType = $(domComponent).attr('data-component');
if (compType === 'staticText') {
var stElements: any[] = [];
$(domComponent).find('[data-statictext]').each((ix: number, stDom: any) => {
const key = $(stDom).attr('data-statictext');
const value = $(stDom).find('input.trans-label-input').val();
const stNode = JSON.parse('{"' + key + '":"' + value + '"}');
stElements.push(stNode);
});
elements.push({ staticText: stElements });
} else {
var dbComponent = { id: compType, label: '', options: {} };
dbComponent.label = $(domComponent).find('input.trans-label-input').val();
var compOptions: any[] = [];
$(domComponent).find('[data-component-option]').each(function (ix: number, optionDom: any) {
const value = $(optionDom).find('.trans-db-text').text();
const label = $(optionDom).find('input.trans-label-input').val();
compOptions.push({ value: value, label: label });
});
dbComponent.options = compOptions;
elements.push(dbComponent);
}
});
obj.dialogElements = elements;
json.push(obj);
});
$('.menu-translate-container[data-menucontainer]').each((ix: number, menuEl: any) => {
var menuId = $(menuEl).attr('data-menucontainer');
var obj = { ctor: menuId, label: '', options: {}, menuItems: {} };
const menuLabel = $(menuEl)
.find('.dialog-element-container[data-menulabel] .trans-label-input')
.val();
obj.label = menuLabel;
var menuItems: any[] = [];
var itemsDom = $(menuEl).find('.menu-item-container .dialog-element-container');
$(itemsDom).each((ix: number, itemDom: any) => {
const value = $(itemDom).find('.trans-db-text').text();
const text = $(itemDom).find('input.trans-label-input').val();
menuItems.push({ value: value, text: text });
});
obj.menuItems = menuItems;
json.push(obj);
});
var ribbonText: any[] = [];
$('.ribbon-translate-container .ribbon-button-container').each((ix: number, buttonEl: any) => {
const buttonId = $(buttonEl).find('.trans-db-text').text();
const buttonText = $(buttonEl).find('input.trans-label-input').val();
ribbonText.push({ buttonId: buttonId, buttonText: buttonText });
});
json.push({ ribbonText: ribbonText });
return json;
}
static startEditor(lang: string) {
createTopDomContainer('.translation-editor');
var transDom = SmoTranslationEditor.getAllTranslationHtml(lang);
$('.translation-editor').append(transDom);
$('body').addClass('translation-mode');
$('.plaintext-translate').each(function (ix: number, el: any) {
var txt = $(el).text();
$(el).closest('.trans-label').find('input.trans-label-input').val(txt);
});
$('.db-translate-container button.trans-expander').off('click').on('click', function (ev: any) {
var exp = $(ev.target).closest('.db-translate-container');
if ($(exp).hasClass('expanded')) {
$(exp).removeClass('expanded');
$(ev.target).removeClass('icon-minus');
$(ev.target).addClass('icon-plus');
} else {
$(exp).addClass('expanded');
$(ev.target).addClass('icon-minus');
$(ev.target).removeClass('icon-plus');
}
});
$('.menu-translate-container button.trans-expander').off('click').on('click', function (ev: any) {
var exp = $(ev.target).closest('.menu-translate-container');
if ($(exp).hasClass('expanded')) {
$(exp).removeClass('expanded');
$(ev.target).removeClass('icon-minus');
$(ev.target).addClass('icon-plus');
} else {
$(exp).addClass('expanded');
$(ev.target).addClass('icon-minus');
$(ev.target).removeClass('icon-plus');
}
});
$('.ribbon-translate-container button.trans-expander').off('click').on('click', function () {
const dom = $('.ribbon-translate-container button.trans-expander');
var exp = $(dom).closest('.ribbon-translate-container');
if ($(exp).hasClass('expanded')) {
$(exp).removeClass('expanded');
$(dom).removeClass('icon-minus');
$(dom).addClass('icon-plus');
} else {
$(exp).addClass('expanded');
$(dom).addClass('icon-minus');
$(dom).removeClass('icon-plus');
}
});
$('.translate-submit-button').off('click').on('click', () => {
var json = SmoTranslationEditor.parseDom();
$('.translation-json-text').val(JSON.stringify(json, null, ' '));
});
}
}