Czy skrypty można wstawiać za pomocą innerHTML?


223

Próbowałem załadować kilka skryptów do strony przy użyciu innerHTMLna zasadzie <div>. Wygląda na to, że skrypt ładuje się do DOM, ale nigdy nie jest wykonywany (przynajmniej w Firefox i Chrome). Czy istnieje sposób na wykonanie skryptów podczas ich wstawianiainnerHTML ?

Przykładowy kod:

<!DOCTYPE html>
<html>
  <body onload="document.getElementById('loader').innerHTML = '<script>alert(\'hi\')<\/script>'">
    Shouldn't an alert saying 'hi' appear?
    <div id="loader"></div>
  </body>
</html>

Odpowiedzi:


88

Musisz użyć eval () aby wykonać dowolny kod skryptu wstawiony jako tekst DOM.

MooTools zrobi to za Ciebie automatycznie i jestem pewien, że jQuery też (w zależności od wersji. Używa jQuery w wersji 1.6+ eval). Oszczędza to wielu kłopotów z analizowaniem <script>tagów i uciekaniem przed zawartością, a także z wieloma innymi „gotchas”.

Ogólnie rzecz biorąc, jeśli idziesz do eval()niego sam, chcesz utworzyć / wysłać kod skryptu bez żadnych znaczników HTML, takich jak <script>, ponieważ nie będą one eval()poprawnie.


12
To, co naprawdę chcę zrobić, to załadować zewnętrzny skrypt, a nie tylko ewaluować jakiś skrypt lokalny. Dodanie znacznika skryptu za pomocą innerHTML jest znacznie krótsze niż utworzenie skryptu elementu DOM i dodanie go do treści, a ja staram się, aby mój kod był jak najkrótszy. Czy musisz tworzyć elementy skryptu dom i dodawać je do dom, a nie tylko za pomocą czegoś takiego jak innerHTML? Czy można to zrobić za pomocą document.write w obrębie funkcji?
Craig

5
Jak sugeruje zombat, użyj frameworka JavaScript do załadowania zewnętrznego skryptu, nie próbuj ponownie wymyślać koła. JQuery czyni to niezwykle łatwym, wystarczy dołączyć JQuery i wywołać: $ .getScript (url). Możesz także podać funkcję zwrotną, która zostanie wykonana po załadowaniu skryptu.
Ariel Popovsky

2
Ariel ma rację. Doceniam staranie się, aby kod był krótki, a dodawanie <script>tagu innerHTMLmoże być krótkie, ale to nie działa. To wszystko jest zwykłym tekstem, dopóki nie zostanie wyświetlone eval(). I niestety eval()nie analizuje tagów HTML, więc powstaje szereg problemów.
zombat

21
eval () nie jest świetnym rozwiązaniem żadnego problemu.
buley

2
Sam próbowałem eval (). To okropny pomysł. Musisz ewaluować wszystko KAŻDY CZAS . Nawet jeśli zadeklarujesz nazwę i wartość zmiennej, musisz ją ponownie zadeklarować / re-eval () za każdym razem, aby działała. To koszmar błędów.
Youstay Igo

88

Oto bardzo interesujące rozwiązanie twojego problemu: http://24ways.org/2005/have-your-dom-and-script-it-too

Więc użyj tego zamiast tagów skryptu:

<img src="empty.gif" onload="alert('test');this.parentNode.removeChild(this);" />


12
To jest genialne!
confile

Nie działa dla mnie, gdy zostanie wstawiony do wyniku żądania ajax: brakuje błędu składniowego; przed instrukcją na początku ciągu skryptu
Oliver

W jaki sposób ludzie dodają wiersz & lt; img src ... do kodu strony? Czy używasz do tego document.write () czy korzystasz z metody document.body.innerHTML + =? Oba mi się nie
udają

Niezbyt praktyczne jest pisanie dużej ilości kodu w onloadatrybucie. Wymaga to również dodatkowego pliku, aby istniał i został załadowany. Rozwiązanie momo jest mniejszym kompromisem.
fregante

15
Możesz Base64 zakodować obraz wyzwalacza jako <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">(to nie wykona żądania sieciowego) Właściwie ... NIE potrzebujesz obrazu, odwołaj się do nieistniejącego obrazu i zamiast tego onloadużyj onerror(ale spowoduje to żądanie sieciowe)
Danny „365CSI” Engelman

83

Oto metoda, która rekurencyjnie zastępuje wszystkie skrypty skryptami wykonywalnymi:

function nodeScriptReplace(node) {
        if ( nodeScriptIs(node) === true ) {
                node.parentNode.replaceChild( nodeScriptClone(node) , node );
        }
        else {
                var i        = 0;
                var children = node.childNodes;
                while ( i < children.length ) {
                        nodeScriptReplace( children[i++] );
                }
        }

        return node;
}
function nodeScriptIs(node) {
        return node.tagName === 'SCRIPT';
}
function nodeScriptClone(node){
        var script  = document.createElement("script");
        script.text = node.innerHTML;
        for( var i = node.attributes.length-1; i >= 0; i-- ) {
                script.setAttribute( node.attributes[i].name, node.attributes[i].value );
        }
        return script;
}

Przykładowe wywołanie:

nodeScriptReplace(document.getElementsByTagName("body")[0]);

9
Jestem trochę zaskoczony, że twoja odpowiedź jest na wyczerpaniu. IMHO, jest to najlepsze rozwiązanie, ta metoda pozwala nawet ograniczyć skrypty z określonymi adresami URL lub treścią.
davidmh

1
@ inf3rno robi czy nie? Kiedyś działało, czy ktokolwiek twierdził kiedyś coś innego?
mmm,

jakie są cele [0]? czy możesz użyć nodeScriptReplace (document.getElementById (). html);
Bao Thai

@BaoThai Tak. Możesz.
mmm

Wydaje się, że nie pomaga w IWebBrowser2; Mogę potwierdzić, że znaczniki skryptu zostały odtworzone za pomocą createElement, ale nadal nie mogę ich wywołać za pomocą InvokeScript ().
Dave

46

Możesz utworzyć skrypt, a następnie wstrzyknąć zawartość.

var g = document.createElement('script');
var s = document.getElementsByTagName('script')[0];
g.text = "alert(\"hi\");"
s.parentNode.insertBefore(g, s);

Działa to we wszystkich przeglądarkach :)


1
Chyba że w dokumencie nie ma żadnych innych elementów skryptu. Użyj document.documentElementzamiast tego.
Eli Gray,

4
Nie jest konieczne, ponieważ piszesz skrypt z innego skryptu. <script> var g = document.createElement('script'); var s = document.getElementsByTagName('script')[0]; //reference this script g.text = "alert(\"hi\");" s.parentNode.insertBefore(g, s); </script>
Pablo Moretti

3
Kto powiedział, że pochodzi z innego skryptu? Możesz uruchomić JavaScript bez <script>elementów. Np <img onerror="..." src="#">a <body onload="...">. Jeśli chcesz być techniczny, nie będzie to działać w dokumentach innych niż HTML / SVG z powodu niejawnej przestrzeni nazw.
Eli Gray,

2
Facebook używa odpowiedzi Pablo w swoim SDK. developers.facebook.com/docs/javascript/quickstart/v2.2#loading
geoyws

30

Użyłem tego kodu, działa dobrze

var arr = MyDiv.getElementsByTagName('script')
for (var n = 0; n < arr.length; n++)
    eval(arr[n].innerHTML)//run script inside div

1
Dzięki. Naprawiono problem z dodaniem kodu Disqus Universal do modalnego wyskakującego okienka utworzonego za pomocą wtyczki Jiny TinyBox2.
gsinha,

3
Niestety to rozwiązanie nie działa, gdy skrypt zawiera funkcje, które zostaną później wywołane.
Jose Gómez

7

Miałem ten problem z innerHTML, musiałem dołączyć skrypt Hotjar do tagu „head” mojej aplikacji Reactjs i musiałby on zostać wykonany zaraz po dołączeniu.

Jednym z dobrych rozwiązań dla dynamicznego importu węzła do znacznika „head” jest moduł React-helment .


Istnieje również przydatne rozwiązanie proponowanego problemu:

Brak tagów skryptowych w innerHTML!

Okazuje się, że HTML5 nie pozwala na dynamiczne dodawanie znaczników skryptu za pomocą właściwości innerHTML. Tak więc poniższe polecenia nie zostaną wykonane i nie będzie ostrzeżenia o treści Hello World!

element.innerHTML = "<script>alert('Hello World!')</script>";

Jest to udokumentowane w specyfikacji HTML5:

Uwaga: elementy skryptu wstawione przy użyciu innerHTML nie są wykonywane po ich wstawieniu.

Ale uwaga, nie oznacza to, że innerHTML jest bezpieczny przed skryptami krzyżowymi. Możliwe jest wykonanie JavaScript za pośrednictwem innerHTML bez użycia tagów, jak pokazano na stronie innerHTML MDN .

Rozwiązanie: Dynamiczne dodawanie skryptów

Aby dynamicznie dodać znacznik skryptu, musisz utworzyć nowy element skryptu i dołączyć go do elementu docelowego.

Możesz to zrobić dla zewnętrznych skryptów:

var newScript = document.createElement("script");
newScript.src = "http://www.example.com/my-script.js";
target.appendChild(newScript);

I wbudowane skrypty:

var newScript = document.createElement("script");
var inlineScript = document.createTextNode("alert('Hello World!');");
newScript.appendChild(inlineScript); 
target.appendChild(newScript);

5

Dla każdego, kto wciąż próbuje to zrobić, nie, nie możesz wstrzyknąć skryptu za pomocą innerHTML, ale można załadować ciąg do znacznika skryptu za pomocą Bloba URL.createObjectURL.

Stworzyłem przykład, który pozwala uruchomić ciąg znaków jako skrypt i uzyskać zwrot eksportu tego skryptu poprzez obietnicę:

function loadScript(scriptContent, moduleId) {
    // create the script tag
    var scriptElement = document.createElement('SCRIPT');

    // create a promise which will resolve to the script's 'exports'
    // (i.e., the value returned by the script)
    var promise = new Promise(function(resolve) {
        scriptElement.onload = function() {
            var exports = window["__loadScript_exports_" + moduleId];
            delete window["__loadScript_exports_" + moduleId];
            resolve(exports);
        }
    });

    // wrap the script contents to expose exports through a special property
    // the promise will access the exports this way
    var wrappedScriptContent =
        "(function() { window['__loadScript_exports_" + moduleId + "'] = " + 
        scriptContent + "})()";

    // create a blob from the wrapped script content
    var scriptBlob = new Blob([wrappedScriptContent], {type: 'text/javascript'});

    // set the id attribute
    scriptElement.id = "__loadScript_module_" + moduleId;

    // set the src attribute to the blob's object url 
    // (this is the part that makes it work)
    scriptElement.src = URL.createObjectURL(scriptBlob);

    // append the script element
    document.body.appendChild(scriptElement);

    // return the promise, which will resolve to the script's exports
    return promise;
}

...

function doTheThing() {
    // no evals
    loadScript('5 + 5').then(function(exports) {
         // should log 10
        console.log(exports)
    });
}

Uprościłem to z mojej faktycznej implementacji, więc nie obiecuję, że nie ma w tym żadnych błędów. Ale zasada działa.

Jeśli nie chcesz odzyskać żadnej wartości po uruchomieniu skryptu, jest to jeszcze łatwiejsze; po prostu pominąć Promisei onloadbity. Nie musisz nawet owijać skryptu ani tworzyć window.__load_script_exports_właściwości globalnej .


1
Właśnie próbowałem i działa na chrome 57. innerHTML na znaczniku skryptu wykonuje tekst.
iPherian

To ciekawe, wcześniej nie działało. Zastanawiam się, czy to zachowanie dotyczy różnych przeglądarek, czy tylko Chrome 57.
JayArby

4

Oto funkcja rekurencyjna, aby ustawić innerHTML elementu, którego używam na naszym serwerze reklam:

// o: container to set the innerHTML
// html: html text to set.
// clear: if true, the container is cleared first (children removed)
function setHTML(o, html, clear) {
    if (clear) o.innerHTML = "";

    // Generate a parseable object with the html:
    var dv = document.createElement("div");
    dv.innerHTML = html;

    // Handle edge case where innerHTML contains no tags, just text:
    if (dv.children.length===0){ o.innerHTML = html; return; }

    for (var i = 0; i < dv.children.length; i++) {
        var c = dv.children[i];

        // n: new node with the same type as c
        var n = document.createElement(c.nodeName);

        // copy all attributes from c to n
        for (var j = 0; j < c.attributes.length; j++)
            n.setAttribute(c.attributes[j].nodeName, c.attributes[j].nodeValue);

        // If current node is a leaf, just copy the appropriate property (text or innerHTML)
        if (c.children.length == 0)
        {
            switch (c.nodeName)
            {
                case "SCRIPT":
                    if (c.text) n.text = c.text;
                    break;
                default:
                    if (c.innerHTML) n.innerHTML = c.innerHTML;
                    break;
            }
        }
        // If current node has sub nodes, call itself recursively:
        else setHTML(n, c.innerHTML, false);
        o.appendChild(n);
    }
}

Można zobaczyć demo tutaj .


3

Możesz to zrobić w następujący sposób:

var mydiv = document.getElementById("mydiv");
var content = "<script>alert(\"hi\");<\/script>";

mydiv.innerHTML = content;
var scripts = mydiv.getElementsByTagName("script");
for (var i = 0; i < scripts.length; i++) {
    eval(scripts[i].innerText);
}

3

Tutaj rozwiązanie, które nie używa evali działa ze skryptami , połączonymi skryptami , a także z modułami .

Funkcja przyjmuje 3 parametry:

  • html : Ciąg z kodem HTML do wstawienia
  • dest : odniesienie do elementu docelowego
  • append : flaga boolean, aby umożliwić dołączanie na końcu elementu docelowego HTML
function insertHTML(html, dest, append=false){
    // if no append is requested, clear the target element
    if(!append) dest.innerHTML = '';
    // create a temporary container and insert provided HTML code
    let container = document.createElement('div');
    container.innerHTML = html;
    // cache a reference to all the scripts in the container
    let scripts = container.querySelectorAll('script');
    // get all child elements and clone them in the target element
    let nodes = container.childNodes;
    for( let i=0; i< nodes.length; i++) dest.appendChild( nodes[i].cloneNode(true) );
    // force the found scripts to execute...
    for( let i=0; i< scripts.length; i++){
        let script = document.createElement('script');
        script.type = scripts[i].type || 'text/javascript';
        if( scripts[i].hasAttribute('src') ) script.src = scripts[i].src;
        script.innerHTML = scripts[i].innerHTML;
        document.head.appendChild(script);
        document.head.removeChild(script);
    }
    // done!
    return true;
}

Mam na myśli ... Dołączanie znacznika skryptu z zawartością kodu jest ewaluacją, prawda?
Kevin B,

@KevinB Istnieją znane różnice ... spróbuj, eval('console.log(this)')a zobaczysz najbardziej oczywistą
colxi

więc kontekst jest inny i? to wciąż tylko ewaluacja.
Kevin B

@KevinB Nie, to nie jest ewaluacja. Spróbuj tego eval('let b=100')... a potem spróbuj uzyskać dostęp bspoza eval .... powodzenia, będziesz go potrzebować
colxi

Pracuje dla mnie. Pozdrawiam
Bezzzo

1

Krasimir Tsonev ma świetne rozwiązanie, które rozwiązuje wszystkie problemy. Jego metoda nie wymaga użycia eval, więc nie występują problemy z wydajnością ani bezpieczeństwem. Pozwala ci ustawić ciąg innerHTML zawierający html z js i natychmiast przetłumaczyć go na element DOM, jednocześnie wykonując części js znajdujące się wzdłuż kodu. krótki, prosty i działa dokładnie tak, jak chcesz.

Ciesz się jego rozwiązaniem:

http://krasimirtsonev.com/blog/article/Convert-HTML-string-to-DOM-element

Ważne notatki:

  1. Musisz owinąć element docelowy znacznikiem div
  2. Musisz owinąć ciąg src znacznikiem div.
  3. Jeśli piszesz ciąg src bezpośrednio i zawiera on części js, zwróć uwagę, aby poprawnie napisać zamykające znaczniki skryptu (z \ przed /), ponieważ jest to ciąg.

1

Użyj $(parent).html(code)zamiast parent.innerHTML = code.

Poniżej naprawiono także używane document.writeskrypty i skrypty ładowane za pomocą srcatrybutu. Niestety nawet to nie działa ze skryptami Google AdSense.

var oldDocumentWrite = document.write;
var oldDocumentWriteln = document.writeln;
try {
    document.write = function(code) {
        $(parent).append(code);
    }
    document.writeln = function(code) {
        document.write(code + "<br/>");
    }
    $(parent).html(html); 
} finally {
    $(window).load(function() {
        document.write = oldDocumentWrite
        document.writeln = oldDocumentWriteln
    })
}

Źródło


1
nieco tutaj późno, ale każdy, kto może korzystać z tej metody, zwróć uwagę, że w JQuery musisz załadować swoje skrypty za pomocą $ .loadScript (url) zamiast <script src = "url> </script> - ten ostatni spowoduje przestarzały błąd synchronicznego XMLHttpRequest w przeglądarkach
Stavm,

1

Spróbuj użyć szablonu i document.importNode. Oto przykład:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Sample</title>
</head>
<body>
<h1 id="hello_world">Sample</h1>
<script type="text/javascript">
 var div = document.createElement("div");
  var t = document.createElement('template');
  t.innerHTML =  "Check Console tab for javascript output: Hello world!!!<br/><script type='text/javascript' >console.log('Hello world!!!');<\/script>";
  
  for (var i=0; i < t.content.childNodes.length; i++){
    var node = document.importNode(t.content.childNodes[i], true);
    div.appendChild(node);
  }
 document.body.appendChild(div);
</script>
 
</body>
</html>


1
To nie działa z Microsoft Edge, jakieś inne obejście?
Dusza

1

Możesz również owinąć swój w <script>ten sposób, a zostanie on wykonany:

<your target node>.innerHTML = '<iframe srcdoc="<script>alert(top.document.title);</script>"></iframe>';

Uwaga: zakres wewnątrz srcdocodnosi się do elementu iframe, więc topaby uzyskać dostęp do dokumentu nadrzędnego, należy użyć polecenia z powyższego przykładu.


1

Robię to za każdym razem, gdy chcę dynamicznie wstawiać znacznik skryptu!

  const html =
    `<script>
        alert('👋 there ! Wanna grab a 🍺'); 
    </script>`;

  const scriptEl = document.createRange().createContextualFragment(html);
  parent.append(scriptEl);

UWAGA: zastosowano ES6

EDYCJA 1: Wyjaśnienie dla was - widziałem wiele używanych odpowiedzi appendChildi chciałem poinformować was, że działa dokładnie tak, jakappend


0

Tak, możesz, ale musisz to zrobić poza DOM, a zamówienie musi być prawidłowe.

var scr = '<scr'+'ipt>alert("foo")</scr'+'ipt>';
window.onload = function(){
    var n = document.createElement("div");
    n.innerHTML = scr;
    document.body.appendChild(n);
}

... zaalarmuje „foo”. To nie zadziała:

document.getElementById("myDiv").innerHTML = scr;

I nawet to nie zadziała, ponieważ węzeł jest wstawiany jako pierwszy:

var scr = '<scr'+'ipt>alert("foo")</scr'+'ipt>';
window.onload = function(){
    var n = document.createElement("div");
    document.body.appendChild(n);
    n.innerHTML = scr;  
}

16
Za to, co jest warte: wydaje się, że nie działa w obecnych przeglądarkach.
Wichert Akkerman

0

Moim rozwiązaniem tego problemu jest ustawienie Mutation Observer do wykrywania <script></script>węzłów, a następnie zastąpienie go nowym <script></script>węzłem z tym samym kodem SRC. Na przykład:

let parentNode = /* node to observe */ void 0
let observer = new MutationObserver(mutations=>{
    mutations.map(mutation=>{
        Array.from(mutation.addedNodes).map(node=>{
            if ( node.parentNode == parentNode ) {
                let scripts = node.getElementsByTagName('script')
                Array.from(scripts).map(script=>{
                    let src = script.src
                    script = document.createElement('script')
                    script.src = src
                    return script
                })
            }
        })
    })
})
observer.observe(document.body, {childList: true, subtree: true});

1
Dzięki, że oddałeś mi głos bez podania przyczyny. Kocham was wszystkich.
gabriel garcia

0

Wspomnienie Gabriela Garcii o MutationObservers jest na dobrej drodze, ale nie całkiem dla mnie zadziałało. Nie jestem pewien, czy było to spowodowane dziwactwem przeglądarki, czy też błędem po mojej stronie, ale wersja, która ostatecznie dla mnie działała, była następująca:

document.addEventListener("DOMContentLoaded", function(event) {
    var observer = new MutationObserver(mutations=>{
        mutations.map(mutation=>{
            Array.from(mutation.addedNodes).map(node=>{
                if (node.tagName === "SCRIPT") {
                    var s = document.createElement("script");
                    s.text=node.text;
                    if (typeof(node.parentElement.added) === 'undefined')
                        node.parentElement.added = [];
                    node.parentElement.added[node.parentElement.added.length] = s;
                    node.parentElement.removeChild(node);
                    document.head.appendChild(s);
                }
            })
        })
    })
    observer.observe(document.getElementById("element_to_watch"), {childList: true, subtree: true,attributes: false});
};

Oczywiście powinieneś zastąpić element_to_watchnazwą modyfikowanego elementu.

node.parentElement.addedsłuży do przechowywania dodawanych tagów skryptu document.head. W funkcji używanej do wczytywania strony zewnętrznej możesz użyć czegoś takiego jak poniżej, aby usunąć niepotrzebne tagi skryptu:

function freeScripts(node){
    if (node === null)
        return;
    if (typeof(node.added) === 'object') {
        for (var script in node.added) {
            document.head.removeChild(node.added[script]);
        }
        node.added = {};
    }
    for (var child in node.children) {
        freeScripts(node.children[child]);
    }
}

I przykład początku funkcji ładowania:

function load(url, id, replace) {
    if (document.getElementById(id) === null) {
        console.error("Element of ID "+id + " does not exist!");
        return;
    }
    freeScripts(document.getElementById(id));
    var xhttp = new XMLHttpRequest();
    // proceed to load in the page and modify innerHTML
}

Zauważasz, że dodajesz nowy za MutationObserverkażdym razem, gdy element jest dodawany do dokumentu, prawda? Przy okazji, zastanawiam się, dlaczego mówisz, że mój kod nie działa.
gabriel garcia

@ gabrielgarcia Powiedziałem, że twój kod nie działa, ponieważ go wypróbowałem i po prostu nie działał. Patrząc na to teraz, jest całkiem możliwe, że to na mnie, a nie na tobie, szczerze przepraszam za sposób, w jaki to sformułowałem. Napraw to teraz.
pixelherodev

re: dodawanie MutationObserver za każdym razem, gdy element jest dodawany do dokumentu, o czym ty mówisz? DOMContentLoaded, cytuję tutaj z MDN, „uruchamia się, gdy początkowy dokument HTML został całkowicie załadowany i przeanalizowany, bez oczekiwania na zakończenie ładowania arkuszy stylów, obrazów i podramek”. To raz i tylko raz. Co więcej, ten skrypt działa bez problemów na mojej stronie, a debugowanie pokazuje, że dzieje się to tylko raz, więc zarówno w praktyce, jak i w teorii.
pixelherodev

1
masz rację ... tęskniłem za tym. Przepraszam również.
gabriel garcia

@ gabrielgarcia Nie ma problemu :)
pixelherodev

0

Dla mnie najlepszym sposobem jest wstawienie nowej zawartości HTML przez innerHtml, a następnie użycie

setTimeout(() => {
        var script_el = document.createElement("script")
        script_el.src = 'script-to-add.js'
        document.body.appendChild(script_el)
    }, 500)

SetTimeout nie jest wymagany, ale działa lepiej. To zadziałało dla mnie.


-1

Wykonaj (Java Script) znacznik z innerHTML

Zamień element skryptu na div mający atrybut class class = "javascript" i zamknij go </div>

Nie zmieniaj treści, którą chcesz uruchomić (wcześniej była w znaczniku skryptowym, a teraz w znaczniku div)

Dodaj styl na swojej stronie ...

<style type="text/css"> .javascript { display: none; } </style>

Teraz uruchom eval za pomocą jquery (Jquery js powinno być już uwzględnione)

   $('.javascript').each(function() {
      eval($(this).text());

    });`

Możesz odkryć więcej tutaj , na moim blogu.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.