Bulk Delete - Version 4.0

Version Description

Add the ability to delete users

Download this release

Release Info

Developer sudar
Plugin Icon 128x128 Bulk Delete
Version 4.0
Comparing to
See all releases

Code changes from version 3.6.0 to 4.0

bulk-delete.php CHANGED
@@ -3,9 +3,9 @@
3
  Plugin Name: Bulk Delete
4
  Plugin Script: bulk-delete.php
5
  Plugin URI: http://sudarmuthu.com/wordpress/bulk-delete
6
- Description: Bulk delete posts from selected categories, tags, post types, custom taxonomies or by post status like drafts, scheduled posts, revisions etc.
7
  Donate Link: http://sudarmuthu.com/if-you-wanna-thank-me
8
- Version: 3.6.0
9
  License: GPL
10
  Author: Sudar
11
  Author URI: http://sudarmuthu.com/
@@ -77,6 +77,9 @@ Domain Path: languages/
77
  2013-07-07 - v3.6.0 - (Dev time: 2 hours)
78
  - Change minimum requirement to WordPress 3.3
79
  - Fix compatibility issues with "The event calendar" Plugin
 
 
 
80
  */
81
 
82
  /* Copyright 2009 Sudar Muthu (email : sudar@sudarmuthu.com)
@@ -95,12 +98,25 @@ Domain Path: languages/
95
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
96
  */
97
 
 
 
 
 
 
 
 
 
98
  /**
99
  * Bulk Delete Main class
100
  */
101
  class Bulk_Delete {
102
 
103
- const VERSION = '3.5';
 
 
 
 
 
104
  const JS_HANDLE = 'bulk-delete';
105
  const JS_VARIABLE = 'BULK_DELETE';
106
 
@@ -112,7 +128,9 @@ class Bulk_Delete {
112
  const CRON_HOOK_TAXONOMY = 'do-bulk-delete-taxonomy';
113
  const CRON_HOOK_POST_TYPES = 'do-bulk-delete-post-types';
114
 
115
- // meta boxes
 
 
116
  const BOX_POST_STATUS = 'bd_by_post_status';
117
  const BOX_CATEGORY = 'bd_by_category';
118
  const BOX_TAG = 'bd_by_tag';
@@ -123,6 +141,9 @@ class Bulk_Delete {
123
  const BOX_POST_REVISION = 'bd_by_post_revision';
124
  const BOX_DEBUG = 'bd_debug';
125
 
 
 
 
126
  /**
127
  * Default constructor
128
  */
@@ -144,20 +165,67 @@ class Bulk_Delete {
144
  * Add navigation menu
145
  */
146
  function add_menu() {
147
- //Add a submenu to Manage
148
- $this->admin_page = add_options_page(__("Bulk Delete", 'bulk-delete'), __("Bulk Delete", 'bulk-delete'), 'delete_posts', basename(__FILE__), array(&$this, 'display_setting_page'));
149
- $this->cron_page = add_options_page(__("Bulk Delete Schedules", 'bulk-delete'), __("Bulk Delete Schedules", 'bulk-delete'), 'delete_posts', 'bulk-delete-cron', array(&$this, 'display_cron_page'));
150
 
151
- add_action( "load-{$this->admin_page}", array(&$this,'create_settings_panel' ) );
152
- add_action('admin_print_scripts-' . $this->admin_page, array(&$this, 'add_script'));
 
 
 
 
 
 
 
 
 
153
 
154
- add_action( "add_meta_boxes_{$this->admin_page}", array( &$this, 'add_meta_boxes' ));
 
 
155
  }
156
 
157
  /**
158
- * Register meta boxes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  */
160
- function add_meta_boxes() {
161
  add_meta_box( self::BOX_POST_STATUS, __( 'By Post Status', 'bulk-delete' ), array( &$this, 'render_by_post_status_box' ), $this->admin_page, 'advanced' );
162
  add_meta_box( self::BOX_CATEGORY, __( 'By Category', 'bulk-delete' ), array( &$this, 'render_by_category_box' ), $this->admin_page, 'advanced' );
163
  add_meta_box( self::BOX_TAG, __( 'By Tag', 'bulk-delete' ), array( &$this, 'render_by_tag_box' ), $this->admin_page, 'advanced' );
@@ -170,19 +238,19 @@ class Bulk_Delete {
170
  }
171
 
172
  /**
173
- * Add settings Panel
174
  */
175
- function create_settings_panel() {
176
 
177
  /**
178
  * Create the WP_Screen object using page handle
179
  */
180
- $this->admin_screen = WP_Screen::get($this->admin_page);
181
 
182
  /**
183
  * Content specified inline
184
  */
185
- $this->admin_screen->add_help_tab(
186
  array(
187
  'title' => __('About Plugin', 'bulk-delete'),
188
  'id' => 'about_tab',
@@ -192,7 +260,7 @@ class Bulk_Delete {
192
  );
193
 
194
  // Add help sidebar
195
- $this->admin_screen->set_help_sidebar(
196
  '<p><strong>' . __('More information', 'bulk-delete') . '</strong></p>' .
197
  '<p><a href = "http://sudarmuthu.com/wordpress/bulk-delete">' . __('Plugin Homepage/support', 'bulk-delete') . '</a></p>' .
198
  '<p><a href = "http://sudarmuthu.com/wordpress/bulk-delete/pro-addons">' . __("Buy pro addons", 'bulk-delete') . '</a></p>' .
@@ -201,13 +269,20 @@ class Bulk_Delete {
201
  );
202
 
203
  /* Trigger the add_meta_boxes hooks to allow meta boxes to be added */
204
- do_action('add_meta_boxes_' . $this->admin_page, null);
205
- do_action('add_meta_boxes', $this->admin_page, null);
206
 
207
  /* Enqueue WordPress' script for handling the meta boxes */
208
  wp_enqueue_script('postbox');
209
  }
210
 
 
 
 
 
 
 
 
211
  /**
212
  * Enqueue JavaScript
213
  */
@@ -215,7 +290,7 @@ class Bulk_Delete {
215
  global $wp_scripts;
216
 
217
  // uses code from http://trentrichardson.com/examples/timepicker/
218
- wp_enqueue_script( 'jquery-ui-timepicker', plugins_url('/js/jquery-ui-timepicker.js', __FILE__), array('jquery-ui-slider', 'jquery-ui-datepicker'), '1.1.1', true);
219
  wp_enqueue_script( self::JS_HANDLE, plugins_url('/js/bulk-delete.js', __FILE__), array('jquery-ui-timepicker'), self::VERSION, TRUE);
220
 
221
  $ui = $wp_scripts->query('jquery-ui-core');
@@ -226,12 +301,13 @@ class Bulk_Delete {
226
 
227
  // JavaScript messages
228
  $msg = array(
229
- 'deletewarning' => __('Are you sure you want to delete all the selected posts', 'bulk-delete')
 
230
  );
231
 
232
  $error = array(
233
  'selectone' => __('Please select posts from at least one option', 'bulk-delete'),
234
- 'enterurl' => __('Please enter at least one page url', 'bulk-delete')
235
  );
236
 
237
  $translation_array = array( 'msg' => $msg, 'error' => $error );
@@ -249,8 +325,12 @@ class Bulk_Delete {
249
  if( ! $this_plugin ) $this_plugin = plugin_basename(__FILE__);
250
 
251
  if( $file == $this_plugin ) {
252
- $settings_link = '<a href="options-general.php?page=bulk-delete.php">' . __('Manage', 'bulk-delete') . '</a>';
253
- array_unshift( $links, $settings_link ); // before other links
 
 
 
 
254
  }
255
  return $links;
256
  }
@@ -275,7 +355,7 @@ class Bulk_Delete {
275
  ?>
276
  <div class="wrap">
277
  <?php screen_icon(); ?>
278
- <h2><?php _e('Bulk Delete', 'bulk-delete');?></h2>
279
 
280
  <form method = "post">
281
  <?php
@@ -290,7 +370,7 @@ class Bulk_Delete {
290
  <div id="post-body" class="metabox-holder columns-2">
291
 
292
  <div id="post-body-content">
293
- <div style="background:#ff0;text-align:center;color: red;padding:1px">
294
  <p><strong><?php _e("WARNING: Posts deleted once cannot be retrieved back. Use with caution.", 'bulk-delete'); ?></strong></p>
295
  </div>
296
  </div><!-- #post-body-content -->
@@ -327,8 +407,8 @@ class Bulk_Delete {
327
  */
328
  function render_by_post_status_box() {
329
 
330
- if ( $this->is_hidden(self::BOX_POST_STATUS) ) {
331
- printf( __('This section just got enabled. Kindly <a href = "%1$s">refresh</a> the page to fully enable it.', 'bulk-delete' ), 'options-general.php?page=bulk-delete.php' );
332
  return;
333
  }
334
 
@@ -437,8 +517,8 @@ class Bulk_Delete {
437
  */
438
  function render_by_category_box() {
439
 
440
- if ( $this->is_hidden(self::BOX_CATEGORY) ) {
441
- printf( __('This section just got enabled. Kindly <a href = "%1$s">refresh</a> the page to fully enable it.', 'bulk-delete' ), 'options-general.php?page=bulk-delete.php' );
442
  return;
443
  }
444
 
@@ -555,8 +635,8 @@ class Bulk_Delete {
555
 
556
  function render_by_tag_box() {
557
 
558
- if ( $this->is_hidden(self::BOX_TAG) ) {
559
- printf( __('This section just got enabled. Kindly <a href = "%1$s">refresh</a> the page to fully enable it.', 'bulk-delete' ), 'options-general.php?page=bulk-delete.php' );
560
  return;
561
  }
562
 
@@ -675,8 +755,8 @@ class Bulk_Delete {
675
  */
676
  function render_by_tax_box() {
677
 
678
- if ( $this->is_hidden(self::BOX_TAX) ) {
679
- printf( __('This section just got enabled. Kindly <a href = "%1$s">refresh</a> the page to fully enable it.', 'bulk-delete' ), 'options-general.php?page=bulk-delete.php' );
680
  return;
681
  }
682
 
@@ -829,8 +909,8 @@ class Bulk_Delete {
829
  */
830
  function render_by_post_type_box() {
831
 
832
- if ( $this->is_hidden(self::BOX_POST_TYPE) ) {
833
- printf( __('This section just got enabled. Kindly <a href = "%1$s">refresh</a> the page to fully enable it.', 'bulk-delete' ), 'options-general.php?page=bulk-delete.php' );
834
  return;
835
  }
836
 
@@ -956,8 +1036,8 @@ class Bulk_Delete {
956
  */
957
  function render_by_page_box() {
958
 
959
- if ( $this->is_hidden(self::BOX_PAGE) ) {
960
- printf( __('This section just got enabled. Kindly <a href = "%1$s">refresh</a> the page to fully enable it.', 'bulk-delete' ), 'options-general.php?page=bulk-delete.php' );
961
  return;
962
  }
963
 
@@ -1076,8 +1156,8 @@ class Bulk_Delete {
1076
  */
1077
  function render_by_url_box() {
1078
 
1079
- if ( $this->is_hidden(self::BOX_URL) ) {
1080
- printf( __('This section just got enabled. Kindly <a href = "%1$s">refresh</a> the page to fully enable it.', 'bulk-delete' ), 'options-general.php?page=bulk-delete.php' );
1081
  return;
1082
  }
1083
 
@@ -1123,8 +1203,8 @@ class Bulk_Delete {
1123
  */
1124
  function render_by_post_revision_box() {
1125
 
1126
- if ( $this->is_hidden(self::BOX_POST_REVISION) ) {
1127
- printf( __('This section just got enabled. Kindly <a href = "%1$s">refresh</a> the page to fully enable it.', 'bulk-delete' ), 'options-general.php?page=bulk-delete.php' );
1128
  return;
1129
  }
1130
 
@@ -1190,6 +1270,158 @@ class Bulk_Delete {
1190
  <?php
1191
  }
1192
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1193
  /**
1194
  * Display the schedule page
1195
  */
@@ -1198,6 +1430,7 @@ class Bulk_Delete {
1198
  if(!class_exists('WP_List_Table')){
1199
  require_once( ABSPATH . WPINC . '/class-wp-list-table.php' );
1200
  }
 
1201
  if (!class_exists('Cron_List_Table')) {
1202
  require_once dirname(__FILE__) . '/include/class-cron-list-table.php';
1203
  }
@@ -1223,24 +1456,61 @@ class Bulk_Delete {
1223
  * Request Handler. Handles both POST and GET requests
1224
  */
1225
  function request_handler() {
1226
- if (isset($_GET['smbd_action'])) {
 
 
1227
 
1228
  switch($_GET['smbd_action']) {
1229
 
1230
  case 'delete-cron':
1231
- //TODO: Check for nonce and referer
1232
  $cron_id = absint($_GET['cron_id']);
1233
  $cron_items = $this->get_cron_schedules();
1234
  wp_unschedule_event($cron_items[$cron_id]['timestamp'], $cron_items[$cron_id]['type'], $cron_items[$cron_id]['args']);
1235
 
1236
  $this->msg = __('The selected scheduled job was successfully deleted ', 'bulk-delete');
1237
- // hook the admin notices action
1238
- add_action( 'admin_notices', array(&$this, 'deleted_notice'), 9 );
1239
 
1240
  break;
1241
  }
1242
  }
1243
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1244
  if ( isset( $_POST['smbd_action'] ) && check_admin_referer( 'sm-bulk-delete-posts', 'sm-bulk-delete-posts-nonce' ) ) {
1245
 
1246
  switch($_POST['smbd_action']) {
@@ -1269,7 +1539,7 @@ class Bulk_Delete {
1269
  }
1270
 
1271
  $this->msg = __('Posts from the selected categories are scheduled for deletion.', 'bulk-delete') . ' ' .
1272
- sprintf( __('See the full list of <a href = "%s">scheduled tasks</a>' , 'bulk-delete'), get_bloginfo("wpurl") . '/wp-admin/options-general.php?page=bulk-delete-cron');
1273
  } else {
1274
  $deleted_count = self::delete_cats($delete_options);
1275
  $this->msg = sprintf( _n('Deleted %d post from the selected categories', 'Deleted %d posts from the selected categories' , $deleted_count, 'bulk-delete' ), $deleted_count);
@@ -1300,7 +1570,7 @@ class Bulk_Delete {
1300
  wp_schedule_event($time, $freq, self::CRON_HOOK_TAGS, array($delete_options));
1301
  }
1302
  $this->msg = __('Posts from the selected tags are scheduled for deletion.', 'bulk-delete') . ' ' .
1303
- sprintf( __('See the full list of <a href = "%s">scheduled tasks</a>' , 'bulk-delete'), get_bloginfo("wpurl") . '/wp-admin/options-general.php?page=bulk-delete-cron');
1304
  } else {
1305
  $deleted_count = self::delete_tags($delete_options);
1306
  $this->msg = sprintf( _n('Deleted %d post from the selected tags', 'Deleted %d posts from the selected tags' , $deleted_count, 'bulk-delete' ), $deleted_count);
@@ -1332,7 +1602,7 @@ class Bulk_Delete {
1332
  wp_schedule_event($time, $freq, self::CRON_HOOK_TAXONOMY, array($delete_options));
1333
  }
1334
  $this->msg = __('Posts from the selected custom taxonomies are scheduled for deletion.', 'bulk-delete') . ' ' .
1335
- sprintf( __('See the full list of <a href = "%s">scheduled tasks</a>' , 'bulk-delete'), get_bloginfo("wpurl") . '/wp-admin/options-general.php?page=bulk-delete-cron');
1336
  } else {
1337
  $deleted_count = self::delete_taxs($delete_options);
1338
  $this->msg = sprintf( _n('Deleted %d post from the selected custom taxonomies', 'Deleted %d posts from the selected custom taxonomies' , $deleted_count, 'bulk-delete' ), $deleted_count);
@@ -1364,7 +1634,7 @@ class Bulk_Delete {
1364
  }
1365
 
1366
  $this->msg = __( 'Posts from the selected custom post type are scheduled for deletion.', 'bulk-delete') . ' ' .
1367
- sprintf( __( 'See the full list of <a href = "%s">scheduled tasks</a>' , 'bulk-delete'), get_bloginfo("wpurl") . '/wp-admin/options-general.php?page=bulk-delete-cron' );
1368
  } else {
1369
  $deleted_count = self::delete_post_types( $delete_options );
1370
  $this->msg = sprintf( _n( 'Deleted %d post from the selected custom post type', 'Deleted %d posts from the selected custom post type' , $deleted_count, 'bulk-delete' ), $deleted_count );
@@ -1398,7 +1668,7 @@ class Bulk_Delete {
1398
  wp_schedule_event($time, $freq, self::CRON_HOOK_POST_STATUS, array($delete_options));
1399
  }
1400
  $this->msg = __('Posts with the selected status are scheduled for deletion.', 'bulk-delete') . ' ' .
1401
- sprintf( __('See the full list of <a href = "%s">scheduled tasks</a>' , 'bulk-delete'), get_bloginfo("wpurl") . '/wp-admin/options-general.php?page=bulk-delete-cron');
1402
  } else {
1403
  $deleted_count = self::delete_post_status($delete_options);
1404
  $this->msg = sprintf( _n('Deleted %d post with the selected post status', 'Deleted %d posts with the selected post status' , $deleted_count, 'bulk-delete' ), $deleted_count);
@@ -1433,7 +1703,7 @@ class Bulk_Delete {
1433
  wp_schedule_event($time, $freq , self::CRON_HOOK_PAGES, array($delete_options));
1434
  }
1435
  $this->msg = __('The selected pages are scheduled for deletion.', 'bulk-delete') . ' ' .
1436
- sprintf( __('See the full list of <a href = "%s">scheduled tasks</a>' , 'bulk-delete'), get_bloginfo("wpurl") . '/wp-admin/options-general.php?page=bulk-delete-cron');
1437
  } else {
1438
  $deleted_count = self::delete_pages($delete_options);
1439
  $this->msg = sprintf( _n('Deleted %d page', 'Deleted %d pages' , $deleted_count, 'bulk-delete' ), $deleted_count);
@@ -1474,17 +1744,17 @@ class Bulk_Delete {
1474
  $this->msg = sprintf( _n( 'Deleted %d post revision', 'Deleted %d post revisions' , $deleted_count, 'bulk-delete' ), $deleted_count);
1475
  break;
1476
  }
1477
-
1478
- // hook the admin notices action
1479
- add_action( 'admin_notices', array(&$this, 'deleted_notice'), 9 );
1480
  }
 
 
 
1481
  }
1482
 
1483
  /**
1484
  * Show deleted notice messages
1485
  */
1486
  function deleted_notice() {
1487
- if ($this->msg != '') {
1488
  echo "<div class = 'updated'><p>" . $this->msg . "</p></div>";
1489
  }
1490
 
@@ -1494,27 +1764,49 @@ class Bulk_Delete {
1494
  }
1495
 
1496
  /**
1497
- * @brief Check whether the box is hidden or not
1498
  *
1499
  * @param $box
1500
  *
1501
  * @return
1502
  */
1503
- private function is_hidden( $box ) {
1504
- $hidden_boxes = $this->get_hidden_boxes();
1505
  return ( is_array( $hidden_boxes ) && in_array( $box, $hidden_boxes ) );
1506
  }
1507
 
1508
  /**
1509
- * Get the list of hidden boxes
1510
  *
1511
  * @return the array of hidden meta boxes
1512
  */
1513
- private function get_hidden_boxes() {
1514
  $current_user = wp_get_current_user();
1515
  return get_user_meta( $current_user->ID, 'metaboxhidden_settings_page_bulk-delete', TRUE );
1516
  }
1517
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1518
  /**
1519
  * Delete posts by category
1520
  */
3
  Plugin Name: Bulk Delete
4
  Plugin Script: bulk-delete.php
5
  Plugin URI: http://sudarmuthu.com/wordpress/bulk-delete
6
+ Description: Bulk delete users and posts from selected categories, tags, post types, custom taxonomies or by post status like drafts, scheduled posts, revisions etc.
7
  Donate Link: http://sudarmuthu.com/if-you-wanna-thank-me
8
+ Version: 4.0
9
  License: GPL
10
  Author: Sudar
11
  Author URI: http://sudarmuthu.com/
77
  2013-07-07 - v3.6.0 - (Dev time: 2 hours)
78
  - Change minimum requirement to WordPress 3.3
79
  - Fix compatibility issues with "The event calendar" Plugin
80
+ 2013-09-09 - v4.0 - (Dev time: 25 hours)
81
+ - Add the ability to delete users
82
+ - Move menu items under tools
83
  */
84
 
85
  /* Copyright 2009 Sudar Muthu (email : sudar@sudarmuthu.com)
98
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
99
  */
100
 
101
+ if ( !class_exists( 'Bulk_Delete_Users' ) ) {
102
+ require_once dirname( __FILE__ ) . '/include/class-bulk-delete-users.php';
103
+ }
104
+
105
+ if ( !class_exists( 'Bulk_Delete_Util' ) ) {
106
+ require_once dirname( __FILE__ ) . '/include/class-bulk-delete-util.php';
107
+ }
108
+
109
  /**
110
  * Bulk Delete Main class
111
  */
112
  class Bulk_Delete {
113
 
114
+ const VERSION = '4.0';
115
+
116
+ // page slugs
117
+ const USERS_PAGE_SLUG = 'bulk-delete-users';
118
+
119
+ // JS constants
120
  const JS_HANDLE = 'bulk-delete';
121
  const JS_VARIABLE = 'BULK_DELETE';
122
 
128
  const CRON_HOOK_TAXONOMY = 'do-bulk-delete-taxonomy';
129
  const CRON_HOOK_POST_TYPES = 'do-bulk-delete-post-types';
130
 
131
+ const CRON_HOOK_USER_ROLE = 'do-bulk-delete-users-by-role';
132
+
133
+ // meta boxes for delete posts
134
  const BOX_POST_STATUS = 'bd_by_post_status';
135
  const BOX_CATEGORY = 'bd_by_category';
136
  const BOX_TAG = 'bd_by_tag';
141
  const BOX_POST_REVISION = 'bd_by_post_revision';
142
  const BOX_DEBUG = 'bd_debug';
143
 
144
+ // meta boxes for delete users
145
+ const BOX_USERS = 'bdu_by_users';
146
+
147
  /**
148
  * Default constructor
149
  */
165
  * Add navigation menu
166
  */
167
  function add_menu() {
 
 
 
168
 
169
+ $this->admin_page = add_submenu_page( 'tools.php', __("Bulk Delete Posts", 'bulk-delete'), __("Bulk Delete Posts", 'bulk-delete'), 'delete_posts', basename(__FILE__), array(&$this, 'display_setting_page'));
170
+ $this->users_page = add_submenu_page( 'tools.php', __("Bulk Delete Users", 'bulk-delete'), __("Bulk Delete Users", 'bulk-delete'), 'delete_users', self::USERS_PAGE_SLUG, array( &$this, 'display_users_page' ));
171
+ $this->cron_page = add_submenu_page( 'tools.php', __("Bulk Delete Schedules", 'bulk-delete'), __("Bulk Delete Schedules", 'bulk-delete'), 'delete_posts', 'bulk-delete-cron', array(&$this, 'display_cron_page'));
172
+
173
+ // enqueue JavaScript
174
+ add_action( 'admin_print_scripts-' . $this->admin_page, array( &$this, 'add_script') );
175
+ add_action( 'admin_print_scripts-' . $this->users_page, array( &$this, 'add_script') );
176
+
177
+ // delete posts page
178
+ add_action( "load-{$this->admin_page}", array( &$this, 'add_delete_posts_settings_panel' ) );
179
+ add_action( "add_meta_boxes_{$this->admin_page}", array( &$this, 'add_delete_posts_meta_boxes' ) );
180
 
181
+ // delete users page
182
+ add_action( "load-{$this->users_page}", array( &$this, 'add_delete_users_settings_panel' ) );
183
+ add_action( "add_meta_boxes_{$this->users_page}", array( &$this, 'add_delete_users_meta_boxes' ) );
184
  }
185
 
186
  /**
187
+ * Add settings Panel for delete posts page
188
+ */
189
+ function add_delete_posts_settings_panel() {
190
+
191
+ /**
192
+ * Create the WP_Screen object using page handle
193
+ */
194
+ $this->delete_posts_screen = WP_Screen::get($this->admin_page);
195
+
196
+ /**
197
+ * Content specified inline
198
+ */
199
+ $this->delete_posts_screen->add_help_tab(
200
+ array(
201
+ 'title' => __('About Plugin', 'bulk-delete'),
202
+ 'id' => 'about_tab',
203
+ 'content' => '<p>' . __('This plugin allows you to delete posts in bulk from selected categories, tags, custom taxonomies or by post status like drafts, pending posts, scheduled posts etc.', 'bulk-delete') . '</p>',
204
+ 'callback' => false
205
+ )
206
+ );
207
+
208
+ // Add help sidebar
209
+ $this->delete_posts_screen->set_help_sidebar(
210
+ '<p><strong>' . __('More information', 'bulk-delete') . '</strong></p>' .
211
+ '<p><a href = "http://sudarmuthu.com/wordpress/bulk-delete">' . __('Plugin Homepage/support', 'bulk-delete') . '</a></p>' .
212
+ '<p><a href = "http://sudarmuthu.com/wordpress/bulk-delete/pro-addons">' . __("Buy pro addons", 'bulk-delete') . '</a></p>' .
213
+ '<p><a href = "http://sudarmuthu.com/blog">' . __("Plugin author's blog", 'bulk-delete') . '</a></p>' .
214
+ '<p><a href = "http://sudarmuthu.com/wordpress/">' . __("Other Plugin's by Author", 'bulk-delete') . '</a></p>'
215
+ );
216
+
217
+ /* Trigger the add_meta_boxes hooks to allow meta boxes to be added */
218
+ do_action('add_meta_boxes_' . $this->admin_page, null);
219
+ do_action('add_meta_boxes', $this->admin_page, null);
220
+
221
+ /* Enqueue WordPress' script for handling the meta boxes */
222
+ wp_enqueue_script('postbox');
223
+ }
224
+
225
+ /**
226
+ * Register meta boxes for delete posts page
227
  */
228
+ function add_delete_posts_meta_boxes() {
229
  add_meta_box( self::BOX_POST_STATUS, __( 'By Post Status', 'bulk-delete' ), array( &$this, 'render_by_post_status_box' ), $this->admin_page, 'advanced' );
230
  add_meta_box( self::BOX_CATEGORY, __( 'By Category', 'bulk-delete' ), array( &$this, 'render_by_category_box' ), $this->admin_page, 'advanced' );
231
  add_meta_box( self::BOX_TAG, __( 'By Tag', 'bulk-delete' ), array( &$this, 'render_by_tag_box' ), $this->admin_page, 'advanced' );
238
  }
239
 
240
  /**
241
+ * Add settings Panel for delete users page
242
  */
243
+ function add_delete_users_settings_panel() {
244
 
245
  /**
246
  * Create the WP_Screen object using page handle
247
  */
248
+ $this->delete_users_screen = WP_Screen::get( $this->users_page );
249
 
250
  /**
251
  * Content specified inline
252
  */
253
+ $this->delete_users_screen->add_help_tab(
254
  array(
255
  'title' => __('About Plugin', 'bulk-delete'),
256
  'id' => 'about_tab',
260
  );
261
 
262
  // Add help sidebar
263
+ $this->delete_users_screen->set_help_sidebar(
264
  '<p><strong>' . __('More information', 'bulk-delete') . '</strong></p>' .
265
  '<p><a href = "http://sudarmuthu.com/wordpress/bulk-delete">' . __('Plugin Homepage/support', 'bulk-delete') . '</a></p>' .
266
  '<p><a href = "http://sudarmuthu.com/wordpress/bulk-delete/pro-addons">' . __("Buy pro addons", 'bulk-delete') . '</a></p>' .
269
  );
270
 
271
  /* Trigger the add_meta_boxes hooks to allow meta boxes to be added */
272
+ do_action('add_meta_boxes_' . $this->users_page, null);
273
+ do_action('add_meta_boxes', $this->users_page, null);
274
 
275
  /* Enqueue WordPress' script for handling the meta boxes */
276
  wp_enqueue_script('postbox');
277
  }
278
 
279
+ /**
280
+ * Register meta boxes for delete users page
281
+ */
282
+ function add_delete_users_meta_boxes() {
283
+ add_meta_box( self::BOX_USERS, __( 'By User Role', 'bulk-delete' ), array( &$this, 'render_delete_users_box' ), $this->users_page, 'advanced' );
284
+ }
285
+
286
  /**
287
  * Enqueue JavaScript
288
  */
290
  global $wp_scripts;
291
 
292
  // uses code from http://trentrichardson.com/examples/timepicker/
293
+ wp_enqueue_script( 'jquery-ui-timepicker', plugins_url( '/js/jquery-ui-timepicker.js', __FILE__ ), array( 'jquery-ui-slider', 'jquery-ui-datepicker' ), '1.4', true );
294
  wp_enqueue_script( self::JS_HANDLE, plugins_url('/js/bulk-delete.js', __FILE__), array('jquery-ui-timepicker'), self::VERSION, TRUE);
295
 
296
  $ui = $wp_scripts->query('jquery-ui-core');
301
 
302
  // JavaScript messages
303
  $msg = array(
304
+ 'deletewarning' => __('Are you sure you want to delete all the selected posts', 'bulk-delete'),
305
+ 'deletewarningusers' => __( 'Are you sure you want to delete all the selected users', 'bulk-delete' )
306
  );
307
 
308
  $error = array(
309
  'selectone' => __('Please select posts from at least one option', 'bulk-delete'),
310
+ 'enterurl' => __('Please enter at least one page url', 'bulk-delete')
311
  );
312
 
313
  $translation_array = array( 'msg' => $msg, 'error' => $error );
325
  if( ! $this_plugin ) $this_plugin = plugin_basename(__FILE__);
326
 
327
  if( $file == $this_plugin ) {
328
+
329
+ $delete_users_link = '<a href="tools.php?page=' . self::USERS_PAGE_SLUG . '">' . __('Bulk Delete Users', 'bulk-delete') . '</a>';
330
+ array_unshift( $links, $delete_users_link ); // before other links
331
+
332
+ $delete_posts_link = '<a href="tools.php?page=bulk-delete.php">' . __('Bulk Delete Posts', 'bulk-delete') . '</a>';
333
+ array_unshift( $links, $delete_posts_link ); // before other links
334
  }
335
  return $links;
336
  }
355
  ?>
356
  <div class="wrap">
357
  <?php screen_icon(); ?>
358
+ <h2><?php _e('Bulk Delete Posts', 'bulk-delete');?></h2>
359
 
360
  <form method = "post">
361
  <?php
370
  <div id="post-body" class="metabox-holder columns-2">
371
 
372
  <div id="post-body-content">
373
+ <div class="updated" >
374
  <p><strong><?php _e("WARNING: Posts deleted once cannot be retrieved back. Use with caution.", 'bulk-delete'); ?></strong></p>
375
  </div>
376
  </div><!-- #post-body-content -->
407
  */
408
  function render_by_post_status_box() {
409
 
410
+ if ( $this->is_posts_box_hidden(self::BOX_POST_STATUS) ) {
411
+ printf( __('This section just got enabled. Kindly <a href = "%1$s">refresh</a> the page to fully enable it.', 'bulk-delete' ), 'tools.php?page=bulk-delete.php' );
412
  return;
413
  }
414
 
517
  */
518
  function render_by_category_box() {
519
 
520
+ if ( $this->is_posts_box_hidden(self::BOX_CATEGORY) ) {
521
+ printf( __('This section just got enabled. Kindly <a href = "%1$s">refresh</a> the page to fully enable it.', 'bulk-delete' ), 'tools.php?page=bulk-delete.php' );
522
  return;
523
  }
524
 
635
 
636
  function render_by_tag_box() {
637
 
638
+ if ( $this->is_posts_box_hidden(self::BOX_TAG) ) {
639
+ printf( __('This section just got enabled. Kindly <a href = "%1$s">refresh</a> the page to fully enable it.', 'bulk-delete' ), 'tools.php?page=bulk-delete.php' );
640
  return;
641
  }
642
 
755
  */
756
  function render_by_tax_box() {
757
 
758
+ if ( $this->is_posts_box_hidden(self::BOX_TAX) ) {
759
+ printf( __('This section just got enabled. Kindly <a href = "%1$s">refresh</a> the page to fully enable it.', 'bulk-delete' ), 'tools.php?page=bulk-delete.php' );
760
  return;
761
  }
762
 
909
  */
910
  function render_by_post_type_box() {
911
 
912
+ if ( $this->is_posts_box_hidden(self::BOX_POST_TYPE) ) {
913
+ printf( __('This section just got enabled. Kindly <a href = "%1$s">refresh</a> the page to fully enable it.', 'bulk-delete' ), 'tools.php?page=bulk-delete.php' );
914
  return;
915
  }
916
 
1036
  */
1037
  function render_by_page_box() {
1038
 
1039
+ if ( $this->is_posts_box_hidden(self::BOX_PAGE) ) {
1040
+ printf( __('This section just got enabled. Kindly <a href = "%1$s">refresh</a> the page to fully enable it.', 'bulk-delete' ), 'tools.php?page=bulk-delete.php' );
1041
  return;
1042
  }
1043
 
1156
  */
1157
  function render_by_url_box() {
1158
 
1159
+ if ( $this->is_posts_box_hidden(self::BOX_URL) ) {
1160
+ printf( __('This section just got enabled. Kindly <a href = "%1$s">refresh</a> the page to fully enable it.', 'bulk-delete' ), 'tools.php?page=bulk-delete.php' );
1161
  return;
1162
  }
1163
 
1203
  */
1204
  function render_by_post_revision_box() {
1205
 
1206
+ if ( $this->is_posts_box_hidden(self::BOX_POST_REVISION) ) {
1207
+ printf( __('This section just got enabled. Kindly <a href = "%1$s">refresh</a> the page to fully enable it.', 'bulk-delete' ), 'tools.php?page=bulk-delete.php' );
1208
  return;
1209
  }
1210
 
1270
  <?php
1271
  }
1272
 
1273
+ /**
1274
+ * Render delete users box
1275
+ */
1276
+ function render_delete_users_box() {
1277
+
1278
+ if ( $this->is_users_box_hidden(self::BOX_USERS) ) {
1279
+ printf( __('This section just got enabled. Kindly <a href = "%1$s">refresh</a> the page to fully enable it.', 'bulk-delete' ), 'tools.php?page=' . self::USERS_PAGE_SLUG );
1280
+ return;
1281
+ }
1282
+ ?>
1283
+ <!-- Users Start-->
1284
+ <h4><?php _e("Select the user roles from which you want to delete users", 'bulk-delete'); ?></h4>
1285
+
1286
+ <fieldset class="options">
1287
+ <table class="optiontable">
1288
+ <?php
1289
+ $users_count = count_users();
1290
+ foreach( $users_count['avail_roles'] as $role => $count ) {
1291
+ ?>
1292
+ <tr>
1293
+ <td scope="row" >
1294
+ <input name="smbdu_roles[]" value = "<?php echo $role; ?>" type = "checkbox" />
1295
+ </td>
1296
+ <td>
1297
+ <label for="smbdu_roles"><?php echo $role; ?> (<?php echo $count . " "; _e("Users", 'bulk-delete'); ?>)</label>
1298
+ </td>
1299
+ </tr>
1300
+ <?php
1301
+ }
1302
+ ?>
1303
+ <tr>
1304
+ <td colspan="2">
1305
+ <h4><?php _e("Choose your filtering options", 'bulk-delete'); ?></h4>
1306
+ </td>
1307
+ </tr>
1308
+
1309
+ <?php
1310
+ if ( !Bulk_Delete_Util::is_simple_login_log_present() ) {
1311
+ $disabled = "disabled";
1312
+ } else {
1313
+ $disabled = '';
1314
+ }
1315
+ ?>
1316
+ <tr>
1317
+ <td scope="row">
1318
+ <input name="smbdu_login_restrict" id="smbdu_login_restrict" value = "true" type = "checkbox" <?php echo $disabled; ?> >
1319
+ </td>
1320
+ <td>
1321
+ <?php _e("Only restrict to users who have not logged in the last ", 'bulk-delete');?>
1322
+ <input type ="textbox" name="smbdu_login_days" id="smbdu_login_days" value ="0" maxlength="4" size="4" <?php echo $disabled; ?> ><?php _e("days", 'bulk-delete');?>
1323
+ <?php
1324
+ if ( !Bulk_Delete_Util::is_simple_login_log_present() ) {
1325
+ ?>
1326
+ <span style = "color:red">
1327
+ <?php _e('Need Simple Login Log Plugin', 'bulk-delete'); ?> <a href = "http://wordpress.org/plugins/simple-login-log/">Install now</a>
1328
+ </span>
1329
+ <?php
1330
+ }
1331
+ ?>
1332
+ </td>
1333
+ </tr>
1334
+
1335
+ <tr>
1336
+ <td scope="row">
1337
+ <input name="smbdu_role_no_posts" id="smbdu_role_no_posts" value = "true" type = "checkbox" >
1338
+ </td>
1339
+ <td>
1340
+ <?php _e( "Only if user doesn't have any post", 'bulk-delete' ); ?>
1341
+ </td>
1342
+ </tr>
1343
+
1344
+ <tr>
1345
+ <td scope="row" colspan="2">
1346
+ <input name="smbdu_userrole_cron" value = "false" type = "radio" checked="checked" /> <?php _e('Delete now', 'bulk-delete'); ?>
1347
+ <input name="smbdu_userrole_cron" value = "true" type = "radio" id = "smbdu_userrole_cron" disabled > <?php _e('Schedule', 'bulk-delete'); ?>
1348
+ <input name="smbdu_userrole_cron_start" id = "smbdu_userrole_cron_start" value = "now" type = "text" disabled><?php _e('repeat ', 'bulk-delete');?>
1349
+ <select name = "smbdu_userrole_cron_freq" id = "smbdu_userrole_cron_freq" disabled>
1350
+ <option value = "-1"><?php _e("Don't repeat", 'bulk-delete'); ?></option>
1351
+ <?php
1352
+ $schedules = wp_get_schedules();
1353
+ foreach($schedules as $key => $value) {
1354
+ ?>
1355
+ <option value = "<?php echo $key; ?>"><?php echo $value['display']; ?></option>
1356
+ <?php
1357
+ }
1358
+ ?>
1359
+ </select>
1360
+ <span class = "bdu-users-by-role-pro" style = "color:red"><?php _e('Only available in Pro Addon', 'bulk-delete'); ?> <a href = "http://sudarmuthu.com/out/buy-bulk-delete-users-by-role-addon">Buy now</a></span>
1361
+ </td>
1362
+ </tr>
1363
+
1364
+ <tr>
1365
+ <td scope="row" colspan="2">
1366
+ <?php _e("Enter time in Y-m-d H:i:s format or enter now to use current time", 'bulk-delete');?>
1367
+ </td>
1368
+ </tr>
1369
+
1370
+ </table>
1371
+ </fieldset>
1372
+
1373
+ <p>
1374
+ <button type="submit" name="smbdu_action" value = "bulk-delete-users-by-role" class="button-primary"><?php _e("Bulk Delete ", 'bulk-delete') ?>&raquo;</button>
1375
+ </p>
1376
+ <!-- Users end-->
1377
+ <?php
1378
+ }
1379
+
1380
+ /**
1381
+ * Display bulk delete users page
1382
+ */
1383
+ function display_users_page() {
1384
+ ?>
1385
+ <div class="wrap">
1386
+ <?php screen_icon(); ?>
1387
+ <h2><?php _e('Bulk Delete Users', 'bulk-delete');?></h2>
1388
+
1389
+ <form method = "post">
1390
+ <?php
1391
+ // nonce for bulk delete
1392
+ wp_nonce_field( 'sm-bulk-delete-users', 'sm-bulk-delete-users-nonce' );
1393
+
1394
+ /* Used to save closed meta boxes and their order */
1395
+ wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false );
1396
+ wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
1397
+ ?>
1398
+ <div id = "poststuff">
1399
+ <div id="post-body" class="metabox-holder columns-2">
1400
+
1401
+ <div id="post-body-content">
1402
+ <div class = "updated">
1403
+ <p><strong><?php _e("WARNING: Users deleted once cannot be retrieved back. Use with caution.", 'bulk-delete'); ?></strong></p>
1404
+ </div>
1405
+ </div><!-- #post-body-content -->
1406
+
1407
+ <div id="postbox-container-1" class="postbox-container">
1408
+ <iframe frameBorder="0" height = "1000" src = "http://sudarmuthu.com/projects/wordpress/bulk-delete/sidebar.php?color=<?php echo get_user_option('admin_color'); ?>&version=<?php echo self::VERSION; ?>"></iframe>
1409
+ </div>
1410
+
1411
+ <div id="postbox-container-2" class="postbox-container">
1412
+ <?php do_meta_boxes( '', 'advanced', null ); ?>
1413
+ </div> <!-- #postbox-container-2 -->
1414
+
1415
+ </div> <!-- #post-body -->
1416
+ </div><!-- #poststuff -->
1417
+ </form>
1418
+ </div><!-- .wrap -->
1419
+
1420
+ <?php
1421
+ // Display credits in Footer
1422
+ add_action( 'in_admin_footer', array(&$this, 'admin_footer' ));
1423
+ }
1424
+
1425
  /**
1426
  * Display the schedule page
1427
  */
1430
  if(!class_exists('WP_List_Table')){
1431
  require_once( ABSPATH . WPINC . '/class-wp-list-table.php' );
1432
  }
1433
+
1434
  if (!class_exists('Cron_List_Table')) {
1435
  require_once dirname(__FILE__) . '/include/class-cron-list-table.php';
1436
  }
1456
  * Request Handler. Handles both POST and GET requests
1457
  */
1458
  function request_handler() {
1459
+
1460
+ // delete schedules
1461
+ if ( isset( $_GET['smbd_action'] ) && check_admin_referer( 'sm-bulk-delete-cron', 'sm-bulk-delete-cron-nonce' ) ) {
1462
 
1463
  switch($_GET['smbd_action']) {
1464
 
1465
  case 'delete-cron':
 
1466
  $cron_id = absint($_GET['cron_id']);
1467
  $cron_items = $this->get_cron_schedules();
1468
  wp_unschedule_event($cron_items[$cron_id]['timestamp'], $cron_items[$cron_id]['type'], $cron_items[$cron_id]['args']);
1469
 
1470
  $this->msg = __('The selected scheduled job was successfully deleted ', 'bulk-delete');
 
 
1471
 
1472
  break;
1473
  }
1474
  }
1475
 
1476
+ // delete users
1477
+ if ( isset( $_POST['smbdu_action'] ) && check_admin_referer( 'sm-bulk-delete-users', 'sm-bulk-delete-users-nonce' ) ) {
1478
+
1479
+ switch( $_POST['smbdu_action'] ) {
1480
+
1481
+ case "bulk-delete-users-by-role":
1482
+ // delete by user role
1483
+
1484
+ $delete_options = array();
1485
+ $delete_options['selected_roles'] = array_get( $_POST, 'smbdu_roles' );
1486
+ $delete_options['no_posts'] = array_get( $_POST, 'smbdu_role_no_posts', FALSE );
1487
+
1488
+ $delete_options['login_restrict'] = array_get( $_POST, 'smbdu_login_restrict', FALSE );
1489
+ $delete_options['login_days'] = array_get( $_POST, 'smbdu_login_days' );
1490
+ $delete_options['limit_to'] = array_get( $_POST, 'smbdu_role_limit' );
1491
+
1492
+ if (array_get( $_POST, 'smbdu_userrole_cron', 'false' ) == 'true' ) {
1493
+ $freq = $_POST['smbdu_userrole_cron_freq'];
1494
+ $time = strtotime( $_POST['smbdu_userrole_cron_start'] ) - ( get_option( 'gmt_offset' ) * 60 * 60 );
1495
+
1496
+ if ( $freq == -1 ) {
1497
+ wp_schedule_single_event( $time, self::CRON_HOOK_USER_ROLE, array( $delete_options ) );
1498
+ } else {
1499
+ wp_schedule_event( $time, $freq , self::CRON_HOOK_USER_ROLE, array( $delete_options ) );
1500
+ }
1501
+
1502
+ $this->msg = __( 'Users from the selected userrole are scheduled for deletion.', 'bulk-delete' ) . ' ' .
1503
+ sprintf( __( 'See the full list of <a href = "%s">scheduled tasks</a>' , 'bulk-delete' ), get_bloginfo( "wpurl" ) . '/wp-admin/tools.php?page=bulk-delete-cron' );
1504
+ } else {
1505
+ $deleted_count = Bulk_Delete_Users::delete_users_by_role( $delete_options );
1506
+ $this->msg = sprintf( _n('Deleted %d user from the selected roles', 'Deleted %d users from the selected role' , $deleted_count, 'bulk-delete' ), $deleted_count );
1507
+ }
1508
+
1509
+ break;
1510
+ }
1511
+ }
1512
+
1513
+ // delete posts
1514
  if ( isset( $_POST['smbd_action'] ) && check_admin_referer( 'sm-bulk-delete-posts', 'sm-bulk-delete-posts-nonce' ) ) {
1515
 
1516
  switch($_POST['smbd_action']) {
1539
  }
1540
 
1541
  $this->msg = __('Posts from the selected categories are scheduled for deletion.', 'bulk-delete') . ' ' .
1542
+ sprintf( __('See the full list of <a href = "%s">scheduled tasks</a>' , 'bulk-delete'), get_bloginfo("wpurl") . '/wp-admin/tools.php?page=bulk-delete-cron');
1543
  } else {
1544
  $deleted_count = self::delete_cats($delete_options);
1545
  $this->msg = sprintf( _n('Deleted %d post from the selected categories', 'Deleted %d posts from the selected categories' , $deleted_count, 'bulk-delete' ), $deleted_count);
1570
  wp_schedule_event($time, $freq, self::CRON_HOOK_TAGS, array($delete_options));
1571
  }
1572
  $this->msg = __('Posts from the selected tags are scheduled for deletion.', 'bulk-delete') . ' ' .
1573
+ sprintf( __('See the full list of <a href = "%s">scheduled tasks</a>' , 'bulk-delete'), get_bloginfo("wpurl") . '/wp-admin/tools.php?page=bulk-delete-cron');
1574
  } else {
1575
  $deleted_count = self::delete_tags($delete_options);
1576
  $this->msg = sprintf( _n('Deleted %d post from the selected tags', 'Deleted %d posts from the selected tags' , $deleted_count, 'bulk-delete' ), $deleted_count);
1602
  wp_schedule_event($time, $freq, self::CRON_HOOK_TAXONOMY, array($delete_options));
1603
  }
1604
  $this->msg = __('Posts from the selected custom taxonomies are scheduled for deletion.', 'bulk-delete') . ' ' .
1605
+ sprintf( __('See the full list of <a href = "%s">scheduled tasks</a>' , 'bulk-delete'), get_bloginfo("wpurl") . '/wp-admin/tools.php?page=bulk-delete-cron');
1606
  } else {
1607
  $deleted_count = self::delete_taxs($delete_options);
1608
  $this->msg = sprintf( _n('Deleted %d post from the selected custom taxonomies', 'Deleted %d posts from the selected custom taxonomies' , $deleted_count, 'bulk-delete' ), $deleted_count);
1634
  }
1635
 
1636
  $this->msg = __( 'Posts from the selected custom post type are scheduled for deletion.', 'bulk-delete') . ' ' .
1637
+ sprintf( __( 'See the full list of <a href = "%s">scheduled tasks</a>' , 'bulk-delete'), get_bloginfo("wpurl") . '/wp-admin/tools.php?page=bulk-delete-cron' );
1638
  } else {
1639
  $deleted_count = self::delete_post_types( $delete_options );
1640
  $this->msg = sprintf( _n( 'Deleted %d post from the selected custom post type', 'Deleted %d posts from the selected custom post type' , $deleted_count, 'bulk-delete' ), $deleted_count );
1668
  wp_schedule_event($time, $freq, self::CRON_HOOK_POST_STATUS, array($delete_options));
1669
  }
1670
  $this->msg = __('Posts with the selected status are scheduled for deletion.', 'bulk-delete') . ' ' .
1671
+ sprintf( __('See the full list of <a href = "%s">scheduled tasks</a>' , 'bulk-delete'), get_bloginfo("wpurl") . '/wp-admin/tools.php?page=bulk-delete-cron');
1672
  } else {
1673
  $deleted_count = self::delete_post_status($delete_options);
1674
  $this->msg = sprintf( _n('Deleted %d post with the selected post status', 'Deleted %d posts with the selected post status' , $deleted_count, 'bulk-delete' ), $deleted_count);
1703
  wp_schedule_event($time, $freq , self::CRON_HOOK_PAGES, array($delete_options));
1704
  }
1705
  $this->msg = __('The selected pages are scheduled for deletion.', 'bulk-delete') . ' ' .
1706
+ sprintf( __('See the full list of <a href = "%s">scheduled tasks</a>' , 'bulk-delete'), get_bloginfo("wpurl") . '/wp-admin/tools.php?page=bulk-delete-cron');
1707
  } else {
1708
  $deleted_count = self::delete_pages($delete_options);
1709
  $this->msg = sprintf( _n('Deleted %d page', 'Deleted %d pages' , $deleted_count, 'bulk-delete' ), $deleted_count);
1744
  $this->msg = sprintf( _n( 'Deleted %d post revision', 'Deleted %d post revisions' , $deleted_count, 'bulk-delete' ), $deleted_count);
1745
  break;
1746
  }
 
 
 
1747
  }
1748
+
1749
+ // hook the admin notices action
1750
+ add_action( 'admin_notices', array(&$this, 'deleted_notice'), 9 );
1751
  }
1752
 
1753
  /**
1754
  * Show deleted notice messages
1755
  */
1756
  function deleted_notice() {
1757
+ if ( isset( $this->msg ) && $this->msg != '' ) {
1758
  echo "<div class = 'updated'><p>" . $this->msg . "</p></div>";
1759
  }
1760
 
1764
  }
1765
 
1766
  /**
1767
+ * @brief Check whether the meta box in posts page is hidden or not
1768
  *
1769
  * @param $box
1770
  *
1771
  * @return
1772
  */
1773
+ private function is_posts_box_hidden( $box ) {
1774
+ $hidden_boxes = $this->get_posts_hidden_boxes();
1775
  return ( is_array( $hidden_boxes ) && in_array( $box, $hidden_boxes ) );
1776
  }
1777
 
1778
  /**
1779
+ * Get the list of hidden boxes in posts page
1780
  *
1781
  * @return the array of hidden meta boxes
1782
  */
1783
+ private function get_posts_hidden_boxes() {
1784
  $current_user = wp_get_current_user();
1785
  return get_user_meta( $current_user->ID, 'metaboxhidden_settings_page_bulk-delete', TRUE );
1786
  }
1787
 
1788
+ /**
1789
+ * @brief Check whether the meta box in users page is hidden or not
1790
+ *
1791
+ * @param $box
1792
+ *
1793
+ * @return
1794
+ */
1795
+ private function is_users_box_hidden( $box ) {
1796
+ $hidden_boxes = $this->get_users_hidden_boxes();
1797
+ return ( is_array( $hidden_boxes ) && in_array( $box, $hidden_boxes ) );
1798
+ }
1799
+
1800
+ /**
1801
+ * Get the list of hidden boxes in users page
1802
+ *
1803
+ * @return the array of hidden meta boxes
1804
+ */
1805
+ private function get_users_hidden_boxes() {
1806
+ $current_user = wp_get_current_user();
1807
+ return get_user_meta( $current_user->ID, 'metaboxhidden_settings_page_bulk-delete-users', TRUE );
1808
+ }
1809
+
1810
  /**
1811
  * Delete posts by category
1812
  */
include/class-bulk-delete-users.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Utility class for deleting users
4
+ *
5
+ * @package Bulk Delete
6
+ * @author Sudar
7
+ */
8
+ class Bulk_Delete_Users {
9
+
10
+ /**
11
+ * Delete users by user role
12
+ */
13
+ public static function delete_users_by_role( $delete_options ) {
14
+
15
+ if( !function_exists( 'wp_delete_user' ) ) {
16
+ require_once( ABSPATH . 'wp-admin/includes/user.php' );
17
+ }
18
+
19
+ $count = 0;
20
+
21
+ foreach ( $delete_options['selected_roles'] as $role ) {
22
+
23
+ $options = array();
24
+ $options['role'] = $role;
25
+ $users = get_users( $options );
26
+
27
+ foreach ( $users as $user ) {
28
+ if ( $delete_options['no_posts'] == TRUE && count_user_posts ( $user->ID ) > 0 ) {
29
+ continue;
30
+ }
31
+
32
+ if ( $delete_options['login_restrict'] == TRUE ) {
33
+ $login_days = $delete_options['login_days'];
34
+ $last_login = self::get_last_login( $user->ID );
35
+
36
+ if ( $last_login != NULL ) {
37
+ if ( strtotime( $last_login ) > strtotime( '-' . $login_days . 'days' ) ) {
38
+ continue;
39
+ }
40
+ } else {
41
+ continue;
42
+ }
43
+ }
44
+
45
+ wp_delete_user( $user->ID );
46
+ $count ++;
47
+ }
48
+ }
49
+
50
+ return $count;
51
+ }
52
+
53
+ /**
54
+ * Find the last login date/time of a user
55
+ */
56
+ private static function get_last_login( $user_id ) {
57
+ global $wpdb;
58
+
59
+ return $wpdb->get_var( $wpdb->prepare( "SELECT time FROM {$wpdb->prefix}" . Bulk_Delete_Util::SIMPLE_LOGIN_LOG_TABLE .
60
+ " WHERE uid = %d ORDER BY time DESC LIMIT 1", $user_id ) );
61
+ }
62
+ }
63
+ ?>
include/class-bulk-delete-util.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Utility class
4
+ *
5
+ * @package Bulk Delete
6
+ * @author Sudar
7
+ */
8
+ class Bulk_Delete_Util {
9
+
10
+ // simple login log
11
+ const SIMPLE_LOGIN_LOG_TABLE = 'simple_login_log';
12
+
13
+ /**
14
+ * Find out if Simple Login Log is installed or not
15
+ * http://wordpress.org/plugins/simple-login-log/
16
+ */
17
+ public static function is_simple_login_log_present() {
18
+ global $wpdb;
19
+
20
+ if( $wpdb->get_row( "SHOW TABLES LIKE '{$wpdb->prefix}" . self::SIMPLE_LOGIN_LOG_TABLE . "'" ) ) {
21
+ return TRUE;
22
+ }
23
+
24
+ return FALSE;
25
+ }
26
+ }
27
+ ?>
include/class-cron-list-table.php CHANGED
@@ -38,7 +38,7 @@ class Cron_List_Table extends WP_List_Table {
38
  echo '<strong>';
39
  _e('Note: ', 'bulk-delete');
40
  echo '</strong>';
41
- _e('Scheduling auto post deletion is available only when you buy pro addons.', 'bulk-delete');
42
  echo '</p>';
43
 
44
  echo '<p>';
@@ -48,47 +48,54 @@ class Cron_List_Table extends WP_List_Table {
48
  echo '<ul style="list-style:disc; padding-left:35px">';
49
 
50
  echo '<li>';
51
- echo '<strong>', __('Bulk Delete Schedule Categories', 'bulk-delete'), '</strong>', ' - ';
52
  echo __('Adds the ability to schedule auto delete of posts based on categories', 'bulk-delete');
53
  echo ' <a href = "http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-categories">', __('More Info', 'bulk-delete'), '</a>.';
54
  echo ' <a href = "http://sudarmuthu.com/out/buy-bulk-delete-category-addon">', __('Buy now', 'bulk-delete'), '</a>';
55
  echo '</li>';
56
 
57
  echo '<li>';
58
- echo '<strong>', __('Bulk Delete Schedule Tags', 'bulk-delete'), '</strong>', ' - ';
59
  echo __('Adds the ability to schedule auto delete of posts based on tags', 'bulk-delete');
60
  echo ' <a href = "http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-tags">', __('More Info', 'bulk-delete'), '</a>.';
61
  echo ' <a href = "http://sudarmuthu.com/out/buy-bulk-delete-tags-addon">', __('Buy now', 'bulk-delete'), '</a>';
62
  echo '</li>';
63
 
64
  echo '<li>';
65
- echo '<strong>', __('Bulk Delete Schedule Custom Taxonomy', 'bulk-delete'), '</strong>', ' - ';
66
  echo __('Adds the ability to schedule auto delete of posts based on custom taxonomies', 'bulk-delete');
67
  echo ' <a href = "http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-taxonomy">', __('More Info', 'bulk-delete'), '</a>.';
68
  echo ' <a href = "http://sudarmuthu.com/out/buy-bulk-delete-taxonomy-addon">', __('Buy now', 'bulk-delete'), '</a>';
69
  echo '</li>';
70
 
71
  echo '<li>';
72
- echo '<strong>', __('Bulk Delete Schedule Custom Post Type', 'bulk-delete'), '</strong>', ' - ';
73
  echo __('Adds the ability to schedule auto delete of posts based on custom post types', 'bulk-delete');
74
  echo ' <a href = "http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-post-types">', __('More Info', 'bulk-delete'), '</a>.';
75
  echo ' <a href = "http://sudarmuthu.com/out/buy-bulk-delete-post-type-addon">', __('Buy now', 'bulk-delete'), '</a>';
76
  echo '</li>';
77
 
78
  echo '<li>';
79
- echo '<strong>', __('Bulk Delete Schedule Pages', 'bulk-delete'), '</strong>', ' - ';
80
  echo __('Adds the ability to schedule auto delete pages', 'bulk-delete');
81
  echo ' <a href = "http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-pages">', __('More Info', 'bulk-delete'), '</a>.';
82
  echo ' <a href = "http://sudarmuthu.com/out/buy-bulk-delete-pages-addon">', __('Buy now', 'bulk-delete'), '</a>';
83
  echo '</li>';
84
 
85
  echo '<li>';
86
- echo '<strong>', __('Bulk Delete Schedule by Post Status', 'bulk-delete'), '</strong>', ' - ';
87
  echo __('Adds the ability to schedule auto delete of posts based on post status like drafts, pending posts, scheduled posts etc.', 'bulk-delete');
88
  echo ' <a href = "http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-post-status">', __('More Info', 'bulk-delete'), '</a>.';
89
  echo ' <a href = "http://sudarmuthu.com/out/buy-bulk-delete-post-status-addon">', __('Buy now', 'bulk-delete'), '</a>';
90
  echo '</li>';
91
 
 
 
 
 
 
 
 
92
  echo '</ul>';
93
  }
94
  }
@@ -148,7 +155,14 @@ class Cron_List_Table extends WP_List_Table {
148
  function column_col_cron_due($item) {
149
  //Build row actions
150
  $actions = array(
151
- 'delete' => sprintf('<a href="?page=%s&smbd_action=%s&cron_id=%s">%s</a>',$_REQUEST['page'], 'delete-cron', $item['id'], __('Delete', 'bulk-delete')),
 
 
 
 
 
 
 
152
  );
153
 
154
  //Return the title contents
38
  echo '<strong>';
39
  _e('Note: ', 'bulk-delete');
40
  echo '</strong>';
41
+ _e('Scheduling auto post or user deletion is available only when you buy pro addons.', 'bulk-delete');
42
  echo '</p>';
43
 
44
  echo '<p>';
48
  echo '<ul style="list-style:disc; padding-left:35px">';
49
 
50
  echo '<li>';
51
+ echo '<strong>', __('Schedule auto delete of Posts by Categories', 'bulk-delete'), '</strong>', ' - ';
52
  echo __('Adds the ability to schedule auto delete of posts based on categories', 'bulk-delete');
53
  echo ' <a href = "http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-categories">', __('More Info', 'bulk-delete'), '</a>.';
54
  echo ' <a href = "http://sudarmuthu.com/out/buy-bulk-delete-category-addon">', __('Buy now', 'bulk-delete'), '</a>';
55
  echo '</li>';
56
 
57
  echo '<li>';
58
+ echo '<strong>', __('Schedule auto delete of Posts by Tags', 'bulk-delete'), '</strong>', ' - ';
59
  echo __('Adds the ability to schedule auto delete of posts based on tags', 'bulk-delete');
60
  echo ' <a href = "http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-tags">', __('More Info', 'bulk-delete'), '</a>.';
61
  echo ' <a href = "http://sudarmuthu.com/out/buy-bulk-delete-tags-addon">', __('Buy now', 'bulk-delete'), '</a>';
62
  echo '</li>';
63
 
64
  echo '<li>';
65
+ echo '<strong>', __('Schedule auto delete of posts by Custom Taxonomy', 'bulk-delete'), '</strong>', ' - ';
66
  echo __('Adds the ability to schedule auto delete of posts based on custom taxonomies', 'bulk-delete');
67
  echo ' <a href = "http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-taxonomy">', __('More Info', 'bulk-delete'), '</a>.';
68
  echo ' <a href = "http://sudarmuthu.com/out/buy-bulk-delete-taxonomy-addon">', __('Buy now', 'bulk-delete'), '</a>';
69
  echo '</li>';
70
 
71
  echo '<li>';
72
+ echo '<strong>', __('Schedule auto delete of Posts by Custom Post Type', 'bulk-delete'), '</strong>', ' - ';
73
  echo __('Adds the ability to schedule auto delete of posts based on custom post types', 'bulk-delete');
74
  echo ' <a href = "http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-post-types">', __('More Info', 'bulk-delete'), '</a>.';
75
  echo ' <a href = "http://sudarmuthu.com/out/buy-bulk-delete-post-type-addon">', __('Buy now', 'bulk-delete'), '</a>';
76
  echo '</li>';
77
 
78
  echo '<li>';
79
+ echo '<strong>', __('Schedule auto delete of Pages', 'bulk-delete'), '</strong>', ' - ';
80
  echo __('Adds the ability to schedule auto delete pages', 'bulk-delete');
81
  echo ' <a href = "http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-pages">', __('More Info', 'bulk-delete'), '</a>.';
82
  echo ' <a href = "http://sudarmuthu.com/out/buy-bulk-delete-pages-addon">', __('Buy now', 'bulk-delete'), '</a>';
83
  echo '</li>';
84
 
85
  echo '<li>';
86
+ echo '<strong>', __('Schedule auto delete of Posts by Post Status', 'bulk-delete'), '</strong>', ' - ';
87
  echo __('Adds the ability to schedule auto delete of posts based on post status like drafts, pending posts, scheduled posts etc.', 'bulk-delete');
88
  echo ' <a href = "http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-post-status">', __('More Info', 'bulk-delete'), '</a>.';
89
  echo ' <a href = "http://sudarmuthu.com/out/buy-bulk-delete-post-status-addon">', __('Buy now', 'bulk-delete'), '</a>';
90
  echo '</li>';
91
 
92
+ echo '<li>';
93
+ echo '<strong>', __('Schedule auto delete of Users by User Role', 'bulk-delete'), '</strong>', ' - ';
94
+ echo __('Adds the ability to schedule auto delete of users based on user role', 'bulk-delete');
95
+ echo ' <a href = "http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-users-by-user-role">', __('More Info', 'bulk-delete'), '</a>.';
96
+ echo ' <a href = "http://sudarmuthu.com/out/buy-bulk-delete-users-by-role-addon">', __('Buy now', 'bulk-delete'), '</a>';
97
+ echo '</li>';
98
+
99
  echo '</ul>';
100
  }
101
  }
155
  function column_col_cron_due($item) {
156
  //Build row actions
157
  $actions = array(
158
+ 'delete' => sprintf( '<a href="?page=%s&smbd_action=%s&cron_id=%s&%s=%s">%s</a>',
159
+ $_REQUEST['page'],
160
+ 'delete-cron',
161
+ $item['id'],
162
+ 'sm-bulk-delete-cron-nonce',
163
+ wp_create_nonce( 'sm-bulk-delete-cron' ),
164
+ __( 'Delete', 'bulk-delete' )
165
+ ),
166
  );
167
 
168
  //Return the title contents
js/bulk-delete.js CHANGED
@@ -47,17 +47,17 @@ jQuery(document).ready(function () {
47
  // for post boxes
48
  postboxes.add_postbox_toggles(pagenow);
49
 
50
- jQuery.each(['cats', 'tags', 'taxs', 'pages', 'post_status', 'types'], function (index, value) {
51
  // invoke the date time picker
52
- jQuery('#smbd_' + value + '_cron_start').datetimepicker({
53
  timeFormat: 'HH:mm:ss'
54
  });
55
 
56
- jQuery('#smbd_' + value + '_restrict').change(function () {
57
  toggle_date_restrict(value);
58
  });
59
 
60
- jQuery('#smbd_' + value + '_limit').change(function () {
61
  toggle_limit_restrict(value);
62
  });
63
 
@@ -98,7 +98,11 @@ jQuery(document).ready(function () {
98
  }
99
 
100
  if (valid) {
101
- return confirm(BULK_DELETE.msg.deletewarning);
 
 
 
 
102
  }
103
 
104
  return false;
47
  // for post boxes
48
  postboxes.add_postbox_toggles(pagenow);
49
 
50
+ jQuery.each(['_cats', '_tags', '_taxs', '_pages', '_post_status', '_types', 'u_userrole'], function (index, value) {
51
  // invoke the date time picker
52
+ jQuery('#smbd' + value + '_cron_start').datetimepicker({
53
  timeFormat: 'HH:mm:ss'
54
  });
55
 
56
+ jQuery('#smbd' + value + '_restrict').change(function () {
57
  toggle_date_restrict(value);
58
  });
59
 
60
+ jQuery('#smbd' + value + '_limit').change(function () {
61
  toggle_limit_restrict(value);
62
  });
63
 
98
  }
99
 
100
  if (valid) {
101
+ if (current_button.lastIndexOf('bulk-delete-users-', 0) === 0) {
102
+ return confirm(BULK_DELETE.msg.deletewarningusers);
103
+ } else {
104
+ return confirm(BULK_DELETE.msg.deletewarning);
105
+ }
106
  }
107
 
108
  return false;
js/jquery-ui-timepicker.js CHANGED
@@ -1,1882 +1,2134 @@
1
- /*
2
- * jQuery timepicker addon
3
- * By: Trent Richardson [http://trentrichardson.com]
4
- * Version 1.1.1
5
- * Last Modified: 11/07/2012
6
- *
7
- * Copyright 2012 Trent Richardson
8
- * You may use this project under MIT or GPL licenses.
9
- * http://trentrichardson.com/Impromptu/GPL-LICENSE.txt
10
- * http://trentrichardson.com/Impromptu/MIT-LICENSE.txt
11
- */
12
-
13
- /*jslint evil: true, white: false, undef: false, nomen: false */
14
-
15
- (function($) {
16
-
17
- /*
18
- * Lets not redefine timepicker, Prevent "Uncaught RangeError: Maximum call stack size exceeded"
19
- */
20
- $.ui.timepicker = $.ui.timepicker || {};
21
- if ($.ui.timepicker.version) {
22
- return;
23
- }
24
-
25
- /*
26
- * Extend jQueryUI, get it started with our version number
27
- */
28
- $.extend($.ui, {
29
- timepicker: {
30
- version: "1.1.1"
31
- }
32
- });
33
-
34
- /*
35
- * Timepicker manager.
36
- * Use the singleton instance of this class, $.timepicker, to interact with the time picker.
37
- * Settings for (groups of) time pickers are maintained in an instance object,
38
- * allowing multiple different settings on the same page.
39
- */
40
- function Timepicker() {
41
- this.regional = []; // Available regional settings, indexed by language code
42
- this.regional[''] = { // Default regional settings
43
- currentText: 'Now',
44
- closeText: 'Done',
45
- amNames: ['AM', 'A'],
46
- pmNames: ['PM', 'P'],
47
- timeFormat: 'HH:mm',
48
- timeSuffix: '',
49
- timeOnlyTitle: 'Choose Time',
50
- timeText: 'Time',
51
- hourText: 'Hour',
52
- minuteText: 'Minute',
53
- secondText: 'Second',
54
- millisecText: 'Millisecond',
55
- timezoneText: 'Time Zone',
56
- isRTL: false
57
- };
58
- this._defaults = { // Global defaults for all the datetime picker instances
59
- showButtonPanel: true,
60
- timeOnly: false,
61
- showHour: true,
62
- showMinute: true,
63
- showSecond: false,
64
- showMillisec: false,
65
- showTimezone: false,
66
- showTime: true,
67
- stepHour: 1,
68
- stepMinute: 1,
69
- stepSecond: 1,
70
- stepMillisec: 1,
71
- hour: 0,
72
- minute: 0,
73
- second: 0,
74
- millisec: 0,
75
- timezone: null,
76
- useLocalTimezone: false,
77
- defaultTimezone: "+0000",
78
- hourMin: 0,
79
- minuteMin: 0,
80
- secondMin: 0,
81
- millisecMin: 0,
82
- hourMax: 23,
83
- minuteMax: 59,
84
- secondMax: 59,
85
- millisecMax: 999,
86
- minDateTime: null,
87
- maxDateTime: null,
88
- onSelect: null,
89
- hourGrid: 0,
90
- minuteGrid: 0,
91
- secondGrid: 0,
92
- millisecGrid: 0,
93
- alwaysSetTime: true,
94
- separator: ' ',
95
- altFieldTimeOnly: true,
96
- altTimeFormat: null,
97
- altSeparator: null,
98
- altTimeSuffix: null,
99
- pickerTimeFormat: null,
100
- pickerTimeSuffix: null,
101
- showTimepicker: true,
102
- timezoneIso8601: false,
103
- timezoneList: null,
104
- addSliderAccess: false,
105
- sliderAccessArgs: null,
106
- controlType: 'slider',
107
- defaultValue: null,
108
- parse: 'strict'
109
- };
110
- $.extend(this._defaults, this.regional['']);
111
- }
112
-
113
- $.extend(Timepicker.prototype, {
114
- $input: null,
115
- $altInput: null,
116
- $timeObj: null,
117
- inst: null,
118
- hour_slider: null,
119
- minute_slider: null,
120
- second_slider: null,
121
- millisec_slider: null,
122
- timezone_select: null,
123
- hour: 0,
124
- minute: 0,
125
- second: 0,
126
- millisec: 0,
127
- timezone: null,
128
- defaultTimezone: "+0000",
129
- hourMinOriginal: null,
130
- minuteMinOriginal: null,
131
- secondMinOriginal: null,
132
- millisecMinOriginal: null,
133
- hourMaxOriginal: null,
134
- minuteMaxOriginal: null,
135
- secondMaxOriginal: null,
136
- millisecMaxOriginal: null,
137
- ampm: '',
138
- formattedDate: '',
139
- formattedTime: '',
140
- formattedDateTime: '',
141
- timezoneList: null,
142
- units: ['hour','minute','second','millisec'],
143
- control: null,
144
-
145
- /*
146
- * Override the default settings for all instances of the time picker.
147
- * @param settings object - the new settings to use as defaults (anonymous object)
148
- * @return the manager object
149
- */
150
- setDefaults: function(settings) {
151
- extendRemove(this._defaults, settings || {});
152
- return this;
153
- },
154
-
155
- /*
156
- * Create a new Timepicker instance
157
- */
158
- _newInst: function($input, o) {
159
- var tp_inst = new Timepicker(),
160
- inlineSettings = {},
161
- fns = {},
162
- overrides, i;
163
-
164
- for (var attrName in this._defaults) {
165
- if(this._defaults.hasOwnProperty(attrName)){
166
- var attrValue = $input.attr('time:' + attrName);
167
- if (attrValue) {
168
- try {
169
- inlineSettings[attrName] = eval(attrValue);
170
- } catch (err) {
171
- inlineSettings[attrName] = attrValue;
172
- }
173
- }
174
- }
175
- }
176
- overrides = {
177
- beforeShow: function (input, dp_inst) {
178
- if ($.isFunction(tp_inst._defaults.evnts.beforeShow)) {
179
- return tp_inst._defaults.evnts.beforeShow.call($input[0], input, dp_inst, tp_inst);
180
- }
181
- },
182
- onChangeMonthYear: function (year, month, dp_inst) {
183
- // Update the time as well : this prevents the time from disappearing from the $input field.
184
- tp_inst._updateDateTime(dp_inst);
185
- if ($.isFunction(tp_inst._defaults.evnts.onChangeMonthYear)) {
186
- tp_inst._defaults.evnts.onChangeMonthYear.call($input[0], year, month, dp_inst, tp_inst);
187
- }
188
- },
189
- onClose: function (dateText, dp_inst) {
190
- if (tp_inst.timeDefined === true && $input.val() !== '') {
191
- tp_inst._updateDateTime(dp_inst);
192
- }
193
- if ($.isFunction(tp_inst._defaults.evnts.onClose)) {
194
- tp_inst._defaults.evnts.onClose.call($input[0], dateText, dp_inst, tp_inst);
195
- }
196
- }
197
- };
198
- for (i in overrides) {
199
- if (overrides.hasOwnProperty(i)) {
200
- fns[i] = o[i] || null;
201
- }
202
- }
203
- tp_inst._defaults = $.extend({}, this._defaults, inlineSettings, o, overrides, {
204
- evnts:fns,
205
- timepicker: tp_inst // add timepicker as a property of datepicker: $.datepicker._get(dp_inst, 'timepicker');
206
- });
207
- tp_inst.amNames = $.map(tp_inst._defaults.amNames, function(val) {
208
- return val.toUpperCase();
209
- });
210
- tp_inst.pmNames = $.map(tp_inst._defaults.pmNames, function(val) {
211
- return val.toUpperCase();
212
- });
213
-
214
- // controlType is string - key to our this._controls
215
- if(typeof(tp_inst._defaults.controlType) === 'string'){
216
- if($.fn[tp_inst._defaults.controlType] === undefined){
217
- tp_inst._defaults.controlType = 'select';
218
- }
219
- tp_inst.control = tp_inst._controls[tp_inst._defaults.controlType];
220
- }
221
- // controlType is an object and must implement create, options, value methods
222
- else{
223
- tp_inst.control = tp_inst._defaults.controlType;
224
- }
225
-
226
- if (tp_inst._defaults.timezoneList === null) {
227
- var timezoneList = ['-1200', '-1100', '-1000', '-0930', '-0900', '-0800', '-0700', '-0600', '-0500', '-0430', '-0400', '-0330', '-0300', '-0200', '-0100', '+0000',
228
- '+0100', '+0200', '+0300', '+0330', '+0400', '+0430', '+0500', '+0530', '+0545', '+0600', '+0630', '+0700', '+0800', '+0845', '+0900', '+0930',
229
- '+1000', '+1030', '+1100', '+1130', '+1200', '+1245', '+1300', '+1400'];
230
-
231
- if (tp_inst._defaults.timezoneIso8601) {
232
- timezoneList = $.map(timezoneList, function(val) {
233
- return val == '+0000' ? 'Z' : (val.substring(0, 3) + ':' + val.substring(3));
234
- });
235
- }
236
- tp_inst._defaults.timezoneList = timezoneList;
237
- }
238
-
239
- tp_inst.timezone = tp_inst._defaults.timezone;
240
- tp_inst.hour = tp_inst._defaults.hour;
241
- tp_inst.minute = tp_inst._defaults.minute;
242
- tp_inst.second = tp_inst._defaults.second;
243
- tp_inst.millisec = tp_inst._defaults.millisec;
244
- tp_inst.ampm = '';
245
- tp_inst.$input = $input;
246
-
247
- if (o.altField) {
248
- tp_inst.$altInput = $(o.altField).css({
249
- cursor: 'pointer'
250
- }).focus(function() {
251
- $input.trigger("focus");
252
- });
253
- }
254
-
255
- if (tp_inst._defaults.minDate === 0 || tp_inst._defaults.minDateTime === 0) {
256
- tp_inst._defaults.minDate = new Date();
257
- }
258
- if (tp_inst._defaults.maxDate === 0 || tp_inst._defaults.maxDateTime === 0) {
259
- tp_inst._defaults.maxDate = new Date();
260
- }
261
-
262
- // datepicker needs minDate/maxDate, timepicker needs minDateTime/maxDateTime..
263
- if (tp_inst._defaults.minDate !== undefined && tp_inst._defaults.minDate instanceof Date) {
264
- tp_inst._defaults.minDateTime = new Date(tp_inst._defaults.minDate.getTime());
265
- }
266
- if (tp_inst._defaults.minDateTime !== undefined && tp_inst._defaults.minDateTime instanceof Date) {
267
- tp_inst._defaults.minDate = new Date(tp_inst._defaults.minDateTime.getTime());
268
- }
269
- if (tp_inst._defaults.maxDate !== undefined && tp_inst._defaults.maxDate instanceof Date) {
270
- tp_inst._defaults.maxDateTime = new Date(tp_inst._defaults.maxDate.getTime());
271
- }
272
- if (tp_inst._defaults.maxDateTime !== undefined && tp_inst._defaults.maxDateTime instanceof Date) {
273
- tp_inst._defaults.maxDate = new Date(tp_inst._defaults.maxDateTime.getTime());
274
- }
275
- tp_inst.$input.bind('focus', function() {
276
- tp_inst._onFocus();
277
- });
278
-
279
- return tp_inst;
280
- },
281
-
282
- /*
283
- * add our sliders to the calendar
284
- */
285
- _addTimePicker: function(dp_inst) {
286
- var currDT = (this.$altInput && this._defaults.altFieldTimeOnly) ? this.$input.val() + ' ' + this.$altInput.val() : this.$input.val();
287
-
288
- this.timeDefined = this._parseTime(currDT);
289
- this._limitMinMaxDateTime(dp_inst, false);
290
- this._injectTimePicker();
291
- },
292
-
293
- /*
294
- * parse the time string from input value or _setTime
295
- */
296
- _parseTime: function(timeString, withDate) {
297
- if (!this.inst) {
298
- this.inst = $.datepicker._getInst(this.$input[0]);
299
- }
300
-
301
- if (withDate || !this._defaults.timeOnly) {
302
- var dp_dateFormat = $.datepicker._get(this.inst, 'dateFormat');
303
- try {
304
- var parseRes = parseDateTimeInternal(dp_dateFormat, this._defaults.timeFormat, timeString, $.datepicker._getFormatConfig(this.inst), this._defaults);
305
- if (!parseRes.timeObj) {
306
- return false;
307
- }
308
- $.extend(this, parseRes.timeObj);
309
- } catch (err) {
310
- $.datepicker.log("Error parsing the date/time string: " + err +
311
- "\ndate/time string = " + timeString +
312
- "\ntimeFormat = " + this._defaults.timeFormat +
313
- "\ndateFormat = " + dp_dateFormat);
314
- return false;
315
- }
316
- return true;
317
- } else {
318
- var timeObj = $.datepicker.parseTime(this._defaults.timeFormat, timeString, this._defaults);
319
- if (!timeObj) {
320
- return false;
321
- }
322
- $.extend(this, timeObj);
323
- return true;
324
- }
325
- },
326
-
327
- /*
328
- * generate and inject html for timepicker into ui datepicker
329
- */
330
- _injectTimePicker: function() {
331
- var $dp = this.inst.dpDiv,
332
- o = this.inst.settings,
333
- tp_inst = this,
334
- litem = '',
335
- uitem = '',
336
- max = {},
337
- gridSize = {},
338
- size = null;
339
-
340
- // Prevent displaying twice
341
- if ($dp.find("div.ui-timepicker-div").length === 0 && o.showTimepicker) {
342
- var noDisplay = ' style="display:none;"',
343
- html = '<div class="ui-timepicker-div'+ (o.isRTL? ' ui-timepicker-rtl' : '') +'"><dl>' + '<dt class="ui_tpicker_time_label"' + ((o.showTime) ? '' : noDisplay) + '>' + o.timeText + '</dt>' +
344
- '<dd class="ui_tpicker_time"' + ((o.showTime) ? '' : noDisplay) + '></dd>';
345
-
346
- // Create the markup
347
- for(var i=0,l=this.units.length; i<l; i++){
348
- litem = this.units[i];
349
- uitem = litem.substr(0,1).toUpperCase() + litem.substr(1);
350
- // Added by Peter Medeiros:
351
- // - Figure out what the hour/minute/second max should be based on the step values.
352
- // - Example: if stepMinute is 15, then minMax is 45.
353
- max[litem] = parseInt((o[litem+'Max'] - ((o[litem+'Max'] - o[litem+'Min']) % o['step'+uitem])), 10);
354
- gridSize[litem] = 0;
355
-
356
- html += '<dt class="ui_tpicker_'+ litem +'_label"' + ((o['show'+uitem]) ? '' : noDisplay) + '>' + o[litem +'Text'] + '</dt>' +
357
- '<dd class="ui_tpicker_'+ litem +'"><div class="ui_tpicker_'+ litem +'_slider"' + ((o['show'+uitem]) ? '' : noDisplay) + '></div>';
358
-
359
- if (o['show'+uitem] && o[litem+'Grid'] > 0) {
360
- html += '<div style="padding-left: 1px"><table class="ui-tpicker-grid-label"><tr>';
361
-
362
- if(litem == 'hour'){
363
- for (var h = o[litem+'Min']; h <= max[litem]; h += parseInt(o[litem+'Grid'], 10)) {
364
- gridSize[litem]++;
365
- var tmph = $.datepicker.formatTime(useAmpm(o.pickerTimeFormat || o.timeFormat)? 'hht':'HH', {hour:h}, o);
366
- html += '<td data-for="'+litem+'">' + tmph + '</td>';
367
- }
368
- }
369
- else{
370
- for (var m = o[litem+'Min']; m <= max[litem]; m += parseInt(o[litem+'Grid'], 10)) {
371
- gridSize[litem]++;
372
- html += '<td data-for="'+litem+'">' + ((m < 10) ? '0' : '') + m + '</td>';
373
- }
374
- }
375
-
376
- html += '</tr></table></div>';
377
- }
378
- html += '</dd>';
379
- }
380
-
381
- // Timezone
382
- html += '<dt class="ui_tpicker_timezone_label"' + ((o.showTimezone) ? '' : noDisplay) + '>' + o.timezoneText + '</dt>';
383
- html += '<dd class="ui_tpicker_timezone" ' + ((o.showTimezone) ? '' : noDisplay) + '></dd>';
384
-
385
- // Create the elements from string
386
- html += '</dl></div>';
387
- var $tp = $(html);
388
-
389
- // if we only want time picker...
390
- if (o.timeOnly === true) {
391
- $tp.prepend('<div class="ui-widget-header ui-helper-clearfix ui-corner-all">' + '<div class="ui-datepicker-title">' + o.timeOnlyTitle + '</div>' + '</div>');
392
- $dp.find('.ui-datepicker-header, .ui-datepicker-calendar').hide();
393
- }
394
-
395
- // add sliders, adjust grids, add events
396
- for(var i=0,l=tp_inst.units.length; i<l; i++){
397
- litem = tp_inst.units[i];
398
- uitem = litem.substr(0,1).toUpperCase() + litem.substr(1);
399
-
400
- // add the slider
401
- tp_inst[litem+'_slider'] = tp_inst.control.create(tp_inst, $tp.find('.ui_tpicker_'+litem+'_slider'), litem, tp_inst[litem], o[litem+'Min'], max[litem], o['step'+uitem]);
402
-
403
- // adjust the grid and add click event
404
- if (o['show'+uitem] && o[litem+'Grid'] > 0) {
405
- size = 100 * gridSize[litem] * o[litem+'Grid'] / (max[litem] - o[litem+'Min']);
406
- $tp.find('.ui_tpicker_'+litem+' table').css({
407
- width: size + "%",
408
- marginLeft: o.isRTL? '0' : ((size / (-2 * gridSize[litem])) + "%"),
409
- marginRight: o.isRTL? ((size / (-2 * gridSize[litem])) + "%") : '0',
410
- borderCollapse: 'collapse'
411
- }).find("td").click(function(e){
412
- var $t = $(this),
413
- h = $t.html(),
414
- n = parseInt(h.replace(/[^0-9]/g),10),
415
- ap = h.replace(/[^apm]/ig),
416
- f = $t.data('for'); // loses scope, so we use data-for
417
-
418
- if(f == 'hour'){
419
- if(ap.indexOf('p') !== -1 && n < 12){
420
- n += 12;
421
- }
422
- else{
423
- if(ap.indexOf('a') !== -1 && n === 12){
424
- n = 0;
425
- }
426
- }
427
- }
428
-
429
- tp_inst.control.value(tp_inst, tp_inst[f+'_slider'], litem, n);
430
-
431
- tp_inst._onTimeChange();
432
- tp_inst._onSelectHandler();
433
- })
434
- .css({
435
- cursor: 'pointer',
436
- width: (100 / gridSize[litem]) + '%',
437
- textAlign: 'center',
438
- overflow: 'hidden'
439
- });
440
- } // end if grid > 0
441
- } // end for loop
442
-
443
- // Add timezone options
444
- this.timezone_select = $tp.find('.ui_tpicker_timezone').append('<select></select>').find("select");
445
- $.fn.append.apply(this.timezone_select,
446
- $.map(o.timezoneList, function(val, idx) {
447
- return $("<option />").val(typeof val == "object" ? val.value : val).text(typeof val == "object" ? val.label : val);
448
- }));
449
- if (typeof(this.timezone) != "undefined" && this.timezone !== null && this.timezone !== "") {
450
- var local_date = new Date(this.inst.selectedYear, this.inst.selectedMonth, this.inst.selectedDay, 12);
451
- var local_timezone = $.timepicker.timeZoneOffsetString(local_date);
452
- if (local_timezone == this.timezone) {
453
- selectLocalTimeZone(tp_inst);
454
- } else {
455
- this.timezone_select.val(this.timezone);
456
- }
457
- } else {
458
- if (typeof(this.hour) != "undefined" && this.hour !== null && this.hour !== "") {
459
- this.timezone_select.val(o.defaultTimezone);
460
- } else {
461
- selectLocalTimeZone(tp_inst);
462
- }
463
- }
464
- this.timezone_select.change(function() {
465
- tp_inst._defaults.useLocalTimezone = false;
466
- tp_inst._onTimeChange();
467
- });
468
- // End timezone options
469
-
470
- // inject timepicker into datepicker
471
- var $buttonPanel = $dp.find('.ui-datepicker-buttonpane');
472
- if ($buttonPanel.length) {
473
- $buttonPanel.before($tp);
474
- } else {
475
- $dp.append($tp);
476
- }
477
-
478
- this.$timeObj = $tp.find('.ui_tpicker_time');
479
-
480
- if (this.inst !== null) {
481
- var timeDefined = this.timeDefined;
482
- this._onTimeChange();
483
- this.timeDefined = timeDefined;
484
- }
485
-
486
- // slideAccess integration: http://trentrichardson.com/2011/11/11/jquery-ui-sliders-and-touch-accessibility/
487
- if (this._defaults.addSliderAccess) {
488
- var sliderAccessArgs = this._defaults.sliderAccessArgs,
489
- rtl = this._defaults.isRTL;
490
- sliderAccessArgs.isRTL = rtl;
491
-
492
- setTimeout(function() { // fix for inline mode
493
- if ($tp.find('.ui-slider-access').length === 0) {
494
- $tp.find('.ui-slider:visible').sliderAccess(sliderAccessArgs);
495
-
496
- // fix any grids since sliders are shorter
497
- var sliderAccessWidth = $tp.find('.ui-slider-access:eq(0)').outerWidth(true);
498
- if (sliderAccessWidth) {
499
- $tp.find('table:visible').each(function() {
500
- var $g = $(this),
501
- oldWidth = $g.outerWidth(),
502
- oldMarginLeft = $g.css(rtl? 'marginRight':'marginLeft').toString().replace('%', ''),
503
- newWidth = oldWidth - sliderAccessWidth,
504
- newMarginLeft = ((oldMarginLeft * newWidth) / oldWidth) + '%',
505
- css = { width: newWidth, marginRight: 0, marginLeft: 0 };
506
- css[rtl? 'marginRight':'marginLeft'] = newMarginLeft;
507
- $g.css(css);
508
- });
509
- }
510
- }
511
- }, 10);
512
- }
513
- // end slideAccess integration
514
-
515
- }
516
- },
517
-
518
- /*
519
- * This function tries to limit the ability to go outside the
520
- * min/max date range
521
- */
522
- _limitMinMaxDateTime: function(dp_inst, adjustSliders) {
523
- var o = this._defaults,
524
- dp_date = new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay);
525
-
526
- if (!this._defaults.showTimepicker) {
527
- return;
528
- } // No time so nothing to check here
529
-
530
- if ($.datepicker._get(dp_inst, 'minDateTime') !== null && $.datepicker._get(dp_inst, 'minDateTime') !== undefined && dp_date) {
531
- var minDateTime = $.datepicker._get(dp_inst, 'minDateTime'),
532
- minDateTimeDate = new Date(minDateTime.getFullYear(), minDateTime.getMonth(), minDateTime.getDate(), 0, 0, 0, 0);
533
-
534
- if (this.hourMinOriginal === null || this.minuteMinOriginal === null || this.secondMinOriginal === null || this.millisecMinOriginal === null) {
535
- this.hourMinOriginal = o.hourMin;
536
- this.minuteMinOriginal = o.minuteMin;
537
- this.secondMinOriginal = o.secondMin;
538
- this.millisecMinOriginal = o.millisecMin;
539
- }
540
-
541
- if (dp_inst.settings.timeOnly || minDateTimeDate.getTime() == dp_date.getTime()) {
542
- this._defaults.hourMin = minDateTime.getHours();
543
- if (this.hour <= this._defaults.hourMin) {
544
- this.hour = this._defaults.hourMin;
545
- this._defaults.minuteMin = minDateTime.getMinutes();
546
- if (this.minute <= this._defaults.minuteMin) {
547
- this.minute = this._defaults.minuteMin;
548
- this._defaults.secondMin = minDateTime.getSeconds();
549
- if (this.second <= this._defaults.secondMin) {
550
- this.second = this._defaults.secondMin;
551
- this._defaults.millisecMin = minDateTime.getMilliseconds();
552
- } else {
553
- if (this.millisec < this._defaults.millisecMin) {
554
- this.millisec = this._defaults.millisecMin;
555
- }
556
- this._defaults.millisecMin = this.millisecMinOriginal;
557
- }
558
- } else {
559
- this._defaults.secondMin = this.secondMinOriginal;
560
- this._defaults.millisecMin = this.millisecMinOriginal;
561
- }
562
- } else {
563
- this._defaults.minuteMin = this.minuteMinOriginal;
564
- this._defaults.secondMin = this.secondMinOriginal;
565
- this._defaults.millisecMin = this.millisecMinOriginal;
566
- }
567
- } else {
568
- this._defaults.hourMin = this.hourMinOriginal;
569
- this._defaults.minuteMin = this.minuteMinOriginal;
570
- this._defaults.secondMin = this.secondMinOriginal;
571
- this._defaults.millisecMin = this.millisecMinOriginal;
572
- }
573
- }
574
-
575
- if ($.datepicker._get(dp_inst, 'maxDateTime') !== null && $.datepicker._get(dp_inst, 'maxDateTime') !== undefined && dp_date) {
576
- var maxDateTime = $.datepicker._get(dp_inst, 'maxDateTime'),
577
- maxDateTimeDate = new Date(maxDateTime.getFullYear(), maxDateTime.getMonth(), maxDateTime.getDate(), 0, 0, 0, 0);
578
-
579
- if (this.hourMaxOriginal === null || this.minuteMaxOriginal === null || this.secondMaxOriginal === null) {
580
- this.hourMaxOriginal = o.hourMax;
581
- this.minuteMaxOriginal = o.minuteMax;
582
- this.secondMaxOriginal = o.secondMax;
583
- this.millisecMaxOriginal = o.millisecMax;
584
- }
585
-
586
- if (dp_inst.settings.timeOnly || maxDateTimeDate.getTime() == dp_date.getTime()) {
587
- this._defaults.hourMax = maxDateTime.getHours();
588
- if (this.hour >= this._defaults.hourMax) {
589
- this.hour = this._defaults.hourMax;
590
- this._defaults.minuteMax = maxDateTime.getMinutes();
591
- if (this.minute >= this._defaults.minuteMax) {
592
- this.minute = this._defaults.minuteMax;
593
- this._defaults.secondMax = maxDateTime.getSeconds();
594
- } else if (this.second >= this._defaults.secondMax) {
595
- this.second = this._defaults.secondMax;
596
- this._defaults.millisecMax = maxDateTime.getMilliseconds();
597
- } else {
598
- if (this.millisec > this._defaults.millisecMax) {
599
- this.millisec = this._defaults.millisecMax;
600
- }
601
- this._defaults.millisecMax = this.millisecMaxOriginal;
602
- }
603
- } else {
604
- this._defaults.minuteMax = this.minuteMaxOriginal;
605
- this._defaults.secondMax = this.secondMaxOriginal;
606
- this._defaults.millisecMax = this.millisecMaxOriginal;
607
- }
608
- } else {
609
- this._defaults.hourMax = this.hourMaxOriginal;
610
- this._defaults.minuteMax = this.minuteMaxOriginal;
611
- this._defaults.secondMax = this.secondMaxOriginal;
612
- this._defaults.millisecMax = this.millisecMaxOriginal;
613
- }
614
- }
615
-
616
- if (adjustSliders !== undefined && adjustSliders === true) {
617
- var hourMax = parseInt((this._defaults.hourMax - ((this._defaults.hourMax - this._defaults.hourMin) % this._defaults.stepHour)), 10),
618
- minMax = parseInt((this._defaults.minuteMax - ((this._defaults.minuteMax - this._defaults.minuteMin) % this._defaults.stepMinute)), 10),
619
- secMax = parseInt((this._defaults.secondMax - ((this._defaults.secondMax - this._defaults.secondMin) % this._defaults.stepSecond)), 10),
620
- millisecMax = parseInt((this._defaults.millisecMax - ((this._defaults.millisecMax - this._defaults.millisecMin) % this._defaults.stepMillisec)), 10);
621
-
622
- if (this.hour_slider) {
623
- this.control.options(this, this.hour_slider, 'hour', { min: this._defaults.hourMin, max: hourMax });
624
- this.control.value(this, this.hour_slider, 'hour', this.hour);
625
- }
626
- if (this.minute_slider) {
627
- this.control.options(this, this.minute_slider, 'minute', { min: this._defaults.minuteMin, max: minMax });
628
- this.control.value(this, this.minute_slider, 'minute', this.minute);
629
- }
630
- if (this.second_slider) {
631
- this.control.options(this, this.second_slider, 'second', { min: this._defaults.secondMin, max: secMax });
632
- this.control.value(this, this.second_slider, 'second', this.second);
633
- }
634
- if (this.millisec_slider) {
635
- this.control.options(this, this.millisec_slider, 'millisec', { min: this._defaults.millisecMin, max: millisecMax });
636
- this.control.value(this, this.millisec_slider, 'millisec', this.millisec);
637
- }
638
- }
639
-
640
- },
641
-
642
- /*
643
- * when a slider moves, set the internal time...
644
- * on time change is also called when the time is updated in the text field
645
- */
646
- _onTimeChange: function() {
647
- var hour = (this.hour_slider) ? this.control.value(this, this.hour_slider, 'hour') : false,
648
- minute = (this.minute_slider) ? this.control.value(this, this.minute_slider, 'minute') : false,
649
- second = (this.second_slider) ? this.control.value(this, this.second_slider, 'second') : false,
650
- millisec = (this.millisec_slider) ? this.control.value(this, this.millisec_slider, 'millisec') : false,
651
- timezone = (this.timezone_select) ? this.timezone_select.val() : false,
652
- o = this._defaults,
653
- pickerTimeFormat = o.pickerTimeFormat || o.timeFormat,
654
- pickerTimeSuffix = o.pickerTimeSuffix || o.timeSuffix;
655
-
656
- if (typeof(hour) == 'object') {
657
- hour = false;
658
- }
659
- if (typeof(minute) == 'object') {
660
- minute = false;
661
- }
662
- if (typeof(second) == 'object') {
663
- second = false;
664
- }
665
- if (typeof(millisec) == 'object') {
666
- millisec = false;
667
- }
668
- if (typeof(timezone) == 'object') {
669
- timezone = false;
670
- }
671
-
672
- if (hour !== false) {
673
- hour = parseInt(hour, 10);
674
- }
675
- if (minute !== false) {
676
- minute = parseInt(minute, 10);
677
- }
678
- if (second !== false) {
679
- second = parseInt(second, 10);
680
- }
681
- if (millisec !== false) {
682
- millisec = parseInt(millisec, 10);
683
- }
684
-
685
- var ampm = o[hour < 12 ? 'amNames' : 'pmNames'][0];
686
-
687
- // If the update was done in the input field, the input field should not be updated.
688
- // If the update was done using the sliders, update the input field.
689
- var hasChanged = (hour != this.hour || minute != this.minute || second != this.second || millisec != this.millisec
690
- || (this.ampm.length > 0 && (hour < 12) != ($.inArray(this.ampm.toUpperCase(), this.amNames) !== -1))
691
- || ((this.timezone === null && timezone != this.defaultTimezone) || (this.timezone !== null && timezone != this.timezone)));
692
-
693
- if (hasChanged) {
694
-
695
- if (hour !== false) {
696
- this.hour = hour;
697
- }
698
- if (minute !== false) {
699
- this.minute = minute;
700
- }
701
- if (second !== false) {
702
- this.second = second;
703
- }
704
- if (millisec !== false) {
705
- this.millisec = millisec;
706
- }
707
- if (timezone !== false) {
708
- this.timezone = timezone;
709
- }
710
-
711
- if (!this.inst) {
712
- this.inst = $.datepicker._getInst(this.$input[0]);
713
- }
714
-
715
- this._limitMinMaxDateTime(this.inst, true);
716
- }
717
- if (useAmpm(o.timeFormat)) {
718
- this.ampm = ampm;
719
- }
720
-
721
- // Updates the time within the timepicker
722
- this.formattedTime = $.datepicker.formatTime(o.timeFormat, this, o);
723
- if (this.$timeObj) {
724
- if(pickerTimeFormat === o.timeFormat){
725
- this.$timeObj.text(this.formattedTime + pickerTimeSuffix);
726
- }
727
- else{
728
- this.$timeObj.text($.datepicker.formatTime(pickerTimeFormat, this, o) + pickerTimeSuffix);
729
- }
730
- }
731
-
732
- this.timeDefined = true;
733
- if (hasChanged) {
734
- this._updateDateTime();
735
- }
736
- },
737
-
738
- /*
739
- * call custom onSelect.
740
- * bind to sliders slidestop, and grid click.
741
- */
742
- _onSelectHandler: function() {
743
- var onSelect = this._defaults.onSelect || this.inst.settings.onSelect;
744
- var inputEl = this.$input ? this.$input[0] : null;
745
- if (onSelect && inputEl) {
746
- onSelect.apply(inputEl, [this.formattedDateTime, this]);
747
- }
748
- },
749
-
750
- /*
751
- * update our input with the new date time..
752
- */
753
- _updateDateTime: function(dp_inst) {
754
- dp_inst = this.inst || dp_inst;
755
- var dt = $.datepicker._daylightSavingAdjust(new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay)),
756
- dateFmt = $.datepicker._get(dp_inst, 'dateFormat'),
757
- formatCfg = $.datepicker._getFormatConfig(dp_inst),
758
- timeAvailable = dt !== null && this.timeDefined;
759
- this.formattedDate = $.datepicker.formatDate(dateFmt, (dt === null ? new Date() : dt), formatCfg);
760
- var formattedDateTime = this.formattedDate;
761
-
762
- /*
763
- * remove following lines to force every changes in date picker to change the input value
764
- * Bug descriptions: when an input field has a default value, and click on the field to pop up the date picker.
765
- * If the user manually empty the value in the input field, the date picker will never change selected value.
766
- */
767
- //if (dp_inst.lastVal !== undefined && (dp_inst.lastVal.length > 0 && this.$input.val().length === 0)) {
768
- // return;
769
- //}
770
-
771
- if (this._defaults.timeOnly === true) {
772
- formattedDateTime = this.formattedTime;
773
- } else if (this._defaults.timeOnly !== true && (this._defaults.alwaysSetTime || timeAvailable)) {
774
- formattedDateTime += this._defaults.separator + this.formattedTime + this._defaults.timeSuffix;
775
- }
776
-
777
- this.formattedDateTime = formattedDateTime;
778
-
779
- if (!this._defaults.showTimepicker) {
780
- this.$input.val(this.formattedDate);
781
- } else if (this.$altInput && this._defaults.altFieldTimeOnly === true) {
782
- this.$altInput.val(this.formattedTime);
783
- this.$input.val(this.formattedDate);
784
- } else if (this.$altInput) {
785
- this.$input.val(formattedDateTime);
786
- var altFormattedDateTime = '',
787
- altSeparator = this._defaults.altSeparator ? this._defaults.altSeparator : this._defaults.separator,
788
- altTimeSuffix = this._defaults.altTimeSuffix ? this._defaults.altTimeSuffix : this._defaults.timeSuffix;
789
-
790
- if (this._defaults.altFormat) altFormattedDateTime = $.datepicker.formatDate(this._defaults.altFormat, (dt === null ? new Date() : dt), formatCfg);
791
- else altFormattedDateTime = this.formattedDate;
792
- if (altFormattedDateTime) altFormattedDateTime += altSeparator;
793
- if (this._defaults.altTimeFormat) altFormattedDateTime += $.datepicker.formatTime(this._defaults.altTimeFormat, this, this._defaults) + altTimeSuffix;
794
- else altFormattedDateTime += this.formattedTime + altTimeSuffix;
795
- this.$altInput.val(altFormattedDateTime);
796
- } else {
797
- this.$input.val(formattedDateTime);
798
- }
799
-
800
- this.$input.trigger("change");
801
- },
802
-
803
- _onFocus: function() {
804
- if (!this.$input.val() && this._defaults.defaultValue) {
805
- this.$input.val(this._defaults.defaultValue);
806
- var inst = $.datepicker._getInst(this.$input.get(0)),
807
- tp_inst = $.datepicker._get(inst, 'timepicker');
808
- if (tp_inst) {
809
- if (tp_inst._defaults.timeOnly && (inst.input.val() != inst.lastVal)) {
810
- try {
811
- $.datepicker._updateDatepicker(inst);
812
- } catch (err) {
813
- $.datepicker.log(err);
814
- }
815
- }
816
- }
817
- }
818
- },
819
-
820
- /*
821
- * Small abstraction to control types
822
- * We can add more, just be sure to follow the pattern: create, options, value
823
- */
824
- _controls: {
825
- // slider methods
826
- slider: {
827
- create: function(tp_inst, obj, unit, val, min, max, step){
828
- var rtl = tp_inst._defaults.isRTL; // if rtl go -60->0 instead of 0->60
829
- return obj.prop('slide', null).slider({
830
- orientation: "horizontal",
831
- value: rtl? val*-1 : val,
832
- min: rtl? max*-1 : min,
833
- max: rtl? min*-1 : max,
834
- step: step,
835
- slide: function(event, ui) {
836
- tp_inst.control.value(tp_inst, $(this), unit, rtl? ui.value*-1:ui.value);
837
- tp_inst._onTimeChange();
838
- },
839
- stop: function(event, ui) {
840
- tp_inst._onSelectHandler();
841
- }
842
- });
843
- },
844
- options: function(tp_inst, obj, unit, opts, val){
845
- if(tp_inst._defaults.isRTL){
846
- if(typeof(opts) == 'string'){
847
- if(opts == 'min' || opts == 'max'){
848
- if(val !== undefined)
849
- return obj.slider(opts, val*-1);
850
- return Math.abs(obj.slider(opts));
851
- }
852
- return obj.slider(opts);
853
- }
854
- var min = opts.min,
855
- max = opts.max;
856
- opts.min = opts.max = null;
857
- if(min !== undefined)
858
- opts.max = min * -1;
859
- if(max !== undefined)
860
- opts.min = max * -1;
861
- return obj.slider(opts);
862
- }
863
- if(typeof(opts) == 'string' && val !== undefined)
864
- return obj.slider(opts, val);
865
- return obj.slider(opts);
866
- },
867
- value: function(tp_inst, obj, unit, val){
868
- if(tp_inst._defaults.isRTL){
869
- if(val !== undefined)
870
- return obj.slider('value', val*-1);
871
- return Math.abs(obj.slider('value'));
872
- }
873
- if(val !== undefined)
874
- return obj.slider('value', val);
875
- return obj.slider('value');
876
- }
877
- },
878
- // select methods
879
- select: {
880
- create: function(tp_inst, obj, unit, val, min, max, step){
881
- var sel = '<select class="ui-timepicker-select" data-unit="'+ unit +'" data-min="'+ min +'" data-max="'+ max +'" data-step="'+ step +'">',
882
- ul = tp_inst._defaults.timeFormat.indexOf('t') !== -1? 'toLowerCase':'toUpperCase',
883
- m = 0;
884
-
885
- for(var i=min; i<=max; i+=step){
886
- sel += '<option value="'+ i +'"'+ (i==val? ' selected':'') +'>';
887
- if(unit == 'hour' && useAmpm(tp_inst._defaults.pickerTimeFormat || tp_inst._defaults.timeFormat))
888
- sel += $.datepicker.formatTime("hh TT", {hour:i}, tp_inst._defaults);
889
- else if(unit == 'millisec' || i >= 10) sel += i;
890
- else sel += '0'+ i.toString();
891
- sel += '</option>';
892
- }
893
- sel += '</select>';
894
-
895
- obj.children('select').remove();
896
-
897
- $(sel).appendTo(obj).change(function(e){
898
- tp_inst._onTimeChange();
899
- tp_inst._onSelectHandler();
900
- });
901
-
902
- return obj;
903
- },
904
- options: function(tp_inst, obj, unit, opts, val){
905
- var o = {},
906
- $t = obj.children('select');
907
- if(typeof(opts) == 'string'){
908
- if(val === undefined)
909
- return $t.data(opts);
910
- o[opts] = val;
911
- }
912
- else o = opts;
913
- return tp_inst.control.create(tp_inst, obj, $t.data('unit'), $t.val(), o.min || $t.data('min'), o.max || $t.data('max'), o.step || $t.data('step'));
914
- },
915
- value: function(tp_inst, obj, unit, val){
916
- var $t = obj.children('select');
917
- if(val !== undefined)
918
- return $t.val(val);
919
- return $t.val();
920
- }
921
- }
922
- } // end _controls
923
-
924
- });
925
-
926
- $.fn.extend({
927
- /*
928
- * shorthand just to use timepicker..
929
- */
930
- timepicker: function(o) {
931
- o = o || {};
932
- var tmp_args = Array.prototype.slice.call(arguments);
933
-
934
- if (typeof o == 'object') {
935
- tmp_args[0] = $.extend(o, {
936
- timeOnly: true
937
- });
938
- }
939
-
940
- return $(this).each(function() {
941
- $.fn.datetimepicker.apply($(this), tmp_args);
942
- });
943
- },
944
-
945
- /*
946
- * extend timepicker to datepicker
947
- */
948
- datetimepicker: function(o) {
949
- o = o || {};
950
- var tmp_args = arguments;
951
-
952
- if (typeof(o) == 'string') {
953
- if (o == 'getDate') {
954
- return $.fn.datepicker.apply($(this[0]), tmp_args);
955
- } else {
956
- return this.each(function() {
957
- var $t = $(this);
958
- $t.datepicker.apply($t, tmp_args);
959
- });
960
- }
961
- } else {
962
- return this.each(function() {
963
- var $t = $(this);
964
- $t.datepicker($.timepicker._newInst($t, o)._defaults);
965
- });
966
- }
967
- }
968
- });
969
-
970
- /*
971
- * Public Utility to parse date and time
972
- */
973
- $.datepicker.parseDateTime = function(dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings) {
974
- var parseRes = parseDateTimeInternal(dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings);
975
- if (parseRes.timeObj) {
976
- var t = parseRes.timeObj;
977
- parseRes.date.setHours(t.hour, t.minute, t.second, t.millisec);
978
- }
979
-
980
- return parseRes.date;
981
- };
982
-
983
- /*
984
- * Public utility to parse time
985
- */
986
- $.datepicker.parseTime = function(timeFormat, timeString, options) {
987
- var o = extendRemove(extendRemove({}, $.timepicker._defaults), options || {});
988
-
989
- // Strict parse requires the timeString to match the timeFormat exactly
990
- var strictParse = function(f, s, o){
991
-
992
- // pattern for standard and localized AM/PM markers
993
- var getPatternAmpm = function(amNames, pmNames) {
994
- var markers = [];
995
- if (amNames) {
996
- $.merge(markers, amNames);
997
- }
998
- if (pmNames) {
999
- $.merge(markers, pmNames);
1000
- }
1001
- markers = $.map(markers, function(val) {
1002
- return val.replace(/[.*+?|()\[\]{}\\]/g, '\\$&');
1003
- });
1004
- return '(' + markers.join('|') + ')?';
1005
- };
1006
-
1007
- // figure out position of time elements.. cause js cant do named captures
1008
- var getFormatPositions = function(timeFormat) {
1009
- var finds = timeFormat.toLowerCase().match(/(h{1,2}|m{1,2}|s{1,2}|l{1}|t{1,2}|z|'.*?')/g),
1010
- orders = {
1011
- h: -1,
1012
- m: -1,
1013
- s: -1,
1014
- l: -1,
1015
- t: -1,
1016
- z: -1
1017
- };
1018
-
1019
- if (finds) {
1020
- for (var i = 0; i < finds.length; i++) {
1021
- if (orders[finds[i].toString().charAt(0)] == -1) {
1022
- orders[finds[i].toString().charAt(0)] = i + 1;
1023
- }
1024
- }
1025
- }
1026
- return orders;
1027
- };
1028
-
1029
- var regstr = '^' + f.toString()
1030
- .replace(/([hH]{1,2}|mm?|ss?|[tT]{1,2}|[lz]|'.*?')/g, function (match) {
1031
- switch (match.charAt(0).toLowerCase()) {
1032
- case 'h': return '(\\d?\\d)';
1033
- case 'm': return '(\\d?\\d)';
1034
- case 's': return '(\\d?\\d)';
1035
- case 'l': return '(\\d?\\d?\\d)';
1036
- case 'z': return '(z|[-+]\\d\\d:?\\d\\d|\\S+)?';
1037
- case 't': return getPatternAmpm(o.amNames, o.pmNames);
1038
- default: // literal escaped in quotes
1039
- return '(' + match.replace(/\'/g, "").replace(/(\.|\$|\^|\\|\/|\(|\)|\[|\]|\?|\+|\*)/g, function (m) { return "\\" + m; }) + ')?';
1040
- }
1041
- })
1042
- .replace(/\s/g, '\\s?') +
1043
- o.timeSuffix + '$',
1044
- order = getFormatPositions(f),
1045
- ampm = '',
1046
- treg;
1047
-
1048
- treg = s.match(new RegExp(regstr, 'i'));
1049
-
1050
- var resTime = {
1051
- hour: 0,
1052
- minute: 0,
1053
- second: 0,
1054
- millisec: 0
1055
- };
1056
-
1057
- if (treg) {
1058
- if (order.t !== -1) {
1059
- if (treg[order.t] === undefined || treg[order.t].length === 0) {
1060
- ampm = '';
1061
- resTime.ampm = '';
1062
- } else {
1063
- ampm = $.inArray(treg[order.t].toUpperCase(), o.amNames) !== -1 ? 'AM' : 'PM';
1064
- resTime.ampm = o[ampm == 'AM' ? 'amNames' : 'pmNames'][0];
1065
- }
1066
- }
1067
-
1068
- if (order.h !== -1) {
1069
- if (ampm == 'AM' && treg[order.h] == '12') {
1070
- resTime.hour = 0; // 12am = 0 hour
1071
- } else {
1072
- if (ampm == 'PM' && treg[order.h] != '12') {
1073
- resTime.hour = parseInt(treg[order.h], 10) + 12; // 12pm = 12 hour, any other pm = hour + 12
1074
- } else {
1075
- resTime.hour = Number(treg[order.h]);
1076
- }
1077
- }
1078
- }
1079
-
1080
- if (order.m !== -1) {
1081
- resTime.minute = Number(treg[order.m]);
1082
- }
1083
- if (order.s !== -1) {
1084
- resTime.second = Number(treg[order.s]);
1085
- }
1086
- if (order.l !== -1) {
1087
- resTime.millisec = Number(treg[order.l]);
1088
- }
1089
- if (order.z !== -1 && treg[order.z] !== undefined) {
1090
- var tz = treg[order.z].toUpperCase();
1091
- switch (tz.length) {
1092
- case 1:
1093
- // Z
1094
- tz = o.timezoneIso8601 ? 'Z' : '+0000';
1095
- break;
1096
- case 5:
1097
- // +hhmm
1098
- if (o.timezoneIso8601) {
1099
- tz = tz.substring(1) == '0000' ? 'Z' : tz.substring(0, 3) + ':' + tz.substring(3);
1100
- }
1101
- break;
1102
- case 6:
1103
- // +hh:mm
1104
- if (!o.timezoneIso8601) {
1105
- tz = tz == 'Z' || tz.substring(1) == '00:00' ? '+0000' : tz.replace(/:/, '');
1106
- } else {
1107
- if (tz.substring(1) == '00:00') {
1108
- tz = 'Z';
1109
- }
1110
- }
1111
- break;
1112
- }
1113
- resTime.timezone = tz;
1114
- }
1115
-
1116
-
1117
- return resTime;
1118
- }
1119
- return false;
1120
- };// end strictParse
1121
-
1122
- // First try JS Date, if that fails, use strictParse
1123
- var looseParse = function(f,s,o){
1124
- try{
1125
- var d = new Date('2012-01-01 '+ s);
1126
- return {
1127
- hour: d.getHours(),
1128
- minutes: d.getMinutes(),
1129
- seconds: d.getSeconds(),
1130
- millisec: d.getMilliseconds(),
1131
- timezone: $.timepicker.timeZoneOffsetString(d)
1132
- };
1133
- }
1134
- catch(err){
1135
- try{
1136
- return strictParse(f,s,o);
1137
- }
1138
- catch(err2){
1139
- $.datepicker.log("Unable to parse \ntimeString: "+ s +"\ntimeFormat: "+ f);
1140
- }
1141
- }
1142
- return false;
1143
- }; // end looseParse
1144
-
1145
- if(typeof o.parse === "function"){
1146
- return o.parse(timeFormat, timeString, o)
1147
- }
1148
- if(o.parse === 'loose'){
1149
- return looseParse(timeFormat, timeString, o);
1150
- }
1151
- return strictParse(timeFormat, timeString, o);
1152
- };
1153
-
1154
- /*
1155
- * Public utility to format the time
1156
- * format = string format of the time
1157
- * time = a {}, not a Date() for timezones
1158
- * options = essentially the regional[].. amNames, pmNames, ampm
1159
- */
1160
- $.datepicker.formatTime = function(format, time, options) {
1161
- options = options || {};
1162
- options = $.extend({}, $.timepicker._defaults, options);
1163
- time = $.extend({
1164
- hour: 0,
1165
- minute: 0,
1166
- second: 0,
1167
- millisec: 0,
1168
- timezone: '+0000'
1169
- }, time);
1170
-
1171
- var tmptime = format,
1172
- ampmName = options.amNames[0],
1173
- hour = parseInt(time.hour, 10);
1174
-
1175
- if (hour > 11) {
1176
- ampmName = options.pmNames[0];
1177
- }
1178
-
1179
- tmptime = tmptime.replace(/(?:HH?|hh?|mm?|ss?|[tT]{1,2}|[lz]|('.*?'|".*?"))/g, function(match) {
1180
- switch (match) {
1181
- case 'HH':
1182
- return ('0' + hour).slice(-2);
1183
- case 'H':
1184
- return hour;
1185
- case 'hh':
1186
- return ('0' + convert24to12(hour)).slice(-2);
1187
- case 'h':
1188
- return convert24to12(hour);
1189
- case 'mm':
1190
- return ('0' + time.minute).slice(-2);
1191
- case 'm':
1192
- return time.minute;
1193
- case 'ss':
1194
- return ('0' + time.second).slice(-2);
1195
- case 's':
1196
- return time.second;
1197
- case 'l':
1198
- return ('00' + time.millisec).slice(-3);
1199
- case 'z':
1200
- return time.timezone === null? options.defaultTimezone : time.timezone;
1201
- case 'T':
1202
- return ampmName.charAt(0).toUpperCase();
1203
- case 'TT':
1204
- return ampmName.toUpperCase();
1205
- case 't':
1206
- return ampmName.charAt(0).toLowerCase();
1207
- case 'tt':
1208
- return ampmName.toLowerCase();
1209
- default:
1210
- return match.replace(/\'/g, "") || "'";
1211
- }
1212
- });
1213
-
1214
- tmptime = $.trim(tmptime);
1215
- return tmptime;
1216
- };
1217
-
1218
- /*
1219
- * the bad hack :/ override datepicker so it doesnt close on select
1220
- // inspired: http://stackoverflow.com/questions/1252512/jquery-datepicker-prevent-closing-picker-when-clicking-a-date/1762378#1762378
1221
- */
1222
- $.datepicker._base_selectDate = $.datepicker._selectDate;
1223
- $.datepicker._selectDate = function(id, dateStr) {
1224
- var inst = this._getInst($(id)[0]),
1225
- tp_inst = this._get(inst, 'timepicker');
1226
-
1227
- if (tp_inst) {
1228
- tp_inst._limitMinMaxDateTime(inst, true);
1229
- inst.inline = inst.stay_open = true;
1230
- //This way the onSelect handler called from calendarpicker get the full dateTime
1231
- this._base_selectDate(id, dateStr);
1232
- inst.inline = inst.stay_open = false;
1233
- this._notifyChange(inst);
1234
- this._updateDatepicker(inst);
1235
- } else {
1236
- this._base_selectDate(id, dateStr);
1237
- }
1238
- };
1239
-
1240
- /*
1241
- * second bad hack :/ override datepicker so it triggers an event when changing the input field
1242
- * and does not redraw the datepicker on every selectDate event
1243
- */
1244
- $.datepicker._base_updateDatepicker = $.datepicker._updateDatepicker;
1245
- $.datepicker._updateDatepicker = function(inst) {
1246
-
1247
- // don't popup the datepicker if there is another instance already opened
1248
- var input = inst.input[0];
1249
- if ($.datepicker._curInst && $.datepicker._curInst != inst && $.datepicker._datepickerShowing && $.datepicker._lastInput != input) {
1250
- return;
1251
- }
1252
-
1253
- if (typeof(inst.stay_open) !== 'boolean' || inst.stay_open === false) {
1254
-
1255
- this._base_updateDatepicker(inst);
1256
-
1257
- // Reload the time control when changing something in the input text field.
1258
- var tp_inst = this._get(inst, 'timepicker');
1259
- if (tp_inst) {
1260
- tp_inst._addTimePicker(inst);
1261
-
1262
- if (tp_inst._defaults.useLocalTimezone) { //checks daylight saving with the new date.
1263
- var date = new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay, 12);
1264
- selectLocalTimeZone(tp_inst, date);
1265
- tp_inst._onTimeChange();
1266
- }
1267
- }
1268
- }
1269
- };
1270
-
1271
- /*
1272
- * third bad hack :/ override datepicker so it allows spaces and colon in the input field
1273
- */
1274
- $.datepicker._base_doKeyPress = $.datepicker._doKeyPress;
1275
- $.datepicker._doKeyPress = function(event) {
1276
- var inst = $.datepicker._getInst(event.target),
1277
- tp_inst = $.datepicker._get(inst, 'timepicker');
1278
-
1279
- if (tp_inst) {
1280
- if ($.datepicker._get(inst, 'constrainInput')) {
1281
- var ampm = useAmpm(tp_inst._defaults.timeFormat),
1282
- dateChars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat')),
1283
- datetimeChars = tp_inst._defaults.timeFormat.toString()
1284
- .replace(/[hms]/g, '')
1285
- .replace(/TT/g, ampm ? 'APM' : '')
1286
- .replace(/Tt/g, ampm ? 'AaPpMm' : '')
1287
- .replace(/tT/g, ampm ? 'AaPpMm' : '')
1288
- .replace(/T/g, ampm ? 'AP' : '')
1289
- .replace(/tt/g, ampm ? 'apm' : '')
1290
- .replace(/t/g, ampm ? 'ap' : '') +
1291
- " " + tp_inst._defaults.separator +
1292
- tp_inst._defaults.timeSuffix +
1293
- (tp_inst._defaults.showTimezone ? tp_inst._defaults.timezoneList.join('') : '') +
1294
- (tp_inst._defaults.amNames.join('')) + (tp_inst._defaults.pmNames.join('')) +
1295
- dateChars,
1296
- chr = String.fromCharCode(event.charCode === undefined ? event.keyCode : event.charCode);
1297
- return event.ctrlKey || (chr < ' ' || !dateChars || datetimeChars.indexOf(chr) > -1);
1298
- }
1299
- }
1300
-
1301
- return $.datepicker._base_doKeyPress(event);
1302
- };
1303
-
1304
- /*
1305
- * Fourth bad hack :/ override _updateAlternate function used in inline mode to init altField
1306
- */
1307
- $.datepicker._base_updateAlternate = $.datepicker._updateAlternate;
1308
- /* Update any alternate field to synchronise with the main field. */
1309
- $.datepicker._updateAlternate = function(inst) {
1310
- var tp_inst = this._get(inst, 'timepicker');
1311
- if(tp_inst){
1312
- var altField = tp_inst._defaults.altField;
1313
- if (altField) { // update alternate field too
1314
- var altFormat = tp_inst._defaults.altFormat || tp_inst._defaults.dateFormat,
1315
- date = this._getDate(inst),
1316
- formatCfg = $.datepicker._getFormatConfig(inst),
1317
- altFormattedDateTime = '',
1318
- altSeparator = tp_inst._defaults.altSeparator ? tp_inst._defaults.altSeparator : tp_inst._defaults.separator,
1319
- altTimeSuffix = tp_inst._defaults.altTimeSuffix ? tp_inst._defaults.altTimeSuffix : tp_inst._defaults.timeSuffix,
1320
- altTimeFormat = tp_inst._defaults.altTimeFormat !== null ? tp_inst._defaults.altTimeFormat : tp_inst._defaults.timeFormat;
1321
-
1322
- altFormattedDateTime += $.datepicker.formatTime(altTimeFormat, tp_inst, tp_inst._defaults) + altTimeSuffix;
1323
- if(!tp_inst._defaults.timeOnly && !tp_inst._defaults.altFieldTimeOnly){
1324
- if(tp_inst._defaults.altFormat)
1325
- altFormattedDateTime = $.datepicker.formatDate(tp_inst._defaults.altFormat, (date === null ? new Date() : date), formatCfg) + altSeparator + altFormattedDateTime;
1326
- else altFormattedDateTime = tp_inst.formattedDate + altSeparator + altFormattedDateTime;
1327
- }
1328
- $(altField).val(altFormattedDateTime);
1329
- }
1330
- }
1331
- else{
1332
- $.datepicker._base_updateAlternate(inst);
1333
- }
1334
- };
1335
-
1336
- /*
1337
- * Override key up event to sync manual input changes.
1338
- */
1339
- $.datepicker._base_doKeyUp = $.datepicker._doKeyUp;
1340
- $.datepicker._doKeyUp = function(event) {
1341
- var inst = $.datepicker._getInst(event.target),
1342
- tp_inst = $.datepicker._get(inst, 'timepicker');
1343
-
1344
- if (tp_inst) {
1345
- if (tp_inst._defaults.timeOnly && (inst.input.val() != inst.lastVal)) {
1346
- try {
1347
- $.datepicker._updateDatepicker(inst);
1348
- } catch (err) {
1349
- $.datepicker.log(err);
1350
- }
1351
- }
1352
- }
1353
-
1354
- return $.datepicker._base_doKeyUp(event);
1355
- };
1356
-
1357
- /*
1358
- * override "Today" button to also grab the time.
1359
- */
1360
- $.datepicker._base_gotoToday = $.datepicker._gotoToday;
1361
- $.datepicker._gotoToday = function(id) {
1362
- var inst = this._getInst($(id)[0]),
1363
- $dp = inst.dpDiv;
1364
- this._base_gotoToday(id);
1365
- var tp_inst = this._get(inst, 'timepicker');
1366
- selectLocalTimeZone(tp_inst);
1367
- var now = new Date();
1368
- this._setTime(inst, now);
1369
- $('.ui-datepicker-today', $dp).click();
1370
- };
1371
-
1372
- /*
1373
- * Disable & enable the Time in the datetimepicker
1374
- */
1375
- $.datepicker._disableTimepickerDatepicker = function(target) {
1376
- var inst = this._getInst(target);
1377
- if (!inst) {
1378
- return;
1379
- }
1380
-
1381
- var tp_inst = this._get(inst, 'timepicker');
1382
- $(target).datepicker('getDate'); // Init selected[Year|Month|Day]
1383
- if (tp_inst) {
1384
- tp_inst._defaults.showTimepicker = false;
1385
- tp_inst._updateDateTime(inst);
1386
- }
1387
- };
1388
-
1389
- $.datepicker._enableTimepickerDatepicker = function(target) {
1390
- var inst = this._getInst(target);
1391
- if (!inst) {
1392
- return;
1393
- }
1394
-
1395
- var tp_inst = this._get(inst, 'timepicker');
1396
- $(target).datepicker('getDate'); // Init selected[Year|Month|Day]
1397
- if (tp_inst) {
1398
- tp_inst._defaults.showTimepicker = true;
1399
- tp_inst._addTimePicker(inst); // Could be disabled on page load
1400
- tp_inst._updateDateTime(inst);
1401
- }
1402
- };
1403
-
1404
- /*
1405
- * Create our own set time function
1406
- */
1407
- $.datepicker._setTime = function(inst, date) {
1408
- var tp_inst = this._get(inst, 'timepicker');
1409
- if (tp_inst) {
1410
- var defaults = tp_inst._defaults;
1411
-
1412
- // calling _setTime with no date sets time to defaults
1413
- tp_inst.hour = date ? date.getHours() : defaults.hour;
1414
- tp_inst.minute = date ? date.getMinutes() : defaults.minute;
1415
- tp_inst.second = date ? date.getSeconds() : defaults.second;
1416
- tp_inst.millisec = date ? date.getMilliseconds() : defaults.millisec;
1417
-
1418
- //check if within min/max times..
1419
- tp_inst._limitMinMaxDateTime(inst, true);
1420
-
1421
- tp_inst._onTimeChange();
1422
- tp_inst._updateDateTime(inst);
1423
- }
1424
- };
1425
-
1426
- /*
1427
- * Create new public method to set only time, callable as $().datepicker('setTime', date)
1428
- */
1429
- $.datepicker._setTimeDatepicker = function(target, date, withDate) {
1430
- var inst = this._getInst(target);
1431
- if (!inst) {
1432
- return;
1433
- }
1434
-
1435
- var tp_inst = this._get(inst, 'timepicker');
1436
-
1437
- if (tp_inst) {
1438
- this._setDateFromField(inst);
1439
- var tp_date;
1440
- if (date) {
1441
- if (typeof date == "string") {
1442
- tp_inst._parseTime(date, withDate);
1443
- tp_date = new Date();
1444
- tp_date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec);
1445
- } else {
1446
- tp_date = new Date(date.getTime());
1447
- }
1448
- if (tp_date.toString() == 'Invalid Date') {
1449
- tp_date = undefined;
1450
- }
1451
- this._setTime(inst, tp_date);
1452
- }
1453
- }
1454
-
1455
- };
1456
-
1457
- /*
1458
- * override setDate() to allow setting time too within Date object
1459
- */
1460
- $.datepicker._base_setDateDatepicker = $.datepicker._setDateDatepicker;
1461
- $.datepicker._setDateDatepicker = function(target, date) {
1462
- var inst = this._getInst(target);
1463
- if (!inst) {
1464
- return;
1465
- }
1466
-
1467
- var tp_date = (date instanceof Date) ? new Date(date.getTime()) : date;
1468
-
1469
- this._updateDatepicker(inst);
1470
- this._base_setDateDatepicker.apply(this, arguments);
1471
- this._setTimeDatepicker(target, tp_date, true);
1472
- };
1473
-
1474
- /*
1475
- * override getDate() to allow getting time too within Date object
1476
- */
1477
- $.datepicker._base_getDateDatepicker = $.datepicker._getDateDatepicker;
1478
- $.datepicker._getDateDatepicker = function(target, noDefault) {
1479
- var inst = this._getInst(target);
1480
- if (!inst) {
1481
- return;
1482
- }
1483
-
1484
- var tp_inst = this._get(inst, 'timepicker');
1485
-
1486
- if (tp_inst) {
1487
- // if it hasn't yet been defined, grab from field
1488
- if(inst.lastVal === undefined){
1489
- this._setDateFromField(inst, noDefault);
1490
- }
1491
-
1492
- var date = this._getDate(inst);
1493
- if (date && tp_inst._parseTime($(target).val(), tp_inst.timeOnly)) {
1494
- date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec);
1495
- }
1496
- return date;
1497
- }
1498
- return this._base_getDateDatepicker(target, noDefault);
1499
- };
1500
-
1501
- /*
1502
- * override parseDate() because UI 1.8.14 throws an error about "Extra characters"
1503
- * An option in datapicker to ignore extra format characters would be nicer.
1504
- */
1505
- $.datepicker._base_parseDate = $.datepicker.parseDate;
1506
- $.datepicker.parseDate = function(format, value, settings) {
1507
- var date;
1508
- try {
1509
- date = this._base_parseDate(format, value, settings);
1510
- } catch (err) {
1511
- // Hack! The error message ends with a colon, a space, and
1512
- // the "extra" characters. We rely on that instead of
1513
- // attempting to perfectly reproduce the parsing algorithm.
1514
- date = this._base_parseDate(format, value.substring(0,value.length-(err.length-err.indexOf(':')-2)), settings);
1515
- $.datepicker.log("Error parsing the date string: " + err + "\ndate string = " + value + "\ndate format = " + format);
1516
- }
1517
- return date;
1518
- };
1519
-
1520
- /*
1521
- * override formatDate to set date with time to the input
1522
- */
1523
- $.datepicker._base_formatDate = $.datepicker._formatDate;
1524
- $.datepicker._formatDate = function(inst, day, month, year) {
1525
- var tp_inst = this._get(inst, 'timepicker');
1526
- if (tp_inst) {
1527
- tp_inst._updateDateTime(inst);
1528
- return tp_inst.$input.val();
1529
- }
1530
- return this._base_formatDate(inst);
1531
- };
1532
-
1533
- /*
1534
- * override options setter to add time to maxDate(Time) and minDate(Time). MaxDate
1535
- */
1536
- $.datepicker._base_optionDatepicker = $.datepicker._optionDatepicker;
1537
- $.datepicker._optionDatepicker = function(target, name, value) {
1538
- var inst = this._getInst(target),
1539
- name_clone;
1540
- if (!inst) {
1541
- return null;
1542
- }
1543
-
1544
- var tp_inst = this._get(inst, 'timepicker');
1545
- if (tp_inst) {
1546
- var min = null,
1547
- max = null,
1548
- onselect = null,
1549
- overrides = tp_inst._defaults.evnts,
1550
- fns = {},
1551
- prop;
1552
- if (typeof name == 'string') { // if min/max was set with the string
1553
- if (name === 'minDate' || name === 'minDateTime') {
1554
- min = value;
1555
- } else if (name === 'maxDate' || name === 'maxDateTime') {
1556
- max = value;
1557
- } else if (name === 'onSelect') {
1558
- onselect = value;
1559
- } else if (overrides.hasOwnProperty(name)) {
1560
- if (typeof (value) === 'undefined') {
1561
- return overrides[name];
1562
- }
1563
- fns[name] = value;
1564
- name_clone = {}; //empty results in exiting function after overrides updated
1565
- }
1566
- } else if (typeof name == 'object') { //if min/max was set with the JSON
1567
- if (name.minDate) {
1568
- min = name.minDate;
1569
- } else if (name.minDateTime) {
1570
- min = name.minDateTime;
1571
- } else if (name.maxDate) {
1572
- max = name.maxDate;
1573
- } else if (name.maxDateTime) {
1574
- max = name.maxDateTime;
1575
- }
1576
- for (prop in overrides) {
1577
- if (overrides.hasOwnProperty(prop) && name[prop]) {
1578
- fns[prop] = name[prop];
1579
- }
1580
- }
1581
- }
1582
- for (prop in fns) {
1583
- if (fns.hasOwnProperty(prop)) {
1584
- overrides[prop] = fns[prop];
1585
- if (!name_clone) { name_clone = $.extend({}, name);}
1586
- delete name_clone[prop];
1587
- }
1588
- }
1589
- if (name_clone && isEmptyObject(name_clone)) { return; }
1590
- if (min) { //if min was set
1591
- if (min === 0) {
1592
- min = new Date();
1593
- } else {
1594
- min = new Date(min);
1595
- }
1596
- tp_inst._defaults.minDate = min;
1597
- tp_inst._defaults.minDateTime = min;
1598
- } else if (max) { //if max was set
1599
- if (max === 0) {
1600
- max = new Date();
1601
- } else {
1602
- max = new Date(max);
1603
- }
1604
- tp_inst._defaults.maxDate = max;
1605
- tp_inst._defaults.maxDateTime = max;
1606
- } else if (onselect) {
1607
- tp_inst._defaults.onSelect = onselect;
1608
- }
1609
- }
1610
- if (value === undefined) {
1611
- return this._base_optionDatepicker.call($.datepicker, target, name);
1612
- }
1613
- return this._base_optionDatepicker.call($.datepicker, target, name_clone || name, value);
1614
- };
1615
- /*
1616
- * jQuery isEmptyObject does not check hasOwnProperty - if someone has added to the object prototype,
1617
- * it will return false for all objects
1618
- */
1619
- var isEmptyObject = function(obj) {
1620
- var prop;
1621
- for (prop in obj) {
1622
- if (obj.hasOwnProperty(obj)) {
1623
- return false;
1624
- }
1625
- }
1626
- return true;
1627
- };
1628
-
1629
- /*
1630
- * jQuery extend now ignores nulls!
1631
- */
1632
- var extendRemove = function(target, props) {
1633
- $.extend(target, props);
1634
- for (var name in props) {
1635
- if (props[name] === null || props[name] === undefined) {
1636
- target[name] = props[name];
1637
- }
1638
- }
1639
- return target;
1640
- };
1641
-
1642
- /*
1643
- * Determine by the time format if should use ampm
1644
- * Returns true if should use ampm, false if not
1645
- */
1646
- var useAmpm = function(timeFormat){
1647
- return (timeFormat.indexOf('t') !== -1 && timeFormat.indexOf('h') !== -1);
1648
- };
1649
-
1650
- /*
1651
- * Converts 24 hour format into 12 hour
1652
- * Returns 12 hour without leading 0
1653
- */
1654
- var convert24to12 = function(hour) {
1655
- if (hour > 12) {
1656
- hour = hour - 12;
1657
- }
1658
-
1659
- if (hour == 0) {
1660
- hour = 12;
1661
- }
1662
-
1663
- return String(hour);
1664
- };
1665
-
1666
- /*
1667
- * Splits datetime string into date ans time substrings.
1668
- * Throws exception when date can't be parsed
1669
- * Returns [dateString, timeString]
1670
- */
1671
- var splitDateTime = function(dateFormat, dateTimeString, dateSettings, timeSettings) {
1672
- try {
1673
- // The idea is to get the number separator occurances in datetime and the time format requested (since time has
1674
- // fewer unknowns, mostly numbers and am/pm). We will use the time pattern to split.
1675
- var separator = timeSettings && timeSettings.separator ? timeSettings.separator : $.timepicker._defaults.separator,
1676
- format = timeSettings && timeSettings.timeFormat ? timeSettings.timeFormat : $.timepicker._defaults.timeFormat,
1677
- timeParts = format.split(separator), // how many occurances of separator may be in our format?
1678
- timePartsLen = timeParts.length,
1679
- allParts = dateTimeString.split(separator),
1680
- allPartsLen = allParts.length;
1681
-
1682
- if (allPartsLen > 1) {
1683
- return [
1684
- allParts.splice(0,allPartsLen-timePartsLen).join(separator),
1685
- allParts.splice(0,timePartsLen).join(separator)
1686
- ];
1687
- }
1688
-
1689
- } catch (err) {
1690
- $.datepicker.log('Could not split the date from the time. Please check the following datetimepicker options' +
1691
- "\nthrown error: " + err +
1692
- "\ndateTimeString" + dateTimeString +
1693
- "\ndateFormat = " + dateFormat +
1694
- "\nseparator = " + timeSettings.separator +
1695
- "\ntimeFormat = " + timeSettings.timeFormat);
1696
-
1697
- if (err.indexOf(":") >= 0) {
1698
- // Hack! The error message ends with a colon, a space, and
1699
- // the "extra" characters. We rely on that instead of
1700
- // attempting to perfectly reproduce the parsing algorithm.
1701
- var dateStringLength = dateTimeString.length - (err.length - err.indexOf(':') - 2),
1702
- timeString = dateTimeString.substring(dateStringLength);
1703
-
1704
- return [$.trim(dateTimeString.substring(0, dateStringLength)), $.trim(dateTimeString.substring(dateStringLength))];
1705
-
1706
- } else {
1707
- throw err;
1708
- }
1709
- }
1710
- return [dateTimeString, ''];
1711
- };
1712
-
1713
- /*
1714
- * Internal function to parse datetime interval
1715
- * Returns: {date: Date, timeObj: Object}, where
1716
- * date - parsed date without time (type Date)
1717
- * timeObj = {hour: , minute: , second: , millisec: } - parsed time. Optional
1718
- */
1719
- var parseDateTimeInternal = function(dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings) {
1720
- var date;
1721
- var splitRes = splitDateTime(dateFormat, dateTimeString, dateSettings, timeSettings);
1722
- date = $.datepicker._base_parseDate(dateFormat, splitRes[0], dateSettings);
1723
- if (splitRes[1] !== '') {
1724
- var timeString = splitRes[1],
1725
- parsedTime = $.datepicker.parseTime(timeFormat, timeString, timeSettings);
1726
-
1727
- if (parsedTime === null) {
1728
- throw 'Wrong time format';
1729
- }
1730
- return {
1731
- date: date,
1732
- timeObj: parsedTime
1733
- };
1734
- } else {
1735
- return {
1736
- date: date
1737
- };
1738
- }
1739
- };
1740
-
1741
- /*
1742
- * Internal function to set timezone_select to the local timezone
1743
- */
1744
- var selectLocalTimeZone = function(tp_inst, date) {
1745
- if (tp_inst && tp_inst.timezone_select) {
1746
- tp_inst._defaults.useLocalTimezone = true;
1747
- var now = typeof date !== 'undefined' ? date : new Date();
1748
- var tzoffset = $.timepicker.timeZoneOffsetString(now);
1749
- if (tp_inst._defaults.timezoneIso8601) {
1750
- tzoffset = tzoffset.substring(0, 3) + ':' + tzoffset.substring(3);
1751
- }
1752
- tp_inst.timezone_select.val(tzoffset);
1753
- }
1754
- };
1755
-
1756
- /*
1757
- * Create a Singleton Insance
1758
- */
1759
- $.timepicker = new Timepicker();
1760
-
1761
- /**
1762
- * Get the timezone offset as string from a date object (eg '+0530' for UTC+5.5)
1763
- * @param date
1764
- * @return string
1765
- */
1766
- $.timepicker.timeZoneOffsetString = function(date) {
1767
- var off = date.getTimezoneOffset() * -1,
1768
- minutes = off % 60,
1769
- hours = (off - minutes) / 60;
1770
- return (off >= 0 ? '+' : '-') + ('0' + (hours * 101).toString()).substr(-2) + ('0' + (minutes * 101).toString()).substr(-2);
1771
- };
1772
-
1773
- /**
1774
- * Calls `timepicker()` on the `startTime` and `endTime` elements, and configures them to
1775
- * enforce date range limits.
1776
- * n.b. The input value must be correctly formatted (reformatting is not supported)
1777
- * @param Element startTime
1778
- * @param Element endTime
1779
- * @param obj options Options for the timepicker() call
1780
- * @return jQuery
1781
- */
1782
- $.timepicker.timeRange = function(startTime, endTime, options) {
1783
- return $.timepicker.handleRange('timepicker', startTime, endTime, options);
1784
- };
1785
-
1786
- /**
1787
- * Calls `datetimepicker` on the `startTime` and `endTime` elements, and configures them to
1788
- * enforce date range limits.
1789
- * @param Element startTime
1790
- * @param Element endTime
1791
- * @param obj options Options for the `timepicker()` call. Also supports `reformat`,
1792
- * a boolean value that can be used to reformat the input values to the `dateFormat`.
1793
- * @param string method Can be used to specify the type of picker to be added
1794
- * @return jQuery
1795
- */
1796
- $.timepicker.dateTimeRange = function(startTime, endTime, options) {
1797
- $.timepicker.dateRange(startTime, endTime, options, 'datetimepicker');
1798
- };
1799
-
1800
- /**
1801
- * Calls `method` on the `startTime` and `endTime` elements, and configures them to
1802
- * enforce date range limits.
1803
- * @param Element startTime
1804
- * @param Element endTime
1805
- * @param obj options Options for the `timepicker()` call. Also supports `reformat`,
1806
- * a boolean value that can be used to reformat the input values to the `dateFormat`.
1807
- * @param string method Can be used to specify the type of picker to be added
1808
- * @return jQuery
1809
- */
1810
- $.timepicker.dateRange = function(startTime, endTime, options, method) {
1811
- method = method || 'datepicker';
1812
- $.timepicker.handleRange(method, startTime, endTime, options);
1813
- };
1814
-
1815
- /**
1816
- * Calls `method` on the `startTime` and `endTime` elements, and configures them to
1817
- * enforce date range limits.
1818
- * @param string method Can be used to specify the type of picker to be added
1819
- * @param Element startTime
1820
- * @param Element endTime
1821
- * @param obj options Options for the `timepicker()` call. Also supports `reformat`,
1822
- * a boolean value that can be used to reformat the input values to the `dateFormat`.
1823
- * @return jQuery
1824
- */
1825
- $.timepicker.handleRange = function(method, startTime, endTime, options) {
1826
- $.fn[method].call(startTime, $.extend({
1827
- onClose: function(dateText, inst) {
1828
- checkDates(this, endTime, dateText);
1829
- },
1830
- onSelect: function(selectedDateTime) {
1831
- selected(this, endTime, 'minDate');
1832
- }
1833
- }, options, options.start));
1834
- $.fn[method].call(endTime, $.extend({
1835
- onClose: function(dateText, inst) {
1836
- checkDates(this, startTime, dateText);
1837
- },
1838
- onSelect: function(selectedDateTime) {
1839
- selected(this, startTime, 'maxDate');
1840
- }
1841
- }, options, options.end));
1842
- // timepicker doesn't provide access to its 'timeFormat' option,
1843
- // nor could I get datepicker.formatTime() to behave with times, so I
1844
- // have disabled reformatting for timepicker
1845
- if (method != 'timepicker' && options.reformat) {
1846
- $([startTime, endTime]).each(function() {
1847
- var format = $(this)[method].call($(this), 'option', 'dateFormat'),
1848
- date = new Date($(this).val());
1849
- if ($(this).val() && date) {
1850
- $(this).val($.datepicker.formatDate(format, date));
1851
- }
1852
- });
1853
- }
1854
- checkDates(startTime, endTime, startTime.val());
1855
-
1856
- function checkDates(changed, other, dateText) {
1857
- if (other.val() && (new Date(startTime.val()) > new Date(endTime.val()))) {
1858
- other.val(dateText);
1859
- }
1860
- }
1861
- selected(startTime, endTime, 'minDate');
1862
- selected(endTime, startTime, 'maxDate');
1863
-
1864
- function selected(changed, other, option) {
1865
- if (!$(changed).val()) {
1866
- return;
1867
- }
1868
- var date = $(changed)[method].call($(changed), 'getDate');
1869
- // timepicker doesn't implement 'getDate' and returns a jQuery
1870
- if (date.getTime) {
1871
- $(other)[method].call($(other), 'option', option, date);
1872
- }
1873
- }
1874
- return $([startTime.get(0), endTime.get(0)]);
1875
- };
1876
-
1877
- /*
1878
- * Keep up with the version
1879
- */
1880
- $.timepicker.version = "1.1.1";
1881
-
1882
- })(jQuery);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*! jQuery Timepicker Addon - v1.4 - 2013-08-11
2
+ * http://trentrichardson.com/examples/timepicker
3
+ * Copyright (c) 2013 Trent Richardson; Licensed MIT */
4
+ (function ($) {
5
+
6
+ /*
7
+ * Lets not redefine timepicker, Prevent "Uncaught RangeError: Maximum call stack size exceeded"
8
+ */
9
+ $.ui.timepicker = $.ui.timepicker || {};
10
+ if ($.ui.timepicker.version) {
11
+ return;
12
+ }
13
+
14
+ /*
15
+ * Extend jQueryUI, get it started with our version number
16
+ */
17
+ $.extend($.ui, {
18
+ timepicker: {
19
+ version: "1.4"
20
+ }
21
+ });
22
+
23
+ /*
24
+ * Timepicker manager.
25
+ * Use the singleton instance of this class, $.timepicker, to interact with the time picker.
26
+ * Settings for (groups of) time pickers are maintained in an instance object,
27
+ * allowing multiple different settings on the same page.
28
+ */
29
+ var Timepicker = function () {
30
+ this.regional = []; // Available regional settings, indexed by language code
31
+ this.regional[''] = { // Default regional settings
32
+ currentText: 'Now',
33
+ closeText: 'Done',
34
+ amNames: ['AM', 'A'],
35
+ pmNames: ['PM', 'P'],
36
+ timeFormat: 'HH:mm',
37
+ timeSuffix: '',
38
+ timeOnlyTitle: 'Choose Time',
39
+ timeText: 'Time',
40
+ hourText: 'Hour',
41
+ minuteText: 'Minute',
42
+ secondText: 'Second',
43
+ millisecText: 'Millisecond',
44
+ microsecText: 'Microsecond',
45
+ timezoneText: 'Time Zone',
46
+ isRTL: false
47
+ };
48
+ this._defaults = { // Global defaults for all the datetime picker instances
49
+ showButtonPanel: true,
50
+ timeOnly: false,
51
+ showHour: null,
52
+ showMinute: null,
53
+ showSecond: null,
54
+ showMillisec: null,
55
+ showMicrosec: null,
56
+ showTimezone: null,
57
+ showTime: true,
58
+ stepHour: 1,
59
+ stepMinute: 1,
60
+ stepSecond: 1,
61
+ stepMillisec: 1,
62
+ stepMicrosec: 1,
63
+ hour: 0,
64
+ minute: 0,
65
+ second: 0,
66
+ millisec: 0,
67
+ microsec: 0,
68
+ timezone: null,
69
+ hourMin: 0,
70
+ minuteMin: 0,
71
+ secondMin: 0,
72
+ millisecMin: 0,
73
+ microsecMin: 0,
74
+ hourMax: 23,
75
+ minuteMax: 59,
76
+ secondMax: 59,
77
+ millisecMax: 999,
78
+ microsecMax: 999,
79
+ minDateTime: null,
80
+ maxDateTime: null,
81
+ onSelect: null,
82
+ hourGrid: 0,
83
+ minuteGrid: 0,
84
+ secondGrid: 0,
85
+ millisecGrid: 0,
86
+ microsecGrid: 0,
87
+ alwaysSetTime: true,
88
+ separator: ' ',
89
+ altFieldTimeOnly: true,
90
+ altTimeFormat: null,
91
+ altSeparator: null,
92
+ altTimeSuffix: null,
93
+ pickerTimeFormat: null,
94
+ pickerTimeSuffix: null,
95
+ showTimepicker: true,
96
+ timezoneList: null,
97
+ addSliderAccess: false,
98
+ sliderAccessArgs: null,
99
+ controlType: 'slider',
100
+ defaultValue: null,
101
+ parse: 'strict'
102
+ };
103
+ $.extend(this._defaults, this.regional['']);
104
+ };
105
+
106
+ $.extend(Timepicker.prototype, {
107
+ $input: null,
108
+ $altInput: null,
109
+ $timeObj: null,
110
+ inst: null,
111
+ hour_slider: null,
112
+ minute_slider: null,
113
+ second_slider: null,
114
+ millisec_slider: null,
115
+ microsec_slider: null,
116
+ timezone_select: null,
117
+ hour: 0,
118
+ minute: 0,
119
+ second: 0,
120
+ millisec: 0,
121
+ microsec: 0,
122
+ timezone: null,
123
+ hourMinOriginal: null,
124
+ minuteMinOriginal: null,
125
+ secondMinOriginal: null,
126
+ millisecMinOriginal: null,
127
+ microsecMinOriginal: null,
128
+ hourMaxOriginal: null,
129
+ minuteMaxOriginal: null,
130
+ secondMaxOriginal: null,
131
+ millisecMaxOriginal: null,
132
+ microsecMaxOriginal: null,
133
+ ampm: '',
134
+ formattedDate: '',
135
+ formattedTime: '',
136
+ formattedDateTime: '',
137
+ timezoneList: null,
138
+ units: ['hour', 'minute', 'second', 'millisec', 'microsec'],
139
+ support: {},
140
+ control: null,
141
+
142
+ /*
143
+ * Override the default settings for all instances of the time picker.
144
+ * @param {Object} settings object - the new settings to use as defaults (anonymous object)
145
+ * @return {Object} the manager object
146
+ */
147
+ setDefaults: function (settings) {
148
+ extendRemove(this._defaults, settings || {});
149
+ return this;
150
+ },
151
+
152
+ /*
153
+ * Create a new Timepicker instance
154
+ */
155
+ _newInst: function ($input, opts) {
156
+ var tp_inst = new Timepicker(),
157
+ inlineSettings = {},
158
+ fns = {},
159
+ overrides, i;
160
+
161
+ for (var attrName in this._defaults) {
162
+ if (this._defaults.hasOwnProperty(attrName)) {
163
+ var attrValue = $input.attr('time:' + attrName);
164
+ if (attrValue) {
165
+ try {
166
+ inlineSettings[attrName] = eval(attrValue);
167
+ } catch (err) {
168
+ inlineSettings[attrName] = attrValue;
169
+ }
170
+ }
171
+ }
172
+ }
173
+
174
+ overrides = {
175
+ beforeShow: function (input, dp_inst) {
176
+ if ($.isFunction(tp_inst._defaults.evnts.beforeShow)) {
177
+ return tp_inst._defaults.evnts.beforeShow.call($input[0], input, dp_inst, tp_inst);
178
+ }
179
+ },
180
+ onChangeMonthYear: function (year, month, dp_inst) {
181
+ // Update the time as well : this prevents the time from disappearing from the $input field.
182
+ tp_inst._updateDateTime(dp_inst);
183
+ if ($.isFunction(tp_inst._defaults.evnts.onChangeMonthYear)) {
184
+ tp_inst._defaults.evnts.onChangeMonthYear.call($input[0], year, month, dp_inst, tp_inst);
185
+ }
186
+ },
187
+ onClose: function (dateText, dp_inst) {
188
+ if (tp_inst.timeDefined === true && $input.val() !== '') {
189
+ tp_inst._updateDateTime(dp_inst);
190
+ }
191
+ if ($.isFunction(tp_inst._defaults.evnts.onClose)) {
192
+ tp_inst._defaults.evnts.onClose.call($input[0], dateText, dp_inst, tp_inst);
193
+ }
194
+ }
195
+ };
196
+ for (i in overrides) {
197
+ if (overrides.hasOwnProperty(i)) {
198
+ fns[i] = opts[i] || null;
199
+ }
200
+ }
201
+
202
+ tp_inst._defaults = $.extend({}, this._defaults, inlineSettings, opts, overrides, {
203
+ evnts: fns,
204
+ timepicker: tp_inst // add timepicker as a property of datepicker: $.datepicker._get(dp_inst, 'timepicker');
205
+ });
206
+ tp_inst.amNames = $.map(tp_inst._defaults.amNames, function (val) {
207
+ return val.toUpperCase();
208
+ });
209
+ tp_inst.pmNames = $.map(tp_inst._defaults.pmNames, function (val) {
210
+ return val.toUpperCase();
211
+ });
212
+
213
+ // detect which units are supported
214
+ tp_inst.support = detectSupport(
215
+ tp_inst._defaults.timeFormat +
216
+ (tp_inst._defaults.pickerTimeFormat ? tp_inst._defaults.pickerTimeFormat : '') +
217
+ (tp_inst._defaults.altTimeFormat ? tp_inst._defaults.altTimeFormat : ''));
218
+
219
+ // controlType is string - key to our this._controls
220
+ if (typeof(tp_inst._defaults.controlType) === 'string') {
221
+ if (tp_inst._defaults.controlType === 'slider' && typeof($.ui.slider) === 'undefined') {
222
+ tp_inst._defaults.controlType = 'select';
223
+ }
224
+ tp_inst.control = tp_inst._controls[tp_inst._defaults.controlType];
225
+ }
226
+ // controlType is an object and must implement create, options, value methods
227
+ else {
228
+ tp_inst.control = tp_inst._defaults.controlType;
229
+ }
230
+
231
+ // prep the timezone options
232
+ var timezoneList = [-720, -660, -600, -570, -540, -480, -420, -360, -300, -270, -240, -210, -180, -120, -60,
233
+ 0, 60, 120, 180, 210, 240, 270, 300, 330, 345, 360, 390, 420, 480, 525, 540, 570, 600, 630, 660, 690, 720, 765, 780, 840];
234
+ if (tp_inst._defaults.timezoneList !== null) {
235
+ timezoneList = tp_inst._defaults.timezoneList;
236
+ }
237
+ var tzl = timezoneList.length, tzi = 0, tzv = null;
238
+ if (tzl > 0 && typeof timezoneList[0] !== 'object') {
239
+ for (; tzi < tzl; tzi++) {
240
+ tzv = timezoneList[tzi];
241
+ timezoneList[tzi] = { value: tzv, label: $.timepicker.timezoneOffsetString(tzv, tp_inst.support.iso8601) };
242
+ }
243
+ }
244
+ tp_inst._defaults.timezoneList = timezoneList;
245
+
246
+ // set the default units
247
+ tp_inst.timezone = tp_inst._defaults.timezone !== null ? $.timepicker.timezoneOffsetNumber(tp_inst._defaults.timezone) :
248
+ ((new Date()).getTimezoneOffset() * -1);
249
+ tp_inst.hour = tp_inst._defaults.hour < tp_inst._defaults.hourMin ? tp_inst._defaults.hourMin :
250
+ tp_inst._defaults.hour > tp_inst._defaults.hourMax ? tp_inst._defaults.hourMax : tp_inst._defaults.hour;
251
+ tp_inst.minute = tp_inst._defaults.minute < tp_inst._defaults.minuteMin ? tp_inst._defaults.minuteMin :
252
+ tp_inst._defaults.minute > tp_inst._defaults.minuteMax ? tp_inst._defaults.minuteMax : tp_inst._defaults.minute;
253
+ tp_inst.second = tp_inst._defaults.second < tp_inst._defaults.secondMin ? tp_inst._defaults.secondMin :
254
+ tp_inst._defaults.second > tp_inst._defaults.secondMax ? tp_inst._defaults.secondMax : tp_inst._defaults.second;
255
+ tp_inst.millisec = tp_inst._defaults.millisec < tp_inst._defaults.millisecMin ? tp_inst._defaults.millisecMin :
256
+ tp_inst._defaults.millisec > tp_inst._defaults.millisecMax ? tp_inst._defaults.millisecMax : tp_inst._defaults.millisec;
257
+ tp_inst.microsec = tp_inst._defaults.microsec < tp_inst._defaults.microsecMin ? tp_inst._defaults.microsecMin :
258
+ tp_inst._defaults.microsec > tp_inst._defaults.microsecMax ? tp_inst._defaults.microsecMax : tp_inst._defaults.microsec;
259
+ tp_inst.ampm = '';
260
+ tp_inst.$input = $input;
261
+
262
+ if (tp_inst._defaults.altField) {
263
+ tp_inst.$altInput = $(tp_inst._defaults.altField).css({
264
+ cursor: 'pointer'
265
+ }).focus(function () {
266
+ $input.trigger("focus");
267
+ });
268
+ }
269
+
270
+ if (tp_inst._defaults.minDate === 0 || tp_inst._defaults.minDateTime === 0) {
271
+ tp_inst._defaults.minDate = new Date();
272
+ }
273
+ if (tp_inst._defaults.maxDate === 0 || tp_inst._defaults.maxDateTime === 0) {
274
+ tp_inst._defaults.maxDate = new Date();
275
+ }
276
+
277
+ // datepicker needs minDate/maxDate, timepicker needs minDateTime/maxDateTime..
278
+ if (tp_inst._defaults.minDate !== undefined && tp_inst._defaults.minDate instanceof Date) {
279
+ tp_inst._defaults.minDateTime = new Date(tp_inst._defaults.minDate.getTime());
280
+ }
281
+ if (tp_inst._defaults.minDateTime !== undefined && tp_inst._defaults.minDateTime instanceof Date) {
282
+ tp_inst._defaults.minDate = new Date(tp_inst._defaults.minDateTime.getTime());
283
+ }
284
+ if (tp_inst._defaults.maxDate !== undefined && tp_inst._defaults.maxDate instanceof Date) {
285
+ tp_inst._defaults.maxDateTime = new Date(tp_inst._defaults.maxDate.getTime());
286
+ }
287
+ if (tp_inst._defaults.maxDateTime !== undefined && tp_inst._defaults.maxDateTime instanceof Date) {
288
+ tp_inst._defaults.maxDate = new Date(tp_inst._defaults.maxDateTime.getTime());
289
+ }
290
+ tp_inst.$input.bind('focus', function () {
291
+ tp_inst._onFocus();
292
+ });
293
+
294
+ return tp_inst;
295
+ },
296
+
297
+ /*
298
+ * add our sliders to the calendar
299
+ */
300
+ _addTimePicker: function (dp_inst) {
301
+ var currDT = (this.$altInput && this._defaults.altFieldTimeOnly) ? this.$input.val() + ' ' + this.$altInput.val() : this.$input.val();
302
+
303
+ this.timeDefined = this._parseTime(currDT);
304
+ this._limitMinMaxDateTime(dp_inst, false);
305
+ this._injectTimePicker();
306
+ },
307
+
308
+ /*
309
+ * parse the time string from input value or _setTime
310
+ */
311
+ _parseTime: function (timeString, withDate) {
312
+ if (!this.inst) {
313
+ this.inst = $.datepicker._getInst(this.$input[0]);
314
+ }
315
+
316
+ if (withDate || !this._defaults.timeOnly) {
317
+ var dp_dateFormat = $.datepicker._get(this.inst, 'dateFormat');
318
+ try {
319
+ var parseRes = parseDateTimeInternal(dp_dateFormat, this._defaults.timeFormat, timeString, $.datepicker._getFormatConfig(this.inst), this._defaults);
320
+ if (!parseRes.timeObj) {
321
+ return false;
322
+ }
323
+ $.extend(this, parseRes.timeObj);
324
+ } catch (err) {
325
+ $.timepicker.log("Error parsing the date/time string: " + err +
326
+ "\ndate/time string = " + timeString +
327
+ "\ntimeFormat = " + this._defaults.timeFormat +
328
+ "\ndateFormat = " + dp_dateFormat);
329
+ return false;
330
+ }
331
+ return true;
332
+ } else {
333
+ var timeObj = $.datepicker.parseTime(this._defaults.timeFormat, timeString, this._defaults);
334
+ if (!timeObj) {
335
+ return false;
336
+ }
337
+ $.extend(this, timeObj);
338
+ return true;
339
+ }
340
+ },
341
+
342
+ /*
343
+ * generate and inject html for timepicker into ui datepicker
344
+ */
345
+ _injectTimePicker: function () {
346
+ var $dp = this.inst.dpDiv,
347
+ o = this.inst.settings,
348
+ tp_inst = this,
349
+ litem = '',
350
+ uitem = '',
351
+ show = null,
352
+ max = {},
353
+ gridSize = {},
354
+ size = null,
355
+ i = 0,
356
+ l = 0;
357
+
358
+ // Prevent displaying twice
359
+ if ($dp.find("div.ui-timepicker-div").length === 0 && o.showTimepicker) {
360
+ var noDisplay = ' style="display:none;"',
361
+ html = '<div class="ui-timepicker-div' + (o.isRTL ? ' ui-timepicker-rtl' : '') + '"><dl>' + '<dt class="ui_tpicker_time_label"' + ((o.showTime) ? '' : noDisplay) + '>' + o.timeText + '</dt>' +
362
+ '<dd class="ui_tpicker_time"' + ((o.showTime) ? '' : noDisplay) + '></dd>';
363
+
364
+ // Create the markup
365
+ for (i = 0, l = this.units.length; i < l; i++) {
366
+ litem = this.units[i];
367
+ uitem = litem.substr(0, 1).toUpperCase() + litem.substr(1);
368
+ show = o['show' + uitem] !== null ? o['show' + uitem] : this.support[litem];
369
+
370
+ // Added by Peter Medeiros:
371
+ // - Figure out what the hour/minute/second max should be based on the step values.
372
+ // - Example: if stepMinute is 15, then minMax is 45.
373
+ max[litem] = parseInt((o[litem + 'Max'] - ((o[litem + 'Max'] - o[litem + 'Min']) % o['step' + uitem])), 10);
374
+ gridSize[litem] = 0;
375
+
376
+ html += '<dt class="ui_tpicker_' + litem + '_label"' + (show ? '' : noDisplay) + '>' + o[litem + 'Text'] + '</dt>' +
377
+ '<dd class="ui_tpicker_' + litem + '"><div class="ui_tpicker_' + litem + '_slider"' + (show ? '' : noDisplay) + '></div>';
378
+
379
+ if (show && o[litem + 'Grid'] > 0) {
380
+ html += '<div style="padding-left: 1px"><table class="ui-tpicker-grid-label"><tr>';
381
+
382
+ if (litem === 'hour') {
383
+ for (var h = o[litem + 'Min']; h <= max[litem]; h += parseInt(o[litem + 'Grid'], 10)) {
384
+ gridSize[litem]++;
385
+ var tmph = $.datepicker.formatTime(this.support.ampm ? 'hht' : 'HH', {hour: h}, o);
386
+ html += '<td data-for="' + litem + '">' + tmph + '</td>';
387
+ }
388
+ }
389
+ else {
390
+ for (var m = o[litem + 'Min']; m <= max[litem]; m += parseInt(o[litem + 'Grid'], 10)) {
391
+ gridSize[litem]++;
392
+ html += '<td data-for="' + litem + '">' + ((m < 10) ? '0' : '') + m + '</td>';
393
+ }
394
+ }
395
+
396
+ html += '</tr></table></div>';
397
+ }
398
+ html += '</dd>';
399
+ }
400
+
401
+ // Timezone
402
+ var showTz = o.showTimezone !== null ? o.showTimezone : this.support.timezone;
403
+ html += '<dt class="ui_tpicker_timezone_label"' + (showTz ? '' : noDisplay) + '>' + o.timezoneText + '</dt>';
404
+ html += '<dd class="ui_tpicker_timezone" ' + (showTz ? '' : noDisplay) + '></dd>';
405
+
406
+ // Create the elements from string
407
+ html += '</dl></div>';
408
+ var $tp = $(html);
409
+
410
+ // if we only want time picker...
411
+ if (o.timeOnly === true) {
412
+ $tp.prepend('<div class="ui-widget-header ui-helper-clearfix ui-corner-all">' + '<div class="ui-datepicker-title">' + o.timeOnlyTitle + '</div>' + '</div>');
413
+ $dp.find('.ui-datepicker-header, .ui-datepicker-calendar').hide();
414
+ }
415
+
416
+ // add sliders, adjust grids, add events
417
+ for (i = 0, l = tp_inst.units.length; i < l; i++) {
418
+ litem = tp_inst.units[i];
419
+ uitem = litem.substr(0, 1).toUpperCase() + litem.substr(1);
420
+ show = o['show' + uitem] !== null ? o['show' + uitem] : this.support[litem];
421
+
422
+ // add the slider
423
+ tp_inst[litem + '_slider'] = tp_inst.control.create(tp_inst, $tp.find('.ui_tpicker_' + litem + '_slider'), litem, tp_inst[litem], o[litem + 'Min'], max[litem], o['step' + uitem]);
424
+
425
+ // adjust the grid and add click event
426
+ if (show && o[litem + 'Grid'] > 0) {
427
+ size = 100 * gridSize[litem] * o[litem + 'Grid'] / (max[litem] - o[litem + 'Min']);
428
+ $tp.find('.ui_tpicker_' + litem + ' table').css({
429
+ width: size + "%",
430
+ marginLeft: o.isRTL ? '0' : ((size / (-2 * gridSize[litem])) + "%"),
431
+ marginRight: o.isRTL ? ((size / (-2 * gridSize[litem])) + "%") : '0',
432
+ borderCollapse: 'collapse'
433
+ }).find("td").click(function (e) {
434
+ var $t = $(this),
435
+ h = $t.html(),
436
+ n = parseInt(h.replace(/[^0-9]/g), 10),
437
+ ap = h.replace(/[^apm]/ig),
438
+ f = $t.data('for'); // loses scope, so we use data-for
439
+
440
+ if (f === 'hour') {
441
+ if (ap.indexOf('p') !== -1 && n < 12) {
442
+ n += 12;
443
+ }
444
+ else {
445
+ if (ap.indexOf('a') !== -1 && n === 12) {
446
+ n = 0;
447
+ }
448
+ }
449
+ }
450
+
451
+ tp_inst.control.value(tp_inst, tp_inst[f + '_slider'], litem, n);
452
+
453
+ tp_inst._onTimeChange();
454
+ tp_inst._onSelectHandler();
455
+ }).css({
456
+ cursor: 'pointer',
457
+ width: (100 / gridSize[litem]) + '%',
458
+ textAlign: 'center',
459
+ overflow: 'hidden'
460
+ });
461
+ } // end if grid > 0
462
+ } // end for loop
463
+
464
+ // Add timezone options
465
+ this.timezone_select = $tp.find('.ui_tpicker_timezone').append('<select></select>').find("select");
466
+ $.fn.append.apply(this.timezone_select,
467
+ $.map(o.timezoneList, function (val, idx) {
468
+ return $("<option />").val(typeof val === "object" ? val.value : val).text(typeof val === "object" ? val.label : val);
469
+ }));
470
+ if (typeof(this.timezone) !== "undefined" && this.timezone !== null && this.timezone !== "") {
471
+ var local_timezone = (new Date(this.inst.selectedYear, this.inst.selectedMonth, this.inst.selectedDay, 12)).getTimezoneOffset() * -1;
472
+ if (local_timezone === this.timezone) {
473
+ selectLocalTimezone(tp_inst);
474
+ } else {
475
+ this.timezone_select.val(this.timezone);
476
+ }
477
+ } else {
478
+ if (typeof(this.hour) !== "undefined" && this.hour !== null && this.hour !== "") {
479
+ this.timezone_select.val(o.timezone);
480
+ } else {
481
+ selectLocalTimezone(tp_inst);
482
+ }
483
+ }
484
+ this.timezone_select.change(function () {
485
+ tp_inst._onTimeChange();
486
+ tp_inst._onSelectHandler();
487
+ });
488
+ // End timezone options
489
+
490
+ // inject timepicker into datepicker
491
+ var $buttonPanel = $dp.find('.ui-datepicker-buttonpane');
492
+ if ($buttonPanel.length) {
493
+ $buttonPanel.before($tp);
494
+ } else {
495
+ $dp.append($tp);
496
+ }
497
+
498
+ this.$timeObj = $tp.find('.ui_tpicker_time');
499
+
500
+ if (this.inst !== null) {
501
+ var timeDefined = this.timeDefined;
502
+ this._onTimeChange();
503
+ this.timeDefined = timeDefined;
504
+ }
505
+
506
+ // slideAccess integration: http://trentrichardson.com/2011/11/11/jquery-ui-sliders-and-touch-accessibility/
507
+ if (this._defaults.addSliderAccess) {
508
+ var sliderAccessArgs = this._defaults.sliderAccessArgs,
509
+ rtl = this._defaults.isRTL;
510
+ sliderAccessArgs.isRTL = rtl;
511
+
512
+ setTimeout(function () { // fix for inline mode
513
+ if ($tp.find('.ui-slider-access').length === 0) {
514
+ $tp.find('.ui-slider:visible').sliderAccess(sliderAccessArgs);
515
+
516
+ // fix any grids since sliders are shorter
517
+ var sliderAccessWidth = $tp.find('.ui-slider-access:eq(0)').outerWidth(true);
518
+ if (sliderAccessWidth) {
519
+ $tp.find('table:visible').each(function () {
520
+ var $g = $(this),
521
+ oldWidth = $g.outerWidth(),
522
+ oldMarginLeft = $g.css(rtl ? 'marginRight' : 'marginLeft').toString().replace('%', ''),
523
+ newWidth = oldWidth - sliderAccessWidth,
524
+ newMarginLeft = ((oldMarginLeft * newWidth) / oldWidth) + '%',
525
+ css = { width: newWidth, marginRight: 0, marginLeft: 0 };
526
+ css[rtl ? 'marginRight' : 'marginLeft'] = newMarginLeft;
527
+ $g.css(css);
528
+ });
529
+ }
530
+ }
531
+ }, 10);
532
+ }
533
+ // end slideAccess integration
534
+
535
+ tp_inst._limitMinMaxDateTime(this.inst, true);
536
+ }
537
+ },
538
+
539
+ /*
540
+ * This function tries to limit the ability to go outside the
541
+ * min/max date range
542
+ */
543
+ _limitMinMaxDateTime: function (dp_inst, adjustSliders) {
544
+ var o = this._defaults,
545
+ dp_date = new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay);
546
+
547
+ if (!this._defaults.showTimepicker) {
548
+ return;
549
+ } // No time so nothing to check here
550
+
551
+ if ($.datepicker._get(dp_inst, 'minDateTime') !== null && $.datepicker._get(dp_inst, 'minDateTime') !== undefined && dp_date) {
552
+ var minDateTime = $.datepicker._get(dp_inst, 'minDateTime'),
553
+ minDateTimeDate = new Date(minDateTime.getFullYear(), minDateTime.getMonth(), minDateTime.getDate(), 0, 0, 0, 0);
554
+
555
+ if (this.hourMinOriginal === null || this.minuteMinOriginal === null || this.secondMinOriginal === null || this.millisecMinOriginal === null || this.microsecMinOriginal === null) {
556
+ this.hourMinOriginal = o.hourMin;
557
+ this.minuteMinOriginal = o.minuteMin;
558
+ this.secondMinOriginal = o.secondMin;
559
+ this.millisecMinOriginal = o.millisecMin;
560
+ this.microsecMinOriginal = o.microsecMin;
561
+ }
562
+
563
+ if (dp_inst.settings.timeOnly || minDateTimeDate.getTime() === dp_date.getTime()) {
564
+ this._defaults.hourMin = minDateTime.getHours();
565
+ if (this.hour <= this._defaults.hourMin) {
566
+ this.hour = this._defaults.hourMin;
567
+ this._defaults.minuteMin = minDateTime.getMinutes();
568
+ if (this.minute <= this._defaults.minuteMin) {
569
+ this.minute = this._defaults.minuteMin;
570
+ this._defaults.secondMin = minDateTime.getSeconds();
571
+ if (this.second <= this._defaults.secondMin) {
572
+ this.second = this._defaults.secondMin;
573
+ this._defaults.millisecMin = minDateTime.getMilliseconds();
574
+ if (this.millisec <= this._defaults.millisecMin) {
575
+ this.millisec = this._defaults.millisecMin;
576
+ this._defaults.microsecMin = minDateTime.getMicroseconds();
577
+ } else {
578
+ if (this.microsec < this._defaults.microsecMin) {
579
+ this.microsec = this._defaults.microsecMin;
580
+ }
581
+ this._defaults.microsecMin = this.microsecMinOriginal;
582
+ }
583
+ } else {
584
+ this._defaults.millisecMin = this.millisecMinOriginal;
585
+ this._defaults.microsecMin = this.microsecMinOriginal;
586
+ }
587
+ } else {
588
+ this._defaults.secondMin = this.secondMinOriginal;
589
+ this._defaults.millisecMin = this.millisecMinOriginal;
590
+ this._defaults.microsecMin = this.microsecMinOriginal;
591
+ }
592
+ } else {
593
+ this._defaults.minuteMin = this.minuteMinOriginal;
594
+ this._defaults.secondMin = this.secondMinOriginal;
595
+ this._defaults.millisecMin = this.millisecMinOriginal;
596
+ this._defaults.microsecMin = this.microsecMinOriginal;
597
+ }
598
+ } else {
599
+ this._defaults.hourMin = this.hourMinOriginal;
600
+ this._defaults.minuteMin = this.minuteMinOriginal;
601
+ this._defaults.secondMin = this.secondMinOriginal;
602
+ this._defaults.millisecMin = this.millisecMinOriginal;
603
+ this._defaults.microsecMin = this.microsecMinOriginal;
604
+ }
605
+ }
606
+
607
+ if ($.datepicker._get(dp_inst, 'maxDateTime') !== null && $.datepicker._get(dp_inst, 'maxDateTime') !== undefined && dp_date) {
608
+ var maxDateTime = $.datepicker._get(dp_inst, 'maxDateTime'),
609
+ maxDateTimeDate = new Date(maxDateTime.getFullYear(), maxDateTime.getMonth(), maxDateTime.getDate(), 0, 0, 0, 0);
610
+
611
+ if (this.hourMaxOriginal === null || this.minuteMaxOriginal === null || this.secondMaxOriginal === null || this.millisecMaxOriginal === null) {
612
+ this.hourMaxOriginal = o.hourMax;
613
+ this.minuteMaxOriginal = o.minuteMax;
614
+ this.secondMaxOriginal = o.secondMax;
615
+ this.millisecMaxOriginal = o.millisecMax;
616
+ this.microsecMaxOriginal = o.microsecMax;
617
+ }
618
+
619
+ if (dp_inst.settings.timeOnly || maxDateTimeDate.getTime() === dp_date.getTime()) {
620
+ this._defaults.hourMax = maxDateTime.getHours();
621
+ if (this.hour >= this._defaults.hourMax) {
622
+ this.hour = this._defaults.hourMax;
623
+ this._defaults.minuteMax = maxDateTime.getMinutes();
624
+ if (this.minute >= this._defaults.minuteMax) {
625
+ this.minute = this._defaults.minuteMax;
626
+ this._defaults.secondMax = maxDateTime.getSeconds();
627
+ if (this.second >= this._defaults.secondMax) {
628
+ this.second = this._defaults.secondMax;
629
+ this._defaults.millisecMax = maxDateTime.getMilliseconds();
630
+ if (this.millisec >= this._defaults.millisecMax) {
631
+ this.millisec = this._defaults.millisecMax;
632
+ this._defaults.microsecMax = maxDateTime.getMicroseconds();
633
+ } else {
634
+ if (this.microsec > this._defaults.microsecMax) {
635
+ this.microsec = this._defaults.microsecMax;
636
+ }
637
+ this._defaults.microsecMax = this.microsecMaxOriginal;
638
+ }
639
+ } else {
640
+ this._defaults.millisecMax = this.millisecMaxOriginal;
641
+ this._defaults.microsecMax = this.microsecMaxOriginal;
642
+ }
643
+ } else {
644
+ this._defaults.secondMax = this.secondMaxOriginal;
645
+ this._defaults.millisecMax = this.millisecMaxOriginal;
646
+ this._defaults.microsecMax = this.microsecMaxOriginal;
647
+ }
648
+ } else {
649
+ this._defaults.minuteMax = this.minuteMaxOriginal;
650
+ this._defaults.secondMax = this.secondMaxOriginal;
651
+ this._defaults.millisecMax = this.millisecMaxOriginal;
652
+ this._defaults.microsecMax = this.microsecMaxOriginal;
653
+ }
654
+ } else {
655
+ this._defaults.hourMax = this.hourMaxOriginal;
656
+ this._defaults.minuteMax = this.minuteMaxOriginal;
657
+ this._defaults.secondMax = this.secondMaxOriginal;
658
+ this._defaults.millisecMax = this.millisecMaxOriginal;
659
+ this._defaults.microsecMax = this.microsecMaxOriginal;
660
+ }
661
+ }
662
+
663
+ if (adjustSliders !== undefined && adjustSliders === true) {
664
+ var hourMax = parseInt((this._defaults.hourMax - ((this._defaults.hourMax - this._defaults.hourMin) % this._defaults.stepHour)), 10),
665
+ minMax = parseInt((this._defaults.minuteMax - ((this._defaults.minuteMax - this._defaults.minuteMin) % this._defaults.stepMinute)), 10),
666
+ secMax = parseInt((this._defaults.secondMax - ((this._defaults.secondMax - this._defaults.secondMin) % this._defaults.stepSecond)), 10),
667
+ millisecMax = parseInt((this._defaults.millisecMax - ((this._defaults.millisecMax - this._defaults.millisecMin) % this._defaults.stepMillisec)), 10),
668
+ microsecMax = parseInt((this._defaults.microsecMax - ((this._defaults.microsecMax - this._defaults.microsecMin) % this._defaults.stepMicrosec)), 10);
669
+
670
+ if (this.hour_slider) {
671
+ this.control.options(this, this.hour_slider, 'hour', { min: this._defaults.hourMin, max: hourMax });
672
+ this.control.value(this, this.hour_slider, 'hour', this.hour - (this.hour % this._defaults.stepHour));
673
+ }
674
+ if (this.minute_slider) {
675
+ this.control.options(this, this.minute_slider, 'minute', { min: this._defaults.minuteMin, max: minMax });
676
+ this.control.value(this, this.minute_slider, 'minute', this.minute - (this.minute % this._defaults.stepMinute));
677
+ }
678
+ if (this.second_slider) {
679
+ this.control.options(this, this.second_slider, 'second', { min: this._defaults.secondMin, max: secMax });
680
+ this.control.value(this, this.second_slider, 'second', this.second - (this.second % this._defaults.stepSecond));
681
+ }
682
+ if (this.millisec_slider) {
683
+ this.control.options(this, this.millisec_slider, 'millisec', { min: this._defaults.millisecMin, max: millisecMax });
684
+ this.control.value(this, this.millisec_slider, 'millisec', this.millisec - (this.millisec % this._defaults.stepMillisec));
685
+ }
686
+ if (this.microsec_slider) {
687
+ this.control.options(this, this.microsec_slider, 'microsec', { min: this._defaults.microsecMin, max: microsecMax });
688
+ this.control.value(this, this.microsec_slider, 'microsec', this.microsec - (this.microsec % this._defaults.stepMicrosec));
689
+ }
690
+ }
691
+
692
+ },
693
+
694
+ /*
695
+ * when a slider moves, set the internal time...
696
+ * on time change is also called when the time is updated in the text field
697
+ */
698
+ _onTimeChange: function () {
699
+ if (!this._defaults.showTimepicker) {
700
+ return;
701
+ }
702
+ var hour = (this.hour_slider) ? this.control.value(this, this.hour_slider, 'hour') : false,
703
+ minute = (this.minute_slider) ? this.control.value(this, this.minute_slider, 'minute') : false,
704
+ second = (this.second_slider) ? this.control.value(this, this.second_slider, 'second') : false,
705
+ millisec = (this.millisec_slider) ? this.control.value(this, this.millisec_slider, 'millisec') : false,
706
+ microsec = (this.microsec_slider) ? this.control.value(this, this.microsec_slider, 'microsec') : false,
707
+ timezone = (this.timezone_select) ? this.timezone_select.val() : false,
708
+ o = this._defaults,
709
+ pickerTimeFormat = o.pickerTimeFormat || o.timeFormat,
710
+ pickerTimeSuffix = o.pickerTimeSuffix || o.timeSuffix;
711
+
712
+ if (typeof(hour) === 'object') {
713
+ hour = false;
714
+ }
715
+ if (typeof(minute) === 'object') {
716
+ minute = false;
717
+ }
718
+ if (typeof(second) === 'object') {
719
+ second = false;
720
+ }
721
+ if (typeof(millisec) === 'object') {
722
+ millisec = false;
723
+ }
724
+ if (typeof(microsec) === 'object') {
725
+ microsec = false;
726
+ }
727
+ if (typeof(timezone) === 'object') {
728
+ timezone = false;
729
+ }
730
+
731
+ if (hour !== false) {
732
+ hour = parseInt(hour, 10);
733
+ }
734
+ if (minute !== false) {
735
+ minute = parseInt(minute, 10);
736
+ }
737
+ if (second !== false) {
738
+ second = parseInt(second, 10);
739
+ }
740
+ if (millisec !== false) {
741
+ millisec = parseInt(millisec, 10);
742
+ }
743
+ if (microsec !== false) {
744
+ microsec = parseInt(microsec, 10);
745
+ }
746
+
747
+ var ampm = o[hour < 12 ? 'amNames' : 'pmNames'][0];
748
+
749
+ // If the update was done in the input field, the input field should not be updated.
750
+ // If the update was done using the sliders, update the input field.
751
+ var hasChanged = (hour !== this.hour || minute !== this.minute || second !== this.second || millisec !== this.millisec || microsec !== this.microsec ||
752
+ (this.ampm.length > 0 && (hour < 12) !== ($.inArray(this.ampm.toUpperCase(), this.amNames) !== -1)) || (this.timezone !== null && timezone !== this.timezone));
753
+
754
+ if (hasChanged) {
755
+
756
+ if (hour !== false) {
757
+ this.hour = hour;
758
+ }
759
+ if (minute !== false) {
760
+ this.minute = minute;
761
+ }
762
+ if (second !== false) {
763
+ this.second = second;
764
+ }
765
+ if (millisec !== false) {
766
+ this.millisec = millisec;
767
+ }
768
+ if (microsec !== false) {
769
+ this.microsec = microsec;
770
+ }
771
+ if (timezone !== false) {
772
+ this.timezone = timezone;
773
+ }
774
+
775
+ if (!this.inst) {
776
+ this.inst = $.datepicker._getInst(this.$input[0]);
777
+ }
778
+
779
+ this._limitMinMaxDateTime(this.inst, true);
780
+ }
781
+ if (this.support.ampm) {
782
+ this.ampm = ampm;
783
+ }
784
+
785
+ // Updates the time within the timepicker
786
+ this.formattedTime = $.datepicker.formatTime(o.timeFormat, this, o);
787
+ if (this.$timeObj) {
788
+ if (pickerTimeFormat === o.timeFormat) {
789
+ this.$timeObj.text(this.formattedTime + pickerTimeSuffix);
790
+ }
791
+ else {
792
+ this.$timeObj.text($.datepicker.formatTime(pickerTimeFormat, this, o) + pickerTimeSuffix);
793
+ }
794
+ }
795
+
796
+ this.timeDefined = true;
797
+ if (hasChanged) {
798
+ this._updateDateTime();
799
+ }
800
+ },
801
+
802
+ /*
803
+ * call custom onSelect.
804
+ * bind to sliders slidestop, and grid click.
805
+ */
806
+ _onSelectHandler: function () {
807
+ var onSelect = this._defaults.onSelect || this.inst.settings.onSelect;
808
+ var inputEl = this.$input ? this.$input[0] : null;
809
+ if (onSelect && inputEl) {
810
+ onSelect.apply(inputEl, [this.formattedDateTime, this]);
811
+ }
812
+ },
813
+
814
+ /*
815
+ * update our input with the new date time..
816
+ */
817
+ _updateDateTime: function (dp_inst) {
818
+ dp_inst = this.inst || dp_inst;
819
+ var dtTmp = (dp_inst.currentYear > 0?
820
+ new Date(dp_inst.currentYear, dp_inst.currentMonth, dp_inst.currentDay) :
821
+ new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay)),
822
+ dt = $.datepicker._daylightSavingAdjust(dtTmp),
823
+ //dt = $.datepicker._daylightSavingAdjust(new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay)),
824
+ //dt = $.datepicker._daylightSavingAdjust(new Date(dp_inst.currentYear, dp_inst.currentMonth, dp_inst.currentDay)),
825
+ dateFmt = $.datepicker._get(dp_inst, 'dateFormat'),
826
+ formatCfg = $.datepicker._getFormatConfig(dp_inst),
827
+ timeAvailable = dt !== null && this.timeDefined;
828
+ this.formattedDate = $.datepicker.formatDate(dateFmt, (dt === null ? new Date() : dt), formatCfg);
829
+ var formattedDateTime = this.formattedDate;
830
+
831
+ // if a slider was changed but datepicker doesn't have a value yet, set it
832
+ if (dp_inst.lastVa === "") {
833
+ dp_inst.currentYear = dp_inst.selectedYear;
834
+ dp_inst.currentMonth = dp_inst.selectedMonth;
835
+ dp_inst.currentDay = dp_inst.selectedDay;
836
+ }
837
+
838
+ /*
839
+ * remove following lines to force every changes in date picker to change the input value
840
+ * Bug descriptions: when an input field has a default value, and click on the field to pop up the date picker.
841
+ * If the user manually empty the value in the input field, the date picker will never change selected value.
842
+ */
843
+ //if (dp_inst.lastVal !== undefined && (dp_inst.lastVal.length > 0 && this.$input.val().length === 0)) {
844
+ // return;
845
+ //}
846
+
847
+ if (this._defaults.timeOnly === true) {
848
+ formattedDateTime = this.formattedTime;
849
+ } else if (this._defaults.timeOnly !== true && (this._defaults.alwaysSetTime || timeAvailable)) {
850
+ formattedDateTime += this._defaults.separator + this.formattedTime + this._defaults.timeSuffix;
851
+ }
852
+
853
+ this.formattedDateTime = formattedDateTime;
854
+
855
+ if (!this._defaults.showTimepicker) {
856
+ this.$input.val(this.formattedDate);
857
+ } else if (this.$altInput && this._defaults.timeOnly === false && this._defaults.altFieldTimeOnly === true) {
858
+ this.$altInput.val(this.formattedTime);
859
+ this.$input.val(this.formattedDate);
860
+ } else if (this.$altInput) {
861
+ this.$input.val(formattedDateTime);
862
+ var altFormattedDateTime = '',
863
+ altSeparator = this._defaults.altSeparator ? this._defaults.altSeparator : this._defaults.separator,
864
+ altTimeSuffix = this._defaults.altTimeSuffix ? this._defaults.altTimeSuffix : this._defaults.timeSuffix;
865
+
866
+ if (!this._defaults.timeOnly) {
867
+ if (this._defaults.altFormat) {
868
+ altFormattedDateTime = $.datepicker.formatDate(this._defaults.altFormat, (dt === null ? new Date() : dt), formatCfg);
869
+ }
870
+ else {
871
+ altFormattedDateTime = this.formattedDate;
872
+ }
873
+
874
+ if (altFormattedDateTime) {
875
+ altFormattedDateTime += altSeparator;
876
+ }
877
+ }
878
+
879
+ if (this._defaults.altTimeFormat) {
880
+ altFormattedDateTime += $.datepicker.formatTime(this._defaults.altTimeFormat, this, this._defaults) + altTimeSuffix;
881
+ }
882
+ else {
883
+ altFormattedDateTime += this.formattedTime + altTimeSuffix;
884
+ }
885
+ this.$altInput.val(altFormattedDateTime);
886
+ } else {
887
+ this.$input.val(formattedDateTime);
888
+ }
889
+
890
+ this.$input.trigger("change");
891
+ },
892
+
893
+ _onFocus: function () {
894
+ if (!this.$input.val() && this._defaults.defaultValue) {
895
+ this.$input.val(this._defaults.defaultValue);
896
+ var inst = $.datepicker._getInst(this.$input.get(0)),
897
+ tp_inst = $.datepicker._get(inst, 'timepicker');
898
+ if (tp_inst) {
899
+ if (tp_inst._defaults.timeOnly && (inst.input.val() !== inst.lastVal)) {
900
+ try {
901
+ $.datepicker._updateDatepicker(inst);
902
+ } catch (err) {
903
+ $.timepicker.log(err);
904
+ }
905
+ }
906
+ }
907
+ }
908
+ },
909
+
910
+ /*
911
+ * Small abstraction to control types
912
+ * We can add more, just be sure to follow the pattern: create, options, value
913
+ */
914
+ _controls: {
915
+ // slider methods
916
+ slider: {
917
+ create: function (tp_inst, obj, unit, val, min, max, step) {
918
+ var rtl = tp_inst._defaults.isRTL; // if rtl go -60->0 instead of 0->60
919
+ return obj.prop('slide', null).slider({
920
+ orientation: "horizontal",
921
+ value: rtl ? val * -1 : val,
922
+ min: rtl ? max * -1 : min,
923
+ max: rtl ? min * -1 : max,
924
+ step: step,
925
+ slide: function (event, ui) {
926
+ tp_inst.control.value(tp_inst, $(this), unit, rtl ? ui.value * -1 : ui.value);
927
+ tp_inst._onTimeChange();
928
+ },
929
+ stop: function (event, ui) {
930
+ tp_inst._onSelectHandler();
931
+ }
932
+ });
933
+ },
934
+ options: function (tp_inst, obj, unit, opts, val) {
935
+ if (tp_inst._defaults.isRTL) {
936
+ if (typeof(opts) === 'string') {
937
+ if (opts === 'min' || opts === 'max') {
938
+ if (val !== undefined) {
939
+ return obj.slider(opts, val * -1);
940
+ }
941
+ return Math.abs(obj.slider(opts));
942
+ }
943
+ return obj.slider(opts);
944
+ }
945
+ var min = opts.min,
946
+ max = opts.max;
947
+ opts.min = opts.max = null;
948
+ if (min !== undefined) {
949
+ opts.max = min * -1;
950
+ }
951
+ if (max !== undefined) {
952
+ opts.min = max * -1;
953
+ }
954
+ return obj.slider(opts);
955
+ }
956
+ if (typeof(opts) === 'string' && val !== undefined) {
957
+ return obj.slider(opts, val);
958
+ }
959
+ return obj.slider(opts);
960
+ },
961
+ value: function (tp_inst, obj, unit, val) {
962
+ if (tp_inst._defaults.isRTL) {
963
+ if (val !== undefined) {
964
+ return obj.slider('value', val * -1);
965
+ }
966
+ return Math.abs(obj.slider('value'));
967
+ }
968
+ if (val !== undefined) {
969
+ return obj.slider('value', val);
970
+ }
971
+ return obj.slider('value');
972
+ }
973
+ },
974
+ // select methods
975
+ select: {
976
+ create: function (tp_inst, obj, unit, val, min, max, step) {
977
+ var sel = '<select class="ui-timepicker-select" data-unit="' + unit + '" data-min="' + min + '" data-max="' + max + '" data-step="' + step + '">',
978
+ format = tp_inst._defaults.pickerTimeFormat || tp_inst._defaults.timeFormat;
979
+
980
+ for (var i = min; i <= max; i += step) {
981
+ sel += '<option value="' + i + '"' + (i === val ? ' selected' : '') + '>';
982
+ if (unit === 'hour') {
983
+ sel += $.datepicker.formatTime($.trim(format.replace(/[^ht ]/ig, '')), {hour: i}, tp_inst._defaults);
984
+ }
985
+ else if (unit === 'millisec' || unit === 'microsec' || i >= 10) { sel += i; }
986
+ else {sel += '0' + i.toString(); }
987
+ sel += '</option>';
988
+ }
989
+ sel += '</select>';
990
+
991
+ obj.children('select').remove();
992
+
993
+ $(sel).appendTo(obj).change(function (e) {
994
+ tp_inst._onTimeChange();
995
+ tp_inst._onSelectHandler();
996
+ });
997
+
998
+ return obj;
999
+ },
1000
+ options: function (tp_inst, obj, unit, opts, val) {
1001
+ var o = {},
1002
+ $t = obj.children('select');
1003
+ if (typeof(opts) === 'string') {
1004
+ if (val === undefined) {
1005
+ return $t.data(opts);
1006
+ }
1007
+ o[opts] = val;
1008
+ }
1009
+ else { o = opts; }
1010
+ return tp_inst.control.create(tp_inst, obj, $t.data('unit'), $t.val(), o.min || $t.data('min'), o.max || $t.data('max'), o.step || $t.data('step'));
1011
+ },
1012
+ value: function (tp_inst, obj, unit, val) {
1013
+ var $t = obj.children('select');
1014
+ if (val !== undefined) {
1015
+ return $t.val(val);
1016
+ }
1017
+ return $t.val();
1018
+ }
1019
+ }
1020
+ } // end _controls
1021
+
1022
+ });
1023
+
1024
+ $.fn.extend({
1025
+ /*
1026
+ * shorthand just to use timepicker.
1027
+ */
1028
+ timepicker: function (o) {
1029
+ o = o || {};
1030
+ var tmp_args = Array.prototype.slice.call(arguments);
1031
+
1032
+ if (typeof o === 'object') {
1033
+ tmp_args[0] = $.extend(o, {
1034
+ timeOnly: true
1035
+ });
1036
+ }
1037
+
1038
+ return $(this).each(function () {
1039
+ $.fn.datetimepicker.apply($(this), tmp_args);
1040
+ });
1041
+ },
1042
+
1043
+ /*
1044
+ * extend timepicker to datepicker
1045
+ */
1046
+ datetimepicker: function (o) {
1047
+ o = o || {};
1048
+ var tmp_args = arguments;
1049
+
1050
+ if (typeof(o) === 'string') {
1051
+ if (o === 'getDate') {
1052
+ return $.fn.datepicker.apply($(this[0]), tmp_args);
1053
+ } else {
1054
+ return this.each(function () {
1055
+ var $t = $(this);
1056
+ $t.datepicker.apply($t, tmp_args);
1057
+ });
1058
+ }
1059
+ } else {
1060
+ return this.each(function () {
1061
+ var $t = $(this);
1062
+ $t.datepicker($.timepicker._newInst($t, o)._defaults);
1063
+ });
1064
+ }
1065
+ }
1066
+ });
1067
+
1068
+ /*
1069
+ * Public Utility to parse date and time
1070
+ */
1071
+ $.datepicker.parseDateTime = function (dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings) {
1072
+ var parseRes = parseDateTimeInternal(dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings);
1073
+ if (parseRes.timeObj) {
1074
+ var t = parseRes.timeObj;
1075
+ parseRes.date.setHours(t.hour, t.minute, t.second, t.millisec);
1076
+ parseRes.date.setMicroseconds(t.microsec);
1077
+ }
1078
+
1079
+ return parseRes.date;
1080
+ };
1081
+
1082
+ /*
1083
+ * Public utility to parse time
1084
+ */
1085
+ $.datepicker.parseTime = function (timeFormat, timeString, options) {
1086
+ var o = extendRemove(extendRemove({}, $.timepicker._defaults), options || {}),
1087
+ iso8601 = (timeFormat.replace(/\'.*?\'/g, '').indexOf('Z') !== -1);
1088
+
1089
+ // Strict parse requires the timeString to match the timeFormat exactly
1090
+ var strictParse = function (f, s, o) {
1091
+
1092
+ // pattern for standard and localized AM/PM markers
1093
+ var getPatternAmpm = function (amNames, pmNames) {
1094
+ var markers = [];
1095
+ if (amNames) {
1096
+ $.merge(markers, amNames);
1097
+ }
1098
+ if (pmNames) {
1099
+ $.merge(markers, pmNames);
1100
+ }
1101
+ markers = $.map(markers, function (val) {
1102
+ return val.replace(/[.*+?|()\[\]{}\\]/g, '\\$&');
1103
+ });
1104
+ return '(' + markers.join('|') + ')?';
1105
+ };
1106
+
1107
+ // figure out position of time elements.. cause js cant do named captures
1108
+ var getFormatPositions = function (timeFormat) {
1109
+ var finds = timeFormat.toLowerCase().match(/(h{1,2}|m{1,2}|s{1,2}|l{1}|c{1}|t{1,2}|z|'.*?')/g),
1110
+ orders = {
1111
+ h: -1,
1112
+ m: -1,
1113
+ s: -1,
1114
+ l: -1,
1115
+ c: -1,
1116
+ t: -1,
1117
+ z: -1
1118
+ };
1119
+
1120
+ if (finds) {
1121
+ for (var i = 0; i < finds.length; i++) {
1122
+ if (orders[finds[i].toString().charAt(0)] === -1) {
1123
+ orders[finds[i].toString().charAt(0)] = i + 1;
1124
+ }
1125
+ }
1126
+ }
1127
+ return orders;
1128
+ };
1129
+
1130
+ var regstr = '^' + f.toString()
1131
+ .replace(/([hH]{1,2}|mm?|ss?|[tT]{1,2}|[zZ]|[lc]|'.*?')/g, function (match) {
1132
+ var ml = match.length;
1133
+ switch (match.charAt(0).toLowerCase()) {
1134
+ case 'h':
1135
+ return ml === 1 ? '(\\d?\\d)' : '(\\d{' + ml + '})';
1136
+ case 'm':
1137
+ return ml === 1 ? '(\\d?\\d)' : '(\\d{' + ml + '})';
1138
+ case 's':
1139
+ return ml === 1 ? '(\\d?\\d)' : '(\\d{' + ml + '})';
1140
+ case 'l':
1141
+ return '(\\d?\\d?\\d)';
1142
+ case 'c':
1143
+ return '(\\d?\\d?\\d)';
1144
+ case 'z':
1145
+ return '(z|[-+]\\d\\d:?\\d\\d|\\S+)?';
1146
+ case 't':
1147
+ return getPatternAmpm(o.amNames, o.pmNames);
1148
+ default: // literal escaped in quotes
1149
+ return '(' + match.replace(/\'/g, "").replace(/(\.|\$|\^|\\|\/|\(|\)|\[|\]|\?|\+|\*)/g, function (m) { return "\\" + m; }) + ')?';
1150
+ }
1151
+ })
1152
+ .replace(/\s/g, '\\s?') +
1153
+ o.timeSuffix + '$',
1154
+ order = getFormatPositions(f),
1155
+ ampm = '',
1156
+ treg;
1157
+
1158
+ treg = s.match(new RegExp(regstr, 'i'));
1159
+
1160
+ var resTime = {
1161
+ hour: 0,
1162
+ minute: 0,
1163
+ second: 0,
1164
+ millisec: 0,
1165
+ microsec: 0
1166
+ };
1167
+
1168
+ if (treg) {
1169
+ if (order.t !== -1) {
1170
+ if (treg[order.t] === undefined || treg[order.t].length === 0) {
1171
+ ampm = '';
1172
+ resTime.ampm = '';
1173
+ } else {
1174
+ ampm = $.inArray(treg[order.t].toUpperCase(), o.amNames) !== -1 ? 'AM' : 'PM';
1175
+ resTime.ampm = o[ampm === 'AM' ? 'amNames' : 'pmNames'][0];
1176
+ }
1177
+ }
1178
+
1179
+ if (order.h !== -1) {
1180
+ if (ampm === 'AM' && treg[order.h] === '12') {
1181
+ resTime.hour = 0; // 12am = 0 hour
1182
+ } else {
1183
+ if (ampm === 'PM' && treg[order.h] !== '12') {
1184
+ resTime.hour = parseInt(treg[order.h], 10) + 12; // 12pm = 12 hour, any other pm = hour + 12
1185
+ } else {
1186
+ resTime.hour = Number(treg[order.h]);
1187
+ }
1188
+ }
1189
+ }
1190
+
1191
+ if (order.m !== -1) {
1192
+ resTime.minute = Number(treg[order.m]);
1193
+ }
1194
+ if (order.s !== -1) {
1195
+ resTime.second = Number(treg[order.s]);
1196
+ }
1197
+ if (order.l !== -1) {
1198
+ resTime.millisec = Number(treg[order.l]);
1199
+ }
1200
+ if (order.c !== -1) {
1201
+ resTime.microsec = Number(treg[order.c]);
1202
+ }
1203
+ if (order.z !== -1 && treg[order.z] !== undefined) {
1204
+ resTime.timezone = $.timepicker.timezoneOffsetNumber(treg[order.z]);
1205
+ }
1206
+
1207
+
1208
+ return resTime;
1209
+ }
1210
+ return false;
1211
+ };// end strictParse
1212
+
1213
+ // First try JS Date, if that fails, use strictParse
1214
+ var looseParse = function (f, s, o) {
1215
+ try {
1216
+ var d = new Date('2012-01-01 ' + s);
1217
+ if (isNaN(d.getTime())) {
1218
+ d = new Date('2012-01-01T' + s);
1219
+ if (isNaN(d.getTime())) {
1220
+ d = new Date('01/01/2012 ' + s);
1221
+ if (isNaN(d.getTime())) {
1222
+ throw "Unable to parse time with native Date: " + s;
1223
+ }
1224
+ }
1225
+ }
1226
+
1227
+ return {
1228
+ hour: d.getHours(),
1229
+ minute: d.getMinutes(),
1230
+ second: d.getSeconds(),
1231
+ millisec: d.getMilliseconds(),
1232
+ microsec: d.getMicroseconds(),
1233
+ timezone: d.getTimezoneOffset() * -1
1234
+ };
1235
+ }
1236
+ catch (err) {
1237
+ try {
1238
+ return strictParse(f, s, o);
1239
+ }
1240
+ catch (err2) {
1241
+ $.timepicker.log("Unable to parse \ntimeString: " + s + "\ntimeFormat: " + f);
1242
+ }
1243
+ }
1244
+ return false;
1245
+ }; // end looseParse
1246
+
1247
+ if (typeof o.parse === "function") {
1248
+ return o.parse(timeFormat, timeString, o);
1249
+ }
1250
+ if (o.parse === 'loose') {
1251
+ return looseParse(timeFormat, timeString, o);
1252
+ }
1253
+ return strictParse(timeFormat, timeString, o);
1254
+ };
1255
+
1256
+ /**
1257
+ * Public utility to format the time
1258
+ * @param {string} format format of the time
1259
+ * @param {Object} time Object not a Date for timezones
1260
+ * @param {Object} [options] essentially the regional[].. amNames, pmNames, ampm
1261
+ * @returns {string} the formatted time
1262
+ */
1263
+ $.datepicker.formatTime = function (format, time, options) {
1264
+ options = options || {};
1265
+ options = $.extend({}, $.timepicker._defaults, options);
1266
+ time = $.extend({
1267
+ hour: 0,
1268
+ minute: 0,
1269
+ second: 0,
1270
+ millisec: 0,
1271
+ microsec: 0,
1272
+ timezone: null
1273
+ }, time);
1274
+
1275
+ var tmptime = format,
1276
+ ampmName = options.amNames[0],
1277
+ hour = parseInt(time.hour, 10);
1278
+
1279
+ if (hour > 11) {
1280
+ ampmName = options.pmNames[0];
1281
+ }
1282
+
1283
+ tmptime = tmptime.replace(/(?:HH?|hh?|mm?|ss?|[tT]{1,2}|[zZ]|[lc]|'.*?')/g, function (match) {
1284
+ switch (match) {
1285
+ case 'HH':
1286
+ return ('0' + hour).slice(-2);
1287
+ case 'H':
1288
+ return hour;
1289
+ case 'hh':
1290
+ return ('0' + convert24to12(hour)).slice(-2);
1291
+ case 'h':
1292
+ return convert24to12(hour);
1293
+ case 'mm':
1294
+ return ('0' + time.minute).slice(-2);
1295
+ case 'm':
1296
+ return time.minute;
1297
+ case 'ss':
1298
+ return ('0' + time.second).slice(-2);
1299
+ case 's':
1300
+ return time.second;
1301
+ case 'l':
1302
+ return ('00' + time.millisec).slice(-3);
1303
+ case 'c':
1304
+ return ('00' + time.microsec).slice(-3);
1305
+ case 'z':
1306
+ return $.timepicker.timezoneOffsetString(time.timezone === null ? options.timezone : time.timezone, false);
1307
+ case 'Z':
1308
+ return $.timepicker.timezoneOffsetString(time.timezone === null ? options.timezone : time.timezone, true);
1309
+ case 'T':
1310
+ return ampmName.charAt(0).toUpperCase();
1311
+ case 'TT':
1312
+ return ampmName.toUpperCase();
1313
+ case 't':
1314
+ return ampmName.charAt(0).toLowerCase();
1315
+ case 'tt':
1316
+ return ampmName.toLowerCase();
1317
+ default:
1318
+ return match.replace(/'/g, "");
1319
+ }
1320
+ });
1321
+
1322
+ return tmptime;
1323
+ };
1324
+
1325
+ /*
1326
+ * the bad hack :/ override datepicker so it doesn't close on select
1327
+ // inspired: http://stackoverflow.com/questions/1252512/jquery-datepicker-prevent-closing-picker-when-clicking-a-date/1762378#1762378
1328
+ */
1329
+ $.datepicker._base_selectDate = $.datepicker._selectDate;
1330
+ $.datepicker._selectDate = function (id, dateStr) {
1331
+ var inst = this._getInst($(id)[0]),
1332
+ tp_inst = this._get(inst, 'timepicker');
1333
+
1334
+ if (tp_inst) {
1335
+ tp_inst._limitMinMaxDateTime(inst, true);
1336
+ inst.inline = inst.stay_open = true;
1337
+ //This way the onSelect handler called from calendarpicker get the full dateTime
1338
+ this._base_selectDate(id, dateStr);
1339
+ inst.inline = inst.stay_open = false;
1340
+ this._notifyChange(inst);
1341
+ this._updateDatepicker(inst);
1342
+ } else {
1343
+ this._base_selectDate(id, dateStr);
1344
+ }
1345
+ };
1346
+
1347
+ /*
1348
+ * second bad hack :/ override datepicker so it triggers an event when changing the input field
1349
+ * and does not redraw the datepicker on every selectDate event
1350
+ */
1351
+ $.datepicker._base_updateDatepicker = $.datepicker._updateDatepicker;
1352
+ $.datepicker._updateDatepicker = function (inst) {
1353
+
1354
+ // don't popup the datepicker if there is another instance already opened
1355
+ var input = inst.input[0];
1356
+ if ($.datepicker._curInst && $.datepicker._curInst !== inst && $.datepicker._datepickerShowing && $.datepicker._lastInput !== input) {
1357
+ return;
1358
+ }
1359
+
1360
+ if (typeof(inst.stay_open) !== 'boolean' || inst.stay_open === false) {
1361
+
1362
+ this._base_updateDatepicker(inst);
1363
+
1364
+ // Reload the time control when changing something in the input text field.
1365
+ var tp_inst = this._get(inst, 'timepicker');
1366
+ if (tp_inst) {
1367
+ tp_inst._addTimePicker(inst);
1368
+ }
1369
+ }
1370
+ };
1371
+
1372
+ /*
1373
+ * third bad hack :/ override datepicker so it allows spaces and colon in the input field
1374
+ */
1375
+ $.datepicker._base_doKeyPress = $.datepicker._doKeyPress;
1376
+ $.datepicker._doKeyPress = function (event) {
1377
+ var inst = $.datepicker._getInst(event.target),
1378
+ tp_inst = $.datepicker._get(inst, 'timepicker');
1379
+
1380
+ if (tp_inst) {
1381
+ if ($.datepicker._get(inst, 'constrainInput')) {
1382
+ var ampm = tp_inst.support.ampm,
1383
+ tz = tp_inst._defaults.showTimezone !== null ? tp_inst._defaults.showTimezone : tp_inst.support.timezone,
1384
+ dateChars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat')),
1385
+ datetimeChars = tp_inst._defaults.timeFormat.toString()
1386
+ .replace(/[hms]/g, '')
1387
+ .replace(/TT/g, ampm ? 'APM' : '')
1388
+ .replace(/Tt/g, ampm ? 'AaPpMm' : '')
1389
+ .replace(/tT/g, ampm ? 'AaPpMm' : '')
1390
+ .replace(/T/g, ampm ? 'AP' : '')
1391
+ .replace(/tt/g, ampm ? 'apm' : '')
1392
+ .replace(/t/g, ampm ? 'ap' : '') +
1393
+ " " + tp_inst._defaults.separator +
1394
+ tp_inst._defaults.timeSuffix +
1395
+ (tz ? tp_inst._defaults.timezoneList.join('') : '') +
1396
+ (tp_inst._defaults.amNames.join('')) + (tp_inst._defaults.pmNames.join('')) +
1397
+ dateChars,
1398
+ chr = String.fromCharCode(event.charCode === undefined ? event.keyCode : event.charCode);
1399
+ return event.ctrlKey || (chr < ' ' || !dateChars || datetimeChars.indexOf(chr) > -1);
1400
+ }
1401
+ }
1402
+
1403
+ return $.datepicker._base_doKeyPress(event);
1404
+ };
1405
+
1406
+ /*
1407
+ * Fourth bad hack :/ override _updateAlternate function used in inline mode to init altField
1408
+ * Update any alternate field to synchronise with the main field.
1409
+ */
1410
+ $.datepicker._base_updateAlternate = $.datepicker._updateAlternate;
1411
+ $.datepicker._updateAlternate = function (inst) {
1412
+ var tp_inst = this._get(inst, 'timepicker');
1413
+ if (tp_inst) {
1414
+ var altField = tp_inst._defaults.altField;
1415
+ if (altField) { // update alternate field too
1416
+ var altFormat = tp_inst._defaults.altFormat || tp_inst._defaults.dateFormat,
1417
+ date = this._getDate(inst),
1418
+ formatCfg = $.datepicker._getFormatConfig(inst),
1419
+ altFormattedDateTime = '',
1420
+ altSeparator = tp_inst._defaults.altSeparator ? tp_inst._defaults.altSeparator : tp_inst._defaults.separator,
1421
+ altTimeSuffix = tp_inst._defaults.altTimeSuffix ? tp_inst._defaults.altTimeSuffix : tp_inst._defaults.timeSuffix,
1422
+ altTimeFormat = tp_inst._defaults.altTimeFormat !== null ? tp_inst._defaults.altTimeFormat : tp_inst._defaults.timeFormat;
1423
+
1424
+ altFormattedDateTime += $.datepicker.formatTime(altTimeFormat, tp_inst, tp_inst._defaults) + altTimeSuffix;
1425
+ if (!tp_inst._defaults.timeOnly && !tp_inst._defaults.altFieldTimeOnly && date !== null) {
1426
+ if (tp_inst._defaults.altFormat) {
1427
+ altFormattedDateTime = $.datepicker.formatDate(tp_inst._defaults.altFormat, date, formatCfg) + altSeparator + altFormattedDateTime;
1428
+ }
1429
+ else {
1430
+ altFormattedDateTime = tp_inst.formattedDate + altSeparator + altFormattedDateTime;
1431
+ }
1432
+ }
1433
+ $(altField).val(altFormattedDateTime);
1434
+ }
1435
+ }
1436
+ else {
1437
+ $.datepicker._base_updateAlternate(inst);
1438
+ }
1439
+ };
1440
+
1441
+ /*
1442
+ * Override key up event to sync manual input changes.
1443
+ */
1444
+ $.datepicker._base_doKeyUp = $.datepicker._doKeyUp;
1445
+ $.datepicker._doKeyUp = function (event) {
1446
+ var inst = $.datepicker._getInst(event.target),
1447
+ tp_inst = $.datepicker._get(inst, 'timepicker');
1448
+
1449
+ if (tp_inst) {
1450
+ if (tp_inst._defaults.timeOnly && (inst.input.val() !== inst.lastVal)) {
1451
+ try {
1452
+ $.datepicker._updateDatepicker(inst);
1453
+ } catch (err) {
1454
+ $.timepicker.log(err);
1455
+ }
1456
+ }
1457
+ }
1458
+
1459
+ return $.datepicker._base_doKeyUp(event);
1460
+ };
1461
+
1462
+ /*
1463
+ * override "Today" button to also grab the time.
1464
+ */
1465
+ $.datepicker._base_gotoToday = $.datepicker._gotoToday;
1466
+ $.datepicker._gotoToday = function (id) {
1467
+ var inst = this._getInst($(id)[0]),
1468
+ $dp = inst.dpDiv;
1469
+ this._base_gotoToday(id);
1470
+ var tp_inst = this._get(inst, 'timepicker');
1471
+ selectLocalTimezone(tp_inst);
1472
+ var now = new Date();
1473
+ this._setTime(inst, now);
1474
+ $('.ui-datepicker-today', $dp).click();
1475
+ };
1476
+
1477
+ /*
1478
+ * Disable & enable the Time in the datetimepicker
1479
+ */
1480
+ $.datepicker._disableTimepickerDatepicker = function (target) {
1481
+ var inst = this._getInst(target);
1482
+ if (!inst) {
1483
+ return;
1484
+ }
1485
+
1486
+ var tp_inst = this._get(inst, 'timepicker');
1487
+ $(target).datepicker('getDate'); // Init selected[Year|Month|Day]
1488
+ if (tp_inst) {
1489
+ inst.settings.showTimepicker = false;
1490
+ tp_inst._defaults.showTimepicker = false;
1491
+ tp_inst._updateDateTime(inst);
1492
+ }
1493
+ };
1494
+
1495
+ $.datepicker._enableTimepickerDatepicker = function (target) {
1496
+ var inst = this._getInst(target);
1497
+ if (!inst) {
1498
+ return;
1499
+ }
1500
+
1501
+ var tp_inst = this._get(inst, 'timepicker');
1502
+ $(target).datepicker('getDate'); // Init selected[Year|Month|Day]
1503
+ if (tp_inst) {
1504
+ inst.settings.showTimepicker = true;
1505
+ tp_inst._defaults.showTimepicker = true;
1506
+ tp_inst._addTimePicker(inst); // Could be disabled on page load
1507
+ tp_inst._updateDateTime(inst);
1508
+ }
1509
+ };
1510
+
1511
+ /*
1512
+ * Create our own set time function
1513
+ */
1514
+ $.datepicker._setTime = function (inst, date) {
1515
+ var tp_inst = this._get(inst, 'timepicker');
1516
+ if (tp_inst) {
1517
+ var defaults = tp_inst._defaults;
1518
+
1519
+ // calling _setTime with no date sets time to defaults
1520
+ tp_inst.hour = date ? date.getHours() : defaults.hour;
1521
+ tp_inst.minute = date ? date.getMinutes() : defaults.minute;
1522
+ tp_inst.second = date ? date.getSeconds() : defaults.second;
1523
+ tp_inst.millisec = date ? date.getMilliseconds() : defaults.millisec;
1524
+ tp_inst.microsec = date ? date.getMicroseconds() : defaults.microsec;
1525
+
1526
+ //check if within min/max times..
1527
+ tp_inst._limitMinMaxDateTime(inst, true);
1528
+
1529
+ tp_inst._onTimeChange();
1530
+ tp_inst._updateDateTime(inst);
1531
+ }
1532
+ };
1533
+
1534
+ /*
1535
+ * Create new public method to set only time, callable as $().datepicker('setTime', date)
1536
+ */
1537
+ $.datepicker._setTimeDatepicker = function (target, date, withDate) {
1538
+ var inst = this._getInst(target);
1539
+ if (!inst) {
1540
+ return;
1541
+ }
1542
+
1543
+ var tp_inst = this._get(inst, 'timepicker');
1544
+
1545
+ if (tp_inst) {
1546
+ this._setDateFromField(inst);
1547
+ var tp_date;
1548
+ if (date) {
1549
+ if (typeof date === "string") {
1550
+ tp_inst._parseTime(date, withDate);
1551
+ tp_date = new Date();
1552
+ tp_date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec);
1553
+ tp_date.setMicroseconds(tp_inst.microsec);
1554
+ } else {
1555
+ tp_date = new Date(date.getTime());
1556
+ tp_date.setMicroseconds(date.getMicroseconds());
1557
+ }
1558
+ if (tp_date.toString() === 'Invalid Date') {
1559
+ tp_date = undefined;
1560
+ }
1561
+ this._setTime(inst, tp_date);
1562
+ }
1563
+ }
1564
+
1565
+ };
1566
+
1567
+ /*
1568
+ * override setDate() to allow setting time too within Date object
1569
+ */
1570
+ $.datepicker._base_setDateDatepicker = $.datepicker._setDateDatepicker;
1571
+ $.datepicker._setDateDatepicker = function (target, date) {
1572
+ var inst = this._getInst(target);
1573
+ if (!inst) {
1574
+ return;
1575
+ }
1576
+
1577
+ if (typeof(date) === 'string') {
1578
+ date = new Date(date);
1579
+ if (!date.getTime()) {
1580
+ $.timepicker.log("Error creating Date object from string.");
1581
+ }
1582
+ }
1583
+
1584
+ var tp_inst = this._get(inst, 'timepicker');
1585
+ var tp_date;
1586
+ if (date instanceof Date) {
1587
+ tp_date = new Date(date.getTime());
1588
+ tp_date.setMicroseconds(date.getMicroseconds());
1589
+ } else {
1590
+ tp_date = date;
1591
+ }
1592
+
1593
+ // This is important if you are using the timezone option, javascript's Date
1594
+ // object will only return the timezone offset for the current locale, so we
1595
+ // adjust it accordingly. If not using timezone option this won't matter..
1596
+ // If a timezone is different in tp, keep the timezone as is
1597
+ if (tp_inst) {
1598
+ // look out for DST if tz wasn't specified
1599
+ if (!tp_inst.support.timezone && tp_inst._defaults.timezone === null) {
1600
+ tp_inst.timezone = tp_date.getTimezoneOffset() * -1;
1601
+ }
1602
+ date = $.timepicker.timezoneAdjust(date, tp_inst.timezone);
1603
+ tp_date = $.timepicker.timezoneAdjust(tp_date, tp_inst.timezone);
1604
+ }
1605
+
1606
+ this._updateDatepicker(inst);
1607
+ this._base_setDateDatepicker.apply(this, arguments);
1608
+ this._setTimeDatepicker(target, tp_date, true);
1609
+ };
1610
+
1611
+ /*
1612
+ * override getDate() to allow getting time too within Date object
1613
+ */
1614
+ $.datepicker._base_getDateDatepicker = $.datepicker._getDateDatepicker;
1615
+ $.datepicker._getDateDatepicker = function (target, noDefault) {
1616
+ var inst = this._getInst(target);
1617
+ if (!inst) {
1618
+ return;
1619
+ }
1620
+
1621
+ var tp_inst = this._get(inst, 'timepicker');
1622
+
1623
+ if (tp_inst) {
1624
+ // if it hasn't yet been defined, grab from field
1625
+ if (inst.lastVal === undefined) {
1626
+ this._setDateFromField(inst, noDefault);
1627
+ }
1628
+
1629
+ var date = this._getDate(inst);
1630
+ if (date && tp_inst._parseTime($(target).val(), tp_inst.timeOnly)) {
1631
+ date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec);
1632
+ date.setMicroseconds(tp_inst.microsec);
1633
+
1634
+ // This is important if you are using the timezone option, javascript's Date
1635
+ // object will only return the timezone offset for the current locale, so we
1636
+ // adjust it accordingly. If not using timezone option this won't matter..
1637
+ if (tp_inst.timezone != null) {
1638
+ // look out for DST if tz wasn't specified
1639
+ if (!tp_inst.support.timezone && tp_inst._defaults.timezone === null) {
1640
+ tp_inst.timezone = date.getTimezoneOffset() * -1;
1641
+ }
1642
+ date = $.timepicker.timezoneAdjust(date, tp_inst.timezone);
1643
+ }
1644
+ }
1645
+ return date;
1646
+ }
1647
+ return this._base_getDateDatepicker(target, noDefault);
1648
+ };
1649
+
1650
+ /*
1651
+ * override parseDate() because UI 1.8.14 throws an error about "Extra characters"
1652
+ * An option in datapicker to ignore extra format characters would be nicer.
1653
+ */
1654
+ $.datepicker._base_parseDate = $.datepicker.parseDate;
1655
+ $.datepicker.parseDate = function (format, value, settings) {
1656
+ var date;
1657
+ try {
1658
+ date = this._base_parseDate(format, value, settings);
1659
+ } catch (err) {
1660
+ // Hack! The error message ends with a colon, a space, and
1661
+ // the "extra" characters. We rely on that instead of
1662
+ // attempting to perfectly reproduce the parsing algorithm.
1663
+ if (err.indexOf(":") >= 0) {
1664
+ date = this._base_parseDate(format, value.substring(0, value.length - (err.length - err.indexOf(':') - 2)), settings);
1665
+ $.timepicker.log("Error parsing the date string: " + err + "\ndate string = " + value + "\ndate format = " + format);
1666
+ } else {
1667
+ throw err;
1668
+ }
1669
+ }
1670
+ return date;
1671
+ };
1672
+
1673
+ /*
1674
+ * override formatDate to set date with time to the input
1675
+ */
1676
+ $.datepicker._base_formatDate = $.datepicker._formatDate;
1677
+ $.datepicker._formatDate = function (inst, day, month, year) {
1678
+ var tp_inst = this._get(inst, 'timepicker');
1679
+ if (tp_inst) {
1680
+ tp_inst._updateDateTime(inst);
1681
+ return tp_inst.$input.val();
1682
+ }
1683
+ return this._base_formatDate(inst);
1684
+ };
1685
+
1686
+ /*
1687
+ * override options setter to add time to maxDate(Time) and minDate(Time). MaxDate
1688
+ */
1689
+ $.datepicker._base_optionDatepicker = $.datepicker._optionDatepicker;
1690
+ $.datepicker._optionDatepicker = function (target, name, value) {
1691
+ var inst = this._getInst(target),
1692
+ name_clone;
1693
+ if (!inst) {
1694
+ return null;
1695
+ }
1696
+
1697
+ var tp_inst = this._get(inst, 'timepicker');
1698
+ if (tp_inst) {
1699
+ var min = null,
1700
+ max = null,
1701
+ onselect = null,
1702
+ overrides = tp_inst._defaults.evnts,
1703
+ fns = {},
1704
+ prop;
1705
+ if (typeof name === 'string') { // if min/max was set with the string
1706
+ if (name === 'minDate' || name === 'minDateTime') {
1707
+ min = value;
1708
+ } else if (name === 'maxDate' || name === 'maxDateTime') {
1709
+ max = value;
1710
+ } else if (name === 'onSelect') {
1711
+ onselect = value;
1712
+ } else if (overrides.hasOwnProperty(name)) {
1713
+ if (typeof (value) === 'undefined') {
1714
+ return overrides[name];
1715
+ }
1716
+ fns[name] = value;
1717
+ name_clone = {}; //empty results in exiting function after overrides updated
1718
+ }
1719
+ } else if (typeof name === 'object') { //if min/max was set with the JSON
1720
+ if (name.minDate) {
1721
+ min = name.minDate;
1722
+ } else if (name.minDateTime) {
1723
+ min = name.minDateTime;
1724
+ } else if (name.maxDate) {
1725
+ max = name.maxDate;
1726
+ } else if (name.maxDateTime) {
1727
+ max = name.maxDateTime;
1728
+ }
1729
+ for (prop in overrides) {
1730
+ if (overrides.hasOwnProperty(prop) && name[prop]) {
1731
+ fns[prop] = name[prop];
1732
+ }
1733
+ }
1734
+ }
1735
+ for (prop in fns) {
1736
+ if (fns.hasOwnProperty(prop)) {
1737
+ overrides[prop] = fns[prop];
1738
+ if (!name_clone) { name_clone = $.extend({}, name); }
1739
+ delete name_clone[prop];
1740
+ }
1741
+ }
1742
+ if (name_clone && isEmptyObject(name_clone)) { return; }
1743
+ if (min) { //if min was set
1744
+ if (min === 0) {
1745
+ min = new Date();
1746
+ } else {
1747
+ min = new Date(min);
1748
+ }
1749
+ tp_inst._defaults.minDate = min;
1750
+ tp_inst._defaults.minDateTime = min;
1751
+ } else if (max) { //if max was set
1752
+ if (max === 0) {
1753
+ max = new Date();
1754
+ } else {
1755
+ max = new Date(max);
1756
+ }
1757
+ tp_inst._defaults.maxDate = max;
1758
+ tp_inst._defaults.maxDateTime = max;
1759
+ } else if (onselect) {
1760
+ tp_inst._defaults.onSelect = onselect;
1761
+ }
1762
+ }
1763
+ if (value === undefined) {
1764
+ return this._base_optionDatepicker.call($.datepicker, target, name);
1765
+ }
1766
+ return this._base_optionDatepicker.call($.datepicker, target, name_clone || name, value);
1767
+ };
1768
+
1769
+ /*
1770
+ * jQuery isEmptyObject does not check hasOwnProperty - if someone has added to the object prototype,
1771
+ * it will return false for all objects
1772
+ */
1773
+ var isEmptyObject = function (obj) {
1774
+ var prop;
1775
+ for (prop in obj) {
1776
+ if (obj.hasOwnProperty(prop)) {
1777
+ return false;
1778
+ }
1779
+ }
1780
+ return true;
1781
+ };
1782
+
1783
+ /*
1784
+ * jQuery extend now ignores nulls!
1785
+ */
1786
+ var extendRemove = function (target, props) {
1787
+ $.extend(target, props);
1788
+ for (var name in props) {
1789
+ if (props[name] === null || props[name] === undefined) {
1790
+ target[name] = props[name];
1791
+ }
1792
+ }
1793
+ return target;
1794
+ };
1795
+
1796
+ /*
1797
+ * Determine by the time format which units are supported
1798
+ * Returns an object of booleans for each unit
1799
+ */
1800
+ var detectSupport = function (timeFormat) {
1801
+ var tf = timeFormat.replace(/'.*?'/g, '').toLowerCase(), // removes literals
1802
+ isIn = function (f, t) { // does the format contain the token?
1803
+ return f.indexOf(t) !== -1 ? true : false;
1804
+ };
1805
+ return {
1806
+ hour: isIn(tf, 'h'),
1807
+ minute: isIn(tf, 'm'),
1808
+ second: isIn(tf, 's'),
1809
+ millisec: isIn(tf, 'l'),
1810
+ microsec: isIn(tf, 'c'),
1811
+ timezone: isIn(tf, 'z'),
1812
+ ampm: isIn(tf, 't') && isIn(timeFormat, 'h'),
1813
+ iso8601: isIn(timeFormat, 'Z')
1814
+ };
1815
+ };
1816
+
1817
+ /*
1818
+ * Converts 24 hour format into 12 hour
1819
+ * Returns 12 hour without leading 0
1820
+ */
1821
+ var convert24to12 = function (hour) {
1822
+ hour %= 12;
1823
+
1824
+ if (hour === 0) {
1825
+ hour = 12;
1826
+ }
1827
+
1828
+ return String(hour);
1829
+ };
1830
+
1831
+ var computeEffectiveSetting = function (settings, property) {
1832
+ return settings && settings[property] ? settings[property] : $.timepicker._defaults[property];
1833
+ };
1834
+
1835
+ /*
1836
+ * Splits datetime string into date and time substrings.
1837
+ * Throws exception when date can't be parsed
1838
+ * Returns {dateString: dateString, timeString: timeString}
1839
+ */
1840
+ var splitDateTime = function (dateTimeString, timeSettings) {
1841
+ // The idea is to get the number separator occurrences in datetime and the time format requested (since time has
1842
+ // fewer unknowns, mostly numbers and am/pm). We will use the time pattern to split.
1843
+ var separator = computeEffectiveSetting(timeSettings, 'separator'),
1844
+ format = computeEffectiveSetting(timeSettings, 'timeFormat'),
1845
+ timeParts = format.split(separator), // how many occurrences of separator may be in our format?
1846
+ timePartsLen = timeParts.length,
1847
+ allParts = dateTimeString.split(separator),
1848
+ allPartsLen = allParts.length;
1849
+
1850
+ if (allPartsLen > 1) {
1851
+ return {
1852
+ dateString: allParts.splice(0, allPartsLen - timePartsLen).join(separator),
1853
+ timeString: allParts.splice(0, timePartsLen).join(separator)
1854
+ };
1855
+ }
1856
+
1857
+ return {
1858
+ dateString: dateTimeString,
1859
+ timeString: ''
1860
+ };
1861
+ };
1862
+
1863
+ /*
1864
+ * Internal function to parse datetime interval
1865
+ * Returns: {date: Date, timeObj: Object}, where
1866
+ * date - parsed date without time (type Date)
1867
+ * timeObj = {hour: , minute: , second: , millisec: , microsec: } - parsed time. Optional
1868
+ */
1869
+ var parseDateTimeInternal = function (dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings) {
1870
+ var date,
1871
+ parts,
1872
+ parsedTime;
1873
+
1874
+ parts = splitDateTime(dateTimeString, timeSettings);
1875
+ date = $.datepicker._base_parseDate(dateFormat, parts.dateString, dateSettings);
1876
+
1877
+ if (parts.timeString === '') {
1878
+ return {
1879
+ date: date
1880
+ };
1881
+ }
1882
+
1883
+ parsedTime = $.datepicker.parseTime(timeFormat, parts.timeString, timeSettings);
1884
+
1885
+ if (!parsedTime) {
1886
+ throw 'Wrong time format';
1887
+ }
1888
+
1889
+ return {
1890
+ date: date,
1891
+ timeObj: parsedTime
1892
+ };
1893
+ };
1894
+
1895
+ /*
1896
+ * Internal function to set timezone_select to the local timezone
1897
+ */
1898
+ var selectLocalTimezone = function (tp_inst, date) {
1899
+ if (tp_inst && tp_inst.timezone_select) {
1900
+ var now = date || new Date();
1901
+ tp_inst.timezone_select.val(-now.getTimezoneOffset());
1902
+ }
1903
+ };
1904
+
1905
+ /*
1906
+ * Create a Singleton Instance
1907
+ */
1908
+ $.timepicker = new Timepicker();
1909
+
1910
+ /**
1911
+ * Get the timezone offset as string from a date object (eg '+0530' for UTC+5.5)
1912
+ * @param {number} tzMinutes if not a number, less than -720 (-1200), or greater than 840 (+1400) this value is returned
1913
+ * @param {boolean} iso8601 if true formats in accordance to iso8601 "+12:45"
1914
+ * @return {string}
1915
+ */
1916
+ $.timepicker.timezoneOffsetString = function (tzMinutes, iso8601) {
1917
+ if (isNaN(tzMinutes) || tzMinutes > 840 || tzMinutes < -720) {
1918
+ return tzMinutes;
1919
+ }
1920
+
1921
+ var off = tzMinutes,
1922
+ minutes = off % 60,
1923
+ hours = (off - minutes) / 60,
1924
+ iso = iso8601 ? ':' : '',
1925
+ tz = (off >= 0 ? '+' : '-') + ('0' + Math.abs(hours)).slice(-2) + iso + ('0' + Math.abs(minutes)).slice(-2);
1926
+
1927
+ if (tz === '+00:00') {
1928
+ return 'Z';
1929
+ }
1930
+ return tz;
1931
+ };
1932
+
1933
+ /**
1934
+ * Get the number in minutes that represents a timezone string
1935
+ * @param {string} tzString formatted like "+0500", "-1245", "Z"
1936
+ * @return {number} the offset minutes or the original string if it doesn't match expectations
1937
+ */
1938
+ $.timepicker.timezoneOffsetNumber = function (tzString) {
1939
+ var normalized = tzString.toString().replace(':', ''); // excuse any iso8601, end up with "+1245"
1940
+
1941
+ if (normalized.toUpperCase() === 'Z') { // if iso8601 with Z, its 0 minute offset
1942
+ return 0;
1943
+ }
1944
+
1945
+ if (!/^(\-|\+)\d{4}$/.test(normalized)) { // possibly a user defined tz, so just give it back
1946
+ return tzString;
1947
+ }
1948
+
1949
+ return ((normalized.substr(0, 1) === '-' ? -1 : 1) * // plus or minus
1950
+ ((parseInt(normalized.substr(1, 2), 10) * 60) + // hours (converted to minutes)
1951
+ parseInt(normalized.substr(3, 2), 10))); // minutes
1952
+ };
1953
+
1954
+ /**
1955
+ * No way to set timezone in js Date, so we must adjust the minutes to compensate. (think setDate, getDate)
1956
+ * @param {Date} date
1957
+ * @param {string} toTimezone formatted like "+0500", "-1245"
1958
+ * @return {Date}
1959
+ */
1960
+ $.timepicker.timezoneAdjust = function (date, toTimezone) {
1961
+ var toTz = $.timepicker.timezoneOffsetNumber(toTimezone);
1962
+ if (!isNaN(toTz)) {
1963
+ date.setMinutes(date.getMinutes() + -date.getTimezoneOffset() - toTz);
1964
+ }
1965
+ return date;
1966
+ };
1967
+
1968
+ /**
1969
+ * Calls `timepicker()` on the `startTime` and `endTime` elements, and configures them to
1970
+ * enforce date range limits.
1971
+ * n.b. The input value must be correctly formatted (reformatting is not supported)
1972
+ * @param {Element} startTime
1973
+ * @param {Element} endTime
1974
+ * @param {Object} options Options for the timepicker() call
1975
+ * @return {jQuery}
1976
+ */
1977
+ $.timepicker.timeRange = function (startTime, endTime, options) {
1978
+ return $.timepicker.handleRange('timepicker', startTime, endTime, options);
1979
+ };
1980
+
1981
+ /**
1982
+ * Calls `datetimepicker` on the `startTime` and `endTime` elements, and configures them to
1983
+ * enforce date range limits.
1984
+ * @param {Element} startTime
1985
+ * @param {Element} endTime
1986
+ * @param {Object} options Options for the `timepicker()` call. Also supports `reformat`,
1987
+ * a boolean value that can be used to reformat the input values to the `dateFormat`.
1988
+ * @param {string} method Can be used to specify the type of picker to be added
1989
+ * @return {jQuery}
1990
+ */
1991
+ $.timepicker.datetimeRange = function (startTime, endTime, options) {
1992
+ $.timepicker.handleRange('datetimepicker', startTime, endTime, options);
1993
+ };
1994
+
1995
+ /**
1996
+ * Calls `datepicker` on the `startTime` and `endTime` elements, and configures them to
1997
+ * enforce date range limits.
1998
+ * @param {Element} startTime
1999
+ * @param {Element} endTime
2000
+ * @param {Object} options Options for the `timepicker()` call. Also supports `reformat`,
2001
+ * a boolean value that can be used to reformat the input values to the `dateFormat`.
2002
+ * @return {jQuery}
2003
+ */
2004
+ $.timepicker.dateRange = function (startTime, endTime, options) {
2005
+ $.timepicker.handleRange('datepicker', startTime, endTime, options);
2006
+ };
2007
+
2008
+ /**
2009
+ * Calls `method` on the `startTime` and `endTime` elements, and configures them to
2010
+ * enforce date range limits.
2011
+ * @param {string} method Can be used to specify the type of picker to be added
2012
+ * @param {Element} startTime
2013
+ * @param {Element} endTime
2014
+ * @param {Object} options Options for the `timepicker()` call. Also supports `reformat`,
2015
+ * a boolean value that can be used to reformat the input values to the `dateFormat`.
2016
+ * @return {jQuery}
2017
+ */
2018
+ $.timepicker.handleRange = function (method, startTime, endTime, options) {
2019
+ options = $.extend({}, {
2020
+ minInterval: 0, // min allowed interval in milliseconds
2021
+ maxInterval: 0, // max allowed interval in milliseconds
2022
+ start: {}, // options for start picker
2023
+ end: {} // options for end picker
2024
+ }, options);
2025
+
2026
+ function checkDates(changed, other) {
2027
+ var startdt = startTime[method]('getDate'),
2028
+ enddt = endTime[method]('getDate'),
2029
+ changeddt = changed[method]('getDate');
2030
+
2031
+ if (startdt !== null) {
2032
+ var minDate = new Date(startdt.getTime()),
2033
+ maxDate = new Date(startdt.getTime());
2034
+
2035
+ minDate.setMilliseconds(minDate.getMilliseconds() + options.minInterval);
2036
+ maxDate.setMilliseconds(maxDate.getMilliseconds() + options.maxInterval);
2037
+
2038
+ if (options.minInterval > 0 && minDate > enddt) { // minInterval check
2039
+ endTime[method]('setDate', minDate);
2040
+ }
2041
+ else if (options.maxInterval > 0 && maxDate < enddt) { // max interval check
2042
+ endTime[method]('setDate', maxDate);
2043
+ }
2044
+ else if (startdt > enddt) {
2045
+ other[method]('setDate', changeddt);
2046
+ }
2047
+ }
2048
+ }
2049
+
2050
+ function selected(changed, other, option) {
2051
+ if (!changed.val()) {
2052
+ return;
2053
+ }
2054
+ var date = changed[method].call(changed, 'getDate');
2055
+ if (date !== null && options.minInterval > 0) {
2056
+ if (option === 'minDate') {
2057
+ date.setMilliseconds(date.getMilliseconds() + options.minInterval);
2058
+ }
2059
+ if (option === 'maxDate') {
2060
+ date.setMilliseconds(date.getMilliseconds() - options.minInterval);
2061
+ }
2062
+ }
2063
+ if (date.getTime) {
2064
+ other[method].call(other, 'option', option, date);
2065
+ }
2066
+ }
2067
+
2068
+ $.fn[method].call(startTime, $.extend({
2069
+ onClose: function (dateText, inst) {
2070
+ checkDates($(this), endTime);
2071
+ },
2072
+ onSelect: function (selectedDateTime) {
2073
+ selected($(this), endTime, 'minDate');
2074
+ }
2075
+ }, options, options.start));
2076
+ $.fn[method].call(endTime, $.extend({
2077
+ onClose: function (dateText, inst) {
2078
+ checkDates($(this), startTime);
2079
+ },
2080
+ onSelect: function (selectedDateTime) {
2081
+ selected($(this), startTime, 'maxDate');
2082
+ }
2083
+ }, options, options.end));
2084
+
2085
+ checkDates(startTime, endTime);
2086
+ selected(startTime, endTime, 'minDate');
2087
+ selected(endTime, startTime, 'maxDate');
2088
+ return $([startTime.get(0), endTime.get(0)]);
2089
+ };
2090
+
2091
+ /**
2092
+ * Log error or data to the console during error or debugging
2093
+ * @param {Object} err pass any type object to log to the console during error or debugging
2094
+ * @return {void}
2095
+ */
2096
+ $.timepicker.log = function (err) {
2097
+ if (window.console) {
2098
+ window.console.log(err);
2099
+ }
2100
+ };
2101
+
2102
+ /*
2103
+ * Add util object to allow access to private methods for testability.
2104
+ */
2105
+ $.timepicker._util = {
2106
+ _extendRemove: extendRemove,
2107
+ _isEmptyObject: isEmptyObject,
2108
+ _convert24to12: convert24to12,
2109
+ _detectSupport: detectSupport,
2110
+ _selectLocalTimezone: selectLocalTimezone,
2111
+ _computeEffectiveSetting: computeEffectiveSetting,
2112
+ _splitDateTime: splitDateTime,
2113
+ _parseDateTimeInternal: parseDateTimeInternal
2114
+ };
2115
+
2116
+ /*
2117
+ * Microsecond support
2118
+ */
2119
+ if (!Date.prototype.getMicroseconds) {
2120
+ Date.prototype.microseconds = 0;
2121
+ Date.prototype.getMicroseconds = function () { return this.microseconds; };
2122
+ Date.prototype.setMicroseconds = function (m) {
2123
+ this.setMilliseconds(this.getMilliseconds() + Math.floor(m / 1000));
2124
+ this.microseconds = m % 1000;
2125
+ return this;
2126
+ };
2127
+ }
2128
+
2129
+ /*
2130
+ * Keep up with the version
2131
+ */
2132
+ $.timepicker.version = "1.4";
2133
+
2134
+ })(jQuery);
languages/bulk-delete.pot CHANGED
@@ -2,9 +2,9 @@
2
  # This file is distributed under the same license as the Bulk Delete package.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: Bulk Delete 3.6.0\n"
6
  "Report-Msgid-Bugs-To: http://wordpress.org/tag/bulk-delete\n"
7
- "POT-Creation-Date: 2013-07-07 16:31:26+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=UTF-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
@@ -12,462 +12,508 @@ msgstr ""
12
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
  "Language-Team: LANGUAGE <LL@li.org>\n"
14
 
15
- #. #-#-#-#-# bulk-delete.pot (Bulk Delete 3.6.0) #-#-#-#-#
16
- #. Plugin Name of the plugin/theme
17
- #: bulk-delete.php:148 bulk-delete.php:278
18
- msgid "Bulk Delete"
 
 
19
  msgstr ""
20
 
21
- #: bulk-delete.php:149 bulk-delete.php:1211
22
  msgid "Bulk Delete Schedules"
23
  msgstr ""
24
 
25
- #: bulk-delete.php:161
26
- msgid "By Post Status"
27
  msgstr ""
28
 
29
- #: bulk-delete.php:162
30
- msgid "By Category"
 
 
 
31
  msgstr ""
32
 
33
- #: bulk-delete.php:163
34
- msgid "By Tag"
35
  msgstr ""
36
 
37
- #: bulk-delete.php:164
38
- msgid "By Custom Taxonomy"
39
  msgstr ""
40
 
41
- #: bulk-delete.php:165
42
- msgid "By Custom Post Types"
43
  msgstr ""
44
 
45
- #: bulk-delete.php:166
46
- msgid "By Page"
47
  msgstr ""
48
 
49
- #: bulk-delete.php:167
50
- msgid "By URL"
51
  msgstr ""
52
 
53
- #: bulk-delete.php:168
54
- msgid "By Post Revision"
55
  msgstr ""
56
 
57
- #: bulk-delete.php:169
58
- msgid "Debug Information"
59
  msgstr ""
60
 
61
- #: bulk-delete.php:187
62
- msgid "About Plugin"
63
  msgstr ""
64
 
65
- #: bulk-delete.php:189
66
- msgid ""
67
- "This plugin allows you to delete posts in bulk from selected categories, "
68
- "tags, custom taxonomies or by post status like drafts, pending posts, "
69
- "scheduled posts etc."
70
  msgstr ""
71
 
72
- #: bulk-delete.php:196
73
- msgid "More information"
74
  msgstr ""
75
 
76
- #: bulk-delete.php:197
77
- msgid "Plugin Homepage/support"
78
  msgstr ""
79
 
80
- #: bulk-delete.php:198
81
- msgid "Buy pro addons"
82
  msgstr ""
83
 
84
- #: bulk-delete.php:199
85
- msgid "Plugin author's blog"
86
  msgstr ""
87
 
88
- #: bulk-delete.php:200
89
- msgid "Other Plugin's by Author"
90
  msgstr ""
91
 
92
- #: bulk-delete.php:229
 
 
 
 
93
  msgid "Are you sure you want to delete all the selected posts"
94
  msgstr ""
95
 
96
- #: bulk-delete.php:233
97
- msgid "Please select posts from at least one option"
98
  msgstr ""
99
 
100
- #: bulk-delete.php:234
101
- msgid "Please enter at least one page url"
102
  msgstr ""
103
 
104
- #: bulk-delete.php:252
105
- msgid "Manage"
106
  msgstr ""
107
 
108
- #: bulk-delete.php:266
109
  msgid "Buy Addons"
110
  msgstr ""
111
 
112
- #: bulk-delete.php:294
113
  msgid "WARNING: Posts deleted once cannot be retrieved back. Use with caution."
114
  msgstr ""
115
 
116
- #: bulk-delete.php:322
117
  msgid "plugin"
118
  msgstr ""
119
 
120
- #: bulk-delete.php:322
121
  msgid "Version"
122
  msgstr ""
123
 
124
- #: bulk-delete.php:322
125
  msgid "by"
126
  msgstr ""
127
 
128
- #: bulk-delete.php:331 bulk-delete.php:441 bulk-delete.php:559
129
- #: bulk-delete.php:679 bulk-delete.php:833 bulk-delete.php:960
130
- #: bulk-delete.php:1080 bulk-delete.php:1127
131
  msgid ""
132
  "This section just got enabled. Kindly <a href = \"%1$s\">refresh</a> the "
133
  "page to fully enable it."
134
  msgstr ""
135
 
136
- #: bulk-delete.php:341 bulk-delete.php:1136
137
  msgid "Select the posts which you want to delete"
138
  msgstr ""
139
 
140
- #: bulk-delete.php:348
141
  msgid "All Draft Posts"
142
  msgstr ""
143
 
144
- #: bulk-delete.php:348
145
  msgid "Drafts"
146
  msgstr ""
147
 
148
- #: bulk-delete.php:355
149
  msgid "All Pending posts"
150
  msgstr ""
151
 
152
- #: bulk-delete.php:355 bulk-delete.php:362 bulk-delete.php:369
153
- #: bulk-delete.php:460 bulk-delete.php:579 bulk-delete.php:736
154
  msgid "Posts"
155
  msgstr ""
156
 
157
- #: bulk-delete.php:362
158
  msgid "All scheduled posts"
159
  msgstr ""
160
 
161
- #: bulk-delete.php:369
162
  msgid "All private posts"
163
  msgstr ""
164
 
165
- #: bulk-delete.php:375 bulk-delete.php:477 bulk-delete.php:596
166
- #: bulk-delete.php:750 bulk-delete.php:877 bulk-delete.php:1013
167
- #: bulk-delete.php:1100
168
  msgid "Choose your filtering options"
169
  msgstr ""
170
 
171
- #: bulk-delete.php:382 bulk-delete.php:486 bulk-delete.php:605
172
- #: bulk-delete.php:759 bulk-delete.php:886
173
  msgid "Only restrict to posts which are "
174
  msgstr ""
175
 
176
- #: bulk-delete.php:384 bulk-delete.php:488 bulk-delete.php:607
177
- #: bulk-delete.php:761 bulk-delete.php:888 bulk-delete.php:1022
178
  msgid "older than"
179
  msgstr ""
180
 
181
- #: bulk-delete.php:385 bulk-delete.php:489 bulk-delete.php:608
182
- #: bulk-delete.php:762 bulk-delete.php:889 bulk-delete.php:1023
183
  msgid "posted within last"
184
  msgstr ""
185
 
186
- #: bulk-delete.php:387 bulk-delete.php:491 bulk-delete.php:610
187
- #: bulk-delete.php:764 bulk-delete.php:891 bulk-delete.php:1025
 
188
  msgid "days"
189
  msgstr ""
190
 
191
- #: bulk-delete.php:393 bulk-delete.php:497 bulk-delete.php:616
192
- #: bulk-delete.php:770 bulk-delete.php:897 bulk-delete.php:1031
193
- #: bulk-delete.php:1106
194
  msgid "Move to Trash"
195
  msgstr ""
196
 
197
- #: bulk-delete.php:394 bulk-delete.php:498 bulk-delete.php:617
198
- #: bulk-delete.php:771 bulk-delete.php:898 bulk-delete.php:1032
199
- #: bulk-delete.php:1107
200
  msgid "Delete permanently"
201
  msgstr ""
202
 
203
- #: bulk-delete.php:401 bulk-delete.php:514 bulk-delete.php:633
204
- #: bulk-delete.php:787 bulk-delete.php:914 bulk-delete.php:1039
205
  msgid "Only delete first "
206
  msgstr ""
207
 
208
- #: bulk-delete.php:402 bulk-delete.php:515 bulk-delete.php:634
209
- #: bulk-delete.php:788 bulk-delete.php:915 bulk-delete.php:1040
210
  msgid "posts."
211
  msgstr ""
212
 
213
- #: bulk-delete.php:403 bulk-delete.php:516 bulk-delete.php:635
214
- #: bulk-delete.php:789 bulk-delete.php:916 bulk-delete.php:1041
215
  msgid ""
216
  "Use this option if there are more than 1000 posts and the script timesout."
217
  msgstr ""
218
 
219
- #: bulk-delete.php:409 bulk-delete.php:522 bulk-delete.php:641
220
- #: bulk-delete.php:795 bulk-delete.php:922 bulk-delete.php:1047
 
221
  msgid "Delete now"
222
  msgstr ""
223
 
224
- #: bulk-delete.php:410 bulk-delete.php:523 bulk-delete.php:642
225
- #: bulk-delete.php:796 bulk-delete.php:923 bulk-delete.php:1048
226
- #: include/class-cron-list-table.php:103
227
  msgid "Schedule"
228
  msgstr ""
229
 
230
- #: bulk-delete.php:411 bulk-delete.php:524 bulk-delete.php:643
231
- #: bulk-delete.php:797 bulk-delete.php:924 bulk-delete.php:1049
 
232
  msgid "repeat "
233
  msgstr ""
234
 
235
- #: bulk-delete.php:413 bulk-delete.php:526 bulk-delete.php:645
236
- #: bulk-delete.php:799 bulk-delete.php:926 bulk-delete.php:1051
 
237
  msgid "Don't repeat"
238
  msgstr ""
239
 
240
- #: bulk-delete.php:423 bulk-delete.php:536 bulk-delete.php:655
241
- #: bulk-delete.php:809 bulk-delete.php:936 bulk-delete.php:1061
 
242
  msgid "Only available in Pro Addon"
243
  msgstr ""
244
 
245
- #: bulk-delete.php:430 bulk-delete.php:550 bulk-delete.php:662
246
- #: bulk-delete.php:816 bulk-delete.php:943 bulk-delete.php:1068
247
- #: bulk-delete.php:1115 bulk-delete.php:1151
248
  msgid "Bulk Delete "
249
  msgstr ""
250
 
251
- #: bulk-delete.php:447
252
  msgid "Select the categories whose post you want to delete"
253
  msgstr ""
254
 
255
- #: bulk-delete.php:471
256
  msgid "All Categories"
257
  msgstr ""
258
 
259
- #: bulk-delete.php:504 bulk-delete.php:623 bulk-delete.php:777
260
- #: bulk-delete.php:904
261
  msgid "Public posts"
262
  msgstr ""
263
 
264
- #: bulk-delete.php:505 bulk-delete.php:624 bulk-delete.php:778
265
- #: bulk-delete.php:905
266
  msgid "Private Posts"
267
  msgstr ""
268
 
269
- #: bulk-delete.php:542
270
  msgid "Enter time in Y-m-d H:i:s format or enter now to use current time"
271
  msgstr ""
272
 
273
- #: bulk-delete.php:566
274
  msgid "Select the tags whose post you want to delete"
275
  msgstr ""
276
 
277
- #: bulk-delete.php:590
278
  msgid "All Tags"
279
  msgstr ""
280
 
281
- #: bulk-delete.php:668
282
  msgid "You don't have any posts assigned to tags in this blog."
283
  msgstr ""
284
 
285
- #: bulk-delete.php:703
286
  msgid "Select the taxonomies whose post you want to delete"
287
  msgstr ""
288
 
289
- #: bulk-delete.php:723
290
  msgid ""
291
  "The selected taxonomy has the following terms. Select the terms whose post "
292
  "you want to delete"
293
  msgstr ""
294
 
295
- #: bulk-delete.php:822
296
  msgid "You don't have any posts assigned to custom taxonomies in this blog."
297
  msgstr ""
298
 
299
- #: bulk-delete.php:857
300
  msgid "Select the custom post type whose post you want to delete"
301
  msgstr ""
302
 
303
- #: bulk-delete.php:949
304
  msgid "You don't have any posts assigned to custom post types in this blog."
305
  msgstr ""
306
 
307
- #: bulk-delete.php:972
308
  msgid "Select the pages which you want to delete"
309
  msgstr ""
310
 
311
- #: bulk-delete.php:979
312
  msgid "All Published Pages"
313
  msgstr ""
314
 
315
- #: bulk-delete.php:979 bulk-delete.php:986 bulk-delete.php:993
316
- #: bulk-delete.php:1000 bulk-delete.php:1007
317
  msgid "Pages"
318
  msgstr ""
319
 
320
- #: bulk-delete.php:986
321
  msgid "All Draft Pages"
322
  msgstr ""
323
 
324
- #: bulk-delete.php:993
325
  msgid "All Scheduled Pages"
326
  msgstr ""
327
 
328
- #: bulk-delete.php:1000
329
  msgid "All Pending Pages"
330
  msgstr ""
331
 
332
- #: bulk-delete.php:1007
333
  msgid "All Private Pages"
334
  msgstr ""
335
 
336
- #: bulk-delete.php:1020
337
  msgid "Only restrict to pages which are "
338
  msgstr ""
339
 
340
- #: bulk-delete.php:1086
341
  msgid "Delete these specific pages"
342
  msgstr ""
343
 
344
- #: bulk-delete.php:1092
345
  msgid "Enter one post url (not post ids) per line"
346
  msgstr ""
347
 
348
- #: bulk-delete.php:1143
349
  msgid "All Revisions"
350
  msgstr ""
351
 
352
- #: bulk-delete.php:1143
353
  msgid "Revisions"
354
  msgstr ""
355
 
356
- #: bulk-delete.php:1164
357
  msgid ""
358
  "If you are seeing a blank page after clicking the Bulk Delete button, then "
359
  msgstr ""
360
 
361
- #: bulk-delete.php:1164
362
  msgid "check out this FAQ"
363
  msgstr ""
364
 
365
- #: bulk-delete.php:1165
366
  msgid "You also need need the following debug information."
367
  msgstr ""
368
 
369
- #: bulk-delete.php:1169
370
  msgid "PHP Version "
371
  msgstr ""
372
 
373
- #: bulk-delete.php:1173
374
  msgid "Plugin Version "
375
  msgstr ""
376
 
377
- #: bulk-delete.php:1177
378
  msgid "Available memory size "
379
  msgstr ""
380
 
381
- #: bulk-delete.php:1181
382
  msgid "Script time out "
383
  msgstr ""
384
 
385
- #: bulk-delete.php:1185
386
  msgid "Script input time "
387
  msgstr ""
388
 
389
- #: bulk-delete.php:1236
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
390
  msgid "The selected scheduled job was successfully deleted "
391
  msgstr ""
392
 
393
- #: bulk-delete.php:1271
394
- msgid "Posts from the selected categories are scheduled for deletion."
395
  msgstr ""
396
 
397
- #: bulk-delete.php:1272 bulk-delete.php:1303 bulk-delete.php:1335
398
- #: bulk-delete.php:1367 bulk-delete.php:1401 bulk-delete.php:1436
 
399
  msgid "See the full list of <a href = \"%s\">scheduled tasks</a>"
400
  msgstr ""
401
 
402
- #: bulk-delete.php:1275
 
 
 
 
 
 
 
 
 
 
403
  msgid "Deleted %d post from the selected categories"
404
  msgid_plural "Deleted %d posts from the selected categories"
405
  msgstr[0] ""
406
  msgstr[1] ""
407
 
408
- #: bulk-delete.php:1302
409
  msgid "Posts from the selected tags are scheduled for deletion."
410
  msgstr ""
411
 
412
- #: bulk-delete.php:1306
413
  msgid "Deleted %d post from the selected tags"
414
  msgid_plural "Deleted %d posts from the selected tags"
415
  msgstr[0] ""
416
  msgstr[1] ""
417
 
418
- #: bulk-delete.php:1334
419
  msgid "Posts from the selected custom taxonomies are scheduled for deletion."
420
  msgstr ""
421
 
422
- #: bulk-delete.php:1338
423
  msgid "Deleted %d post from the selected custom taxonomies"
424
  msgid_plural "Deleted %d posts from the selected custom taxonomies"
425
  msgstr[0] ""
426
  msgstr[1] ""
427
 
428
- #: bulk-delete.php:1366
429
  msgid "Posts from the selected custom post type are scheduled for deletion."
430
  msgstr ""
431
 
432
- #: bulk-delete.php:1370
433
  msgid "Deleted %d post from the selected custom post type"
434
  msgid_plural "Deleted %d posts from the selected custom post type"
435
  msgstr[0] ""
436
  msgstr[1] ""
437
 
438
- #: bulk-delete.php:1400
439
  msgid "Posts with the selected status are scheduled for deletion."
440
  msgstr ""
441
 
442
- #: bulk-delete.php:1404
443
  msgid "Deleted %d post with the selected post status"
444
  msgid_plural "Deleted %d posts with the selected post status"
445
  msgstr[0] ""
446
  msgstr[1] ""
447
 
448
- #: bulk-delete.php:1435
449
  msgid "The selected pages are scheduled for deletion."
450
  msgstr ""
451
 
452
- #: bulk-delete.php:1439
453
  msgid "Deleted %d page"
454
  msgid_plural "Deleted %d pages"
455
  msgstr[0] ""
456
  msgstr[1] ""
457
 
458
- #: bulk-delete.php:1465
459
  msgid "Deleted %d post with the specified urls"
460
  msgid_plural "Deleted %d posts with the specified urls"
461
  msgstr[0] ""
462
  msgstr[1] ""
463
 
464
- #: bulk-delete.php:1474
465
  msgid "Deleted %d post revision"
466
  msgid_plural "Deleted %d post revisions"
467
  msgstr[0] ""
468
  msgstr[1] ""
469
 
470
- #: bulk-delete.php:1935
471
  msgctxt "Cron table date format"
472
  msgid "M j, Y @ G:i"
473
  msgstr ""
@@ -484,7 +530,8 @@ msgstr ""
484
 
485
  #: include/class-cron-list-table.php:41
486
  msgid ""
487
- "Scheduling auto post deletion is available only when you buy pro addons."
 
488
  msgstr ""
489
 
490
  #: include/class-cron-list-table.php:45
@@ -494,7 +541,7 @@ msgid ""
494
  msgstr ""
495
 
496
  #: include/class-cron-list-table.php:51
497
- msgid "Bulk Delete Schedule Categories"
498
  msgstr ""
499
 
500
  #: include/class-cron-list-table.php:52
@@ -504,17 +551,19 @@ msgstr ""
504
  #: include/class-cron-list-table.php:53 include/class-cron-list-table.php:60
505
  #: include/class-cron-list-table.php:67 include/class-cron-list-table.php:74
506
  #: include/class-cron-list-table.php:81 include/class-cron-list-table.php:88
 
507
  msgid "More Info"
508
  msgstr ""
509
 
510
  #: include/class-cron-list-table.php:54 include/class-cron-list-table.php:61
511
  #: include/class-cron-list-table.php:68 include/class-cron-list-table.php:75
512
  #: include/class-cron-list-table.php:82 include/class-cron-list-table.php:89
 
513
  msgid "Buy now"
514
  msgstr ""
515
 
516
  #: include/class-cron-list-table.php:58
517
- msgid "Bulk Delete Schedule Tags"
518
  msgstr ""
519
 
520
  #: include/class-cron-list-table.php:59
@@ -522,7 +571,7 @@ msgid "Adds the ability to schedule auto delete of posts based on tags"
522
  msgstr ""
523
 
524
  #: include/class-cron-list-table.php:65
525
- msgid "Bulk Delete Schedule Custom Taxonomy"
526
  msgstr ""
527
 
528
  #: include/class-cron-list-table.php:66
@@ -531,7 +580,7 @@ msgid ""
531
  msgstr ""
532
 
533
  #: include/class-cron-list-table.php:72
534
- msgid "Bulk Delete Schedule Custom Post Type"
535
  msgstr ""
536
 
537
  #: include/class-cron-list-table.php:73
@@ -540,7 +589,7 @@ msgid ""
540
  msgstr ""
541
 
542
  #: include/class-cron-list-table.php:79
543
- msgid "Bulk Delete Schedule Pages"
544
  msgstr ""
545
 
546
  #: include/class-cron-list-table.php:80
@@ -548,7 +597,7 @@ msgid "Adds the ability to schedule auto delete pages"
548
  msgstr ""
549
 
550
  #: include/class-cron-list-table.php:86
551
- msgid "Bulk Delete Schedule by Post Status"
552
  msgstr ""
553
 
554
  #: include/class-cron-list-table.php:87
@@ -557,34 +606,47 @@ msgid ""
557
  "drafts, pending posts, scheduled posts etc."
558
  msgstr ""
559
 
560
- #: include/class-cron-list-table.php:102
 
 
 
 
 
 
 
 
561
  msgid "Next Due (GMT/UTC)"
562
  msgstr ""
563
 
564
- #: include/class-cron-list-table.php:104
565
  msgid "Type"
566
  msgstr ""
567
 
568
- #: include/class-cron-list-table.php:105
569
  msgid "Options"
570
  msgstr ""
571
 
572
- #: include/class-cron-list-table.php:151
573
  msgid "Delete"
574
  msgstr ""
575
 
576
- #: include/class-cron-list-table.php:176
577
  msgid "You have not scheduled any bulk delete jobs."
578
  msgstr ""
579
 
 
 
 
 
580
  #. Plugin URI of the plugin/theme
581
  msgid "http://sudarmuthu.com/wordpress/bulk-delete"
582
  msgstr ""
583
 
584
  #. Description of the plugin/theme
585
  msgid ""
586
- "Bulk delete posts from selected categories, tags, post types, custom "
587
- "taxonomies or by post status like drafts, scheduled posts, revisions etc."
 
588
  msgstr ""
589
 
590
  #. Author of the plugin/theme
2
  # This file is distributed under the same license as the Bulk Delete package.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: Bulk Delete 4.0\n"
6
  "Report-Msgid-Bugs-To: http://wordpress.org/tag/bulk-delete\n"
7
+ "POT-Creation-Date: 2013-09-09 11:56:26+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=UTF-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
12
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
  "Language-Team: LANGUAGE <LL@li.org>\n"
14
 
15
+ #: bulk-delete.php:169 bulk-delete.php:332 bulk-delete.php:358
16
+ msgid "Bulk Delete Posts"
17
+ msgstr ""
18
+
19
+ #: bulk-delete.php:170 bulk-delete.php:329 bulk-delete.php:1387
20
+ msgid "Bulk Delete Users"
21
  msgstr ""
22
 
23
+ #: bulk-delete.php:171 bulk-delete.php:1444
24
  msgid "Bulk Delete Schedules"
25
  msgstr ""
26
 
27
+ #: bulk-delete.php:201 bulk-delete.php:255
28
+ msgid "About Plugin"
29
  msgstr ""
30
 
31
+ #: bulk-delete.php:203 bulk-delete.php:257
32
+ msgid ""
33
+ "This plugin allows you to delete posts in bulk from selected categories, "
34
+ "tags, custom taxonomies or by post status like drafts, pending posts, "
35
+ "scheduled posts etc."
36
  msgstr ""
37
 
38
+ #: bulk-delete.php:210 bulk-delete.php:264
39
+ msgid "More information"
40
  msgstr ""
41
 
42
+ #: bulk-delete.php:211 bulk-delete.php:265
43
+ msgid "Plugin Homepage/support"
44
  msgstr ""
45
 
46
+ #: bulk-delete.php:212 bulk-delete.php:266
47
+ msgid "Buy pro addons"
48
  msgstr ""
49
 
50
+ #: bulk-delete.php:213 bulk-delete.php:267
51
+ msgid "Plugin author's blog"
52
  msgstr ""
53
 
54
+ #: bulk-delete.php:214 bulk-delete.php:268
55
+ msgid "Other Plugin's by Author"
56
  msgstr ""
57
 
58
+ #: bulk-delete.php:229
59
+ msgid "By Post Status"
60
  msgstr ""
61
 
62
+ #: bulk-delete.php:230
63
+ msgid "By Category"
64
  msgstr ""
65
 
66
+ #: bulk-delete.php:231
67
+ msgid "By Tag"
68
  msgstr ""
69
 
70
+ #: bulk-delete.php:232
71
+ msgid "By Custom Taxonomy"
 
 
 
72
  msgstr ""
73
 
74
+ #: bulk-delete.php:233
75
+ msgid "By Custom Post Types"
76
  msgstr ""
77
 
78
+ #: bulk-delete.php:234
79
+ msgid "By Page"
80
  msgstr ""
81
 
82
+ #: bulk-delete.php:235
83
+ msgid "By URL"
84
  msgstr ""
85
 
86
+ #: bulk-delete.php:236
87
+ msgid "By Post Revision"
88
  msgstr ""
89
 
90
+ #: bulk-delete.php:237
91
+ msgid "Debug Information"
92
  msgstr ""
93
 
94
+ #: bulk-delete.php:283
95
+ msgid "By User Role"
96
+ msgstr ""
97
+
98
+ #: bulk-delete.php:304
99
  msgid "Are you sure you want to delete all the selected posts"
100
  msgstr ""
101
 
102
+ #: bulk-delete.php:305
103
+ msgid "Are you sure you want to delete all the selected users"
104
  msgstr ""
105
 
106
+ #: bulk-delete.php:309
107
+ msgid "Please select posts from at least one option"
108
  msgstr ""
109
 
110
+ #: bulk-delete.php:310
111
+ msgid "Please enter at least one page url"
112
  msgstr ""
113
 
114
+ #: bulk-delete.php:346
115
  msgid "Buy Addons"
116
  msgstr ""
117
 
118
+ #: bulk-delete.php:374
119
  msgid "WARNING: Posts deleted once cannot be retrieved back. Use with caution."
120
  msgstr ""
121
 
122
+ #: bulk-delete.php:402
123
  msgid "plugin"
124
  msgstr ""
125
 
126
+ #: bulk-delete.php:402
127
  msgid "Version"
128
  msgstr ""
129
 
130
+ #: bulk-delete.php:402
131
  msgid "by"
132
  msgstr ""
133
 
134
+ #: bulk-delete.php:411 bulk-delete.php:521 bulk-delete.php:639
135
+ #: bulk-delete.php:759 bulk-delete.php:913 bulk-delete.php:1040
136
+ #: bulk-delete.php:1160 bulk-delete.php:1207 bulk-delete.php:1279
137
  msgid ""
138
  "This section just got enabled. Kindly <a href = \"%1$s\">refresh</a> the "
139
  "page to fully enable it."
140
  msgstr ""
141
 
142
+ #: bulk-delete.php:421 bulk-delete.php:1216
143
  msgid "Select the posts which you want to delete"
144
  msgstr ""
145
 
146
+ #: bulk-delete.php:428
147
  msgid "All Draft Posts"
148
  msgstr ""
149
 
150
+ #: bulk-delete.php:428
151
  msgid "Drafts"
152
  msgstr ""
153
 
154
+ #: bulk-delete.php:435
155
  msgid "All Pending posts"
156
  msgstr ""
157
 
158
+ #: bulk-delete.php:435 bulk-delete.php:442 bulk-delete.php:449
159
+ #: bulk-delete.php:540 bulk-delete.php:659 bulk-delete.php:816
160
  msgid "Posts"
161
  msgstr ""
162
 
163
+ #: bulk-delete.php:442
164
  msgid "All scheduled posts"
165
  msgstr ""
166
 
167
+ #: bulk-delete.php:449
168
  msgid "All private posts"
169
  msgstr ""
170
 
171
+ #: bulk-delete.php:455 bulk-delete.php:557 bulk-delete.php:676
172
+ #: bulk-delete.php:830 bulk-delete.php:957 bulk-delete.php:1093
173
+ #: bulk-delete.php:1180 bulk-delete.php:1305
174
  msgid "Choose your filtering options"
175
  msgstr ""
176
 
177
+ #: bulk-delete.php:462 bulk-delete.php:566 bulk-delete.php:685
178
+ #: bulk-delete.php:839 bulk-delete.php:966
179
  msgid "Only restrict to posts which are "
180
  msgstr ""
181
 
182
+ #: bulk-delete.php:464 bulk-delete.php:568 bulk-delete.php:687
183
+ #: bulk-delete.php:841 bulk-delete.php:968 bulk-delete.php:1102
184
  msgid "older than"
185
  msgstr ""
186
 
187
+ #: bulk-delete.php:465 bulk-delete.php:569 bulk-delete.php:688
188
+ #: bulk-delete.php:842 bulk-delete.php:969 bulk-delete.php:1103
189
  msgid "posted within last"
190
  msgstr ""
191
 
192
+ #: bulk-delete.php:467 bulk-delete.php:571 bulk-delete.php:690
193
+ #: bulk-delete.php:844 bulk-delete.php:971 bulk-delete.php:1105
194
+ #: bulk-delete.php:1322
195
  msgid "days"
196
  msgstr ""
197
 
198
+ #: bulk-delete.php:473 bulk-delete.php:577 bulk-delete.php:696
199
+ #: bulk-delete.php:850 bulk-delete.php:977 bulk-delete.php:1111
200
+ #: bulk-delete.php:1186
201
  msgid "Move to Trash"
202
  msgstr ""
203
 
204
+ #: bulk-delete.php:474 bulk-delete.php:578 bulk-delete.php:697
205
+ #: bulk-delete.php:851 bulk-delete.php:978 bulk-delete.php:1112
206
+ #: bulk-delete.php:1187
207
  msgid "Delete permanently"
208
  msgstr ""
209
 
210
+ #: bulk-delete.php:481 bulk-delete.php:594 bulk-delete.php:713
211
+ #: bulk-delete.php:867 bulk-delete.php:994 bulk-delete.php:1119
212
  msgid "Only delete first "
213
  msgstr ""
214
 
215
+ #: bulk-delete.php:482 bulk-delete.php:595 bulk-delete.php:714
216
+ #: bulk-delete.php:868 bulk-delete.php:995 bulk-delete.php:1120
217
  msgid "posts."
218
  msgstr ""
219
 
220
+ #: bulk-delete.php:483 bulk-delete.php:596 bulk-delete.php:715
221
+ #: bulk-delete.php:869 bulk-delete.php:996 bulk-delete.php:1121
222
  msgid ""
223
  "Use this option if there are more than 1000 posts and the script timesout."
224
  msgstr ""
225
 
226
+ #: bulk-delete.php:489 bulk-delete.php:602 bulk-delete.php:721
227
+ #: bulk-delete.php:875 bulk-delete.php:1002 bulk-delete.php:1127
228
+ #: bulk-delete.php:1346
229
  msgid "Delete now"
230
  msgstr ""
231
 
232
+ #: bulk-delete.php:490 bulk-delete.php:603 bulk-delete.php:722
233
+ #: bulk-delete.php:876 bulk-delete.php:1003 bulk-delete.php:1128
234
+ #: bulk-delete.php:1347 include/class-cron-list-table.php:110
235
  msgid "Schedule"
236
  msgstr ""
237
 
238
+ #: bulk-delete.php:491 bulk-delete.php:604 bulk-delete.php:723
239
+ #: bulk-delete.php:877 bulk-delete.php:1004 bulk-delete.php:1129
240
+ #: bulk-delete.php:1348
241
  msgid "repeat "
242
  msgstr ""
243
 
244
+ #: bulk-delete.php:493 bulk-delete.php:606 bulk-delete.php:725
245
+ #: bulk-delete.php:879 bulk-delete.php:1006 bulk-delete.php:1131
246
+ #: bulk-delete.php:1350
247
  msgid "Don't repeat"
248
  msgstr ""
249
 
250
+ #: bulk-delete.php:503 bulk-delete.php:616 bulk-delete.php:735
251
+ #: bulk-delete.php:889 bulk-delete.php:1016 bulk-delete.php:1141
252
+ #: bulk-delete.php:1360
253
  msgid "Only available in Pro Addon"
254
  msgstr ""
255
 
256
+ #: bulk-delete.php:510 bulk-delete.php:630 bulk-delete.php:742
257
+ #: bulk-delete.php:896 bulk-delete.php:1023 bulk-delete.php:1148
258
+ #: bulk-delete.php:1195 bulk-delete.php:1231 bulk-delete.php:1374
259
  msgid "Bulk Delete "
260
  msgstr ""
261
 
262
+ #: bulk-delete.php:527
263
  msgid "Select the categories whose post you want to delete"
264
  msgstr ""
265
 
266
+ #: bulk-delete.php:551
267
  msgid "All Categories"
268
  msgstr ""
269
 
270
+ #: bulk-delete.php:584 bulk-delete.php:703 bulk-delete.php:857
271
+ #: bulk-delete.php:984
272
  msgid "Public posts"
273
  msgstr ""
274
 
275
+ #: bulk-delete.php:585 bulk-delete.php:704 bulk-delete.php:858
276
+ #: bulk-delete.php:985
277
  msgid "Private Posts"
278
  msgstr ""
279
 
280
+ #: bulk-delete.php:622 bulk-delete.php:1366
281
  msgid "Enter time in Y-m-d H:i:s format or enter now to use current time"
282
  msgstr ""
283
 
284
+ #: bulk-delete.php:646
285
  msgid "Select the tags whose post you want to delete"
286
  msgstr ""
287
 
288
+ #: bulk-delete.php:670
289
  msgid "All Tags"
290
  msgstr ""
291
 
292
+ #: bulk-delete.php:748
293
  msgid "You don't have any posts assigned to tags in this blog."
294
  msgstr ""
295
 
296
+ #: bulk-delete.php:783
297
  msgid "Select the taxonomies whose post you want to delete"
298
  msgstr ""
299
 
300
+ #: bulk-delete.php:803
301
  msgid ""
302
  "The selected taxonomy has the following terms. Select the terms whose post "
303
  "you want to delete"
304
  msgstr ""
305
 
306
+ #: bulk-delete.php:902
307
  msgid "You don't have any posts assigned to custom taxonomies in this blog."
308
  msgstr ""
309
 
310
+ #: bulk-delete.php:937
311
  msgid "Select the custom post type whose post you want to delete"
312
  msgstr ""
313
 
314
+ #: bulk-delete.php:1029
315
  msgid "You don't have any posts assigned to custom post types in this blog."
316
  msgstr ""
317
 
318
+ #: bulk-delete.php:1052
319
  msgid "Select the pages which you want to delete"
320
  msgstr ""
321
 
322
+ #: bulk-delete.php:1059
323
  msgid "All Published Pages"
324
  msgstr ""
325
 
326
+ #: bulk-delete.php:1059 bulk-delete.php:1066 bulk-delete.php:1073
327
+ #: bulk-delete.php:1080 bulk-delete.php:1087
328
  msgid "Pages"
329
  msgstr ""
330
 
331
+ #: bulk-delete.php:1066
332
  msgid "All Draft Pages"
333
  msgstr ""
334
 
335
+ #: bulk-delete.php:1073
336
  msgid "All Scheduled Pages"
337
  msgstr ""
338
 
339
+ #: bulk-delete.php:1080
340
  msgid "All Pending Pages"
341
  msgstr ""
342
 
343
+ #: bulk-delete.php:1087
344
  msgid "All Private Pages"
345
  msgstr ""
346
 
347
+ #: bulk-delete.php:1100
348
  msgid "Only restrict to pages which are "
349
  msgstr ""
350
 
351
+ #: bulk-delete.php:1166
352
  msgid "Delete these specific pages"
353
  msgstr ""
354
 
355
+ #: bulk-delete.php:1172
356
  msgid "Enter one post url (not post ids) per line"
357
  msgstr ""
358
 
359
+ #: bulk-delete.php:1223
360
  msgid "All Revisions"
361
  msgstr ""
362
 
363
+ #: bulk-delete.php:1223
364
  msgid "Revisions"
365
  msgstr ""
366
 
367
+ #: bulk-delete.php:1244
368
  msgid ""
369
  "If you are seeing a blank page after clicking the Bulk Delete button, then "
370
  msgstr ""
371
 
372
+ #: bulk-delete.php:1244
373
  msgid "check out this FAQ"
374
  msgstr ""
375
 
376
+ #: bulk-delete.php:1245
377
  msgid "You also need need the following debug information."
378
  msgstr ""
379
 
380
+ #: bulk-delete.php:1249
381
  msgid "PHP Version "
382
  msgstr ""
383
 
384
+ #: bulk-delete.php:1253
385
  msgid "Plugin Version "
386
  msgstr ""
387
 
388
+ #: bulk-delete.php:1257
389
  msgid "Available memory size "
390
  msgstr ""
391
 
392
+ #: bulk-delete.php:1261
393
  msgid "Script time out "
394
  msgstr ""
395
 
396
+ #: bulk-delete.php:1265
397
  msgid "Script input time "
398
  msgstr ""
399
 
400
+ #: bulk-delete.php:1284
401
+ msgid "Select the user roles from which you want to delete users"
402
+ msgstr ""
403
+
404
+ #: bulk-delete.php:1297
405
+ msgid "Users"
406
+ msgstr ""
407
+
408
+ #: bulk-delete.php:1321
409
+ msgid "Only restrict to users who have not logged in the last "
410
+ msgstr ""
411
+
412
+ #: bulk-delete.php:1327
413
+ msgid "Need Simple Login Log Plugin"
414
+ msgstr ""
415
+
416
+ #: bulk-delete.php:1340
417
+ msgid "Only if user doesn't have any post"
418
+ msgstr ""
419
+
420
+ #: bulk-delete.php:1403
421
+ msgid "WARNING: Users deleted once cannot be retrieved back. Use with caution."
422
+ msgstr ""
423
+
424
+ #: bulk-delete.php:1470
425
  msgid "The selected scheduled job was successfully deleted "
426
  msgstr ""
427
 
428
+ #: bulk-delete.php:1502
429
+ msgid "Users from the selected userrole are scheduled for deletion."
430
  msgstr ""
431
 
432
+ #: bulk-delete.php:1503 bulk-delete.php:1542 bulk-delete.php:1573
433
+ #: bulk-delete.php:1605 bulk-delete.php:1637 bulk-delete.php:1671
434
+ #: bulk-delete.php:1706
435
  msgid "See the full list of <a href = \"%s\">scheduled tasks</a>"
436
  msgstr ""
437
 
438
+ #: bulk-delete.php:1506
439
+ msgid "Deleted %d user from the selected roles"
440
+ msgid_plural "Deleted %d users from the selected role"
441
+ msgstr[0] ""
442
+ msgstr[1] ""
443
+
444
+ #: bulk-delete.php:1541
445
+ msgid "Posts from the selected categories are scheduled for deletion."
446
+ msgstr ""
447
+
448
+ #: bulk-delete.php:1545
449
  msgid "Deleted %d post from the selected categories"
450
  msgid_plural "Deleted %d posts from the selected categories"
451
  msgstr[0] ""
452
  msgstr[1] ""
453
 
454
+ #: bulk-delete.php:1572
455
  msgid "Posts from the selected tags are scheduled for deletion."
456
  msgstr ""
457
 
458
+ #: bulk-delete.php:1576
459
  msgid "Deleted %d post from the selected tags"
460
  msgid_plural "Deleted %d posts from the selected tags"
461
  msgstr[0] ""
462
  msgstr[1] ""
463
 
464
+ #: bulk-delete.php:1604
465
  msgid "Posts from the selected custom taxonomies are scheduled for deletion."
466
  msgstr ""
467
 
468
+ #: bulk-delete.php:1608
469
  msgid "Deleted %d post from the selected custom taxonomies"
470
  msgid_plural "Deleted %d posts from the selected custom taxonomies"
471
  msgstr[0] ""
472
  msgstr[1] ""
473
 
474
+ #: bulk-delete.php:1636
475
  msgid "Posts from the selected custom post type are scheduled for deletion."
476
  msgstr ""
477
 
478
+ #: bulk-delete.php:1640
479
  msgid "Deleted %d post from the selected custom post type"
480
  msgid_plural "Deleted %d posts from the selected custom post type"
481
  msgstr[0] ""
482
  msgstr[1] ""
483
 
484
+ #: bulk-delete.php:1670
485
  msgid "Posts with the selected status are scheduled for deletion."
486
  msgstr ""
487
 
488
+ #: bulk-delete.php:1674
489
  msgid "Deleted %d post with the selected post status"
490
  msgid_plural "Deleted %d posts with the selected post status"
491
  msgstr[0] ""
492
  msgstr[1] ""
493
 
494
+ #: bulk-delete.php:1705
495
  msgid "The selected pages are scheduled for deletion."
496
  msgstr ""
497
 
498
+ #: bulk-delete.php:1709
499
  msgid "Deleted %d page"
500
  msgid_plural "Deleted %d pages"
501
  msgstr[0] ""
502
  msgstr[1] ""
503
 
504
+ #: bulk-delete.php:1735
505
  msgid "Deleted %d post with the specified urls"
506
  msgid_plural "Deleted %d posts with the specified urls"
507
  msgstr[0] ""
508
  msgstr[1] ""
509
 
510
+ #: bulk-delete.php:1744
511
  msgid "Deleted %d post revision"
512
  msgid_plural "Deleted %d post revisions"
513
  msgstr[0] ""
514
  msgstr[1] ""
515
 
516
+ #: bulk-delete.php:2227
517
  msgctxt "Cron table date format"
518
  msgid "M j, Y @ G:i"
519
  msgstr ""
530
 
531
  #: include/class-cron-list-table.php:41
532
  msgid ""
533
+ "Scheduling auto post or user deletion is available only when you buy pro "
534
+ "addons."
535
  msgstr ""
536
 
537
  #: include/class-cron-list-table.php:45
541
  msgstr ""
542
 
543
  #: include/class-cron-list-table.php:51
544
+ msgid "Schedule auto delete of Posts by Categories"
545
  msgstr ""
546
 
547
  #: include/class-cron-list-table.php:52
551
  #: include/class-cron-list-table.php:53 include/class-cron-list-table.php:60
552
  #: include/class-cron-list-table.php:67 include/class-cron-list-table.php:74
553
  #: include/class-cron-list-table.php:81 include/class-cron-list-table.php:88
554
+ #: include/class-cron-list-table.php:95
555
  msgid "More Info"
556
  msgstr ""
557
 
558
  #: include/class-cron-list-table.php:54 include/class-cron-list-table.php:61
559
  #: include/class-cron-list-table.php:68 include/class-cron-list-table.php:75
560
  #: include/class-cron-list-table.php:82 include/class-cron-list-table.php:89
561
+ #: include/class-cron-list-table.php:96
562
  msgid "Buy now"
563
  msgstr ""
564
 
565
  #: include/class-cron-list-table.php:58
566
+ msgid "Schedule auto delete of Posts by Tags"
567
  msgstr ""
568
 
569
  #: include/class-cron-list-table.php:59
571
  msgstr ""
572
 
573
  #: include/class-cron-list-table.php:65
574
+ msgid "Schedule auto delete of posts by Custom Taxonomy"
575
  msgstr ""
576
 
577
  #: include/class-cron-list-table.php:66
580
  msgstr ""
581
 
582
  #: include/class-cron-list-table.php:72
583
+ msgid "Schedule auto delete of Posts by Custom Post Type"
584
  msgstr ""
585
 
586
  #: include/class-cron-list-table.php:73
589
  msgstr ""
590
 
591
  #: include/class-cron-list-table.php:79
592
+ msgid "Schedule auto delete of Pages"
593
  msgstr ""
594
 
595
  #: include/class-cron-list-table.php:80
597
  msgstr ""
598
 
599
  #: include/class-cron-list-table.php:86
600
+ msgid "Schedule auto delete of Posts by Post Status"
601
  msgstr ""
602
 
603
  #: include/class-cron-list-table.php:87
606
  "drafts, pending posts, scheduled posts etc."
607
  msgstr ""
608
 
609
+ #: include/class-cron-list-table.php:93
610
+ msgid "Schedule auto delete of Users by User Role"
611
+ msgstr ""
612
+
613
+ #: include/class-cron-list-table.php:94
614
+ msgid "Adds the ability to schedule auto delete of users based on user role"
615
+ msgstr ""
616
+
617
+ #: include/class-cron-list-table.php:109
618
  msgid "Next Due (GMT/UTC)"
619
  msgstr ""
620
 
621
+ #: include/class-cron-list-table.php:111
622
  msgid "Type"
623
  msgstr ""
624
 
625
+ #: include/class-cron-list-table.php:112
626
  msgid "Options"
627
  msgstr ""
628
 
629
+ #: include/class-cron-list-table.php:164
630
  msgid "Delete"
631
  msgstr ""
632
 
633
+ #: include/class-cron-list-table.php:190
634
  msgid "You have not scheduled any bulk delete jobs."
635
  msgstr ""
636
 
637
+ #. Plugin Name of the plugin/theme
638
+ msgid "Bulk Delete"
639
+ msgstr ""
640
+
641
  #. Plugin URI of the plugin/theme
642
  msgid "http://sudarmuthu.com/wordpress/bulk-delete"
643
  msgstr ""
644
 
645
  #. Description of the plugin/theme
646
  msgid ""
647
+ "Bulk delete users and posts from selected categories, tags, post types, "
648
+ "custom taxonomies or by post status like drafts, scheduled posts, revisions "
649
+ "etc."
650
  msgstr ""
651
 
652
  #. Author of the plugin/theme
readme.txt CHANGED
@@ -2,15 +2,15 @@
2
  Contributors: sudar
3
  Tags: post, comment, delete, bulk, mass, draft, revision, page
4
  Requires at least: 3.3
5
- Tested up to: 3.5.2
6
  Donate Link: http://sudarmuthu.com/if-you-wanna-thank-me
7
- Stable tag: 3.6.0
8
 
9
- Bulk delete posts from selected categories, tags, post types, custom taxonomies or by post status like drafts, scheduled posts, revisions etc.
10
 
11
  == Description ==
12
 
13
- Bulk Delete is a WordPress Plugin which can be used to delete posts in bulk from selected categories, tags, post types or custom taxonomies. This Plugin can also delete all drafts, post revisions, scheduled posts or pages.
14
 
15
  ### Features
16
 
@@ -60,7 +60,11 @@ Like posts, all the above options support the following filters as well
60
 
61
  - Delete all post revisions
62
 
63
- #### Deleting by content and page views
 
 
 
 
64
 
65
  Coming soon :)
66
 
@@ -72,12 +76,13 @@ If you looking for just moving posts, instead of deleting, then use [Bulk Move P
72
 
73
  The following are the list of pro addons that are currently available for purchase to add more features to the Plugin.
74
 
75
- - **Bulk Delete Schedule Categories** - Adds the ability to schedule auto delete of posts based on categories. [More details](http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-categories). [Buy now](http://sudarmuthu.com/out/buy-bulk-delete-category-addon)
76
- - **Bulk Delete Schedule Tags** - Adds the ability to schedule auto delete of posts based on tags. [More details](http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-tags). [Buy now](http://sudarmuthu.com/out/buy-bulk-delete-tags-addon)
77
- - **Bulk Delete Schedule Custom Taxonomies** - Adds the ability to schedule auto delete of posts based on custom taxonomies. [More details](http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-taxonomy). [Buy now](http://sudarmuthu.com/out/buy-bulk-delete-taxonomy-addon)
78
- - **Bulk Delete Schedule Custom post types** - Adds the ability to schedule auto delete of posts based on custom post types. [More details](http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-post-types). [Buy now](http://sudarmuthu.com/out/buy-bulk-delete-post-type-addon)
79
- - **Bulk Delete Schedule Pages** - Adds the ability to schedule auto delete of pages. [More details](http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-pages). [Buy now](http://sudarmuthu.com/out/buy-bulk-delete-pages-addon)
80
- - **Bulk Delete Schedule by Post Status** - Adds the ability to schedule auto delete of posts based on post status like drafts, pending posts, scheduled posts etc. [More details](http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-post-status). [Buy now](http://sudarmuthu.com/out/buy-bulk-delete-post-status-addon)
 
81
 
82
  ### Development
83
 
@@ -206,6 +211,10 @@ The ability to schedule deletion of posts is available as a pro addon.
206
 
207
  == Changelog ==
208
 
 
 
 
 
209
  = 2013-07-07 - v3.6.0 - (Dev time: 2 hours) =
210
  - Change minimum requirement to WordPress 3.3
211
  - Fix compatibility issues with "The event calendar" Plugin
@@ -325,6 +334,9 @@ The ability to schedule deletion of posts is available as a pro addon.
325
 
326
  == Upgrade Notice ==
327
 
 
 
 
328
  = 3.6.0 =
329
  Fix compatibility issues with "The event calendar" Plugin
330
 
2
  Contributors: sudar
3
  Tags: post, comment, delete, bulk, mass, draft, revision, page
4
  Requires at least: 3.3
5
+ Tested up to: 3.6
6
  Donate Link: http://sudarmuthu.com/if-you-wanna-thank-me
7
+ Stable tag: 4.0
8
 
9
+ Bulk delete users based on user role and posts from selected categories, tags, post types, custom taxonomies or by post status like drafts, scheduled posts, revisions etc.
10
 
11
  == Description ==
12
 
13
+ Bulk Delete is a WordPress Plugin which can be used to delete posts in bulk from selected categories, tags, post types or custom taxonomies. This Plugin can also delete all drafts, post revisions, scheduled posts or pages and users based on user role.
14
 
15
  ### Features
16
 
60
 
61
  - Delete all post revisions
62
 
63
+ #### Deleting users
64
+
65
+ - Delete users based on user role
66
+
67
+ #### Deleting posts by content and page views
68
 
69
  Coming soon :)
70
 
76
 
77
  The following are the list of pro addons that are currently available for purchase to add more features to the Plugin.
78
 
79
+ - **Schedule auto delete of Posts by Categories** - Adds the ability to schedule auto delete of posts based on categories. [More details](http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-categories). [Buy now](http://sudarmuthu.com/out/buy-bulk-delete-category-addon)
80
+ - **Schedule auto delete of Posts by Tags** - Adds the ability to schedule auto delete of posts based on tags. [More details](http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-tags). [Buy now](http://sudarmuthu.com/out/buy-bulk-delete-tags-addon)
81
+ - **Schedule auto delete of Posts by Custom Taxonomies** - Adds the ability to schedule auto delete of posts based on custom taxonomies. [More details](http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-taxonomy). [Buy now](http://sudarmuthu.com/out/buy-bulk-delete-taxonomy-addon)
82
+ - **Schedule auto delete of Posts by Custom post types** - Adds the ability to schedule auto delete of posts based on custom post types. [More details](http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-post-types). [Buy now](http://sudarmuthu.com/out/buy-bulk-delete-post-type-addon)
83
+ - **Schedule auto delete of Pages** - Adds the ability to schedule auto delete of pages. [More details](http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-pages). [Buy now](http://sudarmuthu.com/out/buy-bulk-delete-pages-addon)
84
+ - **Schedule auto delete of Posts by Post Status** - Adds the ability to schedule auto delete of posts based on post status like drafts, pending posts, scheduled posts etc. [More details](http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-post-status). [Buy now](http://sudarmuthu.com/out/buy-bulk-delete-post-status-addon)
85
+ - **Schedule auto delete of users by User Role** - Adds the ability to schedule auto delete of users based on user role. [More details](http://sudarmuthu.com/wordpress/bulk-delete/pro-addons#bulk-delete-schedule-users-by-user-role). [Buy now](http://sudarmuthu.com/out/buy-bulk-delete-users-by-role-addon)
86
 
87
  ### Development
88
 
211
 
212
  == Changelog ==
213
 
214
+ = 2013-09-09 - v4.0 - (Dev time: 25 hours) =
215
+ - Add the ability to delete users
216
+ - Move menu items under tools
217
+
218
  = 2013-07-07 - v3.6.0 - (Dev time: 2 hours) =
219
  - Change minimum requirement to WordPress 3.3
220
  - Fix compatibility issues with "The event calendar" Plugin
334
 
335
  == Upgrade Notice ==
336
 
337
+ = 4.0 =
338
+ Add the ability to delete users
339
+
340
  = 3.6.0 =
341
  Fix compatibility issues with "The event calendar" Plugin
342