├── 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 .= '
'; 265 | $html .= '
Start: ' . $event_start_date . ' - ' . $event_start_time . '
'; 266 | $html .= '
End: ' . $event_end_date . ' - ' . $event_end_time . '
'; 267 | $html .= '
Location: ' . $event_location . '
'; 268 | $html .= '
Price: ' . $event_price . '
'; 269 | 270 | $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 += ''; // Close the Hour td 630 | } 631 | 632 | if (showMinutes) { 633 | html += ''; 636 | } 637 | 638 | html += ''; 639 | 640 | 641 | if (showButtonPanel) { 642 | var buttonPanel = ''; 660 | } 661 | html += '
' + 603 | '
' + 604 | hourLabel + 605 | '
' + 606 | ''; 607 | 608 | for (row = 1; row <= rows; row++) { 609 | html += ''; 610 | // AM 611 | if (row == amFirstRow && showPeriodLabels) { 612 | html += ''; 613 | } 614 | // PM 615 | if (row == pmFirstRow && showPeriodLabels) { 616 | html += ''; 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 += '
' + amPmText[0] + '' + amPmText[1] + '
' + // Close the hours cells table 629 | '
'; 634 | html += this._generateHTMLMinutes(inst); 635 | html += '
'; 643 | if (showNowButton) { 644 | buttonPanel += ''; 647 | } 648 | if (showDeselectButton) { 649 | buttonPanel += ''; 652 | } 653 | if (showCloseButton) { 654 | buttonPanel += ''; 657 | } 658 | 659 | html += buttonPanel + '
'; 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 += '
' + 747 | minuteLabel + 748 | '
' + 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 | --------------------------------------------------------------------------------