Smalltalk createPackage: 'Silk'! Domite subclass: #Silk slots: {} package: 'Silk'! !Silk commentStamp! I am subclass of `Domite` with more convenient high-level API. ##Rendering - `aSilk << anObject` uses double-dispatch via `renderOnSilk:`. This allows to create widgets (no formal superclass, anything with `renderOnSilk:` is a widget), as well as incorporating magic on certain types of objects: - blocks: `aSilk << aBlock` runs the block, passing aSilk as a parameter. - associations: `aSilk << (key -> value)` set attribute key to value. Worthful to note is, rendering a collection has its magic already built-in (via `putOn:`) -- if you `stream << aCollection`, its items are `<<`'d in sequence. So, de facto, arrays are deeply flattened when put on a stream via `<<`. ##Convenience - `aCssSelectorString asSilk` returns Silk wrapping an element at a selector. - `anObject inSilk` returns anObject rendered in a document fragment. ##Element creation These messages use DNU to dynamically create elements with any (letters-and-numbers) tag name, Next samples show this on an example of `
`. - `Silk DIV` is shortcut for `Silk newElement: 'div'`. - `aSilk DIV` is shortcut for `[ |tmp| tmp := Silk DIV. aSilk << tmp. tmp] value`. IOW, it not just creates the element and returns it, but also puts in on aSilk. - `aSilk DIV: anObject` is shortcut for `aSilk DIV << anObject; yourself`. IOW, it not just creates and inserts the element, but puts a content into it. ##Conclusions Taken all this together, one can do pretty neat constructs: ``` aSilk P: { 'id'->'mission'. 'We are the champions.' } ``` adds `

We are the champions.

` into `aSilk` and returns the Silk-wrapped `

` with insertion cursor at the end.! !Silk methodsFor: 'accessing'! namespace " XML namespace for elements: html. The default for all virtual Silk tag messages" ^ self element namespaceURI ! ! !Silk methodsFor: 'writing'! newElement: aString xmlns: anotherString | el | el := self class newElement: aString xmlns: anotherString. self << el. ^ el ! nextPut: anObject "Double-dispatches anObject via renderOnSilk: message. If a message returns nil, this fallbacks to superclass. Otherwise, it is assumed renderOnSilk: did its job." (anObject renderOnSilk: self) ifNil: [ super nextPut: anObject ] ! ! !Silk class methodsFor: 'accessing'! htmlNamespace " XML namespace for HTML elements. The default for all virtual Silk tag messages" ^ 'http://www.w3.org/1999/xhtml' ! namespace " XML namespace for elements: html. The default for all virtual Silk tag messages" ^ self htmlNamespace ! ! Trait named: #TSilkBuilder package: 'Silk'! !TSilkBuilder commentStamp! I contain Silk's "build element via DNU" behaviour. I expect #namespace and #newElement:xmlns: to be implemented.! !TSilkBuilder methodsFor: 'convenience'! newSvgElement ^ self newElement: 'svg' xmlns: 'http://www.w3.org/2000/svg' ! ! !TSilkBuilder methodsFor: 'instance creation'! tryMakeDnuElement: aMessage "`DIV` creates a div element. `DIV: anObject` creates a div element and puts contents in it. An element can be optionally inserted by #newElement:xmlns:. When self is an instance and not the class Silk, then the instance's namespace is used for the new element. You can do: svg := Silk newElement: 'svg' xmlns: 'http://www.w3.org/2000/svg'. svg CIRCLE: {'cx' -> 60. 'cy' -> 25. 'r' -> 10}. This creates a svg circle, not a html circle." | selector newElement useArg | selector := aMessage selector. selector asUppercase = selector ifFalse: [ ^ nil ]. selector last = ':' ifTrue: [ useArg := true. selector := selector allButLast ] ifFalse: [ useArg := false ]. (selector includes: ':') ifTrue: [ ^ nil ]. newElement := self newElement: selector asLowercase xmlns: self namespace. useArg ifTrue: [ newElement << aMessage arguments first ]. ^ newElement ! ! !TSilkBuilder methodsFor: 'message handling'! doesNotUnderstand: aMessage "`self DIV` creates (and optionally inserts) a div element. `aSilk DIV: anObject` creates (and optionally inserts) a div element, and puts contents in it" ^ (self tryMakeDnuElement: aMessage) ifNil: [ super doesNotUnderstand: aMessage ] ! ! Silk setTraitComposition: {TSilkBuilder} asTraitComposition! Silk class setTraitComposition: {TSilkBuilder} asTraitComposition! ! ! !Association methodsFor: '*Silk'! renderOnSilk: aSilk key attrPut: value on: aSilk ! ! !BlockClosure methodsFor: '*Silk'! renderOnSilk: aSilk self value: aSilk ! ! !JSObjectProxy methodsFor: '*Silk'! inSilk ^ Silk newStream << self; yourself ! renderOnSilk: aSilk ^ nil ! ! !Object methodsFor: '*Silk'! inSilk ^ Silk newStream << self; yourself ! renderOnSilk: aSilk ^ nil ! ! !String methodsFor: '*Silk'! asSilk ^ Silk at: self asString ! attrPut: anObject on: aSilk aSilk attrAt: self put: anObject ! !