PageSourceSearch

https://www.promfest.ee/wp-content/plugins/translatepress-multilin…/js/trp-translate-dom-changes.js?ver=2.3.9

js promfest.ee collected 2026-10-02 13:52:07 UTC 26,630 bytes, 614 lines download raw bytes

1
2/**
3 * Script to replace dynamically added strings with their translation.
4 */
5function TRP_Translator(){
6
7    this.is_editor = false;
8    var _this = this;
9    var observer = null;
10    var observerConfig = {
11        attributes: true,
12        childList: true,
13        characterData: false,//this could be CDATA so I set it to false in v 1.4.5
14        subtree: true
15    };
16    var translate_numerals_opt = trp_data.trp_translate_numerals_opt;
17    var custom_ajax_url = trp_data.trp_custom_ajax_url;
18    var wp_ajax_url = trp_data.trp_wp_ajax_url;
19    var language_to_query;
20    this.except_characters = " \t\n\r � �.,/`~!@#$€£%^&*():;-_=+[]{}\\|?/<>1234567890'";
21    var trim_characters = " \t\n\r � �\x0A\x0B" + "\302" + "\240";
22    var already_detected = [];
23    var duplicate_detections_allowed = parseInt( trp_data.duplicate_detections_allowed )
24
25    /**
26     * Ajax request to get translations for strings
27     */
28    this.ajax_get_translation = function( nodesInfo, string_originals, url, skip_machine_translation ) {
29        jQuery.ajax({
30            url: url,
31            type: 'post',
32            dataType: 'json',
33            data: {
34                action                   : 'trp_get_translations_regular',
35                all_languages            : 'false',
36                security                 : trp_data['gettranslationsnonceregular'],
37                language                 : language_to_query,
38                original_language        : original_language, // used for trp custom ajax
39                originals                : JSON.stringify( string_originals ),
40                skip_machine_translation : JSON.stringify( skip_machine_translation ),
41                dynamic_strings          : 'true',
42                translate_numerals_opt   : translate_numerals_opt
43            },
44            success: function( response ) {
45                if ( response === 'error' ) {
46                    _this.ajax_get_translation( nodesInfo, string_originals, wp_ajax_url, skip_machine_translation );
47                    console.log( 'Notice: TranslatePress trp-ajax request uses fall back to admin ajax.' );
48                }else{
49                    _this.update_strings( response, nodesInfo );
50                }
51            },
52            error: function( errorThrown ){
53                if ( url == custom_ajax_url && custom_ajax_url != wp_ajax_url ){
54                    _this.ajax_get_translation( nodesInfo, string_originals, wp_ajax_url, skip_machine_translation );
55                    console.log( 'Notice: TranslatePress trp-ajax request uses fall back to admin ajax.' );
56                }else{
57                    _this.update_strings( null, nodesInfo );
58                    console.log( 'TranslatePress AJAX Request Error' );
59                }
60            }
61        });
62    };
63
64    /**
65     * Return given text converted to html.
66     *
67     * Useful for decoding special characters into displayable form.
68     *
69     * @param html
70     * @returns {*}
71     */
72    this.decode_html = function( html ) {
73        var txt = document.createElement( "textarea" );
74        txt.innerHTML = html;
75        return txt.value;
76    };
77
78    /**
79     * Replace original strings with translations if found.
80     */
81    this.update_strings = function( response, nodesInfo ) {
82        _this.pause_observer();
83        if ( response != null && response.length > 0 ){
84            var newEntries = [];
85            for (var j = 0 ; j < nodesInfo.length; j++){
86                var nodeInfo = nodesInfo[j];
87                var translation_found = false;
88                var initial_value = nodeInfo.original;
89                for( var i = 0; i <  response.length; i++ ) {
90                    var response_string = response[i].translationsArray[language_to_query];
91                    if (response[i].original.trim() == nodeInfo.original.trim()) {
92                        // The nodeInfo can contain duplicates and response cannot. We need duplicates to refer to different jQuery objects where the same string appears in different places on the page.
93                        var entry = response[i]
94                        entry.selector = 'data-trp-translate-id'
95                        entry.attribute = ''
96                        newEntries.push( entry )
97                        if ( _this.is_editor ) {
98                            var jquery_object;
99                            var trp_translate_id = 'data-trp-translate-id'
100                            var trp_node_group = 'data-trp-node-group'
101                            if ( nodeInfo.attribute ) {
102                                jquery_object = jQuery(nodeInfo.node)
103                                trp_translate_id = trp_translate_id + '-' + nodeInfo.attribute
104                                trp_node_group = trp_node_group + '-' + nodeInfo.attribute
105                            }else{
106                                jquery_object = jQuery(nodeInfo.node).parent('translate-press');
107                            }
108                            jquery_object.attr( trp_translate_id, response[i].dbID );
109                            jquery_object.attr( trp_node_group, response[i].group );
110                        }
111
112                        if (response_string.translated != '' && language_to_query == current_language ) {
113                            var text_to_set = _this.decode_html( initial_value.replace(initial_value.trim(), response_string.translated));
114                            if ( nodeInfo.attribute ){
115                                nodeInfo.node.setAttribute( nodeInfo.attribute, text_to_set )
116                                if ( nodeInfo.attribute == 'src' ){
117                                    nodeInfo.node.setAttribute( 'srcset', '' )
118                                    nodeInfo.node.setAttribute( 'data-src', text_to_set )
119                                }
120                            }else {
121                                nodeInfo.node.textContent = text_to_set;
122                            }
123                            translation_found = true;
124                        }
125                        break;
126                    }
127                }
128
129                already_detected[ initial_value ] = (initial_value in already_detected ) ? already_detected[ initial_value ] + 1 : 0
130                if ( ! translation_found ){
131                    if ( nodeInfo.attribute ){
132                        if ( nodeInfo.attribute != 'src' ) {
133                            nodeInfo.node.setAttribute(nodeInfo.attribute, initial_value)
134                        }
135                    }else {
136                        nodeInfo.node.textContent = initial_value;
137                    }
138                }
139
140            }
141            // this should always be outside the for loop
142            if ( _this.is_editor ) {
143                window.parent.dispatchEvent( new Event( 'trp_iframe_page_updated' ) );
144                window.dispatchEvent( new Event( 'trp_iframe_page_updated' ) );
145            }
146
147        }else{
148            for (var j = 0 ; j < nodesInfo.length; j++){
149                if ( nodesInfo[j].attribute ){
150                    if ( nodesInfo[j].attribute != 'src' ) {
151                        nodesInfo[j].node.setAttribute(nodesInfo[j].attribute, nodesInfo[j].original)
152                    }
153                }else {
154                    nodesInfo[j].node.textContent = nodesInfo[j].original;
155                }
156                already_detected[ nodesInfo[j].original ] = (nodesInfo[j].original in already_detected ) ? already_detected[ nodesInfo[j].original ] + 1 : 0
157            }
158        }
159        _this.resume_observer();
160    };
161
162    /*
163     * Separated from the detect_new_strings function to allow disconnecting observer.
164     */
165    this.detect_new_strings_callback = function( mutations ){
166        // calling disconnect directly instead of calling pause_observer because we don't need to takeRecords() (called from pause_observer) because we would duplicate mutations
167        observer.disconnect()
168        _this.detect_new_strings( mutations );
169        _this.resume_observer();
170    }
171
172    /**
173     * Detect and remember added strings.
174     */
175    this.detect_new_strings = function( mutations ){
176        var string_originals = [];
177        var nodesInfo = [];
178        var skip_machine_translation = [];
179        var translateable;
180        mutations.forEach( function (mutation) {
181            for (var i = 0; i < mutation.addedNodes.length; i++) {
182                var node = mutation.addedNodes[i]
183                /* if it is an anchor add the trp-edit-translation=preview parameter to it */
184                if ( _this.is_editor ){
185                    var anchor_tags = jQuery(node).find('a')
186                    if ( typeof anchor_tags.context !== 'undefined' )
187                        anchor_tags.context.href = _this.update_query_string('trp-edit-translation', 'preview', anch
187or_tags.context.href);
188                }
189
190                if ( _this.skip_string(node) ){
191                    continue;
192                }
193
194                // Search for innertext modifications or newly added nodes with innertext
195                translateable = _this.get_translateable_textcontent( node )
196                string_originals = string_originals.concat( translateable.string_originals );
197                nodesInfo = nodesInfo.concat( translateable.nodesInfo );
198                skip_machine_translation = skip_machine_translation.concat( translateable.skip_machine_translation );
199
200                // Search for text inside attributes of newly added nodes
201                translateable = _this.get_translateable_attributes( node )
202                string_originals = string_originals.concat( translateable.string_originals );
203                nodesInfo = nodesInfo.concat( translateable.nodesInfo );
204                skip_machine_translation = skip_machine_translation.concat( translateable.skip_machine_translation );
205            }
206
207            if ( mutation.attributeName ){
208                if ( ! _this.in_array( mutation.attributeName, trp_data.trp_attributes_accessors ) ){
209                    return
210                }
211                if ( _this.skip_string_attribute( mutation.target, mutation.attributeName ) || _this.skip_string(mutation.target) ){
212                    return
213                }
214
215                // Search for modified text inside attributes of existing nodes
216                translateable = _this.get_translateable_attributes( mutation.target )
217                string_originals = string_originals.concat( translateable.string_originals );
218                nodesInfo = nodesInfo.concat( translateable.nodesInfo );
219                skip_machine_translation = skip_machine_translation.concat( translateable.skip_machine_translation );
220            }
221        });
222        if ( nodesInfo.length > 0 ) {
223            var ajax_url_to_call = (_this.is_editor) ? wp_ajax_url : custom_ajax_url;
224            _this.ajax_get_translation( nodesInfo, string_originals, ajax_url_to_call, skip_machine_translation );
225        }
226    };
227
228    this.skip_string = function(node){
229        // skip nodes containing these attributes
230        var selectors = trp_data.trp_skip_selectors;
231        for (var i = 0; i < selectors.length ; i++ ){
232            if ( jQuery(node).closest( selectors[ i ] ).length > 0 ){
233                return true;
234            }
235        }
236        return false;
237    };
238
239    this.skip_string_from_auto_translation = function(node){
240        // nodes containing these attributes will not be automatically translated
241        var selectors = trp_data.trp_no_auto_translation_selectors;
242        for (var i = 0; i < selectors.length ; i++ ){
243            if ( jQuery(node).closest( selectors[ i ] ).length > 0 ){
244                return true;
245            }
246        }
247        return false;
248    };
249
250    this.contains_substring_that_needs_skipped = function ( string, attribute ){
251        for (var attribute_to_skip in trp_data.skip_strings_from_dynamic_translation_for_substrings ){
252            if (trp_data.skip_strings_from_dynamic_translation_for_substrings.hasOwnProperty(attribute_to_skip) && attribute === attribute_to_skip) {
253                for ( var i = 0 ; i < trp_data.skip_strings_from_dynamic_translation_for_substrings[attribute_to_skip].length; i++ ){
254                    if ( string.indexOf(trp_data.skip_strings_from_dynamic_translation_for_substrings[attribute_to_skip][i]) !== -1  ){
255                        return true
256                    }
257                }
258            }
259        }
260        return false
261    };
262
263    /*
264     * Skip string based on original string text
265     */
266    this.skip_string_original = function ( string, attribute ){
267        return (
268            ( already_detected[string] > duplicate_detections_allowed ) ||
269            _this.in_array( string, trp_data.skip_strings_from_dynamic_translation ) ||
270            _this.contains_substring_that_needs_skipped( string, attribute)
271        )
272    }
273
274    this.skip_string_attribute = function(node, attribute){
275        // skip nodes containing these attributes
276        var selectors = trp_data.trp_base_selectors;
277        for (var i = 0; i < selectors.length ; i++ ){
278            if ( typeof jQuery(node).attr( selectors[ i ] + '-' + attribute ) !== 'undefined' ){
279                return true;
280            }
281        }
282        return false;
283    };
284
285    this.in_array = function (needle, array ) {
286        var i
287        var length = array.length
288        for( i = length - 1; i >= 0; i-- ){
289            if ( array[i] === needle ){
290                return true
291            }
292        }
293        return false
294    }
295
296    this.get_translateable_textcontent = function( node ){
297        var string_originals = [];
298        var nodesInfo = [];
299        var skip_machine_translation = []
300        if ( node.textContent && _this.trim( node.textContent.trim(), _this.except_characters ) != '' ) {
301
302            var direct_string = get_string_from_node( node );
303            if ( direct_string ) {
304                // a text without HTML was added
305                if ( _this.trim( direct_string.textContent, _this.except_characters ) != '' ) {
306                    var extracted_original = _this.trim(direct_string.textContent, trim_characters);
307                    if ( ! _this.skip_string_original( extracted_original, false )) {
308                        nodesInfo.push({node: node, original: extracted_original, attribute: ''});
309                        string_originals.push(extracted_original)
310                        if ( _this.skip_string_from_auto_translation(node)){
311                            skip_machine_translation.push( extracted_original )
312                        }
313
314                        direct_string.textContent = '';
315                        if (_this.is_editor) {
316                            jQuery(node).wrap('<translate-press></translate-press>');
317                        }
318                    }
319                }
320            }else{
321                // node contains HTML
322                var all_nodes = jQuery( node ).find( '*').addBack();
323                var all_strings = all_nodes.contents().filter(function(){
324                    if( this.nodeType === 3 && /\S/.test(this.nodeValue) ){
325                        if ( ! _this.skip_string(this) ){
326                            return this;
327                        }
328                    }});
329                if ( _this.is_editor ) {
330                    all_strings.wrap('<translate-press></translate-press>');
331                }
332                var all_strings_length = all_strings.length;
333                for (var j = 0; j < all_strings_length; j++ ) {
334                    if ( _this.trim( all_strings[j].textContent, _this.except_characters ) != '' ) {
335                        if ( ! _this.skip_string_original( all_strings[j].textContent, false )) {
336                            nodesInfo.push({node: all_strings[j], original: all_strings[j].textContent, attribute: ''});
337                            string_originals.push(all_strings[j].textContent)
338                            if ( _this.skip_string_from_auto_translation(all_strings[j])){
339                                skip_machine_translation.push( all_strings[j].textContent )
340                            }
341                            if (trp_data ['showdynamiccontentbeforetranslation'] == false) {
342                                all_strings[j].textContent = '';
343                            }
344                        }
345                    }
346                }
347            }
348        }
349        return { 'string_originals': string_originals, 'nodesInfo': nodesInfo, 'skip_machine_translation': skip_machine_translation  };
350    }
351
352    this.get_translateable_attributes = function ( node ) {
353        var nodesInfo = []
354        var string_originals = []
355        var skip_attr_machine_translation = [ 'href', 'src' ]
356        var skip_machine_translation = []
357        for (var trp_attribute_key in trp_data.trp_attributes_selectors) {
358            if (trp_data.trp_attributes_selectors.hasOwnProperty(trp_attribute_key)) {
359                var attribute_selector_item = trp_data.trp_attributes_selectors[trp_attribute_key]
360                if ( typeof attribute_selector_item['selector'] !== 'undefined' ){
361                    var all_nodes = jQuery( node ).find( attribute_selector_item.selector ).addBack( attribute_selector_item.selector )
362
363                    var all_nodes_length = all_nodes.length
364                    for (var j = 0; j < all_nodes_length; j++ ) {
365                        if ( _this.skip_string( all_nodes[j] ) || _this.skip_string_attribute( all_nodes[j], attribute_selector_item.accessor ) ){
366                            continue;
367                        }
368
369                        var attribute_content = all_nodes[j].getAttribute( attribute_selector_item.accessor )
370                        if ( _this.skip_string_original( attribute_content, attribute_selector_item.accessor )){
371                            continue;
372                        }
373                        if ( attribute_content && _this.trim( attribute_content.trim(), _this.except_characters ) != '' ) {
374                            nodesInfo.push({node: all_nodes[j], original: attribute_content, attribute: attribute_selector_item.accessor });
375                            string_originals.push( attribute_content )
376                            if ( trp_data ['showdynamiccontentbeforetranslation'] == false && ( attribute_selector_item.accessor != 'src' ) && ( attribute_selector_item.accessor != 'href' ) ) {
377                                all_nodes[j].setAttribute( attribute_selector_item.accessor, '' );
378                            }
379
380                            if ( _this.skip_string_from_auto_translation(all_nodes[j])){
381                                skip_machine_translation.push( attribute_content )
382                            }else{
383                                for ( var s = 0; s < skip_attr_machine_translation.length; s++ ) {
384                                    if ( attribute_selector_item.accessor === skip_attr_machine_translation[ s ] ){
385                                        skip_machine_translation.push( attribute_content )
386                                        break
387                                    }
388                                }
389                            }
390                        }
391                    }
392                }
393            }
394        }
395        return { 'string_originals': string_originals, 'nodesInfo': nodesInfo, 'skip_machine_translation': skip_machine_translation };
396    }
397
398    function get_string_from_node( node ){
399        if( node.nodeType === 3 && /\S/.test(node.nodeValue) ){
400            if ( ! _this.skip_string(node) ){
401                return node;
402            }
403        }
404    }
405
406    // cleans the gettext wrappers
407    this.cleanup_gettext_wrapper = function(){
408        jQuery('trp-gettext').contents().unwrap();
409    };
410
411    /**
412     * Update url with query string.
413     *
414     */
415    this.update_query_string = function(key, value, url) {
416        if (!url) return url;
417        if ( url.startsWith('#') ){
418            return url;
419        }
420        var re = new RegExp("([?&])" + key + "=.*?(&|#|$)(.*)", "gi"),
421            hash;
422
423        if (re.test(url)) {
424            if (typeof value !== 'undefined' && value !== null)
425                return url.replace(re, '$1' + key + "=" + value + '$2$3');
426            else {
427                hash = url.split('#');
428                url = hash[0].replace(re, '$1$3').replace(/(&|\?)$/, '');
429                if (typeof hash[1] !== 'undefined' && hash[1] !== null)
430                    url += '#' + hash[1];
431                return url;
432            }
433        }
434        else {
435            if (typeof value !== 'undefined' && value !== null ) {
436                var separator = url.indexOf('?') !== -1 ? '&' : '?';
437                hash = url.split('#');
438                url = hash[0] + separator + key + '=' + value;
439                if (typeof hash[1] !== 'undefined' && hash[1] !== null)
440                    url += '#' + hash[1];
441                return url;
442            }
443            else
444                return url;
445        }
446    };
447
448    /**
449     * Initialize and configure observer.
450     */
451    this.initialize = function() {
452        this.is_editor = (typeof window.parent.tpEditorApp !== 'undefined' )
453        if ( this.is_editor ) {
454            trp_data['gettranslationsnonceregular'] = window.parent.trp_dynamic_nonce;
455        }
456
457        current_language = trp_data.trp_current_language;
458        original_language = trp_data.trp_original_language;
459        language_to_query = trp_data.trp_language_to_query;
460        translate_numerals_opt = trp_data.trp_translate_numerals_opt;
461
462        if ( typeof translate_numerals_opt !== "undefined" && translate_numerals_opt !== '' && translate_numerals_opt === "yes") {
463            _this.except_characters = " \t\n\r � �.,/`~!@#$€£%^&*():;-_=+[]{}\\|?/<>'";
464        }
465
466        // create an observer instance
467        observer = new MutationObserver( _this.detect_new_strings_callback );
468
469        _this.resume_observer();
470
471        jQuery( document ).ajaxComplete(function( event, request, settings ) {
472            if( typeof window.parent.jQuery !== "undefined" && window.parent.jQuery('#trp-preview-iframe').length != 0 ) {
473                var settingsdata = "" + settings.data;
474                if( typeof settings.data == 'undefined' || jQuery.isEmptyObject( settings.data ) || settingsdata.indexOf('action=trp_') === -1 ) {
475                    window.parent.dispatchEvent( new Event( 'trp_iframe_page_updated' ) );
476                }
477            }
478        });
479
480        //try a final attempt at cleaning the gettext wrappers
481        _this.cleanup_gettext_wrapper();
482    };
483
484    /**
485     * Resume observing new strings added.
486     */
487    this.resume_observer = function(){
488        if ( language_to_query === '' ) {
489            // No translation languages are set other than default language
490            return;
491        }
492        observer.observe(document.body, observerConfig);
493    };
494
495    /**
496     * Pause observing new strings added.
497     */
498    this.pause_observer = function(){
499        /*
500        Disconnect will delete the existing mutations detected prior to being passed to callback function.
501        So we are calling takeRecords to save them first, disconnect, then detect the strings
502        Disconnect happens before detect_new_strings to avoid detecting mutations from within detect new strings functions
503         */
504        if ( language_to_query === '' ) {
505            // No translation languages are set other than default language
506            return;
507        }
508        var mutations = observer.takeRecords()
509        observer.disconnect()
510        if ( mutations.length > 0 ) {
511            _this.detect_new_strings(mutations)
512        }
513
514    };
515
516    this.trim = function (str, charlist) {
517        //  discuss at: http://locutus.io/php/trim/
518        // original by: Kevin van Zonneveld (http://kvz.io)
519        // improved by: mdsjack (http://www.mdsjack.bo.it)
520        // improved by: Alexander Ermolaev (http://snippets.dzone.com/user/AlexanderErmolaev)
521        // improved by: Kevin van Zonneveld (http://kvz.io)
522        // improved by: Steven Levithan (http://blog.stevenlevithan.com)
523        // improved by: Jack
524        //    input by: Erkekjetter
525        //    input by: DxGx
526        // bugfixed by: Onno Marsman (https://twitter.com/onnomarsman)
527        //   example 1: trim('    Kevin van Zonneveld    ')
528        //   returns 1: 'Kevin van Zonneveld'
529        //   example 2: trim('Hello World', 'Hdle')
530        //   returns 2: 'o Wor'
531        //   example 3: trim(16, 1)
532        //   returns 3: '6'
533        var whitespace = [
534            ' ',
535            '\n',
536            '\r',
537            '\t',
538            '\f',
539            '\x0b',
540            '\xa0',
541            '\u2000',
542            '\u2001',
543            '\u2002',
544            '\u2003',
545            '\u2004',
546            '\u2005',
547            '\u2006',
548            '\u2007',
549            '\u2008',
550            '\u2009',
551            '\u200a',
552            '\u200b',
553            '\u2028',
554            '\u2029',
555            '\u3000'
556        ].join('');
557        var l = 0;
558        var i = 0;
559        str += '';
560        if (charlist) {
561            whitespace += (charlist + '').replace(/([[\]().?/*{}+$^:])/g, '$1');
562        }
563        l = str.length;
564        for (i = 0; i < l; i++) {
565            if (whitespace.indexOf(str.charAt(i)) === -1) {
566                str = str.substring(i);
567                break;
568            }
569        }
570        l = str.length;
571        for (i = l - 1; i >= 0; i--) {
572            if (whitespace.indexOf(str.charAt(i)) === -1) {
573                str = str.substring(0, i + 1);
574                break;
575            }
576        }
577        return whitespace.indexOf(str.charAt(0)) === -1 ? str : '';
578    };
579
580    _this.initialize();
581}
582
583var trpTranslator;
584var current_language;
585var original_language;
586
587function trp_get_IE_version() {
588    var sAgent = window.navigator.userAgent;
589    var Idx = sAgent.indexOf("MSIE");
590
591    // If IE, return version number.
592    if (Idx > 0)
593        return parseInt(sAgent.substring(Idx+ 5, sAgent.indexOf(".", Idx)));
594
595    // If IE 11 then look for Updated user agent string.
596    else if (!!navigator.userAgent.match(/Trident\/7\./))
597        return 11;
598    else
599        return 0; //It is not IE
600}
601
602function trp_allow_detect_dom_changes_to_run(){
603    var IE_version = trp_get_IE_version();
604    if ( IE_version != 0 && IE_version <= 11 ){
605        return false;
606    }
607    return true;
608}
609
610
611// Initialize the Translate Press Editor when the script loads
612if ( trp_allow_detect_dom_changes_to_run() ) {
613    trpTranslator = new TRP_Translator();
614}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.