trellis 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (292) hide show
  1. data/.git/COMMIT_EDITMSG +1 -0
  2. data/.git/HEAD +1 -0
  3. data/.git/config +9 -0
  4. data/.git/description +1 -0
  5. data/.git/hooks/applypatch-msg.sample +15 -0
  6. data/.git/hooks/commit-msg.sample +24 -0
  7. data/.git/hooks/post-commit.sample +8 -0
  8. data/.git/hooks/post-receive.sample +15 -0
  9. data/.git/hooks/post-update.sample +8 -0
  10. data/.git/hooks/pre-applypatch.sample +14 -0
  11. data/.git/hooks/pre-commit.sample +18 -0
  12. data/.git/hooks/pre-rebase.sample +169 -0
  13. data/.git/hooks/prepare-commit-msg.sample +36 -0
  14. data/.git/hooks/update.sample +107 -0
  15. data/.git/index +0 -0
  16. data/.git/info/exclude +6 -0
  17. data/.git/logs/HEAD +1 -0
  18. data/.git/logs/refs/heads/master +1 -0
  19. data/.git/logs/refs/remotes/origin/master +1 -0
  20. data/.git/objects/01/2368d4dd3162ed570e6c1423ecb07a6a35f8c0 +0 -0
  21. data/.git/objects/02/238c88c522b88e71529495460cc5c2c8abe110 +0 -0
  22. data/.git/objects/07/03d3b123189f1da72d3889d9340e681841c96d +0 -0
  23. data/.git/objects/0b/3935fdbf3ce8d37e6cb2a6ec94edaccb1c58dc +0 -0
  24. data/.git/objects/0f/ecd1ad35f280c874ce070b7a97a4c1f86ace54 +0 -0
  25. data/.git/objects/12/61911b063b7e6883c5e9db7ab4396183f6f2b5 +0 -0
  26. data/.git/objects/13/0b7e2d214ec198d07aeed5b462597670ce85ad +0 -0
  27. data/.git/objects/13/214d40f02b63f1233fd7881b1df9bb75d9c0a7 +0 -0
  28. data/.git/objects/14/7522bdc96af0b578935d81c1a7347b40573940 +0 -0
  29. data/.git/objects/16/a7fde706fe92b3548254d29722f5ca311ce63b +2 -0
  30. data/.git/objects/16/fe580663cc29d141bf416dff5bf21bf4ea42a3 +0 -0
  31. data/.git/objects/17/2d55806918bafb4e701515edb27147a4ef6498 +0 -0
  32. data/.git/objects/17/686ef1882ed6c3c07b781fab18739b1c5e48bd +0 -0
  33. data/.git/objects/17/824053c41a2cb523303e20cac42f66f9ca2134 +0 -0
  34. data/.git/objects/18/68448c5f88bc73b2fef6b8f3aa75b663013a3a +1 -0
  35. data/.git/objects/1a/6d9051dc4c7db10bdcf5e17c5cb717b072d30a +0 -0
  36. data/.git/objects/1a/8bdb445fb5668dcd1309cb9de5dedf846c822f +0 -0
  37. data/.git/objects/1b/6fa67b70358fdb82eabcd7ef3bc46d518d0426 +0 -0
  38. data/.git/objects/1c/f6510ed08a274fb3292d40557cebb3e7090767 +0 -0
  39. data/.git/objects/1d/e661ce11d5335e5dd7968ef5698e2eb164eb52 +0 -0
  40. data/.git/objects/1e/301ee672d6040e734af6d0a5491185129d7b67 +0 -0
  41. data/.git/objects/21/2201d46200f13d23d8199bd4a7ecc9165205b3 +0 -0
  42. data/.git/objects/22/f687370babdf38d224d2a9ff039707e8d0aac1 +0 -0
  43. data/.git/objects/24/202d2de95dc31f5f1802e629a16ad45b723267 +0 -0
  44. data/.git/objects/25/4c5b6b8d211d8b9f07310fd342fd7b078a81af +0 -0
  45. data/.git/objects/25/5832653fde9e9748855347fe7bce013aa0a6c7 +0 -0
  46. data/.git/objects/25/806945c89b4b5adb8c8676c308e4653361109c +5 -0
  47. data/.git/objects/26/cb8deaca28bf275769cbb71b88e53a44adb9ca +0 -0
  48. data/.git/objects/2e/1c13fb5f154f89cccd4c645540a2ec8ee0f57a +0 -0
  49. data/.git/objects/2f/437425b2c085bb529379e409d6b4bc7f9f9fba +0 -0
  50. data/.git/objects/2f/7cf4a25937b71f3de52e70c7a47cb2f7da07ea +0 -0
  51. data/.git/objects/31/31bdbbac6e6e7e24fd6ea84332461f67e6e5b6 +0 -0
  52. data/.git/objects/32/02e02de478263912b729283eabc7cdab8a8037 +0 -0
  53. data/.git/objects/32/839ed9baa8c800d9033655d1ee995499825fb5 +0 -0
  54. data/.git/objects/34/2e479c61796125b0e025b483401ec12f3c5824 +2 -0
  55. data/.git/objects/36/7acb017aebab3f627a5208c25885c21fa7fdbb +0 -0
  56. data/.git/objects/38/12910d2e2a8e6eeb79a399d20751675d3fbe77 +0 -0
  57. data/.git/objects/38/3fe1e1a07537db6e0f39c6005b7b4e40fa5ccd +0 -0
  58. data/.git/objects/39/5ef328bc94df9191f4d80a77503a5be9f1f673 +0 -0
  59. data/.git/objects/39/dc76dc096fdf737e9f4f7062b78cbf4ab9628f +0 -0
  60. data/.git/objects/3b/09eb196140b471556ed4c3987c0bc739733617 +0 -0
  61. data/.git/objects/3b/9f97f1bf55834832a92bab1ab900918dc49570 +0 -0
  62. data/.git/objects/40/5ffd3925b39dd905638ec24640dd8efa27cc1b +0 -0
  63. data/.git/objects/42/4a5f37c6fe3a7cac54b0f85688c1cce7da9cdf +0 -0
  64. data/.git/objects/44/9e23363d4e20da6a67be7ab78fc610b4e9852a +0 -0
  65. data/.git/objects/48/90bb5d241cecaa30b28148c920b833056c7508 +0 -0
  66. data/.git/objects/4c/558c43676410a2c0a7d5c7b94fca18923a9af1 +0 -0
  67. data/.git/objects/4f/bf572175dd5245104679ff8f676f6a34466c29 +0 -0
  68. data/.git/objects/53/8583edf00c9f656b32149a0e69e030371737c5 +0 -0
  69. data/.git/objects/5b/8a2c4bf7bb56ae199414096dbda21fd4b8cd63 +0 -0
  70. data/.git/objects/5c/9fe8ff998d56dc076657ef88ee9f784ec160f9 +0 -0
  71. data/.git/objects/5f/8441f9d3507587940f31af04dd9fd5911d2812 +0 -0
  72. data/.git/objects/61/4f2752550e63b793e31dcf98b5ea26c32eb0be +0 -0
  73. data/.git/objects/65/801b0fabdfd673861dbb2ac134d438ab2cc5e4 +3 -0
  74. data/.git/objects/66/2746dffaddbcd311f4ac070b5b9bddcd8894a7 +0 -0
  75. data/.git/objects/69/1ed3b65603a0d0dabfb66720e7163664be78f5 +0 -0
  76. data/.git/objects/69/3dc3b6037df8c1c8dfda916d649e7975481124 +0 -0
  77. data/.git/objects/69/67538427b3a371b33c01c72726ac7e67a04c39 +0 -0
  78. data/.git/objects/69/6cd6c3508e2adcc68829497884ff3d4b774dc2 +0 -0
  79. data/.git/objects/69/6ee93917040308ccd0bfc245d63c1cbeb23dae +0 -0
  80. data/.git/objects/6a/3ac317746a8a2cfc88f3580bbd2b03dc0d50b5 +0 -0
  81. data/.git/objects/6d/6ba435839a65b96cb3f7584a40dd3767a0990f +0 -0
  82. data/.git/objects/6f/32c190a651a82caeefeb55eb797fc5daf05426 +0 -0
  83. data/.git/objects/71/fecbce1bec36eaf4de655763d4bb7ceffc991d +0 -0
  84. data/.git/objects/72/01d78a9e90347fd2f7790f9e8665587791d853 +0 -0
  85. data/.git/objects/73/6c97d6a551363ba4a4956408ba228fcab7f91f +0 -0
  86. data/.git/objects/75/3264271e935e18f4eada49aa1818c6fb9e6abb +0 -0
  87. data/.git/objects/75/cc7d390b4a565495cbdc5a08d4479e7e8dbfeb +0 -0
  88. data/.git/objects/76/7c0c96f68b9ccec34ae538045a63bbe4f97d3e +0 -0
  89. data/.git/objects/7b/47fe9b1ab9f2692b9706054de706430089ecd4 +0 -0
  90. data/.git/objects/7c/73144ef6847d1bc2315aee0925c0e5cf5c09d1 +0 -0
  91. data/.git/objects/7d/9c177c155386606aa1024d5cb020a097eea64a +0 -0
  92. data/.git/objects/7d/e9a3296007c09ba9b7bc1802e1c5d9ad1d5dbb +0 -0
  93. data/.git/objects/7e/b73d408147c8d84881109189a9724292abcb8d +0 -0
  94. data/.git/objects/82/0e3bf5449a8f6b8eb637a653dcb2dc387b393c +0 -0
  95. data/.git/objects/83/a10ce96763cb30d0b81425e9372f89c52f4c9f +0 -0
  96. data/.git/objects/83/bdb801e78444d59225f83cf2f336f114cd23cf +0 -0
  97. data/.git/objects/85/24e583e822abc18b1ffdd0aa356c9c079b5548 +0 -0
  98. data/.git/objects/88/b46cc8de5237897d4ba5023d3efdc83fec4f7c +0 -0
  99. data/.git/objects/89/5eea66474f48a2cbf643ad0ef266fd26f9d2a3 +0 -0
  100. data/.git/objects/8a/539c5fc35d5b770d67241d2ccb6c6855df6a43 +0 -0
  101. data/.git/objects/8a/82cdbfb20cc58fdffe4e13b70b55977deb92c6 +0 -0
  102. data/.git/objects/8b/2b537442e4ac8719ffa71b3ae0c707133df06f +0 -0
  103. data/.git/objects/8c/2a45f0c81284a081f8d7980e9a54210a9dd52c +0 -0
  104. data/.git/objects/8e/10355b4ab312450031966107ef6a65b13833ee +0 -0
  105. data/.git/objects/90/f6c76767ae725f15fa6790dcb6afd1719fa0e2 +0 -0
  106. data/.git/objects/92/92b696aba9840c089241c68d4c6ca14be38a64 +2 -0
  107. data/.git/objects/93/e03faa6906892434fa9f2ad230c4ececc64055 +0 -0
  108. data/.git/objects/93/eca2e28d80fda45d327fdfc6c39ae690af41cd +0 -0
  109. data/.git/objects/95/92fe63bd5403654730cdec76cbf116ef83bbff +0 -0
  110. data/.git/objects/96/bc6dd471c1c188a2eb4308bcc3ca4e6c8faac8 +0 -0
  111. data/.git/objects/96/d855221a9ff4fba57d2b70680b57bd5115cfde +0 -0
  112. data/.git/objects/97/4ec92b2b9eb01a73fb57dc78d463cafdda69a7 +0 -0
  113. data/.git/objects/97/b7ec01ebe089832a0972d20b0a40d54456985c +2 -0
  114. data/.git/objects/97/fa6aaae4cfc7654feb39971e7aee5c41ed1679 +1 -0
  115. data/.git/objects/98/18351c6685a1df5d23686e6346d11d261cc6f0 +0 -0
  116. data/.git/objects/98/3d3b7e55cbbce2f2a52c6e6d830a5dca7b4d42 +0 -0
  117. data/.git/objects/a2/e551049a5388112c21e7f3a4a720bf720601fa +0 -0
  118. data/.git/objects/a3/7066bea4fae90d34761a3ebb3f8a348d94eba1 +0 -0
  119. data/.git/objects/a6/c99619145d1b9b75aa15ca04fbe4db0b2ee79b +0 -0
  120. data/.git/objects/aa/e2234d0a8c1485096c7a7de039eceb4e019e13 +0 -0
  121. data/.git/objects/b0/3b52f4269c9719e02ecd6caabd8aff5adf3aac +0 -0
  122. data/.git/objects/b1/50dbbd89f825302a9e82d8fdd66f238b8e6fc4 +0 -0
  123. data/.git/objects/b4/545fbe393aaa18f1c48de4d9f68f64ebf09ddc +0 -0
  124. data/.git/objects/b6/8691404e088692929d40b988b993f815d76756 +0 -0
  125. data/.git/objects/b7/daa3d844ef5f5cd917084e48edd82e67f8f476 +0 -0
  126. data/.git/objects/bf/4232281dba5a0f17e8b5c16b9cbd0f52274fa8 +0 -0
  127. data/.git/objects/bf/9b84d10f19188beffe5ee99e6f5af035ba77d3 +2 -0
  128. data/.git/objects/c0/06e664b306145de804f74ab27550d62a92f58a +1 -0
  129. data/.git/objects/c2/2806b16775f66453012d085d21fb8698521ea9 +0 -0
  130. data/.git/objects/c2/7f6559350f7adb19d43742b55b2f91d07b6550 +0 -0
  131. data/.git/objects/c3/833df21ed674a77374fd40ed931f900134d38b +0 -0
  132. data/.git/objects/c3/b9da021eb76abad0b17ad6c4be75595e41a7e6 +0 -0
  133. data/.git/objects/c5/ca6b7e3561ea88aba1712aabb984d1167787f1 +0 -0
  134. data/.git/objects/c6/39f65e1f43c75b7f57c5ab28623d295dfb2afd +0 -0
  135. data/.git/objects/c8/6f05fa5539f8e69e94850d1cd917919ff581be +0 -0
  136. data/.git/objects/cb/5e499c8dcc6f2901014a08e74f04b7a4bd6596 +0 -0
  137. data/.git/objects/cc/1289d25eeee6ba31b4e406372cb9abf969fc6f +0 -0
  138. data/.git/objects/cc/784e1b49111c42fa7eae10ad0e788186e672d1 +0 -0
  139. data/.git/objects/cc/bcae254dad515d7ef01a461980544e4483c41e +0 -0
  140. data/.git/objects/ce/bfadad3a7c17148998e04275e6d0d1cf1b2e1e +0 -0
  141. data/.git/objects/ce/f3335522abdc47a2c94be70455dcfaa9a51a27 +0 -0
  142. data/.git/objects/cf/6add7ea568d3d90d6a1f8afb0898b0119b14ff +0 -0
  143. data/.git/objects/d0/719abd196033fec0e4cbe2af68a26ca435389c +0 -0
  144. data/.git/objects/d1/ce63ff0eaec4dba419e5547f99195dfb9b5b20 +0 -0
  145. data/.git/objects/d2/c3feb3c74b892cca0049c0aced6ce786a784c8 +0 -0
  146. data/.git/objects/d7/8a91da13413419da71b539a3c6c7df4d8a2458 +0 -0
  147. data/.git/objects/d8/c7bed2b1357d54b6935536a8a51a3c73d535b4 +0 -0
  148. data/.git/objects/da/c3a1277ccb4806011e33eb32c5609cc45c46f2 +0 -0
  149. data/.git/objects/dc/3f920f4b937d0e2ac512f0bb31d477624c46d5 +0 -0
  150. data/.git/objects/dc/6c389abf5bd0d7b9be2a6bb6af90738a410e59 +0 -0
  151. data/.git/objects/df/07c423309f39c4d5570b0fad65dff23af654ae +0 -0
  152. data/.git/objects/e2/93a5c9e2257b9f3fcb8fa5d5c8147a2f07ba1b +0 -0
  153. data/.git/objects/e4/8464df56bf487e96e21ea99487330266dae3c9 +0 -0
  154. data/.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 +0 -0
  155. data/.git/objects/e8/0c13a1d1f62756fb9f1e4fb9f7512e5b8c5816 +0 -0
  156. data/.git/objects/e9/b8ade2125b2615ddc4c8043940c40ed36215c9 +0 -0
  157. data/.git/objects/ed/59a7599a81ef7ea7c230c3332345a3484febf8 +0 -0
  158. data/.git/objects/f0/d2a74226877e9a28af814d0d3727e4a3487439 +0 -0
  159. data/.git/objects/f0/f3ba7264f1d8e60eb93a907092af98fdc248e4 +0 -0
  160. data/.git/objects/f1/cdcc7ff2dc6298318523b2c4e425f515a58bf7 +3 -0
  161. data/.git/objects/f4/45c96f406f4d2f9839b3fe2fe8bc7f943ab469 +0 -0
  162. data/.git/objects/f7/eef7ce0a650d6f0c85dc4982f481da7bd44362 +0 -0
  163. data/.git/objects/f9/0abf96c5059ce1ba115f73bc9cf33e4663b3e1 +0 -0
  164. data/.git/objects/f9/cf7ae572eaebbd63f809f16a5ef14b9c679790 +0 -0
  165. data/.git/objects/fc/300df53fe2c9467210e2fd4c2f34e1c9c7c89a +0 -0
  166. data/.git/objects/fc/45546596ab8debc4c9f49a7abc7fc14a7bcc7f +4 -0
  167. data/.git/objects/fc/68d89167f69614eda17bd56dfccb4355d818e6 +0 -0
  168. data/.git/objects/fc/e4c02bb132e73b3cb957da182db7b75a47c242 +0 -0
  169. data/.git/objects/fe/be283da7f326344013f1fc847c12c8f29ab6ef +0 -0
  170. data/.git/refs/heads/master +1 -0
  171. data/.git/refs/remotes/origin/master +1 -0
  172. data/.gitignore +5 -0
  173. data/History.txt +4 -0
  174. data/License.txt +22 -0
  175. data/Manifest.txt +291 -0
  176. data/README +156 -0
  177. data/README.txt +156 -0
  178. data/Rakefile +4 -0
  179. data/config/hoe.rb +83 -0
  180. data/config/requirements.rb +15 -0
  181. data/examples/crud_components/html/address_view_edit.xhtml +19 -0
  182. data/examples/crud_components/html/addresses.xhtml +20 -0
  183. data/examples/crud_components/html/images/destroy.gif +0 -0
  184. data/examples/crud_components/html/images/edit.gif +0 -0
  185. data/examples/crud_components/html/images/field-error-marker.gif +0 -0
  186. data/examples/crud_components/html/images/sort-asc.png +0 -0
  187. data/examples/crud_components/html/images/sort-desc.png +0 -0
  188. data/examples/crud_components/html/images/sortable.png +0 -0
  189. data/examples/crud_components/html/style/trellis.css +416 -0
  190. data/examples/crud_components/source/crud_components.rb +77 -0
  191. data/examples/crud_components/source/domain.rb +6 -0
  192. data/examples/examples.txt +65 -0
  193. data/examples/flickr/source/flickr.rb +87 -0
  194. data/examples/guest_book/source/guest_book.rb +61 -0
  195. data/examples/hangman/html/game_over.xhtml +23 -0
  196. data/examples/hangman/html/guess.xhtml +47 -0
  197. data/examples/hangman/html/images/a_disabled.png +0 -0
  198. data/examples/hangman/html/images/a_enabled.png +0 -0
  199. data/examples/hangman/html/images/b_disabled.png +0 -0
  200. data/examples/hangman/html/images/b_enabled.png +0 -0
  201. data/examples/hangman/html/images/c_disabled.png +0 -0
  202. data/examples/hangman/html/images/c_enabled.png +0 -0
  203. data/examples/hangman/html/images/d_disabled.png +0 -0
  204. data/examples/hangman/html/images/d_enabled.png +0 -0
  205. data/examples/hangman/html/images/e_disabled.png +0 -0
  206. data/examples/hangman/html/images/e_enabled.png +0 -0
  207. data/examples/hangman/html/images/f_disabled.png +0 -0
  208. data/examples/hangman/html/images/f_enabled.png +0 -0
  209. data/examples/hangman/html/images/g_disabled.png +0 -0
  210. data/examples/hangman/html/images/g_enabled.png +0 -0
  211. data/examples/hangman/html/images/h_disabled.png +0 -0
  212. data/examples/hangman/html/images/h_enabled.png +0 -0
  213. data/examples/hangman/html/images/i_disabled.png +0 -0
  214. data/examples/hangman/html/images/i_enabled.png +0 -0
  215. data/examples/hangman/html/images/j_disabled.png +0 -0
  216. data/examples/hangman/html/images/j_enabled.png +0 -0
  217. data/examples/hangman/html/images/k_disabled.png +0 -0
  218. data/examples/hangman/html/images/k_enabled.png +0 -0
  219. data/examples/hangman/html/images/l_disabled.png +0 -0
  220. data/examples/hangman/html/images/l_enabled.png +0 -0
  221. data/examples/hangman/html/images/m_disabled.png +0 -0
  222. data/examples/hangman/html/images/m_enabled.png +0 -0
  223. data/examples/hangman/html/images/n_disabled.png +0 -0
  224. data/examples/hangman/html/images/n_enabled.png +0 -0
  225. data/examples/hangman/html/images/o_disabled.png +0 -0
  226. data/examples/hangman/html/images/o_enabled.png +0 -0
  227. data/examples/hangman/html/images/p_disabled.png +0 -0
  228. data/examples/hangman/html/images/p_enabled.png +0 -0
  229. data/examples/hangman/html/images/q_disabled.png +0 -0
  230. data/examples/hangman/html/images/q_enabled.png +0 -0
  231. data/examples/hangman/html/images/r_disabled.png +0 -0
  232. data/examples/hangman/html/images/r_enabled.png +0 -0
  233. data/examples/hangman/html/images/s_disabled.png +0 -0
  234. data/examples/hangman/html/images/s_enabled.png +0 -0
  235. data/examples/hangman/html/images/t_disabled.png +0 -0
  236. data/examples/hangman/html/images/t_enabled.png +0 -0
  237. data/examples/hangman/html/images/u_disabled.png +0 -0
  238. data/examples/hangman/html/images/u_enabled.png +0 -0
  239. data/examples/hangman/html/images/v_disabled.png +0 -0
  240. data/examples/hangman/html/images/v_enabled.png +0 -0
  241. data/examples/hangman/html/images/w_disabled.png +0 -0
  242. data/examples/hangman/html/images/w_enabled.png +0 -0
  243. data/examples/hangman/html/images/x_disabled.png +0 -0
  244. data/examples/hangman/html/images/x_enabled.png +0 -0
  245. data/examples/hangman/html/images/y_disabled.png +0 -0
  246. data/examples/hangman/html/images/y_enabled.png +0 -0
  247. data/examples/hangman/html/images/z_disabled.png +0 -0
  248. data/examples/hangman/html/images/z_enabled.png +0 -0
  249. data/examples/hangman/html/resources/word_list.txt +35 -0
  250. data/examples/hangman/html/start.xhtml +18 -0
  251. data/examples/hangman/html/style/hangman.css +27 -0
  252. data/examples/hangman/source/hangman.rb +71 -0
  253. data/examples/hilo/html/game_over.xhtml +15 -0
  254. data/examples/hilo/html/guess.xhtml +18 -0
  255. data/examples/hilo/html/start.xhtml +17 -0
  256. data/examples/hilo/source/hilo.rb +51 -0
  257. data/examples/simplest/html/start.xhtml +0 -0
  258. data/examples/simplest/source/simplest.rb +39 -0
  259. data/examples/stateful_counters/html/counters.xhtml +49 -0
  260. data/examples/stateful_counters/html/style/main.css +441 -0
  261. data/examples/stateful_counters/source/stateful_counters.rb +68 -0
  262. data/lib/trellis/core_components.rb +870 -0
  263. data/lib/trellis/jquery.rb +38 -0
  264. data/lib/trellis/logging.rb +60 -0
  265. data/lib/trellis/trellis.rb +701 -0
  266. data/lib/trellis/utils.rb +209 -0
  267. data/lib/trellis/version.rb +35 -0
  268. data/lib/trellis.rb +27 -0
  269. data/script/console +10 -0
  270. data/script/destroy +14 -0
  271. data/script/generate +14 -0
  272. data/script/txt2html +82 -0
  273. data/setup.rb +1585 -0
  274. data/tasks/deployment.rake +34 -0
  275. data/tasks/environment.rake +7 -0
  276. data/tasks/rspec.rake +21 -0
  277. data/tasks/website.rake +17 -0
  278. data/test/application_spec.rb +79 -0
  279. data/test/component_spec.rb +88 -0
  280. data/test/core_extensions_spec.rb +108 -0
  281. data/test/fixtures/application_spec_applications.rb +51 -0
  282. data/test/page_spec.rb +46 -0
  283. data/test/renderer_spec.rb +12 -0
  284. data/test/router_spec.rb +52 -0
  285. data/test/spec.opts +1 -0
  286. data/test/spec_helper.rb +16 -0
  287. data/website/index.html +124 -0
  288. data/website/index.txt +119 -0
  289. data/website/javascripts/rounded_corners_lite.inc.js +285 -0
  290. data/website/stylesheets/screen.css +138 -0
  291. data/website/template.html.erb +48 -0
  292. metadata +512 -0
@@ -0,0 +1,870 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #--
4
+ # Copyright &169;2001-2008 Integrallis Software, LLC.
5
+ # All Rights Reserved.
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject to
13
+ # the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+ #++
26
+
27
+ require 'trellis/trellis'
28
+ require 'paginator'
29
+
30
+ module Trellis
31
+ module CoreComponents
32
+
33
+ # Component that triggers an action on the server with a subsequent full
34
+ # page refresh
35
+ #
36
+ class ActionLink < Trellis::Component
37
+ render do |tag|
38
+ source = tag.attr['tid']
39
+ context = tag.attr['context']
40
+ target_page = tag.attr['page'] || tag.globals.page.class.name
41
+ url_root = tag.globals.page.class.url_root
42
+
43
+ if context
44
+ value = tag.locals.instance_eval(context) || tag.globals.instance_eval(context)
45
+ end
46
+
47
+ if source
48
+ if context
49
+ id = "#{source}_#{value}"
50
+ href = "#{url_root}/#{target_page}.select_#{source}/#{value}"
51
+ else
52
+ id = "#{source}"
53
+ href = "#{url_root}/#{target_page}.select_#{source}"
54
+ end
55
+ else
56
+ if context
57
+ id = "action_link_#{value}"
58
+ href = "#{url_root}/#{target_page}.select/#{value}"
59
+ else
60
+ id = "action_link"
61
+ href = "#{url_root}/#{target_page}.select"
62
+ end
63
+ end
64
+
65
+ builder = Builder::XmlMarkup.new
66
+ text = builder.a('${contents}', "href" => href, "id" => id)
67
+ text.replace_ant_style_property('contents', tag.expand)
68
+ end
69
+ end
70
+
71
+ #
72
+ #
73
+ #
74
+ class Loop < Trellis::Component
75
+ render do |tag|
76
+ value_name = tag.attr['value']
77
+ value = "#{value_name}="
78
+ # make value available by name to the page
79
+ source = tag.attr['source']
80
+ start, finish = source.split('..')
81
+ content = ''
82
+ (start..finish).each do |n|
83
+ tag.locals.send(value.to_sym, n)
84
+ content << tag.expand
85
+ end
86
+ content
87
+ end
88
+ end
89
+
90
+ #
91
+ #
92
+ #
93
+ class Each < Trellis::Component
94
+ render do |tag|
95
+ value_name = tag.attr['value']
96
+ value = "#{value_name}="
97
+ # make value available by name to the page
98
+ source = tag.attr['source']
99
+ begin
100
+ iterator = tag.locals.instance_eval(source) || tag.globals.instance_eval(source)
101
+ rescue NoMethodError
102
+ iterator = []
103
+ end
104
+ content = ''
105
+ iterator.each do |n|
106
+ tag.locals.send(value.to_sym, n)
107
+ content << tag.expand
108
+ end if iterator
109
+ content
110
+ end
111
+ end
112
+
113
+ #
114
+ #
115
+ #
116
+ class Value < Trellis::Component
117
+ render do |tag|
118
+ name = tag.attr['name']
119
+ unless name.include?('.')
120
+ value = tag.locals.send(name.to_sym) || tag.globals.send(name.to_sym)
121
+ else
122
+ target_name, method = name.split('.')
123
+ target = tag.locals.send(target_name.to_sym) || tag.globals.send(target_name.to_sym)
124
+ value = target.send(method.to_sym) if target
125
+ end
126
+ value
127
+ end
128
+ end
129
+
130
+ #
131
+ #
132
+ #
133
+ class PageLink < Trellis::Component
134
+ render do |tag|
135
+ url_root = tag.globals.page.class.url_root
136
+ page_name = tag.attr['tpage']
137
+ id = tag.attr['tid'] || page_name
138
+ href = "#{url_root}/#{page_name}"
139
+ contents = tag.expand
140
+ builder = Builder::XmlMarkup.new
141
+ builder.a(contents, "href" => href, "id" => "page_link_#{id}")
142
+ end
143
+ end
144
+
145
+ #
146
+ #
147
+ #
148
+ class Img < Trellis::Component
149
+ render do |tag|
150
+ attrs = tag.attr.exclude_keys('src', 'alt')
151
+
152
+ # resolve the ${} variables
153
+ attrs['src'] = Utils.expand_properties_in_tag(tag.attr['src'], tag)
154
+ attrs['alt'] = Utils.expand_properties_in_tag(tag.attr['alt'], tag)
155
+
156
+ builder = Builder::XmlMarkup.new
157
+ builder.img(attrs)
158
+ end
159
+ end
160
+
161
+ #
162
+ #
163
+ #
164
+ class Remove < Trellis::Component
165
+ render do |tag|
166
+ # do nothing
167
+ end
168
+ end
169
+
170
+ #
171
+ #
172
+ #
173
+ class If < Trellis::Component
174
+ render do |tag|
175
+ # resolve the ${} variables
176
+ test = Utils.expand_properties_in_tag(tag.attr['test'], tag)
177
+
178
+ # TODO I'm suspecting this is buggy! plus should we catch exceptions?
179
+ local = tag.locals.instance_eval(test)
180
+ global = tag.globals.instance_eval(test)
181
+
182
+ result = false
183
+ if local
184
+ result = local
185
+ elsif global
186
+ result = global
187
+ end
188
+
189
+ content = ''
190
+ if result
191
+ content << tag.expand
192
+ else
193
+ # find the else tag and expand it
194
+ end
195
+ content
196
+ end
197
+ end
198
+
199
+ #
200
+ #
201
+ #
202
+ class Unless < Trellis::Component
203
+ render do |tag|
204
+ # resolve the ${} variables
205
+ test = Utils.expand_properties_in_tag(tag.attr['test'], tag)
206
+
207
+ local = tag.locals.instance_eval(test)
208
+ global = tag.globals.instance_eval(test)
209
+
210
+ result = false
211
+ if local
212
+ result = !local
213
+ elsif global
214
+ result = !global
215
+ end
216
+
217
+ content = ''
218
+ if result
219
+ content << tag.expand
220
+ else
221
+ # find the else tag and expand it
222
+ end
223
+ content
224
+ end
225
+ end
226
+
227
+ #
228
+ #
229
+ #
230
+ class Button < Trellis::Component
231
+ render do |tag|
232
+ attrs = tag.attr.exclude_keys('tid', 'name', 'type')
233
+ attrs['name'] = "#{tag.attr['tid']}"
234
+ contents = tag.expand
235
+ builder = Builder::XmlMarkup.new
236
+ builder.button(contents, attrs)
237
+ end
238
+ end
239
+
240
+ #
241
+ #
242
+ #
243
+ class Form < Trellis::Component
244
+ render do |tag|
245
+ url_root = tag.globals.page.class.url_root
246
+ form_name = tag.attr['tid']
247
+ on_behalf = tag.attr['on_behalf']
248
+ method = tag.attr['method'] || 'GET'
249
+ tag.locals.form_name = form_name
250
+ href = "#{url_root}/#{tag.globals.page.class.name}.submit_#{(on_behalf ? on_behalf : form_name)}"
251
+ builder = Builder::XmlMarkup.new
252
+ builder.form("name" => form_name, "action" => href, "method" => method) do |form|
253
+ form << tag.expand
254
+ end
255
+ end
256
+ end
257
+
258
+ #
259
+ #
260
+ #
261
+ class Submit < Trellis::Component
262
+ tag_name "submit"
263
+
264
+ contained_in "form"
265
+
266
+ render do |tag|
267
+ attrs = tag.attr.exclude_keys('tid', 'name', 'type')
268
+ attrs['name'] = "#{tag.locals.form_name}_#{tag.attr['tid']}"
269
+ attrs['type'] = 'submit'
270
+ builder = Builder::XmlMarkup.new
271
+ builder.input(attrs)
272
+ end
273
+ end
274
+
275
+ #
276
+ #
277
+ #
278
+ class CheckBox < Trellis::Component
279
+ tag_name "check_box"
280
+
281
+ contained_in "form"
282
+
283
+ render do |tag|
284
+ attrs = tag.attr.exclude_keys('tid', 'name', 'type')
285
+ attrs['name'] = "#{tag.locals.form_name}_#{tag.attr['tid']}"
286
+ attrs['type'] = 'checkbox'
287
+ builder = Builder::XmlMarkup.new
288
+ builder.input(attrs)
289
+ end
290
+ end
291
+
292
+ #
293
+ #
294
+ #
295
+ class TextField < Trellis::Component
296
+ tag_name "text_field"
297
+
298
+ contained_in "form"
299
+
300
+ render do |tag|
301
+ attrs = tag.attr.exclude_keys('tid', 'name', 'type', 'value')
302
+ attrs['name'] = "#{tag.locals.form_name}_#{tag.attr['tid']}"
303
+ value = tag.attr['value']
304
+ literal = tag.attr['literal'] =~ /^(y|yes|true)$/
305
+ if value
306
+ if !literal
307
+ resolved_value = ''
308
+ unless value.include?('.')
309
+ resolved_value = tag.locals.send(value.to_sym) || tag.globals.send(value.to_sym)
310
+ else
311
+ target_name, method = value.split('.')
312
+ target = tag.locals.send(target_name.to_sym) || tag.globals.send(target_name.to_sym)
313
+ resolved_value = target.send(method.to_sym) if target
314
+ end
315
+ attrs['value'] = resolved_value if resolved_value
316
+ else
317
+ attrs['value'] = value
318
+ end
319
+ end
320
+
321
+ attrs['type'] = 'text'
322
+ builder = Builder::XmlMarkup.new
323
+ builder.input(attrs)
324
+ end
325
+ end
326
+
327
+ #
328
+ #
329
+ #
330
+ class TextArea < Trellis::Component
331
+ tag_name "text_area"
332
+
333
+ contained_in "form"
334
+
335
+ render do |tag|
336
+ attrs = tag.attr.exclude_keys('tid', 'name', 'keep_contents')
337
+ attrs['name'] = "#{tag.locals.form_name}_#{tag.attr['tid']}"
338
+ keep_contents = tag.attr['keep_contents']
339
+ if keep_contents
340
+ contents = keep_contents =~ /^(y|yes|true)$/ ? tag.expand : ''
341
+ else
342
+ contents = ''
343
+ end
344
+ builder = Builder::XmlMarkup.new
345
+ builder.textarea(contents, attrs)
346
+ end
347
+ end
348
+
349
+ #
350
+ #
351
+ #
352
+ class Password < Trellis::Component
353
+ tag_name "password"
354
+
355
+ contained_in "form"
356
+
357
+ render do |tag|
358
+ attrs = tag.attr.exclude_keys('tid', 'name', 'type')
359
+ attrs['name'] = "#{tag.locals.form_name}_#{tag.attr['tid']}"
360
+ attrs['type'] = 'password'
361
+ builder = Builder::XmlMarkup.new
362
+ builder.input(attrs)
363
+ end
364
+ end
365
+
366
+ #
367
+ #
368
+ #
369
+ class Hidden < Trellis::Component
370
+ tag_name "hidden"
371
+
372
+ contained_in "form"
373
+
374
+ render do |tag|
375
+ attrs = tag.attr.exclude_keys('tid', 'name', 'type', 'value')
376
+ attrs['name'] = "#{tag.locals.form_name}_#{tag.attr['tid']}"
377
+ value = tag.attr['value']
378
+ literal = tag.attr['literal'] =~ /^(y|yes|true)$/
379
+ if value
380
+ if !literal
381
+ resolved_value = ''
382
+ unless value.include?('.')
383
+ resolved_value = tag.locals.send(value.to_sym) || tag.globals.send(value.to_sym)
384
+ else
385
+ target_name, method = value.split('.')
386
+ target = tag.locals.send(target_name.to_sym) || tag.globals.send(target_name.to_sym)
387
+ resolved_value = target.send(method.to_sym)
388
+ end
389
+ attrs['value'] = resolved_value
390
+ else
391
+ attrs['value'] = value
392
+ end
393
+ end
394
+
395
+ attrs['type'] = 'hidden'
396
+ builder = Builder::XmlMarkup.new
397
+ builder.input(attrs)
398
+ end
399
+ end
400
+
401
+ #
402
+ #
403
+ #
404
+ class Select < Trellis::Component
405
+ tag_name "select"
406
+
407
+ contained_in "form"
408
+
409
+ render do |tag|
410
+ attrs = tag.attr.exclude_keys('tid', 'select_if', 'selected_value', 'source')
411
+ attrs['name'] = "#{tag.locals.form_name}_#{tag.attr['tid']}"
412
+ expression = tag.attr['source'] # something we can iterate over
413
+ selected = Utils.evaluate_tag_attribute('select_if', tag)
414
+ selected_value = Utils.evaluate_tag_attribute('selected_value', tag)
415
+ value_accessor = Utils.expand_properties_in_tag(tag.attr['value'], tag)
416
+
417
+ builder = Builder::XmlMarkup.new
418
+ builder.select(attrs) do
419
+ if expression.include? '..'
420
+ start, finish = expression.split('..')
421
+ (start..finish).each do |item|
422
+ value = value_accessor ? item.instance_eval(value_accessor) : item
423
+ unless item == selected
424
+ builder.option item, :value => value
425
+ else
426
+ builder.option item, selected => (selected_value.nil? ? 'yes' : selected_value), :value => value
427
+ end
428
+ end
429
+ else
430
+ source = Utils.evaluate_tag_attribute('source', tag)
431
+ if source.respond_to? :each
432
+ source.each do |item|
433
+ value = value_accessor ? item.instance_eval(value_accessor) : item
434
+ unless item == selected
435
+ builder.option item, :value => value
436
+ else
437
+ builder.option item, :selected => (selected_value.nil? ? 'yes' : selected_value), :value => value
438
+ end
439
+ end
440
+ elsif source.respond_to? :each_pair
441
+ source.each_pair do |name,value|
442
+ unless value == selected
443
+ builder.option name, :value => value
444
+ else
445
+ builder.option name, :selected => (selected_value.nil? ? value : selected_value), :value => value
446
+ end
447
+ end
448
+ end
449
+ end
450
+ end
451
+ end
452
+ end
453
+
454
+
455
+ # Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
456
+ # assigned to the template (identified by +object+). The text of label will default to the attribute name unless you specify
457
+ # it explicitly. Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
458
+ # onto the HTML as an HTML element attribute as in the example shown.
459
+ #
460
+ # ==== Examples
461
+ # label(:post, :title)
462
+ # #=> <label for="post_title">Title</label>
463
+ #
464
+ # label(:post, :title, "A short title")
465
+ # #=> <label for="post_title">A short title</label>
466
+ #
467
+ # label(:post, :title, "A short title", :class => "title_label")
468
+ # #=> <label for="post_title" class="title_label">A short title</label>
469
+ class Label < Trellis::Component
470
+ render do |tag|
471
+ target = "#{tag.locals.form_name}_#{tag.attr['for']}"
472
+ contents = tag.expand
473
+ builder = Builder::XmlMarkup.new
474
+ builder.label(contents, :for => target)
475
+ end
476
+ end
477
+
478
+ # Creates a file upload field. If you are using file uploads then you will also need
479
+ # to set the multipart option for the form tag:
480
+ #
481
+ # <%= form_tag { :action => "post" }, { :multipart => true } %>
482
+ # <label for="file">File to Upload</label> <%= file_field_tag "file" %>
483
+ # <%= submit_tag %>
484
+ # <%= end_form_tag %>
485
+ #
486
+ # The specified URL will then be passed a File object containing the selected file, or if the field
487
+ # was left blank, a StringIO object.
488
+ #
489
+ # ==== Options
490
+ # * Creates standard HTML attributes for the tag.
491
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
492
+ #
493
+ # ==== Examples
494
+ # file_field_tag 'attachment'
495
+ # # => <input id="attachment" name="attachment" type="file" />
496
+ #
497
+ # file_field_tag 'avatar', :class => 'profile-input'
498
+ # # => <input class="profile-input" id="avatar" name="avatar" type="file" />
499
+ #
500
+ # file_field_tag 'picture', :disabled => true
501
+ # # => <input disabled="disabled" id="picture" name="picture" type="file" />
502
+ #
503
+ # file_field_tag 'resume', :value => '~/resume.doc'
504
+ # # => <input id="resume" name="resume" type="file" value="~/resume.doc" />
505
+ #
506
+ # file_field_tag 'user_pic', :accept => 'image/png,image/gif,image/jpeg'
507
+ # # => <input accept="image/png,image/gif,image/jpeg" id="user_pic" name="user_pic" type="file" />
508
+ #
509
+ # file_field_tag 'file', :accept => 'text/html', :class => 'upload', :value => 'index.html'
510
+ # # => <input accept="text/html" class="upload" id="file" name="file" type="file" value="index.html" />
511
+ class File < Trellis::Component
512
+
513
+ render do |tag|
514
+
515
+ end
516
+ end
517
+
518
+ # TODO: Need radio button group and a standalone radio button
519
+
520
+
521
+ #
522
+ #
523
+ #
524
+ class Grid < Trellis::Component
525
+ is_stateful
526
+
527
+ tag_name "grid"
528
+
529
+ attr_accessor :source
530
+ field :page_position, :persistent => true
531
+ attr_reader :properties
532
+ attr_reader :commands
533
+ attr_reader :counter_method
534
+ attr_reader :counter_method_arguments
535
+ attr_reader :retrieve_block
536
+ attr_reader :sort_properties
537
+ field :sorted_by, :persistent => true
538
+ field :sort_direction, :persistent => true, :default_value => :ascending
539
+
540
+ def initialize
541
+ @properties = []
542
+ @sort_properties = []
543
+ @commands = []
544
+ end
545
+
546
+ # must be called before rendering
547
+ def columns(*syms)
548
+ syms.each do |sym|
549
+ @properties << sym
550
+ end
551
+ end
552
+
553
+ def add_command(options=[])
554
+ # extract options
555
+ name = options[:name] if options
556
+ page = options[:page] if options
557
+ context = options[:context] if options
558
+ image = options[:image] if options
559
+
560
+ @commands << lambda do |tag, object|
561
+ tid = tag.attr['tid']
562
+ value = object.send(context.to_sym)
563
+ url_root = tag.globals.page.class.url_root
564
+ page = tag.globals.page.class.name unless page
565
+
566
+ href = "#{url_root}/#{page}.#{name}_#{tid}/#{value}"
567
+
568
+ %{
569
+ <a href="#{href}">
570
+ <img src='#{image}'/>
571
+ </a>
572
+ }
573
+ end
574
+ end
575
+
576
+ def sort_by_all_except(*syms)
577
+ @sort_properties = @properties.reject { |property| syms.includes?(property)}
578
+ end
579
+
580
+ def sort_by(*syms)
581
+ unless syms.first == :all
582
+ @sort_properties = syms
583
+ else
584
+ @sort_properties = @properties
585
+ end
586
+ end
587
+
588
+ def size_accessor(symbol, args)
589
+ @counter_method, @counter_method_arguments = symbol, args
590
+ end
591
+
592
+ def retrieve_method(&block)
593
+ @retrieve_block = block
594
+ end
595
+
596
+ # event handlers
597
+
598
+ def on_page(page)
599
+ @page_position = page.to_i # must use the cohersion built in capabilities
600
+ end
601
+
602
+ def on_sort(property)
603
+ to_sym = property.to_sym
604
+ if @sort_properties.include?(to_sym)
605
+ if @sorted_by != to_sym
606
+ @sorted_by = to_sym
607
+ else
608
+ @sort_direction = @sort_direction == :ascending ? :descending : :ascending
609
+ end
610
+ end
611
+ end
612
+
613
+ render do |tag|
614
+ url_root = tag.globals.page.class.url_root
615
+ # get the page object
616
+ page = tag.globals.page
617
+ # get the tag properties
618
+ tid = tag.attr['tid']
619
+ rows_per_page = tag.attr['rows_per_page'] || 3
620
+ # get the instance of the component from the page
621
+ grid = page.send("grid_#{tid}")
622
+ # get the properties or columns
623
+ properties = grid.properties
624
+ # get the sort properties
625
+ sort_properties = grid.sort_properties
626
+ # current page
627
+ current_page = grid.page_position || 1
628
+ # sort information
629
+ sorted_by = grid.sorted_by
630
+ sort_direction = grid.sort_direction
631
+ commands = grid.commands
632
+ # get the source from either the tag or the component instance itself
633
+ source = tag.attr['source'] || grid.source
634
+ # get the array or collection that we can iterate over
635
+ iterator = tag.locals.send(source.to_sym) || tag.globals.send(source.to_sym)
636
+
637
+ builder = Builder::XmlMarkup.new
638
+
639
+ # build the table
640
+ builder.div(:class => "t-data-grid") {
641
+
642
+ # configure pagination
643
+ #TODO need to implement page ranging
644
+ range = tag.attr['page_range'] || 5
645
+ available_rows = 0
646
+ unless grid.counter_method
647
+ available_rows = iterator.length
648
+ else
649
+ unless grid.counter_method_arguments
650
+ available_rows = iterator.send(grid.counter_method)
651
+ else
652
+ available_rows = iterator.send(grid.counter_method, grid.counter_method_arguments)
653
+ end
654
+ end
655
+
656
+ # configure the pager
657
+ pager = Paginator.new(available_rows, rows_per_page) do |offset, per_page|
658
+ rows = nil
659
+ unless grid.retrieve_method
660
+ if sorted_by
661
+ iterator = iterator.sort_by { |row| row.send(sorted_by) }
662
+ iterator.reverse! if sort_direction == :descending
663
+ end
664
+ rows = iterator[offset, per_page]
665
+ else
666
+ rows = grid.retrieve_method.call
667
+ end
668
+ rows
669
+ end
670
+
671
+ # get the current page from the pager
672
+ rows = pager.page(current_page)
673
+
674
+ # render the pager control if necessary
675
+ unless pager.number_of_pages < 2
676
+ builder.div(:class => "t-data-grid-pager") {
677
+ # loop over the pages
678
+ (1..pager.number_of_pages).each do |page_num|
679
+ if page_num == current_page
680
+ builder.span("#{page_num}", :class => "current")
681
+ else
682
+ builder.a("#{page_num}", :href => "#{url_root}/#{page.class.name}.page_grid#{tid}/#{page_num}", :title => "Go to page #{page_num}")
683
+ end
684
+ end
685
+ }
686
+ end
687
+
688
+ # build the html
689
+ builder.table(:class => "t-data-grid") {
690
+ # header
691
+ builder.thead {
692
+ builder.tr {
693
+ properties.each_index { |index|
694
+ property = properties[index]
695
+ field_name = property.to_s.humanize
696
+
697
+ if properties.length == index + 1
698
+ css_class = "#{field_name} t-last"
699
+ elsif index == 0
700
+ css_class = "#{field_name} t-first"
701
+ else
702
+ css_class = "#{field_name}"
703
+ end
704
+
705
+ sort_image = 'sortable.png'
706
+ if property == sorted_by
707
+ if sort_direction == :ascending
708
+ sort_image = 'sort-desc.png'
709
+ elsif sort_direction == :descending
710
+ sort_image = 'sort-asc.png'
711
+ end
712
+ end
713
+
714
+ unless sort_properties.include?(property)
715
+ builder.th(field_name, :class => css_class)
716
+ else
717
+ builder.th(:class => css_class) {
718
+ builder.a(field_name, :href => "#{url_root}/#{page.class.name}.sort_grid#{tid}/#{property}")
719
+ builder.a(:href => "#{url_root}/#{page.class.name}.sort_grid#{tid}/#{property}") {
720
+ builder.img(:alt => "[Sortable]", :class => "t-sort-icon", :id => "#{property}:sort", :src => "#{url_root}/images/#{sort_image}", :name => "#{property}:sort")
721
+ }
722
+ }
723
+ end
724
+ }
725
+ # add columns for commands
726
+ if commands && !commands.empty?
727
+ (1..commands.size).each do
728
+ builder.th
729
+ end
730
+ end
731
+ }
732
+ }
733
+
734
+ # body
735
+ builder.tbody {
736
+ # data
737
+ index = 0
738
+ rows.each do |item|
739
+ if (rows.last_item_number - rows.first_item_number) == index + 1
740
+ css_class = "t-last"
741
+ elsif index == 0
742
+ css_class = "t-first"
743
+ else
744
+ css_class = nil
745
+ end
746
+ index = index + 1
747
+
748
+ if css_class
749
+ builder.tr(:class => css_class) {
750
+ properties.each { |property|
751
+ field_value = item.send(property)
752
+ builder.td("#{field_value}", :class => "#{property}")
753
+ }
754
+ # add columns for commands
755
+ if commands && !commands.empty?
756
+ commands.each { |command|
757
+ builder.td { |td| td << command.call(tag, item)}
758
+ }
759
+ end
760
+ }
761
+ else
762
+ builder.tr {
763
+ properties.each { |property|
764
+ field_value = item.send(property)
765
+ builder.td("#{field_value}", :class => "#{property}")
766
+ }
767
+ # add columns for commands
768
+ if commands && !commands.empty?
769
+ commands.each { |command|
770
+ builder.td { |td| td << command.call(tag, item)}
771
+ }
772
+ end
773
+ }
774
+ end
775
+ end if rows
776
+ }
777
+ }
778
+ }
779
+ end
780
+ end
781
+
782
+ class ObjectEditor < Trellis::Component
783
+ is_stateful
784
+
785
+ depends_on :form, :submit
786
+
787
+ field :model, :persistent => true
788
+ attr_reader :retrieve_block
789
+ attr_reader :properties
790
+ attr_accessor :submit_text
791
+ attr_reader :submit_block
792
+
793
+ def initialize
794
+ @properties = []
795
+ end
796
+
797
+ # must be called before rendering
798
+ def fields(*syms)
799
+ syms.each do |sym|
800
+ @properties << sym
801
+ end
802
+ end
803
+
804
+ def on_submit(&block)
805
+ # populate the object with values from the session
806
+ if page.params
807
+ properties.each do |property|
808
+ value = page.params[property.to_sym]
809
+ @model.instance_variable_set("@#{property}".to_sym, value)
810
+ end
811
+ end
812
+ if block_given?
813
+ @submit_block = block
814
+ else
815
+ @submit_block.call(@model)
816
+ end
817
+ end
818
+
819
+ render do |tag|
820
+ url_root = tag.globals.page.class.url_root
821
+ # get the page object
822
+ page = tag.globals.page
823
+ # get the tag properties
824
+ tid = tag.attr['tid']
825
+ # get the instance of the component from the page
826
+ editor = page.send("object_editor_#{tid}")
827
+ # get the properties or columns
828
+ properties = editor.properties
829
+ # get the source from either the tag or the component instance itself
830
+ source = tag.attr['model'] || editor.model
831
+ submit_text = tag.attr['submit_text'] || editor.submit_text || "Submit"
832
+
833
+ builder = Builder::XmlMarkup.new
834
+
835
+ # the editor encloses a form
836
+ form = tag.render("form", "tid" => "form_#{tid}", "method" => "post", "on_behalf" => "object_editor#{tid}") do
837
+ builder.div(:class => "t-beaneditor") {
838
+ properties.each { |property|
839
+ field_name = property.to_s.humanize
840
+ field_value = source.send(property)
841
+ builder.div(:class => "t-beaneditor-row") {
842
+ builder.label(field_name, :for => property, :id => "#{property}:label")
843
+ builder.input(:id => property, :name => property, :type => "text", :value => field_value)
844
+ builder.img(:alt => "[Error]", :class => "t-error-icon t-invisible", :id => "#{property}:icon", :src => "#{url_root}/images/field-error-marker.gif", :name => "#{property}:icon")
845
+ }
846
+ } if source
847
+ builder.div(:class => "t-beaneditor-row") {
848
+ builder << tag.render("submit", "tid" => "submit", "name" => "whats_the_name", "value" => submit_text)
849
+ }
850
+ }
851
+ end
852
+ form
853
+ end
854
+
855
+ end
856
+
857
+ class Div < Trellis::Component
858
+ render do |tag|
859
+ attrs = tag.attr.exclude_keys('id', 'title')
860
+ attrs['id'] = Utils.expand_properties_in_tag(tag.attr['id'], tag) if tag.attr['id']
861
+ attrs['title'] = Utils.expand_properties_in_tag(tag.attr['title'], tag) if tag.attr['title']
862
+ builder = Builder::XmlMarkup.new
863
+ builder.div(attrs) { |div|
864
+ div << tag.expand
865
+ }
866
+ end
867
+ end
868
+
869
+ end
870
+ end