123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- Smalltalk createPackage: 'DOMite'!
- ProtoStream subclass: #Domite
- instanceVariableNames: 'element reference'
- package: 'DOMite'!
- !Domite commentStamp!
- I am (hopefully thin) wrapper around the notion of "cursor in a page".
- I represent a DOM node _and_ a point where
- to insert new content into it.
- So I play both the role of a container that inserts
- as well as the role of an element being inserted.
- I inherit from `ProtoStream`.
- Creation API:
- - `Domite new` creates an insertion point at the bottom of `<body>`.
- - `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.
- - `Domite fromElement: aDomElement` wraps an element and set the cursor to its end.
- CSS selector API:
- - `Domite at: aSelector` wraps an element found by `document.querySelector(aSelector)`.
- Manipulation API:
- - `aDomite << obj` inserts obj at the insertion point.
- - `aDomite resetContents` deletes contents of the wrapped element.
- Cursor moving API:
- Take this sample HTML, where `[n]` are just markers, not real content:
- ```
- <body>
- <h1>header</h1>
- [4]<p>[2]Hello[1]world[3]</p>[5]
- <small>footer</small>
- </body>
- ```
- If `d` is a `Domite` representing `[1]`, then:
- - `d setToStart` would move `d` to be at `[2]`,
- - `d setToEnd` would move `d` to be at `[3]`,
- - `d setToBefore` would move `d` to be at `[4]`, and
- - `d setToAfter` would move `d` to be at `[5]`.
- It is not presumed one would use `setToXxx`
- to actually move around in a single instance.
- It is envisioned this API will be used mostly
- in combination with `copy`, like
- `afterMe := self copy setToAfter`.!
- !Domite methodsFor: 'accessing'!
- at: aString
- ^ self class fromElement: (self element querySelector: aString)
- !
- attrAt: aString
- (element hasAttribute: aString)
- ifTrue: [ ^ element getAttribute: aString ]
- ifFalse: [ ^ nil ]
- !
- attrAt: aString put: anotherString
- element setAttribute: aString to: anotherString
- !
- element
- ^ element
- !
- element: anObject
- element := anObject
- !
- propAt: aString
- ^ element at: aString
- !
- propAt: aString put: anObject
- ^ element at: aString put: anObject
- !
- reference
- ^ reference
- !
- reference: anObject
- reference := anObject
- ! !
- !Domite methodsFor: 'comparing'!
- = aDomite
- ^ self class = aDomite class and: [
- self element = aDomite element and: [
- self reference = aDomite reference ]]
- ! !
- !Domite methodsFor: 'converting'!
- asJQuery
- ^ self element asJQuery
- ! !
- !Domite methodsFor: 'deletion'!
- resetContents
- <
- var element = self['@element'], child;
- while (child = element.firstChild) element.removeChild(child);
- self['@reference'] = null;
- >
- ! !
- !Domite methodsFor: 'events'!
- off: aString unbind: aBlock
- self element removeEventListener: aString block: aBlock useCapture: false
- !
- on: aString bind: aBlock
- self element addEventListener: aString block: aBlock useCapture: false
- ! !
- !Domite methodsFor: 'initialization'!
- initialize
- super initialize.
- element := document body.
- reference := nil asJSON
- ! !
- !Domite methodsFor: 'insertion'!
- nextPut: anObject
- self nextPutString: anObject printString
- !
- nextPutDomNode: aDomElement
- self element
- insertBefore: aDomElement
- reference: self reference
- !
- nextPutJSObject: aJSObject
- (Domite isDomNode: aJSObject)
- ifTrue: [ self nextPutDomNode: aJSObject ]
- ifFalse: [ self nextPut: aJSObject ]
- !
- nextPutString: aString
- self nextPutDomNode: (
- document createTextNode: aString asString )
- ! !
- !Domite methodsFor: 'positioning'!
- reset
- self reference: self element firstChild
- !
- setToAfter
- self
- reference: self element nextSibling;
- element: self element parentNode
- !
- setToBefore
- self
- reference: self element;
- element: self element parentNode
- !
- setToEnd
- self reference: nil asJSON "null"
- ! !
- !Domite methodsFor: 'streaming'!
- putOn: aStream
- aStream nextPutDomNode: self element
- ! !
- !Domite methodsFor: 'testing'!
- atEnd
- ^ self reference isNil
- !
- atStart
- ^ self reference = self element firstChild
- !
- canSetToUpperLevel
- ^ self element parentNode notNil
- !
- isInvalid
- ^ self element isNil
- ! !
- !Domite class methodsFor: 'instance creation'!
- at: aString
- ^ self fromElement: (document querySelector: aString)
- !
- fromElement: aDomElement
- aDomElement ifNotNil: [
- (self isDomNode: aDomElement) ifFalse: [
- self error: self name, ': Need a DOM node' ]].
- ^ self new
- element: aDomElement;
- yourself
- !
- fromElement: aDomElement cursorBefore: anotherDomElement
- aDomElement ifNotNil: [
- (self isDomNode: aDomElement) ifFalse: [
- self error: self name, ': Need a DOM node' ]].
- ^ self new
- element: aDomElement;
- referenceElement: anotherDomElement;
- yourself
- !
- newElement: aString
- ^ self fromElement: (document createElement: aString)
- !
- newStream
- ^ self fromElement: document createDocumentFragment
- ! !
- !Domite class methodsFor: 'testing'!
- isDomNode: anObject
- <
- return anObject.nodeType >> 0 &&
- Object.prototype.toString.call(anObject) !!== "[object Object]"
- >
- ! !
- !ProtoStream methodsFor: '*DOMite'!
- nextPutDomNode: aNode
- self nextPut: aNode
- ! !
|