Projet

Général

Profil

Paste
Télécharger (26,5 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / date / date_popup / date_popup.module @ db9ffd17

1
<?php
2
/**
3
 * @file
4
 * A module to enable jquery calendar and time entry popups.
5
 * Requires the Date API.
6
 *
7
 * Add a type of #date_popup to any date, time, or datetime field that will
8
 * use this popup. Set #date_format to the way the date should be presented
9
 * to the user in the form. Set #default_value to be a date in the local
10
 * timezone, and note the timezone name in #date_timezone.
11
 *
12
 * The element will create two textfields, one for the date and one for the
13
 * time. The date textfield will include a jQuery popup calendar date picker,
14
 * and the time textfield uses a jQuery timepicker.
15
 *
16
 * If no time elements are included in the format string, only the date
17
 * textfield will be created. If no date elements are included in the format
18
 * string, only the time textfield, will be created.
19
 *
20
 */
21

    
22
/**
23
 * Load needed files.
24
 *
25
 * Play nice with jQuery UI.
26
 */
27
function date_popup_add() {
28
  static $loaded = FALSE;
29
  if ($loaded) {
30
    return;
31
  }
32
  drupal_add_library('system', 'ui.datepicker');
33
  drupal_add_library('date_popup', 'timeentry');
34

    
35
  // Add the wvega-timepicker library if it's available.
36
  $wvega_path = date_popup_get_wvega_path();
37
  if ($wvega_path) {
38
    drupal_add_js($wvega_path . '/jquery.timepicker.js');
39
    drupal_add_css($wvega_path . '/jquery.timepicker.css');
40
  }
41
  $loaded = TRUE;
42
}
43

    
44
/**
45
 * Get the location of the Willington Vega timepicker library.
46
 *
47
 * @return
48
 *   The location of the library, or FALSE if the library isn't installed.
49
 */
50
function date_popup_get_wvega_path() {
51
  $path = FALSE;
52
  if (function_exists('libraries_get_path')) {
53
    $path = libraries_get_path('wvega-timepicker');
54
    if (!file_exists($path)) {
55
      $path = FALSE;
56
    }
57
  }
58
  elseif (file_exists('sites/all/libraries/wvega-timepicker/jquery.timepicker.js')) {
59
    $path = 'sites/all/libraries/wvega-timepicker';
60
  }
61
  return $path;
62
}
63

    
64
/**
65
 * Get the name of the preferred default timepicker.
66
 *
67
 * If the wvega timepicker is available on the system, default to using that,
68
 * unless the administrator has specifically chosen otherwise.
69
 */
70
function date_popup_get_preferred_timepicker() {
71
  $wvega_available = date_popup_get_wvega_path();
72
  return $wvega_available ? 'wvega' : 'default';
73
}
74

    
75
/**
76
 * Implements hook_library().
77
 */
78
function date_popup_library() {
79
  $libraries = array();
80

    
81
  $path = drupal_get_path('module', 'date_popup');
82
  $libraries['timeentry'] = array(
83
    'title' => 'Time Entry',
84
    'website' => 'http://plugins.jquery.com/project/timeEntry',
85
    'version' => '1.4.7',
86
    'js' => array(
87
      $path . '/jquery.timeentry.pack.js' => array(),
88
    ),
89
    'css' => array(
90
      $path . '/themes/jquery.timeentry.css' => array(),
91
    ),
92
  );
93
  return $libraries;
94
}
95

    
96
/**
97
 * Create a unique CSS id name and output a single inline JS block for
98
 * each startup function to call and settings array to pass it.  This
99
 * used to create a unique CSS class for each unique combination of
100
 * function and settings, but using classes requires a DOM traversal
101
 * and is much slower than an id lookup.  The new approach returns to
102
 * requiring a duplicate copy of the settings/code for every element
103
 * that uses them, but is much faster.  We could combine the logic by
104
 * putting the ids for each unique function/settings combo into
105
 * Drupal.settings and searching for each listed id.
106
 *
107
 * @param $pfx
108
 *   The CSS class prefix to search the DOM for.
109
 *   TODO : unused ?
110
 * @param $func
111
 *   The jQuery function to invoke on each DOM element containing the
112
 * returned CSS class.
113
 * @param $settings
114
 *   The settings array to pass to the jQuery function.
115
 * @returns
116
 *   The CSS id to assign to the element that should have
117
 * $func($settings) invoked on it.
118
 */
119
function date_popup_js_settings_id($id, $func, $settings) {
120
  static $js_added = FALSE;
121
  static $id_count = array();
122

    
123
  // Make sure popup date selector grid is in correct year.
124
  if (!empty($settings['yearRange'])) {
125
    $parts = explode(':', $settings['yearRange']);
126
    // Set the default date to 0 or the lowest bound if the date ranges do not include the current year
127
    // Necessary for the datepicker to render and select dates correctly
128
    $defaultDate = ($parts[0] > 0 || 0 > $parts[1]) ? $parts[0] : 0;
129
    $settings += array('defaultDate' => (string) $defaultDate . 'y');
130
  }
131

    
132
  if (!$js_added) {
133
    drupal_add_js(drupal_get_path('module', 'date_popup') .'/date_popup.js');
134
    $js_added = TRUE;
135
  }
136

    
137
  // We use a static array to account for possible multiple form_builder()
138
  // calls in the same request (form instance on 'Preview').
139
  if (!isset($id_count[$id])) {
140
    $id_count[$id] = 0;
141
  }
142

    
143
// It looks like we need the additional id_count for this to
144
// work correctly when there are multiple values.
145
//  $return_id = "$id-$func-popup";
146
  $return_id = "$id-$func-popup-". $id_count[$id]++;
147
  $js_settings['datePopup'][$return_id] = array(
148
    'func' => $func,
149
    'settings' => $settings
150
  );
151
  drupal_add_js($js_settings, 'setting');
152
  return $return_id;
153
}
154

    
155
function date_popup_theme() {
156
  return array(
157
    'date_popup' => array('render element' => 'element'),
158
    );
159
}
160

    
161
/**
162
 * Implements hook_element_info().
163
 *
164
 * Set the #type to date_popup and fill the element #default_value with
165
 * a date adjusted to the proper local timezone in datetime format (YYYY-MM-DD HH:MM:SS).
166
 *
167
 * The element will create two textfields, one for the date and one for the
168
 * time. The date textfield will include a jQuery popup calendar date picker,
169
 * and the time textfield uses a jQuery timepicker.
170
 *
171
 * NOTE - Converting a date stored in the database from UTC to the local zone
172
 * and converting it back to UTC before storing it is not handled by this
173
 * element and must be done in pre-form and post-form processing!!
174
 *
175
 * #date_timezone
176
 *   The local timezone to be used to create this date.
177
 *
178
 * #date_format
179
 *   Unlike earlier versions of this popup, most formats will work.
180
 *
181
 * #date_increment
182
 *   Increment minutes and seconds by this amount, default is 1.
183
 *
184
 * #date_year_range
185
 *   The number of years to go back and forward in a year selector,
186
 *   default is -3:+3 (3 back and 3 forward).
187
 *
188
 * #datepicker_options
189
 *   An associative array representing the jQuery datepicker options you want
190
 *   to set for this element. Use the jQuery datepicker option names as keys.
191
 *   Hard coded defaults are:
192
 *   - changeMonth => TRUE
193
 *   - changeYear => TRUE
194
 *   - autoPopUp => 'focus'
195
 *   - closeAtTop => FALSE
196
 *   - speed => 'immediate'
197
 */
198
function date_popup_element_info() {
199
  $timepicker = date_popup_get_preferred_timepicker();
200
  $type['date_popup'] = array(
201
    '#input' => TRUE,
202
    '#tree' => TRUE,
203
    '#date_timezone' => date_default_timezone(),
204
    '#date_flexible' => 0,
205
    '#date_format' => variable_get('date_format_short', 'm/d/Y - H:i'),
206
    '#datepicker_options' => array(),
207
    '#timepicker' => variable_get('date_popup_timepicker', $timepicker),
208
    '#date_increment' => 1,
209
    '#date_year_range' => '-3:+3',
210
    '#date_label_position' => 'above',
211
    '#process' => array('date_popup_element_process'),
212
    '#value_callback' => 'date_popup_element_value_callback',
213
    '#theme_wrappers' => array('date_popup'),
214
  );
215
  if (module_exists('ctools')) {
216
    $type['date_popup']['#pre_render'] = array('ctools_dependent_pre_render');
217
  }
218
  return $type;
219
}
220

    
221
function date_popup_date_granularity($element) {
222
  $granularity = date_format_order($element['#date_format']);
223
  return array_intersect($granularity, array('month', 'day', 'year'));
224
}
225

    
226
function date_popup_time_granularity($element) {
227
  $granularity = date_format_order($element['#date_format']);
228
  return array_intersect($granularity, array('hour', 'minute', 'second'));
229
}
230

    
231
function date_popup_date_format($element) {
232
  return (date_limit_format($element['#date_format'], date_popup_date_granularity($element)));
233
}
234

    
235
function date_popup_time_format($element) {
236
  return date_popup_format_to_popup_time(date_limit_format($element['#date_format'], date_popup_time_granularity($element)), $element['#timepicker']);
237
}
238

    
239
/**
240
 * Element value callback for date_popup element.
241
 */
242
function date_popup_element_value_callback($element, $input = FALSE, &$form_state) {
243
  $granularity = date_format_order($element['#date_format']);
244
  $has_time = date_has_time($granularity);
245
  $date = NULL;
246
  $return = $has_time ? array('date' => '', 'time' => '') : array('date' => '');
247
  // Normal input from submitting the form element.
248
  // Check is_array() to skip the string input values created by Views pagers.
249
  // Those string values, if present, should be interpreted as empty input.
250
  if ($input !== FALSE && is_array($input)) {
251
    $return = $input;
252
    $date = date_popup_input_date($element, $input);
253
  }
254
  // No input? Try the default value.
255
  elseif (!empty($element['#default_value'])) {
256
    $date = date_default_date($element);
257
  }
258
  // Date with errors won't re-display.
259
  if (date_is_date($date)) {
260
    $return['date'] = !$date->timeOnly ? date_format_date($date, 'custom', date_popup_date_format($element)) : '';
261
    $return['time'] = $has_time ? date_format_date($date, 'custom', date_popup_time_format($element)) : '';
262
  }
263
  elseif (!empty($input)) {
264
    $return = $input;
265
  }
266
  return $return;
267

    
268
}
269

    
270
/**
271
 * Javascript popup element processing.
272
 * Add popup attributes to $element.
273
 */
274
function date_popup_element_process($element, &$form_state, $form) {
275
  if (date_hidden_element($element)) {
276
    return $element;
277
  }
278

    
279
  date_popup_add();
280
  module_load_include('inc', 'date_api', 'date_api_elements');
281

    
282
  $element['#tree'] = TRUE;
283
  $element['#theme_wrappers'] = array('date_popup');
284

    
285
  if (!empty($element['#ajax'])) {
286
    $element['#ajax'] += array(
287
      'trigger_as' => array('name' =>$element['#name']),
288
      'event' => 'change',
289
    );
290
  }
291

    
292
  $element['date'] = date_popup_process_date_part($element);
293
  $element['time'] = date_popup_process_time_part($element);
294

    
295
  if (isset($element['#element_validate'])) {
296
    array_push($element['#element_validate'], 'date_popup_validate');
297
  }
298
  else {
299
    $element['#element_validate'] = array('date_popup_validate');
300
  }
301

    
302
  $context = array(
303
   'form' => $form,
304
  );
305
  drupal_alter('date_popup_process', $element, $form_state, $context);
306

    
307
  return $element;
308
}
309

    
310
/**
311
 * Process the date portion of the element.
312
 */
313
function date_popup_process_date_part(&$element) {
314
  $granularity = date_format_order($element['#date_format']);
315
  $date_granularity = date_popup_date_granularity($element);
316
  if (empty($date_granularity)) return array();
317

    
318
  // The datepicker can't handle zero or negative values like 0:+1
319
  // even though the Date API can handle them, so rework the value
320
  // we pass to the datepicker to use defaults it can accept (such as +0:+1)
321
  // date_range_string() adds the necessary +/- signs to the range string.
322
  $this_year = date_format(date_now(), 'Y');
323
  $date = '';
324
  if (!empty($element['#value']['date'])) {
325
    $date = new DateObject($element['#value']['date'], $element['#date_timezone'], date_popup_date_format($element));
326
  }
327
  $range = date_range_years($element['#date_year_range'], $date);
328
  $year_range = date_range_string($range);
329

    
330
  // Add the dynamic datepicker options. Allow element-specific datepicker
331
  // preferences to override these options for whatever reason they see fit.
332
  $settings = $element['#datepicker_options'] + array(
333
    'changeMonth' => TRUE,
334
    'changeYear' => TRUE,
335
    'autoPopUp' => 'focus',
336
    'closeAtTop' => FALSE,
337
    'speed' => 'immediate',
338
    'firstDay' => intval(variable_get('date_first_day', 0)),
339
    //'buttonImage' => base_path() . drupal_get_path('module', 'date_api') ."/images/calendar.png",
340
    //'buttonImageOnly' => TRUE,
341
    'dateFormat' => date_popup_format_to_popup(date_popup_date_format($element), 'datepicker'),
342
    'yearRange' => $year_range,
343
    // Custom setting, will be expanded in Drupal.behaviors.date_popup()
344
    'fromTo' => isset($fromto),
345
  );
346

    
347
  // Create a unique id for each set of custom settings.
348
  $id = date_popup_js_settings_id($element['#id'], 'datepicker', $settings);
349

    
350
  // Manually build this element and set the value - this will prevent corrupting
351
  // the parent value
352
  $parents = array_merge($element['#parents'], array('date'));
353
  $sub_element = array(
354
    '#type' => 'textfield',
355
    '#title' => theme('date_part_label_date', array('part_type' => 'date', 'element' => $element)),
356
    '#title_display' => $element['#date_label_position'] == 'above' ? 'before' : 'invisible',
357
    '#default_value' => $element['#value']['date'],
358
    '#id' => $id,
359
    '#input' => FALSE,
360
    '#size' => !empty($element['#size']) ? $element['#size'] : 20,
361
    '#maxlength' => !empty($element['#maxlength']) ? $element['#maxlength'] : 30,
362
    '#attributes' => $element['#attributes'],
363
    '#parents' => $parents,
364
    '#name' => array_shift($parents) . '['. implode('][', $parents) .']',
365
    '#ajax' => !empty($element['#ajax']) ? $element['#ajax'] : FALSE,
366
  );
367
  $sub_element['#value'] = $sub_element['#default_value'];
368
  // TODO, figure out exactly when we want this description. In many places it is not desired.
369
  $sub_element['#description'] = ' '. t('E.g., @date', array('@date' => date_format_date(date_example_date(), 'custom', date_popup_date_format($element))));
370

    
371
  return $sub_element;
372
}
373

    
374
/**
375
 * Process the time portion of the element.
376
 */
377
function date_popup_process_time_part(&$element) {
378
  $granularity = date_format_order($element['#date_format']);
379
  $has_time = date_has_time($granularity);
380
  if (empty($has_time)) return array();
381

    
382
  switch ($element['#timepicker']) {
383
    case 'default':
384
      $func = 'timeEntry';
385
      $settings = array(
386
        'show24Hours' => strpos($element['#date_format'], 'H') !== FALSE ? TRUE : FALSE,
387
        'showSeconds' => (in_array('second', $granularity) ? TRUE : FALSE),
388
        'timeSteps' => array(1, intval($element['#date_increment']), (in_array('second', $granularity) ? $element['#date_increment'] : 0)),
389
        'spinnerImage' => '',
390
        'fromTo' => isset($fromto),
391
        );
392
      if (strpos($element['#date_format'], 'a') !== FALSE) {
393
        // Then we are using lowercase am/pm.
394
        $settings['ampmNames'] = array('am', 'pm');
395
      }
396
      if (strpos($element['#date_format'], ' A') !== FALSE || strpos($element['#date_format'], ' a') !== FALSE) {
397
        $settings['ampmPrefix'] = ' ';
398
      }
399
      break;
400
    case 'wvega':
401
      $func = 'timepicker';
402
      $time_granularity = array_intersect($granularity, array('hour', 'minute', 'second'));
403
      $format = date_popup_format_to_popup_time(date_limit_format($element['#date_format'], $time_granularity), 'wvega');
404
      // The first value in the dropdown list should be the same as the element
405
      // default_value, but it needs to be in JS format (i.e. milliseconds since
406
      // the epoch).
407
      $start_time = new DateObject($element['#default_value'], $element['#date_timezone'], DATE_FORMAT_DATETIME);
408
      date_increment_round($start_time, $element['#date_increment']);
409
      $start_time = $start_time->format(DATE_FORMAT_UNIX) * 1000;
410
      $settings = array(
411
        'timeFormat' => $format,
412
        'interval' => $element['#date_increment'],
413
        'startTime' => $start_time,
414
        'scrollbar' => TRUE,
415
      );
416
      break;
417
    default:
418
      $func = '';
419
      $settings = array();
420
      break;
421
  }
422

    
423
  // Create a unique id for each set of custom settings.
424
  $id = date_popup_js_settings_id($element['#id'], $func, $settings);
425

    
426
  // Manually build this element and set the value - this will prevent corrupting
427
  // the parent value
428
  $parents = array_merge($element['#parents'], array('time'));
429
  $sub_element = array(
430
    '#type' => 'textfield',
431
    '#title' => theme('date_part_label_time', array('part_type' => 'time', 'element' => $element)),
432
    '#title_display' => $element['#date_label_position'] == 'above' ? 'before' : 'invisible',
433
    '#default_value' => $element['#value']['time'],
434
    '#id' => $id,
435
    '#size' => 15,
436
    '#maxlength' => 10,
437
    '#attributes' => $element['#attributes'],
438
    '#parents' => $parents,
439
    '#name' => array_shift($parents) . '['. implode('][', $parents) .']',
440
    '#ajax' => !empty($element['#ajax']) ? $element['#ajax'] : FALSE,
441
  );
442

    
443
  $sub_element['#value'] = $sub_element['#default_value'];
444

    
445
  // TODO, figure out exactly when we want this description. In many places it is not desired.
446
  $example_date = date_now();
447
  date_increment_round($example_date, $element['#date_increment']);
448
  $sub_element['#description'] = t('E.g., @date', array('@date' => date_format_date($example_date, 'custom', date_popup_time_format($element))));
449

    
450
  return ($sub_element);
451
}
452

    
453
/**
454
 * Massage the input values back into a single date.
455
 *
456
 * When used as a Views widget, the validation step always gets triggered,
457
 * even with no form submission. Before form submission $element['#value']
458
 * contains a string, after submission it contains an array.
459
 *
460
 */
461
function date_popup_validate($element, &$form_state) {
462

    
463
  if (date_hidden_element($element)) {
464
    return;
465
  }
466

    
467
  if (is_string($element['#value'])) {
468
    return;
469
  }
470

    
471
  module_load_include('inc', 'date_api', 'date_api_elements');
472
  date_popup_add();
473

    
474
  $input_exists = NULL;
475
  $input = drupal_array_get_nested_value($form_state['values'], $element['#parents'], $input_exists);
476

    
477
  drupal_alter('date_popup_pre_validate', $element, $form_state, $input);
478

    
479
  $granularity = date_format_order($element['#date_format']);
480
  $date_granularity = date_popup_date_granularity($element);
481
  $time_granularity = date_popup_time_granularity($element);
482
  $has_time = date_has_time($granularity);
483

    
484
  $label = !empty($element['#date_title']) ? $element['#date_title'] : (!empty($element['#title']) ? $element['#title'] : '');
485
  $label = t($label);
486

    
487
  $date = date_popup_input_date($element, $input);
488

    
489
  // If the date has errors, display them.
490
  // If something was input but there is no date, the date is invalid.
491
  // If the field is empty and required, set error message and return.
492
  $error_field = implode('][', $element['#parents']);
493
  if (empty($date) || !empty($date->errors)) {
494
    if (is_object($date) && !empty($date->errors)) {
495
      $message = t('The value input for field %field is invalid:', array('%field' => $label));
496
      $message .= '<br />' . implode('<br />', $date->errors);
497
      form_set_error($error_field, $message);
498
      return;
499
    }
500
    if (!empty($input['date'])) {
501
      $message = t('The value input for field %field is invalid.', array('%field' => $label));
502
      form_set_error($error_field, $message);
503
      return;
504
    }
505
    if ($element['#required']) {
506
      $message = t('A valid date is required for %title.', array('%title' => $label));
507
      form_set_error($error_field, $message);
508
      return;
509
    }
510
  }
511

    
512
  // If the created date is valid, set it.
513
  $value = !empty($date) ? $date->format(DATE_FORMAT_DATETIME) : NULL;
514
  form_set_value($element, $value, $form_state);
515
}
516

    
517
/**
518
 * Helper function for extracting a date value out of user input.
519
 *
520
 * @param autocomplete
521
 *   Should we add a time value to complete the date if there is no time?
522
 *   Useful anytime the time value is optional.
523
 */
524
function date_popup_input_date($element, $input, $auto_complete = FALSE) {
525
  if (empty($input) || !is_array($input) || !array_key_exists('date', $input) || empty($input['date'])) {
526
    return NULL;
527
  }
528
  date_popup_add();
529
  $granularity = date_format_order($element['#date_format']);
530
  $has_time = date_has_time($granularity);
531
  $flexible = !empty($element['#date_flexible']) ? $element['#date_flexible'] : 0;
532

    
533
  $format = date_popup_date_format($element);
534
  $format .= $has_time ? ' ' . date_popup_time_format($element) : '';
535
  $datetime = $input['date'];
536
  $datetime .= $has_time ? ' ' . $input['time'] : '';
537
  $date = new DateObject($datetime, $element['#date_timezone'], $format);
538
  if (is_object($date)) {
539
    $date->limitGranularity($granularity);
540
    if ($date->validGranularity($granularity, $flexible)) {
541
      date_increment_round($date, $element['#date_increment']);
542
    }
543
    return $date;
544
  }
545
  return NULL;
546
}
547

    
548
/**
549
 * Allowable time formats.
550
 */
551
function date_popup_time_formats($with_seconds = FALSE) {
552
  return array(
553
    'H:i:s',
554
    'h:i:sA',
555
    );
556
}
557

    
558
/**
559
 * Format options array.
560
 *
561
 * TODO Remove any formats not supported by the widget, if any.
562
 */
563
function date_popup_formats() {
564
  $formats = str_replace('i', 'i:s', array_keys(system_get_date_formats('short')));
565
  $formats = drupal_map_assoc($formats);
566
  return $formats;
567
}
568

    
569
/**
570
 * Recreate a date format string so it has the values popup expects.
571
 *
572
 * @param string $format
573
 *   a normal date format string, like Y-m-d
574
 * @return string
575
 *   A format string in popup format, like YMD-, for the
576
 *   earlier 'calendar' version, or m/d/Y for the later 'datepicker'
577
 *   version.
578
 */
579
function date_popup_format_to_popup($format) {
580
  if (empty($format)) {
581
    $format = 'Y-m-d';
582
  }
583
  $replace = date_popup_datepicker_format_replacements();
584
  return strtr($format, $replace);
585
}
586

    
587
/**
588
 * Recreate a time format string so it has the values popup expects.
589
 *
590
 * @param string $format
591
 *   a normal time format string, like h:i (a)
592
 * @return string
593
 *   a format string that the popup can accept like h:i a
594
 */
595
function date_popup_format_to_popup_time($format, $timepicker = NULL) {
596
  if (empty($format)) {
597
    $format = 'H:i';
598
  }
599
  $format = str_replace(array('/', '-', ' .', ',', 'F', 'M', 'l', 'z', 'w', 'W', 'd', 'j', 'm', 'n', 'y', 'Y'), '', $format);
600
  $format = strtr($format, date_popup_timepicker_format_replacements($timepicker));
601
  return $format;
602
}
603

    
604
/**
605
 * Reconstruct popup format string into normal format string.
606
 *
607
 * @param string $format
608
 *   a string in popup format, like YMD-
609
 * @return string
610
 *   a normal date format string, like Y-m-d
611
 */
612
function date_popup_popup_to_format($format) {
613
  $replace = array_flip(date_popup_datepicker_format_replacements());
614
  return strtr($format, $replace);
615
}
616

    
617
/**
618
 * Return a map of format replacements required for a given timepicker.
619
 *
620
 * Client-side time entry plugins don't support all possible date formats.
621
 * This function returns a map of format replacements required to change any
622
 * input format into one that the given timepicker can support.
623
 *
624
 * @param $timepicker
625
 *   The time entry plugin being used: either 'wvega' or 'default'.
626
 * @return
627
 *   A map of replacements.
628
 */
629
function date_popup_timepicker_format_replacements($timepicker = 'default') {
630
  switch ($timepicker) {
631
    case 'wvega':
632
      return array(
633
        'a' => 'A', // The wvega timepicker only supports uppercase AM/PM.
634
      );
635
    default:
636
      return array(
637
        'G' => 'H', // The default timeEntry plugin requires leading zeros.
638
        'g' => 'h',
639
        );
640
  }
641
}
642

    
643
/**
644
 * The format replacement patterns for the new datepicker.
645
 */
646
function date_popup_datepicker_format_replacements() {
647
  return array(
648
  'd' => 'dd',
649
  'j' => 'd',
650
  'l' => 'DD',
651
  'D' => 'D',
652
  'm' => 'mm',
653
  'n' => 'm',
654
  'F' => 'MM',
655
  'M' => 'M',
656
  'Y' => 'yy',
657
  'y' => 'y',
658
  );
659
}
660

    
661
/**
662
 * Format a date popup element.
663
 *
664
 * Use a class that will float date and time next to each other.
665
 */
666
function theme_date_popup($vars) {
667
  $element = $vars['element'];
668
  $attributes = !empty($element['#wrapper_attributes']) ? $element['#wrapper_attributes'] : array('class' => array());
669
  $attributes['class'][] = 'container-inline-date';
670
  // If there is no description, the floating date elements need some extra padding below them.
671
  $wrapper_attributes = array('class' => array('date-padding'));
672
  if (empty($element['date']['#description'])) {
673
    $wrapper_attributes['class'][] = 'clearfix';
674
  }
675
  // Add an wrapper to mimic the way a single value field works, for ease in using #states.
676
  if (isset($element['#children'])) {
677
    $element['#children'] = '<div id="' . $element['#id'] . '" ' . drupal_attributes($wrapper_attributes) .'>' . $element['#children'] . '</div>';
678
  }
679
  return '<div ' . drupal_attributes($attributes) .'>' . theme('form_element', $element) . '</div>';
680
}
681

    
682
/**
683
 * Implements hook_menu().
684
 */
685
function date_popup_menu() {
686
  $items = array();
687
  // TODO Fix this later.
688
  $items['admin/config/date/date_popup'] = array(
689
    'title' => 'Date Popup',
690
    'description' => 'Configure the Date Popup settings.',
691
    'page callback' => 'drupal_get_form',
692
    'page arguments' => array('date_popup_settings'),
693
    'access callback' => 'user_access',
694
    'access arguments' => array('administer site configuration'),
695
  );
696
  return $items;
697
}
698
/**
699
 * General configuration form for controlling the Date Popup behaviour.
700
 */
701
function date_popup_settings() {
702
  $wvega_available = date_popup_get_wvega_path();
703
  $preferred_timepicker = date_popup_get_preferred_timepicker();
704

    
705
  $form['#prefix'] = t('<p>The Date Popup module allows for manual time entry or use of a jQuery timepicker plugin. The Date module comes with a default jQuery timepicker which is already installed. The module also supports a dropdown timepicker that must be downloaded separately. The dropdown timepicker will not appear as an option until the code is available in the libraries folder. If you do not want to use a jQuery timepicker, you can choose the "Manual time entry" option below and users will get a regular textfield instead.</p>');
706
  $form['date_popup_timepicker'] = array(
707
    '#type' => 'select',
708
    '#options' => array(
709
      'default' => t('Use default jQuery timepicker'),
710
      'wvega' => t('Use dropdown timepicker'),
711
      'none' => t('Manual time entry, no jQuery timepicker')
712
    ),
713
    '#title' => t('Timepicker'),
714
    '#default_value' => variable_get('date_popup_timepicker', $preferred_timepicker),
715
  );
716

    
717
  if (!$wvega_available) {
718
    $form['#prefix'] .= t('<p>To install the dropdown timepicker, create a <code>!directory</code> directory in your site installation. Then visit <a href="@download">@download</a>, download the latest copy and unzip it. You will see files with names like jquery.timepicker-1.1.2.js and jquery.timepicker-1.1.2.css. Rename them to jquery.timepicker.js and jquery.timepicker.css and copy them into <code>!directory</code>.</p>', array('!directory' => 'sites/all/libraries/wvega-timepicker', '@download' => 'https://github.com/wvega/timepicker/archives/master'));
719
    unset($form['date_popup_timepicker']['#options']['wvega']);
720
  }
721

    
722
  $css = <<<EOM
723
/* ___________ IE6 IFRAME FIX ________ */
724
.ui-datepicker-cover {
725
  display: none; /*sorry for IE5*/
726
  display/**/: block; /*sorry for IE5*/
727
  position: absolute; /*must have*/
728
  z-index: -1; /*must have*/
729
  filter: mask(); /*must have*/
730
  top: -4px; /*must have*/
731
  left: -4px; /*must have*/ /* LTR */
732
  width: 200px; /*must have*/
733
  height: 200px; /*must have*/
734
}
735
EOM;
736

    
737
  $form['#suffix'] = t('<p>The Date Popup calendar includes some css for IE6 that breaks css validation. Since IE 6 is now superceded by IE 7, 8, and 9, the special css for IE 6 has been removed from the regular css used by the Date Popup. If you find you need that css after all, you can add it back in your theme. Look at the way the Garland theme adds special IE-only css in in its page.tpl.php file. The css you need is:</p>') .'<blockquote><PRE>' . $css .'</PRE></blockquote>';
738

    
739
  return system_settings_form($form);
740
}