import window from 'global/window';
import document from 'global/document';
import EventEmitter from 'events';
import ObjectAssign from 'object-assign';
import Cookies from 'js-cookie';
import Promise from 'es6-promise';
import qs from 'query-string';
import 'classlist-polyfill';
import { version } from '../package.json';
import { createLogger, getDeviceInfo } from './utils';
import TextManager from './managers/TextManager';
import VideoManager from './managers/VideoManager';
import PageSpeechManager from './managers/PageSpeechManager';
import PageTranslatorManager from './managers/PageTranslatorManager';
import { ytEmbedReplace, videojsReplace } from './tools';
import DesktopDrawer from './components/DesktopDrawer/DesktopDrawer';
import MobileDrawer from './components/MobileDrawer/MobileDrawer';
/**
* Classe principal do plugin da Hand Talk.
*/
class HT extends EventEmitter {
/**
* Versão do plugin
*/
static version = version;
static defaultConfig = {
bottom: '0px',
side: 'right',
zIndex: 1000000,
maxTextSize: 500,
debug: false,
doNotTrack: false,
parentElement: document.body,
exceptions: [],
textEnabled: true,
videoEnabled: false,
ytEmbedReplace: false,
videojsReplace: false,
align: 'default',
mobileEnabled: true,
pageSpeech: false
};
/**
* Tags que devem ser ignoradas pelo tradutor de texto
* @type {string[]}
*/
static skipTags = ['script', 'head', 'style', 'body', 'document', 'table', 'thead', 'tbody'];
static deviceInfo = getDeviceInfo();
static customCorrespondingObject = {
pele: ['UNT-BRACOS', 'UNT-CABECA', 'UNT-CABELO'],
calca: ['UNT-CALCA', 'UNT-CINTO', 'UNT-SAPATO'],
manga_curta: ['UNT-CAMISA_MCURTA'],
manga_longa: ['UNT-CAMISA_MLONGA'],
manga_longa_sem_gravata: ['UNT-CAMISA_MLONGA_SGRAVATA'],
chapeu_natal: ['UNT-CHAPEU_NATAL'],
chapeu_palha: ['UNT-CHAPEU_PALHA'],
kimono: ['UNT-KIMONO'],
};
/**
* Objeto responsável pelo log (.error, .warn, .info, .debug)
* @type {object}
*/
logger = null;
/**
* Objeto de configuração
* @type {{side: string, zIndex: number, maxTextSize: number, debug: boolean, doNotTrack: boolean, parentElement: HTMLElement, exceptions: Array, textEnabled: boolean, videoEnabled: boolean, ytEmbedReplace: boolean, align: string, mobileEnabled: boolean, mobileConfig{bottom, side, videoEnabled, ytEmbedReplace, videojsReplace}, pageSpeech}}
*/
config = {};
/**
* Instancia do gerenciador de texto
* @type {TextManager}
*/
textManager = null;
/**
* Instancia do gerenciador de vídeos
* @type {VideoManager}
*/
videoManager = null;
/**
* Instancia do gerenciador de texto para voz
* @type {SpeechSynthesizerManager}
*/
pageSpeechManager = null;
/**
* Instancia do gerenciador de tradução de texto para linguagem de sinais.
*/
pageTranslatorManager = null;
_token = null;
hugoLoaded = false;
/**
* @param {object} config - Objeto de configuração
*/
constructor(config) {
super();
const mobileConfig = config.mobileConfig;
// Mescla as configurações
this.config = ObjectAssign(HT.defaultConfig, config);
if (HT.deviceInfo.isMobile) this.config = ObjectAssign(this.config, mobileConfig);
// Define console como debug quando depuração está ativada
this.logger = this.config.debug ? createLogger(3) : createLogger(1);
// Verifica se o plugin foi inicializado mais de uma vez
if (window.hasHtInitialized) return this.logger.error('O plugin da Hand Talk já foi inicializado nesta página');
this._token = TOKEN || this.config.token;
// Marca como plugin inicializado
window.hasHtInitialized = true;
// Preferência de lado (side)
const preferredSide = Cookies.get(PREFERRED_SIDE_COOKIE_NAME);
if (preferredSide) this.config.side = preferredSide;
if (this.config.side !== 'left' && this.config.side !== 'right') this.side = 'right';
// Condicionais para instanciar o leitor/tradutor de sites
let pageSpeechReady = this.config.pageSpeech && !HT.deviceInfo.isMobile;
let textTranslatorReady = (this.config.textEnabled && !HT.deviceInfo.isMobile) || (this.config.mobileEnabled && HT.deviceInfo.isMobile);
// Cria o drawer
if (textTranslatorReady || pageSpeechReady) {
this.drawer = HT.deviceInfo.isMobile ? new MobileDrawer(this) : new DesktopDrawer(this);
this.config.parentElement.appendChild(this.drawer.elem);
}
// Instancia o manipulador de elementos
if (textTranslatorReady || pageSpeechReady) this.textManager = new TextManager(this);
// Instancia o gerenciador de vídeos
if (this.config.videoEnabled) this.videoManager = new VideoManager(this);
// Instancia o leitor de sites
if (pageSpeechReady) this.pageSpeechManager = new PageSpeechManager(this);
// Instancia do tradutor de sites
if (textTranslatorReady) this.pageTranslatorManager = new PageTranslatorManager(this);
this.logger.info('Plugin da Hand Talk iniciado com as seguintes configurações', this.config);
videojsReplace(this);
ytEmbedReplace(this);
if (this.config.ytEmbedReplace) this.once('videoManagerReady', this.replaceYtEmbedAll);
if (this.config.videojsReplace) this.once('videoManagerReady', this.replaceVideoJsAll);
}
/**
* Adiciona os listeners de click em um iframe existente
* @param iframe - Iframe que receberá os listeners
* */
addListenersToIframe = (iframe) => {
if (this.textManager) {
try {
this.textManager.addIframeListeners(iframe);
} catch (e) {
this.logger.warn('Falha ao adicionar EventListener no iframe ' + e);
}
}
}
/**
* Busca por iframes e adiciona os listeners de click
*/
addIframesListenersAll = () => {
if (this.textManager) this.textManager.addIframesListenersAll();
}
get side() { return this.config.side; }
set side(value) {
this.config.side = value;
if (this.textManager && this.drawer) this.drawer.side = value;
}
get align() { return this.config.align; }
set align(value) {
if (this.textManager && this.drawer) this.drawer.align = value;
}
_getHugoPromise = null;
_hugo = null;
get hugo() {
if (this._getHugoPromise) return this._getHugoPromise;
return this._getHugoPromise = Promise.resolve()
.then(this._auth)
.then(this._loadHugo);
}
_authPromise = null;
_auth = () => {
if (this._authPromise) return this._authPromise;
return this._authPromise = new Promise((resolve, reject) => {
this.emit('authenticating');
const xmlHttp = new XMLHttpRequest();
this.uid = Cookies.get(HT_PLUGIN_UID_COOKIE_NAME);
const params = {
token: this._token,
};
if (this.config.videoEnabled) params.videoEnabled = true;
xmlHttp.open('GET', `${TRANSLATE_AUTH_URL}?${qs.stringify(params)}`);
xmlHttp.setRequestHeader('Version', '3');
if (this.uid) xmlHttp.setRequestHeader('UID', this.uid);
const on = () => {
let data;
try {
data = JSON.parse(xmlHttp.responseText);
} catch (e) {
data = {};
}
const { uid, custom, error, message } = data;
this.logger.debug('auth uid', uid);
this.logger.debug('auth custom', custom);
this.logger.debug('auth error', error);
this.logger.debug('auth message', message);
if (xmlHttp.status === 406)
this.logger.warn(`${error || 'A sua assinatura está desabilitada'}. Acesse ` +
'https://account.handtalk.me para regularizar o serviço ou entre em contato via ' +
'suporte@handtalk.com.br');
if (xmlHttp.status !== 200) {
this.emit('errorOnAuth', message || 'Opa! Um problema ocorreu :/ aguarde um instante e recarregue a página.');
return reject(data.message || data);
}
if (!this.uid && uid)
Cookies.set(HT_PLUGIN_UID_COOKIE_NAME, uid, { expires: 10 * 365 });
this.uid = Cookies.get(HT_PLUGIN_UID_COOKIE_NAME);
this.emit('authenticated');
resolve(data);
this.hugo.then((hugo) => {
const customDict = custom || {};
const finalCustom = {};
for (let key in customDict) {
const value = customDict[key];
HT.customCorrespondingObject[key].forEach((corresponding) => {
finalCustom[corresponding] = {
visible: !(value === 'hide'),
map: value === 'hide' ? undefined : (value.indexOf('http://') === 0 || value.indexOf('https://') === 0) && value ||
`https://api.handtalk.me/unity/textures/${value}`, // TODO: atualiza link das texturas
};
});
}
this.logger.info(finalCustom);
this.emit('customizing');
hugo.custom(finalCustom)
.then(this.emit.bind(this, 'customized'));
});
};
xmlHttp.onload = on;
xmlHttp.onerror = on;
xmlHttp.send();
});
};
_hugoPromise = null;
/**
* Injeta biblioteca hugo.js e inicia objeto Hugo
* @function
*/
startHugo = () => {
if (this._hugoPromise) return this._hugoPromise;
return this._hugoPromise = new Promise((resolve, reject) => {
const scriptElem = document.createElement('script');
scriptElem.src = HUGO_JS_SCRIPT_URL;
scriptElem.onload = () => {
this._hugo = new Hugo(this._token);
this._hugo.on('downloadDependenciesProgress', this.emit.bind(this, 'hugoDownloadDependenciesProgress'));
this._hugo.on('downloadSceneDataProgress', this.emit.bind(this, 'hugoDownloadSceneDataProgress'));
this._hugo.on('translating', this.emit.bind(this, 'translating'));
this._hugo.on('errorOnTranslate', this.emit.bind(this, 'errorOnTranslate'));
this._hugo.on('translated', this.emit.bind(this, 'translated'));
this._hugo.on('notCompatible', this.emit.bind(this, 'notCompatible'));
resolve(this._hugo);
};
scriptElem.onerror = reject;
this.config.parentElement.appendChild(scriptElem);
});
};
_loadHugo = () => (this.startHugo()
.then(() => (this._hugo.load()))
.then(() => (this.emit('hugoLoaded')))
.then(() => (this.hugoLoaded = true))
.then(() => (this._hugo)));
/**
* Muda o plugin de lado
* @function
*/
toggleSide = () => {
if (this.side === 'right') this.side = 'left';
else if (this.side === 'left') this.side = 'right';
};
/**
* Remove e destroi plugin da página
* @function
*/
destroy = () => {
this.open = false;
if (this.textManager) this.textManager.destroy();
// Desativa e destroi o pageTranslator (caso exista)
if (this.pageTranslatorManager && this.pageTranslatorManager.activated)
this.pageTranslatorManager.activated = false;
// Desativa e destroi o pageSpeech (caso exista)
if (this.pageSpeechManager && this.pageSpeechManager.activated)
this.pageSpeechManager.activated = false;
// Remove o drawer da pagina
if (this.drawer) this.drawer.elem.remove();
window.hasHtInitialized = false;
};
}
export default HT;