XMLStyle.Mod 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. MODULE XMLStyle; (** Stefan Walthert *)
  2. (** AUTHOR "swalthert"; PURPOSE ""; *)
  3. IMPORT
  4. XMLObjects, CSS2, XML, XMLComponents;
  5. TYPE
  6. String= CSS2.String;
  7. SelectorRuleSet = RECORD
  8. selector: CSS2.Selector;
  9. ruleSet: CSS2.RuleSet;
  10. order: LONGINT
  11. END;
  12. PROCEDURE AttachStyle*(root: XML.Element; css: CSS2.StyleSheet);
  13. VAR selRS: POINTER TO ARRAY OF SelectorRuleSet; noSel: LONGINT;
  14. ruleSets, selectors, simpleSelectors: XMLObjects.Enumerator; ruleSet, selector, simpleSelector: ANY;
  15. propChanger: XMLComponents.PropertyChanger; hasDynamic: BOOLEAN;
  16. BEGIN
  17. IF (root = NIL) OR (css = NIL) THEN RETURN END;
  18. noSel := 0;
  19. (* compute number of rule sets in style sheet *)
  20. ruleSets := css.GetRuleSets();
  21. WHILE ruleSets.HasMoreElements() DO
  22. ruleSet := ruleSets.GetNext();
  23. selectors := ruleSet(CSS2.RuleSet).GetSelectors();
  24. WHILE selectors.HasMoreElements() DO
  25. selector := selectors.GetNext();
  26. INC(noSel)
  27. END
  28. END;
  29. NEW(selRS, noSel);
  30. (* store rule sets of style sheet in array *)
  31. noSel := 0;
  32. ruleSets := css.GetRuleSets();
  33. WHILE ruleSets.HasMoreElements() DO
  34. ruleSet := ruleSets.GetNext();
  35. selectors := ruleSet(CSS2.RuleSet).GetSelectors();
  36. WHILE selectors.HasMoreElements() DO
  37. selector := selectors.GetNext();
  38. selRS[noSel].selector := selector(CSS2.Selector);
  39. selRS[noSel].ruleSet := ruleSet(CSS2.RuleSet);
  40. selRS[noSel].order := noSel;
  41. INC(noSel)
  42. END
  43. END;
  44. (* sort selRS by specifity of selectors (selRS[].selector.GetSpecifity(a, b, c)) and their original order *)
  45. HeapSort(selRS^);
  46. (* attach style of unimportant declarations *)
  47. FOR noSel := 0 TO LEN(selRS) - 1 DO
  48. IF selRS[noSel].ruleSet.HasNotImportantDeclarations() THEN
  49. simpleSelectors := selRS[noSel].selector.GetSimpleSelectors();
  50. IF simpleSelectors.HasMoreElements() THEN
  51. simpleSelector := simpleSelectors.GetNext(); NEW(propChanger); hasDynamic := FALSE;
  52. FindMatch(root, simpleSelector(CSS2.SimpleSelector), selRS[noSel].ruleSet,
  53. propChanger, hasDynamic, FALSE)
  54. END
  55. END
  56. END;
  57. (* attach style of important declarations *)
  58. FOR noSel := 0 TO LEN(selRS) - 1 DO
  59. IF selRS[noSel].ruleSet.HasImportantDeclarations() THEN
  60. simpleSelectors := selRS[noSel].selector.GetSimpleSelectors();
  61. IF simpleSelectors.HasMoreElements() THEN
  62. simpleSelector := simpleSelectors.GetNext(); NEW(propChanger); hasDynamic := FALSE;
  63. FindMatch(root, simpleSelector(CSS2.SimpleSelector), selRS[noSel].ruleSet,
  64. propChanger, hasDynamic, TRUE)
  65. END
  66. END
  67. END
  68. END AttachStyle;
  69. PROCEDURE HeapSort(VAR selRS: ARRAY OF SelectorRuleSet);
  70. VAR left, right: LONGINT; elem: SelectorRuleSet;
  71. PROCEDURE Sift(left, right: LONGINT);
  72. VAR i, j: LONGINT; elem: SelectorRuleSet;
  73. PROCEDURE Less(VAR elem1, elem2: SelectorRuleSet): BOOLEAN;
  74. VAR a1, a2, b1, b2, c1, c2: LONGINT;
  75. BEGIN
  76. elem1.selector.GetSpecifity(a1, b1, c1); elem2.selector.GetSpecifity(a2, b2, c2);
  77. RETURN (a1 < a2) OR ((a1 = a2) & (b1 < b2)) OR ((a1 = a2) & (b1 = b2) & (c1 < c2))
  78. OR ((a1 = a2) & (b1 = b2) & (c1 = c2) & (elem1.order < elem2.order))
  79. END Less;
  80. BEGIN
  81. i := left; j := 2 * left; elem := selRS[left];
  82. IF (j < right) & Less(selRS[j], selRS[j + 1]) THEN INC(j) END;
  83. WHILE (j <= right) & Less(elem, selRS[j]) DO
  84. selRS[i] := selRS[j]; i := j; j := 2 * j;
  85. IF (j < right) & Less(selRS[j], selRS[j + 1]) THEN INC(j) END;
  86. END;
  87. selRS[i] := elem
  88. END Sift;
  89. BEGIN
  90. left := LEN(selRS) DIV 2 + 1; right := LEN(selRS) - 1;
  91. WHILE left > 0 DO DEC(left); Sift(left, right) END;
  92. WHILE right > 0 DO
  93. elem := selRS[0]; selRS[0] := selRS[right]; selRS[right] := elem;
  94. DEC(right); Sift(left, right)
  95. END
  96. END HeapSort;
  97. PROCEDURE FindMatch(elem: XML.Element; simpleSelector: CSS2.SimpleSelector; ruleSet: CSS2.RuleSet;
  98. propChanger: XMLComponents.PropertyChanger; VAR hasDynamic: BOOLEAN; important: BOOLEAN);
  99. VAR children: XMLObjects.Enumerator; child: ANY; nextSimpleSelector: CSS2.SimpleSelector; sibling: XML.Element;
  100. match: BOOLEAN;
  101. BEGIN
  102. nextSimpleSelector := simpleSelector.GetNext();
  103. match := MatchSimpleSelector(elem, simpleSelector, propChanger, hasDynamic);
  104. IF (nextSimpleSelector = NIL) & match & (elem IS XMLComponents.CSS2Component) THEN
  105. IF hasDynamic THEN propChanger.SetChangingComponent(elem(XMLComponents.CSS2Component), ruleSet)
  106. ELSE AttachStyleToComponent(elem(XMLComponents.CSS2Component), ruleSet, important)
  107. END
  108. END;
  109. IF (nextSimpleSelector # NIL) & match THEN
  110. CASE nextSimpleSelector.GetCombinator() OF
  111. | CSS2.Sibling:
  112. sibling := elem.GetNextSibling();
  113. IF sibling # NIL THEN
  114. FindMatch(sibling, nextSimpleSelector, ruleSet, propChanger.Copy(), hasDynamic, important)
  115. END
  116. | CSS2.Child, CSS2.Descendant:
  117. children := elem.GetContents();
  118. IF SelectFirstChild(nextSimpleSelector) THEN
  119. child := children.GetNext();
  120. IF (child # NIL) & (child IS XML.Element) THEN
  121. FindMatch(child(XML.Element), nextSimpleSelector, ruleSet, propChanger.Copy(), hasDynamic, important)
  122. END
  123. ELSE
  124. WHILE children.HasMoreElements() DO
  125. child := children.GetNext();
  126. IF child IS XML.Element THEN
  127. FindMatch(child(XML.Element), nextSimpleSelector, ruleSet, propChanger.Copy(), hasDynamic, important)
  128. END
  129. END
  130. END
  131. ELSE
  132. END
  133. END;
  134. IF simpleSelector.GetCombinator() = CSS2.Descendant THEN
  135. children := elem.GetContents();
  136. WHILE children.HasMoreElements() DO
  137. child := children.GetNext();
  138. IF child IS XML.Element THEN
  139. FindMatch(child(XML.Element), simpleSelector, ruleSet, propChanger.Copy(), hasDynamic, important)
  140. END
  141. END
  142. END
  143. END FindMatch;
  144. PROCEDURE MatchSimpleSelector(elem: XML.Element; simpleSelector: CSS2.SimpleSelector;
  145. propChanger: XMLComponents.PropertyChanger; VAR hasDynamic: BOOLEAN): BOOLEAN;
  146. VAR s1, s2: String; enum: XMLObjects.Enumerator; c: ANY; match: BOOLEAN;
  147. BEGIN
  148. s1 := elem.GetName();
  149. s2 := simpleSelector.GetElementName();
  150. IF (s2 = NIL) OR (s2^ = "*") OR (s1^ = s2^) THEN
  151. enum := simpleSelector.GetSubSelectors();
  152. match := TRUE;
  153. WHILE enum.HasMoreElements() & match DO
  154. c := enum.GetNext();
  155. match := MatchSubSelector(elem, c(CSS2.SubSelector), propChanger, hasDynamic)
  156. END;
  157. RETURN match
  158. ELSE RETURN FALSE
  159. END
  160. END MatchSimpleSelector;
  161. PROCEDURE MatchSubSelector(elem: XML.Element; subSelector: CSS2.SubSelector;
  162. propChanger: XMLComponents.PropertyChanger; VAR hasDynamic: BOOLEAN): BOOLEAN;
  163. VAR s1, s2: String; rel: SHORTINT; attribute: XML.Attribute;
  164. BEGIN
  165. IF subSelector IS CSS2.Id THEN
  166. s1 := elem.GetId(); s2 := subSelector(CSS2.Id).GetValue();
  167. RETURN (s1 # NIL) & (s2 # NIL) & (s1^ = s2^)
  168. ELSIF subSelector IS CSS2.Class THEN
  169. WITH subSelector: CSS2.Class DO
  170. attribute := elem.GetAttribute("class");
  171. IF attribute # NIL THEN
  172. s1 := attribute.GetValue();
  173. s2 := subSelector.GetValue();
  174. RETURN s1^ = s2^
  175. ELSE
  176. RETURN FALSE
  177. END
  178. END
  179. ELSIF subSelector IS CSS2.Attribute THEN
  180. WITH subSelector: CSS2.Attribute DO
  181. s1 := subSelector.GetName();
  182. rel := subSelector.GetRelation();
  183. attribute := elem.GetAttribute(s1^);
  184. IF attribute # NIL THEN
  185. IF rel = CSS2.Undefined THEN
  186. RETURN TRUE
  187. ELSE
  188. s1 := attribute.GetValue();
  189. s2 := subSelector.GetValue();
  190. IF rel = CSS2.Equal THEN
  191. RETURN s1^ = s2^
  192. ELSIF rel = CSS2.Includes THEN (* not implemented *)
  193. RETURN FALSE
  194. ELSIF rel = CSS2.Dashmatch THEN (* not implemented *)
  195. RETURN FALSE
  196. END
  197. END
  198. ELSE
  199. RETURN FALSE
  200. END
  201. END
  202. ELSIF subSelector IS CSS2.Pseudo THEN
  203. s1 := subSelector(CSS2.Pseudo).GetType();
  204. IF s1 = NIL THEN
  205. RETURN FALSE
  206. ELSIF s1^ = "first-child" THEN
  207. RETURN TRUE
  208. ELSE
  209. IF elem IS XMLComponents.VisualComponent THEN
  210. hasDynamic := TRUE;
  211. propChanger.AddListenedComponent(elem(XMLComponents.CSS2Component), s1^);
  212. RETURN TRUE
  213. ELSE
  214. RETURN FALSE
  215. END
  216. END
  217. ELSE
  218. END
  219. END MatchSubSelector;
  220. PROCEDURE AttachStyleToComponent(comp: XMLComponents.CSS2Component; ruleSet: CSS2.RuleSet; important: BOOLEAN);
  221. VAR declarations: XMLObjects.Enumerator; declaration: ANY;
  222. BEGIN
  223. declarations := ruleSet.GetDeclarations();
  224. WHILE declarations.HasMoreElements() DO
  225. declaration := declarations.GetNext();
  226. IF declaration(CSS2.Declaration).IsImportant() = important THEN
  227. comp.properties.SetValue(declaration(CSS2.Declaration))
  228. END
  229. END
  230. END AttachStyleToComponent;
  231. PROCEDURE SelectFirstChild(simpleSelector: CSS2.SimpleSelector): BOOLEAN;
  232. VAR subSelectors: XMLObjects.Enumerator; c: ANY; s: String;
  233. BEGIN
  234. subSelectors := simpleSelector.GetSubSelectors();
  235. WHILE subSelectors.HasMoreElements() DO
  236. c := subSelectors.GetNext();
  237. IF (c IS CSS2.Pseudo) THEN
  238. s := c(CSS2.Pseudo).GetType();
  239. IF (s # NIL) & (s^ = "first-child") THEN RETURN TRUE END
  240. END
  241. END;
  242. RETURN FALSE
  243. END SelectFirstChild;
  244. END XMLStyle.