The cog package provides functionality to develop reusable components (cogs) for Isomorphic Go web applications.

uxcog.go 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. // The UXToolkit Project
  2. // Copyright (c) Wirecog, LLC. All rights reserved.
  3. // Use of this source code is governed by a BSD-style
  4. // license, which can be found in the LICENSE file.
  5. package cog
  6. import (
  7. "errors"
  8. "reflect"
  9. "strings"
  10. "github.com/isomorphicgo/isokit"
  11. "github.com/uxtoolkit/reconcile"
  12. _ "golang.org/x/net/html"
  13. "honnef.co/go/js/dom"
  14. )
  15. type UXCog struct {
  16. Cog
  17. cogType reflect.Type
  18. cogPrefixName string
  19. cogPackagePath string
  20. cogTemplatePath string
  21. templateSet *isokit.TemplateSet
  22. Props map[string]interface{}
  23. element *dom.Element
  24. id string
  25. hasBeenRendered bool
  26. parseTree *reconcile.ParseTree
  27. cleanupFunc func()
  28. }
  29. func (u *UXCog) getCogPrefixName() string {
  30. if u.cogType != nil {
  31. result := strings.Split(u.cogType.PkgPath(), `/`)
  32. return "cog:" + result[len(result)-1]
  33. } else {
  34. return ""
  35. }
  36. }
  37. func (u *UXCog) ID() string {
  38. return u.id
  39. }
  40. func (u *UXCog) SetID(id string) {
  41. u.id = id
  42. }
  43. func (u *UXCog) SetCleanupFunc(cleanupFunc func()) {
  44. u.cleanupFunc = cleanupFunc
  45. }
  46. func (u *UXCog) SetElement(element *dom.Element) {
  47. u.element = element
  48. }
  49. func (u *UXCog) Element() *dom.Element {
  50. return u.element
  51. }
  52. func (u *UXCog) CogInit(ts *isokit.TemplateSet) {
  53. u.hasBeenRendered = false
  54. u.Props = make(map[string]interface{})
  55. if ts != nil {
  56. u.templateSet = ts
  57. }
  58. u.cogTemplatePath = DefaultGoSourcePath + "/" + u.cogType.PkgPath() + "/" + DefaultTemplatesDirectoryName
  59. u.cogPrefixName = u.getCogPrefixName()
  60. if isokit.OperatingEnvironment() == isokit.ServerEnvironment {
  61. u.RegisterCogTemplates()
  62. }
  63. }
  64. func (u *UXCog) TemplateSet() *isokit.TemplateSet {
  65. return u.templateSet
  66. }
  67. func (u *UXCog) SetTemplateSet(ts *isokit.TemplateSet) {
  68. u.templateSet = ts
  69. }
  70. func (u *UXCog) CogType() reflect.Type {
  71. return u.cogType
  72. }
  73. func (u *UXCog) SetCogType(cogType reflect.Type) {
  74. u.cogType = cogType
  75. }
  76. func (u *UXCog) CogTemplatePath() string {
  77. return u.cogTemplatePath
  78. }
  79. func (u *UXCog) SetCogTemplatePath(path string) {
  80. u.cogTemplatePath = path
  81. }
  82. func (u *UXCog) RegisterCogTemplates() {
  83. u.templateSet.GatherCogTemplates(u.cogTemplatePath, u.cogPrefixName, ".tmpl")
  84. }
  85. func (u *UXCog) GetProps() map[string]interface{} {
  86. return u.Props
  87. }
  88. func (u *UXCog) SetProp(key string, value interface{}) {
  89. u.Props[key] = value
  90. if ReactivityEnabled == true && u.hasBeenRendered == true {
  91. u.Render()
  92. }
  93. }
  94. func (u *UXCog) BatchPropUpdate(props map[string]interface{}) {
  95. for k, v := range props {
  96. u.Props[k] = v
  97. }
  98. if ReactivityEnabled == true && u.hasBeenRendered == true {
  99. u.Render()
  100. }
  101. }
  102. func (u *UXCog) RenderCogTemplate() {
  103. var populateRenderedContent bool
  104. if u.hasBeenRendered == false {
  105. populateRenderedContent = true
  106. } else {
  107. populateRenderedContent = false
  108. }
  109. rp := isokit.RenderParams{Data: u.Props, Disposition: isokit.PlacementReplaceInnerContents, Element: *u.element, ShouldPopulateRenderedContent: populateRenderedContent}
  110. u.templateSet.Render(u.getCogPrefixName()+"/"+strings.Split(u.getCogPrefixName(), ":")[1], &rp)
  111. if u.hasBeenRendered == false {
  112. u.hasBeenRendered = true
  113. D := dom.GetWindow().Document()
  114. cogRoot := D.GetElementByID(u.id).FirstChild().(*dom.HTMLDivElement)
  115. contents := cogRoot.InnerHTML()
  116. parseTree, err := reconcile.NewParseTree([]byte(contents))
  117. if err != nil {
  118. println("Encountered an error: ", err)
  119. } else {
  120. u.parseTree = parseTree
  121. }
  122. }
  123. }
  124. func (u *UXCog) Render() error {
  125. document := dom.GetWindow().Document()
  126. e := document.GetElementByID(u.ID())
  127. if u.hasBeenRendered == true && e == nil {
  128. if u.cleanupFunc != nil {
  129. u.cleanupFunc()
  130. return nil
  131. }
  132. }
  133. if strings.ToLower(e.GetAttribute("data-component")) != "cog" {
  134. return errors.New("The cog container div must have a \"data-component\" attribute with a value specified as \"cog\".")
  135. }
  136. if u.hasBeenRendered == false {
  137. // Initial Render
  138. u.SetElement(&e)
  139. u.RenderCogTemplate()
  140. return nil
  141. } else if u.element != nil {
  142. // Re-render
  143. if VDOMEnabled == true {
  144. rp := isokit.RenderParams{Data: u.Props, Disposition: isokit.PlacementReplaceInnerContents, Element: *u.element, ShouldPopulateRenderedContent: true, ShouldSkipFinalRenderStep: true}
  145. u.templateSet.Render(u.getCogPrefixName()+"/"+strings.Split(u.getCogPrefixName(), ":")[1], &rp)
  146. D := dom.GetWindow().Document()
  147. cogRoot := D.GetElementByID(u.id).FirstChild().(*dom.HTMLDivElement)
  148. //contents := cogRoot.InnerHTML()
  149. newTree, err := reconcile.NewParseTree([]byte(rp.RenderedContent))
  150. if err != nil {
  151. println("Encountered an error: ", err)
  152. }
  153. changes, err := u.parseTree.Compare(newTree)
  154. if err != nil {
  155. println("Encountered an error: ", err)
  156. }
  157. if len(changes) > 0 {
  158. changes.ApplyChanges(cogRoot)
  159. u.parseTree = newTree
  160. }
  161. } else {
  162. u.RenderCogTemplate()
  163. }
  164. return nil
  165. }
  166. return nil
  167. }