unit ScriptItem; { [ScriptItem] [1.0] Delphi 2005 April 2008 LICENSE The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at "http://www.mozilla.org/MPL/" Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is "[ScriptItem.pas / ScriptItem.dfm]". The Initial Developer of the Original Code is Martin Holmes (Victoria, BC, Canada, "http://www.mholmes.com/"). Copyright (C) 2005-8 Martin Holmes and the University of Victoria Computing and Media Centre. The code was co-developed for university and personal projects, and rights are shared by Martin Holmes and the University of Victoria. All Rights Reserved. } { Written by Martin Holmes, October 2005 - March 2008. This unit and associated form constitutes a dialog box used to create and edit a JavaScript code which can run as a "script item", which forms part of a list of "TransformItems" (which are either search-replace pairs, or script items). A ScriptItem is a block of JavaScript code (which runs in isolation from other ScriptItems, but which has access to a block of global JavaScript code, which can be edited in the other tab of any ScriptItem window). Dependencies: FormState (to save and reload form state, and also to use its AppDirPath property). TntUnicode libraries (Troy Wolbrink). JEDI Code Library, JEDI Visual Component Library (Project JEDI) UniSynEdit (Martin Waldenburg, Maël Hörz) JavaScript Bridge (Sterling Bates et al.; this is an interface to the Mozilla SpiderMonkey dll js3215R.dll msvcr70.dll (Microsoft); required by JavaScript Bridge Mozilla SpiderMonkey dll js3215R.dll (ECMAScript engine). } interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, TntForms, JvExControls, JvComponent, JvEditorCommon, JvUnicodeEditor, JvUnicodeHLEditor, ComCtrls, TntComCtrls, StdCtrls, TntStdCtrls, Buttons, TntButtons, FormState, SynEditHighlighter, SynHighlighterJScript, SynEdit, SynMemo, jsintf, js15decl, TntDialogs; procedure JSErrorRep(cx: PJSContext; message: PChar; report: PJSErrorReport); cdecl; type TufrmScriptItem = class(TTntForm) ubnOK: TTntBitBtn; ubnCancel: TTntBitBtn; ulbInstructions: TTntLabel; upcJavaScript: TTntPageControl; utsLocal: TTntTabSheet; utsGlobal: TTntTabSheet; ubnCheck: TTntBitBtn; ustScriptName: TTntStaticText; ueScriptName: TTntEdit; usmLocal: TSynMemo; usmGlobal: TSynMemo; synHLJavaScript: TSynJScriptSyn; ustmsgCodeOK: TTntStaticText; procedure ubnCheckClick(Sender: TObject); procedure TntFormDestroy(Sender: TObject); procedure TntFormCreate(Sender: TObject); procedure ubnOKClick(Sender: TObject); private FFormStateSaver: TFormStateSaver; FJSEngine: TJSEngine; FJSScript: TJSScript; JSInput: TJSString; JSResult: TJSString; JSInputFilePath: TJSString; JSInputFileName: TJSString; function CheckCode: Boolean; function CreateErrorReport(ErrRep: PJSErrorReport): WideString; procedure SetUpScriptEngine; procedure DestroyScriptEngine; { Private declarations } public procedure Clear; { Public declarations } end; var ufrmScriptItem: TufrmScriptItem; implementation {$R *.DFM} procedure TufrmScriptItem.ubnOKClick(Sender: TObject); begin if (CheckCode = False) then ModalResult := mrNone else ModalResult := mrOK; end; function TufrmScriptItem.CheckCode: Boolean; begin JSInput.Value := 'This is just a test'; JSInputFileName.Value := 'testfile.txt'; JSInputFilePath.Value := 'c:\test\textfile.txt'; Result := FJSEngine.Global.Evaluate(usmGlobal.Text + #13#10#13#10 + usmLocal.Text); end; procedure TufrmScriptItem.Clear; begin ueScriptName.Text := ''; Application.ProcessMessages; usmLocal.Lines.Clear; Application.ProcessMessages; usmGlobal.Lines.Clear; Application.ProcessMessages; upcJavaScript.ActivePage := utsLocal; Application.ProcessMessages; end; procedure TufrmScriptItem.SetUpScriptEngine; begin FJSEngine := TJSEngine.Create(100000); FJSScript := TJSScript.Create; JSInput := TJSString.Create('Empty', FJSEngine, 'JSInput'); JSResult := TJSString.Create('Empty', FJSEngine, 'JSResult'); JSInputFilePath := TJSString.Create('Empty', FJSEngine, 'JSInputFilePath'); JSInputFileName := TJSString.Create('Empty', FJSEngine, 'JSInputFileName'); JSInput.Connect(FJSEngine, 'JSInput'); JSResult.Connect(FJSEngine, 'JSResult'); JSInputFilePath.Connect(FJSEngine, 'JSInputFilePath'); JSInputFileName.Connect(FJSEngine, 'JSInputFileName'); FJSEngine.SetErrorReporter(JSErrorRep); end; procedure TufrmScriptItem.DestroyScriptEngine; begin FreeAndNil(JSInput); FreeAndNil(JSResult); FreeAndNil(JSInputFilePath); FreeAndNil(JSInputFileName); FreeAndNil(FJSScript); FreeAndNil(FJSEngine); end; procedure TufrmScriptItem.TntFormCreate(Sender: TObject); begin FFormStateSaver := TFormStateSaver.Create(Self, True); SetUpScriptEngine; end; procedure TufrmScriptItem.TntFormDestroy(Sender: TObject); begin DestroyScriptEngine; FreeAndNil(FFormStateSaver); end; procedure TufrmScriptItem.ubnCheckClick(Sender: TObject); begin if CheckCode then WideShowMessage(ustmsgCodeOK.Caption); end; procedure JSErrorRep(cx: PJSContext; message: PChar; report: PJSErrorReport); begin WideShowMessage(WideString(message) + #13#10 + ufrmScriptItem.CreateErrorReport(report)); end; function TufrmScriptItem.CreateErrorReport(ErrRep: PJSErrorReport): WideString; var LineNoRep: WideString; OffendingLine: WideString; OffendingToken: WideString; begin {JSErrorReport = record filename: PChar; // source file name, URL, etc., or null lineno: uintN; // source line number linebuf: PChar; // offending source line tokenptr: PChar; // points to error token in linebuf (for caret positioning?) uclinebuf: pjschar; // unicode line buffer uctokenptr: pjschar; // unicode token pointers flags: uintN; errorNumber: uintN; // see js.msg ucmessage: pjschar; // default error message messageArgs: ppjschar; // arguments for the error message end; } //Calculate where the line number is. if integer(ErrRep.lineNo) < usmGlobal.Lines.Count then LineNoRep := 'Global: line ' + IntToStr(ErrRep.lineno-1) else LineNoRep := 'Local: line ' + IntToStr(ErrRep.LineNo - usmGlobal.Lines.Count); Result := LineNoRep; OffendingLine := WideString(ErrRep.uclinebuf); if Length(OffendingLine) > 80 then OffendingLine := Copy(OffendingLine, 1, 80) + '...'; Result := Result + #13#10 + OffendingLine; OffendingToken := WideString(ErrRep.uctokenptr); if Length(OffendingToken) > 0 then Result := Result + #13#10 + '"' + OffendingToken + '"'; end; end.