FluentUI: urejevalniki in prikaz kode
Nazaj: FluentUI (knjižnica oblikovalskega sistema)
Pet različnih gradnikov za urejanje in prikaz kode ter vprašanje, kateri je za kaj – s sklicem na vir resnice za vsakega.
1. Pregled in namen
Stanje: mešano – pet gradnikov [Live] (vsak s svojim nosilcem,
med njimi codeEditor() in htmlSplitEditor()) ter preklop med
urejevalnikom in označevanjem (preklop WYSIWYG, namenoma opisan zunaj te
strani – nosilec wvdsmd), navedeno po razdelkih.
Ta stran razvrsti pet različnih gradnikov za urejanje in prikaz kode in za vsakega poveže vir resnice – ne nadomešča dokumentacije vtičnikov. Ciljna skupina: vsak razvijalec, ki se mora odločiti, kateri gradnik je pravi za »prikaz kode«, »urejanje kode« ali »urejanje vsebine wikija« (razdelek 5).
2. Predpogoji
- Za
codeEditor()inhtmlSplitEditor(): DokuWiki z omogočenim vtičnikomwkfluentuiin naloženimhelper_plugin_wkfluentui_widgets. - Za GeSHi: nobenih – funkcija jedra, vedno na voljo.
- Za urejevalnik Markdown: omogočen vtičnik
wvdsmd, stran ima oznako Markdown (samodejno zaznavanje).
3. Pojmi
Skupni vzorec vseh petih gradnikov: postopna izboljšava nad delujočim poljem textarea – ista konvencija kot pri razširitvah JS v API-ju pomočnika (brez druge poti kode, označevanje ostane oblika shranjevanja).
| Gradnik | Kaj je | Uporaba | Nosilec / vir resnice | Stanje |
|---|---|---|---|---|
| GeSHi (izris kode) | skladenjsko barvanje ob izrisu – ni urejevalnik, brez lastnega API-ja | <code sql>…</code>/<file> v besedilu wikija in ograjeni bloki kode (```lang) v vdelavah Markdown ({{md>…}} / <markdown>); oznaka jezika = ime jezika v GeSHi | jedro DokuWiki (inc/parser/xhtml.php → priloženi GeSHi); videz prek žetonov --wk-wiki-code-* | izvedeno (jedro) |
| Urejevalnik izvorne kode | CodeMirror, postopno nad navadnim <textarea> – brez JavaScripta polje textarea v celoti deluje | vsak skrbniški modul prek codeEditor() (prvi porabnik: delovna površina SQL v wksqliteds) | skupna kopija v wkfluentui/cm/ + gradnik codeEditor() (pogodba: razdelka 6/7 spodaj) | izvedeno |
| Deljeni urejevalnik HTML | izvorna koda in urejljiv izrisan predogled drug ob drugem; odločilna ostane izvorna koda, predogled pa vanjo zapiše na novo oblikovano kodo | skrbniške površine, ki vzdržujejo fragmente HTML (prvi porabnik: upravljanje izsečkov v wksnippet) | gradnik htmlSplitEditor() + scripts/htmlsplit.js, predogled v okvirju iframe s peskovnikom (pogodba: razdelka 8/9 spodaj) | izvedeno |
| Standardni urejevalnik DokuWiki | urejevalnik strani v jedru: orodna vrstica (krepko/povezava/…) + textarea + predogled, potek osnutka in zaklepanja | vsaka stran wikija, do=edit | jedro DokuWiki | izvedeno (jedro) |
| Urejevalnik Markdown | priloženi EasyMDE na straneh Markdown nadomesti textarea; vrstica z oznako je nevidna, potek shranjevanja in osnutka v DokuWikiju ostane nespremenjen; brez JS ostane textarea | strani z oznako Markdown (samodejno zaznavanje) | vtičnik wvdsmd – vir resnice: Markdown (wvdsmd) | izvedeno |
4. Prvi koraki
Najmanjši primer: urejevalnik CodeMirror vklopite nad obstoječim poljem textarea za SQL.
/** @var helper_plugin_wkfluentui_widgets $widgets */
$widgets = plugin_load('helper', 'wkfluentui_widgets');
?>
<textarea id="sql-input" name="sql"></textarea>
<?php echo $widgets->codeEditor('sql-input', 'sql'); ?>
Napačno: codeEditor() usmeriti na textarea, ki ob klicu še ni v
DOM (npr. klic pred oznako <textarea>). Aktivacijski HTML se z id
sklicuje na element, ki mora obstajati – vrstni red v zgornjem primeru
(najprej textarea) je zavezujoč.
5. Uporaba: vodnik »kateri gradnik za kaj«
| Potreba | Gradnik |
|---|---|
| prikazati kodo v vsebini | <code lang> (GeSHi) – nikoli ne vdelajte urejevalnika |
| urejati kodo ali SQL v skrbniškem modulu | codeEditor() (en klic ob polju textarea, lastna kopija knjižnice v vtičniku ni potrebna) |
| urejati fragment HTML z vizualnim preverjanjem | htmlSplitEditor() (ovije isto polje textarea; za stran z izvorno kodo vsebuje codeEditor()) |
| zapisati polje HTML v obrazcu kot oblikovano besedilo (opis, komentar) | htmlSplitEditor() z 'variant' => 'field' – glejte Različica polja |
| urejati vsebino wikija | urejevalnik jedra; strani Markdown samodejno prek wvdsmd |
| brez posnemanja urejevalnikov Monaco/VS Code | namerna meja, enako kot pri mehaniki postavitve (layout) |
6. Referenca API: codeEditor()
| Metapodatek | Vrednost |
|---|---|
| Jezik | PHP |
| Imenski prostor | helper_plugin_wkfluentui_widgets |
| Datoteka | lib/plugins/wkfluentui/helper/widgets.php |
| Vidnost | public |
| Stabilnost | stabilno (stopnja 1) |
public function codeEditor(string $textareaId, string $language, array $opts = []): string
Povzetek: vrne aktivacijski HTML brez <script>
(atributi data-wk-code-editor) za obstoječe, poimenovano polje
textarea; scripts/codeeditor.js (postopna izboljšava) ga ob
nalaganju nadomesti s primerkom CodeMirror.
Vrnjena vrednost: string – aktivacijski HTML. Prazen niz pri
neznani oznaki $language ali praznem $textareaId – v obeh
primerih ostane textarea nedotaknjena kot navadno polje, brez napake.
7. Parametri, možnosti in stanja
| Parameter | Tip | Obvezno | Opis |
|---|---|---|---|
$textareaId | string | da | id obstoječega ciljnega polja textarea |
$language | string | da | ena od sql|php|css|javascript|markdown|html; vsaka druga oznaka vrne prazen niz |
$opts['submitId'] | string | ne | id gumba za oddajo, ki ga klikne Ctrl+Enter – urejevalnik pred tem sinhronizira vsebino v textarea |
Dostop do primerka (odjemalec): wkCodeEditor.get(textareaId) –
null, dokler CodeMirror ni naložen oziroma če se ne naloži; razrešite ob
kliku, ne ob gradnji strani. Dogodek wk-codeeditor-ready se dodatno sproži na
polju textarea.
Zakasnjeno nalaganje: knjižnice se naložijo le na straneh z vsaj eno aktivacijsko oznako – brez povečanja obsega na celotnem spletnem mestu.
Priložena kopija (utemeljen popravek specifikacije): skupna kopija
CodeMirror (5.65.16 + načini sql/php/css/javascript/markdown/htmlmixed z
odvisnostmi clike/xml/htmlmixed, datoteka z licenco MIT) je v
wkfluentui/cm/ – ne v prvotno predvidenem vendor/cm/, ker
spletna plast DokuWikija neposreden dostop do imenikov vendor/ zavrne
(na tej namestitvi empirično preverjeno, 404).
Obnašanje ob napaki:
| Pogoj | Obnašanje |
|---|---|
neznana oznaka $language | vrne prazen niz, textarea ostane nedotaknjena |
prazen $textareaId | vrne prazen niz |
| ciljnega polja textarea ni v DOM | brez napake PHP (gradnik DOM ne preverja); scripts/codeeditor.js ob nalaganju ne najde cilja in ničesar ne vklopi – tiho zmanjšano delovanje na strani odjemalca |
8. Referenca API: htmlSplitEditor()
| Metapodatek | Vrednost |
|---|---|
| Jezik | PHP |
| Imenski prostor | helper_plugin_wkfluentui_widgets |
| Datoteka | lib/plugins/wkfluentui/helper/widgets.php |
| Nosilec interakcije | lib/plugins/wkfluentui/scripts/htmlsplit.js |
| Vidnost | public |
| Stabilnost | stabilno (stopnja 1) |
public function htmlSplitEditor(string $textareaHtml, string $textareaId, array $opts = []): string
Povzetek: že pripravljeno polje textarea, ki ga ustvari klicatelj,
ovije z deljenim urejevalnikom: levo izvorna koda (CodeMirror, jezik
html – isti gradnik kot v razdelku 6), desno izrisan predogled.
Predogled je mogoče urejati; kar spremenite tam, se na novo oblikovano
zapiše v izvorno kodo.
Zakaj polje textarea ostane klicatelju: ta knjižnica namenoma nima golega gradnika textarea – ime, velikost, obveznost in oznaka polja v obrazcu spadajo k zaslonu, ne k oblikovnemu sistemu. Gradnik zato dobi pripravljen HTML, ki ga je klicatelj že zaščitil (escape), in okoli njega postavi le okolje. Posledica: poslani POST je z gradnikom ali brez njega enak.
| Parameter | Tip | Obvezno | Opis |
|---|---|---|---|
$textareaHtml | string | da | celoten HTML <textarea …> klicatelja, ki ga je ta že zaščitil (escape) |
$textareaId | string | da | njegov id; prazen ⇒ $textareaHtml se vrne nespremenjen |
$opts['mode'] | string | ne | začetni pogled source|split|preview (privzeto split); prednost ima pogled, ki ga je uporabnik prej shranil |
$opts['editable'] | bool | ne | false ⇒ predogled je samo za branje, skupina za oblikovanje v celoti odpade (privzeto true) |
$opts['storageKey'] | string | ne | imenski prostor shranjenega pogleda (wk-htmlsplit:<storageKey> v localStorage); prazen ⇒ pogled se ne zapomni |
$opts['submitId'] | string | ne | id gumba za shranjevanje – posreduje se codeEditor(), torej Ctrl+Enter |
$opts['lang'] | string | ne | jezik vsebine v predogledu (privzeto $conf['lang']) |
$opts['variant'] | string | ne | field ⇒ različica polja (spodaj); mode, editable in storageKey se tedaj ne berejo. Brez te možnosti je izhod deljeni urejevalnik, nespremenjen |
$opts['label'] | string | le polje | dostopno ime vnosne površine – v oblikovanem pogledu oznaka polja textarea ni vidna |
$opts['rows'] | int | ne | le polje: najmanjša višina v vrsticah (privzeto 6, najmanj 2) |
Brez JavaScripta: orodna vrstica ima na strežniku nastavljen hidden
(njeni gumbi za pogled in oblikovanje bi bili sicer mrtvi kontrolniki),
površina za predogled je prazna in hidden. Ostane natanko polje textarea
klicatelja. Vsi gumbi gradnika so type=„button“, polje za naslov
povezave pa nima name – poslati se torej ne more nič.
Predogled kot iframe s peskovnikom: sandbox=„allow-same-origin“,
brez allow-scripts (oboje skupaj je dokumentirana izjema
peskovnika in torej ne bi bilo zaščita). To je nujno, ker smejo
vzdrževani fragmenti HTML vsebovati <style> in <script>:
izrisan v vrstici bi <style> preoblikoval skrbniško stran okoli
sebe. Preverjeno na tej namestitvi – <script> iz fragmenta se ne
izvede ne v okvirju ne v nadrejenem dokumentu, njegov <style>
učinkuje znotraj okvirja, skrbniška stran pa ostane merljivo
nespremenjena. Slogovne datoteke gostiteljske strani se kopirajo v
dokument predogleda, da predogled kaže pravo kaskado in ne ročno vzdrževanega
približka.
Značke za nadomestne oznake: nadomestne oznake vtičnikov ({{wk:lang>…}},
{{wk:image>…}}) niso HTML. V predogledu postane vsaka od njih
nedeljiva značka, ki je ni mogoče urejati, nosi žeton dobesedno in ga
dobesedno tudi zapiše nazaj – sicer bi en sam pritisk tipke žeton razbil ali
pa bi kodiranje besedila iz < v
{{wk:lang>de:< 24 Stunden}} naredilo <, s čimer se vzorec
vtičnika, ki žeton uporablja, ne bi več ujemal. Značke nastanejo iz
izvorne kode, ne iz razčlenjenega drevesa: samo tam še obstajajo izvorni
bajti, samo tam je nadomestna oznaka, ki sama vsebuje HTML
(…Industrie-<br/>erfahrung…), en sam žeton, in samo tam je
mogoče ločiti položaj besedila od položaja atributa. Nadomestne oznake v
vrednostih atributov ostanejo nedotaknjene.
Pot shranjevanja: polje textarea je in ostane polje obrazca. Sprememba v
predogledu, ki je ob kliku na Shrani še v času zadržanja, se dokonča v
fazi zajemanja (capture) na document – pred prenosom iz CodeMirror v
textarea, ki je vezan na sam obrazec. Brez tega vrstnega reda se zadnji
pritiski tipk v predogledu ob shranjevanju izgubijo, in to neopazno.
Prvi porabnik: upravljanje izsečkov (wksnippet,
do=admin&page=wksnippet&edit=<key>).
Različica polja
Polje HTML v obrazcu ni fragment HTML: njegovo označevanje je oblika shranjevanja, ne predmet dela. Opis, koraki za ponovitev in komentar delovnega elementa so v Azure DevOps taka polja in se tam urejajo kot oblikovano besedilo. 'variant' => 'field' za to ponudi isti mehanizem z drugačnim obrazom.
$ta = $widgets->textArea([ 'name' => 'System.Description', 'id' => 'wk-wi-desc', 'value' => $html, 'rows' => 10, 'ariaLabel' => 'Opis', 'bare' => true, ]); echo $widgets->htmlSplitEditor($ta, 'wk-wi-desc', [ 'variant' => 'field', 'label' => 'Opis', 'rows' => 10, ]);
| Enako kot deljeni urejevalnik | Drugače |
|---|---|
| polje textarea ostane polje obrazca; nedotaknjeno polje vrne svojo vrednost bajt za bajtom, tudi po preklopu na HTML in nazaj | privzeto oblikovano besedilo, izvorna koda je eno stikalo (HTML); brez deljenega pogleda |
| predogled v okvirju iframe s peskovnikom brez skriptov | višina sledi vsebini med rows vrsticami in 60 % okna, celotna površina je vnosno polje |
| čakajoči vnos se ob oddaji prevzame | pisava iz okolice gradnika (v obrazcu 13/18), ne bralna velikost strani |
noben kontrolnik nima name | ena vrstica: krepko, ležeče, podčrtano, koda, oznake, številčenje, povezava, odstrani oblikovanje, HTML; Ctrl+B/I/U, Ctrl+K |
naslov povezave se prikaže šele na ukaz (Escape ga zapre) |
|
brez značk za nadomestne oznake ({{ je v polju besedilo), brez opozorila o odločilni izvorni kodi, brez sporočila o pisanju |
Obvezno polje in nadomestno besedilo. Če ima textarea required, ga skript odstrani in ob oddaji preveri sam – prazno je tudi tisto, kar ostane od izpraznjenega polja (<p><br></p>, ): ustavi, aria-invalid, rob v barvi napake, sporočilo v vrstici stanja, fokus v polje. Razlog: v oblikovanem pogledu je textarea skrita, skrito obvezno polje pa brskalnik zavrne le v konzoli (»an invalid form control is not focusable«) – gumb bi navidezno ne naredil ničesar. Brez skripta ostane required na polju textarea. placeholder polja textarea se prikaže tudi na oblikovani površini.
Dostopnost. Površina je role=„textbox“ z aria-multiline, ime dobi iz label, aria-required/aria-placeholder pa po polju textarea; ukazi imajo aria-keyshortcuts.
Dve posebnosti brskalnikov, izmerjeni:
- Koda v vrstici ne obstaja kot ukaz urejevalnika, očiten nadomestek pa odpove: Chrome iz
<code>, vstavljenega zinsertHTML, naredi<span style="font-family: …">. Element se zato nastavi prek obsega (range); ta en korak ni na skladu za razveljavitev. - Označi vse, izbriši, tipkaj je oblikovanje izbrisanega začetka prevzelo kot slog tipkanja – po kodi kot
<font face="Consolas…">. Do praznega izbrisana površina se zato začne s praznim odstavkom in svežo vstavitveno točko.
Zaščita pri porabniku. Različica polja je mlajša od gradnika. Starejši komplet možnost prezre in nariše deljeni urejevalnik. Porabnik zato odgovor sprejme le, če ima data-wk-hs-variant="field", sicer nariše svoje polje textarea (tako wkdoadowi).
Prvi porabnik: pogled delovnega elementa (wkdoadowi): opis, koraki za ponovitev, komentar. Ni vključeno: vstavljanje slik (ADO jih naloži kot priloge).
9. Krožna pot: kaj urejanje v predogledu spremeni v izvorni kodi
Urejanje v predogledu pomeni, da brskalnik označevanje na novo serializira. To prinese izgube, zato so tukaj navedene s številkami, ne zamolčane. Če delate le levo v izvorni kodi, ne sprožite ničesar od tega.
Izmerjeno na vseh 55 datotekah izsečkov te namestitve (izvorna koda → predogled → urejanje → izvorna koda):
| Rezultat | Datoteke |
|---|---|
| dokument se razčleni enako (elementi, atributi, besedilo) | 55 / 55 |
| nadomestne oznake bajtno enake (430) | 55 / 55 |
vsebina <script> in <style> bajtno enaka | 55 / 55 |
| datoteka kot celota bajtno enaka | 28 / 55 |
| razlika izključno v zamikih | 23 / 55 |
| razlika tudi drugje | 4 / 55 |
Štiri odstopanja ne vplivajo na prikaz: znakovne reference za
natisljive znake postanejo sam znak (✓ → kljukica,
» → francoski narekovaj), poševnica praznega elementa pa
odpade (<br/> → <br>).
Razveljavitev: zapis nazaj je ena sprememba v CodeMirror – en
sam Ctrl+Z v polju z izvorno kodo razveljavi celotno novo oblikovanje.
Orodna vrstica to pove tudi tam, kjer se to zgodi.
Zamik: štirje presledki; element, katerega podrejeni elementi so vsi
vrstični, ostane v eni vrstici, dokler vrstica ne preseže 110 znakov.
Vsebina <script>, <style>, <pre> in
<textarea> se nikoli ne prelomi ali na novo zamakne – tam zamik
spremeni pomen, ne videza.
10. Popolni primeri: preklop urejevalnik ↔ označevanje [Live]
Nosilec wvdsmd (polna izvedba 2026-07-14) –
vir: lib/plugins/wvdsmd/script.js.
Na straneh Markdown ima orodna vrstica EasyMDE stikalo med pogledom
urejevalnika (EasyMDE z orodno vrstico za oblikovanje in predogledom) in
pogledom označevanja (surovo polje textarea z vidno vrstico z oznako –
enako stanju brez JS). Preklopiti je mogoče kadar koli v obe smeri in
učinkuje takoj, brez shranjevanja; nazadnje izbrani pogled se zapomni za
vsakega uporabnika (localStorage,
wk-editor-view:<login>); vsak preklop se napove prek regije
aria-live. Lastnosti:
- Označevanje ostane oblika shranjevanja (Markdown) – oba pogleda urejata isto polje textarea, ponovne serializacije ni, zato so preklopi že po zasnovi brez izgub (bajtno enaka izvorna koda, dokazano v preizkusnem zagonu).
- Brez JS ostane surovo polje textarea; stikalo obstaja le v pogledu, izboljšanem z JS.
- Strani s sintakso DokuWiki obdržijo urejevalnik jedra z njegovo orodno vrstico za sintakso in gumbom »Predogled« – tam je le en pogled za urejanje, stikalo bi bilo brez pomena.
Poglobitev – konkretni ukazi orodne vrstice: dve podstrani dokumentirata, katere žetone sintakse DokuWiki oziroma Markdown vstavljajo orodne vrstice urejevalnikov (EasyMDE za Markdown, orodna vrstica jedra za sintakso DokuWiki), po shemi kategorij predstavitvenih strani DevExpress ASPxHtmlEditor (https://demos.devexpress.com/ASPxHTMLEditorDemos/Default.aspx), vendar prenesene na dejansko ciljno obliko te knjižnice namesto splošnih oznak HTML:
- Oblikovanje – oblike znakov in odstavkov kot žetoni sintakse
- Tabele in mediji – sintaksa preglednic, medijev in povezav
11. Omejitve in robni primeri
- Seznam jezikov določa GeSHi; neznane oznake se izrišejo kot navadno besedilo (brez napake). Enako velja v vdelavah Markdown:
ParsedownRenderervwvdsdwmdograjene bloke z znano oznako jezika obarva prek DokuWikijeve poti GeSHi (p_xhtml_cached_geshi()); bloki brez oznake ali z neznano oznako ostanejo surovi<pre>iz Parsedown – zmanjšano, nikoli izgubljeno. Oznaka pogonaparsedown-1.7.4-geshi1razveljavi stare predpomnilnike. - CodeMirror in EasyMDE sta priložena (brez povezave, brez odvisnosti od CDN) – posodobitve so namerne spremembe vtičnika z oznako različice.
- Površine urejevalnikov so v vsebinski plošči in sledijo receptu za zapolnitev plošče iz datagrid (sestavine 1–3 veljajo za urejevalnike enako).
- Brez posnemanja urejevalnikov Monaco/VS Code (razdelek 5).
12. Dostopnost in združljivost
- Celoten pregled: Accessibility (zbrano na enem mestu, tu ni podvojeno).
- Brez JavaScripta ostane vsak gradnik navadno, v celoti dostopno polje textarea.
- CodeMirror in EasyMDE sta priložena – združljivost z brskalniki sledi opombam ob izdaji posameznega ponudnika (v tej knjižnici ni posebej preizkušena).
13. Odpravljanje težav
Simptom: codeEditor() ne izriše ničesar (prazen niz), čeprav sta
textarea in jezik videti pravilna.
Vzrok: $language vsebuje oznako zunaj
sql/php/css/javascript/markdown/html (pazite na velike in
male črke – preverjanje je natančno, brez normalizacije).
Rešitev: oznako preverite glede na natančen seznam v razdelku 7.
Simptom: ograjeni blok kode v Markdown ni obarvan.
Vzrok: neznana ali manjkajoča oznaka jezika za tremi krativci (```) ali pa ima predpomnilnik še staro oznako pogona.
Rešitev: oznako jezika preverite glede na seznam GeSHi; ob sumu na
zastarel predpomnilnik: oznaka pogona parsedown-1.7.4-geshi1 vsili
razveljavitev.
Simptom: pri deljenem urejevalniku HTML manjka orodna vrstica ali pa predogled ostane prazen, čeprav je označevanje v izvorni kodi.
Vzrok: dostavljeni sveženj skriptov je zastarel.
lib/exe/js.php datoteke iz scripts/ združi prek
/* DOKUWIKI:include */ in svojega predpomnilnika ne opira na
njihov čas spremembe – sprememba samo v scripts/htmlsplit.js svežnja torej
ne obnovi, brskalnik pa ima poleg tega svojo kopijo pod nespremenjeno oznako
tseed.
Rešitev: posodobite čas spremembe datoteke script.js vtičnika (touch) in
stran v brskalniku znova naložite brez predpomnilnika (Ctrl+Shift+R). Velja za
vsako spremembo v vključeni datoteki s skriptom, ne le za ta gradnik.
14. Sorodne teme
- DW-urejevalnik: oblikovanje · DW-urejevalnik: tabele in mediji – poglobitev orodne vrstice za oblikovanje
- DW-urejevalnik: Adaptivity · DW-urejevalnik: Accessibility – Adaptivity/Accessibility
- Markdown (wvdsmd) – pisanje v Markdown (oznaka, EasyMDE, uvoz/izvoz)
- SQLite Data Studio: skrbniški vmesnik – delovna površina SQL (porabnik CodeMirror)
- FluentUI: namizni DataGrid – predstavitveni pregled · FluentUI: skrbniško območje podrobno – plošče, v katerih so urejevalniki
- FluentUI: javni API pomočnika – konvencija »postopna izboljšava«