obitum-rails_admin 0.0.1 → 0.0.2

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 (232) hide show
  1. data/Gemfile +1 -8
  2. data/README.md +6 -2
  3. data/app/assets/javascripts/rails_admin/custom/ui.js +5 -5
  4. data/app/assets/javascripts/rails_admin/jquery-ui-1.8.16.custom.js +4727 -4727
  5. data/app/assets/javascripts/rails_admin/jquery.colorpicker.js +484 -484
  6. data/app/assets/javascripts/rails_admin/jquery.pjax.js +250 -85
  7. data/app/assets/javascripts/rails_admin/jquery.ui.timepicker.js +1371 -1219
  8. data/app/assets/javascripts/rails_admin/ra.filter-box.js +30 -30
  9. data/app/assets/javascripts/rails_admin/ra.filtering-select.js +11 -11
  10. data/app/assets/javascripts/rails_admin/ra.nested-form-hooks.coffee +40 -0
  11. data/app/assets/javascripts/rails_admin/ra.remote-form.js +41 -39
  12. data/app/assets/javascripts/rails_admin/rails_admin.js.erb +4 -3
  13. data/app/assets/javascripts/rails_admin/themes/default/ui.js +6 -6
  14. data/app/assets/javascripts/rails_admin/ui.js.coffee +44 -16
  15. data/app/assets/stylesheets/rails_admin/base/README +2 -2
  16. data/app/assets/stylesheets/rails_admin/base/theming.css.scss +226 -113
  17. data/app/assets/stylesheets/rails_admin/custom/mixins.css.scss +1 -1
  18. data/app/assets/stylesheets/rails_admin/custom/theming.css.scss +2 -2
  19. data/app/assets/stylesheets/rails_admin/custom/variables.css.scss +3 -3
  20. data/app/assets/stylesheets/rails_admin/imports.css.scss.erb +36 -1
  21. data/app/assets/stylesheets/rails_admin/jquery.ui.timepicker.css.scss +16 -0
  22. data/app/assets/stylesheets/rails_admin/ra.filtering-multiselect.css.scss +2 -2
  23. data/app/assets/stylesheets/rails_admin/themes/default/mixins.css.scss +1 -1
  24. data/app/assets/stylesheets/rails_admin/themes/default/theming.css.scss +2 -2
  25. data/app/assets/stylesheets/rails_admin/themes/default/variables.css.scss +3 -3
  26. data/app/controllers/rails_admin/application_controller.rb +13 -18
  27. data/app/controllers/rails_admin/main_controller.rb +15 -16
  28. data/app/helpers/rails_admin/application_helper.rb +67 -41
  29. data/app/helpers/rails_admin/form_builder.rb +31 -29
  30. data/app/helpers/rails_admin/main_helper.rb +4 -4
  31. data/app/views/layouts/rails_admin/_secondary_navigation.html.haml +12 -11
  32. data/app/views/layouts/rails_admin/application.html.haml +29 -29
  33. data/app/views/rails_admin/main/_dashboard_history.html.haml +1 -1
  34. data/app/views/rails_admin/main/_delete_notice.html.haml +4 -5
  35. data/app/views/rails_admin/main/_form_colorpicker.html.haml +2 -1
  36. data/app/views/rails_admin/main/_form_datetime.html.haml +1 -1
  37. data/app/views/rails_admin/main/_form_enumeration.html.haml +1 -1
  38. data/app/views/rails_admin/main/_form_field.html.haml +1 -1
  39. data/app/views/rails_admin/main/_form_file_upload.html.haml +3 -2
  40. data/app/views/rails_admin/main/_form_filtering_multiselect.html.haml +6 -4
  41. data/app/views/rails_admin/main/_form_filtering_select.html.haml +6 -5
  42. data/app/views/rails_admin/main/_form_nested_many.html.haml +41 -6
  43. data/app/views/rails_admin/main/_form_nested_one.html.haml +34 -5
  44. data/app/views/rails_admin/main/_form_text.html.haml +3 -3
  45. data/app/views/rails_admin/main/_submit_buttons.html.haml +6 -4
  46. data/app/views/rails_admin/main/bulk_delete.html.haml +7 -3
  47. data/app/views/rails_admin/main/dashboard.html.haml +4 -4
  48. data/app/views/rails_admin/main/delete.html.haml +7 -3
  49. data/app/views/rails_admin/main/edit.html.haml +1 -1
  50. data/app/views/rails_admin/main/export.html.haml +57 -53
  51. data/app/views/rails_admin/main/history.html.haml +8 -8
  52. data/app/views/rails_admin/main/index.html.haml +22 -21
  53. data/app/views/rails_admin/main/new.html.haml +1 -1
  54. data/app/views/rails_admin/main/show.html.haml +2 -2
  55. data/config/initializers/active_record_extensions.rb +2 -2
  56. data/config/initializers/haml.rb +0 -1
  57. data/config/locales/rails_admin.en.yml +5 -5
  58. data/lib/generators/rails_admin/templates/initializer.erb +7 -7
  59. data/lib/rails_admin/abstract_model.rb +36 -53
  60. data/lib/rails_admin/adapters/active_record/abstract_object.rb +32 -0
  61. data/lib/rails_admin/adapters/active_record.rb +56 -137
  62. data/lib/rails_admin/config/actions/base.rb +40 -28
  63. data/lib/rails_admin/config/actions/bulk_delete.rb +10 -11
  64. data/lib/rails_admin/config/actions/dashboard.rb +9 -5
  65. data/lib/rails_admin/config/actions/delete.rb +12 -9
  66. data/lib/rails_admin/config/actions/edit.rb +13 -9
  67. data/lib/rails_admin/config/actions/export.rb +9 -6
  68. data/lib/rails_admin/config/actions/history_index.rb +9 -5
  69. data/lib/rails_admin/config/actions/history_show.rb +9 -5
  70. data/lib/rails_admin/config/actions/index.rb +23 -13
  71. data/lib/rails_admin/config/actions/new.rb +14 -10
  72. data/lib/rails_admin/config/actions/show.rb +10 -5
  73. data/lib/rails_admin/config/actions/show_in_app.rb +9 -4
  74. data/lib/rails_admin/config/actions.rb +16 -16
  75. data/lib/rails_admin/config/configurable.rb +92 -0
  76. data/lib/rails_admin/config/fields/association.rb +6 -23
  77. data/lib/rails_admin/config/fields/base.rb +58 -40
  78. data/lib/rails_admin/config/fields/factories/belongs_to_association.rb +26 -0
  79. data/lib/rails_admin/config/fields/factories/carrierwave.rb +21 -2
  80. data/lib/rails_admin/config/fields/factories/devise.rb +9 -12
  81. data/lib/rails_admin/config/fields/factories/dragonfly.rb +13 -7
  82. data/lib/rails_admin/config/fields/factories/paperclip.rb +14 -15
  83. data/lib/rails_admin/config/fields/group.rb +16 -3
  84. data/lib/rails_admin/config/fields/types/belongs_to_association.rb +2 -2
  85. data/lib/rails_admin/config/fields/types/carrierwave.rb +0 -9
  86. data/lib/rails_admin/config/fields/types/color.rb +3 -3
  87. data/lib/rails_admin/config/fields/types/dragonfly.rb +1 -19
  88. data/lib/rails_admin/config/fields/types/file_upload.rb +7 -11
  89. data/lib/rails_admin/config/fields/types/has_many_association.rb +1 -6
  90. data/lib/rails_admin/config/fields/types/has_one_association.rb +4 -4
  91. data/lib/rails_admin/config/fields/types/paperclip.rb +0 -18
  92. data/lib/rails_admin/config/fields/types/password.rb +2 -2
  93. data/lib/rails_admin/config/fields/types/polymorphic_association.rb +2 -2
  94. data/lib/rails_admin/config/fields/types/text.rb +2 -2
  95. data/lib/rails_admin/config/fields.rb +7 -17
  96. data/lib/rails_admin/config/has_fields.rb +9 -9
  97. data/lib/rails_admin/config/has_groups.rb +2 -2
  98. data/lib/rails_admin/config/hideable.rb +2 -2
  99. data/lib/rails_admin/config/model.rb +23 -17
  100. data/lib/rails_admin/config/proxyable/proxy.rb +43 -0
  101. data/lib/rails_admin/config/proxyable.rb +12 -0
  102. data/lib/rails_admin/config/sections/base.rb +14 -4
  103. data/lib/rails_admin/config.rb +38 -9
  104. data/lib/rails_admin/extension.rb +1 -1
  105. data/lib/rails_admin/extensions/history/auditing_adapter.rb +6 -6
  106. data/lib/rails_admin/extensions/history/history.rb +2 -1
  107. data/lib/rails_admin/extensions/paper_trail/auditing_adapter.rb +13 -13
  108. data/lib/rails_admin/support/csv_converter.rb +6 -6
  109. data/lib/rails_admin/version.rb +1 -1
  110. data/lib/rails_admin.rb +0 -5
  111. data/spec/controllers/main_controller_spec.rb +7 -7
  112. data/spec/dummy_app/Gemfile +10 -7
  113. data/spec/dummy_app/Rakefile +1 -1
  114. data/spec/dummy_app/app/assets/images/rails.png +0 -0
  115. data/spec/dummy_app/app/assets/javascripts/application.js +15 -0
  116. data/spec/dummy_app/app/assets/stylesheets/application.css +13 -0
  117. data/spec/dummy_app/app/controllers/players_controller.rb +1 -1
  118. data/spec/dummy_app/app/models/cms/basic_page.rb +1 -1
  119. data/spec/dummy_app/app/models/division.rb +5 -1
  120. data/spec/dummy_app/app/models/field_test.rb +4 -4
  121. data/spec/dummy_app/app/models/nested_field_test.rb +3 -0
  122. data/spec/dummy_app/app/models/player.rb +3 -3
  123. data/spec/dummy_app/app/views/layouts/application.html.erb +3 -2
  124. data/spec/dummy_app/app/views/players/show.html.haml +1 -1
  125. data/spec/dummy_app/config/application.rb +20 -5
  126. data/spec/dummy_app/config/database.yml +5 -2
  127. data/spec/dummy_app/config/environments/development.rb +11 -1
  128. data/spec/dummy_app/config/environments/production.rb +19 -3
  129. data/spec/dummy_app/config/environments/test.rb +5 -7
  130. data/spec/dummy_app/config/initializers/devise.rb +6 -0
  131. data/spec/dummy_app/config/initializers/inflections.rb +5 -0
  132. data/spec/dummy_app/config/initializers/secret_token.rb +1 -1
  133. data/spec/dummy_app/{foo/test/dummy/config → config}/initializers/wrap_parameters.rb +1 -1
  134. data/spec/dummy_app/config/routes.rb +1 -1
  135. data/spec/dummy_app/db/migrate/00000000000006_devise_create_users.rb +35 -5
  136. data/spec/dummy_app/db/migrate/20120118122004_add_categories.rb +1 -1
  137. data/spec/dummy_app/doc/README_FOR_APP +2 -0
  138. data/spec/dummy_app/public/404.html +26 -0
  139. data/spec/dummy_app/{foo/test/dummy/public → public}/422.html +0 -0
  140. data/spec/dummy_app/{foo/test/dummy/public → public}/500.html +0 -1
  141. data/spec/dummy_app/public/favicon.ico +0 -0
  142. data/spec/dummy_app/public/robots.txt +5 -0
  143. data/spec/helpers/application_helper_spec.rb +107 -42
  144. data/spec/{requests → integration}/authorization/cancan_spec.rb +4 -5
  145. data/spec/{requests → integration}/basic/bulk_action/rails_admin_basic_bulk_action_spec.rb +0 -0
  146. data/spec/{requests → integration}/basic/bulk_destroy/rails_admin_basic_bulk_destroy_spec.rb +3 -16
  147. data/spec/{requests → integration}/basic/create/rails_admin_basic_create_spec.rb +0 -2
  148. data/spec/{requests → integration}/basic/create/rails_admin_namespaced_model_create_spec.rb +0 -16
  149. data/spec/{requests → integration}/basic/delete/rails_admin_basic_delete_spec.rb +0 -0
  150. data/spec/{requests → integration}/basic/destroy/rails_admin_basic_destroy_spec.rb +0 -0
  151. data/spec/{requests → integration}/basic/edit/rails_admin_basic_edit_spec.rb +2 -2
  152. data/spec/{requests → integration}/basic/export/rails_admin_basic_export_spec.rb +1 -1
  153. data/spec/{requests → integration}/basic/list/rails_admin_basic_list_spec.rb +12 -38
  154. data/spec/{requests → integration}/basic/new/rails_admin_basic_new_spec.rb +0 -0
  155. data/spec/{requests → integration}/basic/new/rails_admin_namespaced_model_new_spec.rb +0 -0
  156. data/spec/{requests → integration}/basic/show/rails_admin_basic_show_spec.rb +1 -2
  157. data/spec/{requests → integration}/basic/update/rails_admin_basic_update_spec.rb +2 -2
  158. data/spec/{requests → integration}/config/edit/rails_admin_config_edit_spec.rb +65 -24
  159. data/spec/{requests → integration}/config/list/rails_admin_config_list_spec.rb +2 -2
  160. data/spec/{requests → integration}/config/show/rails_admin_config_show_spec.rb +20 -0
  161. data/spec/{requests → integration}/history/rails_admin_history_spec.rb +3 -3
  162. data/spec/{requests → integration}/rails_admin_spec.rb +8 -41
  163. data/spec/{requests → integration}/relation_spec.rb +0 -0
  164. data/spec/spec_helper.rb +13 -31
  165. data/spec/{lib → unit/adapters/active_record}/abstract_object_spec.rb +17 -5
  166. data/spec/unit/adapters/active_record_spec.rb +53 -0
  167. data/spec/{requests/actions.rb → unit/config/actions_spec.rb} +47 -31
  168. data/spec/unit/config/fields/base_spec.rb +291 -0
  169. data/spec/unit/config/model_spec.rb +75 -0
  170. data/spec/unit/config/sections_spec.rb +123 -0
  171. data/spec/{lib/rails_admin_spec.rb → unit/config_spec.rb} +65 -93
  172. metadata +127 -223
  173. data/app/assets/javascripts/rails_admin/jquery_nested_form.js +0 -58
  174. data/app/views/layouts/rails_admin/_navigation.html.haml +0 -17
  175. data/lib/rails_admin/abstract_object.rb +0 -28
  176. data/lib/rails_admin/config/base.rb +0 -111
  177. data/lib/rails_admin/config/proxy.rb +0 -40
  178. data/lib/rails_admin/generic_support.rb +0 -18
  179. data/spec/dummy_app/config/initializers/quiet_assets.rb +0 -10
  180. data/spec/dummy_app/foo/Gemfile +0 -17
  181. data/spec/dummy_app/foo/MIT-LICENSE +0 -20
  182. data/spec/dummy_app/foo/README.rdoc +0 -3
  183. data/spec/dummy_app/foo/Rakefile +0 -39
  184. data/spec/dummy_app/foo/app/assets/javascripts/foo/application.js +0 -9
  185. data/spec/dummy_app/foo/app/assets/stylesheets/foo/application.css +0 -7
  186. data/spec/dummy_app/foo/app/controllers/foo/application_controller.rb +0 -4
  187. data/spec/dummy_app/foo/app/helpers/foo/application_helper.rb +0 -4
  188. data/spec/dummy_app/foo/app/models/foo/bar.rb +0 -4
  189. data/spec/dummy_app/foo/app/views/layouts/foo/application.html.erb +0 -14
  190. data/spec/dummy_app/foo/config/routes.rb +0 -2
  191. data/spec/dummy_app/foo/foo.gemspec +0 -23
  192. data/spec/dummy_app/foo/lib/foo/engine.rb +0 -5
  193. data/spec/dummy_app/foo/lib/foo/version.rb +0 -3
  194. data/spec/dummy_app/foo/lib/foo.rb +0 -4
  195. data/spec/dummy_app/foo/lib/tasks/foo_tasks.rake +0 -4
  196. data/spec/dummy_app/foo/script/rails +0 -6
  197. data/spec/dummy_app/foo/test/dummy/Rakefile +0 -7
  198. data/spec/dummy_app/foo/test/dummy/app/assets/javascripts/application.js +0 -9
  199. data/spec/dummy_app/foo/test/dummy/app/assets/stylesheets/application.css +0 -7
  200. data/spec/dummy_app/foo/test/dummy/app/controllers/application_controller.rb +0 -3
  201. data/spec/dummy_app/foo/test/dummy/app/helpers/application_helper.rb +0 -2
  202. data/spec/dummy_app/foo/test/dummy/app/views/layouts/application.html.erb +0 -14
  203. data/spec/dummy_app/foo/test/dummy/config/application.rb +0 -45
  204. data/spec/dummy_app/foo/test/dummy/config/boot.rb +0 -10
  205. data/spec/dummy_app/foo/test/dummy/config/database.yml +0 -25
  206. data/spec/dummy_app/foo/test/dummy/config/environment.rb +0 -5
  207. data/spec/dummy_app/foo/test/dummy/config/environments/development.rb +0 -30
  208. data/spec/dummy_app/foo/test/dummy/config/environments/production.rb +0 -60
  209. data/spec/dummy_app/foo/test/dummy/config/environments/test.rb +0 -39
  210. data/spec/dummy_app/foo/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  211. data/spec/dummy_app/foo/test/dummy/config/initializers/inflections.rb +0 -10
  212. data/spec/dummy_app/foo/test/dummy/config/initializers/mime_types.rb +0 -5
  213. data/spec/dummy_app/foo/test/dummy/config/initializers/secret_token.rb +0 -7
  214. data/spec/dummy_app/foo/test/dummy/config/initializers/session_store.rb +0 -8
  215. data/spec/dummy_app/foo/test/dummy/config/locales/en.yml +0 -5
  216. data/spec/dummy_app/foo/test/dummy/config/routes.rb +0 -4
  217. data/spec/dummy_app/foo/test/dummy/config.ru +0 -4
  218. data/spec/dummy_app/foo/test/dummy/db/schema.rb +0 -21
  219. data/spec/dummy_app/foo/test/dummy/public/404.html +0 -26
  220. data/spec/dummy_app/foo/test/dummy/public/favicon.ico +0 -0
  221. data/spec/dummy_app/foo/test/dummy/script/rails +0 -6
  222. data/spec/dummy_app/foo/test/fixtures/foo/bars.yml +0 -11
  223. data/spec/dummy_app/foo/test/foo_test.rb +0 -7
  224. data/spec/dummy_app/foo/test/integration/navigation_test.rb +0 -10
  225. data/spec/dummy_app/foo/test/test_helper.rb +0 -10
  226. data/spec/dummy_app/foo/test/unit/foo/bar_test.rb +0 -9
  227. data/spec/generator_helpers.rb +0 -30
  228. data/spec/generators/install_generator_spec.rb +0 -85
  229. data/spec/generators/uninstall_generator_spec.rb +0 -35
  230. data/spec/lib/custom_field.rb +0 -7
  231. data/spec/requests/config/navigation/rails_admin_config_navigation_spec.rb +0 -107
  232. data/spec/requests/config/rails_admin_config_spec.rb +0 -227
@@ -1,1219 +1,1371 @@
1
- /*
2
- * jQuery UI Timepicker 0.2.5
3
- *
4
- * Copyright 2010-2011, Francois Gelinas
5
- * Dual licensed under the MIT or GPL Version 2 licenses.
6
- * http://jquery.org/license
7
- *
8
- * http://fgelinas.com/code/timepicker
9
- *
10
- * Depends:
11
- * jquery.ui.core.js
12
- * jquery.ui.position.js (only if position settngs are used)
13
- *
14
- * Change version 0.1.0 - moved the t-rex up here
15
- *
16
- ____
17
- ___ .-~. /_"-._
18
- `-._~-. / /_ "~o\ :Y
19
- \ \ / : \~x. ` ')
20
- ] Y / | Y< ~-.__j
21
- / ! _.--~T : l l< /.-~
22
- / / ____.--~ . ` l /~\ \<|Y
23
- / / .-~~" /| . ',-~\ \L|
24
- / / / .^ \ Y~Y \.^>/l_ "--'
25
- / Y .-"( . l__ j_j l_/ /~_.-~ .
26
- Y l / \ ) ~~~." / `/"~ / \.__/l_
27
- | \ _.-" ~-{__ l : l._Z~-.___.--~
28
- | ~---~ / ~~"---\_ ' __[>
29
- l . _.^ ___ _>-y~
30
- \ \ . .-~ .-~ ~>--" /
31
- \ ~---" / ./ _.-'
32
- "-.,_____.,_ _.--~\ _.-~
33
- ~~ ( _} -Row
34
- `. ~(
35
- ) \
36
- /,`--'~\--'~\
37
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
38
- ->T-Rex<-
39
- */
40
-
41
- (function ($, undefined) {
42
-
43
- $.extend($.ui, { timepicker: { version: "0.2.5"} });
44
-
45
- var PROP_NAME = 'timepicker';
46
- var tpuuid = new Date().getTime();
47
-
48
- /* Time picker manager.
49
- Use the singleton instance of this class, $.timepicker, to interact with the time picker.
50
- Settings for (groups of) time pickers are maintained in an instance object,
51
- allowing multiple different settings on the same page. */
52
-
53
- function Timepicker() {
54
- this.debug = true; // Change this to true to start debugging
55
- this._curInst = null; // The current instance in use
56
- this._isInline = false; // true if the instance is displayed inline
57
- this._disabledInputs = []; // List of time picker inputs that have been disabled
58
- this._timepickerShowing = false; // True if the popup picker is showing , false if not
59
- this._inDialog = false; // True if showing within a "dialog", false if not
60
- this._dialogClass = 'ui-timepicker-dialog'; // The name of the dialog marker class
61
- this._mainDivId = 'ui-timepicker-div'; // The ID of the main timepicker division
62
- this._inlineClass = 'ui-timepicker-inline'; // The name of the inline marker class
63
- this._currentClass = 'ui-timepicker-current'; // The name of the current hour / minutes marker class
64
- this._dayOverClass = 'ui-timepicker-days-cell-over'; // The name of the day hover marker class
65
-
66
- this.regional = []; // Available regional settings, indexed by language code
67
- this.regional[''] = { // Default regional settings
68
- hourText: 'Hour', // Display text for hours section
69
- minuteText: 'Minute', // Display text for minutes link
70
- amPmText: ['AM', 'PM'] // Display text for AM PM
71
- };
72
- this._defaults = { // Global defaults for all the time picker instances
73
- showOn: 'focus', // 'focus' for popup on focus,
74
- // 'button' for trigger button, or 'both' for either (not yet implemented)
75
- button: null, // 'button' element that will trigger the timepicker
76
- showAnim: 'fadeIn', // Name of jQuery animation for popup
77
- showOptions: {}, // Options for enhanced animations
78
- appendText: '', // Display text following the input box, e.g. showing the format
79
-
80
- beforeShow: null, // Define a callback function executed before the timepicker is shown
81
- onSelect: null, // Define a callback function when a hour / minutes is selected
82
- onClose: null, // Define a callback function when the timepicker is closed
83
-
84
- timeSeparator: ':', // The character to use to separate hours and minutes.
85
- periodSeparator: ' ', // The character to use to separate the time from the time period.
86
- showPeriod: false, // Define whether or not to show AM/PM with selected time
87
- showPeriodLabels: true, // Show the AM/PM labels on the left of the time picker
88
- showLeadingZero: true, // Define whether or not to show a leading zero for hours < 10. [true/false]
89
- showMinutesLeadingZero: true, // Define whether or not to show a leading zero for minutes < 10.
90
- altField: '', // Selector for an alternate field to store selected time into
91
- defaultTime: 'now', // Used as default time when input field is empty or for inline timePicker
92
- // (set to 'now' for the current time, '' for no highlighted time)
93
- myPosition: 'left top', // Position of the dialog relative to the input.
94
- // see the position utility for more info : http://jqueryui.com/demos/position/
95
- atPosition: 'left bottom', // Position of the input element to match
96
- // Note : if the position utility is not loaded, the timepicker will attach left top to left bottom
97
- //NEW: 2011-02-03
98
- onHourShow: null, // callback for enabling / disabling on selectable hours ex : function(hour) { return true; }
99
- onMinuteShow: null, // callback for enabling / disabling on time selection ex : function(hour,minute) { return true; }
100
- // 2011-03-22 - v 0.0.9
101
- zIndex: null, // specify zIndex
102
-
103
- hours: {
104
- starts: 0, // first displayed hour
105
- ends: 23 // last displayed hour
106
- },
107
- minutes: {
108
- starts: 0, // first displayed minute
109
- ends: 55, // last displayed minute
110
- interval: 5 // interval of displayed minutes
111
- },
112
- rows: 4, // number of rows for the input tables, minimum 2, makes more sense if you use multiple of 2
113
- // 2011-08-05 0.2.4
114
- showHours: true, // display the hours section of the dialog
115
- showMinutes: true // display the minute section of the dialog
116
-
117
- };
118
- $.extend(this._defaults, this.regional['']);
119
-
120
- this.tpDiv = $('<div id="' + this._mainDivId + '" class="ui-timepicker ui-widget ui-helper-clearfix ui-corner-all " style="display: none"></div>');
121
- }
122
-
123
- $.extend(Timepicker.prototype, {
124
- /* Class name added to elements to indicate already configured with a time picker. */
125
- markerClassName: 'hasTimepicker',
126
-
127
- /* Debug logging (if enabled). */
128
- log: function () {
129
- if (this.debug)
130
- console.log.apply('', arguments);
131
- },
132
-
133
- // TODO rename to "widget" when switching to widget factory
134
- _widgetTimepicker: function () {
135
- return this.tpDiv;
136
- },
137
-
138
- /* Override the default settings for all instances of the time picker.
139
- @param settings object - the new settings to use as defaults (anonymous object)
140
- @return the manager object */
141
- setDefaults: function (settings) {
142
- extendRemove(this._defaults, settings || {});
143
- return this;
144
- },
145
-
146
- /* Attach the time picker to a jQuery selection.
147
- @param target element - the target input field or division or span
148
- @param settings object - the new settings to use for this time picker instance (anonymous) */
149
- _attachTimepicker: function (target, settings) {
150
- // check for settings on the control itself - in namespace 'time:'
151
- var inlineSettings = null;
152
- for (var attrName in this._defaults) {
153
- var attrValue = target.getAttribute('time:' + attrName);
154
- if (attrValue) {
155
- inlineSettings = inlineSettings || {};
156
- try {
157
- inlineSettings[attrName] = eval(attrValue);
158
- } catch (err) {
159
- inlineSettings[attrName] = attrValue;
160
- }
161
- }
162
- }
163
- var nodeName = target.nodeName.toLowerCase();
164
- var inline = (nodeName == 'div' || nodeName == 'span');
165
-
166
- if (!target.id) {
167
- this.uuid += 1;
168
- target.id = 'tp' + this.uuid;
169
- }
170
- var inst = this._newInst($(target), inline);
171
- inst.settings = $.extend({}, settings || {}, inlineSettings || {});
172
- if (nodeName == 'input') {
173
- this._connectTimepicker(target, inst);
174
- // init inst.hours and inst.minutes from the input value
175
- this._setTimeFromField(inst);
176
- } else if (inline) {
177
- this._inlineTimepicker(target, inst);
178
- }
179
-
180
-
181
- },
182
-
183
- /* Create a new instance object. */
184
- _newInst: function (target, inline) {
185
- var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars
186
- return {
187
- id: id, input: target, // associated target
188
- inline: inline, // is timepicker inline or not :
189
- tpDiv: (!inline ? this.tpDiv : // presentation div
190
- $('<div class="' + this._inlineClass + ' ui-timepicker ui-widget ui-helper-clearfix"></div>'))
191
- };
192
- },
193
-
194
- /* Attach the time picker to an input field. */
195
- _connectTimepicker: function (target, inst) {
196
- var input = $(target);
197
- inst.append = $([]);
198
- inst.trigger = $([]);
199
- if (input.hasClass(this.markerClassName)) { return; }
200
- this._attachments(input, inst);
201
- input.addClass(this.markerClassName).
202
- keydown(this._doKeyDown).
203
- keyup(this._doKeyUp).
204
- bind("setData.timepicker", function (event, key, value) {
205
- inst.settings[key] = value;
206
- }).
207
- bind("getData.timepicker", function (event, key) {
208
- return this._get(inst, key);
209
- });
210
- $.data(target, PROP_NAME, inst);
211
- },
212
-
213
- /* Handle keystrokes. */
214
- _doKeyDown: function (event) {
215
- var inst = $.timepicker._getInst(event.target);
216
- var handled = true;
217
- inst._keyEvent = true;
218
- if ($.timepicker._timepickerShowing) {
219
- switch (event.keyCode) {
220
- case 9: $.timepicker._hideTimepicker();
221
- handled = false;
222
- break; // hide on tab out
223
- case 13:
224
- $.timepicker._updateSelectedValue(inst);
225
- $.timepicker._hideTimepicker();
226
-
227
- return false; // don't submit the form
228
- break; // select the value on enter
229
- case 27: $.timepicker._hideTimepicker();
230
- break; // hide on escape
231
- default: handled = false;
232
- }
233
- }
234
- else if (event.keyCode == 36 && event.ctrlKey) { // display the time picker on ctrl+home
235
- $.timepicker._showTimepicker(this);
236
- }
237
- else {
238
- handled = false;
239
- }
240
- if (handled) {
241
- event.preventDefault();
242
- event.stopPropagation();
243
- }
244
- },
245
-
246
- /* Update selected time on keyUp */
247
- /* Added verion 0.0.5 */
248
- _doKeyUp: function (event) {
249
- var inst = $.timepicker._getInst(event.target);
250
- $.timepicker._setTimeFromField(inst);
251
- $.timepicker._updateTimepicker(inst);
252
- },
253
-
254
- /* Make attachments based on settings. */
255
- _attachments: function (input, inst) {
256
- var appendText = this._get(inst, 'appendText');
257
- var isRTL = this._get(inst, 'isRTL');
258
- if (inst.append) { inst.append.remove(); }
259
- if (appendText) {
260
- inst.append = $('<span class="' + this._appendClass + '">' + appendText + '</span>');
261
- input[isRTL ? 'before' : 'after'](inst.append);
262
- }
263
- input.unbind('focus.timepicker', this._showTimepicker);
264
- if (inst.trigger) { inst.trigger.remove(); }
265
-
266
- var showOn = this._get(inst, 'showOn');
267
- if (showOn == 'focus' || showOn == 'both') { // pop-up time picker when in the marked field
268
- input.bind("focus.timepicker", this._showTimepicker);
269
- }
270
- if (showOn == 'button' || showOn == 'both') { // pop-up time picker when 'button' element is clicked
271
- var button = this._get(inst, 'button');
272
- $(button).bind("click.timepicker", function () {
273
- if ($.timepicker._timepickerShowing && $.timepicker._lastInput == input[0]) { $.timepicker._hideTimepicker(); }
274
- else { $.timepicker._showTimepicker(input[0]); }
275
- return false;
276
- });
277
-
278
- }
279
- },
280
-
281
-
282
- /* Attach an inline time picker to a div. */
283
- _inlineTimepicker: function(target, inst) {
284
- var divSpan = $(target);
285
- if (divSpan.hasClass(this.markerClassName))
286
- return;
287
- divSpan.addClass(this.markerClassName).append(inst.tpDiv).
288
- bind("setData.timepicker", function(event, key, value){
289
- inst.settings[key] = value;
290
- }).bind("getData.timepicker", function(event, key){
291
- return this._get(inst, key);
292
- });
293
- $.data(target, PROP_NAME, inst);
294
-
295
- this._setTimeFromField(inst);
296
- this._updateTimepicker(inst);
297
- inst.tpDiv.show();
298
- },
299
-
300
- /* Pop-up the time picker for a given input field.
301
- @param input element - the input field attached to the time picker or
302
- event - if triggered by focus */
303
- _showTimepicker: function (input) {
304
- input = input.target || input;
305
- if (input.nodeName.toLowerCase() != 'input') { input = $('input', input.parentNode)[0]; } // find from button/image trigger
306
- if ($.timepicker._isDisabledTimepicker(input) || $.timepicker._lastInput == input) { return; } // already here
307
-
308
- // fix v 0.0.8 - close current timepicker before showing another one
309
- $.timepicker._hideTimepicker();
310
-
311
- var inst = $.timepicker._getInst(input);
312
- if ($.timepicker._curInst && $.timepicker._curInst != inst) {
313
- $.timepicker._curInst.tpDiv.stop(true, true);
314
- }
315
- var beforeShow = $.timepicker._get(inst, 'beforeShow');
316
- extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {}));
317
- inst.lastVal = null;
318
- $.timepicker._lastInput = input;
319
-
320
- $.timepicker._setTimeFromField(inst);
321
-
322
- // calculate default position
323
- if ($.timepicker._inDialog) { input.value = ''; } // hide cursor
324
- if (!$.timepicker._pos) { // position below input
325
- $.timepicker._pos = $.timepicker._findPos(input);
326
- $.timepicker._pos[1] += input.offsetHeight; // add the height
327
- }
328
- var isFixed = false;
329
- $(input).parents().each(function () {
330
- isFixed |= $(this).css('position') == 'fixed';
331
- return !isFixed;
332
- });
333
- if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled
334
- $.timepicker._pos[0] -= document.documentElement.scrollLeft;
335
- $.timepicker._pos[1] -= document.documentElement.scrollTop;
336
- }
337
-
338
- var offset = { left: $.timepicker._pos[0], top: $.timepicker._pos[1] };
339
-
340
- $.timepicker._pos = null;
341
- // determine sizing offscreen
342
- inst.tpDiv.css({ position: 'absolute', display: 'block', top: '-1000px' });
343
- $.timepicker._updateTimepicker(inst);
344
-
345
-
346
- // position with the ui position utility, if loaded
347
- if ( ( ! inst.inline ) && ( typeof $.ui.position == 'object' ) ) {
348
- inst.tpDiv.position({
349
- of: inst.input,
350
- my: $.timepicker._get( inst, 'myPosition' ),
351
- at: $.timepicker._get( inst, 'atPosition' ),
352
- // offset: $( "#offset" ).val(),
353
- // using: using,
354
- collision: 'flip'
355
- });
356
- var offset = inst.tpDiv.offset();
357
- $.timepicker._pos = [offset.top, offset.left];
358
- }
359
-
360
-
361
- // reset clicked state
362
- inst._hoursClicked = false;
363
- inst._minutesClicked = false;
364
-
365
- // fix width for dynamic number of time pickers
366
- // and adjust position before showing
367
- offset = $.timepicker._checkOffset(inst, offset, isFixed);
368
- inst.tpDiv.css({ position: ($.timepicker._inDialog && $.blockUI ?
369
- 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
370
- left: offset.left + 'px', top: offset.top + 'px'
371
- });
372
- if ( ! inst.inline ) {
373
- var showAnim = $.timepicker._get(inst, 'showAnim');
374
- var duration = $.timepicker._get(inst, 'duration');
375
- var zIndex = $.timepicker._get(inst, 'zIndex');
376
- var postProcess = function () {
377
- $.timepicker._timepickerShowing = true;
378
- var borders = $.timepicker._getBorders(inst.tpDiv);
379
- inst.tpDiv.find('iframe.ui-timepicker-cover'). // IE6- only
380
- css({ left: -borders[0], top: -borders[1],
381
- width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight()
382
- });
383
- };
384
-
385
- // if not zIndex specified in options, use target zIndex + 1
386
- if ( ! zIndex) {
387
- zIndex = $(input).attr('zIndex') + 1;
388
- }
389
- inst.tpDiv.attr('zIndex', zIndex);
390
- inst.tpDiv.css('zIndex', zIndex);
391
-
392
- if ($.effects && $.effects[showAnim]) {
393
- inst.tpDiv.show(showAnim, $.timepicker._get(inst, 'showOptions'), duration, postProcess);
394
- }
395
- else {
396
- inst.tpDiv[showAnim || 'show']((showAnim ? duration : null), postProcess);
397
- }
398
- if (!showAnim || !duration) { postProcess(); }
399
- if (inst.input.is(':visible') && !inst.input.is(':disabled')) { inst.input.focus(); }
400
- $.timepicker._curInst = inst;
401
- }
402
- },
403
-
404
- /* Generate the time picker content. */
405
- _updateTimepicker: function (inst) {
406
- inst.tpDiv.empty().append(this._generateHTML(inst));
407
- this._rebindDialogEvents(inst);
408
-
409
- },
410
-
411
- _rebindDialogEvents: function (inst) {
412
- var borders = $.timepicker._getBorders(inst.tpDiv),
413
- self = this;
414
- inst.tpDiv
415
- .find('iframe.ui-timepicker-cover') // IE6- only
416
- .css({ left: -borders[0], top: -borders[1],
417
- width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight()
418
- })
419
- .end()
420
- // after the picker html is appended bind the click & double click events (faster in IE this way
421
- // then letting the browser interpret the inline events)
422
- // the binding for the minute cells also exists in _updateMinuteDisplay
423
- .find('.ui-timepicker-minute-cell')
424
- .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectMinutes, this))
425
- .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectMinutes, this))
426
- .end()
427
- .find('.ui-timepicker-hour-cell')
428
- .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectHours, this))
429
- .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectHours, this))
430
- .end()
431
- .find('.ui-timepicker td a')
432
- .bind('mouseout', function () {
433
- $(this).removeClass('ui-state-hover');
434
- if (this.className.indexOf('ui-timepicker-prev') != -1) $(this).removeClass('ui-timepicker-prev-hover');
435
- if (this.className.indexOf('ui-timepicker-next') != -1) $(this).removeClass('ui-timepicker-next-hover');
436
- })
437
- .bind('mouseover', function () {
438
- if ( ! self._isDisabledTimepicker(inst.inline ? inst.tpDiv.parent()[0] : inst.input[0])) {
439
- $(this).parents('.ui-timepicker-calendar').find('a').removeClass('ui-state-hover');
440
- $(this).addClass('ui-state-hover');
441
- if (this.className.indexOf('ui-timepicker-prev') != -1) $(this).addClass('ui-timepicker-prev-hover');
442
- if (this.className.indexOf('ui-timepicker-next') != -1) $(this).addClass('ui-timepicker-next-hover');
443
- }
444
- })
445
- .end()
446
- .find('.' + this._dayOverClass + ' a')
447
- .trigger('mouseover')
448
- .end();
449
- },
450
-
451
- /* Generate the HTML for the current state of the date picker. */
452
- _generateHTML: function (inst) {
453
-
454
- var h, m, row, col, html, hoursHtml, minutesHtml = '',
455
- showPeriod = (this._get(inst, 'showPeriod') == true),
456
- showPeriodLabels = (this._get(inst, 'showPeriodLabels') == true),
457
- showLeadingZero = (this._get(inst, 'showLeadingZero') == true),
458
- showHours = (this._get(inst, 'showHours') == true),
459
- showMinutes = (this._get(inst, 'showMinutes') == true),
460
- amPmText = this._get(inst, 'amPmText'),
461
- rows = this._get(inst, 'rows'),
462
- amRows = 0,
463
- pmRows = 0,
464
- amItems = 0,
465
- pmItems = 0,
466
- amFirstRow = 0,
467
- pmFirstRow = 0,
468
- hours = Array(),
469
- hours_options = this._get(inst, 'hours'),
470
- hoursPerRow = null,
471
- hourCounter = 0,
472
- hourLabel = this._get(inst, 'hourText');
473
-
474
- // prepare all hours and minutes, makes it easier to distribute by rows
475
- for (h = hours_options.starts; h <= hours_options.ends; h++) {
476
- hours.push (h);
477
- }
478
- hoursPerRow = Math.ceil(hours.length / rows); // always round up
479
-
480
- if (showPeriodLabels) {
481
- for (hourCounter = 0; hourCounter < hours.length; hourCounter++) {
482
- if (hours[hourCounter] < 12) {
483
- amItems++;
484
- }
485
- else {
486
- pmItems++;
487
- }
488
- }
489
- hourCounter = 0;
490
-
491
- amRows = Math.floor(amItems / hours.length * rows);
492
- pmRows = Math.floor(pmItems / hours.length * rows);
493
-
494
- // assign the extra row to the period that is more densly populated
495
- if (rows != amRows + pmRows) {
496
- // Make sure: AM Has Items and either PM Does Not, AM has no rows yet, or AM is more dense
497
- if (amItems && (!pmItems || !amRows || (pmRows && amItems / amRows >= pmItems / pmRows))) {
498
- amRows++;
499
- } else {
500
- pmRows++;
501
- }
502
- }
503
- amFirstRow = Math.min(amRows, 1);
504
- pmFirstRow = amRows + 1;
505
- hoursPerRow = Math.ceil(Math.max(amItems / amRows, pmItems / pmRows));
506
- }
507
-
508
-
509
- html = '<table class="ui-timepicker-table ui-widget-content ui-corner-all"><tr>';
510
-
511
- if (showHours) {
512
-
513
- html += '<td class="ui-timepicker-hours">' +
514
- '<div class="ui-timepicker-title ui-widget-header ui-helper-clearfix ui-corner-all">' +
515
- hourLabel +
516
- '</div>' +
517
- '<table class="ui-timepicker">';
518
-
519
- for (row = 1; row <= rows; row++) {
520
- html += '<tr>';
521
- // AM
522
- if (row == amFirstRow && showPeriodLabels) {
523
- html += '<th rowspan="' + amRows.toString() + '" class="periods" scope="row">' + amPmText[0] + '</th>';
524
- }
525
- // PM
526
- if (row == pmFirstRow && showPeriodLabels) {
527
- html += '<th rowspan="' + pmRows.toString() + '" class="periods" scope="row">' + amPmText[1] + '</th>';
528
- }
529
- for (col = 1; col <= hoursPerRow; col++) {
530
- if (showPeriodLabels && row < pmFirstRow && hours[hourCounter] >= 12) {
531
- html += this._generateHTMLHourCell(inst, undefined, showPeriod, showLeadingZero);
532
- } else {
533
- html += this._generateHTMLHourCell(inst, hours[hourCounter], showPeriod, showLeadingZero);
534
- hourCounter++;
535
- }
536
- }
537
- html += '</tr>';
538
- }
539
- html += '</tr></table>' + // Close the hours cells table
540
- '</td>'; // Close the Hour td
541
- }
542
-
543
- if (showMinutes) {
544
- html += '<td class="ui-timepicker-minutes">';
545
- html += this._generateHTMLMinutes(inst);
546
- html += '</td>';
547
- }
548
-
549
- html += '</tr></table>';
550
-
551
- /* IE6 IFRAME FIX (taken from datepicker 1.5.3, fixed in 0.1.2 */
552
- html += ($.browser.msie && parseInt($.browser.version,10) < 7 && !inst.inline ?
553
- '<iframe src="javascript:false;" class="ui-timepicker-cover" frameborder="0"></iframe>' : '');
554
-
555
- return html;
556
- },
557
-
558
- /* Special function that update the minutes selection in currently visible timepicker
559
- * called on hour selection when onMinuteShow is defined */
560
- _updateMinuteDisplay: function (inst) {
561
- var newHtml = this._generateHTMLMinutes(inst);
562
- inst.tpDiv.find('td.ui-timepicker-minutes').html(newHtml);
563
- this._rebindDialogEvents(inst);
564
- // after the picker html is appended bind the click & double click events (faster in IE this way
565
- // then letting the browser interpret the inline events)
566
- // yes I know, duplicate code, sorry
567
- /* .find('.ui-timepicker-minute-cell')
568
- .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectMinutes, this))
569
- .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectMinutes, this));
570
- */
571
-
572
- },
573
-
574
- /*
575
- * Generate the minutes table
576
- * This is separated from the _generateHTML function because is can be called separately (when hours changes)
577
- */
578
- _generateHTMLMinutes: function (inst) {
579
-
580
- var m, row, html = '',
581
- rows = this._get(inst, 'rows'),
582
- minutes = Array(),
583
- minutes_options = this._get(inst, 'minutes'),
584
- minutesPerRow = null,
585
- minuteCounter = 0,
586
- showMinutesLeadingZero = (this._get(inst, 'showMinutesLeadingZero') == true),
587
- onMinuteShow = this._get(inst, 'onMinuteShow'),
588
- minuteLabel = this._get(inst, 'minuteText');
589
-
590
- if ( ! minutes_options.starts) {
591
- minutes_options.starts = 0;
592
- }
593
- if ( ! minutes_options.ends) {
594
- minutes_options.ends = 59;
595
- }
596
- for (m = minutes_options.starts; m <= minutes_options.ends; m += minutes_options.interval) {
597
- minutes.push(m);
598
- }
599
- minutesPerRow = Math.round(minutes.length / rows + 0.49); // always round up
600
-
601
- /*
602
- * The minutes table
603
- */
604
- // if currently selected minute is not enabled, we have a problem and need to select a new minute.
605
- if (onMinuteShow &&
606
- (onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours , inst.minutes]) == false) ) {
607
- // loop minutes and select first available
608
- for (minuteCounter = 0; minuteCounter < minutes.length; minuteCounter += 1) {
609
- m = minutes[minuteCounter];
610
- if (onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours, m])) {
611
- inst.minutes = m;
612
- break;
613
- }
614
- }
615
- }
616
-
617
-
618
-
619
- html += '<div class="ui-timepicker-title ui-widget-header ui-helper-clearfix ui-corner-all">' +
620
- minuteLabel +
621
- '</div>' +
622
- '<table class="ui-timepicker">';
623
-
624
- minuteCounter = 0;
625
- for (row = 1; row <= rows; row++) {
626
- html += '<tr>';
627
- while (minuteCounter < row * minutesPerRow) {
628
- var m = minutes[minuteCounter];
629
- var displayText = '';
630
- if (m !== undefined ) {
631
- displayText = (m < 10) && showMinutesLeadingZero ? "0" + m.toString() : m.toString();
632
- }
633
- html += this._generateHTMLMinuteCell(inst, m, displayText);
634
- minuteCounter++;
635
- }
636
- html += '</tr>';
637
- }
638
-
639
- html += '</table>';
640
-
641
- return html;
642
- },
643
-
644
- /* Generate the content of a "Hour" cell */
645
- _generateHTMLHourCell: function (inst, hour, showPeriod, showLeadingZero) {
646
-
647
- var displayHour = hour;
648
- if ((hour > 12) && showPeriod) {
649
- displayHour = hour - 12;
650
- }
651
- if ((displayHour == 0) && showPeriod) {
652
- displayHour = 12;
653
- }
654
- if ((displayHour < 10) && showLeadingZero) {
655
- displayHour = '0' + displayHour;
656
- }
657
-
658
- var html = "";
659
- var enabled = true;
660
- var onHourShow = this._get(inst, 'onHourShow'); //custom callback
661
-
662
- if (hour == undefined) {
663
- html = '<td><span class="ui-state-default ui-state-disabled">&nbsp;</span></td>';
664
- return html;
665
- }
666
-
667
- if (onHourShow) {
668
- enabled = onHourShow.apply((inst.input ? inst.input[0] : null), [hour]);
669
- }
670
-
671
- if (enabled) {
672
- html = '<td class="ui-timepicker-hour-cell" data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" data-hour="' + hour.toString() + '">' +
673
- '<a class="ui-state-default ' +
674
- (hour == inst.hours ? 'ui-state-active' : '') +
675
- '">' +
676
- displayHour.toString() +
677
- '</a></td>';
678
- }
679
- else {
680
- html =
681
- '<td>' +
682
- '<span class="ui-state-default ui-state-disabled ' +
683
- (hour == inst.hours ? ' ui-state-active ' : ' ') +
684
- '">' +
685
- displayHour.toString() +
686
- '</span>' +
687
- '</td>';
688
- }
689
- return html;
690
- },
691
-
692
- /* Generate the content of a "Hour" cell */
693
- _generateHTMLMinuteCell: function (inst, minute, displayText) {
694
- var html = "";
695
- var enabled = true;
696
- var onMinuteShow = this._get(inst, 'onMinuteShow'); //custom callback
697
- if (onMinuteShow) {
698
- //NEW: 2011-02-03 we should give the hour as a parameter as well!
699
- enabled = onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours,minute]); //trigger callback
700
- }
701
-
702
- if (minute == undefined) {
703
- html = '<td><span class="ui-state-default ui-state-disabled">&nbsp;</span></td>';
704
- return html;
705
- }
706
-
707
- if (enabled) {
708
- html = '<td class="ui-timepicker-minute-cell" data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" data-minute="' + minute.toString() + '" >' +
709
- '<a class="ui-state-default ' +
710
- (minute == inst.minutes ? 'ui-state-active' : '') +
711
- '" >' +
712
- displayText +
713
- '</a></td>';
714
- }
715
- else {
716
-
717
- html = '<td>' +
718
- '<span class="ui-state-default ui-state-disabled" >' +
719
- displayText +
720
- '</span>' +
721
- '</td>';
722
- }
723
- return html;
724
- },
725
-
726
-
727
- /* Enable the date picker to a jQuery selection.
728
- @param target element - the target input field or division or span */
729
- _enableTimepicker: function(target) {
730
- var $target = $(target),
731
- target_id = $target.attr('id'),
732
- inst = $.data(target, PROP_NAME);
733
-
734
- if (!$target.hasClass(this.markerClassName)) {
735
- return;
736
- }
737
- var nodeName = target.nodeName.toLowerCase();
738
- if (nodeName == 'input') {
739
- target.disabled = false;
740
- inst.trigger.filter('button').
741
- each(function() { this.disabled = false; }).end();
742
- }
743
- else if (nodeName == 'div' || nodeName == 'span') {
744
- var inline = $target.children('.' + this._inlineClass);
745
- inline.children().removeClass('ui-state-disabled');
746
- }
747
- this._disabledInputs = $.map(this._disabledInputs,
748
- function(value) { return (value == target_id ? null : value); }); // delete entry
749
- },
750
-
751
- /* Disable the time picker to a jQuery selection.
752
- @param target element - the target input field or division or span */
753
- _disableTimepicker: function(target) {
754
- var $target = $(target);
755
- var inst = $.data(target, PROP_NAME);
756
- if (!$target.hasClass(this.markerClassName)) {
757
- return;
758
- }
759
- var nodeName = target.nodeName.toLowerCase();
760
- if (nodeName == 'input') {
761
- target.disabled = true;
762
-
763
- inst.trigger.filter('button').
764
- each(function() { this.disabled = true; }).end();
765
-
766
- }
767
- else if (nodeName == 'div' || nodeName == 'span') {
768
- var inline = $target.children('.' + this._inlineClass);
769
- inline.children().addClass('ui-state-disabled');
770
- }
771
- this._disabledInputs = $.map(this._disabledInputs,
772
- function(value) { return (value == target ? null : value); }); // delete entry
773
- this._disabledInputs[this._disabledInputs.length] = $target.attr('id');
774
- },
775
-
776
- /* Is the first field in a jQuery collection disabled as a timepicker?
777
- @param target_id element - the target input field or division or span
778
- @return boolean - true if disabled, false if enabled */
779
- _isDisabledTimepicker: function (target_id) {
780
- if ( ! target_id) { return false; }
781
- for (var i = 0; i < this._disabledInputs.length; i++) {
782
- if (this._disabledInputs[i] == target_id) { return true; }
783
- }
784
- return false;
785
- },
786
-
787
- /* Check positioning to remain on screen. */
788
- _checkOffset: function (inst, offset, isFixed) {
789
- var tpWidth = inst.tpDiv.outerWidth();
790
- var tpHeight = inst.tpDiv.outerHeight();
791
- var inputWidth = inst.input ? inst.input.outerWidth() : 0;
792
- var inputHeight = inst.input ? inst.input.outerHeight() : 0;
793
- var viewWidth = document.documentElement.clientWidth + $(document).scrollLeft();
794
- var viewHeight = document.documentElement.clientHeight + $(document).scrollTop();
795
-
796
- offset.left -= (this._get(inst, 'isRTL') ? (tpWidth - inputWidth) : 0);
797
- offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0;
798
- offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
799
-
800
- // now check if datepicker is showing outside window viewport - move to a better place if so.
801
- offset.left -= Math.min(offset.left, (offset.left + tpWidth > viewWidth && viewWidth > tpWidth) ?
802
- Math.abs(offset.left + tpWidth - viewWidth) : 0);
803
- offset.top -= Math.min(offset.top, (offset.top + tpHeight > viewHeight && viewHeight > tpHeight) ?
804
- Math.abs(tpHeight + inputHeight) : 0);
805
-
806
- return offset;
807
- },
808
-
809
- /* Find an object's position on the screen. */
810
- _findPos: function (obj) {
811
- var inst = this._getInst(obj);
812
- var isRTL = this._get(inst, 'isRTL');
813
- while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) {
814
- obj = obj[isRTL ? 'previousSibling' : 'nextSibling'];
815
- }
816
- var position = $(obj).offset();
817
- return [position.left, position.top];
818
- },
819
-
820
- /* Retrieve the size of left and top borders for an element.
821
- @param elem (jQuery object) the element of interest
822
- @return (number[2]) the left and top borders */
823
- _getBorders: function (elem) {
824
- var convert = function (value) {
825
- return { thin: 1, medium: 2, thick: 3}[value] || value;
826
- };
827
- return [parseFloat(convert(elem.css('border-left-width'))),
828
- parseFloat(convert(elem.css('border-top-width')))];
829
- },
830
-
831
-
832
- /* Close time picker if clicked elsewhere. */
833
- _checkExternalClick: function (event) {
834
- if (!$.timepicker._curInst) { return; }
835
- var $target = $(event.target);
836
- if ($target[0].id != $.timepicker._mainDivId &&
837
- $target.parents('#' + $.timepicker._mainDivId).length == 0 &&
838
- !$target.hasClass($.timepicker.markerClassName) &&
839
- !$target.hasClass($.timepicker._triggerClass) &&
840
- $.timepicker._timepickerShowing && !($.timepicker._inDialog && $.blockUI))
841
- $.timepicker._hideTimepicker();
842
- },
843
-
844
- /* Hide the time picker from view.
845
- @param input element - the input field attached to the time picker */
846
- _hideTimepicker: function (input) {
847
- var inst = this._curInst;
848
- if (!inst || (input && inst != $.data(input, PROP_NAME))) { return; }
849
- if (this._timepickerShowing) {
850
- var showAnim = this._get(inst, 'showAnim');
851
- var duration = this._get(inst, 'duration');
852
- var postProcess = function () {
853
- $.timepicker._tidyDialog(inst);
854
- this._curInst = null;
855
- };
856
- if ($.effects && $.effects[showAnim]) {
857
- inst.tpDiv.hide(showAnim, $.timepicker._get(inst, 'showOptions'), duration, postProcess);
858
- }
859
- else {
860
- inst.tpDiv[(showAnim == 'slideDown' ? 'slideUp' :
861
- (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess);
862
- }
863
- if (!showAnim) { postProcess(); }
864
- var onClose = this._get(inst, 'onClose');
865
- if (onClose) {
866
- onClose.apply(
867
- (inst.input ? inst.input[0] : null),
868
- [(inst.input ? inst.input.val() : ''), inst]); // trigger custom callback
869
- }
870
- this._timepickerShowing = false;
871
- this._lastInput = null;
872
- if (this._inDialog) {
873
- this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
874
- if ($.blockUI) {
875
- $.unblockUI();
876
- $('body').append(this.tpDiv);
877
- }
878
- }
879
- this._inDialog = false;
880
- }
881
- },
882
-
883
- /* Tidy up after a dialog display. */
884
- _tidyDialog: function (inst) {
885
- inst.tpDiv.removeClass(this._dialogClass).unbind('.ui-timepicker');
886
- },
887
-
888
- /* Retrieve the instance data for the target control.
889
- @param target element - the target input field or division or span
890
- @return object - the associated instance data
891
- @throws error if a jQuery problem getting data */
892
- _getInst: function (target) {
893
- try {
894
- return $.data(target, PROP_NAME);
895
- }
896
- catch (err) {
897
- throw 'Missing instance data for this timepicker';
898
- }
899
- },
900
-
901
- /* Get a setting value, defaulting if necessary. */
902
- _get: function (inst, name) {
903
- return inst.settings[name] !== undefined ?
904
- inst.settings[name] : this._defaults[name];
905
- },
906
-
907
- /* Parse existing time and initialise time picker. */
908
- _setTimeFromField: function (inst) {
909
- if (inst.input.val() == inst.lastVal) { return; }
910
- var defaultTime = this._get(inst, 'defaultTime');
911
-
912
- var timeToParse = defaultTime == 'now' ? this._getCurrentTimeRounded(inst) : defaultTime;
913
- if ((inst.inline == false) && (inst.input.val() != '')) { timeToParse = inst.input.val() }
914
-
915
- var timeVal = inst.lastVal = timeToParse;
916
-
917
-
918
-
919
- if (timeToParse == '') {
920
- inst.hours = -1;
921
- inst.minutes = -1;
922
- } else {
923
- var time = this.parseTime(inst, timeVal);
924
- inst.hours = time.hours;
925
- inst.minutes = time.minutes;
926
- }
927
-
928
- $.timepicker._updateTimepicker(inst);
929
- },
930
- /* Set the dates for a jQuery selection.
931
- @param target element - the target input field or division or span
932
- @param date Date - the new date */
933
- _setTimeTimepicker: function(target, time) {
934
- var inst = this._getInst(target);
935
- if (inst) {
936
- this._setTime(inst, time);
937
- this._updateTimepicker(inst);
938
- this._updateAlternate(inst, time);
939
- }
940
- },
941
-
942
- /* Set the time directly. */
943
- _setTime: function(inst, time, noChange) {
944
- var origHours = inst.hours;
945
- var origMinutes = inst.minutes;
946
- var time = this.parseTime(inst, time);
947
- inst.hours = time.hours;
948
- inst.minutes = time.minutes;
949
-
950
- if ((origHours != inst.hours || origMinutes != inst.minuts) && !noChange) {
951
- inst.input.trigger('change');
952
- }
953
- this._updateTimepicker(inst);
954
- this._updateSelectedValue(inst);
955
- },
956
-
957
- /* Return the current time, ready to be parsed, rounded to the closest 5 minute */
958
- _getCurrentTimeRounded: function (inst) {
959
- var currentTime = new Date();
960
- var timeSeparator = this._get(inst, 'timeSeparator');
961
- // setting selected time , least priority first
962
- var currentMinutes = currentTime.getMinutes()
963
- // round to closest 5
964
- currentMinutes = Math.round( currentMinutes / 5 ) * 5;
965
-
966
- return currentTime.getHours().toString() + timeSeparator + currentMinutes.toString();
967
- },
968
-
969
- /*
970
- * Pase a time string into hours and minutes
971
- */
972
- parseTime: function (inst, timeVal) {
973
- var retVal = new Object();
974
- retVal.hours = -1;
975
- retVal.minutes = -1;
976
-
977
- var timeSeparator = this._get(inst, 'timeSeparator'),
978
- amPmText = this._get(inst, 'amPmText'),
979
- showHours = this._get(inst, 'showHours'),
980
- showMinutes = this._get(inst, 'showMinutes'),
981
- showPeriod = (this._get(inst, 'showPeriod') == true),
982
- p = timeVal.indexOf(timeSeparator);
983
-
984
- // check if time separator found
985
- if (p != -1) {
986
- retVal.hours = parseInt(timeVal.substr(0, p), 10);
987
- retVal.minutes = parseInt(timeVal.substr(p + 1), 10);
988
- }
989
- // check for hours only
990
- else if ( (showHours) && ( ! showMinutes) ) {
991
- retVal.hours = parseInt(timeVal, 10);
992
- }
993
- // check for minutes only
994
- else if ( ( ! showHours) && (showMinutes) ) {
995
- retVal.minutes = parseInt(timeVal, 10);
996
- }
997
-
998
- if (showHours) {
999
- var timeValUpper = timeVal.toUpperCase();
1000
- if ((retVal.hours < 12) && (showPeriod) && (timeValUpper.indexOf(amPmText[1].toUpperCase()) != -1)) {
1001
- retVal.hours += 12;
1002
- }
1003
- // fix for 12 AM
1004
- if ((retVal.hours == 12) && (showPeriod) && (timeValUpper.indexOf(amPmText[0].toUpperCase()) != -1)) {
1005
- retVal.hours = 0;
1006
- }
1007
- }
1008
-
1009
- return retVal;
1010
- },
1011
-
1012
-
1013
-
1014
- selectHours: function (event) {
1015
- var $td = $(event.currentTarget),
1016
- id = $td.attr("data-timepicker-instance-id"),
1017
- newHours = $td.attr("data-hour"),
1018
- fromDoubleClick = event.data.fromDoubleClick,
1019
- $target = $(id),
1020
- inst = this._getInst($target[0]),
1021
- showMinutes = (this._get(inst, 'showMinutes') == true);
1022
-
1023
- // don't select if disabled
1024
- if ( $.timepicker._isDisabledTimepicker($target.attr('id')) ) { return false }
1025
-
1026
- $td.parents('.ui-timepicker-hours:first').find('a').removeClass('ui-state-active');
1027
- $td.children('a').addClass('ui-state-active');
1028
- inst.hours = newHours;
1029
-
1030
- // added for onMinuteShow callback
1031
- var onMinuteShow = this._get(inst, 'onMinuteShow');
1032
- if (onMinuteShow) {
1033
- // this will trigger a callback on selected hour to make sure selected minute is allowed.
1034
- this._updateMinuteDisplay(inst);
1035
- }
1036
-
1037
- this._updateSelectedValue(inst);
1038
-
1039
- inst._hoursClicked = true;
1040
- if ((inst._minutesClicked) || (fromDoubleClick) || (showMinutes == false)) {
1041
- $.timepicker._hideTimepicker();
1042
- }
1043
- // return false because if used inline, prevent the url to change to a hashtag
1044
- return false;
1045
- },
1046
-
1047
- selectMinutes: function (event) {
1048
- var $td = $(event.currentTarget),
1049
- id = $td.attr("data-timepicker-instance-id"),
1050
- newMinutes = $td.attr("data-minute"),
1051
- fromDoubleClick = event.data.fromDoubleClick,
1052
- $target = $(id),
1053
- inst = this._getInst($target[0]),
1054
- showHours = (this._get(inst, 'showHours') == true);
1055
-
1056
- // don't select if disabled
1057
- if ( $.timepicker._isDisabledTimepicker($target.attr('id')) ) { return false }
1058
-
1059
- $td.parents('.ui-timepicker-minutes:first').find('a').removeClass('ui-state-active');
1060
- $td.children('a').addClass('ui-state-active');
1061
-
1062
- inst.minutes = newMinutes;
1063
- this._updateSelectedValue(inst);
1064
-
1065
- inst._minutesClicked = true;
1066
- if ((inst._hoursClicked) || (fromDoubleClick) || (showHours == false)) {
1067
- $.timepicker._hideTimepicker();
1068
- // return false because if used inline, prevent the url to change to a hashtag
1069
- return false;
1070
- }
1071
-
1072
- // return false because if used inline, prevent the url to change to a hashtag
1073
- return false;
1074
- },
1075
-
1076
- _updateSelectedValue: function (inst) {
1077
- var newTime = this._getParsedTime(inst);
1078
- if (inst.input) {
1079
- inst.input.val(newTime);
1080
- inst.input.trigger('change');
1081
- }
1082
- var onSelect = this._get(inst, 'onSelect');
1083
- if (onSelect) { onSelect.apply((inst.input ? inst.input[0] : null), [newTime, inst]); } // trigger custom callback
1084
- this._updateAlternate(inst, newTime);
1085
- return newTime;
1086
- },
1087
-
1088
- /* this function process selected time and return it parsed according to instance options */
1089
- _getParsedTime: function(inst) {
1090
-
1091
- if ((inst.hours < 0) || (inst.hours > 23)) { inst.hours = 12; }
1092
- if ((inst.minutes < 0) || (inst.minutes > 59)) { inst.minutes = 0; }
1093
-
1094
- var period = "",
1095
- showPeriod = (this._get(inst, 'showPeriod') == true),
1096
- showLeadingZero = (this._get(inst, 'showLeadingZero') == true),
1097
- showHours = (this._get(inst, 'showHours') == true),
1098
- showMinutes = (this._get(inst, 'showMinutes') == true),
1099
- amPmText = this._get(inst, 'amPmText'),
1100
- selectedHours = inst.hours ? inst.hours : 0,
1101
- selectedMinutes = inst.minutes ? inst.minutes : 0,
1102
- displayHours = selectedHours ? selectedHours : 0,
1103
- parsedTime = '';
1104
-
1105
- if (showPeriod) {
1106
- if (inst.hours == 0) {
1107
- displayHours = 12;
1108
- }
1109
- if (inst.hours < 12) {
1110
- period = amPmText[0];
1111
- }
1112
- else {
1113
- period = amPmText[1];
1114
- if (displayHours > 12) {
1115
- displayHours -= 12;
1116
- }
1117
- }
1118
- }
1119
-
1120
- var h = displayHours.toString();
1121
- if (showLeadingZero && (displayHours < 10)) { h = '0' + h; }
1122
-
1123
- var m = selectedMinutes.toString();
1124
- if (selectedMinutes < 10) { m = '0' + m; }
1125
-
1126
- if (showHours) {
1127
- parsedTime += h;
1128
- }
1129
- if (showHours && showMinutes) {
1130
- parsedTime += this._get(inst, 'timeSeparator');
1131
- }
1132
- if (showMinutes) {
1133
- parsedTime += m;
1134
- }
1135
- if (showHours) {
1136
- if (period.length > 0) { parsedTime += this._get(inst, 'periodSeparator') + period; }
1137
- }
1138
-
1139
- return parsedTime;
1140
- },
1141
-
1142
- /* Update any alternate field to synchronise with the main field. */
1143
- _updateAlternate: function(inst, newTime) {
1144
- var altField = this._get(inst, 'altField');
1145
- if (altField) { // update alternate field too
1146
- $(altField).each(function(i,e) {
1147
- $(e).val(newTime);
1148
- });
1149
- }
1150
- },
1151
-
1152
- /* This might look unused but it's called by the $.fn.timepicker function with param getTime */
1153
- /* added v 0.2.3 - gitHub issue #5 - Thanks edanuff */
1154
- _getTimeTimepicker : function(input) {
1155
- var inst = this._getInst(input);
1156
- return this._getParsedTime(inst);
1157
- },
1158
- _getHourTimepicker: function(input) {
1159
- var inst = this._getInst(input);
1160
- if ( inst == undefined) { return -1; }
1161
- return inst.hours;
1162
- },
1163
- _getMinuteTimepicker: function(input) {
1164
- var inst= this._getInst(input);
1165
- if ( inst == undefined) { return -1; }
1166
- return inst.minutes;
1167
- }
1168
-
1169
- });
1170
-
1171
-
1172
-
1173
- /* Invoke the timepicker functionality.
1174
- @param options string - a command, optionally followed by additional parameters or
1175
- Object - settings for attaching new timepicker functionality
1176
- @return jQuery object */
1177
- $.fn.timepicker = function (options) {
1178
-
1179
- /* Initialise the date picker. */
1180
- if (!$.timepicker.initialized) {
1181
- $(document).mousedown($.timepicker._checkExternalClick).
1182
- find('body').append($.timepicker.tpDiv);
1183
- $.timepicker.initialized = true;
1184
- }
1185
-
1186
- var otherArgs = Array.prototype.slice.call(arguments, 1);
1187
- if (typeof options == 'string' && (options == 'getTime' || options == 'getHour' || options == 'getMinute' ))
1188
- return $.timepicker['_' + options + 'Timepicker'].
1189
- apply($.timepicker, [this[0]].concat(otherArgs));
1190
- if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string')
1191
- return $.timepicker['_' + options + 'Timepicker'].
1192
- apply($.timepicker, [this[0]].concat(otherArgs));
1193
- return this.each(function () {
1194
- typeof options == 'string' ?
1195
- $.timepicker['_' + options + 'Timepicker'].
1196
- apply($.timepicker, [this].concat(otherArgs)) :
1197
- $.timepicker._attachTimepicker(this, options);
1198
- });
1199
- };
1200
-
1201
- /* jQuery extend now ignores nulls! */
1202
- function extendRemove(target, props) {
1203
- $.extend(target, props);
1204
- for (var name in props)
1205
- if (props[name] == null || props[name] == undefined)
1206
- target[name] = props[name];
1207
- return target;
1208
- };
1209
-
1210
- $.timepicker = new Timepicker(); // singleton instance
1211
- $.timepicker.initialized = false;
1212
- $.timepicker.uuid = new Date().getTime();
1213
- $.timepicker.version = "0.2.5";
1214
-
1215
- // Workaround for #4055
1216
- // Add another global to avoid noConflict issues with inline event handlers
1217
- window['TP_jQuery_' + tpuuid] = $;
1218
-
1219
- })(jQuery);
1
+ /*
2
+ * jQuery UI Timepicker 0.2.9
3
+ *
4
+ * Copyright 2010-2011, Francois Gelinas
5
+ * Dual licensed under the MIT or GPL Version 2 licenses.
6
+ * http://jquery.org/license
7
+ *
8
+ * http://fgelinas.com/code/timepicker
9
+ *
10
+ * Depends:
11
+ * jquery.ui.core.js
12
+ * jquery.ui.position.js (only if position settngs are used)
13
+ *
14
+ * Change version 0.1.0 - moved the t-rex up here
15
+ *
16
+ ____
17
+ ___ .-~. /_"-._
18
+ `-._~-. / /_ "~o\ :Y
19
+ \ \ / : \~x. ` ')
20
+ ] Y / | Y< ~-.__j
21
+ / ! _.--~T : l l< /.-~
22
+ / / ____.--~ . ` l /~\ \<|Y
23
+ / / .-~~" /| . ',-~\ \L|
24
+ / / / .^ \ Y~Y \.^>/l_ "--'
25
+ / Y .-"( . l__ j_j l_/ /~_.-~ .
26
+ Y l / \ ) ~~~." / `/"~ / \.__/l_
27
+ | \ _.-" ~-{__ l : l._Z~-.___.--~
28
+ | ~---~ / ~~"---\_ ' __[>
29
+ l . _.^ ___ _>-y~
30
+ \ \ . .-~ .-~ ~>--" /
31
+ \ ~---" / ./ _.-'
32
+ "-.,_____.,_ _.--~\ _.-~
33
+ ~~ ( _} -Row
34
+ `. ~(
35
+ ) \
36
+ /,`--'~\--'~\
37
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
38
+ ->T-Rex<-
39
+ */
40
+
41
+ (function ($, undefined) {
42
+
43
+ $.extend($.ui, { timepicker: { version: "0.2.9"} });
44
+
45
+ var PROP_NAME = 'timepicker';
46
+ var tpuuid = new Date().getTime();
47
+
48
+ /* Time picker manager.
49
+ Use the singleton instance of this class, $.timepicker, to interact with the time picker.
50
+ Settings for (groups of) time pickers are maintained in an instance object,
51
+ allowing multiple different settings on the same page. */
52
+
53
+ function Timepicker() {
54
+ this.debug = true; // Change this to true to start debugging
55
+ this._curInst = null; // The current instance in use
56
+ this._isInline = false; // true if the instance is displayed inline
57
+ this._disabledInputs = []; // List of time picker inputs that have been disabled
58
+ this._timepickerShowing = false; // True if the popup picker is showing , false if not
59
+ this._inDialog = false; // True if showing within a "dialog", false if not
60
+ this._dialogClass = 'ui-timepicker-dialog'; // The name of the dialog marker class
61
+ this._mainDivId = 'ui-timepicker-div'; // The ID of the main timepicker division
62
+ this._inlineClass = 'ui-timepicker-inline'; // The name of the inline marker class
63
+ this._currentClass = 'ui-timepicker-current'; // The name of the current hour / minutes marker class
64
+ this._dayOverClass = 'ui-timepicker-days-cell-over'; // The name of the day hover marker class
65
+
66
+ this.regional = []; // Available regional settings, indexed by language code
67
+ this.regional[''] = { // Default regional settings
68
+ hourText: 'Hour', // Display text for hours section
69
+ minuteText: 'Minute', // Display text for minutes link
70
+ amPmText: ['AM', 'PM'], // Display text for AM PM
71
+ closeButtonText: 'Done', // Text for the confirmation button (ok button)
72
+ nowButtonText: 'Now', // Text for the now button
73
+ deselectButtonText: 'Deselect' // Text for the deselect button
74
+ };
75
+ this._defaults = { // Global defaults for all the time picker instances
76
+ showOn: 'focus', // 'focus' for popup on focus,
77
+ // 'button' for trigger button, or 'both' for either (not yet implemented)
78
+ button: null, // 'button' element that will trigger the timepicker
79
+ showAnim: 'fadeIn', // Name of jQuery animation for popup
80
+ showOptions: {}, // Options for enhanced animations
81
+ appendText: '', // Display text following the input box, e.g. showing the format
82
+
83
+ beforeShow: null, // Define a callback function executed before the timepicker is shown
84
+ onSelect: null, // Define a callback function when a hour / minutes is selected
85
+ onClose: null, // Define a callback function when the timepicker is closed
86
+
87
+ timeSeparator: ':', // The character to use to separate hours and minutes.
88
+ periodSeparator: ' ', // The character to use to separate the time from the time period.
89
+ showPeriod: false, // Define whether or not to show AM/PM with selected time
90
+ showPeriodLabels: true, // Show the AM/PM labels on the left of the time picker
91
+ showLeadingZero: true, // Define whether or not to show a leading zero for hours < 10. [true/false]
92
+ showMinutesLeadingZero: true, // Define whether or not to show a leading zero for minutes < 10.
93
+ altField: '', // Selector for an alternate field to store selected time into
94
+ defaultTime: 'now', // Used as default time when input field is empty or for inline timePicker
95
+ // (set to 'now' for the current time, '' for no highlighted time)
96
+ myPosition: 'left top', // Position of the dialog relative to the input.
97
+ // see the position utility for more info : http://jqueryui.com/demos/position/
98
+ atPosition: 'left bottom', // Position of the input element to match
99
+ // Note : if the position utility is not loaded, the timepicker will attach left top to left bottom
100
+ //NEW: 2011-02-03
101
+ onHourShow: null, // callback for enabling / disabling on selectable hours ex : function(hour) { return true; }
102
+ onMinuteShow: null, // callback for enabling / disabling on time selection ex : function(hour,minute) { return true; }
103
+
104
+ hours: {
105
+ starts: 0, // first displayed hour
106
+ ends: 23 // last displayed hour
107
+ },
108
+ minutes: {
109
+ starts: 0, // first displayed minute
110
+ ends: 55, // last displayed minute
111
+ interval: 5 // interval of displayed minutes
112
+ },
113
+ rows: 4, // number of rows for the input tables, minimum 2, makes more sense if you use multiple of 2
114
+ // 2011-08-05 0.2.4
115
+ showHours: true, // display the hours section of the dialog
116
+ showMinutes: true, // display the minute section of the dialog
117
+ optionalMinutes: false, // optionally parse inputs of whole hours with minutes omitted
118
+
119
+ // buttons
120
+ showCloseButton: false, // shows an OK button to confirm the edit
121
+ showNowButton: false, // Shows the 'now' button
122
+ showDeselectButton: false // Shows the deselect time button
123
+
124
+ };
125
+ $.extend(this._defaults, this.regional['']);
126
+
127
+ this.tpDiv = $('<div id="' + this._mainDivId + '" class="ui-timepicker ui-widget ui-helper-clearfix ui-corner-all " style="display: none"></div>');
128
+ }
129
+
130
+ $.extend(Timepicker.prototype, {
131
+ /* Class name added to elements to indicate already configured with a time picker. */
132
+ markerClassName: 'hasTimepicker',
133
+
134
+ /* Debug logging (if enabled). */
135
+ log: function () {
136
+ if (this.debug)
137
+ console.log.apply('', arguments);
138
+ },
139
+
140
+ _widgetTimepicker: function () {
141
+ return this.tpDiv;
142
+ },
143
+
144
+ /* Override the default settings for all instances of the time picker.
145
+ @param settings object - the new settings to use as defaults (anonymous object)
146
+ @return the manager object */
147
+ setDefaults: function (settings) {
148
+ extendRemove(this._defaults, settings || {});
149
+ return this;
150
+ },
151
+
152
+ /* Attach the time picker to a jQuery selection.
153
+ @param target element - the target input field or division or span
154
+ @param settings object - the new settings to use for this time picker instance (anonymous) */
155
+ _attachTimepicker: function (target, settings) {
156
+ // check for settings on the control itself - in namespace 'time:'
157
+ var inlineSettings = null;
158
+ for (var attrName in this._defaults) {
159
+ var attrValue = target.getAttribute('time:' + attrName);
160
+ if (attrValue) {
161
+ inlineSettings = inlineSettings || {};
162
+ try {
163
+ inlineSettings[attrName] = eval(attrValue);
164
+ } catch (err) {
165
+ inlineSettings[attrName] = attrValue;
166
+ }
167
+ }
168
+ }
169
+ var nodeName = target.nodeName.toLowerCase();
170
+ var inline = (nodeName == 'div' || nodeName == 'span');
171
+
172
+ if (!target.id) {
173
+ this.uuid += 1;
174
+ target.id = 'tp' + this.uuid;
175
+ }
176
+ var inst = this._newInst($(target), inline);
177
+ inst.settings = $.extend({}, settings || {}, inlineSettings || {});
178
+ if (nodeName == 'input') {
179
+ this._connectTimepicker(target, inst);
180
+ // init inst.hours and inst.minutes from the input value
181
+ this._setTimeFromField(inst);
182
+ } else if (inline) {
183
+ this._inlineTimepicker(target, inst);
184
+ }
185
+
186
+
187
+ },
188
+
189
+ /* Create a new instance object. */
190
+ _newInst: function (target, inline) {
191
+ var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars
192
+ return {
193
+ id: id, input: target, // associated target
194
+ inline: inline, // is timepicker inline or not :
195
+ tpDiv: (!inline ? this.tpDiv : // presentation div
196
+ $('<div class="' + this._inlineClass + ' ui-timepicker ui-widget ui-helper-clearfix"></div>'))
197
+ };
198
+ },
199
+
200
+ /* Attach the time picker to an input field. */
201
+ _connectTimepicker: function (target, inst) {
202
+ var input = $(target);
203
+ inst.append = $([]);
204
+ inst.trigger = $([]);
205
+ if (input.hasClass(this.markerClassName)) { return; }
206
+ this._attachments(input, inst);
207
+ input.addClass(this.markerClassName).
208
+ keydown(this._doKeyDown).
209
+ keyup(this._doKeyUp).
210
+ bind("setData.timepicker", function (event, key, value) {
211
+ inst.settings[key] = value;
212
+ }).
213
+ bind("getData.timepicker", function (event, key) {
214
+ return this._get(inst, key);
215
+ });
216
+ $.data(target, PROP_NAME, inst);
217
+ },
218
+
219
+ /* Handle keystrokes. */
220
+ _doKeyDown: function (event) {
221
+ var inst = $.timepicker._getInst(event.target);
222
+ var handled = true;
223
+ inst._keyEvent = true;
224
+ if ($.timepicker._timepickerShowing) {
225
+ switch (event.keyCode) {
226
+ case 9: $.timepicker._hideTimepicker();
227
+ handled = false;
228
+ break; // hide on tab out
229
+ case 13:
230
+ $.timepicker._updateSelectedValue(inst);
231
+ $.timepicker._hideTimepicker();
232
+
233
+ return false; // don't submit the form
234
+ break; // select the value on enter
235
+ case 27: $.timepicker._hideTimepicker();
236
+ break; // hide on escape
237
+ default: handled = false;
238
+ }
239
+ }
240
+ else if (event.keyCode == 36 && event.ctrlKey) { // display the time picker on ctrl+home
241
+ $.timepicker._showTimepicker(this);
242
+ }
243
+ else {
244
+ handled = false;
245
+ }
246
+ if (handled) {
247
+ event.preventDefault();
248
+ event.stopPropagation();
249
+ }
250
+ },
251
+
252
+ /* Update selected time on keyUp */
253
+ /* Added verion 0.0.5 */
254
+ _doKeyUp: function (event) {
255
+ var inst = $.timepicker._getInst(event.target);
256
+ $.timepicker._setTimeFromField(inst);
257
+ $.timepicker._updateTimepicker(inst);
258
+ },
259
+
260
+ /* Make attachments based on settings. */
261
+ _attachments: function (input, inst) {
262
+ var appendText = this._get(inst, 'appendText');
263
+ var isRTL = this._get(inst, 'isRTL');
264
+ if (inst.append) { inst.append.remove(); }
265
+ if (appendText) {
266
+ inst.append = $('<span class="' + this._appendClass + '">' + appendText + '</span>');
267
+ input[isRTL ? 'before' : 'after'](inst.append);
268
+ }
269
+ input.unbind('focus.timepicker', this._showTimepicker);
270
+ input.unbind('click.timepicker', this._adjustZIndex);
271
+
272
+ if (inst.trigger) { inst.trigger.remove(); }
273
+
274
+ var showOn = this._get(inst, 'showOn');
275
+ if (showOn == 'focus' || showOn == 'both') { // pop-up time picker when in the marked field
276
+ input.bind("focus.timepicker", this._showTimepicker);
277
+ input.bind("click.timepicker", this._adjustZIndex);
278
+ }
279
+ if (showOn == 'button' || showOn == 'both') { // pop-up time picker when 'button' element is clicked
280
+ var button = this._get(inst, 'button');
281
+ $(button).bind("click.timepicker", function () {
282
+ if ($.timepicker._timepickerShowing && $.timepicker._lastInput == input[0]) {
283
+ $.timepicker._hideTimepicker();
284
+ } else if (!inst.input.is(':disabled')) {
285
+ $.timepicker._showTimepicker(input[0]);
286
+ }
287
+ return false;
288
+ });
289
+
290
+ }
291
+ },
292
+
293
+
294
+ /* Attach an inline time picker to a div. */
295
+ _inlineTimepicker: function(target, inst) {
296
+ var divSpan = $(target);
297
+ if (divSpan.hasClass(this.markerClassName))
298
+ return;
299
+ divSpan.addClass(this.markerClassName).append(inst.tpDiv).
300
+ bind("setData.timepicker", function(event, key, value){
301
+ inst.settings[key] = value;
302
+ }).bind("getData.timepicker", function(event, key){
303
+ return this._get(inst, key);
304
+ });
305
+ $.data(target, PROP_NAME, inst);
306
+
307
+ this._setTimeFromField(inst);
308
+ this._updateTimepicker(inst);
309
+ inst.tpDiv.show();
310
+ },
311
+
312
+ _adjustZIndex: function(input) {
313
+ input = input.target || input;
314
+ var inst = $.timepicker._getInst(input);
315
+ inst.tpDiv.css('zIndex', $.timepicker._getZIndex(input) +1);
316
+ },
317
+
318
+ /* Pop-up the time picker for a given input field.
319
+ @param input element - the input field attached to the time picker or
320
+ event - if triggered by focus */
321
+ _showTimepicker: function (input) {
322
+ input = input.target || input;
323
+ if (input.nodeName.toLowerCase() != 'input') { input = $('input', input.parentNode)[0]; } // find from button/image trigger
324
+
325
+ if ($.timepicker._isDisabledTimepicker(input) || $.timepicker._lastInput == input) { return; } // already here
326
+
327
+ // fix v 0.0.8 - close current timepicker before showing another one
328
+ $.timepicker._hideTimepicker();
329
+
330
+ var inst = $.timepicker._getInst(input);
331
+ if ($.timepicker._curInst && $.timepicker._curInst != inst) {
332
+ $.timepicker._curInst.tpDiv.stop(true, true);
333
+ }
334
+ var beforeShow = $.timepicker._get(inst, 'beforeShow');
335
+ extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {}));
336
+ inst.lastVal = null;
337
+ $.timepicker._lastInput = input;
338
+
339
+ $.timepicker._setTimeFromField(inst);
340
+
341
+ // calculate default position
342
+ if ($.timepicker._inDialog) { input.value = ''; } // hide cursor
343
+ if (!$.timepicker._pos) { // position below input
344
+ $.timepicker._pos = $.timepicker._findPos(input);
345
+ $.timepicker._pos[1] += input.offsetHeight; // add the height
346
+ }
347
+ var isFixed = false;
348
+ $(input).parents().each(function () {
349
+ isFixed |= $(this).css('position') == 'fixed';
350
+ return !isFixed;
351
+ });
352
+ if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled
353
+ $.timepicker._pos[0] -= document.documentElement.scrollLeft;
354
+ $.timepicker._pos[1] -= document.documentElement.scrollTop;
355
+ }
356
+
357
+ var offset = { left: $.timepicker._pos[0], top: $.timepicker._pos[1] };
358
+
359
+ $.timepicker._pos = null;
360
+ // determine sizing offscreen
361
+ inst.tpDiv.css({ position: 'absolute', display: 'block', top: '-1000px' });
362
+ $.timepicker._updateTimepicker(inst);
363
+
364
+
365
+ // position with the ui position utility, if loaded
366
+ if ( ( ! inst.inline ) && ( typeof $.ui.position == 'object' ) ) {
367
+ inst.tpDiv.position({
368
+ of: inst.input,
369
+ my: $.timepicker._get( inst, 'myPosition' ),
370
+ at: $.timepicker._get( inst, 'atPosition' ),
371
+ // offset: $( "#offset" ).val(),
372
+ // using: using,
373
+ collision: 'flip'
374
+ });
375
+ var offset = inst.tpDiv.offset();
376
+ $.timepicker._pos = [offset.top, offset.left];
377
+ }
378
+
379
+
380
+ // reset clicked state
381
+ inst._hoursClicked = false;
382
+ inst._minutesClicked = false;
383
+
384
+ // fix width for dynamic number of time pickers
385
+ // and adjust position before showing
386
+ offset = $.timepicker._checkOffset(inst, offset, isFixed);
387
+ inst.tpDiv.css({ position: ($.timepicker._inDialog && $.blockUI ?
388
+ 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
389
+ left: offset.left + 'px', top: offset.top + 'px'
390
+ });
391
+ if ( ! inst.inline ) {
392
+ var showAnim = $.timepicker._get(inst, 'showAnim');
393
+ var duration = $.timepicker._get(inst, 'duration');
394
+
395
+ var postProcess = function () {
396
+ $.timepicker._timepickerShowing = true;
397
+ var borders = $.timepicker._getBorders(inst.tpDiv);
398
+ inst.tpDiv.find('iframe.ui-timepicker-cover'). // IE6- only
399
+ css({ left: -borders[0], top: -borders[1],
400
+ width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight()
401
+ });
402
+ };
403
+
404
+ // Fixed the zIndex problem for real (I hope) - FG - v 0.2.9
405
+ $.timepicker._adjustZIndex(input);
406
+ //inst.tpDiv.css('zIndex', $.timepicker._getZIndex(input) +1);
407
+
408
+ if ($.effects && $.effects[showAnim]) {
409
+ inst.tpDiv.show(showAnim, $.timepicker._get(inst, 'showOptions'), duration, postProcess);
410
+ }
411
+ else {
412
+ inst.tpDiv[showAnim || 'show']((showAnim ? duration : null), postProcess);
413
+ }
414
+ if (!showAnim || !duration) { postProcess(); }
415
+ if (inst.input.is(':visible') && !inst.input.is(':disabled')) { inst.input.focus(); }
416
+ $.timepicker._curInst = inst;
417
+ }
418
+ },
419
+
420
+ // This is a copy of the zIndex function of UI core 1.8.??
421
+ // Copied in the timepicker to stay backward compatible.
422
+ _getZIndex: function (target) {
423
+ var elem = $( target ), position, value;
424
+ while ( elem.length && elem[ 0 ] !== document ) {
425
+ position = elem.css( "position" );
426
+ if ( position === "absolute" || position === "relative" || position === "fixed" ) {
427
+ value = parseInt( elem.css( "zIndex" ), 10 );
428
+ if ( !isNaN( value ) && value !== 0 ) {
429
+ return value;
430
+ }
431
+ }
432
+ elem = elem.parent();
433
+ }
434
+ },
435
+
436
+ /* Generate the time picker content. */
437
+ _updateTimepicker: function (inst) {
438
+ inst.tpDiv.empty().append(this._generateHTML(inst));
439
+ this._rebindDialogEvents(inst);
440
+
441
+ },
442
+
443
+ _rebindDialogEvents: function (inst) {
444
+ var borders = $.timepicker._getBorders(inst.tpDiv),
445
+ self = this;
446
+ inst.tpDiv
447
+ .find('iframe.ui-timepicker-cover') // IE6- only
448
+ .css({ left: -borders[0], top: -borders[1],
449
+ width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight()
450
+ })
451
+ .end()
452
+ // after the picker html is appended bind the click & double click events (faster in IE this way
453
+ // then letting the browser interpret the inline events)
454
+ // the binding for the minute cells also exists in _updateMinuteDisplay
455
+ .find('.ui-timepicker-minute-cell')
456
+ .unbind()
457
+ .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectMinutes, this))
458
+ .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectMinutes, this))
459
+ .end()
460
+ .find('.ui-timepicker-hour-cell')
461
+ .unbind()
462
+ .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectHours, this))
463
+ .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectHours, this))
464
+ .end()
465
+ .find('.ui-timepicker td a')
466
+ .unbind()
467
+ .bind('mouseout', function () {
468
+ $(this).removeClass('ui-state-hover');
469
+ if (this.className.indexOf('ui-timepicker-prev') != -1) $(this).removeClass('ui-timepicker-prev-hover');
470
+ if (this.className.indexOf('ui-timepicker-next') != -1) $(this).removeClass('ui-timepicker-next-hover');
471
+ })
472
+ .bind('mouseover', function () {
473
+ if ( ! self._isDisabledTimepicker(inst.inline ? inst.tpDiv.parent()[0] : inst.input[0])) {
474
+ $(this).parents('.ui-timepicker-calendar').find('a').removeClass('ui-state-hover');
475
+ $(this).addClass('ui-state-hover');
476
+ if (this.className.indexOf('ui-timepicker-prev') != -1) $(this).addClass('ui-timepicker-prev-hover');
477
+ if (this.className.indexOf('ui-timepicker-next') != -1) $(this).addClass('ui-timepicker-next-hover');
478
+ }
479
+ })
480
+ .end()
481
+ .find('.' + this._dayOverClass + ' a')
482
+ .trigger('mouseover')
483
+ .end()
484
+ .find('.ui-timepicker-now').bind("click",function(e) {
485
+ $.timepicker.selectNow(e);
486
+ }).end()
487
+ .find('.ui-timepicker-deselect').bind("click",function(e) {
488
+ $.timepicker.deselectTime(e);
489
+ }).end()
490
+ .find('.ui-timepicker-close').bind("click",function(e) {
491
+ $.timepicker._hideTimepicker();
492
+ }).end();
493
+ },
494
+
495
+ /* Generate the HTML for the current state of the time picker. */
496
+ _generateHTML: function (inst) {
497
+
498
+ var h, m, row, col, html, hoursHtml, minutesHtml = '',
499
+ showPeriod = (this._get(inst, 'showPeriod') == true),
500
+ showPeriodLabels = (this._get(inst, 'showPeriodLabels') == true),
501
+ showLeadingZero = (this._get(inst, 'showLeadingZero') == true),
502
+ showHours = (this._get(inst, 'showHours') == true),
503
+ showMinutes = (this._get(inst, 'showMinutes') == true),
504
+ amPmText = this._get(inst, 'amPmText'),
505
+ rows = this._get(inst, 'rows'),
506
+ amRows = 0,
507
+ pmRows = 0,
508
+ amItems = 0,
509
+ pmItems = 0,
510
+ amFirstRow = 0,
511
+ pmFirstRow = 0,
512
+ hours = Array(),
513
+ hours_options = this._get(inst, 'hours'),
514
+ hoursPerRow = null,
515
+ hourCounter = 0,
516
+ hourLabel = this._get(inst, 'hourText'),
517
+ showCloseButton = this._get(inst, 'showCloseButton'),
518
+ closeButtonText = this._get(inst, 'closeButtonText'),
519
+ showNowButton = this._get(inst, 'showNowButton'),
520
+ nowButtonText = this._get(inst, 'nowButtonText'),
521
+ showDeselectButton = this._get(inst, 'showDeselectButton'),
522
+ deselectButtonText = this._get(inst, 'deselectButtonText'),
523
+ showButtonPanel = showCloseButton || showNowButton || showDeselectButton;
524
+
525
+
526
+
527
+ // prepare all hours and minutes, makes it easier to distribute by rows
528
+ for (h = hours_options.starts; h <= hours_options.ends; h++) {
529
+ hours.push (h);
530
+ }
531
+ hoursPerRow = Math.ceil(hours.length / rows); // always round up
532
+
533
+ if (showPeriodLabels) {
534
+ for (hourCounter = 0; hourCounter < hours.length; hourCounter++) {
535
+ if (hours[hourCounter] < 12) {
536
+ amItems++;
537
+ }
538
+ else {
539
+ pmItems++;
540
+ }
541
+ }
542
+ hourCounter = 0;
543
+
544
+ amRows = Math.floor(amItems / hours.length * rows);
545
+ pmRows = Math.floor(pmItems / hours.length * rows);
546
+
547
+ // assign the extra row to the period that is more densly populated
548
+ if (rows != amRows + pmRows) {
549
+ // Make sure: AM Has Items and either PM Does Not, AM has no rows yet, or AM is more dense
550
+ if (amItems && (!pmItems || !amRows || (pmRows && amItems / amRows >= pmItems / pmRows))) {
551
+ amRows++;
552
+ } else {
553
+ pmRows++;
554
+ }
555
+ }
556
+ amFirstRow = Math.min(amRows, 1);
557
+ pmFirstRow = amRows + 1;
558
+ hoursPerRow = Math.ceil(Math.max(amItems / amRows, pmItems / pmRows));
559
+ }
560
+
561
+
562
+ html = '<table class="ui-timepicker-table ui-widget-content ui-corner-all"><tr>';
563
+
564
+ if (showHours) {
565
+
566
+ html += '<td class="ui-timepicker-hours">' +
567
+ '<div class="ui-timepicker-title ui-widget-header ui-helper-clearfix ui-corner-all">' +
568
+ hourLabel +
569
+ '</div>' +
570
+ '<table class="ui-timepicker">';
571
+
572
+ for (row = 1; row <= rows; row++) {
573
+ html += '<tr>';
574
+ // AM
575
+ if (row == amFirstRow && showPeriodLabels) {
576
+ html += '<th rowspan="' + amRows.toString() + '" class="periods" scope="row">' + amPmText[0] + '</th>';
577
+ }
578
+ // PM
579
+ if (row == pmFirstRow && showPeriodLabels) {
580
+ html += '<th rowspan="' + pmRows.toString() + '" class="periods" scope="row">' + amPmText[1] + '</th>';
581
+ }
582
+ for (col = 1; col <= hoursPerRow; col++) {
583
+ if (showPeriodLabels && row < pmFirstRow && hours[hourCounter] >= 12) {
584
+ html += this._generateHTMLHourCell(inst, undefined, showPeriod, showLeadingZero);
585
+ } else {
586
+ html += this._generateHTMLHourCell(inst, hours[hourCounter], showPeriod, showLeadingZero);
587
+ hourCounter++;
588
+ }
589
+ }
590
+ html += '</tr>';
591
+ }
592
+ html += '</tr></table>' + // Close the hours cells table
593
+ '</td>'; // Close the Hour td
594
+ }
595
+
596
+ if (showMinutes) {
597
+ html += '<td class="ui-timepicker-minutes">';
598
+ html += this._generateHTMLMinutes(inst);
599
+ html += '</td>';
600
+ }
601
+
602
+ html += '</tr>';
603
+
604
+
605
+ if (showButtonPanel) {
606
+ var buttonPanel = '<tr><td colspan="3"><div class="ui-timepicker-buttonpane ui-widget-content">';
607
+ if (showNowButton) {
608
+ buttonPanel += '<button type="button" class="ui-timepicker-now ui-state-default ui-corner-all" '
609
+ + ' data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" >'
610
+ + nowButtonText + '</button>';
611
+ }
612
+ if (showDeselectButton) {
613
+ buttonPanel += '<button type="button" class="ui-timepicker-deselect ui-state-default ui-corner-all" '
614
+ + ' data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" >'
615
+ + deselectButtonText + '</button>';
616
+ }
617
+ if (showCloseButton) {
618
+ buttonPanel += '<button type="button" class="ui-timepicker-close ui-state-default ui-corner-all" '
619
+ + ' data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" >'
620
+ + closeButtonText + '</button>';
621
+ }
622
+
623
+ html += buttonPanel + '</div></td></tr>';
624
+ }
625
+ html += '</table>';
626
+
627
+ /* IE6 IFRAME FIX (taken from datepicker 1.5.3, fixed in 0.1.2 */
628
+ html += ($.browser.msie && parseInt($.browser.version,10) < 7 && !inst.inline ?
629
+ '<iframe src="javascript:false;" class="ui-timepicker-cover" frameborder="0"></iframe>' : '');
630
+
631
+ return html;
632
+ },
633
+
634
+ /* Special function that update the minutes selection in currently visible timepicker
635
+ * called on hour selection when onMinuteShow is defined */
636
+ _updateMinuteDisplay: function (inst) {
637
+ var newHtml = this._generateHTMLMinutes(inst);
638
+ inst.tpDiv.find('td.ui-timepicker-minutes').html(newHtml);
639
+ this._rebindDialogEvents(inst);
640
+ // after the picker html is appended bind the click & double click events (faster in IE this way
641
+ // then letting the browser interpret the inline events)
642
+ // yes I know, duplicate code, sorry
643
+ /* .find('.ui-timepicker-minute-cell')
644
+ .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectMinutes, this))
645
+ .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectMinutes, this));
646
+ */
647
+
648
+ },
649
+
650
+ /*
651
+ * Generate the minutes table
652
+ * This is separated from the _generateHTML function because is can be called separately (when hours changes)
653
+ */
654
+ _generateHTMLMinutes: function (inst) {
655
+
656
+ var m, row, html = '',
657
+ rows = this._get(inst, 'rows'),
658
+ minutes = Array(),
659
+ minutes_options = this._get(inst, 'minutes'),
660
+ minutesPerRow = null,
661
+ minuteCounter = 0,
662
+ showMinutesLeadingZero = (this._get(inst, 'showMinutesLeadingZero') == true),
663
+ onMinuteShow = this._get(inst, 'onMinuteShow'),
664
+ minuteLabel = this._get(inst, 'minuteText');
665
+
666
+ if ( ! minutes_options.starts) {
667
+ minutes_options.starts = 0;
668
+ }
669
+ if ( ! minutes_options.ends) {
670
+ minutes_options.ends = 59;
671
+ }
672
+ for (m = minutes_options.starts; m <= minutes_options.ends; m += minutes_options.interval) {
673
+ minutes.push(m);
674
+ }
675
+ minutesPerRow = Math.round(minutes.length / rows + 0.49); // always round up
676
+
677
+ /*
678
+ * The minutes table
679
+ */
680
+ // if currently selected minute is not enabled, we have a problem and need to select a new minute.
681
+ if (onMinuteShow &&
682
+ (onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours , inst.minutes]) == false) ) {
683
+ // loop minutes and select first available
684
+ for (minuteCounter = 0; minuteCounter < minutes.length; minuteCounter += 1) {
685
+ m = minutes[minuteCounter];
686
+ if (onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours, m])) {
687
+ inst.minutes = m;
688
+ break;
689
+ }
690
+ }
691
+ }
692
+
693
+
694
+
695
+ html += '<div class="ui-timepicker-title ui-widget-header ui-helper-clearfix ui-corner-all">' +
696
+ minuteLabel +
697
+ '</div>' +
698
+ '<table class="ui-timepicker">';
699
+
700
+ minuteCounter = 0;
701
+ for (row = 1; row <= rows; row++) {
702
+ html += '<tr>';
703
+ while (minuteCounter < row * minutesPerRow) {
704
+ var m = minutes[minuteCounter];
705
+ var displayText = '';
706
+ if (m !== undefined ) {
707
+ displayText = (m < 10) && showMinutesLeadingZero ? "0" + m.toString() : m.toString();
708
+ }
709
+ html += this._generateHTMLMinuteCell(inst, m, displayText);
710
+ minuteCounter++;
711
+ }
712
+ html += '</tr>';
713
+ }
714
+
715
+ html += '</table>';
716
+
717
+ return html;
718
+ },
719
+
720
+ /* Generate the content of a "Hour" cell */
721
+ _generateHTMLHourCell: function (inst, hour, showPeriod, showLeadingZero) {
722
+
723
+ var displayHour = hour;
724
+ if ((hour > 12) && showPeriod) {
725
+ displayHour = hour - 12;
726
+ }
727
+ if ((displayHour == 0) && showPeriod) {
728
+ displayHour = 12;
729
+ }
730
+ if ((displayHour < 10) && showLeadingZero) {
731
+ displayHour = '0' + displayHour;
732
+ }
733
+
734
+ var html = "";
735
+ var enabled = true;
736
+ var onHourShow = this._get(inst, 'onHourShow'); //custom callback
737
+
738
+ if (hour == undefined) {
739
+ html = '<td><span class="ui-state-default ui-state-disabled">&nbsp;</span></td>';
740
+ return html;
741
+ }
742
+
743
+ if (onHourShow) {
744
+ enabled = onHourShow.apply((inst.input ? inst.input[0] : null), [hour]);
745
+ }
746
+
747
+ if (enabled) {
748
+ html = '<td class="ui-timepicker-hour-cell" data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" data-hour="' + hour.toString() + '">' +
749
+ '<a class="ui-state-default ' +
750
+ (hour == inst.hours ? 'ui-state-active' : '') +
751
+ '">' +
752
+ displayHour.toString() +
753
+ '</a></td>';
754
+ }
755
+ else {
756
+ html =
757
+ '<td>' +
758
+ '<span class="ui-state-default ui-state-disabled ' +
759
+ (hour == inst.hours ? ' ui-state-active ' : ' ') +
760
+ '">' +
761
+ displayHour.toString() +
762
+ '</span>' +
763
+ '</td>';
764
+ }
765
+ return html;
766
+ },
767
+
768
+ /* Generate the content of a "Hour" cell */
769
+ _generateHTMLMinuteCell: function (inst, minute, displayText) {
770
+ var html = "";
771
+ var enabled = true;
772
+ var onMinuteShow = this._get(inst, 'onMinuteShow'); //custom callback
773
+ if (onMinuteShow) {
774
+ //NEW: 2011-02-03 we should give the hour as a parameter as well!
775
+ enabled = onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours,minute]); //trigger callback
776
+ }
777
+
778
+ if (minute == undefined) {
779
+ html = '<td><span class="ui-state-default ui-state-disabled">&nbsp;</span></td>';
780
+ return html;
781
+ }
782
+
783
+ if (enabled) {
784
+ html = '<td class="ui-timepicker-minute-cell" data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" data-minute="' + minute.toString() + '" >' +
785
+ '<a class="ui-state-default ' +
786
+ (minute == inst.minutes ? 'ui-state-active' : '') +
787
+ '" >' +
788
+ displayText +
789
+ '</a></td>';
790
+ }
791
+ else {
792
+
793
+ html = '<td>' +
794
+ '<span class="ui-state-default ui-state-disabled" >' +
795
+ displayText +
796
+ '</span>' +
797
+ '</td>';
798
+ }
799
+ return html;
800
+ },
801
+
802
+
803
+ /* Enable the date picker to a jQuery selection.
804
+ @param target element - the target input field or division or span */
805
+ _enableTimepicker: function(target) {
806
+ var $target = $(target),
807
+ target_id = $target.attr('id'),
808
+ inst = $.data(target, PROP_NAME);
809
+
810
+ if (!$target.hasClass(this.markerClassName)) {
811
+ return;
812
+ }
813
+ var nodeName = target.nodeName.toLowerCase();
814
+ if (nodeName == 'input') {
815
+ target.disabled = false;
816
+ var button = this._get(inst, 'button');
817
+ $(button).removeClass('ui-state-disabled').disabled = false;
818
+ inst.trigger.filter('button').
819
+ each(function() { this.disabled = false; }).end();
820
+ }
821
+ else if (nodeName == 'div' || nodeName == 'span') {
822
+ var inline = $target.children('.' + this._inlineClass);
823
+ inline.children().removeClass('ui-state-disabled');
824
+ inline.find('button').each(
825
+ function() { this.disabled = false }
826
+ )
827
+ }
828
+ this._disabledInputs = $.map(this._disabledInputs,
829
+ function(value) { return (value == target_id ? null : value); }); // delete entry
830
+ },
831
+
832
+ /* Disable the time picker to a jQuery selection.
833
+ @param target element - the target input field or division or span */
834
+ _disableTimepicker: function(target) {
835
+ var $target = $(target);
836
+ var inst = $.data(target, PROP_NAME);
837
+ if (!$target.hasClass(this.markerClassName)) {
838
+ return;
839
+ }
840
+ var nodeName = target.nodeName.toLowerCase();
841
+ if (nodeName == 'input') {
842
+ var button = this._get(inst, 'button');
843
+
844
+ $(button).addClass('ui-state-disabled').disabled = true;
845
+ target.disabled = true;
846
+
847
+ inst.trigger.filter('button').
848
+ each(function() { this.disabled = true; }).end();
849
+
850
+ }
851
+ else if (nodeName == 'div' || nodeName == 'span') {
852
+ var inline = $target.children('.' + this._inlineClass);
853
+ inline.children().addClass('ui-state-disabled');
854
+ inline.find('button').each(
855
+ function() { this.disabled = true }
856
+ )
857
+
858
+ }
859
+ this._disabledInputs = $.map(this._disabledInputs,
860
+ function(value) { return (value == target ? null : value); }); // delete entry
861
+ this._disabledInputs[this._disabledInputs.length] = $target.attr('id');
862
+ },
863
+
864
+ /* Is the first field in a jQuery collection disabled as a timepicker?
865
+ @param target_id element - the target input field or division or span
866
+ @return boolean - true if disabled, false if enabled */
867
+ _isDisabledTimepicker: function (target_id) {
868
+ if ( ! target_id) { return false; }
869
+ for (var i = 0; i < this._disabledInputs.length; i++) {
870
+ if (this._disabledInputs[i] == target_id) { return true; }
871
+ }
872
+ return false;
873
+ },
874
+
875
+ /* Check positioning to remain on screen. */
876
+ _checkOffset: function (inst, offset, isFixed) {
877
+ var tpWidth = inst.tpDiv.outerWidth();
878
+ var tpHeight = inst.tpDiv.outerHeight();
879
+ var inputWidth = inst.input ? inst.input.outerWidth() : 0;
880
+ var inputHeight = inst.input ? inst.input.outerHeight() : 0;
881
+ var viewWidth = document.documentElement.clientWidth + $(document).scrollLeft();
882
+ var viewHeight = document.documentElement.clientHeight + $(document).scrollTop();
883
+
884
+ offset.left -= (this._get(inst, 'isRTL') ? (tpWidth - inputWidth) : 0);
885
+ offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0;
886
+ offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
887
+
888
+ // now check if datepicker is showing outside window viewport - move to a better place if so.
889
+ offset.left -= Math.min(offset.left, (offset.left + tpWidth > viewWidth && viewWidth > tpWidth) ?
890
+ Math.abs(offset.left + tpWidth - viewWidth) : 0);
891
+ offset.top -= Math.min(offset.top, (offset.top + tpHeight > viewHeight && viewHeight > tpHeight) ?
892
+ Math.abs(tpHeight + inputHeight) : 0);
893
+
894
+ return offset;
895
+ },
896
+
897
+ /* Find an object's position on the screen. */
898
+ _findPos: function (obj) {
899
+ var inst = this._getInst(obj);
900
+ var isRTL = this._get(inst, 'isRTL');
901
+ while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) {
902
+ obj = obj[isRTL ? 'previousSibling' : 'nextSibling'];
903
+ }
904
+ var position = $(obj).offset();
905
+ return [position.left, position.top];
906
+ },
907
+
908
+ /* Retrieve the size of left and top borders for an element.
909
+ @param elem (jQuery object) the element of interest
910
+ @return (number[2]) the left and top borders */
911
+ _getBorders: function (elem) {
912
+ var convert = function (value) {
913
+ return { thin: 1, medium: 2, thick: 3}[value] || value;
914
+ };
915
+ return [parseFloat(convert(elem.css('border-left-width'))),
916
+ parseFloat(convert(elem.css('border-top-width')))];
917
+ },
918
+
919
+
920
+ /* Close time picker if clicked elsewhere. */
921
+ _checkExternalClick: function (event) {
922
+ if (!$.timepicker._curInst) { return; }
923
+ var $target = $(event.target);
924
+ if ($target[0].id != $.timepicker._mainDivId &&
925
+ $target.parents('#' + $.timepicker._mainDivId).length == 0 &&
926
+ !$target.hasClass($.timepicker.markerClassName) &&
927
+ !$target.hasClass($.timepicker._triggerClass) &&
928
+ $.timepicker._timepickerShowing && !($.timepicker._inDialog && $.blockUI))
929
+ $.timepicker._hideTimepicker();
930
+ },
931
+
932
+ /* Hide the time picker from view.
933
+ @param input element - the input field attached to the time picker */
934
+ _hideTimepicker: function (input) {
935
+ var inst = this._curInst;
936
+ if (!inst || (input && inst != $.data(input, PROP_NAME))) { return; }
937
+ if (this._timepickerShowing) {
938
+ var showAnim = this._get(inst, 'showAnim');
939
+ var duration = this._get(inst, 'duration');
940
+ var postProcess = function () {
941
+ $.timepicker._tidyDialog(inst);
942
+ this._curInst = null;
943
+ };
944
+ if ($.effects && $.effects[showAnim]) {
945
+ inst.tpDiv.hide(showAnim, $.timepicker._get(inst, 'showOptions'), duration, postProcess);
946
+ }
947
+ else {
948
+ inst.tpDiv[(showAnim == 'slideDown' ? 'slideUp' :
949
+ (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess);
950
+ }
951
+ if (!showAnim) { postProcess(); }
952
+ var onClose = this._get(inst, 'onClose');
953
+ if (onClose) {
954
+ onClose.apply(
955
+ (inst.input ? inst.input[0] : null),
956
+ [(inst.input ? inst.input.val() : ''), inst]); // trigger custom callback
957
+ }
958
+ this._timepickerShowing = false;
959
+ this._lastInput = null;
960
+ if (this._inDialog) {
961
+ this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
962
+ if ($.blockUI) {
963
+ $.unblockUI();
964
+ $('body').append(this.tpDiv);
965
+ }
966
+ }
967
+ this._inDialog = false;
968
+ }
969
+ },
970
+
971
+
972
+
973
+ /* Tidy up after a dialog display. */
974
+ _tidyDialog: function (inst) {
975
+ inst.tpDiv.removeClass(this._dialogClass).unbind('.ui-timepicker');
976
+ },
977
+
978
+ /* Retrieve the instance data for the target control.
979
+ @param target element - the target input field or division or span
980
+ @return object - the associated instance data
981
+ @throws error if a jQuery problem getting data */
982
+ _getInst: function (target) {
983
+ try {
984
+ return $.data(target, PROP_NAME);
985
+ }
986
+ catch (err) {
987
+ throw 'Missing instance data for this timepicker';
988
+ }
989
+ },
990
+
991
+ /* Get a setting value, defaulting if necessary. */
992
+ _get: function (inst, name) {
993
+ return inst.settings[name] !== undefined ?
994
+ inst.settings[name] : this._defaults[name];
995
+ },
996
+
997
+ /* Parse existing time and initialise time picker. */
998
+ _setTimeFromField: function (inst) {
999
+ if (inst.input.val() == inst.lastVal) { return; }
1000
+ var defaultTime = this._get(inst, 'defaultTime');
1001
+
1002
+ var timeToParse = defaultTime == 'now' ? this._getCurrentTimeRounded(inst) : defaultTime;
1003
+ if ((inst.inline == false) && (inst.input.val() != '')) { timeToParse = inst.input.val() }
1004
+
1005
+ if (timeToParse instanceof Date) {
1006
+ inst.hours = timeToParse.getHours();
1007
+ inst.minutes = timeToParse.getMinutes();
1008
+ } else {
1009
+ var timeVal = inst.lastVal = timeToParse;
1010
+ if (timeToParse == '') {
1011
+ inst.hours = -1;
1012
+ inst.minutes = -1;
1013
+ } else {
1014
+ var time = this.parseTime(inst, timeVal);
1015
+ inst.hours = time.hours;
1016
+ inst.minutes = time.minutes;
1017
+ }
1018
+ }
1019
+
1020
+
1021
+ $.timepicker._updateTimepicker(inst);
1022
+ },
1023
+
1024
+ /* Update or retrieve the settings for an existing time picker.
1025
+ @param target element - the target input field or division or span
1026
+ @param name object - the new settings to update or
1027
+ string - the name of the setting to change or retrieve,
1028
+ when retrieving also 'all' for all instance settings or
1029
+ 'defaults' for all global defaults
1030
+ @param value any - the new value for the setting
1031
+ (omit if above is an object or to retrieve a value) */
1032
+ _optionTimepicker: function(target, name, value) {
1033
+ var inst = this._getInst(target);
1034
+ if (arguments.length == 2 && typeof name == 'string') {
1035
+ return (name == 'defaults' ? $.extend({}, $.timepicker._defaults) :
1036
+ (inst ? (name == 'all' ? $.extend({}, inst.settings) :
1037
+ this._get(inst, name)) : null));
1038
+ }
1039
+ var settings = name || {};
1040
+ if (typeof name == 'string') {
1041
+ settings = {};
1042
+ settings[name] = value;
1043
+ }
1044
+ if (inst) {
1045
+ if (this._curInst == inst) {
1046
+ this._hideTimepicker();
1047
+ }
1048
+ extendRemove(inst.settings, settings);
1049
+ this._updateTimepicker(inst);
1050
+ }
1051
+ },
1052
+
1053
+
1054
+ /* Set the time for a jQuery selection.
1055
+ @param target element - the target input field or division or span
1056
+ @param time String - the new time */
1057
+ _setTimeTimepicker: function(target, time) {
1058
+ var inst = this._getInst(target);
1059
+ if (inst) {
1060
+ this._setTime(inst, time);
1061
+ this._updateTimepicker(inst);
1062
+ this._updateAlternate(inst, time);
1063
+ }
1064
+ },
1065
+
1066
+ /* Set the time directly. */
1067
+ _setTime: function(inst, time, noChange) {
1068
+ var origHours = inst.hours;
1069
+ var origMinutes = inst.minutes;
1070
+ var time = this.parseTime(inst, time);
1071
+ inst.hours = time.hours;
1072
+ inst.minutes = time.minutes;
1073
+
1074
+ if ((origHours != inst.hours || origMinutes != inst.minuts) && !noChange) {
1075
+ inst.input.trigger('change');
1076
+ }
1077
+ this._updateTimepicker(inst);
1078
+ this._updateSelectedValue(inst);
1079
+ },
1080
+
1081
+ /* Return the current time, ready to be parsed, rounded to the closest 5 minute */
1082
+ _getCurrentTimeRounded: function (inst) {
1083
+ var currentTime = new Date(),
1084
+ currentMinutes = currentTime.getMinutes(),
1085
+ // round to closest 5
1086
+ adjustedMinutes = Math.round( currentMinutes / 5 ) * 5;
1087
+ currentTime.setMinutes(adjustedMinutes);
1088
+ return currentTime;
1089
+ },
1090
+
1091
+ /*
1092
+ * Parse a time string into hours and minutes
1093
+ */
1094
+ parseTime: function (inst, timeVal) {
1095
+ var retVal = new Object();
1096
+ retVal.hours = -1;
1097
+ retVal.minutes = -1;
1098
+
1099
+ var timeSeparator = this._get(inst, 'timeSeparator'),
1100
+ amPmText = this._get(inst, 'amPmText'),
1101
+ showHours = this._get(inst, 'showHours'),
1102
+ showMinutes = this._get(inst, 'showMinutes'),
1103
+ optionalMinutes = this._get(inst, 'optionalMinutes'),
1104
+ showPeriod = (this._get(inst, 'showPeriod') == true),
1105
+ p = timeVal.indexOf(timeSeparator);
1106
+
1107
+ // check if time separator found
1108
+ if (p != -1) {
1109
+ retVal.hours = parseInt(timeVal.substr(0, p), 10);
1110
+ retVal.minutes = parseInt(timeVal.substr(p + 1), 10);
1111
+ }
1112
+ // check for hours only
1113
+ else if ( (showHours) && ( !showMinutes || optionalMinutes ) ) {
1114
+ retVal.hours = parseInt(timeVal, 10);
1115
+ }
1116
+ // check for minutes only
1117
+ else if ( ( ! showHours) && (showMinutes) ) {
1118
+ retVal.minutes = parseInt(timeVal, 10);
1119
+ }
1120
+
1121
+ if (showHours) {
1122
+ var timeValUpper = timeVal.toUpperCase();
1123
+ if ((retVal.hours < 12) && (showPeriod) && (timeValUpper.indexOf(amPmText[1].toUpperCase()) != -1)) {
1124
+ retVal.hours += 12;
1125
+ }
1126
+ // fix for 12 AM
1127
+ if ((retVal.hours == 12) && (showPeriod) && (timeValUpper.indexOf(amPmText[0].toUpperCase()) != -1)) {
1128
+ retVal.hours = 0;
1129
+ }
1130
+ }
1131
+
1132
+ return retVal;
1133
+ },
1134
+
1135
+ selectNow: function(e) {
1136
+
1137
+ var id = $(e.target).attr("data-timepicker-instance-id"),
1138
+ $target = $(id),
1139
+ inst = this._getInst($target[0]);
1140
+
1141
+ //if (!inst || (input && inst != $.data(input, PROP_NAME))) { return; }
1142
+ var currentTime = new Date();
1143
+ inst.hours = currentTime.getHours();
1144
+ inst.minutes = currentTime.getMinutes();
1145
+ this._updateSelectedValue(inst);
1146
+ this._updateTimepicker(inst);
1147
+ this._hideTimepicker();
1148
+ },
1149
+
1150
+ deselectTime: function(e) {
1151
+ var id = $(e.target).attr("data-timepicker-instance-id"),
1152
+ $target = $(id),
1153
+ inst = this._getInst($target[0]);
1154
+ inst.hours = -1;
1155
+ inst.minutes = -1;
1156
+ this._updateSelectedValue(inst);
1157
+ this._hideTimepicker();
1158
+ },
1159
+
1160
+
1161
+ selectHours: function (event) {
1162
+ var $td = $(event.currentTarget),
1163
+ id = $td.attr("data-timepicker-instance-id"),
1164
+ newHours = parseInt($td.attr("data-hour")),
1165
+ fromDoubleClick = event.data.fromDoubleClick,
1166
+ $target = $(id),
1167
+ inst = this._getInst($target[0]),
1168
+ showMinutes = (this._get(inst, 'showMinutes') == true);
1169
+
1170
+ // don't select if disabled
1171
+ if ( $.timepicker._isDisabledTimepicker($target.attr('id')) ) { return false }
1172
+
1173
+ $td.parents('.ui-timepicker-hours:first').find('a').removeClass('ui-state-active');
1174
+ $td.children('a').addClass('ui-state-active');
1175
+ inst.hours = newHours;
1176
+
1177
+ // added for onMinuteShow callback
1178
+ var onMinuteShow = this._get(inst, 'onMinuteShow');
1179
+ if (onMinuteShow) {
1180
+ // this will trigger a callback on selected hour to make sure selected minute is allowed.
1181
+ this._updateMinuteDisplay(inst);
1182
+ }
1183
+
1184
+ this._updateSelectedValue(inst);
1185
+
1186
+ inst._hoursClicked = true;
1187
+ if ((inst._minutesClicked) || (fromDoubleClick) || (showMinutes == false)) {
1188
+ $.timepicker._hideTimepicker();
1189
+ }
1190
+ // return false because if used inline, prevent the url to change to a hashtag
1191
+ return false;
1192
+ },
1193
+
1194
+ selectMinutes: function (event) {
1195
+ var $td = $(event.currentTarget),
1196
+ id = $td.attr("data-timepicker-instance-id"),
1197
+ newMinutes = parseInt($td.attr("data-minute")),
1198
+ fromDoubleClick = event.data.fromDoubleClick,
1199
+ $target = $(id),
1200
+ inst = this._getInst($target[0]),
1201
+ showHours = (this._get(inst, 'showHours') == true);
1202
+
1203
+ // don't select if disabled
1204
+ if ( $.timepicker._isDisabledTimepicker($target.attr('id')) ) { return false }
1205
+
1206
+ $td.parents('.ui-timepicker-minutes:first').find('a').removeClass('ui-state-active');
1207
+ $td.children('a').addClass('ui-state-active');
1208
+
1209
+ inst.minutes = newMinutes;
1210
+ this._updateSelectedValue(inst);
1211
+
1212
+ inst._minutesClicked = true;
1213
+ if ((inst._hoursClicked) || (fromDoubleClick) || (showHours == false)) {
1214
+ $.timepicker._hideTimepicker();
1215
+ // return false because if used inline, prevent the url to change to a hashtag
1216
+ return false;
1217
+ }
1218
+
1219
+ // return false because if used inline, prevent the url to change to a hashtag
1220
+ return false;
1221
+ },
1222
+
1223
+ _updateSelectedValue: function (inst) {
1224
+ var newTime = this._getParsedTime(inst);
1225
+ if (inst.input) {
1226
+ inst.input.val(newTime);
1227
+ inst.input.trigger('change');
1228
+ }
1229
+ var onSelect = this._get(inst, 'onSelect');
1230
+ if (onSelect) { onSelect.apply((inst.input ? inst.input[0] : null), [newTime, inst]); } // trigger custom callback
1231
+ this._updateAlternate(inst, newTime);
1232
+ return newTime;
1233
+ },
1234
+
1235
+ /* this function process selected time and return it parsed according to instance options */
1236
+ _getParsedTime: function(inst) {
1237
+
1238
+ if (inst.hours == -1 && inst.minutes == -1) {
1239
+ return '';
1240
+ }
1241
+
1242
+ if ((inst.hours < 0) || (inst.hours > 23)) { inst.hours = 12; }
1243
+ if ((inst.minutes < 0) || (inst.minutes > 59)) { inst.minutes = 0; }
1244
+
1245
+ var period = "",
1246
+ showPeriod = (this._get(inst, 'showPeriod') == true),
1247
+ showLeadingZero = (this._get(inst, 'showLeadingZero') == true),
1248
+ showHours = (this._get(inst, 'showHours') == true),
1249
+ showMinutes = (this._get(inst, 'showMinutes') == true),
1250
+ optionalMinutes = (this._get(inst, 'optionalMinutes') == true),
1251
+ amPmText = this._get(inst, 'amPmText'),
1252
+ selectedHours = inst.hours ? inst.hours : 0,
1253
+ selectedMinutes = inst.minutes ? inst.minutes : 0,
1254
+ displayHours = selectedHours ? selectedHours : 0,
1255
+ parsedTime = '';
1256
+
1257
+ if (showPeriod) {
1258
+ if (inst.hours == 0) {
1259
+ displayHours = 12;
1260
+ }
1261
+ if (inst.hours < 12) {
1262
+ period = amPmText[0];
1263
+ }
1264
+ else {
1265
+ period = amPmText[1];
1266
+ if (displayHours > 12) {
1267
+ displayHours -= 12;
1268
+ }
1269
+ }
1270
+ }
1271
+
1272
+ var h = displayHours.toString();
1273
+ if (showLeadingZero && (displayHours < 10)) { h = '0' + h; }
1274
+
1275
+ var m = selectedMinutes.toString();
1276
+ if (selectedMinutes < 10) { m = '0' + m; }
1277
+
1278
+ if (showHours) {
1279
+ parsedTime += h;
1280
+ }
1281
+ if (showHours && showMinutes && (!optionalMinutes || m != 0)) {
1282
+ parsedTime += this._get(inst, 'timeSeparator');
1283
+ }
1284
+ if (showMinutes && (!optionalMinutes || m != 0)) {
1285
+ parsedTime += m;
1286
+ }
1287
+ if (showHours) {
1288
+ if (period.length > 0) { parsedTime += this._get(inst, 'periodSeparator') + period; }
1289
+ }
1290
+
1291
+ return parsedTime;
1292
+ },
1293
+
1294
+ /* Update any alternate field to synchronise with the main field. */
1295
+ _updateAlternate: function(inst, newTime) {
1296
+ var altField = this._get(inst, 'altField');
1297
+ if (altField) { // update alternate field too
1298
+ $(altField).each(function(i,e) {
1299
+ $(e).val(newTime);
1300
+ });
1301
+ }
1302
+ },
1303
+
1304
+ /* This might look unused but it's called by the $.fn.timepicker function with param getTime */
1305
+ /* added v 0.2.3 - gitHub issue #5 - Thanks edanuff */
1306
+ _getTimeTimepicker : function(input) {
1307
+ var inst = this._getInst(input);
1308
+ return this._getParsedTime(inst);
1309
+ },
1310
+ _getHourTimepicker: function(input) {
1311
+ var inst = this._getInst(input);
1312
+ if ( inst == undefined) { return -1; }
1313
+ return inst.hours;
1314
+ },
1315
+ _getMinuteTimepicker: function(input) {
1316
+ var inst= this._getInst(input);
1317
+ if ( inst == undefined) { return -1; }
1318
+ return inst.minutes;
1319
+ }
1320
+
1321
+ });
1322
+
1323
+
1324
+
1325
+ /* Invoke the timepicker functionality.
1326
+ @param options string - a command, optionally followed by additional parameters or
1327
+ Object - settings for attaching new timepicker functionality
1328
+ @return jQuery object */
1329
+ $.fn.timepicker = function (options) {
1330
+
1331
+ /* Initialise the time picker. */
1332
+ if (!$.timepicker.initialized) {
1333
+ $(document).mousedown($.timepicker._checkExternalClick).
1334
+ find('body').append($.timepicker.tpDiv);
1335
+ $.timepicker.initialized = true;
1336
+ }
1337
+
1338
+ var otherArgs = Array.prototype.slice.call(arguments, 1);
1339
+ if (typeof options == 'string' && (options == 'getTime' || options == 'getHour' || options == 'getMinute' ))
1340
+ return $.timepicker['_' + options + 'Timepicker'].
1341
+ apply($.timepicker, [this[0]].concat(otherArgs));
1342
+ if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string')
1343
+ return $.timepicker['_' + options + 'Timepicker'].
1344
+ apply($.timepicker, [this[0]].concat(otherArgs));
1345
+ return this.each(function () {
1346
+ typeof options == 'string' ?
1347
+ $.timepicker['_' + options + 'Timepicker'].
1348
+ apply($.timepicker, [this].concat(otherArgs)) :
1349
+ $.timepicker._attachTimepicker(this, options);
1350
+ });
1351
+ };
1352
+
1353
+ /* jQuery extend now ignores nulls! */
1354
+ function extendRemove(target, props) {
1355
+ $.extend(target, props);
1356
+ for (var name in props)
1357
+ if (props[name] == null || props[name] == undefined)
1358
+ target[name] = props[name];
1359
+ return target;
1360
+ };
1361
+
1362
+ $.timepicker = new Timepicker(); // singleton instance
1363
+ $.timepicker.initialized = false;
1364
+ $.timepicker.uuid = new Date().getTime();
1365
+ $.timepicker.version = "0.2.9";
1366
+
1367
+ // Workaround for #4055
1368
+ // Add another global to avoid noConflict issues with inline event handlers
1369
+ window['TP_jQuery_' + tpuuid] = $;
1370
+
1371
+ })(jQuery);