mirror of
https://gitlab.com/freepascal.org/fpc/pas2js.git
synced 2025-05-03 21:33:37 +02:00
3220 lines
91 KiB
ObjectPascal
3220 lines
91 KiB
ObjectPascal
{
|
|
This file is part of the Free Pascal run time library.
|
|
Copyright (c) 2019-Now by Michael Van Canneyt, member of the
|
|
Free Pascal development team
|
|
|
|
WEB Widget Set
|
|
|
|
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 webwidget;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Types, Classes, SysUtils, JS, Web, weborworker;
|
|
|
|
Const
|
|
|
|
SElementData = 'wwElement';
|
|
STopElementData = SElementData+'Top';
|
|
SContentElementData = SElementData+'Content';
|
|
SElementClass = 'wwClass';
|
|
|
|
sEventAbort = 'abort';
|
|
SEventAnimationCancel = 'animationcancel';
|
|
SEventAnimationEnd = 'animationend';
|
|
SEventAnimationIteration = 'animationiteration';
|
|
SEventAnimationStart = 'animationstart';
|
|
sEventAuxClick = 'auxclick';
|
|
sEventBlur = 'blur';
|
|
SEventCancel = 'cancel';
|
|
SEventCanPlay = 'canplay';
|
|
SEventCanPlayThrough = 'canplaythrough';
|
|
SEventChange = 'change';
|
|
sEventClick = 'click';
|
|
sEventCompositionEnd = 'compositionend';
|
|
sEventCompositionStart = 'compositionstart';
|
|
sEventCompositionUpdate = 'compositionupdate';
|
|
sEventContextMenu = 'contextmenu';
|
|
sEventCopy = 'copy';
|
|
sEventCut = 'cut';
|
|
sEventCueChange = 'cuechange';
|
|
sEventDblClick = 'dblclick';
|
|
sEventDurationChange = 'durationchange';
|
|
sEventEnded = 'ended';
|
|
sEventError = 'error';
|
|
sEventFocus = 'focus';
|
|
sEventFocusIn = 'focusin';
|
|
sEventFocusOut = 'focusout';
|
|
SEventGotPointerCapture = 'gotpointercapture';
|
|
SEventInput = 'input';
|
|
SEventInvalid = 'invalid';
|
|
sEventKeyDown = 'keydown';
|
|
sEventKeyPress = 'keypress';
|
|
sEventKeyUp = 'keyup';
|
|
sEventLoad = 'load';
|
|
sEventLoadedData = 'loadeddata';
|
|
sEventLoadedMetaData = 'loadedmetadata';
|
|
sEventLoadend = 'loadend';
|
|
sEventLoadStart = 'loadstart';
|
|
SEventLostPointerCapture = 'lostpointercapture';
|
|
sEventMouseDown = 'mousedown';
|
|
sEventMouseEnter = 'mouseenter';
|
|
sEventMouseLeave = 'mouseleave';
|
|
sEventMouseMove = 'mousemove';
|
|
sEventMouseOut = 'mouseout';
|
|
sEventMouseUp = 'mouseup';
|
|
sEventOverFlow = 'overflow';
|
|
sEventPaste = 'paste';
|
|
sEventPause = 'pause';
|
|
sEventPlay = 'play';
|
|
SEventPointerCancel = 'pointercancel';
|
|
SEventPointerDown = 'pointerdown';
|
|
SEventPointerEnter = 'pointerenter';
|
|
SEventPointerLeave = 'pointerleave';
|
|
SEventPointerMove = 'pointermove';
|
|
SEventPointerOut = 'pointerout';
|
|
SEventPointerOver = 'pointerover';
|
|
SEventPointerUp = 'pointerup';
|
|
sEventReset = 'reset';
|
|
sEventResize = 'resize';
|
|
sEventScroll = 'scroll';
|
|
sEventSelect = 'select';
|
|
sEventSubmit = 'submit';
|
|
sEventTouchStart = 'touchstart';
|
|
SEventTransitionCancel = 'transitioncancel';
|
|
SEventTransitionEnd = 'transitionend';
|
|
SEventTransitionRun = 'transitionrun';
|
|
SEventTransitionStart = 'transitionstart';
|
|
SEventWheel = 'wheel';
|
|
|
|
|
|
Type
|
|
EWidgets = Class(Exception);
|
|
TCustomWebWidget = Class;
|
|
|
|
THTMLNotifyEvent = Procedure (Sender : TObject; Event : TJSEvent) of object;
|
|
|
|
TEventDispatch = Record
|
|
MsgStr : String;
|
|
HTMLEvent : TJSEvent;
|
|
EventHandler : THTMLNotifyEvent;
|
|
end;
|
|
|
|
{ TStyleItem }
|
|
TStylePriority = (spNone,spImportant);
|
|
|
|
TStyleItem = Class(TCollectionItem)
|
|
private
|
|
FPriority: TStylePriority;
|
|
FName: String;
|
|
FValue: String;
|
|
FImported : Boolean;
|
|
procedure SetPriority(AValue: TStylePriority);
|
|
procedure SetValue(AValue: String);
|
|
procedure SetName(AValue: String);
|
|
Protected
|
|
procedure MarkDirty;
|
|
Public
|
|
Property Imported : Boolean read FImported;
|
|
Procedure Assign(Source : TPersistent) ; override;
|
|
Published
|
|
Property Name : String Read FName Write SetName;
|
|
Property Value : String Read FValue Write SetValue;
|
|
Property Priority : TStylePriority Read FPriority Write SetPriority;
|
|
end;
|
|
|
|
{ TWebWidgetStyles }
|
|
|
|
TWebWidgetStyles = Class(TOwnedCollection)
|
|
private
|
|
Function GetStyleItem(aIndex : Integer): TStyleItem;
|
|
procedure SetStyleItem(aIndex : Integer; AValue: TStyleItem);
|
|
Protected
|
|
Procedure MarkDirty(aItem : TStyleItem);
|
|
Procedure ApplyToDOM(aElement : TJSHTMlElement;aItem : TStyleItem); virtual; overload;
|
|
Public
|
|
Function Widget : TCustomWebWidget;
|
|
// Manipulate
|
|
Function Add(Const aName : String; const aValue : String= '') : TStyleItem; overload;
|
|
Function EnsureStyle(Const aName : String; const aValue : String= '') : TStyleItem;
|
|
Function IndexOfStyle(Const aName : String) : integer;
|
|
Function FindStyle(Const aName : String) : TStyleItem;
|
|
Function GetStyle(Const aName : String) : TStyleItem;
|
|
Function RemoveStyle(Const aName : String) : String;
|
|
Procedure RefreshFromDOM(aElement : TJSHTMlElement = Nil;DoClear : Boolean = True);virtual;
|
|
Procedure ClearImported;
|
|
Procedure ApplyToDOM(aElement : TJSHTMlElement = Nil); virtual; overload;
|
|
Property Styles[aIndex : Integer] : TStyleItem Read GetStyleItem Write SetStyleItem; default;
|
|
end;
|
|
|
|
|
|
TStyleRefresh = (srOnElementID, // Only refresh styles if ElementID was set and we bind to existing element.
|
|
srAlways, // Always refresh styles
|
|
srNever); // Never refresh
|
|
TStyleRefreshes = Set of TStyleRefresh;
|
|
|
|
{ TReferenceItem }
|
|
TJSHTMLElementArray = Array of TJSHTMLElement;
|
|
|
|
TReferenceItem = Class(TCollectionItem)
|
|
private
|
|
FName: String;
|
|
FSelector: String;
|
|
procedure SetInputValidity(AValue: String);
|
|
procedure SetName(AValue: String);
|
|
procedure SetSelector(AValue: String);
|
|
function GetElement: TJSHTMLElement;
|
|
function GetElements: TJSHTMLElementArray;
|
|
Protected
|
|
procedure SetInputValue(AValue: String); virtual;
|
|
Function GetInputValue : String; virtual;
|
|
Procedure MarkDirty; virtual;
|
|
Public
|
|
Procedure Refresh;
|
|
Function Exists : Boolean;
|
|
Function IsArray : Boolean;
|
|
Function IsInput : Boolean;
|
|
Property Element : TJSHTMLElement Read GetElement;
|
|
Property Elements : TJSHTMLElementArray Read GetElements;
|
|
Property InputValue : String Read GetInputValue Write SetInputValue;
|
|
Property InputValidity : String Write SetInputValidity;
|
|
Published
|
|
Property Selector : String Read FSelector Write SetSelector;
|
|
Property Name : String Read FName Write SetName;
|
|
end;
|
|
|
|
{ TWebWidgetReferences }
|
|
|
|
TWebWidgetReferences = Class(TOwnedCollection)
|
|
Private
|
|
FRefs : TJSObject; // Arrays of elements, even for single element
|
|
function GetReferenceItem(aIndex : Integer): TReferenceItem;
|
|
procedure SetReferenceItem(aIndex : Integer; AValue: TReferenceItem);
|
|
Protected
|
|
Procedure MarkDirty(aItem : TReferenceItem);
|
|
Procedure RefreshFromDOM(aItem : TReferenceItem;aElement : TJSHTMlElement);
|
|
Property Refs : TJSObject Read FRefs;
|
|
Public
|
|
Function Widget : TCustomWebWidget;
|
|
// Manipulate
|
|
Function Add(Const aName : String; aSelector : String = '') : TReferenceItem; overload;
|
|
Function EnsureReference(Const aName : String; Const aSelector : String = '') : TReferenceItem;
|
|
Function IndexOfReference(Const aName : String) : Integer;
|
|
Function FindReference(Const aName : String) : TReferenceItem;
|
|
Function GetReference(Const aName : String) : TReferenceItem;
|
|
Procedure RemoveReference(Const aName : String);
|
|
Function ElementExists(Const aName : String) : Boolean;
|
|
Function ElementIsArray(Const aName : String) : Boolean;
|
|
Function FindElementByName(Const aName : String) : TJSHTMLElement;
|
|
Function GetElementByName(Const aName : String) : TJSHTMLElement;
|
|
Function GetElementsByName(Const aName : String) : TJSHTMLElementArray;
|
|
Procedure RefreshFromDOM(aElement : TJSHTMlElement = Nil);virtual;
|
|
Property Items[aIndex : Integer] : TReferenceItem Read GetReferenceItem Write SetReferenceItem;
|
|
Property References[aName : String] : TReferenceItem Read GetReference; default;
|
|
end;
|
|
|
|
{$DispatchStrField name}
|
|
{ TCustomWebWidget }
|
|
|
|
TCustomWebWidget = Class(TComponent)
|
|
Private
|
|
const MaxEvents = 66;
|
|
Class Var WidgetID : NativeInt;
|
|
Const FEventNames : Array[0..MaxEvents] of String = (
|
|
// When adding, only add at the end !!
|
|
sEventAbort, //0
|
|
SEventAnimationCancel,
|
|
SEventAnimationEnd,
|
|
SEventAnimationIteration,
|
|
SEventAnimationStart,
|
|
sEventAuxClick ,
|
|
sEventBlur ,
|
|
SEventCancel ,
|
|
SEventCanPlay ,
|
|
SEventCanPlayThrough ,
|
|
SEventChange , // 10
|
|
sEventClick ,
|
|
sEventCompositionEnd ,
|
|
sEventCompositionStart ,
|
|
sEventCompositionUpdate ,
|
|
sEventContextMenu ,
|
|
sEventCopy ,
|
|
sEventCut ,
|
|
sEventCueChange ,
|
|
sEventDblClick ,
|
|
sEventDurationChange , // 20
|
|
sEventEnded ,
|
|
sEventError ,
|
|
sEventFocus ,
|
|
sEventFocusIn ,
|
|
sEventFocusOut ,
|
|
SEventGotPointerCapture ,
|
|
SEventInput ,
|
|
SEventInvalid ,
|
|
sEventKeyDown ,
|
|
sEventKeyPress , // 30
|
|
sEventKeyUp ,
|
|
sEventLoad ,
|
|
sEventLoadedData ,
|
|
sEventLoadedMetaData ,
|
|
sEventLoadend ,
|
|
sEventLoadStart ,
|
|
SEventLostPointerCapture ,
|
|
sEventMouseDown ,
|
|
sEventMouseEnter ,
|
|
sEventMouseLeave , // 40
|
|
sEventMouseMove ,
|
|
sEventMouseOut ,
|
|
sEventMouseUp ,
|
|
sEventOverFlow ,
|
|
sEventPaste ,
|
|
sEventPause ,
|
|
sEventPlay ,
|
|
SEventPointerCancel ,
|
|
SEventPointerDown ,
|
|
SEventPointerEnter ,
|
|
SEventPointerLeave ,
|
|
SEventPointerMove ,
|
|
SEventPointerOut ,
|
|
SEventPointerOver ,
|
|
SEventPointerUp ,
|
|
sEventReset ,
|
|
sEventResize ,
|
|
sEventScroll ,
|
|
sEventSelect ,
|
|
sEventSubmit ,
|
|
sEventTouchStart ,
|
|
SEventTransitionCancel ,
|
|
SEventTransitionEnd ,
|
|
SEventTransitionRun ,
|
|
SEventTransitionStart ,
|
|
SEventWheel
|
|
);
|
|
private
|
|
FAfterRenderHTML: TNotifyEvent;
|
|
FAfterUnRenderHTML: TNotifyEvent;
|
|
FBeforeRenderHTML: TNotifyEvent;
|
|
FBeforeUnRenderHTML: TNotifyEvent;
|
|
FParent : TCustomWebWidget;
|
|
FMyHook : TJSRawEventHandler;
|
|
// Set by setting ParentID or Parent
|
|
FParentElement : TJSHTMLElement;
|
|
FElement : TJSHTMLElement;
|
|
FOwnsElement : Boolean;
|
|
FParentID : String;
|
|
FElementID : String;
|
|
FChildren : TJSArray;
|
|
FClasses : String;
|
|
FMyEvents : TJSObject;
|
|
FStyleRefresh: TStyleRefresh;
|
|
FStyles: TWebWidgetStyles;
|
|
FVisible : Boolean;
|
|
FDisplay : String;
|
|
FReferences : TWebWidgetReferences;
|
|
FAttrs : TJSObject;
|
|
function GetChildCount: Integer;
|
|
function GetChild(aIndex : Integer): TCustomWebWidget;
|
|
function GetClasses: String;
|
|
function GetDataset(aName : String): String;
|
|
function GetElement: TJSHTMLELement;
|
|
function GetExternalElement: Boolean;
|
|
function GetFixedElementID: Boolean;
|
|
function GetHaveReferences: Boolean;
|
|
function GetHTMLEvent(AIndex: Integer): THTMLNotifyEvent;
|
|
function GetIsElementDirty: Boolean;
|
|
function GetParent: TCustomWebWidget;
|
|
function GetParentElement: TJSHTMLELement;
|
|
function GetParentID: String;
|
|
function GetElementID: String;
|
|
function GetReference(const aName : string): TJSHTMLElement;
|
|
function GetReferenceItem(aName : String): TReferenceItem;
|
|
function GetReferenceList(const aName : string): TJSHTMLElementArray;
|
|
function GetReferences: TWebWidgetReferences;
|
|
function GetRendered: Boolean;
|
|
function GetVisible: Boolean;
|
|
procedure SetClasses(AValue: String);
|
|
procedure SetDataset(aName : String; AValue: String);
|
|
procedure SetElementID(AValue: String);
|
|
procedure SetHTMLEvent(AIndex: Integer; AValue: THTMLNotifyEvent);
|
|
procedure SetParent(AValue: TCustomWebWidget);
|
|
procedure SetParentID(AValue: String);
|
|
Procedure AddChild(aValue : TCustomWebWidget);
|
|
Procedure RemoveChild(aValue : TCustomWebWidget);
|
|
procedure SetReferences(AValue: TWebWidgetReferences);
|
|
procedure SetStyles(AValue: TWebWidgetStyles);
|
|
procedure SetVisible(AValue: Boolean);
|
|
procedure SetAttr(const aName : string; AValue: String);
|
|
Function GetAttr(const aName : String) : String;
|
|
// This protected section is not meant to be made public
|
|
Protected
|
|
// Events mechanism
|
|
procedure EventEntry(aEvent: TJSEvent); virtual;
|
|
// Low level
|
|
procedure RemoveEvent(aElement: TJSHTMLElement; const aEvent: String);
|
|
procedure HookupEvent(aElement: TJSHTMLElement; const aEvent: String);
|
|
Procedure HookupEvents(aElement : TJSHTMLElement); virtual;
|
|
// Add to internal list, if rendered, calls hookup
|
|
Procedure AddEvent(aName : String; AHandler : THTMLNotifyEvent);
|
|
// Remove from internal list, if rendered, calls RemoveEvent
|
|
Procedure DeleteEvent(aName : String);
|
|
// Override these if you want somehow to grab a fixed element on a page.
|
|
Class Function FixedParent : TJSHTMLElement; virtual;
|
|
Class Function DefaultParentElement : TJSHTMLElement; virtual;
|
|
Class Function DefaultParent : TCustomWebWidget; virtual;
|
|
Class Function FixedElement : TJSHTMLElement; virtual;
|
|
// Generate an ID
|
|
Class function GenerateID: String;
|
|
// Find element in DOM tree.
|
|
Class Function FindElement(aID : String) : TJSHTMLElement;
|
|
// Create element in DOM tree, set ID if it is nonzero
|
|
Class function CreateElement (aTag : String; aID : String) : TJSHTMLElement;
|
|
Class function CreateElement (aParent:TJSElement; aTag : String; aID : String) : TJSHTMLElement;
|
|
// references are relative to this element. By default, this is the element of the widget.
|
|
// override if you want for instance to indicate the parent element.
|
|
function GetReferenceElement: TJSHTMLELement; virtual;
|
|
// override if you want content to go in this sub-element instead of directly below the toplevel element.
|
|
function GetContentElement: TJSHTMLELement; virtual;
|
|
// Override this if Element is not the top element of this widget.
|
|
function GetTopElement: TJSHTMLELement; virtual;
|
|
// Auxiliary function to create a displayable name of this widget
|
|
Function DisplayElementName : String;
|
|
// Called after ElementID was set
|
|
procedure DoOnSetElementID; virtual;
|
|
// Make sure there is an element.
|
|
function EnsureElement: TJSHTMLElement;
|
|
// Set parent element to nil. No rendering is done. Can be called when there are no DOM elements
|
|
Procedure InvalidateParentElement;
|
|
// Set element to nil, clears styles and references. Can be called when there are no DOM elements
|
|
Procedure InvalidateElement; virtual;
|
|
// Name of the tag to create. Set to '' if you don't want RenderHTML to create one.
|
|
Function HTMLTag : String; virtual; abstract;
|
|
// Class names that must always be applied for this widget to work.
|
|
// They are only added during render, and do not show up in classes property.
|
|
// e.g. for a bootstrap button widget, this would return "btn"
|
|
Function WidgetClasses : String; virtual;
|
|
// Override this if you want to create custom styles collection
|
|
Function CreateStyles : TWebWidgetStyles; virtual;
|
|
// Override this if you want to create custom references
|
|
Function CreateReferences : TWebWidgetReferences; virtual;
|
|
// Forces apply of visible, sets Visible property
|
|
procedure ApplyVisible(aElement : TJSHTMLElement; AValue: Boolean); virtual;
|
|
// Here all properties from the widget are applied to the element.
|
|
// This is called during RenderHTML, but also when binding ElementID to an Element.
|
|
Procedure ApplyWidgetSettings(aElement : TJSHTMLElement); virtual;
|
|
{
|
|
Actually create & render element.
|
|
This gets the element as created by RenderHTML and the parent as received by RenderHTML.
|
|
If aElement is nil, the DoRenderHTML is responsible for creating & attaching it to the parent element.
|
|
Must return the value for Element.
|
|
}
|
|
Function DoRenderHTML(aParent,aElement : TJSHTMLElement) :TJSHTMLElement; virtual;
|
|
// Apply data to Element, Top and Content. Can only be called when the 3 are set, i.e. after RenderHTML or when Element is set from ElementID.
|
|
Procedure ApplyData; virtual;
|
|
Procedure RemoveData; virtual;
|
|
// Update references
|
|
Procedure RefreshReferences; virtual;
|
|
// Create html. Creates element below parent, and renders HTML using doRenderHTML
|
|
Function RenderHTML(aParent : TJSHTMLELement) : TJSHTMLElement;
|
|
// Override this if you need to do additional actions besides removing top element from parent. Parent is always assigned
|
|
Procedure DoUnRender(aParent : TJSHTMLElement) ; virtual;
|
|
// Remove HTML, if any. aParent can be nil.
|
|
Procedure UnRender(aParent : TJSHTMLElement); overload;
|
|
// Dispatch an event
|
|
Function DispatchEvent(aName : String; aEvent : TJSEvent = Nil) : Boolean;
|
|
// the rendered or attached element if ElementID was set. Can be Nil;
|
|
// This is the "main" widget of the rendered HTML. There can be HTML below or HTML before.
|
|
Property Element : TJSHTMLELement Read GetElement;
|
|
// The attached parent element. Obtained through Parent or ParentID. Can be Nil;
|
|
// Not necessarily the parent element of Element, but definitely the parent of TopElement;
|
|
Property ParentElement : TJSHTMLELement Read GetParentElement;
|
|
// Content Element. By default equals Element.
|
|
Property ContentElement : TJSHTMLELement Read GetContentElement;
|
|
// Top Element. The parent element is the direct parent of this element.
|
|
Property TopElement : TJSHTMLELement Read GetTopElement;
|
|
// Is true if this class created the element (i.e. it was not obtained with ElementID)
|
|
Property OwnsElement : Boolean Read FOwnsElement;
|
|
// My Events
|
|
Property MyEvents : TJSObject Read FMyEvents;
|
|
// Return true if the ElementID is referring to an existing element.
|
|
Property ExternalElement : Boolean Read GetExternalElement;
|
|
// since reading references creates the collection, we want a way to see if there are any without creating them.
|
|
Property HaveReferences : Boolean Read GetHaveReferences;
|
|
// Property attrs
|
|
Property StoredAttrs : TJSObject Read FAttrs;
|
|
Public
|
|
Class var CreateDataTags : Boolean;
|
|
Public
|
|
Constructor Create(aOwner : TComponent); override;
|
|
Destructor Destroy; override;
|
|
// Does this element allow childern ?
|
|
Class Function AllowChildren : Boolean; virtual;
|
|
// Manipulate Classes
|
|
Class Function AddRemoveClasses(const Source, aAddClasses, aRemoveClasses : String; Normalize : Boolean = false) : String;
|
|
// Number of classes in search and replace must match.
|
|
Class Function ReplaceClasses(const Source, aSearchClasses, aReplaceClasses : String; Normalize : Boolean = false) : String;
|
|
Class Function RemoveClasses(const Source, aClasses : String; Normalize : Boolean = false) : String;
|
|
Class Function RemoveClasses(el : TJSHTMLElement; const aClasses : String; Normalize : Boolean = false) : String;
|
|
Class Function AddClasses(const Source, aClasses : String; Normalize : Boolean = false) : String;
|
|
Class Function AddClasses(el : TJSHTMLElement; const aClasses : String; Normalize : Boolean = false) : String;
|
|
Class Function AddRemoveClasses(el : TJSHTMLElement; const aAddClasses, aRemoveClasses : String; Normalize : Boolean = false) : String;
|
|
// Number of classes in search and replace must match.
|
|
Class Function ReplaceClasses(el : TJSHTMLElement; const aSearchClasses, aReplaceClasses : String; Normalize : Boolean = false) : String;
|
|
// Manipulate styles
|
|
function EnsureStyle(const aName: String): TStyleItem;
|
|
function AddStyle(const aName,aValue: String): TStyleItem;
|
|
function GetStyleValue(const aName : String) : String;
|
|
function RemoveStyle(const aName: String): String;
|
|
// Remove data from dataset
|
|
Procedure RemoveData(const aName : String);
|
|
// Set attributes
|
|
// Re-render
|
|
Procedure Refresh;
|
|
// Unrender
|
|
Procedure Unrender; overload;
|
|
// Focus widget. Will render if it was not yet rendered.
|
|
Procedure Focus;
|
|
// These work on the classes property, and on the current element if rendered. Returns the new value of classes.
|
|
Function AddRemoveClasses(const aAddClasses, aRemoveClasses : String; Normalize : Boolean = false) : String;
|
|
Function ReplaceClasses(const aSearchClasses, aReplaceClasses : String; Normalize : Boolean = false) : String;
|
|
Function RemoveClasses(const aClasses : String; Normalize : Boolean = false) : String;
|
|
Function AddClasses(const aClasses : String; Normalize : Boolean = false) : String;
|
|
// Finding widgets
|
|
Function FindWidgetByID(aElementID : String; Recurse : Boolean = True) : TCustomWebWidget;
|
|
Function GetWidgetByID(aElementID : String; Recurse : Boolean = True) : TCustomWebWidget;
|
|
|
|
// For complex HTML, this is the toplevel element
|
|
Property Parent : TCustomWebWidget Read GetParent Write SetParent;
|
|
// Are we rendered, i.e. is Element valid ?
|
|
Property IsRendered : Boolean Read GetRendered;
|
|
// Do we need to refresh our internal properties from the element? Currently true if rendered.
|
|
// Use this when reading properties and you want/need to refresh a property from the element.
|
|
Property IsElementDirty : Boolean Read GetIsElementDirty;
|
|
// Child widgets. Note that this can differ significantly from
|
|
Property ChildCount : Integer Read GetChildCount;
|
|
Property Children [aIndex : Integer] : TCustomWebWidget Read GetChild;
|
|
Property Data[aName : String] : String Read GetDataset Write SetDataset;
|
|
// This works with style Display: none.
|
|
Property Visible : Boolean Read GetVisible Write SetVisible;
|
|
// Is the ElementID Set
|
|
Property FixedElementID : Boolean Read GetFixedElementID;
|
|
// This protected section can be published in descendents
|
|
Protected
|
|
// Parent or Element ID: Will be used when determining the HTML element when rendering.
|
|
// Only one of the Parent or Element ID can be set.
|
|
Property ParentID : String Read GetParentID Write SetParentID;
|
|
Property ElementID : String Read GetElementID Write SetElementID;
|
|
// When reading, returns the actual classes if rendered.
|
|
// When rendering, these classes are added to any existing ones if the element exists.
|
|
Property Classes : String Read GetClasses Write SetClasses;
|
|
// Apply these styles when rendering. Depending on StyleRefresh, styles are imported from actual element.
|
|
Property Styles: TWebWidgetStyles Read FStyles Write SetStyles;
|
|
// When rendering, should we refresh styles ?
|
|
Property StyleRefresh : TStyleRefresh Read FStyleRefresh Write FStyleRefresh;
|
|
// Possible references to sub widgets, based on CSS selectors
|
|
Property References : TWebWidgetReferences Read GetReferences write SetReferences;
|
|
// Direct named acces
|
|
Property Reference[aName : String] : TReferenceItem Read GetReferenceItem;
|
|
// Easy access to an element
|
|
Property Elements[const aName : string] : TJSHTMLElement Read GetReference;
|
|
// Easy access to an element list
|
|
Property ElementList[const aName : string] : TJSHTMLElementArray Read GetReferenceList;
|
|
// Easy access to attributed
|
|
Property Attrs[const aName : string] : String Read GetAttr Write SetAttr;
|
|
// Events of TWebWidget
|
|
Property BeforeRenderHTML : TNotifyEvent Read FBeforeRenderHTML Write FBeforeRenderHTML;
|
|
Property AfterRenderHTML : TNotifyEvent Read FAfterRenderHTML Write FAfterRenderHTML;
|
|
Property BeforeUnRenderHTML : TNotifyEvent Read FBeforeUnRenderHTML Write FBeforeUnRenderHTML;
|
|
Property AfterUnRenderHTML : TNotifyEvent Read FAfterUnRenderHTML Write FAfterUnRenderHTML;
|
|
// HTML DOM events
|
|
Property OnAbort: THTMLNotifyEvent Index 0 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnAnimationCancel: THTMLNotifyEvent Index 1 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnAnimationEnd: THTMLNotifyEvent Index 2 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnAnimationIteration: THTMLNotifyEvent Index 3 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnAnimationStart: THTMLNotifyEvent Index 4 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnAuxClick : THTMLNotifyEvent Index 5 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnBlur : THTMLNotifyEvent Index 6 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnCancel : THTMLNotifyEvent Index 7 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnCanPlay : THTMLNotifyEvent Index 8 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnCanPlayThrough : THTMLNotifyEvent Index 9 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnChange : THTMLNotifyEvent Index 10 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnClick : THTMLNotifyEvent Index 11 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnCompositionEnd : THTMLNotifyEvent Index 12 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnCompositionStart : THTMLNotifyEvent Index 13 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnCompositionUpdate : THTMLNotifyEvent Index 14 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnContextMenu : THTMLNotifyEvent Index 15 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnCopy : THTMLNotifyEvent Index 16 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnCut : THTMLNotifyEvent Index 17 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnCueChange : THTMLNotifyEvent Index 18 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnDblClick : THTMLNotifyEvent Index 19 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnDurationChange : THTMLNotifyEvent Index 20 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnEnded : THTMLNotifyEvent Index 21 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnError : THTMLNotifyEvent Index 22 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnFocus : THTMLNotifyEvent Index 23 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnFocusIn : THTMLNotifyEvent Index 24 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnFocusOut : THTMLNotifyEvent Index 25 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnGotPointerCapture : THTMLNotifyEvent Index 26 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnInput : THTMLNotifyEvent Index 27 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnInvalid : THTMLNotifyEvent Index 28 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnKeyDown : THTMLNotifyEvent Index 29 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnKeyPress : THTMLNotifyEvent Index 30 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnKeyUp : THTMLNotifyEvent Index 31 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnLoad : THTMLNotifyEvent Index 32 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnLoadedData : THTMLNotifyEvent Index 33 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnLoadedMetaData : THTMLNotifyEvent Index 34 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnLoadend : THTMLNotifyEvent Index 35 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnLoadStart : THTMLNotifyEvent Index 36 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnLostPointerCapture : THTMLNotifyEvent Index 37 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnMouseDown : THTMLNotifyEvent Index 38 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnMouseEnter : THTMLNotifyEvent Index 39 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnMouseLeave : THTMLNotifyEvent Index 40 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnMouseMove : THTMLNotifyEvent Index 41 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnMouseOut : THTMLNotifyEvent Index 42 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnMouseUp : THTMLNotifyEvent Index 43 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnOverFlow : THTMLNotifyEvent Index 44 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnPaste : THTMLNotifyEvent Index 45 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnPause : THTMLNotifyEvent Index 46 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnPlay : THTMLNotifyEvent Index 47 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnPointerCancel : THTMLNotifyEvent Index 48 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnPointerDown : THTMLNotifyEvent Index 49 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnPointerEnter : THTMLNotifyEvent Index 50 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnPointerLeave : THTMLNotifyEvent Index 51 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnPointerMove : THTMLNotifyEvent Index 52 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnPointerOut : THTMLNotifyEvent Index 53 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnPointerOver : THTMLNotifyEvent Index 54 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnPointerUp : THTMLNotifyEvent Index 55 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnReset : THTMLNotifyEvent Index 56 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnResize : THTMLNotifyEvent Index 57 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnScroll : THTMLNotifyEvent Index 58 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnSelect : THTMLNotifyEvent Index 59 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnSubmit : THTMLNotifyEvent Index 60 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnTouchStart : THTMLNotifyEvent Index 61 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnTransitionCancel : THTMLNotifyEvent Index 62 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnTransitionEnd : THTMLNotifyEvent Index 63 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnTransitionRun : THTMLNotifyEvent Index 64 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnTransitionStart : THTMLNotifyEvent Index 65 Read GetHTMLEvent Write SetHTMLEvent;
|
|
Property OnWheel : THTMLNotifyEvent Index 66 Read GetHTMLEvent Write SetHTMLEvent;
|
|
end;
|
|
TCustomWebWidgetClass = Class of TCustomWebWidget;
|
|
|
|
{ TWebWidget }
|
|
|
|
TWebWidget = Class(TCustomWebWidget)
|
|
Published
|
|
// Properties
|
|
Property ParentID;
|
|
Property ElementID;
|
|
Property Classes;
|
|
Property Styles;
|
|
Property StyleRefresh;
|
|
Property Visible;
|
|
// Events
|
|
Property BeforeRenderHTML;
|
|
Property AfterRenderHTML;
|
|
Property OnAbort;
|
|
Property OnAnimationCancel;
|
|
Property OnAnimationEnd;
|
|
Property OnAnimationIteration;
|
|
Property OnAnimationStart;
|
|
Property OnAuxClick;
|
|
Property OnBlur;
|
|
Property OnCancel;
|
|
Property OnCanPlay;
|
|
Property OnCanPlayThrough;
|
|
Property OnChange;
|
|
Property OnClick;
|
|
Property OnCompositionEnd;
|
|
Property OnCompositionStart;
|
|
Property OnCompositionUpdate;
|
|
Property OnContextMenu;
|
|
Property OnCopy;
|
|
Property OnCut;
|
|
Property OnCueChange;
|
|
Property OnDblClick;
|
|
Property OnDurationChange;
|
|
Property OnEnded ;
|
|
Property OnError ;
|
|
Property OnFocus;
|
|
Property OnFocusIn ;
|
|
Property OnFocusOut ;
|
|
Property OnGotPointerCapture;
|
|
Property OnInput;
|
|
Property OnInvalid;
|
|
Property OnKeyDown;
|
|
Property OnKeyPress;
|
|
Property OnKeyUp;
|
|
Property OnLoad;
|
|
Property OnLoadedData;
|
|
Property OnLoadedMetaData;
|
|
Property OnLoadend;
|
|
Property OnLoadStart;
|
|
Property OnLostPointerCapture;
|
|
Property OnMouseDown;
|
|
Property OnMouseEnter;
|
|
Property OnMouseLeave;
|
|
Property OnMouseMove;
|
|
Property OnMouseOut;
|
|
Property OnMouseUp;
|
|
Property OnOverFlow;
|
|
Property OnPaste;
|
|
Property OnPause;
|
|
Property OnPlay;
|
|
Property OnPointerCancel;
|
|
Property OnPointerDown;
|
|
Property OnPointerEnter;
|
|
Property OnPointerLeave;
|
|
Property OnPointerMove;
|
|
Property OnPointerOut;
|
|
Property OnPointerOver;
|
|
Property OnPointerUp;
|
|
Property OnReset;
|
|
Property OnResize;
|
|
Property OnScroll;
|
|
Property OnSelect;
|
|
Property OnSubmit;
|
|
Property OnTouchStart;
|
|
Property OnTransitionCancel;
|
|
Property OnTransitionEnd;
|
|
Property OnTransitionRun;
|
|
Property OnTransitionStart;
|
|
Property OnWheel;
|
|
end;
|
|
TWebWidgetClass = Class of TWebWidget;
|
|
|
|
{ TContainerWidget }
|
|
|
|
TContainerWidget = Class(TWebWidget)
|
|
private
|
|
const KnownStyleCount = 6;
|
|
Const KnownStyles : Array[0..KnownStyleCount] of string = ('min-width','max-width','min-height','max-height','display','width','height');
|
|
function GetKnownStyle(AIndex: Integer): String;
|
|
procedure SetKnownStyle(AIndex: Integer; AValue: String);
|
|
Public
|
|
Function HTMLTag : String; override;
|
|
Constructor Create(aOwner : TComponent); override;
|
|
Property MinWidth : String Index 0 Read GetKnownStyle Write SetKnownStyle;
|
|
Property MaxWidth : String Index 1 Read GetKnownStyle Write SetKnownStyle;
|
|
Property MinHeight : String Index 2 Read GetKnownStyle Write SetKnownStyle;
|
|
Property MaxHeight : String Index 3 Read GetKnownStyle Write SetKnownStyle;
|
|
Property Display : String Index 4 Read GetKnownStyle Write SetKnownStyle;
|
|
Property Width : String Index 5 Read GetKnownStyle Write SetKnownStyle;
|
|
Property Height : String Index 6 Read GetKnownStyle Write SetKnownStyle;
|
|
end;
|
|
|
|
{ TCustomTemplateWidget }
|
|
|
|
TCustomTemplateWidget = Class(TWebWidget)
|
|
private
|
|
FContainerTag: String;
|
|
procedure SetContainerTag(AValue: String);
|
|
Protected
|
|
function GetReferenceElement: TJSHTMLELement; override;
|
|
function GetTemplateHTML: String; virtual; abstract;
|
|
Procedure ApplyTemplate(aElement : TJSHTMLElement); virtual;
|
|
Function DoRenderHTML(aParent, aElement: TJSHTMLElement) : TJSHTMLElement; override;
|
|
// When set, a tag will be created and the template will be rendered below this tag.
|
|
Property ContainerTag : String Read FContainerTag Write SetContainerTag;
|
|
Public
|
|
Function HTMLTag : String; override;
|
|
end;
|
|
|
|
{ TSimpleTemplateWidget }
|
|
|
|
TSimpleTemplateWidget = Class(TCustomTemplateWidget)
|
|
Private
|
|
FTemplate: String;
|
|
procedure SetTemplate(AValue: String);
|
|
Protected
|
|
function GetTemplateHTML: String; override;
|
|
Public
|
|
Property Reference;
|
|
Property References;
|
|
Property Elements;
|
|
Property ElementList;
|
|
Published
|
|
// The template.
|
|
Property Template : String Read FTemplate Write SetTemplate;
|
|
Property ContainerTag;
|
|
end;
|
|
|
|
|
|
TTemplateWidget = Class(TCustomTemplateWidget)
|
|
Private
|
|
FTemplate: TStrings;
|
|
procedure DoTemplateChanged(Sender: TObject);
|
|
procedure SetTemplate(AValue: TStrings);
|
|
Protected
|
|
function GetTemplateHTML: String; override;
|
|
Public
|
|
Constructor Create(aOwner : TComponent); override;
|
|
Destructor Destroy; override;
|
|
Property Reference;
|
|
Property References;
|
|
Property Elements;
|
|
Property ElementList;
|
|
Published
|
|
// The template.
|
|
Property Template : TStrings Read FTemplate Write SetTemplate;
|
|
Property ContainerTag;
|
|
end;
|
|
|
|
{ TCustomLoopTemplateWidget }
|
|
|
|
{ TLoopTemplateValue }
|
|
|
|
TLoopTemplateValue = Class
|
|
Public
|
|
Index : Integer;
|
|
Name : String;
|
|
Value : String;
|
|
end;
|
|
TGetLoopTemplateValueEvent = Procedure (Sender : TObject; aValue : TLoopTemplateValue) of object;
|
|
|
|
{ TLoopTemplateGroup }
|
|
|
|
TLoopTemplateGroup = class(TCollectionItem)
|
|
private
|
|
FGroupValue : string;
|
|
FFooterTemplate: String;
|
|
FHeaderTemplate: String;
|
|
FName: string;
|
|
procedure SetFooterTemplate(AValue: String);
|
|
procedure SetHeaderTemplate(AValue: String);
|
|
procedure SetName(AValue: string);
|
|
Public
|
|
function GetDisplayName: string; override;
|
|
Procedure Assign(Source: TPersistent); override;
|
|
Published
|
|
Property Name : string Read FName Write SetName;
|
|
Property HeaderTemplate : String Read FHeaderTemplate Write SetHeaderTemplate;
|
|
Property FooterTemplate : String Read FFooterTemplate Write SetFooterTemplate;
|
|
end;
|
|
|
|
{ TLoopTemplateGroupList }
|
|
|
|
TLoopTemplateGroupList = class(TOwnedCollection)
|
|
private
|
|
function GetG(aIndex : Integer): TLoopTemplateGroup;
|
|
procedure SetG(aIndex : Integer; AValue: TLoopTemplateGroup);
|
|
Protected
|
|
procedure Update(Item: TCollectionItem); override;
|
|
Public
|
|
Function IndexOfGroup(const aName : string) : Integer;
|
|
Function FindGroup(const aName : string) : TLoopTemplateGroup;
|
|
Function GetGroup(const aName : string) : TLoopTemplateGroup;
|
|
Function AddGroup(Const aName,aHeader,aFooter : String) : TLoopTemplateGroup;
|
|
Property Groups [aIndex : Integer] : TLoopTemplateGroup Read GetG Write SetG; default;
|
|
end;
|
|
|
|
TCustomLoopTemplateWidget = Class(TCustomTemplateWidget)
|
|
Private
|
|
FFooter: String;
|
|
FGroups: TLoopTemplateGroupList;
|
|
FHeader: String;
|
|
FOnGetValue: TGetLoopTemplateValueEvent;
|
|
FTemplate: String;
|
|
procedure SetFooter(AValue: String);
|
|
procedure SetGroups(AValue: TLoopTemplateGroupList);
|
|
procedure SetHeader(AValue: String);
|
|
procedure SetTemplate(AValue: String);
|
|
Protected
|
|
Type
|
|
{ TLoopEnumerator }
|
|
TLoopEnumerator = Class
|
|
private
|
|
FGroup : TLoopTemplateGroup;
|
|
FWidget: TCustomLoopTemplateWidget;
|
|
FIndex : Integer;
|
|
FCurrValues : TLoopTemplateValue;
|
|
public
|
|
constructor Create(AWidget : TCustomLoopTemplateWidget; aValues: TLoopTemplateValue); reintroduce; virtual;
|
|
destructor destroy; override;
|
|
Function GetValue(Const aName : String): String; virtual;
|
|
function MoveNext: Boolean; virtual;
|
|
Property Widget: TCustomLoopTemplateWidget Read FWidget;
|
|
Property Index : Integer Read FIndex;
|
|
Property CurrValues : TLoopTemplateValue Read FCurrValues;
|
|
end;
|
|
Protected
|
|
// Template support
|
|
function GetTemplateHTML: String; override;
|
|
Function RenderTemplate(aEnum : TLoopEnumerator; aTemplate : String) : String; virtual;
|
|
// Grouping support
|
|
function RenderGroupFooters(aStartIndex: Integer; aEnum: TLoopEnumerator): String; virtual;
|
|
function RenderGroupHeaders(aEnum: TLoopEnumerator): String; virtual;
|
|
function GetGroupValue(enum: TLoopEnumerator; aGroupIndex: Integer; aGroup: TLoopTemplateGroup): String; virtual;
|
|
// Create all kinds of helper classes
|
|
Class Function CreateGroups(aOwner : TComponent) : TLoopTemplateGroupList; virtual;
|
|
Class Function CreateCurrValues : TLoopTemplateValue; virtual;
|
|
Function CreateLoopEnumerator (aCurrValues : TLoopTemplateValue) : TLoopEnumerator; virtual; abstract;
|
|
Public
|
|
Constructor Create(aOwner : TComponent); override;
|
|
Destructor Destroy; override;
|
|
Property Elements;
|
|
Property ElementList;
|
|
Protected
|
|
// The templates.
|
|
Property Groups : TLoopTemplateGroupList Read FGroups Write SetGroups;
|
|
Property HeaderTemplate : String Read FHeader Write SetHeader;
|
|
Property ItemTemplate : String Read FTemplate Write SetTemplate;
|
|
Property FooterTemplate : String Read FFooter Write SetFooter;
|
|
Property OnGetValue : TGetLoopTemplateValueEvent Read FOnGetValue Write FOnGetValue;
|
|
end;
|
|
|
|
{ TSimpleLoopTemplateWidget }
|
|
|
|
{ TSimpleLoopTemplateGroup }
|
|
|
|
TSimpleLoopTemplateGroup = Class(TLoopTemplateGroup)
|
|
private
|
|
FGroupValueTemplate: String;
|
|
procedure SetGroupValueTemplate(AValue: String);
|
|
Published
|
|
Property GroupValueTemplate : String Read FGroupValueTemplate Write SetGroupValueTemplate;
|
|
end;
|
|
|
|
TSimpleLoopTemplateWidget = Class(TCustomLoopTemplateWidget)
|
|
private
|
|
FItemCount: Integer;
|
|
FOnGetGroupValue: TGetLoopTemplateValueEvent;
|
|
procedure SetItemCount(AValue: Integer);
|
|
Protected
|
|
Type
|
|
{ TSimpleLoopEnumerator }
|
|
TSimpleLoopEnumerator = Class(TLoopEnumerator)
|
|
private
|
|
FMaxCount: Integer;
|
|
public
|
|
function MoveNext: Boolean; override;
|
|
Property MaxCount : Integer Read FMaxCount;
|
|
end;
|
|
Protected
|
|
Function CreateLoopEnumerator(aCurrValues : TLoopTemplateValue) : TLoopEnumerator; override;
|
|
function GetGroupValue(aEnum: TLoopEnumerator; aGroupIndex: Integer; aGroup: TLoopTemplateGroup): String; override;
|
|
Class Function CreateGroups(aOwner : TComponent) : TLoopTemplateGroupList; override;
|
|
Published
|
|
Property ItemCount : Integer Read FItemCount Write SetItemCount;
|
|
Property Groups;
|
|
Property HeaderTemplate;
|
|
Property ItemTemplate;
|
|
Property FooterTemplate;
|
|
Property OnGetValue;
|
|
Property OnGetGroupValue : TGetLoopTemplateValueEvent Read FOnGetGroupValue Write FOnGetGroupValue;
|
|
Property References;
|
|
Property ContainerTag;
|
|
end;
|
|
|
|
|
|
{ TListLoopTemplateWidget }
|
|
|
|
TListLoopTemplateWidget = Class(TCustomLoopTemplateWidget)
|
|
Public
|
|
Type
|
|
TListKind = (lkCollection,lkFPList,lkList,lkObjectArray,lkJSArray);
|
|
TValueMode = (vmRTTI,vmProperty);
|
|
Const
|
|
ListKindNames : Array[TlistKind] of string = ('Collection','FPList','List','ObjectArray','JSArray');
|
|
private
|
|
FListKind: TListKind;
|
|
FList : JSValue;
|
|
procedure CheckList(aKind: TListKind);
|
|
function GetCollection: TCollection;
|
|
function GetFPList: TFPList;
|
|
function GetJSArray: TJSArray;
|
|
function GetList: TList;
|
|
function GetObjectArray: TObjectDynArray;
|
|
function GetValueMode: TValueMode;
|
|
procedure SetCollection(AValue: TCollection);
|
|
procedure SetFPList(AValue: TFPList);
|
|
procedure SetJSArray(AValue: TJSArray);
|
|
procedure SetList(AValue: TList);
|
|
procedure SetObjectArray(AValue: TObjectDynArray);
|
|
Protected
|
|
Type
|
|
{ TListLoopEnumerator }
|
|
TListLoopEnumerator = Class(TLoopEnumerator)
|
|
private
|
|
FArray : TJSArray;
|
|
FCurrent : JSValue;
|
|
public
|
|
function MoveNext: Boolean; override;
|
|
Property Current : JSValue Read FCurrent;
|
|
end;
|
|
{ TRTTIListLoopEnumerator }
|
|
TRTTIListLoopEnumerator = Class(TListLoopEnumerator)
|
|
public
|
|
function GetValue(const aName: String): String; override;
|
|
end;
|
|
{ TPropListLoopEnumerator }
|
|
TPropListLoopEnumerator = Class(TListLoopEnumerator)
|
|
public
|
|
function GetValue(const aName: String): String; override;
|
|
end;
|
|
Protected
|
|
// Return JS array from list
|
|
Function GetArray : TJSArray;virtual;
|
|
Function CreateLoopEnumerator(aCurrValues : TLoopTemplateValue) : TLoopEnumerator; override;
|
|
Public
|
|
Property Collection : TCollection Read GetCollection Write SetCollection;
|
|
Property FPList : TFPList Read GetFPList Write SetFPList;
|
|
Property List : TList Read GetList Write SetList;
|
|
Property ObjectArray : TObjectDynArray Read GetObjectArray Write SetObjectArray;
|
|
Property JSArray : TJSArray Read GetJSArray Write SetJSArray;
|
|
// What kind of list do we have
|
|
Property ListKind : TListKind Read FListKind;
|
|
// How do we get values from the objects.
|
|
Property ValueMode : TValueMode Read GetValueMode;
|
|
Published
|
|
Property HeaderTemplate;
|
|
Property ItemTemplate;
|
|
Property FooterTemplate;
|
|
Property OnGetValue;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses Strutils, TypInfo;
|
|
|
|
ResourceString
|
|
SErrCannotSetParentAndElementID = 'ElementID and ParentID cannot be set at the same time.';
|
|
SErrCannotRenderWithoutParent = '%s: Cannot render without parent';
|
|
SErrInvalidChildIndex = 'Invalid child index: value %d is not in valid range [0..%d]';
|
|
SErrUnknownStyle = 'Unknown style: %s';
|
|
SErrElementIDNotAllowed = '%s: Setting element ID is not allowed';
|
|
SErrParentIDNotAllowed = '%s: Setting parent ID is not allowed';
|
|
SErrParentNotAllowed = '%s: Setting parent is not allowed';
|
|
SErrChildrenNotAllowed = '%s: Parent does not allow children';
|
|
SErrWidgetNotFound = 'Widget with ID "%s" not found.';
|
|
SErrUnknownReference = 'Unknown reference: %s';
|
|
SErrNoElement = '%s: Element reference is empty: "%s"';
|
|
SErrNotRendered = 'Cannot perform this operation: Widget not rendered';
|
|
SErrCannotRefreshNoWidget = 'Cannot refresh references without widget';
|
|
SErrNotInput = 'Reference %s is not an input element';
|
|
SErrListNotA = '%s: List is not a %';
|
|
SErrUnknownTemplateGroup = 'Unknown template group item: "%s"';
|
|
SErrDuplicateTemplateGroup = 'Duplicate template group item: "%s"';
|
|
SErrNoGroupInNonGroupTemplate = 'Group name can only be used group value template';
|
|
|
|
{ TSimpleLoopTemplateGroup }
|
|
|
|
procedure TSimpleLoopTemplateGroup.SetGroupValueTemplate(AValue: String);
|
|
begin
|
|
if FGroupValueTemplate=AValue then Exit;
|
|
FGroupValueTemplate:=AValue;
|
|
Changed(False);
|
|
end;
|
|
|
|
{ TLoopTemplateGroupList }
|
|
|
|
function TLoopTemplateGroupList.GetG(aIndex : Integer): TLoopTemplateGroup;
|
|
begin
|
|
Result:=TLoopTemplateGroup(Items[aIndex])
|
|
end;
|
|
|
|
procedure TLoopTemplateGroupList.SetG(aIndex : Integer; AValue: TLoopTemplateGroup);
|
|
begin
|
|
Items[aIndex]:=aValue;
|
|
end;
|
|
|
|
procedure TLoopTemplateGroupList.Update(Item: TCollectionItem);
|
|
begin
|
|
inherited Update(Item);
|
|
if Owner is TCustomLoopTemplateWidget then
|
|
With TCustomLoopTemplateWidget(Owner) do
|
|
if IsRendered then
|
|
Refresh;
|
|
end;
|
|
|
|
function TLoopTemplateGroupList.IndexOfGroup(const aName: string): Integer;
|
|
begin
|
|
Result:=Count-1;
|
|
While (Result>=0) and Not SameText(GetG(Result).Name,aName) do
|
|
Dec(Result);
|
|
end;
|
|
|
|
function TLoopTemplateGroupList.FindGroup(const aName: string): TLoopTemplateGroup;
|
|
|
|
Var
|
|
Idx: Integer;
|
|
|
|
begin
|
|
Idx:=IndexOfGroup(aName);
|
|
if (Idx=-1) then
|
|
Result:=Nil
|
|
else
|
|
Result:=GetG(Idx);
|
|
end;
|
|
|
|
function TLoopTemplateGroupList.GetGroup(const aName: string): TLoopTemplateGroup;
|
|
begin
|
|
Result:=FindGroup(aName);
|
|
if (Result=Nil) then
|
|
raise EWidgets.CreateFmt(SErrUnknownTemplateGroup, [aName]);
|
|
end;
|
|
|
|
function TLoopTemplateGroupList.AddGroup(const aName, aHeader, aFooter: String): TLoopTemplateGroup;
|
|
begin
|
|
if IndexOfGroup(aName)<>-1 then
|
|
raise EWidgets.CreateFmt(SErrDuplicateTemplateGroup, [aName]);
|
|
Result:=add as TLoopTemplateGroup;
|
|
Result.Name:=aName;
|
|
Result.FFooterTemplate:=aFooter;
|
|
Result.HeaderTemplate:=aHeader;
|
|
end;
|
|
|
|
{ TLoopTemplateGroup }
|
|
|
|
procedure TLoopTemplateGroup.SetFooterTemplate(AValue: String);
|
|
begin
|
|
if FFooterTemplate=AValue then Exit;
|
|
FFooterTemplate:=AValue;
|
|
Changed(False);
|
|
end;
|
|
|
|
procedure TLoopTemplateGroup.SetHeaderTemplate(AValue: String);
|
|
begin
|
|
if FHeaderTemplate=AValue then Exit;
|
|
FHeaderTemplate:=AValue;
|
|
Changed(False);
|
|
end;
|
|
|
|
procedure TLoopTemplateGroup.SetName(AValue: string);
|
|
begin
|
|
if FName=AValue then Exit;
|
|
if Assigned(Collection) then
|
|
if TLoopTemplateGroupList(Collection).IndexOfGroup(aValue)<>-1 then
|
|
raise EWidgets.CreateFmt(SErrDuplicateTemplateGroup, [aValue]);
|
|
FName:=AValue;
|
|
end;
|
|
|
|
function TLoopTemplateGroup.GetDisplayName: string;
|
|
begin
|
|
Result:=Name;
|
|
if Result='' then
|
|
Result:=Inherited GetDisplayName;
|
|
end;
|
|
|
|
procedure TLoopTemplateGroup.Assign(Source: TPersistent);
|
|
|
|
Var
|
|
G : TLoopTemplateGroup;
|
|
|
|
begin
|
|
if Source is TLoopTemplateGroup then
|
|
begin
|
|
G:=Source as TLoopTemplateGroup;
|
|
FName:=G.Name;
|
|
FHeaderTemplate:=G.HeaderTemplate;
|
|
FFooterTemplate:=G.FooterTemplate;
|
|
end
|
|
else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
{ TListLoopTemplateWidget.TPropListLoopEnumerator }
|
|
|
|
function TListLoopTemplateWidget.TPropListLoopEnumerator.GetValue(const aName: String): String;
|
|
|
|
Var
|
|
V : JSValue;
|
|
|
|
begin
|
|
V:=TJSObject(Current)[aName];
|
|
if IsDefined(V) then
|
|
Result:=String(V);
|
|
end;
|
|
|
|
{ TListLoopTemplateWidget.TRTTIListLoopEnumerator }
|
|
|
|
function TListLoopTemplateWidget.TRTTIListLoopEnumerator.GetValue(const aName: String): String;
|
|
|
|
Const
|
|
AllowedTypes = [tkInteger,tkChar, tkString, tkEnumeration, tkSet,tkDouble, tkBool, tkJSValue];
|
|
|
|
Var
|
|
O : TObject;
|
|
MP : TTypeMemberProperty;
|
|
|
|
begin
|
|
O:=TObject(Current);
|
|
MP:=GetPropInfo(O,aName,AllowedTypes);
|
|
if Assigned(Mp) then
|
|
case MP.TypeInfo.Kind of
|
|
tkInteger : Result:=IntToStr(GetOrdProp(O,MP));
|
|
tkChar,
|
|
tkString : Result:=GetStrProp(O,MP);
|
|
tkEnumeration : Result:=GetEnumName(TTypeInfoEnum(MP.TypeInfo),GetOrdProp(O,MP));
|
|
tkSet : Result:=GetSetProp(O,MP);
|
|
tkDouble : Result:=FloatToStr(GetFloatProp(O,MP));
|
|
tkBool : Result:=BoolToStr(GetBoolProp(O,MP));
|
|
tkJSValue : Result:=String(GetJSValueProp(O,MP));
|
|
end;
|
|
end;
|
|
|
|
{ TListLoopTemplateWidget.TListLoopEnumerator }
|
|
|
|
function TListLoopTemplateWidget.TListLoopEnumerator.MoveNext: Boolean;
|
|
begin
|
|
Result:=Index<FArray.Length-1;
|
|
Result:=inherited MoveNext;
|
|
if Result then
|
|
FCurrent:=FArray[Index];
|
|
end;
|
|
|
|
{ TListLoopTemplateWidget }
|
|
|
|
|
|
procedure TListLoopTemplateWidget.CheckList(aKind: TListKind);
|
|
|
|
begin
|
|
If FListKind<>aKind then
|
|
Raise EWidgets.CreateFmt(SErrListNotA,[DisplayElementName,ListKindNames[aKind]]);
|
|
end;
|
|
|
|
function TListLoopTemplateWidget.GetCollection: TCollection;
|
|
begin
|
|
CheckList(lkCollection);
|
|
Result:=TCollection(FList)
|
|
end;
|
|
|
|
function TListLoopTemplateWidget.GetFPList: TFPList;
|
|
begin
|
|
CheckList(lkFPList);
|
|
Result:=TFPList(FList)
|
|
end;
|
|
|
|
function TListLoopTemplateWidget.GetJSArray: TJSArray;
|
|
begin
|
|
CheckList(lkJSArray);
|
|
Result:=TJSArray(FList)
|
|
end;
|
|
|
|
function TListLoopTemplateWidget.GetList: TList;
|
|
begin
|
|
CheckList(lkList);
|
|
Result:=TList(FList)
|
|
end;
|
|
|
|
function TListLoopTemplateWidget.GetObjectArray: TObjectDynArray;
|
|
begin
|
|
CheckList(lkObjectArray);
|
|
Result:=TObjectDynArray(FList);
|
|
end;
|
|
|
|
|
|
function TListLoopTemplateWidget.GetValueMode: TValueMode;
|
|
begin
|
|
if ListKind in [lkCollection,lkFPList,lkList,lkObjectArray] then
|
|
Result:=vmRTTI
|
|
else
|
|
Result:=vmProperty;
|
|
end;
|
|
|
|
procedure TListLoopTemplateWidget.SetCollection(AValue: TCollection);
|
|
begin
|
|
Flist:=aValue;
|
|
FListKind:=lkCollection;
|
|
end;
|
|
|
|
procedure TListLoopTemplateWidget.SetFPList(AValue: TFPList);
|
|
begin
|
|
Flist:=aValue;
|
|
FListKind:=lkFPList;
|
|
end;
|
|
|
|
procedure TListLoopTemplateWidget.SetJSArray(AValue: TJSArray);
|
|
begin
|
|
FList:=AValue;
|
|
FListKind:=lkJSArray;
|
|
end;
|
|
|
|
procedure TListLoopTemplateWidget.SetList(AValue: TList);
|
|
begin
|
|
Flist:=aValue;
|
|
FListKind:=lkList;
|
|
end;
|
|
|
|
procedure TListLoopTemplateWidget.SetObjectArray(AValue: TObjectDynArray);
|
|
begin
|
|
FListKind:=lkObjectArray;
|
|
FList:=aValue;
|
|
end;
|
|
|
|
|
|
function TListLoopTemplateWidget.GetArray: TJSArray;
|
|
|
|
Var
|
|
I : integer;
|
|
C : TCollection;
|
|
begin
|
|
Case FListKind of
|
|
lkCollection :
|
|
begin
|
|
C:=TCollection(Flist);
|
|
Result:=TJSArray.New(C.Count);
|
|
for I:=0 To C.Count-1 do
|
|
Result[i]:=C.Items[i];
|
|
end;
|
|
lkFPList:
|
|
Result:=TJSArray(TFPList(FList).List);
|
|
lkList:
|
|
Result:=TJSArray(TList(FList).List);
|
|
lkJSArray,
|
|
lkObjectArray :
|
|
Result:=TJSArray(FList);
|
|
end;
|
|
end;
|
|
|
|
function TListLoopTemplateWidget.CreateLoopEnumerator(aCurrValues : TLoopTemplateValue) : TLoopEnumerator;
|
|
|
|
begin
|
|
Case ValueMode of
|
|
vmRTTI : Result:=TRTTIListLoopEnumerator.Create(Self,aCurrValues);
|
|
vmProperty : Result:=TPropListLoopEnumerator.Create(Self,aCurrValues);
|
|
end;
|
|
TListLoopEnumerator(Result).FArray:=GetArray;
|
|
end;
|
|
|
|
{ TSimpleLoopTemplateWidget.TSimpleLoopEnumerator }
|
|
|
|
function TSimpleLoopTemplateWidget.TSimpleLoopEnumerator.MoveNext: Boolean;
|
|
begin
|
|
Result:=(Index<MaxCount-1);
|
|
if Result then
|
|
Inherited MoveNext;
|
|
end;
|
|
|
|
{ TSimpleLoopTemplateWidget }
|
|
|
|
procedure TSimpleLoopTemplateWidget.SetItemCount(AValue: Integer);
|
|
begin
|
|
if FItemCount=AValue then Exit;
|
|
FItemCount:=AValue;
|
|
if IsRendered then
|
|
Refresh;
|
|
end;
|
|
|
|
function TSimpleLoopTemplateWidget.CreateLoopEnumerator(aCurrValues : TLoopTemplateValue) : TLoopEnumerator;
|
|
|
|
Var
|
|
L : TSimpleLoopEnumerator;
|
|
|
|
begin
|
|
L:=TSimpleLoopEnumerator.Create(Self,aCurrValues);
|
|
L.FMaxCount:=Self.ItemCount;
|
|
Result:=L;
|
|
end;
|
|
|
|
function TSimpleLoopTemplateWidget.GetGroupValue(aEnum: TLoopEnumerator; aGroupIndex: Integer; aGroup: TLoopTemplateGroup): String;
|
|
begin
|
|
aEnum.CurrValues.Name:=aGroup.Name;
|
|
if TSimpleLoopTemplateGroup(aGroup).GroupValueTemplate<>'' then
|
|
aEnum.FCurrValues.Value:=RenderTemplate(aEnum,TSimpleLoopTemplateGroup(aGroup).GroupValueTemplate);
|
|
if Assigned(OnGetGroupValue) then
|
|
OnGetGroupValue(Self,aEnum.CurrValues);
|
|
Result:=aEnum.CurrValues.Value;
|
|
// Writeln('Group Value: ',aEnum.CurrValues.Value,' for ',aGroup.Name);
|
|
end;
|
|
|
|
class function TSimpleLoopTemplateWidget.CreateGroups(aOwner: TComponent): TLoopTemplateGroupList;
|
|
begin
|
|
Result:=TLoopTemplateGroupList.Create(aOwner,TSimpleLoopTemplateGroup)
|
|
end;
|
|
|
|
{ TCustomLoopTemplateWidget }
|
|
|
|
procedure TCustomLoopTemplateWidget.SetTemplate(AValue: String);
|
|
begin
|
|
if FTemplate=aValue then exit;
|
|
FTemplate:=aValue;
|
|
if IsRendered then
|
|
Refresh;
|
|
end;
|
|
|
|
Class function TCustomLoopTemplateWidget.CreateGroups(aOwner : TComponent): TLoopTemplateGroupList;
|
|
begin
|
|
Result:=TLoopTemplateGroupList.Create(AOwner,TLoopTemplateGroup);
|
|
end;
|
|
|
|
Class function TCustomLoopTemplateWidget.CreateCurrValues: TLoopTemplateValue;
|
|
begin
|
|
Result:=TLoopTemplateValue.Create;
|
|
end;
|
|
|
|
function TCustomLoopTemplateWidget.RenderTemplate(aEnum: TLoopEnumerator; aTemplate: String): String;
|
|
|
|
Var
|
|
E : TJSRegexp;
|
|
|
|
begin
|
|
E:=TJSRegexp.New('{{([_\w]*)}}','g');
|
|
Result:=TJSString(aTemplate).Replace(E,Function (Const match,p1 : string; offset : Integer; AString : String) : string
|
|
begin
|
|
aEnum.CurrValues.Value:=aEnum.GetValue(p1);
|
|
// Writeln(p1,' -> ',aEnum.CurrValues.Value);
|
|
if Assigned(OnGetValue) then
|
|
begin
|
|
aEnum.CurrValues.Index:=aEnum.Index;
|
|
aEnum.CurrValues.Name:=P1;
|
|
OnGetValue(Self,aEnum.CurrValues);
|
|
end;
|
|
Result:=AEnum.CurrValues.Value;
|
|
end);
|
|
end;
|
|
|
|
procedure TCustomLoopTemplateWidget.SetFooter(AValue: String);
|
|
begin
|
|
if FFooter=AValue then Exit;
|
|
FFooter:=AValue;
|
|
if IsRendered then
|
|
Refresh;
|
|
end;
|
|
|
|
procedure TCustomLoopTemplateWidget.SetGroups(AValue: TLoopTemplateGroupList);
|
|
begin
|
|
if FGroups=AValue then Exit;
|
|
FGroups.Assign(AValue);
|
|
if IsRendered then
|
|
Refresh;
|
|
end;
|
|
|
|
procedure TCustomLoopTemplateWidget.SetHeader(AValue: String);
|
|
begin
|
|
if FHeader=AValue then Exit;
|
|
FHeader:=AValue;
|
|
if IsRendered then
|
|
Refresh;
|
|
end;
|
|
|
|
function TCustomLoopTemplateWidget.GetGroupValue(enum : TLoopEnumerator; aGroupIndex : Integer; aGroup : TLoopTemplateGroup) : String;
|
|
|
|
begin
|
|
Result:=aGroup.Name;
|
|
end;
|
|
|
|
function TCustomLoopTemplateWidget.RenderGroupHeaders(aEnum : TLoopEnumerator) : String;
|
|
|
|
Var
|
|
GrpIdx: Integer;
|
|
grp : TLoopTemplateGroup;
|
|
StartGroups : Boolean;
|
|
S,V : String;
|
|
|
|
begin
|
|
// Writeln('Rendering group headers for row ',aEnum.Index);
|
|
Result:='';
|
|
StartGroups:=False;
|
|
For GrpIdx:=0 to Groups.Count-1 do
|
|
begin
|
|
Grp:=Groups[GrpIdx];
|
|
aEnum.FGroup:=Grp;
|
|
V:=GetGroupValue(aEnum,GrpIdx,Grp);
|
|
if Not StartGroups then
|
|
StartGroups:=(aEnum.Index=0) or (V<>Grp.FGroupValue);
|
|
if StartGroups and (aEnum.Index>0) then
|
|
Result:=Result+RenderGroupFooters(grpIdx,aEnum);
|
|
Grp.FGroupValue:=V;
|
|
if StartGroups then
|
|
begin
|
|
S:=RenderTemplate(aEnum,Grp.HeaderTemplate);
|
|
// Writeln('Rendering group ',Grp.Name,' (',V,') header template: ',Grp.HeaderTemplate,' : ',S);
|
|
Result:=Result+S;
|
|
end;
|
|
aEnum.FGroup:=Nil;
|
|
end;
|
|
end;
|
|
|
|
function TCustomLoopTemplateWidget.RenderGroupFooters(aStartIndex : Integer; aEnum : TLoopEnumerator) : String;
|
|
|
|
|
|
Var
|
|
GrpIdx : Integer;
|
|
grp : TLoopTemplateGroup;
|
|
|
|
begin
|
|
Result:='';
|
|
For GrpIdx:=Groups.Count-1 downto aStartIndex do
|
|
begin
|
|
Grp:=Groups[GrpIdx];
|
|
Result:=Result+RenderTemplate(aEnum,Grp.FooterTemplate);
|
|
end;
|
|
end;
|
|
|
|
function TCustomLoopTemplateWidget.GetTemplateHTML: String;
|
|
|
|
Var
|
|
Enum : TLoopEnumerator;
|
|
|
|
begin
|
|
Enum:=CreateLoopEnumerator(CreateCurrValues);
|
|
try
|
|
Result:=RenderTemplate(Enum,HeaderTemplate);
|
|
While enum.MoveNext do
|
|
begin
|
|
if Groups.Count>0 then
|
|
Result:=Result+RenderGroupHeaders(Enum);
|
|
Result:=Result+RenderTemplate(Enum,ItemTemplate);
|
|
end;
|
|
if Groups.Count>0 then
|
|
Result:=Result+RenderGroupFooters(0,Enum);
|
|
Result:=Result+RenderTemplate(Enum,FooterTemplate);
|
|
finally
|
|
Enum.Free;
|
|
end;
|
|
end;
|
|
|
|
constructor TCustomLoopTemplateWidget.Create(aOwner: TComponent);
|
|
begin
|
|
inherited Create(aOwner);
|
|
FGroups:=CreateGroups(Self);
|
|
end;
|
|
|
|
destructor TCustomLoopTemplateWidget.Destroy;
|
|
begin
|
|
FreeAndNil(FGroups);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
|
|
{ TCustomLoopTemplateWidget.TLoopEnumerator }
|
|
|
|
constructor TCustomLoopTemplateWidget.TLoopEnumerator.Create(AWidget: TCustomLoopTemplateWidget; aValues: TLoopTemplateValue);
|
|
begin
|
|
FWidget:=AWidget;
|
|
FCurrValues:=aValues;
|
|
FIndex:=-1;
|
|
end;
|
|
|
|
destructor TCustomLoopTemplateWidget.TLoopEnumerator.destroy;
|
|
begin
|
|
FreeAndNil(FCurrValues);
|
|
inherited destroy;
|
|
end;
|
|
|
|
function TCustomLoopTemplateWidget.TLoopEnumerator.GetValue(const aName: String): String;
|
|
begin
|
|
Case AName of
|
|
'_index_' : Result:=IntToStr(Index);
|
|
'_row_' : Result:=IntToStr(Index+1);
|
|
'_group_' : if Assigned(FGroup) then
|
|
Result:=FGroup.FGroupValue
|
|
else
|
|
Raise EWidgets.Create(SErrNoGroupInNonGroupTemplate);
|
|
else
|
|
Result:='';
|
|
end;
|
|
end;
|
|
|
|
function TCustomLoopTemplateWidget.TLoopEnumerator.MoveNext: Boolean;
|
|
begin
|
|
Result:=True;
|
|
Inc(FIndex);
|
|
FCurrValues.Index:=FIndex;
|
|
end;
|
|
|
|
{ TSimpleTemplateWidget }
|
|
|
|
procedure TSimpleTemplateWidget.SetTemplate(AValue: String);
|
|
|
|
begin
|
|
FTemplate:=AValue;
|
|
if isRendered then
|
|
Refresh;
|
|
end;
|
|
|
|
function TSimpleTemplateWidget.GetTemplateHTML: String;
|
|
begin
|
|
Result:=FTemplate;
|
|
end;
|
|
|
|
|
|
{ TWebWidgetReferences }
|
|
|
|
function TWebWidgetReferences.GetReferenceItem(aIndex : Integer): TReferenceItem;
|
|
begin
|
|
Result:=TReferenceItem(Inherited Items[aIndex])
|
|
end;
|
|
|
|
procedure TWebWidgetReferences.SetReferenceItem(aIndex : Integer; AValue: TReferenceItem);
|
|
begin
|
|
Items[aIndex]:=aValue;
|
|
end;
|
|
|
|
procedure TWebWidgetReferences.MarkDirty(aItem: TReferenceItem);
|
|
begin
|
|
if Assigned(Widget) and Assigned(Widget.Element) then
|
|
RefreshFromDOM(aItem,Widget.GetReferenceElement)
|
|
end;
|
|
|
|
procedure TWebWidgetReferences.RefreshFromDOM(aItem: TReferenceItem; aElement: TJSHTMlElement);
|
|
|
|
Var
|
|
a : TJSHTMlElementArray;
|
|
Nodes : TJSNodeList;
|
|
I : integer;
|
|
|
|
begin
|
|
if aElement=Nil then
|
|
begin
|
|
if (Widget=Nil) then
|
|
Raise EWidgets.Create(SErrCannotRefreshNoWidget);
|
|
if (Widget.Element=Nil) then
|
|
Raise EWidgets.Create(SErrNotRendered);
|
|
aElement:=Widget.GetReferenceElement;
|
|
end;
|
|
if FRefs=Nil then
|
|
FRefs:=New([]);
|
|
try
|
|
Nodes:=aElement.querySelectorAll(aItem.Selector);
|
|
SetLength(a,Nodes.length);
|
|
For I:=0 to Nodes.length-1 do
|
|
A[i]:=TJSHTMLElement(Nodes[i]);
|
|
except
|
|
SetLength(a,0);
|
|
end;
|
|
FRefs[LowerCase(aItem.Name)]:=A;
|
|
end;
|
|
|
|
function TWebWidgetReferences.Widget: TCustomWebWidget;
|
|
begin
|
|
Result:=TCustomWebWidget(owner);
|
|
end;
|
|
|
|
function TWebWidgetReferences.Add(const aName: String; aSelector: String): TReferenceItem;
|
|
begin
|
|
Result:=Add as TReferenceItem;
|
|
Result.FName:=aName;
|
|
Result.Selector:=aSelector;
|
|
if (aSelector<>'') then
|
|
MarkDirty(Result)
|
|
end;
|
|
|
|
function TWebWidgetReferences.EnsureReference(const aName: String; const aSelector: String): TReferenceItem;
|
|
begin
|
|
Result:=FindReference(aName);
|
|
if Result=Nil then
|
|
Result:=Add(aName,aSelector);
|
|
end;
|
|
|
|
function TWebWidgetReferences.IndexOfReference(const aName: String): Integer;
|
|
begin
|
|
Result:=Count-1;
|
|
While (Result>=0) and not SameText(GetReferenceItem(Result).Name,aName) do
|
|
Dec(Result);
|
|
end;
|
|
|
|
function TWebWidgetReferences.FindReference(const aName: String): TReferenceItem;
|
|
|
|
Var
|
|
Idx:Integer;
|
|
|
|
begin
|
|
Idx:=IndexOfReference(aName);
|
|
if Idx=-1 then
|
|
Result:=Nil
|
|
else
|
|
Result:=GetReferenceItem(Idx)
|
|
end;
|
|
|
|
function TWebWidgetReferences.GetReference(const aName: String): TReferenceItem;
|
|
begin
|
|
Result:=FindReference(aName);
|
|
if (Result=Nil) then
|
|
Raise EWidgets.CreateFmt(SErrUnknownReference,[aName]);
|
|
end;
|
|
|
|
procedure TWebWidgetReferences.RemoveReference(const aName: String);
|
|
Var
|
|
Idx:Integer;
|
|
|
|
begin
|
|
Idx:=IndexOfReference(aName);
|
|
if Idx<>-1 then
|
|
Delete(Idx);
|
|
end;
|
|
|
|
function TWebWidgetReferences.ElementExists(const aName: String): Boolean;
|
|
|
|
Var
|
|
V : JSValue;
|
|
begin
|
|
Result:=Assigned(FRefs);
|
|
if Result then
|
|
begin
|
|
V:=FRefs[LowerCase(aName)];
|
|
Result:=Assigned(V);
|
|
end;
|
|
end;
|
|
|
|
function TWebWidgetReferences.ElementIsArray(const aName: String): Boolean;
|
|
Var
|
|
V : JSValue;
|
|
begin
|
|
Result:=Assigned(FRefs);
|
|
if Result then
|
|
begin
|
|
V:=FRefs[LowerCase(aName)];
|
|
Result:=isArray(V) and (TJSArray(V).Length>1);
|
|
end;
|
|
end;
|
|
|
|
function TWebWidgetReferences.FindElementByName(const aName: String): TJSHTMLElement;
|
|
Var
|
|
J : JSValue;
|
|
Arr : TJSArray absolute J;
|
|
|
|
begin
|
|
Result:=Nil;
|
|
if FRefs=Nil then
|
|
exit;
|
|
J:=FRefs[LowerCase(aName)];
|
|
if isArray(J) and (Arr.Length>0) then
|
|
Result:=TJSHTMLElement(Arr[0])
|
|
end;
|
|
|
|
function TWebWidgetReferences.GetElementByName(const aName: String): TJSHTMLElement;
|
|
|
|
begin
|
|
Result:=FindElementByName(aName);
|
|
if Result=Nil then
|
|
Raise EWidgets.CreateFmt(SErrNoElement,[Widget.DisplayElementName,aName]);
|
|
end;
|
|
|
|
function TWebWidgetReferences.GetElementsByName(const aName: String): TJSHTMLElementArray;
|
|
Var
|
|
J : JSValue;
|
|
Arr : TJSArray absolute J;
|
|
|
|
begin
|
|
Result:=Nil;
|
|
if FRefs=Nil then
|
|
exit;
|
|
J:=FRefs[LowerCase(aName)];
|
|
if isArray(J) and (Arr.Length>0) then
|
|
Result:=TJSHTMLElementArray(Arr)
|
|
end;
|
|
|
|
procedure TWebWidgetReferences.RefreshFromDOM(aElement: TJSHTMlElement);
|
|
|
|
Var
|
|
I : Integer;
|
|
|
|
begin
|
|
For I:=0 to Count-1 do
|
|
RefreshFromDOM(GetReferenceItem(I),aElement);
|
|
end;
|
|
|
|
{ TReferenceItem }
|
|
|
|
procedure TReferenceItem.SetName(AValue: String);
|
|
begin
|
|
if FName=AValue then Exit;
|
|
FName:=AValue;
|
|
MarkDirty;
|
|
end;
|
|
|
|
procedure TReferenceItem.SetInputValidity(AValue: String);
|
|
begin
|
|
if IsInput then
|
|
// this is not quite correct, but will work
|
|
TJSHTMLInputElement(Element).setCustomValidity(aValue)
|
|
else
|
|
Raise EWidgets.CreateFmt(SErrNotInput,[Name]);
|
|
end;
|
|
|
|
function TReferenceItem.GetElement: TJSHTMLElement;
|
|
begin
|
|
if Assigned(Collection) then
|
|
Result:=(Collection as TWebWidgetReferences).GetElementByName(Name)
|
|
else
|
|
Result:=Nil;
|
|
end;
|
|
|
|
function TReferenceItem.GetElements: TJSHTMLElementArray;
|
|
begin
|
|
if Assigned(Collection) then
|
|
Result:=(Collection as TWebWidgetReferences).GetElementsByName(Name)
|
|
else
|
|
Result:=Nil;
|
|
end;
|
|
|
|
procedure TReferenceItem.SetInputValue(AValue: String);
|
|
begin
|
|
if IsInput then
|
|
TJSObject(Element)['value']:=aValue
|
|
else
|
|
Raise EWidgets.CreateFmt(SErrNotInput,[Name])
|
|
end;
|
|
|
|
procedure TReferenceItem.MarkDirty;
|
|
begin
|
|
if Assigned(Collection) then
|
|
(Collection as TWebWidgetReferences).MarkDirty(Self);
|
|
end;
|
|
|
|
procedure TReferenceItem.Refresh;
|
|
begin
|
|
MarkDirty;
|
|
end;
|
|
|
|
function TReferenceItem.Exists: Boolean;
|
|
begin
|
|
if Assigned(Collection) then
|
|
Result:=(Collection as TWebWidgetReferences).ElementExists(Name)
|
|
else
|
|
Result:=False;
|
|
end;
|
|
|
|
function TReferenceItem.IsArray: Boolean;
|
|
begin
|
|
if Assigned(Collection) then
|
|
Result:=(Collection as TWebWidgetReferences).ElementIsArray(Name)
|
|
else
|
|
Result:=False;
|
|
end;
|
|
|
|
function TReferenceItem.IsInput: Boolean;
|
|
|
|
Var
|
|
el : TJSHTMLELement;
|
|
|
|
begin
|
|
Result:=Exists and not IsArray;
|
|
if Result then
|
|
begin
|
|
el:=Element;
|
|
Result:= ((el is TJSHTMLInputElement)
|
|
or (el is TJSHTMLTextAreaElement)
|
|
or (el is TJSHTMLSelectElement));
|
|
end;
|
|
end;
|
|
|
|
function TReferenceItem.GetInputValue: String;
|
|
begin
|
|
if IsInput then
|
|
Result:=String(TJSObject(Element)['value'])
|
|
else
|
|
Raise EWidgets.CreateFmt(SErrNotInput,[Name])
|
|
end;
|
|
|
|
procedure TReferenceItem.SetSelector(AValue: String);
|
|
begin
|
|
if FSelector=AValue then Exit;
|
|
FSelector:=AValue;
|
|
MarkDirty;
|
|
end;
|
|
|
|
{ TCustomTemplateWidget }
|
|
|
|
procedure TCustomTemplateWidget.ApplyTemplate(aElement: TJSHTMLElement);
|
|
begin
|
|
aElement.InnerHTML:=GetTemplateHTML;
|
|
end;
|
|
|
|
function TCustomTemplateWidget.DoRenderHTML(aParent, aElement: TJSHTMLElement): TJSHTMLElement;
|
|
begin
|
|
if (ContainerTag='') then
|
|
begin
|
|
ApplyTemplate(AParent);
|
|
Result:= TJSHTMLElement(aParent.firstElementChild);
|
|
end
|
|
else
|
|
begin
|
|
ApplyTemplate(aElement);
|
|
Result:=aElement;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomTemplateWidget.SetContainerTag(AValue: String);
|
|
begin
|
|
if FContainerTag=AValue then Exit;
|
|
FContainerTag:=AValue;
|
|
if IsRendered then
|
|
Refresh;
|
|
end;
|
|
|
|
function TCustomTemplateWidget.GetReferenceElement: TJSHTMLELement;
|
|
begin
|
|
If (ContainerTag='') then
|
|
Result:=ParentElement
|
|
else
|
|
Result:=Element;
|
|
end;
|
|
|
|
function TCustomTemplateWidget.HTMLTag: String;
|
|
begin
|
|
Result:=ContainerTag;
|
|
end;
|
|
|
|
{ TTemplateWidget }
|
|
|
|
procedure TTemplateWidget.SetTemplate(AValue: TStrings);
|
|
begin
|
|
if FTemplate=AValue then Exit;
|
|
FTemplate.Assign(AValue);
|
|
end;
|
|
|
|
function TTemplateWidget.GetTemplateHTML: String;
|
|
begin
|
|
Result:=FTemplate.Text;
|
|
end;
|
|
|
|
|
|
procedure TTemplateWidget.DoTemplateChanged(Sender: TObject);
|
|
begin
|
|
if isRendered then
|
|
Refresh;
|
|
end;
|
|
|
|
constructor TTemplateWidget.Create(aOwner: TComponent);
|
|
|
|
begin
|
|
inherited Create(aOwner);
|
|
FTemplate:=TStringList.Create;
|
|
TStringList(FTemplate).OnChange:=@DoTemplateChanged;
|
|
FContainerTag:='';
|
|
end;
|
|
|
|
destructor TTemplateWidget.Destroy;
|
|
|
|
begin
|
|
FreeAndNil(FTemplate);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ TContainerWidget }
|
|
|
|
function TContainerWidget.GetKnownStyle(AIndex: Integer): String;
|
|
|
|
var
|
|
S : TStyleItem;
|
|
begin
|
|
S:=Styles.FindStyle(KnownStyles[aIndex]);
|
|
if Assigned(S) then
|
|
Result:=S.Value;
|
|
end;
|
|
|
|
procedure TContainerWidget.SetKnownStyle(AIndex: Integer; AValue: String);
|
|
|
|
begin
|
|
Styles.EnsureStyle(KnownStyles[aIndex]).Value:=aValue;
|
|
end;
|
|
|
|
function TContainerWidget.HTMLTag: String;
|
|
begin
|
|
Result:='div';
|
|
end;
|
|
|
|
constructor TContainerWidget.Create(aOwner: TComponent);
|
|
begin
|
|
inherited Create(aOwner);
|
|
MinWidth:='32px';
|
|
MinHeight:='12px';
|
|
Width:='50%';
|
|
Height:='50%';
|
|
end;
|
|
|
|
|
|
|
|
{ TWebWidgetStyles }
|
|
|
|
function TWebWidgetStyles.GetStyleItem(aIndex: Integer): TStyleItem;
|
|
begin
|
|
Result:=TStyleItem(Items[Aindex]);
|
|
end;
|
|
|
|
procedure TWebWidgetStyles.SetStyleItem(aIndex: Integer; AValue: TStyleItem);
|
|
begin
|
|
Items[aIndex]:=aValue;
|
|
end;
|
|
|
|
procedure TWebWidgetStyles.MarkDirty(aItem: TStyleItem);
|
|
|
|
Var
|
|
El : TJSHTMLElement;
|
|
|
|
begin
|
|
If Assigned(Widget) then
|
|
begin
|
|
if Not (csLoading in Widget.ComponentState) then
|
|
begin
|
|
el:=Widget.Element;
|
|
if Assigned(El) then
|
|
ApplyToDom(El,aItem);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TWebWidgetStyles.ApplyToDOM(aElement: TJSHTMlElement; aItem: TStyleItem);
|
|
|
|
Const
|
|
Prios : Array[TStylePriority] of string = ('','important');
|
|
|
|
begin
|
|
With AItem do
|
|
if (Name<>'') then
|
|
if (Value<>'') then
|
|
aElement.Style.setProperty(Name,Value,Prios[Priority])
|
|
else
|
|
aElement.Style.removeProperty(name);
|
|
end;
|
|
|
|
function TWebWidgetStyles.Add(const aName: String; const aValue: String): TStyleItem;
|
|
begin
|
|
Result:=Add as TStyleItem;
|
|
// Don't use Name
|
|
Result.FName:=aName;
|
|
if aValue<>'' then
|
|
Result.Value:=aValue; // will trigger markdirty
|
|
end;
|
|
|
|
function TWebWidgetStyles.EnsureStyle(const aName: String; const aValue: String): TStyleItem;
|
|
begin
|
|
Result:=FindStyle(aName);
|
|
if Result=Nil then
|
|
Result:=Add(aName,aValue)
|
|
else if AValue<>'' then
|
|
Result.Value:=aValue
|
|
end;
|
|
|
|
function TWebWidgetStyles.Widget: TCustomWebWidget;
|
|
begin
|
|
Result:=TCustomWebWidget(Owner);
|
|
end;
|
|
|
|
function TWebWidgetStyles.IndexOfStyle(const aName: String): integer;
|
|
|
|
begin
|
|
Result:=Count-1;
|
|
While (Result>=0) and not SameText(aName,GetStyleItem(Result).Name) do
|
|
Dec(Result);
|
|
end;
|
|
|
|
function TWebWidgetStyles.FindStyle(const aName: String): TStyleItem;
|
|
|
|
Var
|
|
Idx : integer;
|
|
|
|
begin
|
|
Idx:=IndexOfStyle(aName);
|
|
If Idx=-1 then
|
|
Result:=Nil
|
|
else
|
|
Result:=GetStyleItem(Idx)
|
|
end;
|
|
|
|
function TWebWidgetStyles.GetStyle(const aName: String): TStyleItem;
|
|
begin
|
|
Result:=FindStyle(aName);
|
|
if Result=Nil then
|
|
Raise EWidgets.CreateFmt(SErrUnknownStyle,[aName]);
|
|
end;
|
|
|
|
function TWebWidgetStyles.RemoveStyle(const aName: String): String;
|
|
|
|
Var
|
|
I : Integer;
|
|
el : TJSHTMLElement;
|
|
|
|
begin
|
|
I:=IndexOfStyle(aName);
|
|
if I<>-1 then
|
|
begin
|
|
Result:=Styles[i].Value;
|
|
Delete(I);
|
|
end;
|
|
if Assigned(Widget) then
|
|
begin
|
|
el:=Widget.Element;
|
|
if Assigned(el) then
|
|
begin
|
|
if (Result='') then
|
|
Result:=el.style.getPropertyValue(aName);
|
|
el.style.removeProperty(aName);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TWebWidgetStyles.RefreshFromDOM(aElement : TJSHTMlElement = Nil;DoClear: Boolean = False);
|
|
|
|
Var
|
|
S : TJSCSSStyleDeclaration;
|
|
I : integer;
|
|
K : String;
|
|
W : TCustomWebWidget;
|
|
itm : TStyleItem;
|
|
|
|
begin
|
|
if aElement= Nil then
|
|
begin
|
|
W:=Widget;
|
|
if assigned(W) then
|
|
aElement:=W.Element;
|
|
if AElement=Nil then exit;
|
|
end;
|
|
if DoClear then
|
|
Clear;
|
|
S:=aElement.style;
|
|
For I:=0 to S.length-1 do
|
|
begin
|
|
K:=S.Item(I);
|
|
itm:=FindStyle(K);
|
|
if Itm=Nil then
|
|
begin
|
|
Itm:=Add(K);
|
|
Itm.FImported:=True;
|
|
end;
|
|
Itm.FValue:=S.getPropertyValue(K);
|
|
Case LowerCase(S.getPropertyPriority(K)) of
|
|
'important' : Itm.FPriority:=spImportant;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TWebWidgetStyles.ClearImported;
|
|
|
|
Var
|
|
I : integer;
|
|
|
|
begin
|
|
I:=Count-1;
|
|
While I>=0 do
|
|
begin
|
|
If GetStyleItem(I).Fimported then
|
|
Delete(I);
|
|
Dec(I);
|
|
end;
|
|
end;
|
|
|
|
procedure TWebWidgetStyles.ApplyToDOM(aElement : TJSHTMlElement = Nil);
|
|
|
|
Var
|
|
I : Integer;
|
|
|
|
begin
|
|
if (AElement=Nil) and (Widget<>Nil) then
|
|
aElement:=Widget.Element;
|
|
if AElement<>Nil then
|
|
For I:=0 to Count-1 do
|
|
ApplyToDOM(aElement,GetStyleItem(i));
|
|
end;
|
|
|
|
{ TStyleItem }
|
|
|
|
procedure TStyleItem.MarkDirty;
|
|
|
|
begin
|
|
If Assigned(Collection) then
|
|
TWebWidgetStyles(Collection).MarkDirty(Self);
|
|
end;
|
|
|
|
procedure TStyleItem.SetValue(AValue: String);
|
|
begin
|
|
if FValue=AValue then Exit;
|
|
FValue:=aValue;
|
|
MarkDirty;
|
|
end;
|
|
|
|
procedure TStyleItem.SetPriority(AValue: TStylePriority);
|
|
begin
|
|
if FPriority=AValue then Exit;
|
|
FPriority:=AValue;
|
|
MarkDirty;
|
|
end;
|
|
|
|
|
|
procedure TStyleItem.SetName(AValue: String);
|
|
begin
|
|
if aValue=FName then Exit;
|
|
FName:=AValue;
|
|
MarkDirty;
|
|
end;
|
|
|
|
procedure TStyleItem.Assign(Source: TPersistent);
|
|
|
|
Var
|
|
SI : TStyleItem;
|
|
|
|
begin
|
|
if Source is TStyleItem then
|
|
begin
|
|
SI:=Source as TStyleItem;
|
|
FName:=SI.FName;
|
|
FValue:=SI.FValue;
|
|
FImported:=SI.FImported;
|
|
MarkDirty;
|
|
end
|
|
else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
|
|
{ TCustomWebWidget }
|
|
|
|
function TCustomWebWidget.DisplayElementName: String;
|
|
|
|
begin
|
|
Result:=Name;
|
|
If Result='' then
|
|
Result:=' <'+HTMLTag+'>';
|
|
if Assigned(FElement) then
|
|
Result:=Result+'#'+FElement.ID;
|
|
Result:=Result+' (Type: '+ClassName+')';
|
|
end;
|
|
|
|
function TCustomWebWidget.EnsureElement : TJSHTMLElement;
|
|
|
|
var
|
|
P : TJSHTMLElement;
|
|
|
|
begin
|
|
Result:=GetElement;
|
|
if Result=Nil then
|
|
begin
|
|
// If we have a parent, make sure it has it's element
|
|
if Assigned(Parent) then
|
|
Parent.EnsureElement;
|
|
P:=ParentElement;
|
|
if (P=Nil) and (FixedElement=Nil) then
|
|
Raise EWidgets.CreateFmt(SErrCannotRenderWithoutParent,[DisplayElementName])
|
|
else
|
|
begin
|
|
Result:=RenderHTML(P);
|
|
FOwnsElement:=True;
|
|
FElement:=Result;
|
|
end;
|
|
ApplyData;
|
|
RefreshReferences; // After data, so data can be used in selectors
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomWebWidget.InvalidateParentElement;
|
|
|
|
Var
|
|
I : Integer;
|
|
C : TCustomWebWidget;
|
|
|
|
begin
|
|
FParentElement:=nil;
|
|
For I:=0 to ChildCount-1 do
|
|
begin
|
|
C:=Children[i];
|
|
if Assigned(C) then // Can be Nil
|
|
C.InvalidateParentElement;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomWebWidget.InvalidateElement;
|
|
|
|
Var
|
|
I : Integer;
|
|
C : TCustomWebWidget;
|
|
|
|
begin
|
|
If FStyles.Count>0 then
|
|
FStyles.ClearImported;
|
|
if Assigned(Freferences) then
|
|
FReferences.FRefs:=Nil;
|
|
FElement:=nil;
|
|
For I:=0 to ChildCount-1 do
|
|
begin
|
|
C:=Children[i];
|
|
if Assigned(C) then // Can be Nil
|
|
C.InvalidateElement;
|
|
end;
|
|
end;
|
|
|
|
function TCustomWebWidget.WidgetClasses: String;
|
|
begin
|
|
Result:='';
|
|
end;
|
|
|
|
|
|
function TCustomWebWidget.GetElement: TJSHTMLELement;
|
|
|
|
Var
|
|
El : TJSHTMLElement;
|
|
|
|
|
|
begin
|
|
if (FElement=Nil) and not (csLoading in ComponentState) then
|
|
begin
|
|
if (FElementID<>'') then
|
|
begin
|
|
El:=FindElement(FElementID);
|
|
if Assigned(El) then
|
|
begin
|
|
ApplyWidgetSettings(el);
|
|
HookupEvents(el);
|
|
end;
|
|
FElement:=El;
|
|
if Assigned(El) then
|
|
ApplyData;
|
|
RefreshReferences;// After data, so data can be used in selectors
|
|
end;
|
|
end;
|
|
Result:=FElement;
|
|
end;
|
|
|
|
function TCustomWebWidget.GetExternalElement: Boolean;
|
|
begin
|
|
Result:=(FElementID<>'')
|
|
end;
|
|
|
|
function TCustomWebWidget.GetFixedElementID: Boolean;
|
|
begin
|
|
Result:=(FElementID<>'');
|
|
end;
|
|
|
|
function TCustomWebWidget.GetHaveReferences: Boolean;
|
|
begin
|
|
Result:=Assigned(FReferences);
|
|
end;
|
|
|
|
function TCustomWebWidget.GetHTMLEvent(AIndex: Integer): THTMLNotifyEvent;
|
|
|
|
Var
|
|
Fun : JSValue;
|
|
begin
|
|
Result:=nil;
|
|
if Assigned(FMyEvents) and (aIndex>=0) and (aIndex<=MaxEvents) then
|
|
begin
|
|
Fun:=FMyEvents[FEventNames[aindex]];
|
|
if Not isUndefined(Fun) then
|
|
Result:=THTMLNotifyEvent(Fun);
|
|
end;
|
|
end;
|
|
|
|
function TCustomWebWidget.GetIsElementDirty: Boolean;
|
|
begin
|
|
Result:=IsRendered;
|
|
end;
|
|
|
|
function TCustomWebWidget.GetClasses: String;
|
|
begin
|
|
if IsRendered Then
|
|
FClasses:=FElement.ClassName;
|
|
Result:=FClasses;
|
|
end;
|
|
|
|
function TCustomWebWidget.GetDataset(aName : String): String;
|
|
|
|
Var
|
|
el : TJSHTMLElement;
|
|
|
|
begin
|
|
el:=Element;
|
|
if Assigned(El) then
|
|
Result:=El.Dataset[aName]
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TCustomWebWidget.GetChildCount: Integer;
|
|
begin
|
|
Result:=FChildren.Length;
|
|
end;
|
|
|
|
function TCustomWebWidget.GetChild(aIndex : Integer): TCustomWebWidget;
|
|
begin
|
|
if (aIndex<0) or (aIndex>=FChildren.Length) then
|
|
Raise EListError.CreateFmt(SErrInvalidChildIndex,[aIndex,FChildren.Length-1]);
|
|
Result:=TCustomWebWidget(FChildren[aIndex]);
|
|
end;
|
|
|
|
function TCustomWebWidget.GetContentElement: TJSHTMLELement;
|
|
begin
|
|
Result:=Element;
|
|
end;
|
|
|
|
function TCustomWebWidget.GetParent: TCustomWebWidget;
|
|
begin
|
|
Result:=FParent;
|
|
end;
|
|
|
|
function TCustomWebWidget.GetParentElement: TJSHTMLELement;
|
|
|
|
Var
|
|
El : TJSHTMLElement;
|
|
|
|
begin
|
|
if (FParentElement=Nil) then
|
|
begin
|
|
El:=TopElement;
|
|
if Assigned(el) then
|
|
FParentElement:=TJSHTMLElement(el.parentElement)
|
|
else if (FParentID<>'') then
|
|
FParentElement:=FindElement(FParentID)
|
|
else if Assigned(FParent) then
|
|
FParentElement:=FParent.ContentElement
|
|
else
|
|
FParentElement:=DefaultParentElement;
|
|
end;
|
|
Result:=FParentElement;
|
|
end;
|
|
|
|
function TCustomWebWidget.GetParentID: String;
|
|
|
|
Var
|
|
E : TJSHTMLElement;
|
|
|
|
begin
|
|
Result:='';
|
|
E:=ParentElement;
|
|
if Assigned(E) then
|
|
Result:=E.ID
|
|
else
|
|
Result:=FParentID;
|
|
end;
|
|
|
|
function TCustomWebWidget.GetElementID: String;
|
|
|
|
Var
|
|
El : TJSHTMLElement;
|
|
|
|
begin
|
|
El:=Element;
|
|
If Assigned(El) then
|
|
Result:=el.ID
|
|
else
|
|
Result:=FElementID;
|
|
end;
|
|
|
|
function TCustomWebWidget.GetReference(const aName : string): TJSHTMLElement;
|
|
begin
|
|
if Assigned(FReferences) then
|
|
Result:=FReferences.GetElementByName(aName)
|
|
else
|
|
Result:=Nil;
|
|
end;
|
|
|
|
function TCustomWebWidget.GetReferenceItem(aName : String): TReferenceItem;
|
|
begin
|
|
if Assigned(FReferences) then
|
|
Result:=FReferences.GetReference(aName)
|
|
else
|
|
Result:=Nil;
|
|
end;
|
|
|
|
function TCustomWebWidget.GetReferenceList(const aName : string): TJSHTMLElementArray;
|
|
begin
|
|
if Assigned(FReferences) then
|
|
Result:=FReferences.GetElementsByName(aName)
|
|
else
|
|
Result:=Nil;
|
|
end;
|
|
|
|
function TCustomWebWidget.GetReferences: TWebWidgetReferences;
|
|
begin
|
|
if (FReferences=Nil) then
|
|
FReferences:=CreateReferences;
|
|
Result:=FReferences;
|
|
|
|
end;
|
|
|
|
function TCustomWebWidget.GetRendered: Boolean;
|
|
begin
|
|
Result:=(FElement<>Nil)
|
|
end;
|
|
|
|
function TCustomWebWidget.GetTopElement: TJSHTMLELement;
|
|
begin
|
|
Result:=Element;
|
|
end;
|
|
|
|
function TCustomWebWidget.GetVisible: Boolean;
|
|
begin
|
|
Result:=FVisible;
|
|
end;
|
|
|
|
procedure TCustomWebWidget.SetAttr(const aName : string; AValue: String);
|
|
begin
|
|
if IsRendered then
|
|
Element[aName]:=aValue;
|
|
if Not Assigned(FAttrs) then
|
|
FAttrs:=TJSObject.New;
|
|
FAttrs[aName]:=aValue;
|
|
end;
|
|
|
|
procedure TCustomWebWidget.SetClasses(AValue: String);
|
|
begin
|
|
FClasses:=AddClasses(AValue,WidgetClasses);
|
|
If IsRendered then
|
|
FElement.ClassName:=FClasses;
|
|
end;
|
|
|
|
procedure TCustomWebWidget.SetDataset(aName : String; AValue: String);
|
|
|
|
Var
|
|
El : TJSHTMLElement;
|
|
|
|
begin
|
|
el:=Element;
|
|
If (El=Nil) then
|
|
Raise EWidgets.Create(SErrNotRendered);
|
|
el.Dataset[aName]:=aValue;
|
|
end;
|
|
|
|
procedure TCustomWebWidget.SetElementID(AValue: String);
|
|
begin
|
|
if (FElementID=AValue) then Exit;
|
|
if (aValue<>'') then
|
|
begin
|
|
if (FParentID<>'') then
|
|
Raise EWidgets.CreateFmt(SErrCannotSetParentAndElementID,[DisplayElementName]);
|
|
if FixedElement<>Nil then
|
|
Raise EWidgets.CreateFmt(SErrElementIDNotAllowed,[DisplayElementName]);
|
|
FElementID:=AValue;
|
|
end
|
|
else
|
|
begin
|
|
FElementID:=AValue;
|
|
if IsRendered then
|
|
Unrender(ParentElement);
|
|
end;
|
|
DoOnSetElementID;
|
|
end;
|
|
|
|
procedure TCustomWebWidget.DoOnSetElementID;
|
|
|
|
begin
|
|
// Override in descendents
|
|
end;
|
|
|
|
procedure TCustomWebWidget.SetHTMLEvent(AIndex: Integer; AValue: THTMLNotifyEvent);
|
|
|
|
Var
|
|
EventName : String;
|
|
|
|
begin
|
|
if (aIndex<0) or (aIndex>MaxEvents) then
|
|
exit;
|
|
EventName:=FEventNames[aIndex];
|
|
if Assigned(aValue) then
|
|
AddEvent(EventName,AValue)
|
|
else
|
|
DeleteEvent(EventName);
|
|
end;
|
|
|
|
procedure TCustomWebWidget.SetParent(AValue: TCustomWebWidget);
|
|
|
|
Var
|
|
ReRender : Boolean;
|
|
begin
|
|
if (AValue=FParent) then exit;
|
|
if (FixedParent<>Nil) then
|
|
Raise EWidgets.CreateFmt(SErrParentNotAllowed,[DisplayElementName]);
|
|
if Assigned(aValue) then
|
|
if Not aValue.AllowChildren then
|
|
Raise EWidgets.CreateFmt(SErrChildrenNotAllowed,[DisplayElementName]);
|
|
If Assigned(FParent) then
|
|
FParent.RemoveChild(Self);
|
|
// Unrender
|
|
ReRender:=IsRendered;
|
|
if ReRender then
|
|
UnRender(ParentElement);
|
|
If (aValue=Nil) and (csDestroying in ComponentState) then
|
|
exit;
|
|
// here we re-render if needed
|
|
InvalidateParentElement;
|
|
If Assigned(aValue) then
|
|
begin
|
|
FParentID:='';
|
|
aValue.AddChild(Self); // Sets FParent
|
|
end;
|
|
if ReRender and Assigned(ParentElement) then
|
|
begin
|
|
FElement:=RenderHTML(ParentElement);
|
|
if Assigned(FElement) then
|
|
begin
|
|
ApplyData;
|
|
RefreshReferences;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomWebWidget.SetParentID(AValue: String);
|
|
|
|
Var
|
|
ReRender : Boolean;
|
|
|
|
begin
|
|
if (FParentID=AValue) then exit;
|
|
if (aValue<>'') then
|
|
begin
|
|
if (FElementID<>'') then
|
|
Raise EWidgets.CreateFmt(SErrCannotSetParentAndElementID,[DisplayElementName]);
|
|
if (FixedParent<>Nil) then
|
|
Raise EWidgets.CreateFmt(SErrParentIDNotAllowed,[DisplayElementName]);
|
|
end;
|
|
ReRender:=IsRendered;
|
|
if ReRender then
|
|
UnRender(ParentElement);
|
|
if (aValue<>'') and Assigned(FParent) then
|
|
FParent.RemoveChild(Self);
|
|
FParentID:=aValue;
|
|
InvalidateParentElement;
|
|
if ReRender and Assigned(ParentElement) then
|
|
EnsureElement;
|
|
end;
|
|
|
|
procedure TCustomWebWidget.AddChild(aValue: TCustomWebWidget);
|
|
begin
|
|
if AValue=Nil then exit;
|
|
aValue.FParent:=Self;
|
|
if FChildren.IndexOf(aValue)=-1 then
|
|
FChildren.Push(aValue);
|
|
end;
|
|
|
|
procedure TCustomWebWidget.RemoveChild(aValue: TCustomWebWidget);
|
|
|
|
Var
|
|
I : NativeInt;
|
|
|
|
begin
|
|
if AValue=Nil then exit;
|
|
I:=FChildren.indexOf(aValue);
|
|
if I>=0 then
|
|
begin
|
|
FChildren.splice(I,1);
|
|
aValue.FParent:=Nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomWebWidget.SetReferences(AValue: TWebWidgetReferences);
|
|
begin
|
|
if (aValue=FReferences) then exit;
|
|
References.Assign(aValue);
|
|
if IsRendered then
|
|
References.RefreshFromDOM(GetReferenceElement);
|
|
end;
|
|
|
|
procedure TCustomWebWidget.SetStyles(AValue: TWebWidgetStyles);
|
|
begin
|
|
if FStyles=AValue then Exit;
|
|
FStyles.Assign(AValue);
|
|
end;
|
|
|
|
procedure TCustomWebWidget.SetVisible(AValue: Boolean);
|
|
|
|
Var
|
|
el : TJSHTMLElement;
|
|
|
|
begin
|
|
if aValue=FVisible then
|
|
Exit;
|
|
el:=Element;
|
|
if Assigned(el) then
|
|
ApplyVisible(el,aValue)
|
|
else
|
|
FVisible:=aValue;
|
|
end;
|
|
|
|
function TCustomWebWidget.GetAttr(const aName: String): String;
|
|
Var
|
|
el : TJSObject;
|
|
begin
|
|
Result:='';
|
|
if IsRendered then
|
|
el:=Element
|
|
else
|
|
el:=FAttrs;
|
|
if Assigned(el) and isDefined(el[aName]) then
|
|
Result:=String(el[aName]);
|
|
end;
|
|
|
|
procedure TCustomWebWidget.ApplyVisible(aElement: TJSHTMLElement;AValue: Boolean);
|
|
|
|
begin
|
|
if aValue then
|
|
begin
|
|
if (FDisplay<>'') then
|
|
aElement.Style.setProperty('display',FDisplay)
|
|
else
|
|
aElement.Style.removeProperty('display');
|
|
end
|
|
else
|
|
begin
|
|
FDisplay:=aElement.Style.getPropertyValue('display');
|
|
aElement.Style.setProperty('display','none');
|
|
end;
|
|
FVisible:=aValue;
|
|
end;
|
|
|
|
procedure TCustomWebWidget.EventEntry(aEvent: TJSEvent);
|
|
|
|
Var
|
|
R : TEventDispatch;
|
|
Fun : JSValue;
|
|
|
|
begin
|
|
R.MsgStr:=aEvent._type;
|
|
R.HTMLEvent:=aEvent;
|
|
if Assigned(FMyEvents) then
|
|
Fun:=FMyEvents[R.MsgStr]
|
|
else
|
|
Fun:=nil;
|
|
if Not (isUndefined(Fun) or isNull(Fun)) then
|
|
R.EventHandler:=THTMLNotifyEvent(Fun);
|
|
DispatchStr(R);
|
|
if (R.EventHandler<>Nil) then
|
|
R.EventHandler(Self,R.HTMLEvent);
|
|
end;
|
|
|
|
function TCustomWebWidget.CreateStyles: TWebWidgetStyles;
|
|
begin
|
|
Result:=TWebWidgetStyles.Create(Self,TStyleItem);
|
|
end;
|
|
|
|
function TCustomWebWidget.CreateReferences: TWebWidgetReferences;
|
|
begin
|
|
Result:=TWebWidgetReferences.Create(Self,TReferenceItem);
|
|
end;
|
|
|
|
procedure TCustomWebWidget.RemoveEvent(aElement: TJSHTMLElement; const aEvent: String);
|
|
begin
|
|
aElement.RemoveEventListener(aEvent,FMyHook);
|
|
end;
|
|
|
|
procedure TCustomWebWidget.HookupEvent(aElement: TJSHTMLElement; const aEvent : String);
|
|
|
|
begin
|
|
aElement.addEventListener(aEvent,FMyHook);
|
|
end;
|
|
|
|
procedure TCustomWebWidget.HookupEvents(aElement: TJSHTMLElement);
|
|
|
|
Var
|
|
Event : String;
|
|
|
|
begin
|
|
if Assigned(FMyEvents) then
|
|
for Event in TJSObject.keys(FMyEvents) do
|
|
HookupEvent(aElement,Event);
|
|
end;
|
|
|
|
procedure TCustomWebWidget.AddEvent(aName: String; AHandler: THTMLNotifyEvent);
|
|
|
|
Var
|
|
el : TJSHTMLElement;
|
|
|
|
begin
|
|
if FMyEvents=nil then
|
|
FMyEvents:=TJSObject.New;
|
|
FMyEvents[aName]:=aHandler;
|
|
El:=FElement;
|
|
if Assigned(El) then
|
|
HookupEvent(el,aName);
|
|
end;
|
|
|
|
procedure TCustomWebWidget.DeleteEvent(aName: String);
|
|
|
|
Var
|
|
el : TJSHTMLElement;
|
|
|
|
begin
|
|
if (FMyEvents<>nil) and FMyEvents.hasOwnProperty(aName) then
|
|
JSDelete(FMyEvents,aName);
|
|
El:=Element;
|
|
if Assigned(El) then
|
|
RemoveEvent(el,aName);
|
|
end;
|
|
|
|
class function TCustomWebWidget.FixedParent: TJSHTMLElement;
|
|
begin
|
|
Result:=Nil;
|
|
end;
|
|
|
|
class function TCustomWebWidget.DefaultParentElement: TJSHTMLElement;
|
|
begin
|
|
Result:=Nil;
|
|
end;
|
|
|
|
class function TCustomWebWidget.DefaultParent: TCustomWebWidget;
|
|
begin
|
|
Result:=nil;
|
|
end;
|
|
|
|
class function TCustomWebWidget.FixedElement: TJSHTMLElement;
|
|
begin
|
|
Result:=Nil;
|
|
end;
|
|
|
|
|
|
|
|
class function TCustomWebWidget.FindElement(aID: String): TJSHTMLElement;
|
|
begin
|
|
Result:=TJSHTMLElement(Document.getElementbyID(aID));
|
|
end;
|
|
|
|
class function TCustomWebWidget.CreateElement(aTag: String; aID: String
|
|
): TJSHTMLElement;
|
|
begin
|
|
Result:=TJSHTMLElement(Document.createElement(aTag));
|
|
if aID<>'' then
|
|
Result.id:=aID;
|
|
end;
|
|
|
|
class function TCustomWebWidget.CreateElement(aParent: TJSElement;
|
|
aTag: String; aID: String): TJSHTMLElement;
|
|
begin
|
|
Result := CreateElement(aTag, aID);
|
|
if aParent <> nil then
|
|
aParent.appendChild(Result);
|
|
end;
|
|
|
|
function TCustomWebWidget.GetReferenceElement: TJSHTMLELement;
|
|
begin
|
|
Result:=Element;
|
|
end;
|
|
|
|
procedure TCustomWebWidget.Refresh;
|
|
|
|
Var
|
|
I : integer;
|
|
|
|
begin
|
|
if IsRendered then
|
|
UnRender(ParentElement);
|
|
InvalidateParentElement;
|
|
EnsureElement;
|
|
For I:=0 to ChildCount-1 do
|
|
Children[i].Refresh;
|
|
end;
|
|
|
|
procedure TCustomWebWidget.Unrender;
|
|
|
|
Var
|
|
P : TJSHTMLElement;
|
|
|
|
begin
|
|
P:=ParentElement;
|
|
If Assigned(P) then
|
|
UnRender(P);
|
|
end;
|
|
|
|
procedure TCustomWebWidget.Focus;
|
|
begin
|
|
if not IsRendered then
|
|
ReFresh;
|
|
Element.Focus;
|
|
end;
|
|
|
|
|
|
procedure TCustomWebWidget.ApplyWidgetSettings(aElement: TJSHTMLElement);
|
|
|
|
// Normally, this should be called BEFORE FElement is set.
|
|
// But we'll be extra careful, and not rely on getters using FElement.
|
|
|
|
Var
|
|
S : String;
|
|
|
|
begin
|
|
if aElement.id='' then
|
|
aElement.id:=GenerateID;
|
|
// Don't use Classes, it will return FElement.Classname when set
|
|
S:=AddClasses(FClasses,WidgetClasses);
|
|
if (S<>'') then
|
|
AddClasses(aElement,S);
|
|
if FStyles.Count>0 then
|
|
FStyles.ApplyToDOM(aElement);
|
|
if Not FVisible then
|
|
ApplyVisible(aElement,FVisible);
|
|
// Maybe we should put this under control of a property or so ?
|
|
// TStyleRefresh = (srAlways,srOnElementID,srNever)
|
|
if (StyleRefresh = srAlways)
|
|
or ((FelementID<>'') and (FElementID<>'')) then
|
|
FStyles.RefreshFromDom(aElement,False);
|
|
if Assigned(FAttrs) then
|
|
for S in TJSObject.getOwnPropertyNames(FAttrs) do
|
|
if IndexText(S,['id','class'])=-1 then
|
|
aElement[S]:=String(FAttrs[S]);
|
|
end;
|
|
|
|
function TCustomWebWidget.DoRenderHTML(aParent, aElement: TJSHTMLElement): TJSHTMLElement;
|
|
begin
|
|
If AParent=Nil then
|
|
Console.Log(DisplayElementName+': render without parent!');
|
|
Result:=aElement;
|
|
end;
|
|
|
|
|
|
procedure TCustomWebWidget.ApplyData;
|
|
|
|
Var
|
|
AID : String;
|
|
|
|
Procedure MaybeSet(El : TJSHTMLElement; AName : String);
|
|
|
|
begin
|
|
if Assigned(el) and CreateDataTags then
|
|
el.Dataset[aName]:=AID;
|
|
end;
|
|
|
|
begin
|
|
AID:=ElementID;
|
|
if assigned(Element) and Not CreateDataTags then
|
|
Element.dataset[SElementClass]:=ClassName;
|
|
MaybeSet(Element,SElementData);
|
|
MaybeSet(TopElement,STopElementData);
|
|
if AllowChildren then
|
|
MaybeSet(ContentElement,SContentElementData);
|
|
end;
|
|
|
|
procedure TCustomWebWidget.RemoveData;
|
|
|
|
Procedure MaybeUnSet(El : TJSHTMLElement; AName : String);
|
|
|
|
begin
|
|
if Assigned(el) then
|
|
jsDelete(el.Dataset,aName);
|
|
end;
|
|
|
|
begin
|
|
MaybeUnSet(Element,SElementData);
|
|
MaybeUnSet(TopElement,STopElementData);
|
|
MaybeUnSet(ContentElement,SContentElementData);
|
|
end;
|
|
|
|
procedure TCustomWebWidget.RefreshReferences;
|
|
begin
|
|
if Assigned(FReferences) then
|
|
if Assigned(Element) then
|
|
References.RefreshFromDom(GetReferenceElement)
|
|
else
|
|
References.FRefs:=Nil;
|
|
end;
|
|
|
|
class function TCustomWebWidget.GenerateID: String;
|
|
|
|
begin
|
|
Inc(WidgetID);
|
|
Result:='ww-'+intToStr(WidgetID);
|
|
end;
|
|
|
|
function TCustomWebWidget.RenderHTML(aParent: TJSHTMLELement): TJSHTMLElement;
|
|
|
|
Var
|
|
aTag : String;
|
|
|
|
begin
|
|
aTag:=HTMLTag;
|
|
if aTag='' then
|
|
Result:=Nil
|
|
else
|
|
begin
|
|
Result:=FixedElement;
|
|
if Result=Nil then
|
|
Result:=CreateElement(HTMLTag,GenerateID);
|
|
end;
|
|
if Assigned(Result) and Assigned(aParent) then
|
|
aParent.AppendChild(Result);
|
|
if Assigned(FBeforeRenderHTML) then
|
|
FBeforeRenderHTML(Self);
|
|
Result:=DoRenderHTML(aParent,Result);
|
|
if Assigned(Result) then
|
|
begin
|
|
ApplyWidgetSettings(Result);
|
|
HookupEvents(Result);
|
|
end;
|
|
if Assigned(FAfterRenderHTML) then
|
|
FAfterRenderHTML(Self);
|
|
end;
|
|
|
|
procedure TCustomWebWidget.DoUnRender(aParent: TJSHTMLElement);
|
|
|
|
Var
|
|
el : TJSHTMLElement;
|
|
|
|
begin
|
|
if Assigned(aParent) and Assigned(FElement) then
|
|
begin
|
|
if FOwnsElement then
|
|
begin
|
|
el:=TopElement;
|
|
if (El.ParentElement=aParent) then
|
|
aParent.removeChild(el);
|
|
end;
|
|
InvalidateElement;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomWebWidget.UnRender(aParent: TJSHTMLElement);
|
|
begin
|
|
if Assigned(FBeforeUnRenderHTML) then
|
|
FBeforeUnRenderHTML(Self);
|
|
RemoveData;
|
|
if assigned(AParent) then
|
|
DoUnRender(aParent);
|
|
if Assigned(FAfterUnRenderHTML) then
|
|
FAfterUnRenderHTML(Self);
|
|
end;
|
|
|
|
function TCustomWebWidget.DispatchEvent(aName: String; aEvent: TJSEvent): Boolean;
|
|
|
|
begin
|
|
if not IsRendered then
|
|
exit;
|
|
if (aEvent=Nil) then
|
|
aEvent:=TJSEvent.New(aName);
|
|
Result:=Element.dispatchEvent(aEvent);
|
|
end;
|
|
|
|
constructor TCustomWebWidget.Create(aOwner: TComponent);
|
|
begin
|
|
inherited Create(aOwner);
|
|
FChildren:=TJSArray.New;
|
|
FStyles:=CreateStyles;
|
|
FMyHook:=@EventEntry;
|
|
FParent:=DefaultParent;
|
|
FVisible:=True;
|
|
end;
|
|
|
|
destructor TCustomWebWidget.Destroy;
|
|
|
|
Var
|
|
I : integer;
|
|
C : TCustomWebWidget;
|
|
|
|
begin
|
|
For I:=0 to FChildren.Length-1 do
|
|
begin
|
|
C:=TCustomWebWidget(FChildren[i]);
|
|
FChildren[i]:=Nil;
|
|
C.Free;
|
|
end;
|
|
FChildren.Length:=0;
|
|
Parent:=Nil;
|
|
ParentID:='';
|
|
FChildren:=Nil;
|
|
FreeAndNil(FStyles);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
class function TCustomWebWidget.AllowChildren: Boolean;
|
|
begin
|
|
Result:=True;
|
|
end;
|
|
|
|
class function TCustomWebWidget.AddRemoveClasses(const Source, aAddClasses,
|
|
aRemoveClasses: String; Normalize: Boolean): String;
|
|
var
|
|
T : TJSStringDynArray;
|
|
i : integer;
|
|
S : String;
|
|
begin
|
|
Result:=Source;
|
|
if Normalize then
|
|
Result:=TJSString(Result).replace(TJSRegexp.New('\s\s+','g'),' ');
|
|
T:=TJSString(Result).split(' ');
|
|
For S in TJSString(aRemoveClasses).split(' ') do
|
|
if (S<>'') then
|
|
begin
|
|
I:=TJSArray(T).indexOf(S);
|
|
if (I<>-1) then
|
|
TJSArray(T).splice(i,1);
|
|
end;
|
|
For S in TJSString(aAddClasses).split(' ') do
|
|
if (S<>'') then
|
|
begin
|
|
I:=TJSArray(T).indexOf(S);
|
|
if (I=-1) then
|
|
TJSArray(T).Push(S);
|
|
end;
|
|
Result:=TJSArray(T).join(' ');
|
|
end;
|
|
|
|
class function TCustomWebWidget.ReplaceClasses(const Source, aSearchClasses, aReplaceClasses : String; Normalize: Boolean): String;
|
|
var
|
|
Dest,Srch,Repl : TJSStringDynArray;
|
|
sIdx,I : integer;
|
|
S : String;
|
|
|
|
begin
|
|
Srch:=TJSString(aSearchClasses).split(' ');
|
|
Repl:=TJSString(aReplaceClasses).split(' ');
|
|
Result:=Source;
|
|
if Normalize then
|
|
Result:=TJSString(Result).replace(TJSRegexp.New('\s\s+','g'),' ');
|
|
Dest:=TJSString(Result).split(' ');
|
|
For sIdx:=0 to length(Srch)-1 do
|
|
begin
|
|
S:=Srch[sIdx];
|
|
if (S<>'') then
|
|
begin
|
|
I:=TJSArray(Dest).indexOf(S);
|
|
if (I<>-1) then
|
|
begin
|
|
TJSArray(Dest).splice(i,1);
|
|
if sIdx<Length(Repl) then
|
|
begin
|
|
I:=TJSArray(Dest).indexOf(Repl[sIdx]);
|
|
if I=-1 then
|
|
TJSArray(Dest).Push(Repl[sIdx]);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
Result:=TJSArray(Dest).join(' ');
|
|
end;
|
|
|
|
class function TCustomWebWidget.RemoveClasses(const Source, aClasses: String; Normalize : Boolean = false): String;
|
|
|
|
begin
|
|
Result:=AddRemoveClasses(Source,'',aClasses,Normalize);
|
|
end;
|
|
|
|
class function TCustomWebWidget.RemoveClasses(el: TJSHTMLElement; const aClasses: String; Normalize : Boolean = false): String;
|
|
|
|
begin
|
|
Result:=RemoveClasses(el.ClassName,aClasses,Normalize);
|
|
el.ClassName:=Result;
|
|
end;
|
|
|
|
class function TCustomWebWidget.AddClasses(const Source, aClasses: String; Normalize : Boolean = false): String;
|
|
|
|
begin
|
|
Result:=AddRemoveClasses(Source,aClasses,'',Normalize);
|
|
end;
|
|
|
|
class function TCustomWebWidget.AddClasses(el: TJSHTMLElement; const aClasses: String; Normalize : Boolean = false): String;
|
|
|
|
begin
|
|
Result:=AddClasses(el.ClassName,aClasses,Normalize);
|
|
el.ClassName:=Trim(Result);
|
|
end;
|
|
|
|
class function TCustomWebWidget.AddRemoveClasses(el: TJSHTMLElement;
|
|
const aAddClasses, aRemoveClasses: String; Normalize: Boolean): String;
|
|
begin
|
|
Result:=AddRemoveClasses(el.ClassName,aAddClasses,aRemoveClasses,Normalize);
|
|
el.ClassName:=Trim(Result);
|
|
end;
|
|
|
|
class function TCustomWebWidget.ReplaceClasses(el: TJSHTMLElement;
|
|
const aSearchClasses, aReplaceClasses: String; Normalize: Boolean): String;
|
|
begin
|
|
Result:=ReplaceClasses(el.ClassName,aSearchClasses, aReplaceClasses,Normalize);
|
|
el.ClassName:=Trim(Result);
|
|
end;
|
|
|
|
function TCustomWebWidget.AddRemoveClasses(const aAddClasses,
|
|
aRemoveClasses: String; Normalize: Boolean): String;
|
|
begin
|
|
FClasses:=AddRemoveClasses(FClasses,aAddClasses,aRemoveClasses,Normalize);
|
|
Result:=FClasses;
|
|
if IsRendered then
|
|
Result:=AddRemoveClasses(FElement,aAddClasses,aRemoveClasses,Normalize)
|
|
end;
|
|
|
|
function TCustomWebWidget.ReplaceClasses(const aSearchClasses,
|
|
aReplaceClasses: String; Normalize: Boolean): String;
|
|
begin
|
|
FClasses:=ReplaceClasses(FClasses,aSearchClasses,aReplaceClasses,Normalize);
|
|
Result:=FClasses;
|
|
if IsRendered then
|
|
Result:=ReplaceClasses(FElement,aSearchClasses,aReplaceClasses,Normalize)
|
|
end;
|
|
|
|
function TCustomWebWidget.RemoveClasses(const aClasses: String; Normalize : Boolean = false): String;
|
|
begin
|
|
FClasses:=RemoveClasses(FClasses,aClasses,Normalize);
|
|
Result:=FClasses;
|
|
if IsRendered then
|
|
Result:=RemoveClasses(FElement,aClasses,Normalize)
|
|
end;
|
|
|
|
function TCustomWebWidget.AddClasses(const aClasses: String; Normalize: Boolean): String;
|
|
begin
|
|
FClasses:=AddClasses(FClasses,aClasses,Normalize);
|
|
Result:=FClasses;
|
|
if IsRendered then
|
|
Result:=AddClasses(FElement,aClasses,Normalize)
|
|
end;
|
|
|
|
function TCustomWebWidget.FindWidgetByID(aElementID: String; Recurse: Boolean): TCustomWebWidget;
|
|
|
|
Var
|
|
I : Integer;
|
|
|
|
begin
|
|
Result:=Nil;
|
|
if aElementID='' then
|
|
exit;
|
|
if (aElementID=elementID) then
|
|
Exit(Self);
|
|
I:=ChildCount-1;
|
|
// First this level. Typical layout is not so nested.
|
|
While (i>=0) and (Result=Nil) do
|
|
begin
|
|
Result:=Children[i];
|
|
if (Result.ElementID<>aElementID) then
|
|
Result:=nil;
|
|
Dec(I);
|
|
end;
|
|
If (Result=Nil) and (Recurse) then
|
|
begin
|
|
I:=ChildCount-1;
|
|
While (i>=0) and (Result=Nil) do
|
|
begin
|
|
Result:=Children[i].FindWidgetByID(aElementID,True);
|
|
Dec(I);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCustomWebWidget.GetWidgetByID(aElementID: String; Recurse: Boolean): TCustomWebWidget;
|
|
begin
|
|
Result:=FindWidgetByID(aElementID,Recurse);
|
|
if Result=Nil then
|
|
Raise EWidgets.CreateFmt(SErrWidgetNotFound,[aElementID]);
|
|
end;
|
|
|
|
function TCustomWebWidget.EnsureStyle(const aName: String): TStyleItem;
|
|
begin
|
|
Result:=Styles.EnsureStyle(aName);
|
|
end;
|
|
|
|
function TCustomWebWidget.AddStyle(const aName, aValue: String): TStyleItem;
|
|
begin
|
|
Result:=EnsureStyle(aName);
|
|
Result.Value:=aValue;
|
|
end;
|
|
|
|
function TCustomWebWidget.GetStyleValue(const aName : String): String;
|
|
|
|
Var
|
|
S : TStyleItem;
|
|
|
|
begin
|
|
S:=Styles.FindStyle(aName);
|
|
if Assigned(S) then
|
|
Result:=S.Value
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TCustomWebWidget.RemoveStyle(const aName: String): String;
|
|
|
|
begin
|
|
Result:=Styles.RemoveStyle(aName);
|
|
end;
|
|
|
|
procedure TCustomWebWidget.RemoveData(const aName: String);
|
|
begin
|
|
if IsRendered then
|
|
jsDelete(Element.Dataset,aName)
|
|
end;
|
|
|
|
end.
|
|
|