Ich habe einen jQuery Passwortstärke Analyser programmiert. Dieser ist natürlich sehr basic und der Code alles andere als optimal oder perfekt. Ich kriege selber Gänsehaut wenn ich ihn sehe. Man könnte so viel Code kürzern, modularisieren, verbessern, auslagern; so viel Design aufhübschen, interaktiver machen und so weiter. Aber die Zeit hab ich einfach jetzt nicht.
Ich empfehle also natürlich professionelle Plugins dieser Art. Den Code zeige ich rein aus informativen und lerntechnischen Gründen.

Wenn ich mal Zeit habe dann würde ich sicherlich probieren, meine Features in etwa wie hier auszubauen. Ein Großteil davon hat sich an 1, 2 Tagen aber nicht realisieren lassen.

Hier der Code und darunter die Erklärung einzelner Zeilen:

Update 05.06. – es gibt mittlerweile eine verbesserte Version 1.2 des Tools, bitte also den neuen Code aus diesem Post beachten!

$(document).ready(function() 
{
$("input[type=checkbox]:first").change(function(){
if($("#passwort").is('.pw')) {
	var passtemp = $("#passwort").val();
	$("#passwrap").replaceWith('<div id="passwrap"><input type="text" id="passwort" class="text" size="30" maxlength="30" /></div>');
	$("#passwort").val(passtemp);
} else {
	var passtemp = $("#passwort").val();
	$("#passwrap").replaceWith('<div id="passwrap"><input type="password" id="passwort" class="pw" size="30" maxlength="30" /></div>');
	$("#passwort").val(passtemp); 
}
});
	
$("#passwort").keyup(function() {
	ratePW();
});

function runde(zahl) {
  var rzahl = (Math.round(zahl * 10) / 10).toString();
  rzahl += (rzahl.indexOf('.') == -1)? '.0' : '0';
  return rzahl.substring(0, rzahl.indexOf('.') + 2);
}

function ratePW() 
{
var strength = 0,
klein = 0,
cap = 0,
zahl = 0,
zeichen = 0;

var passlength = $("#passwort").val().length;
var input = $("#passwort").val();

$("#length").text("Passwortlänge: "+passlength+" Zeichen");
	
for(var i=0,l=passlength;i<l ;i++)
{
var x = input.charCodeAt(i);
if ( x >= 97 && x < = 122 ) {
	klein += 1;
	if ( klein > 4 ) {
		strength += 0.2;
	} else {
		strength += 1;
	}
	} else if ( x >= 65 && x < = 90 ) {
		cap += 1;
		if ( cap > 4 ) {
			strength += 0.6;
		} else {
			strength += 2;
		}
		} else if ( x >= 48 && x < = 57 ) {
			zahl += 1;
			if ( zahl > 4 ) {
				strength += 1;
			} else {
				strength += 3;
			}
			} else if ( x >= 32 && x < = 47 || x >= 123 && x < = 126 || x >= 91 && x < = 96 || x >= 58 && x < = 64  ) {
				zeichen += 1;
				if ( zeichen > 4 ) {
					strength += 2;
				} else {
					strength += 4;
				}
				} else {
					alert("Fehler");
					}
}

var rstrength = runde(strength);
$("#strength").text("Passwortstärke: "+rstrength);
if (rstrength == 0.0) { 
	$("#cover").css({'width':'208px','left':'1px'});
	} else if (rstrength > 0.0 && rstrength < = 4.0) {
		$("#cover").css({'width':'173px','left':'35px'});
		} else if (rstrength > 3.0 && rstrength < = 8.0) {
			$("#cover").css({'width':'139px','left':'69px'});
			} else if (rstrength > 6.0 && rstrength < = 12.0) {
				$("#cover").css({'width':'105px','left':'103px'});
				} else if (rstrength > 9.0 && rstrength < = 16.0) {
					$("#cover").css({'width':'71px','left':'137px'});
					} else if (rstrength > 12.0 && rstrength < = 20.0) {
						$("#cover").css({'width':'37px','left':'171px'});
						} else if (rstrength > 15.0) {
							$("#cover").css({'display':'none'});
							} 
};	
}); //$(document).ready

Zeile 3:
Das Ändern einer Checkbox lässt sich mit

.change()

ganz leicht abfangen, hier wird die Checkbox durch eine ID identifiziert:

$("#hide").change(function() {

Man könnte auch

$("input[type=checkbox]:first").change

nehmen; wie immer führen viele Wege nach Rom,

.change()

ist jedenfalls an dieser Stelle perfekt um Buttons aus dem Weg zu gehen.

Zeile 4:
Prüfen, ob ein Element eine Klasse besitzt.

is()

bietet sich da an.

if($("#passwort").is('.pw')

Zeile 5:

.val()

fragt den Inhalt eines Elements ab, Inhalt auslesen. Mit

.val("bla")

(Zeile 7) kann man den Inhalt setzen.

var passtemp = $("#passwort").val();

Zeile 6:

replaceWith()

ersetzt den Knoten, der angesprochen wird mitsamt Inhalt mit dem neuen Inhalt. Wird

replaceWith()

auf einen Tag mit ID angewendet so verschwindet auch der Tag mit der ID. Daher habe ich in den neuen Inhalt diesen Tag mit drin.

$("#passwrap").replaceWith('<div id="passwrap"><input type="text" id="passwort" class="text" size="30" maxlength="30" /></div>');

Ich glaub das könnte man mit

append()

,

html()

oder anderen inhaltsverändernden Funktionen eleganter lösen aber mein Beispiel ging in meinem Kopf am schnellsten umzusetzen, bei anderen Methoden hätte ich erstmal nochmal lesen müssen ^^

Zeile 15-17:
Beim Tastendruck im Textfeld mit der ID passwort wird die Funktion

ratePW()

ausgeführt.

$("#passwort").keyup(function() {
	ratePW();
});
.keydown()

und

.keypress()

sind hier nicht geeignet. Testet mal aus, wie es sich dann verhält.

Zeile 19-23
Das Runden einer Zahl, in diesem Fall auf 1 Nachkommastelle. Full Credits an die Herren hier, denn von dort habe ich den Code geklaut. Hatte nicht die Nerven das selber zu schreiben. Danke 🙂

function runde(zahl) {
  var rzahl = (Math.round(zahl * 10) / 10).toString();
  rzahl += (rzahl.indexOf('.') == -1)? '.0' : '0';
  return rzahl.substring(0, rzahl.indexOf('.') + 2);
}

Wenn vorhanden wird „.0“ auch angezeigt. Ich überlege noch ob das besser aussieht als ausblenden. Weil wenn mit Nachkommastellen dann eigentlich ja immer. Egal, keine Zeit 😀

Zeile 33
Wie oben beschrieben komme ich mit

.val()

an den Inhalt des Eingabefelds und mit

.length()

an die Anzahl der Zeichen, die Länge.

var passlength = $("#passwort").val().length;

Zeile 34,38-40:
Jedes mal, wenn eine Taste in das Eingabefeld eingetragen wird, bekommt

input

den Inhalt des Textfelds neu zugewiesen. Durch die Schleife wird jedes Zeichen dieser Zeichenkette einzeln durchlaufen. Die letzte Zeile weist x den dezimalen Charcode des Zeichen zu.

var input = $("#passwort").val();
for(var i=0,l=passlength;i<l ;i++)
{
var x = input.charCodeAt(i);

Ich hatte erst mit

.Number()

und

.parseInt()

probiert an den dezimalen Charcode zu kommen, kam immer NaN bei raus. Weiß einer wieso? Bin mir nicht so sicher, was da im Hintergrund abläuft. Bei Google findet man zu 90% diese 2 Funktionen, nur ganz selten die

.charCodeAt()

.

Zeile 41-71:
Ich spar es mir den Code hier nochmal zu posten. Die Zeilen überprüfen, welcher Typ Zeichen der zwischengespeicherte Char ist. Ich weiß, das if if else Chaos ist tödlich. Zudem sind viel zu viel Zeichen erlaubt. Die Fehlerbehandlung wie z.B. bei Umlauten ist noch nicht fertig. Es gehen auch Zeichen, die normalerweise bei Passwörtern nicht erlaubt sind. Viele Details die jemand mit Langerweile besser machen würde 😉

Ein verbautes Gimmick: Wird eine Zeichenart mehr als 4 Mal benutzt gibt es Abzüge in der Wertung. Damit soll die Variation der Zeichentypen gefördert werden. Schrecklich gelöst 😀

klein += 1;
   if ( klein > 4 ) {
	strength += 0.2;
   } else {
	strength += 1;
   }

Zeile 74:
Wie oben beschrieben, rundet den Wert.

var rstrength = runde(strength);

Zeile 76-90:
Was in diesem Codeblock passiert ist mir wieder etwas peinlich. Ginge wahrscheinlich kürzer und besser, aber es funktioniert ^^
Mit

.css()

setze ich je nach Wertung die CSS Eigenschaften des #cover DIVs. Dies ist ein grauer Balken über dem eigentlichen, bunten Balken. Dieser graue Balken wird Stück für Stück kleiner, bis er bei über 20 Punkten ganz ausgeblendet wird. Die meisten vermuten, man habe nur einen farbigen Balken, der immer größer wird. Aber ich glaube das ist schwerer zu realisieren als 2 Balken. einer davon überdeckendend.

$("#cover").css({'width':'208px','left':'1px'});
  $("#cover").css({'width':'173px','left':'35px'});
    $("#cover").css({'width':'139px','left':'69px'});
      //..
        $("#cover").css({'display':'none'});

So, das wars. Eigentlich schon ein tolles Ding. Man kann natürlich auch eins der hunderten jQuery Plugins nutzen, die es mittlerweile gibt wie Sand am Meer. 😉

Und, wie stark ist euer Passwort?

Als kleine Vorbereitung den Artikel von gestern über PixelSearch lesen, wenn ihr den noch nicht kennt.

Zusätzlich zu PixelSearch spielt PixelGetColor eine sehr wichtige Rolle beim Automatisieren von komplexeren Aufgaben. Wie der Name der Funktion schon deutlich macht wird die Farbe eines Pixels abgefragt. Das Prinzip ist also recht simpel:

PixelGetColor(x, y)

liefert den dezimalen Farbwert des Pixels mit den Koordinaten x und y. Da wir bei PixelSearch und auch sonst aber öfter mit den hexadezimalen Farbwerten arbeiten bietet es sich an PixelGetColor immer mit der Funktion Hex zu kombinieren:

Hex(PixelGetColor(x, y),6)

Hex wandelt den übergebenen Wert um, 6 gibt die Länge des hexadezimalen Rückgabewerts an (bis zu 8).
Das Ergebnis von

Hex(PixelGetColor(x, y),6)

auf einem bestimmten Pixel losgelassen ist eine simple Zeichenkette wie „A3BB4C“ und kann daher auch ganz einfach so abgefragt werden:

If Hex(PixelGetColor($x, $y),6)="A3FFBC" Then
   do something
EndIf

Ansonsten kommt nicht viel dazu, es wird nur etwas konkreter.
Und zwar geht es um dieses geniale Flash Game hier: Curveball.
Vielleicht kennen es einige von euch ja. Die Grafik des Spiels ist ebenso bahnbrechend wie die Spielidee. Ein Wunder, dass es noch kostenlos ist!
Aber Spaß beiseite, das Spiel eignet sich suuper zum üben dieser Funktionen.

Der Hauptteil des Bots ohne Kommentare sieht in etwa so aus:

MsgBox(0,"Ecke oben links","Führe die Maus über die Ecke des Flash-Games oben links, du hast " & ($waittime/1000) & " Sekunden.")
Sleep($waittime)
$ol = MouseGetPos()
$center = PixelSearch($ol[0],$ol[1],$ol[0]+472,$ol[1]+315, 0xFFFFFF)
while True
	If Hex(PixelGetColor($center[0]+170,$center[1]+110),6)="FFFFFF" Then
		MouseClick("left",$center[0]+170,$center[1]+110,1,1)
	EndIf
	If Hex(PixelGetColor($center[0]+170,$center[1]+110),6)="EFFFEC" Then
		MouseClick("left",$center[0]+170,$center[1]+110,1,1)
	EndIf
	$Ballposition = PixelSearch($ol[0],$ol[1],$ol[0]+472,$ol[1]+315, 0xF0FFED, 2, 1)
	If Not @error Then
		MouseMove( $Ballposition[0], $Ballposition[1], 1)
	EndIf
WEnd

Simpel aber äußerst effektiv. Wie im letzten Post gestern schon geschrieben; das ganze PixelSearch-relative Variablen-PixelGetColor-Gedöns zu schreiben kostet erstaunlich viel Zeit. Es sieht so simpel aus aber bei jedem Spiel muss man theoretisch komplett von vorn anfangen. Überlegen, ob man Pixel sucht, relative Koordinaten abfragt und ganz viel Kleinkram. Aber ist das Werk vollbracht ist es natürlich viel mächtiger als wenn der User jedes Mal am Anfang alle Koordinaten übergeben muss.

Interessiert, wie es aussieht? Dann Spielseite geöffnet und mein Script gezogen:
Download section
CurveBot.exe
oder
CurveBot.au3 (mit all meinen Tests, Kommentaren etc, die nötig waren, bis ich den Bot funktionstüchtig fertig hatte)

Ich hoffe ihr konntet was lernen!

Letztens, beim Artikel über HotKeySet in AutoIt, fragte HansDampf, wie man denn ohne vorgegebene Pixel/Koordinaten in Scripten arbeiten könne. Ich erwähnte in dem Artikel Farberkennung, war mit der Materie aber noch nicht so vertraut.

Mittlerweile habe ich mich mit der Funktion von Farberkennung in AutoIt beschäftigt und bin begeistert.
PixelSearch() sucht euch den ersten Pixel in einem rechteckigen Zielgebiet, auf den die Farbbeschreibung zustimmt.

Code:

Pixelsearch(x-linksoben, y-linksoben, x-rechtsunten, y-rechtsunten, Hexfarbe[, Abweichung, Schritte])

Was für die Funktion also nötig ist sind 2 Koordinatenpaare und die gesuchte Farbe. Die Koordinate der oberen linken Ecke des Suchfelds (x und y) und von der unteren rechten Ecke (x und y). Um von den fixen Koordinaten wegzukommen sollte man den User 1x auffordern, an eine bestimmte Stelle zu klicken und danach alles relativ zum 1. Klick zusammen mit PixelSearch gestalten.
Je größer der Wert der Abweichung, desto ungenauer kann man die Farbe angeben, desto mehr Pixel werden gefunden.
Das letzte Argument gibt an, jeder wievielte Pixel untersucht werden soll. 10 würde bewirken, dass nur jeder 10 Pixel getestet wird. Das pusht natürlich die Geschwindigkeit.

Ein Beispiel:

$waittime = 3000
MsgBox(0,"Ecke oben links","Führe die Maus über die Ecke des Flash-Games oben links, du hast " & ($waittime/1000) & " Sekunden.")
Sleep($waittime)
$ol = MouseGetPos()
; Nur zur Kontrolle
MsgBox(0,"pos",""&$ol[0]&" | " &$ol[1]&" | " &$ol[0]+472& " | " &$ol[1]+315&"")
$center = PixelSearch($ol[0],$ol[1],$ol[0]+472,$ol[1]+315, 0xFFFFFF)
MouseMove($center[0],$center[1],1)

while True
	If Hex(PixelGetColor($center[0]+170,$center[1]+110),6)="FFFFFF" Then
		MouseClick("left",$center[0]+170,$center[1]+110,1,1)
		Sleep(2000)
	EndIf
[...]

Ich frage den Benutzer bewusst nach 1 Koordinate; die der oberen linken Ecke des Flash Games. Diese bekomme ich einfach in dem der Spieler die Maus darüber bewegt und ich die Mauskoordinaten auslese. Da ich weiß wie groß das Flash Game Fenster ist kann ich PixelSearch auch das 2. Koordinatenpaar der Ecke unten rechts übergeben, ohne es vom Nutzer zu fordern.
Ab dem Punkt gehe ich nur noch über relative Pixelangaben vor, keine festen Werte mehr, die angepasst werden müssen.

Der Code geht noch weiter und funktioniert wunderbar in allen Browsern, in allen Monitorauflösungen und Fensterpositionen. Die Lösung kostet auf jeden Fall viel mehr Zeit als wenn der User am Anfang ein paar Koordinaten angibt aber einmal programmiert ist es eben die bessere Lösung.

Stay Tuned, morgen oder Übermorgen zeige ich den ersten Bot basierend auf PixelSearch und PixelGetColor.

So, heute mal etwas AutoIt.
Mein Hintergrund: Cursed Treasure ist, mal abgesehen von den letzten 2 Level nicht sonderlich schwer. In diesen 2 letzten Spielen allerdings wird man praktisch gezwungen sehr schnell zu klicken. Es gibt in dem Spiel 3 Skills aber diese haben keine Tastenkürzel. Man muss tatsächlich immer drauf klicken.

Um das Problem schnell zu beheben habe ich mir eine neue Funktion in AutoIt gesucht:

HotKeySet("[kürzel]", "[funktion]")

Mit dem Code kann man eine Überwachung starten, die auf eine bestimmte Tastenkombination eine bestimmte Funktion startet.

Ein Beispiel:

HotKeySet("!c", "meteor") ; Überwache ALT+C

Func meteor()
	$coords = MouseGetPos() ; Speichere aktuelle Position
	MouseClick("left",1030,600,1,1) ; Koordinaten des Skills
	MouseClick("left",$coords[0],$coords[1],1,1) ; Skill auf der gespeicherten Position nutzen
EndFunc

; Script am Leben halten
while 1
    sleep(100000000)
WEnd

Mein WordPress Syntax Highlighting Plugin kennt sogar AutoIt, nicht schlecht.

Jedenfalls reagiert Zeile 1 auf die Tastenkombination ALT+C. Alt wird dabei zu einem „!“, mehr dazu auf der Send() Infoseite von AutoIt.
Beim Drücken dieser Tasten wird die meteor Funktion aufgerufen. Diese speichert die aktuellen Mauskoordinaten, klickt eine feste Stelle des Bildschirms an und klickt danach wieder auf der Stelle, auf der die Maus stand, als die Tastenkombination gedrückt wurde.

Ich stelle also die Koordinaten des Meteor Skills ein, führe die Maus über ein Zielobjekt (jetzt mal rein theoretisch z.B. einen Wave 40 unsichtbaren Ninja Champion aus Level 15) und drücke die Tastenkombination. Die Maus speichert den Ort, klickt auf den Skill und führt diesen an der alten Stelle aus. Das ganze dauert nur wenige Millisekunden 🙂

Zeile 10-12 sorgen dafür, dass das Script endlos lange läuft und man beliebig oft die Tastenkombinationen nutzen kann.

So sähe also ein kleines Cursed Treasure Script aus:

HotKeySet("!{SPACE}", "wave")
HotKeySet("!y", "wood")
HotKeySet("!x", "haste")
HotKeySet("!c", "meteor")

Func wave()
	$coords = MouseGetPos()
	MouseClick("left",970,450,1,1)
	MouseClick("left",$coords[0],$coords[1],1,1)
EndFunc

Func wood()
	$coords = MouseGetPos()
	MouseClick("left",920,600,1,1) ; 
	MouseClick("left",$coords[0],$coords[1],1,1)
EndFunc

Func haste()
	$coords = MouseGetPos()
	MouseClick("left",1135,773,1,1)
	MouseClick("left",$coords[0],$coords[1],1,1)
EndFunc

Func meteor()
	$coords = MouseGetPos()
	MouseClick("left",1030,600,1,1)
	MouseClick("left",$coords[0],$coords[1],1,1)
EndFunc

while 1
    sleep(100000000)
WEnd

Neue Wave mit Alt+Leertaste, Alt+Y/X/C nutzen die 3 Skills. Koordinaten anpassen ist aber Voraussetzung.
Das ganze ließe sich auf weniger als 10 Zeilen zusammenstampfen und statt Koordinaten vielleicht mit Farberkennung verallgemeinern aber war ja nur so ein kleiner Spaß.

Heute nutzen wir das bereits bekannte .slideToggle() um ein dynamisches Menü (vertikal, horizontal kommt später) zu erstellen.

Beispiel

javascript“ line=“1″>
$(document).ready(function() {
$(„.erweiterbar“).click(function() {
$(this).toggleClass(„expanded“);
$(this).next().slideToggle(„300“);
});
}); //$(document).ready

html4strict“ line=“1″>

  • Als kleiner Augenschmaus, die Pünktchen 😉
    Ansonsten nicht weiter kompliziert und nicht weiter spannend.
    Kann allerdings noch verbessert werden, hab jetzt jedoch keine Zeit mehr.
    Soweit, stay tuned!

    Cheat Engine zu nutzen ist eigentlich nicht schwer. Starten, Prozess wählen und loslegen.
    Nun lagert der Chrome allerdings alle Fenster, Tabs, Plugins etc in eigene Prozesse aus, folgende Prozessliste ist in der Cheat Engine also nicht ungewöhnlich:
    Welcher von den vielen chrome.exe Prozessen jetzt tatsächlich für das gewünschte Flash Game zuständig ist, bleibt auf den ersten Blick unklar.

    So findet ihr den richtigen Chrome Prozess:
    Öffnet in Chrome zuerst den internen Taskmanager mit [Shift]+[Esc], lasst euch die Spalte Vorgangs-ID anzeigen und sucht nach eurem Zielobjekt. Bei mir im Test ist es ein Flash Game, ich suche also nach dem Flash Plugin.
    Achtung: Bei Flash Games nicht den Tab merken sondern das Plugin selbst. Der „Tab: Curdes Treasure | Armor Games“ enthält zwar im Auge des Benutzers das Spiel aber es werden ja selbst die Plugins ausgelagert.

    Meine Beispiel Vorgangs-ID ist 2012, soll das ein Zeichen sein?? O.o

    Merkt euch die Vorgangs-ID und öffnet den Windows Rechner ([Win]+[R]->calc.exe). Stellt diesen auf Ansicht -> Programmierer und wählt „Dez“ als Zahlensystem aus. Gebt eure ID Nummer ein und klick dann auf „Hex“, nun erscheint die Zahl als hexadezimale Zahl. Ihr könnt auch jeden anderen Dezimal -> Hexadezimal Umrechner nutzen oder es im Kopf rechnen 😉
    Ziel ist es, eure Vorgangs-ID in hexadezimaler Form zu haben.

    Schaut erneut in die Prozessliste von Cheat Engine und durchsucht die letzte Stellen der Speicheradresse, hier sollte einer der vielen Chrome Prozesse mit eurer hexadezimalen Zeichenfolge enden. Wenn das so ist habt ihr es geschafft.

    Ich arbeite seit Kurzem mit jQuery und versuche Stück für Stück die enormen Möglichkeiten kennenzulernen.

    Ziel: 2 „Buttons“, die beim Klick, ohne Reload, versteckte Elemente angezeigen. Einmal indem die CSS Eigenschaft display geändert wird, einmal mit der jQuery slide Animation. Das Ganze wird in Funktionen verpackt die mit Übergabeparametern arbeiten. Letztenendes soll dank toggleClass ein wechselndes Hintergrundbild realisiert werden.
    javascript“ line=“1″>
    $(document).ready(function() {

    $(„#changelog-btn“).click(function(){ BtnCss(„#changelog“); });
    $(„#impressum-btn“).click(function(){ BtnSlide(„#impressum“); });

    function BtnCss(BtnId) {
    if( $(BtnId).css(„display“)==“block“ ) {
    $(BtnId).css(„display“, „none“);
    } else {
    $(BtnId).css(„display“, „block“);
    }
    $(BtnId+“-btn“).toggleClass(„active-btn“);
    }; //BtnImg

    function BtnSlide(BtnId) {
    $(BtnId).slideToggle(„slow“);
    $(BtnId+“-btn“).toggleClass(„active-btn“);
    }; //BtnSlide

    }); //$(document).ready

    html4strict“ line=“1″>

    changelog (set css style) |
    impressum (jQuery slide)

    Beispiel

    Details:

    $(document).ready(function() {

    stellt sicher, dass die jQuery Code erst reagiert, wenn das DOM komplett fertig geladen wurde. Damit wird verhinder, dass jQuery auf Elemente zugreift/manipuliert, die noch nicht geladen wurden.
    Der Changelog Button fragt beim Klick mit

    .css("attribut")

    das aktuelle CSS Attribut „display“ der „#changelog“ ID ab und setzt den Wert mit

    .css("attribut", "wert")

    dann entweder auf none oder block.
    Zusätzlich wird mit

    .toggleClass

    dem Tag eine Klasse, basierend auf den aktuellen Status, hinzugefügt oder entfernt, die das Hintergrundbild des Tags (den Pfeil) ändert.
    Mit

    .slideToggle

    aktiviere ich, je nach aktuellem Status, die slide Animation (

    .slideUp, .slideDown

    oder

    .slideToggle

    ) für die „#changelog“ ID.
    Funktionen sollte klar sein. Sind hier eigentlich unnötig, zeigen aber den generellen Aufbau.