Silk.st 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. Smalltalk createPackage: 'Silk'!
  2. Domite subclass: #Silk
  3. slots: {}
  4. package: 'Silk'!
  5. !Silk commentStamp!
  6. I am subclass of `Domite` with more convenient high-level API.
  7. ##Rendering
  8. - `aSilk << anObject` uses double-dispatch via `renderOnSilk:`.
  9. This allows to create widgets
  10. (no formal superclass, anything with `renderOnSilk:` is a widget),
  11. as well as incorporating magic on certain types of objects:
  12. - blocks: `aSilk << aBlock` runs the block, passing aSilk as a parameter.
  13. - associations: `aSilk << (key -> value)` set attribute key to value.
  14. Worthful to note is, rendering a collection has its magic
  15. already built-in (via `putOn:`) -- if you `stream << aCollection`,
  16. its items are `<<`'d in sequence.
  17. So, de facto, arrays are deeply flattened when put on a stream via `<<`.
  18. ##Convenience
  19. - `aCssSelectorString asSilk` returns Silk wrapping an element at a selector.
  20. - `anObject inSilk` returns anObject rendered in a document fragment.
  21. ##Element creation
  22. These messages use DNU to dynamically create
  23. elements with any (letters-and-numbers) tag name,
  24. Next samples show this on an example of `<div>`.
  25. - `Silk DIV` is shortcut for `Silk newElement: 'div'`.
  26. - `aSilk DIV` is shortcut for
  27. `[ |tmp| tmp := Silk DIV. aSilk << tmp. tmp] value`.
  28. IOW, it not just creates the element and returns it,
  29. but also puts in on aSilk.
  30. - `aSilk DIV: anObject` is shortcut for
  31. `aSilk DIV << anObject; yourself`.
  32. IOW, it not just creates and inserts the element,
  33. but puts a content into it.
  34. ##Conclusions
  35. Taken all this together, one can do pretty neat constructs:
  36. ```
  37. aSilk P: { 'id'->'mission'. 'We are the champions.' }
  38. ```
  39. adds `<p id="mission">We are the champions.</p>` into `aSilk`
  40. and returns the Silk-wrapped `<p>` with insertion cursor at the end.!
  41. !Silk methodsFor: 'accessing'!
  42. namespace
  43. "<String>
  44. XML namespace for elements: html.
  45. The default for all virtual Silk tag messages"
  46. ^self element namespaceURI
  47. ! !
  48. !Silk methodsFor: 'error handling'!
  49. doesNotUnderstand: aMessage
  50. "`aSilk DIV` creates a div element and inserts it.
  51. `aSilk DIV: anObject` creates a div element, inserts it
  52. and puts contents in it"
  53. (self class tryMakeDnuElement: aMessage in: self)
  54. ifNil: [ ^ super doesNotUnderstand: aMessage ]
  55. ifNotNil: [ :newElement | self << newElement. ^ newElement ]
  56. ! !
  57. !Silk methodsFor: 'writing'!
  58. nextPut: anObject
  59. "Double-dispatches anObject via renderOnSilk: message.
  60. If a message returns nil, this fallbacks to superclass.
  61. Otherwise, it is assumed renderOnSilk: did its job."
  62. (anObject renderOnSilk: self)
  63. ifNil: [ super nextPut: anObject ]
  64. ! !
  65. !Silk class methodsFor: 'accessing'!
  66. htmlNamespace
  67. "<String>
  68. XML namespace for HTML elements.
  69. The default for all virtual Silk tag messages"
  70. ^'http://www.w3.org/1999/xhtml'
  71. !
  72. namespace
  73. "<String>
  74. XML namespace for elements: html.
  75. The default for all virtual Silk tag messages"
  76. ^self htmlNamespace
  77. ! !
  78. !Silk class methodsFor: 'instance creation'!
  79. newElement: aString in: aSilk
  80. "<Silk>
  81. creates a new element in the same xml namespace as aSilk.
  82. When aSilk is the class Silk, the default behavior applies (html namespace for new elements)"
  83. aSilk namespace = self htmlNamespace ifTrue: [
  84. ^self newElement: aString].
  85. "actually, the lines above are not needed if you want to use the namespaced method always"
  86. ^self newElement: aString xmlns: aSilk namespace
  87. !
  88. tryMakeDnuElement: aMessage in: aSilk
  89. "`DIV` creates a div element.
  90. `DIV: anObject` creates a div element and puts contents in it.
  91. When aSilk is an instance and not the class Silk,
  92. and the instance has an xml namespace other than the default #html,
  93. Then that namespace is used for the new element.
  94. You can do:
  95. svg := Silk newElement: 'svg' xmlns: 'http://www.w3.org/2000/svg'.
  96. svg CIRCLE: {'cx' -> 60. 'cy' -> 25. 'r' -> 10}.
  97. This creates a svg circle, not a html circle."
  98. | selector newElement useArg |
  99. selector := aMessage selector.
  100. selector asUppercase = selector
  101. ifFalse: [ ^ nil ].
  102. selector last = ':'
  103. ifTrue: [ useArg := true. selector := selector allButLast ]
  104. ifFalse: [ useArg := false ].
  105. (selector includes: ':')
  106. ifTrue: [ ^ nil ].
  107. newElement := self newElement: selector asLowercase in: aSilk.
  108. useArg ifTrue: [ newElement << aMessage arguments first ].
  109. ^ newElement
  110. ! !
  111. !Silk class methodsFor: 'message handling'!
  112. doesNotUnderstand: aMessage
  113. "`Silk DIV` creates a div element.
  114. `Silk DIV: anObject` creates a div element and puts contents in it"
  115. ^ (self tryMakeDnuElement: aMessage in: self)
  116. ifNil: [ super doesNotUnderstand: aMessage ]
  117. ! !
  118. !Association methodsFor: '*Silk'!
  119. renderOnSilk: aSilk
  120. key attrPut: value on: aSilk
  121. ! !
  122. !BlockClosure methodsFor: '*Silk'!
  123. renderOnSilk: aSilk
  124. self value: aSilk
  125. ! !
  126. !JSObjectProxy methodsFor: '*Silk'!
  127. inSilk
  128. ^ Silk newStream << self; yourself
  129. !
  130. renderOnSilk: aSilk
  131. ^ nil
  132. ! !
  133. !Object methodsFor: '*Silk'!
  134. inSilk
  135. ^ Silk newStream << self; yourself
  136. !
  137. renderOnSilk: aSilk
  138. ^ nil
  139. ! !
  140. !String methodsFor: '*Silk'!
  141. asSilk
  142. ^ Silk at: self asString
  143. !
  144. attrPut: anObject on: aSilk
  145. aSilk attrAt: self put: anObject
  146. ! !