DOMite.st 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. Smalltalk createPackage: 'DOMite'!
  2. ProtoStream subclass: #Domite
  3. instanceVariableNames: 'element reference'
  4. package: 'DOMite'!
  5. !Domite commentStamp!
  6. I am (hopefully thin) wrapper around the notion of "cursor in a page".
  7. I represent a DOM node _and_ a point where
  8. to insert new content into it.
  9. So I play both the role of a container that inserts
  10. as well as the role of an element being inserted.
  11. I inherit from `ProtoStream`.
  12. Creation API:
  13. - `Domite new` creates an insertion point at the bottom of `<body>`.
  14. - `Domite newStream` is unique way to create pieces of content. It creates an instance "floating in thin air" (wrapper around DOM DocumentFragment) that can be filled with any contents and then inserted in a page.
  15. - `Domite fromElement: aDomElement` wraps an element and set the cursor to its end.
  16. CSS selector API:
  17. - `Domite at: aSelector` wraps an element found by `document.querySelector(aSelector)`.
  18. Manipulation API:
  19. - `aDomite << obj` inserts obj at the insertion point.
  20. - `aDomite resetContents` deletes contents of the wrapped element.
  21. Cursor moving API:
  22. Take this sample HTML, where `[n]` are just markers, not real content:
  23. ```
  24. <body>
  25. <h1>header</h1>
  26. [4]<p>[2]Hello[1]world[3]</p>[5]
  27. <small>footer</small>
  28. </body>
  29. ```
  30. If `d` is a `Domite` representing `[1]`, then:
  31. - `d setToStart` would move `d` to be at `[2]`,
  32. - `d setToEnd` would move `d` to be at `[3]`,
  33. - `d setToBefore` would move `d` to be at `[4]`, and
  34. - `d setToAfter` would move `d` to be at `[5]`.
  35. It is not presumed one would use `setToXxx`
  36. to actually move around in a single instance.
  37. It is envisioned this API will be used mostly
  38. in combination with `copy`, like
  39. `afterMe := self copy setToAfter`.!
  40. !Domite methodsFor: 'accessing'!
  41. = aDomite
  42. ^ self class = aDomite class and: [
  43. self element = aDomite element and: [
  44. self reference = aDomite reference ]]
  45. !
  46. at: aString
  47. ^ self class fromElement: (self element querySelector: aString)
  48. !
  49. attrAt: aString
  50. (element hasAttribute: aString)
  51. ifTrue: [ ^ element getAttribute: aString ]
  52. ifFalse: [ ^ nil ]
  53. !
  54. attrAt: aString put: anotherString
  55. element setAttribute: aString to: anotherString
  56. !
  57. element
  58. ^ element
  59. !
  60. element: anObject
  61. element := anObject
  62. !
  63. propAt: aString
  64. ^ element at: aString
  65. !
  66. propAt: aString put: anObject
  67. ^ element at: aString put: anObject
  68. !
  69. reference
  70. ^ reference
  71. !
  72. reference: anObject
  73. reference := anObject
  74. ! !
  75. !Domite methodsFor: 'deletion'!
  76. resetContents
  77. <
  78. var element = self['@element'], child;
  79. while (child = element.firstChild) element.removeChild(child);
  80. self['@reference'] = null;
  81. >
  82. ! !
  83. !Domite methodsFor: 'events'!
  84. off: aString unbind: aBlock
  85. self removeEventListener: aString block: aBlock useCapture: false
  86. !
  87. on: aString bind: aBlock
  88. self addEventListener: aString block: aBlock useCapture: false
  89. ! !
  90. !Domite methodsFor: 'initialization'!
  91. initialize
  92. super initialize.
  93. element := document body.
  94. reference := nil asJSON
  95. ! !
  96. !Domite methodsFor: 'insertion'!
  97. nextPut: anObject
  98. self nextPutString: anObject printString
  99. !
  100. nextPutDomNode: aDomElement
  101. self element
  102. insertBefore: aDomElement
  103. reference: self reference
  104. !
  105. nextPutString: aString
  106. self nextPutDomNode: (
  107. document createTextNode: aString asString )
  108. ! !
  109. !Domite methodsFor: 'positioning'!
  110. reset
  111. self reference: self element firstChild
  112. !
  113. setToAfter
  114. self
  115. reference: self element nextSibling;
  116. element: self element parentNode
  117. !
  118. setToBefore
  119. self
  120. reference: self element;
  121. element: self element parentNode
  122. !
  123. setToEnd
  124. self reference: nil asJSON "null"
  125. ! !
  126. !Domite methodsFor: 'streaming'!
  127. putOn: aStream
  128. aStream nextPutDomNode: self element
  129. ! !
  130. !Domite methodsFor: 'testing'!
  131. atEnd
  132. ^ self reference isNil
  133. !
  134. atStart
  135. ^ self reference = self element firstChild
  136. !
  137. canSetToUpperLevel
  138. ^ self element parentNode notNil
  139. !
  140. isInvalid
  141. ^ self element isNil
  142. ! !
  143. !Domite class methodsFor: 'instance creation'!
  144. at: aString
  145. ^ self fromElement: (document querySelector: aString)
  146. !
  147. fromElement: aDomElement
  148. aDomElement ifNotNil: [
  149. (self isDomNode: aDomElement) ifFalse: [
  150. self error: self name, ': Need a DOM node' ]].
  151. ^ self new
  152. element: aDomElement;
  153. yourself
  154. !
  155. fromElement: aDomElement cursorBefore: anotherDomElement
  156. aDomElement ifNotNil: [
  157. (self isDomNode: aDomElement) ifFalse: [
  158. self error: self name, ': Need a DOM node' ]].
  159. ^ self new
  160. element: aDomElement;
  161. referenceElement: anotherDomElement;
  162. yourself
  163. !
  164. newElement: aString
  165. ^ self fromElement: (document createElement: aString)
  166. !
  167. newStream
  168. ^ self fromElement: document createDocumentFragment
  169. ! !
  170. !Domite class methodsFor: 'testing'!
  171. isDomNode: anObject
  172. <
  173. return anObject.nodeType >> 0 &&
  174. Object.prototype.toString.call(anObject) !!== "[object Object]"
  175. >
  176. ! !
  177. !JSObjectProxy methodsFor: '*DOMite'!
  178. putOn: aStream
  179. (Domite isDomNode: jsObject)
  180. ifTrue: [ aStream nextPutDomNode: jsObject ]
  181. ifFalse: [ aStream nextPut: self ]
  182. ! !
  183. !ProtoStream methodsFor: '*DOMite'!
  184. nextPutDomNode: aNode
  185. self nextPut: aNode
  186. ! !