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

uxcog.go 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  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. if isokit.UseStaticTemplateBundleFile == true && isokit.StaticTemplateBundleFileExists() == true {
  62. return
  63. }
  64. u.RegisterCogTemplates()
  65. }
  66. }
  67. func (u *UXCog) TemplateSet() *isokit.TemplateSet {
  68. return u.templateSet
  69. }
  70. func (u *UXCog) SetTemplateSet(ts *isokit.TemplateSet) {
  71. u.templateSet = ts
  72. }
  73. func (u *UXCog) CogType() reflect.Type {
  74. return u.cogType
  75. }
  76. func (u *UXCog) SetCogType(cogType reflect.Type) {
  77. u.cogType = cogType
  78. }
  79. func (u *UXCog) CogTemplatePath() string {
  80. return u.cogTemplatePath
  81. }
  82. func (u *UXCog) SetCogTemplatePath(path string) {
  83. u.cogTemplatePath = path
  84. }
  85. func (u *UXCog) RegisterCogTemplates() {
  86. u.templateSet.GatherCogTemplates(u.cogTemplatePath, u.cogPrefixName, ".tmpl")
  87. }
  88. func (u *UXCog) GetProps() map[string]interface{} {
  89. return u.Props
  90. }
  91. func (u *UXCog) SetProp(key string, value interface{}) {
  92. u.Props[key] = value
  93. if ReactivityEnabled == true && u.hasBeenRendered == true {
  94. u.Render()
  95. }
  96. }
  97. func (u *UXCog) BatchPropUpdate(props map[string]interface{}) {
  98. for k, v := range props {
  99. u.Props[k] = v
  100. }
  101. if ReactivityEnabled == true && u.hasBeenRendered == true {
  102. u.Render()
  103. }
  104. }
  105. func (u *UXCog) RenderCogTemplate() {
  106. var populateRenderedContent bool
  107. if u.hasBeenRendered == false {
  108. populateRenderedContent = true
  109. } else {
  110. populateRenderedContent = false
  111. }
  112. rp := isokit.RenderParams{Data: u.Props, Disposition: isokit.PlacementReplaceInnerContents, Element: *u.element, ShouldPopulateRenderedContent: populateRenderedContent}
  113. u.templateSet.Render(u.getCogPrefixName()+"/"+strings.Split(u.getCogPrefixName(), ":")[1], &rp)
  114. if u.hasBeenRendered == false {
  115. u.hasBeenRendered = true
  116. D := dom.GetWindow().Document()
  117. cogRoot := D.GetElementByID(u.id).FirstChild().(*dom.HTMLDivElement)
  118. contents := cogRoot.InnerHTML()
  119. parseTree, err := reconcile.NewParseTree([]byte(contents))
  120. if err != nil {
  121. println("Encountered an error: ", err)
  122. } else {
  123. u.parseTree = parseTree
  124. }
  125. }
  126. }
  127. func (u *UXCog) Render() error {
  128. document := dom.GetWindow().Document()
  129. e := document.GetElementByID(u.ID())
  130. if u.hasBeenRendered == true && e == nil {
  131. if u.cleanupFunc != nil {
  132. u.cleanupFunc()
  133. return nil
  134. }
  135. }
  136. if strings.ToLower(e.GetAttribute("data-component")) != "cog" {
  137. return errors.New("The cog container div must have a \"data-component\" attribute with a value specified as \"cog\".")
  138. }
  139. if u.hasBeenRendered == false {
  140. // Initial Render
  141. u.SetElement(&e)
  142. u.RenderCogTemplate()
  143. return nil
  144. } else if u.element != nil {
  145. // Re-render
  146. if VDOMEnabled == true {
  147. rp := isokit.RenderParams{Data: u.Props, Disposition: isokit.PlacementReplaceInnerContents, Element: *u.element, ShouldPopulateRenderedContent: true, ShouldSkipFinalRenderStep: true}
  148. u.templateSet.Render(u.getCogPrefixName()+"/"+strings.Split(u.getCogPrefixName(), ":")[1], &rp)
  149. D := dom.GetWindow().Document()
  150. cogRoot := D.GetElementByID(u.id).FirstChild().(*dom.HTMLDivElement)
  151. //contents := cogRoot.InnerHTML()
  152. newTree, err := reconcile.NewParseTree([]byte(rp.RenderedContent))
  153. if err != nil {
  154. println("Encountered an error: ", err)
  155. }
  156. changes, err := u.parseTree.Compare(newTree)
  157. if err != nil {
  158. println("Encountered an error: ", err)
  159. }
  160. if len(changes) > 0 {
  161. changes.ApplyChanges(cogRoot)
  162. u.parseTree = newTree
  163. }
  164. } else {
  165. u.RenderCogTemplate()
  166. }
  167. return nil
  168. }
  169. return nil
  170. }