'From Pharo6.0 of 13 May 2016 [Latest update: #60510] on 9 September 2017 at 9:59:54.260227 pm'!
Object subclass: #ZnHtmlOutputStream
instanceVariableNames: 'stream'
classVariableNames: ''
poolDictionaries: ''
category: 'Zinc-HTTP-Streaming'!
!ZnHtmlOutputStream commentStamp: 'SvenVanCaekenberghe 11/30/2015 22:06' prior: 0!
I am ZnHtmlOutputStream. I wrap another character write stream to offer a richer API for generating correct HTML markup.
See https://en.wikipedia.org/wiki/HTML
My streaming protocol contains the traditional write stream operations. These are raw and do not do any conversions/escaping.
My html protocols contains a rich API for generating correct HTML.
String streamContents: [ :out | | html |
html := ZnHtmlOutputStream on: out.
html html5.
html tag: #html do: [
html tag: #body do: [
html tag: #div class: #main do: [
html tag: #p with: 'Hello World & Universe !!'.
html tag: #hr.
html
tag: #em
attributes: #(class big id 1 disable nil)
with: 'The END' ] ] ] ].
ZnHtmlOutputStream streamContents: [ :html |
html page: 'Hello World' do: [
html tag: #div class: #main do: [
html tag: #p with: 'Hello World & Universe !!' ] ] ]
Part of Zinc HTTP Components.!
!ZnHtmlOutputStream methodsFor: 'initialize-release' stamp: 'SvenVanCaekenberghe 11/25/2015 10:40'!
on: writeStream
stream := writeStream! !
!ZnHtmlOutputStream methodsFor: 'initialize-release' stamp: 'SvenVanCaekenberghe 11/25/2015 10:40'!
close
stream close! !
!ZnHtmlOutputStream methodsFor: 'html' stamp: 'SvenVanCaekenberghe 11/30/2015 21:43'!
escapeCharacter: char
"Write char, escaping it as needed for regular text"
char == $<
ifTrue: [ stream nextPutAll: '<' ]
ifFalse: [
char == $&
ifTrue: [ stream nextPutAll: '&' ]
ifFalse: [ stream nextPut: char ] ]! !
!ZnHtmlOutputStream methodsFor: 'html' stamp: 'SvenVanCaekenberghe 11/30/2015 21:50'!
escapeAttributeValue: string
"Write string, escaping characters as needed for the value of an attribute"
string do: [ :each |
each == $"
ifTrue: [ stream nextPutAll: '"' ]
ifFalse: [ stream nextPut: each ] ] ! !
!ZnHtmlOutputStream methodsFor: 'html' stamp: 'SvenVanCaekenberghe 11/30/2015 21:41'!
comment: string
"Write an HTML/XML comment consisting of string, as in "
stream
nextPutAll: ''! !
!ZnHtmlOutputStream methodsFor: 'html' stamp: 'SvenVanCaekenberghe 11/30/2015 21:51'!
page: title do: block
"Write out a standard page using title, then execute block"
"ZnHtmlOutputStream streamContents: [ :html | html page: 'Hello' do: [ html tag: #p with: 'World' ] ]"
self html5; tag: #html do: [
self
tag: #head do: [ self tag: #title with: title ];
tag: #body do: [
self tag: #h1 with: title.
block value ] ]! !
!ZnHtmlOutputStream methodsFor: 'html' stamp: 'SvenVanCaekenberghe 11/30/2015 21:42'!
escape: string
"Write string, escaping characters as needed for regular text"
string do: [ :each | self escapeCharacter: each ]! !
!ZnHtmlOutputStream methodsFor: 'html' stamp: 'SvenVanCaekenberghe 11/30/2015 21:50'!
format: string with: args
"Like String>>#format: format the string template using the args given, esacaping characters when needed for regular text"
| input currentChar |
input := string readStream.
[ input atEnd ] whileFalse: [
(currentChar := input next) == ${
ifTrue: [ | expression index |
expression := input upTo: $}.
index := Integer readFrom: expression ifFail: [ expression ].
self escape: (args at: index) asString ]
ifFalse: [
currentChar == $\
ifTrue: [ input atEnd ifFalse: [ self escapeCharacter: stream next ] ]
ifFalse: [ self escapeCharacter: currentChar ] ] ]! !
!ZnHtmlOutputStream methodsFor: 'html' stamp: 'SvenVanCaekenberghe 11/30/2015 21:45'!
str: string
"Write string, escaping characters as needed for regular text"
self escape: string! !
!ZnHtmlOutputStream methodsFor: 'html' stamp: 'SvenVanCaekenberghe 11/30/2015 21:44'!
html5
"Write the standard HTML5 DOCTYPE tag"
stream nextPutAll: ''! !
!ZnHtmlOutputStream methodsFor: 'private html' stamp: 'SvenVanCaekenberghe 11/25/2015 16:03'!
renderAttributes: attributes
attributes pairsDo: [ :name :value |
stream space; nextPutAll: name.
value ifNotNil: [
stream nextPutAll: '="'.
self escapeAttributeValue: value asString.
stream nextPut: $" ] ]! !
!ZnHtmlOutputStream methodsFor: 'private html' stamp: 'SvenVanCaekenberghe 11/25/2015 16:04'!
openTag: tag attributes: attributes
stream
nextPut: $<;
nextPutAll: tag.
self renderAttributes: attributes.
stream nextPut: $>! !
!ZnHtmlOutputStream methodsFor: 'private html' stamp: 'SvenVanCaekenberghe 11/25/2015 11:23'!
closeTag: tag
stream
nextPut: $<;
nextPut: $/;
nextPutAll: tag;
nextPut: $>! !
!ZnHtmlOutputStream methodsFor: 'private html' stamp: 'SvenVanCaekenberghe 11/25/2015 11:21'!
openTag: tag
stream
nextPut: $<;
nextPutAll: tag;
nextPut: $>! !
!ZnHtmlOutputStream methodsFor: 'private html' stamp: 'SvenVanCaekenberghe 11/25/2015 15:45'!
openTag: tag attribute: name value: value
stream
nextPut: $<;
nextPutAll: tag;
space;
nextPutAll: name.
value ifNotNil: [
stream nextPutAll: '="'.
self escapeAttributeValue: value asString.
stream nextPut: $" ].
stream nextPut: $>! !
!ZnHtmlOutputStream methodsFor: 'html tags' stamp: 'SvenVanCaekenberghe 11/30/2015 21:55'!
tag: tag attributes: attributes
"Write a standalone, self closing HTML tag with attributes, a collection of alternating keys and values"
""
stream
nextPut: $<;
nextPutAll: tag.
self renderAttributes: attributes.
stream
nextPut: $/;
nextPut: $>! !
!ZnHtmlOutputStream methodsFor: 'html tags' stamp: 'SvenVanCaekenberghe 11/30/2015 22:01'!
tag: tag do: block
"Write an HTML tag, executing block to generate enclosed content"
"..."
self openTag: tag.
block value.
self closeTag: tag! !
!ZnHtmlOutputStream methodsFor: 'html tags' stamp: 'SvenVanCaekenberghe 11/30/2015 21:56'!
tag: tag attributes: attributePairs with: string
"Write an HTML tag with attributes, a collection of alternating keys and values, using the escaped string as content"
"string"
self openTag: tag attributes: attributePairs.
self escape: string.
self closeTag: tag ! !
!ZnHtmlOutputStream methodsFor: 'html tags' stamp: 'SvenVanCaekenberghe 11/30/2015 22:00'!
tag: tag id: cssId with: string
"Write an HTML tag with id cssId, using the escaped string as content"
"string"
self openTag: tag attribute: #id value: cssId.
self escape: string.
self closeTag: tag ! !
!ZnHtmlOutputStream methodsFor: 'html tags' stamp: 'SvenVanCaekenberghe 11/30/2015 21:55'!
tag: tag attributes: attributePairs do: block
"Write an HTML tag with attributes, a collection of alternating keys and values, executing block to generate enclosed content"
" ... "
self openTag: tag attributes: attributePairs.
block value.
self closeTag: tag! !
!ZnHtmlOutputStream methodsFor: 'html tags' stamp: 'SvenVanCaekenberghe 11/30/2015 21:57'!
tag: tag class: cssClass do: block
"Write an HTML tag with class cssClass, executing block to generate enclosed content"
" ... "
self openTag: tag attribute: #class value: cssClass.
block value.
self closeTag: tag! !
!ZnHtmlOutputStream methodsFor: 'html tags' stamp: 'SvenVanCaekenberghe 11/30/2015 21:59'!
tag: tag id: cssId do: block
"Write an HTML tag with id cssId, executing block to generate enclosed content"
" ... "
self openTag: tag attribute: #id value: cssId.
block value.
self closeTag: tag! !
!ZnHtmlOutputStream methodsFor: 'html tags' stamp: 'SvenVanCaekenberghe 11/30/2015 21:58'!
tag: tag class: cssClass with: string
"Write an HTML tag with class cssClass, using the escaped string as content"
"string"
self openTag: tag attribute: #class value: cssClass.
self escape: string.
self closeTag: tag ! !
!ZnHtmlOutputStream methodsFor: 'html tags' stamp: 'SvenVanCaekenberghe 11/30/2015 22:01'!
tag: tag with: string
"Write an HTML tag using the escaped string as content"
"string"
self openTag: tag.
self escape: string.
self closeTag: tag ! !
!ZnHtmlOutputStream methodsFor: 'html tags' stamp: 'SvenVanCaekenberghe 11/30/2015 21:55'!
tag: tag
"Write a standalone, self closing HTML tag"
""
stream
nextPut: $<;
nextPutAll: tag;
nextPut: $/;
nextPut: $>! !
!ZnHtmlOutputStream methodsFor: 'streaming' stamp: 'SvenVanCaekenberghe 11/25/2015 10:46'!
next: count putAll: collection startingAt: offset
"Write count characters from collection starting at offset."
stream
next: count
putAll: collection
startingAt: offset! !
!ZnHtmlOutputStream methodsFor: 'streaming' stamp: 'SvenVanCaekenberghe 11/25/2015 15:50'!
<< collection
^ self nextPutAll: collection asString! !
!ZnHtmlOutputStream methodsFor: 'streaming' stamp: 'SvenVanCaekenberghe 11/25/2015 10:46'!
nextPut: object
^ stream nextPut: object! !
!ZnHtmlOutputStream methodsFor: 'streaming' stamp: 'SvenVanCaekenberghe 11/25/2015 10:43'!
flush
stream flush! !
!ZnHtmlOutputStream methodsFor: 'streaming' stamp: 'SvenVanCaekenberghe 11/25/2015 10:43'!
next: count putAll: collection
self
next: count
putAll: collection
startingAt: 1! !
!ZnHtmlOutputStream methodsFor: 'streaming' stamp: 'SvenVanCaekenberghe 11/25/2015 10:43'!
nextPutAll: collection
self
next: collection size
putAll: collection
startingAt: 1! !
!ZnHtmlOutputStream methodsFor: 'streaming' stamp: 'SvenVanCaekenberghe 11/25/2015 10:43'!
print: object
object printOn: self! !
!ZnHtmlOutputStream methodsFor: 'streaming' stamp: 'SvenVanCaekenberghe 11/25/2015 10:43'!
space
self nextPut: Character space! !
!ZnHtmlOutputStream methodsFor: 'accessing' stamp: 'SvenVanCaekenberghe 11/25/2015 10:40'!
wrappedStream
^ stream! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
ZnHtmlOutputStream class
instanceVariableNames: ''!
!ZnHtmlOutputStream class methodsFor: 'instance creation' stamp: 'SvenVanCaekenberghe 11/25/2015 10:39'!
on: writeStream
^ self new
on: writeStream;
yourself! !
!ZnHtmlOutputStream class methodsFor: 'convenience' stamp: 'SvenVanCaekenberghe 11/30/2015 22:04'!
streamContents: block
"Execute block with a ZnHtmlOutputStream as argument to generate HTML, returning the resulting string"
"ZnHtmlOutputStream streamContents: [ :html |
html page: 'Hello World' do: [
html tag: #p with: 'Hello World & Universe !!' ] ]"
^ String streamContents: [ :out |
block value: (self on: out) ]! !