zen 0.2.8 → 0.3b

Sign up to get free protection for your applications and to get access to all the features.
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
+ });