├── js
├── event_showcase_public_scripts.js
├── event_showcase_admin_scripts.js
└── jquery.ui.timepicker.js
├── css
├── event_showcase_public_styles.css
├── event_showcase_admin_styles.css
└── jquery.ui.timepicker.css
├── readme.txt
├── .gitattributes
├── .gitignore
└── event_showcase_class.php
/js/event_showcase_public_scripts.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Public Scripts
3 | * interactivity for the front end. Blank for now
4 | */
5 |
6 | jQuery(document).ready(function($){
7 |
8 |
9 | });
10 |
--------------------------------------------------------------------------------
/css/event_showcase_public_styles.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Public Styles
3 | * Front end stytes to format our additional content meta
4 | */
5 |
6 | .additional-meta{
7 | margin-bottom: 15px;
8 | }
9 | .additional-meta .meta{
10 | margin-bottom: 5px;
11 | }
12 | .additional-meta .meta span{
13 | margin-left: 10px;
14 | }
15 |
--------------------------------------------------------------------------------
/js/event_showcase_admin_scripts.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Admin Scripts
3 | * activates jQuery datepicker and timepicker on the backend
4 | */
5 |
6 | jQuery(document).ready(function($){
7 |
8 | //activate datepicker
9 | $('.admin-datepicker').datepicker();
10 |
11 | //activate timepicker
12 | $('.admin-timepicker').timepicker();
13 | });
14 |
--------------------------------------------------------------------------------
/css/event_showcase_admin_styles.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Admin Styles
3 | * Styling the back-end of our content type
4 | */
5 |
6 | .field-container{
7 | margin-bottom: 10px;
8 | }
9 | .field-container label{
10 | display: block;
11 | margin-bottom: 5px;
12 | width: 100%;
13 | }
14 | .field-container input,
15 | .field-container textarea,
16 | .field-container select{
17 | width: 100%;
18 | }
19 |
--------------------------------------------------------------------------------
/readme.txt:
--------------------------------------------------------------------------------
1 | #Object Oreintation Example#
2 |
3 | This example creates a new 'event' content type that can be used to display additional event meta.
4 |
5 | This content is used in one of my SitePoint examples on object orientation in Wordpress
6 |
7 | Create a new directory inside your child theme such as `includes` and then another called `event_showcase` and add the PHP class file and CSS/JS directories.
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 | ehthumbs.db
4 |
5 | # Folder config file
6 | Desktop.ini
7 |
8 | # Recycle Bin used on file shares
9 | $RECYCLE.BIN/
10 |
11 | # Windows Installer files
12 | *.cab
13 | *.msi
14 | *.msm
15 | *.msp
16 |
17 | # Windows shortcuts
18 | *.lnk
19 |
20 | # =========================
21 | # Operating System Files
22 | # =========================
23 |
24 | # OSX
25 | # =========================
26 |
27 | .DS_Store
28 | .AppleDouble
29 | .LSOverride
30 |
31 | # Thumbnails
32 | ._*
33 |
34 | # Files that might appear on external disk
35 | .Spotlight-V100
36 | .Trashes
37 |
38 | # Directories potentially created on remote AFP share
39 | .AppleDB
40 | .AppleDesktop
41 | Network Trash Folder
42 | Temporary Items
43 | .apdisk
44 |
--------------------------------------------------------------------------------
/css/jquery.ui.timepicker.css:
--------------------------------------------------------------------------------
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 |
58 |
--------------------------------------------------------------------------------
/event_showcase_class.php:
--------------------------------------------------------------------------------
1 | set_directory_value(); //set the directory url on creation
21 | add_action('init', array($this,'add_content_type')); //add content type
22 | add_action('init', array($this,'check_flush_rewrite_rules')); //flush re-write rules for permalinks (because of content type)
23 | add_action('add_meta_boxes', array($this,'add_meta_boxes_for_content_type')); //add meta boxes
24 | add_action('wp_enqueue_scripts', array($this,'enqueue_public_scripts_and_styles')); //enqueue public facing elements
25 | add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts_and_styles')); //enqueues admin elements
26 | add_action('save_post_' . $this->content_type_name, array($this,'save_custom_content_type')); //handles saving of content type meta info
27 | add_action('display_content_type_meta', array($this,'display_additional_meta_data')); //displays the saved content type meta info
28 | }
29 |
30 | //sets the directory (path) so that we can use this for our enqueuing
31 | public function set_directory_value(){
32 | $this->directory = get_stylesheet_directory_uri() . '/includes/event_showcase';
33 | }
34 |
35 | //check if we need to flush rewrite rules
36 | public function check_flush_rewrite_rules(){
37 | $has_been_flushed = get_option($this->content_type_name . '_flush_rewrite_rules');
38 | //if we haven't flushed re-write rules, flush them (should be triggered only once)
39 | if($has_been_flushed != true){
40 | flush_rewrite_rules(true);
41 | update_option($this->content_type_name . '_flush_rewrite_rules', true);
42 | }
43 | }
44 |
45 | //enqueue public scripts and styles
46 | public function enqueue_public_scripts_and_styles(){
47 | //font awesome styles
48 | wp_enqueue_style(
49 | 'font-awesome',
50 | '//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css'
51 | );
52 | //public styles
53 | wp_enqueue_style(
54 | $this->content_type_name . '_public_styles',
55 | $this->directory . '/css/' . $this->content_type_name . '_public_styles.css'
56 | );
57 | //public scripts
58 | wp_enqueue_script(
59 | $this->content_type_name . '_public_scripts',
60 | $this->directory . '/js/' . $this->content_type_name . '_public_scripts.js',
61 | array('jquery')
62 | );
63 | }
64 |
65 | //enqueue admin scripts and styles
66 | public function enqueue_admin_scripts_and_styles(){
67 |
68 | global $pagenow, $post_type;
69 |
70 | //process only on post edit page for custom content type
71 | if(($post_type == $this->content_type_name) && ($pagenow == 'post-new.php' || $pagenow == 'post.php')){
72 |
73 | //admin styles
74 | wp_enqueue_style(
75 | $this->content_type_name . '_public_styles',
76 | $this->directory . '/css/' . $this->content_type_name . '_admin_styles.css'
77 | );
78 | //jquery ui styles for datepicker
79 | wp_enqueue_style(
80 | $this->content_type_name . '_jquery_ui_style',
81 | '//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css'
82 | );
83 | //timepicker styles
84 | wp_enqueue_style(
85 | 'jquery_ui_timepicker_styles',
86 | $this->directory . '/css/jquery.ui.timepicker.css'
87 | );
88 | //timepicker script
89 | wp_enqueue_script(
90 | 'jquery_ui_timepicker_script',
91 | $this->directory . '/js/jquery.ui.timepicker.js'
92 | );
93 | //admin scripts (depends on datepicker and timepicker)
94 | wp_enqueue_script(
95 | $this->content_type_name . '_public_scripts',
96 | $this->directory . '/js/' . $this->content_type_name . '_admin_scripts.js',
97 | array('jquery','jquery-ui-datepicker','jquery_ui_timepicker_script')
98 | );
99 | }
100 | }
101 |
102 | //adding our new content type
103 | public function add_content_type(){
104 | $labels = array(
105 | 'name' => ucwords($this->singular_name),
106 | 'singular_name' => ucwords($this->singular_name),
107 | 'menu_name' => ucwords($this->plural_name),
108 | 'name_admin_bar' => ucwords($this->singular_name),
109 | 'add_new' => ucwords($this->singular_name),
110 | 'add_new_item' => 'Add New ' . ucwords($this->singular_name),
111 | 'new_item' => 'New ' . ucwords($this->singular_name),
112 | 'edit_item' => 'Edit ' . ucwords($this->singular_name),
113 | 'view_item' => 'View ' . ucwords($this->plural_name),
114 | 'all_items' => 'All ' . ucwords($this->plural_name),
115 | 'search_items' => 'Search ' . ucwords($this->plural_name),
116 | 'parent_item_colon' => 'Parent ' . ucwords($this->plural_name) . ':',
117 | 'not_found' => 'No ' . ucwords($this->plural_name) . ' found.',
118 | 'not_found_in_trash' => 'No ' . ucwords($this->plural_name) . ' found in Trash.',
119 | );
120 |
121 | $args = array(
122 | 'labels' => $labels,
123 | 'public' => true,
124 | 'publicly_queryable'=> true,
125 | 'show_ui' => true,
126 | 'show_in_nav' => true,
127 | 'query_var' => true,
128 | 'hierarchical' => false,
129 | 'supports' => array('title','editor','thumbnail'),
130 | 'has_archive' => true,
131 | 'menu_position' => 20,
132 | 'show_in_admin_bar' => true,
133 | 'menu_icon' => 'dashicons-format-status'
134 | );
135 |
136 | //register your content type
137 | register_post_type($this->content_type_name, $args);
138 |
139 | }
140 |
141 | //adding meta box to save additional meta data for the content type
142 | public function add_meta_boxes_for_content_type(){
143 |
144 | //add a meta box
145 | add_meta_box(
146 | $this->singular_name . '_meta_box', //id
147 | ucwords($this->singular_name) . ' Information', //box name
148 | array($this,'display_function_for_content_type_meta_box'), //display function
149 | $this->content_type_name, //content type
150 | 'normal', //context
151 | 'default' //priority
152 | );
153 |
154 | }
155 |
156 | //displays the visual output of the meta box in admin (where we will save our meta data)
157 | public function display_function_for_content_type_meta_box($post){
158 |
159 | //collect meta information
160 | $event_subtitle = get_post_meta($post->ID,'event_subtitle', true);
161 | $event_start_date = get_post_meta($post->ID,'event_start_date', true);
162 | $event_end_date = get_post_meta($post->ID,'event_end_date', true);
163 | $event_start_time = get_post_meta($post->ID,'event_start_time', true);
164 | $event_end_time = get_post_meta($post->ID,'event_end_time', true);
165 | $event_location = get_post_meta($post->ID,'event_location', true);
166 | $event_price = get_post_meta($post->ID,'event_price', true);
167 |
168 | //set nonce
169 | wp_nonce_field($this->content_type_name . '_nonce', $this->content_type_name . '_nonce_field');
170 |
171 | ?>
172 |
Enter additional information about your event below
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 | content_type_name . '_nonce_field'])){
210 | return $post_id;
211 | }
212 | //verify nonce
213 | if(!wp_verify_nonce($_POST[$this->content_type_name . '_nonce_field'] , $this->content_type_name . '_nonce')){
214 | return $post_id;
215 | }
216 | //check for autosaves
217 | if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE){
218 | return $post_id;
219 | }
220 | //check if the user can edit
221 | if(!current_user_can('edit_posts')){
222 | return $post_id;
223 | }
224 |
225 | //collect sanitized information
226 | $event_subtitle =sanitize_text_field($_POST['event_subtitle']);
227 | $event_start_date =sanitize_text_field($_POST['event_start_date']);
228 | $event_end_date = sanitize_text_field($_POST['event_end_date']);
229 | $event_start_time =sanitize_text_field($_POST['event_start_time']);
230 | $event_end_time = sanitize_text_field($_POST['event_end_time']);
231 | $event_location = sanitize_text_field(wpautop($_POST['event_location']));
232 | $event_price = sanitize_text_field($_POST['event_price']);
233 |
234 | //save post meta
235 | update_post_meta($post_id,'event_subtitle',$event_subtitle);
236 | update_post_meta($post_id,'event_start_date',$event_start_date);
237 | update_post_meta($post_id,'event_end_date',$event_end_date);
238 | update_post_meta($post_id,'event_start_time',$event_start_time);
239 | update_post_meta($post_id,'event_end_time',$event_end_time);
240 | update_post_meta($post_id,'event_location',$event_location);
241 | update_post_meta($post_id,'event_price', $event_price);
242 |
243 | }
244 |
245 | //display additional meta information for the content type
246 | //@hooked using 'display_additional_meta_data' in theme
247 | function display_additional_meta_data(){
248 | global $post, $post_type;
249 |
250 | //if we are on our custom post type
251 | if($post_type == $this->content_type_name){
252 |
253 | //collect information
254 | $event_subtitle = get_post_meta($post->ID,'event_subtitle', true);
255 | $event_start_date = get_post_meta($post->ID,'event_start_date', true);
256 | $event_end_date = get_post_meta($post->ID,'event_end_date', true);
257 | $event_start_time = get_post_meta($post->ID,'event_start_time', true);
258 | $event_end_time = get_post_meta($post->ID,'event_end_time', true);
259 | $event_location = get_post_meta($post->ID,'event_location', true);
260 | $event_price = get_post_meta($post->ID,'event_price', true);
261 |
262 | $html = '';
263 | $html .= '' . $event_subtitle . '
';
264 | $html .= '';
271 |
272 | echo $html;
273 | }
274 |
275 | }
276 | }
277 |
278 | //create new object
279 | $event_showcase = new event_showcase;
280 |
281 |
282 | ?>
--------------------------------------------------------------------------------
/js/jquery.ui.timepicker.js:
--------------------------------------------------------------------------------
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 = $('');
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 | $(''))
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 = $('' + appendText + '');
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 = $('');
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 = '';
599 |
600 | if (showHours) {
601 |
602 | html += '' +
603 | '' +
606 | '';
607 |
608 | for (row = 1; row <= rows; row++) {
609 | html += '';
610 | // AM
611 | if (row == amFirstRow && showPeriodLabels) {
612 | html += '| ' + amPmText[0] + ' | ';
613 | }
614 | // PM
615 | if (row == pmFirstRow && showPeriodLabels) {
616 | html += '' + amPmText[1] + ' | ';
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 += ' ';
627 | }
628 | html += ' ' + // Close the hours cells table
629 | ' | '; // Close the Hour td
630 | }
631 |
632 | if (showMinutes) {
633 | html += '';
634 | html += this._generateHTMLMinutes(inst);
635 | html += ' | ';
636 | }
637 |
638 | html += '
';
639 |
640 |
641 | if (showButtonPanel) {
642 | var buttonPanel = '';
643 | if (showNowButton) {
644 | buttonPanel += '';
647 | }
648 | if (showDeselectButton) {
649 | buttonPanel += '';
652 | }
653 | if (showCloseButton) {
654 | buttonPanel += '';
657 | }
658 |
659 | html += buttonPanel + ' |
';
660 | }
661 | html += '
';
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 += '' +
749 | '';
750 |
751 | minuteCounter = 0;
752 | for (row = 1; row <= rows; row++) {
753 | html += '';
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 += '
';
764 | }
765 |
766 | html += '
';
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 = ' | ';
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 = '' +
807 | '' +
810 | displayHour.toString() +
811 | ' | ';
812 | }
813 | else {
814 | html =
815 | '' +
816 | '' +
819 | displayHour.toString() +
820 | '' +
821 | ' | ';
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 = ' | ';
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 = '' +
852 | '' +
855 | displayText +
856 | ' | ';
857 | }
858 | else {
859 |
860 | html = '' +
861 | '' +
862 | displayText +
863 | '' +
864 | ' | ';
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);
1497 |
--------------------------------------------------------------------------------