Help:Extension:ParserFunctions/cs

Rozšíření poskytuje jedenáct dalších syntaktických funkcí pro doplnění "", které jsou již na MediaWiki přítomny. (Může být nakonfigurován tak, aby poskytoval další funkce syntaktického analyzátoru pro zpracování řetězců; tyto řetězcové funkce jsou dokumentovány .) Všechny funkce analyzátoru poskytované tímto rozšířením mají podobu:

#expr
Tato funkce vyhodnocuje matematický výraz a vrací vypočítanou hodnotu. Tato funkce je k dispozici také v prostřednictvím funkce.



Dostupné operátory jsou uvedeny vpravo, v pořadí podle priority. Viz Nápověda:Výpočet pro více informací o funkci každého operátora. Přesnost a formát vráceného výsledku se bude lišit v závislosti na operačním systému serveru, na kterém je spuštěna wiki, a na formátu čísla jazyka webu.

Při vyhodnocování pomocí boolean algebry se nula vyhodnocuje na  a jakákoli nenulová hodnota, kladná nebo záporná, se vyhodnocuje na  :



Prázdný vstupní výraz vrací prázdný řetězec. Neplatné výrazy vracejí jednu z několika chybových zpráv, které lze zachytit pomocí funkce :



Pořadí sčítání a odčítání operandů před nebo za číslem je smysluplné a lze s ním zacházet jako s kladnou nebo zápornou hodnotou namísto s operandem s chybným vstupem:



Pokud používáte výstup magických slov, musíte je nejprve naformátovat, abyste odstranili čárky a přeložili číslice. Například má za následek, kde chceme 0, které lze získat pomocí. To je obzvláště důležité v některých jazycích, kde jsou přeloženy číslice. Například v bengálštině produkuje ৩০,০৬১.



Zaokrouhlování
Rounds z čísla nalevo na násobek 1/10 zvýšené na mocninu, s exponentem rovným zkrácené hodnotě čísla uvedeného napravo.

Chcete-li zaokrouhlit nahoru nebo dolů, použijte unary  nebo   resp.

Řetězce
Výrazy fungují pouze s číslovými hodnotami, nemohou porovnávat řetězce nebo znaky. Místo toho lze použít.



#if
Tato funkce vyhodnocuje testovací řetězec a určuje, zda je nebo není prázdný. Zkušební řetězec obsahující pouze mezeru je považován za prázdný.





Tato funkce nejprve otestuje, zda první parametr není prázdný. Pokud není první parametr prázdný, funkce zobrazí druhý argument. Pokud je první parametr prázdný nebo obsahuje pouze prázdná místa (mezery, řádky atd.), zobrazí se třetí argument.



Testovací řetězec je vždy interpretován jako čistý text, takže matematické výrazy se nevyhodnocují:



Poslední parametr (false) může být vynechán:



Funkce může být vnořena. Chcete-li to provést, vnořte vnitřní funkci #if v její plné podobě namísto parametru uzavírací funkce #if. Je možné až sedm úrovní vnoření. Může záviset na wiki nebo na omezení paměti.

Parametr můžete také použít jako testovací řetězec v příkazu #if. Musíte se ujistit, že za název proměnné přidáte  (symbol kanálu, svislítko, svislá čára). (Pokud parametr nemá hodnotu, vyhodnotí se jako řetězec " " na prázdný řetězec.)



Viz, kde jsou další příklady této funkce analyzátoru.

#ifeq
Tato syntaktická funkce porovnává dva vstupní řetězce. Určuje, zda jsou totožné a vrací jeden ze dvou řetězců na základě výsledku. Pokud je požadováno další srovnání a výstupní řetězce, zvažte použití přepínače.



Pokud mají oba řetězce platné číselné hodnoty, jsou řetězce porovnány numericky:



Jinak je porovnání provedeno jako text; toto srovnání rozlišuje velká a malá písmena:


 * →  (porovnání s podobným příkladem výše, bez uvozovek)
 * →  (ve srovnání s podobným příkladem výše, přičemž   vrací první platné číslo)
 * →  (porovnání s podobným příkladem výše, bez uvozovek)
 * →  (ve srovnání s podobným příkladem výše, přičemž   vrací první platné číslo)

Jako praktický příklad zvažte existující. Pomocí parseru vyberte mezi dvěma standardními časy, krátkými a dlouhými. Tento parametr bere jako první vstup k porovnání s řetězcem "short" – neexistuje žádná konvence pro objednávku, ale je jednodušší přečíst, pokud je parametr první. Kód šablony je definován jako:



následující výsledek:


 * → 20
 * → 40
 * → 40

#iferror
Tato funkce vezme vstupní řetězec a vrátí jeden ze dvou výsledků; funkce se vyhodnotí na, pokud vstupní řetězec obsahuje objekt HTML s chybou  , generovaný jinými funkcemi analyzátoru, jako jsou  ,   a  , chyby šablony, jako jsou smyčky a rekurze a další chyby analyzátoru „failsoft“.



Jeden nebo oba zpětné řetězce lze vynechat. Pokud je řetězec  (správný) vynechán, je vrácen testovací řetězec , pokud není chybný. Pokud je také vynechán řetězec, vrací se při chybě prázdný řetězec:


 * → &zwnj;
 * → &zwnj;
 * → &zwnj;
 * → &zwnj;
 * → &zwnj;
 * → &zwnj;

#ifexpr
Tato funkce vyhodnocuje matematický výraz a vrací jeden ze dvou řetězců v závislosti na booleovské hodnotě výsledku:



Vstup  (výraz) je vyhodnocen přesně jako u   výše, přičemž jsou k dispozici stejní operátoři. Výstup je pak vyhodnocen jako booleovský výraz.

Prázdný vstupní výraz je vyhodnocen jako :



Jak bylo uvedeno výše, nula se vyhodnocuje jako  a jakákoli nenulová hodnota se vyhodnocuje jako. Takže tato funkce je ekvivalentní jedné s použitím pouze  a  :



s výjimkou prázdného nebo nesprávného vstupního výrazu (chybová zpráva je považována za prázdný řetězec; není rovno nule, takže dostaneme hodnotu ).

porovnání

Je možné vynechat jednu nebo obě zpětné hodnoty. Žádný výstup není uveden, když je příslušná větev ponechána prázdná:



#ifexist
Tato funkce vezme vstupní řetězec, interpretuje jej jako název stránky a vrací jednu ze dvou hodnot v závislosti na tom, zda stránka existuje na místní wiki.



Funkce se vyhodnotí jako, pokud stránka existuje, ať už obsahuje obsah, je viditelně prázdná (obsahuje metadata, jako jsou odkazy kategorií nebo , ale není viditelný obsah), je prázdný nebo je. Pouze stránky, které jsou přesměrovány, se vyhodnotí jako, včetně toho, zda stránka dříve existovala, ale byla odstraněna.



Funkce se vyhodnotí jako  pro, které byly přizpůsobeny, a pro , které jsou definovány softwarem.



Pokud stránka zkontroluje cíl pomocí, pokud existuje, pak se tato stránka objeví v seznamu  pro cílovou stránku. Pokud tedy byl kód  zahrnut na této stránce v přímém přenosu, /Foo uvede.

Na wiki používajících sdílené úložiště médií lze  použít ke kontrole, zda byl soubor nahrán do úložiště, ale nikoli do samotné wiki:



Pokud byla pro soubor vytvořena stránka místního popisu, výsledkem je exists pro všechny výše uvedené.

nefunguje s odkazy na interwiki.

pokud existují limity
je považován za "drahou parserovou funkci". Na libovolnou stránku lze zahrnout pouze omezený počet funkcí (včetně funkcí uvnitř přenesených šablon). Pokud je tento limit překročen, všechny další funkce  automaticky vrátí false, ať už cílová stránka existuje nebo ne, a stránka je roztříděna do kategorie Category:. Název se může lišit v závislosti na jazyce obsahu vaší wiki.

Pro některé případy použití je možné emulovat efekt ifexist s css pomocí selektorů  (pro výběr odkazů na neexistující stránky) nebo   (pro výběr odkazů na existující stránky). Navíc, protože počet drahých syntaktických funkcí, které lze použít na jedné stránce, je řízen, lze také v případě potřeby zvýšit limit v LocalSettings.php.

pokud existují a hledané stránky
Stránka, která neexistuje a je testována na použití #ifexist, skončí na Hledaných stránkách.

#rel2abs
Tato funkce převádí relativní cestu k souboru na absolutní dráhu souboru.



V rámci vstupu  je platná následující syntaxe:


 * → aktuální úroveň
 * → jít o jednu úroveň výše
 * → přejděte o jednu úroveň dolů do podadresáře /foo

Pokud není zadán, bude místo toho použit celý název stránky:



Neplatná syntaxe, například  nebo , je ignorována. Protože není povoleno více než dvě po sobě jdoucí úplné zastávky, lze takové sekvence použít k oddělení následných příkazů:



#switch
Tato funkce porovnává jednu vstupní hodnotu s několika testovacími případy a v případě shody vrací přidružený řetězec.

Příklady:




 * 1) switch s tagy částečného převodu může ovlivnit konfigurační soubor, který umožňuje editoru neobeznámenému s kódováním šablony prohlížet a upravovat konfigurovatelné prvky.

Výchozí
(výchozí výsledek) je vrácen, pokud žádný řetězec  neodpovídá   (srovnávacímu řetězci):



V této syntaxi musí být výchozím výsledkem poslední parametr a nesmí obsahovat nezpracované znaménko rovná se (znaménko rovná se bez ). Pokud ano, bude se s ním zacházet jako s případovým porovnáním a pokud se neshodují žádné případy, nebude se zobrazovat žádný text. Důvodem je, že výchozí hodnota nebyla definována (je prázdná). Pokud se však případ shoduje, bude vrácen přidružený řetězec.



Alternativně může být výchozí výsledek explicitně deklarován řetězcem  z " ".

Výchozí výsledky deklarované tímto způsobem mohou být umístěny kdekoli v rámci funkce:



Pokud je parametr  vynechán a nedojde k žádné shodě, nevrací se žádný výsledek  :



Výsledky seskupení
Je možné nechat hodnoty 'propadnout', když několik řetězců  vrací stejný řetězec. To minimalizuje duplicitu.

Zde se všechny případy 2, 3 a 4 vrátí ; případy 6 a 7 vrátí. " " v posledním parametru může být ve výše uvedeném případě vynechán.

Použití s parametry
Funkce může být použita s parametry jako zkušební řetězec. V tomto případě není nutné umístit svislítko za název parametru, protože je velmi nepravděpodobné, že se rozhodnete nastavit případ jako řetězec " ". (Toto je hodnota, kterou bude parametr přednastaven, pokud svislítko chybí a parametr neexistuje nebo nemá hodnotu. Viz .)



Ve výše uvedeném případě, pokud se  rovná , funkce vrátí. Pokud se rovná, funkce vrátí. Pokud je parametr prázdný nebo neexistuje, vrátí funkce.

Stejně jako ve výše uvedené části lze případy kombinovat a získat jediný výsledek.



Pokud se  rovná ,   nebo  , funkce vrátí. Pokud se rovná, funkce vrátí. Pokud je parametr prázdný nebo neexistuje, vrátí funkce.

Kromě toho lze výchozí výsledek vynechat, pokud si nepřejete nic vrátit. Pokud se hodnota parametru testu neshoduje s žádným z případů.



V tomto případě funkce vrací prázdný řetězec, pokud neexistuje  a je rovno   nebo , v tomto případě vrací  , respektive.

To má stejný účinek jako prohlášení výchozího výsledku za prázdný.



Pokud se z nějakého důvodu rozhodnete nastavit případ jako " ", funkce vrátí výsledek tohoto případu, pokud parametr neexistuje nebo nemá hodnotu. Parametr by musel existovat a mít jinou hodnotu než řetězec " ", aby vrátil výchozí výsledek funkce.


 * (when  doesn't exist or is empty):
 * →  Foo 
 * (when  has the value " "):
 * →  Bar 
 * (when  has the value " "):
 * →  Foo 

In this hypothetical case, you would need to add the pipe to the parameter.

Srovnávací chování
Stejně jako u  je porovnání provedeno číselně, pokud porovnávaný řetězec i řetězec případu jsou číselné nebo jako řetězec citlivý na velikost písmen jinak:





Řetězec  může být prázdný:



Jakmile je nalezena shoda, následující  (případy) jsou ignorovány:



Prosté znaky rovná se
Řetězce "Case" nemohou obsahovat prosté znaménka rovná se. Chcete-li tento problém vyřešit, vytvořte šablonu = obsahující jediné znaménko rovná se:  nebo nahraďte znak rovná se html kódem.

Příklad:





Nahrazování #ifeq
lze použít ke snížení hloubky rozšíření.

Například:



je ekvivalentní



tj. hluboké založení, lineární:

Na druhou stranu by výměna spínače mohla být komplikovaná / nepraktická pro IF vnořené do obou větví (zobrazeny s alternativami odsazení, odsazené na obou stranách), čímž by se vytvořil plný symetrický strom:

#time
Tato syntaktická funkce bere datum a nebo čas (v gregoriánském kalendáři) a formátuje je podle dané syntaxe. Lze určit objekt datum / čas. Výchozí hodnota je hodnota kouzelného slova, tj. čas, kdy byla stránka naposledy vykreslena do HTML.



Seznam přijatých formátovacích kódů je uveden v tabulce vpravo. Jakýkoli znak ve formátovacím řetězci, který není rozpoznán, je předán nezměněný. To platí také pro mezery (systém je pro interpretaci kódů nepotřebuje). Existují také dva způsoby, jak uniknout znakům uvnitř formátovacího řetězce: Kromě toho je digraph  interpretován jako jediné doslovné "x".
 * 1) Zpětné lomítko následované znakem formátování je interpretováno jako jediný literálový znak
 * 2) Znaky uzavřené ve dvojitých uvozovkách se považují za doslovné znaky a uvozovky se odstraní.



Objekt  může být v jakémkoli formátu akceptovaném funkcí strtotime v PHP. Jsou přijímány jak absolutní (např. ), tak relativní (např.  ) časy.


 * &rarr; 
 * &rarr; 
 * &rarr; 
 * &rarr; 
 * &rarr; 
 * &rarr; 
 * &rarr; 

(kód jazyka) v ISO 639-3 (?) Umožňuje zobrazení řetězce ve vybraném jazyce



Parametr  určuje, zda objekt datum / čas odkazuje na místní časové pásmo nebo UTC.

Toto je booleovský parametr: jeho hodnota je určena přetypováním hodnoty argumentu (viz oficiální dokumentace PHP pro podrobnosti o tom, jak se řetězec přetypuje na booleovské hodnoty).

Podrobnosti viz následující příklady:





Pokud jste vypočítali časovou značku Unixu, můžete ji použít při výpočtech data tak, že předběžně čekáte na symbol.



Lze zadat úplná nebo částečná absolutní data; funkce "vyplní" části data, které nejsou specifikovány pomocí "aktuálních" hodnot:



Čtyřciferné číslo je vždy interpretováno jako rok, nikdy ne jako hodiny a minuty:



Šestimístné číslo je interpretováno jako hodiny, minuty a sekundy, pokud je to možné, ale jinak jako chyba (nikoli například rok a měsíc):


 * →  Vstup je považován za čas, spíše než za rok + měsíc.
 * →  Ačkoli 19:60:09 není platný čas, 196009 není interpretován jako září 1960.

Funkce převádějící matematcké znaky na data:



Celková délka formátovacích řetězců  je omezena na 6000 znaků.

Pproblém s časovými pásmy
Tato funkce syntaktického analyzátoru #time (konkrétně v  PHP DateTime ) obsahuje chybu, která neumožňuje předávání nečísel jako kompenzace relativního časového pásma. Tento problém se netýká při použití hodinového časového pásma, jako je EDT. Například:


 * &rarr;

Indie je však s časovým posunem +5,5 hodiny od UTC a proto použití jejího časového pásma obvykle neumožňuje správný výpočet relativního časového pásma. Co se stane:


 * &rarr;

Chcete-li tento problém vyřešit, jednoduše převeďte čas na minuty nebo sekundy, například:


 * &rarr;
 * &rarr;

(Tim Starling, vývojář této funkce, poskytl přesnou syntaxi tohoto řešení.)

#timel
Tato funkce je identická s, když parametr   je nastaven na  , takže vždy používá místní čas wiki (jak je nastaveno v ).

Syntaxe funkce je:





Viz například následující příklady:





#titleparts
Tato funkce rozdělí nadpis stránky na segmenty na základě lomítek a poté vrátí některé z těchto segmentů jako výstup.



Pokud není zadán parametr počet segmentů k návratu, je výchozí hodnota "0", která vrací všechny segmenty z prvního segmentu k návratu (včetně). Pokud není zadán parametr první segment k návratu nebo je "0", bude výchozí hodnota "1":


 * →  See also.
 * →  See also.

Negative values are accepted for both values. Negative values for the number of segments to return parameter effectively 'strips' segments from the end of the string. Negative values for the first segment to return translates to "start with this segment counting from the right":


 * →  Strips one segment from the end of the string. See also.
 * →   Strips all 4 segments from the end of the string
 * →   Strips 5 segments from the end of the string (more than exist)
 * →   Returns last segment. See also.
 * →   Strips one segment from the end of the string, then returns the second segment and beyond
 * →   Start copying at the second last element; strip one segment from the end of the string

Before processing, the pagename parameter is HTML-decoded: if it contains some standard HTML character entities, they will be converted to plain characters (internally encoded with UTF-8, i.e. the same encoding as in the MediaWiki source page using this parser function).


 * For example, any occurrence of,  , or   in pagename will be replaced by.
 * No other conversion from HTML to plain text is performed, so HTML tags are left intact at this initial step even if they are invalid in page titles.

Then the decoded pagename is canonicalized into a standard page title supported by MediaWiki, as much as possible:


 * 1) All underscores are automatically replaced with spaces:
 * →  Not bah_boo, despite the underscore in the original.
 * 1) The string is split a maximum of 25 times; further slashes are ignored and the 25th element will contain the rest of the string. The string is also limited to 255 characters, as it is treated as a page title:
 * If for whatever reason you needed to push this function to its limit, although very unlikely, it is possible to bypass the 25 split limit by nesting function calls:
 * 1) Finally the first substring is capitalized according to the capitalization settings of the local wiki (if that substring also starts by a local namespace name, that namespace name is also normalized).
 * 1) Finally the first substring is capitalized according to the capitalization settings of the local wiki (if that substring also starts by a local namespace name, that namespace name is also normalized).
 * 1) Finally the first substring is capitalized according to the capitalization settings of the local wiki (if that substring also starts by a local namespace name, that namespace name is also normalized).

{{Warning|1= Certain characters that are illegal in a page title will cause #titleparts to not parse the string:


 * → {{#titleparts: {one/two} | 1 | 1 }}. Does not produce the expected: {one
 * → {{#titleparts: page/123 | 1 | 2 }}. Does not work because brackets are illegal in page titles and this parser function does not process links embedded in its input pagename parameter, even when they use the MediaWiki syntax, or any other HTML or MediaWiki tags.
 * → "{{#titleparts: red/#00FF00/blue| 1 | 3 }}". Does not work because "#" is also illegal in page titles.

}}

StringFunctions
All of these functions are integrated from the StringFunctions extension, but are only available if an administrator sets   in.

All of these functions operate in O(n) time complexity, making them safe against DoS attacks.

#len:
The #len function returns the length of the given string. The syntax is:

The return value is always a number of characters in the source string (after expansions of template invocations, but before conversion to HTML). If no string is specified, the return value is zero.

#pos:
The #pos function returns the position of a given search term within the string. The syntax is:

The offset parameter, if specified, tells a starting position where this function should begin searching.

If the search term is found, the return value is a zero-based integer of the first position within the string.

If the search term is not found, the function returns an empty string.

#rpos:
The #rpos function returns the last position of a given search term within the string. The syntax is:

If the search term is found, the return value is a zero-based integer of its last position within the string.

If the search term is not found, the function returns -1.

#sub:
The #sub function returns a substring from the given string. The syntax is:

The start parameter, if positive (or zero), specifies a zero-based index of the first character to be returned.

Example: returns.

returns.

If the start parameter is negative, it specifies how many characters from the end should be returned.

Example: returns.

The length parameter, if present and positive, specifies the maximum length of the returned string.

Example: returns.

If the length parameter is negative, it specifies how many characters will be omitted from the end of the string.

Example: returns.

If the start parameter is negative, it specifies how many characters from the end should be returned. The length parameter, if present and positive, specifies the maximum length of the returned string from the starting point.

Example: returns.

#replace:
The #replace function returns the given string with all occurrences of a search term replaced with a replacement term.

If the search term is unspecified or empty, a single space will be searched for.

If the replacement term is unspecified or empty, all occurrences of the search term will be removed from the string.

Currently the syntax doesn't provide a switch to toggle case-sensitivity setting. But you may make use of magic words of formatting as a workaround. (e.g. your_string_here ) For example, if you want to remove the word "Category:" from the string regardless of its case, you may type:
 * Case-insensitive replace:

But the disadvantage is that the output will become all lower-case. If you want to keep the casing after replacement, you have to use multiple nesting levels (i.e. multiple replace calls) to achieve the same thing.

#explode:
The #explode function splits the given string into pieces and then returns one of the pieces. The syntax is:

The delimiter parameter specifies a string to be used to divide the string into pieces. This delimiter string is then not part of any piece, and when two delimiter strings are next to each other, they create an empty piece between them. If this parameter is not specified, a single space is used. The limit parameter is available in ParserFunctions only, not the standalone StringFunctions version, and allows you to limit the number of parts returned, with all remaining text included in the final part.

The position parameter specifies which piece is to be returned. Pieces are counted from 0. If this parameter is not specified, the first piece is used (piece with number 0). When a negative value is used as position, the pieces are counted from the end. In this case, piece number -1 means the last piece. Examples:


 * returns.
 * returns.
 * returns.
 * returns.

The return value is the position-th piece. If there are fewer pieces than the position specifies, an empty string is returned.

#urldecode:
converts the escape characters from a 'URL encoded' string string back to readable text. The syntax is:

Notes:
 * This function works by directly exposing PHP's urldecode function.
 * A character-code-reference can be found at www.w3schools.com.
 * The opposite,, has been integrated into MediaWiki as of version 1.18; for examples see Help:Magic Words.
 * urldecode was merged from Stringfunctions in 2010, by commit 1b75afd18d3695bdb6ffbfccd0e4aec064785363

Limits
This module defines three global settings:


 * 
 * 
 * 

These are used to limit some parameters of some functions to ensure the functions operate in O(n) time complexity, and are therefore safe against DoS attacks.

$wgStringFunctionsLimitSearch
This setting is used by #pos, #rpos, #replace, and #explode. All these functions search for a substring in a larger string while they operate, which can run in O(n*m) and therefore make the software more vulnerable to DoS attacks. By setting this value to a specific small number, the time complexity is decreased to O(n).

This setting limits the maximum allowed length of the string being searched for.

The default value is 30 multibyte characters.

$wgStringFunctionsLimitReplace
This setting is used by #replace. This function replaces all occurrences of one string for another, which can be used to quickly generate very large amounts of data, and therefore makes the software more vulnerable to DoS attacks. This setting limits the maximum allowed length of the replacing string.

The default value is 30 multibyte characters.

Substitution
Parser functions can be substituted by prefixing the hash character with :


 * → the code   will be inserted in the wikitext since the page exists.

Substitution does not work within, you can use &hellip;  for this purpose.

Redirects
Especially   could be handy in  to pages including dates, but this does not work.

Escaping pipe characters in tables
Parser functions will mangle syntax and pipe characters, treating all the raw pipe characters as parameter dividers. To avoid this, most wikis used a template    :! with its contents only a raw pipe character, since MW 1.24 a  replaced this kludge. This 'hides' the pipe from the MediaWiki parser, ensuring that it is not considered until after all the templates and variables on a page have been expanded. It will then be interpreted as a table row or column separator. Alternatively, raw HTML table syntax can be used, although this is less intuitive and more error-prone.

You can also escape the pipe character for display as a plain, uninterpreted character using an HTML entity:.

Stripping whitespace
Whitespace, including newlines, tabs, and spaces, is stripped from the beginning and end of all the parameters of these parser functions. If this is not desirable, comparison of strings can be done after putting them in quotation marks.



To prevent the trimming of then and else parts, see m:Template:If. Some people achieve this by using &lt; &gt;  &lt; /nowiki &gt; instead of spaces.


 * → foofoo
 * → foofoo

However, this method can be used to render a single whitespace character only, since the parser squeezes multiple whitespace characters in a row into one.




 * || → || foofoo 
 * }

In this example, the  style is used to force the whitespace to be preserved by the browser, but even with it the spaces are not shown. This happens because the spaces are stripped by the software, before being sent to the browser.

It is possible to workaround this behavior replacing whitespaces with  (breakable space) or   (non-breakable space), since they are not modified by the software:


 * →  foofoo 
 * → foofoo