glimmer-dsl-opal 0.4.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +56 -0
  3. data/README.md +892 -16
  4. data/VERSION +1 -1
  5. data/app/assets/images/glimmer/images/calendar.gif +0 -0
  6. data/app/assets/images/glimmer/images/ui-icons_222222_256x240.png +0 -0
  7. data/app/assets/images/glimmer/images/ui-icons_444444_256x240.png +0 -0
  8. data/app/assets/images/glimmer/images/ui-icons_555555_256x240.png +0 -0
  9. data/app/assets/images/glimmer/images/ui-icons_777620_256x240.png +0 -0
  10. data/app/assets/images/glimmer/images/ui-icons_777777_256x240.png +0 -0
  11. data/app/assets/images/glimmer/images/ui-icons_cc0000_256x240.png +0 -0
  12. data/app/assets/images/glimmer/images/ui-icons_ffffff_256x240.png +0 -0
  13. data/app/assets/stylesheets/glimmer/glimmer.css +15 -0
  14. data/app/assets/stylesheets/glimmer/jquery-ui.css +1312 -0
  15. data/app/assets/stylesheets/glimmer/jquery-ui.structure.css +886 -0
  16. data/app/assets/stylesheets/glimmer/jquery-ui.theme.css +443 -0
  17. data/app/assets/stylesheets/glimmer/jquery.ui.timepicker.css +57 -0
  18. data/lib/glimmer-dsl-opal.rb +16 -9
  19. data/lib/glimmer-dsl-opal/ext/date.rb +49 -3
  20. data/lib/glimmer-dsl-opal/samples/elaborate/tic_tac_toe.rb +23 -0
  21. data/lib/glimmer-dsl-opal/samples/hello/hello_checkbox.rb +85 -0
  22. data/lib/glimmer-dsl-opal/samples/hello/hello_checkbox_group.rb +68 -0
  23. data/lib/glimmer-dsl-opal/samples/hello/hello_combo.rb +5 -5
  24. data/lib/glimmer-dsl-opal/samples/hello/hello_custom_widget.rb +3 -3
  25. data/lib/glimmer-dsl-opal/samples/hello/hello_date_time.rb +63 -0
  26. data/lib/glimmer-dsl-opal/samples/hello/hello_group.rb +104 -0
  27. data/lib/glimmer-dsl-opal/samples/hello/hello_list_single_selection.rb +1 -1
  28. data/lib/glimmer-dsl-opal/samples/hello/hello_radio.rb +108 -0
  29. data/lib/glimmer-dsl-opal/samples/hello/hello_radio_group.rb +84 -0
  30. data/lib/glimmer-dsl-opal/samples/hello/hello_table.rb +283 -0
  31. data/lib/glimmer-dsl-opal/vendor/jquery-ui-timepicker/GPL-LICENSE.txt +278 -0
  32. data/lib/glimmer-dsl-opal/vendor/jquery-ui-timepicker/MIT-LICENSE.txt +20 -0
  33. data/lib/glimmer-dsl-opal/vendor/jquery-ui-timepicker/jquery.ui.timepicker.css +57 -0
  34. data/lib/glimmer-dsl-opal/vendor/jquery-ui-timepicker/jquery.ui.timepicker.js +1496 -0
  35. data/lib/glimmer-dsl-opal/vendor/jquery-ui/AUTHORS.txt +333 -0
  36. data/lib/glimmer-dsl-opal/vendor/jquery-ui/LICENSE.txt +43 -0
  37. data/lib/glimmer-dsl-opal/vendor/jquery-ui/images/ui-icons_444444_256x240.png +0 -0
  38. data/lib/glimmer-dsl-opal/vendor/jquery-ui/images/ui-icons_555555_256x240.png +0 -0
  39. data/lib/glimmer-dsl-opal/vendor/jquery-ui/images/ui-icons_777620_256x240.png +0 -0
  40. data/lib/glimmer-dsl-opal/vendor/jquery-ui/images/ui-icons_777777_256x240.png +0 -0
  41. data/lib/glimmer-dsl-opal/vendor/jquery-ui/images/ui-icons_cc0000_256x240.png +0 -0
  42. data/lib/glimmer-dsl-opal/vendor/jquery-ui/images/ui-icons_ffffff_256x240.png +0 -0
  43. data/lib/glimmer-dsl-opal/vendor/jquery-ui/jquery-ui.min.css +7 -0
  44. data/lib/glimmer-dsl-opal/vendor/jquery-ui/jquery-ui.min.js +13 -0
  45. data/lib/glimmer-dsl-opal/vendor/jquery-ui/jquery-ui.structure.min.css +5 -0
  46. data/lib/glimmer-dsl-opal/vendor/jquery-ui/jquery-ui.theme.min.css +5 -0
  47. data/lib/glimmer-dsl-opal/vendor/jquery-ui/package.json +74 -0
  48. data/lib/glimmer-dsl-swt.rb +1 -0
  49. data/lib/glimmer/data_binding/element_binding.rb +2 -1
  50. data/lib/glimmer/data_binding/table_items_binding.rb +29 -18
  51. data/lib/glimmer/dsl/opal/block_property_expression.rb +41 -0
  52. data/lib/glimmer/dsl/opal/checkbox_group_selection_data_binding_expression.rb +61 -0
  53. data/lib/glimmer/dsl/opal/custom_widget_expression.rb +8 -6
  54. data/lib/glimmer/dsl/opal/dsl.rb +6 -0
  55. data/lib/glimmer/dsl/opal/property_expression.rb +4 -3
  56. data/lib/glimmer/dsl/opal/radio_group_selection_data_binding_expression.rb +61 -0
  57. data/lib/glimmer/dsl/opal/shell_expression.rb +7 -2
  58. data/lib/glimmer/dsl/opal/widget_expression.rb +6 -1
  59. data/lib/glimmer/engine.rb +9 -0
  60. data/lib/glimmer/swt.rb +3 -3
  61. data/lib/glimmer/swt/button_proxy.rb +16 -2
  62. data/lib/glimmer/swt/checkbox_proxy.rb +81 -0
  63. data/lib/glimmer/swt/color_proxy.rb +45 -45
  64. data/lib/glimmer/swt/combo_proxy.rb +6 -6
  65. data/lib/glimmer/swt/composite_proxy.rb +2 -2
  66. data/lib/glimmer/swt/custom/checkbox_group.rb +142 -0
  67. data/lib/glimmer/swt/custom/radio_group.rb +143 -0
  68. data/lib/glimmer/swt/date_time_proxy.rb +145 -0
  69. data/lib/glimmer/swt/display_proxy.rb +6 -2
  70. data/lib/glimmer/swt/fill_layout_proxy.rb +1 -1
  71. data/lib/glimmer/swt/grid_layout_proxy.rb +19 -8
  72. data/lib/glimmer/swt/group_proxy.rb +38 -0
  73. data/lib/glimmer/swt/label_proxy.rb +27 -7
  74. data/lib/glimmer/swt/layout_data_proxy.rb +39 -21
  75. data/lib/glimmer/swt/layout_proxy.rb +4 -4
  76. data/lib/glimmer/swt/list_proxy.rb +3 -3
  77. data/lib/glimmer/swt/make_shift_shell_proxy.rb +4 -4
  78. data/lib/glimmer/swt/message_box_proxy.rb +10 -8
  79. data/lib/glimmer/swt/property_owner.rb +2 -2
  80. data/lib/glimmer/swt/radio_proxy.rb +82 -0
  81. data/lib/glimmer/swt/row_layout_proxy.rb +33 -10
  82. data/lib/glimmer/swt/scrolled_composite_proxy.rb +20 -0
  83. data/lib/glimmer/swt/shell_proxy.rb +29 -9
  84. data/lib/glimmer/swt/tab_folder_proxy.rb +5 -5
  85. data/lib/glimmer/swt/tab_item_proxy.rb +7 -7
  86. data/lib/glimmer/swt/table_column_proxy.rb +62 -12
  87. data/lib/glimmer/swt/table_item_proxy.rb +14 -7
  88. data/lib/glimmer/swt/table_proxy.rb +225 -16
  89. data/lib/glimmer/swt/text_proxy.rb +50 -2
  90. data/lib/glimmer/swt/widget_proxy.rb +88 -39
  91. data/lib/glimmer/ui/custom_widget.rb +8 -8
  92. data/lib/net/http.rb +1 -5
  93. data/lib/uri.rb +3 -3
  94. metadata +56 -9
  95. data/lib/glimmer/data_binding/ext/observable_model.rb +0 -40
@@ -0,0 +1,278 @@
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ Preamble
10
+
11
+ The licenses for most software are designed to take away your
12
+ freedom to share and change it. By contrast, the GNU General Public
13
+ License is intended to guarantee your freedom to share and change free
14
+ software--to make sure the software is free for all its users. This
15
+ General Public License applies to most of the Free Software
16
+ Foundation's software and to any other program whose authors commit to
17
+ using it. (Some other Free Software Foundation software is covered by
18
+ the GNU Lesser General Public License instead.) You can apply it to
19
+ your programs, too.
20
+
21
+ When we speak of free software, we are referring to freedom, not
22
+ price. Our General Public Licenses are designed to make sure that you
23
+ have the freedom to distribute copies of free software (and charge for
24
+ this service if you wish), that you receive source code or can get it
25
+ if you want it, that you can change the software or use pieces of it
26
+ in new free programs; and that you know you can do these things.
27
+
28
+ To protect your rights, we need to make restrictions that forbid
29
+ anyone to deny you these rights or to ask you to surrender the rights.
30
+ These restrictions translate to certain responsibilities for you if you
31
+ distribute copies of the software, or if you modify it.
32
+
33
+ For example, if you distribute copies of such a program, whether
34
+ gratis or for a fee, you must give the recipients all the rights that
35
+ you have. You must make sure that they, too, receive or can get the
36
+ source code. And you must show them these terms so they know their
37
+ rights.
38
+
39
+ We protect your rights with two steps: (1) copyright the software, and
40
+ (2) offer you this license which gives you legal permission to copy,
41
+ distribute and/or modify the software.
42
+
43
+ Also, for each author's protection and ours, we want to make certain
44
+ that everyone understands that there is no warranty for this free
45
+ software. If the software is modified by someone else and passed on, we
46
+ want its recipients to know that what they have is not the original, so
47
+ that any problems introduced by others will not reflect on the original
48
+ authors' reputations.
49
+
50
+ Finally, any free program is threatened constantly by software
51
+ patents. We wish to avoid the danger that redistributors of a free
52
+ program will individually obtain patent licenses, in effect making the
53
+ program proprietary. To prevent this, we have made it clear that any
54
+ patent must be licensed for everyone's free use or not licensed at all.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ GNU GENERAL PUBLIC LICENSE
60
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
+
62
+ 0. This License applies to any program or other work which contains
63
+ a notice placed by the copyright holder saying it may be distributed
64
+ under the terms of this General Public License. The "Program", below,
65
+ refers to any such program or work, and a "work based on the Program"
66
+ means either the Program or any derivative work under copyright law:
67
+ that is to say, a work containing the Program or a portion of it,
68
+ either verbatim or with modifications and/or translated into another
69
+ language. (Hereinafter, translation is included without limitation in
70
+ the term "modification".) Each licensee is addressed as "you".
71
+
72
+ Activities other than copying, distribution and modification are not
73
+ covered by this License; they are outside its scope. The act of
74
+ running the Program is not restricted, and the output from the Program
75
+ is covered only if its contents constitute a work based on the
76
+ Program (independent of having been made by running the Program).
77
+ Whether that is true depends on what the Program does.
78
+
79
+ 1. You may copy and distribute verbatim copies of the Program's
80
+ source code as you receive it, in any medium, provided that you
81
+ conspicuously and appropriately publish on each copy an appropriate
82
+ copyright notice and disclaimer of warranty; keep intact all the
83
+ notices that refer to this License and to the absence of any warranty;
84
+ and give any other recipients of the Program a copy of this License
85
+ along with the Program.
86
+
87
+ You may charge a fee for the physical act of transferring a copy, and
88
+ you may at your option offer warranty protection in exchange for a fee.
89
+
90
+ 2. You may modify your copy or copies of the Program or any portion
91
+ of it, thus forming a work based on the Program, and copy and
92
+ distribute such modifications or work under the terms of Section 1
93
+ above, provided that you also meet all of these conditions:
94
+
95
+ a) You must cause the modified files to carry prominent notices
96
+ stating that you changed the files and the date of any change.
97
+
98
+ b) You must cause any work that you distribute or publish, that in
99
+ whole or in part contains or is derived from the Program or any
100
+ part thereof, to be licensed as a whole at no charge to all third
101
+ parties under the terms of this License.
102
+
103
+ c) If the modified program normally reads commands interactively
104
+ when run, you must cause it, when started running for such
105
+ interactive use in the most ordinary way, to print or display an
106
+ announcement including an appropriate copyright notice and a
107
+ notice that there is no warranty (or else, saying that you provide
108
+ a warranty) and that users may redistribute the program under
109
+ these conditions, and telling the user how to view a copy of this
110
+ License. (Exception: if the Program itself is interactive but
111
+ does not normally print such an announcement, your work based on
112
+ the Program is not required to print an announcement.)
113
+
114
+ These requirements apply to the modified work as a whole. If
115
+ identifiable sections of that work are not derived from the Program,
116
+ and can be reasonably considered independent and separate works in
117
+ themselves, then this License, and its terms, do not apply to those
118
+ sections when you distribute them as separate works. But when you
119
+ distribute the same sections as part of a whole which is a work based
120
+ on the Program, the distribution of the whole must be on the terms of
121
+ this License, whose permissions for other licensees extend to the
122
+ entire whole, and thus to each and every part regardless of who wrote it.
123
+
124
+ Thus, it is not the intent of this section to claim rights or contest
125
+ your rights to work written entirely by you; rather, the intent is to
126
+ exercise the right to control the distribution of derivative or
127
+ collective works based on the Program.
128
+
129
+ In addition, mere aggregation of another work not based on the Program
130
+ with the Program (or with a work based on the Program) on a volume of
131
+ a storage or distribution medium does not bring the other work under
132
+ the scope of this License.
133
+
134
+ 3. You may copy and distribute the Program (or a work based on it,
135
+ under Section 2) in object code or executable form under the terms of
136
+ Sections 1 and 2 above provided that you also do one of the following:
137
+
138
+ a) Accompany it with the complete corresponding machine-readable
139
+ source code, which must be distributed under the terms of Sections
140
+ 1 and 2 above on a medium customarily used for software interchange; or,
141
+
142
+ b) Accompany it with a written offer, valid for at least three
143
+ years, to give any third party, for a charge no more than your
144
+ cost of physically performing source distribution, a complete
145
+ machine-readable copy of the corresponding source code, to be
146
+ distributed under the terms of Sections 1 and 2 above on a medium
147
+ customarily used for software interchange; or,
148
+
149
+ c) Accompany it with the information you received as to the offer
150
+ to distribute corresponding source code. (This alternative is
151
+ allowed only for noncommercial distribution and only if you
152
+ received the program in object code or executable form with such
153
+ an offer, in accord with Subsection b above.)
154
+
155
+ The source code for a work means the preferred form of the work for
156
+ making modifications to it. For an executable work, complete source
157
+ code means all the source code for all modules it contains, plus any
158
+ associated interface definition files, plus the scripts used to
159
+ control compilation and installation of the executable. However, as a
160
+ special exception, the source code distributed need not include
161
+ anything that is normally distributed (in either source or binary
162
+ form) with the major components (compiler, kernel, and so on) of the
163
+ operating system on which the executable runs, unless that component
164
+ itself accompanies the executable.
165
+
166
+ If distribution of executable or object code is made by offering
167
+ access to copy from a designated place, then offering equivalent
168
+ access to copy the source code from the same place counts as
169
+ distribution of the source code, even though third parties are not
170
+ compelled to copy the source along with the object code.
171
+
172
+ 4. You may not copy, modify, sublicense, or distribute the Program
173
+ except as expressly provided under this License. Any attempt
174
+ otherwise to copy, modify, sublicense or distribute the Program is
175
+ void, and will automatically terminate your rights under this License.
176
+ However, parties who have received copies, or rights, from you under
177
+ this License will not have their licenses terminated so long as such
178
+ parties remain in full compliance.
179
+
180
+ 5. You are not required to accept this License, since you have not
181
+ signed it. However, nothing else grants you permission to modify or
182
+ distribute the Program or its derivative works. These actions are
183
+ prohibited by law if you do not accept this License. Therefore, by
184
+ modifying or distributing the Program (or any work based on the
185
+ Program), you indicate your acceptance of this License to do so, and
186
+ all its terms and conditions for copying, distributing or modifying
187
+ the Program or works based on it.
188
+
189
+ 6. Each time you redistribute the Program (or any work based on the
190
+ Program), the recipient automatically receives a license from the
191
+ original licensor to copy, distribute or modify the Program subject to
192
+ these terms and conditions. You may not impose any further
193
+ restrictions on the recipients' exercise of the rights granted herein.
194
+ You are not responsible for enforcing compliance by third parties to
195
+ this License.
196
+
197
+ 7. If, as a consequence of a court judgment or allegation of patent
198
+ infringement or for any other reason (not limited to patent issues),
199
+ conditions are imposed on you (whether by court order, agreement or
200
+ otherwise) that contradict the conditions of this License, they do not
201
+ excuse you from the conditions of this License. If you cannot
202
+ distribute so as to satisfy simultaneously your obligations under this
203
+ License and any other pertinent obligations, then as a consequence you
204
+ may not distribute the Program at all. For example, if a patent
205
+ license would not permit royalty-free redistribution of the Program by
206
+ all those who receive copies directly or indirectly through you, then
207
+ the only way you could satisfy both it and this License would be to
208
+ refrain entirely from distribution of the Program.
209
+
210
+ If any portion of this section is held invalid or unenforceable under
211
+ any particular circumstance, the balance of the section is intended to
212
+ apply and the section as a whole is intended to apply in other
213
+ circumstances.
214
+
215
+ It is not the purpose of this section to induce you to infringe any
216
+ patents or other property right claims or to contest validity of any
217
+ such claims; this section has the sole purpose of protecting the
218
+ integrity of the free software distribution system, which is
219
+ implemented by public license practices. Many people have made
220
+ generous contributions to the wide range of software distributed
221
+ through that system in reliance on consistent application of that
222
+ system; it is up to the author/donor to decide if he or she is willing
223
+ to distribute software through any other system and a licensee cannot
224
+ impose that choice.
225
+
226
+ This section is intended to make thoroughly clear what is believed to
227
+ be a consequence of the rest of this License.
228
+
229
+ 8. If the distribution and/or use of the Program is restricted in
230
+ certain countries either by patents or by copyrighted interfaces, the
231
+ original copyright holder who places the Program under this License
232
+ may add an explicit geographical distribution limitation excluding
233
+ those countries, so that distribution is permitted only in or among
234
+ countries not thus excluded. In such case, this License incorporates
235
+ the limitation as if written in the body of this License.
236
+
237
+ 9. The Free Software Foundation may publish revised and/or new versions
238
+ of the General Public License from time to time. Such new versions will
239
+ be similar in spirit to the present version, but may differ in detail to
240
+ address new problems or concerns.
241
+
242
+ Each version is given a distinguishing version number. If the Program
243
+ specifies a version number of this License which applies to it and "any
244
+ later version", you have the option of following the terms and conditions
245
+ either of that version or of any later version published by the Free
246
+ Software Foundation. If the Program does not specify a version number of
247
+ this License, you may choose any version ever published by the Free Software
248
+ Foundation.
249
+
250
+ 10. If you wish to incorporate parts of the Program into other free
251
+ programs whose distribution conditions are different, write to the author
252
+ to ask for permission. For software which is copyrighted by the Free
253
+ Software Foundation, write to the Free Software Foundation; we sometimes
254
+ make exceptions for this. Our decision will be guided by the two goals
255
+ of preserving the free status of all derivatives of our free software and
256
+ of promoting the sharing and reuse of software generally.
257
+
258
+ NO WARRANTY
259
+
260
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
+ REPAIR OR CORRECTION.
269
+
270
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
+ POSSIBILITY OF SUCH DAMAGES.""''''''
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 John Resig, http://jquery.com/
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,57 @@
1
+ /*
2
+ * Timepicker stylesheet
3
+ * Highly inspired from datepicker
4
+ * FG - Nov 2010 - Web3R
5
+ *
6
+ * version 0.0.3 : Fixed some settings, more dynamic
7
+ * version 0.0.4 : Removed width:100% on tables
8
+ * version 0.1.1 : set width 0 on tables to fix an ie6 bug
9
+ */
10
+
11
+ .ui-timepicker-inline { display: inline; }
12
+
13
+ #ui-timepicker-div { padding: 0.2em; }
14
+ .ui-timepicker-table { display: inline-table; width: 0; }
15
+ .ui-timepicker-table table { margin:0.15em 0 0 0; border-collapse: collapse; }
16
+
17
+ .ui-timepicker-hours, .ui-timepicker-minutes { padding: 0.2em; }
18
+
19
+ .ui-timepicker-table .ui-timepicker-title { line-height: 1.8em; text-align: center; }
20
+ .ui-timepicker-table td { padding: 0.1em; width: 2.2em; }
21
+ .ui-timepicker-table th.periods { padding: 0.1em; width: 2.2em; }
22
+
23
+ /* span for disabled cells */
24
+ .ui-timepicker-table td span {
25
+ display:block;
26
+ padding:0.2em 0.3em 0.2em 0.5em;
27
+ width: 1.2em;
28
+
29
+ text-align:right;
30
+ text-decoration:none;
31
+ }
32
+ /* anchors for clickable cells */
33
+ .ui-timepicker-table td a {
34
+ display:block;
35
+ padding:0.2em 0.3em 0.2em 0.5em;
36
+ width: 1.2em;
37
+ cursor: pointer;
38
+ text-align:right;
39
+ text-decoration:none;
40
+ }
41
+
42
+
43
+ /* buttons and button pane styling */
44
+ .ui-timepicker .ui-timepicker-buttonpane {
45
+ background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0;
46
+ }
47
+ .ui-timepicker .ui-timepicker-buttonpane button { margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
48
+ /* The close button */
49
+ .ui-timepicker .ui-timepicker-close { float: right }
50
+
51
+ /* the now button */
52
+ .ui-timepicker .ui-timepicker-now { float: left; }
53
+
54
+ /* the deselect button */
55
+ .ui-timepicker .ui-timepicker-deselect { float: left; }
56
+
57
+
@@ -0,0 +1,1496 @@
1
+ /*
2
+ * jQuery UI Timepicker
3
+ *
4
+ * Copyright 2010-2013, 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 settings 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 ($) {
42
+
43
+ $.extend($.ui, { timepicker: { version: "0.3.3"} });
44
+
45
+ var PROP_NAME = 'timepicker',
46
+ 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._disabledInputs = []; // List of time picker inputs that have been disabled
57
+ this._timepickerShowing = false; // True if the popup picker is showing , false if not
58
+ this._inDialog = false; // True if showing within a "dialog", false if not
59
+ this._dialogClass = 'ui-timepicker-dialog'; // The name of the dialog marker class
60
+ this._mainDivId = 'ui-timepicker-div'; // The ID of the main timepicker division
61
+ this._inlineClass = 'ui-timepicker-inline'; // The name of the inline marker class
62
+ this._currentClass = 'ui-timepicker-current'; // The name of the current hour / minutes marker class
63
+ this._dayOverClass = 'ui-timepicker-days-cell-over'; // The name of the day hover marker class
64
+
65
+ this.regional = []; // Available regional settings, indexed by language code
66
+ this.regional[''] = { // Default regional settings
67
+ hourText: 'Hour', // Display text for hours section
68
+ minuteText: 'Minute', // Display text for minutes link
69
+ amPmText: ['AM', 'PM'], // Display text for AM PM
70
+ closeButtonText: 'Done', // Text for the confirmation button (ok button)
71
+ nowButtonText: 'Now', // Text for the now button
72
+ deselectButtonText: 'Deselect' // Text for the deselect button
73
+ };
74
+ this._defaults = { // Global defaults for all the time picker instances
75
+ showOn: 'focus', // 'focus' for popup on focus,
76
+ // 'button' for trigger button, or 'both' for either (not yet implemented)
77
+ button: null, // 'button' element that will trigger the timepicker
78
+ showAnim: 'fadeIn', // Name of jQuery animation for popup
79
+ showOptions: {}, // Options for enhanced animations
80
+ appendText: '', // Display text following the input box, e.g. showing the format
81
+
82
+ beforeShow: null, // Define a callback function executed before the timepicker is shown
83
+ onSelect: null, // Define a callback function when a hour / minutes is selected
84
+ onClose: null, // Define a callback function when the timepicker is closed
85
+
86
+ timeSeparator: ':', // The character to use to separate hours and minutes.
87
+ periodSeparator: ' ', // The character to use to separate the time from the time period.
88
+ showPeriod: false, // Define whether or not to show AM/PM with selected time
89
+ showPeriodLabels: true, // Show the AM/PM labels on the left of the time picker
90
+ showLeadingZero: true, // Define whether or not to show a leading zero for hours < 10. [true/false]
91
+ showMinutesLeadingZero: true, // Define whether or not to show a leading zero for minutes < 10.
92
+ altField: '', // Selector for an alternate field to store selected time into
93
+ defaultTime: 'now', // Used as default time when input field is empty or for inline timePicker
94
+ // (set to 'now' for the current time, '' for no highlighted time)
95
+ myPosition: 'left top', // Position of the dialog relative to the input.
96
+ // see the position utility for more info : http://jqueryui.com/demos/position/
97
+ atPosition: 'left bottom', // Position of the input element to match
98
+ // Note : if the position utility is not loaded, the timepicker will attach left top to left bottom
99
+ //NEW: 2011-02-03
100
+ onHourShow: null, // callback for enabling / disabling on selectable hours ex : function(hour) { return true; }
101
+ onMinuteShow: null, // callback for enabling / disabling on time selection ex : function(hour,minute) { return true; }
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
+ manual: [] // optional extra manual entries for 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
+ maxTime: {
125
+ hour: null,
126
+ minute: null
127
+ },
128
+ minTime: {
129
+ hour: null,
130
+ minute: null
131
+ }
132
+
133
+ };
134
+ $.extend(this._defaults, this.regional['']);
135
+
136
+ this.tpDiv = $('<div id="' + this._mainDivId + '" class="ui-timepicker ui-widget ui-helper-clearfix ui-corner-all " style="display: none"></div>');
137
+ }
138
+
139
+ $.extend(Timepicker.prototype, {
140
+ /* Class name added to elements to indicate already configured with a time picker. */
141
+ markerClassName: 'hasTimepicker',
142
+
143
+ /* Debug logging (if enabled). */
144
+ log: function () {
145
+ if (this.debug)
146
+ console.log.apply('', arguments);
147
+ },
148
+
149
+ _widgetTimepicker: function () {
150
+ return this.tpDiv;
151
+ },
152
+
153
+ /* Override the default settings for all instances of the time picker.
154
+ @param settings object - the new settings to use as defaults (anonymous object)
155
+ @return the manager object */
156
+ setDefaults: function (settings) {
157
+ extendRemove(this._defaults, settings || {});
158
+ return this;
159
+ },
160
+
161
+ /* Attach the time picker to a jQuery selection.
162
+ @param target element - the target input field or division or span
163
+ @param settings object - the new settings to use for this time picker instance (anonymous) */
164
+ _attachTimepicker: function (target, settings) {
165
+ // check for settings on the control itself - in namespace 'time:'
166
+ var inlineSettings = null;
167
+ for (var attrName in this._defaults) {
168
+ var attrValue = target.getAttribute('time:' + attrName);
169
+ if (attrValue) {
170
+ inlineSettings = inlineSettings || {};
171
+ try {
172
+ inlineSettings[attrName] = eval(attrValue);
173
+ } catch (err) {
174
+ inlineSettings[attrName] = attrValue;
175
+ }
176
+ }
177
+ }
178
+ var nodeName = target.nodeName.toLowerCase();
179
+ var inline = (nodeName == 'div' || nodeName == 'span');
180
+
181
+ if (!target.id) {
182
+ this.uuid += 1;
183
+ target.id = 'tp' + this.uuid;
184
+ }
185
+ var inst = this._newInst($(target), inline);
186
+ inst.settings = $.extend({}, settings || {}, inlineSettings || {});
187
+ if (nodeName == 'input') {
188
+ this._connectTimepicker(target, inst);
189
+ // init inst.hours and inst.minutes from the input value
190
+ this._setTimeFromField(inst);
191
+ } else if (inline) {
192
+ this._inlineTimepicker(target, inst);
193
+ }
194
+
195
+
196
+ },
197
+
198
+ /* Create a new instance object. */
199
+ _newInst: function (target, inline) {
200
+ var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars
201
+ return {
202
+ id: id, input: target, // associated target
203
+ inline: inline, // is timepicker inline or not :
204
+ tpDiv: (!inline ? this.tpDiv : // presentation div
205
+ $('<div class="' + this._inlineClass + ' ui-timepicker ui-widget ui-helper-clearfix"></div>'))
206
+ };
207
+ },
208
+
209
+ /* Attach the time picker to an input field. */
210
+ _connectTimepicker: function (target, inst) {
211
+ var input = $(target);
212
+ inst.append = $([]);
213
+ inst.trigger = $([]);
214
+ if (input.hasClass(this.markerClassName)) { return; }
215
+ this._attachments(input, inst);
216
+ input.addClass(this.markerClassName).
217
+ keydown(this._doKeyDown).
218
+ keyup(this._doKeyUp).
219
+ bind("setData.timepicker", function (event, key, value) {
220
+ inst.settings[key] = value;
221
+ }).
222
+ bind("getData.timepicker", function (event, key) {
223
+ return this._get(inst, key);
224
+ });
225
+ $.data(target, PROP_NAME, inst);
226
+ },
227
+
228
+ /* Handle keystrokes. */
229
+ _doKeyDown: function (event) {
230
+ var inst = $.timepicker._getInst(event.target);
231
+ var handled = true;
232
+ inst._keyEvent = true;
233
+ if ($.timepicker._timepickerShowing) {
234
+ switch (event.keyCode) {
235
+ case 9: $.timepicker._hideTimepicker();
236
+ handled = false;
237
+ break; // hide on tab out
238
+ case 13:
239
+ $.timepicker._updateSelectedValue(inst);
240
+ $.timepicker._hideTimepicker();
241
+
242
+ return false; // don't submit the form
243
+ break; // select the value on enter
244
+ case 27: $.timepicker._hideTimepicker();
245
+ break; // hide on escape
246
+ default: handled = false;
247
+ }
248
+ }
249
+ else if (event.keyCode == 36 && event.ctrlKey) { // display the time picker on ctrl+home
250
+ $.timepicker._showTimepicker(this);
251
+ }
252
+ else {
253
+ handled = false;
254
+ }
255
+ if (handled) {
256
+ event.preventDefault();
257
+ event.stopPropagation();
258
+ }
259
+ },
260
+
261
+ /* Update selected time on keyUp */
262
+ /* Added verion 0.0.5 */
263
+ _doKeyUp: function (event) {
264
+ var inst = $.timepicker._getInst(event.target);
265
+ $.timepicker._setTimeFromField(inst);
266
+ $.timepicker._updateTimepicker(inst);
267
+ },
268
+
269
+ /* Make attachments based on settings. */
270
+ _attachments: function (input, inst) {
271
+ var appendText = this._get(inst, 'appendText');
272
+ var isRTL = this._get(inst, 'isRTL');
273
+ if (inst.append) { inst.append.remove(); }
274
+ if (appendText) {
275
+ inst.append = $('<span class="' + this._appendClass + '">' + appendText + '</span>');
276
+ input[isRTL ? 'before' : 'after'](inst.append);
277
+ }
278
+ input.unbind('focus.timepicker', this._showTimepicker);
279
+ input.unbind('click.timepicker', this._adjustZIndex);
280
+
281
+ if (inst.trigger) { inst.trigger.remove(); }
282
+
283
+ var showOn = this._get(inst, 'showOn');
284
+ if (showOn == 'focus' || showOn == 'both') { // pop-up time picker when in the marked field
285
+ input.bind("focus.timepicker", this._showTimepicker);
286
+ input.bind("click.timepicker", this._adjustZIndex);
287
+ }
288
+ if (showOn == 'button' || showOn == 'both') { // pop-up time picker when 'button' element is clicked
289
+ var button = this._get(inst, 'button');
290
+
291
+ // Add button if button element is not set
292
+ if(button == null) {
293
+ button = $('<button class="ui-timepicker-trigger" type="button">...</button>');
294
+ input.after(button);
295
+ }
296
+
297
+ $(button).bind("click.timepicker", function () {
298
+ if ($.timepicker._timepickerShowing && $.timepicker._lastInput == input[0]) {
299
+ $.timepicker._hideTimepicker();
300
+ } else if (!inst.input.is(':disabled')) {
301
+ $.timepicker._showTimepicker(input[0]);
302
+ }
303
+ return false;
304
+ });
305
+
306
+ }
307
+ },
308
+
309
+
310
+ /* Attach an inline time picker to a div. */
311
+ _inlineTimepicker: function(target, inst) {
312
+ var divSpan = $(target);
313
+ if (divSpan.hasClass(this.markerClassName))
314
+ return;
315
+ divSpan.addClass(this.markerClassName).append(inst.tpDiv).
316
+ bind("setData.timepicker", function(event, key, value){
317
+ inst.settings[key] = value;
318
+ }).bind("getData.timepicker", function(event, key){
319
+ return this._get(inst, key);
320
+ });
321
+ $.data(target, PROP_NAME, inst);
322
+
323
+ this._setTimeFromField(inst);
324
+ this._updateTimepicker(inst);
325
+ inst.tpDiv.show();
326
+ },
327
+
328
+ _adjustZIndex: function(input) {
329
+ input = input.target || input;
330
+ var inst = $.timepicker._getInst(input);
331
+ inst.tpDiv.css('zIndex', $.timepicker._getZIndex(input) +1);
332
+ },
333
+
334
+ /* Pop-up the time picker for a given input field.
335
+ @param input element - the input field attached to the time picker or
336
+ event - if triggered by focus */
337
+ _showTimepicker: function (input) {
338
+ input = input.target || input;
339
+ if (input.nodeName.toLowerCase() != 'input') { input = $('input', input.parentNode)[0]; } // find from button/image trigger
340
+
341
+ if ($.timepicker._isDisabledTimepicker(input) || $.timepicker._lastInput == input) { return; } // already here
342
+
343
+ // fix v 0.0.8 - close current timepicker before showing another one
344
+ $.timepicker._hideTimepicker();
345
+
346
+ var inst = $.timepicker._getInst(input);
347
+ if ($.timepicker._curInst && $.timepicker._curInst != inst) {
348
+ $.timepicker._curInst.tpDiv.stop(true, true);
349
+ }
350
+ var beforeShow = $.timepicker._get(inst, 'beforeShow');
351
+ extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {}));
352
+ inst.lastVal = null;
353
+ $.timepicker._lastInput = input;
354
+
355
+ $.timepicker._setTimeFromField(inst);
356
+
357
+ // calculate default position
358
+ if ($.timepicker._inDialog) { input.value = ''; } // hide cursor
359
+ if (!$.timepicker._pos) { // position below input
360
+ $.timepicker._pos = $.timepicker._findPos(input);
361
+ $.timepicker._pos[1] += input.offsetHeight; // add the height
362
+ }
363
+ var isFixed = false;
364
+ $(input).parents().each(function () {
365
+ isFixed |= $(this).css('position') == 'fixed';
366
+ return !isFixed;
367
+ });
368
+
369
+ var offset = { left: $.timepicker._pos[0], top: $.timepicker._pos[1] };
370
+
371
+ $.timepicker._pos = null;
372
+ // determine sizing offscreen
373
+ inst.tpDiv.css({ position: 'absolute', display: 'block', top: '-1000px' });
374
+ $.timepicker._updateTimepicker(inst);
375
+
376
+
377
+ // position with the ui position utility, if loaded
378
+ if ( ( ! inst.inline ) && ( typeof $.ui.position == 'object' ) ) {
379
+ inst.tpDiv.position({
380
+ of: inst.input,
381
+ my: $.timepicker._get( inst, 'myPosition' ),
382
+ at: $.timepicker._get( inst, 'atPosition' ),
383
+ // offset: $( "#offset" ).val(),
384
+ // using: using,
385
+ collision: 'flip'
386
+ });
387
+ var offset = inst.tpDiv.offset();
388
+ $.timepicker._pos = [offset.top, offset.left];
389
+ }
390
+
391
+
392
+ // reset clicked state
393
+ inst._hoursClicked = false;
394
+ inst._minutesClicked = false;
395
+
396
+ // fix width for dynamic number of time pickers
397
+ // and adjust position before showing
398
+ offset = $.timepicker._checkOffset(inst, offset, isFixed);
399
+ inst.tpDiv.css({ position: ($.timepicker._inDialog && $.blockUI ?
400
+ 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
401
+ left: offset.left + 'px', top: offset.top + 'px'
402
+ });
403
+ if ( ! inst.inline ) {
404
+ var showAnim = $.timepicker._get(inst, 'showAnim');
405
+ var duration = $.timepicker._get(inst, 'duration');
406
+
407
+ var postProcess = function () {
408
+ $.timepicker._timepickerShowing = true;
409
+ var borders = $.timepicker._getBorders(inst.tpDiv);
410
+ inst.tpDiv.find('iframe.ui-timepicker-cover'). // IE6- only
411
+ css({ left: -borders[0], top: -borders[1],
412
+ width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight()
413
+ });
414
+ };
415
+
416
+ // Fixed the zIndex problem for real (I hope) - FG - v 0.2.9
417
+ $.timepicker._adjustZIndex(input);
418
+ //inst.tpDiv.css('zIndex', $.timepicker._getZIndex(input) +1);
419
+
420
+ if ($.effects && $.effects[showAnim]) {
421
+ inst.tpDiv.show(showAnim, $.timepicker._get(inst, 'showOptions'), duration, postProcess);
422
+ }
423
+ else {
424
+ inst.tpDiv.show((showAnim ? duration : null), postProcess);
425
+ }
426
+ if (!showAnim || !duration) { postProcess(); }
427
+ if (inst.input.is(':visible') && !inst.input.is(':disabled')) { inst.input.focus(); }
428
+ $.timepicker._curInst = inst;
429
+ }
430
+ },
431
+
432
+ // This is an enhanced copy of the zIndex function of UI core 1.8.?? For backward compatibility.
433
+ // Enhancement returns maximum zindex value discovered while traversing parent elements,
434
+ // rather than the first zindex value found. Ensures the timepicker popup will be in front,
435
+ // even in funky scenarios like non-jq dialog containers with large fixed zindex values and
436
+ // nested zindex-influenced elements of their own.
437
+ _getZIndex: function (target) {
438
+ var elem = $(target);
439
+ var maxValue = 0;
440
+ var position, value;
441
+ while (elem.length && elem[0] !== document) {
442
+ position = elem.css("position");
443
+ if (position === "absolute" || position === "relative" || position === "fixed") {
444
+ value = parseInt(elem.css("zIndex"), 10);
445
+ if (!isNaN(value) && value !== 0) {
446
+ if (value > maxValue) { maxValue = value; }
447
+ }
448
+ }
449
+ elem = elem.parent();
450
+ }
451
+
452
+ return maxValue;
453
+ },
454
+
455
+ /* Refresh the time picker
456
+ @param target element - The target input field or inline container element. */
457
+ _refreshTimepicker: function(target) {
458
+ var inst = this._getInst(target);
459
+ if (inst) {
460
+ this._updateTimepicker(inst);
461
+ }
462
+ },
463
+
464
+
465
+ /* Generate the time picker content. */
466
+ _updateTimepicker: function (inst) {
467
+ inst.tpDiv.empty().append(this._generateHTML(inst));
468
+ this._rebindDialogEvents(inst);
469
+
470
+ },
471
+
472
+ _rebindDialogEvents: function (inst) {
473
+ var borders = $.timepicker._getBorders(inst.tpDiv),
474
+ self = this;
475
+ inst.tpDiv
476
+ .find('iframe.ui-timepicker-cover') // IE6- only
477
+ .css({ left: -borders[0], top: -borders[1],
478
+ width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight()
479
+ })
480
+ .end()
481
+ // after the picker html is appended bind the click & double click events (faster in IE this way
482
+ // then letting the browser interpret the inline events)
483
+ // the binding for the minute cells also exists in _updateMinuteDisplay
484
+ .find('.ui-timepicker-minute-cell')
485
+ .unbind()
486
+ .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectMinutes, this))
487
+ .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectMinutes, this))
488
+ .end()
489
+ .find('.ui-timepicker-hour-cell')
490
+ .unbind()
491
+ .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectHours, this))
492
+ .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectHours, this))
493
+ .end()
494
+ .find('.ui-timepicker td a')
495
+ .unbind()
496
+ .bind('mouseout', function () {
497
+ $(this).removeClass('ui-state-hover');
498
+ if (this.className.indexOf('ui-timepicker-prev') != -1) $(this).removeClass('ui-timepicker-prev-hover');
499
+ if (this.className.indexOf('ui-timepicker-next') != -1) $(this).removeClass('ui-timepicker-next-hover');
500
+ })
501
+ .bind('mouseover', function () {
502
+ if ( ! self._isDisabledTimepicker(inst.inline ? inst.tpDiv.parent()[0] : inst.input[0])) {
503
+ $(this).parents('.ui-timepicker-calendar').find('a').removeClass('ui-state-hover');
504
+ $(this).addClass('ui-state-hover');
505
+ if (this.className.indexOf('ui-timepicker-prev') != -1) $(this).addClass('ui-timepicker-prev-hover');
506
+ if (this.className.indexOf('ui-timepicker-next') != -1) $(this).addClass('ui-timepicker-next-hover');
507
+ }
508
+ })
509
+ .end()
510
+ .find('.' + this._dayOverClass + ' a')
511
+ .trigger('mouseover')
512
+ .end()
513
+ .find('.ui-timepicker-now').bind("click", function(e) {
514
+ $.timepicker.selectNow(e);
515
+ }).end()
516
+ .find('.ui-timepicker-deselect').bind("click",function(e) {
517
+ $.timepicker.deselectTime(e);
518
+ }).end()
519
+ .find('.ui-timepicker-close').bind("click",function(e) {
520
+ $.timepicker._hideTimepicker();
521
+ }).end();
522
+ },
523
+
524
+ /* Generate the HTML for the current state of the time picker. */
525
+ _generateHTML: function (inst) {
526
+
527
+ var h, m, row, col, html, hoursHtml, minutesHtml = '',
528
+ showPeriod = (this._get(inst, 'showPeriod') == true),
529
+ showPeriodLabels = (this._get(inst, 'showPeriodLabels') == true),
530
+ showLeadingZero = (this._get(inst, 'showLeadingZero') == true),
531
+ showHours = (this._get(inst, 'showHours') == true),
532
+ showMinutes = (this._get(inst, 'showMinutes') == true),
533
+ amPmText = this._get(inst, 'amPmText'),
534
+ rows = this._get(inst, 'rows'),
535
+ amRows = 0,
536
+ pmRows = 0,
537
+ amItems = 0,
538
+ pmItems = 0,
539
+ amFirstRow = 0,
540
+ pmFirstRow = 0,
541
+ hours = Array(),
542
+ hours_options = this._get(inst, 'hours'),
543
+ hoursPerRow = null,
544
+ hourCounter = 0,
545
+ hourLabel = this._get(inst, 'hourText'),
546
+ showCloseButton = this._get(inst, 'showCloseButton'),
547
+ closeButtonText = this._get(inst, 'closeButtonText'),
548
+ showNowButton = this._get(inst, 'showNowButton'),
549
+ nowButtonText = this._get(inst, 'nowButtonText'),
550
+ showDeselectButton = this._get(inst, 'showDeselectButton'),
551
+ deselectButtonText = this._get(inst, 'deselectButtonText'),
552
+ showButtonPanel = showCloseButton || showNowButton || showDeselectButton;
553
+
554
+
555
+
556
+ // prepare all hours and minutes, makes it easier to distribute by rows
557
+ for (h = hours_options.starts; h <= hours_options.ends; h++) {
558
+ hours.push (h);
559
+ }
560
+ hoursPerRow = Math.ceil(hours.length / rows); // always round up
561
+
562
+ if (showPeriodLabels) {
563
+ for (hourCounter = 0; hourCounter < hours.length; hourCounter++) {
564
+ if (hours[hourCounter] < 12) {
565
+ amItems++;
566
+ }
567
+ else {
568
+ pmItems++;
569
+ }
570
+ }
571
+ hourCounter = 0;
572
+
573
+ amRows = Math.floor(amItems / hours.length * rows);
574
+ pmRows = Math.floor(pmItems / hours.length * rows);
575
+
576
+ // assign the extra row to the period that is more densely populated
577
+ if (rows != amRows + pmRows) {
578
+ // Make sure: AM Has Items and either PM Does Not, AM has no rows yet, or AM is more dense
579
+ if (amItems && (!pmItems || !amRows || (pmRows && amItems / amRows >= pmItems / pmRows))) {
580
+ amRows++;
581
+ } else {
582
+ pmRows++;
583
+ }
584
+ }
585
+ amFirstRow = Math.min(amRows, 1);
586
+ pmFirstRow = amRows + 1;
587
+
588
+ if (amRows == 0) {
589
+ hoursPerRow = Math.ceil(pmItems / pmRows);
590
+ } else if (pmRows == 0) {
591
+ hoursPerRow = Math.ceil(amItems / amRows);
592
+ } else {
593
+ hoursPerRow = Math.ceil(Math.max(amItems / amRows, pmItems / pmRows));
594
+ }
595
+ }
596
+
597
+
598
+ html = '<table class="ui-timepicker-table ui-widget-content ui-corner-all"><tr>';
599
+
600
+ if (showHours) {
601
+
602
+ html += '<td class="ui-timepicker-hours">' +
603
+ '<div class="ui-timepicker-title ui-widget-header ui-helper-clearfix ui-corner-all">' +
604
+ hourLabel +
605
+ '</div>' +
606
+ '<table class="ui-timepicker">';
607
+
608
+ for (row = 1; row <= rows; row++) {
609
+ html += '<tr>';
610
+ // AM
611
+ if (row == amFirstRow && showPeriodLabels) {
612
+ html += '<th rowspan="' + amRows.toString() + '" class="periods" scope="row">' + amPmText[0] + '</th>';
613
+ }
614
+ // PM
615
+ if (row == pmFirstRow && showPeriodLabels) {
616
+ html += '<th rowspan="' + pmRows.toString() + '" class="periods" scope="row">' + amPmText[1] + '</th>';
617
+ }
618
+ for (col = 1; col <= hoursPerRow; col++) {
619
+ if (showPeriodLabels && row < pmFirstRow && hours[hourCounter] >= 12) {
620
+ html += this._generateHTMLHourCell(inst, undefined, showPeriod, showLeadingZero);
621
+ } else {
622
+ html += this._generateHTMLHourCell(inst, hours[hourCounter], showPeriod, showLeadingZero);
623
+ hourCounter++;
624
+ }
625
+ }
626
+ html += '</tr>';
627
+ }
628
+ html += '</table>' + // Close the hours cells table
629
+ '</td>'; // Close the Hour td
630
+ }
631
+
632
+ if (showMinutes) {
633
+ html += '<td class="ui-timepicker-minutes">';
634
+ html += this._generateHTMLMinutes(inst);
635
+ html += '</td>';
636
+ }
637
+
638
+ html += '</tr>';
639
+
640
+
641
+ if (showButtonPanel) {
642
+ var buttonPanel = '<tr><td colspan="3"><div class="ui-timepicker-buttonpane ui-widget-content">';
643
+ if (showNowButton) {
644
+ buttonPanel += '<button type="button" class="ui-timepicker-now ui-state-default ui-corner-all" '
645
+ + ' data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" >'
646
+ + nowButtonText + '</button>';
647
+ }
648
+ if (showDeselectButton) {
649
+ buttonPanel += '<button type="button" class="ui-timepicker-deselect ui-state-default ui-corner-all" '
650
+ + ' data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" >'
651
+ + deselectButtonText + '</button>';
652
+ }
653
+ if (showCloseButton) {
654
+ buttonPanel += '<button type="button" class="ui-timepicker-close ui-state-default ui-corner-all" '
655
+ + ' data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" >'
656
+ + closeButtonText + '</button>';
657
+ }
658
+
659
+ html += buttonPanel + '</div></td></tr>';
660
+ }
661
+ html += '</table>';
662
+
663
+ return html;
664
+ },
665
+
666
+ /* Special function that update the minutes selection in currently visible timepicker
667
+ * called on hour selection when onMinuteShow is defined */
668
+ _updateMinuteDisplay: function (inst) {
669
+ var newHtml = this._generateHTMLMinutes(inst);
670
+ inst.tpDiv.find('td.ui-timepicker-minutes').html(newHtml);
671
+ this._rebindDialogEvents(inst);
672
+ // after the picker html is appended bind the click & double click events (faster in IE this way
673
+ // then letting the browser interpret the inline events)
674
+ // yes I know, duplicate code, sorry
675
+ /* .find('.ui-timepicker-minute-cell')
676
+ .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectMinutes, this))
677
+ .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectMinutes, this));
678
+ */
679
+
680
+ },
681
+
682
+ /*
683
+ * Generate the minutes table
684
+ * This is separated from the _generateHTML function because is can be called separately (when hours changes)
685
+ */
686
+ _generateHTMLMinutes: function (inst) {
687
+
688
+ var m, row, html = '',
689
+ rows = this._get(inst, 'rows'),
690
+ minutes = Array(),
691
+ minutes_options = this._get(inst, 'minutes'),
692
+ minutesPerRow = null,
693
+ minuteCounter = 0,
694
+ showMinutesLeadingZero = (this._get(inst, 'showMinutesLeadingZero') == true),
695
+ onMinuteShow = this._get(inst, 'onMinuteShow'),
696
+ minuteLabel = this._get(inst, 'minuteText');
697
+
698
+ if ( ! minutes_options.starts) {
699
+ minutes_options.starts = 0;
700
+ }
701
+ if ( ! minutes_options.ends) {
702
+ minutes_options.ends = 59;
703
+ }
704
+ if ( ! minutes_options.manual) {
705
+ minutes_options.manual = [];
706
+ }
707
+ for (m = minutes_options.starts; m <= minutes_options.ends; m += minutes_options.interval) {
708
+ minutes.push(m);
709
+ }
710
+ for (i = 0; i < minutes_options.manual.length;i++) {
711
+ var currMin = minutes_options.manual[i];
712
+
713
+ // Validate & filter duplicates of manual minute input
714
+ if (typeof currMin != 'number' || currMin < 0 || currMin > 59 || $.inArray(currMin, minutes) >= 0) {
715
+ continue;
716
+ }
717
+ minutes.push(currMin);
718
+ }
719
+
720
+ // Sort to get correct order after adding manual minutes
721
+ // Use compare function to sort by number, instead of string (default)
722
+ minutes.sort(function(a, b) {
723
+ return a-b;
724
+ });
725
+
726
+ minutesPerRow = Math.round(minutes.length / rows + 0.49); // always round up
727
+
728
+ /*
729
+ * The minutes table
730
+ */
731
+ // if currently selected minute is not enabled, we have a problem and need to select a new minute.
732
+ if (onMinuteShow &&
733
+ (onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours , inst.minutes]) == false) ) {
734
+ // loop minutes and select first available
735
+ for (minuteCounter = 0; minuteCounter < minutes.length; minuteCounter += 1) {
736
+ m = minutes[minuteCounter];
737
+ if (onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours, m])) {
738
+ inst.minutes = m;
739
+ break;
740
+ }
741
+ }
742
+ }
743
+
744
+
745
+
746
+ html += '<div class="ui-timepicker-title ui-widget-header ui-helper-clearfix ui-corner-all">' +
747
+ minuteLabel +
748
+ '</div>' +
749
+ '<table class="ui-timepicker">';
750
+
751
+ minuteCounter = 0;
752
+ for (row = 1; row <= rows; row++) {
753
+ html += '<tr>';
754
+ while (minuteCounter < row * minutesPerRow) {
755
+ var m = minutes[minuteCounter];
756
+ var displayText = '';
757
+ if (m !== undefined ) {
758
+ displayText = (m < 10) && showMinutesLeadingZero ? "0" + m.toString() : m.toString();
759
+ }
760
+ html += this._generateHTMLMinuteCell(inst, m, displayText);
761
+ minuteCounter++;
762
+ }
763
+ html += '</tr>';
764
+ }
765
+
766
+ html += '</table>';
767
+
768
+ return html;
769
+ },
770
+
771
+ /* Generate the content of a "Hour" cell */
772
+ _generateHTMLHourCell: function (inst, hour, showPeriod, showLeadingZero) {
773
+
774
+ var displayHour = hour;
775
+ if ((hour > 12) && showPeriod) {
776
+ displayHour = hour - 12;
777
+ }
778
+ if ((displayHour == 0) && showPeriod) {
779
+ displayHour = 12;
780
+ }
781
+ if ((displayHour < 10) && showLeadingZero) {
782
+ displayHour = '0' + displayHour;
783
+ }
784
+
785
+ var html = "";
786
+ var enabled = true;
787
+ var onHourShow = this._get(inst, 'onHourShow'); //custom callback
788
+ var maxTime = this._get(inst, 'maxTime');
789
+ var minTime = this._get(inst, 'minTime');
790
+
791
+ if (hour == undefined) {
792
+ html = '<td><span class="ui-state-default ui-state-disabled">&nbsp;</span></td>';
793
+ return html;
794
+ }
795
+
796
+ if (onHourShow) {
797
+ enabled = onHourShow.apply((inst.input ? inst.input[0] : null), [hour]);
798
+ }
799
+
800
+ if (enabled) {
801
+ if ( !isNaN(parseInt(maxTime.hour)) && hour > maxTime.hour ) enabled = false;
802
+ if ( !isNaN(parseInt(minTime.hour)) && hour < minTime.hour ) enabled = false;
803
+ }
804
+
805
+ if (enabled) {
806
+ html = '<td class="ui-timepicker-hour-cell" data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" data-hour="' + hour.toString() + '">' +
807
+ '<a class="ui-state-default ' +
808
+ (hour == inst.hours ? 'ui-state-active' : '') +
809
+ '">' +
810
+ displayHour.toString() +
811
+ '</a></td>';
812
+ }
813
+ else {
814
+ html =
815
+ '<td>' +
816
+ '<span class="ui-state-default ui-state-disabled ' +
817
+ (hour == inst.hours ? ' ui-state-active ' : ' ') +
818
+ '">' +
819
+ displayHour.toString() +
820
+ '</span>' +
821
+ '</td>';
822
+ }
823
+ return html;
824
+ },
825
+
826
+ /* Generate the content of a "Hour" cell */
827
+ _generateHTMLMinuteCell: function (inst, minute, displayText) {
828
+ var html = "";
829
+ var enabled = true;
830
+ var hour = inst.hours;
831
+ var onMinuteShow = this._get(inst, 'onMinuteShow'); //custom callback
832
+ var maxTime = this._get(inst, 'maxTime');
833
+ var minTime = this._get(inst, 'minTime');
834
+
835
+ if (onMinuteShow) {
836
+ //NEW: 2011-02-03 we should give the hour as a parameter as well!
837
+ enabled = onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours,minute]); //trigger callback
838
+ }
839
+
840
+ if (minute == undefined) {
841
+ html = '<td><span class="ui-state-default ui-state-disabled">&nbsp;</span></td>';
842
+ return html;
843
+ }
844
+
845
+ if (enabled && hour !== null) {
846
+ if ( !isNaN(parseInt(maxTime.hour)) && !isNaN(parseInt(maxTime.minute)) && hour >= maxTime.hour && minute > maxTime.minute ) enabled = false;
847
+ if ( !isNaN(parseInt(minTime.hour)) && !isNaN(parseInt(minTime.minute)) && hour <= minTime.hour && minute < minTime.minute ) enabled = false;
848
+ }
849
+
850
+ if (enabled) {
851
+ html = '<td class="ui-timepicker-minute-cell" data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" data-minute="' + minute.toString() + '" >' +
852
+ '<a class="ui-state-default ' +
853
+ (minute == inst.minutes ? 'ui-state-active' : '') +
854
+ '" >' +
855
+ displayText +
856
+ '</a></td>';
857
+ }
858
+ else {
859
+
860
+ html = '<td>' +
861
+ '<span class="ui-state-default ui-state-disabled" >' +
862
+ displayText +
863
+ '</span>' +
864
+ '</td>';
865
+ }
866
+ return html;
867
+ },
868
+
869
+
870
+ /* Detach a timepicker from its control.
871
+ @param target element - the target input field or division or span */
872
+ _destroyTimepicker: function(target) {
873
+ var $target = $(target);
874
+ var inst = $.data(target, PROP_NAME);
875
+ if (!$target.hasClass(this.markerClassName)) {
876
+ return;
877
+ }
878
+ var nodeName = target.nodeName.toLowerCase();
879
+ $.removeData(target, PROP_NAME);
880
+ if (nodeName == 'input') {
881
+ inst.append.remove();
882
+ inst.trigger.remove();
883
+ $target.removeClass(this.markerClassName)
884
+ .unbind('focus.timepicker', this._showTimepicker)
885
+ .unbind('click.timepicker', this._adjustZIndex);
886
+ } else if (nodeName == 'div' || nodeName == 'span')
887
+ $target.removeClass(this.markerClassName).empty();
888
+ },
889
+
890
+ /* Enable the date picker to a jQuery selection.
891
+ @param target element - the target input field or division or span */
892
+ _enableTimepicker: function(target) {
893
+ var $target = $(target),
894
+ target_id = $target.attr('id'),
895
+ inst = $.data(target, PROP_NAME);
896
+
897
+ if (!$target.hasClass(this.markerClassName)) {
898
+ return;
899
+ }
900
+ var nodeName = target.nodeName.toLowerCase();
901
+ if (nodeName == 'input') {
902
+ target.disabled = false;
903
+ var button = this._get(inst, 'button');
904
+ $(button).removeClass('ui-state-disabled').disabled = false;
905
+ inst.trigger.filter('button').
906
+ each(function() { this.disabled = false; }).end();
907
+ }
908
+ else if (nodeName == 'div' || nodeName == 'span') {
909
+ var inline = $target.children('.' + this._inlineClass);
910
+ inline.children().removeClass('ui-state-disabled');
911
+ inline.find('button').each(
912
+ function() { this.disabled = false }
913
+ )
914
+ }
915
+ this._disabledInputs = $.map(this._disabledInputs,
916
+ function(value) { return (value == target_id ? null : value); }); // delete entry
917
+ },
918
+
919
+ /* Disable the time picker to a jQuery selection.
920
+ @param target element - the target input field or division or span */
921
+ _disableTimepicker: function(target) {
922
+ var $target = $(target);
923
+ var inst = $.data(target, PROP_NAME);
924
+ if (!$target.hasClass(this.markerClassName)) {
925
+ return;
926
+ }
927
+ var nodeName = target.nodeName.toLowerCase();
928
+ if (nodeName == 'input') {
929
+ var button = this._get(inst, 'button');
930
+
931
+ $(button).addClass('ui-state-disabled').disabled = true;
932
+ target.disabled = true;
933
+
934
+ inst.trigger.filter('button').
935
+ each(function() { this.disabled = true; }).end();
936
+
937
+ }
938
+ else if (nodeName == 'div' || nodeName == 'span') {
939
+ var inline = $target.children('.' + this._inlineClass);
940
+ inline.children().addClass('ui-state-disabled');
941
+ inline.find('button').each(
942
+ function() { this.disabled = true }
943
+ )
944
+
945
+ }
946
+ this._disabledInputs = $.map(this._disabledInputs,
947
+ function(value) { return (value == target ? null : value); }); // delete entry
948
+ this._disabledInputs[this._disabledInputs.length] = $target.attr('id');
949
+ },
950
+
951
+ /* Is the first field in a jQuery collection disabled as a timepicker?
952
+ @param target_id element - the target input field or division or span
953
+ @return boolean - true if disabled, false if enabled */
954
+ _isDisabledTimepicker: function (target_id) {
955
+ if ( ! target_id) { return false; }
956
+ for (var i = 0; i < this._disabledInputs.length; i++) {
957
+ if (this._disabledInputs[i] == target_id) { return true; }
958
+ }
959
+ return false;
960
+ },
961
+
962
+ /* Check positioning to remain on screen. */
963
+ _checkOffset: function (inst, offset, isFixed) {
964
+ var tpWidth = inst.tpDiv.outerWidth();
965
+ var tpHeight = inst.tpDiv.outerHeight();
966
+ var inputWidth = inst.input ? inst.input.outerWidth() : 0;
967
+ var inputHeight = inst.input ? inst.input.outerHeight() : 0;
968
+ var viewWidth = document.documentElement.clientWidth + $(document).scrollLeft();
969
+ var viewHeight = document.documentElement.clientHeight + $(document).scrollTop();
970
+
971
+ offset.left -= (this._get(inst, 'isRTL') ? (tpWidth - inputWidth) : 0);
972
+ offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0;
973
+ offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
974
+
975
+ // now check if timepicker is showing outside window viewport - move to a better place if so.
976
+ offset.left -= Math.min(offset.left, (offset.left + tpWidth > viewWidth && viewWidth > tpWidth) ?
977
+ Math.abs(offset.left + tpWidth - viewWidth) : 0);
978
+ offset.top -= Math.min(offset.top, (offset.top + tpHeight > viewHeight && viewHeight > tpHeight) ?
979
+ Math.abs(tpHeight + inputHeight) : 0);
980
+
981
+ return offset;
982
+ },
983
+
984
+ /* Find an object's position on the screen. */
985
+ _findPos: function (obj) {
986
+ var inst = this._getInst(obj);
987
+ var isRTL = this._get(inst, 'isRTL');
988
+ while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) {
989
+ obj = obj[isRTL ? 'previousSibling' : 'nextSibling'];
990
+ }
991
+ var position = $(obj).offset();
992
+ return [position.left, position.top];
993
+ },
994
+
995
+ /* Retrieve the size of left and top borders for an element.
996
+ @param elem (jQuery object) the element of interest
997
+ @return (number[2]) the left and top borders */
998
+ _getBorders: function (elem) {
999
+ var convert = function (value) {
1000
+ return { thin: 1, medium: 2, thick: 3}[value] || value;
1001
+ };
1002
+ return [parseFloat(convert(elem.css('border-left-width'))),
1003
+ parseFloat(convert(elem.css('border-top-width')))];
1004
+ },
1005
+
1006
+
1007
+ /* Close time picker if clicked elsewhere. */
1008
+ _checkExternalClick: function (event) {
1009
+ if (!$.timepicker._curInst) { return; }
1010
+ var $target = $(event.target);
1011
+ if ($target[0].id != $.timepicker._mainDivId &&
1012
+ $target.parents('#' + $.timepicker._mainDivId).length == 0 &&
1013
+ !$target.hasClass($.timepicker.markerClassName) &&
1014
+ !$target.hasClass($.timepicker._triggerClass) &&
1015
+ $.timepicker._timepickerShowing && !($.timepicker._inDialog && $.blockUI))
1016
+ $.timepicker._hideTimepicker();
1017
+ },
1018
+
1019
+ /* Hide the time picker from view.
1020
+ @param input element - the input field attached to the time picker */
1021
+ _hideTimepicker: function (input) {
1022
+ var inst = this._curInst;
1023
+ if (!inst || (input && inst != $.data(input, PROP_NAME))) { return; }
1024
+ if (this._timepickerShowing) {
1025
+ var showAnim = this._get(inst, 'showAnim');
1026
+ var duration = this._get(inst, 'duration');
1027
+ var postProcess = function () {
1028
+ $.timepicker._tidyDialog(inst);
1029
+ this._curInst = null;
1030
+ };
1031
+ if ($.effects && $.effects[showAnim]) {
1032
+ inst.tpDiv.hide(showAnim, $.timepicker._get(inst, 'showOptions'), duration, postProcess);
1033
+ }
1034
+ else {
1035
+ inst.tpDiv[(showAnim == 'slideDown' ? 'slideUp' :
1036
+ (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess);
1037
+ }
1038
+ if (!showAnim) { postProcess(); }
1039
+
1040
+ this._timepickerShowing = false;
1041
+
1042
+ this._lastInput = null;
1043
+ if (this._inDialog) {
1044
+ this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
1045
+ if ($.blockUI) {
1046
+ $.unblockUI();
1047
+ $('body').append(this.tpDiv);
1048
+ }
1049
+ }
1050
+ this._inDialog = false;
1051
+
1052
+ var onClose = this._get(inst, 'onClose');
1053
+ if (onClose) {
1054
+ onClose.apply(
1055
+ (inst.input ? inst.input[0] : null),
1056
+ [(inst.input ? inst.input.val() : ''), inst]); // trigger custom callback
1057
+ }
1058
+
1059
+ }
1060
+ },
1061
+
1062
+
1063
+
1064
+ /* Tidy up after a dialog display. */
1065
+ _tidyDialog: function (inst) {
1066
+ inst.tpDiv.removeClass(this._dialogClass).unbind('.ui-timepicker');
1067
+ },
1068
+
1069
+ /* Retrieve the instance data for the target control.
1070
+ @param target element - the target input field or division or span
1071
+ @return object - the associated instance data
1072
+ @throws error if a jQuery problem getting data */
1073
+ _getInst: function (target) {
1074
+ try {
1075
+ return $.data(target, PROP_NAME);
1076
+ }
1077
+ catch (err) {
1078
+ throw 'Missing instance data for this timepicker';
1079
+ }
1080
+ },
1081
+
1082
+ /* Get a setting value, defaulting if necessary. */
1083
+ _get: function (inst, name) {
1084
+ return inst.settings[name] !== undefined ?
1085
+ inst.settings[name] : this._defaults[name];
1086
+ },
1087
+
1088
+ /* Parse existing time and initialise time picker. */
1089
+ _setTimeFromField: function (inst) {
1090
+ if (inst.input.val() == inst.lastVal) { return; }
1091
+ var defaultTime = this._get(inst, 'defaultTime');
1092
+
1093
+ var timeToParse = defaultTime == 'now' ? this._getCurrentTimeRounded(inst) : defaultTime;
1094
+ if ((inst.inline == false) && (inst.input.val() != '')) { timeToParse = inst.input.val() }
1095
+
1096
+ if (timeToParse instanceof Date) {
1097
+ inst.hours = timeToParse.getHours();
1098
+ inst.minutes = timeToParse.getMinutes();
1099
+ } else {
1100
+ var timeVal = inst.lastVal = timeToParse;
1101
+ if (timeToParse == '') {
1102
+ inst.hours = -1;
1103
+ inst.minutes = -1;
1104
+ } else {
1105
+ var time = this.parseTime(inst, timeVal);
1106
+ inst.hours = time.hours;
1107
+ inst.minutes = time.minutes;
1108
+ }
1109
+ }
1110
+
1111
+
1112
+ $.timepicker._updateTimepicker(inst);
1113
+ },
1114
+
1115
+ /* Update or retrieve the settings for an existing time picker.
1116
+ @param target element - the target input field or division or span
1117
+ @param name object - the new settings to update or
1118
+ string - the name of the setting to change or retrieve,
1119
+ when retrieving also 'all' for all instance settings or
1120
+ 'defaults' for all global defaults
1121
+ @param value any - the new value for the setting
1122
+ (omit if above is an object or to retrieve a value) */
1123
+ _optionTimepicker: function(target, name, value) {
1124
+ var inst = this._getInst(target);
1125
+ if (arguments.length == 2 && typeof name == 'string') {
1126
+ return (name == 'defaults' ? $.extend({}, $.timepicker._defaults) :
1127
+ (inst ? (name == 'all' ? $.extend({}, inst.settings) :
1128
+ this._get(inst, name)) : null));
1129
+ }
1130
+ var settings = name || {};
1131
+ if (typeof name == 'string') {
1132
+ settings = {};
1133
+ settings[name] = value;
1134
+ }
1135
+ if (inst) {
1136
+ extendRemove(inst.settings, settings);
1137
+ if (this._curInst == inst) {
1138
+ this._hideTimepicker();
1139
+ this._updateTimepicker(inst);
1140
+ }
1141
+ if (inst.inline) {
1142
+ this._updateTimepicker(inst);
1143
+ }
1144
+ }
1145
+ },
1146
+
1147
+
1148
+ /* Set the time for a jQuery selection.
1149
+ @param target element - the target input field or division or span
1150
+ @param time String - the new time */
1151
+ _setTimeTimepicker: function(target, time) {
1152
+ var inst = this._getInst(target);
1153
+ if (inst) {
1154
+ this._setTime(inst, time);
1155
+ this._updateTimepicker(inst);
1156
+ this._updateAlternate(inst, time);
1157
+ }
1158
+ },
1159
+
1160
+ /* Set the time directly. */
1161
+ _setTime: function(inst, time, noChange) {
1162
+ var origHours = inst.hours;
1163
+ var origMinutes = inst.minutes;
1164
+ if (time instanceof Date) {
1165
+ inst.hours = time.getHours();
1166
+ inst.minutes = time.getMinutes();
1167
+ } else {
1168
+ var time = this.parseTime(inst, time);
1169
+ inst.hours = time.hours;
1170
+ inst.minutes = time.minutes;
1171
+ }
1172
+
1173
+ if ((origHours != inst.hours || origMinutes != inst.minutes) && !noChange) {
1174
+ inst.input.trigger('change');
1175
+ }
1176
+ this._updateTimepicker(inst);
1177
+ this._updateSelectedValue(inst);
1178
+ },
1179
+
1180
+ /* Return the current time, ready to be parsed, rounded to the closest minute by interval */
1181
+ _getCurrentTimeRounded: function (inst) {
1182
+ var currentTime = new Date(),
1183
+ currentMinutes = currentTime.getMinutes(),
1184
+ minutes_options = this._get(inst, 'minutes'),
1185
+ // round to closest interval
1186
+ adjustedMinutes = Math.round(currentMinutes / minutes_options.interval) * minutes_options.interval;
1187
+ currentTime.setMinutes(adjustedMinutes);
1188
+ return currentTime;
1189
+ },
1190
+
1191
+ /*
1192
+ * Parse a time string into hours and minutes
1193
+ */
1194
+ parseTime: function (inst, timeVal) {
1195
+ var retVal = new Object();
1196
+ retVal.hours = -1;
1197
+ retVal.minutes = -1;
1198
+
1199
+ if(!timeVal)
1200
+ return '';
1201
+
1202
+ var timeSeparator = this._get(inst, 'timeSeparator'),
1203
+ amPmText = this._get(inst, 'amPmText'),
1204
+ showHours = this._get(inst, 'showHours'),
1205
+ showMinutes = this._get(inst, 'showMinutes'),
1206
+ optionalMinutes = this._get(inst, 'optionalMinutes'),
1207
+ showPeriod = (this._get(inst, 'showPeriod') == true),
1208
+ p = timeVal.indexOf(timeSeparator);
1209
+
1210
+ // check if time separator found
1211
+ if (p != -1) {
1212
+ retVal.hours = parseInt(timeVal.substr(0, p), 10);
1213
+ retVal.minutes = parseInt(timeVal.substr(p + 1), 10);
1214
+ }
1215
+ // check for hours only
1216
+ else if ( (showHours) && ( !showMinutes || optionalMinutes ) ) {
1217
+ retVal.hours = parseInt(timeVal, 10);
1218
+ }
1219
+ // check for minutes only
1220
+ else if ( ( ! showHours) && (showMinutes) ) {
1221
+ retVal.minutes = parseInt(timeVal, 10);
1222
+ }
1223
+
1224
+ if (showHours) {
1225
+ var timeValUpper = timeVal.toUpperCase();
1226
+ if ((retVal.hours < 12) && (showPeriod) && (timeValUpper.indexOf(amPmText[1].toUpperCase()) != -1)) {
1227
+ retVal.hours += 12;
1228
+ }
1229
+ // fix for 12 AM
1230
+ if ((retVal.hours == 12) && (showPeriod) && (timeValUpper.indexOf(amPmText[0].toUpperCase()) != -1)) {
1231
+ retVal.hours = 0;
1232
+ }
1233
+ }
1234
+
1235
+ return retVal;
1236
+ },
1237
+
1238
+ selectNow: function(event) {
1239
+ var id = $(event.target).attr("data-timepicker-instance-id"),
1240
+ $target = $(id),
1241
+ inst = this._getInst($target[0]);
1242
+ //if (!inst || (input && inst != $.data(input, PROP_NAME))) { return; }
1243
+ var currentTime = new Date();
1244
+ inst.hours = currentTime.getHours();
1245
+ inst.minutes = currentTime.getMinutes();
1246
+ this._updateSelectedValue(inst);
1247
+ this._updateTimepicker(inst);
1248
+ this._hideTimepicker();
1249
+ },
1250
+
1251
+ deselectTime: function(event) {
1252
+ var id = $(event.target).attr("data-timepicker-instance-id"),
1253
+ $target = $(id),
1254
+ inst = this._getInst($target[0]);
1255
+ inst.hours = -1;
1256
+ inst.minutes = -1;
1257
+ this._updateSelectedValue(inst);
1258
+ this._hideTimepicker();
1259
+ },
1260
+
1261
+
1262
+ selectHours: function (event) {
1263
+ var $td = $(event.currentTarget),
1264
+ id = $td.attr("data-timepicker-instance-id"),
1265
+ newHours = parseInt($td.attr("data-hour")),
1266
+ fromDoubleClick = event.data.fromDoubleClick,
1267
+ $target = $(id),
1268
+ inst = this._getInst($target[0]),
1269
+ showMinutes = (this._get(inst, 'showMinutes') == true);
1270
+
1271
+ // don't select if disabled
1272
+ if ( $.timepicker._isDisabledTimepicker($target.attr('id')) ) { return false }
1273
+
1274
+ $td.parents('.ui-timepicker-hours:first').find('a').removeClass('ui-state-active');
1275
+ $td.children('a').addClass('ui-state-active');
1276
+ inst.hours = newHours;
1277
+
1278
+ // added for onMinuteShow callback
1279
+ var onMinuteShow = this._get(inst, 'onMinuteShow'),
1280
+ maxTime = this._get(inst, 'maxTime'),
1281
+ minTime = this._get(inst, 'minTime');
1282
+ if (onMinuteShow || maxTime.minute || minTime.minute) {
1283
+ // this will trigger a callback on selected hour to make sure selected minute is allowed.
1284
+ this._updateMinuteDisplay(inst);
1285
+ }
1286
+
1287
+ this._updateSelectedValue(inst);
1288
+
1289
+ inst._hoursClicked = true;
1290
+ if ((inst._minutesClicked) || (fromDoubleClick) || (showMinutes == false)) {
1291
+ $.timepicker._hideTimepicker();
1292
+ }
1293
+ // return false because if used inline, prevent the url to change to a hashtag
1294
+ return false;
1295
+ },
1296
+
1297
+ selectMinutes: function (event) {
1298
+ var $td = $(event.currentTarget),
1299
+ id = $td.attr("data-timepicker-instance-id"),
1300
+ newMinutes = parseInt($td.attr("data-minute")),
1301
+ fromDoubleClick = event.data.fromDoubleClick,
1302
+ $target = $(id),
1303
+ inst = this._getInst($target[0]),
1304
+ showHours = (this._get(inst, 'showHours') == true);
1305
+
1306
+ // don't select if disabled
1307
+ if ( $.timepicker._isDisabledTimepicker($target.attr('id')) ) { return false }
1308
+
1309
+ $td.parents('.ui-timepicker-minutes:first').find('a').removeClass('ui-state-active');
1310
+ $td.children('a').addClass('ui-state-active');
1311
+
1312
+ inst.minutes = newMinutes;
1313
+ this._updateSelectedValue(inst);
1314
+
1315
+ inst._minutesClicked = true;
1316
+ if ((inst._hoursClicked) || (fromDoubleClick) || (showHours == false)) {
1317
+ $.timepicker._hideTimepicker();
1318
+ // return false because if used inline, prevent the url to change to a hashtag
1319
+ return false;
1320
+ }
1321
+
1322
+ // return false because if used inline, prevent the url to change to a hashtag
1323
+ return false;
1324
+ },
1325
+
1326
+ _updateSelectedValue: function (inst) {
1327
+ var newTime = this._getParsedTime(inst);
1328
+ if (inst.input) {
1329
+ inst.input.val(newTime);
1330
+ inst.input.trigger('change');
1331
+ }
1332
+ var onSelect = this._get(inst, 'onSelect');
1333
+ if (onSelect) { onSelect.apply((inst.input ? inst.input[0] : null), [newTime, inst]); } // trigger custom callback
1334
+ this._updateAlternate(inst, newTime);
1335
+ return newTime;
1336
+ },
1337
+
1338
+ /* this function process selected time and return it parsed according to instance options */
1339
+ _getParsedTime: function(inst) {
1340
+
1341
+ if (inst.hours == -1 && inst.minutes == -1) {
1342
+ return '';
1343
+ }
1344
+
1345
+ // default to 0 AM if hours is not valid
1346
+ if ((inst.hours < inst.hours.starts) || (inst.hours > inst.hours.ends )) { inst.hours = 0; }
1347
+ // default to 0 minutes if minute is not valid
1348
+ if ((inst.minutes < inst.minutes.starts) || (inst.minutes > inst.minutes.ends)) { inst.minutes = 0; }
1349
+
1350
+ var period = "",
1351
+ showPeriod = (this._get(inst, 'showPeriod') == true),
1352
+ showLeadingZero = (this._get(inst, 'showLeadingZero') == true),
1353
+ showHours = (this._get(inst, 'showHours') == true),
1354
+ showMinutes = (this._get(inst, 'showMinutes') == true),
1355
+ optionalMinutes = (this._get(inst, 'optionalMinutes') == true),
1356
+ amPmText = this._get(inst, 'amPmText'),
1357
+ selectedHours = inst.hours ? inst.hours : 0,
1358
+ selectedMinutes = inst.minutes ? inst.minutes : 0,
1359
+ displayHours = selectedHours ? selectedHours : 0,
1360
+ parsedTime = '';
1361
+
1362
+ // fix some display problem when hours or minutes are not selected yet
1363
+ if (displayHours == -1) { displayHours = 0 }
1364
+ if (selectedMinutes == -1) { selectedMinutes = 0 }
1365
+
1366
+ if (showPeriod) {
1367
+ if (inst.hours == 0) {
1368
+ displayHours = 12;
1369
+ }
1370
+ if (inst.hours < 12) {
1371
+ period = amPmText[0];
1372
+ }
1373
+ else {
1374
+ period = amPmText[1];
1375
+ if (displayHours > 12) {
1376
+ displayHours -= 12;
1377
+ }
1378
+ }
1379
+ }
1380
+
1381
+ var h = displayHours.toString();
1382
+ if (showLeadingZero && (displayHours < 10)) { h = '0' + h; }
1383
+
1384
+ var m = selectedMinutes.toString();
1385
+ if (selectedMinutes < 10) { m = '0' + m; }
1386
+
1387
+ if (showHours) {
1388
+ parsedTime += h;
1389
+ }
1390
+ if (showHours && showMinutes && (!optionalMinutes || m != 0)) {
1391
+ parsedTime += this._get(inst, 'timeSeparator');
1392
+ }
1393
+ if (showMinutes && (!optionalMinutes || m != 0)) {
1394
+ parsedTime += m;
1395
+ }
1396
+ if (showHours) {
1397
+ if (period.length > 0) { parsedTime += this._get(inst, 'periodSeparator') + period; }
1398
+ }
1399
+
1400
+ return parsedTime;
1401
+ },
1402
+
1403
+ /* Update any alternate field to synchronise with the main field. */
1404
+ _updateAlternate: function(inst, newTime) {
1405
+ var altField = this._get(inst, 'altField');
1406
+ if (altField) { // update alternate field too
1407
+ $(altField).each(function(i,e) {
1408
+ $(e).val(newTime);
1409
+ });
1410
+ }
1411
+ },
1412
+
1413
+ _getTimeAsDateTimepicker: function(input) {
1414
+ var inst = this._getInst(input);
1415
+ if (inst.hours == -1 && inst.minutes == -1) {
1416
+ return '';
1417
+ }
1418
+
1419
+ // default to 0 AM if hours is not valid
1420
+ if ((inst.hours < inst.hours.starts) || (inst.hours > inst.hours.ends )) { inst.hours = 0; }
1421
+ // default to 0 minutes if minute is not valid
1422
+ if ((inst.minutes < inst.minutes.starts) || (inst.minutes > inst.minutes.ends)) { inst.minutes = 0; }
1423
+
1424
+ return new Date(0, 0, 0, inst.hours, inst.minutes, 0);
1425
+ },
1426
+ /* This might look unused but it's called by the $.fn.timepicker function with param getTime */
1427
+ /* added v 0.2.3 - gitHub issue #5 - Thanks edanuff */
1428
+ _getTimeTimepicker : function(input) {
1429
+ var inst = this._getInst(input);
1430
+ return this._getParsedTime(inst);
1431
+ },
1432
+ _getHourTimepicker: function(input) {
1433
+ var inst = this._getInst(input);
1434
+ if ( inst == undefined) { return -1; }
1435
+ return inst.hours;
1436
+ },
1437
+ _getMinuteTimepicker: function(input) {
1438
+ var inst= this._getInst(input);
1439
+ if ( inst == undefined) { return -1; }
1440
+ return inst.minutes;
1441
+ }
1442
+
1443
+ });
1444
+
1445
+
1446
+
1447
+ /* Invoke the timepicker functionality.
1448
+ @param options string - a command, optionally followed by additional parameters or
1449
+ Object - settings for attaching new timepicker functionality
1450
+ @return jQuery object */
1451
+ $.fn.timepicker = function (options) {
1452
+ /* Initialise the time picker. */
1453
+ if (!$.timepicker.initialized) {
1454
+ $(document).mousedown($.timepicker._checkExternalClick);
1455
+ $.timepicker.initialized = true;
1456
+ }
1457
+
1458
+ /* Append timepicker main container to body if not exist. */
1459
+ if ($("#"+$.timepicker._mainDivId).length === 0) {
1460
+ $('body').append($.timepicker.tpDiv);
1461
+ }
1462
+
1463
+ var otherArgs = Array.prototype.slice.call(arguments, 1);
1464
+ if (typeof options == 'string' && (options == 'getTime' || options == 'getTimeAsDate' || options == 'getHour' || options == 'getMinute' ))
1465
+ return $.timepicker['_' + options + 'Timepicker'].
1466
+ apply($.timepicker, [this[0]].concat(otherArgs));
1467
+ if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string')
1468
+ return $.timepicker['_' + options + 'Timepicker'].
1469
+ apply($.timepicker, [this[0]].concat(otherArgs));
1470
+ return this.each(function () {
1471
+ typeof options == 'string' ?
1472
+ $.timepicker['_' + options + 'Timepicker'].
1473
+ apply($.timepicker, [this].concat(otherArgs)) :
1474
+ $.timepicker._attachTimepicker(this, options);
1475
+ });
1476
+ };
1477
+
1478
+ /* jQuery extend now ignores nulls! */
1479
+ function extendRemove(target, props) {
1480
+ $.extend(target, props);
1481
+ for (var name in props)
1482
+ if (props[name] == null || props[name] == undefined)
1483
+ target[name] = props[name];
1484
+ return target;
1485
+ };
1486
+
1487
+ $.timepicker = new Timepicker(); // singleton instance
1488
+ $.timepicker.initialized = false;
1489
+ $.timepicker.uuid = new Date().getTime();
1490
+ $.timepicker.version = "0.3.3";
1491
+
1492
+ // Workaround for #4055
1493
+ // Add another global to avoid noConflict issues with inline event handlers
1494
+ window['TP_jQuery_' + tpuuid] = $;
1495
+
1496
+ })(jQuery);