Samstag, 27. Mai 2017

Anwendungen mit HTML-UI

HTML - ein UI-Format, das bleiben wird
Zum Beispiel ein Property Editor
Ein Rückblick
Der HTML-Code der Anwendung
Anzeige- und Änderungsmodus
Model View Controller
Buttons
Feldauswahl
Tabellen
Editierbarkeit mit contenteditable
Einen Wert löschen
Die node-webkit-Anwendung
Dateiauswahl
Das Datenmodell - die Klasse Properties
Der Dateivergleich
Unit Tests

HTML - ein UI-Format, das bleiben wird

In klassischen Entwicklungsumgebungen werden dem Anwendungsentwickler meist spezielle Werkzeuge für die Entwicklung der Oberfläche bereitgestellt: er darf aus irgendwelchen standardisierten Paletten Eingabecontrols wie Tabellen, Eingabefelder, Auswahlknöpfe, Schaltflächen auswählen und auf einen Oberflächentwurf ziehen, er darf Beschriftungen und sonstige Texte vorsehen, er darf Bereiche und Feldgruppen definieren, er darf per Mausclick Feldinhalte an sein Datenmodell binden und vom Control ausgelöste Ereignisse mit seinem Code verknüpfen usw.

Solche Werkzeuge sind ja sicher ganz nett (wobei ich meist als erstes auf ihre Unvollkommenheiten, auf schlechte Benutzerführung und auf fehlende Features stoße), aber letztlich muß eine so entworfene Oberfläche in einem maschinenlesbaren Format abgespeichert werden, das die Anwendung nachher zum Rendern benutzt. Die für Entwickler interessante Frage ist daher nicht nach dem UI und den Regeln, die ihm seine Entwicklungsumgebung zum Malen erlaubt, sondern nach der vollständigen Referenz über alle Möglichkeiten, die das konkrete Format, wie derartige Oberflächen dann gespeichert werden, bietet.

Nun gibt es ein maschinenlesbares, lebendiges, sehr flexibles, umfassend dokumentiertes, an Möglichkeiten unglaublich facettenreiches und noch immer weiter wachsendes Format zur Entwicklung von Benutzeroberflächen: es handelt sich um die Sprachtriade HTML, CSS und JavaScript. Durch die mittlerweile weitgehend standardkonforme Implementierung in allen gängigen Browsern haben diese Formate eine breite Basis. Auch auf Kleingeräten haben sie sich längst durchgesetzt. Man kann die CSS-Stilregeln der Oberflächen mit wenig Aufwand so gestalten, daß derselbe HTML-Code je nach Größe des Displays unterschiedlich dargestellt wird. Und wen die Browser-Sandbox zu sehr einschränkt, der kann seine App oder nativ entwickelte Anwendung mit einem HTML-Viewer ausrüsten, um die Oberfläche in seiner eigenen Umgebung zu präsentieren.

Die Webstandards leben und werden weiterentwickelt - die Entwicklungen der letzten Jahre sind ermutigend. HTML5, CSS3 und ES6 werden von den User-Agents dieser Welt mittlerweile doch tatsächlich so verstanden, wie es spezifiziert wurde![3]

Zum Beispiel ein Property Editor

Ich will in diesem Blog eine Anwendung zur Anzeige und Pflege von Property Files vorstellen, wobei ich die vielen kleinen Dinge en detail bespreche, die bei so einer Oberfläche auftreten. Die Entwicklung folgt den ES6-Standards für JavaScript. Die Anwendung kann in einem Browser aufgerufen werden, dann fungiert sie aber als reine Anzeigetransaktion, da ein Browser es nicht erlaubt, Dateien zu sichern. Dieselbe Anwendung - derselbe HTML-,CSS- und JavaScript-Code kann aber auch als node-webkit-Anwendung aufgerufen werden, dann können Daten auch editiert und Änderungen gespeichert werden.

Property Files werden gern als sprachspezifische Textressourcen für eine Anwendung eingesetzt, daher kann man einen Property Editor in der Rurik Internationalisierung einordnen. Da sie manuell als Klartextdateien bearbeitet werden, ist ein Werkzeug nützlich, das die Schlüssel/Wert-Paare mehrerer Property Files miteinander vergleicht, so dass fehlende oder mehrfach gepflegte Werte schnell erkannt werden.

Hier ein Screenshot der fertigen Anwendung, deren Quelltextdateien ich bei github mit dem Repositorynamen property-editor führe.

Ein Rückblick

Vor sieben Jahren stellte ich auf diesem Blog einige Konzepte für die Pflege von tabellenförmigen Daten in einer Webanwendung vor. Ich hatte dazu einen kleinen, nach wie vor lauffähigen Prototypen verfaßt, an dem die Konzepte sichtbar werden sollten.

Meine Absicht war, ohne irgendwelche magischen Tools und Bibliotheken auszukommen, sondern möglichst direkt die Standards des Webs zu verwenden - und das sind nach wie vor HTML, CSS und JavaScript, weiter nichts - um die Tabellenpflege zu entwerfen. [1]

Die Änderungsvormerkungen (was muss beim späteren Druck auf Sichern geändert, was gelöscht, was eingefügt werden) hatte ich mittels CSS-Klassen direkt im HTML-DOM verwaltet, so daß ein und dieselben dynamisch vergebenen CSS-Markierungen nicht nur beim Sichern vom JavaScript-Code abgegrast werden, um die zu ändernden Daten für den Server zu sammeln, sondern jederzeit auch dem Benutzer den Änderungszustand seiner Daten vor Augen führen:

Die Kommunikation mit dem Server - zum Lesen und Abspeichern der Daten - führte ich damals schon mit dem JSON-Format durch, das mittlerweile in Webanwendungen omnipräsent ist und das etwas schwerfälligere XML weitgehend abgelöst hat.[2]

Das konkrete Datenformat dient als level of indirection dazu, die Benutzeroberfläche zu entkoppeln: auf der Gegenseite stand für meinen Prototypen minimalistisch gehaltener Perl-Code zum Parsen von JSON und zum Verwalten der Daten in einer CSV-Datei. Es könnte aber auch eine ganz andere Implementierung zum Einsatz kommen, in einer anderen Programmiersprache, nicht per CGI oder mit einem anderen Server aufgerufen (tatsächlich läuft die Beispielapplikation mittlerweile nicht mehr auf Apache, sondern auf nginx), mit einem anderen Datenformat für die Persistenzebene usw.

Der HTML-Code der Anwendung

Das Markup der Anwendung definiert feste Bestandteile und Container für dynamisch gefüllte Inhalte.

Durch die Wechselwirkung mit JavaScript verschwimmt die Grenze zwischen "statischem" HTML und HTML-Template. Das ist so gewollt. Wo allerdings JavaScript HTML-Code in größerem Stil generieren soll, weit über bloße Strukturtags hinaus (wie etwa <table>, <tr> und <td> zur Strukturierung von Tabellendaten), empfiehlt sich der Einsatz von Templates. Hierzu wird man in HTML5 durch das neu eingeführte <template-Tag ermutigt. Der Einsatz hochkomplexer Parsergeneratoren wie handlebars ist zweifellos spannend, aber meist überdimensioniert, wo der standardmäßige DOM-Zugriff bereits ausreicht, um konkrete Instanzen des Templates zu erzeugen, in das HTML-DOM zu importieren und einzufügen.

In unserer Beispielapplikation wird kein Template benötigt. Was hier dynamisch erzeugt wird, ist nur die Tabelle mit den Schlüsseln und Werten, ohne ausgefeilte Extras. Mehr zur Tabellendarstellung folgt weiter unten.

Viele überflüssige Schlacken können in HTML5 weggelassen werden, so die Angaben type="text/css" in Stylesheet-Referenzen und type="text/javascript" in JavaScript-Elementen, denn dies sind die Defaults. Worauf man aber nicht verzichten sollte, ist die Angabe der Zeichencodierung. Ausdrücklich warnt das W3C:

You should always specify a character encoding on every HTML document, or bad things will happen. You can do it the hard way (HTTP Content-Type header), the easy way ( declaration), or the new way ( attribute), but please do it. The web thanks you.

Nach dem Laden auszuführendes JavaScript muß überhaupt nicht mehr an Events wie load oder DOMContentLoaded gebunden werden, sondern kann direkt vor das schließende </body>-Element eingebaut werden. Dann ist der HTML-Code bereits vollständig in die DOM-Struktur transformiert, so daß alle gewünschten, die erste Anzeige des Dokuments vorbereitenden Aktionen noch ausgeführt werden können.

Aus diesen Grundsätzen abgeleitet, bekommt das für den Property-Editor benötigte index.html-Dokument schließlich die folgende Gestalt:

<!DOCTYPE html>
<html language="en">
  <head>
   <meta charset="utf-8"/>
   <title>Comparing Property Files</title>
   <link rel="stylesheet" href="main.css" />
   <script src="ui.js"></script>
    <script src="i18n.js"></script>
  </head>
  <body>

    <h1>Comparing Property Files</h1>
   
    <div class="input-area">
      <div>
        <input type="file" multiple id="property-files">
        <label class="button" for="property-files" data-action="choose-files">
          Choose files
        </label>
        <div class="additional-info" id="selected-files">No files selected</div>
      </div>
      <div id="toolbar">
        <button data-action="reload">Reload</button>
        <button data-action="save">Save</button>       
      </div>
    </div>

    <div id="table-container"></div>
  
    <script src="control.js"></script>
  </body>
</html>

Anzeige- und Änderungsmodus

Dasselbe UI soll - als Webanwendung im Browser aufgerufen - ein reines Hilfsmittel zur Anzeige und zum Vergleich von Property Files sein, aber in einer geeigneten Umgebung, die auch das Speichern erlaubt (wie etwa node-webkit), im Änderungsmodus betrieben werden können.

Diese Unterscheidung mache ich, indem ich im Querystring der URL den Wert edit vermerke, also z.B. index.html?edit aufrufe statt bloß index.html. Dieser wird im Client ausgewertet und ist danach als Konstante im gesamten JavaScript control.js verfügbar:

   const editMode       = /\bedit\b/.test(document.location.search)
Den Anzeigemodus erhalte ich, wenn ich die URL ohne Queryteil aufrufe:
http://ruediger-plantiko.net/property-editor/
Dieselbe Webseite präsentiert sich im Änderungsmodus, wenn ich im Queryteil das Wort edit übergebe:
http://ruediger-plantiko.net/property-editor/?edit

Der augenscheinlichste Unterschied, daß man sich im Änderungsmodus befindet, ist die Anwesenheit eines (wenn auch anfangs noch inaktiven) Save-Buttons. Wenn man Property-Files eingelesen hat, zeigen sich weitere Unterschiede: die Zellen sind editierbar, Zellen, Schlüssel und ganze Zeilen sind auch löschbar. Die komplette Oberfläche für den Änderungsmodus funktioniert im Browser - mit der einen, entscheidenden Ausnahme der Save-Funktion. Denn einem Browser ist es nicht erlaubt, Dateien auf dem Computer des Benutzers zu speichern. Das wird nur möglich, wenn wir dasselbe UI in Form einer node-webkit-Anwendung betreiben. Mehr dazu weiter unten.

Diesen Weg - mit dem edit-Parameter im Querystring - habe ich gewählt, weil sich Anzeige- und Änderungsmodus nur geringfügig unterscheiden. Bei größeren Unterschieden würde ich zwei separate HTML-Dokumente entwerfen, eines für den Anzeige-, eines für den Änderungsmodus. Das JavaScript läßt sich trotzdem wiederverwenden, und es kann vom HTML-Dokument den gewünschten Modus als Attribut des <script>-Elements auslesen. Beispielsweise könnte man den Änderungsmodus als Boolesches Attribut data-edit entwerfen (seine Präsenz möge den Änderungsmodus anzeigen):

<script src="control.js" data-edit>
Im Script selbst kann man dann zu Beginn dieses Attribut einlesen (document.currentScript enthält immer das <script>-Element des gerade ausgeführten JavaScript-Codes):
const editMode = document.currentScript.hasAttribute("data-edit")

Model View Controller

Das MVC-Paradigma ist im Grunde nichts weiter als eine praktische Anwendung von separation of concerns. Der Kerm von MVC ist die Trennung der "eigentlichen Logik" (Model) von den für den Betrieb einer Benutzeroberfläche notwendigen Programmteilen (View, Controller).

Für diese Entkopplung ist nicht immer ein ausgefeiltes MVC-Framework nötig: in einfachen Fällen genügt es, die eigentliche Anwendungslogik in ein eigenes UI-freies Softwaremodul zu verlegen, das von einem einzigen Controllermodul control.js aus aufgerufen wird. Dieses Controllermodul ist seinerseits nicht nur mit dem Awendungsmodul, sondern auch mit der konkreten Oberfläche gekoppelt. Es reagiert auf Eingaben und sonstige Dialog-Ereignisse, ruft abhängig von diesen Ereignissen Anwendungslogik auf und aktualisiert dann die Oberfläche, was etwa aufgrund geänderter Anwendungsdaten nötig wird.

Statt einen Ereignisbus als weitere Komponente zu entwerfen, mit dem sich Models und UI-Methoden für "change"-Events registrieren können, kann man im einfachsten Fall die gewünschten Reaktionen auch direkt als Funktionsaufruf implementieren. Eine Funktion eines anderen Moduls aufzurufen, ist immerhin auch eine Benachrichtigung. Ein Event Bus kann nützliche Dienste zur Entkopplung leisten - vor allem, wenn zur Laufzeit mehrere Models von wechselndem Objekttyp involviert sind. In einfachen Fällen sollte man aber auch einfache Lösungen wählen – hier also die fest verdrahteten Zusammenhänge.

Die Anwendungslogik des Property Editors enthält das Modul i18n. Das Controllermodul heißt control.js. Potentiell wiederverwendbare Funktionen im Bereich des HTML-UI befinden sich im Modul ui.js. Die konkrete Oberfläche selbst besteht aus einem HTML-File index.html und einem CSS-File main.css.

Buttons

Als das entscheidende, einen Button definierende Merkmal dürfte unwidersprochen gelten, daß man ihn drücken kann!

Damit dieses Drücken auch Sinn und Zweck hat, sollte mit dem Druck eine Aktion verbunden sein. Auf HTML-DOM-Ebene bedeutet dies, daß eine Funktion für das click-Event eines Buttons registriert ist. Nun können mehrere Buttons dieselbe Aktion auslösen, z.B. Blätterbuttons, die aus Usability-Gründen oberhalb und unterhalb einer Tabelle angeboten werden.

Um einen Button als für eine bestimmte Aktion zuständig zu erklären, könnte man ihm im HTML-Code ein Custom-Attribut data-action geben (Custom-Attribute sollen immer mit dem Präfix data- versehen werden), das ausdrückt, welche Aktion er auslösen soll. Üblich ist außerdem, Buttons in einem gemeinsamen Bereich, einer Toolbar zu plazieren:

<div id="toolbar">
  <button data-action="reload">Reload</button>
  <button data-action="save">Save</button>        
</div>
Im Controller verknüpfen wir die Aktionen mit ihren Behandlerfunktionen, wobei die eigentliche Realisierung an den allgemeinen UI-Modul ui.js delegiert wird:
// -------------------------------------------------------------------
// Define handlers for user events
// -------------------------------------------------------------------
   function setHandlers() {
     var clickHandlers = {
        "reload": handleReload,
        "save": handleSave
     }
     ui.registerHandlers("click",clickHandlers)
     inputFiles.addEventListener("change",handleChooseFiles)
     
     tableContainer.addEventListener("input",handleInput)
     if (editMode) tableContainer.addEventListener("mouseover",handleOnMouseOver)
     
   }
Hier haben wir eine Funktion setHandlers(), die für alle möglichen User-Events die Behandlerfunktionen definiert - nicht nur für die Buttons, sondern auch für das Dateiauswahlfeld, das hier eine Sonderrolle spielt, für Textänderungen in den Datenzellen, und für das mouseover-Event, wenn der Mauszeiger über Tabellenzellen bewegt wird. Für die Buttons reload und save wird, wie erwähnt, die allgemeine Logik aus ui.js verwendet.

Die allgemeine Funktion registerHandlers bekommt für ein bestimmtes Event, z.B. click, einen JavaScript-Hash von Aktionen und zugeordneten Behandlerfunktionen übergeben. registerHandlers ermittelt aus dem Aktionskey wie save einen CSS-Selector wie [data-action="save"] und verwendet diesen, um alle Elemente zu ermitteln, die für das Event registriert werden sollen. In der Loop über die Elemente wird die Standardfunktion addEventListener benutzt, um für jedes Element die angegebene Behandlerfunktion zu registrieren.

Nun gibt es auch bei der Eventbehandlung gemeinsame Teile. Um Codeduplikationen in den Eventbehandlern zu vermeiden, empfiehlt es sich, diese nicht direkt zu registrieren, sondern einen generischen Callback, innerhalb dessen sie dann aufgerufen werden. Hier ist das die Funktion mainCallback, die hier nur ein elementares Fehlerhandling macht (wenn eine unterwartete Ausnahme austritt, wird der Text dieser Ausnahme in einem alert angezeigt. Dies kann durch eine elaborierte Funktion ersetzt werden, indem man beim Aufruf dem Optionen-Hash eine eigene Funktion mainCallback übergibt.

// ----------------------------------------------------------------------------
// Handle user events ("action"s)
// ----------------------------------------------------------------------------  
  function registerHandlers(event,handlers,opt={}) {

    setDefaultOptions()

    for (let action of Object.keys(handlers)) {
      let elements = getElementsByAction(action,opt.selector)
      if (elements.length) {
        for (let e of getElementsByAction(action,opt.selector)) {
          e.addEventListener(event,evt=>mainCallback.call(this,evt,handlers[action]))
        }  
      } else {
        console.log(`No element found for action ${action}`)
      }
    }    

    function setDefaultOptions() {
      opt.mainCallback = opt.mainCallback || mainCallback
    }

  }  
  
// CSS Selector to return elements for a given action (changeable by module consumer)
  function actionSelector(action) {
    return `[data-action="${action}"]`
  }

// Shorthand to give an array of all elements for an action
  function getElementsByAction(action,selector=actionSelector) {
    return Array.from( document.querySelectorAll(selector(action)))
  }  

// Main callback, wraps all single registered callbacks
// Rewritable by module consumer
  function mainCallback(event,callback) {
     try {
       callback.call(this,event)
     } catch (e) {
       alert(e.message)
     }
  }

Feldauswahl

Eine weitere Eigenschaft von Buttons ist - neben ihrer gleichsam essentiellen Eigenschaft, clickbar zu sein - in manchen Fällen aber gerade nicht clickbar zu sein - oder sogar völlig zu veschwinden. Die Ableitung solcher funktionaler Attribute eines UI-Controls aus dem aktuellen Zustand der Applikation ist die Feldauswahl.

Auch hier gibt es wieder einen speziellen und einen allgemeinen Teil. Um mit dem allgemeinen Teil anzufangen: eine allgemeine Funktion fieldSelection bekommt für disabled bzw. invisible je einen Hash mit der Angabe (als Boolescher Wert), für welche actions der betreffende Zustand zu setzen und für welche er zurückzusetzen ist:

// ----------------------------------------------------------------------------
// Field selection: which fields are disabled, which are invisible
// ----------------------------------------------------------------------------  
function fieldSelection(opt) {  
  evaluateFieldSelection(opt.disabled,(e,state)=>{e.disabled=state})
  evaluateFieldSelection(opt.invisible,(e,state)=>{e.style.display=state?"none":""})
}

function evaluateFieldSelection(actions,setState) {
  if (typeof actions != "object") return
  for( let action in actions) {
    for (let e of getElementsByAction(action)) {
      setState(e,actions[action])
    }
  }
}
Die Regeln selbst sind natürlich anwendungsspezifisch. In unserer Beispielapplikation hängen die Zustände der Buttons nur vom allgemeinen editMode sowie von der Information ab, ob es für irgendeines der angezeigten Property-Files eine Datenänderung (durch den Benutzer) gibt. Diese Funktion fieldSelection() wird im Anschluß am Ende jedes Ereignisbehandlers aufgerufen, der (wenigstens potentiell) die Datenänderung ermöglicht:
// -------------------------------------------------------------------
// Make parts of the UI invisible or disabled, depending on the mode
// -------------------------------------------------------------------
  function fieldSelection() {
    var dataLoss = getDataLoss()
    ui.fieldSelection({
      invisible:{
        "save":!editMode
      },
      disabled:{
        "reload":!tableContainer.querySelector("table"),
        "save":editMode && !dataLoss
      }
    })
    
  }

Tabellen

Die dataTables von Allan Jardine sind unbestritten der Mercedes unter den Tabellenpräsentationen. Sortieren (auch nach mehreren Spalten), Suchen, Filtern, fixierbare Zeilen und Spalten, Blättern, Datenquelle auf dem Client oder Server, editierbare Zellen und formatierbare Zellinhalte: es wäre schön, wenn es eine in den Browser eingebaute, über Standard-Sprachelemente ansprechbare Komponente gäbe, die all das bietet. Aber davon sind wir noch weit entfernt.[4]

Andererseits genügen oft die Standardfunktionen der HTML-<table> für eine einfache Darstellung tabellenförmiger Daten: immerhin gibt es eingebaut bereits Kopf- und Fußzeilen, über Zeilen wie auch über Spalten verbindbare Zellen, Beschriftungen, editierbare Zellen und ein komfortables, auf Tabellendaten zugeschnittenes DOM-API.

Für strukturierte Daten nehmen wir das gute alte Standardbeispiel, einen Umsatzbericht: ein Unternehmen habe in seiner Nordgruppe 15.273 Mio. $, in der Südgruppe nur 10.358 Mio. $ erwirtschaftet. Diese Daten mögen von irgendwoher kommen, z.B. durch einen Ajax-Request vom Server. Dann liegen sie in Form von JavaScript-Daten vor, z.B.

[
  ["North","15.273 M$"],
  ["South","10.358 M$"]
]
Schön wäre nun die Möglichkeit, diese Daten in ein Tabellenobjekt zu übernehmen (das dann in den HTML-DOM eingebunden wird), etwa so:
var t = new ui.Table()
  t.addHeader(["Region","Revenue"])
  t.addData([
    ["North","15.273 M$"],
    ["South","10.358 M$"]
  ])

Nehmen wir ferner an, wir haben einen Bereich (repräsentiert durch ein <div>-Element) als Behälter für eine dynamisch zu erstellende Tabelle, auf den wir im JavaScript mit einer Konstanten tableContainer zugreifen. Dann würden wir die Tabelle mit dieser appendChild().Anweisung präsentieren:

tableContainer.appendChild( t.table )
Mit einer Prise CSS für thead td, tbody td und tbody td:first-child gewürzt, erscheinen die Daten dann wie folgt:

Hier nun der JavaScript-Code für eine Klasse, die das leistet - wir verwenden die Klassen-Syntax von ES6 sowie die eingebauten DOM-API-Funktionen für Tabellen wie insertRow(), insertCell() usw. Die Klasse ist eine Art Scharnier zwischen den Rohdaten und dem Markup für deren Präsentation.

class Table {

  constructor() {
    this.table = createElement("TABLE")
    this.thead = this.table.createTHead()
    this.tbody = this.table.createTBody()
  }

// Expects an array, containing the header texts
  addHeaders(headers) {
     var tr = this.thead.insertRow()
     for (let col of headers) {
       tr.insertCell().textContent = col
     }
     return this
  }

// Expects an array of arrays, containing cell data
  addData(data) {
    for (let row of data) {          
      let tr = this.tbody.insertRow()
      for (let cell of row) {            
        tr.insertCell().textContent = cell
      }
    }
    return this 
  }
}
Mit dieser Klasse ist ein Grundstock gelegt, der sich bei Bedarf erweitern läßt. So genügte es mir schon für diese Anwendung nicht mehr, nur Werte als Tabellendaten zu übergeben: ich wollte auch pro Zelle ein Objekt mit Attributwerten übergeben können, etwa eine oder mehrere CSS-Stilklassen für das <td>-Element. Daher habe ich die Methode addData() wie folgt umgeschrieben:
  addData(data) {
    for (let row of data) {           
      let tr = this.tbody.insertRow()
      for (let cell of row){             
        let td = tr.insertCell()
        if (typeof cell == "object") {
          setAttributes(td,cell)
        } else {
          td.textContent = cell
        }
      }
    }
    return this  
  }
...
// ----------------------------------------------------------------------------
// Set attributes on a (given) element
// ----------------------------------------------------------------------------  
   function setAttributes(e,atts) {
     if (atts) {
       for (let a in atts) {
         if (a=="childNodes") {
           for (let n of atts[a]) e.appendChild(n)
         } else if (a=="textContent" || a=="innerHTML") {
           e[a] = atts[a]   
         } else if (a=="classList") {
           atts[a].forEach(c=>e.classList.add(c))        
         } else {
           e.setAttribute(a,atts[a])
         }
       }
     }
   }
Die Funktion setAttribute(), die hierbei abfiel, ist für beliebige Elemente verwendbar, nicht nur für den konkreten Fall der Tabellenzellen.

Editierbarkeit mit contenteditable

Wenn man einem HTML-Element (hier: den mit <td> beschriebenen Tabellenzellen) das Boolesche Attribut contenteditable gibt, so verwandelt sich ihr textförmiger Inhalt in ein Eingabefeld, wenn der Benutzer darauf klickt. Er kann dann Text eingeben, und standardmäßig wird er bereits in das Element übernommen, wenn der Benutzer seine Eingabe abschließt, indem er beispielsweise den Focus wechselt.

Wenn die Eingaben darüberhinaus noch im eigenen JavaScript-Code weiterverwendet werden sollen, kann man sich für das Ereignis input registrieren, das allerdings nach jeder Eingabe ausgelöst wird, also nach jedem eingegebenen Buchstaben eines Wortes. Ein Ereignis für eine vollständig abgeschlossene Eingabe gibt es nicht (also für den Zeitpunkt, wenn sich das Eingabefeld wieder schließt und das Element wieder in ein reines Textanzeigeelement verwandelt).

In unserer Anwendung geben wir im editMode allen generierten Zellen das Attribut contenteditable. Nach den Benutzereingaben wollen wir die geänderten Texte in das jeweilige Properties-Objekt übernehmen. Dazu registrieren wir uns für das input-Event auf der Ebene des tableContainer-Elements:

tableContainer.addEventListener("input",handleInput)
Nun ist der tableContainer nur ein Bereich, der potentiell mit einer Tabelle gefüllt wird und dann erst editierbare Tabellenzellen enthält. Wir kommen aber mit dieser einen Registrierung auf Ebene des umfassenden Bereichs aus, weil nicht abgefangene Events aufsteigen (bubbling up), bis sie auf einen Ereignisbehandler stoßen. Das erste, was wir daher im Eventbehandler prüfen, ob das originale Element, das das Event ausgelöst hat, wirklich eine Tabellenzelle ist:
// -------------------------------------------------------------------
// The user edited cell content
// -------------------------------------------------------------------
   function handleInput(e) {
     var cell = e.target
     if (cell.nodeName != "TD") return
     // Value before change 
     var oldValue = cell.getAttribute("data-old-value")
     // Current value = value after change
     var newValue = getCellText( cell )
     // Leave if there were no changes     
     if (newValue == oldValue) return
     // Key (first column) or value (subsequent columns) changed?
     if (cell.cellIndex == 0) {
        // Key changed
        if (newValue.match(/\S/)) {
          updateKey( cell )
        }
     } else {
        // Value changed
        var key = getCellText( cell.parentNode.firstElementChild )
        updatePropertyValue(
          cell.cellIndex-1,
          key,
          newValue)
     }    
     // Save this change
     cell.setAttribute("data-old-value",newValue)
     // Mark blank cells   
     cell.classList.toggle("empty",!/\S/.test(newValue))
     // Recompute button states
     fieldSelection()
   }
Danach müssen wir unterscheiden, ob ein Schlüssel (erste Spalte der Tabelle, also cell.cellIndex == 0) oder ein Wert (cell.cellIndex > 0) geändert wurde. Im ersten Fall müssen die in den einzelnen Property-Files angegebenen Werte dem neuen Schlüsselwert zugeordnet werden, während die alte Schlüssel/Wert-Paarung gelöscht wird. Wurde ein Wert geändert, muß dagegen diese Änderung nur in dem betroffenen Property-Objekt nachgezogen werden.

Einen Wert löschen

Das contenteditable-Attribut reicht nicht aus, um dem Benutzer auch die Möglichkeit zu bieten, einen Wert oder eine ganze Zeile von Schlüssel/Wert-Paaren zu löschen. Hierfür sehen wir im editMode ein kleines am rechten Rand der Zelle vor, das als Schaltfläche zum Löschen fungiert.

Die HTML-Struktur einer Zelle, die die Löschfunktion anbietet, ist nun

<td data-old-value="Behalten" title="Defined in row 1" contenteditable="true">
  Behalten
  <div class="delete" contenteditable="false">✖</div>
</td>

Aus Effizienzgründen ist es sinnvoll, die "Verzierung" mit der Löschfunktion erst dann einzubauen, wenn sie auch wirklich benötigt wird, d.h. beim Event mouseover. Wir registrieren also den Behandler

if (editMode) tableContainer.addEventListener("mouseover",handleOnMouseOver)
Wieder prüfen wir, wenn das Ereignis ausgelöst wird, zuerst, ob das auslösende Element wirklich eine Tabellenzelle ist:
  // In change mode, offer the "delete" icon
  function handleOnMouseOver(e) {
    var cell = e.target;
    // Only on table cell level
    if (cell.nodeName != "TD") return
    // If the value is missing anyway, "delete" makes no sense
    if (cell.classList.contains("missing")) return
    // An empty cell requires a real text node as first child
    // Otherwise, the "contentEditable" attribute won't work properly
    if (cell.classList.contains("empty")) setCellText(cell," ")
    // Not if the icon had already been created earlier
    var deleteArea = cell.querySelector(".delete")
    if (!deleteArea) {
      // First mouseover: create it
      deleteArea = document.createElement("div")
      // Mark the div as delete area
      deleteArea.className = "delete"
      // It's not editable, unlike the rest of the cell
      deleteArea.contentEditable = false
      // The icon as unicode symbol: 
      deleteArea.textContent = "✖"
      // Plug it into cell
      cell.appendChild(deleteArea)
      // Attach the "handleDelete" function
      deleteArea.addEventListener("click",handleDelete)
    }    
  }
Wurde das Ereignis wirklich in einer Zelle ausgelöst, so bauen wir das Lösch-Handle ein, falls es nicht bereits existierte (von einem früheren mouseover erzeugt), und registrieren den Behandler handleDelete zur Ausführung des Löschens.

Dieses Lösch-Handle fällt nun etwas aus dem Rahmen: es gehört zwar zu der Zelle, die wir mit contenteditable markiert haben, ist aber selbst nicht editierbar. Es muß also von der Editierbarkeit explizit ausgeschlossen werden. Auch können wir nun den in der Zelle eingegebenen Text nicht einfach mit dem Attribut textContent ansprechen, da dieses Attribut auch die Textinhalte untergeordneter Elemente liefert. Daher brauchen wir eigene Funktionen zum Lesen und Schreiben von Text in den Zellen:

// -------------------------------------------------------------------
// Read a value from a table cell
// -------------------------------------------------------------------
  function getCellText(cell) {
    // Check the first text node only
    var text = cell.firstChild && cell.firstChild.data || ""
    // If the cell is marked empty, the content is ""
    if (!text.match(/\S/) && cell.classList.contains("empty")) return ""
    return text
  }
  
// -------------------------------------------------------------------
// Set a table cell with a value
// -------------------------------------------------------------------
  function setCellText(cell,text) {
    if (cell.childNodes.length == 0 || cell.firstChild.nodeType == Node.TEXT_NODE) {
      var textNode = document.createTextNode(text)
      cell.insertBefore(textNode,cell.firstChild)
    }
    else {
      cell.firstChild.data = text
    }
  }
Was geschieht nun bei Click auf "Löschen"?
// -------------------------------------------------------------------
// Delete one or several key/value pairs
// -------------------------------------------------------------------
  function handleDelete(e) {
    if (!e.target.classList.contains("delete")) return
    var cell = e.target.parentElement
    var row = cell.parentElement
    var key = getCellText( row.firstElementChild )
    var value = getCellText( cell )
    // Does the cell belong to the column of a single properties file? 
    var index = cell.cellIndex
    if (index > 0) {
      // Delete value in a single properties file
      propList[index-1].deleteValue(key,value)
    } else {
      // Delete values of that row from all properties files 
      propList.forEach((p,i)=>p.deleteValue(key,getCellText(row.cells[i+1])))
    }    
    compare(propList)
    e.stopPropagation()
    fieldSelection()
  }
Die Information über die zu löschenden Werte werden ermittelt, danach wird die deleteValue()-Methode der entsprechenden Property-Objekte aufgerufen. Ist dies erfolgt, wird der Vergleich der Property-Objekte neu aufgerufen, und schließlich die Feldauswahl. Das weitere Aufsteigen des Click-Events in der DOM-Hierarchie muß hier unbedingt verhindert werden (mittels e.stopPropagation(), da das Click-Event sonst auch noch den Editiermodus für die betreffende Zelle öffnen würde, wenn es beim <td>-Element angekommen ist.

Die node-webkit-Anwendung

Die JavaScript-Plattform node-webkit (auch unter dem neuen Namen nwjs.io) würde es erlauben, eine Anwendung mit HTML-UI zusammen mit der nodejs-Laufzeit in eine ausführbare Datei zu packen (genauer: je Zielsystem eine, also eine für Windows, eine für Linux, eine für Mac). Diese Lösung finde ich unelegant, da der Hersteller einer Software riesige redundante Datenmengen im Weltnetz herumschleudert: wenn er effizient und ohne Redundanzen entwickelt, benötigt die Logik seiner Applikation vielleicht nur 50 KB, aber die ausführbare Datei kann locker tausendmal so groß werden. Dem steht natürlich der Vorteil gegenüber, daß der Anwender keinen separaten Installationsaufwand hat, da er node und webkit nicht separat installieren muß. Als Vorteil könnte man auch ansehen, daß node und webkit in der ausführbaren Version auf einem festen, eingefrorenen Stand sind. Es kann nicht zu einem Abbruch der Anwendung aufgrund inkompatibler Erweiterungen von node und webkit kommen.

Wie auch immer. Meine bevorzugte Version ist ein Icon nwjs auf dem Desktop, auf das ich per Drag und Drop den Ordner mit meinen Anwendungsressourcen ziehen kann, um sie zu öffnen und auszuführen. Einzig notwendig ist dafür, daß der Ordner ein package.json File mit Angaben für die nwjs-Laufzeit enthält. Wichtigste Angabe ist main, das eine URL für das Start-HTML (das index.html) enthalten muß, mit dem die Applikation beginnt. Dies ist in der Regel eine File-URL, kann aber auch eine http-URL sein. Für eine Anwendung wie diese, deren Ressourcen vollständig aus dem Web geladen werden, genügt es, daß der Ordner die Datei package.json enthält.

So sieht das package.json für den Property Editor aus:

{  
  "name": "propcmp",  
  "main": "http://ruediger-plantiko.net/property-editor/?edit",
  "node-remote": "http://ruediger-plantiko.net",
  "window": {
    "width":1800,
    "height":1200
  }
}
Man kann Angaben zur Fenstergröße, aber auch zum Resizing machen, könnte einen Fenstertitel angeben, weitere Browser-Plugins zulassen u.a.m. Es gibt eine Spezifikation für derartige Package-Files von CommonJS-Anwendungen, allerdings interpretiert nwjs nur einen Teil der dort spezifizierten Felder.

Dateiauswahl

Die Applikation ist eine reine Client-Applikation. Datenquelle ist das Dateisystem. Die Daten, die der Benutzer anzeigen oder bearbeiten will, sind .poperties-Dateien auf seiner Festplatte. Nun ist in Browser das Arbeiten mit dem Dateisystem gewissen Restriktionen unterworfen, aber die Auswahl und das Einlesen von Dateien sind möglich. Was im Browser aber nicht geht, ist das Speichern und die Verwendung von Dateinamen im JavaScript-Layer.

In HTML5 wurde eine File API eingeführt, eine Klasse für dateiartige Objekte, Spezialisierung der Klasse Blob, die beliebige, nicht änderbare Rohdaten repräsentiert. Es können Dateiobjekte mit JavaScript-generiertem Inhalt erzeugt werden, z.B. kann man Graphiken dynamisch generieren und im Browser anzeigen oder zum Download anbieten, ohne daß der Server hierfür etwas tun müßte. Dateiobjekte können auch mit der Klasse FileReader gelesen und ausgewertet werden – und natürlich an den Server hochgeladen werden.

Zur Eingabe von Dateien gibt es das Element <input type="file">. Es ist an den Dateiauswahldialog des jeweiligen Betriebssystems angeschlossen. Mehrfachauswahl ist mit dem Booleschen Attribut multiple möglich. Hat der User seine Auswahl durch Druck auf "Öffnen" bestätigt, löst das Element ein change-Event aus und stellt die ausgewählten Dateien in Form von File-Objekten in seinem DOM-Listenattribut files zur Verfügung. Diese files kann man dann in seinem Ereignisbehandler verwenden.

Leider ist man mit diesem Dateiauswahldialog sehr nah am Betriebssystem, verläßt gleichsam die reine Browser-Ebene. Die Darstellung des Dialogs und des <input type="file">-Elements lassen sich kaum beeinflussen. Das <input type="file">-Element wird wie ein Standardbutton dargestellt, die Zuweisung von Stilklassen für Rahmen, Hintergrundfarbe usw. bleibt wirkungslos.

Die einfachste Lösung für dieses Problem ist, dem Eingabeelement einen <label> zuzuordnen und das Eingabeelement selbst unsichtbar zu machen. Indem das <label>-Element mit dem for-Attribut dem (unsichtbaren) Eingabeelement zugeordnet wird, übernimmt es dessen Funktion (auch bei Click auf den Label erscheint der Auswahldialog, und nach erfolgter Auswahl löst das Eingabeelement das change-Event aus).

Mit diesem Wissen implementiert man also eine Dateiauswahlmöglichkeit im HTML-Dokument wie folgt:

   <div>
     <input type="file" multiple id="property-files">
     <label class="button" for="property-files" data-action="choose-files">
       Choose files
     </label>
     <div class="additional-info" id="selected-files">No files selected</div>
   </div>
Das Element <div class="additional-info" id="selected-files"> soll dabei die Namen der vom Benutzer ausgewählten Dateien anzeigen, es muß bei jedem change aktualisiert werden.

Das <label class="button"> ist dann per CSS so eingerichtet, daß es wie ein Button aussieht (was gar nicht so schlimm gelogen ist, da es ja auch wie ein Button funktioniert):

button, label.button {
    background-color: blue;
    color: white;
    padding: 4px 8px;
    font-weight: bold;
    font-size:11pt;
    margin: 7px 1px;
    border: solid gray 1px;
    cursor:pointer;
}
input[type=file] {
  display:none;
}
Die Registrierung des change-Behandlers erfolgt dann ganz normal über addEventListener:
     const inputFiles     = document.getElementById("property-files")
  ...
  inputFiles.addEventListener("change",handleChooseFiles)
Der Ereignisbehandler selbst liest dann die Dateien ein, stellt sie dar und aktualisiert den Statustext #selected-files:
// -------------------------------------------------------------------
// The user selected some files via button "Choose files"
// -------------------------------------------------------------------
   function handleChooseFiles(evt) {
     var files = Array.from(this.files)
     var status
     if (files.length) {
       status = files.map(f=>f.name).join(',')
       reload(files)
     }
     else {
       status = "No files selected"
     }
     document.getElementById("selected-files").textContent = status      
   }

Die Entwickler von node-webkit haben wegen dieser Unzulänglichkeiten im HTML erwogen, ein eigenes API für die Dateiauswahl zu entwickeln, was natürlich technisch möglich wäre. Letztlich haben sie zugunsten der Standardkompatibilität entschieden, die Dateiauswahl ebenfalls wie die Browser über das Element <input type="file"> zu steuern. Der obige Code verhält sich also in einer node-webkit-Anwendung identisch wie in einem Browser.

Anzumerken ist noch, daß ein File-Objekt im normalen Webmodus aus Sicherheitsgründen die Information über den Pfad der Datei verbirgt. Bekommt die Webseite aber höhere Zugriffsrechte, so enthält das File-Objekt auch ein Attribut path mit dem Dateipfad. Dies ist zum Beispiel der Fall, wenn die Seite als UI einer nwjs-Anwendung eingesetzt wird.

Das Datenmodell - die Klasse Properties

Als Modell für .properties-Dateien verwende ich eine Klasse i18n.Properties. Sie nimmt im Konstruktor einen Dateinamen und einen Array von Textzeilen entgegen, woher auch immer dieser Array kommt. Es gibt also keine Verknüpfung mit der File-Klasse, die Textzeilen könnten auch aus einer <textarea> genommen werden, in die sie der Benutzer manuell eingibt, oder per HTTP-Request von einer entfernten Quelle (z.B. als Konfigurationsdatei für eine Web-Anwendung, die beim Laden gelesen wird). Die Klasse wird darüberhinaus auch von der konkreten Codierung des Zeilenumbruchs unabhängig, die ja je Betriebssystem variieren kann.

Die Textzeilen werden nun einzeln dem Parser vorgelegt, der in einer eigenen Klasse PropertyRow implementiert ist, und durch Instanzen dieser Klasse ersetzt: das ist buchstäblich ein Array.map(), der den Text der Zeile in ein geparsedes Objekt verwandelt.

Mit Hilfe eines regulären Ausdrucks läßt sich der Parser für die .properties-Syntax sehr kompakt notieren:

  parseRow(row) {
    var o = {}
    // Parse text line into structured PropertyRow object
    // Using a regex, which represents the rules
    // - Arbitrary whitespace at the beginning allowed
    // - Followed by a 'key=value' instruction, where key must not contain '#'
    // - Or followed by a '#' and arbitrary more characters ( = comment )
    // - A line consisting only of whitespace is allowed 
    row.replace(
      /\s*(?:(?:([^#=\s]+)\s*=(.*))|#\s*(.*?)\s*$)/,
      (match,key,value,comment)=>
        Object.assign(o, {
          key:key,
          value:value,
          comment:comment
        }))
    if (Object.keys(o).length == 0) {
      o = (/\S/.test(row)) ? { error:true, input:row } : { blank: true, comment: "" }
    }
    if (o.key && !/\S/.test(o.value)) o.empty = true
    return o
  }

Im Konstruktor der Klasse PropertyRow wird das vom Parser erzeugte einfache JavaScript-Objekt in die gerade entstehende Instanz von PropertyRow injiziert:

  constructor(row) {
    if (row!==undefined) {
      Object.assign(this,this.parseRow(row))
    }
  }

Für Zeilen, die ein Schlüssel/Wert-Paar enthalten, werden Schlüssel und Wert als eigene Komponenten key und value fortgeschrieben. Kommentare, erkennbar an ihrer Einleitung mit einem Doppelkreuz #, landen in der Komponente comment. Leerzeilen werden mit dem Booleschen Attribut blank markiert. Ist nur der Wert leer, erhält das PropertyRow-Objekt das Attribut empty.

Zeilen, die nicht dem erlaubten Format entsprechen, werden als fehlerhaft markiert, bleiben aber im Array erhalten, solange ihre Entfernung durch Aufruf der Methode stripErrors() nicht ausdrücklich gefordert wird.

Das Properties-Objekt ist auch modifizierbar. Sobald erstmalig etwas geändert wurde, wird eine Kopie der Instanz erzeugt und im Attribut old gespeichert. Das virtuelle Attribut dataLoss gibt die Information zurück, ob etwas geändert wurde:

//-----------------------------------------------------------------------    
// Have data been changed
//-----------------------------------------------------------------------    
    get dataLoss() {
      return !!this.old
    }
Das Properties-Objekt kann nach den Schlüsseln sortiert werden. Dabei werden Kommentare, die einem Name/Wert-Paar vorangehen, mitsortiert. Eine Präambel aus Kommentaren, die das Dokument einleitet, bleibt auch beim Sortieren am Anfang stehen. Das Ende der Präambel muß allerdings durch eine Leerzeile erkannt werden, da sie sonst mit dem einleitenden Kommentar des ersten Name/Wert-Paars verwechselt werden kann.

Der Dateivergleich

Die Hauptfunktion der ganzen Anwendung ist der Vergleich mehrerer Property Files. Dafür wurde sie auch geschrieben: wenn Property Files für die Ablage sprachabhängiger Texte verwendet werden, ist es nützlich, ein Werkzeug zu haben, das die Texte in den verschiedenen Sprachen vergleicht. Sind die Texte zu einem Schlüsselwert in allen Sprachen gepflegt? Kommen Texte zu einem Schlüssel mehrfach vor (was eigentlich weder erwünscht noch erlaubt ist, aber natürlich passieren kann)?

Für diese Aufgabe gibt es die Funktion i18n.compareProperties(propList). Das Argument propList ist ein Array von Properties-Objekten. Das Ergebnis ist eine nach Schlüsseln sortierte Tabelle (als Array von Arrays), mit den Schlüsselwerten als erster Spalte und danach einer Spalte für jedes der übergebenen Properties-Objekte. Kommen Schlüssel mehrfach vor, so werden zu diesem Schlüssel mehrere Zeilen erzeugt - gerade soviele, um alle Vielfachheiten in allen Properties-Objekten darzustellen.

In jeder Zelle ab der zweiten Spalte wird ein JavaScript-Objekt übergeben, das im Attribut value den Wert und in title einen Kommentar (z.B. den Hinweis, in welcher Zeile der Datei dieser Eintrag gefunden wurde) enthält. Enthält der Wert nur Leerzeichen, so ist das Boolesche Attribut empty gesetzt. Fehlt zum Schlüssel dieser Zeile ein Wert, so ist das Boolesche Attribut missing gesetzt (das kann auch so sein, wenn für einen Schlüssel in einem Dokument mehrere Werte existieren, in einem anderen nur einer).

Die Übergabestruktur ist nicht HTML-spezifisch, die Funktion könnte daher auch in völlig anderen UIs verwendet werden.

Unit Tests

Um das Modul i18n mit Unit Tests abzusichern, verwende ich das Testframwework Mocha in Kombination mit der Chai Assertion Library (in seinem "klassischen" TDD flavour).

Mocha läßt sich sowohl als Kommando unter nodejs aufrufen, als auch im Web in einer Testseite. Am Anfang meines Testfiles i18n.test.js kann ich die Tatsache, ob es gerade im Desktop unter nodejs oder im Browser in der Testrunner-Seite aufgerufen wird, daran, ob die globale Variable window existiert (Browser) oder nicht existiert (Desktop). Je nach Umgebung lade ich das zu testende Modul i18n mittels nodejs, oder weiß bereits, daß es existiert (weil es in der Testseite via <script> includiert wurde).

// Setup the test frameworks, depending on environment
if (typeof window == "undefined") {  
  // My desktop environment (mocha / nodejs)
  let mut = process.env.MODULE_UNDER_TEST
  i18n = require(mut).i18n
  assert = require('chai').assert
} else {  
  // Browser (via testrunner page)
  i18n = window.i18n
  assert = chai.assert
}
Im Editor UltraEdit habe ich mir einen Menüpunkt make test konfiguriert, der genau dieses Kommando im aktuellen Verzeichnis aufruft. Die Aktion test ist im Makefile wie folgt definiert:
test:
 @H:/uedit32/tools/nodejs-test.bat H:/Documents/i18n/i18n.js
.PHONY: test
Das Batchfile nodejs-test.bat, auf das ich mich hier beziehe, enthält folgende Anweisungen:
@echo off
set NODE_PATH=C:/Program Files/nodejs/node_modules
set MODULE_UNDER_TEST=%1
"C:/Program Files/nodejs/node_modules/.bin/mocha" -u tdd
Im Ultraedit werden mir nun während des Entwickelns die Ergebnisse der Unit Tests im Ausgabefenster angezeigt:

Ich kann aber auch diese HTML-Testseite im Browser aufrufen, dort kann ich mit der Developer Toolbar den Code auch debuggen:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Property Files - Unit Tests</title>
    <link rel="stylesheet" type="text/css" href="mocha.css">
  </head>
  <body>
  <h1>Property Files - Unit Tests</h1>
  <div class="samples">   
    <div id="mocha"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.4.1/mocha.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.min.js"></script>

    <!-- Code to test: -->
    <script src="../i18n.js"></script>
    <!-- Test files: -->
    <script>
      mocha.setup("tdd")
    </script>
    <script src="i18n.test.js"></script>
    <script>
      mocha.run();
    </script>
  </body>
</html>
Hier ist der Link auf die Testrunnerseite
http://ruediger-plantiko.net/property-editor/test/
Die Resultatseite erscheint so in einem Chrome Browser:

Fußnoten

[1] Lediglich dem JavaScript mußte ich in jener Zeit noch mit dem Framework Prototype etwas auf die Beine helfen, und auch nur, um es flüssiger und lesbarer zu machen - inzwischen sind auch solche Frameworks, auch das verbreitete jQuery, weitgehend überflüssig.
[2] Wobei XML weiterhin bleiben wird, es hat seine ganz besonderen Stärken wie Typsicherheit dank XML Schema und die gute Transformierbarkeit in andere Formate dank XSLT. Behalten wir seine Nähe zu HTML im Hinterkopf - und daß es auch in allen gängigen Browsern nativ unterstützt wird.
[3] Selbst das Sorgenkind IE/Edge ist mittlerweile zu einem tolerierbaren Erwachsenen herangewachsen, über dessen Leistungen man zwar nicht in Begeisterungsstürme verfällt, aber der immerhin seine Arbeit mehr oder weniger befriedigend macht.
[4] Soweit ich das überblicke, scheint es noch nicht einmal einen Working Draft für eine solche Komponente zu geben.

Sonntag, 12. Februar 2017

Über direkte Demokratie

Der Staat - Obrigkeit oder Instrument des Volkswillens?
Volksentscheide
Herrschaft, Macht und Volksherrschaft
Die Systemfrage
Die Gefahren des Machtmißbrauchs
Gewaltentrennung
Die unpolitische Masse
Subsidiarität
Vox Populi: Vox Dei oder Vox Rindvieh?
Populisten und Demagogen
Demokratie und Multikulti
Fazit

Der Staat - Obrigkeit oder Instrument des Volkswillens?

Wer als Deutscher einige Zeit in der Schweiz lebt, der spürt unmittelbar, daß die Schweizer im allgemeinen ein besseres Verhältnis zu ihrem Staat haben als die Deutschen - es lebt in ihrem Bewusstsein, daß ihr Staat etwas ist, das sie sich leisten und der in seiner konkreten Ausprägung ihrem gemeinsamen Willen unterworfen ist.

Dagegen fühlen sich Deutsche einer Obrigkeit unterstellt, die, selbst wenn sie sie wählen, nicht in ihrem Dienst steht, sondern ihnen als eine potentiell feindliche Gruppe gegenübersteht. Dasselbe Gefühl, nur von der anderen Seite, haben auch deutsche Politiker: obwohl allein durch Wahlen, also vom Volk legitimiert, fühlen sie sich vor dem Volk durch einen besonderen Sachverstand und ein besonders hohes Verantwortungsbewußtsein ausgezeichnet; je weiter sie in der Hierarchie nach oben gelangen, umso mehr glauben sie, die Kompetenz eines Erziehers zu besitzen, der das Volk dorthin führen muß, wohin es von sich aus nicht will. Exemplarisch wird das im Ausspruch des Bundespräsidenten Gauck deutlich: Die Eliten sind gar nicht das Problem, die Bevölkerungen sind im Moment das Problem.

Volksentscheide

Mir erscheint das Verhältnis der Schweizer zu ihrem Staat als das gesündere; es mag eine Reihe historischer Gründe für diesen Unterschied geben – ein wichtiger Grund liegt aber sicher darin, daß in der Schweiz auf Bundesebene über politische Schicksalsfragen abgestimmt werden kann. Diese direkte Einflussmöglichkeit fördert das Bewußtsein, daß dieser Staat ihr Projekt ist und es um ihre eigenen Interessen geht.

Das legendäre Nein des Schweizer Volkes zum EWR-Beitritt (am 6. Dezember 1992) - wenn auch mit einer unglaublich knappen Mehrheit - zeugt, rückblickend gesehen, von einer grandiosen Weitsicht. Von Sorge um das Wohl des Schweizer Volkes getragen sind auch die Abstimmungen über die Begrenzung der Religionsausübungsfreiheit (in Deutschland schrankenlos gemäss Art.4(2) GG) durch das Minarettverbot (am 29. November 2009) – eine deutliche Abgrenzung vom Islam – und die Rückkehr zum Einwanderungsreglement von vor 2007, also mit Kontingenten und Höchstzahlen (die Masseneinwanderungsinitiative (MEI) vom 9. Februar 2014, auch wenn die Umsetzung dieses Volksentscheides von der politischen Klasse sabotiert wurde, was zugleich ein Durchsetzungsproblem der direkten Demokratie aufwirft).

Dem kann man in Deutschland als ein Beispiel von vielen die Einführung des Euro am 2. Mai 1998 entgegenhalten: sie geschah gegen den Willen des deutschen Volkes, und die Regierung wusste das. Kanzler Helmut Kohl sagte rückblickend in einem Interview: Bei der Einführung des Euro war ich wie ein Diktator... Eine Volksabstimmung über die Einführung des Euro hätten wir verloren... und zwar im Verhältnis 7 zu 3. [1] Auch hier muß man dem Volk einen größeren Weitblick als seinen Eliten attestieren: die Geschichte Deutschlands und Europas wäre sicher ganz anders verlaufen ohne diese desaströse EU-Währung und den Versuch, die Völker Europas von der Wirtschaft her, mit dem Hilfsmittel einer Währung, von oben zu einem neuen Kunstvolk nach dem Vorbild der USA zusammenzuschweißen.

Herrschaft, Macht und Volksherrschaft

Max Weber lehrte uns, Macht von Herrschaft zu unterscheiden: während Macht die Chance bedeutet, innerhalb einer sozialen Beziehung den eigenen Willen auch gegen Widerstreben durchzusetzen, gleichviel worauf diese Chance beruht, bedeutet Herrschaft die Chance, für einen Befehl bestimmten Inhalts bei angebbaren Personen Gehorsam zu finden. [2] Der "soziologisch amorphen" Macht steht die in einem historischen und sozialen Kontext gegründete Herrschaft gegenüber. Vereinfacht gesagt, ist Herrschaft gewollte Macht: die Menschen, die bereit sind, dem Herrschaftsinhaber Gehorsam zu leisten, haben dabei das Wohl eines gemeinsamen Ganzen im Auge - ihres Volkes, in dem ihre Eigenexistenz und ihre Einzelperson verankert sind: damit das Volk blühen und gedeihen kann, müssen gewisse kollektive Aufgaben im Interesse aller geregelt werden.

Seit der Zeit der Aufklärung wurde es Mode zu glauben, alle Herrschaft vor dem Aufkommen der modernen parlamentarischen Demokratien wäre illegitim, nicht wirklich vom Volk gewollt gewesen. Das ist naiv. Selbstverständlich war auch die Herrschaft der Könige und Fürsten nicht bloß Macht, sondern Herrschaft im Weberschen Sinne. Ich hatte das bereits in meinem Blogpost Gewollte Herrschaft ausgeführt.

Die Systemfrage

In einem intellektualistischen Zeitalter neigen wir dazu, die Frage nach dem konkreten Herrschaftssystem überzubewerten. Seit Platos Politeia-Träumereien war es eine grosse Versuchung für Intellektuelle, den ganzen Staatsaufbau idealerweise aus einigen einfachen Prinzipien herzuleiten und die Gesellschaft aus der als richtig erkannten Idee heraus neu zu formen.

In Abwandlung von Wilhelm Busch könnte man formulieren

(...)
Im Durchschnitt ist man kummervoll
und weiß nicht, was man machen soll.

Nicht so der Intellektuelle: kaum mißfällt
ihm diese altgebackne Welt,
so knetet er aus weicher Kleie
eine einwandfreie neue.

Das ist eine ungesunde Übertreibung des Theoretischen - mehr noch, es ist eine Hybris, die überall dort, wo sie sich Geltung verschaffen konnte, gewaltige Verheerungen angerichtet hat. Denn politische Systematiker haben die Neigung, gewachsene und eingespielte soziale Strukturen, auf denen das Funktionieren der Gesellschaft gründet, ohne viel Federlesens abzuschaffen, wenn sie nicht in ihr Schema von der idealen Gesellschaft passen.

Vor allem ist die Systemfrage nicht von der überragenden Bedeutung, wie sie vielen erscheint. Es gibt wichtigere Dinge, die über das Gelingen oder Scheitern eines zivilisierten Gemeinwesens entscheiden. In der Hauptsache hängt das Gelingen eines Staates davon ab, daß in der Bevölkerung ein gemeinsamer moralischer Kompass existiert - daß der einzelne gewissermaßen moralisch eingenordet ist, daß er mit seinen Mitmenschen die Bereitschaft zum guten Handeln teilt und sich überhaupt mit seinen Mitmenschen - über alle persönlichen Sympathien und Antipathien hinausgehend - als Teil einer Gemeinschaft empfindet, sich um deren Wohl sorgt und bereit ist, für sie Opfer zu bringen, nicht nur Nutzen aus ihr zu ziehen.

Das stellt auch die Frage nach der Demokratie – gar der direkten, um die es hier geht, in die richtigen Relationen. Es sind grundsätzlich auch andere politische Ordnungen als die parlamentarisch-demokratische denkbar, in denen legitime Herrschaft ausgeübt wird und ein zivilisiertes Miteinander herrscht: die genannten Voraussetzungen – das Zusammengehörigkeitsgefühl und das Bemühen des einzelnen, sich an den gemeinsamen moralischen Normen zu orientieren – sind viel entscheidender für Gedeih und Verderb der Gesellschaft als ihre historisch-konkrete politische Gestalt.

Die Gefahren des Machtmißbrauchs

Alle Macht geht vom Volk aus... und kehrt nie wieder dorthin zurück, lautet ein alter sarkastischer Scherz. Menschen haben leider eine unausrottbare natürliche Neigung, die Macht, die ihnen einmal übertragen wurde, zu behalten, zu festigen, auszudehnen und für ihre persönlichen Zwecke auszunutzen. Ihre Machtposition birgt ein gewaltiges Schadensrisiko. Die einzige Möglichkeit, dieses Schadenspotential wenigstens begrenzt zu halten, besteht in der Kontrolle der Herrschenden durch das Volk, in dessen Dienst sie stehen.

Gerade weil Völker nun einmal die relevanten politischen Subjekte darstellen, die den Angelpunkt politischen Denkens bilden müssen, kann einem jedes Mittel recht sein, um die politischen Repräsentanten möglichst eng an den Volkswillen zu binden. Gerade wenn man weiß, daß Korruption und Machtmißbrauch nicht aus der menschlichen Natur auszumerzen sind, sollte einem daran liegen, die Übertragung politischer Macht auf einzelne Volksvertreter möglichst risikoarm zu gestalten: beispielsweise ist es allgemeine Meinung, daß Volksvertreter nicht auf Lebenszeit, sondern nur für einige Jahre ein politisches Amt übernehmen sollten.

Um es mit Montesquieu zu sagen:

Die politische Freiheit ist nur unter maßvollen Regierungen anzutreffen. Indes besteht sie selbst in maßvollen Staaten nicht immer, sondern nur dann, wenn man die Macht nicht mißbraucht. Eine ewige Erfahrung lehrt jedoch, daß jeder Mensch, der Macht hat, dazu getrieben wird, sie zu mißbrauchen. Er geht immer weiter, bis er an Grenzen stößt. Wer hätte das gedacht: Sogar die Tugend hat Grenzen nötig.[3]

Gewaltentrennung

Ein wichtiges Korrektiv, um Machtmißbrauch zu verhindern, ist auch die Grenzsetzung politischen Handelns durch das Recht, das von einer streng getrennten Judikative überwacht und gepflegt wird – also die Gewaltentrennung (der Begriff Gewaltentrennung ist nicht nur die korrekte Übersetzung aus dem englischen Original separation of powers, sondern drückt auch besser als "Gewaltenteilung" aus, daß es nicht um eine Zusammenarbeit verschiedener, im Grunde gleichgeschalteter Herrschaftsbereiche geht, sondern um echte gegenseitige Kontrolle). Auch dieser Grundsatz der Gewaltentrennung ist in Deutschland nicht verwirklicht, vor allem wegen der Ernennung der rechtsprechenden durch die exekutive und legislative Gewalt.

Wenn eine Gesellschaft beansprucht, auf eine bestimmte Weise verfasst zu sein, diese Verfassung aber nur auf dem Papier vorzufinden ist, gedeiht die Heuchelei. In dem Bewusstsein, daß selbst die grundlegendsten Prinzipien des Rechtsstaats nicht verwirklicht sind, erscheint auch jeder andere Rechtsbruch als eine läßliche Sünde. Bis in die höchsten politischen Kreise hinein (und dort mit den schlimmsten Auswirkungen) blüht die Mentalität des Legal - illegal - scheißegal, so daß wir ständig mit ungeahndeten Rechtsbrüchen gewaltigen Ausmaßes durch die Exekutive konfrontiert sind.

Wie auch immer das Recht im einzelnen ausgestaltet ist - die Rechtssicherheit ist von entscheidender Bedeutung für ein zivilisiertes Gemeinwesen. Aus gutem Grund zitierte Papst Benedikt XVI. auf seiner Rede vor dem Bundestag am 22.9.2011 den Ausspruch des Hl. Augustinus:

Nimm das Recht weg – was ist dann ein Staat noch anderes als eine große Räuberbande? [4]
Jeder Bürger braucht Rechtssicherheit - für die Planung seiner Arbeit, seiner Unternehmungen, seines eigenen Lebens wie auch des seiner Familie. Rechtssicherheit gehört zu den kollektiven Gütern, für deren Erhalt wir uns einen Staat überhaupt leisten (darin ist sie z.B. der Infrastruktur ähnlich, nur fundamentaler).

Die unpolitische Masse

Ein häufiger Einwand gegen direkte Demokratie – der genauso gegen Demokratie überhaupt angeführt werden kann – lautet: ein großer Teil der Bevölkerung sei doch an politischen Fragen sowieso desinteressiert, würde sich gar nicht um Politik kümmern.

Ja und? Es war so, seit es Menschen gibt: der vermutlich größte Teil der Menschen kreist um seine persönlichen Belange - sie bestellen ihre Scholle, kümmern sich um ihre Lieben und lassen ansonsten den Kaiser einen guten Mann sein. Daran ist auch nichts Verwerfliches. Schon der "Prediger" resümierte vor Jahrtausenden:

Darum merkte ich, daß nichts Besseres darin ist, denn fröhlich sein und sich gütlich tun in seinem Leben. Denn ein jeglicher Mensch, der da isst und trinkt, und hat guten Mut in seiner Arbeit, das ist eine Gabe Gottes. (Prediger 3,12-13)

Herrschaftssysteme kommen und vergehen, Monarchen und Präsidenten treten auf und ab, aber die Erde dreht sich weiter, und die grundlegende metaphysische Verfasstheit dieser Welt, die moralische Substanz und die Natur des Menschen bleiben sich gleich. Wieso sollte man es daher als ein Übel ansehen, wenn viele sich gar nicht um die Details kümmern? Sie arbeiten und kümmern sich um ihren Anteil am Ganzen - sie sind nicht Parteigenosse, nicht Bezirksrat oder Stadtpräsident, aber sie sind die tragenden Säulen der Gesellschaft: sie schaffen den Wohlstand, von dessen Zehnten die Eliten sich bloß nähren, hier lebt in gesunden Gesellschaften auch der Gemeinschaftsgeist und das Zusammengehörigkeitsgefühl. Von Gnaden dieser schaffenden Menschen, und um dieser Menschen willen, existiert der ganze Laden überhaupt.

Auf die Frage der Abstimmungen bezogen, gilt natürlich: Wer schweigt, scheint zuzustimmen. Mehr als die Möglichkeit ihrer Stimme kann kein System den Menschen geben. Ob sie es gebrauchen, liegt in ihrer Hand. Die Alternative: der Versuch, das Volk zu höherer politischer Bewußtheit zu erziehen, ist stets gescheitert und mündete sehr schnell in Systeme, in denen der Staat sich zum Vormund seiner Bürger aufspielt.

Subsidiarität

Demokratie kann umso weniger gelingen, je mehr den Menschen von oben in ihre Belange hineingepfuscht wird. Die Kompetenzen der Eliten sind nicht nur durch Gewaltentrennung zu kontrollieren, sondern grundsätzlich auf das mindest Nötige zu beschränken. Dieser Grundsatz heißt Subsidiarität. So steht die ganze Machtpyramide auf gesundem Fundament: der, der ganz oben steht, hat nicht etwa die meiste Macht, sondern nur gerade soviel Macht, wie benötigt wird, um Belange zu regeln, die in darunterliegenden Einheiten nicht geregelt werden können. Ganz unten in der Pyramide steht der Souverän, der Mensch – durch sein Leben verankert in seiner Familie, seiner Gemeinde, seiner Religion, seinen Vereinen, seinem Volk. Was auch immer er sinnvoll für sich regeln kann, soll er tun – wo es nicht möglich ist, sollen dies die nächsten ihn umhüllenden Gemeinschaften tun.

Das Subsidiaritätsprinzip wird in der Theorie überall gutgeheißen, es steht in allen Verfassungen, sogar in einer für alle Mitglieder verbindlichen EU-Charta. In der politischen Praxis wird ihm aber entgegengearbeitet: so ist es beispielsweise das unverhohlene Ziel der EU wie auch der Bundesregierung, Kompetenzen der Nationalstaaten in der Innen-, Außen-, Verteidigungs- und Wirtschaftspolitik auf die EU zu übertragen und auch die Legislativen der EU-Mitgliedsstaaten zu bloßen Akklamationsorganen für Gesetzesentwürfe aus Brüssel zu degradieren.

Vox Populi: Vox Dei oder Vox Rindvieh?

Vox Populi - vox Dei, Volkes Stimme - Gottes Stimme, liegt darin nicht eine unerhörte Anmaßung? Schon Alkuin, Berater Karls des Großen, hielt nicht viel von diesem offenbar uralten Grundsatz:
Auf diejenigen muss man nicht hören, die zu sagen pflegen, "Volkes Stimme, Gottes Stimme", da die Lärmsucht des Pöbels immer dem Wahnsinn sehr nahe kommt. [5]
Später wurde der Spruch in Deutschland zu Vox populi, vox Rindvieh verhunzt, worin sich die gleiche Verachtung des "Pöbels" ausdrückt. Immer sind da Leute, die es besser wissen als das Volk, in dessen Auftrag sie handeln sollten. Die die Macht, sobald sie sie einmal ergriffen haben, für alles mögliche gebrauchen, ohne sich ihrem Volk noch in irgendeiner Weise verpflichtet zu fühlen. Das Volk wird zum lästigen Pöbel degradiert, für den man sich allenfalls schämt und den es lediglich ins politische Kalkül einzubeziehen gilt. Ansonsten weiß man besser als dieser Pöbel, was zu tun ist – man kann ja lesen und schreiben, man gehört einer anderen Klasse an: der politischen Klasse.

Dabei enthielt der Ausspruch vox populi - vox Dei eine tiefe Wahrheit. Selbstverständlich ist nicht etwa die Hybris gemeint, das Volk würde sich zum Gott erheben, den Platz Gottes einnehmen. Vielmehr wird gesagt, daß Völker gottgewollt sind und die wahre Legitimationsquelle für alles politische Handeln darstellen. Jedes politische Amt besteht nur zu Diensten des Volkes, ist nur durch das Volk legitimiert. Solange Völker sich als Völker empfinden, spricht durch sie der Wille Gottes.

Daß es in einem Volk Experten und Laien gibt, daß es Kluge und weniger Kluge gibt – geschenkt. Entscheidend ist, daß alle sich gemeinsam dem Besten ihres Volkes verpflichtet fühlen. Auch Kompliziertes muß sich, wenn es sehr Grundsätzliches betrifft, in seinen Grundzügen einfach darstellen lassen können.

Auch handelt direkte Demokratie nicht davon, alle, auch die kleinsten alltäglichen Details des Regierens per Volksentscheid regeln zu wollen – diese werden weiterhin delegiert an die politischen Repräsentanten, und selbstverständlich ist zu ihrer Ausführung Expertise notwendig. Der Repräsentant weiß, daß die von ihm angekündigten politischen Zielsetzungen vom Volk gebilligt sind, sonst wäre er nicht da, wo er ist. Er muß nicht bei allem, was er nun in seiner Amtszeit im politischen Alltag entscheidet, den neuesten Umfragen hinterherhecheln. Das wäre eine mißverstandene direkte Demokratie zu Lasten der politischen Handlungsfähigkeit des Volkes. Die Volksentscheide gehen vielmehr auf die großen Linien, auf Langfristiges, Strategisches, auf Schicksalsfragen der Nation (oder was ein hinreichender Teil des Volkes dafür hält).

Populisten und Demagogen

Das Pejorativum Populismus wird in den Haupststrommedien im Endlosmodus verwendet, um die Opposition zur herrschenden politischen Klasse zu diskreditieren. Hierzu hat Frank Furedi bereits 2014 in einem brillanten Essay alles Relevante gesagt.

Der im Wort enthaltene Vorwurf an den politischen Gegner lautet im Kern, er würde (unzulässigerweise) "einfache Lösungen für komplizierte Probleme" anbieten. Da erheben sich gleich mehrere Einwände:

  • Erstens gibt es ja für eine Reihe von Problemen tatsächlich einfache und zugleich wirkungsvolle Lösungen.
  • Zweitens gibt es auch eine Scheinkomplexität, hinter der sich Machthaber verschanzen können: tatsächlich Einfaches kann als unglaublich kompliziert und detailreich dargestellt werden, so daß nur Experten es noch überschauen können (die man daher also dringend weiterhin auf der payroll des Volkes braucht). Der Kaiser kann glauben, ein gar kunstvolles Gewand zu tragen, das aus den exotischsten Stoffen verwoben und versponnen ist - und doch in Wahrheit nackt sein.
  • Drittens ist die unzulässige Vereinfachung, die es natürlich unbestreitbar gibt, nicht auf ein bestimmtes politisches Lager beschränkt. Die einzige Möglichkeit, diese zu entkräften, liegt darin, die voraussehbaren schädlichen Wirkungen dieser Vereinfachung seinerseits in einfacher, für alle verständlichen Form im politischen Diskurs klarzustellen.
Auf Volksentscheide angewandt, bleibt hier zu wiederholen: was sich überhaupt sagen läßt, das läßt sich auch klar sagen. Gerade wenn es um die großen Linien, die Schicksalsfragen des Volkes geht, die jeden angehen, muß es möglich sein, den richtigen Weg und den Nutzen für das Volk auch in klaren Worten und in einfacher Form zu vermitteln, so daß nicht nur Experten es verstehen, sondern jeder mit gesundem Menschenverstand begabte Bürger ebenso.

Aber kann nicht ein Demagoge mit populistischen Sprüchen an die Macht kommen? Ja, das ist möglich. Wenn er Demokratie und Gewaltentrennung beibehält, kann man ihn zügig wieder abschaffen. Wenn er sich nicht daran hält, wird es schwieriger: dann ist ein – in der Regel blutiger – Seitenstrang der Geschichte eröffnet, bis Recht, Gesetz und volkslegitimierte Herrschaft wiederhergestellt sind. Noch schwieriger wird es, wenn sich die Machthaber nur teilweise an Demokratie und Gewaltentrennung halten und sich nur mit den Lippen zu ihrer Verfassung bekennen - wie im heutigen Deutschland.

Grundsätzlich ist zu diesem Einwand zu sagen: leider gibt es grundsätzlich keine Herrschaftsform ohne das Risiko ihrer Selbstabschaffung. Demokratie, Volksentscheide, Gewaltentrennung sind auch nur Sicherungsmechanismen, sie enthalten keine Ewigkeitsgarantie für das System, das sie schützen sollen. Ja, es ist wahr: es kann demokratisch die Abschaffung der Demokratie beschlossen werden. So wie "leben immer lebensgefährlich ist", so gibt es auch keine Versicherung gegen den Untergang einer gesellschaftlichen Ordnung. Die Möglichkeit existiert und ist prinzipiell unvermeidlich. Das habe ich an anderem Ort bereits diskutiert.

Demokratie und Multikulti

Ein wirklich ernstzunehmender Einwand gegen Demokratie liegt darin, daß ihre Grundlage, ein gesunder, seine Identität pflegender Demos, seit Jahrzehnten systematisch zersetzt wird. Ohne Demos aber gibt es keine Demokratie. Unter dem jeder Begründung enthobenen Dogma "Diversität ist gut" wird schon seit langem der massenhaften Einwanderung anderer Völker nicht nur kein Widerstand entgegengesetzt, sondern sie wird noch ausdrücklich willkommen geheißen.

Wenn aber das Volk durch einen Vielvölkerstaat ersetzt wird, zerfällt seine Handlungs- und Willenseinheit, Voraussetzung aller demokratischen Entscheidungsfindung. Statt Demokratie gibt es dann nur noch Lobby- und Partikularinteressen, der Staat wird nur noch zur Interessenvertretung einzelner konkurrierender Gruppen, die ihn sich zur Beute zu machen suchen. Wenn das Volk demontiert wird, gibt es auch nicht mehr das gemeinsame Wohl des Volkes als Ziel, dem sich alle Gruppen verpflichtet fühlen. Die Geschichte lehrt, daß in solchen Gemengelagen früher oder später ein brutaler Kampf der einzelnen Gruppen um die Vorherrschaft einsetzt. Das friedliche Miteinander ist eine Fiktion.

Aber gerade weil das so ist: gerade weil die Fragmentierung des Volkes zwar droht, aber noch nicht besteht, ist die Forderung nach mehr Demokratie wichtig, weil sie das Volk stärkt, solange es noch eine Mehrheit in seinem eigenen Territorium darstellt.

Fazit

Es gibt keine andere Legitimation politischer Herrschaft als durch das Volk: das ist eigentlich mit dem alten Satz vox populi - vox Dei gemeint. Bei jeder andere vermeintlichen Rechtfertigung von Herrschaft stellt sich sofort die alte Juvenalsche Frage quis custodiet custodes? Wer überwacht die Bewacher?

Aus diesem Grunde sind Volksentscheide zu wichtigen, strategischen Fragen der Politik eine wertvolle Ergänzung der repräsentativen Demokratie. Sie stärken auch das Bewußtsein des Volkes, daß der Staat sein Projekt ist - daß er seinem Volk zu dienen hat.

Gerade in einer Zeit, in der die Regierungen mit globalistischen Flausen im Kopf auf eine Zerstörung gewachsener Völker hinarbeiten, gerade wenn die Uhr des multikulturalistischen Zerstörungswerkes erbarmungslos tickt, sollten die Völker alle nur möglichen Pfeile im Köcher haben, die ihnen zur Selbstbehauptung gegen ihre irrlichtelierenden Herrscher noch zur Verfügung stehen. Dazu gehören insbesondere bindende Volksentscheide (und nicht etwa bloß konsultative Volksbefragungen), wie in der Schweiz.

Quellen

[1] Kohl, Helmut: Bei der Euro-Einführung war ich ein Diktator, merkur.de vom 11.4.2013, https://www.merkur.de/politik/helmut-kohl-bei-euro-einfuehrung-diktator-zr-2846068.html
[2] Weber, Max: Wirtschaft und Gesellschaft, Mohr Siebeck (Tübingen), 1972, S. 28.
[3] Montesquieu, Vom Geist der Gesetze (1748), Reclam (Stuttgart) 1994, S. 215.
[4] Augustinus von Hippo, De civitate dei, IV.4.1
[5] Brief Alkuins an Karl den Großen (798?), bei http://www.dmgh.de

Montag, 7. November 2016

Jass-Spielpläne ohne Wiederbegegnung

Das Jass ist ein im alemannischen Sprachraum verbreitetes Kartenspiel mit vier Spielern. Es erfreut sich in der Schweiz großer Beliebtheit, und häufig werden Jass-Turniere organisiert, bei denen in jeder Runde an mehreren Tischen gleichzeitig gespielt wird. In der folgenden Runde wechseln die Teilnehmer nach einem vorgegebenen Spielplan ihre Plätze und spielen somit gegen andere Teilnehmer.

Dabei soll der Spielplan gewährleisten, dass eine maximale Durchmischung stattfindet. Die Spieler sollen idealerweise in jeder Runde gegen andere Spieler antreten. Eine Frage, die man sich hierbei stellen kann, ist:

Wieviele Runden kann ein Spielplan maximal vorsehen, so daß sich keine zwei Teilnehmer in mehr als einer Runde an einem Tisch begegnen?
Nehmen wir ein Turnier in mittlerer Größe an, mit 24 Teilnehmern, die somit an sechs Tischen miteinander spielen.

Eine Obergrenze für die Rundenzahl ergibt sich natürlich aus der Gesamtzahl der Spieler: da jeder Spieler in jeder Runde gegen drei andere Teilnehmer antritt, kann es nicht mehr als sieben solcher Runden geben: denn in einer achten Runde gäbe es nur noch zwei Spieler, gegen die er noch nicht gespielt hat – nicht genug, um einen Tisch vollzubekommen. Nun ist dies eine sehr theoretische Obergrenze. Ich kann zeigen:

Ein Spielplan kann maximal genau M=5 Runden enthalten, in denen jeder Spieler in jeder Runde gegen andere Leute spielt.
Obwohl ich das dringende Gefühl habe, dass es einen sehr einfachen Beweis geben muss, um M=6 und M=7 auszuschließen, habe ich einen solchen nicht gefunden. Ich kann im folgenden nur einen Computerbeweis angeben, und diese haben die unschöne Eigenschaft, daß sie von einigen sehr puristischen Mathematikern nicht anerkannt werden – im Grunde mit dem gleichen Argument, das vor über zweitausend Jahren schon zum Ausschluß von Archimedes aus der Akademie der Wissenschaften von Alexandria geführt hat: daß nämlich durch Beweise dieser Art – ebenso wie durch die damaligen Wasserverdrängungsmessungen des Archimedes – "der reine Geist der Mathematik mit schmutziger Materie befleckt wird". Wer es also schafft, einen rein geistigen Unmöglichkeitsbeweis für die Fälle M=6 oder wenigstens M=7 zu führen, ist herzlich eingeladen, mir diesen mitzuteilen.

Doch nun zur Beweisführung. Sämtliche möglichen Spielpläne maschinell durchzurechnen, sprengt sehr schnell die Leistungsgrenzen von Computern. Eine einzelne Runde bietet ja bereits 24! = 6.2045·1023 Möglichkeiten, die Spieler aufzuteilen. Mit allzu brutaler brute force geht es also nicht. Wir müssen ein paar Symmetrie- oder Äquivalenzüberlegungen voranstellen, um die Zahl der zu prüfenden Möglichkeiten signifikant zu verringern.

Schauen wir uns einmal einen Spielplan mit zwei Runden an, der die gewünschte Bedingung erfüllt:

Wie man an den Farben direkt sieht, sitzt jeder Spieler in der zweiten Runde tatsächlich nur mit Spielern an einem Tisch, die in der ersten Runde an anderen Tischen saßen. Dieses Beispiel beweist also durch seine bloße Existenz: M≥2.

Hinter diesem Übergang von der ersten zur zweiten Runde verbirgt sich eine Struktur, die ich (4,4)-Relation nenne: wenn wir uns die Sache auf Tischebene anschauen, steht jeder Tisch der ersten Runde mit genau vier Tischen der zweiten Runde in Beziehung: nämlich mit den vier Tischen, an denen seine Spieler in der zweiten Runde Platz nehmen. Umgekehrt steht jeder Tisch der zweiten Runde mit genau vier Tischen der ersten Runde in Beziehung: nämlich den vier Tischen der ersten Runde, von denen die Spieler des Tisches in der zweiten Runde herkommen.

Wir haben also dem Übergang von der ersten zur zweiten Runde eine Relation R auf der Menge mit 6 Elementen zugeordnet, mit der Eigenschaft |R-1(x)| = |R(x)| = 4 für alle x = 1,...,6. Eine solche Relation läßt sich – wie jede Relation – als eine nur mit Nullen und Einsen besetzte quadratische Matrix darstellen. In diesem Beispiel hat die Matrix die Gestalt

Relation der beschriebenen Art sind dadurch charakterisiert, dass ihre darstellende Matrix in jeder Spalte und in jeder Zeile genau vier Einsen (und somit genau zwei Nullen) enthält. Die Menge solcher Matrizen bildet eine Teilmenge aus der Menge aller Relationen. Hat letztere 236 Elemente (klar: auf jedem Platz der Matrix kann eine Eins oder eine Null stehen), enthält erstere nur noch 67950 Elemente. Das kann man durch simples Durchzählen ermitteln, etwa in einem kleinen JavaScript-Programm. Es ist aber auch eine in der Mathematik bekannte Zahl aus der relativ gut erforschten OEIS-Zahlenfolge A001499. Nennen wir diese Menge R64;4

Nun ist klar, dass die Lösung eine Lösung bliebe, wenn man die Tische in der ersten oder zweiten Runde einfach nur umstellen würde. Mathematisch gesprochen, bedeutet dies, dass die Gruppe 𝔖6 der 6!=720 Permutationen (möglichen Umstellungen) der Tische von links und von rechts auf R64;4 operiert. Lösungen, die bis auf Anordnung der Tische gleich sind, wollen wir als äquivalent betrachten.

In welche Äquivalenzklassen (Orbits) zerfällt die Menge R64;4 bezüglich dieser Relation?

Auch dies lässt sich rechnerisch ermitteln: ich habe es hier getan. Das Ergebnis:

  1. Orbit mit 1350 Elementen
  2. Orbit mit 16200 Elementen
  3. Orbit mit 7200 Elementen
  4. Orbit mit 43200 Elementen

Hier sind Repräsentanten für die vier Orbits, wobei ich jeweils eine symmetrische Matrix als Repräsentant gewählt habe:

Da die Umordnung der Tische in jedem Schritt keine "neue" Lösung generiert, kann man sich nun darauf beschränken, für jeden Übergang von einer Runde zur nächsten genau eine aus diesen vier konkreten Relationen zugrundezulegen. Man hat damit eine Vorschrift, an welchen Tischen die Spieler eines Tischs in der nächsten Runde gelangen, wobei automatisch sichergestellt ist, dass keine zwei Spieler desselben Tischs wieder an einem Tisch landen - denn die vier Spieler eines Tischs der neuen Runde kommen ja immer auch von vier verschiedenen Tischen der vorherigen Runde. Ob die Spieler allerdings in irgendwelchen davor liegenden Runden gegeneinander spielten, kann man damit natürlich nicht ausschliessen.

Es gibt nun noch einen weiteren Freiheitsgrad: man kann nicht nur für den Übergang von einer Runde zur nächsten eine der vier o.a. Relationen verwenden, sondern darüberhinaus auch die vier Spieler, die an einem Tisch landen, beliebig permutieren. Das scheint auf den ersten Blick genauso irrelevant wie das Umordnen der Tische - ist es aber nicht: denn die Reihenfolge der Spieler am Tisch entscheidet darüber, an welchen vier Folgetischen sie jeweils landen (wenn die Konvention ist, dass der erste Spieler des Tischs am am weitesten links liegenden Tisch landet, zu dem sein Tisch in Relation steht, der zweite dann am zweiten usw. - dies nur, um für die (4,4)-Relationen eine eindeutige Vorschrift für den Übergang zur nächsten Runde einzurichten). Da wir an jedem Tisch eine solche Permutation vornehmen können, haben wir weitere (4!)6 = 191'102'976 Möglichkeiten pro Runde.

Das ist immer noch gewaltig viel, möchte man meinen – zumal sich die Möglichkeiten ja pro Runde multiplizieren müssten. Nun fallen aber viele Möglichkeiten weg – die meisten Spielpläne verletzen die Anforderung, dass keine Wiederbegegnung mit Spielern aus früheren Runden stattfinden darf: diese brauchen dann natürlich auch nicht mehr fortgesetzt zu werden. Ebenso gibt es terminale, nicht mehr erweiterbaren Spielpläne, bei denen jede mögliche nächste Runde zu Wiederbegegnungen führt.

Diese Überlegung gab mir Anlaß zu einem Backtracking-Verfahren: ich versuche, einen bereits bis zu einer bestimmten Runde aufgebauten Plan weiter zu ergänzen, indem ich die Tischpermutationen und die Übergangsmatrizen der Reihe nach anwende und schaue, ob eine begegnungsfreie Runde entstanden ist. Wenn ja, versuche ich, noch weiter fortzufahren (also weitere Runden anzufügen). Wenn nein, melde ich den bis dahin aufgebauten Spielplan als "terminalen" (nicht mehr erweiterbaren) Spielplan an das Hauptprogramm und setze dann auf der letzten Runde auf, um weitere Kombinationen zu finden (pro Runde habe ich mir den "Zustand" gemerkt: den aktuell verwendeten Vektor von sechs Permutationen, und die aktuell verwendete Übergangsmatrix).

Hier ist eine terminale Lösung mit fünf Runden – zugleich der Beweis für M≥5 (ich habe die Tischanordnungsfreiheit noch genutzt, um die Spieler 1 bis 4 ab der zweiten Runde an den Tischen 1 bis 4 sitzen zu lassen):

Der Algorithmus ist alle Möglichkeiten durchgegangen und hat keine Spielpläne mit mehr als fünf Runden gefunden. Das ist der Computerbeweis für M=5.

Wer es selbst überprüfen möchte (ich finde zwar keinen Fehler im Algorithmus, aber Menschen machen Fehler): der Algorithmus ist hier als Worker implementiert und wird von der Webseite http://ruediger-plantiko.net/jass/ aufgerufen.

Sonntag, 23. Oktober 2016

Preist ihn, alle Völker!

Die Israelreise, die ich mit meinem Sohn im Sommer 2015 unternommen habe, hat sich mir (und auch meinem Sohn) tief eingeprägt. Ein besonders starker Eindruck - unter den vielen wertvollen - waren mir meine Empfindungen bei Betrachtung der Galerie im Innenhof der Verkündigungsbasilika in Nazareth, die also zu Ehren der Erwählung von Maria als Mutter des inkarnierten Gottes erbaut wurde. Eine Erwählung, die sie in Demut, aber auch in vollem Bewusstsein der Größe dieses Ereignisses für die gesamte Menschheit annahm: "Von nun an werden mich selig preisen alle Geschlechter."

Die Galerie zeigt nun, Nation für Nation, Bilder, die diesen Moment würdigen. Diese Bilder drücken sehr schön die einzelnen Volksseelen aus, das Wesenhafte, das jedem dieser einzelnen Völker eignet. Jedes Volk preist diesen Moment auf seine ganz besondere Weise. Sie alle porträtieren die Maria in einer Weise, die ihre Art ausdrückt, sich dem Ideal anzunähern.

Nur als ein Beispiel für diese vielen Wesensarten bringe ich ohne Kommentar hier das Bild der Spanier.

Ich finde es anrührend, sich das zu vergegenwärtigen: die Völker haben ihre ganz besondere Weise zu sein, die sich über die Generationen entwickelt hat, ihr kostbares Eigenes. Und aus diesem Eigenen heraus richten sie ihren Blick hinauf - zu Gott. So hat jedes Volk sein positives, sein verehrungswürdiges Moment, seinen ganz spezifischen Beitrag zum Ganzen.

Die Frage "Was ist denn dieses Volkswesen?" können wir nicht so einfach beantworten, wie wir zum Beispiel die Frage nach irgendeinem Ding dieser Welt beantworten können (etwa: ist Australien eine Insel?). Das ist aber nicht besonders verwunderlich. Wir können ja nicht einmal die Frage nach dem Wesen eines einzelnen Menschen befriedigend beantworten. Auch wenn wir einen intuitiven Begriff von ihm haben und selbstverständlich seine Existenz als ganz besonderes, einzigartiges Geschöpf anerkennen: der ganz konkrete Mensch, wie er vor uns steht, läßt sich wesenhaft nicht erschöpfend beschreiben. Um wieviel hoffnungsloser ist diese Frage dann für Menschengruppen, für Völker. Für den Gläubigen urständet eine Menschenseele ebenso wie eine Volksseele in der spirituellen Welt. Natürlich ist sie real - aber auf eine tiefere Weise, als es unser gewöhnlicher, an den Dingen dieser Welt geschulter Verstand fassen kann. Die katholische Tradition wußte noch vom Engel eines Volkes (Anthroposophen würden verbessern: nein, Erzengel!), der dessen Schicksal impulsiert, anleitet, auf seinem Weg durch die Zeiten begleitet und der in besonderer Weise mit der Essenz seines Volkes verbunden ist.

Der Psalm 117 (Vulgata: 116), mit nur zwei Versen der kürzeste Psalm, ja das kürzeste Kapitel der gesamten Bibel, drückt den Gedanken der vielen Völker, die um den Altar des Höchsten versammelt sind, sehr schön aus:

Interessante Nebenbemerkung, dass hier in einem typisch orientalischen Parallelismus zweimal das Gleiche mit leicht unterschiedlichen Wörtern gesagt wird: die goyim (Völker) loben den Herrn, und die ha'umim (Völker) preisen ihn. Zwar haben die beiden Wörter für "Volk" leicht unterschiedliche Bedeutungswolken – das erste (goyim) wird in der Bibel oft, aber nicht immer, auf die Fremdvölker der Ungläubigen verengt. Aber schon in einer der ersten Verwendungen, in Exodus 19,6, verheißt Gott den Israeliten, "ein Königreich von Priestern und ein heiliges Volk" (goy kadosch) zu sein.

So zelebriere ich Multikulti: nicht die Abschaffung der Grenzen und die allgemeine Völkerwanderung schafft das Multikulturelle, sondern wir haben bereits das Multikulturelle: es ist die Bejahung der gewordenen Traditionsströme - in ihren Räumen und mit ihren besonderen Menschengruppen, in denen sich dieses Spezifische jeweils zubereitet hat und weiter entwickelt.

Auf Twitter würdigte jemand zu Recht den folgenden kurzen Gesang der russischen Nationalhymne als die letzte Bastion des Volkszusammenhaltes, von der - wie von Familie und Religion - noch ernsthafter und entschiedener Widerstand gegen das Globalisierungsprojekt der Eliten kommt.

Er hat recht: mit Mätzchen wie Wirtschaftssanktionen kann man diesem Geist nicht beikommen - im Gegenteil, es wird ihn weiter stärken. Sicher ist das nicht so ein spontanes Singen gewesen, wie es auf den ersten Blick scheint: denn die Russen mögen ihr Volk lieben, aber sie tragen deswegen trotzdem nicht die ganze Zeit Russlandfähnchen mit sich herum. Es sieht eher wie ein Flashmob aus. Wie auch immer - die Vorführung überzeugt.

Tatsächlich ist die russische Hymne ein beeindruckendes Zeugnis für diese edle, dem Ideal zugewandte Seite des Völkischen, die die höchsten Willenskräfte jedes einzelnen ansprechen kann, der diesem Volk entstammt und der sich den Erhalt und die Weiterentwicklung dieses Eigenen zur persönlichen Aufgabe gemacht hat.

Nachdem ein anderer Twitterer auf die dunklen Seiten der Völker hinwies – in diesem Fall der USA – erinnerte der ThinkPunk, dass – bei allen Schattenseiten der gegenwärtigen Politik – auch dieser Hymne etwas Großes und Besonderes zugrundeliegt, das es zu zelebrieren gilt.

Natürlich gilt es wie bei uns zu fragen: entspricht diese dunkle Seite der US-Politik mit ihrem zerstörerischen Interventionismus überhaupt der Volkseigenart, dem Wesen des Volkes, wie es sich zum Beispiel in seinen Hymnen ausdrückt? Die Frage stellt sich umso drängender, wenn ein Gauck zum Beispiel ganz unverhohlen ausspricht, dass er nicht die Eliten, sondern die Völker als das gegenwärtige Problem ansieht.

Jedenfalls hat das Ideale der USA seine Berechtigung und ist zu würdigen, so wie jedes andere Volk sein Ideales pflegen und würdigen sollte. Der Blogger Angel Millar ("People of Shambhala") versucht in dem sehr lesenswerten Artikel American Dasein, the USA and deep identity in the multipolar world, sich dem Wesenhaften der USA, dem Guten der USA, in seinen Grundzügen anzunähern.

In diesem Kontext gesehen, haben wir Deutsche natürlich die Aufgabe, unsere eigene historische Besonderheit, die Mission unserer Volksseele zu erspüren. Wie kann dieses Besondere der Deutschen, das ideale Besondere der Deutschen denn verstanden werden? (Und von Richtern und Henkern will ich nichts hören, wenn ich nach dem idealen Volkswesen frage – finsterste Abgründe tun sich bei allen Völkern auf.)

Da ist zunächst die Hymne – die erste Strophe Deutschland, Deutschland über alles, daß man also sein eigenes Volk mehr liebt als die anderen Völker der Welt, ist zunächst nicht besonders spektakulär: so wird auch ein Kind seine eigene Mutter mehr lieben als alle andern Mütter dieser Welt, und dieser Vergleich ist sehr eng, denn Völker sind Herkunftsgemeinschaften (mit einer historisch-kulturellen und einer biologischen Komponente).

Die heute führende dritte Strophe Einigkeit und Recht und Freiheit für das deutsche Vaterland hat dagegen eine herausragende Bedeutung, denn sie vermittelt zwischen dem kollektiven Ideal (Einigkeit) und dem Freiheitsideal durch das Recht, genauer: die Rechtsstaatlichkeit. Die beiden im Extrem unguten Stimmungen: des Aufgehens in der Masse einerseits und des Rufes nach absoluter individueller Selbstbestimmung andererseits werden versöhnt im Recht. Das Recht schützt die Freiheit und lebt von der Einigkeit. Das Recht - und damit die Freiheit - könnte auf Dauer nicht existieren ohne die freiwillige Leistung der Menschen, sich gemeinsam als ein Volk zu fühlen. Denn ohne Demos, ohne Volk, entartet auch die Demokratie zu einer Karikatur (nämlich zum Lobbyismus: jede Teilgruppe kämpft für ihre Interessen, ohne daß ein gemeinsames Höheres gesehen wird, dem sich alle verpflichtet fühlen). Allein schon in diesem Vers drückt sich der deutsche Wunsch nach der Mitte aus, in der man das Wesentliche zu finden hofft.

Nicht nur geographisch ist der Begriff "Mitte Europas" zu verstehen – er steht auch für ein den Extremen abholdes, vermittelndes Element, und für die Frage nach dem Wesentlichen in all dem flackernden Hin- und Hergewoge um uns herum, die berühmte deutsche Innerlichkeit. Das ideale Deutsche, das wesenhaft Deutsche wird besonders gut durch die Figur des Faust porträtiert, der "weit entfernt von allem Schein / nur in der Wesen Tiefe trachtet."

Diese deutsche Suche nach einem überdauernden Sinn, nach der Gewissheit spendenden, aus der eigenen Wesenstiefe bejahten Überzeugung, birgt eine gewaltige Kraft. Wenn sich diese Überzeugung einmal Bahn bricht, ist sie von einer großen Kraft und verleiht eine gewaltige, kaum überwindbare Wehrhaftigkeit. Denn vor der Wahrheit, nach der hier gesucht wird, flieht das ganze Nachtgezücht wie die Vampire vor dem Licht. "Deutsch sein heißt: eine Sache um ihrer selbst willen zu tun", hat Richard Wagner einmal gesagt. Wenn die Deutschen zu diesem Geist wieder finden, können sie auch wieder ihr Licht leuchten lassen im großen Regenbogen der Völker dieser Welt.

In der Suche nach dem geistig Wesenhaften können wir unseren Beitrag zu einem echten Multikulti leisten!