onthefly.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. // Package onthefly provides Lua functions for building HTML, XML and TinySVG documents
  2. package onthefly
  3. import (
  4. "errors"
  5. log "github.com/sirupsen/logrus"
  6. lua "github.com/xyproto/gopher-lua"
  7. "github.com/xyproto/onthefly"
  8. "github.com/xyproto/tinysvg"
  9. )
  10. const (
  11. // PageClass is an identifier for the Page class in Lua
  12. PageClass = "Page"
  13. // TagClass is an identifier for the Tag class in Lua
  14. TagClass = "Tag"
  15. )
  16. // Get the first argument, "self", and cast it from userdata to a library (which is really a hash map).
  17. func checkTag(L *lua.LState) *onthefly.Tag {
  18. ud := L.CheckUserData(1)
  19. if tag, ok := ud.Value.(*onthefly.Tag); ok {
  20. return tag
  21. }
  22. L.ArgError(1, "Tag expected")
  23. return nil
  24. }
  25. // Create a new onthefly.Tag node. onthefly.Tag data as the first argument is optional.
  26. // Logs an error if the given onthefly.Tag can't be parsed.
  27. // Always returns a onthefly.Tag Node.
  28. func constructTag(L *lua.LState) (*lua.LUserData, error) {
  29. // Use the first argument as the name of the tag
  30. name := L.ToString(1)
  31. if name == "" {
  32. L.ArgError(1, "tag name expected")
  33. return nil, errors.New("tag name expected")
  34. }
  35. // Create a new onthefly.Tag
  36. tag := onthefly.NewTag(name)
  37. // Create a new userdata struct
  38. ud := L.NewUserData()
  39. ud.Value = tag
  40. L.SetMetatable(ud, L.GetTypeMetatable(TagClass))
  41. return ud, nil
  42. }
  43. // Take a Tag and a onthefly.Tag path.
  44. // Remove a key from a map. Return true if successful.
  45. func tagAddNewTag(L *lua.LState) int {
  46. tag := checkTag(L) // arg 1
  47. name := L.ToString(2)
  48. if name == "" {
  49. L.ArgError(2, "tag name expected")
  50. return 0 // no results
  51. }
  52. // Create a new Tag
  53. newTag := tag.AddNewTag(name)
  54. // Create a new userdata struct
  55. ud := L.NewUserData()
  56. ud.Value = newTag
  57. L.SetMetatable(ud, L.GetTypeMetatable(TagClass))
  58. // Return the newly created userdata
  59. L.Push(ud)
  60. return 1 // number of results
  61. }
  62. func tagString(L *lua.LState) int {
  63. tag := checkTag(L) // arg 1
  64. // Return the description as a Lua string
  65. L.Push(lua.LString(tag.String()))
  66. return 1 // number of results
  67. }
  68. // The hash map methods that are to be registered
  69. var tagMethods = map[string]lua.LGFunction{
  70. "__tostring": tagString,
  71. "addNewTag": tagAddNewTag,
  72. }
  73. // Get the first argument, "self", and cast it from userdata to a library (which is really a hash map).
  74. func checkPage(L *lua.LState) *onthefly.Page {
  75. ud := L.CheckUserData(1)
  76. if page, ok := ud.Value.(*onthefly.Page); ok {
  77. return page
  78. }
  79. L.ArgError(1, "Page expected")
  80. return nil
  81. }
  82. func constructHTML5Page(L *lua.LState) (*lua.LUserData, error) {
  83. // Use the first argument as the title of the page
  84. title := L.ToString(1)
  85. // Create a new HTML5 Page
  86. page := onthefly.NewHTML5Page(title)
  87. // Create a new userdata struct
  88. ud := L.NewUserData()
  89. ud.Value = page
  90. L.SetMetatable(ud, L.GetTypeMetatable(PageClass))
  91. return ud, nil
  92. }
  93. // Construct an onthefly.Page that represents a TinySVG object
  94. func constructTinySVGPage(L *lua.LState) (*lua.LUserData, error) {
  95. // Use the first argument as the title of the page
  96. w := L.ToInt(1)
  97. h := L.ToInt(2)
  98. description := L.ToString(3)
  99. // Create a new TinySVG Page
  100. document, svgTag := tinysvg.NewTinySVG(w, h)
  101. svgTag.Describe(description)
  102. // Create a new userdata struct
  103. ud := L.NewUserData()
  104. ud.Value = document
  105. L.SetMetatable(ud, L.GetTypeMetatable(PageClass))
  106. return ud, nil
  107. }
  108. // Create a new onthefly.Page
  109. func constructPage(L *lua.LState) (*lua.LUserData, error) {
  110. // Use the first argument as the title of the page
  111. title := L.ToString(1)
  112. if title == "" {
  113. L.ArgError(1, "page title expected")
  114. return nil, errors.New("page title expected")
  115. }
  116. // Use the second argument as the name of the root tag
  117. rootTagName := L.ToString(2)
  118. if rootTagName == "" {
  119. L.ArgError(1, "root tag name expected")
  120. return nil, errors.New("root tag name expected")
  121. }
  122. // Create a new onthefly.Page
  123. page := onthefly.NewPage(title, rootTagName)
  124. // Create a new userdata struct
  125. ud := L.NewUserData()
  126. ud.Value = page
  127. L.SetMetatable(ud, L.GetTypeMetatable(PageClass))
  128. return ud, nil
  129. }
  130. // Return the Page as a string
  131. func pageString(L *lua.LState) int {
  132. page := checkPage(L) // arg 1
  133. // Return the Page as a Lua string
  134. L.Push(lua.LString(page.String()))
  135. return 1 // number of results
  136. }
  137. // The hash map methods that are to be registered
  138. var pageMethods = map[string]lua.LGFunction{
  139. "__tostring": pageString,
  140. }
  141. // Load makes functions related to onthefly.Page nodes available to the given Lua state
  142. func Load(L *lua.LState) {
  143. // Register the Page class and the methods that belongs with it.
  144. metaTablePage := L.NewTypeMetatable(PageClass)
  145. metaTablePage.RawSetH(lua.LString("__index"), metaTablePage)
  146. L.SetFuncs(metaTablePage, pageMethods)
  147. metaTableTag := L.NewTypeMetatable(TagClass)
  148. metaTableTag.RawSetH(lua.LString("__index"), metaTableTag)
  149. L.SetFuncs(metaTableTag, tagMethods)
  150. // The constructor for Page
  151. L.SetGlobal("Page", L.NewFunction(func(L *lua.LState) int {
  152. // Construct a new Page
  153. userdata, err := constructPage(L)
  154. if err != nil {
  155. log.Error(err)
  156. L.Push(lua.LString(err.Error()))
  157. return 1 // Number of returned values
  158. }
  159. // Return the Lua Page object
  160. L.Push(userdata)
  161. return 1 // number of results
  162. }))
  163. // The constructor for HTML5 returns a Page
  164. L.SetGlobal("HTML5", L.NewFunction(func(L *lua.LState) int {
  165. // Construct a new Page
  166. userdata, err := constructHTML5Page(L)
  167. if err != nil {
  168. log.Error(err)
  169. L.Push(lua.LString(err.Error()))
  170. return 1 // Number of returned values
  171. }
  172. // Return the Lua Page object
  173. L.Push(userdata)
  174. return 1 // number of results
  175. }))
  176. // The constructor for TinySVG returns a Page
  177. L.SetGlobal("TinySVG", L.NewFunction(func(L *lua.LState) int {
  178. // Construct a new Page
  179. userdata, err := constructTinySVGPage(L)
  180. if err != nil {
  181. log.Error(err)
  182. L.Push(lua.LString(err.Error()))
  183. return 1 // Number of returned values
  184. }
  185. // Return the Lua Page object
  186. L.Push(userdata)
  187. return 1 // number of results
  188. }))
  189. // The constructor for Tag
  190. L.SetGlobal("Tag", L.NewFunction(func(L *lua.LState) int {
  191. // Construct a new Tag
  192. userdata, err := constructTag(L)
  193. if err != nil {
  194. log.Error(err)
  195. L.Push(lua.LString(err.Error()))
  196. return 1 // Number of returned values
  197. }
  198. // Return the Lua Tag object
  199. L.Push(userdata)
  200. return 1 // number of results
  201. }))
  202. }