dt-translate-angular
Version:
An Angular component/library for translating and rendering sign language gestures.
732 lines (718 loc) • 27.7 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, NgModule } from '@angular/core';
// src/translate-controller.ts
class TranslateController {
constructor() {
this.appMode = 4;
this.apiKey = '';
this.initialized = false;
this.requestedText = '';
this.unityInstance = null;
this.character = 0;
this.speed = 0.025;
this.backgroundColor = null;
this.characterClothColor = null;
this.selectedLanguage = 'en';
this.baseUrl = 'https://translateai.deaftawk.com:3001';
this.poseWorldLandmarks = 'false';
this.totalCharacters = 3;
this.showLangDd = false;
this.enableCharacterChange = true;
this.unityCallback = null;
this.windowVisible = true;
this.windowWidth = 275;
this.windowHeight = 155;
this.isWindowVisible = true;
this.appVersion = '1.0.0';
this.CHARACTER_POS_TEXT = 'standstill';
this.languages = ['en', 'da'];
this.shouldShowLogs = true;
window.addEventListener('focus', () => {
this.showLogs('WINDOW FOCUS CALLED ============>');
if (this.requestedText === this.CHARACTER_POS_TEXT)
return;
const lastSentText = this.requestedText;
this.requestedText = '';
if (lastSentText) {
this.playPose({ text: lastSentText }, undefined, false);
}
});
}
init(data) {
this.showLogs('INIT CALLED ============>');
if (this.initialized)
return;
this.initialized = true;
if (!data.key)
throw new Error('API key is required');
this.apiKey = data.key;
this.character = data.character || 0;
this.appMode = [3, 4].indexOf(data.appMode) > -1 ? data.appMode : 4;
this.backgroundColor = data.backgroundColor || null;
this.characterClothColor = data.characterClothColor || null;
this.speed = data.speed || 0.025;
this.enableCharacterChange = data.chanageCharacter ?? true;
this.showLangDd = data.showLanguageChangeOptions ?? false;
this.selectedLanguage = 'en';
this.baseUrl = data.baseUrl || "https://translateai.deaftawk.com:3001";
this.poseWorldLandmarks = data.poseWorldLandmarks || 'false';
const dims = this.checkDimensions(data.width ? data.width : 0);
this.showLogs(`DATA WIDTH ${data.width}`);
this.showLogs(`WINDOW WIDTH HEIGHT ${dims.valid}`);
if (dims.valid) {
this.windowWidth = data.width;
this.windowHeight = dims.height;
}
this.buildUI();
this.appMode === 4 ? this.initializeUnityWebGL() : this.playVideoMode(this.CHARACTER_POS_TEXT);
}
async buildUI() {
/* ==========>> CONTAINER ELEMENT UI METHODS CALL <<============= */
this.showLogs('BUILDING UI ============>');
const characterContainer = await this.createCharacterChangeBtns();
const crossButton = await this.createCrossButton();
const versionContainer = await this.createVersionContainer();
const miniBtn = await this.createMiniButton();
const loader = await this.createLoader();
const slider = await this.createSlider();
const container = document.createElement('div');
container.id = 'webgl_m_c';
container.style.position = 'absolute';
container.style.top = '50px';
container.style.left = '50px';
container.style.width = `${this.windowWidth}px`;
container.style.height = `${this.windowHeight}px`;
container.style.border = '1px solid #ccc';
container.style.borderRadius = '5px';
container.style.cursor = 'grab';
container.style.display = 'flex';
container.style.zIndex = '99999';
container.style.background = '#fff';
container.addEventListener('mouseover', () => this.toggleControls(true));
container.addEventListener('mouseout', () => this.toggleControls(false));
/* ==========>> CONTAINER UI <<============= */
document.body.appendChild(container);
document.body.appendChild(miniBtn);
this.makeCanvasDraggable(container);
/* ==========>> CONTAINER ELEMENT UI APPEND <<============= */
container.append(characterContainer);
container.append(crossButton);
container.append(versionContainer);
container.append(loader);
container.append(slider);
/* ----------------------*******---------------------- */
this.showLogs(`MODE ============>${this.appMode}`);
if (this.appMode === 3) {
const video = document.createElement('video');
video.id = 'signLanguagePlayer';
video.style.width = `${this.windowWidth}px`;
video.style.height = `${this.windowHeight}px`;
video.style.borderRadius = '5px';
video.style.objectFit = 'cover';
video.muted = true;
video.autoplay = true;
video.loop = true;
video.playsInline = true;
container.appendChild(video);
}
else {
const canvas = document.createElement('canvas');
canvas.id = 'unityCanvas';
canvas.style.width = `${this.windowWidth}px`;
canvas.style.height = `${this.windowHeight}px`;
canvas.style.borderRadius = '5px';
canvas.style.display = 'none';
container.appendChild(canvas);
}
}
initializeUnityWebGL() {
const buildUrl = 'https://d1g156dmclqesu.cloudfront.net/vanilla/Build';
const loaderUrl = `${buildUrl}/webglbuild.loader.js`;
const config = {
dataUrl: `${buildUrl}/webglbuild.data`,
frameworkUrl: `${buildUrl}/webglbuild.framework.js`,
codeUrl: `${buildUrl}/webglbuild.wasm`,
streamingAssetsUrl: 'StreamingAssets',
companyName: 'DeafTawk',
productName: 'Translate',
productVersion: '1.0.2',
};
const script = document.createElement('script');
script.src = loaderUrl;
script.onload = () => {
//@ts-ignore
createUnityInstance(document.getElementById('unityCanvas'), config, () => { }).then(instance => {
this.unityInstance = instance;
this.playPose({ text: this.CHARACTER_POS_TEXT });
});
};
document.body.appendChild(script);
}
playPose(data, callback, showLoader = true) {
this.showLogs(`PLSY PODR CALLED ============>`);
if (!data.text || data.text === this.requestedText) {
callback?.("No text provided to play pose");
return;
}
this.showLogs(`PLAYING POSE ============>${data.text}`);
this.requestedText = data.text;
callback ? this.unityCallback = callback : '';
if (showLoader)
this.toggleLoader(true);
this.showLogs(`PLAYING POSE 1============>${data.text}`);
if (data.language && ['en', 'da'].indexOf(data.language) > -1) {
this.selectedLanguage = data.language;
}
this.showLogs(`PLAYING POSE 2============>this.appMode${this.appMode}`);
if (this.appMode === 3) {
this.playVideoMode(data.text);
}
else {
this.showLogs(`INSIDE MODE 4 CHECING UNITY INSTANCE`);
if (this.unityInstance) {
const payload = {
text: data.text,
baseUrl: this.baseUrl,
poseWorldLandmarks: this.poseWorldLandmarks,
character: this.character,
nextFrameTime: this.speed,
apiKey: this.apiKey,
backgroundColor: this.backgroundColor,
characterClothColor: this.characterClothColor,
language: this.selectedLanguage,
};
this.showLogs(`DATA FOR UNITY REQ ${JSON.stringify(payload)}`);
this.unityInstance.SendMessage('NetworkManager', 'RecievePoseText', JSON.stringify(payload));
}
else {
this.showLogs(`NO UNITY INSTANCE RETRYING...`);
setTimeout(() => this.playPose(data, callback), 1000);
}
}
}
async playVideoMode(text, showLoader = true) {
const signed = this.selectedLanguage === 'en' ? 'ase' : 'dsl';
if (showLoader)
this.toggleLoader(true);
const baseUrl = this.baseUrl;
const url = `${baseUrl}/spoken_text_to_signed_pose?text=${encodeURIComponent(text)}&spoken=${this.selectedLanguage}&signed=${signed}&myown=3&character=${this.character}&apikey=${this.apiKey}`;
try {
const res = await fetch(url);
const blob = await res.blob();
const video = document.getElementById('signLanguagePlayer');
if (video) {
video.src = URL.createObjectURL(blob);
}
this.toggleLoader(false);
this.unityCallback?.('');
}
catch (err) {
this.toggleLoader(false);
console.error(err);
this.unityCallback?.('');
}
}
showWindow() {
const mainWindow = document.getElementById('webgl_m_c');
const minibtn = document.getElementById('sw_btn');
if (mainWindow)
mainWindow.style.display = 'flex';
if (minibtn)
minibtn.style.display = 'none';
}
hideWindow() {
const mainWindow = document.getElementById('webgl_m_c');
const minibtn = document.getElementById('sw_btn');
if (mainWindow)
mainWindow.style.display = 'none';
if (minibtn)
minibtn.style.display = 'flex';
}
toggleControls(show) {
const header = document.getElementById('webgl_header');
const version = document.getElementById('versionContainer');
const charContainer = document.getElementById('characterContainer');
if (!header || !version || !charContainer)
return;
if (show) {
header.style.display = 'flex';
version.style.display = 'none';
if (this.requestedText && this.enableCharacterChange) {
charContainer.style.display = 'flex';
}
}
else {
header.style.display = 'none';
version.style.display = 'flex';
charContainer.style.display = 'none';
}
}
handleWebglError(error) {
this.showLogs('ERROR RECIEVED FROM UNITY');
if (typeof this.unityCallback === 'function') {
this.unityCallback(error);
}
console.error('WEBGL-ERROR ----> ', error);
}
onDataReceived(message) {
this.showLogs('DATA RECIEVED FROM UNITY');
this.toggleLoader(false);
const unityCanvas = document.getElementById('unityCanvas');
const loader = document.getElementById('webgl_loader');
if (loader)
loader.style.display = 'none';
if (unityCanvas) {
unityCanvas.style.display = 'flex';
unityCanvas.style.cursor = 'grab';
}
const langDropdown = document.getElementById('webgl-lang-dd');
if (langDropdown) {
langDropdown.style.display = this.showLangDd ? 'block' : 'none';
}
if (!this.requestedText || this.requestedText === this.CHARACTER_POS_TEXT)
return;
const textContainer = document.getElementById('t_c_slider');
if (textContainer) {
textContainer.textContent = this.requestedText;
}
const slider = document.getElementById('webgl_slider');
if (slider) {
slider.style.display = 'flex';
}
if (typeof this.unityCallback === 'function') {
this.unityCallback('');
}
const banner = document.getElementById('webgl_bnr');
if (banner) {
banner.style.display = 'none';
}
}
checkDimensions(width) {
const minWidth = 275;
const maxWidth = 500;
const baseHeight = 155;
const ratio = baseHeight / minWidth;
if (width <= 0) {
return { valid: false, height: baseHeight };
}
if (width < minWidth || width > maxWidth)
return { valid: false, height: baseHeight };
return { valid: true, height: Math.round(width * ratio) };
}
createCharacterChangeBtns() {
const characterContainer = document.createElement("div");
characterContainer.id = "characterContainer";
characterContainer.style.position = "absolute";
characterContainer.style.width = `${this.windowWidth}px`;
characterContainer.style.textAlign = "center";
characterContainer.style.display = "none";
characterContainer.style.justifyContent = "center";
characterContainer.style.top = "40%";
const btnsParent = document.createElement("div");
btnsParent.style.width = `${this.windowWidth}px`;
btnsParent.style.textAlign = "center";
btnsParent.style.display = "flex";
btnsParent.style.justifyContent = "space-between";
const prevCharacter = document.createElement("div");
prevCharacter.className = "round-btn";
prevCharacter.id = "prevCharacter";
prevCharacter.textContent = "◀";
prevCharacter.title = "Previous character";
const nextCharacter = document.createElement("div");
nextCharacter.className = "round-btn";
nextCharacter.id = "nextCharacter";
nextCharacter.textContent = "▶";
nextCharacter.title = "Next character";
const roundButtonStyle = `
width: 28px;
height: 28px;
border-radius: 16px;
display: flex;
justify-content: center;
align-items: center;
border: 1px solid navajowhite;
background-color: #d4dceb;
font-size: 11px;
color: gray;
cursor: pointer;
`;
prevCharacter.style.cssText = roundButtonStyle;
nextCharacter.style.cssText = roundButtonStyle;
btnsParent.appendChild(prevCharacter);
btnsParent.appendChild(nextCharacter);
characterContainer.appendChild(btnsParent);
nextCharacter.addEventListener("click", () => {
this.handleCharacterChange('next');
});
prevCharacter.addEventListener("click", () => {
this.handleCharacterChange('prev');
});
return characterContainer;
}
handleCharacterChange(action) {
const data = { text: this.requestedText };
switch (action) {
case 'next':
this.character = (this.character + 1) % this.totalCharacters;
this.requestedText = '';
this.playPose(data);
break;
default:
this.character = (this.character - 1 + this.totalCharacters) % this.totalCharacters;
this.requestedText = '';
this.playPose(data);
break;
}
}
createCrossButton() {
const div = document.createElement('div');
div.id = 'webgl_header';
div.style.position = 'absolute';
div.style.width = "24px";
div.style.height = "24px";
div.style.display = "none";
div.style.justifyContent = "center";
div.style.alignItems = "center";
div.style.right = '5px';
div.style.cursor = 'pointer';
const crossSymbol = document.createElement('span');
crossSymbol.textContent = '×';
crossSymbol.style.fontSize = '20px';
crossSymbol.style.color = '#000';
crossSymbol.style.userSelect = 'none';
div.appendChild(crossSymbol);
div.addEventListener('click', () => {
this.handleWindowToggle();
});
return div;
}
handleWindowToggle() {
if (this.isWindowVisible) {
this.isWindowVisible = false;
this.hideWindow();
this.toggleLoader(false);
return;
}
this.isWindowVisible = true;
this.showWindow();
}
createVersionContainer() {
const versionContainer = document.createElement('span');
versionContainer.id = 'versionContainer';
versionContainer.textContent = `V-${this.appVersion}`;
versionContainer.style.display = "flex";
versionContainer.style.position = "absolute";
versionContainer.style.zIndex = "9";
versionContainer.style.right = "10px";
versionContainer.style.top = "6px";
versionContainer.style.color = "#d7d1d1";
versionContainer.style.fontSize = "12px";
return versionContainer;
}
makeCanvasDraggable(canvas) {
let offsetX, offsetY, isDragging = false;
canvas.addEventListener('mousedown', (event) => {
isDragging = true;
offsetX = event.clientX - canvas.offsetLeft;
offsetY = event.clientY - canvas.offsetTop;
canvas.style.cursor = "grabbing";
});
document.addEventListener('mousemove', (event) => {
if (isDragging) {
canvas.style.left = event.clientX - offsetX + 'px';
canvas.style.top = event.clientY - offsetY + 'px';
}
});
document.addEventListener('mouseup', () => {
isDragging = false;
canvas.style.cursor = "grab";
});
}
createMiniButton() {
const div = document.createElement('div');
div.id = 'sw_btn';
div.style.position = 'absolute';
div.style.bottom = '5px';
div.style.right = '5px';
div.style.width = "34px";
div.style.height = "34px";
div.style.border = "1px solid #cec6c6";
div.style.cursor = 'pointer';
div.style.display = "none";
div.style.justifyContent = "center";
div.style.alignItems = "center";
div.style.animation = 'bounce 3s infinite';
div.style.backgroundColor = '#aba3a3';
div.style.borderRadius = '50%';
div.title = 'Show Translate window';
const arrowUp = document.createElement('span');
arrowUp.textContent = '↑';
arrowUp.style.fontSize = '20px';
arrowUp.style.color = '#000';
arrowUp.style.userSelect = 'none';
div.appendChild(arrowUp);
div.addEventListener('click', () => {
this.handleWindowToggle();
});
const style = document.createElement('style');
style.textContent = `
bounce {
0%, 20%, 50%, 80%, 100% {
transform: translateY(0);
}
40% {
transform: translateY(-10px);
}
60% {
transform: translateY(-5px);
}
}
`;
document.head.appendChild(style);
return div;
}
createLoader() {
let loader = document.createElement('div');
loader.id = 'webgl_loader';
loader.style.position = 'absolute';
loader.style.width = `${this.windowWidth}px`;
loader.style.height = `${this.windowHeight}px`;
loader.style.cursor = 'grab';
loader.style.display = 'flex';
loader.style.justifyContent = 'center';
loader.style.alignItems = 'center';
loader.style.zIndex = '1';
let spinner = document.createElement('div');
spinner.style.border = '4px solid #f3f3f3';
spinner.style.borderTop = '4px solid #3498db';
spinner.style.borderRadius = '50%';
spinner.style.width = '28px';
spinner.style.height = '28px';
spinner.style.animation = 'spin 1s linear infinite';
spinner.style.borderColor = '#2a4b8d #2a4b8d rgb(255, 255, 255)';
loader.appendChild(spinner);
const styleSheet = document.createElement('style');
styleSheet.type = 'text/css';
styleSheet.innerText = `
spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
`;
document.head.appendChild(styleSheet);
return loader;
}
createSlider() {
const div = document.createElement('div');
div.id = 'webgl_slider';
Object.assign(div.style, {
position: 'absolute',
width: `${this.windowWidth - 11}px`,
height: "24px",
display: "none",
justifyContent: "flex-start",
alignItems: "center",
overflow: 'hidden',
padding: '3px 6px',
backgroundColor: '#000',
fontSize: '12px',
textAlign: 'center',
bottom: '-1px',
borderBottomRightRadius: '5px',
borderBottomLeftRadius: '5px',
color: '#fff'
});
const text = document.createElement('span');
text.id = "t_c_slider";
text.textContent = '';
Object.assign(text.style, {
display: 'inline-block',
whiteSpace: 'nowrap',
position: 'relative',
paddingLeft: '100%',
animation: 'slideLeft linear infinite'
});
div.appendChild(text);
const style = document.createElement('style');
style.textContent = `
slideLeft {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-100%);
}
}
`;
document.head.appendChild(style);
const resizeObserver = new ResizeObserver(() => {
this.updateSliderAnimation(div, text);
});
resizeObserver.observe(text);
return div;
}
updateSliderAnimation(container, textElement, pixelsPerSecond = 50) {
textElement.style.animation = 'none';
void textElement.offsetWidth;
const containerWidth = container.offsetWidth;
const textWidth = textElement.scrollWidth;
const distance = containerWidth + textWidth;
const duration = distance / pixelsPerSecond;
textElement.style.animation = `slideLeft ${duration}s linear infinite`;
}
toggleLoader(show) {
const loader = document.getElementById('webgl_loader');
if (!loader)
return;
if (show) {
loader.style.display = 'flex';
}
else {
loader.style.display = 'none';
}
}
createLanguageDropdown() {
const style = document.createElement('style');
style.textContent = `
.dropdown-container {
position: relative;
width: 65px;
font-family: sans-serif;
font-size: 12px;
top: 2px;
left: 3px;
display: none;
}
.dropdown-selected {
padding: 3px;
background: #f0f0f0;
border: 1px solid #ccc;
cursor: pointer;
border-radius: 2px;
display: flex;
justify-content: center;
align-items: center;
color: #424a9f;
}
.dropdown-list {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: white;
border: 1px solid #ccc;
height: 56px;
max-height: 130px;
overflow-y: auto;
margin-top: 4px;
border-radius: 6px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
z-index: 100;
}
.dropdown-item {
padding: 6px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
}
.dropdown-item:hover {
background-color: #424a9f;
color: #fff
}
.hidden {
display: none;
}
.dropdown-list::-webkit-scrollbar {
width: 6px;
}
.dropdown-list::-webkit-scrollbar-thumb {
background-color: #888;
border-radius: 3px;
}
.dropdown-list::-webkit-scrollbar-thumb:hover {
background-color: #555;
}
`;
document.head.appendChild(style);
const container = document.createElement('div');
container.className = 'dropdown-container';
container.id = 'webgl-lang-dd';
const selected = document.createElement('div');
selected.className = 'dropdown-selected';
selected.textContent = this.languages[0].toUpperCase();
const list = document.createElement('div');
list.className = 'dropdown-list hidden';
this.languages.forEach(lang => {
const item = document.createElement('div');
item.className = 'dropdown-item';
item.textContent = lang.toUpperCase();
item.addEventListener('click', () => {
selected.textContent = lang.toUpperCase();
this.selectedLanguage = lang;
list.classList.add('hidden');
});
list.appendChild(item);
});
selected.addEventListener('click', () => {
list.classList.toggle('hidden');
});
container.appendChild(selected);
container.appendChild(list);
document.addEventListener('click', (e) => {
const target = e.target;
if (container && !container.contains(target)) {
list.classList.add('hidden');
}
});
return container;
}
showLogs(message) {
if (!this.shouldShowLogs)
return;
console.log('DT ANGUALR SDK:', message);
}
}
const Translate = new TranslateController();
window.onDataRecieved = (message) => {
Translate.onDataReceived(message);
};
class TranslateService {
init(options) {
this.controller = new TranslateController();
this.controller.init(options);
}
playPose(data, callback) {
this.controller?.playPose(data, callback);
}
showWindow(text) {
this.controller?.showWindow();
}
hideWindow(text) {
this.controller?.hideWindow();
}
}
TranslateService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TranslateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
TranslateService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TranslateService, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TranslateService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
class TranslateModule {
}
TranslateModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TranslateModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
TranslateModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: TranslateModule });
TranslateModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TranslateModule, providers: [TranslateService] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TranslateModule, decorators: [{
type: NgModule,
args: [{
providers: [TranslateService]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { TranslateModule, TranslateService };
//# sourceMappingURL=dt-translate-angular.mjs.map