| 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;
 |