| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- const { phrasebook } = require("./phrasebook");
- /**
- * This class is responsible for automatic translate site content of the
- * HTML elements. It could automatic translate content for all elements
- * which are in the "translate" class, and has "phrase" attribute. This
- * attrobite store phrase to translate, and insert into element innerText
- * or placeholder (for inputs). It could also observate HTML Node, and
- * automatic update transltions when any item had been changed.
- */
- class autotranslate {
- /**
- * @var {?phrasebook}
- * This store phrasebook to get translates from, or store null, to get
- * translate content from global translate function.
- */
- #phrasebook;
- /**
- * @var {?MutationObserver}
- * This store observer object, when it is connecter and waiting for
- * changaes, or store null, when observer currently not working.
- */
- #observer;
- /**
- * This create new autotranslator. It require phrasebook, to loads
- * translations for phrases from. When null had been given, the it
- * use global translate function.
- *
- * @throws {Error} - When trying to use it in the NodeJS.
- *
- * @param {?phrasebook} phrasebook
- */
- constructor(phrasebook = null) {
- NODE: throw new Error("It is not avairable in the NodeJS.");
- this.#observer = null;
- this.#phrasebook = phrasebook;
- }
- /**
- * It return class name for elements, which would be translated by
- * autotranslator.
- *
- * @returns {string} - Class name for autotranslating elements.
- */
- static get_class_name() {
- return "translate";
- }
- /**
- * This return selector for choose elements which must be autotranslated.
- *
- * @returns {string} - Selector of the elements to translate.
- */
- get #class_selector() {
- return "." + autotranslate.get_class_name();
- }
- /**
- * This return name of attribute which store phrase to translate.
- *
- * @returns {string} - Name of attribute which store phrase.
- */
- static get_attribute_name() {
- return "phrase";
- }
- /**
- * This check that autotranslator is connected and waiting to changes.
- *
- * @returns {bool} - True when observer is connected, fakse when not.
- */
- get is_connected() {
- return this.#observer !== null;
- }
- /**
- * This search elements which could be translated in the element given
- * in the parameter. When null given, then it search elements in the
- * all document.
- *
- * @param {?HTMLElement} where - Item to load items from or null.
- * @returns {Array} - Array of elements to translate.
- */
- #get_all_items(where = null) {
- if (where === null) {
- where = document;
- }
- return Array.from(
- where.querySelectorAll(this.#class_selector)
- );
- }
- /**
- * It translate given phrase, baseed on loaded phrasebook, or when not
- * loaded any, then use global translate function. When it also not
- * exists, then throws error in debug mode, or return not translated
- * phrase on production.
- *
- * @throws {Error} - When any option to translate not exists.
- *
- * @param {string} content - Phrase to translate.
- * @returns {string} - Translated content.
- */
- #translate(content) {
- if (this.#phrasebook !== null) {
- return this.#phrasebook.translate(content);
- }
- if (_ === undefined) {
- DEBUG: throw new Error("All translate options are unavairable.");
- return content;
- }
- return _(content);
- }
- /**
- * This add mutable observer to the body. It wait for DOM modifications,
- * and when any new node had been adder, or any phrase attribute had
- * been changed, then it trying to translate it.
- *
- * @returns {autotranslate} - This object to chain load.
- */
- connect() {
- if (this.is_connected) {
- return this;
- }
- const body = document.querySelector("body");
- const callback = (targets) => { this.#process(targets); };
- const options = {
- childList: true,
- attributes: true,
- characterData: false,
- subtree: true,
- attributeFilter: [ autotranslate.get_attribute_name() ],
- attributeOldValue: false,
- characterDataOldValue: false
- };
- this.#observer = new MutationObserver(callback);
- this.#observer.observe(body, options);
- return this;
- }
- /**
- * This prcoess all given in the array mutable records.
- *
- * @param {Array} targets - Array with mutable records.
- */
- #process(targets) {
- targets.forEach(count => {
- if (count.type === "attributes") {
- this.#update_single(count.target);
- return;
- }
- this.#get_all_items(count.target).forEach(count => {
- this.#update_single(count);
- });
- });
- }
- /**
- * This disconnect observer, and remove it.
- *
- * @returns {autotranslate} - This object to chain loading.
- */
- disconnect() {
- if (!this.is_connected) {
- return this;
- }
- this.#observer.disconnect();
- this.#observer = null;
- return this;
- }
- /**
- * This update single element, based on phrase attribute. When element
- * is standard HTMLElement, then it place translated content into
- * innerText, but when element is input, like HTMLInputElement or
- * HTMLTextAreaElement, then it place result into placeholder. When
- * input is button, or submit, then it put content into value.
- *
- * @param {HTMLElement} target - Element to translate
- */
- #update_single(target) {
- const attrobute_name = autotranslate.get_attribute_name();
- const phrase = target.getAttribute(attrobute_name);
- const result = this.#translate(phrase);
- if (target instanceof HTMLInputElement) {
- if (target.type === "button" || target.type === "submit") {
- target.value = result;
- return;
- }
-
- target.placeholder = result;
- return;
- }
- if (target instanceof HTMLTextAreaElement) {
- target.placeholder = result;
- return;
- }
- target.innerText = result
- }
- /**
- * This update translation of all elements in the document. It is useable
- * when new autotranslator is created.
- *
- * @returns {autotranslate} - Instance of object to chain loading.
- */
- update() {
- this.#get_all_items().forEach(count => {
- this.#update_single(count);
- });
- return this;
- }
- }
- exports.autotranslate = autotranslate;
|