123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839 |
- {
- This file is part of the Free Pascal Run time library.
- Copyright (c) 2022 by Michael Van Canneyt ([email protected])
- This file contains the tests for the CSS parser
- 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.
- **********************************************************************}
- unit tcCSSResolver;
- {$mode ObjFPC}{$H+}
- interface
- uses
- Classes, SysUtils, Contnrs, fpcunit, testregistry, fpCSSParser, fpCSSTree,
- fpCSSResolver;
- type
- TDemoNodeAttribute = (
- naLeft,
- naTop,
- naWidth,
- naHeight,
- naBorder,
- naDisplay,
- naColor
- );
- TDemoNodeAttributes = set of TDemoNodeAttribute;
- const
- DemoAttributeNames: array[TDemoNodeAttribute] of string = (
- // case sensitive!
- 'left',
- 'top',
- 'width',
- 'height',
- 'border',
- 'display',
- 'color'
- );
- DemoAttrIDBase = 100;
- DemoPseudoClassIDBase = 100;
- type
- TDemoPseudoClass = (
- pcActive,
- pcHover
- );
- TDemoPseudoClasses = set of TDemoPseudoClass;
- const
- DemoPseudoClassNames: array[TDemoPseudoClass] of string = (
- // case sensitive!
- ':active',
- ':hover'
- );
- type
- { TDemoNode }
- TDemoNode = class(TComponent,ICSSNode)
- private
- class var FAttributeInitialValues: array[TDemoNodeAttribute] of string;
- private
- FActive: boolean;
- FAttributeValues: array[TDemoNodeAttribute] of string;
- FHover: boolean;
- FNodes: TFPObjectList; // list of TDemoNode
- FCSSClasses: TStrings;
- FParent: TDemoNode;
- FStyleElements: TCSSElement;
- FStyle: string;
- function GetAttribute(AIndex: TDemoNodeAttribute): string;
- function GetNodeCount: integer;
- function GetNodes(Index: integer): TDemoNode;
- procedure SetActive(const AValue: boolean);
- procedure SetAttribute(AIndex: TDemoNodeAttribute; const AValue: string);
- procedure SetHover(const AValue: boolean);
- procedure SetParent(const AValue: TDemoNode);
- procedure SetStyleElements(const AValue: TCSSElement);
- procedure SetStyle(const AValue: string);
- protected
- procedure Notification(AComponent: TComponent; Operation: TOperation);
- override;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure Clear;
- function GetCSSID: TCSSString; virtual;
- class function CSSTypeName: TCSSString; virtual;
- function GetCSSTypeName: TCSSString;
- class function CSSTypeID: TCSSNumericalID; virtual;
- function GetCSSTypeID: TCSSNumericalID;
- class function GetAttributeInitialValue(Attr: TDemoNodeAttribute): string; virtual;
- function HasCSSClass(const aClassName: TCSSString): boolean; virtual;
- function CheckCSSValue(AttrID: TCSSNumericalID; Value: TCSSElement
- ): boolean; virtual;
- procedure SetCSSValue(AttrID: TCSSNumericalID; Value: TCSSElement); virtual;
- function GetCSSParent: ICSSNode; virtual;
- function GetCSSIndex: integer; virtual;
- function GetCSSNextSibling: ICSSNode; virtual;
- function GetCSSPreviousSibling: ICSSNode; virtual;
- function GetCSSChildCount: integer; virtual;
- function GetCSSChild(const anIndex: integer): ICSSNode; virtual;
- function GetCSSNextOfType: ICSSNode; virtual;
- function GetCSSPreviousOfType: ICSSNode; virtual;
- function GetCSSAttributeClass: TCSSString; virtual;
- function HasCSSAttribute(const AttrID: TCSSNumericalID): boolean; virtual;
- function GetCSSAttribute(const AttrID: TCSSNumericalID): TCSSString; virtual;
- function HasCSSPseudoClass(const {%H-}AttrID: TCSSNumericalID): boolean; virtual;
- function GetCSSEmpty: boolean; virtual;
- function GetCSSDepth: integer; virtual;
- property Parent: TDemoNode read FParent write SetParent;
- property NodeCount: integer read GetNodeCount;
- property Nodes[Index: integer]: TDemoNode read GetNodes; default;
- property CSSClasses: TStrings read FCSSClasses;
- property StyleElements: TCSSElement read FStyleElements write SetStyleElements;
- property Style: string read FStyle write SetStyle;
- // CSS attributes
- property Left: string index naLeft read GetAttribute write SetAttribute;
- property Top: string index naTop read GetAttribute write SetAttribute;
- property Width: string index naWidth read GetAttribute write SetAttribute;
- property Height: string index naHeight read GetAttribute write SetAttribute;
- property Border: string index naBorder read GetAttribute write SetAttribute;
- property Display: string index naDisplay read GetAttribute write SetAttribute;
- property Color: string index naColor read GetAttribute write SetAttribute;
- property Attribute[Attr: TDemoNodeAttribute]: string read GetAttribute write SetAttribute;
- // CSS pseudo classes
- property Active: boolean read FActive write SetActive;
- property Hover: boolean read FHover write SetHover;
- function HasPseudoClass(PseudoClass: TDemoPseudoClass): boolean;
- end;
- TDemoNodeClass = class of TDemoNode;
- { TDemoDiv }
- TDemoDiv = class(TDemoNode)
- public
- class function CSSTypeName: TCSSString; override;
- class function CSSTypeID: TCSSNumericalID; override;
- end;
- { TDemoSpan }
- TDemoSpan = class(TDemoNode)
- public
- class function CSSTypeName: TCSSString; override;
- class function CSSTypeID: TCSSNumericalID; override;
- end;
- { TDemoButton }
- TDemoButton = class(TDemoNode)
- private
- FCaption: string;
- procedure SetCaption(const AValue: string);
- public
- class var CSSCaptionID: TCSSNumericalID;
- class function CSSTypeName: TCSSString; override;
- class function CSSTypeID: TCSSNumericalID; override;
- function HasCSSAttribute(const AttrID: TCSSNumericalID): boolean; override;
- function GetCSSAttribute(const AttrID: TCSSNumericalID): TCSSString;
- override;
- procedure SetCSSValue(AttrID: TCSSNumericalID; Value: TCSSElement); override;
- property Caption: string read FCaption write SetCaption;
- end;
- { TDemoDocument }
- TDemoDocument = class(TComponent)
- private
- FNumericalIDs: array[TCSSNumericalIDKind] of TCSSNumericalIDs;
- FCSSResolver: TCSSResolver;
- FStyle: string;
- FStyleElements: TCSSElement;
- function GetNumericalIDs(Kind: TCSSNumericalIDKind): TCSSNumericalIDs;
- procedure SetNumericalIDs(Kind: TCSSNumericalIDKind;
- const AValue: TCSSNumericalIDs);
- procedure SetStyle(const AValue: string);
- procedure SetStyleElements(const AValue: TCSSElement);
- public
- Root: TDemoNode;
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure ApplyStyle; virtual;
- procedure ApplyStyleToNode(Node: TDemoNode); virtual;
- property NumericalIDs[Kind: TCSSNumericalIDKind]: TCSSNumericalIDs read GetNumericalIDs write SetNumericalIDs;
- property StyleElements: TCSSElement read FStyleElements write SetStyleElements;
- property Style: string read FStyle write SetStyle;
- property CSSResolver: TCSSResolver read FCSSResolver;
- end;
- { TCustomTestCSSResolver }
- TCustomTestCSSResolver = class(TTestCase)
- Private
- FDoc: TDemoDocument;
- protected
- procedure SetUp; override;
- procedure TearDown; override;
- public
- property Doc: TDemoDocument read FDoc;
- end;
- { TTestCSSResolver }
- TTestCSSResolver = class(TCustomTestCSSResolver)
- published
- procedure Test_Selector_Universal;
- procedure Test_Selector_Type;
- // Test list spaces "div, button ,span {}"
- procedure Test_Selector_Id;
- procedure Test_Selector_Class;
- procedure Test_Selector_ClassClass; // AND combinator
- procedure Test_Selector_ClassSpaceClass; // Descendant combinator
- procedure Test_Selector_TypeCommaType; // OR combinator
- procedure Test_Selector_ClassGTClass; // child combinator
- procedure Test_Selector_TypePlusType; // adjacent sibling combinator
- procedure Test_Selector_TypeTildeType; // general sibling combinator
- procedure Test_Selector_HasAttribute;
- procedure Test_Selector_AttributeEquals;
- procedure Test_Selector_AttributeEqualsI;
- procedure Test_Selector_AttributeBeginsWith;
- procedure Test_Selector_AttributeEndsWith;
- procedure Test_Selector_AttributeBeginsWithHyphen;
- procedure Test_Selector_AttributeContainsWord;
- procedure Test_Selector_AttributeContainsSubstring;
- // ToDo: "all"
- // pseudo classes
- procedure Test_Selector_Root;
- procedure Test_Selector_Empty;
- procedure Test_Selector_FirstChild;
- procedure Test_Selector_LastChild;
- procedure Test_Selector_OnlyChild;
- procedure Test_Selector_Not;
- procedure Test_Selector_NthChild;
- procedure Test_Selector_NthLastChild;
- procedure Test_Selector_NthChildOf;
- procedure Test_Selector_FirstOfType;
- procedure Test_Selector_LastOfType;
- procedure Test_Selector_OnlyOfType;
- procedure Test_Selector_NthOfType;
- procedure Test_Selector_NthLastOfType;
- procedure Test_Selector_Is;
- procedure Test_Selector_Where;
- // ToDo: div:has(>img)
- // ToDo: div:has(+img)
- // ToDo: :dir()
- // ToDo: :lang()
- // custom pseudo classes
- procedure Test_Selector_Hover;
- // inline style
- procedure Test_InlineStyle;
- // ToDo: specifity
- // pseudo elements
- // skipping for forward compatibility
- // ToDo: invalid token in selector makes selector invalid
- end;
- function LinesToStr(const Args: array of const): string;
- implementation
- function LinesToStr(const Args: array of const): string;
- var
- s: String;
- i: Integer;
- begin
- s:='';
- for i:=Low(Args) to High(Args) do
- case Args[i].VType of
- vtChar: s += Args[i].VChar+LineEnding;
- vtString: s += Args[i].VString^+LineEnding;
- vtPChar: s += Args[i].VPChar+LineEnding;
- vtWideChar: s += AnsiString(Args[i].VWideChar)+LineEnding;
- vtPWideChar: s += AnsiString(Args[i].VPWideChar)+LineEnding;
- vtAnsiString: s += AnsiString(Args[i].VAnsiString)+LineEnding;
- vtWidestring: s += AnsiString(WideString(Args[i].VWideString))+LineEnding;
- vtUnicodeString:s += AnsiString(UnicodeString(Args[i].VUnicodeString))+LineEnding;
- end;
- Result:=s;
- end;
- { TCustomTestCSSResolver }
- procedure TCustomTestCSSResolver.SetUp;
- begin
- inherited SetUp;
- FDoc:=TDemoDocument.Create(nil);
- end;
- procedure TCustomTestCSSResolver.TearDown;
- begin
- FreeAndNil(FDoc);
- inherited TearDown;
- end;
- { TTestCSSResolver }
- procedure TTestCSSResolver.Test_Selector_Universal;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Doc.Style:='* { left: 10px; }';
- Doc.ApplyStyle;
- AssertEquals('Root.left','10px',Doc.Root.Left);
- end;
- procedure TTestCSSResolver.Test_Selector_Type;
- var
- Button: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Button:=TDemoButton.Create(Doc);
- Button.Parent:=Doc.Root;
- Doc.Style:='button { left: 11px; }';
- Doc.ApplyStyle;
- AssertEquals('Root.left','',Doc.Root.Left);
- AssertEquals('Button.left','11px',Button.Left);
- end;
- procedure TTestCSSResolver.Test_Selector_Id;
- var
- Button1: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Button1:=TDemoButton.Create(Doc);
- Button1.Name:='Button1';
- Button1.Parent:=Doc.Root;
- Doc.Style:='#Button1 { left: 12px; }';
- Doc.ApplyStyle;
- AssertEquals('Root.left','',Doc.Root.Left);
- AssertEquals('Button1.left','12px',Button1.Left);
- end;
- procedure TTestCSSResolver.Test_Selector_Class;
- var
- Button1: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Button1:=TDemoButton.Create(Doc);
- Button1.CSSClasses.Add('west');
- Button1.Parent:=Doc.Root;
- Doc.Style:='.west { left: 13px; }';
- Doc.ApplyStyle;
- AssertEquals('Root.left','',Doc.Root.Left);
- AssertEquals('Button1.left','13px',Button1.Left);
- end;
- procedure TTestCSSResolver.Test_Selector_ClassClass;
- var
- Button1, Button2: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Button1:=TDemoButton.Create(Doc);
- Button1.CSSClasses.Add('west');
- Button1.Parent:=Doc.Root;
- Button2:=TDemoButton.Create(Doc);
- Button2.CSSClasses.DelimitedText:='west south';
- AssertEquals('Button2.CSSClasses.Count',2,Button2.CSSClasses.Count);
- Button2.Parent:=Doc.Root;
- Doc.Style:='.west.south { left: 10px; }';
- Doc.ApplyStyle;
- AssertEquals('Root.left','',Doc.Root.Left);
- AssertEquals('Button1.left','',Button1.Left);
- AssertEquals('Button2.left','10px',Button2.Left);
- end;
- procedure TTestCSSResolver.Test_Selector_ClassSpaceClass;
- var
- Button1: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Doc.Root.CSSClasses.Add('bird');
- Button1:=TDemoButton.Create(Doc);
- Button1.CSSClasses.Add('west');
- Button1.Parent:=Doc.Root;
- Doc.Style:='.bird .west { left: 10px; }';
- Doc.ApplyStyle;
- AssertEquals('Root.left','',Doc.Root.Left);
- AssertEquals('Button1.left','10px',Button1.Left);
- end;
- procedure TTestCSSResolver.Test_Selector_TypeCommaType;
- var
- Button1: TDemoButton;
- Div1: TDemoDiv;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Button1:=TDemoButton.Create(Doc);
- Button1.Parent:=Doc.Root;
- Div1:=TDemoDiv.Create(Doc);
- Div1.Parent:=Doc.Root;
- Doc.Style:='div, button { left: 10px; }';
- Doc.ApplyStyle;
- AssertEquals('Root.left','',Doc.Root.Left);
- AssertEquals('Button1.left','10px',Button1.Left);
- AssertEquals('Div1.left','10px',Div1.Left);
- end;
- procedure TTestCSSResolver.Test_Selector_ClassGTClass;
- var
- Div1, Div2: TDemoDiv;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Doc.Root.CSSClasses.Add('lvl1');
- Div1:=TDemoDiv.Create(Doc);
- Div1.CSSClasses.Add('lvl2');
- Div1.Parent:=Doc.Root;
- Div2:=TDemoDiv.Create(Doc);
- Div2.CSSClasses.Add('lvl3');
- Div2.Parent:=Div1;
- Doc.Style:=LinesToStr([
- '.lvl1>.lvl2 { left: 10px; }',
- '.lvl1>.lvl3 { top: 11px; }',
- '.lvl2>.lvl3 { width: 12px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.left','',Doc.Root.Left);
- AssertEquals('Root.top','',Doc.Root.Top);
- AssertEquals('Root.width','',Doc.Root.Width);
- AssertEquals('Div1.left','10px',Div1.Left);
- AssertEquals('Div1.top','',Div1.Top);
- AssertEquals('Div1.width','',Div1.Width);
- AssertEquals('Div2.left','',Div2.Left);
- AssertEquals('Div2.top','',Div2.Top);
- AssertEquals('Div2.width','12px',Div2.Width);
- end;
- procedure TTestCSSResolver.Test_Selector_TypePlusType;
- var
- Button1, Button2, Button3: TDemoButton;
- Div1: TDemoDiv;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Button1:=TDemoButton.Create(Doc);
- Button1.Parent:=Doc.Root;
- Div1:=TDemoDiv.Create(Doc);
- Div1.Parent:=Doc.Root;
- Button2:=TDemoButton.Create(Doc);
- Button2.Parent:=Doc.Root;
- Button3:=TDemoButton.Create(Doc);
- Button3.Parent:=Doc.Root;
- Doc.Style:='div+button { left: 10px; }';
- Doc.ApplyStyle;
- AssertEquals('Root.left','',Doc.Root.Left);
- AssertEquals('Button1.left','',Button1.Left);
- AssertEquals('Div1.left','',Div1.Left);
- AssertEquals('Button2.left','10px',Button2.Left);
- AssertEquals('Button3.left','',Button3.Left);
- end;
- procedure TTestCSSResolver.Test_Selector_TypeTildeType;
- var
- Button1, Button2, Button3: TDemoButton;
- Div1: TDemoDiv;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Button1:=TDemoButton.Create(Doc);
- Button1.Parent:=Doc.Root;
- Div1:=TDemoDiv.Create(Doc);
- Div1.Parent:=Doc.Root;
- Button2:=TDemoButton.Create(Doc);
- Button2.Parent:=Doc.Root;
- Button3:=TDemoButton.Create(Doc);
- Button3.Parent:=Doc.Root;
- Doc.Style:='div~button { left: 10px; }';
- Doc.ApplyStyle;
- AssertEquals('Root.left','',Doc.Root.Left);
- AssertEquals('Button1.left','',Button1.Left);
- AssertEquals('Div1.left','',Div1.Left);
- AssertEquals('Button2.left','10px',Button2.Left);
- AssertEquals('Button3.left','10px',Button3.Left);
- end;
- procedure TTestCSSResolver.Test_Selector_HasAttribute;
- var
- Button1: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Button1:=TDemoButton.Create(Doc);
- Button1.Parent:=Doc.Root;
- Button1.Left:='2px';
- Doc.Style:=LinesToStr([
- '[left] { top: 3px; }',
- '[caption] { width: 4px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Top','3px',Doc.Root.Top);
- AssertEquals('Root.Width','',Doc.Root.Width);
- AssertEquals('Button1.Top','3px',Button1.Top);
- AssertEquals('Button1.Width','4px',Button1.Width);
- end;
- procedure TTestCSSResolver.Test_Selector_AttributeEquals;
- var
- Button1: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Doc.Root.Left:='2px';
- Button1:=TDemoButton.Create(Doc);
- Button1.Parent:=Doc.Root;
- Button1.Left:='3px';
- Button1.Color:='maybe black';
- Doc.Style:=LinesToStr([
- '[left=2px] { top: 4px; }',
- '[color="maybe black"] { width: 5px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Top','4px',Doc.Root.Top);
- AssertEquals('Button1.Top','',Button1.Top);
- AssertEquals('Button1.Width','5px',Button1.Width);
- end;
- procedure TTestCSSResolver.Test_Selector_AttributeEqualsI;
- var
- Button1: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Doc.Root.Left:='2px';
- Button1:=TDemoButton.Create(Doc);
- Button1.Parent:=Doc.Root;
- Button1.Left:='3px';
- Button1.Color:='maybe Black';
- Doc.Style:=LinesToStr([
- '[left="2Px" i] { top: 4px; }',
- '[color="Maybe bLack" i] { width: 5px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Top','4px',Doc.Root.Top);
- AssertEquals('Button1.Top','',Button1.Top);
- AssertEquals('Button1.Width','5px',Button1.Width);
- end;
- procedure TTestCSSResolver.Test_Selector_AttributeBeginsWith;
- var
- Button1: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Doc.Root.Left:='Foo';
- Button1:=TDemoButton.Create(Doc);
- Button1.Parent:=Doc.Root;
- Button1.Left:='Foo Bar';
- Doc.Style:=LinesToStr([
- '[left^=Fo] { top: 4px; }',
- '[left^="Foo B"] { width: 5px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Top','4px',Doc.Root.Top);
- AssertEquals('Root.Width','',Doc.Root.Width);
- AssertEquals('Button1.Top','4px',Button1.Top);
- AssertEquals('Button1.Width','5px',Button1.Width);
- end;
- procedure TTestCSSResolver.Test_Selector_AttributeEndsWith;
- var
- Button1: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Doc.Root.Left:='Foo';
- Button1:=TDemoButton.Create(Doc);
- Button1.Parent:=Doc.Root;
- Button1.Left:='Foo Bar';
- Doc.Style:=LinesToStr([
- '[left$=o] { top: 4px; }',
- '[left$="o Bar"] { width: 5px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Top','4px',Doc.Root.Top);
- AssertEquals('Root.Width','',Doc.Root.Width);
- AssertEquals('Button1.Top','',Button1.Top);
- AssertEquals('Button1.Width','5px',Button1.Width);
- end;
- procedure TTestCSSResolver.Test_Selector_AttributeBeginsWithHyphen;
- var
- Button1: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Doc.Root.Left:='Foo';
- Button1:=TDemoButton.Create(Doc);
- Button1.Parent:=Doc.Root;
- Button1.Left:='Foo-Bar';
- Doc.Style:=LinesToStr([
- '[left|=Foo] { top: 4px; }',
- '[left|="Fo"] { width: 5px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Top','4px',Doc.Root.Top);
- AssertEquals('Root.Width','',Doc.Root.Width);
- AssertEquals('Button1.Top','4px',Button1.Top);
- AssertEquals('Button1.Width','',Button1.Width);
- end;
- procedure TTestCSSResolver.Test_Selector_AttributeContainsWord;
- var
- Button1: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Doc.Root.Left:='One Two Three';
- Button1:=TDemoButton.Create(Doc);
- Button1.Parent:=Doc.Root;
- Button1.Left:='Four Five';
- Doc.Style:=LinesToStr([
- '[left~=One] { top: 4px; }',
- '[left~=Two] { width: 5px; }',
- '[left~=Three] { height: 6px; }',
- '[left~="Four Five"] { color: #123; }', // not one word, so does not match!
- '[left~=our] { display: none; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Top','4px',Doc.Root.Top);
- AssertEquals('Root.Width','5px',Doc.Root.Width);
- AssertEquals('Root.Height','6px',Doc.Root.Height);
- AssertEquals('Root.Color','',Doc.Root.Color);
- AssertEquals('Root.Display','',Doc.Root.Display);
- AssertEquals('Button1.Top','',Button1.Top);
- AssertEquals('Button1.Width','',Button1.Width);
- AssertEquals('Button1.Height','',Button1.Height);
- AssertEquals('Button1.Color','',Button1.Color);
- AssertEquals('Button1.Display','',Button1.Display);
- end;
- procedure TTestCSSResolver.Test_Selector_AttributeContainsSubstring;
- var
- Button1: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Doc.Root.Left:='Foo';
- Button1:=TDemoButton.Create(Doc);
- Button1.Parent:=Doc.Root;
- Button1.Left:='Foo Bar';
- Doc.Style:=LinesToStr([
- '[left*=oo] { top: 4px; }',
- '[left*="o B"] { width: 5px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Top','4px',Doc.Root.Top);
- AssertEquals('Root.Width','',Doc.Root.Width);
- AssertEquals('Button1.Top','4px',Button1.Top);
- AssertEquals('Button1.Width','5px',Button1.Width);
- end;
- procedure TTestCSSResolver.Test_Selector_Root;
- var
- Button1: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Doc.Root.Left:='Foo';
- Button1:=TDemoButton.Create(Doc);
- Button1.Parent:=Doc.Root;
- Doc.Style:=LinesToStr([
- ':roOt { top: 4px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Top','4px',Doc.Root.Top);
- AssertEquals('Button1.Top','',Button1.Top);
- end;
- procedure TTestCSSResolver.Test_Selector_Empty;
- var
- Div1, Div11, Div2: TDemoDiv;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Div1:=TDemoDiv.Create(Doc);
- Div1.Parent:=Doc.Root;
- Div11:=TDemoDiv.Create(Doc);
- Div11.Parent:=Div1;
- Div2:=TDemoDiv.Create(Doc);
- Div2.Parent:=Doc.Root;
- Doc.Style:=LinesToStr([
- ':eMpty { left: 1px; }',
- 'div:emPty { top: 2px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Left','',Doc.Root.Left);
- AssertEquals('Root.Top','',Doc.Root.Top);
- AssertEquals('Div1.Left','',Div1.Left);
- AssertEquals('Div1.Top','',Div1.Top);
- AssertEquals('Div11.Left','1px',Div11.Left);
- AssertEquals('Div11.Top','2px',Div11.Top);
- AssertEquals('Div2.Left','1px',Div2.Left);
- AssertEquals('Div2.Top','2px',Div2.Top);
- end;
- procedure TTestCSSResolver.Test_Selector_FirstChild;
- var
- Div1, Div11, Div12, Div2: TDemoDiv;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Div1:=TDemoDiv.Create(Doc);
- Div1.Parent:=Doc.Root;
- Div11:=TDemoDiv.Create(Doc);
- Div11.Parent:=Div1;
- Div12:=TDemoDiv.Create(Doc);
- Div12.Parent:=Div1;
- Div2:=TDemoDiv.Create(Doc);
- Div2.Parent:=Doc.Root;
- Doc.Style:=LinesToStr([
- ':first-child { left: 1px; }',
- 'div:first-child { top: 2px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Left','1px',Doc.Root.Left);
- AssertEquals('Root.Top','',Doc.Root.Top);
- AssertEquals('Div1.Left','1px',Div1.Left);
- AssertEquals('Div1.Top','2px',Div1.Top);
- AssertEquals('Div11.Left','1px',Div11.Left);
- AssertEquals('Div11.Top','2px',Div11.Top);
- AssertEquals('Div12.Left','',Div12.Left);
- AssertEquals('Div12.Top','',Div12.Top);
- AssertEquals('Div2.Left','',Div2.Left);
- AssertEquals('Div2.Top','',Div2.Top);
- end;
- procedure TTestCSSResolver.Test_Selector_LastChild;
- var
- Div1, Div11, Div2: TDemoDiv;
- Button12: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Div1:=TDemoDiv.Create(Doc);
- Div1.Parent:=Doc.Root;
- Div11:=TDemoDiv.Create(Doc);
- Div11.Parent:=Div1;
- Button12:=TDemoButton.Create(Doc);
- Button12.Parent:=Div1;
- Div2:=TDemoDiv.Create(Doc);
- Div2.Parent:=Doc.Root;
- Doc.Style:=LinesToStr([
- ':last-child { left: 6px; }',
- 'div:last-child { top: 7px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Left','6px',Doc.Root.Left);
- AssertEquals('Root.Top','',Doc.Root.Top);
- AssertEquals('Div1.Left','',Div1.Left);
- AssertEquals('Div1.Top','',Div1.Top);
- AssertEquals('Div11.Left','',Div11.Left);
- AssertEquals('Div11.Top','',Div11.Top);
- AssertEquals('Button12.Left','6px',Button12.Left);
- AssertEquals('Button12.Top','',Button12.Top);
- AssertEquals('Div2.Left','6px',Div2.Left);
- AssertEquals('Div2.Top','7px',Div2.Top);
- end;
- procedure TTestCSSResolver.Test_Selector_OnlyChild;
- var
- Div1, Div11, Div2: TDemoDiv;
- Button12: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Div1:=TDemoDiv.Create(Doc);
- Div1.Parent:=Doc.Root;
- Div11:=TDemoDiv.Create(Doc);
- Div11.Parent:=Div1;
- Div2:=TDemoDiv.Create(Doc);
- Div2.Parent:=Doc.Root;
- Button12:=TDemoButton.Create(Doc);
- Button12.Parent:=Div2;
- Doc.Style:=LinesToStr([
- ':only-child { left: 8px; }',
- 'div:only-child { top: 9px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Left','8px',Doc.Root.Left);
- AssertEquals('Root.Top','',Doc.Root.Top);
- AssertEquals('Div1.Left','',Div1.Left);
- AssertEquals('Div1.Top','',Div1.Top);
- AssertEquals('Div11.Left','8px',Div11.Left);
- AssertEquals('Div11.Top','9px',Div11.Top);
- AssertEquals('Div2.Left','',Div2.Left);
- AssertEquals('Div2.Top','',Div2.Top);
- AssertEquals('Button12.Left','8px',Button12.Left);
- AssertEquals('Button12.Top','',Button12.Top);
- end;
- procedure TTestCSSResolver.Test_Selector_Not;
- var
- Div1, Div11, Div2: TDemoDiv;
- Button12: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Div1:=TDemoDiv.Create(Doc);
- Div1.Parent:=Doc.Root;
- Div11:=TDemoDiv.Create(Doc);
- Div11.Parent:=Div1;
- Div2:=TDemoDiv.Create(Doc);
- Div2.Parent:=Doc.Root;
- Button12:=TDemoButton.Create(Doc);
- Button12.Parent:=Div2;
- Doc.Style:=LinesToStr([
- ':not(:only-child) { left: 8px; }',
- ':not(div:only-child) { top: 9px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Left','',Doc.Root.Left);
- AssertEquals('Root.Top','9px',Doc.Root.Top);
- AssertEquals('Div1.Left','8px',Div1.Left);
- AssertEquals('Div1.Top','9px',Div1.Top);
- AssertEquals('Div11.Left','',Div11.Left);
- AssertEquals('Div11.Top','',Div11.Top);
- AssertEquals('Div2.Left','8px',Div2.Left);
- AssertEquals('Div2.Top','9px',Div2.Top);
- AssertEquals('Button12.Left','',Button12.Left);
- AssertEquals('Button12.Top','9px',Button12.Top);
- end;
- procedure TTestCSSResolver.Test_Selector_NthChild;
- var
- Div1, Div2, Div3, Div4: TDemoDiv;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Div1:=TDemoDiv.Create(Doc);
- Div1.Parent:=Doc.Root;
- Div2:=TDemoDiv.Create(Doc);
- Div2.Parent:=Doc.Root;
- Div3:=TDemoDiv.Create(Doc);
- Div3.Parent:=Doc.Root;
- Div4:=TDemoDiv.Create(Doc);
- Div4.Parent:=Doc.Root;
- Doc.Style:=LinesToStr([
- ':nth-child(2n+1) { left: 8px; }',
- ':nth-child(n+3) { border: 6px; }',
- ':nth-child(-n+2) { display: inline; }',
- ':nth-child(even) { top: 3px; }',
- ':nth-child(odd) { width: 4px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Left','',Doc.Root.Left);
- AssertEquals('Root.Border','',Doc.Root.Border);
- AssertEquals('Root.Display','',Doc.Root.Display);
- AssertEquals('Root.Top','',Doc.Root.Top);
- AssertEquals('Root.Width','',Doc.Root.Width);
- AssertEquals('Div1.Left','8px',Div1.Left);
- AssertEquals('Div1.Border','',Div1.Border);
- AssertEquals('Div1.Display','inline',Div1.Display);
- AssertEquals('Div1.Top','',Div1.Top);
- AssertEquals('Div1.Width','4px',Div1.Width);
- AssertEquals('Div2.Left','',Div2.Left);
- AssertEquals('Div2.Border','',Div2.Border);
- AssertEquals('Div2.Display','inline',Div2.Display);
- AssertEquals('Div2.Top','3px',Div2.Top);
- AssertEquals('Div2.Width','',Div2.Width);
- AssertEquals('Div3.Left','8px',Div3.Left);
- AssertEquals('Div3.Border','6px',Div3.Border);
- AssertEquals('Div3.Display','',Div3.Display);
- AssertEquals('Div3.Top','',Div3.Top);
- AssertEquals('Div3.Width','4px',Div3.Width);
- AssertEquals('Div4.Left','',Div4.Left);
- AssertEquals('Div4.Border','6px',Div4.Border);
- AssertEquals('Div4.Display','',Div4.Display);
- AssertEquals('Div4.Top','3px',Div4.Top);
- AssertEquals('Div4.Width','',Div4.Width);
- end;
- procedure TTestCSSResolver.Test_Selector_NthLastChild;
- var
- Div1, Div2, Div3, Div4: TDemoDiv;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Div1:=TDemoDiv.Create(Doc);
- Div1.Parent:=Doc.Root;
- Div2:=TDemoDiv.Create(Doc);
- Div2.Parent:=Doc.Root;
- Div3:=TDemoDiv.Create(Doc);
- Div3.Parent:=Doc.Root;
- Div4:=TDemoDiv.Create(Doc);
- Div4.Parent:=Doc.Root;
- Doc.Style:=LinesToStr([
- ':nth-last-child(2n+1) { left: 8px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Left','',Doc.Root.Left);
- AssertEquals('Div1.Left','',Div1.Left);
- AssertEquals('Div2.Left','8px',Div2.Left);
- AssertEquals('Div3.Left','',Div3.Left);
- AssertEquals('Div4.Left','8px',Div4.Left);
- end;
- procedure TTestCSSResolver.Test_Selector_NthChildOf;
- var
- Div1, Div2, Div3, Div4: TDemoDiv;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Div1:=TDemoDiv.Create(Doc);
- Div1.Parent:=Doc.Root;
- Div2:=TDemoDiv.Create(Doc);
- Div2.Parent:=Doc.Root;
- Div2.Top:='3px';
- Div3:=TDemoDiv.Create(Doc);
- Div3.Parent:=Doc.Root;
- Div3.Top:='3px';
- Div4:=TDemoDiv.Create(Doc);
- Div4.Parent:=Doc.Root;
- Div4.Top:='3px';
- Doc.Style:=LinesToStr([
- ':nth-child(2n+1 of [top=3px]) { left: 5px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Left','',Doc.Root.Left);
- AssertEquals('Div1.Left','',Div1.Left);
- AssertEquals('Div2.Left','5px',Div2.Left);
- AssertEquals('Div3.Left','',Div3.Left);
- AssertEquals('Div4.Left','5px',Div4.Left);
- end;
- procedure TTestCSSResolver.Test_Selector_FirstOfType;
- var
- Div1, Div11, Div13, Div2: TDemoDiv;
- Button12: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Div1:=TDemoDiv.Create(Doc);
- Div1.Parent:=Doc.Root;
- Div11:=TDemoDiv.Create(Doc);
- Div11.Parent:=Div1;
- Button12:=TDemoButton.Create(Doc);
- Button12.Parent:=Div1;
- Div13:=TDemoDiv.Create(Doc);
- Div13.Parent:=Div1;
- Div2:=TDemoDiv.Create(Doc);
- Div2.Parent:=Doc.Root;
- Doc.Style:=LinesToStr([
- ':first-of-type { left: 6px; }',
- 'div:first-of-type { top: 7px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Left','6px',Doc.Root.Left);
- AssertEquals('Root.Top','',Doc.Root.Top);
- AssertEquals('Div1.Left','6px',Div1.Left);
- AssertEquals('Div1.Top','7px',Div1.Top);
- AssertEquals('Div11.Left','6px',Div11.Left);
- AssertEquals('Div11.Top','7px',Div11.Top);
- AssertEquals('Button12.Left','6px',Button12.Left);
- AssertEquals('Button12.Top','',Button12.Top);
- AssertEquals('Div13.Left','',Div13.Left);
- AssertEquals('Div13.Top','',Div13.Top);
- AssertEquals('Div2.Left','',Div2.Left);
- AssertEquals('Div2.Top','',Div2.Top);
- end;
- procedure TTestCSSResolver.Test_Selector_LastOfType;
- var
- Div1, Div11, Div13, Div2: TDemoDiv;
- Button12: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Div1:=TDemoDiv.Create(Doc);
- Div1.Parent:=Doc.Root;
- Div11:=TDemoDiv.Create(Doc);
- Div11.Parent:=Div1;
- Button12:=TDemoButton.Create(Doc);
- Button12.Parent:=Div1;
- Div13:=TDemoDiv.Create(Doc);
- Div13.Parent:=Div1;
- Div2:=TDemoDiv.Create(Doc);
- Div2.Parent:=Doc.Root;
- Doc.Style:=LinesToStr([
- ':last-of-type { left: 6px; }',
- 'div:last-of-type { top: 7px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Left','6px',Doc.Root.Left);
- AssertEquals('Root.Top','',Doc.Root.Top);
- AssertEquals('Div1.Left','',Div1.Left);
- AssertEquals('Div1.Top','',Div1.Top);
- AssertEquals('Div11.Left','',Div11.Left);
- AssertEquals('Div11.Top','',Div11.Top);
- AssertEquals('Button12.Left','6px',Button12.Left);
- AssertEquals('Button12.Top','',Button12.Top);
- AssertEquals('Div13.Left','6px',Div13.Left);
- AssertEquals('Div13.Top','7px',Div13.Top);
- AssertEquals('Div2.Left','6px',Div2.Left);
- AssertEquals('Div2.Top','7px',Div2.Top);
- end;
- procedure TTestCSSResolver.Test_Selector_OnlyOfType;
- var
- Div1, Div11, Div2: TDemoDiv;
- Button12: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Div1:=TDemoDiv.Create(Doc);
- Div1.Parent:=Doc.Root;
- Div11:=TDemoDiv.Create(Doc);
- Div11.Parent:=Div1;
- Button12:=TDemoButton.Create(Doc);
- Button12.Parent:=Div1;
- Div2:=TDemoDiv.Create(Doc);
- Div2.Parent:=Doc.Root;
- Doc.Style:=LinesToStr([
- ':only-of-type { left: 6px; }',
- 'div:only-of-type { top: 7px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Left','6px',Doc.Root.Left);
- AssertEquals('Root.Top','',Doc.Root.Top);
- AssertEquals('Div1.Left','',Div1.Left);
- AssertEquals('Div1.Top','',Div1.Top);
- AssertEquals('Div11.Left','6px',Div11.Left);
- AssertEquals('Div11.Top','7px',Div11.Top);
- AssertEquals('Button12.Left','6px',Button12.Left);
- AssertEquals('Button12.Top','',Button12.Top);
- AssertEquals('Div2.Left','',Div2.Left);
- AssertEquals('Div2.Top','',Div2.Top);
- end;
- procedure TTestCSSResolver.Test_Selector_NthOfType;
- var
- Div1, Div2, Div3, Div4: TDemoDiv;
- Button1, Button2: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Div1:=TDemoDiv.Create(Doc);
- Div1.Parent:=Doc.Root;
- Button1:=TDemoButton.Create(Doc);
- Button1.Parent:=Doc.Root;
- Div2:=TDemoDiv.Create(Doc);
- Div2.Parent:=Doc.Root;
- Div3:=TDemoDiv.Create(Doc);
- Div3.Parent:=Doc.Root;
- Button2:=TDemoButton.Create(Doc);
- Button2.Parent:=Doc.Root;
- Div4:=TDemoDiv.Create(Doc);
- Div4.Parent:=Doc.Root;
- Doc.Style:=LinesToStr([
- ':nth-of-type(2n+1) { left: 8px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Left','',Doc.Root.Left);
- AssertEquals('Div1.Left','8px',Div1.Left);
- AssertEquals('Button1.Left','8px',Button1.Left);
- AssertEquals('Div2.Left','',Div2.Left);
- AssertEquals('Div3.Left','8px',Div3.Left);
- AssertEquals('Button2.Left','',Button2.Left);
- AssertEquals('Div4.Left','',Div4.Left);
- end;
- procedure TTestCSSResolver.Test_Selector_NthLastOfType;
- var
- Div1, Div2, Div3, Div4: TDemoDiv;
- Button1, Button2: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Div1:=TDemoDiv.Create(Doc);
- Div1.Parent:=Doc.Root;
- Button1:=TDemoButton.Create(Doc);
- Button1.Parent:=Doc.Root;
- Div2:=TDemoDiv.Create(Doc);
- Div2.Parent:=Doc.Root;
- Div3:=TDemoDiv.Create(Doc);
- Div3.Parent:=Doc.Root;
- Button2:=TDemoButton.Create(Doc);
- Button2.Parent:=Doc.Root;
- Div4:=TDemoDiv.Create(Doc);
- Div4.Parent:=Doc.Root;
- Doc.Style:=LinesToStr([
- ':nth-last-of-type(2n+1) { left: 8px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Left','',Doc.Root.Left);
- AssertEquals('Div1.Left','',Div1.Left);
- AssertEquals('Button1.Left','',Button1.Left);
- AssertEquals('Div2.Left','8px',Div2.Left);
- AssertEquals('Div3.Left','',Div3.Left);
- AssertEquals('Button2.Left','8px',Button2.Left);
- AssertEquals('Div4.Left','8px',Div4.Left);
- end;
- procedure TTestCSSResolver.Test_Selector_Is;
- var
- Div1, Div2: TDemoDiv;
- Button1, Button2: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Div1:=TDemoDiv.Create(Doc);
- Div1.Parent:=Doc.Root;
- Div1.Top:='3px';
- Button1:=TDemoButton.Create(Doc);
- Button1.Parent:=Doc.Root;
- Div2:=TDemoDiv.Create(Doc);
- Div2.Parent:=Doc.Root;
- Button2:=TDemoButton.Create(Doc);
- Button2.Parent:=Doc.Root;
- Button2.Top:='3px';
- Doc.Style:=LinesToStr([
- ':is(div, button)[top=3px] { left: 7px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Left','',Doc.Root.Left);
- AssertEquals('Div1.Left','7px',Div1.Left);
- AssertEquals('Button1.Left','',Button1.Left);
- AssertEquals('Div2.Left','',Div2.Left);
- AssertEquals('Button2.Left','7px',Button2.Left);
- end;
- procedure TTestCSSResolver.Test_Selector_Where;
- var
- Div1, Div2: TDemoDiv;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Div1:=TDemoDiv.Create(Doc);
- Div1.Parent:=Doc.Root;
- Div1.Top:='3px';
- Div2:=TDemoDiv.Create(Doc);
- Div2.Parent:=Div1;
- Div2.Top:='3px';
- Doc.Style:=LinesToStr([
- ':where(div[top=3px]) { left: 1px; }',
- 'div div { left: 2px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Left','',Doc.Root.Left);
- AssertEquals('Div1.Left','1px',Div1.Left);
- AssertEquals('Div2.Left','2px',Div2.Left);
- end;
- procedure TTestCSSResolver.Test_Selector_Hover;
- var
- Div1, Div11: TDemoDiv;
- Button1: TDemoButton;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Div1:=TDemoDiv.Create(Doc);
- Div1.Parent:=Doc.Root;
- Div1.Hover:=true;
- Button1:=TDemoButton.Create(Doc);
- Button1.Parent:=Div1;
- Button1.Hover:=true;
- Div11:=TDemoDiv.Create(Doc);
- Div11.Parent:=Div1;
- Doc.Style:=LinesToStr([
- ':hover { left: 1px; }',
- 'button:hover { top: 2px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Left','',Doc.Root.Left);
- AssertEquals('Root.Top','',Doc.Root.Top);
- AssertEquals('Div1.Left','1px',Div1.Left);
- AssertEquals('Div1.Top','',Div1.Top);
- AssertEquals('Button1.Left','1px',Button1.Left);
- AssertEquals('Button1.Top','2px',Button1.Top);
- AssertEquals('Div11.Left','',Div11.Left);
- AssertEquals('Div11.Top','',Div11.Top);
- end;
- procedure TTestCSSResolver.Test_InlineStyle;
- var
- Div1: TDemoDiv;
- begin
- Doc.Root:=TDemoNode.Create(nil);
- Div1:=TDemoDiv.Create(Doc);
- Div1.Parent:=Doc.Root;
- Div1.Style:='left: 10px; top: 5px';
- Doc.Style:=LinesToStr([
- 'div { left: 6px; }',
- '']);
- Doc.ApplyStyle;
- AssertEquals('Root.Left','',Doc.Root.Left);
- AssertEquals('Div1.Left','10px',Div1.Left);
- AssertEquals('Div1.Top','5px',Div1.Top);
- end;
- { TDemoDiv }
- class function TDemoDiv.CSSTypeName: TCSSString;
- begin
- Result:='div';
- end;
- class function TDemoDiv.CSSTypeID: TCSSNumericalID;
- begin
- Result:=101;
- end;
- { TDemoSpan }
- class function TDemoSpan.CSSTypeName: TCSSString;
- begin
- Result:='span';
- end;
- class function TDemoSpan.CSSTypeID: TCSSNumericalID;
- begin
- Result:=102;
- end;
- { TDemoButton }
- procedure TDemoButton.SetCaption(const AValue: string);
- begin
- if FCaption=AValue then Exit;
- FCaption:=AValue;
- end;
- class function TDemoButton.CSSTypeName: TCSSString;
- begin
- Result:='button';
- end;
- class function TDemoButton.CSSTypeID: TCSSNumericalID;
- begin
- Result:=103;
- end;
- function TDemoButton.HasCSSAttribute(const AttrID: TCSSNumericalID): boolean;
- begin
- Result:=(AttrID=CSSCaptionID) or inherited HasCSSAttribute(AttrID);
- end;
- function TDemoButton.GetCSSAttribute(const AttrID: TCSSNumericalID): TCSSString;
- begin
- if AttrID=CSSCaptionID then
- Result:=Caption
- else
- Result:=inherited GetCSSAttribute(AttrID);
- end;
- procedure TDemoButton.SetCSSValue(AttrID: TCSSNumericalID; Value: TCSSElement);
- begin
- if AttrID=CSSCaptionID then
- SetCaption(Value.AsString)
- else
- inherited SetCSSValue(AttrID, Value);
- end;
- { TDemoDocument }
- procedure TDemoDocument.SetStyle(const AValue: string);
- var
- ss: TStringStream;
- aParser: TCSSParser;
- begin
- if FStyle=AValue then Exit;
- FStyle:=AValue;
- FreeAndNil(FStyleElements);
- aParser:=nil;
- ss:=TStringStream.Create(Style);
- try
- aParser:=TCSSParser.Create(ss);
- FStyleElements:=aParser.Parse;
- finally
- aParser.Free;
- end;
- end;
- function TDemoDocument.GetNumericalIDs(Kind: TCSSNumericalIDKind
- ): TCSSNumericalIDs;
- begin
- Result:=FNumericalIDs[Kind];
- end;
- procedure TDemoDocument.SetNumericalIDs(Kind: TCSSNumericalIDKind;
- const AValue: TCSSNumericalIDs);
- begin
- FNumericalIDs[Kind]:=AValue;
- end;
- procedure TDemoDocument.SetStyleElements(const AValue: TCSSElement);
- begin
- if FStyleElements=AValue then Exit;
- FStyleElements.Free;
- FStyleElements:=AValue;
- end;
- constructor TDemoDocument.Create(AOwner: TComponent);
- var
- Attr: TDemoNodeAttribute;
- TypeIDs, AttributeIDs, PseudoClassIDs: TCSSNumericalIDs;
- NumKind: TCSSNumericalIDKind;
- AttrID: TCSSNumericalID;
- PseudoClass: TDemoPseudoClass;
- begin
- inherited Create(AOwner);
- for NumKind in TCSSNumericalIDKind do
- FNumericalIDs[NumKind]:=TCSSNumericalIDs.Create(NumKind);
- // register all css types
- TypeIDs:=FNumericalIDs[nikType];
- TypeIDs['*']:=CSSTypeID_Universal;
- if TypeIDs['*']<>CSSTypeID_Universal then
- raise Exception.Create('20220909004740');
- TypeIDs[TDemoNode.CSSTypeName]:=TDemoNode.CSSTypeID;
- TypeIDs[TDemoDiv.CSSTypeName]:=TDemoDiv.CSSTypeID;
- TypeIDs[TDemoButton.CSSTypeName]:=TDemoButton.CSSTypeID;
- // register all css attributes
- AttributeIDs:=FNumericalIDs[nikAttribute];
- AttributeIDs['all']:=CSSAttributeID_All;
- // add basic element attributes
- AttrID:=DemoAttrIDBase;
- for Attr in TDemoNodeAttribute do
- begin
- AttributeIDs[DemoAttributeNames[Attr]]:=AttrID;
- inc(AttrID);
- end;
- // add button caption attribute
- TDemoButton.CSSCaptionID:=AttrID;
- AttributeIDs['caption']:=AttrID;
- inc(AttrID);
- // register css pseudo attributes
- PseudoClassIDs:=FNumericalIDs[nikPseudoClass];
- AttrID:=DemoPseudoClassIDBase;
- for PseudoClass in TDemoPseudoClass do
- begin
- PseudoClassIDs[DemoPseudoClassNames[PseudoClass]]:=AttrID;
- inc(AttrID);
- end;
- if PseudoClassIDs[DemoPseudoClassNames[pcHover]]<>DemoPseudoClassIDBase+ord(pcHover) then
- raise Exception.Create('20231008232201');
- // create the css resolver
- FCSSResolver:=TCSSResolver.Create(nil);
- for NumKind in TCSSNumericalIDKind do
- CSSResolver.NumericalIDs[NumKind]:=FNumericalIDs[NumKind];
- // create a demo root node
- Root:=TDemoNode.Create(Self);
- Root.Name:='Root';
- end;
- destructor TDemoDocument.Destroy;
- var
- NumKind: TCSSNumericalIDKind;
- begin
- FreeAndNil(FCSSResolver);
- FreeAndNil(Root);
- FreeAndNil(FStyleElements);
- for NumKind in TCSSNumericalIDKind do
- FreeAndNil(FNumericalIDs[NumKind]);
- inherited Destroy;
- end;
- procedure TDemoDocument.ApplyStyle;
- procedure Traverse(Node: TDemoNode);
- var
- i: Integer;
- begin
- ApplyStyleToNode(Node);
- for i:=0 to Node.NodeCount-1 do
- Traverse(Node[i]);
- end;
- begin
- if CSSResolver.StyleCount=0 then
- CSSResolver.AddStyle(StyleElements)
- else
- CSSResolver.Styles[0]:=StyleElements;
- Traverse(Root);
- end;
- procedure TDemoDocument.ApplyStyleToNode(Node: TDemoNode);
- begin
- CSSResolver.Compute(Node,Node.StyleElements);
- end;
- { TDemoNode }
- function TDemoNode.GetAttribute(AIndex: TDemoNodeAttribute): string;
- begin
- Result:=FAttributeValues[AIndex];
- end;
- function TDemoNode.GetNodeCount: integer;
- begin
- Result:=FNodes.Count;
- end;
- function TDemoNode.GetNodes(Index: integer): TDemoNode;
- begin
- Result:=TDemoNode(FNodes[Index]);
- end;
- procedure TDemoNode.SetAttribute(AIndex: TDemoNodeAttribute;
- const AValue: string);
- begin
- if FAttributeValues[AIndex]=AValue then exit;
- FAttributeValues[AIndex]:=AValue;
- end;
- procedure TDemoNode.SetHover(const AValue: boolean);
- begin
- if FHover=AValue then Exit;
- FHover:=AValue;
- end;
- procedure TDemoNode.SetParent(const AValue: TDemoNode);
- begin
- if FParent=AValue then Exit;
- if AValue=Self then
- raise Exception.Create('cycle');
- if FParent<>nil then
- begin
- FParent.FNodes.Remove(Self);
- end;
- FParent:=AValue;
- if FParent<>nil then
- begin
- FParent.FNodes.Add(Self);
- FreeNotification(FParent);
- end;
- end;
- procedure TDemoNode.SetActive(const AValue: boolean);
- begin
- if FActive=AValue then Exit;
- FActive:=AValue;
- end;
- procedure TDemoNode.SetStyleElements(const AValue: TCSSElement);
- begin
- if FStyleElements=AValue then Exit;
- FreeAndNil(FStyleElements);
- FStyleElements:=AValue;
- end;
- procedure TDemoNode.SetStyle(const AValue: string);
- var
- ss: TStringStream;
- aParser: TCSSParser;
- begin
- if FStyle=AValue then Exit;
- FStyle:=AValue;
- FreeAndNil(FStyleElements);
- aParser:=nil;
- ss:=TStringStream.Create(Style);
- try
- aParser:=TCSSParser.Create(ss);
- FStyleElements:=aParser.ParseInline;
- finally
- aParser.Free;
- end;
- end;
- procedure TDemoNode.Notification(AComponent: TComponent; Operation: TOperation);
- begin
- inherited Notification(AComponent, Operation);
- if AComponent=Self then exit;
- if Operation=opRemove then
- begin
- if FNodes<>nil then
- FNodes.Remove(AComponent);
- end;
- end;
- constructor TDemoNode.Create(AOwner: TComponent);
- var
- a: TDemoNodeAttribute;
- begin
- inherited Create(AOwner);
- FNodes:=TFPObjectList.Create(false);
- FCSSClasses:=TStringList.Create;
- FCSSClasses.Delimiter:=' ';
- for a in TDemoNodeAttribute do
- FAttributeValues[a]:=FAttributeInitialValues[a];
- end;
- destructor TDemoNode.Destroy;
- begin
- Clear;
- FreeAndNil(FNodes);
- FreeAndNil(FCSSClasses);
- inherited Destroy;
- end;
- procedure TDemoNode.Clear;
- var
- i: Integer;
- begin
- FCSSClasses.Clear;
- for i:=NodeCount-1 downto 0 do
- Nodes[i].Parent:=nil;
- FNodes.Clear;
- end;
- function TDemoNode.GetCSSID: TCSSString;
- begin
- Result:=Name;
- end;
- class function TDemoNode.CSSTypeName: TCSSString;
- begin
- Result:='node';
- end;
- class function TDemoNode.GetAttributeInitialValue(Attr: TDemoNodeAttribute
- ): string;
- begin
- case Attr of
- naLeft: Result:='0px';
- naTop: Result:='0px';
- naWidth: Result:='';
- naHeight: Result:='';
- naBorder: Result:='1px';
- naDisplay: Result:='inline';
- naColor: Result:='#000';
- end;
- end;
- function TDemoNode.HasCSSClass(const aClassName: TCSSString): boolean;
- var
- i: Integer;
- begin
- for i:=0 to CSSClasses.Count-1 do
- if aClassName=CSSClasses[i] then
- exit(true);
- Result:=false;
- end;
- function TDemoNode.CheckCSSValue(AttrID: TCSSNumericalID; Value: TCSSElement
- ): boolean;
- begin
- if (AttrID<DemoAttrIDBase) or (AttrID>ord(High(TDemoNodeAttribute))+DemoAttrIDBase) then
- exit(false);
- Result:=Value<>nil;
- end;
- procedure TDemoNode.SetCSSValue(AttrID: TCSSNumericalID; Value: TCSSElement);
- var
- Attr: TDemoNodeAttribute;
- s: TCSSString;
- begin
- if (AttrID<DemoAttrIDBase) or (AttrID>ord(High(TDemoNodeAttribute))+DemoAttrIDBase) then
- raise Exception.Create('TDemoNode.SetCSSValue invalid AttrID '+IntToStr(AttrID));
- Attr:=TDemoNodeAttribute(AttrID-DemoAttrIDBase);
- s:=Value.AsString;
- {$IFDEF VerboseCSSResolver}
- writeln('TDemoNode.SetCSSValue ',DemoAttributeNames[Attr],':="',s,'"');
- {$ENDIF}
- Attribute[Attr]:=s;
- end;
- function TDemoNode.GetCSSParent: ICSSNode;
- begin
- Result:=Parent;
- end;
- function TDemoNode.GetCSSIndex: integer;
- begin
- if Parent=nil then
- Result:=-1
- else
- Result:=Parent.FNodes.IndexOf(Self);
- end;
- function TDemoNode.GetCSSNextSibling: ICSSNode;
- var
- i: Integer;
- begin
- i:=GetCSSIndex;
- if (i<0) or (i+1>=Parent.NodeCount) then
- Result:=nil
- else
- Result:=Parent.Nodes[i+1];
- end;
- function TDemoNode.GetCSSPreviousSibling: ICSSNode;
- var
- i: Integer;
- begin
- i:=GetCSSIndex;
- if i<1 then
- Result:=nil
- else
- Result:=Parent.Nodes[i-1];
- end;
- function TDemoNode.GetCSSChildCount: integer;
- begin
- Result:=NodeCount;
- end;
- function TDemoNode.GetCSSChild(const anIndex: integer): ICSSNode;
- begin
- Result:=Nodes[anIndex];
- end;
- function TDemoNode.GetCSSNextOfType: ICSSNode;
- var
- i, Cnt: Integer;
- MyID: TCSSNumericalID;
- aNode: TDemoNode;
- begin
- Result:=nil;
- i:=GetCSSIndex;
- if i<0 then exit;
- inc(i);
- MyID:=CSSTypeID;
- Cnt:=Parent.NodeCount;
- while i<Cnt do
- begin
- aNode:=Parent.Nodes[i];
- if aNode.CSSTypeID=MyID then
- exit(aNode);
- inc(i);
- end;
- end;
- function TDemoNode.GetCSSPreviousOfType: ICSSNode;
- var
- i: Integer;
- MyID: TCSSNumericalID;
- aNode: TDemoNode;
- begin
- Result:=nil;
- i:=GetCSSIndex;
- if i<0 then exit;
- dec(i);
- MyID:=CSSTypeID;
- while i>=0 do
- begin
- aNode:=Parent.Nodes[i];
- if aNode.CSSTypeID=MyID then
- exit(aNode);
- dec(i);
- end;
- end;
- function TDemoNode.GetCSSAttributeClass: TCSSString;
- begin
- FCSSClasses.Delimiter:=' ';
- Result:=FCSSClasses.DelimitedText;
- end;
- function TDemoNode.HasCSSAttribute(const AttrID: TCSSNumericalID): boolean;
- begin
- Result:=(AttrID>=DemoAttrIDBase) and (AttrID<=DemoAttrIDBase+ord(High(TDemoNodeAttribute)));
- end;
- function TDemoNode.GetCSSAttribute(const AttrID: TCSSNumericalID): TCSSString;
- var
- Attr: TDemoNodeAttribute;
- begin
- if (AttrID<DemoAttrIDBase) or (AttrID>DemoAttrIDBase+ord(High(TDemoNodeAttribute))) then
- exit('');
- Attr:=TDemoNodeAttribute(AttrID-DemoAttrIDBase);
- Result:=Attribute[Attr];
- end;
- function TDemoNode.HasCSSPseudoClass(const AttrID: TCSSNumericalID): boolean;
- begin
- if (AttrID>=DemoPseudoClassIDBase) and (AttrID<=DemoPseudoClassIDBase+ord(High(TDemoPseudoClass))) then
- Result:=HasPseudoClass(TDemoPseudoClass(AttrID-DemoPseudoClassIDBase))
- else
- Result:=false;
- end;
- function TDemoNode.GetCSSEmpty: boolean;
- begin
- Result:=NodeCount=0;
- end;
- function TDemoNode.GetCSSDepth: integer;
- var
- Node: TDemoNode;
- begin
- Result:=0;
- Node:=Parent;
- while Node<>nil do
- begin
- inc(Result);
- Node:=Node.Parent;
- end;
- end;
- function TDemoNode.HasPseudoClass(PseudoClass: TDemoPseudoClass): boolean;
- begin
- case PseudoClass of
- pcActive: Result:=Active;
- pcHover: Result:=Hover;
- end;
- end;
- function TDemoNode.GetCSSTypeName: TCSSString;
- begin
- Result:=CSSTypeName;
- end;
- class function TDemoNode.CSSTypeID: TCSSNumericalID;
- begin
- Result:=100;
- end;
- function TDemoNode.GetCSSTypeID: TCSSNumericalID;
- begin
- Result:=CSSTypeID;
- end;
- initialization
- RegisterTests([TTestCSSResolver]);
- end.
|