PageSourceSearch

https://www.elvisthemusic.com/wp-content/plugins/translatepress-mu…/js/trp-translate-dom-changes.js?ver=3.3.5

js elvisthemusic.com collected 2026-09-24 18:01:24 UTC 26,967 bytes, 622 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                   : ( _this.is_editor ) ? 'trp_get_translations_regular' : 'trp_get_translations_domchanges',
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        if ( trp_data['showdynamiccontentbeforetranslation'] === true ){
468            observer = new MutationObserver(mutations => {
469                setTimeout(() => _this.detect_new_strings_callback(mutations), 0);
470            });
471        }
472
473        else {
474            observer = observer = new MutationObserver(_this.detect_new_strings_callback)
475        }
476
477        _this.resume_observer();
478
479        jQuery( document ).ajaxComplete(function( event, request, settings ) {
480            if( typeof window.parent.jQuery !== "undefined" && window.parent.jQuery('#trp-preview-iframe').length != 0 ) {
481                var settingsdata = "" + settings.data;
482                if( typeof settings.data == 'undefined' || jQuery.isEmptyObject( settings.data ) || settingsdata.indexOf('action=trp_') === -1 ) {
483                    window.parent.dispatchEvent( new Event( 'trp_iframe_page_updated' ) );
484                }
485            }
486        });
487
488        //try a final attempt at cleaning the gettext wrappers
489        _this.cleanup_gettext_wrapper();
490    };
491
492    /**
493     * Resume observing new strings added.
494     */
495    this.resume_observer = function(){
496        if ( language_to_query === '' ) {
497            // No translation languages are set other than default language
498            return;
499        }
500        observer.observe(document.body, observerConfig);
501    };
502
503    /**
504     * Pause observing new strings added.
505     */
506    this.pause_observer = function(){
507        /*
508        Disconnect will delete the existing mutations detected prior to being passed to callback function.
509        So we are calling takeRecords to save them first, disconnect, then detect the strings
510        Disconnect happens before detect_new_strings to avoid detecting mutations from within detect new strings functions
511         */
512        if ( language_to_query === '' ) {
513            // No translation languages are set other than default language
514            return;
515        }
516        var mutations = observer.takeRecords()
517        observer.disconnect()
518        if ( mutations.length > 0 ) {
519            _this.detect_new_strings(mutations)
520        }
521
522    };
523
524    this.trim = function (str, charlist) {
525        //  discuss at: http://locutus.io/php/trim/
526        // original by: Kevin van Zonneveld (http://kvz.io)
527        // improved by: mdsjack (http://www.mdsjack.bo.it)
528        // improved by: Alexander Ermolaev (http://snippets.dzone.com/user/AlexanderErmolaev)
529        // improved by: Kevin van Zonneveld (http://kvz.io)
530        // improved by: Steven Levithan (http://blog.stevenlevithan.com)
531        // improved by: Jack
532        //    input by: Erkekjetter
533        //    input by: DxGx
534        // bugfixed by: Onno Marsman (https://twitter.com/onnomarsman)
535        //   example 1: trim('    Kevin van Zonneveld    ')
536        //   returns 1: 'Kevin van Zonneveld'
537        //   example 2: trim('Hello World', 'Hdle')
538        //   returns 2: 'o Wor'
539        //   example 3: trim(16, 1)
540        //   returns 3: '6'
541        var whitespace = [
542            ' ',
543            '\n',
544            '\r',
545            '\t',
546            '\f',
547            '\x0b',
548            '\xa0',
549            '\u2000',
550            '\u2001',
551            '\u2002',
552            '\u2003',
553            '\u2004',
554            '\u2005',
555            '\u2006',
556            '\u2007',
557            '\u2008',
558            '\u2009',
559            '\u200a',
560            '\u200b',
561            '\u2028',
562            '\u2029',
563            '\u3000'
564        ].join('');
565        var l = 0;
566        var i = 0;
567        str += '';
568        if (charlist) {
569            whitespace += (charlist + '').replace(/([[\]().?/*{}+$^:])/g, '$1');
570        }
571        l = str.length;
572        for (i = 0; i < l; i++) {
573            if (whitespace.indexOf(str.charAt(i)) === -1) {
574                str = str.substring(i);
575                break;
576            }
577        }
578        l = str.length;
579        for (i = l - 1; i >= 0; i--) {
580            if (whitespace.indexOf(str.charAt(i)) === -1) {
581                str = str.substring(0, i + 1);
582                break;
583            }
584        }
585        return whitespace.indexOf(str.charAt(0)) === -1 ? str : '';
586    };
587
588    _this.initialize();
589}
590
591var trpTranslator;
592var current_language;
593var original_language;
594
595function trp_get_IE_version() {
596    var sAgent = window.navigator.userAgent;
597    var Idx = sAgent.indexOf("MSIE");
598
599    // If IE, return version number.
600    if (Idx > 0)
601        return parseInt(sAgent.substring(Idx+ 5, sAgent.indexOf(".", Idx)));
602
603    // If IE 11 then look for Updated user agent string.
604    else if (!!navigator.userAgent.match(/Trident\/7\./))
605        return 11;
606    else
607        return 0; //It is not IE
608}
609
610function trp_allow_detect_dom_changes_to_run(){
611    var IE_version = trp_get_IE_version();
612    if ( IE_version != 0 && IE_version <= 11 ){
613        return false;
614    }
615    return true;
616}
617
618
619// Initialize the Translate Press Editor when the script loads
620if ( trp_allow_detect_dom_changes_to_run() ) {
621    trpTranslator = new TRP_Translator();
622}

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.