Semafor

Pro ukázku semaforu si pořídíme pohled shora. Obrázek by měl být klikací. Abychom se vyhli použití mapy (je možné zadat, že jednotlivé [například obdélníkové] oblasti spouští různé funkce), můžeme rozdělit obrázek do více částí.

Protože by to mohlo být poučnější, použil jsem ve finálním příkladě jen dva z devíti obrázků jako klikací; oba jsou stejné, v příkladu je reprezentuje tag:

<img src="o6.gif" border="0" onclick="jedeauto()">

Obrázky jsou bez okrajů, protože ty by rušily. U obrázku je definována funkce, která se zavolá, když na něj někdo klikne. Kliknutím je simulován příjezd auta.

Obrázky jsou uspořádány pomocí tabulky bez okrajů, aby se vůči sobě při změně velikosti stránky nemohly posouvat. Byly všechny vytvořeny v programu Malování a uloženy ve formátu GIF. Jsou také všechny stejně velké. Některé se opakují, obrázky semaforu mají naopak více variant (program je bude měnit).

Nakonec byla přidána na začátek a konec tabulky vždy ještě jedna řádka, ve které je pouze ve střední buňce tlačítko. To je vytvářeno kódem:

<input type=button value="Tlačítko pro chodce" onclick="dechodec()">

Obě výše zmíněné funkce spouštějí cyklus střídání světelných signálů (například pro auto: ze stůj na připravte se, pak na volno, pak oranžová a nakonec zpět stůj). Pokud takový cyklus probíhá a někdo zadá další signál, musí se tento někde zapamatovat. Proto jsou v Javascriptu na začátku definovány dvě pracovní proměnné, které na začátku mají samozřejmě neaktivní hodnotu:

var c = "ne";
var a = "ne";

Hodnota se nastaví na "ano" tlačítkem nebo kliknutím na obrázek, a smaže se, pokud začne být událost obsluhována. Při testování se ovšem jakákoli jiná hodnota, než "ano" (například "Ano"), považuje za "ne". V ukázce se tyto startovací funkce jmenují jedeauto() a dechodec(). Pokud neprobíhá žádná obsluha, provede se nastartování cyklu kontrolní funkcí check_a_c(), která se na konci každé z těcho funkcí volá.

Úloha semafor je klasická sekvenční úloha. U sekvenčních úloh probíhá vždy předem definovaná posloupnost stavů. Do dalšího stavu se přechází buď po splnění podmínky (zde se může pomyslný diagram návaznosti stavů i větvit), nebo (a to v našem případě téměř výhradně) po uplynutí předem známého času. K tomu nám v Javascriptu slouží časovač; následujícím příkazem nastavíme a spustíme časovač:

setTimeout("hlavni()", 3333);

Po 3333 ms (3 a třetina sekundy) se spustí předem definovaná funkce Javascriptu. Javascript umožňuje, aby běželo více úloh najednou - pokud se nastaví časovač, stránka je stále plně funkční. V našem příladě si naopak musíme ohlídat, aby se nepustil tentýž cyklus dvakrát.

Jedním z možných řešení by bylo, aby každému stavu odpovídala jedna funce v Javascriptu. Skutečné reprezentaci stavů ovšem lépe odpovídá zavést další proměnnou, stav, kterou lze snadno kdykoli otestovat. V našem případě bude nabývat stavů s0 (nic se neděje), c1 až c3 (zobrazují se signály pro chodce) a s1 až s5 (probíhá signalizace pro auta). K signálům každého cyklu musíme připočíst červenou ve všech směrech, kterou daná sekvence vždy končí. Ta je dlouhá zejména v případě ukončení zelené pro chodce, protože chodci, kteří vstoupí do vozovky, ještě mají právo přejít. Kdyby tato časová prodleva byla na začátku, zdržovalo by to zobrazení signálu "připravit", což by bylo chybné řešení (omezující plynulost provozu).

Protože časovač volá vždy stejnou funkci, musí se uvnitř funkce podle hodnoty klíče (proměnná "stav") rozhodnout, která větev se volá. K tomu slouží konstrukce do-case, která má v Javascriptu podobný tvar, jako v C++. Na začátku je příkaz:

switch (proměnná_stavu)
     {

uvnitř kterého jsou jednotlivé možnosti proměnné switche vyjmenovány pomocí case:

case "konkrétní_hodnota":

Aby se neprovedl celý zbytek programu, musí každá podvětev končit příkazem:

break;

, který znamená, že se bude pokračovat až za pravou složenou závorkou celé konstrukce do-case. Toto řešení je ve srovnání s obdobnou konstrukcí, použitou v jazyce Algol, dosti těžkopádné. Je to pravděpodobně tím, že autoři jazyka C neznali jazyk Algol (ten totiž nebyl navržen programátory, ale matematiky - specialisty na algoritmy [1958, ETH Zurich ], a přes řadu implementací jej používali zase jen matematici).

Pokud po vypršení časovače dojde například k obsluze (de facto ukončení) stavu s3, je změněn stav na s4 a znovu spuštěn časovač - konkrétní úsek pak vypadá následovně:

case "s3":
      document.o3.src="o3o.gif";
      document.o4.src="o4o.gif";
      stav="s4";
      setTimeout("hlavni()", 3333);
    break;

Poslední stav by pak místo nastavení barev na semaforu (všude červená) měl jen vrátit výchozí stav (s0) a zkontrolovat, zda během minulého cyklu nedošlo ke stisku tlačítka pro chodce, nebo k příjezdu auta. Při obsluze má tlačítko chodce přednost, i když auto přijelo dříve (není použit zásobník požadavků). Je nutno dodat, že v uváděném příkladě je červená na světlech zbytečně přenastavena na tutéž červenou. Je to obvyklé opatření - než opustíme stavový diagram, pak se znovu inicializují všechny hodnoty. U větších systémů tento přístup zvyšuje spolehlivost.

Celé spuštění cyklu pak provádí funkce check_a_c(), která zkontroluje, zda není nějaký požadavek, a pokud ano, nastaví vhodný výchozí stav a spustí sekvenci voláním funkce hlavni(). Funkce check_a_c() je volána z několika míst, jednak při ukončení cyklů, nebo při příchodu požadavku na obsluhu.

Celé řešení je zde. Zdrojový kód si můžete prohlédnout sami.