Coverage for phml\nodes\element.py: 60%

48 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-11-30 09:38 -0600

1from __future__ import annotations 

2 

3from typing import TYPE_CHECKING, Optional 

4 

5from .parent import Parent 

6 

7if TYPE_CHECKING: 

8 from .position import Position 

9 from .root import Root 

10 from .types import Properties 

11 

12 

13class Element(Parent): 

14 """Element (Parent) represents an Element ([DOM]). 

15 

16 A tagName field must be present. It represents the element's local name ([DOM]). 

17 

18 The properties field represents information associated with the element. 

19 The value of the properties field implements the Properties interface. 

20 

21 If the tagName field is 'template', a content field can be present. The value 

22 of the content field implements the Root interface. 

23 

24 If the tagName field is 'template', the element must be a leaf. 

25 

26 If the tagName field is 'noscript', its children should be represented as if 

27 scripting is disabled ([HTML]). 

28 

29 

30 For example, the following HTML: 

31 

32 ```html 

33 <a href="https://alpha.com" class="bravo" download></a> 

34 ``` 

35 

36 Yields: 

37 

38 ```javascript 

39 { 

40 type: 'element', 

41 tagName: 'a', 

42 properties: { 

43 href: 'https://alpha.com', 

44 className: ['bravo'], 

45 download: true 

46 }, 

47 children: [] 

48 } 

49 ``` 

50 """ 

51 

52 def __init__( 

53 self, 

54 tag: str = "element", 

55 properties: Optional[Properties] = {}, 

56 parent: Optional[Element | Root] = None, 

57 startend: bool = False, 

58 position: Optional[Position] = None, 

59 children: Optional[list] = None, 

60 ): 

61 super().__init__(position, children) 

62 self.properties = properties 

63 self.tag = tag 

64 self.startend = startend 

65 self.parent = parent 

66 self.locals = {} 

67 

68 def __eq__(self, obj) -> bool: 

69 if obj is None: 

70 return False 

71 

72 if obj.type == self.type: 

73 if self.tag != obj.tag: 

74 return False 

75 if self.startend != obj.startend: 

76 return False 

77 if self.properties != obj.properties: 

78 return False 

79 

80 for c, oc in zip(self.children, obj.children): 

81 if c != oc: 

82 return False 

83 return True 

84 else: 

85 return False 

86 

87 def start_tag(self) -> str: 

88 """Builds the open/start tag for the element. 

89 

90 Note: 

91 It will return `/>` if the tag is self closing. 

92 

93 Returns: 

94 str: Built element start tag. 

95 """ 

96 opening = f"<{self.tag}" 

97 

98 attributes = [] 

99 for prop in self.properties: 

100 if isinstance(self.properties[prop], bool) or self.properties[prop] in ["yes", "no"]: 

101 if self.properties[prop] == "yes" or self.properties[prop]: 

102 attributes.append(prop) 

103 else: 

104 attributes.append(f'{prop}="{self.properties[prop]}"') 

105 if len(attributes) > 0: 

106 attributes = " " + " ".join(attributes) 

107 else: 

108 attributes = "" 

109 

110 closing = f"{' /' if self.startend else ''}>" 

111 

112 return opening + attributes + closing 

113 

114 def end_tag(self) -> str: 

115 """Build the elements end tag. 

116 

117 Returns: 

118 str: Built element end tag. 

119 """ 

120 return f"</{self.tag}>" if not self.startend else None 

121 

122 def __repr__(self) -> str: 

123 out = f"{self.type}(tag: {self.tag}, properties: {self.properties}, children: {len(self.children)})" 

124 return out