Přiřazení skriptu stylem – behavior a HTC
Donedávna bylo možno pomocí kaskádových stylů ovlivnit pouze vzhled, ale už ne reakci na uživatelské události. V HTML zdroji očištěném od formátovacího balastu tak zůstávaly ještě skripty, které do logické struktury dokumentu nepatří. V tomto výjimečně obsáhlém příspěvku vám představíme nepříliš známou CSS vlastnost „behavior“, která deklaruje chování HTML prvků a skripty odsouvá do externích souborů.
CSS vlastnost behavior
se dá přeložit doslova jako „chování“. Přináší nový pohled na implementaci JavaScriptu do HTML stránek. Pokud neumíte JavaScript, nebudete umět zpočátku používat ani behavior
. To ovšem neznamená, že pro začátečníky není užitečný, protože odněkud okopírovaný behavior
se do stránek zapracovává snadněji než odněkud okopírovaný JavaScript.
Hlavní přínos stylového chování zaznamenají zejména webdesignéři, kteří chtějí své úsporné, logické a čisté kódy obohatit o uživatelský komfort bez ztráty kytičky. Je to taková „vyšší dívčí“ JavaScriptu a kaskádových stylů. Kdo tedy neovládá základy zápisu kaskádových stylů (CSS), neoceňuje jejich výhody a barvu písma zadává stále elementem <font>
, toho behavior
rozhodně nenadchne.
Vývoj umístění informací ve webových dokumentech. Před příchodem CSS, využití CSS a důsledné oddělení obsahu pomocí behavior.
Vlastnost behavior
je podporována v Microsoft Internet Exploreru od verze 5.0 a v Netscape 6, tedy v Mozille (o ladění pro Mozillu se zmíním níže). Úspěšně zasáhne asi 95 % aktuální internetové populace, přičemž skóre se časem bude jenom zlepšovat. V prohlížečích, které behavior
nepodporují (čtyřkové verze, Opera), navíc nebude házet žádné chyby. Technologie patří do standardů W3C a je na ní vidět, že byla vymýšlena pro XML dokumenty, čili výhledy jsou slibné.
Jak behavior
zhruba funguje
V klasickém JavaScriptu se do každého HTML prvku, který má něco dělat (například zobrazit hlášku při kliknutí myší), musel připsat ovladač (například onclick="..."
pro kliknutí), jehož hodnotou byl skript. To je nešikovné zejména v případě, že má prvek ovladačů víc (například onmouseover
a onmouseout
pro rollover efekty) a na stránce se často opakují ve stejné kombinaci (dynamické menu, obrázková galerie a podobně). Navíc informační hodnota skutečnosti, že element má něco udělat například při najetí myší, je téměř nulová, neměla by tedy patřit do proudu hlavního dokumentu.
Při použití behavior
stačí HTML prvku nějakým způsobem přiřadit styl. V zápisu stylu se potom uvede adresa na externí soubor s příponou *.htc. Zkratka HTC znamená HTml Component (doporučuji nezaměňovat s THC, což je TetraHydroCanabinol). Takzvaný HTC soubor obsahuje popis chování prvku včetně potřebných skriptů. Základní zápis je:
selektor {behavior: url(‚soubor.htc‘)}
Takto lze ovlivnit mnoho prvků stránky. Samozřejmě fungují všechny klasické zápisy stylů pomocí tříd (class
) a identifikátorů (id
), styl se může dát do externího souboru. O zápisu CSS stylů už se na Intervalu napsalo mnoho. Chování lze připsat také přímo pro jeden konkrétní prvek (což je poněkud méně elegantní, nicméně funkční):
<tag style=“behavior: url(‚soubor.htc‘)“>
Pokud jste dočetli až sem, rád bych vás nějak povzbudil. Dále budu totiž popisovat techniky použití behavior
a syntaxi HTC komponent, což nemůže být moc zábavné čtení. Osobně se domnívám, že studium je na pár hodin nebo dnů. Až vás bude nudit, proklikejte si alespoň příklady, měli byste z nich hodně pochopit. Byl bych rád, kdybyste si odnesli poznatek, že dynamické HTML se dá dělat úsporně, modulárně a bez „zkažení“ HTML kódu.
Příklad první – výstražná hláška
Budu chtít, aby se mi po kliknutí na HTML element zobrazila výstražná hláška. V praxi není moc použitelný pro svou přílišnou jednoduchost, jde však o oblíbený příklad v učebnicích JavaScriptu. Pro přehlednost napřed zopakuji, jak se to dělá klasicky v JavaScriptu, a pak stejnou funkčnost rozběhám pomocí behavior
.
V JavaScriptu se pro vyvolání hlášky používá metoda alert()
. V tomto případě se hláška zobrazí po kliknutí na odstavec (element <p>
):
<p onclick=“alert(‚Ukázka události onclick‘)“>Odstavec s atributem onclick.</p>
Viz příklad výstražná hláška pomocí JavaScriptu.
Pomocí CSS behavior
vypadá zápis téže věci jinak:
<p style=“behavior: url(‚alert.htc‘)“>Odstavec se stylem behavior.</p>
Styl se v tomto případě odvolává na soubor alert.htc, ve kterém je teprve napsáno, co se má při jaké události stát:
<component>
<attach event=“onclick“ onevent=“hlaska()“>
<script>
function hlaska()
{
window.alert(„Ukázka vazby skriptu pomocí behavior“);
}
</script>
</component>
Viz příklad výstražná hláška pomocí behavior.
Druhý řádek s textem <attach...
říká, že při události kliknutí (event="onclick"
) se má zavolat funkce hlaska()
. Ta je potom klasicky definována ve skriptu. V tomto případě je funkce relativně jednoduchá, pouze jeden alert()
.
Příklad dvou různých zápisů měl pouze naznačit filosofii techniky behavior
a srovnat ji s klasickým JavaScriptem. Možná jste si všimli, že zápis a rozběhání konstrukce pomocí behavior
je trochu složitější než JavaScript. Proč propaguji metodu, která se jeví neefektivní?
Vtip je v tom, že první příklad byl extrémně jednoduchý. Při složitějších skriptech a vazbách na dokument efektivita stylového zápisu výrazně stoupá, kódy se naopak v porovnání s JavaScriptem zjednodušují. To se pokusím dokázat v dalších příkladech. Pravidlo zní – čím je skript složitější a čím častěji se bude používat, tím více se vyplatí napsat HTC komponentu a používat behavior
.
Příklad druhý – změna pozadí buňky
Častým dotazem začínajících autorů je, jak udělat buňku tabulky, která po najetí myší změní barvu, třeba ze žluté na červenou (u buňky nelze použít :hover
jako u odkazu). V JavaScriptu se například dvě takové buňky dají udělat poněkud obšírnějším zápisem (včetně stylu):
<style>
td {background-color: yellow}
</style>
…
<td onmouseover=“this.style.backgroundColor = ‚red'“ onmouseout=“this.style.backgroundColor = ‚yellow'“>Buňka měnící
barvu</td> <td onmouseover=“this.style.backgroundColor = ‚red'“ onmouseout=“this.style.backgroundColor = ‚yellow'“>Druhá buňka měnící barvu</td>
Viz příklad podbarvení buňky pomocí JavaScriptu.
Buňka tabulky (td
) při přejetí myší (onmouseover
) zaznamená, že tento objekt (this
, tedy buňka) má nastavit styl, konkrétně barvu pozadí (background-color
) na hodnotu červená (red
). Totéž do žluta při odjetí myši (onmouseout
).
Pokud je takových buněk na stránce více, lze pro tento účel napsat funkci, ale i potom je zápis do tagu buňky poněkud neúsporný a nepřehledný. Účelem tohoto článku je zejména ukázat výhody vazby skriptu pomocí behavior
. Jednou z předností je úsporný a logický zápis, pokud se aktivního prvku použije na stránce (respektive na celém webu) opakovaně. Stejného efektu přebarvení buněk se tedy dá dosáhnout pomocí behavior
:
<style>
td {background-color: yellow; behavior: url(‚prebarveni.htc‘)}
</style>
…
<td>Buňka měnící barvu</td>
<td>Druhá buňka měnící barvu</td>
Přebarvovací skript je v tomto případě přenesen do externího souboru prebarveni.htc. Ten vypadá takto:
<component>
<attach event=“onmouseover“ onevent=“prebarvit(‚red‘)“>
<attach event=“onmouseout“ onevent=“prebarvit(‚yellow‘)“>
<script>
function prebarvit(barva)
{
element.style.backgroundColor = barva;
}
</script>
</component>
Viz příklad podbarvení buňky pomocí behavior.
Syntaxe HTC
Na výše uvedeném příkladu proberu trochu více (třebaže stále velmi povrchně) syntaxi HTC souboru. Jistě vidíte, že to je takové „skoro XML“.
Vše je formálně obaleno elementem <component>
. Následuje vazba událostí s funkcemi, to jsou elementy <attach ...>
, kde se říká, při jaké události (event
) se spouští jaká funkce (onevent
). V tomto případě volám při onmouseover
funkci s parametrem prebarvit(„red“). Při onmouseout
volám tutéž funkci s parametrem „yellow“. (Komu vadí, že se barvy zapisují rovnou do skriptu, nechť se podívá na poslední příklad, tam vysvětluji předávání parametrů pomocí property
.)
Kromě vazeb (attach
) může HTC soubor na začátku obsahovat elementy vlastností (property
), metod (method
) a událostí (event
). V tomto článku elementy method
a event
nezmiňuji.
Následuje vlastní zápis JavaScriptu vymezený klasicky elementem script
. Obsahuje pouze deklaraci funkcí (protože nic jiného se prostě nemá jak provést) a má i mnohá další specifika. (Například nemá smysl používat v těchto skriptech metodu document.write()
, protože by se vykonávala až při uživatelských událostech, a to je dokument pro zápis již uzavřený.)
Objekt element
Dalším specifikem je objekt element
. Ve příkladu si všimněte řádku:
element.style.backgroundColor = barva;
Co znamená element
? Objekt element
je obdoba objektu this
, zjednodušeně řečeno. Když v zápisu použiji objekt element
, ví skript, že tím myslím HTML element v dokumentu, který vyvolal probíhající událost. Přidržím-li se příkladu se změnou barvy buňky, pak element
v tomto případě reprezentuje buňku, přes kterou jede myš a které se mění barva pozadí.
Objekt element
je velice užitečný. Využije se téměř v každém HTC skriptu, těžko by se bez něj dalo obejít. (Když už píšu o objektu element
, měl bych zmínit, že ve skriptech v HTC souboru je element
vrcholem objektového modelu, zatímco jinde v JavaScriptu je tím vrcholem objekt window
.)
Příklad třetí – nové okno bez lišt
Zatímco předchozí příklady byly spíše intelektuální hříčky, otevírání odkazu do nového okna bez menu už je věc dosti často používaná, například v galeriích fotografií. První obrázek zde většinou slouží jako náhled a odkaz na druhý obrázek, který se má otevřít v novém okně.
Stejně jako v ostatních příkladech proberu napřed klasický zápis JavaScriptem a pak bude následovat zjednodušená verze používající behavior
. JavaScriptový zápis odkazu otevírající nové okno přesně stanovené velikosti, bez menu, adresy a tlačítek:
<a href=“druhy.gif“ onclick=“window.open(‚druhy.gif‘,“,’menubar=no, width=400, height=300′); return false;“><img src=“prvni.gif“ border=“0″></a>
Viz příklad nové okno bez lišt pomocí JavaScriptu.
Jak je vidět, při kliknutí na odkaz (onclick
) se otevírá nové okno (window.open()
), do kterého se načítá určitý soubor (v tomto případě druhy.gif
). Okno není pojmenované a má určité rozměry. Odkaz míří na soubor druhy.gif
(atributem href
), aby se tento soubor dal dosáhnout i v klientech bez podpory JavaScriptu (například Google Spider). Příkaz return false
zabrání tomu, aby odkaz na tento cíl prokliknul. Obsahem odkazu je třeba obrázek.
JavaScriptový zápis je sice plně funkční, ale pokud je na stránce nebo na webu takových odkazů více, je docela obtěžující vše vypisovat. Sice si lze vypomoci JavaScriptovou funkcí, ale nejefektivnější bude použít behavior
:
<a href=“druhy.gif“ style=“behavior: url(‚noveokno.htc‘)“><img src=“prvni.gif“ border=“0″></a>
V tomto případě zapisuji náhodou styl přímo. Míří na soubor noveokno.htc, který vypadá takto:
<public:component>
<public:attach event=“onclick“ handler=“noveokno“
/>
<script>
function noveokno()
{
window.open( element.href,“_blank“,“menubar=no, width=400,
height=300″);
event.returnValue = false; //něco jako return false
}
</script>
</public:component>
Viz příklad nové okno bez lišt pomocí JavaScriptu i behavior.
Řádek s attach
říká, že při kliknutí (onclick
) se použije ovladač (handler) noveokno
. Zápis handler="noveokno"
je skoro ekvivalentní zápisu event="noveokno()"
, syntaktický rozdíl je v závorkách. Funkční rozdíl je v tom, že pomocí onevent
by se nedala zrušit předdefinovaná událost (prokliknutí odkazu), což pomocí handleru jde. (To se konkrétně nedělá pomocí primitivního return false
, ale přes korektnější zápis event.returnValue = false
.) Prostě onevent
je jenom navěšení další nezávislé události, kdežto handler
je doslova ovladač. V praxi se ale s tímto rozdílem setkáte minimálně, můžete to pustit z hlavy, pamatujte si jenom, že u onevent
se píší závorky.
Pozorný čtenář na tomto příkladu jistě vidí i trochu striktnější použití náznaků XML syntaxe, která ale v Microsoft Internet Exploreru nemá žádný vliv na funkčnost.
Prosím všimněte si, že metoda window.open
do nového okna načítá soubor, jehož adresa je v proměnné element.href
. Co je element.href
? Tedy element
je to, co událost vyvolalo, v tomto případě náš odkaz. Odkaz má v JavaScriptu vlastnost href
, jejíž hodnotou je cíl odkazu. Takže element.href
má hodnotu „druhy.gif“ (protože href="druhy.gif"
).
Na tomto příkladu jsem chtěl ilustrovat, že skripty přiřazené pomocí behavior
umí číst vlastnosti dokumentu (v tomto případě cíl odkazu) a využívat je.
Když není podpora
Kritický čtenář si určitě na předchozích příkladech musel říkat: „Je to všechno hezké, ale co se stane v prohlížečích, zejména starších, které behavior nepodporují?“ Odpověď je jednoduchá – nestane se nic. Než na základě toho behavior
zavrhnete, dovolte mi poznamenat tři věci:
- většinou lze vše zapsat tak, aby to i ve starších prohlížečích alespoň nějak fungovalo
- ignorování
behavior
staršími prohlížeči je ve skutečnosti výhodou - drtivé většině uživatelů
behavior
funguje
Právě předchozí příklad s novým oknem byl ukázkou toho, jak vše napsat tak, aby i ve starším prohlížeči byla zachována částečná funkce. Zápis byl:
<a href=“druhy.gif“ style=“behavior: url(‚noveokno.htc‘)“><img src=“prvni.gif“ border=“0″></a>
Jistě tušíte, že starší prohlížeč vidí pouze normální odkaz s nějakým nesmyslným stylem. Pár procent uživatelů tak po kliknutí sice nebude mít přesně velké nové okno, ale k cíli se dostanou také. Je to to podobné jako u CSS stylů – většinou fungují, ale spolehnout se na ně nelze, takže je stejně nutné dokument strukturovat logicky.
JavaScripty se mají psát tak, aby nehlásily chyby. Jestli mě na skriptech něco fakt štve, tak to, jak musím každou chvíli podmínkovat, jestli mám správný prohlížeč. Podmínky přitom tvoří nezřídka polovinu kódu. V naprosté většině případů podmínka zjišťuje, zda je prohlížeč Microsoft Internet Explorer verze 4 nebo 5 a vyšší, pak se nechá skript běžet. Pro jiné prohlížeče se většinou vypíná, alternativy pro Mozillu a Netscape píší jenom opravdoví profíci, kteří mají spoustu času.
Použití behavior
a HTC situaci radikálně zjednodušuje. Nemusí se nic podmínkovat. Buď uživatel má moderní prohlížeč a skript běží, nebo ho nemá a skript neběží, což ale znamená, že ani nechybuje.
Jak je to s těmi prohlížeči – CSS vlastnost behavior
podporují Mozilla a Microsoft Internet Explorer od verze 5. To podle mých statistik (duben 2002) zasáhne přibližně 95 % uživatelů, ne-li více. Microsoft Internet Explorer 4, který behavior
nezná, používá něco kolem dvou procent uživatelů. Netscape 4 naštěstí vymírá, Opera je velmi minoritní a možná se behavior
časem naučí. Zůstává tedy jediný opravdový problém – Mozilla podporuje behavior
trochu jinak!
Podpora behavior
v Mozille
Mozilla nepoužívá soubory *.htc, ale *.xbl. Syntaxe souborů XBL se od HTC trochu liší. Protože teorii zápisu XBL formátu nemám ještě nastudovanou, musím případné zájemce požádat, aby si někde našli nějaký XBL soubor a syntaxi odkoukali. Soubory XBL jsou psány v čistém XML (takže například <script>
je označen jako cdata
).
Budete-li chtít, aby vaše skripty fungovaly i v Mozille, budete muset HTC soubory zkopírovat a trochu přepsat do XBL. Symbolicky je pak zápis stylu do HTML trochu delší:
selektor {behavior: url(soubor.htc), url(soubor.xbl)}
Opět je to, co může vypadat jako nevýhoda, ve skutečnosti trochu výhodou. V HTC souborech lze používat obraty specifické pro MSIE a XBL ladit pro Mozillu. Žádné zdlouhavé podmínkování.
Property, předávání parametrů
Nejdůležitější rys čtvrtého příkladu (dále v článku) je element property
:
<property name=“hoverSrc“ >
Tento element zaručuje, že v proměnné hoverSrc
(totéž, co element.hoverSrc
) bude hodnota, kterou skript nalezne v HTML kódu jako hodnotu atributu téhož jména. V příkladu to bude jméno hoverSrc
s hodnotou „druhy.gif“, která se vezme z HTML kódu:
<img src=“prvni.gif“ style=“behavior: url(‚menici.htc‘)“ hoverSrc=“druhy.gif“>
Toto je způsob, kterým můžete HTC skriptům předávat parametry. Do HTC přidáte element property
a k HTML elementu prostě přidáte další atribut libovolného jména (ale raději nepoužívejte klíčová slova). Toto jméno pak můžete rovnou použít ve skriptu jako jméno proměnné.
Na tomto místě je dobré na chvíli se zarazit. Přidáváním nestandardních atributů do HTML dokumentů vznikají invalidní dokumenty. Může to tedy být dobrá cesta do pekla. Naštěstí se všechny prohlížeče k neznámým atributům chovají neutrálně, prostě je ignorují. Pravdou ovšem zůstává, že dokument potom není validní. Pokud toto chcete řešit, buďto property nepoužívejte, nebo pište dokumenty v XML a nový atribut zapracujte do DTD. To je mimochodem cesta, kterou se asi bude ubírat další vývoj.
Příklad čtvrtý – záměna obrázků po přejetí myší
Jde o klasický příklad, který jsem si nemohl dovolit vynechat. Mám odkaz, v něm obrázek, a chci, aby se při najetí myší obrázek zaměnil jiným obrázkem (obvykle podobným, ale s nějakou drobnou změnou).
Klasickým JavaScriptem:
<img src=“prvni.gif“ onmouseover=“this.src = ‚druhy.gif'“ onmouseout=“this.src = ‚prvni.gif'“>
Při přejetí myší (onmouseover
) se adresa tohoto obrázku (this.src
) zamění adresou druhého obrázku. Při odjetí myši (onmouseout
) se vrátí původní obrázek. V praxi se ještě na konec stránky dává skript, který druhý obrázek přednačítá, aby neblikal, to zde ale není důležité.
Pomocí behavior
je zápis jiný. Přibývá nový atribut (s libovolným jménem) hoverSrc
, který obsahuje adresu druhého obrázku:
<img src=“prvni.gif“ style=“behavior: url(‚menici.htc‘)“ hoverSrc=“druhy.gif“>
Menšinové prohlížeče, které behavior
nepodporují, obrázky nezamění, což opět není žádná tragédie. Výpis souboru menici.htc:
<component>
<property name=“hoverSrc“ >
<attach event=“onmouseover“ handler=“zamen“>
<attach event=“onmouseout“ handler=“zamen“>
<attach event=“onDocumentReady“ handler=“prednatahni“>
<script>
function zamen()
{
promenna = element.src;
element.src = hoverSrc;
hoverSrc = promenna;
}
function prednatahni()
{
stahuj = new Image();
stahuj.src = hoverSrc;
}
</script>
</component>
Viz příklad záměna obrázků po přejetí myší.
Jistě vidíte několik elementů attach
, které přiřazují událostem ovladače. Při přejetí a odjetí myší se spouští tatáž funkce zamen()
. Je psaná tak, aby třetí pomocnou proměnnou prostě prohodila dvě vlastnosti.
Všimněte si prosím události onDocumentReady
:
<attach event=“onDocumentReady“ handler=“prednatahni“>
Událost onDocumentReady
je specifická pro HTC skripty. Spouští se ve chvíli, kdy je dokument celý načtený ze serveru. Je to jediná událost, která umožňuje provést něco bez zásahu uživatele. V tomto příkladu se v tuto chvíli spouští funkce prednatahni()
, která začne načítat druhý obrázek, aby byl dříve k dispozici.
Vize knihovny komponent
Ve čtyřech příkladech jsem se snažil použít všechny postupy, které je třeba znát pro psaní vlastních plnohodnotných HTC souborů. Nyní si můžete pro běžné skripty, které ve stránkách budete používat, napsat svoje vlastní komponenty. Postupně by vám tak měla vzniknout knihovna komponent. Komponentu, kterou si dnes napíšete, můžete používat navždy. Jediné, co bude při psaní další stránky potřeba, bude přiřadit prvkům styly.
Domnívám se, že behavior
bude užitečný zejména na větších webech (jeden HTC se bude načítat do všech stránek). Opravdový význam bude mít u projektů, na kterých pracuje více lidí, z nichž někdo neovládá JavaScript (nebo dokonce nikdo). Dobře zdokumentovaná knihovna může sloužit mnoha autorům, aniž by jí museli rozumět. Nemusíte svoje kolegy učit JavaScript, stačí, když je naučíte CSS.
Určitě znáte z nternetu knihovny „hotových“ JavaScriptů. Jejich kvalita je různá (nezřídka záporná), protože implementace cizího skriptu nebývá úplně triviální. Předpokládám, že podobně časem vzniknou knihovny HTC komponent. Jejich význam bude v jednoduchosti použití. Pokud budou dobře zdokumentované, bude autorovi stránky stačit zkopírovat HTC soubor, přiřadit styl a občas přidat případné další atributy (property
) podle dokumentace.
Souhrn
Problematika stylu behavior
je velmi široká. Úplně jsem například pominul autorem definované události (event
) a metody (method
), protože je nepovažuji za moc užitečné. Také ještě musím zmínit, že Microsoft Internet Explorer má v prohlížeči zabudovaná takzvaná defaultní chování, což jsou chování, která programátorům MSIE připadla užitečná (mně třeba ne).
Zpočátku jsem se domníval, že bude problém obejít se bez metody document.write()
, která je v HTC nepoužitelná. Dá se ale dobře nahradit vlastnostmi innerHTML
a outerHTM
či metodou insertAdjacentHTML()
. Když se naváží na událost onDocumentReady
, lze dosahovat dobrých výsledků.
element.insertAdjacentHTML(beforeEnd, „text přidaný před konec elementu“);
Odkazy, zdroje
- Behavioral Extensions to CSS – starší návrh specifikace W3C, který pravděpodobně implementoval Microsoft Internet Explorer
- Introduction to DHTML Behaviors – úvod do behaviors na stránkách Microsoftu
- User Groups : Client User Group : Internet Explorer – od poloviny stránky dále se probírá behavior, zajímavá je zejména knihovna Steva Gorea
- XBL – XML Binding Language – novější specifikace W3C, příklady pro Mozillu
Starší komentáře ke článku
Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.
Mohlo by vás také zajímat
-
Od iPhonu po Android: Ultra HDR přináší nový standard fotografií
1. listopadu 2024 -
Landing page: Jak vytvořit landing page s vysokým CTR
7. května 2024 -
Vlastní web pomocí AI už může vytvořit opravdu každý
8. srpna 2024
Nejnovější
-
Výkonný a kompaktní: ASOME Max Studio s výjimečným poměrem cena/výkon
11. listopadu 2024 -
Šokující data od Microsoftu: Kyberútoky rostou o stovky procent!
8. listopadu 2024 -
Chcete jedinečnou doménu? Objevte koncovky FOOD, MEME a MUSIC!
7. listopadu 2024 -
OpenAI představilo novou funkci ChatGPT Search
6. listopadu 2024