unit DOM; {$MODE OBJFPC} {$M+} // . // TODO . { The node types, and which node types they may have as children, are as follows: * Document -- Element (maximum of one), ProcessingInstruction, Comment, DocumentType * DocumentFragment -- Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference * DocumentType -- no children * EntityReference -- Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference * Element -- Element, Text, Comment, ProcessingInstruction, CDATASection, EntityReference * Attr -- Text, EntityReference * ProcessingInstruction -- no children * Comment -- no children * Text -- no children * CDATASection -- no children * Entity -- Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference * Notation -- no children } interface uses classes, type_fixes, contnrs; const NullString { : TDOMString} = ''; type TDOMString = String; // FIXME. TDOMImplementation = class; TNodeType = (ntUnknown, ntElement, ntAttribute, ntText, ntCDATASection, ntEntityReference, ntEntity, ntProcessingInstruction, ntComment, ntDocument, ntDocumentType, ntDocumentFragment, ntNotation, { custom extensions: } ntDocumentTypeElement = 100, ntDocumentTypeAttribute); TDocument = class; TNamedNodeMap = class; TNodeList = class; TNode = class; TLoadChildNodes = function(Sender : TObject; aNode : TNode) : TNodeList of object; TNode = class protected // don't write. fNodeName : TDOMString; protected procedure EnsureWritable(); inline; private fNodeType : TNodeType; fParentNode : TNode; fPreviousSibling : TNode; fNextSibling : TNode; fOwnerDocument : TDocument; fChildNodes : TNodeList; fChildNodesRequested : TLoadChildNodes; fAttributes : TNamedNodeMap; fNodeValue : TDOMString; // TODO better to make "GetNodeValue" etc virtual or not? protected function GetNodeValue() : TDOMString; inline; procedure SetNodeValue(aValue : TDOMString); inline; function GetChildNodes() : TNodeList; inline; function GetFirstChild() : TNode; inline; function GetLastChild() : TNode; inline; public procedure SetAttributes(aItem : TNamedNodeMap); inline; // used by the XML parser. protected constructor Create(aOwnerDocument : TDocument; aNodeType : TNodeType; aNodeName : TDOMString); public destructor Destroy(); override; procedure SetChildNodes(aChildNodes : TNodeList); // used by the XML parser (for TDocumentType, TElement, ...). protected procedure SetRelations(aParentNode, aPreviousSibling, aNextSibling : TNode); //inline; procedure RemoveRelations(); published // our ext. property ChildNodesRequested : TLoadChildNodes read fChildNodesRequested write fChildNodesRequested; published // official DOM Level 1 core interface: property nodeName : TDOMString read fNodeName; property nodeValue : TDOMString read GetNodeValue write SetNodeValue; // raises(DOMException) on setting, raises(DOMException) on retrieval. property nodeType : TNodeType read fNodeType; // unsigned short? property parentNode : TNode read fParentNode; property childNodes : TNodeList read GetChildNodes; property firstChild : TNode read GetFirstChild; property lastChild : TNode read GetLastChild; property previousSibling : TNode read fPreviousSibling; property nextSibling : TNode read fNextSibling; property attributes : TNamedNodeMap read fAttributes; property ownerDocument : TDocument read fOwnerDocument; function insertBefore(aNewChild, aRefChild : TNode) : TNode; // raises DOMException. function replaceChild(aNewChild, aOldChild : TNode) : TNode; // raises DOMException. function removeChild(aOldChild : TNode) : TNode; // raises DOMException. function appendChild(aNewChild : TNode) : TNode; // raises DOMException. function hasChildNodes() : Boolean; function cloneNode(aBDeep : Boolean) : TNode; virtual; end; TAttr = class(TNode) private fBSpecified : Boolean; fValue : TDOMString; public constructor Create(aOwnerDocument : TDocument; aName : TDOMString; aValue : TDOMString = ''; aBSpecified : Boolean = False); published // official DOM Level 1 core interface: // the Node attributes parentNode, previousSibling, and nextSibling have a null value for Attr objects. property name : TDOMString read fNodeName; property specified : Boolean read fBSpecified; property value : TDOMString read fValue; end; TElement = class(TNode) public constructor Create(aOwnerDocument : TDocument; aNodeName : TDOMString; aAttributes : TNamedNodeMap); published // official DOM Level 1 core interface: property tagName : TDOMString read fNodeName; // just use NodeName directly! this property is useless. function getAttribute(aName : TDOMString) : TDOMString; inline; procedure setAttribute(aName, aValue : TDOMString); // raises DOMException. procedure removeAttribute(const aName : TDOMString); // raises DOMException. function getAttributeNode(aName : TDOMString) : TAttr; procedure setAttributeNode(aAttr : TAttr); // raises DOMException. function removeAttributeNode(aOldAttr : TAttr) : TAttr; // raises DOMException. function getElementsByTagName(aName : TDOMString) : TNodeList; procedure normalize(); end; TCharacterData = class(TNode) protected function GetLength() : TCardinal; inline; published // official DOM Level 1 core interface: property data : TDOMString read fNodeValue write fNodeValue; // raises(DOMException) on setting, raises(DOMException) on retrieval. property length : TCardinal read GetLength; function substringData(aOffset, aCount : TCardinal) : TDOMString; // raises(DOMException); procedure appendData(aArg : TDOMString); // raises DOMException. procedure insertData(aOffset : TCardinal; aArg : TDOMString); // raises DOMException. procedure deleteData(aOffset, aCount : TCardinal); // raises DOMException. procedure replaceData(aOffset, aCount : TCardinal; aArg : TDOMString); // raises DOMException. end; TText = class(TCharacterData) public constructor Create(aOwnerDocument : TDocument; aNodeValue : TDOMString); protected constructor Create(aOwnerDocument : TDocument; aNodeType : TNodeType; aNodeName : TDOMString); published // official DOM Level 1 core interface: function splitText(aOffset : TCardinal) : TText; // raises DOMException. end; TCDATASection = class(TText) public constructor Create(aOwnerDocument : TDocument); published // official DOM Level 1 core interface: end; TComment = class(TCharacterData) public constructor Create(aOwnerDocument : TDocument); published // official DOM Level 1 core interface: end; TProcessingInstruction = class(TNode) private // fTarget in NodeName. // fData in NodeValue. public constructor Create(aOwnerDocument : TDocument; aTarget, aData : TDOMString); published // official DOM Level 1 core interface: property target : TDOMString read fNodeName; property data : TDOMString read fNodeValue; // raises DOMException on setting. end; // DOMImplementation. // DOMImplementationLS. TEntity = class(TNode) private fPublicID : TDOMString; fSystemID : TDOMString; fNDATAName : TDOMString; // TODO make this settable? fContents : TDOMString; public constructor Create(aOwnerDocument : TDocument; aNodeName, aPublicID, aSystemID, aNotationName : TDOMString; aContents : TDOMString); published property Contents : TDOMString read fContents; published // official DOM Level 1 core interface: property publicId : TDOMString read fPublicID; property systemId : TDOMString read fSystemID; property notationName : TDOMString read fNDATAName; // For unparsed entities, the name of the notation for the entity. For parsed entities, this is null. end; TNotation = class(TNode) private fPublicID : TDOMString; fSystemID : TDOMString; public constructor Create(aOwnerDocument : TDocument; aNodeName, aPublicID, aSystemID : TDOMString); published // official DOM Level 1 core interface: property publicId : TDOMString read fPublicID; property systemId : TDOMString read fSystemID; end; TDocumentType = class(TNode) private fEntities : TNamedNodeMap; fNotations : TNamedNodeMap; public constructor Create(aOwnerDocument : TDocument; aNodeName : TDOMString; aChildren : TNodeList {takes ownership}); //aEntities, aNotations : TNamedNodeMap); destructor Destroy(); override; published procedure AddEntity(aEntity : TEntity); function HasEntityP(const aEntityName : TDOMString) : Boolean; function GetEntity(const aEntityName : TDOMString) : TEntity; procedure AddNotation(aNotation : TNotation); function HasNotationP(const aName : TDOMString) : Boolean; function GetNotation(const aName : TDOMString) : TNotation; published // official DOM Level 1 core interface: property name : TDOMString read fNodeName; property entities : TNamedNodeMap read fEntities; property notations : TNamedNodeMap read fNotations; end; TEntityReference = class(TNode) public constructor Create(aOwnerDocument : TDocument; aNodeName : TDOMString); end; TDocumentFragment = class(TNode) public constructor Create(aOwnerDocument : TDocument); end; TDocument = class(TNode) private fImplementation : TDOMImplementation; fDocumentType : TDocumentType; fDocumentElement : TElement; protected procedure SetImplementation(aItem : TDOMImplementation); inline; public procedure SetDocumentType(aDocumentType : TDocumentType); // used by XML parser (speed optimization so that appendChild doesn't need to be virtual). procedure SetDocumentElement(aDocumentElement : TElement); // used by XML parser (speed optimization so that appendChild doesn't need to be virtual). public constructor Create(); // TODO doctype? published // official DOM Level 1 core interface: property doctype : TDocumentType read fDocumentType; property implementation_ : TDOMImplementation read fImplementation; property documentElement : TElement read fDocumentElement; function createElement(aTagName : TDOMString) : TElement; // raises DOMException. function createDocumentFragment() : TDocumentFragment; // r... function createTextNode(aData : TDOMString) : TText; // r... function createComment(aData : TDOMString) : TComment; // r... function createCDATASection(aData : TDOMString) : TCDATASection; // raises ... function createProcessingInstruction(aTarget : TDOMString; aData : TDOMString) : TProcessingInstruction; // raises... function createAttribute(aName : TDOMString) : TAttr; // raises... function createEntityReference(aName : TDOMString) : TEntityReference; // raises. function getElementsByTagName(aTagName : TDOMString) : TNodeList; end; // DocumentLS. // also a read-only version? TNamedNodeMap = class private fMap : TFPHashObjectList; protected function GetCount() : TCardinal; inline; public constructor Create(); destructor Destroy(); override; published // official DOM Level 1 core interface: function getNamedItem(aName : TDOMString) : TNode; function setNamedItem(aArg : TNode) : TNode; // raises... // Removes a node specified by name. If the removed node is an Attr with a default value it is immediately replaced. function removeNamedItem(const aName : TDOMString) : TNode; // raises... DOES NOT FREE THE NODE. function item(aIndex : TCardinal) : TNode; property length : TCardinal read GetCount; end; TNodeList = class private fList : TObjectList; protected function GetLength() : TCardinal; public constructor Create(aBOwnsElements : Boolean); destructor Destroy(); override; procedure Append(aNode : TNode); inline; public // be careful, doesn't disassociate the node. procedure RemoveAt(aIndex : TCardinal); procedure ReplaceAt(aIndex : TCardinal; aNewNode : TNode); procedure InsertAt(aIndex : TCardinal; aNewNode : TNode); published // official DOM Level 1 core interface: function item(aIndex : TCardinal) : TNode; inline; property length : TCardinal read GetLength; end; TDOMImplementation = class function hasFeature(const aFeature : TDOMString; const aVersion : TDOMString) : Boolean; end; // NodeList ? // TypeInfo. // XMLNS_NAMESPACE. //'EMPTY_NAMESPACE', 'EMPTY_PREFIX', { typing crap: } TDocumentTypeBodyItem = class end; // FIXME is it OK that this is a TNode? TDocumentTypeElementDeclaration = class(TNode) private fContent : TDocumentTypeBodyItem; public property Content : TDocumentTypeBodyItem read fContent; constructor Create(aOwnerDocument : TDocument; aNodeName : TDOMString; aContent : TDocumentTypeBodyItem); public destructor Destroy(); override; end; TDocumentTypeBodyAttributeDefaultImportance = (imOptional, imRequired, imImplied, imFixed); TDocumentTypeBodyAttributeDefault = class private fValue : TDOMString; fImportance : TDocumentTypeBodyAttributeDefaultImportance; published property Importance : TDocumentTypeBodyAttributeDefaultImportance read fImportance write fImportance; property Value : TDOMString read fValue write fValue; constructor Create(aImportance : TDocumentTypeBodyAttributeDefaultImportance; aValue : TDOMString); end; TDocumentTypeBodyAttributeBaseType = (atString, atID, atIDREF, atIDREFS, atENTITY, atENTITIES, atNMTOKEN, atNMTOKENS, atEnumeration, atNotation); // FIXME is it OK that this is a TNode? // FIXME split type out. TDocumentTypeAttributeDeclaration = class(TNode) private fBaseType_ : TDocumentTypeBodyAttributeBaseType; fDefault_ : TDocumentTypeBodyAttributeDefault; protected procedure SetDefault(aDefault : TDocumentTypeBodyAttributeDefault); public property BaseType_ : TDocumentTypeBodyAttributeBaseType read fBaseType_; // RO. property Default_ : TDocumentTypeBodyAttributeDefault read fDefault_ write SetDefault; // NodeName = attribute name. ElementName : TDOMString; // since XML doesn't just put the !ATTLIST inside the !ELEMENT, we have to remember which ATTLIST is for which ELEMENT here. destructor Destroy(); override; constructor Create(aOwnerDocument : TDocument; aAttributeName : TDOMString; aElementName : TDOMString; aBaseType_ : TDocumentTypeBodyAttributeBaseType); end; TDocumentTypeBodyNotationChoice = class(TDocumentTypeAttributeDeclaration) private fList : TObjectList; public destructor Destroy(); override; published constructor Create(aOwnerDocument : TDocument; aAttributeName : TDOMString; aElementName : TDOMString); procedure Add(vItem : TNotation); end; TDocumentTypeAttributeBodyEnumeration = class(TDocumentTypeAttributeDeclaration) private fList : TStringList; public destructor Destroy(); override; published procedure Append(vItem : TDOMString); constructor Create(aOwnerDocument : TDocument; aAttributeName : TDOMString; aElementName : TDOMString); end; function getDOMImplementation() : TDOMImplementation; implementation uses sysutils; type TDOMExceptionCode = ( INDEX_SIZE_ERR = 1, DOMSTRING_SIZE_ERR = 2, HIERARCHY_REQUEST_ERR = 3, WRONG_DOCUMENT_ERR = 4, INVALID_CHARACTER_ERR = 5, NO_DATA_ALLOWED_ERR = 6, NO_MODIFICATION_ALLOWED_ERR = 7, NOT_FOUND_ERR = 8, NOT_SUPPORTED_ERR = 9, INUSE_ATTRIBUTE_ERR = 10 ); EDOMException = class(Exception) private fCode : TDOMExceptionCode; published constructor Create(aCode : TDOMExceptionCode); property Code : TDOMExceptionCode read fCode; end; { TNode } function TNode.GetNodeValue() : TDOMString; inline; begin Result := fNodeValue; end; procedure TNode.SetNodeValue(aValue : TDOMString); inline; begin fNodeValue := aValue; // TODO raise TDOMException.Create(NO_MODIFICATION_ALLOWED_ERR); end; function TNode.GetChildNodes() : TNodeList; inline; begin if not Assigned(fChildNodes) and Assigned(fChildNodesRequested) then fChildNodes := fChildNodesRequested(Self, Self); if not Assigned(fChildNodes) then fChildNodes := TNodeList.Create(True); Result := fChildNodes; end; function TNode.GetFirstChild() : TNode; inline; var vChildNodes : TNodeList; begin vChildNodes := GetChildNodes(); if Assigned(vChildNodes) and (vChildNodes.length > 0) then Result := vChildNodes.item(0) else Result := nil; end; function TNode.GetLastChild() : TNode; inline; var vChildNodes : TNodeList; begin vChildNodes := GetChildNodes(); if Assigned(vChildNodes) and (vChildNodes.length > 0) then Result := vChildNodes.item(vChildNodes.length - 1) else Result := nil; end; function TNode.hasChildNodes() : Boolean; var vChildNodes : TNodeList; begin vChildNodes := GetChildNodes(); if Assigned(vChildNodes) then Result := vChildNodes.length > 0 else // oops. Result := False; end; constructor TNode.Create(aOwnerDocument : TDocument; aNodeType : TNodeType; aNodeName : TDOMString); begin fOwnerDocument := aOwnerDocument; fNodeType := aNodeType; fNodeName := aNodeName; end; procedure TNode.EnsureWritable(); inline; begin // raise TDOMException.Create(NO_MODIFICATION_ALLOWED_ERR); end; procedure TNode.SetRelations(aParentNode, aPreviousSibling, aNextSibling : TNode); //inline; begin fParentNode := aParentNode; fPreviousSibling := aPreviousSibling; fNextSibling := aNextSibling; end; procedure TNode.RemoveRelations(); begin if Assigned(fParentNode) then begin fParentNode.removeChild(Self); fParentNode := nil; assert(fNextSibling = nil); assert(fPreviousSibling = nil); end; end; procedure TNode.SetChildNodes(aChildNodes : TNodeList); begin FreeAndNil(fChildNodes); fChildNodes := aChildNodes; end; destructor TNode.Destroy(); begin RemoveRelations(); FreeAndNil(fChildNodes); FreeAndNil(fAttributes); inherited Destroy(); end; procedure TNode.SetAttributes(aItem : TNamedNodeMap); inline; begin if Assigned(fAttributes) then FreeAndNil(fAttributes); fAttributes := aItem; end; { Adds the node newChild to the end of the list of children of this node. If the newChild is already in the tree, it is first removed. Parameters newChild The node to add. If it is a DocumentFragment object, the entire contents of the document fragment are moved into the child list of this node. Return Value The node added. Exceptions DOMException HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not allow children of the type of the newChild node, or if the node to append is one of this node's ancestors. WRONG_DOCUMENT_ERR: Raised if newChild was created from a different document than the one that created this node. NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. } function TNode.appendChild(aNewChild : TNode) : TNode; // raises DOMException. var vChildNodes : TNodeList; vPrevious : TNode; begin if Assigned(fParentNode) then begin RemoveRelations(); end; Result := aNewChild; // FIXME handle DocumentFragment. vChildNodes := GetChildNodes(); vPrevious := nil; if vChildNodes.length > 0 then vPrevious := vChildNodes.item(vChildNodes.length - 1); aNewChild.SetRelations(Self, vPrevious, nil); vChildNodes.Append(aNewChild); end; function TNode.insertBefore(aNewChild, aRefChild : TNode) : TNode; // raises DOMException. var vChildNodes : TNodeList; vPrevious : TNode; vNode : TNode; vChildIndex : TCardinal; begin // FIXME HIERARCHY_REQUEST_ERR. // FIXME WRONG_DOCUMENT_ERR. Result := aNewChild; EnsureWritable(); vChildNodes := GetChildNodes(); vPrevious := nil; if vChildNodes.length > 0 then for vChildIndex := 0 to vChildNodes.length - 1 do begin vNode := vChildNodes.item(vChildIndex); if vNode = aRefChild then begin aNewChild.SetRelations(Self, vPrevious, vNode); vChildNodes.InsertAt(vChildIndex, aNewChild); Exit; end; vPrevious := vNode; end; raise EDOMException.Create(NOT_FOUND_ERR); end; { Replaces the child node oldChild with newChild in the list of children, and returns the oldChild node. If the newChild is already in the tree, it is first removed. } function TNode.replaceChild(aNewChild, aOldChild : TNode) : TNode; // raises DOMException. var vChildNodes : TNodeList; vNode : TNode; vPrevious : TNode; vNext : TNode; vChildIndex : TCardinal; begin // FIXME HIERARCHY_REQUEST_ERR. // FIXME WRONG_DOCUMENT_ERR. Result := nil; EnsureWritable(); vChildNodes := GetChildNodes(); removeChild(aNewChild); if vChildNodes.length > 0 then for vChildIndex := 0 to vChildNodes.length - 1 do begin vNode := vChildNodes.item(vChildIndex); if vNode = aOldChild then begin vPrevious := vNode.previousSibling; vNext := vNode.nextSibling; vNode.RemoveRelations(); aNewChild.SetRelations(Self, vPrevious, vNext); vChildNodes.ReplaceAt(vChildIndex, aNewChild); Result := aOldChild; Exit; end; end; raise EDOMException.Create(NOT_FOUND_ERR); end; function TNode.removeChild(aOldChild : TNode) : TNode; // raises DOMException. var vChildNodes : TNodeList; vChildIndex : TCardinal; vNode : TNode; begin EnsureWritable(); vChildNodes := GetChildNodes(); if vChildNodes.length > 0 then for vChildIndex := 0 to vChildNodes.length - 1 do begin vNode := vChildNodes.item(vChildIndex); if vNode = aOldChild then begin vNode.RemoveRelations(); vChildNodes.RemoveAt(vChildIndex); Result := vNode; end; end; end; function TNode.cloneNode(aBDeep : Boolean) : TNode; begin // FIXME! Result := nil; end; { TAttr } constructor TAttr.Create(aOwnerDocument : TDocument; aName : TDOMString; aValue : TDOMString = ''; aBSpecified : Boolean = False); begin inherited Create(aOwnerDocument, ntAttribute, aName); fBSpecified := aBSpecified; fValue := aValue; end; { TElement } constructor TElement.Create(aOwnerDocument : TDocument; aNodeName : TDOMString; aAttributes : TNamedNodeMap); begin inherited Create(aOwnerDocument, ntElement, aNodeName); SetAttributes(aAttributes); end; function TElement.getAttribute(aName : TDOMString) : TDOMString; inline; var vNode : TNode; begin vNode := fAttributes.getNamedItem(aName); if Assigned(vNode) then Result := vNode.NodeValue else Result := NullString; end; procedure TElement.setAttribute(aName, aValue : TDOMString); // raises DOMException. begin EnsureWritable(); fAttributes.setNamedItem(TAttr.Create(OwnerDocument, aName, aValue)); end; procedure TElement.removeAttribute(const aName : TDOMString); // raises DOMException. begin EnsureWritable(); fAttributes.removeNamedItem(aName); end; function TElement.getAttributeNode(aName : TDOMString) : TAttr; inline; begin Result := TAttr(fAttributes.getNamedItem(aName)); end; procedure TElement.setAttributeNode(aAttr : TAttr); // raises DOMException. begin EnsureWritable(); fAttributes.setNamedItem(aAttr); end; function TElement.removeAttributeNode(aOldAttr : TAttr) : TAttr; // raises DOMException. begin EnsureWritable(); assert(Assigned(aOldAttr)); if fAttributes.getNamedItem(aOldAttr.Name) = aOldAttr then fAttributes.removeNamedItem(aOldAttr.Name) // FIXME is that enough? else raise EDOMException.Create(NOT_FOUND_ERR); Result := aOldAttr; end; function TElement.getElementsByTagName(aName : TDOMString) : TNodeList; var vChildNodes : TNodeList; vChildIndex : TCardinal; begin Result := TNodeList.Create(False); try vChildNodes := GetChildNodes(); if vChildNodes.length > 0 then for vChildIndex := 0 to vChildNodes.length - 1 do begin end; except FreeAndNil(Result); raise; end; end; {Puts all Text nodes in the full depth of the sub-tree underneath this Element into a "normal" form where only markup (e.g., tags, comments, processing instructions, CDATA sections, and entity references) separates Text nodes, i.e., there are no adjacent Text nodes. This can be used to ensure that the DOM view of a document is the same as if it were saved and re-loaded, and is useful when operations (such as XPointer lookups) that depend on a particular document tree structure are to be used.} procedure TElement.normalize(); var vChildIndex : TCardinal; vChildNodes : TNodeList; vPreviousNode : TNode; vNode : TNode; begin vChildNodes := GetChildNodes(); vPreviousNode := nil; vChildIndex := 0; if vChildNodes.length > 0 then while vChildIndex < vChildNodes.length do begin vNode := vChildNodes.item(vChildIndex); if (vNode is TText) and (vPreviousNode <> nil) and (vPreviousNode is TText) then begin // adjacent text nodes. // merge into previous node: TText(vPreviousNode).appendData(TText(vNode).data); vChildNodes.RemoveAt(vChildIndex); end else begin vPreviousNode := vNode; Inc(vChildIndex); end; end; end; { TCharacterData } function TCharacterData.GetLength() : TCardinal; inline; begin Result := system.Length(fNodeValue); // FIXME encoding? end; function TCharacterData.substringData(aOffset, aCount : TCardinal) : TDOMString; // raises(DOMException); begin Result := Copy(fNodeValue, aOffset + 1, aCount); end; procedure TCharacterData.appendData(aArg : TDOMString); // raises DOMException. begin fNodeValue := fNodeValue + aArg; end; procedure TCharacterData.insertData(aOffset : TCardinal; aArg : TDOMString); // raises DOMException. begin Insert(aArg, fNodeValue, aOffset + 1); end; procedure TCharacterData.deleteData(aOffset, aCount : TCardinal); // raises DOMException. begin Delete(fNodeValue, aOffset + 1, aCount); end; procedure TCharacterData.replaceData(aOffset, aCount : TCardinal; aArg : TDOMString); // raises DOMException. begin // TODO optimize. Delete(fNodeValue, aOffset + 1, aCount); Insert(aArg, fNodeValue, aOffset + 1); end; { TDocument } constructor TDocument.Create(); begin inherited Create(nil{???}, ntDocument, '#document'); end; procedure TDocument.SetDocumentType(aDocumentType : TDocumentType); begin fDocumentType := aDocumentType; end; procedure TDocument.SetDocumentElement(aDocumentElement : TElement); begin fDocumentElement := aDocumentElement; end; procedure TDocument.SetImplementation(aItem : TDOMImplementation); inline; begin fImplementation := aItem; end; function TDocument.createElement(aTagName : TDOMString) : TElement; // raises DOMException. begin Result := TElement.Create(Self, aTagName, TNamedNodeMap.Create()); end; function TDocument.createDocumentFragment() : TDocumentFragment; // r... begin Result := TDocumentFragment.Create(Self); end; function TDocument.createTextNode(aData : TDOMString) : TText; // r... begin Result := TText.Create(Self, aData); end; function TDocument.createComment(aData : TDOMString) : TComment; // r... begin Result := TComment.Create(Self); end; function TDocument.createCDATASection(aData : TDOMString) : TCDATASection; // raises ... begin Result := TCDATASection.Create(Self); end; function TDocument.createProcessingInstruction(aTarget : TDOMString; aData : TDOMString) : TProcessingInstruction; // raises... begin Result := TProcessingInstruction.Create(Self, aTarget, aData); end; function TDocument.createAttribute(aName : TDOMString) : TAttr; // raises... begin Result := TAttr.Create(Self, aName); end; function TDocument.createEntityReference(aName : TDOMString) : TEntityReference; // raises. begin Result := TEntityReference.Create(Self, aName); end; function TDocument.getElementsByTagName(aTagName : TDOMString) : TNodeList; begin // FIXME. Result := nil; end; { TEntity } constructor TEntity.Create(aOwnerDocument : TDocument; aNodeName, aPublicID, aSystemID, aNotationName : TDOMString; aContents : TDOMString); begin inherited Create(aOwnerDocument, ntEntity, aNodeName); fPublicID := aPublicID; fSystemID := aSystemID; fNDATAName := aNotationName; fContents := aContents; end; { TDocumentType } constructor TDocumentType.Create(aOwnerDocument : TDocument; aNodeName : TDOMString; aChildren : TNodeList {takes ownership}); var vChildIndex : TCardinal; vChildItem : TNode; begin inherited Create(aOwnerDocument, ntDocumentType, aNodeName); SetChildNodes(aChildren); fEntities := TNamedNodeMap.Create(); fNotations := TNamedNodeMap.Create(); if aChildren.length > 0 then for vChildIndex := 0 to aChildren.length - 1 do begin vChildItem := aChildren.item(vChildIndex); if vChildItem is TEntity then begin // TODO check dupes? fEntities.setNamedItem(vChildItem); end else if vChildItem is TNotation then begin // TODO check dupes? fNotations.setNamedItem(vChildItem); end; end; end; destructor TDocumentType.Destroy(); begin FreeAndNil(fEntities); FreeAndNil(fNotations); inherited Destroy(); end; procedure TDocumentType.AddEntity(aEntity : TEntity); begin fEntities.setNamedItem(aEntity); end; function TDocumentType.HasEntityP(const aEntityName : TDOMString) : Boolean; begin Result := fEntities.getNamedItem(aEntityName) <> nil; end; function TDocumentType.GetEntity(const aEntityName : TDOMString) : TEntity; begin Result := fEntities.getNamedItem(aEntityName) as TEntity; end; procedure TDocumentType.AddNotation(aNotation : TNotation); begin fNotations.setNamedItem(aNotation); end; function TDocumentType.HasNotationP(const aName : TDOMString) : Boolean; begin Result := GetNotation(aName) <> nil; end; function TDocumentType.GetNotation(const aName : TDOMString) : TNotation; begin Result := fNotations.getNamedItem(aName) as TNotation; end; { TDocumentFragment } constructor TDocumentFragment.Create(aOwnerDocument : TDocument); begin inherited Create(aOwnerDocument, ntDocumentFragment, '#document-fragment'); end; { TComment } constructor TComment.Create(aOwnerDocument : TDocument); begin inherited Create(aOwnerDocument, ntComment, '#comment'); end; { TCDATASection } constructor TCDATASection.Create(aOwnerDocument : TDocument); begin inherited Create(aOwnerDocument, ntCDATASection, '#cdata-section'); end; { TText } constructor TText.Create(aOwnerDocument : TDocument; aNodeValue : TDOMString); begin inherited Create(aOwnerDocument, ntText, '#text'); SetNodeValue(aNodeValue); end; constructor TText.Create(aOwnerDocument : TDocument; aNodeType : TNodeType; aNodeName : TDOMString); begin inherited Create(aOwnerDocument, aNodeType, aNodeName); end; function TText.splitText(aOffset : TCardinal) : TText; // raises DOMException. var vFirstString : TDOMString; vSecondString : TDOMString; begin EnsureWritable(); //if aOffset < Self.length then begin vFirstString := Self.substringData(0, aOffset); vSecondString := Self.substringData(aOffset, Self.length); Self.data := vSecondString; Result := OwnerDocument.createTextNode(vFirstString); if Assigned(ParentNode) then ParentNode.insertBefore(Result, Self) else ; // FIXME ???? end; { TProcessingInstruction } constructor TProcessingInstruction.Create(aOwnerDocument : TDocument; aTarget, aData : TDOMString); begin inherited Create(aOwnerDocument, ntProcessingInstruction, aTarget); SetNodeValue(aData); end; { TEntityReference } constructor TEntityReference.Create(aOwnerDocument : TDocument; aNodeName : TDOMString); begin inherited Create(aOwnerDocument, ntEntityReference, aNodeName); end; { TDOMImplementation } function TDOMImplementation.hasFeature(const aFeature : TDOMString; const aVersion : TDOMString) : Boolean; begin Result := False; end; { TNodeList } constructor TNodeList.Create(aBOwnsElements : Boolean); begin fList := TObjectList.Create(aBOwnsElements); end; function TNodeList.GetLength() : TCardinal; inline; begin Result := fList.Count; end; function TNodeList.item(aIndex : TCardinal) : TNode; inline; begin Result := TNode(fList[aIndex]); end; procedure TNodeList.Append(aNode : TNode); inline; begin fList.Add(aNode); end; procedure TNodeList.RemoveAt(aIndex : TCardinal); begin fList.Delete(aIndex); end; procedure TNodeList.ReplaceAt(aIndex : TCardinal; aNewNode : TNode); begin // FIXME does this work? (i.e. Free the old node?) fList[aIndex] := aNewNode; end; procedure TNodeList.InsertAt(aIndex : TCardinal; aNewNode : TNode); begin fList.Insert(aIndex, aNewNode); end; destructor TNodeList.Destroy(); begin FreeAndNil(fList); inherited Destroy(); end; { EDOMException } constructor EDOMException.Create(aCode : TDOMExceptionCode); var vString : String; begin vString := 'Unknown error'; case aCode of INDEX_SIZE_ERR: vString := 'index size error.'; DOMSTRING_SIZE_ERR: vString := 'string size error.'; HIERARCHY_REQUEST_ERR: vString := 'hierarchy request error.'; WRONG_DOCUMENT_ERR: vString := 'wrong document.'; INVALID_CHARACTER_ERR: vString := 'invalid character.'; NO_DATA_ALLOWED_ERR: vString := 'no data allowed.'; NO_MODIFICATION_ALLOWED_ERR: vString := 'no modification allowed.'; NOT_FOUND_ERR: vString := 'not found.'; NOT_SUPPORTED_ERR: vString := 'not supported.'; INUSE_ATTRIBUTE_ERR: vString := 'attribute is in use.'; end; fCode := aCode; inherited Create(vString); end; { TNamedNodeMap } constructor TNamedNodeMap.Create(); begin fMap := TFPHashObjectList.Create(True); end; destructor TNamedNodeMap.Destroy(); begin FreeAndNil(fMap); inherited Destroy(); end; function TNamedNodeMap.GetCount() : TCardinal; inline; begin Result := fMap.Count; end; function TNamedNodeMap.getNamedItem(aName : TDOMString) : TNode; begin Result := fMap.Find(aName) as TNode; end; function TNamedNodeMap.setNamedItem(aArg : TNode) : TNode; // raises... begin // FIXME check write protection. Result := removeNamedItem(aArg.NodeName); fMap.Add(aArg.NodeName, aArg); end; // Removes a node specified by name. If the removed node is an Attr with a default value it is immediately replaced. function TNamedNodeMap.removeNamedItem(const aName : TDOMString) : TNode; // raises... DOES NOT FREE THE NODE. var i : Integer; begin i := fMap.FindIndexOf(aName); if i <> -1 then begin fMap.OwnsObjects := False; Result := fMap.Items[i] as TNode; fMap.Delete(i); fMap.OwnsObjects := True; end; end; function TNamedNodeMap.item(aIndex : TCardinal) : TNode; begin Result := fMap.Items[aIndex] as TNode; end; { TDocumentTypeElementDeclaration } constructor TDocumentTypeElementDeclaration.Create(aOwnerDocument : TDocument; aNodeName : TDOMString; aContent : TDocumentTypeBodyItem); begin inherited Create(aOwnerDocument, ntDocumentTypeElement, aNodeName); fContent := aContent; end; destructor TDocumentTypeElementDeclaration.Destroy(); begin FreeAndNil(Self.Content); inherited Destroy(); end; { TDocumentTypeAttributeDeclaration } constructor TDocumentTypeAttributeDeclaration.Create(aOwnerDocument : TDocument; aAttributeName : TDOMString; aElementName : TDOMString; aBaseType_ : TDocumentTypeBodyAttributeBaseType); begin inherited Create(aOwnerDocument, ntDocumentTypeAttribute, aAttributeName); ElementName := aElementName; fBaseType_ := aBaseType_; fDefault_ := nil; end; procedure TDocumentTypeAttributeDeclaration.SetDefault(aDefault : TDocumentTypeBodyAttributeDefault); begin if fDefault_ = aDefault then Exit; if Assigned(fDefault_) then FreeAndNil(fDefault_); fDefault_ := aDefault; end; destructor TDocumentTypeAttributeDeclaration.Destroy(); begin FreeAndNil(Self.fDefault_); //FreeAndNil(fBaseType_); inherited Destroy(); end; { TNotation } constructor TNotation.Create(aOwnerDocument : TDocument; aNodeName, aPublicID, aSystemID : TDOMString); begin inherited Create(aOwnerDocument, ntNotation, aNodeName); fPublicID := aPublicID; fSystemID := aSystemID; end; { TDocumentTypeBodyAttributeDefault } constructor TDocumentTypeBodyAttributeDefault.Create(aImportance : TDocumentTypeBodyAttributeDefaultImportance; aValue : TDOMString); begin fImportance := aImportance; fValue := aValue; end; { TDocumentTypeBodyNotationChoice } destructor TDocumentTypeBodyNotationChoice.Destroy(); begin FreeAndNil(fList); inherited Destroy(); end; constructor TDocumentTypeBodyNotationChoice.Create(aOwnerDocument : TDocument; aAttributeName : TDOMString; aElementName : TDOMString); begin inherited Create(aOwnerDocument, aAttributeName, aElementName, atNotation); fList := TObjectList.Create(True); end; procedure TDocumentTypeBodyNotationChoice.Add(vItem : TNotation); begin fList.Add(vItem); end; { TDocumentTypeAttributeBodyEnumeration } destructor TDocumentTypeAttributeBodyEnumeration.Destroy(); begin FreeAndNil(fList); inherited Destroy(); end; constructor TDocumentTypeAttributeBodyEnumeration.Create(aOwnerDocument : TDocument; aAttributeName : TDOMString; aElementName : TDOMString); begin inherited Create(aOwnerDocument, aAttributeName, aElementName, atEnumeration); fList := TStringList.Create(); end; procedure TDocumentTypeAttributeBodyEnumeration.Append(vItem : TDOMString); begin fList.Add(vItem); end; var vDOMImplementation : TDOMImplementation = nil; function getDOMImplementation() : TDOMImplementation; begin if not Assigned(vDOMImplementation) then vDOMImplementation := TDOMImplementation.Create(); Result := vDOMImplementation; end; initialization finalization FreeAndNil(vDOMImplementation); end.