zen 0.2.8 → 0.3b

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (422) hide show
  1. data/.gems +20 -0
  2. data/.mailmap +3 -2
  3. data/.rvmrc +1 -1
  4. data/.travis.yml +13 -2
  5. data/.yardopts +13 -0
  6. data/AUTHORS +1 -1
  7. data/README.md +115 -77
  8. data/Rakefile +6 -7
  9. data/bin/zen +12 -5
  10. data/guide/_static/categories/categories.png +0 -0
  11. data/guide/_static/categories/category_groups.png +0 -0
  12. data/guide/_static/categories/new_category.png +0 -0
  13. data/guide/_static/categories/new_category_group.png +0 -0
  14. data/guide/_static/comments/comments.png +0 -0
  15. data/guide/_static/comments/edit_comment.png +0 -0
  16. data/guide/_static/custom_fields/custom_field_groups.png +0 -0
  17. data/guide/_static/custom_fields/custom_field_types.png +0 -0
  18. data/guide/_static/custom_fields/custom_fields.png +0 -0
  19. data/guide/_static/custom_fields/edit_custom_field_general.png +0 -0
  20. data/guide/_static/custom_fields/edit_custom_field_group.png +0 -0
  21. data/guide/_static/custom_fields/edit_custom_field_settings.png +0 -0
  22. data/guide/_static/custom_fields/edit_custom_field_type.png +0 -0
  23. data/guide/_static/menus/edit_menu.png +0 -0
  24. data/guide/_static/menus/edit_menu_item.png +0 -0
  25. data/guide/_static/menus/menu_items.png +0 -0
  26. data/guide/_static/menus/menus.png +0 -0
  27. data/guide/_static/sections/edit_entry.png +0 -0
  28. data/guide/_static/sections/edit_entry_categories.png +0 -0
  29. data/guide/_static/sections/edit_entry_general.png +0 -0
  30. data/guide/_static/sections/edit_entry_meta.png +0 -0
  31. data/guide/_static/sections/edit_section_comments.png +0 -0
  32. data/guide/_static/sections/edit_section_general.png +0 -0
  33. data/guide/_static/sections/edit_section_groups.png +0 -0
  34. data/guide/_static/sections/entries.png +0 -0
  35. data/guide/_static/sections/sections.png +0 -0
  36. data/guide/_static/settings/overview_general.png +0 -0
  37. data/guide/_static/settings/overview_security.png +0 -0
  38. data/guide/_static/settings/overview_user.png +0 -0
  39. data/guide/_static/users/edit_user.png +0 -0
  40. data/guide/_static/users/edit_user_group.png +0 -0
  41. data/guide/_static/users/edit_user_group_permissions.png +0 -0
  42. data/guide/_static/users/edit_user_permissions.png +0 -0
  43. data/guide/_static/users/overview.png +0 -0
  44. data/guide/_static/users/user_groups_overview.png +0 -0
  45. data/guide/asset_management.md +117 -0
  46. data/{CHANGELOG.md → guide/changelog.md} +42 -0
  47. data/guide/css/common.css +20 -0
  48. data/guide/getting_started.md +61 -0
  49. data/guide/hacking.md +52 -0
  50. data/guide/installation.md +44 -0
  51. data/guide/javascript.md +352 -0
  52. data/lib/vendor/sequel_sluggable.rb +137 -0
  53. data/lib/yard/tags.rb +13 -0
  54. data/lib/zen.rb +148 -78
  55. data/lib/zen/asset_groups.rb +85 -0
  56. data/lib/zen/bin/create.rb +267 -56
  57. data/lib/zen/bin/default.rb +84 -0
  58. data/lib/zen/controller/admin_controller.rb +1 -82
  59. data/lib/zen/controller/base_controller.rb +9 -5
  60. data/lib/zen/controller/frontend_controller.rb +0 -1
  61. data/lib/zen/controller/main_controller.rb +30 -42
  62. data/lib/zen/controller/preview.rb +9 -8
  63. data/lib/zen/controller/translations.rb +49 -0
  64. data/lib/zen/error.rb +17 -0
  65. data/lib/zen/event.rb +118 -0
  66. data/lib/zen/helper/asset.rb +63 -0
  67. data/lib/zen/helper/breadcrumb.rb +1 -4
  68. data/lib/zen/helper/controller.rb +73 -0
  69. data/lib/zen/helper/locale.rb +42 -0
  70. data/lib/zen/helper/message.rb +0 -3
  71. data/lib/zen/helper/search.rb +54 -0
  72. data/lib/zen/helper/stacked_aspect.rb +249 -0
  73. data/lib/zen/helper/theme.rb +3 -10
  74. data/lib/zen/language.rb +356 -181
  75. data/lib/zen/language/en/zen_general.rb +52 -0
  76. data/lib/zen/language/en/zen_models.rb +19 -0
  77. data/lib/zen/language/nl/zen_general.rb +57 -0
  78. data/lib/zen/language/nl/zen_models.rb +22 -0
  79. data/lib/zen/language/translation.rb +132 -0
  80. data/lib/zen/languages.rb +9 -0
  81. data/lib/zen/layout/admin.xhtml +3 -3
  82. data/lib/zen/layout/login.xhtml +1 -1
  83. data/lib/zen/markup.rb +189 -0
  84. data/lib/zen/model/helper.rb +65 -0
  85. data/lib/zen/model/init.rb +62 -0
  86. data/lib/zen/model/methods.rb +6 -7
  87. data/lib/zen/package.rb +312 -201
  88. data/lib/zen/package/all.rb +4 -3
  89. data/lib/zen/package/categories/lib/categories.rb +29 -41
  90. data/lib/zen/package/categories/lib/categories/controller/categories.rb +185 -130
  91. data/lib/zen/package/categories/lib/categories/controller/category_groups.rb +172 -102
  92. data/lib/zen/package/categories/lib/categories/helper/category.rb +4 -9
  93. data/lib/zen/package/categories/lib/categories/helper/category_frontend.rb +86 -0
  94. data/lib/zen/package/categories/lib/categories/language/en/categories.rb +40 -0
  95. data/lib/zen/package/categories/lib/categories/language/en/category_groups.rb +39 -0
  96. data/lib/zen/package/categories/lib/categories/language/nl/categories.rb +42 -0
  97. data/lib/zen/package/categories/lib/categories/language/nl/category_groups.rb +42 -0
  98. data/lib/zen/package/categories/lib/categories/model/category.rb +21 -25
  99. data/lib/zen/package/categories/lib/categories/model/category_group.rb +21 -15
  100. data/lib/zen/package/categories/lib/categories/view/admin/categories/form.xhtml +25 -26
  101. data/lib/zen/package/categories/lib/categories/view/admin/categories/index.xhtml +24 -24
  102. data/lib/zen/package/categories/lib/categories/view/admin/category-groups/form.xhtml +18 -20
  103. data/lib/zen/package/categories/lib/categories/view/admin/category-groups/index.xhtml +21 -18
  104. data/lib/zen/package/comments/lib/comments.rb +30 -50
  105. data/lib/zen/package/comments/lib/comments/anti_spam.rb +138 -0
  106. data/lib/zen/package/comments/lib/comments/controller/comments.rb +159 -92
  107. data/lib/zen/package/comments/lib/comments/controller/comments_form.rb +122 -34
  108. data/lib/zen/package/comments/lib/comments/helper/comment.rb +0 -3
  109. data/lib/zen/package/comments/lib/comments/helper/comment_frontend.rb +90 -0
  110. data/lib/zen/package/comments/lib/comments/language/en/comments.rb +57 -0
  111. data/lib/zen/package/comments/lib/comments/language/nl/comments.rb +61 -0
  112. data/lib/zen/package/comments/lib/comments/model/comment.rb +147 -49
  113. data/lib/zen/package/comments/lib/comments/model/comment_status.rb +0 -2
  114. data/lib/zen/package/comments/lib/comments/view/admin/comments/form.xhtml +37 -40
  115. data/lib/zen/package/comments/lib/comments/view/admin/comments/index.xhtml +23 -42
  116. data/lib/zen/package/comments/migrations/1308774099_comment_status.rb +12 -4
  117. data/lib/zen/package/comments/migrations/1313851786_remove_defensio_signature.rb +11 -0
  118. data/lib/zen/package/custom_fields/lib/custom_fields.rb +40 -43
  119. data/lib/zen/package/custom_fields/lib/custom_fields/blue_form_parameters.rb +72 -17
  120. data/lib/zen/package/custom_fields/lib/custom_fields/controller/custom_field_groups.rb +154 -118
  121. data/lib/zen/package/custom_fields/lib/custom_fields/controller/custom_field_types.rb +147 -90
  122. data/lib/zen/package/custom_fields/lib/custom_fields/controller/custom_fields.rb +161 -131
  123. data/lib/zen/package/custom_fields/lib/custom_fields/helper/custom_field.rb +4 -9
  124. data/lib/zen/package/custom_fields/lib/custom_fields/language/en/custom_field_groups.rb +42 -0
  125. data/lib/zen/package/custom_fields/lib/custom_fields/language/en/custom_field_types.rb +51 -0
  126. data/lib/zen/package/custom_fields/lib/custom_fields/language/en/custom_fields.rb +60 -0
  127. data/lib/zen/package/custom_fields/lib/custom_fields/language/nl/custom_field_groups.rb +41 -0
  128. data/lib/zen/package/custom_fields/lib/custom_fields/language/nl/custom_field_types.rb +49 -0
  129. data/lib/zen/package/custom_fields/lib/custom_fields/language/nl/custom_fields.rb +61 -0
  130. data/lib/zen/package/custom_fields/lib/custom_fields/model/custom_field.rb +18 -23
  131. data/lib/zen/package/custom_fields/lib/custom_fields/model/custom_field_group.rb +20 -19
  132. data/lib/zen/package/custom_fields/lib/custom_fields/model/custom_field_method.rb +1 -3
  133. data/lib/zen/package/custom_fields/lib/custom_fields/model/custom_field_type.rb +19 -9
  134. data/lib/zen/package/custom_fields/lib/custom_fields/model/custom_field_value.rb +18 -15
  135. data/lib/zen/package/custom_fields/lib/custom_fields/view/admin/custom-field-groups/form.xhtml +18 -18
  136. data/lib/zen/package/custom_fields/lib/custom_fields/view/admin/custom-field-groups/index.xhtml +30 -24
  137. data/lib/zen/package/custom_fields/lib/custom_fields/view/admin/custom-field-types/form.xhtml +15 -10
  138. data/lib/zen/package/custom_fields/lib/custom_fields/view/admin/custom-field-types/index.xhtml +24 -19
  139. data/lib/zen/package/custom_fields/lib/custom_fields/view/admin/custom-fields/form.xhtml +18 -13
  140. data/lib/zen/package/custom_fields/lib/custom_fields/view/admin/custom-fields/index.xhtml +30 -20
  141. data/lib/zen/package/extensions/lib/extensions.rb +20 -0
  142. data/lib/zen/package/extensions/lib/extensions/controller/extensions.rb +41 -0
  143. data/lib/zen/package/extensions/lib/extensions/language/en/extensions.rb +23 -0
  144. data/lib/zen/package/extensions/lib/extensions/language/nl/extensions.rb +25 -0
  145. data/lib/zen/package/extensions/lib/extensions/view/admin/extensions/index.xhtml +86 -0
  146. data/lib/zen/package/menu.rb +109 -0
  147. data/lib/zen/package/menus/lib/menus.rb +25 -34
  148. data/lib/zen/package/menus/lib/menus/controller/menu_items.rb +143 -107
  149. data/lib/zen/package/menus/lib/menus/controller/menus.rb +166 -115
  150. data/lib/zen/package/menus/lib/menus/helper/menu.rb +2 -8
  151. data/lib/zen/package/menus/lib/menus/helper/menu_frontend.rb +114 -0
  152. data/lib/zen/package/menus/lib/menus/language/en/menu_items.rb +48 -0
  153. data/lib/zen/package/menus/lib/menus/language/en/menus.rb +48 -0
  154. data/lib/zen/package/menus/lib/menus/language/nl/menu_items.rb +48 -0
  155. data/lib/zen/package/menus/lib/menus/language/nl/menus.rb +50 -0
  156. data/lib/zen/package/menus/lib/menus/model/menu.rb +24 -17
  157. data/lib/zen/package/menus/lib/menus/model/menu_item.rb +37 -13
  158. data/lib/zen/package/menus/lib/menus/view/admin/menu-items/form.xhtml +32 -25
  159. data/lib/zen/package/menus/lib/menus/view/admin/menu-items/index.xhtml +23 -17
  160. data/lib/zen/package/menus/lib/menus/view/admin/menus/form.xhtml +31 -26
  161. data/lib/zen/package/menus/lib/menus/view/admin/menus/index.xhtml +20 -22
  162. data/lib/zen/package/sections/lib/sections.rb +25 -68
  163. data/lib/zen/package/sections/lib/sections/controller/section_entries.rb +178 -138
  164. data/lib/zen/package/sections/lib/sections/controller/sections.rb +173 -140
  165. data/lib/zen/package/sections/lib/sections/helper/section.rb +3 -6
  166. data/lib/zen/package/sections/lib/sections/helper/section_frontend.rb +146 -0
  167. data/lib/zen/package/sections/lib/sections/language/en/section_entries.rb +50 -0
  168. data/lib/zen/package/sections/lib/sections/language/en/sections.rb +55 -0
  169. data/lib/zen/package/sections/lib/sections/language/nl/section_entries.rb +50 -0
  170. data/lib/zen/package/sections/lib/sections/language/nl/sections.rb +55 -0
  171. data/lib/zen/package/sections/lib/sections/model/section.rb +36 -36
  172. data/lib/zen/package/sections/lib/sections/model/section_entry.rb +52 -59
  173. data/lib/zen/package/sections/lib/sections/model/section_entry_status.rb +2 -2
  174. data/lib/zen/package/sections/lib/sections/view/admin/form.xhtml +24 -21
  175. data/lib/zen/package/sections/lib/sections/view/admin/index.xhtml +24 -26
  176. data/lib/zen/package/sections/lib/sections/view/admin/section-entries/form.xhtml +33 -31
  177. data/lib/zen/package/sections/lib/sections/view/admin/section-entries/index.xhtml +24 -25
  178. data/lib/zen/package/sections/migrations/1308672298_use_id_for_default_section.rb +14 -10
  179. data/lib/zen/package/sections/migrations/1308813320_section_entry_statuses.rb +12 -6
  180. data/lib/zen/package/settings/lib/settings.rb +25 -120
  181. data/lib/zen/package/settings/lib/settings/blue_form_parameters.rb +157 -0
  182. data/lib/zen/package/settings/lib/settings/controller/settings.rb +94 -69
  183. data/lib/zen/package/settings/lib/settings/language/en/settings.rb +41 -0
  184. data/lib/zen/package/settings/lib/settings/language/nl/settings.rb +41 -0
  185. data/lib/zen/package/settings/lib/settings/model/setting.rb +0 -2
  186. data/lib/zen/package/settings/lib/settings/setting.rb +379 -0
  187. data/lib/zen/package/settings/lib/settings/setting_groups.rb +11 -0
  188. data/lib/zen/package/settings/lib/settings/settings.rb +83 -0
  189. data/lib/zen/package/settings/lib/settings/settings_group.rb +84 -0
  190. data/lib/zen/package/settings/lib/settings/singleton_methods.rb +35 -0
  191. data/lib/zen/package/settings/lib/settings/view/admin/settings/index.xhtml +15 -57
  192. data/lib/zen/package/settings/migrations/1321197919_remove_unused_columns.rb +17 -0
  193. data/lib/zen/package/users/lib/users.rb +51 -36
  194. data/lib/zen/package/users/lib/users/controller/user_groups.rb +133 -98
  195. data/lib/zen/package/users/lib/users/controller/users.rb +253 -136
  196. data/lib/zen/package/users/lib/users/helper/access.rb +102 -0
  197. data/lib/zen/package/users/lib/users/helper/acl.rb +113 -0
  198. data/lib/zen/package/users/lib/users/helper/users.rb +41 -24
  199. data/lib/zen/package/users/lib/users/language/en/permissions.rb +16 -0
  200. data/lib/zen/package/users/lib/users/language/en/user_groups.rb +39 -0
  201. data/lib/zen/package/users/lib/users/language/en/users.rb +73 -0
  202. data/lib/zen/package/users/lib/users/language/nl/permissions.rb +16 -0
  203. data/lib/zen/package/users/lib/users/language/nl/user_groups.rb +41 -0
  204. data/lib/zen/package/users/lib/users/language/nl/users.rb +74 -0
  205. data/lib/zen/package/users/lib/users/model/permission.rb +28 -0
  206. data/lib/zen/package/users/lib/users/model/user.rb +104 -65
  207. data/lib/zen/package/users/lib/users/model/user_group.rb +28 -24
  208. data/lib/zen/package/users/lib/users/model/user_status.rb +27 -0
  209. data/lib/zen/package/users/lib/users/public/admin/css/users/permissions.css +22 -0
  210. data/lib/zen/package/users/lib/users/public/admin/js/users/permissions.js +33 -0
  211. data/lib/zen/package/users/lib/users/settings.rb +19 -0
  212. data/lib/zen/package/users/lib/users/view/admin/user-groups/form.xhtml +58 -28
  213. data/lib/zen/package/users/lib/users/view/admin/user-groups/index.xhtml +19 -15
  214. data/lib/zen/package/users/lib/users/view/admin/users/form.xhtml +93 -62
  215. data/lib/zen/package/users/lib/users/view/admin/users/index.xhtml +23 -36
  216. data/lib/zen/package/users/lib/users/view/admin/users/login.xhtml +13 -6
  217. data/lib/zen/package/users/lib/users/view/admin/users/permissions.xhtml +33 -0
  218. data/lib/zen/package/users/lib/users/view/admin/users/register.xhtml +52 -0
  219. data/lib/zen/package/users/migrations/1313786058_update_default_date.rb +41 -0
  220. data/lib/zen/package/users/migrations/1316432327_permissions.rb +36 -0
  221. data/lib/zen/package/users/migrations/1320272365_status_ids.rb +67 -0
  222. data/lib/zen/public/admin/css/zen/buttons.css +3 -11
  223. data/lib/zen/public/admin/css/zen/datepicker.css +23 -13
  224. data/lib/zen/public/admin/css/zen/editor.css +6 -14
  225. data/lib/zen/public/admin/css/zen/forms.css +19 -12
  226. data/lib/zen/public/admin/css/zen/general.css +22 -18
  227. data/lib/zen/public/admin/css/zen/grid.css +1 -20
  228. data/lib/zen/public/admin/css/zen/layout.css +26 -11
  229. data/lib/zen/public/admin/css/zen/messages.css +3 -10
  230. data/lib/zen/public/admin/css/zen/reset.css +13 -15
  231. data/lib/zen/public/admin/css/zen/tables.css +8 -10
  232. data/lib/zen/public/admin/css/zen/tabs.css +2 -10
  233. data/lib/zen/public/admin/css/zen/window.css +2 -8
  234. data/lib/zen/public/admin/js/vendor/datepicker.js +540 -240
  235. data/lib/zen/public/admin/js/vendor/mootools/core.js +273 -283
  236. data/lib/zen/public/admin/js/vendor/mootools/more.js +131 -136
  237. data/lib/zen/public/admin/js/zen/index.js +8 -14
  238. data/lib/zen/public/admin/js/zen/lib/editor.js +36 -33
  239. data/lib/zen/public/admin/js/zen/lib/editor/markdown.js +0 -6
  240. data/lib/zen/public/admin/js/zen/lib/editor/textile.js +0 -6
  241. data/lib/zen/public/admin/js/zen/lib/html_table.js +0 -5
  242. data/lib/zen/public/admin/js/zen/lib/tabs.js +22 -79
  243. data/lib/zen/public/admin/js/zen/lib/window.js +12 -22
  244. data/lib/zen/spec/bacon/color_output.rb +1 -1
  245. data/lib/zen/spec/helper.rb +6 -11
  246. data/lib/zen/spec/simplecov.rb +3 -3
  247. data/lib/zen/task.rb +0 -1
  248. data/lib/zen/task/build.rake +46 -26
  249. data/lib/zen/task/clean.rake +14 -7
  250. data/lib/zen/task/db.rake +31 -29
  251. data/lib/zen/task/package.rake +19 -23
  252. data/lib/zen/task/proto.rake +3 -5
  253. data/lib/zen/task/setup.rake +4 -0
  254. data/lib/zen/task/test.rake +31 -6
  255. data/lib/zen/task/theme.rake +13 -19
  256. data/lib/zen/theme.rb +377 -52
  257. data/lib/zen/validation.rb +22 -30
  258. data/lib/zen/version.rb +1 -2
  259. data/lib/zen/view/bottom.xhtml +9 -1
  260. data/lib/zen/view/head.xhtml +1 -1
  261. data/lib/zen/view/main.xhtml +1 -1
  262. data/lib/zen/view/search.xhtml +9 -0
  263. data/proto/app/Rakefile +0 -1
  264. data/proto/app/app.rb +21 -16
  265. data/proto/app/config/config.rb.erb +41 -0
  266. data/proto/app/config/{database.rb → database.rb.erb} +17 -14
  267. data/proto/app/config/middlewares.rb +1 -1
  268. data/proto/app/theme/default/index.xhtml +25 -0
  269. data/proto/app/theme/theme.rb +19 -0
  270. data/proto/app/{log/database/dev → tmp}/.gitkeep +0 -0
  271. data/proto/migration.rb +3 -5
  272. data/proto/rack/thin.yml +41 -0
  273. data/proto/rack/unicorn.rb +38 -0
  274. data/spec/Rakefile +15 -11
  275. data/spec/fixtures/zen/helper/controller.rb +13 -0
  276. data/spec/fixtures/zen/helper/locale.rb +7 -0
  277. data/spec/fixtures/zen/helper/message.rb +0 -4
  278. data/spec/fixtures/zen/language.rb +31 -0
  279. data/spec/fixtures/zen/language/en/spec.rb +11 -0
  280. data/spec/fixtures/zen/language/nl/spec.rb +11 -0
  281. data/spec/fixtures/zen/package.rb +0 -10
  282. data/spec/fixtures/zen/package/categories/helper/category_frontend.rb +16 -0
  283. data/spec/fixtures/zen/package/comments/helper/comment_frontend.rb +16 -0
  284. data/spec/fixtures/zen/package/sections/helper/section_frontend.rb +16 -0
  285. data/spec/fixtures/zen/package/settings/controller/settings.rb +17 -0
  286. data/spec/fixtures/zen/package/users/helper/access.rb +18 -0
  287. data/spec/fixtures/zen/package/users/helper/acl.rb +23 -0
  288. data/spec/fixtures/zen/theme/{default-section → default}/index.xhtml +0 -0
  289. data/spec/helper.rb +12 -27
  290. data/{proto/app/log/database/live → spec/public}/.gitkeep +0 -0
  291. data/spec/zen/controller/admin_controller.rb +5 -6
  292. data/spec/zen/controller/main_controller.rb +29 -35
  293. data/spec/zen/controller/preview.rb +9 -10
  294. data/spec/zen/event.rb +44 -0
  295. data/spec/zen/helper/breadcrumb.rb +4 -5
  296. data/spec/zen/helper/controller.rb +21 -0
  297. data/spec/zen/helper/locale.rb +25 -0
  298. data/spec/zen/helper/message.rb +4 -11
  299. data/spec/zen/helper/theme.rb +11 -14
  300. data/spec/zen/language.rb +48 -22
  301. data/spec/zen/markup.rb +39 -0
  302. data/spec/zen/package.rb +48 -11
  303. data/spec/zen/package/categories/controller/categories.rb +99 -45
  304. data/spec/zen/package/categories/controller/category_groups.rb +109 -33
  305. data/spec/zen/package/categories/helper/category.rb +19 -32
  306. data/spec/zen/package/categories/helper/category_frontend.rb +61 -0
  307. data/spec/zen/package/comments/anti_spam.rb +50 -0
  308. data/spec/zen/package/comments/controller/comments.rb +121 -61
  309. data/spec/zen/package/comments/controller/comments_form.rb +120 -94
  310. data/spec/zen/package/comments/helper/comment.rb +13 -13
  311. data/spec/zen/package/comments/helper/comment_frontend.rb +92 -0
  312. data/spec/zen/package/custom_fields/blue_form_parameters.rb +50 -50
  313. data/spec/zen/package/custom_fields/controller/custom_field_groups.rb +135 -43
  314. data/spec/zen/package/custom_fields/controller/custom_field_types.rb +153 -48
  315. data/spec/zen/package/custom_fields/controller/custom_fields.rb +130 -51
  316. data/spec/zen/package/custom_fields/helper/custom_field.rb +8 -8
  317. data/spec/zen/package/extensions/controller/extensions.rb +38 -0
  318. data/spec/zen/package/menus/controller/menu_items.rb +121 -42
  319. data/spec/zen/package/menus/controller/menus.rb +125 -38
  320. data/spec/zen/package/menus/helper/menu.rb +26 -26
  321. data/spec/zen/package/menus/helper/menu_frontend.rb +104 -0
  322. data/spec/zen/package/sections/controller/section_entries.rb +145 -89
  323. data/spec/zen/package/sections/controller/sections.rb +130 -35
  324. data/spec/zen/package/sections/helper/section.rb +27 -38
  325. data/spec/zen/package/sections/helper/section_frontend.rb +160 -0
  326. data/spec/zen/package/settings/controller/settings.rb +73 -8
  327. data/spec/zen/package/settings/settings.rb +119 -0
  328. data/spec/zen/package/users/controller/user_groups.rb +134 -34
  329. data/spec/zen/package/users/controller/users.rb +189 -44
  330. data/spec/zen/package/users/helper/access.rb +29 -0
  331. data/spec/zen/package/users/helper/acl.rb +46 -0
  332. data/spec/zen/package/users/helper/users.rb +20 -64
  333. data/spec/zen/theme.rb +7 -9
  334. data/spec/zen/validation.rb +1 -2
  335. data/zen.gemspec +25 -22
  336. metadata +303 -222
  337. data/lib/zen/asset.rb +0 -292
  338. data/lib/zen/bin/runner.rb +0 -118
  339. data/lib/zen/error/language_error.rb +0 -10
  340. data/lib/zen/error/package_error.rb +0 -10
  341. data/lib/zen/error/plugin_error.rb +0 -10
  342. data/lib/zen/error/theme_error.rb +0 -10
  343. data/lib/zen/error/validation_error.rb +0 -10
  344. data/lib/zen/helper/acl.rb +0 -182
  345. data/lib/zen/helper/blue_form_vendor.rb +0 -689
  346. data/lib/zen/language/en/zen_general.yml +0 -25
  347. data/lib/zen/language/en/zen_models.yml +0 -13
  348. data/lib/zen/language/nl/zen_general.yml +0 -25
  349. data/lib/zen/language/nl/zen_models.yml +0 -13
  350. data/lib/zen/model/settings.rb +0 -78
  351. data/lib/zen/package/base.rb +0 -62
  352. data/lib/zen/package/categories/lib/categories/language/en/categories.yml +0 -36
  353. data/lib/zen/package/categories/lib/categories/language/en/category_groups.yml +0 -34
  354. data/lib/zen/package/categories/lib/categories/language/nl/categories.yml +0 -40
  355. data/lib/zen/package/categories/lib/categories/language/nl/category_groups.yml +0 -34
  356. data/lib/zen/package/categories/lib/categories/plugin/categories.rb +0 -141
  357. data/lib/zen/package/comments/lib/comments/language/en/comments.yml +0 -48
  358. data/lib/zen/package/comments/lib/comments/language/nl/comments.yml +0 -50
  359. data/lib/zen/package/comments/lib/comments/plugin/anti_spam.rb +0 -156
  360. data/lib/zen/package/comments/lib/comments/plugin/comments.rb +0 -115
  361. data/lib/zen/package/custom_fields/lib/custom_fields/language/en/custom_field_groups.yml +0 -33
  362. data/lib/zen/package/custom_fields/lib/custom_fields/language/en/custom_field_types.yml +0 -40
  363. data/lib/zen/package/custom_fields/lib/custom_fields/language/en/custom_fields.yml +0 -54
  364. data/lib/zen/package/custom_fields/lib/custom_fields/language/nl/custom_field_groups.yml +0 -33
  365. data/lib/zen/package/custom_fields/lib/custom_fields/language/nl/custom_field_types.yml +0 -40
  366. data/lib/zen/package/custom_fields/lib/custom_fields/language/nl/custom_fields.yml +0 -54
  367. data/lib/zen/package/menus/lib/menus/language/en/menu_items.yml +0 -41
  368. data/lib/zen/package/menus/lib/menus/language/en/menus.yml +0 -40
  369. data/lib/zen/package/menus/lib/menus/language/nl/menu_items.yml +0 -41
  370. data/lib/zen/package/menus/lib/menus/language/nl/menus.yml +0 -40
  371. data/lib/zen/package/menus/lib/menus/plugin/menus.rb +0 -152
  372. data/lib/zen/package/sections/lib/sections/language/en/section_entries.yml +0 -44
  373. data/lib/zen/package/sections/lib/sections/language/en/sections.yml +0 -48
  374. data/lib/zen/package/sections/lib/sections/language/nl/section_entries.yml +0 -44
  375. data/lib/zen/package/sections/lib/sections/language/nl/sections.yml +0 -48
  376. data/lib/zen/package/sections/lib/sections/plugin/section_entries.rb +0 -244
  377. data/lib/zen/package/sections/lib/sections/plugin/sections.rb +0 -87
  378. data/lib/zen/package/settings/lib/settings/language/en/settings.yml +0 -36
  379. data/lib/zen/package/settings/lib/settings/language/nl/settings.yml +0 -37
  380. data/lib/zen/package/settings/lib/settings/plugin/group_base.rb +0 -39
  381. data/lib/zen/package/settings/lib/settings/plugin/setting_base.rb +0 -133
  382. data/lib/zen/package/settings/lib/settings/plugin/settings.rb +0 -251
  383. data/lib/zen/package/users/lib/users/controller/access_rules.rb +0 -284
  384. data/lib/zen/package/users/lib/users/language/en/access_rules.yml +0 -38
  385. data/lib/zen/package/users/lib/users/language/en/user_groups.yml +0 -32
  386. data/lib/zen/package/users/lib/users/language/en/users.yml +0 -57
  387. data/lib/zen/package/users/lib/users/language/nl/access_rules.yml +0 -38
  388. data/lib/zen/package/users/lib/users/language/nl/user_groups.yml +0 -32
  389. data/lib/zen/package/users/lib/users/language/nl/users.yml +0 -57
  390. data/lib/zen/package/users/lib/users/model/access_rule.rb +0 -42
  391. data/lib/zen/package/users/lib/users/public/admin/js/users/access_rules.js +0 -65
  392. data/lib/zen/package/users/lib/users/public/admin/js/users/lib/access_rules.js +0 -49
  393. data/lib/zen/package/users/lib/users/view/admin/access-rules/form.xhtml +0 -120
  394. data/lib/zen/package/users/lib/users/view/admin/access-rules/index.xhtml +0 -102
  395. data/lib/zen/plugin.rb +0 -182
  396. data/lib/zen/plugin/base.rb +0 -46
  397. data/lib/zen/plugin/helper.rb +0 -47
  398. data/lib/zen/plugin/markup/lib/markup.rb +0 -14
  399. data/lib/zen/plugin/markup/lib/markup/language/en/markup.yml +0 -6
  400. data/lib/zen/plugin/markup/lib/markup/markup.rb +0 -165
  401. data/lib/zen/public/admin/js/zen/lib/asset.js +0 -111
  402. data/lib/zen/task/plugin.rake +0 -18
  403. data/lib/zen/theme/base.rb +0 -65
  404. data/proto/app/config/config.rb +0 -18
  405. data/spec/fixtures/zen/language/en/spec.yml +0 -10
  406. data/spec/fixtures/zen/language/nl/spec.yml +0 -7
  407. data/spec/fixtures/zen/package/settings/plugin/settings.rb +0 -20
  408. data/spec/zen/asset.rb +0 -97
  409. data/spec/zen/bin/create.rb +0 -89
  410. data/spec/zen/bin/runner.rb +0 -47
  411. data/spec/zen/helper/acl.rb +0 -149
  412. data/spec/zen/package/categories/plugin/categories.rb +0 -92
  413. data/spec/zen/package/comments/plugin/anti_spam.rb +0 -59
  414. data/spec/zen/package/comments/plugin/comments.rb +0 -107
  415. data/spec/zen/package/menus/plugin/menus.rb +0 -120
  416. data/spec/zen/package/sections/plugin/section_entries.rb +0 -161
  417. data/spec/zen/package/sections/plugin/sections.rb +0 -75
  418. data/spec/zen/package/settings/plugin/settings.rb +0 -33
  419. data/spec/zen/package/users/controller/access_rules.rb +0 -90
  420. data/spec/zen/plugin.rb +0 -64
  421. data/spec/zen/plugin/helper.rb +0 -11
  422. data/spec/zen/plugin/markup.rb +0 -44
@@ -1,9 +1,3 @@
1
- /**
2
- * Stylesheet used for the Javascript class Zen.Window.
3
- *
4
- * @author Yorick Peterse
5
- * @since 0.2.6
6
- */
7
1
  .window
8
2
  {
9
3
  background: #fff;
@@ -34,7 +28,7 @@
34
28
 
35
29
  .window header
36
30
  {
37
- background: #ddd -webkit-gradient(linear, left bottom, left top,
31
+ background: #ddd -webkit-gradient(linear, left bottom, left top,
38
32
  from(#ddd), to(#eee));
39
33
  background: #ddd -moz-linear-gradient(bottom, top, #ddd, #eee);
40
34
  border-top: 1px solid #fff;
@@ -56,7 +50,7 @@
56
50
 
57
51
  .window header .close
58
52
  {
59
- background: url('../../images/zen/icons/close.png') no-repeat center left;
53
+ background: url('/admin/images/zen/icons/close.png') no-repeat center left;
60
54
  cursor: pointer;
61
55
  float: left;
62
56
  height: 16px;
@@ -1,3 +1,27 @@
1
+ /*
2
+ ---
3
+ name: Locale.nl-NL.DatePicker
4
+ description: Dutch Language File for DatePicker
5
+ authors: Arian Stolwijk
6
+ requires: [More/Locale]
7
+ provides: Locale.nl-NL.DatePicker
8
+ ...
9
+ */
10
+
11
+ Locale.define('en-US', 'DatePicker', {
12
+ select_a_time: Zen.translations[
13
+ 'zen_general.datepicker.select_a_time'],
14
+ use_mouse_wheel: Zen.translations[
15
+ 'zen_general.datepicker.use_mouse_wheel'
16
+ ],
17
+ time_confirm_button: Zen.translations[
18
+ 'zen_general.datepicker.time_confirm_button'
19
+ ],
20
+ apply_range: Zen.translations['zen_general.datepicker.apply_range'],
21
+ cancel : Zen.translations['zen_general.datepicker.cancel'],
22
+ week : Zen.translations['zen_general.datepicker.week']
23
+ });
24
+
1
25
  /*
2
26
  ---
3
27
  name: Picker
@@ -8,6 +32,7 @@ provides: Picker
8
32
  ...
9
33
  */
10
34
  var Picker = new Class({
35
+
11
36
  Implements: [Options, Events],
12
37
 
13
38
  options: {/*
@@ -23,7 +48,9 @@ var Picker = new Class({
23
48
  positionOffset: {x: 0, y: 0},
24
49
  pickerPosition: 'bottom',
25
50
  draggable: true,
26
- showOnInit: true
51
+ showOnInit: true,
52
+ columns: 1,
53
+ footer: false
27
54
  },
28
55
 
29
56
  initialize: function(options){
@@ -44,6 +71,7 @@ var Picker = new Class({
44
71
  opacity: 0
45
72
  }
46
73
  }).inject(options.inject || document.body);
74
+ picker.addClass('column_' + options.columns);
47
75
 
48
76
  if (options.useFadeInOut){
49
77
  picker.set('tween', {
@@ -55,16 +83,28 @@ var Picker = new Class({
55
83
  // Build the header
56
84
  var header = this.header = new Element('div.header').inject(picker);
57
85
 
58
- this.closeButton = new Element('div.closeButton[text=x]')
86
+ var title = this.title = new Element('div.title').inject(header);
87
+ var titleID = this.titleID = 'pickertitle-' + String.uniqueID();
88
+ this.titleText = new Element('div', {
89
+ 'role': 'heading',
90
+ 'class': 'titleText',
91
+ 'id': titleID,
92
+ 'aria-live': 'assertive',
93
+ 'aria-atomic': 'true'
94
+ }).inject(title);
95
+
96
+ this.closeButton = new Element('div.closeButton[text=x][role=button]')
59
97
  .addEvent('click', this.close.pass(false, this))
60
98
  .inject(header);
61
99
 
62
- var title = this.title = new Element('div.title').inject(header);
63
- this.titleText = new Element('div.titleText').inject(title);
64
-
65
100
  // Build the body of the picker
66
101
  var body = this.body = new Element('div.body').inject(picker);
67
102
 
103
+ if (options.footer){
104
+ this.footer = new Element('div.footer').inject(picker);
105
+ picker.addClass('footer');
106
+ }
107
+
68
108
  // oldContents and newContents are used to slide from the old content to a new one.
69
109
  var slider = this.slider = new Element('div.slider', {
70
110
  styles: {
@@ -92,6 +132,9 @@ var Picker = new Class({
92
132
  }
93
133
  }).inject(slider);
94
134
 
135
+ this.originalColumns = options.columns;
136
+ this.setColumns(options.columns);
137
+
95
138
  // IFrameShim for select fields in IE
96
139
  var shim = this.shim = window['IframeShim'] ? new IframeShim(picker) : null;
97
140
 
@@ -102,29 +145,18 @@ var Picker = new Class({
102
145
  } : null);
103
146
  picker.setStyle('cursor', 'move');
104
147
  }
105
-
106
- this.addEvent('open', function(){
107
- picker.setStyle('display', 'block');
108
- if (shim) shim.show();
109
- }, true);
110
-
111
- this.addEvent('hide', function(){
112
- picker.setStyle('display', 'none');
113
- if (shim) shim.hide();
114
- }, true);
115
-
116
148
  },
117
149
 
118
150
  open: function(noFx){
119
151
  if (this.opened == true) return this;
120
152
  this.opened = true;
153
+ var picker = this.picker.setStyle('display', 'block').set('aria-hidden', 'false')
154
+ if (this.shim) this.shim.show();
121
155
  this.fireEvent('open');
122
156
  if (this.options.useFadeInOut && !noFx){
123
- this.picker.fade('in').get('tween').chain(function(){
124
- this.fireEvent('show');
125
- }.bind(this));
157
+ picker.fade('in').get('tween').chain(this.fireEvent.pass('show', this));
126
158
  } else {
127
- this.picker.setStyle('opacity', 1);
159
+ picker.setStyle('opacity', 1);
128
160
  this.fireEvent('show');
129
161
  }
130
162
  return this;
@@ -138,13 +170,16 @@ var Picker = new Class({
138
170
  if (this.opened == false) return this;
139
171
  this.opened = false;
140
172
  this.fireEvent('close');
173
+ var self = this, picker = this.picker, hide = function(){
174
+ picker.setStyle('display', 'none').set('aria-hidden', 'true');
175
+ if (self.shim) self.shim.hide();
176
+ self.fireEvent('hide');
177
+ };
141
178
  if (this.options.useFadeInOut && !noFx){
142
- this.picker.fade('out').get('tween').chain(function(){
143
- this.fireEvent('hide');
144
- }.bind(this));
179
+ picker.fade('out').get('tween').chain(hide);
145
180
  } else {
146
- this.picker.setStyle('opacity', 0);
147
- this.fireEvent('hide');
181
+ picker.setStyle('opacity', 0);
182
+ hide();
148
183
  }
149
184
  return this;
150
185
  },
@@ -200,6 +235,7 @@ var Picker = new Class({
200
235
 
201
236
  setBodySize: function(){
202
237
  var bodysize = this.bodysize = this.body.getSize();
238
+
203
239
  this.slider.setStyles({
204
240
  width: 2 * bodysize.x,
205
241
  height: bodysize.y
@@ -215,11 +251,44 @@ var Picker = new Class({
215
251
  });
216
252
  },
217
253
 
218
- setContent: function(){
219
- var content = Array.from(arguments), fx;
254
+ setColumnContent: function(column, content){
255
+ var columnElement = this.columns[column];
256
+ if (!columnElement) return this;
220
257
 
221
- if (['right', 'left', 'fade'].contains(content[1])) fx = content[1];
222
- if (content.length == 1 || fx) content = content[0];
258
+ var type = typeOf(content);
259
+ if (['string', 'number'].contains(type)) columnElement.set('text', content);
260
+ else columnElement.empty().adopt(content);
261
+
262
+ return this;
263
+ },
264
+
265
+ setColumnsContent: function(content, fx){
266
+ var old = this.columns;
267
+ this.columns = this.newColumns;
268
+ this.newColumns = old;
269
+
270
+ content.forEach(function(_content, i){
271
+ this.setColumnContent(i, _content);
272
+ }, this);
273
+ return this.setContent(null, fx);
274
+ },
275
+
276
+ setColumns: function(columns){
277
+ var _columns = this.columns = new Elements, _newColumns = this.newColumns = new Elements;
278
+ for (var i = columns; i--;){
279
+ _columns.push(new Element('div.column').addClass('column_' + (columns - i)));
280
+ _newColumns.push(new Element('div.column').addClass('column_' + (columns - i)));
281
+ }
282
+
283
+ var oldClass = 'column_' + this.options.columns, newClass = 'column_' + columns;
284
+ this.picker.removeClass(oldClass).addClass(newClass);
285
+
286
+ this.options.columns = columns;
287
+ return this;
288
+ },
289
+
290
+ setContent: function(content, fx){
291
+ if (content) return this.setColumnsContent([content], fx);
223
292
 
224
293
  // swap contents so we can fill the newContents again and animate
225
294
  var old = this.oldContents;
@@ -227,9 +296,7 @@ var Picker = new Class({
227
296
  this.newContents = old;
228
297
  this.newContents.empty();
229
298
 
230
- var type = typeOf(content);
231
- if (['string', 'number'].contains(type)) this.newContents.set('text', content);
232
- else this.newContents.adopt(content);
299
+ this.newContents.adopt(this.columns);
233
300
 
234
301
  this.setBodySize();
235
302
 
@@ -273,8 +340,15 @@ var Picker = new Class({
273
340
  return this.picker;
274
341
  },
275
342
 
276
- setTitle: function(text){
277
- this.titleText.set('text', text);
343
+ setTitle: function(content, fn){
344
+ if (!fn) fn = Function.from;
345
+ this.titleText.empty().adopt(
346
+ Array.from(content).map(function(item, i){
347
+ return typeOf(item) == 'element'
348
+ ? item
349
+ : new Element('div.column', {text: fn(item, this.options)}).addClass('column_' + (i + 1));
350
+ }, this)
351
+ );
278
352
  return this;
279
353
  },
280
354
 
@@ -297,14 +371,16 @@ provides: Picker.Attach
297
371
  ...
298
372
  */
299
373
  Picker.Attach = new Class({
374
+
300
375
  Extends: Picker,
301
376
 
302
377
  options: {/*
303
- onAttachedEvent: function(event){},
378
+ onAttached: function(event){},
304
379
 
305
380
  toggleElements: null, // deprecated
306
381
  toggle: null, // When set it deactivate toggling by clicking on the input */
307
- showOnInit: false
382
+ showOnInit: false, // overrides the Picker option
383
+ blockKeydown: true
308
384
  },
309
385
 
310
386
  initialize: function(attachTo, options){
@@ -316,7 +392,7 @@ Picker.Attach = new Class({
316
392
  this.inputs = [];
317
393
 
318
394
  var documentEvent = function(event){
319
- if (this.attachedElements.contains(event.target)) return null;
395
+ if (this.attachedElements.contains(event.target)) return;
320
396
  this.close();
321
397
  }.bind(this);
322
398
  var document = this.picker.getDocument().addEvent('click', documentEvent);
@@ -342,40 +418,63 @@ Picker.Attach = new Class({
342
418
  allElements = [].append(elements).combine(toggles),
343
419
  self = this;
344
420
 
345
- var eventWrapper = function(fn, element){
421
+ var closeEvent = function(event){
422
+ var stopInput = self.options.blockKeydown
423
+ && event.type == 'keydown'
424
+ && !(['tab', 'esc'].contains(event.key)),
425
+ isCloseKey = event.type == 'keydown'
426
+ && (['tab', 'esc'].contains(event.key)),
427
+ isA = event.target.get('tag') == 'a';
428
+
429
+ if (stopInput || isA) event.preventDefault();
430
+ if (isCloseKey || isA) self.close();
431
+ };
432
+
433
+ var getOpenEvent = function(element){
346
434
  return function(event){
347
- if (event.type == 'keydown' && ['tab', 'esc'].contains(event.key) == false) return false;
348
- if (event.target.get('tag') == 'a') event.stop();
349
- self.fireEvent('attachedEvent', [event, element]);
435
+ var tag = event.target.get('tag');
436
+ if (tag == 'input' && event.type == 'click' && !element.match(':focus') || (self.opened && self.input == element)) return;
437
+ if (tag == 'a') event.stop();
350
438
  self.position(element);
351
- fn();
439
+ self.open();
440
+ self.fireEvent('attached', [event, element]);
352
441
  };
353
442
  };
354
443
 
355
- allElements.each(function(element, i){
444
+ var getToggleEvent = function(open, close){
445
+ return function(event){
446
+ if (self.opened) close(event);
447
+ else open(event);
448
+ };
449
+ };
450
+
451
+ allElements.each(function(element){
356
452
 
357
453
  // The events are already attached!
358
- if (self.attachedElements.contains(element)) return null;
454
+ if (self.attachedElements.contains(element)) return;
359
455
 
360
- var tag = element.get('tag');
456
+ var events = {},
457
+ tag = element.get('tag'),
458
+ openEvent = getOpenEvent(element),
459
+ // closeEvent does not have a depency on element
460
+ toggleEvent = getToggleEvent(openEvent, closeEvent);
361
461
 
362
- var events = {};
363
462
  if (tag == 'input'){
364
463
  // Fix in order to use togglers only
365
464
  if (!toggles.length){
366
465
  events = {
367
- focus: eventWrapper(self.open.bind(self), element),
368
- keydown: eventWrapper(self.close.bind(self), element),
369
- click: eventWrapper(self.open.bind(self), element)
466
+ focus: openEvent,
467
+ click: openEvent,
468
+ keydown: closeEvent
370
469
  };
371
470
  }
372
471
  self.inputs.push(element);
373
472
  } else {
374
473
  if (toggles.contains(element)){
375
474
  self.toggles.push(element);
376
- events.click = eventWrapper(self.toggle.bind(self), element);
475
+ events.click = toggleEvent
377
476
  } else {
378
- events.click = eventWrapper(self.open.bind(self), element);
477
+ events.click = openEvent;
379
478
  }
380
479
  }
381
480
  element.addEvents(events);
@@ -398,7 +497,7 @@ Picker.Attach = new Class({
398
497
 
399
498
  allElements.each(function(element){
400
499
  var i = self.attachedElements.indexOf(element);
401
- if (i < 0) return null;
500
+ if (i < 0) return;
402
501
 
403
502
  var events = self.attachedEvents[i];
404
503
  element.removeEvents(events);
@@ -410,33 +509,17 @@ Picker.Attach = new Class({
410
509
 
411
510
  var inputIndex = self.inputs.indexOf(element);
412
511
  if (toggleIndex != -1) delete self.inputs[inputIndex];
413
-
414
512
  });
415
513
  return this;
416
514
  },
417
515
 
418
516
  destroy: function(){
419
517
  this.detach();
420
- this.parent();
518
+ return this.parent();
421
519
  }
422
520
 
423
521
  });
424
522
 
425
- /*
426
- ---
427
- name: Locale.en-US.DatePicker
428
- description: English Language File for DatePicker
429
- authors: Arian Stolwijk
430
- requires: [More/Locale]
431
- provides: Locale.en-US.DatePicker
432
- ...
433
- */
434
- Locale.define('en-US', 'DatePicker', {
435
- select_a_time: 'Select a time',
436
- use_mouse_wheel: 'Use the mouse wheel to quickly change value',
437
- time_confirm_button: 'OK'
438
- });
439
-
440
523
  /*
441
524
  ---
442
525
  name: Picker.Date
@@ -458,6 +541,8 @@ this.DatePicker = Picker.Date = new Class({
458
541
  minDate: new Date('3/4/2010'), // Date object or a string
459
542
  maxDate: new Date('3/4/2011'), // same as minDate
460
543
  availableDates: {}, //
544
+ invertAvailable: false,
545
+
461
546
  format: null,*/
462
547
 
463
548
  timePicker: false,
@@ -470,8 +555,12 @@ this.DatePicker = Picker.Date = new Class({
470
555
  startDay: 1, // Sunday (0) through Saturday (6) - be aware that this may affect your layout, since the days on the right might have a different margin
471
556
 
472
557
  startView: 'days', // allowed values: {time, days, months, years}
558
+ openLastView: false,
473
559
  pickOnly: false, // 'years', 'months', 'days', 'time'
474
560
  canAlwaysGoUp: ['months', 'days'],
561
+ updateAll : false, //whether or not to update all inputs when selecting a date
562
+
563
+ weeknumbers: false,
475
564
 
476
565
  // if you like to use your own translations
477
566
  months_abbr: null,
@@ -495,11 +584,15 @@ this.DatePicker = Picker.Date = new Class({
495
584
  this.parent(attachTo, options);
496
585
 
497
586
  this.setOptions(options);
498
- var options = this.options;
587
+ options = this.options;
499
588
 
500
589
  // If we only want to use one picker / backwards compatibility
501
590
  ['year', 'month', 'day', 'time'].some(function(what){
502
- if (options[what + 'PickerOnly']) return options.pickOnly = what;
591
+ if (options[what + 'PickerOnly']){
592
+ options.pickOnly = what;
593
+ return true;
594
+ }
595
+ return false;
503
596
  });
504
597
  if (options.pickOnly){
505
598
  options[options.pickOnly + 'Picker'] = true;
@@ -509,10 +602,7 @@ this.DatePicker = Picker.Date = new Class({
509
602
  // backward compatibility for startView
510
603
  var newViews = ['days', 'months', 'years'];
511
604
  ['month', 'year', 'decades'].some(function(what, i){
512
- if (options.startView == what){
513
- options.startView = newViews[i];
514
- return true;
515
- }
605
+ return (options.startView == what) && (options.startView = newViews[i]);
516
606
  });
517
607
 
518
608
  options.canAlwaysGoUp = options.canAlwaysGoUp ? Array.from(options.canAlwaysGoUp) : [];
@@ -532,42 +622,35 @@ this.DatePicker = Picker.Date = new Class({
532
622
  if (options.timePicker) options.format = (options.format) + (options.format ? ' ' : '') + Locale.get('Date.shortTime');
533
623
  }
534
624
 
535
- // This is where we store the selected date
536
- this.date = limitDate(new Date(), options.minDate, options.maxDate);
537
-
538
625
  // Some link or input has fired an event!
539
- this.addEvent('attachedEvent', function(event, element){
626
+ this.addEvent('attached', function(event, element){
627
+
628
+ // This is where we store the selected date
629
+ if (!this.currentView || !options.openLastView) this.currentView = options.startView;
630
+
631
+ this.date = limitDate(new Date(), options.minDate, options.maxDate);
540
632
  var tag = element.get('tag'), input;
541
- if (tag == 'input'){
542
- input = element;
543
- } else {
633
+ if (tag == 'input') input = element;
634
+ else {
544
635
  var index = this.toggles.indexOf(element);
545
636
  if (this.inputs[index]) input = this.inputs[index];
546
637
  }
547
- this.date = new Date()
548
- if (input){
549
- var date = Date.parse(input.get('value'));
550
- if (date == null || !date.isValid()){
551
- var storeDate = input.retrieve('datepicker:value');
552
- if (storeDate) date = Date.parse(storeDate);
553
- }
554
- if (date != null && date.isValid()) this.date = date;
555
- }
638
+ this.getInputDate(input);
556
639
  this.input = input;
640
+ this.setColumns(this.originalColumns);
557
641
  }.bind(this), true);
558
642
 
643
+ },
559
644
 
560
- // Start rendering the default view.
561
- this.currentView = options.startView;
562
- this.addEvent('open', function(){
563
- var view = this.currentView,
564
- cap = view.capitalize();
565
- if (this['render' + cap]){
566
- this['render' + cap](this.date.clone());
567
- this.currentView = view;
568
- }
569
- }.bind(this));
570
-
645
+ getInputDate: function(input){
646
+ this.date = new Date();
647
+ if (!input) return;
648
+ var date = Date.parse(input.get('value'));
649
+ if (date == null || !date.isValid()){
650
+ var storeDate = input.retrieve('datepicker:value');
651
+ if (storeDate) date = Date.parse(storeDate);
652
+ }
653
+ if (date != null && date.isValid()) this.date = date;
571
654
  },
572
655
 
573
656
  // Control the previous and next elements
@@ -579,18 +662,18 @@ this.DatePicker = Picker.Date = new Class({
579
662
  this.next = new Element('div.next[html=&#187;]').inject(this.header);
580
663
  },
581
664
 
582
- hidePrevious: function($next, $show){
583
- this[$next ? 'next' : 'previous'].setStyle('display', $show ? 'block' : 'none');
665
+ hidePrevious: function(_next, _show){
666
+ this[_next ? 'next' : 'previous'].setStyle('display', _show ? 'block' : 'none');
584
667
  return this;
585
668
  },
586
669
 
587
- showPrevious: function($next){
588
- return this.hidePrevious($next, true);
670
+ showPrevious: function(_next){
671
+ return this.hidePrevious(_next, true);
589
672
  },
590
673
 
591
- setPreviousEvent: function(fn, $next){
592
- this[$next ? 'next' : 'previous'].removeEvents('click');
593
- if (fn) this[$next ? 'next' : 'previous'].addEvent('click', fn);
674
+ setPreviousEvent: function(fn, _next){
675
+ this[_next ? 'next' : 'previous'].removeEvents('click');
676
+ if (fn) this[_next ? 'next' : 'previous'].addEvent('click', fn);
594
677
  return this;
595
678
  },
596
679
 
@@ -606,26 +689,48 @@ this.DatePicker = Picker.Date = new Class({
606
689
  return this.setPreviousEvent(fn, true);
607
690
  },
608
691
 
692
+ setColumns: function(columns, view, date, viewFx){
693
+ var ret = this.parent(columns), method;
694
+
695
+ if ((view || this.currentView)
696
+ && (method = 'render' + (view || this.currentView).capitalize())
697
+ && this[method]
698
+ ) this[method](date || this.date.clone(), viewFx);
699
+
700
+ return ret;
701
+ },
702
+
609
703
  // Render the Pickers
610
704
 
611
705
  renderYears: function(date, fx){
612
-
613
- var options = this.options;
706
+ var options = this.options, pages = options.columns, perPage = options.yearsPerPage,
707
+ _columns = [], _dates = [];
708
+ this.dateElements = [];
614
709
 
615
710
  // start neatly at interval (eg. 1980 instead of 1987)
616
- while (date.get('year') % options.yearsPerPage > 0) date.decrement('year', 1);
617
-
618
- this.setTitle(options.years_title(date, options));
711
+ date = date.clone().decrement('year', date.get('year') % perPage);
712
+
713
+ var iterateDate = date.clone().decrement('year', Math.floor((pages - 1) / 2) * perPage);
714
+
715
+ for (var i = pages; i--;){
716
+ var _date = iterateDate.clone();
717
+ _dates.push(_date);
718
+ _columns.push(renderers.years(
719
+ timesSelectors.years(options, _date.clone()),
720
+ options,
721
+ this.date.clone(),
722
+ this.dateElements,
723
+ function(date){
724
+ if (options.pickOnly == 'years') this.select(date);
725
+ else this.renderMonths(date, 'fade');
726
+ this.date = date;
727
+ }.bind(this)
728
+ ));
729
+ iterateDate.increment('year', perPage);
730
+ }
619
731
 
620
- this.setContent(renderers.years(
621
- options,
622
- date.clone(),
623
- this.date.clone(),
624
- function(date){
625
- if (options.pickOnly == 'years') this.select(date);
626
- else this.renderMonths(date, 'fade');
627
- }.bind(this)
628
- ), fx);
732
+ this.setColumnsContent(_columns, fx);
733
+ this.setTitle(_dates, options.years_title);
629
734
 
630
735
  // Set limits
631
736
  var limitLeft = (options.minDate && date.get('year') <= options.minDate.get('year')),
@@ -634,30 +739,43 @@ this.DatePicker = Picker.Date = new Class({
634
739
  this[(limitRight ? 'hide' : 'show') + 'Next']();
635
740
 
636
741
  this.setPreviousEvent(function(){
637
- this.renderYears(date.decrement('year', options.yearsPerPage), 'left');
742
+ this.renderYears(date.decrement('year', perPage), 'left');
638
743
  }.bind(this));
639
744
 
640
745
  this.setNextEvent(function(){
641
- this.renderYears(date.increment('year', options.yearsPerPage), 'right');
746
+ this.renderYears(date.increment('year', perPage), 'right');
642
747
  }.bind(this));
643
748
 
644
749
  // We can't go up!
645
750
  this.setTitleEvent(null);
751
+
752
+ this.currentView = 'years';
646
753
  },
647
754
 
648
755
  renderMonths: function(date, fx){
649
- var options = this.options;
650
- this.setTitle(options.months_title(date, options));
756
+ var options = this.options, years = options.columns, _columns = [], _dates = [],
757
+ iterateDate = date.clone().decrement('year', Math.floor((years - 1) / 2));
758
+ this.dateElements = [];
759
+
760
+ for (var i = years; i--;){
761
+ var _date = iterateDate.clone();
762
+ _dates.push(_date);
763
+ _columns.push(renderers.months(
764
+ timesSelectors.months(options, _date.clone()),
765
+ options,
766
+ this.date.clone(),
767
+ this.dateElements,
768
+ function(date){
769
+ if (options.pickOnly == 'months') this.select(date);
770
+ else this.renderDays(date, 'fade');
771
+ this.date = date;
772
+ }.bind(this)
773
+ ));
774
+ iterateDate.increment('year', 1);
775
+ }
651
776
 
652
- this.setContent(renderers.months(
653
- options,
654
- date.clone(),
655
- this.date.clone(),
656
- function(date){
657
- if (options.pickOnly == 'months') this.select(date);
658
- else this.renderDays(date, 'fade');
659
- }.bind(this)
660
- ), fx);
777
+ this.setColumnsContent(_columns, fx);
778
+ this.setTitle(_dates, options.months_title);
661
779
 
662
780
  // Set limits
663
781
  var year = date.get('year'),
@@ -667,11 +785,11 @@ this.DatePicker = Picker.Date = new Class({
667
785
  this[(limitRight ? 'hide' : 'show') + 'Next']();
668
786
 
669
787
  this.setPreviousEvent(function(){
670
- this.renderMonths(date.decrement('year', 1), 'left');
788
+ this.renderMonths(date.decrement('year', years), 'left');
671
789
  }.bind(this));
672
790
 
673
791
  this.setNextEvent(function(){
674
- this.renderMonths(date.increment('year', 1), 'right');
792
+ this.renderMonths(date.increment('year', years), 'right');
675
793
  }.bind(this));
676
794
 
677
795
  var canGoUp = options.yearPicker && (options.pickOnly != 'months' || options.canAlwaysGoUp.contains('months'));
@@ -679,21 +797,34 @@ this.DatePicker = Picker.Date = new Class({
679
797
  this.renderYears(date, 'fade');
680
798
  }.bind(this) : null;
681
799
  this.setTitleEvent(titleEvent);
800
+
801
+ this.currentView = 'months';
682
802
  },
683
803
 
684
804
  renderDays: function(date, fx){
685
- var options = this.options;
686
- this.setTitle(options.days_title(date, options));
805
+ var options = this.options, months = options.columns, _columns = [], _dates = [],
806
+ iterateDate = date.clone().decrement('month', Math.floor((months - 1) / 2));
807
+ this.dateElements = [];
808
+
809
+ for (var i = months; i--;){
810
+ _date = iterateDate.clone();
811
+ _dates.push(_date);
812
+ _columns.push(renderers.days(
813
+ timesSelectors.days(options, _date.clone()),
814
+ options,
815
+ this.date.clone(),
816
+ this.dateElements,
817
+ function(date){
818
+ if (options.pickOnly == 'days' || !options.timePicker) this.select(date)
819
+ else this.renderTime(date, 'fade');
820
+ this.date = date;
821
+ }.bind(this)
822
+ ));
823
+ iterateDate.increment('month', 1);
824
+ }
687
825
 
688
- this.setContent(renderers.days(
689
- options,
690
- date.clone(),
691
- this.date.clone(),
692
- function(date){
693
- if (options.pickOnly == 'days' || !options.timePicker) this.select(date)
694
- else this.renderTime(date, 'fade');
695
- }.bind(this)
696
- ), fx);
826
+ this.setColumnsContent(_columns, fx);
827
+ this.setTitle(_dates, options.days_title);
697
828
 
698
829
  var yearmonth = date.format('%Y%m').toInt(),
699
830
  limitLeft = (options.minDate && yearmonth <= options.minDate.format('%Y%m')),
@@ -702,11 +833,11 @@ this.DatePicker = Picker.Date = new Class({
702
833
  this[(limitRight ? 'hide' : 'show') + 'Next']();
703
834
 
704
835
  this.setPreviousEvent(function(){
705
- this.renderDays(date.decrement('month', 1), 'left');
836
+ this.renderDays(date.decrement('month', months), 'left');
706
837
  }.bind(this));
707
838
 
708
839
  this.setNextEvent(function(){
709
- this.renderDays(date.increment('month', 1), 'right');
840
+ this.renderDays(date.increment('month', months), 'right');
710
841
  }.bind(this));
711
842
 
712
843
  var canGoUp = options.pickOnly != 'days' || options.canAlwaysGoUp.contains('days');
@@ -714,16 +845,21 @@ this.DatePicker = Picker.Date = new Class({
714
845
  this.renderMonths(date, 'fade');
715
846
  }.bind(this) : null;
716
847
  this.setTitleEvent(titleEvent);
848
+
849
+ this.currentView = 'days';
717
850
  },
718
851
 
719
852
  renderTime: function(date, fx){
720
853
  var options = this.options;
721
- this.setTitle(options.time_title(date, options));
854
+ this.setTitle(date, options.time_title);
855
+
856
+ var originalColumns = this.originalColumns = options.columns;
857
+ this.currentView = null; // otherwise you'd get crazy recursion
858
+ if (originalColumns != 1) this.setColumns(1);
722
859
 
723
860
  this.setContent(renderers.time(
724
861
  options,
725
862
  date.clone(),
726
- this.date.clone(),
727
863
  function(date){
728
864
  this.select(date);
729
865
  }.bind(this)
@@ -737,129 +873,167 @@ this.DatePicker = Picker.Date = new Class({
737
873
 
738
874
  var canGoUp = options.pickOnly != 'time' || options.canAlwaysGoUp.contains('time');
739
875
  var titleEvent = (canGoUp) ? function(){
740
- this.renderDays(date, 'fade');
876
+ this.setColumns(originalColumns, 'days', date, 'fade');
741
877
  }.bind(this) : null;
742
878
  this.setTitleEvent(titleEvent);
879
+
880
+ this.currentView = 'time';
743
881
  },
744
882
 
745
- select: function(date){
883
+ select: function(date, all){
746
884
  this.date = date;
747
- if (this.input){
748
- this.input.set('value', date.format(this.options.format))
749
- .store('datepicker:value', date.strftime())
750
- }
751
- this.fireEvent('select', date);
885
+ var formatted = date.format(this.options.format),
886
+ time = date.strftime(),
887
+ inputs = (!this.options.updateAll && !all && this.input) ? [this.input] : this.inputs;
888
+
889
+ inputs.each(function(input){
890
+ input.set('value', formatted).store('datepicker:value', time).fireEvent('change');
891
+ }, this);
892
+
893
+ this.fireEvent('select', [date].concat(inputs));
752
894
  this.close();
895
+ return this;
753
896
  }
754
897
 
755
898
  });
756
899
 
900
+
757
901
  // Renderers only output elements and calculate the limits!
758
902
 
903
+ var timesSelectors = {
904
+
905
+ years: function(options, date){
906
+ var times = [];
907
+ for (var i = 0; i < options.yearsPerPage; i++){
908
+ times.push(+date);
909
+ date.increment('year', 1);
910
+ }
911
+ return times;
912
+ },
913
+
914
+ months: function(options, date){
915
+ var times = [];
916
+ date.set('month', 0);
917
+ for (var i = 0; i <= 11; i++){
918
+ times.push(+date);
919
+ date.increment('month', 1);
920
+ }
921
+ return times;
922
+ },
923
+
924
+ days: function(options, date){
925
+ var times = [];
926
+ date.set('date', 1);
927
+ while (date.get('day') != options.startDay) date.set('date', date.get('date') - 1);
928
+ for (var i = 0; i < 42; i++){
929
+ times.push(+date);
930
+ date.increment('day', 1);
931
+ }
932
+ return times;
933
+ }
934
+
935
+ };
936
+
759
937
  var renderers = {
760
938
 
761
- years: function(options, date, currentDate, fn){
762
- var limit = {left: false, right: false},
763
- container = new Element('div.years'),
764
- today = new Date(),
765
- year, element, classes;
939
+ years: function(years, options, currentDate, dateElements, fn){
940
+ var container = new Element('div.years'),
941
+ today = new Date(), element, classes;
766
942
 
767
- for (var i = 0; i < options.yearsPerPage; i++){
768
- year = date.get('year');
943
+ years.each(function(_year, i){
944
+ var date = new Date(_year), year = date.get('year');
769
945
 
770
946
  classes = '.year.year' + i;
771
947
  if (year == today.get('year')) classes += '.today';
772
948
  if (year == currentDate.get('year')) classes += '.selected';
773
949
  element = new Element('div' + classes, {text: year}).inject(container);
774
950
 
775
- if (isUnavailable('year', date, options)) element.addClass('unavailable');
776
- else element.addEvent('click', fn.pass(date.clone()));
951
+ dateElements.push({element: element, time: _year});
777
952
 
778
- date.increment('year', 1);
779
- }
953
+ if (isUnavailable('year', date, options)) element.addClass('unavailable');
954
+ else element.addEvent('click', fn.pass(date));
955
+ });
780
956
 
781
957
  return container;
782
958
  },
783
959
 
784
- months: function(options, date, currentDate, fn){
960
+ months: function(months, options, currentDate, dateElements, fn){
785
961
  var today = new Date(),
786
962
  month = today.get('month'),
787
- limit = {left: false, right: false},
788
- thisyear = (date.get('year') == today.get('year')),
789
- selectedyear = (date.get('year') == currentDate.get('year')),
963
+ thisyear = today.get('year'),
964
+ selectedyear = currentDate.get('year'),
790
965
  container = new Element('div.months'),
791
- months = options.months_abbr || Locale.get('Date.months_abbr'),
792
- elelement, classes;
966
+ monthsAbbr = options.months_abbr || Locale.get('Date.months_abbr'),
967
+ element, classes;
793
968
 
794
- date.set('month', 0);
795
- if (options.minDate){
796
- date.decrement('month', 1);
797
- date.set('date', date.get('lastdayofmonth'));
798
- date.increment('month', 1);
799
- }
800
-
801
- date.set('date', date.get('lastdayofmonth'));
802
-
803
- for (var i = 0; i <= 11; i++){
969
+ months.each(function(_month, i){
970
+ var date = new Date(_month), year = date.get('year');
804
971
 
805
972
  classes = '.month.month' + (i + 1);
806
- if (i == month && thisyear) classes += '.today';
807
- if (i == currentDate.get('month') && selectedyear) classes += '.selected';
808
- element = new Element('div' + classes, {text: months[i]}).inject(container);
973
+ if (i == month && year == thisyear) classes += '.today';
974
+ if (i == currentDate.get('month') && year == selectedyear) classes += '.selected';
975
+ element = new Element('div' + classes, {text: monthsAbbr[i]}).inject(container);
809
976
 
810
- if (isUnavailable('month', date, options)) element.addClass('unavailable');
811
- else element.addEvent('click', fn.pass(date.clone()));
977
+ dateElements.push({element: element, time: _month});
812
978
 
813
- date.increment('month', 1);
814
- date.set('date', date.get('lastdayofmonth'));
815
- }
979
+ if (isUnavailable('month', date, options)) element.addClass('unavailable');
980
+ else element.addEvent('click', fn.pass(date));
981
+ });
816
982
 
817
983
  return container;
818
984
  },
819
985
 
820
- days: function(options, date, currentDate, fn){
821
- var month = date.get('month'),
822
- limit = {left: false, right: false},
986
+ days: function(days, options, currentDate, dateElements, fn){
987
+ var month = new Date(days[14]).get('month'),
823
988
  todayString = new Date().toDateString(),
824
989
  currentString = currentDate.toDateString(),
825
- container = new Element('div.days'),
826
- titles = new Element('div.titles').inject(container),
990
+ weeknumbers = options.weeknumbers,
991
+ container = new Element('table.days' + (weeknumbers ? '.weeknumbers' : ''), {
992
+ role: 'grid', 'aria-labelledby': this.titleID
993
+ }),
994
+ header = new Element('thead').inject(container),
995
+ body = new Element('tbody').inject(container),
996
+ titles = new Element('tr.titles').inject(header),
827
997
  localeDaysShort = options.days_abbr || Locale.get('Date.days_abbr'),
828
998
  day, classes, element, weekcontainer, dateString;
829
999
 
830
- date.setDate(1);
831
- while (date.getDay() != options.startDay) date.setDate(date.getDate() - 1);
832
-
1000
+ if (weeknumbers) new Element('th.title.day.weeknumber', {
1001
+ text: Locale.get('DatePicker.week')
1002
+ }).inject(titles);
833
1003
  for (day = options.startDay; day < (options.startDay + 7); day++){
834
- new Element('div.title.day.day' + (day % 7), {
835
- text: localeDaysShort[(day % 7)]
1004
+ new Element('th.title.day.day' + (day % 7), {
1005
+ text: localeDaysShort[(day % 7)],
1006
+ role: 'columnheader'
836
1007
  }).inject(titles);
837
1008
  }
838
1009
 
839
- for (var i = 0; i < 42; i++){
1010
+ days.each(function(_date, i){
1011
+ var date = new Date(_date);
840
1012
 
841
1013
  if (i % 7 == 0){
842
- weekcontainer = new Element('div.week.week' + (Math.floor(i / 7))).inject(container);
1014
+ weekcontainer = new Element('tr.week.week' + (Math.floor(i / 7))).set('role', 'row').inject(body);
1015
+ if (weeknumbers) new Element('td.day.weeknumber', {text: date.get('week')}).inject(weekcontainer);
843
1016
  }
844
1017
 
845
1018
  dateString = date.toDateString();
846
1019
  classes = '.day.day' + date.get('day');
847
1020
  if (dateString == todayString) classes += '.today';
848
- if (dateString == currentString) classes += '.selected';
849
1021
  if (date.get('month') != month) classes += '.otherMonth';
1022
+ element = new Element('td' + classes, {text: date.getDate(), role: 'gridcell'}).inject(weekcontainer);
1023
+
1024
+ if (dateString == currentString) element.addClass('selected').set('aria-selected', 'true');
1025
+ else element.set('aria-selected', 'false');
850
1026
 
851
- element = new Element('div' + classes, {text: date.getDate()}).inject(weekcontainer);
1027
+ dateElements.push({element: element, time: _date});
852
1028
 
853
1029
  if (isUnavailable('date', date, options)) element.addClass('unavailable');
854
1030
  else element.addEvent('click', fn.pass(date.clone()));
855
-
856
- date.increment('day', 1);
857
- }
1031
+ });
858
1032
 
859
1033
  return container;
860
1034
  },
861
1035
 
862
- time: function(options, date, currentDate, fn){
1036
+ time: function(options, date, fn){
863
1037
  var container = new Element('div.time'),
864
1038
  // make sure that the minutes are timeWheelStep * k
865
1039
  initMinutes = (date.get('minutes') / options.timeWheelStep).round() * options.timeWheelStep
@@ -935,7 +1109,6 @@ Picker.Date.defineRenderer = function(name, fn){
935
1109
  return this;
936
1110
  };
937
1111
 
938
-
939
1112
  var limitDate = function(date, min, max){
940
1113
  if (min && date < min) return min;
941
1114
  if (max && date > max) return max;
@@ -945,18 +1118,19 @@ var limitDate = function(date, min, max){
945
1118
  var isUnavailable = function(type, date, options){
946
1119
  var minDate = options.minDate,
947
1120
  maxDate = options.maxDate,
948
- availableDates = options.availableDates;
1121
+ availableDates = options.availableDates,
1122
+ year, month, day, ms;
949
1123
 
950
1124
  if (!minDate && !maxDate && !availableDates) return false;
951
1125
  date.clearTime();
952
1126
 
953
1127
  if (type == 'year'){
954
- var year = date.get('year');
1128
+ year = date.get('year');
955
1129
  return (
956
1130
  (minDate && year < minDate.get('year')) ||
957
1131
  (maxDate && year > maxDate.get('year')) ||
958
1132
  (
959
- (availableDates != null) && (
1133
+ (availableDates != null && !options.invertAvailable) && (
960
1134
  availableDates[year] == null ||
961
1135
  Object.getLength(availableDates[year]) == 0 ||
962
1136
  Object.getLength(
@@ -970,14 +1144,14 @@ var isUnavailable = function(type, date, options){
970
1144
  }
971
1145
 
972
1146
  if (type == 'month'){
973
- var year = date.get('year'),
974
- month = date.get('month') + 1,
975
- ms = date.format('%Y%m').toInt();
1147
+ year = date.get('year');
1148
+ month = date.get('month') + 1;
1149
+ ms = date.format('%Y%m').toInt();
976
1150
  return (
977
1151
  (minDate && ms < minDate.format('%Y%m').toInt()) ||
978
1152
  (maxDate && ms > maxDate.format('%Y%m').toInt()) ||
979
1153
  (
980
- (availableDates != null) && (
1154
+ (availableDates != null && !options.invertAvailable) && (
981
1155
  availableDates[year] == null ||
982
1156
  availableDates[year][month] == null ||
983
1157
  availableDates[year][month].length == 0
@@ -987,26 +1161,152 @@ var isUnavailable = function(type, date, options){
987
1161
  }
988
1162
 
989
1163
  // type == 'date'
990
- var year = date.get('year'),
991
- month = date.get('month') + 1,
992
- day = date.get('date');
993
- return (
994
- (minDate && date < minDate) ||
995
- (maxDate && date > maxDate) ||
996
- (
997
- (availableDates != null) && (
998
- availableDates[year] == null ||
999
- availableDates[year][month] == null ||
1000
- !availableDates[year][month].contains(day)
1001
- )
1002
- )
1003
- );
1164
+ year = date.get('year');
1165
+ month = date.get('month') + 1;
1166
+ day = date.get('date');
1167
+
1168
+ var dateAllow = (minDate && date < minDate) || (minDate && date > maxDate);
1169
+ if (availableDates != null){
1170
+ dateAllow = dateAllow
1171
+ || availableDates[year] == null
1172
+ || availableDates[year][month] == null
1173
+ || !availableDates[year][month].contains(day);
1174
+ if (options.invertAvailable) dateAllow = !dateAllow;
1175
+ }
1176
+
1177
+ return dateAllow;
1004
1178
  };
1005
1179
 
1180
+ })();
1181
+
1182
+ /*
1183
+ ---
1184
+ name: Picker.Date.Range
1185
+ description: Select a Range of Dates
1186
+ authors: Arian Stolwijk
1187
+ requires: [Picker, Picker.Date]
1188
+ provides: Picker.Date.Range
1189
+ ...
1190
+ */
1191
+ Picker.Date.Range = new Class({
1006
1192
 
1007
- // Parse times
1008
- Date.defineParsers(
1009
- '%H:%M( ?%p)?' // "11:05pm", "11:05 am" and "11:05"
1010
- );
1193
+ Extends: Picker.Date,
1011
1194
 
1012
- })();
1195
+ options: {
1196
+ getStartEndDate: function(input){
1197
+ return input.get('value').split('-').map(function(date){
1198
+ var parsed = Date.parse(date);
1199
+ return Date.isValid(parsed) ? parsed : null;
1200
+ }).clean();
1201
+ },
1202
+ setStartEndDate: function(input, dates){
1203
+ input.set('value', dates.map(function(date){
1204
+ return date.format(this.options.format);
1205
+ }, this).join(' - '));
1206
+ },
1207
+ footer: true,
1208
+ columns: 3
1209
+ },
1210
+
1211
+ getInputDate: function(input){
1212
+ if (!input) return;
1213
+
1214
+ var dates = input.retrieve('datepicker:value');
1215
+ if (dates && dates.length) dates = dates.map(Date.parse);
1216
+ if (!dates || !dates.length || dates.some(function(date){
1217
+ return !Date.isValid(date);
1218
+ })){
1219
+ dates = this.options.getStartEndDate.call(this, input);
1220
+ if (!dates.length || !dates.every(function(date){
1221
+ return Date.isValid(date);
1222
+ })) dates = [this.date];
1223
+ }
1224
+ if (dates.length == 1) this.date = this.startDate = this.endDate = dates[0];
1225
+ else if (dates.length == 2){
1226
+ this.date = this.startDate = dates[0];
1227
+ this.endDate = dates[1];
1228
+ }
1229
+ },
1230
+
1231
+ constructPicker: function(){
1232
+ this.parent();
1233
+ var footer = this.footer, self = this;
1234
+ if (!footer) return;
1235
+
1236
+ var events = {
1237
+ click: function(){
1238
+ this.focus();
1239
+ },
1240
+ blur: function(){
1241
+ var date = Date.parse(this.get('value'));
1242
+ if (date.isValid) self[(this == startInput ? 'start' : 'end') + 'Date'] = date;
1243
+ self.updateRangeSelection();
1244
+ },
1245
+ keydown: function(event){
1246
+ if (event.key == 'enter') self.selectRange();
1247
+ }
1248
+ };
1249
+
1250
+ var startInput = this.startInput = new Element('input', {events: events}).inject(footer);
1251
+ new Element('span', {text: ' - '}).inject(footer);
1252
+ var endInput = this.endInput = new Element('input', {events: events}).inject(footer);
1253
+
1254
+ this.applyButton = new Element('button.apply', {
1255
+ text: Locale.get('DatePicker.apply_range'),
1256
+ events: {click: self.selectRange.pass([], self)}
1257
+ }).inject(footer);
1258
+
1259
+ this.cancelButton = new Element('button.cancel', {
1260
+ text: Locale.get('DatePicker.cancel'),
1261
+ events: {click: self.close.pass(false, self)}
1262
+ }).inject(footer);
1263
+ },
1264
+
1265
+ renderDays: function(){
1266
+ this.parent.apply(this, arguments);
1267
+ this.updateRangeSelection();
1268
+ },
1269
+
1270
+ select: function(date){
1271
+ if (this.startDate && (this.endDate == this.startDate || date > this.endDate) && date >= this.startDate) this.endDate = date;
1272
+ else {
1273
+ this.startDate = date;
1274
+ this.endDate = date;
1275
+ }
1276
+ this.updateRangeSelection();
1277
+ },
1278
+
1279
+ selectRange: function(){
1280
+ this.date = this.startDate;
1281
+ var dates = [this.startDate, this.endDate], input = this.input;
1282
+
1283
+ this.options.setStartEndDate.call(this, input, dates);
1284
+ input.store('datepicker:value', dates.map(function(date){
1285
+ return date.strftime();
1286
+ })).fireEvent('change');
1287
+
1288
+ this.fireEvent('select', dates, input);
1289
+ this.close();
1290
+ return this;
1291
+ },
1292
+
1293
+ updateRangeSelection: function(){
1294
+ var start = this.startDate,
1295
+ end = this.endDate || start;
1296
+
1297
+ if (this.dateElements) for (var i = this.dateElements.length; i--;){
1298
+ var el = this.dateElements[i];
1299
+ if (el.time >= start && el.time <= end) el.element.addClass('selected');
1300
+ else el.element.removeClass('selected');
1301
+ }
1302
+
1303
+ var formattedFirst = start.format(this.options.format)
1304
+ formattedEnd = end.format(this.options.format);
1305
+
1306
+ this.startInput.set('value', formattedFirst);
1307
+ this.endInput.set('value', formattedEnd);
1308
+
1309
+ return this;
1310
+ }
1311
+
1312
+ });