123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446 |
- { *********************************************************************
- This file is part of the Free Component Library (FCL)
- Copyright (c) 2016 Michael Van Canneyt.
-
- Javascript minifier
-
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
- **********************************************************************}
- { ---------------------------------------------------------------------
- Javascript minifier, based on an implementation by Douglas Crockford,
- see original copyright.
- ---------------------------------------------------------------------}
- { jsmin.c
- 2013-03-29
- Copyright (c) 2002 Douglas Crockford (www.crockford.com)
- Permission is hereby granted, free of charge, to any person obtaining a copy of
- this software and associated documentation files (the "Software"), to deal in
- the Software without restriction, including without limitation the rights to
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
- of the Software, and to permit persons to whom the Software is furnished to do
- so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
- The Software shall be used for Good, not Evil.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
- }
- {$IFNDEF FPC_DOTTEDUNITS}
- unit jsminifier;
- {$ENDIF FPC_DOTTEDUNITS}
- {$mode objfpc}{$H+}
- {$inline on}
- interface
- {$IFDEF FPC_DOTTEDUNITS}
- uses System.SysUtils,System.Classes,Fcl.Streams.Buffer;
- {$ELSE FPC_DOTTEDUNITS}
- uses sysutils,classes,bufstream;
- {$ENDIF FPC_DOTTEDUNITS}
- Const
- EOS = #0;
- Type
- { TJSONMinifier }
- EJSONMinifier = Class(Exception);
- TJSONMinifier = Class(TComponent)
- Private
- FA : AnsiChar;
- FB : AnsiChar;
- FFileHeader: TStrings;
- FLookahead : AnsiChar;
- FX : AnsiChar;
- FY : AnsiChar ;
- Fin : TStream;
- Fout : TStream;
- procedure SetFileHeader(AValue: TStrings);
- Protected
- // Token reading routines
- function Peek : AnsiChar;
- function Get : AnsiChar;inline;
- function Next : AnsiChar;
- // Token writing routines
- procedure Putc(c: AnsiChar);inline;
- Procedure Reset;
- procedure DoHeader; virtual;
- procedure Error(Const Msg: string);
- Class Function isAlphaNum(c: AnsiChar): boolean;
- Class Function iif(B : Boolean; Const ifTrue,ifFalse : integer) : integer; inline;
- procedure Action(d: Byte);
- procedure Minify;
- Public
- Constructor Create(AOwner : TComponent); override;
- Destructor Destroy; override;
- Procedure Execute(Const SourceFilename,DestFilename : String);
- Procedure Execute(Source,Dest : TStream);
- Procedure Execute(SourceFilenames : TStrings; Const DestFilename : String);
- Procedure Execute(SourceFileNames : Array of string; Const DestFilename : String);
- Published
- Property FileHeader : TStrings Read FFileHeader Write SetFileHeader;
- end;
- Implementation
- Resourcestring
- SErrUnterminatedComment = 'Unterminated comment.';
- SErrUnterminatedStringLiteral = 'Unterminated string literal.';
- SErrUnterminatedSetInRegexp = 'Unterminated set in Regular Expression literal.';
- SerrUnterminatedRegexp = 'Unterminated Regular Expression literal.';
- class function TJSONMinifier.iif(B: Boolean; const ifTrue, ifFalse: integer
- ): integer;
- begin
- if B then
- Result:=ifTrue
- else
- Result:=ifFalse;
- end;
- procedure TJSONMinifier.Error(const Msg: string);
- begin
- Raise EJSONMinifier.Create('JSMIN Error: '+Msg);
- end;
- procedure TJSONMinifier.SetFileHeader(AValue: TStrings);
- begin
- if FFileHeader=AValue then Exit;
- FFileHeader.Assign(AValue);
- end;
- procedure TJSONMinifier.Reset;
- begin
- FA:=EOS;
- FB:=EOS;
- FLookahead:=EOS;
- FX:=EOS;
- FY:=EOS;
- end;
- class function TJSONMinifier.isAlphaNum(c: AnsiChar): boolean;
- begin
- Result:= (C in ['a'..'z']) or (c in ['0'..'9']) or (c in ['A'..'Z']) or (C in ['_','$','\']) or (c > #126);
- end;
- function TJSONMinifier.Get: AnsiChar;
- begin
- Result:=FLookahead;
- FLookahead:=EOS;
- if (Result=EOS) then
- if Fin.Read(Result,sizeof(Result))=0 then exit;
- if (Result>' ') or (Result in [#10,EOS]) then
- Exit;
- if (Result=#13) then
- Result:=#10
- else
- Result:=' ';
- end;
- function TJSONMinifier.Peek: AnsiChar;
- begin
- FLookahead := get();
- result:=FLookahead;
- end;
- function TJSONMinifier.Next: AnsiChar;
- var
- c : AnsiChar;
- begin
- c:= get();
- if (c='/') then
- case peek of
- '/': Repeat
- c := get();
- until (c <= #10);
- '*':
- begin
- Get();
- while (c <> ' ') do
- case get of
- '*':
- begin
- if (peek()= '/') then
- begin
- get();
- c:=' ';
- end;
- end;
- EOS:
- Error(SErrUnterminatedComment);
- end;
- end;
- end;
- FY:=FX;
- FX:=c;
- Result:=c;
- end;
- procedure TJSONMinifier.Putc(c: AnsiChar);
- begin
- Fout.writebuffer(c,sizeof(c));
- end;
- procedure TJSONMinifier.Action(d : Byte);
- Procedure Do1;
- begin
- putc(FA);
- if ((FY in [#10,' '])
- and (FA in ['+','-','*','/'])
- and (FB in ['+','-','*','/'])) then
- putc(FY);
- end;
- Procedure Do2;
- begin
- FA:=FB;
- if (FA in ['''','"','`']) then
- While true do
- begin
- putc(FA);
- FA:= get();
- if (FA=FB) then
- break;
- if (FA='\') then
- begin
- putc(FA);
- FA:=get();
- end;
- if (FA=EOS) then
- Error(SErrUnterminatedStringLiteral);
- end;
- end;
- begin
- if (D=1) then
- Do1;
- if (D in [1,2]) then
- Do2;
- FB := next();
- if (FB='/') and (FA in ['(',',','=',':','[','!','&','|','?','+','-','~','*','/','{',#10]) then
- begin
- putc(FA);
- if (FA in ['/','*']) then
- putc(' ');
- putc(FB);
- While true do
- begin
- FA := get();
- if (FA='[') then
- begin
- While true do
- begin
- putc(FA);
- FA := get();
- if (FA = ']') then
- break;
- if (FA = '\') then
- begin
- putc(FA);
- FA := get();
- end;
- if (FA = EOS) then
- Error(SErrUnterminatedSetInRegexp);
- end
- end
- else if (FA = '/') then
- begin
- case (peek()) of
- '/', '*':
- Error(SErrUnterminatedSetInRegexp);
- end;
- Break;
- end
- else if (FA ='\') then
- begin
- putc(FA);
- FA := get();
- end;
- if (FA = EOS) then
- Error(SErrUnterminatedRegexp);
- putc(FA);
- end;
- FB := next();
- end;
- end;
- procedure TJSONMinifier.Minify;
- begin
- if (peek()= #$EF) then
- begin
- get();
- get();
- get();
- end;
- FA:=#10;
- action(3);
- while (FA <> EOS) do
- begin
- case (FA) of
- ' ':
- action(iif(isAlphanum(FB),1,2));
- #10:
- case (FB) of
- '{', '[', '(', '+', '-', '!', '~':
- Action(1);
- ' ':
- Action(3);
- else
- Action(iif(isAlphanum(FB), 1 , 2));
- end;
- else
- case (FB) of
- ' ':
- Action(iif(isAlphanum(FA),1,3));
- #10:
- case (FA) of
- '}',']',')','+','-','"', '''', '`':
- Action(1);
- else
- Action(iif(isAlphanum(FA), 1, 3));
- end;
- else
- Action(1);
- end;
- end;
- end;
- end;
- constructor TJSONMinifier.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- FFileHeader:=TStringList.Create;
- end;
- destructor TJSONMinifier.Destroy;
- begin
- FreeAndNil(FFileHeader);
- inherited Destroy;
- end;
- procedure TJSONMinifier.Execute(const SourceFilename, DestFilename: String);
- Var
- Src,Dest : TBufStream;
- begin
- Dest:=Nil;
- Src:=TReadBufStream.Create(TFileStream.Create(SourceFileName,fmOpenRead or fmShareDenyWrite),1000);
- try
- Src.SourceOwner:=True;
- Dest:=TWriteBufStream.Create(TFileStream.create(DestFileName,fmCreate),1000);
- Dest.SourceOwner:=True;
- Execute(Src,Dest);
- finally
- Src.Free;
- Dest.Free;
- end;
- end;
- procedure TJSONMinifier.DoHeader;
- Var
- S,L : String;
- begin
- For S in FFileHeader do
- begin
- L:='// '+S+sLineBreak;
- Fout.WriteBuffer(L[1],Length(L));
- end;
- end;
- procedure TJSONMinifier.Execute(Source, Dest: TStream);
- begin
- Fin:=Source;
- Fout:=Dest;
- try
- Reset;
- DoHeader;
- Minify;
- finally
- Fin:=Nil;
- Fout:=Nil;
- end;
- end;
- procedure TJSONMinifier.Execute(SourceFilenames: TStrings;const DestFilename: String);
- Var
- Src,Dest : TBufStream;
- I : Integer;
- begin
- Dest:=Src;
- Dest:=TWriteBufStream.Create(TFileStream.create(DestFileName,fmCreate),1000);
- try
- Dest.SourceOwner:=True;
- for I:=0 to SourceFileNames.Count-1 do
- begin
- Src:=TReadBufStream.Create(TFileStream.Create(SourceFileNames[i],fmOpenRead or fmShareDenyWrite),1000);
- Src.SourceOwner:=True;
- Execute(Src,Dest);
- FreeAndNil(Src);
- end;
- finally
- FreeAndNil(Src);
- FreeAndNil(Dest);
- end;
- end;
- procedure TJSONMinifier.Execute(SourceFileNames: array of string;
- const DestFilename: String);
- Var
- S : TStrings;
- begin
- S:=TStringList.Create;
- try
- S.AddStrings(SourceFileNames);
- Execute(S,DestFileName);
- finally
- S.Free;
- end;
- end;
- end.
|