# Monday, October 15, 2007

Der Acrobat Reader kann PDF Dateien nicht nur als normale Applikation darstellen, er bindet sich auch als Plug-In in den Webbrowser ein. Gerade dieser Integration in den Browser und damit in das Internet hat viel zur Beliebtheit von PDF beigetragen.

Aus der Sicht von JavaScript verhält sich aber Acrobat eingebettet im Browser ein bisschen anders, als im eigenständigen Modus. Um auf diese Unterschiede reagieren zu können, gibt es am Doc Objekt, das einem PDF Dokument entspricht, die Eigenschaft external. Im Kontext eines Dokuments kann dieser Zustand einfach mit this.external abgefragt werden. Aus der JavaScript Referenz:

Specifies whether the current document is being viewed in the Acrobat application or in an external window (such as a web browser).

Ich möchte diesen Wert beim Laden des Dokuments abfragen. Da ich aber zur Erzeugung der PDF Dateien Acrobat automatisiere, werden in diesem Prozess auch die Dokumente einmal geladen. Ein automatisierter Acrobat mag aber offensichtlich diesen Wert nicht verraten und hängt sich auf.

Alternative frage ich jetzt einfach die path Eigenschaft ab. Damit erhält man den Pfad zum PDF Dokument. Fängt der mit http an, dann kam die Datei vom Webserver und man ist damit im Webbrowser:

// Mag Acrobat nicht immer
var external1 = this.external;
// Workaround
var external2 = (this.path.substr(0, 4) == "http");
Monday, October 15, 2007 6:20:57 PM (W. Europe Daylight Time, UTC+02:00)  #
  Disclaimer  |  Comments [0]  | 
# Monday, September 17, 2007

Ich bin ein JavaScript Anfänger. Vor dem aktuellen Projekt gab es nur ein bisschen Html mit JavaScript als DOM Manipulator dazwischen. Echte Profis werden sicher nichts lernen können.

Ein Problem bei JavaScript ist, dass das Web voller kleiner Snippets ist, die tolle Sachen im Browser anstellen. Man findet nicht sehr viel zu den Konzepten der Sprache. JavaScript ist eine gute Ergänzung zu Html, weil es ebenso tolerant ist. Kein Compiler achtet auf die Typisierung, implizite Typkonvertierung überall.

Natürlich kann eine Sprache, die sehr schnell zu "es funktioniert" führt, auch zu unsauberem Code verleiten. Wer die Sprache konzeptionell missbraucht gerät in Gefahr, über unangenehme Randbedingungen zu stolpern, die nicht mehr funktionieren.

Ein Beispiel sind zwei sehr häufig benötigte Konstruktionen, das Array und die Hashtable.

JavaScript verfügt über ein Array Objekt das sich wie erwartet verhält. Nur ein bisschen dynamischer. So passt sich die Länge zum Beispiel automatisch an:

var testArray = new Array();
testArray[0] = "Test0";
testArray[99] = "Test99";
// Der Test liefert true:
if (testArray.length = 100)
    result = "Array Länge: 100";

Für die Hashtable gibt es aber kein entsprechendes Objekt. Aber dafür gibt es eine sehr interessante Eigenschaft von JavaScript. Alle Eigenschaften (und Methoden) eines Objekt sind, wie für eine dynamische Sprache zu erwarten, dynamisch. Können also zu jeder Zeit verändert werden. Der JavaScript Interpreter verwaltet diese Eigenschaften offensichtlich in einer Hashtable. Diese Objekt-Hashtable steht auch dem Entwickler zur Verfügung. Eine Einschränkung hat diese Objekt-Hashtable aber: Nachdem Objekt Eigenschaften benannt sind, können die Keys der Hashtable auch entsprechend nur Namen oder allgemeiner Zeichenketten sein. Genauso können natürlich als Hash Key keine Schlüsselworte des Core Objects verwendet werden (z. B: "toString").

var hashObject = new Object();
// Werte zuweisen
hashObject["key1"] = "Wert1";
hashObject["key2"] = 1234;
// Werte auslesen über Hashtable
var test = hashObject["key1"];
// Werte auslesen über Eigenschaft
var test2 = hashObject.key2
// Test, ob ein Wert vorhanden ist
if (hashObject.hasOwnProperty("key1"))
    result = "vorhanden";

Mit der for...in und der for each...in Schleife kann man ganz einfach über die Schlüsselworte oder Schlüsselwerte der Objekt-Hashtable iterieren.

for (key in hashObject)
    allKeys += key;

Bis dahin kann man das alles ganz einfach aus der JavaScript Referenz lernen. Leider liest man nicht immer die komplette Dokumentation, bevor man sich in einer neuen Sprache austobt. Google liefert zu jeder Frage sehr viele kleine Lösungen. Leider kann Google die Qualität des Codes nicht beurteilen. Schlechter Code prominent platziert führt zu neuen Programmierern mit schlechtem Stil.

Der häufigste Fehler in diesem Zusammenhang ist, dass man das Array als Hashtable verwendet:

var newHashObject = new Array();

JavaScript ist eine objektbasiert Skriptsprache und damit ist auch das Array ein Objekt. Damit funktioniert zunächst alles wie bisher. Trotzdem sollte man das so nicht machen.

Erstens leidet die Lesbarkeit. Wenn man Array definiert, erwartet man ein Array. Wenn der Zugriff dann über Variablen erfolgt, ahnt man vielleicht nicht, dass gar keine Array Indizes, sondern Hashtable Keys verwandt werden:

var result = hashObject[item];

Ist item ein Integer, mit dem auf das Array zugegriffen wird? Oder ist item ein String, mit dem auf eine Objekt Eigenschaft zugegriffen wird?

Auch der Entwickler kann sich selbst verwirren. Die Länge des Arrays wird über length abgefragt. Missbraucht man das Array als Objekt-Hashtable liefert length natürlich den falschen Wert (0).

Richtig ärgerlich wird es aber dann bei dem in Operator, der unter anderem in den Schleifen zum Einsatz kommt. Eigenschaften der vordefinierten Core Objekte werden dabei nicht ausgegeben. Nur per Code definierte Eigenschaften werden berücksichtigt, dafür aber über die ganze Prototypen Hierarchie (JavaScripts quasi Vererbung).

Ich wollte zum Beispiel das Array per Prototype um eine contains Methode erweitern.

Array.prototype.contains = function(element) 
{
    for (var i = 0; i < this.length; i++) 
    {
        if (this[i] == element) 
        {
            return true;
        }
    }
    return false;
};

Diese Erweiterung funktioniert toll. Nur jedes Array hat jetzt ein weiteres Hash Element mit dem Key contains. Bei einer Schleife wird dieses Element mit ausgegeben. Und zwar bei jeder Objekt-Hashtable, die fälschlicherweise mit "new Array" statt "new Object()" definiert wurde.

Merke: Kein Array verwenden, wenn eigentlich nur ein Object gebraucht wird!

Monday, September 17, 2007 1:21:25 PM (W. Europe Daylight Time, UTC+02:00)  #
  Disclaimer  |  Comments [0]  | 
# Monday, August 27, 2007

JavaScript ist eine dynamische Sprache. Der Code wird nicht kompiliert sondern zur Laufzeit interpretiert. Dadurch wird unter anderem die eval Funktion ermöglicht, die innerhalb eines Skripts einfach JavaScript Code als Parameter entgegen nimmt und ausführt.

Im .NET Framework gibt (gab) es eine Variante von JavaScript: JScript. Damit sollte es möglich sein, aus einem C# Programm heraus diese JScript Funktion aufzurufen. Und tatsächlich ist dies sogar sehr einfach möglich. Man muss sich nur darüber im klaren sein, dass das nur eine Übergangslösung ist.

Aus MSDN VsaEngine:

Use of this type is not recommended because it is being deprecated in Visual Studio 2005; there will be no replacement for this feature. Please see the ICodeCompiler documentation for additional help.

In .NET 3.0 ist es damit bereits Schluss. In Zukunft wird wohl eher die Dynamic Language Runtime DLR dafür verwendet.

Das hilft mir aber jetzt nichts. Die einfachste Lösung habe ich in einem Kommentar zu einem Blog-Eintrag von Rick Strahl gefunden: Evaluating JavaScript code from C#

Man erzeugt sich eine JScript Dll:

  1. Mit Notepad eine Datei mit folgendem Text erzeugen:
    class EvalClass { function Evaluate(expression: String) { return eval(expression, "unsafe"); } }
  2. Die Datei als C:\MyEval.js speichern.
  3. Einen VS2005 Command Prompt (Start, Programs, VS2005, VS2005 Tools) öffnen.
  4. Auf C:\ wechseln.
  5. Folgenden Befehl absetzen:
    jsc /t:library C:\MyEval.js
  6. Es wird eine neue Datei mit dem Namen MyEval.dll erzeugt.
  7. MyEval.dll in das gewünschte C# Projekt kopieren und referenzieren. Zusätzlich muss noch Microsoft.Jscript.dll referenziert werden.
  8. So kann jetzt die JavaScript Eval Funktion aufgerufen werden:
    EvalClass evalClass = new EvalClass();
    object result = evalClass.Evaluate("2+3");

 

Das  funktioniert wirklich so einfach und gut. Was ich später noch ändern musste, war der zusätzlich String Parameter "unsafe" bei der JScript eval Funktion. Ohne diesen Parameter gibt es eine Security Exception, wenn der übergebene JavaScript Code eine neue Funktion deklariert.

Monday, August 27, 2007 10:57:25 PM (W. Europe Daylight Time, UTC+02:00)  #
  Disclaimer  |  Comments [0]  | 
# Wednesday, August 22, 2007

Früher waren Webseiten in HTML codiert und animierte Gifs haben für ein bisschen Bewegung gesorgt. Ein paar besonders tolle Entwickler konnten per JavaScript Bilder austauschen, wenn sich der Mauszeiger darüber bewegt hat. Daten wurden in ein Formular eingegeben und per Submit an den Server übertragen. Damals war alles aus heutiger Sicht überschaubar.

Das Web 2.0 hat uns AJAX und damit den nach Hause telefonierenden, per JavaScript programmierten Browser gebracht. Damit erstellte Webanwendungen stehen in ihrer Funktionalität in manchen Bereichen Desktop Applikationen in nicht mehr nach. Aber ohne JavaScript geht auf diesen Seiten gar nichts mehr.

Gleichzeitig können fast alle neueren Browser-Schwachstellen mit deaktiviertem JavaScript umgangen werden. Also ohne JavaScript ist man sicherer, aber es funktioniert nicht mehr sehr viel. Was tun?

noscript_logo

NoScript ist ein Firefox Add-on, dass es ermöglicht JavaScript in Abhängigkeit der besuchten Webseite an- und abzuschalten. Bei der ersten Installation ist JavaScript auf allen Webseiten verboten. Besucht man vertrauenswürdige Seite, die JavaScript benötigt, kann man die Ausführung von JavaScript erlauben und NoScript merkt sich diese Einstellung. Im Laufe der Zeit erstellt man sich damit eine eigene Whitelist der erlaubten Seiten und bemerkt NoScript dann eigentlich nicht mehr.

NoScript ermöglicht es auch, JavaScript generell wieder zu aktivieren. Dieser Versuchung sollte man aber widerstehen.

Inzwischen sind noch ein paar weitere Funktionen für ein sichereres Browsen in NoScript implementiert worden. Es können Java, Flash und andere Plug-ins von nicht vertrauenswürdigen Seiten blockiert werden. Auch gegen Cross-Site Scripting (XSS) Angriffe versucht NoScript zu schützen.

Ich selbst verwende seit einiger Zeit NoScript und würde nicht mehr ohne diese zusätzliche Sicherheit surfen. Diese Woche hat hier bei CPTec ein Kollege einen Virus in Quarantäne geschickt. In den eher dubiosen Ecken des WWW hat unser Virenscanner in seinem HTTP Traffic den Bösewicht entdeckt. Noch mal Glück gehabt. Aber selbst bei täglichen Updates der Patterns ist die Scan Engine potentiell immer zu langsam für den jeweils neuesten Schädling. Jetzt wird bei uns NoScript zur Pflichtinstallation.

Wednesday, August 22, 2007 10:26:45 AM (W. Europe Daylight Time, UTC+02:00)  #
  Disclaimer  |  Comments [0]  | 
# Monday, August 20, 2007

Innerhalb der Acrobat Welt gibt es einige Integrations-Technologien von Adobe: Für die PDF Erzeugung ist die teure, exklusive PDF Library sicher am Besten geeignet und Plug-Ins schreibt man eher mit der Acrobat SDK in C. Für Interaktionen und einfache Automatisierungen im PDF Dokument ist JavaScript gedacht.

Eine standardisierte Skriptsprache (ECMA) auf dem Niveau von JavaScript ist sicher besser als zum Beispiel VBA (Visual Basic for Applications), mit dem man in Microsoft Office seit Jahrzehnten (ab 1995) seine Makros bastelt. Adobe hat eine dynamische, objektorientierte und modernere Sprache gewählt. Aber manches ist bei VBA besser gewesen.

Ich weiß jetzt nicht mehr, wann ich das erste mal den Microsoft VBA Editor geöffnet hatte. Es ist sehr lange her,  aber IntelliSense war damals schon vorhanden. Auch war der Code in Abhängigkeit von seiner Eigenschaft farbig markiert. Syntax highlighting nennt man das im Englischen. Was hat Adobe da zu bieten?

Acrobat JavaScript Editor

So sieht also der JavaScript Edit in Acrobat aus. Ganze 3 Buttons, farbloser Text, keinerlei Code Unterstützung und auch keine kontextsensitiven Menüs. Zum Glück kann man in den Eigenschaften einen externen Editor angeben. Am Besten einen, der JavaScript kennt.

Ohne externen Editor geht es bei längerem Code auch gar nicht mehr:

Adobe Acrobat Error

Nun quält der Microsoft Visual Studio verwöhnte Entwickler sein JavaScript irgendwie in die PDF Datei und macht dabei einen Fehler. Fehler machen gehört dazu, der Debugger hoffentlich auch.

Acrobat JavaScript Debugger

Das ist im Vergleich zum Editor jetzt fast schon eine überladene Benutzeroberfläche. Oben links finden sich die Buttons zur Ablaufsteuerung: Resume Execution, Interrupt, Quit, Step over, Step into, Step out

Dazu gibt es keine mir bekannten Hotkeys. Also fleißig mit der Maus die kleinen Knöpfchen angepeilt. Ist man an einer interessanten Stelle im Code angelangt, will man natürlich in das Code Fenster wechseln. Wer versucht mit Tabulator-Taste das aktive Steuerelement zu wechseln ist natürlich zu sehr von einem Windows Standard geblendet. Dieser Dialog will die Maus bewegt sehen!

Aber jetzt kommt der Clou! Zwischen dem aktuellen Skript und den obigen Buttons liegt ein Fenster mit der Skript Hierarchie:

Adobe_JavaScript_Debugger

Na gut, so ist das nun mal, irgendwo muss die Maus ja immer drüber. Denkste! Das Steuerelement im rot markierten Bereich hat Hot Tracking aktiviert. Die bloße Berührung mit dem Mauszeiger lässt den Code an die entsprechende Stelle springen. Weg ist der Kontext. Also die Übung wiederholen und diesmal die Maus im weiten Bogen bewegen. Na also geht doch!

Acrobat_Debugger_LineColumn

Im unteren Teil werden zwei Textboxen und einmal die aktuelle Zeile und Spalte angezeigt. Aber Vorsicht, die Anzeige gehört nur zur unteren Textbox, zur Console. Die aktuelle Zeile im Script-Fenster muss man selber zählen. Wer gut im Raten ist, kann auch das kleine Gitter # anklicken und zu einer Zeilennummer springen.

Puh! Diese kleine Lästerei musste jetzt sein. Das Nervenkostüm und die Produktivität leidet in der Adobe Welt aus meiner Sicht sehr. Ich erwarte ja keine Visual Studio oder Eclipse Umgebung, aber diesen Dialog verwendet Adobe-Intern sicher niemand. Heute will man doch eigentlich in der eigenen Infrastruktur externe Entwickler fördern. Das ist gut für das Hauptprodukt. Microsoft ist da meines Erachtens ein paar Schritte weiter.

Monday, August 20, 2007 10:42:57 PM (W. Europe Daylight Time, UTC+02:00)  #
  Disclaimer  |  Comments [0]  |