<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de-x-formal">
	<id>https://glossar.hs-augsburg.de/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Sautter</id>
	<title>GlossarWiki - Benutzerbeiträge [de-formal]</title>
	<link rel="self" type="application/atom+xml" href="https://glossar.hs-augsburg.de/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Sautter"/>
	<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/Spezial:Beitr%C3%A4ge/Sautter"/>
	<updated>2026-07-17T14:16:22Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.43.3</generator>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_Canvas:_MiniPong_04&amp;diff=42018</id>
		<title>HTML5-Tutorium: Canvas: MiniPong 04</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_Canvas:_MiniPong_04&amp;diff=42018"/>
		<updated>2016-12-02T10:31:15Z</updated>

		<summary type="html">&lt;p&gt;Sautter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:Canvas:MiniPong:Menü}}&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;: &amp;lt;code&amp;gt;[https://glossar.hs-augsburg.de/beispiel/tutorium/es5/minipong/WK_MiniPong04/web/index.html index.html]&amp;lt;/code&amp;gt;&lt;br /&gt;
([https://glossar.hs-augsburg.de/beispiel/tutorium/es5/minipong/WK_MiniPong04/ WK_MiniPong04 (SVN)])&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Leeres Projekt&#039;&#039;&#039;: &amp;lt;code&amp;gt;[https://glossar.hs-augsburg.de/beispiel/tutorium/es5/minipong/WK_MiniPong04_empty/web/index.html index.html]&amp;lt;/code&amp;gt;&lt;br /&gt;
([https://glossar.hs-augsburg.de/beispiel/tutorium/es5/minipong/WK_MiniPong04/ WK_MiniPong04_empty (SVN)])&lt;br /&gt;
&lt;br /&gt;
==Ziel: Das fertige Spiel „MiniPong“==&lt;br /&gt;
Im vierten Teil des Tutoriums wird eine funktionsfähige Version von MiniPong erstellt.&lt;br /&gt;
&lt;br /&gt;
===Use Cases===&lt;br /&gt;
[[Datei:MiniPong04 UseCases.png|ohne|882px|Use Cases der Tutoriums-Anwendung &amp;lt;code&amp;gt;MiniPong&amp;lt;/code&amp;gt;]]&lt;br /&gt;
Ein Spieler kann das Spiel starten (Startknopf) und vorzeitig &lt;br /&gt;
beenden (Stopp-Knopf). Nach Spielstart kann der Spieler den Schläger&lt;br /&gt;
am unteren Rand des Spielfeldes mit Hilfe der Richtungstasten des &lt;br /&gt;
Keyboards nach links und rechts bewegen.&lt;br /&gt;
&lt;br /&gt;
Nachdem das Spiel gestartet wurde, bewegt sich&lt;br /&gt;
der Ball geradlinig im Spielfeld, wobei die Startrichtung zufällig&lt;br /&gt;
gewählt wird. Kollisionen mit der linken, oberen oder &lt;br /&gt;
rechten Wand haben eine Richtungsänderung des Balls&lt;br /&gt;
zur Folge (Einfallswinkel = Ausfallwinkel). &lt;br /&gt;
Eine Kollision mit der unteren Wand beendet das Spiel.&lt;br /&gt;
&lt;br /&gt;
Ziel des Spiels sind möglichst viele Kollision des Balls mit&lt;br /&gt;
dem Schläger. Eine derartige Kollision hat eine Richtungsänderung&lt;br /&gt;
sowie einen Punktgewinn zur Folge. Der aktuelle Punktestand (Score)&lt;br /&gt;
wird der Benutzer jederzeit angezeigt.&lt;br /&gt;
&lt;br /&gt;
Wird der Schläger im Moment der Kollision bewegt, so wird der Ball abhängig von der &lt;br /&gt;
Bewegungsrichtung und Geschwindigkeit des Schlägers abgelenkt (Simulation von Reibung).&lt;br /&gt;
&lt;br /&gt;
===Klassenmodell===&lt;br /&gt;
[[Datei:MiniPong04 ClassModel Overview.png|gerahmt|rechts|Klassendiagramm]]&lt;br /&gt;
&lt;br /&gt;
Eine genauere Analyse der Use Cases zeigt, welche Module benötigt werden. Die verschiedenen Module sind verschieden eingefärbt:&lt;br /&gt;
&lt;br /&gt;
* grau: Initialisierung der Anwendung&lt;br /&gt;
* blau: Model der Anwendung&lt;br /&gt;
* orange: Controller zur Behandlung der Benutzeraktionen (mit Ausnahme von Formularelementen, die in der View enthalten sind)&lt;br /&gt;
* flieder: Anwendungslogik&lt;br /&gt;
* grün: Anwendungsview&lt;br /&gt;
* gelb: Kollisionserkennung und -behandlung&lt;br /&gt;
&lt;br /&gt;
Die Kollisionserkennung und -behandlung erhält eine eigene Farbe, da sie eine Mischung aus&lt;br /&gt;
Model und Controller ist. Einerseits verändert sie die Bewegungseigenschaften beweglicher Objekte (Ball und Schläger).&lt;br /&gt;
Das heißt, sie ist wesentlicher Bestandteil des Models. Andererseits informiert sie die Anwendung über Aktionen&lt;br /&gt;
des Balls, damit diese entsprechend reagieren kann. Dies ist eine typische Controllertätigkeit.&lt;br /&gt;
&lt;br /&gt;
Das Modul „&amp;lt;code&amp;gt;init&amp;lt;/code&amp;gt;“ hat die Aufgabe das Spiel zu initialisieren. Es definiert eine [[Prozedur]]&lt;br /&gt;
„&amp;lt;code&amp;gt;init&amp;lt;/code&amp;gt;“, die zunächst die wesentlichen Objekte erstellt und dann das Spiel startet.&lt;br /&gt;
Im Klassendiagramm sind rote Pfeile zu den Modulen eingezeichnet, die das Init-Modul benötigt.&lt;br /&gt;
Das sind die beiden Funktionsmodule „&amp;lt;code&amp;gt;logic/minipong&amp;lt;/code&amp;gt;“ und „&amp;lt;code&amp;gt;control/keyboard&amp;lt;/code&amp;gt;“&lt;br /&gt;
sowie diverse Model- und View-Klassen. Für jede dieser Klassen – mit Ausnahme der Klasse „&amp;lt;code&amp;gt;ModelStage&amp;lt;/code&amp;gt;“ – &lt;br /&gt;
erzeugt &amp;lt;code&amp;gt;init&amp;lt;/code&amp;gt; ein oder im Falle der Text-Klassen sogar jeweils zwei Objekte. Als Stage-Objekt kommt &lt;br /&gt;
– wie im [[HTML5-Tutorium: Canvas: MiniPong 03|dritten Teil des Tutoriums]] – das in der HTML-Datei enthaltene&lt;br /&gt;
Canvas-Element zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Die View-Objekte sind ausschließlich der Prozedur „&amp;lt;code&amp;gt;init&amp;lt;/code&amp;gt;“ bekannt. Diese Objekte visualisieren &lt;br /&gt;
die Model-Objekte, {{dh}} den aktuellen Zustand des Spiels. Da sich der Zustand des Spiel häufig ändert,&lt;br /&gt;
erstellt &amp;lt;code&amp;gt;init&amp;lt;/code&amp;gt; eine View-Loop, deren Aufgabe es ist, die Visualisierung möglichst häufig, &lt;br /&gt;
am Besten 60 mal pro Sekunde zu erneuern. Dazu ruft die View-Loop, sobald sie einmal gestartet wurde,&lt;br /&gt;
regelmäßig die Zeichenmethode „&amp;lt;code&amp;gt;draw&amp;lt;/code&amp;gt;“ der einzelnen View-Objekte auf.&lt;br /&gt;
&lt;br /&gt;
[[Datei:MiniPong04 ClassModel ViewLoop.png|gerahmt|rechts|Klassendiagramm: Detailansicht der View-Loop]]&lt;br /&gt;
Die View umfasst sechs Elemente:&lt;br /&gt;
* einen Button (Start, Stopp)&lt;br /&gt;
* einen Ball&lt;br /&gt;
* einen Schläger&lt;br /&gt;
* zwei Textfelder (Score und allgemeine Informationen)&lt;br /&gt;
Die View-Loop muss diese sechs Objekte selbstverständlich kennen, damit sie sie darstellen kann. &lt;br /&gt;
Die entsprechenden Beziehungspfeile (blau markiert) werden allerdings nur der nebenstehenden Detailansicht des Klassendiagramms,&lt;br /&gt;
aber nicht im Übersichtsdiagramm angezeigt (da das Übersichtsdiagramm anderenfalls etwas unübersichtlich werden würde). &lt;br /&gt;
Die Init-Prozedur erzeugt diese View-Objekte sowie die zugehörigen Model-Objekte, ordnet&lt;br /&gt;
den View-Objekten die entsprechenden Model-Objekte zu und übergibt dann ein Array mit allen View-Objekten der View-Loop.&lt;br /&gt;
Sobald diese gestartet wird, aktualisiert sie regelmäßig die grafische Darstellung dieser Objekte im HTML-Dokument.&lt;br /&gt;
&lt;br /&gt;
Die Init-Prozedur lädt ein weiteres Modul: „&amp;lt;code&amp;gt;control/keyboard&amp;lt;/code&amp;gt;“. Diese fängt alle Tastaturereignisse ab.&lt;br /&gt;
Sobald auf der Tastatur eine entsprechende Taste gedrückt wird, ruft sie sie Methode „&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;“ des Schlägers&lt;br /&gt;
({{dh}} eines Objektes der Klasse „&amp;lt;code&amp;gt;ModelPaddle&amp;lt;/code&amp;gt;“) auf und setzt ihn in Bewegung. &lt;br /&gt;
Welcher Taste welche Bewegungsrichtung zugeordnet ist, wird in der Init-Datei „&amp;lt;code&amp;gt;init.json&amp;lt;/code&amp;gt;“ festgelegt.&lt;br /&gt;
&lt;br /&gt;
Danach wird das eigentlich Spiel gestartet. Die Spiellogik wird in der Prozedur „&amp;lt;code&amp;gt;minipong&amp;lt;/code&amp;gt;“ implementiert.&lt;br /&gt;
Diese Prozedur muss die Model-Objekte des Spiels kennen, da sie deren Werte liest und in bestimmten Situationen verändert. &lt;br /&gt;
Jede Änderung einen Model-Objekts, dem ein View-Objekt zugeordnet ist, wird fast sofort (genau gesagt, bei der nächsten Ausführung&lt;br /&gt;
der View-Loop) visualisiert. Da dies automatisch geschieht, muss die Prozedur „&amp;lt;code&amp;gt;minipong&amp;lt;/code&amp;gt;“ nichts weiter machen,&lt;br /&gt;
als beispielsweise den Text innerhalb eines Text-Objekts der Klasse  „&amp;lt;code&amp;gt;ModelText&amp;lt;/code&amp;gt;“ oder die Beschriftung des Buttons&lt;br /&gt;
innerhalb des Objekts der Klasse  „&amp;lt;code&amp;gt;ModelButton&amp;lt;/code&amp;gt;“  zu ändern. Sobald dies geschehen ist, wird die Änderung im HTML-Dokument &lt;br /&gt;
automatisch übernommen.&lt;br /&gt;
&lt;br /&gt;
Die Spiellogik hat zwei wichtige Aufgaben:&lt;br /&gt;
# Simulation der Ball/Schläger-Physik&lt;br /&gt;
# Reaktion auf Ereignisse wie Aktivierung des Start-Buttons (Spielstart), Kollision des Schlägers mit dem Ball (Punktgewinn), Verlust des Balles am unteren Bühnenrand (Spielende) etc.&lt;br /&gt;
&lt;br /&gt;
[[Datei:MiniPong04 ClassModel ModelLoop.png|gerahmt|rechts|Klassendiagramm: Detailansicht der Model-Loop]]&lt;br /&gt;
Für die erste Aufgabe verwendet sie eine Model-Loop, die alle beweglichen Objekte, {{dh}} alle Model-Objekte, die über die Methode &lt;br /&gt;
„&amp;lt;code&amp;gt;move&amp;lt;/code&amp;gt;“ verfügen, möglichst oft pro Sekunde mittels dieser Methode an eine neue Position verschiebt.&lt;br /&gt;
Das nebenstehende Diagramm zeigt abermals eine Detailansicht des Klassendiagramm, in dem alle für die ModelLoop notwendigen&lt;br /&gt;
Beziehungen eingetragen wurde. Auch hier gilt: Im Übersichtsdiagramm wurden die Beziehungspfeile, die im nebenstehenden Diagramm blau markiert wurden,&lt;br /&gt;
aus Gründen der Übersichtlichkeit  nicht eingetragen.&lt;br /&gt;
 &lt;br /&gt;
Die Model-Loop wird nicht von der Init-Prozedur, sondern von der Prozedur „&amp;lt;code&amp;gt;minipong&amp;lt;/code&amp;gt;“ erzeugt. &lt;br /&gt;
Die Init-Prozedur benötigt dieses Objekt nicht. Die MiniPong-Prozedur benötigt es dagegen nicht nur, sondern muss es auch&lt;br /&gt;
gemäß ihren Bedürfnissen initialisieren: Der ModelLoop muss eine spezielle Kollisionsfunktion übergeben werden.&lt;br /&gt;
&lt;br /&gt;
Die zweite Aufgabe „Reaktion auf Ereignisse“ zerfällt in zwei Aufgaben. &lt;br /&gt;
# Reaktion auf Aktionen des Benutzers.&lt;br /&gt;
# Reaktion auf Aktionen des Balls.&lt;br /&gt;
&lt;br /&gt;
In diesem Spiel gibt es nur eine Benutzeraktion, auf die &amp;lt;code&amp;gt;minipong&amp;lt;/code&amp;gt; direkt reagieren muss: „Klick auf den Start-Stopp-Button“ .&lt;br /&gt;
Die zweite Aktion „Bewegung des Schlägers“ wird vom Keyboard-Controller direkt an den Schläger weitergeleitet.&lt;br /&gt;
Für die Aktion „Button-Klick“ muss &amp;lt;code&amp;gt;minipong&amp;lt;/code&amp;gt; dem Button in jedem der beiden Spielzustände „Spiel gestoppt“ und „Spiel gestartet“&lt;br /&gt;
eine geeignete Callback-Funktion zuweisen, die die jeweils passende Aktion ausführt. Im Fall „Spiel gestoppt“ muss der Button-Klick das&lt;br /&gt;
Spiel starten und im Fall „Spiel gestartet“ muss er das Spiel beenden.&lt;br /&gt;
&lt;br /&gt;
Der Ball kann zwei weitere Aktionen ausführen. Er kann mit dem Schläger kollidieren oder das Spielfeld verlassen. Beide Aktionen werden von&lt;br /&gt;
der Kollisionsbehandlung erkannt. Jedes mal, wenn eine wenn der Ball eine dieser beiden Aktionen durchführt &lt;br /&gt;
muss die Prozedur „&amp;lt;code&amp;gt;minipong&amp;lt;/code&amp;gt;“ darüber informiert werden, damit sie entsprechend reagieren kann.&lt;br /&gt;
Hierzu definiert sie mit Hilfe der drei Hilfs-Prozeduren „&amp;lt;code&amp;gt;collisionBallPaddle&amp;lt;/code&amp;gt;“,  „&amp;lt;code&amp;gt;collisionBallPaddle&amp;lt;/code&amp;gt;“ &lt;br /&gt;
und „&amp;lt;code&amp;gt;collisionStagePaddle&amp;lt;/code&amp;gt;“ eine geeignete Kollissionsprozedur, die sie der Model-Loop zur Kollisionserkennung und&lt;br /&gt;
-behandlung übergibt.&lt;br /&gt;
&lt;br /&gt;
==Neues Projekt anlegen==&lt;br /&gt;
&lt;br /&gt;
Legen Sie ein neues Projekt mit dem Namen „&amp;lt;code&amp;gt;MiniPong04&amp;lt;/code&amp;gt;“ an.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie folgende Ordner:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;web&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;web/css&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;web/json&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;web/js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;web/js/lib&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;web/js/lib/require&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;web/js/app&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;web/js/app/collision&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;web/js/app/control&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;web/js/app/logic&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;web/js/app/model&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;web/js/app/view&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie folgende Dateien des Projektes [https://glossar.hs-augsburg.de/beispiel/tutorium/es5/minipong/WK_MiniPong03/ MiniPong03]&lt;br /&gt;
in das neue Projekt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;web/index.html&amp;lt;/code&amp;gt; (Ersetzen Sie im Titel „&amp;lt;code&amp;gt;MiniPong03&amp;lt;/code&amp;gt;“ durch „&amp;lt;code&amp;gt;MiniPong04&amp;lt;/code&amp;gt;“ und ersetzen Sie im Copyright-Kommentar meinen Namen durch Ihren Namen.)&lt;br /&gt;
* &amp;lt;code&amp;gt;web/css/main.css&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;web/js/lib/require/json.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;web/js/lib/require/require.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;web/js/lib/require/text.js&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die Datei  „&amp;lt;code&amp;gt;web/js/main.js&amp;lt;/code&amp;gt;“ und fügen Sie folgenden Code ein:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
requirejs.config&lt;br /&gt;
({&lt;br /&gt;
  baseUrl: &#039;js&#039;, // By default load any modules from directory js&lt;br /&gt;
  paths :&lt;br /&gt;
  {&lt;br /&gt;
    app:       &#039;app&#039;,&lt;br /&gt;
&lt;br /&gt;
    model:     &#039;app/model&#039;,&lt;br /&gt;
    view:      &#039;app/view&#039;,&lt;br /&gt;
    control:   &#039;app/control&#039;,&lt;br /&gt;
    logic:     &#039;app/logic&#039;,&lt;br /&gt;
    collision: &#039;app/collision&#039;,&lt;br /&gt;
&lt;br /&gt;
    loadjson:  &#039;lib/require/json&#039;,&lt;br /&gt;
    text:      &#039;lib/require/text&#039;,&lt;br /&gt;
    json:      &#039;../json&#039;&lt;br /&gt;
  }&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
requirejs&lt;br /&gt;
( [&#039;loadjson!json/init.json&#039;, &#039;app/init&#039;],&lt;br /&gt;
  function(initJSON, init)&lt;br /&gt;
  {&lt;br /&gt;
    //init(window, initJSON);&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für jedes Modulpaket wurden ein Ordner angelegt und eine Kurzbezeichnung des Modulpfades festgelegt:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;datatable-rt&amp;quot;&lt;br /&gt;
 !  Farbe !! Ordner !! Modulpfad !!  Modulzweck&lt;br /&gt;
 |-&lt;br /&gt;
 | data-title=&amp;quot;Farbe&amp;quot;           | grau&lt;br /&gt;
 | data-title=&amp;quot;Ordner&amp;quot;         | &amp;lt;code&amp;gt;js/app&amp;lt;/code&amp;gt;&lt;br /&gt;
 | data-title=&amp;quot;Modulpfad&amp;quot;    | &amp;lt;code&amp;gt;app&amp;lt;/code&amp;gt;&lt;br /&gt;
 | data-title=&amp;quot;Modulzweck&amp;quot;  | Initialisierung der Anwendung&lt;br /&gt;
 |-&lt;br /&gt;
 | data-title=&amp;quot;Farbe&amp;quot;           | blau&lt;br /&gt;
 | data-title=&amp;quot;Ordner&amp;quot;         | &amp;lt;code&amp;gt;web/js/app/model&amp;lt;/code&amp;gt;&lt;br /&gt;
 | data-title=&amp;quot;Modulpfad&amp;quot;    | &amp;lt;code&amp;gt;model&amp;lt;/code&amp;gt;&lt;br /&gt;
 | data-title=&amp;quot;Modulzweck&amp;quot;  | Model der Anwendung&lt;br /&gt;
 |-&lt;br /&gt;
 | data-title=&amp;quot;Farbe&amp;quot;           | grün&lt;br /&gt;
 | data-title=&amp;quot;Ordner&amp;quot;         | &amp;lt;code&amp;gt;web/js/app/view&amp;lt;/code&amp;gt;&lt;br /&gt;
 | data-title=&amp;quot;Modulpfad&amp;quot;    | &amp;lt;code&amp;gt;view&amp;lt;/code&amp;gt;&lt;br /&gt;
 | data-title=&amp;quot;Modulzweck&amp;quot;  | Anwendungsview&lt;br /&gt;
 |-&lt;br /&gt;
 | data-title=&amp;quot;Farbe&amp;quot;           | orange&lt;br /&gt;
 | data-title=&amp;quot;Ordner&amp;quot;         | &amp;lt;code&amp;gt;web/js/app/controller&amp;lt;/code&amp;gt;&lt;br /&gt;
 | data-title=&amp;quot;Modulpfad&amp;quot;    | &amp;lt;code&amp;gt;controller&amp;lt;/code&amp;gt;&lt;br /&gt;
 | data-title=&amp;quot;Modulzweck&amp;quot;  | Controller zur Behandlung der Benutzeraktionen&lt;br /&gt;
 |-&lt;br /&gt;
 | data-title=&amp;quot;Farbe&amp;quot;           | flieder&lt;br /&gt;
 | data-title=&amp;quot;Ordner&amp;quot;         | &amp;lt;code&amp;gt;web/js/app/logic&amp;lt;/code&amp;gt;&lt;br /&gt;
 | data-title=&amp;quot;Modulpfad&amp;quot;    | &amp;lt;code&amp;gt;logic&amp;lt;/code&amp;gt;&lt;br /&gt;
 | data-title=&amp;quot;Modulzweck&amp;quot;  | Anwendungslogik&lt;br /&gt;
 |-&lt;br /&gt;
 | data-title=&amp;quot;Farbe&amp;quot;           | gelb&lt;br /&gt;
 | data-title=&amp;quot;Ordner&amp;quot;         | &amp;lt;code&amp;gt;web/js/app/collision&amp;lt;/code&amp;gt;&lt;br /&gt;
 | data-title=&amp;quot;Modulpfad&amp;quot;    | &amp;lt;code&amp;gt;collision&amp;lt;/code&amp;gt;&lt;br /&gt;
 | data-title=&amp;quot;Modulzweck&amp;quot;  | Kollisionserkennung und -behandlung&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie für jedes Modul, das im Klassendiagramm aufgeführt ist (mit Ausnahme von &amp;lt;code&amp;gt;ModelStage&amp;lt;/code&amp;gt;), eine &#039;&#039;&#039;leere&#039;&#039;&#039; Datei&lt;br /&gt;
in der dieses Modul implementiert wird. Achten Sie darauf, dass die Farbmarkierung der Module mit den Farbmarkierungen &lt;br /&gt;
der Modulordner übereinstimmt:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;web/js/app&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;init.js&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;web/js/app/model&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;button.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ball.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;paddle.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;text.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;loop.js&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;web/js/app/view&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;button.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ball.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;paddle.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;text.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;loop.js&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;web/js/app/control&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;keyboard.js&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;web/js/app/logic&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;minipong.js&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;web/js/app/collision&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;ball_paddle.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;stage_ball.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;stage_paddle.js&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fügen Sie in jede Datei ein RequireJS-Kommando ein, das dafür sorgt, dass die benötigten Module geladen und die darin definierten&lt;br /&gt;
Funktionen ({{dh}} Prozeduren bzw. Konstruktorfunktionen) dem Modul zur Verfügung gestellt werden. Welche Module&lt;br /&gt;
ein Modul benötigt, erkennt man an den roten Pfeilen im Klassendiagramm.&lt;br /&gt;
&lt;br /&gt;
Von den meisten Module geht kein roter Pfeil aus. In diese können Sie jeweils folgenden Code einfügen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang =&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
define&lt;br /&gt;
( [],&lt;br /&gt;
  function()&lt;br /&gt;
  { &amp;quot;use strict&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    return null;&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Von &amp;lt;code&amp;gt;minipong&amp;lt;/code&amp;gt; gehen vier rote Pfeile aus. Das heißt, in die Datei „&amp;lt;code&amp;gt;minipong.js&amp;lt;/code&amp;gt;“&lt;br /&gt;
müssen sie folgende RequireJS-Kommando einfügen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang =&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
define&lt;br /&gt;
([&#039;collision/ball_paddle&#039;, &#039;collision/stage_ball&#039;, &#039;collision/stage_paddle&#039;,&lt;br /&gt;
    &#039;model/loop&#039;&lt;br /&gt;
  ],&lt;br /&gt;
  function(collisionBallPaddle, collisionStageBall, collisionStagePaddle,&lt;br /&gt;
           ModelLoop&lt;br /&gt;
  )&lt;br /&gt;
  { &amp;quot;use strict&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    return null;&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Reihenfolge, in der Sie die benötigten Module laden, ist unwichtig.&lt;br /&gt;
Wichtig ist nur, dass die Reihenfolge der Kurzbezeichnungen der Modulpfade mit&lt;br /&gt;
der Reihenfolge der Parameter in der Callback-Funktion übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Das Init-Modul „&amp;lt;code&amp;gt;app/init&amp;lt;/code&amp;gt;“ benötigt sehr viele andere Module, um seine Aufgabe zu erledigen.&lt;br /&gt;
Entsprechend lang ist die Modulliste:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang =&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
define&lt;br /&gt;
( [&#039;model/button&#039;, &#039;view/button&#039;,&lt;br /&gt;
   &#039;model/ball&#039;,   &#039;view/ball&#039;,&lt;br /&gt;
   &#039;model/paddle&#039;, &#039;view/paddle&#039;,&lt;br /&gt;
   &#039;model/text&#039;,   &#039;view/text&#039;,&lt;br /&gt;
   &#039;view/loop&#039;,&lt;br /&gt;
   &#039;control/keyboard&#039;,&lt;br /&gt;
   &#039;logic/minipong&#039;&lt;br /&gt;
  ],&lt;br /&gt;
  function(ModelButton, ViewButton,&lt;br /&gt;
           ModelBall,   ViewBall,&lt;br /&gt;
           ModelPaddle, ViewPaddle,&lt;br /&gt;
           ModelText,   ViewText,&lt;br /&gt;
           ViewLoop,&lt;br /&gt;
           controlKeyboard,&lt;br /&gt;
           minipong&lt;br /&gt;
          )&lt;br /&gt;
  { &amp;quot;use strict&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    return null;&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt fehlt noch die Datei „&amp;lt;code&amp;gt;web/json/init.json&amp;lt;/code&amp;gt;“, die schon ziemliche Ausmaße angenommen hat,&lt;br /&gt;
weil sie für (fast) jedes Modul im Diagramm geeignete Initialwerte enthält:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;canvas&amp;quot;:&lt;br /&gt;
  {&lt;br /&gt;
    &amp;quot;element&amp;quot;: &amp;quot;canvas&amp;quot;,&lt;br /&gt;
    &amp;quot;width&amp;quot;:   400,&lt;br /&gt;
    &amp;quot;height&amp;quot;:  300&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;game&amp;quot;:&lt;br /&gt;
  {&lt;br /&gt;
    &amp;quot;fps&amp;quot;: 120,&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:   &amp;quot;Willkommen bei MiniPong&amp;quot;,&lt;br /&gt;
    &amp;quot;ballLost&amp;quot;:  &amp;quot;Das Spiel ist vorbei :-(&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;startGame&amp;quot;: &amp;quot;Spiel starten&amp;quot;,&lt;br /&gt;
    &amp;quot;stopGame&amp;quot;:  &amp;quot;Spiel beenden&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;model&amp;quot;:&lt;br /&gt;
  {&lt;br /&gt;
    &amp;quot;buttonStartStop&amp;quot;:&lt;br /&gt;
    {},&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;ball&amp;quot;:&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;r&amp;quot;:   10,&lt;br /&gt;
      &amp;quot;pos&amp;quot;: { &amp;quot;x&amp;quot;: 195 , &amp;quot;y&amp;quot;: 10 },&lt;br /&gt;
      &amp;quot;vel&amp;quot;: { &amp;quot;x&amp;quot;: { &amp;quot;min&amp;quot;:  50, &amp;quot;max&amp;quot;: 200 },&lt;br /&gt;
               &amp;quot;y&amp;quot;: { &amp;quot;min&amp;quot;: 150, &amp;quot;max&amp;quot;: 200 }&lt;br /&gt;
             }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;paddle&amp;quot;:&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;width&amp;quot;:    50,&lt;br /&gt;
      &amp;quot;height&amp;quot;:    8,&lt;br /&gt;
      &amp;quot;pos&amp;quot;:      {&amp;quot;x&amp;quot;: 175, &amp;quot;y&amp;quot;: 287},&lt;br /&gt;
      &amp;quot;vel&amp;quot;:      {&amp;quot;x&amp;quot;: 100, &amp;quot;y&amp;quot;:   0},&lt;br /&gt;
      &amp;quot;acc&amp;quot;:      {&amp;quot;x&amp;quot;: 500, &amp;quot;y&amp;quot;:   0},&lt;br /&gt;
      &amp;quot;friction&amp;quot;: 0.3&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;info&amp;quot;:&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;pos&amp;quot;: {&amp;quot;x&amp;quot;: 10, &amp;quot;y&amp;quot;: 130}&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;score&amp;quot;:&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;pos&amp;quot;:      {&amp;quot;x&amp;quot;: 5, &amp;quot;y&amp;quot;: 3},&lt;br /&gt;
      &amp;quot;template&amp;quot;: &amp;quot;Punkte: $1&amp;quot;,&lt;br /&gt;
      &amp;quot;value&amp;quot;:    0&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;view&amp;quot;:&lt;br /&gt;
  {&lt;br /&gt;
    &amp;quot;buttonStartStop&amp;quot;:&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;elementID&amp;quot;: &amp;quot;button_start_stop&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;ball&amp;quot;:&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;color&amp;quot;:       &amp;quot;#55AA55&amp;quot;,&lt;br /&gt;
      &amp;quot;borderWidth&amp;quot;: 1,&lt;br /&gt;
      &amp;quot;borderColor&amp;quot;: &amp;quot;#000000&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;paddle&amp;quot;:&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;color&amp;quot;:       &amp;quot;#aa55cc&amp;quot;,&lt;br /&gt;
      &amp;quot;borderWidth&amp;quot;: 1,&lt;br /&gt;
      &amp;quot;borderColor&amp;quot;: &amp;quot;#000000&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;info&amp;quot;:&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;color&amp;quot;:     &amp;quot;#000000&amp;quot;,&lt;br /&gt;
      &amp;quot;font&amp;quot;:      &amp;quot;bold 25px Verdana, Geneva, sans-serif&amp;quot;,&lt;br /&gt;
      &amp;quot;textAlign&amp;quot;: &amp;quot;center&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;score&amp;quot;:&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;color&amp;quot;:  &amp;quot;#000000&amp;quot;,&lt;br /&gt;
      &amp;quot;font&amp;quot;:   &amp;quot;bold 20px \&amp;quot;Courier New\&amp;quot;, Courier, monospace&amp;quot;,&lt;br /&gt;
      &amp;quot;textBaseline&amp;quot;: &amp;quot;top&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;control&amp;quot;:&lt;br /&gt;
  {&lt;br /&gt;
    &amp;quot;player&amp;quot;:&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;left&amp;quot;:  {&amp;quot;key&amp;quot;: &amp;quot;ArrowLeft&amp;quot;,  &amp;quot;keyCode&amp;quot;: 37},&lt;br /&gt;
      &amp;quot;right&amp;quot;: {&amp;quot;key&amp;quot;: &amp;quot;ArrowRight&amp;quot;, &amp;quot;keyCode&amp;quot;: 39}&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn Sie alles richtig gemacht haben, sollten Sie jetzt die Datei  „&amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt;“ im Browser fehlerfrei laden können.&lt;br /&gt;
Diese Datei hat noch keine Inhalte, mit Ausnahme eines leeren Canvas-Elements. In der Browser-Konsole sollten aber keine Fehler gemeldet werden.&lt;br /&gt;
&lt;br /&gt;
Es ist allerdings ziemlich unbefriedigend, wenn man eine Web-Anwendung ausführt und überhaupt nichts zu sehen ist, &lt;br /&gt;
obwohl etwas passiert. Öffnen Sie in den Browser-Entwicklertools die Netzwerkansicht und laden Sie die HTML-Datei erneut. Dann sollten Sie sehen,&lt;br /&gt;
dass 23 Dateien geladen werden. (Hier ist später noch Optimierungsarbeit angesagt. Die Anzahl der Dateien muss reduziert und die Inhalte müssen komprimiert werden. &lt;br /&gt;
Das ist aber nicht Thema dieses Tutoriums.)&lt;br /&gt;
&lt;br /&gt;
Sie können probehalber auch mal &amp;lt;code&amp;gt;console.log&amp;lt;/code&amp;gt;-Befehle in die einzelnen Dateien einfügen, damit Sie sehen in welcher Reihenfolge die Module geladen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang =&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
console.log(&#039;Modul &amp;quot;MODULNAME&amp;quot; wird geladen&#039;); &lt;br /&gt;
// Ersetzen Sie MODULNAME durch den Namen des aktuellen Moduls.&lt;br /&gt;
&lt;br /&gt;
define&lt;br /&gt;
( [],&lt;br /&gt;
  function()&lt;br /&gt;
  { &amp;quot;use strict&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    console.log(&#039;Modul &amp;quot;MODULNAME&amp;quot; wurde geladen&#039;); &lt;br /&gt;
    // Ersetzen Sie MODULNAME durch den Namen des aktuellen Moduls.&lt;br /&gt;
&lt;br /&gt;
    return null;&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als Musterlösung gibt es das Projekt [https://glossar.hs-augsburg.de/beispiel/tutorium/es5/minipong/WK_MiniPong04/ WK_MiniPong04_empty (SVN)].&lt;br /&gt;
Die darin enthaltene Datei &amp;lt;code&amp;gt;[https://glossar.hs-augsburg.de/beispiel/tutorium/es5/minipong/WK_MiniPong04_empty/web/index.html index.html]&amp;lt;/code&amp;gt;&lt;br /&gt;
verwendet allerdings den Befehl „&amp;lt;code&amp;gt;terminal.log&amp;lt;/code&amp;gt;“, den Sie als Übungsaufgabe im Praktikum erstellen sollten. Damit werden die Log-Nachrichten&lt;br /&gt;
im HTML-Dokument und nicht in der Browser-Konsole ausgegeben. Außerdem wurde der Logging-Code etwas erweitert, sodass anhand von Einrückungen&lt;br /&gt;
zu sehen ist, welcher Code von welchem Modul geladen wird.&lt;br /&gt;
&lt;br /&gt;
==Models und Views==&lt;br /&gt;
&lt;br /&gt;
===Ball===&lt;br /&gt;
[[Datei:MiniPong04 ClassModel Ball.png|gerahmt|rechts|Ball-View und Ball-Model]]&lt;br /&gt;
&lt;br /&gt;
In den vorangegangenen Teilen des Tutoriums war der Ball ständig sichtbar und ständig in Bewegung. Das heißt,&lt;br /&gt;
es wurden nur folgende Attribute und Methoden benötigt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; (Radius)&lt;br /&gt;
* &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (aktuelle $x$-Position)&lt;br /&gt;
* &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt; (aktuelle $y$-Position)&lt;br /&gt;
* &amp;lt;code&amp;gt;vx&amp;lt;/code&amp;gt; (aktuelle Geschwindigkeit in $x$-Richtung)&lt;br /&gt;
* &amp;lt;code&amp;gt;vy&amp;lt;/code&amp;gt; (aktuelle Geschwindigkeit in $y$-Richtung)&lt;br /&gt;
* &amp;lt;code&amp;gt;move(p_seconds)&amp;lt;/code&amp;gt; (Bewegen des Balls an die neue Position, die sich aus der aktuellen Position, der Geschwindigkeit und der Anzahl der Sekunden seit der letzten Berechnung der Position ergibt)&lt;br /&gt;
&lt;br /&gt;
Im eigentlichen Spiel werden jedoch viel mehr Attribute und Methoden benötigt. Der Ball ist beim Start der Web-Anwendung zunächst unsichtbar, &lt;br /&gt;
erst beim Spielstart wird er sichtbar gemacht&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;visible&amp;lt;/code&amp;gt; (Flag, ob der Ball sichtbar oder unsichtbar ist)&lt;br /&gt;
* &amp;lt;code&amp;gt;show()&amp;lt;/code&amp;gt; (Methode, um den Ball sichtbar zu machen)&lt;br /&gt;
* &amp;lt;code&amp;gt;hide()&amp;lt;/code&amp;gt; (Methode, um den Ball unsichtbar zu machen)&lt;br /&gt;
&lt;br /&gt;
Solange der Ball unsichtbar ist, bewegt er sich nicht (&amp;lt;code&amp;gt;vx === 0&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;vy === 0&amp;lt;/code&amp;gt;) und befindet sich an seiner Startposition&lt;br /&gt;
(&amp;lt;code&amp;gt;x === x_start&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;y === y_start&amp;lt;/code&amp;gt;). Bei einem Reset des Spiels werden Position und Geschwindigkeit auf passende Startwerte gesetzt.&lt;br /&gt;
Diese werden dem Konstruktor  „&amp;lt;code&amp;gt;ModelBall&amp;lt;/code&amp;gt;“ im Parameter  „&amp;lt;code&amp;gt;p_init&amp;lt;/code&amp;gt;“ übergeben und vom Konstruktor dann in folgenden Attribute gespeichert:&lt;br /&gt;
* &amp;lt;code&amp;gt;x_start&amp;lt;/code&amp;gt; ($x$-Koordinate der Startposition)&lt;br /&gt;
* &amp;lt;code&amp;gt;y_start&amp;lt;/code&amp;gt; ($y$-Koordinate der Startposition)&lt;br /&gt;
* &amp;lt;code&amp;gt;vx_start_min&amp;lt;/code&amp;gt; (minimale Startgeschwindigkeit in $x$-Richtung)&lt;br /&gt;
* &amp;lt;code&amp;gt;vx_start_max&amp;lt;/code&amp;gt; (maximale Startgeschwindigkeit in $x$-Richtung)&lt;br /&gt;
* &amp;lt;code&amp;gt;vy_start_min&amp;lt;/code&amp;gt; (minimale Startgeschwindigkeit in $y$-Richtung)&lt;br /&gt;
* &amp;lt;code&amp;gt;vy_start_max&amp;lt;/code&amp;gt; (maximale Startgeschwindigkeit in $y$-Richtung)&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass der Ball bei jedem Spielstart von derselben Position aus startet, aber dass die Startgeschwindigkeit (in Grenzen) zufällig gewählt wird.&lt;br /&gt;
&lt;br /&gt;
Um das Spiel starten, beenden und neu starten zu können, werden drei weitere Methoden benötigt:  &lt;br /&gt;
* &amp;lt;code&amp;gt;reset()&amp;lt;/code&amp;gt; (Ball anhalten, unsichtbar machen und auf die Startposition setzen)&lt;br /&gt;
* &amp;lt;code&amp;gt;start()&amp;lt;/code&amp;gt; (Ball auf eine zufällige Startgeschwindigkeit setzen)&lt;br /&gt;
* &amp;lt;code&amp;gt;stop()&amp;lt;/code&amp;gt; (Ball anhalten, {{dh}}, die Geschwindigkeit auf Null setzen)&lt;br /&gt;
&lt;br /&gt;
Insgesamt ergibt sich damit folgender Code:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Konstruktor&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function ModelBall(p_init)&lt;br /&gt;
{&lt;br /&gt;
  this.r            = p_init.r;&lt;br /&gt;
&lt;br /&gt;
  this.x_start      = p_init.pos.x;&lt;br /&gt;
  this.y_start      = p_init.pos.y;&lt;br /&gt;
  this.vx_start_min = p_init.vel.x.min;&lt;br /&gt;
  this.vx_start_max = p_init.vel.x.max;&lt;br /&gt;
  this.vy_start_min = p_init.vel.y.min;&lt;br /&gt;
  this.vy_start_max = p_init.vel.y.max;&lt;br /&gt;
&lt;br /&gt;
  this.reset(); // initializes further attributes&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Öffentliche Methoden&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
ModelBall.prototype =&lt;br /&gt;
{&lt;br /&gt;
  reset:&lt;br /&gt;
    function()&lt;br /&gt;
    {&lt;br /&gt;
      this.stop(); // By default, the ball does not move around.&lt;br /&gt;
      this.hide(); // By default, the ball is invisible.&lt;br /&gt;
&lt;br /&gt;
      this.x = this.x_start;&lt;br /&gt;
      this.y = this.y_start;&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
  show:&lt;br /&gt;
    function() { this.visible = true; },&lt;br /&gt;
&lt;br /&gt;
  hide:&lt;br /&gt;
    function() { this.visible = false; },&lt;br /&gt;
&lt;br /&gt;
  stop:&lt;br /&gt;
    function()&lt;br /&gt;
    {&lt;br /&gt;
      this.vx = 0;&lt;br /&gt;
      this.vy = 0;&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
  start:&lt;br /&gt;
    function()&lt;br /&gt;
    {&lt;br /&gt;
      // react only if the ball is not already moving&lt;br /&gt;
      if (this.visible === true &amp;amp;&amp;amp; this.vx === 0 &amp;amp;&amp;amp; this.vy === 0)&lt;br /&gt;
      {&lt;br /&gt;
        this.vx = (Math.random() &amp;lt; 0.5 ? 1 : -1)*&lt;br /&gt;
                  (this.vx_start_min + &lt;br /&gt;
                   Math.random()*(this.vx_start_max - this.vx_start_min)&lt;br /&gt;
                  );&lt;br /&gt;
        this.vy = (Math.random() &amp;lt; 0.5 ? 1 : -1)*&lt;br /&gt;
                  (this.vy_start_min +&lt;br /&gt;
                   Math.random()*(this.vy_start_max - this.vy_start_min)&lt;br /&gt;
                  );&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
  move:&lt;br /&gt;
    function(p_seconds)&lt;br /&gt;
    {&lt;br /&gt;
      this.x += this.vx * p_seconds;&lt;br /&gt;
      this.y += this.vy * p_seconds;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fügen Sie diesen Code in den Rumpf der Callback-Funktion in der Datei  „&amp;lt;code&amp;gt;model/ball.js&amp;lt;/code&amp;gt;“ ein. Vergessen Sie nicht,&lt;br /&gt;
in dieser Callback-Funktionen den Return-Befehl&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
  return null;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
durch den Return-Befehl&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
  return ModelBall;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
zu ersetzen.&lt;br /&gt;
&lt;br /&gt;
Die Ball-View ändert sich nur in einem Aspekt gegenüber der Version aus dem zweiten und dritten Teil des Tutoriums.&lt;br /&gt;
Die Draw-Funktion darf den Ball nur zeichnen, wenn er sichtbar ist. Kopieren Sie also den Inhalt der Datei&lt;br /&gt;
[https://glossar.hs-augsburg.de/beispiel/tutorium/es5/minipong/WK_MiniPong03/web/js/app/view/ball.js &amp;lt;code&amp;gt;view/ball.js&amp;lt;/code&amp;gt;]&lt;br /&gt;
aus dem dritten Teil des Tutoriums in die neue Datei &amp;lt;code&amp;gt;view/ball.js&amp;lt;/code&amp;gt; und fügen Sie die If-Anweisung&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
if (this.model.visible === true)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
vor den eigentlichen Zeichenbefehl „&amp;lt;code&amp;gt;p_context.drawImage&amp;lt;/code&amp;gt;“ ein.&lt;br /&gt;
&lt;br /&gt;
===Paddle===&lt;br /&gt;
[[Datei:MiniPong04 ClassModel Paddle.png|gerahmt|rechts|Schläger-Model und Schläger-View]]&lt;br /&gt;
&lt;br /&gt;
Der Schläger ist sehr ähnlich aufgebaut wie der Ball. (Der Code ist also nicht [[DRY]]. Man sollte zwei allgemeine Klassen  „&amp;lt;code&amp;gt;ModelGeoObject&amp;lt;/code&amp;gt;“&lt;br /&gt;
und „&amp;lt;code&amp;gt;ViewGeoObject&amp;lt;/code&amp;gt;“ definieren, von denen alle Geo-Objekt-Klassen wie „&amp;lt;code&amp;gt;ModelBall&amp;lt;/code&amp;gt;“, „&amp;lt;code&amp;gt;ViewBall&amp;lt;/code&amp;gt;“ gemeinsame&lt;br /&gt;
Eigenschaften erben.)&lt;br /&gt;
&lt;br /&gt;
Dem Schläger ist neben Position und Geschwindigkeit auch noch eine Beschleunigung zugeordnet. &lt;br /&gt;
Für alle drei Vektoren sind feste Startwerte vorgegeben, {{dh}} der Schläger befindet sich bei Spielbeginn stets an derselben Stelle, &lt;br /&gt;
startet, sobald der Benutzer ihn bewegt, mit derselben Geschwindigkeit und wird stets im gleichen Maße schneller, je länger der&lt;br /&gt;
Spieler die entsprechende Steuertaste gedrückt hält. &lt;br /&gt;
&lt;br /&gt;
Gegenüber dem Ball gibt es ein weiteres Attribut, das für die Realisierung der Use Cases wichtig ist:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;friction&amp;lt;/code&amp;gt; (Reibung, genauer Reibungsfaktor)&lt;br /&gt;
&lt;br /&gt;
Wenn sich der Schläger bei eine Kollision mit dem Ball bewegt, wird der Ball aufgrund der Reibung kurzzeitig vom Schläger in $x$-Richtung&lt;br /&gt;
mitgezogen. Das heißt, die $x$-Geschwindigkeit des Schlägers ändert sich. Diese wird berechnet, indem zur $x$-Geschwindigkeit&lt;br /&gt;
des Balls die $x$-Geschwindigkeit mal dem Reibungsfaktor des Schlägers addiert wird. Wenn die Reibung gleich Null ist hat also  bei einer Kollision&lt;br /&gt;
die Geschwindigkeit des Schlägers keine Auswirkung auf die Geschwindigkeit des Balls. Je größer der Reibungsfaktor gewählt wird, desto größer ist die Ablenkung.&lt;br /&gt;
In der Musterlösung wurde der Reibungsfaktor auf $0,3$ gesetzt.&lt;br /&gt;
&lt;br /&gt;
Zu guter Letzt werden noch vier berechnete Attribute für den Schläger definiert:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;get left() { return this.x; }&amp;lt;/code&amp;gt;  (linke $x$-Koordinate des Schlägers)&lt;br /&gt;
* &amp;lt;code&amp;gt;get right() { return this.x + this.width; }&amp;lt;/code&amp;gt;  (rechte $x$-Koordinate des Schlägers)&lt;br /&gt;
* &amp;lt;code&amp;gt;get top() { return this.y; }&amp;lt;/code&amp;gt; (linke $y$-Koordinate des Schlägers)&lt;br /&gt;
* &amp;lt;code&amp;gt;get bottom() { return this.y + this.height; }&amp;lt;/code&amp;gt; (rechte $y$-Koordinate des Schlägers)&lt;br /&gt;
&lt;br /&gt;
Diese Attribute vereinfachen die Kollisionsfunktionen etwas, bei denen mehrfach auf die verschiedenen Seiten des Schlägers zugegriffen werden muss.&lt;br /&gt;
&lt;br /&gt;
Insgesamt sieht die Implementierung des Schlägermodells folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Konstruktor&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function ModelPaddle(p_init)&lt;br /&gt;
{&lt;br /&gt;
  this.width    = p_init.width;&lt;br /&gt;
  this.height   = p_init.height;&lt;br /&gt;
&lt;br /&gt;
  this.x_start  = p_init.pos.x;&lt;br /&gt;
  this.y_start  = p_init.pos.y;&lt;br /&gt;
  this.vx_start = p_init.vel.x;&lt;br /&gt;
  this.vy_start = p_init.vel.y;&lt;br /&gt;
  this.ax_start = p_init.acc.x;&lt;br /&gt;
  this.ay_start = p_init.acc.y;&lt;br /&gt;
&lt;br /&gt;
  this.friction = p_init.friction;&lt;br /&gt;
&lt;br /&gt;
  this.reset(); // initializes further attributes&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Öffentliche Methoden&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
ModelPaddle.prototype =&lt;br /&gt;
{&lt;br /&gt;
  reset:&lt;br /&gt;
    function()&lt;br /&gt;
    {&lt;br /&gt;
      this.stop(); // By default, the paddle does not move around.&lt;br /&gt;
      this.hide(); // By default, the paddle is invisible.&lt;br /&gt;
&lt;br /&gt;
      this.x = this.x_start;&lt;br /&gt;
      this.y = this.y_start;&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
  show:&lt;br /&gt;
    function()&lt;br /&gt;
    { this.visible = true; },&lt;br /&gt;
&lt;br /&gt;
  hide:&lt;br /&gt;
    function()&lt;br /&gt;
    { this.visible = false; },&lt;br /&gt;
&lt;br /&gt;
  stop:&lt;br /&gt;
    function()&lt;br /&gt;
    { this.vx = 0;&lt;br /&gt;
      this.vy = 0;&lt;br /&gt;
&lt;br /&gt;
      this.ax = 0;&lt;br /&gt;
      this.ay = 0;&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
  start:&lt;br /&gt;
    function(p_direction)&lt;br /&gt;
    {&lt;br /&gt;
      // react only if the paddle is visible not already moving&lt;br /&gt;
      if (this.visible === true &amp;amp;&amp;amp;&lt;br /&gt;
          this.vx === 0 &amp;amp;&amp;amp; this.vy === 0&lt;br /&gt;
         )&lt;br /&gt;
      {&lt;br /&gt;
        switch (p_direction)&lt;br /&gt;
        {&lt;br /&gt;
          case &amp;quot;left&amp;quot;:&lt;br /&gt;
            this.vx = -this.vx_start;&lt;br /&gt;
            this.ax = -this.ax_start;&lt;br /&gt;
            break;&lt;br /&gt;
          case &amp;quot;right&amp;quot;:&lt;br /&gt;
            this.vx =  this.vx_start;&lt;br /&gt;
            this.ax =  this.ax_start;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
          case &amp;quot;up&amp;quot;:&lt;br /&gt;
            this.vy = -this.vy_start;&lt;br /&gt;
            this.ay = -this.ay_start;&lt;br /&gt;
            break;&lt;br /&gt;
          case &amp;quot;down&amp;quot;:&lt;br /&gt;
            this.vy =  this.vy_start;&lt;br /&gt;
            this.ay =  this.ay_start;&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
  move:&lt;br /&gt;
    function(p_seconds)&lt;br /&gt;
    { this.x  += this.vx * p_seconds;&lt;br /&gt;
      this.y  += this.vy * p_seconds;&lt;br /&gt;
      this.vx += this.ax * p_seconds;&lt;br /&gt;
      this.vy += this.ay * p_seconds;&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
  /** The left side of the paddle (read only). */&lt;br /&gt;
  get left()   { return this.x; },&lt;br /&gt;
&lt;br /&gt;
  /** The right side of the paddle (read only). */&lt;br /&gt;
  get right()  { return this.x + this.width; },&lt;br /&gt;
&lt;br /&gt;
  /** The top side of the paddle (read only). */&lt;br /&gt;
  get top()    { return this.y; },&lt;br /&gt;
&lt;br /&gt;
  /** The bottom side of the paddle (read only). */&lt;br /&gt;
  get bottom() { return this.y + this.height; }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass es hier empfehlenswert ist, das Prototyp-Objekt des Konstruktors nicht mit mehrere Befehle der Art&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
ModelPaddle.prototype.reset = function(){...};&lt;br /&gt;
ModelPaddle.prototype.show = function(){...};&lt;br /&gt;
ModelPaddle.prototype.hide = function(){...};&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
zu befüllen, sondern ein eigenes Prototyp-Objekt zu definieren:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
ModelPaddle.prototype =&lt;br /&gt;
{&lt;br /&gt;
  reset: function(){...},&lt;br /&gt;
  show:  function(){...},&lt;br /&gt;
  hide:  function(){...},&lt;br /&gt;
  ...&lt;br /&gt;
  get left() { return this.x; },&lt;br /&gt;
  get right()  { return this.x + this.width; },&lt;br /&gt;
  ...&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Grund ist, dass es keine einfache Syntax gibt, eine Getter- oder einer Setter-Methode zu einem&lt;br /&gt;
bestehenden Objekt hinzuzufügen. Im obigen Beispiel müsste man beispielsweise Folgendes schreiben,&lt;br /&gt;
um Getter-Methoden zum Objekt „&amp;lt;code&amp;gt;ModelPaddle.prototype&amp;lt;/code&amp;gt;“ nachträglich hinzuzufügen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Object.defineProperty(ModelPaddle.prototype, &lt;br /&gt;
                      &#039;left&#039;, &lt;br /&gt;
                      { get: function() { return this.x; } }&lt;br /&gt;
                     );&lt;br /&gt;
Object.defineProperty(ModelPaddle.prototype,&lt;br /&gt;
                      &#039;right&#039;, &lt;br /&gt;
                      { get: function() { return this.x + this.width; } }&lt;br /&gt;
                     );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Text===&lt;br /&gt;
[[Datei:MiniPong04 ClassModel Text.png|gerahmt|rechts|Text-Model und Text-View]]&lt;br /&gt;
&lt;br /&gt;
Im Spiel „MiniPong“ gibt es zwei Textfelder: Eines zum Anzeigen des Scores und eines zum Anzeigen von allgemeinen Informationen.&lt;br /&gt;
Im Gegensatz zu Ball und Schläger bewegt sich ein Text (in diesem Spiel!) nicht. Daher benötigt man im Model wesentlich weniger Attribute und Methode.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; ($x$-Position des Text-Aufhängepunkt)&lt;br /&gt;
* &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt; ($y$-Position der Text-Baseline)&lt;br /&gt;
* &amp;lt;code&amp;gt;template&amp;lt;/code&amp;gt; (ein optionaler String, der die Zeichenfolge „&amp;lt;code&amp;gt;$1&amp;lt;/code&amp;gt;“ enthält)&lt;br /&gt;
* &amp;lt;code&amp;gt;value&amp;lt;/code&amp;gt; (der Wert, der im Textfeld dargestellt werden soll)&lt;br /&gt;
* &amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt; (ein Read-only-Attribut:  „&amp;lt;code&amp;gt;return template.replace(&#039;$1&#039;, value)&amp;lt;/code&amp;gt;“)&lt;br /&gt;
* &amp;lt;code&amp;gt;visible&amp;lt;/code&amp;gt; (Sichtbarkeit des Textes)&lt;br /&gt;
&lt;br /&gt;
Das zugehörige View-Objekt legt das Aussehen des Textes fest:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;color&amp;lt;/code&amp;gt; (Textfarbe)&lt;br /&gt;
* &amp;lt;code&amp;gt;font&amp;lt;/code&amp;gt; (Textfont, analog zu CSS-Fonts)&lt;br /&gt;
* &amp;lt;code&amp;gt;textAlign&amp;lt;/code&amp;gt; ([http://www.w3schools.com/tags/canvas_textalign.asp Aufhängepunkt}: &amp;lt;code&amp;gt;left&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;center&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;right&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;lt;code&amp;gt;textBaseline&amp;lt;/code&amp;gt; ([http://www.w3schools.com/tags/canvas_textbaseline.as Baseline}: &amp;lt;code&amp;gt;top&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;bottom&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;middle&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;alphabetic&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;hanging&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Damit sollte eigentlich klar sein, wie die Model- und die View-Klasse aussehen-&lt;br /&gt;
Fügen Sie diesen Code in den Rumpf der Callback-Funktion in der Datei  „&amp;lt;code&amp;gt;model/text.js&amp;lt;/code&amp;gt;“ ein. Vergessen Sie nicht,&lt;br /&gt;
in dieser Callback-Funktionen den Return-Befehl&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
  return null;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
durch den Return-Befehl&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
  return ModelText;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
zu ersetzen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;ModelText&amp;lt;/code&amp;gt;: Konstruktor&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function ModelText(p_init)&lt;br /&gt;
{&lt;br /&gt;
  this.x        = p_init.pos.x;&lt;br /&gt;
  this.y        = p_init.pos.y;&lt;br /&gt;
&lt;br /&gt;
  this.template = p_init.template;&lt;br /&gt;
  this.value    = p_init.value;&lt;br /&gt;
&lt;br /&gt;
  this.visible  = true;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;ModelText&amp;lt;/code&amp;gt;: Öffentliche Methoden&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
ModelText.prototype =&lt;br /&gt;
{&lt;br /&gt;
  // read only attribute&lt;br /&gt;
  get text()&lt;br /&gt;
  { return (this.value == null)&lt;br /&gt;
           ? &#039;&#039;&lt;br /&gt;
           : (this.template == null)&lt;br /&gt;
             ? this.value.toString()&lt;br /&gt;
             : this.template.replace(&#039;$1&#039;, this.value);&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die View-Klasse ist auch nicht viel komplexer. Auf die Vorberechnung &lt;br /&gt;
eine Mini-Canvas, der anstelle des Textes in den Haupt-Canvas kopiert wird, wird hier verzichtet.&lt;br /&gt;
Da sich der Text-Inhalt ändern kann und üblicherweise auch regelmäßig ändert, müsste man&lt;br /&gt;
bei jeder Text-Änderung einen neuen derartigen Mini-Canvas erstellen. Das ist zwar möglich,&lt;br /&gt;
führt hier aber zu weit.&lt;br /&gt;
   &lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;ViewText&amp;lt;/code&amp;gt;: Konstruktor&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function ViewText(p_model, p_init /*, p_document*/)&lt;br /&gt;
{&lt;br /&gt;
  this.model        = p_model;&lt;br /&gt;
&lt;br /&gt;
  this.color        = p_init.color        || &#039;black&#039;;&lt;br /&gt;
  this.font         = p_init.font         || &#039;normal&#039;;&lt;br /&gt;
  this.textAlign    = p_init.textAlign    || &#039;left&#039;;&lt;br /&gt;
  this.textBaseline = p_init.textBaseline || &#039;alphabetic&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;ViewText&amp;lt;/code&amp;gt;: Öffentliche Methoden&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
  ViewText.prototype.draw =&lt;br /&gt;
    function(p_context)&lt;br /&gt;
    {&lt;br /&gt;
      var l_model = this.model;&lt;br /&gt;
&lt;br /&gt;
      if (l_model.value == null || l_model.value.toString() === &#039;&#039;)&lt;br /&gt;
        return;&lt;br /&gt;
&lt;br /&gt;
      // ALL font attributes must be set, as it cannot be&lt;br /&gt;
      // guaranteed that another module has changed some font&lt;br /&gt;
      // attributes before.&lt;br /&gt;
      p_context.font         = this.font;&lt;br /&gt;
      p_context.textAlign    = this.textAlign;&lt;br /&gt;
      p_context.textBaseline = this.textBaseline;&lt;br /&gt;
      p_context.fillStyle    = this.color;&lt;br /&gt;
      p_context.fillText(l_model.text,&lt;br /&gt;
                         (this.textAlign === &#039;center&#039;)&lt;br /&gt;
                           ? p_context.canvas.clientWidth/2&lt;br /&gt;
                           : l_model.x,&lt;br /&gt;
                         l_model.y&lt;br /&gt;
                        );&lt;br /&gt;
    };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Haben Sie an die Return-Anweisung „&amp;lt;code&amp;gt;return ViewText;&amp;lt;/code&amp;gt;“ gedacht? &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===Button===&lt;br /&gt;
[[Datei:MiniPong04 ClassModel Button.png|gerahmt|rechts|Button-Model und Button-View]]&lt;br /&gt;
&lt;br /&gt;
Einen grafischen Button, der im Canvas dargestellt wird, könnte man mit Hilfe eines Rechtecks oder Kreises und eines Textes realisieren. &lt;br /&gt;
Button-Klicks müsste man dann mit Hilfe eine Kollisionserkennung („Mausspitze kollidiert mit Button“) und -behandlung verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Hier soll ein einfacherer Weg eingeschlafen werden: Als Button wird ein HTML-Button verwendet, der sich&lt;br /&gt;
außerhalb der Bühne befindet. Folgende Attribute sind in einem Model-Objekt enthalten:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;label&amp;lt;/code&amp;gt; (Der Text, mit dem der Button im HTML-Dokument beschriftet sein soll.)&lt;br /&gt;
* &amp;lt;code&amp;gt;class&amp;lt;/code&amp;gt; (Ein CSS-Klassen-Name, der dem Button-Element im HTML-Dokument zugeordnet wird. Beispielsweise kann man eine CSS-Klasse „&amp;lt;code&amp;gt;.hidden&amp;lt;/code&amp;gt;“ definieren, mit dem man den Button unsichtbar machen kann.)&lt;br /&gt;
* &amp;lt;code&amp;gt;onClick&amp;lt;/code&amp;gt; (Eine Methode, die aufgerufen wird, sobald der Button geklickt wird.)&lt;br /&gt;
&lt;br /&gt;
Der Konstruktor übernimmt für diese Attribute wie üblich alle Initialwerte aus einen Init-Objekt namens „&amp;lt;code&amp;gt;p_init&amp;lt;/code&amp;gt;“. Ein Problem besteht dabei allerdings.&lt;br /&gt;
Das Init-Objekt wird üblicherweise aus einer JSON-Datei eingelesen. Eine derartige Datei kann keine JavaScript-Funktionen enthalten.&lt;br /&gt;
Das heißt, das Attribut „&amp;lt;code&amp;gt;onClick&amp;lt;/code&amp;gt;“ wird üblicherweise mit „&amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;“ initialisiert. Es ist dann die Aufgabe einer Logikkomponente&lt;br /&gt;
diesem Attribut zur rechten Zeit eine geeignete Prozedur zuzuweisen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ModelButton: Konstruktor&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function ModelButton(p_init)&lt;br /&gt;
{&lt;br /&gt;
  this.label   = p_init.label || null;&lt;br /&gt;
  this.class   = p_init.class || null;&lt;br /&gt;
  this.onClick = p_init.onClick || null;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die View ist auch nicht sonderlich kompliziert.&lt;br /&gt;
Der Konstruktor speichert wie üblich das zugehörige Model im Attribut „&amp;lt;code&amp;gt;model&amp;lt;/code&amp;gt;“.&lt;br /&gt;
Außerdem sucht er im HTML-Dokument denjenigen Button, der mit dem Model verknüpft werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ViewButton: Konstruktor&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function ViewButton(p_model, p_init, p_document)&lt;br /&gt;
{&lt;br /&gt;
  this.model   = p_model;&lt;br /&gt;
  this.element = p_document.getElementById(p_init.elementID);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sie Draw-Methode macht nichts weiter, als alle Attribute, die im Model definiert sind,&lt;br /&gt;
in das zugehörige HTML-Button-Element zu kopieren, sofern das jeweilige Attribut im Model&lt;br /&gt;
definiert ist und sich vom im HTML-Button-Element gespeicherten Wert unterscheidet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ViewButton: Öffentliche Methoden&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
ViewButton.prototype.draw =&lt;br /&gt;
  function()&lt;br /&gt;
  {&lt;br /&gt;
    var l_model   = this.model,&lt;br /&gt;
        l_element = this.element;&lt;br /&gt;
&lt;br /&gt;
    if (l_model.label != null &amp;amp;&amp;amp; l_element.innerHTML !== this.model.label)&lt;br /&gt;
    { l_element.innerHTML = this.model.label; }&lt;br /&gt;
    if (l_model.class != null &amp;amp;&amp;amp; l_element.className !== this.model.class)&lt;br /&gt;
    { l_element.className = this.model.class; }&lt;br /&gt;
    if (l_model.onClick != null &amp;amp;&amp;amp; l_element.onclick !== this.model.onClick)&lt;br /&gt;
    { l_element.onclick = this.model.onClick; }&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da die Draw-Methode regelmäßig aufgerufen wird (ca. 60 mal pro Sekunde),&lt;br /&gt;
ändert sich das Aussehen und/oder das Verhalten des Button automatisch mit &lt;br /&gt;
jeder Änderung des Models.&lt;br /&gt;
&lt;br /&gt;
Eigentlich ist das ein Overkill. So eine Änderung des Button-Labels und des Button-Verhaltens&lt;br /&gt;
passiert nur ein paar mal pro Spiel.&lt;br /&gt;
Hätte die Logik eine direkten Zugriff auf die View, könnte man es sich sparen, &lt;br /&gt;
den Button durch die View-Loop aktualisieren zu lassen.&lt;br /&gt;
Wenn man der Spiellogik – aus gutem Grund – keinen direkten Zugriff auf die View gewähren will&lt;br /&gt;
und man den Button auch nicht viele Dutzend mal pro Sekunde aktualisieren will, hilft der Einsatz des &lt;br /&gt;
so genannten [[Observer-Pattern]]s weiter. Dies soll hier aber nicht weiter verfolgt werden.&lt;br /&gt;
&lt;br /&gt;
==Keyboard-Controller==&lt;br /&gt;
[[Datei:MiniPong04 ClassModel controlKeyboard.png|gerahmt|rechts|Der Keyboard-Controller]]&lt;br /&gt;
&lt;br /&gt;
Der Controller zum Steuern des Paddles per Tastatur ändert sich nicht. Er kann eins zu eins&lt;br /&gt;
[https://glossar.hs-augsburg.de/beispiel/tutorium/es5/minipong/WK_MiniPong03/web/js/app/control/keyboard.js aus dem dritten Teil des Tutoriums übernommen]&lt;br /&gt;
und in die Datei &amp;lt;code&amp;gt;controller/keyboard.js&amp;lt;/code&amp;gt; eingefügt werden.&lt;br /&gt;
&lt;br /&gt;
==Kollisionserkennung und -behandlung==&lt;br /&gt;
[[Datei:MiniPong04 ClassModel Collision.png|gerahmt|rechts|Hilfsprozeduren für Kollisionserkennung und -behandlung]]&lt;br /&gt;
&lt;br /&gt;
Im [[HTML5-Tutorium:_Canvas:_MiniPong_03|dritten Teil des Tutoriums]] wurde die Kollisionserkennung und -behandlung&lt;br /&gt;
als Teil des Models angesehen. Ihre einzige Aufgabe war es, Geschwindigkeit und Position von beweglichen Objekten &lt;br /&gt;
im Falle von Kollisionen zu korrigieren. Nun kommt noch eine weitere Aufgabe hinzu. Sie muss die Spiellogik über&lt;br /&gt;
Kollisionen informieren, die das Spielgeschehen beeinflussen. Im Fall von MiniPong sind das die Kollision von Schläger und Ball&lt;br /&gt;
(Punktgewinn) sowie die Kollision von Schläger und unterem Bühnenrand (Spielende). &lt;br /&gt;
&lt;br /&gt;
Bislang gab es lediglich ein Modul namens &lt;br /&gt;
„&amp;lt;code&amp;gt;[https://glossar.hs-augsburg.de/beispiel/tutorium/es5/minipong/WK_MiniPong03/web/js/app/model/collision.js|collision.js]&amp;lt;code&amp;gt;“&lt;br /&gt;
zur Kollisionsbehandlung. Dieses Modul droht zum Giganten zu werden, wenn immer mehr und mehr Kollisionsarten behandelt werden müssen.&lt;br /&gt;
Daher wird es in mehrere Teilmodule aufgespalten.&lt;br /&gt;
&lt;br /&gt;
Die Kollisionsprozedur „&amp;lt;code&amp;gt;collisionStagePaddle&amp;lt;/code&amp;gt;“ kann eins zu eins vom dritten Teil des Tutoriums übernommen werden,&lt;br /&gt;
da die Kollision des Paddles mit der Wand keine Auswirkung auf das Spielgeschehen hat. Diese Kollision bewirkt lediglich,&lt;br /&gt;
dass der Schläger gestoppt wird. Und das erledigt die Prozedur von sich aus. Allerdings gibt es durchaus Situationen, in denen auch &lt;br /&gt;
eine Kollision zwischen Schläger und Wand von der Spiellogik behandelt werden muss. Beispielsweise kann man im Breakout-Spiel [[Wikipedia:Bolo|Bolo]] &lt;br /&gt;
mit dem Schläger gegen die Wand „donnern“. Wenn man dies fest genug macht, wenn also der Schläger bei der Kollision mit der Wand eine gewisse&lt;br /&gt;
Geschwindigkeit hat, wird der Raum leicht erschüttert. Dies hat auch Auswirkungen auf den Ball, dessen Geschwindigkeitsvektor dadurch leicht verändert wird.&lt;br /&gt;
Auf diese Weise kann man den Ball, wenn er irgendwo feststeckt, häufig wieder frei bekommen.  &lt;br /&gt;
&lt;br /&gt;
Die folgende Implementierung der Kollisionsprozedur „&amp;lt;code&amp;gt;collisionStagePaddle&amp;lt;/code&amp;gt;“ unterscheidet sich in einer Hinsicht von der ursprünglichen&lt;br /&gt;
Implementierung. Anstatt die Ränder des Schlägers zu berechnen, werden die neuen Schläger-Attribute „&amp;lt;code&amp;gt;left&amp;lt;/code&amp;gt;“, „&amp;lt;code&amp;gt;right&amp;lt;/code&amp;gt;“, „&amp;lt;code&amp;gt;top&amp;lt;/code&amp;gt;“ und „&amp;lt;code&amp;gt;bottom&amp;lt;/code&amp;gt;“ verwendet. Fügen Sie diesen Code ins Modul „&amp;lt;code&amp;gt;collision/stage_paddle&amp;lt;/code&amp;gt;“ ein (und vergessen Sie nicht, den Return-Befehl anzupassen).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function collisionStagePaddle(p_stage, p_paddle)&lt;br /&gt;
{&lt;br /&gt;
  // If the paddle collides with the left wall of the stage,&lt;br /&gt;
  // stop it and move it back to the stage.&lt;br /&gt;
  if (p_paddle.vx &amp;lt; 0 &amp;amp;&amp;amp; p_paddle.left &amp;lt;= 0)&lt;br /&gt;
  {&lt;br /&gt;
    p_paddle.stop();&lt;br /&gt;
    p_paddle.x = 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // If the paddle collides with the right wall of the stage,&lt;br /&gt;
  // stop it and move it back to the stage.&lt;br /&gt;
  if (p_paddle.vx &amp;gt; 0 &amp;amp;&amp;amp; p_paddle.right &amp;gt;= p_stage.width)&lt;br /&gt;
  {&lt;br /&gt;
    p_paddle.stop();&lt;br /&gt;
    p_paddle.x = p_stage.width - p_paddle.width;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // If the paddle collides with the top wall of the stage,&lt;br /&gt;
  // stop it and move it back to the stage.&lt;br /&gt;
  if (p_paddle.vy &amp;lt; 0 &amp;amp;&amp;amp; p_paddle.top &amp;lt;= 0)&lt;br /&gt;
  {&lt;br /&gt;
    p_paddle.stop();&lt;br /&gt;
    p_paddle.y = 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // If the paddle collides with the bottom wall of the stage,&lt;br /&gt;
  // stop it and move it back onto the stage.&lt;br /&gt;
  if (p_paddle.vy &amp;gt; 0 &amp;amp;&amp;amp; p_paddle.bottom &amp;gt;= p_stage.height)&lt;br /&gt;
  {&lt;br /&gt;
    p_paddle.stop();&lt;br /&gt;
    p_paddle.y = p_stage.height - p_paddle.height;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Kollisionsprozedur „&amp;lt;code&amp;gt;collisionStageBall&amp;lt;/code&amp;gt;“ ist im Modul „&amp;lt;code&amp;gt;collision/stage_ball&amp;lt;/code&amp;gt;“ enthalten. &lt;br /&gt;
Die Prozedur kann allerdings nicht eins zu eins vom dritten Teil des Tutoriums übernommen werden, da die Kollision mit&lt;br /&gt;
der unteren Wand anders behandelt werden muss, als die Kollision mit den übrigen Wänden.&lt;br /&gt;
&lt;br /&gt;
Bei einer Kollision mit der unteren Wand wird das Spiel beendet. Für die Behandlung des Spielendes ist die &lt;br /&gt;
Spiellogik zuständig. Um über diese Art der Kollision benachrichtigt zu werden, übergibt sie der &lt;br /&gt;
Kollisionsprozedur „&amp;lt;code&amp;gt;collisionStageBall&amp;lt;/code&amp;gt;“ im Parameter „&amp;lt;code&amp;gt;cb_hit&amp;lt;/code&amp;gt;“ eine Callback-Funktion,&lt;br /&gt;
die im Falle einer Kollision mit der unteren Wand aufgerufen werden soll. Damit die Spiel erst endet,&lt;br /&gt;
wenn der Ball die Bühne vollständig verlassen hat, wird die untere Wand etwas nach unten in den nicht sichtbaren Bereich &lt;br /&gt;
verschoben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function collisionStageBall(p_stage, p_ball, cb_exit)&lt;br /&gt;
{&lt;br /&gt;
  // If the ball collides with the left or the right wall of the stage&lt;br /&gt;
  // mirror its x-velocity and move the ball back onto the stage.&lt;br /&gt;
  if (p_ball.x &amp;lt;= p_ball.r)&lt;br /&gt;
  {&lt;br /&gt;
    p_ball.vx = -p_ball.vx;&lt;br /&gt;
    p_ball.x += 2*(p_ball.r - p_ball.x);&lt;br /&gt;
  }&lt;br /&gt;
  if (p_ball.x &amp;gt;= p_stage.width - p_ball.r)&lt;br /&gt;
  {&lt;br /&gt;
    p_ball.vx = -p_ball.vx;&lt;br /&gt;
    p_ball.x -=  2*(p_ball.r - p_stage.width + p_ball.x);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // If the ball collides with the top wall of the stage&lt;br /&gt;
  // mirror its y-velocity and move the ball back onto the stage.&lt;br /&gt;
  if (p_ball.y &amp;lt;= p_ball.r)&lt;br /&gt;
  {&lt;br /&gt;
    p_ball.vy = -p_ball.vy;&lt;br /&gt;
    p_ball.y += 2*(p_ball.r - p_ball.y);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // If the ball leaves the bottom of the stage, call cb_exit.&lt;br /&gt;
  // Factor 1.5: The ball is really outside the stage an thus invisible,&lt;br /&gt;
  // even if the view draws a very thick border around it.&lt;br /&gt;
  if (p_ball.y &amp;gt;= p_stage.height + 1.5*p_ball.r)&lt;br /&gt;
  { if (cb_exit) &lt;br /&gt;
      cb_exit(); &lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu guter Letzt muss noch die Kollisionserkennung und -behandlung für Kollisionen des Balls mit dem Schläger&lt;br /&gt;
realisiert werden. Über jede derartige Kollision wird die Spiellogik ebenfalls mit einer Callback-Funktion informiert,&lt;br /&gt;
damit letztere den Punktestand aktualisieren kann. &lt;br /&gt;
&lt;br /&gt;
Die nachfolgende Implementierung der Kollisionserkennung und -behandlung ist ziemlich primitiv, da nur zwei Fälle &lt;br /&gt;
unterschieden werden: Kollision des Balles mit der oberen oder der unteren Seite des Schlägers. Das reicht &lt;br /&gt;
zunächst für unsere Zwecke, führt aber schon zu Problemen, wenn ein senkrechter Schläger an einer Seitenwand&lt;br /&gt;
verwendet werden soll. In diesem Fall müsste die Kollisionsprozedur umgeschrieben werden. &lt;br /&gt;
&lt;br /&gt;
Bei einer korrekten Kollisionserkennung und -behandlung zwischen Kreis und Rechteck müssen 8 Fälle unterschieden werden:&lt;br /&gt;
Kollision des Balls mit einer der vier Seitenwände sowie Kollision des Balls mit einer der vier Ecken.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function collisionBallPaddle(p_ball, p_paddle, cb_hit)&lt;br /&gt;
{&lt;br /&gt;
  if (p_ball.y + p_ball.r     &amp;gt;= p_paddle.top    &amp;amp;&amp;amp;&lt;br /&gt;
      p_ball.y + p_ball.r     &amp;lt;= p_paddle.bottom &amp;amp;&amp;amp;&lt;br /&gt;
      p_ball.x + 0.5*p_ball.r &amp;gt;= p_paddle.left   &amp;amp;&amp;amp;&lt;br /&gt;
      p_ball.x - 0.5*p_ball.r &amp;lt;= p_paddle.right&lt;br /&gt;
     )&lt;br /&gt;
  {&lt;br /&gt;
    // Resolve penetration.&lt;br /&gt;
    if (p_ball.vy &amp;gt; 0)        // The ball is moving from top to bottom.&lt;br /&gt;
    { p_ball.y = p_paddle.top - p_ball.r; }&lt;br /&gt;
    else                       // The ball is moving from bottom to top.&lt;br /&gt;
    { p_ball.y = p_paddle.bottom + p_ball.r; }&lt;br /&gt;
&lt;br /&gt;
    // Modify the velocity of the ball.&lt;br /&gt;
    p_ball.vy = -p_ball.vy;&lt;br /&gt;
    p_ball.vx += p_paddle.friction*p_paddle.vx;&lt;br /&gt;
&lt;br /&gt;
    // If the paddle hits the ball, invoke the callback function.&lt;br /&gt;
    if (cb_hit) &lt;br /&gt;
    {  cb_hit(); }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Model- und View-Loop==&lt;br /&gt;
[[Datei:MiniPong04 ClassModel Loop.png|gerahmt|rechts|Model-Loop und View-Loop]]&lt;br /&gt;
&lt;br /&gt;
Die Model-Loop sowie die View-Loop waren bislang Bestandteile des Moduls &lt;br /&gt;
„&amp;lt;code&amp;gt;[https://glossar.hs-augsburg.de/beispiel/tutorium/es5/minipong/WK_MiniPong03/web/js/app/minipong.js minipong.js]&amp;lt;/code&amp;gt;“. Da dieses Modul deutlich größer wird, &lt;br /&gt;
sollte es zerschlagen werden. Für die MiniPong-Anwedung wird es in vier Einzelmodule unterteilt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;init&amp;lt;/code&amp;gt; (Erzeugung aller wesentlichen Objekte; Starten der View Loop; Verknüpfung des Keyboard-Controllers mit dem Paddle)&lt;br /&gt;
* &amp;lt;code&amp;gt;ViewLoop&amp;lt;/code&amp;gt; (Visualisierung der aktuellen Zustände der grafischen Objekte des Spiels)&lt;br /&gt;
* &amp;lt;code&amp;gt;ModelLoop&amp;lt;/code&amp;gt; (Berechnung der Positionen der beweglichen Objekte des Spiels; Information der Spiellogik über bestimmte Kollisionen)&lt;br /&gt;
* &amp;lt;code&amp;gt;minipong&amp;lt;/code&amp;gt; (Die Spiellogik)&lt;br /&gt;
&lt;br /&gt;
Zunächst werden die beiden Loops in eigenständige Module ausgelagert. Beide Module werden als Klassen realisiert.&lt;br /&gt;
Das heißt, es müssen jeweils ein &amp;lt;code&amp;gt;ViewLoop&amp;lt;/code&amp;gt;- und ein  &amp;lt;code&amp;gt;ModelLoop&amp;lt;/code&amp;gt;-Objekt erzeugt werden.&lt;br /&gt;
Diese Objekte kennen jeweils zwei Methoden &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;stop&amp;lt;/code&amp;gt;, den denen die Loops&lt;br /&gt;
gestartet und auch wieder angehalten werden können. Im Falle der View-Loop ist das für das Spiel MiniPong nicht&lt;br /&gt;
sonderlich wichtig, da diese View nur einmal gestartet wird und dann dauerhaft aktiv ist. Bei einer komplexeren Web-Anwendung &lt;br /&gt;
solle man allerdings versuchen, die View-Loop nur während des eigentlichen Spiels zu aktivieren. So eine Loop &lt;br /&gt;
kostet Rechenpower und belastet damit insbesondere den Akku von mobilen Geräten.&lt;br /&gt;
&lt;br /&gt;
Die Model-Loop muss auch schon von der MiniPong-Spiellogik gestartet und gestoppt werden können. Dies wird insbesondere&lt;br /&gt;
bei einer erweiterten Variante deutlich, bei dem das Spiel durch eine Pause-Taste temporär angehalten werden kann&lt;br /&gt;
([https://glossar.hs-augsburg.de/beispiel/tutorium/es5/minipong/WK_MiniPong04/web/index_pause.html index_pause.html]). &lt;br /&gt;
&lt;br /&gt;
Die View-Loop erhält als Argumente das Fenster in dem die Web-Anwendung läuft, einen Canvas, auf dem grafische Objekte visualisiert werden sollen&lt;br /&gt;
sowie eine Liste, die die Views dieser Objekte enthält. Die View-Loop löscht zu Beginn den Canvas und ruft dann für alle View-Objekte die &lt;br /&gt;
Methode „&amp;lt;code&amp;gt;draw&amp;lt;/code&amp;gt;“ auf. Dieser übergibt sie als Argument den 2D-Context des Canvas-Elements. Allerdings ist die Draw-Methode nicht&lt;br /&gt;
verpflichtet, ein Objekt auf den Canvas zu zeichnen. Sie kann auch ein Objekt im DOM-Baum des HTML-Dokuments aktualisieren.&lt;br /&gt;
Dies macht beispielsweise die Draw-Methode des Button-Objekts. &lt;br /&gt;
  &lt;br /&gt;
&#039;&#039;&#039;ViewLoop: Konstruktor&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function ViewLoop(p_window, p_canvas, p_views)&lt;br /&gt;
{&lt;br /&gt;
  var l_context = p_canvas.getContext(&amp;quot;2d&amp;quot;),&lt;br /&gt;
      n         = p_views.length;&lt;br /&gt;
&lt;br /&gt;
  this.v_window = p_window;&lt;br /&gt;
&lt;br /&gt;
  this.m_update_view =&lt;br /&gt;
    function m_update_view()&lt;br /&gt;
    {&lt;br /&gt;
      // clear canvas&lt;br /&gt;
      l_context.clearRect(0, 0, p_canvas.width, p_canvas.height);&lt;br /&gt;
&lt;br /&gt;
       // draw all visual objects&lt;br /&gt;
      for (var i = 0;  i &amp;lt;n; i++ )&lt;br /&gt;
      { p_views[i].draw(l_context); }&lt;br /&gt;
&lt;br /&gt;
      p_window.requestAnimationFrame(m_update_view);&lt;br /&gt;
    };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aktiviert und am Laufen gehalten wird die View-Loop wie üblich mit Hilfe der Methode „&amp;lt;code&amp;gt;window.requestAnimationFrame&amp;lt;/code&amp;gt;“.&lt;br /&gt;
Diese Methode liefert beim Aufruf eine Integerzahl zurück, die den Timer eindeutig identifiziert. Wenn man diesen Identifikator speichert,&lt;br /&gt;
kann man die rekursiven Aufrufe der Methode  „&amp;lt;code&amp;gt;window.cancelAnimationFrame&amp;lt;/code&amp;gt;“ unterbrechen und so die Loop anhalten.&lt;br /&gt;
Dies wird ausgenutzt, um die Start- und die Stopp-Methode zu realisieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ViewLoop:Öffentliche Methoden&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
ViewLoop.prototype =&lt;br /&gt;
{&lt;br /&gt;
  start:&lt;br /&gt;
    function()&lt;br /&gt;
    {&lt;br /&gt;
      if (this.v_timer == null)&lt;br /&gt;
      { this.v_timer = this.v_window.requestAnimationFrame(this.m_update_view); }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
  stop:&lt;br /&gt;
    function()&lt;br /&gt;
    {&lt;br /&gt;
      if (this.v_timer != null)&lt;br /&gt;
      {&lt;br /&gt;
        this.v_window.cancelAnimationFrame(this.v_timer);&lt;br /&gt;
        delete this.v_timer;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Model-Loop-Konstruktor erwartet als Input eine Kollisionsprozedur, die &lt;br /&gt;
(angestrebte) Update-Frequenz sowie eine Liste von Model-Objekten,&lt;br /&gt;
die bewegt werden sollen.&lt;br /&gt;
&lt;br /&gt;
Der Konstruktor definiert ein „privates“ Attribut „&amp;lt;code&amp;gt;v_milliseconds&amp;lt;/code&amp;gt;“ und eine „private“ Methode „&amp;lt;code&amp;gt;m_update_model&amp;lt;/code&amp;gt;“&lt;br /&gt;
auf die die Start- und die Stop-Methode zugreifen können. (In Wirklichkeit handelt es sich um ein öffentliches Attribut und um eine öffentliche Methode, &lt;br /&gt;
da im Prototype-Objekt keine privaten Elemente definiert werden können. Ich kennzeichne private Elemente einfach mittels eines Namenszusatzes&lt;br /&gt;
„&amp;lt;code&amp;gt;v_...&amp;lt;/code&amp;gt;“ – &amp;lt;code&amp;gt;v&amp;lt;/code&amp;gt; für „(Zustands-)Variable“ – bzw. „&amp;lt;code&amp;gt;m_...&amp;lt;/code&amp;gt;“ – &amp;lt;code&amp;gt;m&amp;lt;/code&amp;gt; für „Methode“ – und weiß damit,&lt;br /&gt;
dass ich von außerhalb nicht auf derartige Elemente zugreifen darf.)&lt;br /&gt;
&lt;br /&gt;
Die Methode „&amp;lt;code&amp;gt;m_update_model&amp;lt;/code&amp;gt;“ ruft zunächst für alle Objekte die Move-Methode auf und führt anschließend (a posteriori) &lt;br /&gt;
mit Hilfe der Kollisionsprozedur eine Kollisionserkennung und -behandlung durch.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ModelLoop: Konstruktor&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function ModelLoop(p_collision, p_f, p_models)&lt;br /&gt;
{&lt;br /&gt;
  var l_seconds = 1 / p_f;&lt;br /&gt;
  this.v_milliseconds = 1000 * l_seconds;&lt;br /&gt;
&lt;br /&gt;
  this.m_update_model =&lt;br /&gt;
    function ()&lt;br /&gt;
    {&lt;br /&gt;
      // move around all movable objects&lt;br /&gt;
      for (var i = 0, n = p_models.length; i &amp;lt; n; i++)&lt;br /&gt;
      { p_models[i].move(l_seconds); }&lt;br /&gt;
&lt;br /&gt;
      // detect and handle collision (a posteriori)&lt;br /&gt;
      p_collision();&lt;br /&gt;
    };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Model-Update-Methode soll regelmäßig alle &amp;lt;code&amp;gt;v_milliseconds&amp;lt;/code&amp;gt; Millisekunden aufgerufen werden.&lt;br /&gt;
Dazu wird die JavaScript-Funktion „&amp;lt;code&amp;gt;setInterval&amp;lt;/code&amp;gt;“ verwendet (die allerdings die Zeitvorgaben nicht sonderlich genau nimmt).&lt;br /&gt;
Diese Funktion liefert, wie auch schon &amp;lt;code&amp;gt;requestAnimationFrame&amp;lt;/code&amp;gt;, beim Aufruf eine Integerzahl zurück,&lt;br /&gt;
mit der der Timer eindeutig identifiziert wird. Mit Hilfe dieses Identifikators und der Funktion  „&amp;lt;code&amp;gt;clearInterval&amp;lt;/code&amp;gt;“&lt;br /&gt;
kann man den Timer  anhalten. Die Star- und die Stopp-Methoden können daher auf genau dieselbe Art realisiert werden,&lt;br /&gt;
wie bei der View-Loop:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ModelLoop:Öffentliche Methoden&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
ModelLoop.prototype =&lt;br /&gt;
{&lt;br /&gt;
  start:&lt;br /&gt;
    function()&lt;br /&gt;
    {&lt;br /&gt;
      if (this.v_timer == null)&lt;br /&gt;
      { this.v_timer = setInterval(this.m_update_model, this.v_milliseconds) }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
  stop:&lt;br /&gt;
    function()&lt;br /&gt;
    {&lt;br /&gt;
       if (this.v_timer != null)&lt;br /&gt;
       {&lt;br /&gt;
         clearInterval(this.v_timer);&lt;br /&gt;
         delete this.v_timer;&lt;br /&gt;
       }&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Initialisierung==&lt;br /&gt;
[[Datei:MiniPong04 ClassModel init.png|gerahmt|rechts|Initialisierung der Web-Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Die Initialisierungsprozedur „&amp;lt;code&amp;gt;init&amp;lt;/code&amp;gt;“ hat zwei Aufgaben: Zunächst muss sie alle wesentlichen Objekte erstellen&lt;br /&gt;
und anschließend die Anwendung zu Laufen bringen.&lt;br /&gt;
 &lt;br /&gt;
Sie erwartet zwei Argumente: das Fenster, in dem die Anwendung läuft und das Initialisierungsobjekt,&lt;br /&gt;
das in der JSON-Datei definiert wurde:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function init(p_window, p_init)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend muss sie die benötigten Objekte erstellen bzw. aus dem HTML-Dokument extrahieren.&lt;br /&gt;
&lt;br /&gt;
Die View-Objekte benötigen das im  Browser-Fenster enthalten HTML-Dokument sowie das&lt;br /&gt;
darin enthalten Canvas-Element. Auch der Keyboard-Controller benötigt das HTML-Dokument.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var l_canvas_init = p_init.canvas,&lt;br /&gt;
    l_document    = p_window.document,&lt;br /&gt;
    l_canvas      = l_document.getElementById(l_canvas_init.element),&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als nächstes müssen alle Model- und View-Objekte erzeugt werden, mit Ausnahme des &amp;lt;code&amp;gt;ModelStage&amp;lt;/code&amp;gt;-Objekts.&lt;br /&gt;
(Als  &amp;lt;code&amp;gt;ModelStage&amp;lt;/code&amp;gt;-Objekt wird das Canvas-Init-Objekt verwendet. Es enthält die Größe und die Breite der Bühne,&lt;br /&gt;
und das ist alles was die Kollisionsprozedur von der Bühne wissen muss.)&lt;br /&gt;
&lt;br /&gt;
Jedem Model-Objekt werden die Initialisierungsinformationen übergeben, die für das jeweilige Objekt im Initialisierungsobjekt&lt;br /&gt;
„&amp;lt;code&amp;gt;p_init&amp;lt;/code&amp;gt;“ enthalten sind. Jedem View-Objekt werden nicht nur Initialisierungsinformationen aus dem &lt;br /&gt;
&amp;lt;code&amp;gt;p_init&amp;lt;/code&amp;gt;-Objekt übergeben, sondern auch das Model, das es darstellen soll. Darüber hinaus benötigen&lt;br /&gt;
einige Objekte das Objekt „&amp;lt;code&amp;gt;p_dokument&amp;lt;/code&amp;gt;“, um einen Mini-Canvas zum Cachen der grafischen Darstellung &lt;br /&gt;
des Models erstellen zu können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
l_model_button = new ModelButton(p_init.model.buttonStartStop),&lt;br /&gt;
l_view_button  = new ViewButton(l_model_button, p_init.view.buttonStartStop, l_document),&lt;br /&gt;
&lt;br /&gt;
l_model_ball   = new ModelBall(p_init.model.ball),&lt;br /&gt;
l_view_ball    = new ViewBall(l_model_ball, p_init.view.ball, l_document),&lt;br /&gt;
&lt;br /&gt;
l_model_paddle = new ModelPaddle(p_init.model.paddle),&lt;br /&gt;
l_view_paddle  = new ViewPaddle(l_model_paddle, p_init.view.paddle, l_document),&lt;br /&gt;
&lt;br /&gt;
l_model_info   = new ModelText(p_init.model.info),&lt;br /&gt;
l_view_info    = new ViewText(l_model_info, p_init.view.info),&lt;br /&gt;
&lt;br /&gt;
l_model_score  = new ModelText(p_init.model.score),&lt;br /&gt;
l_view_score   = new ViewText(l_model_score, p_init.view.score),&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als nächstes werden die soeben erzeugten Model- und View-Objekte in Container gesteckt, &lt;br /&gt;
damit sie möglichst einfach an die Spiellogik bzw.  die View-Loop übergeben werden können.&lt;br /&gt;
&lt;br /&gt;
Die Modelle werden in ein [[Hasharray]] (= JavaScript-Objekt) gepackt, da die Spiellogik &lt;br /&gt;
namentlich auf die Objekte zugreifen können muss. &lt;br /&gt;
für die View-Loop wird ein einfaches Array als Container eingesetzt, da diese einfach der&lt;br /&gt;
Reihe nach für alle View-Objekte die Draw-Methode aufruft.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
l_models = { stage:  l_canvas_init,&lt;br /&gt;
             button: l_model_button,&lt;br /&gt;
             ball:   l_model_ball,&lt;br /&gt;
             paddle: l_model_paddle,&lt;br /&gt;
             info:   l_model_info,&lt;br /&gt;
             score:  l_model_score&lt;br /&gt;
           },&lt;br /&gt;
&lt;br /&gt;
l_views  = [ l_view_button, l_view_ball, l_view_paddle, l_view_info, l_view_score];&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie üblich muss noch die Größe des Canvas angepasst werden. Diese Größe ist wie immer im Objekt&lt;br /&gt;
„&amp;lt;code&amp;gt;l_canvas_init&amp;lt;/code&amp;gt;“ enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
l_canvas.width  = l_canvas_init.width;&lt;br /&gt;
l_canvas.height = l_canvas_init.height;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu guter Letzt muss Init-Prozedur eine View-Loop erzeugen und starten (das geht in einem Aufwasch,&lt;br /&gt;
die die Loop nicht mehr angehalten werden soll), den Keyboard-Controller dem Paddle zuweisen &lt;br /&gt;
und die Spiellogik starten.&lt;br /&gt;
&lt;br /&gt;
Bei jedem dieser drei Aufrufe übergibt sie einige der zuvor erzeugten Objekte:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ViewLoop&amp;lt;/code&amp;gt;: alle View-Objekte&lt;br /&gt;
* &amp;lt;code&amp;gt;controlKeyboard&amp;lt;/code&amp;gt;: das Paddle-Model&lt;br /&gt;
* &amp;lt;code&amp;gt;minipong&amp;lt;/code&amp;gt;: alle Model-Objekte&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
new ViewLoop(p_window, l_canvas, l_views).start();&lt;br /&gt;
controlKeyboard(p_window, p_init.control.player, l_model_paddle);&lt;br /&gt;
minipong(p_init.game, l_models);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==MiniPong==&lt;br /&gt;
[[Datei:MiniPong04 ClassModel minipong.png|gerahmt|rechts|MiniPong: Die Spiellogik]]&lt;br /&gt;
&lt;br /&gt;
Nachdem das Modul „&amp;lt;code&amp;gt;logic/minipong&amp;lt;/code&amp;gt;“ von allen übrigen Aufgaben befreit wurde, ist es nur noch für die Umsetzung der Spiellogik verantwortlich.&lt;br /&gt;
&lt;br /&gt;
Sie erhält von der Init-Prozedur die für sie bestimmten Initialisierungswerte sowie die Model-Objekte, die sie manipulieren kann und soll. &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function minipong(p_init, p_models)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zunächst speichert sie alle Model-Objekte in lokalen Variablen. Das hat den Vorteil, dass sie nicht immer&lt;br /&gt;
Aufrufe der Art „&amp;lt;code&amp;gt;p_models.xyz&amp;lt;/code&amp;gt;“ tätigen muss. Außerdem legt sie ein leeres Array  „&amp;lt;code&amp;gt;l_models_movable&amp;lt;/code&amp;gt;“&lt;br /&gt;
an, das später alle beweglichen Model-Objekte enthält, {{dh}} alle Model-Objekte, für die die Methode „&amp;lt;code&amp;gt;l_move&amp;lt;/code&amp;gt;“&lt;br /&gt;
existiert. Zu guter Letzt legt sie in der Variablen „&amp;lt;code&amp;gt;l_model_loop&amp;lt;/code&amp;gt;“ eine Model-Loop an, die die Position und Geschwindigkeit&lt;br /&gt;
der beweglichen Objekte regelmäßig aktualisiert. Als Kollisionsprozedur wird diesem Objekt die Funktion „&amp;lt;code&amp;gt;f_collision&amp;lt;/code&amp;gt;“&lt;br /&gt;
übergeben. Diese Funktion wird weiter unten im Funktionsrumpf von &amp;lt;code&amp;gt;minipong&amp;lt;/code&amp;gt; definiert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var l_stage          = p_models.stage,&lt;br /&gt;
    l_button         = p_models.button,&lt;br /&gt;
    l_info           = p_models.info,&lt;br /&gt;
    l_score          = p_models.score,&lt;br /&gt;
    l_ball           = p_models.ball,&lt;br /&gt;
    l_paddle         = p_models.paddle,&lt;br /&gt;
    l_models_movable = [],&lt;br /&gt;
    l_model_loop     = new ModelLoop(f_collision, p_init.fps, l_models_movable);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun ist es an der Zeit, das  Array  „&amp;lt;code&amp;gt;l_models_movable&amp;lt;/code&amp;gt;“ zu befüllen. Dazu wird einfach die Liste mit&lt;br /&gt;
allen Model-Objekt durchlaufen. Alle Objekte, die die Methode „&amp;lt;code&amp;gt;move&amp;lt;/code&amp;gt;“ enthalten werden in dieses Array eingefügt.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// Store all model objects that have a move method within the array l_models_movable.&lt;br /&gt;
for (var k in p_models)&lt;br /&gt;
{ if (p_models.hasOwnProperty(k) &amp;amp;&amp;amp; p_models[k].move != null)&lt;br /&gt;
  { l_models_movable.push(p_models[k]); }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den letzten beiden Anweisunges startet „&amp;lt;code&amp;gt;minipong&amp;lt;/code&amp;gt;“ die Anwendung. Sie ruft &lt;br /&gt;
dazu die Prozedur „&amp;lt;code&amp;gt;f_stop&amp;lt;/code&amp;gt;“ auf (da der Benutzer das Spiel erst mittel eine Klicks auf den Start-Knopf starten muss) und schreibt eine&lt;br /&gt;
Willkommensbotschaft in Info-Textfeld (und überschreibt damit die Meldung, die die Stopp-Funktion ins Info-Textfeld geschrieben hat.).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// Stop the game and display a welcome message.&lt;br /&gt;
f_stop();&lt;br /&gt;
l_info.value = p_init.welcome;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen noch vier Prozeduren definiert werden, die in verschiedenen Spielsituationen aufgerufen werden.&lt;br /&gt;
Alle vier Prozeduren werden &#039;&#039;&#039;im Rumpf&#039;&#039;&#039; der Prozedur „&amp;lt;code&amp;gt;minipong&amp;lt;/code&amp;gt;“ definiert. Das heißt, nur &lt;br /&gt;
&amp;lt;code&amp;gt;minipong&amp;lt;/code&amp;gt; kann auf diese Prozeduren zugreifen. Sie kann sie allerdings als Callback-Funktionen &lt;br /&gt;
an andere Prozeduren und Methoden weiterleiten. Und genau das macht sie auch.&lt;br /&gt;
&lt;br /&gt;
Ganz wichtig für die Spiellogik ist die Definition einer geeigneten Kollisionsprozedur, &lt;br /&gt;
die der Model-Loop übergeben wird, um die Kollisionserkennung und -behandlung damit durchzuführen.&lt;br /&gt;
Diese Prozedur verwendet die drei Hilfsprozeduren „&amp;lt;code&amp;gt;collisionBallPaddle&amp;lt;/code&amp;gt;“, „&amp;lt;code&amp;gt;collisionCanvasBall&amp;lt;/code&amp;gt;“ und „&amp;lt;code&amp;gt; collisionStagePaddle&amp;lt;/code&amp;gt;“,&lt;br /&gt;
die einfach nacheinander aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
Zwei dieser Prozeduren erwarten als Input eine Callback-Funktion, die im Falle von bestimmten Kollisiones von der Kollisionsbehandlung aufgerufen werden.&lt;br /&gt;
Die Prozedur „&amp;lt;code&amp;gt;collisionBallPaddle&amp;lt;/code&amp;gt;“ ruft die Callback-Funktion auf, sobald der Ball mit dem Schläger kollidiert.&lt;br /&gt;
In diesem Fall soll der Punktestand erhöht werden. Das wird mit einer sehr einfachen anonymen Prozedur erledigt:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function(){ l_score.value++; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Diese Prozedur erhöht bei jedem Aufruf im Text-Feld „&amp;lt;code&amp;gt;score&amp;lt;/code&amp;gt;“ den Wert „&amp;lt;code&amp;gt;value&amp;lt;/code&amp;gt;“ um eins.&lt;br /&gt;
Sobald ein neues Spiel gestartet wird, wird dieser Vert auf &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Die  Prozedur „&amp;lt;code&amp;gt;collisionCanvasBall&amp;lt;/code&amp;gt;“ informiert &amp;lt;code&amp;gt;minipong&amp;lt;/code&amp;gt; mittels Callback, wenn der Ball die Bühne verlässt.&lt;br /&gt;
Als Callback-Funktion wird dieser Prozedur die Prozedur „&amp;lt;code&amp;gt;f_stop&amp;lt;/code&amp;gt;“ (siehe unten) übergeben, um das Spiel zu beenden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// Collision detection and handling.&lt;br /&gt;
function f_collision()&lt;br /&gt;
{&lt;br /&gt;
  collisionBallPaddle(l_ball, l_paddle, function(){ l_score.value++; });&lt;br /&gt;
  collisionCanvasBall(l_stage, l_ball, f_stop);&lt;br /&gt;
  collisionStagePaddle(l_stage, l_paddle);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ganz zu Beginn des Spiels, bei einem Abbruch durch den Spieler mittel Button-Klick und sobald der&lt;br /&gt;
Ball die Bühne verlässt wird das Spiel beendet. Dies ist die Aufgabe der Prozedur  „&amp;lt;code&amp;gt;f_stop&amp;lt;/code&amp;gt;“.&lt;br /&gt;
&lt;br /&gt;
Sie hält die Model-Loop und Ball an und macht Ball und Schläger unsichtbar.&lt;br /&gt;
Dann ändert sie das Aussehen und das Verhalten des Start-Stopp-Buttons:&lt;br /&gt;
Sie weißt ihm das Label „&amp;lt;code&amp;gt;p_init.startGame&amp;lt;/code&amp;gt;“ (&amp;lt;code&amp;gt;=== &amp;quot;Spiel starten&amp;quot;&amp;lt;/code&amp;gt; gemäß &amp;lt;code&amp;gt;init.json&amp;lt;/code&amp;gt;)&lt;br /&gt;
und die Prozedur „&amp;lt;code&amp;gt;p_start&amp;lt;/code&amp;gt;“ zu. Das heißt, bei einem Klick auf diesen Button wird die Prozedur „&amp;lt;code&amp;gt;p_start&amp;lt;/code&amp;gt;“&lt;br /&gt;
ausgeführt. Zu guter Letzt schreibt sie ins Info-Textfeld eine Nachricht, dass das Spiel beendet ist. Diese Nachricht&lt;br /&gt;
kann man überschreiben, indem man direkt im Anschluss an einen Aufruf von „&amp;lt;code&amp;gt;p_stop&amp;lt;/code&amp;gt;“ eine&lt;br /&gt;
andere Nachricht ins Info-Textfeld schreibt. Dies geschieht direkt nachdem die Web-Anwendung gestartet wurde (siehe oben),&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// Stop the game.&lt;br /&gt;
function f_stop()&lt;br /&gt;
{&lt;br /&gt;
  l_model_loop.stop();&lt;br /&gt;
  l_ball.stop();&lt;br /&gt;
  l_ball.hide();&lt;br /&gt;
  l_paddle.hide();&lt;br /&gt;
&lt;br /&gt;
  l_button.label   = p_init.startGame;&lt;br /&gt;
  l_button.onClick = f_start;&lt;br /&gt;
&lt;br /&gt;
  l_info.value     = p_init.ballLost;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Spiel angehalten wurde, kann man es mit einem Klick auf den Start-Stopp-Button starten.&lt;br /&gt;
Die zugehörige Start-Prozedur muss das Spiel zunächst zurücksetzen. Sie setzt den Score-Wert auf &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;,&lt;br /&gt;
löscht den Inhalt des Info-Textfeldes und setzt Schläger und Ball zurück an die Startpositionen.&lt;br /&gt;
Dann ändert sie das Aussehen und das Verhalten des Start-Stopp-Buttons:&lt;br /&gt;
Sie weißt ihm das Label „&amp;lt;code&amp;gt;p_init.stopGame&amp;lt;/code&amp;gt;“ (&amp;lt;code&amp;gt;=== &amp;quot;Spiel beenden&amp;quot;&amp;lt;/code&amp;gt; gemäß &amp;lt;code&amp;gt;init.json&amp;lt;/code&amp;gt;)&lt;br /&gt;
und die Prozedur „&amp;lt;code&amp;gt;p_stop&amp;lt;/code&amp;gt;“ zu. Das heißt, bei einem Klick auf diesen Button wird die Prozedur „&amp;lt;code&amp;gt;p_stop&amp;lt;/code&amp;gt;“&lt;br /&gt;
ausgeführt und das Spiel sofort beendet.&lt;br /&gt;
&lt;br /&gt;
Nun kann sie das eigentliche Spiel starten. Dazu macht sie Schläger und Ball sichtbar und startet dann den Ball und die Model-Loop.&lt;br /&gt;
Jetzt sind die Call-Backfunktionen der Kollisionsprozedur scharfgeschaltet. Das heißt, Kollisionen vom Schläger mit dem Ball&lt;br /&gt;
werden mit einem Puktgewinn belohnt (dieser wird durch die View-Loop auch sofort angezeigt). &lt;br /&gt;
Wenn der Ball die Bühne verlässt oder wenn der Benutzer den Start-Stopp-Button drückt, wird das Spiel beendet. &lt;br /&gt;
Der erspielte Score ist zu diesem Zeitpunkt noch sichtbar. Erst mit einem neuen Spielstart wird er wieder auf &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// Start the game.&lt;br /&gt;
function f_start()&lt;br /&gt;
{&lt;br /&gt;
  l_score.value = 0;&lt;br /&gt;
  l_info.value  = &#039;&#039;;&lt;br /&gt;
  l_ball.reset();&lt;br /&gt;
  l_paddle.reset();&lt;br /&gt;
&lt;br /&gt;
  l_button.label   = p_init.stopGame;&lt;br /&gt;
  l_button.onClick = f_stop;&lt;br /&gt;
&lt;br /&gt;
  l_paddle.show();&lt;br /&gt;
  l_ball.show();&lt;br /&gt;
  l_ball.start();&lt;br /&gt;
  l_model_loop.start();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wurden alle Module vollständig erstellt und MiniPong sollte gespielt werden können.&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
# {{Quelle|Braun, H. (2011): Webanimationen mit Canvas}}&lt;br /&gt;
# {{Quelle|Kowarschick, W.: Multimedia-Programmierung}}&lt;br /&gt;
[[Kategorie: HTML5-Tutorium: Canvas: MiniPong]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&lt;/div&gt;</summary>
		<author><name>Sautter</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_04&amp;diff=41181</id>
		<title>HTML5-Tutorium: JavaScript: Hello World 04</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_04&amp;diff=41181"/>
		<updated>2016-10-13T11:43:41Z</updated>

		<summary type="html">&lt;p&gt;Sautter: /* Automatische Generierung der Schnittstellenbeschreibung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;: [https://glossar.hs-augsburg.de/beispiel/tutorium/es5/hello_world/WK_HelloWorld04/index.html &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt;]&lt;br /&gt;
([https://glossar.hs-augsburg.de/beispiel/tutorium/es5/hello_world/WK_HelloWorld04/ SVN-Repository])&lt;br /&gt;
==Anwendungsfälle (Use Cases)==&lt;br /&gt;
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_03|dritten Teil des Tutoriums]]&lt;br /&gt;
ändern sich die die Anwendungsfälle nicht. Die Anwendung leistet also genau dasselbe wie zuvor.&lt;br /&gt;
&lt;br /&gt;
In diesem Teil des Tutoriums geht es darum, die Anwendung zu verbessern, so dass sie schneller ausgeliefert werden kann.&lt;br /&gt;
Außerdem wird die Entwicklungsarbeit mit Hilfe von Automatisierungstools erleichtert.&lt;br /&gt;
&lt;br /&gt;
==Erstellen eines neuen Projektes==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Projekt  „&amp;lt;code&amp;gt;HelloWorld04&amp;lt;/code&amp;gt;“ und legen Sie dieses in Ihrem Repository ab.&lt;br /&gt;
Kopieren Sie anschließend alle Dateien aus dem dritten Teil des Tutoriums, passen Sie den Titel in der HTML-Datei an und committen Sie abermals.&lt;br /&gt;
&lt;br /&gt;
==Erstellen einer Ordnerstruktur==&lt;br /&gt;
&lt;br /&gt;
Grundsätzlich gilt auch bei der Programmierung: Ordnung ist das halbe Leben.&lt;br /&gt;
&lt;br /&gt;
Web-Anwendungen werden sehr schnell sehr groß. Also sollte man sich eine geeignete Ordnerstruktur überlegen.&lt;br /&gt;
Üblicherweise legt man CSS-Dateien in einen Ordner namens  „&amp;lt;code&amp;gt;css&amp;lt;/code&amp;gt;“ und JavaScript-Dateien in einen Ordner namens&lt;br /&gt;
„&amp;lt;code&amp;gt;js&amp;lt;/code&amp;gt;“ oder  „&amp;lt;code&amp;gt;lib&amp;lt;/code&amp;gt;“. Sollten es viele CSS- und/oder JavaScript-Dateien werden, &lt;br /&gt;
legt man im entsprechenden Ordner geeignete Unterordner an.&lt;br /&gt;
&lt;br /&gt;
Legen Sie im Root-Verzeichnis Ihres Projektes folgende Ordner an:&lt;br /&gt;
* &amp;lt;code&amp;gt;web&amp;lt;/code&amp;gt;: Der Inhalt dieses Ordners kommt später auf einen echten Web-Server.&lt;br /&gt;
* &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;: Dieser Ordner enthält die unkomprimierten CSS- und JavaScript-Dateien.&lt;br /&gt;
* &amp;lt;code&amp;gt;doc&amp;lt;/code&amp;gt;: Dieser Ordner enthält die Projektdokumentation.&lt;br /&gt;
* &amp;lt;code&amp;gt;conf&amp;lt;/code&amp;gt;: Dieser Ordner enthält Projektkonfigurationsdateien mit Ausnahme zweier zentraler Node.js-Dateien.&lt;br /&gt;
Legen Sie innerhalb der Ordner „&amp;lt;code&amp;gt;web&amp;lt;/code&amp;gt;“ und „&amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;“ jeweils zwei weitere Ordner an:  „&amp;lt;code&amp;gt;css&amp;lt;/code&amp;gt;“ sowie „&amp;lt;code&amp;gt;js&amp;lt;/code&amp;gt;“ .&lt;br /&gt;
&lt;br /&gt;
Öffnen Sie im Editorbereich die Datei  „&amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt;“ und verschieben sie in den Ordner „&amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;“.&lt;br /&gt;
Verschieben sie anschließend die Dateien „&amp;lt;code&amp;gt;main.css&amp;lt;/code&amp;gt;“ und „&amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt;“&lt;br /&gt;
in die passenden Ordner „&amp;lt;code&amp;gt;src/css&amp;lt;/code&amp;gt;“ bzw. „&amp;lt;code&amp;gt;src/js&amp;lt;/code&amp;gt;“. Sie werden feststellen, dass die Verweise in den Link- und Script-Elementen der&lt;br /&gt;
Datei „&amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt;“ automatisch angepasst werden. Verschieben Sie nun die Datei „&amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt;“ in den Ordner „&amp;lt;code&amp;gt;web&amp;lt;/code&amp;gt;“.&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis dieser Aktion ist, dass Ihre Web-Anwendung nicht mehr im Root-Verzeichnis des Projektes liegt, sondern (sauber strukturiert) in zwei Unterordnern.&lt;br /&gt;
Daher ist es möglich, im Root-Verzeichnis des Projektes weitere projektspezifische Ordner und Dateien anzulegen wie „&amp;lt;code&amp;gt;conf&amp;lt;/code&amp;gt;“ und „&amp;lt;code&amp;gt;doc&amp;lt;/code&amp;gt;“,&lt;br /&gt;
die später nicht auf den Web-Server, auf dem die Anwendung schließlich laufen soll, kopiert werden. Auch der Ordner „&amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;“ existiert nur&lt;br /&gt;
in der Entwicklungsumgebung, lediglich der Inhalt des Web-Ordners ist für den Liveserver bestimmt.&lt;br /&gt;
&lt;br /&gt;
==Laden der CSS- und der JavaScript-Dateien==&lt;br /&gt;
&lt;br /&gt;
Eine Web-Anwendung funktioniert nur – wie Sie bereits erfahren haben –, wenn nicht nur die HTML-Datei, sondern auch die zugehörigen&lt;br /&gt;
CSS- und JavaScript-Dateien geladen werden. Allerdings gibt es dabei ein Problem: Die CSS- und JavaScript-Dateien werden im Laufe der&lt;br /&gt;
Zeit immer zahlreicher und/oder größer. Diese Dateien zu laden, dauert seine Zeit. &lt;br /&gt;
&lt;br /&gt;
In der aktuellen Version der Web-Anwendung stehen diese Verweise auf diese Dateien im &amp;lt;code&amp;gt;head&amp;lt;/code&amp;gt;-Bereich des Dokuments.&lt;br /&gt;
Dieser wird vollständig geladen, bevor der &amp;lt;code&amp;gt;body&amp;lt;/code&amp;gt;-Bereich eingelesen wird. Das heißt aber, dass der Browser keine Inhalte des HTML-Dokuments&lt;br /&gt;
darstellen kann, solange er die JavaScript- und CSS-Dateien lädt. Wenn dies zu lange dauert, verliert der Besucher die Geduld und verlässt die Seite &lt;br /&gt;
vorzeitig.&lt;br /&gt;
&lt;br /&gt;
Besser wäre es daher andersherum vorzugehen: Es wird zuerst der &amp;lt;code&amp;gt;body&amp;lt;/code&amp;gt;-Bereich geladen und dann die JavaScript- und die CSS-Dateien.&lt;br /&gt;
Im Falle von JavaScript ist das durchaus sinnvoll, aber im Falle von CSS hat das den Effekt, dass der Browser keine Layout-Vorgaben erhalten hat,&lt;br /&gt;
wenn er mit dem [[Rendern]] der Seite beginnt. Also verwendet er die browserspezifischen Defaultwerte. Das heißt, die Seite sieht zunächst&lt;br /&gt;
ganz anders aus, als vom Designer geplant. Wenn dann die CSS-Dateien geladen wurden, wird die Seite erneut gerendert und verändert ihr Aussehen.&lt;br /&gt;
Auch das ist verwirrend und wirkt unprofessionell.&lt;br /&gt;
&lt;br /&gt;
Was also machen?&lt;br /&gt;
&lt;br /&gt;
===CSS-Dateien===&lt;br /&gt;
Für CSS-Dateien empfiehlt Google ernsthaft, die Link-Element ganz ans Ende der HTML-Datei  zu stellen, also &#039;&#039;&#039;nach&#039;&#039;&#039; dem &lt;br /&gt;
schließenden &amp;lt;code&amp;gt;html&amp;lt;/code&amp;gt;-Tag.&lt;br /&gt;
Damit das Problem mit dem falschen Layout nicht auftritt, sollen wichtige CSS-Befehle direkt – {{dh}} als CSS-Befehle innerhalb eines&lt;br /&gt;
&amp;lt;code&amp;gt;style&amp;lt;/code&amp;gt;-Elements – in die HTML-Datei eingefügt &lt;br /&gt;
werden.&amp;lt;ref&amp;gt;{{Quelle|Google (Web)}}, Kapitel „CSS-Bereitstellung optimieren“, https://developers.google.com/speed/docs/insights/OptimizeCSSDelivery&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Von beiden Vorschlägen rate ich dringend ab. Der erste Vorschlag ist nicht [[HTML]]-konform, ja noch nicht einmal [[SGML]]-konform. Es mag sein,&lt;br /&gt;
dass diverse Browser diese Syntax verstehen, korrekt ist sie trotzdem nicht. Der zweite Vorschlag hat zur Folge, dass Struktur (HTML) und Layout (CSS)&lt;br /&gt;
vermischt werden. Dies war einer der Kardinalfehler von frühen HTML-Versionen aus der Zeit, bevor es das CSS-Format gab. &#039;&#039;&#039;Tun Sie das nicht.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Gehen Sie so vor, wie es unter [[Multimedia-Programmierung: Best Practices#CSS|Multimedia-Programmierung: Best Practices]] beschrieben ist.&lt;br /&gt;
&lt;br /&gt;
In unserer Anwendung haben wir bislang alles richtig gemacht, bis auf die fehlende automatische Komprimierung.&lt;br /&gt;
Diese wird weiter unten beschrieben.&lt;br /&gt;
&lt;br /&gt;
===JavaScript-Dateien===&lt;br /&gt;
&lt;br /&gt;
Für JavaScript-Dateien empfiehlt Google ebenfalls, diese nicht schon zu Beginn zu laden.&amp;lt;ref&amp;gt;{{Quelle|Google (Web)}}, Kapitel „CSS-Bereitstellung optimieren“, https://developers.google.com/speed/docs/insights/BlockingJS&amp;lt;/ref&amp;gt; Dieser Vorschlag ist sehr sinnvoll.&lt;br /&gt;
&lt;br /&gt;
Entfernen Sie das &amp;lt;code&amp;gt;script&amp;lt;/code&amp;gt;-Element aus dem HTML-Header-Bereich und fügen Sie es vor dem schließenden &amp;lt;code&amp;gt;body&amp;lt;/code&amp;gt;-Tag  ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
   ...&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;../src/js/main.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Node.js===&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorium befasst sich zwar nicht mit der Erstellung von Node.js-Anwendungen, aber trotzdem benötigen wir&lt;br /&gt;
Node, da für die Komprimierung von Dateien und das automatische Erstellen von JavaScript-Dokumentationen&lt;br /&gt;
derartige Anwendungen zum Einsatz kommen.&lt;br /&gt;
&lt;br /&gt;
Installieren Sie zunächst Node.js: &amp;lt;code&amp;gt;https://nodejs.org/en/&amp;lt;/code&amp;gt;, Version 6.x. Diverse Versionen finden Sie unter https://nodejs.org/en/download/.&lt;br /&gt;
Achten Sie darauf, dass die Option „&amp;lt;code&amp;gt;Add to PATH&amp;lt;/code&amp;gt;“ ausgewählt wurde.&lt;br /&gt;
&lt;br /&gt;
Starten Sie WebStorm neu und öffnen Sie das Terminal, um zu testen, ob &amp;lt;code&amp;gt;node&amp;lt;/code&amp;gt; funktioniert:&lt;br /&gt;
&lt;br /&gt;
* Klick auf Icon in der linken unteren Ecke → &amp;lt;code&amp;gt;Terminal&amp;lt;/code&amp;gt;&lt;br /&gt;
* Geben Sie Folgendes ins Terminal ein: &amp;lt;code&amp;gt;node --version&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn Node.js korrekt installiert und der Pfad mit den &amp;lt;code&amp;gt;node&amp;lt;/code&amp;gt;-Binaries in die Systemvariable &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; eingetragen wurde,&lt;br /&gt;
sollte die Versionsnummer von &amp;lt;code&amp;gt;node&amp;lt;/code&amp;gt; ausgegeben werden.&lt;br /&gt;
&lt;br /&gt;
Sie werden feststellen, das Node.js zum Großteil über Konsolbefehle gesteuert wird. &lt;br /&gt;
Zu Beginn mag das für eingefleischte [[Klickibunti]]-Fans ungewohnt sein, aber Sie werden sich schnell daran gewöhnen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Tipp&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
Profis verwenden unter Windows lieber ein Terminal, dass eine [[Unix]]-[[Shell]] simuliert.&lt;br /&gt;
(Unter Linux und Mac OS X ist das nicht notwendig. Dort läuft sowieso schon eine UNIX-Shell.)&lt;br /&gt;
&lt;br /&gt;
Installieren Sie  „Git for Windows“ (https://git-for-windows.github.io/). In diesem Paket ist die „Git BASH“ enthalten.&lt;br /&gt;
Das ist eine Terminal-Anwendung, die eine Linux-Bash emuliert und zahlreiche nützliche Befehle wie „&amp;lt;code&amp;gt;which&amp;lt;/code&amp;gt;“, „&amp;lt;code&amp;gt;[[vi]]&amp;lt;/code&amp;gt;“ ( :-) ) etc. zur&lt;br /&gt;
Verfügung stellt.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Wurzel-Verzeichnis Ihres Projektes einen Ordner namens „&amp;lt;code&amp;gt;node-modules&amp;lt;/code&amp;gt;“.&lt;br /&gt;
Wenn die Frage „&#039;&#039;Do you want to schedule the following directory for addition tu Subversion?&#039;&#039;“&lt;br /&gt;
aufpoppt, antworten Sie unbedingt mit &#039;&#039;&#039;„&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;“&#039;&#039;&#039;, anderenfalls werden Hunderte von Node.js-Dateien ins Repository geschrieben,&lt;br /&gt;
obwohl das überflüssig ist und &#039;&#039;&#039;sehr viel&#039;&#039;&#039; Zeit in Anspruch nimmt.&lt;br /&gt;
Anschließend führen Sie folgende Befehle aus:&lt;br /&gt;
&lt;br /&gt;
* Rechtsklick auf den neu erstellten Ordner → &amp;lt;code&amp;gt;Subversion&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Ignore&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;node-modules&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Öffnen Sie nun ein beliebiges Terminal (WebStorm-Terminal, Git Bash, Mac-OS-X-Terminal ...)&lt;br /&gt;
und geben Sie folgenden Befehl ein (npm = node package manager, https://www.npmjs.com/)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm install -g bower grunt-cli jsdoc ink-docstrap yuglify mincss coffee-script jshint&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden die wichtigsten Node.js-Pakete &#039;&#039;global&#039;&#039;  (Option „&amp;lt;code&amp;gt;-g&amp;lt;/code&amp;gt;“) installiert, die wir im Folgenden noch benötigen, evtl. implizit. &lt;br /&gt;
Die globale Installation hat zur Folge, dass jedes Projekt auf diese Pakete zugreifen kann, Pakete also nicht mehrfach installiert werden.&lt;br /&gt;
&lt;br /&gt;
* Rechtsklick im Dateibaum auf  „&amp;lt;code&amp;gt;node-modules&amp;lt;/code&amp;gt;“ → &amp;lt;code&amp;gt;Mark Directory as&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;excluded&amp;lt;/code&amp;gt;&lt;br /&gt;
* Rechtsklick im Dateibaum auf  „&amp;lt;code&amp;gt;doc&amp;lt;/code&amp;gt;“ → &amp;lt;code&amp;gt;Mark Directory as&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;excluded&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beide Ordner enthalten Dateien, die nicht von Ihnen stammen. Diese Dateien können Fehler und Hinweise enthalten, &lt;br /&gt;
veraltete (“deprecated”) Schnittstellen verwenden etc. (Oh ja, auch andere Programmierer machen Fehler. :-) )&lt;br /&gt;
Und wenn Sie diese beiden Ordner nicht als „&amp;lt;code&amp;gt;excluded markieren&amp;lt;/code&amp;gt;“, prüft WebStorm bei jedem Commit die&lt;br /&gt;
darin enthaltenen Dateien und weist Sie auf jedes Problem hin. Das können so viele Probleme sein, dass Sie die&lt;br /&gt;
Probleme in Ihren eigenen Dateien vollkommen übersehen.&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir den Node-Paket-Manager „&amp;lt;code&amp;gt;npm&amp;lt;/code&amp;gt;“ (https://docs.npmjs.com/) sowie Grunt (http://gruntjs.com/) initialisieren.&lt;br /&gt;
Grunt ist &#039;&#039;&#039;der&#039;&#039;&#039; “JavaScript Task Runner”, also ein Programm, das Aufgaben (&#039;&#039;tasks&#039;&#039;) wie {{zB}} das Komprimieren von Programmdateien&lt;br /&gt;
durchführt. &lt;br /&gt;
&lt;br /&gt;
Die Initialisierung erfolgt durch die Bereitstellung von zwei Dateien „&amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt;“&lt;br /&gt;
und „&amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt;“ im Root-Verzeichnis der Web-Anwendung. Eine initiale Version der &amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt; könnten Sie theoretisch mit&lt;br /&gt;
&amp;lt;code&amp;gt;npm init&amp;lt;/code&amp;gt; erstellen. Einfacher ist es jedoch, wenn Sie die beiden Dateien von Hand erstellen und folgenden Code einfügen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt;&#039;&#039;&#039; (https://docs.npmjs.com/files/package.json)&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;name&amp;quot;: &amp;quot;hello_world_04&amp;quot;,&lt;br /&gt;
  &amp;quot;version&amp;quot;: &amp;quot;1.0.0&amp;quot;,&lt;br /&gt;
  &amp;quot;description&amp;quot;: &amp;quot;Hello World Tutorium&amp;quot;,&lt;br /&gt;
  &amp;quot;main&amp;quot;: &amp;quot;gruntfile.js&amp;quot;,&lt;br /&gt;
  &amp;quot;directories&amp;quot;: {&lt;br /&gt;
    &amp;quot;doc&amp;quot;: &amp;quot;./doc&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;dependencies&amp;quot;: {},&lt;br /&gt;
  &amp;quot;devDependencies&amp;quot;: {&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;scripts&amp;quot;: {&lt;br /&gt;
    &amp;quot;test&amp;quot;: &amp;quot;echo \&amp;quot;Error: no test specified\&amp;quot; &amp;amp;&amp;amp; exit 1&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;repository&amp;quot;: {&lt;br /&gt;
    &amp;quot;type&amp;quot;: &amp;quot;svn&amp;quot;,&lt;br /&gt;
    &amp;quot;url&amp;quot;: &amp;quot;https://glossar.hs-augsburg.de/kowa/tutorium/es5/hello_world/WK_HelloWorld04&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;author&amp;quot;: &amp;quot;Wolfgang Kowarschick&amp;quot;,&lt;br /&gt;
  &amp;quot;license&amp;quot;: &amp;quot;CC-BY-NC-SA-4.0&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anmerkung: Diese Datei wird durch &amp;lt;code&amp;gt;npm&amp;lt;/code&amp;gt; automatisch erstellt und geändert. Daher ist das Layout (welches mir nicht gefällt) vorgegeben.&lt;br /&gt;
&lt;br /&gt;
Hier sollten Sie natürlich nicht meinen Repositorypfad und meinen Namen einfügen, sondern jeweils Ihren. Sie können außerdem die&lt;br /&gt;
Lizenz anpassen (https://spdx.org/licenses/), unter die Sie Ihre Software stellen, wenn Sie das möchten.&lt;br /&gt;
&lt;br /&gt;
Die meisten Attribute sind selbsterklärend. Wichtig sind für uns vor allem zwei Attribute: „&amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;“ und&lt;br /&gt;
„&amp;lt;code&amp;gt;devDependencies&amp;lt;/code&amp;gt;“. Das erste enthält den Namen der Konfigurationsdatei von Grunt. Diese fehlt noch. Also: Legen Sie sie an&lt;br /&gt;
(und vergessen Sie nicht, Ihren Namen anstelle meines Namens einzufügen):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;gruntfile.js&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @author Wolfgang Kowarschick &amp;lt;kowa@hs-augsburg.de&amp;gt;&lt;br /&gt;
 * @see http://gruntjs.com/getting-started&lt;br /&gt;
 *&lt;br /&gt;
 * Created on 2016/09/28.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
module.exports = function(grunt)&lt;br /&gt;
{&lt;br /&gt;
  // Project configuration.&lt;br /&gt;
  grunt.initConfig&lt;br /&gt;
  ({&lt;br /&gt;
    pkg: grunt.file.readJSON(&#039;package.json&#039;),&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  // Load the plugin that provides the &amp;quot;uglify&amp;quot; task.&lt;br /&gt;
&lt;br /&gt;
  // Default task(s).&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Datei ist noch ziemlich leer, aber das wird sich ändern.&lt;br /&gt;
&lt;br /&gt;
Vorausschauend sollten Sie eine überflüssige WebStorm-Fehlerprüfung deaktivieren, da anderenfalls künftige Versionen&lt;br /&gt;
Versionen der Datei „&amp;lt;code&amp;gt;gruntfile.js&amp;lt;/code&amp;gt;“ jedes Mal eine Fehlermeldung zur Folge haben, wenn die&lt;br /&gt;
Datei ins SVN-Repository geschrieben werden soll:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;WebStorm&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Settings&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;Preferences&amp;lt;/code&amp;gt;  → &amp;lt;code&amp;gt;Editor&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Inspections&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;JavaScript&amp;lt;/code&amp;gt; aufklappen → &amp;lt;code&amp;gt;General&amp;lt;/code&amp;gt; aufklappen → &amp;lt;code&amp;gt;Unsued JavaScript / ActionScript global symbol&amp;lt;/code&amp;gt; deselektieren&lt;br /&gt;
* &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Spannender ist in der Datei „&amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt;“ das Attribut „&amp;lt;code&amp;gt;devDependencies&amp;lt;/code&amp;gt;“ (dev = developer).&lt;br /&gt;
Diese enthält eine Liste von von NodeJS-Paketen, die für die Entwicklung der Web-Anwendung benötigt werden. &lt;br /&gt;
Fügen wir einmal das Paket „&amp;lt;code&amp;gt;ink-docstrap&amp;lt;/code&amp;gt;“ in diese Liste ein. &lt;br /&gt;
(Diese Pakte wird nachfolgend für die Erstellung der JavaScript-Dokumentation mittels JSDoc verwendet. Es enthält ein Theme, welches deutlich schöner ist als das Standard-JSDoc-Theme.)&lt;br /&gt;
&lt;br /&gt;
Führen Sie folgenden Befehl im WebStorm-Terminal (oder mittels eine beliebigen anderen Shell im Wurzelverzeichnis Ihrer Web-Anwendung) aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm install ink-docstrap --save-dev&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies bewirkt zweierlei:&lt;br /&gt;
&lt;br /&gt;
# Das Node.js-Paket „&amp;lt;code&amp;gt; ink-docstrap&amp;lt;/code&amp;gt;“ sowie diverse weitere Node.js-Pakete, die diese Paket benötigt, werden einem Projektordner namens „&amp;lt;code&amp;gt;node_modules&amp;lt;/code&amp;gt;“ abgelegt. &lt;br /&gt;
# Das Name und die Versionsnummer des Paketes werden in das Attribut „&amp;lt;code&amp;gt;devDependencies&amp;lt;/code&amp;gt;“ in der  Datei „&amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt;“ eingefügt (wegen der Option „&amp;lt;code&amp;gt;--save-dev&amp;lt;/code&amp;gt;“).&lt;br /&gt;
&lt;br /&gt;
Nun steht Ihnen der Befehl&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm update&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
zur Verfügung. Mit diesem können Sie die lokal gespeicherte Versionen der im Attribut „&amp;lt;code&amp;gt;devDependencies&amp;lt;/code&amp;gt;“ aufgeführte Node.js-Pakete&lt;br /&gt;
jederzeit auf die neueste Version aktualisieren. Sie können ein Paket auch löschen und dann mittels &amp;lt;code&amp;gt;npm update&amp;lt;/code&amp;gt; (https://docs.npmjs.com/cli/update) wieder herstellen.&lt;br /&gt;
Versuchen Sie es:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm remove ink-docstrap&lt;br /&gt;
npm list&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt ist der Ordner „&amp;lt;code&amp;gt;node_modules&amp;lt;/code&amp;gt;“  wieder leer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm update&lt;br /&gt;
npm list&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und jetzt enthält der wieder „&amp;lt;code&amp;gt;ink-docstrap&amp;lt;/code&amp;gt;“ sowie andere dafür notwendige Pakete.&lt;br /&gt;
&lt;br /&gt;
Sie können auch überprüfen, ob Sie in Ihrem Projekt irgendwelche veralteten Pakete benutzen (https://docs.npmjs.com/cli/outdated):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm outdated&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kein Ergebnis ist hier ein gutes Ergebnis!&lt;br /&gt;
&lt;br /&gt;
Und natürlich funktioniert das Ganze auch für global gespeicherte Pakete:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm -g outdated&lt;br /&gt;
npm -g list&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Allerdings muss man veraltete globale Pakete erneut installieren. Zum Beispiel muss &amp;lt;code&amp;gt;npm&amp;lt;/code&amp;gt;&lt;br /&gt;
relativ häufig aktualisiert werden (sobald  „&amp;lt;code&amp;gt;npm -g outdated&amp;lt;/code&amp;gt;“ das Paket als veraltet meldet):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm install -g npm&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Automatische Generierung der Schnittstellenbeschreibung==&lt;br /&gt;
&lt;br /&gt;
Installieren Sie zunächst JSDoc:&lt;br /&gt;
&lt;br /&gt;
* Öffnen Sie das WebStorm-Terminal.&lt;br /&gt;
* &amp;lt;code&amp;gt;npm install -g jsdoc&amp;lt;/code&amp;gt; (installiert JSDoc als Node.js-Anwendung; das haben Sie eigentlich schon gemacht, aber das macht nix :-) )&lt;br /&gt;
* &amp;lt;code&amp;gt;npm install ink-docstrap&amp;lt;/code&amp;gt; (installiert ein JSDoc-Theme namens „&amp;lt;code&amp;gt;docstrap&amp;lt;/code&amp;gt;“ ([https://docstrap.github.io/docstrap/]); das haben Sie auch schon gemacht, aber das macht auch nix.)&lt;br /&gt;
&lt;br /&gt;
Übrigens: Es gibt auch noch andere Themes für JSDoc.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Projektordner  „&amp;lt;code&amp;gt;conf&amp;lt;/code&amp;gt;“ eine JSON-Datei namens „&amp;lt;code&amp;gt;jsdoc.json&amp;lt;/code&amp;gt;“.&lt;br /&gt;
Fügen Sie in diese Datei folgenden Code ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;tags&amp;quot;:&lt;br /&gt;
  {&lt;br /&gt;
    &amp;quot;allowUnknownTags&amp;quot;: true&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;source&amp;quot;:&lt;br /&gt;
  {&lt;br /&gt;
    &amp;quot;include&amp;quot;:        [&amp;quot;./src/js&amp;quot;],&lt;br /&gt;
    &amp;quot;includePattern&amp;quot;: &amp;quot;.+\\.js$&amp;quot;,&lt;br /&gt;
    &amp;quot;excludePattern&amp;quot;: &amp;quot;doc&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;plugins&amp;quot;:&lt;br /&gt;
  [],&lt;br /&gt;
  &amp;quot;templates&amp;quot;:&lt;br /&gt;
  {&lt;br /&gt;
    &amp;quot;systemName&amp;quot;:      &amp;quot;WK_HelloWorld04&amp;quot;,&lt;br /&gt;
    &amp;quot;footer&amp;quot;:          &amp;quot;&amp;quot;,&lt;br /&gt;
    &amp;quot;copyright&amp;quot;:       &amp;quot;Copyright © 2016 by Wolfgang Kowarschick, License &amp;lt;a href=\&amp;quot;https://creativecommons.org/licenses/by-nc-sa/4.0/\&amp;quot;&amp;gt;CC-BY-NC-SA-4.0&amp;lt;/a&amp;gt;&amp;quot;,&amp;quot;,&lt;br /&gt;
    &amp;quot;navType&amp;quot;:         &amp;quot;vertical&amp;quot;,&lt;br /&gt;
    &amp;quot;theme&amp;quot;:           &amp;quot;cerulean&amp;quot;,&lt;br /&gt;
    &amp;quot;linenums&amp;quot;:        true,&lt;br /&gt;
    &amp;quot;cleverLinks&amp;quot;:     false,&lt;br /&gt;
    &amp;quot;monospaceLinks&amp;quot;:  true,&lt;br /&gt;
    &amp;quot;collapseSymbols&amp;quot;: true,&lt;br /&gt;
    &amp;quot;inverseNav&amp;quot;:      false&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;opts&amp;quot;:&lt;br /&gt;
  {&lt;br /&gt;
    &amp;quot;template&amp;quot;:    &amp;quot;node_modules/ink-docstrap/template&amp;quot;,&lt;br /&gt;
    &amp;quot;encoding&amp;quot;:    &amp;quot;utf8&amp;quot;,&lt;br /&gt;
    &amp;quot;destination&amp;quot;: &amp;quot;doc/jsdoc&amp;quot;,&lt;br /&gt;
    &amp;quot;recurse&amp;quot;:     true,&lt;br /&gt;
    &amp;quot;private&amp;quot;:     true&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Ersetzen Sie im Attribut „&amp;lt;code&amp;gt;footer&amp;lt;/code&amp;gt;“ meinen Namen durch Ihren, denn es ist Ihr Projekt, nicht meines. :-)&lt;br /&gt;
&lt;br /&gt;
In dieser Datei wird festgelegt, wie die HTML-Dokumentation des Codes, die JSDoc erstellt, aussehen soll.&lt;br /&gt;
Insbesondere ist das Template angegeben, das zur Erstellung dieser HTML-Dokumente verwendet werden soll.&lt;br /&gt;
Es handelt sich um „&amp;lt;code&amp;gt;docstrap&amp;lt;/code&amp;gt;“, welches wir zuvor heruntergeladen haben. Von der&lt;br /&gt;
Projektroot aus gesehen, befinden sich die Template-Dateien im Ordner&lt;br /&gt;
„&amp;lt;code&amp;gt;node_modules/ink-docstrap/template&amp;lt;/code&amp;gt;“.&lt;br /&gt;
&lt;br /&gt;
Weitere Konfigurationsparameter werden unter http://usejsdoc.org/about-configuring-jsdoc.html beschrieben.&lt;br /&gt;
&lt;br /&gt;
Sie können nun eine Dokumentation für Ihr Projekt erstellen:&lt;br /&gt;
&lt;br /&gt;
* Öffnen Sie das WebStorm-Terminal.&lt;br /&gt;
* &amp;lt;code&amp;gt;jsdoc -c conf/jsdoc.json&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach kurzer Zeit sollte der Ordner „&amp;lt;code&amp;gt;doc/jsdoc&amp;lt;/code&amp;gt;“ erstellt und mit Inhalten gefüllt worden sein.&lt;br /&gt;
Sehen Sie sich die Datei „&amp;lt;code&amp;gt;doc/jsdoc/index.html&amp;lt;/code&amp;gt;“ im Browser an. Diese enthält nicht viel,&lt;br /&gt;
außer einem Drop-Down-Menü „&amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;“, in dem die drei von Ihnen erstellen globalen Funktionen&lt;br /&gt;
„&amp;lt;code&amp;gt;greet&amp;lt;/code&amp;gt;“, „&amp;lt;code&amp;gt;greet_on_enter&amp;lt;/code&amp;gt;“ und „&amp;lt;code&amp;gt;init&amp;lt;/code&amp;gt;“ enthalten sind.&lt;br /&gt;
Klicken Sie auf eine dieser Funktionen.&lt;br /&gt;
&lt;br /&gt;
===Globale Speicherung der JSDoc-Templates===&lt;br /&gt;
&lt;br /&gt;
Wenn Sie  „&amp;lt;code&amp;gt;ink-docstrap&amp;lt;/code&amp;gt;“ nicht mittels &lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;npm install ink-docstrap&amp;lt;/source&amp;gt; &lt;br /&gt;
sondern global mittels &lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;npm install -g ink-docstrap&amp;lt;/source&amp;gt;&lt;br /&gt;
installieren (so wie Sie das auch schon bereits getan haben), werden die Template-Dateien nicht im Projektordner, sondern im globalen &amp;lt;code&amp;gt;npm&amp;lt;/code&amp;gt;-Ordner abgelegt.&lt;br /&gt;
Das hat den Vorteil, dass Sie dieses Template für viele Projekte einsetzen können und es nicht bei jedem Projekt separat speichern müssen.&lt;br /&gt;
Der Nachteil ist, dass in der Datei „&amp;lt;code&amp;gt;conf/jsdoc.json&amp;lt;/code&amp;gt;“ den absoluten Pfad des globalen Ordners angeben müssen.&lt;br /&gt;
Dieser Pfad sieht auf jedem Rechner anders aus. Bei mir lautet er beispielsweise:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;c:/Users/kowa/AppData/Roaming/npm/node_modules/ink-docstrap/template&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das heißt, wenn man im Team an einem Projekt arbeitet, müsste man für jedes Teammitglied eine eigene&lt;br /&gt;
JSDoc-Konfigurationsdatei anlegen, was nicht wirklich schön, aber doch machbar ist.&lt;br /&gt;
&lt;br /&gt;
In der [https://glossar.hs-augsburg.de/beispiel/tutorium/es5/hello_world/WK_HelloWorld04/ Musterlösung] verwende ich die lokale Version „&amp;lt;code&amp;gt;docstrap&amp;lt;/code&amp;gt;“&lt;br /&gt;
(es gibt allerdings auch eine Datei namens  „&amp;lt;code&amp;gt;conf/jsdoc_wk.json&amp;lt;/code&amp;gt;“, die speziell für meinen Rechner erstellt wurde).&lt;br /&gt;
Die lokale Version von „&amp;lt;code&amp;gt;docstrap&amp;lt;/code&amp;gt;“ ist allerdings nicht im Repository enthalten. Nachdem Sie die Musterlösung auf Ihren Rechner kopiert haben&lt;br /&gt;
(vergessen, wie&#039;s geht? → [[HTML5-Tutorium:_JavaScript:_Hello_World_01#Projekte_aus_einem_Subversion-Repository_laden|HTML5-Tutorium: JavaScript: Hello World 04]]),&lt;br /&gt;
müssen Sie im WebStorm-Terminal der Musterlösung noch folgenden Befehl ausführe:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm update&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und schon sollten Sie die Dokumentation wie gewohnt erstellen können.&lt;br /&gt;
&lt;br /&gt;
Sobald Sie „&amp;lt;code&amp;gt;grunt&amp;lt;/code&amp;gt;“ konfiguriert haben, sollte „&amp;lt;code&amp;gt;conf/jsdoc.json&amp;lt;/code&amp;gt;“  auch in Ihrem Projekt fehlende Node.js-PAkete nachinstallieren.&lt;br /&gt;
&lt;br /&gt;
===Erstellung der JSDoc-Dokumentation mittels Klickibunti===&lt;br /&gt;
&lt;br /&gt;
Falls Sie die Dokumentation nicht immer über das Terminal erstellen möchten, können Sie &lt;br /&gt;
in WebStorm einen neuen Menüeintrag erstellen, über den Sie die Erzeugung der HTML-Dokumentation starten können.&lt;br /&gt;
&lt;br /&gt;
Sie müssen dazu den Pfad ermitteln, unter dem Ihre &amp;lt;code&amp;gt;jsdoc&amp;lt;/code&amp;gt;-Datei zu finden ist.&lt;br /&gt;
Unter Unix (Mac OS X, Linux, FreeBSD ...) lautet der Befehl, den Sie in ein Terminal eingeben müssen:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
which jsdoc&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Unter &#039;&#039;&#039;Windows&#039;&#039;&#039; funktioniert dieser Befehl nur in der &#039;&#039;&#039;Git BASH&#039;&#039;&#039;. &lt;br /&gt;
Außerdem müssen Sie unter Windows den Bash-Pfad modifizieren, damit auch Windows etwas damit anfangen kann.&lt;br /&gt;
Bei mir gibt dieser Befehl Folgendes aus:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/c/Users/kowa/AppData/Roaming/npm/jsdoc&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Um &amp;lt;code&amp;gt;jsdoc&amp;lt;/code&amp;gt; in WebStorm aufrufen zu können, müssen Sie in diesem Pfad „&amp;lt;code&amp;gt;/c&amp;lt;/code&amp;gt;“ durch&lt;br /&gt;
„&amp;lt;code&amp;gt;c:&amp;lt;/code&amp;gt;“ und  „&amp;lt;code&amp;gt;jsdoc&amp;lt;/code&amp;gt;“ durch „&amp;lt;code&amp;gt;jsdoc.cmd&amp;lt;/code&amp;gt;“ ersetzen.&lt;br /&gt;
(Mac-User brauchen nichts zu ändern. :-) )&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie nun den neuen WebStorm-Menü-Eintrag:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Settings&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Tools&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;External Tools&amp;lt;/code&amp;gt;&lt;br /&gt;
* Klick auf das Plus-Icon&lt;br /&gt;
** &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;JSDoc&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;Group&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;Documentation&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;Description&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;Generates JavaScript documentation&amp;lt;/code&amp;gt;&lt;br /&gt;
** Hacken bei allen Optionen (&amp;lt;code&amp;gt;Options&amp;lt;/code&amp;gt;)&lt;br /&gt;
** Hacken bei allen Show-in-Optionen (&amp;lt;code&amp;gt;Show i&amp;lt;/code&amp;gt;) mit Ausnahme von „&amp;lt;code&amp;gt;Search results&amp;lt;/code&amp;gt;“&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;c:/Users/kowa/AppData/Roaming/npm/jsdoc.cmd&amp;lt;/code&amp;gt; (hier kommt Ihr zuvor ermittelter Pfad hinein)&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;-c $ProjectFileDir$/conf/jsdoc.json&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;$ProjectFileDir$&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun finden Sie an mehreren Stellen in WebStorm einen neuens Menüpunkt  &amp;lt;code&amp;gt;Documentation&amp;lt;/code&amp;gt; mit einem Unterpunkt &amp;lt;code&amp;gt;JSDoc&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* Hauptmenü : &amp;lt;code&amp;gt;Tools&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Documentation&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;JSDoc&amp;lt;/code&amp;gt;&lt;br /&gt;
* Rechtsklick auf Datei im Projekt-Dateibaum → &amp;lt;code&amp;gt;Documentation&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;JSDoc&amp;lt;/code&amp;gt;&lt;br /&gt;
* Rechtsklick auf Datei im Editor-Tab-Menü → &amp;lt;code&amp;gt;Documentation&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;JSDoc&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn Sie auf einen dieser Unterpunkte  „&amp;lt;code&amp;gt;JSDoc&amp;lt;/code&amp;gt;“ klicken, erhalten Sie entweder eine Fehlermeldung ( :-( ) oder es wird eine neue Dokumentation erstellt&lt;br /&gt;
({{dh}}, Sie haben alles richtig gemacht :-) ).&lt;br /&gt;
&lt;br /&gt;
Um sicher zu gehen, können Sie versuchsweise den Ordner „&amp;lt;code&amp;gt;doc&amp;lt;/code&amp;gt;“ gemeinsam mit seinem gesamten Inhalt löschen und &lt;br /&gt;
&amp;lt;code&amp;gt;JSDoc&amp;lt;/code&amp;gt; nochmals aufrufen.&lt;br /&gt;
&lt;br /&gt;
===Sicherung im Repository===&lt;br /&gt;
&lt;br /&gt;
Bevor Sie weitermachen: Haben Sie Ihre aktuelle Version von &amp;lt;code&amp;gt;Hello World 04&amp;lt;/code&amp;gt; schon im Repository gesichert?&lt;br /&gt;
&lt;br /&gt;
Bevor Sie die Projekt-Dateien per &#039;&#039;Commit&#039;&#039; ins Repository übertragen, sollten Sie überprüfen,&lt;br /&gt;
dass der Ordner „&amp;lt;code&amp;gt;node_modules&amp;lt;/code&amp;gt;“ auf der sogenannten „Ignore List“ steht (und damit nicht&lt;br /&gt;
ins Repository eingespielt wird):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Files&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;WebStorm&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Settings&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;Preferences&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Ignored Files&amp;lt;/code&amp;gt;&lt;br /&gt;
* Wenn in dieser Liste der Ordner „&amp;lt;code&amp;gt;node_modules&amp;lt;/code&amp;gt;“ nicht aufgeführt wird, fügen Sie ihn mittels des Plus-Icons ein.&lt;br /&gt;
&lt;br /&gt;
Sie sollten allerdings den Ordner „&amp;lt;code&amp;gt;doc&amp;lt;/code&amp;gt;“ zu Ihrem&lt;br /&gt;
Repository hinzufügen:&lt;br /&gt;
&lt;br /&gt;
* Klick auf „&amp;lt;code&amp;gt;doc&amp;lt;/code&amp;gt;“ im Dateibaum → &amp;lt;code&amp;gt;Subversion&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Add to VCS&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun können Sie Ihr Projekt committen.&lt;br /&gt;
&lt;br /&gt;
==Automatische Komprimierung von CSS- und Javascript-Dateien==&lt;br /&gt;
&lt;br /&gt;
Nun folgt die letzte Optimierung der Anwendung. Die von Ihnen erstellten CSS- und Javascript-Dateien&lt;br /&gt;
enthalten viele Leerzeichen, Kommentare, „sprechende“ anstelle von kurzen Variablenbezeichnern etc.&lt;br /&gt;
Das ist für Sie und alle anderen Entwickler von Vorteil. Für die Übertragung der Daten zu einem&lt;br /&gt;
Client (Browser) bedeutet das nur unnötiges Datenvolumen. Gerade für mobile Tarife mit Volumenbegrenzung&lt;br /&gt;
sollten so wenig Daten wie möglich übertragen werden.&lt;br /&gt;
&lt;br /&gt;
Glücklicherweise gibt es Tools, die bestehende CSS- oder Javascript-Dateien komprimieren (meist sogar so, dass&lt;br /&gt;
sich die [[Semantik]] der Dateien nicht ändert; aber wie gesagt: Auch andere Programmierer machen hin und wieder Fehler).&lt;br /&gt;
&lt;br /&gt;
Wir verwenden die Node.js-Tools „&amp;lt;code&amp;gt;cssmin&amp;lt;/code&amp;gt;“ für CSS-Dateien (https://www.npmjs.com/package/cssmin) und&lt;br /&gt;
„&amp;lt;code&amp;gt;UglifyJS2&amp;lt;/code&amp;gt;“ für JavaScript-Dateien (https://github.com/mishoo/UglifyJS2). Gesteuert werden diese  “minimizer”&lt;br /&gt;
oder  “minifier” mittels dem bereits installierten &#039;&#039;JavaScript Task Runner&#039;&#039; &amp;lt;code&amp;gt;grunt&amp;lt;/code&amp;gt;“.&lt;br /&gt;
&lt;br /&gt;
Die für das Minimieren mittels Grunt notwendige Pakete wurden ebenfalls bereits installiert: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm install grunt grunt-contrib-watch grunt-contrib-cssmin grunt-contrib-uglify --save-dev&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen die Aufgaben zu Komprimieren der JavaScript- und CSS-Dateien in die Konfigurationsdatei&lt;br /&gt;
eingefügt werden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;gruntfile.js&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @author Wolfgang Kowarschick &amp;lt;kowa@hs-augsburg.de&amp;gt;&lt;br /&gt;
 * @see http://gruntjs.com/getting-started&lt;br /&gt;
 *&lt;br /&gt;
 * Created on 2016/09/28.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
module.exports = function(grunt)&lt;br /&gt;
{&lt;br /&gt;
  // Project configuration.&lt;br /&gt;
  grunt.initConfig&lt;br /&gt;
  ({&lt;br /&gt;
    pkg: grunt.file.readJSON(&#039;package.json&#039;),&lt;br /&gt;
&lt;br /&gt;
    cssmin:&lt;br /&gt;
    { options: /* https://github.com/jakubpawlowicz/clean-css */&lt;br /&gt;
      { roundingPrecision:   -1&lt;br /&gt;
      },&lt;br /&gt;
      minimize: &lt;br /&gt;
      { files:&lt;br /&gt;
        { &#039;web/css/main.css&#039;:  [&#039;src/css/main.css&#039;],&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    &lt;br /&gt;
    uglify:&lt;br /&gt;
    { options:&lt;br /&gt;
      { mangle: true&lt;br /&gt;
      },&lt;br /&gt;
      minimize:&lt;br /&gt;
      { files:&lt;br /&gt;
        { &#039;web/js/main.js&#039;: [&#039;src/js/main.js&#039;]&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  // Load the plugin that provides the &amp;quot;uglify&amp;quot; task.&lt;br /&gt;
  grunt.loadNpmTasks(&#039;grunt-contrib-uglify&#039;);&lt;br /&gt;
  grunt.loadNpmTasks(&#039;grunt-contrib-cssmin&#039;);&lt;br /&gt;
&lt;br /&gt;
  // Default task(s).&lt;br /&gt;
  grunt.registerTask(&#039;default&#039;, [&#039;uglify&#039;, &#039;cssmin&#039;]);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion  „&amp;lt;code&amp;gt;grunt.initConfig&amp;lt;/code&amp;gt;“ aufgerufen und es wird ihr ein [[JavaScript-Objekt]] übergeben.&lt;br /&gt;
Im Attribut „&amp;lt;code&amp;gt;pkg&amp;lt;/code&amp;gt;“ wird grund der Inhalt der Datei  „&amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt;“ übergeben, damit Grunt ermitteln kann, welche Pakete verfügbar sind.&lt;br /&gt;
Die beiden anderen Attribute „&amp;lt;code&amp;gt;cssmin&amp;lt;/code&amp;gt;“ und „&amp;lt;code&amp;gt;uglify&amp;lt;/code&amp;gt;“ (deren Namen frei wählbar sind), werden die beiden Minifier konfiguriert.&lt;br /&gt;
&lt;br /&gt;
In beiden Fällen können Optionen angegeben werden, wie {{zB}}, dass CSS-Maß-Angaben nicht gerundet werden sollen (es gibt Designer, die bestehen&lt;br /&gt;
beim Pixelmaß auf vier Nachkommastellen) oder dass geeignete JavaScript-Variablen-Namen „durch die Mangel gedreht“ (&#039;&#039;mangle&#039;&#039;), also durch kürze Namen ersetzt werden sollen.&lt;br /&gt;
Wichtiger ist jedoch die Angabe, welche Dateien überhaupt erstellt werden und was für Sourcedaten dafür verwendet werden solle.&lt;br /&gt;
&lt;br /&gt;
Zu jeder Datei, die erstellt wird, kann man eine ganze Liste von Sourcedateien angeben. Das hat zur Folge, dass alle  diese Sourcedateien nacheinander&lt;br /&gt;
komprimiert und dann in die Zieldatei eingefügt werden. Auch das ist eine Optimierung, um das Laden einer Web-Anwendung zu beschleunigen.&lt;br /&gt;
Eine große Datei wird per HTTP schneller übertragen als viele kleine. Künftig werden wir daher möglichst wenige komprimierte Dateien verwenden&lt;br /&gt;
und möglichst viele nicht-komprimierte JavaScript-Dateien in komprimierter Form nacheinander in nur eine einzige JavaScript-Datei einfügen.&lt;br /&gt;
&lt;br /&gt;
Nach der Initialisierungsfunktion werden noch zwei weitere Funktionen aufgerufen. Mit &amp;lt;code&amp;gt;grunt.loadNpmTasks&amp;lt;/code&amp;gt; &lt;br /&gt;
werden die Grunt-Plugins geladen, die die eigentliche Komprimierung vornehmen. Und mittels &amp;lt;code&amp;gt;grunt.loadNpmTasks&amp;lt;/code&amp;gt; &lt;br /&gt;
wird festgelegt, welche Aufgaben standardmäßig ausgeführt werden, sobald der Befehl „&amp;lt;code&amp;gt;grunt&amp;lt;/code&amp;gt;“ ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
So, nun sollte alles funktionieren. Öffnen Sie das Web-Storm-Terminal und tippen Sie nacheinander folgende Befehle ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
grunt cssmin&lt;br /&gt;
grunt uglify&lt;br /&gt;
grunt&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sie werden feststellen, das jedes mal bestimmte Dateien in den Ordnern „&amp;lt;code&amp;gt;web/css&amp;lt;/code&amp;gt;“ oder/und „&amp;lt;code&amp;gt;web/js&amp;lt;/code&amp;gt;“&lt;br /&gt;
erstellt werden, die mit dem Namensbestandteil „&amp;lt;code&amp;gt;min&amp;lt;/code&amp;gt;“ beginnen. Sehen Sie sich die erzeugten Dateien an.&lt;br /&gt;
Sie enthalten denselben Code wie die Original-Dateien, nur in komprimierter (und daher für den Menschen schwer lesbarer) Form.&lt;br /&gt;
&lt;br /&gt;
Als nächstes muss die Datei „&amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt;“ angepasst werden. Diese lädt noch die unkomprimierten Dateien aus dem &lt;br /&gt;
Sourceverzeichnis. Künftig soll sie die komprimierten Dateien aus dem eigentlichen Web-Ordner laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;link rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; href=&amp;quot;css/main.css&amp;quot;/&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;js/main.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ab sofort werden nicht mehr die Sourcedateien geladen, sondern die komprimierten, durch die Minifier erstellten Dateien.&lt;br /&gt;
Das heißt aber für uns, dass wir bei jeder Änderung an den Sourcedateien den Befehl „&amp;lt;code&amp;gt;grunt&amp;lt;/code&amp;gt;“&lt;br /&gt;
ausführen müssen, um die Aktualisierungen in die komprimierten Versionen der JavaScript bzw. CSS-Dateien zu übertragen.&lt;br /&gt;
&lt;br /&gt;
Das ist lästig und geht besser.&lt;br /&gt;
&lt;br /&gt;
==Automatische Aktualisierung der komprimierten Dateien==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe des Node.js-Paketes „&amp;lt;code&amp;gt;grunt-contrib-watch&amp;lt;/code&amp;gt;“ kann man beliebige Dateibäume überwachen,&lt;br /&gt;
ob sich darin bestimmte Dateien ändern. In einem derartigen Fall kann man automatisch andere Grunt Tasks ausführen lassen.&lt;br /&gt;
Das lässt sich ausnutzen, um nach Änderungen einer CSS-Datei oder einer JavaScript-Datei die zugehörige komprimierte&lt;br /&gt;
Datei automatisch neu zu erstellen.&lt;br /&gt;
&lt;br /&gt;
Öffnen Sie das WebStorm-Terminal und laden Sie das besagte Paket:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm install grunt-contrib-watch --save-dev&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Erweitern Sie nun die Datei „&amp;lt;code&amp;gt;gruntfile.js&amp;lt;/code&amp;gt;“ um einen Watch-Task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
watch:&lt;br /&gt;
{ css:&lt;br /&gt;
  { files:   [&#039;src/css/**/*.css&#039;],&lt;br /&gt;
    tasks:   [&#039;cssmin&#039;]&lt;br /&gt;
  },&lt;br /&gt;
  js:&lt;br /&gt;
  { files:   [&#039;src/js/**/*.js&#039;],&lt;br /&gt;
    tasks:   [&#039;uglify&#039;]&lt;br /&gt;
  },&lt;br /&gt;
  web:&lt;br /&gt;
  { files:   [&#039;web/**/*&#039;],&lt;br /&gt;
    options: { livereload: true,&lt;br /&gt;
               spawn:      false&lt;br /&gt;
             }&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Vergessen Sie nicht, das Plugin zu laden:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
grunt.loadNpmTasks(&#039;grunt-contrib-watch&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Der Watch-Task überwacht drei verschiedene Ordner:&lt;br /&gt;
&lt;br /&gt;
* Wann immer sich eine CSS-Datei im Ordner „&amp;lt;code&amp;gt;src/css&amp;lt;/code&amp;gt;“ oder einem darin enthaltenen Unterordner ändert, wird der Task  „&amp;lt;code&amp;gt;cssmin&amp;lt;/code&amp;gt;“ ausgeführt.&lt;br /&gt;
* Wann immer sich eine JavaScript-Datei im Ordner „&amp;lt;code&amp;gt;src/js&amp;lt;/code&amp;gt;“ oder einem darin enthaltenen Unterordner ändert, wird der Task  „&amp;lt;code&amp;gt;uglify&amp;lt;/code&amp;gt;“ ausgeführt.&lt;br /&gt;
* Wann immer sich irgendeine Datei im Web-Ordner ändert, wird eine &amp;lt;code&amp;gt;livereload&amp;lt;/code&amp;gt;-Nachricht (http://livereload.com/) an einen lokalen Node-Server mit der Portnummer 35729 geschickt. (Keine Angst: Sie brauchen diesen Server nicht zu starten oder zu stoppen.) &lt;br /&gt;
&lt;br /&gt;
Damit kann man Browser anweisen, die HTML-Dateien neu zu laden, wann immer sich etwas ändert. &lt;br /&gt;
Dies erfolgt entweder mit speziellen Browser-Plugins (http://livereload.com/extensions/) oder mit einem kleinen JavaScript-Hack:&lt;br /&gt;
Fügen Sie ans Ende der Datei „&amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt;“ folgenden Code ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// Enable live reloading for the WebStorm web server.&lt;br /&gt;
if (location.port === &#039;63342&#039;)  // WebStorm server port&lt;br /&gt;
{&lt;br /&gt;
  var l_script = document.createElement(&#039;script&#039;);&lt;br /&gt;
  l_script.src = &#039;http://&#039;+(location.host||&#039;localhost&#039;).split(&#039;:&#039;)[0]+&#039;:35729/livereload.js&#039;;&lt;br /&gt;
  document.body.appendChild(l_script);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Dieser Code sorgt dafür, dass in eine HTML-Datei, die die Datei „&amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt;“ einbindet,&lt;br /&gt;
ein zusätzliches &amp;lt;code&amp;gt;script&amp;lt;/code&amp;gt;-Element&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;http://localhost:35729/livereload.js&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
erstellt und in jede HTML-Datei eingefügt wird, die von einem WebStorm-Test-Server ausgeliefert wird.&lt;br /&gt;
(Der URI kann anstelle des Servernamens „&amp;lt;code&amp;gt;localhost&amp;lt;/code&amp;gt;“ auf den echten Namen des Entwicklungsrechners enthalten, sofern &lt;br /&gt;
der Entwicklungsrechner einen derartigen Namen hat.)&lt;br /&gt;
&lt;br /&gt;
Unter Port 35729 wird auf dem Entwicklungsrechner von &amp;lt;code&amp;gt;grunt-contrib-watch&amp;lt;/code&amp;gt; der bereits erwähnte Livereload-Server gestartet und&lt;br /&gt;
die JavaScript-Datei „&amp;lt;code&amp;gt;livereload.js&amp;lt;/code&amp;gt;“ zur Verfügung gestellt. Wenn man diese Datei in seine HTML-Datei einbindet,&lt;br /&gt;
aktualisiert sich die HTML-Datei fortan automatisch, sobald &amp;lt;code&amp;gt;grunt-contrib-watch&amp;lt;/code&amp;gt; eine Änderung, wie {{zB}} &lt;br /&gt;
die Aktualisierung einer CSS-Datei, einer JavaScript-Datei, der HTML-Datei selbst oder einer anderen Datei, die von der HTML-Datei eingebunden wird,&lt;br /&gt;
meldet.&lt;br /&gt;
&lt;br /&gt;
Allerdings wäre es falsch, &amp;lt;code&amp;gt;livereload.js&amp;lt;/code&amp;gt; auf einem Live-Server ohne Testumgebung einzubinden. Dort läuft der Livereload-Server überhaupt nicht.&lt;br /&gt;
Daher wird zunächst ermittelt, über welchen Port die aktuelle HTML-Datei von Server ausgeliefert wurde.&lt;br /&gt;
Üblicherweise ist dies 80, 443, 8080, 8888 oder 8443. Andere Ports werden selten verwendet. &lt;br /&gt;
Der Port 63342 wird mit fast absoluter Sicherheit nicht von irgendeinem realen Server verwendet. Daher verwendet ihn WebStorm.&lt;br /&gt;
Und daher kann mit dem Test &amp;lt;code&amp;gt;if (location.port == 63342)&amp;lt;/code&amp;gt; ermittelt werden, ob die aktuelle HTML-Datei von einem WebStorm-Server&lt;br /&gt;
ausgeliefert wurde oder von einem anderen Server. (Die globale JavaScript-Variable „&amp;lt;code&amp;gt;location&amp;lt;/code&amp;gt;“ enthält den URI, über den die HTML-Datei&lt;br /&gt;
vom Browser geladen wurde.)&lt;br /&gt;
&lt;br /&gt;
Nun ist es an der Zeit, den &amp;lt;code&amp;gt;grunt-contrib-watch&amp;lt;/code&amp;gt; zu testen. Öffnen Sie „&amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt;“ im Browser&lt;br /&gt;
(Rechtsklick auf &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Run &#039;index.html&#039;&amp;lt;/code&amp;gt;) und tippen Sie dann &lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
grunt watch&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
ins WebStorm-Terminal.&lt;br /&gt;
Ändern Sie nun einen Eintrag in der Datei „&amp;lt;code&amp;gt;main.css&amp;lt;/code&amp;gt;“ ({{zB}} &amp;lt;code&amp;gt;font-family: &amp;quot;Comic Sans MS&amp;quot;;&amp;lt;/code&amp;gt; :-) ).&lt;br /&gt;
Wenn Sie diese Änderungen speichern (Strg-S, Apfel-S), sollten Sie zweierlei Reaktionen bemerken.&lt;br /&gt;
Zum einem wird ins Terminal die Meldung&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
&amp;gt;&amp;gt; File &amp;quot;web/css/main.css&amp;quot; changed.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
geschrieben und zum anderen ändert sich das Layout der HTML-Datei im Browser, da die aktualisierte CSS-Datei&lt;br /&gt;
automatisch vom Browser nachgeladen wurde.&lt;br /&gt;
&lt;br /&gt;
Wenn Sie die Designsünde  „Comic Sans MS“ rückgängig machen, ändert sich die Datei kurz darauf auch im Browser wieder.&lt;br /&gt;
&lt;br /&gt;
Das ist schon ziemlich „cool“, hat aber noch einen Schönheitsfehler: Das WebStorm-Terminal kann nicht anderweitig genutzt werden, solange &lt;br /&gt;
der Grunt-Watcher läuft. Um dieses Problem zu beheben, wird ein WebStorm-Startup-Task definiert, der den Watcher bei jedem&lt;br /&gt;
Programmstart von WebStorm automatisch in einem eigenständigen [[Task]] startet. Der Watcher läuft dann, solange WebStorm geöffnet ist. &lt;br /&gt;
Für seine Ausgaben steht im ein eigenes Ausgabefenster zur Verfügung. Was will man mehr? &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Settings&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Tools&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Startup Tasks&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
* Klick auf grünes Plus-Icon → &amp;lt;code&amp;gt;Add New Configuration&amp;lt;/code&amp;gt; → &amp;lt;code&amp;gt;Grunt.js&amp;lt;/code&amp;gt;&lt;br /&gt;
** Name: &amp;lt;code&amp;gt;Watch&amp;lt;/code&amp;gt;&lt;br /&gt;
** Gruntfile: &amp;lt;code&amp;gt;.../HelloWorld04/gruntfile.js&amp;lt;/code&amp;gt; (sollte bereits korrekt eingetragen sein)&lt;br /&gt;
** Tasks: &amp;lt;code&amp;gt;watch&amp;lt;/code&amp;gt;&lt;br /&gt;
** Der Node-Interpreter und das Node.js-Paket &amp;lt;code&amp;gt;grunt-cli&amp;lt;/code&amp;gt; sollten ebenfalls bereits korrekt eingetragen sein.&lt;br /&gt;
* &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; (Unter „&amp;lt;code&amp;gt;Run configuration&amp;lt;/code&amp;gt;“ sollte jetzt „&amp;lt;code&amp;gt;Watch&amp;lt;/code&amp;gt;“ stehen.)&lt;br /&gt;
* &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn Sie nun WebStorm neu starten, sollte und der Menüzeile am unteren Fensterrand ein Tabulator mit dem Namen „&amp;lt;code&amp;gt;Run&amp;lt;/code&amp;gt;“ vorhanden sein.&lt;br /&gt;
Wenn Sie das zugehörige Fenster öffnen sollte dort Folgendes stehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
grunt watch&lt;br /&gt;
Running &amp;quot;watch&amp;quot; task&lt;br /&gt;
Waiting...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald Sie irgendeine Änderung an eine &amp;lt;code&amp;gt;web&amp;lt;/code&amp;gt;- oder &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt;-Datei der Web-App vornehmen, werden alle im Watcher definierten Aktionen&lt;br /&gt;
(automatische Komprimierung bzw. Neuladen des HTML-Datei im Browser) automatisch ausgeführt. Der Watcher läuft solange, bis Sie WebStorm beenden.&lt;br /&gt;
Es erfolgt dabei sogar noch eine Sicherheitsabfrage, on Sie den Prozess „&amp;lt;code&amp;gt;Watch&amp;lt;/code&amp;gt;“ beenden wollen.&lt;br /&gt;
&amp;lt;!-- „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“ npm install grunt-contrib-watch --save-dev--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==JSHint==&lt;br /&gt;
&lt;br /&gt;
Ein weiteres sehr interessantes Tool, das den JavaScript-Entwickler unterstützt, ist JSHint (http://jshint.com/).&lt;br /&gt;
JSHint analysiert vorhandenen JavaScript-Code und weist auf Fehler und Strukturprobleme hin.&lt;br /&gt;
&lt;br /&gt;
Installieren Sie zunächst die zugehörigen Node.js-Pakete:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm install grunt-contrib-jshint jshint-stylish --save-dev&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend fügen Sie einen JSHit-Task in die Datei „&amp;lt;code&amp;gt;gruntfile.js&amp;lt;/code&amp;gt;“ ein.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
jshint:&lt;br /&gt;
{&lt;br /&gt;
  all: [&#039;gruntfile.js&#039;, &#039;src/**/*.js&#039;],&lt;br /&gt;
&lt;br /&gt;
  options:&lt;br /&gt;
  {&lt;br /&gt;
    esversion: 5,&lt;br /&gt;
    eqeqeq:    true,&lt;br /&gt;
    eqnull:    true,&lt;br /&gt;
    browser:   true,&lt;br /&gt;
    reporter:  require(&#039;jshint-stylish&#039;)&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nun können alle vom Entwickler erstellten JavaScript-Dateien analysiert werden. Geben Sie dazu im &lt;br /&gt;
WebStorm-Terminal einfach folgenden Befehl ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
grunt jshint&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Gut ist es, wenn JSHint mit der Meldung &amp;lt;code&amp;gt;v No problems&amp;lt;/code&amp;gt; antwortet.&lt;br /&gt;
Ansonsten sollten Sie in Ihrem Code die von JSHint gemeldeten Probleme beheben.&lt;br /&gt;
&lt;br /&gt;
Machen Sie ruhig mal die Probe auf&#039;s Exempel: Ersetzen Sie „&amp;lt;code&amp;gt;===&amp;lt;/code&amp;gt;“&lt;br /&gt;
in der Datei „&amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt;“ durch „&amp;lt;code&amp;gt;==&amp;lt;/code&amp;gt;“ und lassen Sie&lt;br /&gt;
danach JSHint laufen. Dann sollte JSHint Folgendes melden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
Expected &#039;===&#039; and instead saw &#039;==&#039;.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird ausgedrückt, dass ein nicht-strikter Gleichheitstest verwendet wurde,&lt;br /&gt;
aber ein strikter Gleichheitstest verwendet werden sollte. Der Grund ist, dass&lt;br /&gt;
nicht-strikte Fehlertests manchmal subtile Fehler zur Folge haben, die &lt;br /&gt;
nur sehr schwer zu entdecken sind.&lt;br /&gt;
&lt;br /&gt;
JSHint kennt zahlreiche Optionen, die festlegen, welche Fehler und Probleme gemeldet&lt;br /&gt;
werden sollen und welche nicht: http://jshint.com/docs/options/.&lt;br /&gt;
Hier muss jeder Programmierer seinen eigenen Stil finden.&lt;br /&gt;
&lt;br /&gt;
Folgende Optionen wurden im obigen Beispiel verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&lt;br /&gt;
&amp;lt;/dt&amp;gt;&amp;lt;code&amp;gt;esversion: 5&amp;lt;/code&amp;gt;&amp;lt;/dt&amp;gt;&lt;br /&gt;
&amp;lt;dd&amp;gt;Des sollen die Regeln von [[EcmaScript 5]] beachtet werden.&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dt&amp;gt;&amp;lt;code&amp;gt;eqeqeq: true&amp;lt;/code&amp;gt;&amp;lt;/dt&amp;gt;&lt;br /&gt;
&amp;lt;dd&amp;gt;Es sollt stets ein strikter Gleichheitstest verwendet werden.&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dt&amp;gt;&amp;lt;code&amp;gt;eqnull&amp;lt;/code&amp;gt;&amp;lt;/dt&amp;gt;&lt;br /&gt;
&amp;lt;dd&amp;gt;Der nicht-strikte Gleichheitstest &amp;lt;code&amp;gt;... == null&amp;lt;/code&amp;gt; ist allerdings erlaubt. &lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dt&amp;gt;&amp;lt;code&amp;gt;browser: true&amp;lt;/code&amp;gt;&amp;lt;/dt&amp;gt;&lt;br /&gt;
&amp;lt;dd&amp;gt;Globale Variable sollte man eigentlich grundsätzlich nicht verwenden. &lt;br /&gt;
Mit dieser Option werden für die globalen Variablen, die die Browser bereitgestellt werden &lt;br /&gt;
(&amp;lt;code&amp;gt;document&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;console&amp;lt;/code&amp;gt; etc) Ausnahmen definiert.&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anmerkung&#039;&#039;&#039;: Wäre der nicht-strikte Gleichheitstest auf „&amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;“ nicht erlaubt, müsste man in viele Fällen&lt;br /&gt;
„&amp;lt;code&amp;gt;... === null || ...=== undefined&amp;lt;/code&amp;gt;“ schreiben. Geben Sie folgende Befehle einfach mal &lt;br /&gt;
in die Konsole Ihres Web-Browsers ein (Firefox: Strg-Shift-K, Opera und Chrome: Strg-Shift-I -&amp;gt; &amp;lt;code&amp;gt;Console&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var a;&lt;br /&gt;
var b = null;&lt;br /&gt;
&lt;br /&gt;
// Strikte Gleichheitstests&lt;br /&gt;
console.log(a === null);                     /* -&amp;gt; false */&lt;br /&gt;
console.log(a === undefined);                /* -&amp;gt; true  */&lt;br /&gt;
console.log(b === null);                     /* -&amp;gt; true  */&lt;br /&gt;
console.log(b === undefined);                /* -&amp;gt; false */&lt;br /&gt;
console.log(a === null || a === undefined);  /* -&amp;gt; true  */&lt;br /&gt;
console.log(b === null || b === undefined);  /* -&amp;gt; true  */&lt;br /&gt;
&lt;br /&gt;
// Nicht-strikte Gleichheitstests&lt;br /&gt;
console.log(a == null);                      /* -&amp;gt; true  */&lt;br /&gt;
console.log(a == undefined);                 /* -&amp;gt; true  */&lt;br /&gt;
console.log(b == null);                      /* -&amp;gt; true  */&lt;br /&gt;
console.log(b == undefined);                 /* -&amp;gt; true  */&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Abschluss können Sie entweder einen WebStorm-Menü-Eintrag hinzufügen, um den JSHint jederzeit per Klickibunti starten zu können.&lt;br /&gt;
Oder Sie fügen gleich „&amp;lt;code&amp;gt;jslint&amp;lt;/code&amp;gt;“ in die Watch-Liste ein, damit jede Änderung sofort analysiert und Fehler sofort angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;gruntfile.js&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
watch:&lt;br /&gt;
{ ...&lt;br /&gt;
  js:&lt;br /&gt;
  { files:   [&#039;src/js/**/*.js&#039;],&lt;br /&gt;
    tasks:   [&#039;uglify&#039;, &#039;jshint&#039;]&lt;br /&gt;
  },&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Allerdings kann JSHint manchmal nerven. :-) Dagegen helfen dann spezielle Kommentare im JavaScript-Code, die &lt;br /&gt;
JSHint anweisen für die Prüfung des nachfolgenden Codes spezielle Optionen zu benutzen, die dann {{iAllg}}&lt;br /&gt;
eine etwas weniger strenge Überprüfung des Codes zur Folge haben (http://jshint.com/docs/).&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;3&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Multimedia-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Sautter</name></author>
	</entry>
</feed>