1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # HTML output facilities
20 # You can define subclass and override methods head and body
24 # redef body do add("p").text("Hello World!")
27 # HTMLPage use fluent interface so you can chain calls as:
28 # add("div").attr("id", "mydiv").text("My Div")
36 private var root
= new HTMLTag("html")
37 private var current
: HTMLTag = root
38 private var stack
= new List[HTMLTag]
40 # Render the page as a html string
49 return "<!DOCTYPE html>{root.html}"
52 # Add a html tag to the current element
53 # add("div").attr("id", "mydiv").text("My Div")
54 fun add
(tag
: String): HTMLTag do
55 var node
= new HTMLTag(tag
)
60 # Add a raw html string
61 # add_html("<a href='#top'>top</a>")
62 fun add_html
(html
: String) do current
.add
(new HTMLRaw(html
))
66 # add("li").text("item1")
67 # add("li").text("item2")
69 fun open
(tag
: String): HTMLTag do
75 # Close previously opened tag
76 # Ensure: tag = previous.tag
77 fun close
(tag
: String) do
78 if not tag
== current
.tag
then
79 print
"Error: Trying to close '{tag}', last opened tag was '{current.tag}'."
85 # Save html page in the specified file
86 fun save
(file
: String) do
87 var out
= new OFStream.open
(file
)
88 out
.write
(self.render
)
94 # HTML tagname: 'div' for <div></div>
96 init(tag
: String) do self.tag
= tag
98 init with_attrs
(tag
: String, attrs
: Map[String, String]) do
104 var attrs
: Map[String, String] = new HashMap[String, String]
106 # Get the attributed value of 'prop' or null if 'prop' is undifened
107 fun get_attr
(key
: String): nullable String do
108 if not attrs
.has_key
(key
) then return null
112 # Set a 'value' for 'key'
113 # var img = new HTMLTag("img")
114 # img.attr("src", "./image.png").attr("alt", "image")
115 fun attr
(key
: String, value
: String): HTMLTag do
120 # Add a CSS class to the HTML tag
121 # var img = new HTMLTag("img")
122 # img.add_class("logo").add_class("fullpage")
123 fun add_class
(klass
: String): HTMLTag do
127 private var classes
: Set[String] = new HashSet[String]
129 # Add multiple CSS classes
130 fun add_classes
(classes
: Collection[String]): HTMLTag do
131 self.classes
.add_all
(classes
)
135 # Set a CSS 'value' for 'prop'
136 # var img = new HTMLTag("img")
137 # img.css("border", "2px solid black").css("position", "absolute")
138 fun css
(prop
: String, value
: String): HTMLTag do
139 css_props
[prop
] = value
142 private var css_props
: Map[String, String] = new HashMap[String, String]
144 # Get CSS value for 'prop'
145 fun get_css
(prop
: String): nullable String do
146 if not css_props
.has_key
(prop
) then return null
147 return css_props
[prop
]
150 # Add a HTML 'child' to self
151 # var ul = new HTMLTag("ul")
152 # ul.add(new HTMLTag("li"))
153 fun add
(child
: HTMLTag) do children
.add
(child
)
155 # List of children HTML elements
156 var children
: Set[HTMLTag] = new HashSet[HTMLTag]
158 # Set text of element
159 # var p = new HTMLTag("p")
160 # p.text("Hello World!")
161 # Text is escaped see: standard::String::html_escape
162 fun text
(txt
: String): HTMLTag do
167 private var content
= new Buffer
169 # Append text to element
170 # var p = new HTMLTag("p")
171 # p.append("Hello").append("<br/>").append("World!")
172 # Text is escaped see: standard::String::html_escape
173 fun append
(txt
: String): HTMLTag do
178 # Render the element as HTML string
180 var attrs
= render_attrs
181 var content
= render_content
185 if tag
!= "script" and content
.is_empty
then
190 str
.append
("</{tag}>")
195 private fun render_attrs
: String do
196 var cls
= render_classes
199 if not cls
.is_empty
then
201 str
.append
(render_classes
)
203 if not css
.is_empty
then
205 str
.append
(render_css
)
207 if not attrs
.is_empty
then str
.append
(" ")
209 for key
, value
in attrs
do
210 if key
== "class" or key
== "style" then continue
211 str
.append
("{key}=\"{value}\
"")
212 if count
< attrs
.length
- 1 then
220 private fun render_css
: String do
221 if not attrs
.has_key
("style") and css_props
.is_empty
then return ""
223 css
.append
("style=\"")
224 if attrs.has_key("style
") then css.append("{attrs["style"]}; ")
225 css.append(css_props.join("; ", ": "))
230 private fun render_classes
: String do
231 if not attrs
.has_key
("class") and classes
.is_empty
then return ""
233 cls
.append
("class=\"")
234 if attrs.has_key("class") then cls.append("{attrs["class"]} ")
235 cls.append(classes.join(" "))
240 private fun render_content
: String do
242 str
.append
(content
.to_s
.html_escape
)
243 for child
in children
do
244 str
.append
(child
.html
)
250 private class HTMLRaw
253 init(content
: String) do self.content
.append
(content
)
254 redef fun html
do return content
.to_s