Projet

Général

Profil

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

root / drupal7 / sites / all / modules / webform / js / webform.js @ 8c72e82a

1
/**
2
 * @file
3
 * JavaScript behaviors for the front-end display of webforms.
4
 */
5

    
6
(function ($) {
7

    
8
  "use strict";
9

    
10
  Drupal.behaviors.webform = Drupal.behaviors.webform || {};
11

    
12
  Drupal.behaviors.webform.attach = function (context) {
13
    // Calendar datepicker behavior.
14
    Drupal.webform.datepicker(context);
15

    
16
    // Conditional logic.
17
    if (Drupal.settings.webform && Drupal.settings.webform.conditionals) {
18
      Drupal.webform.conditional(context);
19
    }
20
  };
21

    
22
  Drupal.webform = Drupal.webform || {};
23

    
24
  Drupal.webform.datepicker = function (context) {
25
    $('div.webform-datepicker').each(function () {
26
      var $webformDatepicker = $(this);
27
      var $calendar = $webformDatepicker.find('input.webform-calendar');
28

    
29
      // Ensure the page we're on actually contains a datepicker.
30
      if ($calendar.length == 0) {
31
        return;
32
      }
33

    
34
      var startDate = $calendar[0].className.replace(/.*webform-calendar-start-(\d{4}-\d{2}-\d{2}).*/, '$1').split('-');
35
      var endDate = $calendar[0].className.replace(/.*webform-calendar-end-(\d{4}-\d{2}-\d{2}).*/, '$1').split('-');
36
      var firstDay = $calendar[0].className.replace(/.*webform-calendar-day-(\d).*/, '$1');
37
      // Convert date strings into actual Date objects.
38
      startDate = new Date(startDate[0], startDate[1] - 1, startDate[2]);
39
      endDate = new Date(endDate[0], endDate[1] - 1, endDate[2]);
40

    
41
      // Ensure that start comes before end for datepicker.
42
      if (startDate > endDate) {
43
        var laterDate = startDate;
44
        startDate = endDate;
45
        endDate = laterDate;
46
      }
47

    
48
      var startYear = startDate.getFullYear();
49
      var endYear = endDate.getFullYear();
50

    
51
      // Set up the jQuery datepicker element.
52
      $calendar.datepicker({
53
        dateFormat: 'yy-mm-dd',
54
        yearRange: startYear + ':' + endYear,
55
        firstDay: parseInt(firstDay),
56
        minDate: startDate,
57
        maxDate: endDate,
58
        onSelect: function (dateText, inst) {
59
          var date = dateText.split('-');
60
          $webformDatepicker.find('select.year, input.year').val(+date[0]).trigger('change');
61
          $webformDatepicker.find('select.month').val(+date[1]).trigger('change');
62
          $webformDatepicker.find('select.day').val(+date[2]).trigger('change');
63
        },
64
        beforeShow: function (input, inst) {
65
          // Get the select list values.
66
          var year = $webformDatepicker.find('select.year, input.year').val();
67
          var month = $webformDatepicker.find('select.month').val();
68
          var day = $webformDatepicker.find('select.day').val();
69

    
70
          // If empty, default to the current year/month/day in the popup.
71
          var today = new Date();
72
          year = year ? year : today.getFullYear();
73
          month = month ? month : today.getMonth() + 1;
74
          day = day ? day : today.getDate();
75

    
76
          // Make sure that the default year fits in the available options.
77
          year = (year < startYear || year > endYear) ? startYear : year;
78

    
79
          // jQuery UI Datepicker will read the input field and base its date off
80
          // of that, even though in our case the input field is a button.
81
          $(input).val(year + '-' + month + '-' + day);
82
        }
83
      });
84

    
85
      // Prevent the calendar button from submitting the form.
86
      $calendar.click(function (event) {
87
        $(this).focus();
88
        event.preventDefault();
89
      });
90
    });
91
  };
92

    
93
  Drupal.webform.conditional = function (context) {
94
    // Add the bindings to each webform on the page.
95
    $.each(Drupal.settings.webform.conditionals, function (formKey, settings) {
96
      var $form = $('.' + formKey + ':not(.webform-conditional-processed)');
97
      $form.each(function (index, currentForm) {
98
        var $currentForm = $(currentForm);
99
        $currentForm.addClass('webform-conditional-processed');
100
        $currentForm.bind('change', {'settings': settings}, Drupal.webform.conditionalCheck);
101

    
102
        // Trigger all the elements that cause conditionals on this form.
103
        Drupal.webform.doConditions($currentForm, settings);
104
      });
105
    });
106
  };
107

    
108
  /**
109
   * Event handler to respond to field changes in a form.
110
   *
111
   * This event is bound to the entire form, not individual fields.
112
   */
113
  Drupal.webform.conditionalCheck = function (e) {
114
    var $triggerElement = $(e.target).closest('.webform-component');
115
    var $form = $triggerElement.closest('form');
116
    var triggerElementKey = $triggerElement.attr('class').match(/webform-component--[^ ]+/)[0];
117
    var settings = e.data.settings;
118
    if (settings.sourceMap[triggerElementKey]) {
119
      Drupal.webform.doConditions($form, settings);
120
    }
121
  };
122

    
123
  /**
124
   * Processes all conditional.
125
   */
126
  Drupal.webform.doConditions = function ($form, settings) {
127

    
128
    var stackPointer;
129
    var resultStack;
130

    
131
    /**
132
     * Initializes an execution stack for a conditional group's rules and
133
     * sub-conditional rules.
134
     */
135
    function executionStackInitialize(andor) {
136
      stackPointer = -1;
137
      resultStack = [];
138
      executionStackPush(andor);
139
    }
140

    
141
    /**
142
     * Starts a new subconditional for the given and/or operator.
143
     */
144
    function executionStackPush(andor) {
145
      resultStack[++stackPointer] = {
146
        results: [],
147
        andor: andor,
148
      };
149
    }
150

    
151
    /**
152
     * Adds a rule's result to the current sub-condtional.
153
     */
154
    function executionStackAccumulate(result) {
155
      resultStack[stackPointer]['results'].push(result);
156
    }
157

    
158
    /**
159
     * Finishes a sub-conditional and adds the result to the parent stack frame.
160
     */
161
    function executionStackPop() {
162
      // Calculate the and/or result.
163
      var stackFrame = resultStack[stackPointer];
164
      // Pop stack and protect against stack underflow.
165
      stackPointer = Math.max(0, stackPointer - 1);
166
      var $conditionalResults = stackFrame['results'];
167
      var filteredResults = $.map($conditionalResults, function(val) {
168
        return val ? val : null;
169
      });
170
      return stackFrame['andor'] === 'or'
171
                ? filteredResults.length > 0
172
                : filteredResults.length === $conditionalResults.length;
173
    }
174

    
175
    // Track what has be set/shown for each target component.
176
    var targetLocked = [];
177

    
178
    $.each(settings.ruleGroups, function (rgid_key, rule_group) {
179
      var ruleGroup = settings.ruleGroups[rgid_key];
180

    
181
      // Perform the comparison callback and build the results for this group.
182
      executionStackInitialize(ruleGroup['andor']);
183
      $.each(ruleGroup['rules'], function (m, rule) {
184
        switch (rule['source_type']) {
185
          case 'component':
186
            var elementKey = rule['source'];
187
            var element = $form.find('.' + elementKey)[0];
188
            var existingValue = settings.values[elementKey] ? settings.values[elementKey] : null;
189
            executionStackAccumulate(window['Drupal']['webform'][rule.callback](element, existingValue, rule['value']));
190
            break;
191
          case 'conditional_start':
192
            executionStackPush(rule['andor']);
193
            break;
194
          case 'conditional_end':
195
            executionStackAccumulate(executionStackPop());
196
            break;
197
        }
198
      });
199
      var conditionalResult = executionStackPop();
200

    
201
      $.each(ruleGroup['actions'], function (aid, action) {
202
        var $target = $form.find('.' + action['target']);
203
        var actionResult = action['invert'] ? !conditionalResult : conditionalResult;
204
        switch (action['action']) {
205
          case 'show':
206
            if (actionResult != Drupal.webform.isVisible($target)) {
207
              var $targetElements = actionResult
208
                                      ? $target.find('.webform-conditional-disabled').removeClass('webform-conditional-disabled')
209
                                      : $target.find(':input').addClass('webform-conditional-disabled');
210
              $targetElements.webformProp('disabled', !actionResult);
211
              $target.toggleClass('webform-conditional-hidden', !actionResult);
212
              if (actionResult) {
213
                $target.show();
214
              }
215
              else {
216
                $target.hide();
217
                // Record that the target was hidden.
218
                targetLocked[action['target']] = 'hide';
219
              }
220
              if ($target.is('tr')) {
221
                Drupal.webform.restripeTable($target.closest('table').first());
222
              }
223
            }
224
            break;
225
          case 'require':
226
            var $requiredSpan = $target.find('.form-required, .form-optional').first();
227
            if (actionResult != $requiredSpan.hasClass('form-required')) {
228
              var $targetInputElements = $target.find("input:text,textarea,input[type='email'],select,input:radio,input:file");
229
              // Rather than hide the required tag, remove it so that other jQuery can respond via Drupal behaviors.
230
              Drupal.detachBehaviors($requiredSpan);
231
              $targetInputElements
232
                .webformProp('required', actionResult)
233
                .toggleClass('required', actionResult);
234
              if (actionResult) {
235
                $requiredSpan.replaceWith('<span class="form-required" title="' + Drupal.t('This field is required.') + '">*</span>');
236
              }
237
              else {
238
                $requiredSpan.replaceWith('<span class="form-optional"></span>');
239
              }
240
              Drupal.attachBehaviors($requiredSpan);
241
            }
242
            break;
243
          case 'set':
244
            var isLocked = targetLocked[action['target']];
245
            var $texts = $target.find("input:text,textarea,input[type='email']");
246
            var $selects = $target.find('select,select option,input:radio,input:checkbox');
247
            var $markups = $target.filter('.webform-component-markup');
248
            if (actionResult) {
249
              var multiple = $.map(action['argument'].split(','), $.trim);
250
              $selects.webformVal(multiple);
251
              $texts.val([action['argument']]);
252
              // A special case is made for markup. It is sanitized with filter_xss_admin on the server.
253
              // otherwise text() should be used to avoid an XSS vulnerability. text() however would
254
              // preclude the use of tags like <strong> or <a>
255
              $markups.html(action['argument']);
256
            }
257
            else {
258
              // Markup not set? Then restore original markup as provided in
259
              // the attribute data-webform-markup.
260
              $markups.each(function() {
261
                var $this = $(this);
262
                var original = $this.data('webform-markup');
263
                if (original !== undefined) {
264
                  $this.html(original);
265
                }
266
              });
267
            }
268
            if (!isLocked) {
269
              // If not previously hidden or set, disable the element readonly or readonly-like behavior.
270
              $selects.webformProp('disabled', actionResult);
271
              $texts.webformProp('readonly', actionResult);
272
              targetLocked[action['target']] = actionResult ? 'set' : false;
273
            }
274
            break;
275
        }
276
      }); // End look on each action for one conditional
277
    }); // End loop on each conditional
278
  };
279

    
280
  /**
281
   * Event handler to prevent propogation of events, typically click for disabling
282
   * radio and checkboxes.
283
   */
284
  Drupal.webform.stopEvent = function () {
285
    return false;
286
  };
287

    
288
  Drupal.webform.conditionalOperatorStringEqual = function (element, existingValue, ruleValue) {
289
    var returnValue = false;
290
    var currentValue = Drupal.webform.stringValue(element, existingValue);
291
    $.each(currentValue, function (n, value) {
292
      if (value.toLowerCase() === ruleValue.toLowerCase()) {
293
        returnValue = true;
294
        return false; // break.
295
      }
296
    });
297
    return returnValue;
298
  };
299

    
300
  Drupal.webform.conditionalOperatorStringNotEqual = function (element, existingValue, ruleValue) {
301
    var found = false;
302
    var currentValue = Drupal.webform.stringValue(element, existingValue);
303
    $.each(currentValue, function (n, value) {
304
      if (value.toLowerCase() === ruleValue.toLowerCase()) {
305
        found = true;
306
      }
307
    });
308
    return !found;
309
  };
310

    
311
  Drupal.webform.conditionalOperatorStringContains = function (element, existingValue, ruleValue) {
312
    var returnValue = false;
313
    var currentValue = Drupal.webform.stringValue(element, existingValue);
314
    $.each(currentValue, function (n, value) {
315
      if (value.toLowerCase().indexOf(ruleValue.toLowerCase()) > -1) {
316
        returnValue = true;
317
        return false; // break.
318
      }
319
    });
320
    return returnValue;
321
  };
322

    
323
  Drupal.webform.conditionalOperatorStringDoesNotContain = function (element, existingValue, ruleValue) {
324
    var found = false;
325
    var currentValue = Drupal.webform.stringValue(element, existingValue);
326
    $.each(currentValue, function (n, value) {
327
      if (value.toLowerCase().indexOf(ruleValue.toLowerCase()) > -1) {
328
        found = true;
329
      }
330
    });
331
    return !found;
332
  };
333

    
334
  Drupal.webform.conditionalOperatorStringBeginsWith = function (element, existingValue, ruleValue) {
335
    var returnValue = false;
336
    var currentValue = Drupal.webform.stringValue(element, existingValue);
337
    $.each(currentValue, function (n, value) {
338
      if (value.toLowerCase().indexOf(ruleValue.toLowerCase()) === 0) {
339
        returnValue = true;
340
        return false; // break.
341
      }
342
    });
343
    return returnValue;
344
  };
345

    
346
  Drupal.webform.conditionalOperatorStringEndsWith = function (element, existingValue, ruleValue) {
347
    var returnValue = false;
348
    var currentValue = Drupal.webform.stringValue(element, existingValue);
349
    $.each(currentValue, function (n, value) {
350
      if (value.toLowerCase().lastIndexOf(ruleValue.toLowerCase()) === value.length - ruleValue.length) {
351
        returnValue = true;
352
        return false; // break.
353
      }
354
    });
355
    return returnValue;
356
  };
357

    
358
  Drupal.webform.conditionalOperatorStringEmpty = function (element, existingValue, ruleValue) {
359
    var currentValue = Drupal.webform.stringValue(element, existingValue);
360
    var returnValue = true;
361
    $.each(currentValue, function (n, value) {
362
      if (value !== '') {
363
        returnValue = false;
364
        return false; // break.
365
      }
366
    });
367
    return returnValue;
368
  };
369

    
370
  Drupal.webform.conditionalOperatorStringNotEmpty = function (element, existingValue, ruleValue) {
371
    return !Drupal.webform.conditionalOperatorStringEmpty(element, existingValue, ruleValue);
372
  };
373

    
374
  Drupal.webform.conditionalOperatorSelectGreaterThan = function (element, existingValue, ruleValue) {
375
    var currentValue = Drupal.webform.stringValue(element, existingValue);
376
    return Drupal.webform.compare_select(currentValue[0], ruleValue, element) > 0;
377
  };
378

    
379
  Drupal.webform.conditionalOperatorSelectGreaterThanEqual = function (element, existingValue, ruleValue) {
380
    var currentValue = Drupal.webform.stringValue(element, existingValue);
381
    var comparison = Drupal.webform.compare_select(currentValue[0], ruleValue, element);
382
    return comparison > 0 || comparison === 0;
383
  };
384

    
385
  Drupal.webform.conditionalOperatorSelectLessThan = function (element, existingValue, ruleValue) {
386
    var currentValue = Drupal.webform.stringValue(element, existingValue);
387
    return Drupal.webform.compare_select(currentValue[0], ruleValue, element) < 0;
388
  };
389

    
390
  Drupal.webform.conditionalOperatorSelectLessThanEqual = function (element, existingValue, ruleValue) {
391
    var currentValue = Drupal.webform.stringValue(element, existingValue);
392
    var comparison = Drupal.webform.compare_select(currentValue[0], ruleValue, element);
393
    return comparison < 0 || comparison === 0;
394
  };
395

    
396
  Drupal.webform.conditionalOperatorNumericEqual = function (element, existingValue, ruleValue) {
397
    // See float comparison: http://php.net/manual/en/language.types.float.php
398
    var currentValue = Drupal.webform.stringValue(element, existingValue);
399
    var epsilon = 0.000001;
400
    // An empty string does not match any number.
401
    return currentValue[0] === '' ? false : (Math.abs(parseFloat(currentValue[0]) - parseFloat(ruleValue)) < epsilon);
402
  };
403

    
404
  Drupal.webform.conditionalOperatorNumericNotEqual = function (element, existingValue, ruleValue) {
405
    // See float comparison: http://php.net/manual/en/language.types.float.php
406
    var currentValue = Drupal.webform.stringValue(element, existingValue);
407
    var epsilon = 0.000001;
408
    // An empty string does not match any number.
409
    return currentValue[0] === '' ? true : (Math.abs(parseFloat(currentValue[0]) - parseFloat(ruleValue)) >= epsilon);
410
  };
411

    
412
  Drupal.webform.conditionalOperatorNumericGreaterThan = function (element, existingValue, ruleValue) {
413
    var currentValue = Drupal.webform.stringValue(element, existingValue);
414
    return parseFloat(currentValue[0]) > parseFloat(ruleValue);
415
  };
416

    
417
  Drupal.webform.conditionalOperatorNumericGreaterThanEqual = function (element, existingValue, ruleValue) {
418
    return Drupal.webform.conditionalOperatorNumericGreaterThan(element, existingValue, ruleValue) ||
419
           Drupal.webform.conditionalOperatorNumericEqual(element, existingValue, ruleValue);
420
  };
421

    
422
  Drupal.webform.conditionalOperatorNumericLessThan = function (element, existingValue, ruleValue) {
423
    var currentValue = Drupal.webform.stringValue(element, existingValue);
424
    return parseFloat(currentValue[0]) < parseFloat(ruleValue);
425
  };
426

    
427
  Drupal.webform.conditionalOperatorNumericLessThanEqual = function (element, existingValue, ruleValue) {
428
    return Drupal.webform.conditionalOperatorNumericLessThan(element, existingValue, ruleValue) ||
429
           Drupal.webform.conditionalOperatorNumericEqual(element, existingValue, ruleValue);
430
  };
431

    
432
  Drupal.webform.conditionalOperatorDateEqual = function (element, existingValue, ruleValue) {
433
    var currentValue = Drupal.webform.dateValue(element, existingValue);
434
    return currentValue === ruleValue;
435
  };
436

    
437
  Drupal.webform.conditionalOperatorDateNotEqual = function (element, existingValue, ruleValue) {
438
    return !Drupal.webform.conditionalOperatorDateEqual(element, existingValue, ruleValue);
439
  };
440

    
441
  Drupal.webform.conditionalOperatorDateBefore = function (element, existingValue, ruleValue) {
442
    var currentValue = Drupal.webform.dateValue(element, existingValue);
443
    return (currentValue !== false) && currentValue < ruleValue;
444
  };
445

    
446
  Drupal.webform.conditionalOperatorDateBeforeEqual = function (element, existingValue, ruleValue) {
447
    var currentValue = Drupal.webform.dateValue(element, existingValue);
448
    return (currentValue !== false) && (currentValue < ruleValue || currentValue === ruleValue);
449
  };
450

    
451
  Drupal.webform.conditionalOperatorDateAfter = function (element, existingValue, ruleValue) {
452
    var currentValue = Drupal.webform.dateValue(element, existingValue);
453
    return (currentValue !== false) && currentValue > ruleValue;
454
  };
455

    
456
  Drupal.webform.conditionalOperatorDateAfterEqual = function (element, existingValue, ruleValue) {
457
    var currentValue = Drupal.webform.dateValue(element, existingValue);
458
    return (currentValue !== false) && (currentValue > ruleValue || currentValue === ruleValue);
459
  };
460

    
461
  Drupal.webform.conditionalOperatorTimeEqual = function (element, existingValue, ruleValue) {
462
    var currentValue = Drupal.webform.timeValue(element, existingValue);
463
    return currentValue === ruleValue;
464
  };
465

    
466
  Drupal.webform.conditionalOperatorTimeNotEqual = function (element, existingValue, ruleValue) {
467
    return !Drupal.webform.conditionalOperatorTimeEqual(element, existingValue, ruleValue);
468
  };
469

    
470
  Drupal.webform.conditionalOperatorTimeBefore = function (element, existingValue, ruleValue) {
471
    // Date and time operators intentionally exclusive for "before".
472
    var currentValue = Drupal.webform.timeValue(element, existingValue);
473
    return (currentValue !== false) && (currentValue < ruleValue);
474
  };
475

    
476
  Drupal.webform.conditionalOperatorTimeBeforeEqual = function (element, existingValue, ruleValue) {
477
    // Date and time operators intentionally exclusive for "before".
478
    var currentValue = Drupal.webform.timeValue(element, existingValue);
479
    return (currentValue !== false) && (currentValue < ruleValue || currentValue === ruleValue);
480
  };
481

    
482
  Drupal.webform.conditionalOperatorTimeAfter = function (element, existingValue, ruleValue) {
483
    // Date and time operators intentionally inclusive for "after".
484
    var currentValue = Drupal.webform.timeValue(element, existingValue);
485
    return (currentValue !== false) && (currentValue > ruleValue);
486
  };
487

    
488
  Drupal.webform.conditionalOperatorTimeAfterEqual = function (element, existingValue, ruleValue) {
489
    // Date and time operators intentionally inclusive for "after".
490
    var currentValue = Drupal.webform.timeValue(element, existingValue);
491
    return (currentValue !== false) && (currentValue > ruleValue || currentValue === ruleValue);
492
  };
493

    
494
  /**
495
   * Utility function to compare values of a select component.
496
   * @param string a
497
   *   First select option key to compare
498
   * @param string b
499
   *   Second select option key to compare
500
   * @param array options
501
   *   Associative array where the a and b are within the keys
502
   * @return integer based upon position of $a and $b in $options
503
   *   -N if $a above (<) $b
504
   *   0 if $a = $b
505
   *   +N if $a is below (>) $b
506
   */
507
  Drupal.webform.compare_select = function (a, b, element) {
508
    var optionList = [];
509
    $('option,input:radio,input:checkbox', element).each(function () {
510
      optionList.push($(this).val());
511
    });
512
    var a_position = optionList.indexOf(a);
513
    var b_position = optionList.indexOf(b);
514
    return (a_position < 0 || b_position < 0) ? null : a_position - b_position;
515
  };
516

    
517
  /**
518
   * Utility to return current visibility. Uses actual visibility, except for
519
   * hidden components which use the applied disabled class.
520
   */
521
  Drupal.webform.isVisible = function ($element) {
522
    return $element.hasClass('webform-component-hidden')
523
              ? !$element.find('input').first().hasClass('webform-conditional-disabled')
524
              : $element.closest('.webform-conditional-hidden').length == 0;
525
  };
526

    
527
  /**
528
   * Utility function to get a string value from a select/radios/text/etc. field.
529
   */
530
  Drupal.webform.stringValue = function (element, existingValue) {
531
    var value = [];
532
    if (element) {
533
      var $element = $(element);
534
      if (Drupal.webform.isVisible($element)) {
535
        // Checkboxes and radios.
536
        $element.find('input[type=checkbox]:checked,input[type=radio]:checked').each(function () {
537
          value.push(this.value);
538
        });
539
        // Select lists.
540
        if (!value.length) {
541
          var selectValue = $element.find('select').val();
542
          if (selectValue) {
543
            if ($.isArray(selectValue)) {
544
              value = selectValue;
545
            }
546
            else {
547
              value.push(selectValue);
548
            }
549
          }
550
        }
551
        // Simple text fields. This check is done last so that the select list in
552
        // select-or-other fields comes before the "other" text field.
553
        if (!value.length) {
554
          $element.find('input:not([type=checkbox],[type=radio]),textarea').each(function () {
555
            value.push(this.value);
556
          });
557
        }
558
      }
559
    }
560
    else {
561
      switch ($.type(existingValue)) {
562
        case 'array':
563
          value = existingValue;
564
          break;
565
        case 'string':
566
          value.push(existingValue);
567
          break;
568
      }
569
    }
570
    return value;
571
  };
572

    
573
  /**
574
   * Utility function to calculate a second-based timestamp from a time field.
575
   */
576
  Drupal.webform.dateValue = function (element, existingValue) {
577
    var value = false;
578
    if (element) {
579
      var $element = $(element);
580
      if (Drupal.webform.isVisible($element)) {
581
        var day = $element.find('[name*=day]').val();
582
        var month = $element.find('[name*=month]').val();
583
        var year = $element.find('[name*=year]').val();
584
        // Months are 0 indexed in JavaScript.
585
        if (month) {
586
          month--;
587
        }
588
        if (year !== '' && month !== '' && day !== '') {
589
          value = Date.UTC(year, month, day) / 1000;
590
        }
591
      }
592
    }
593
    else {
594
      if ($.type(existingValue) === 'array' && existingValue.length) {
595
        existingValue = existingValue[0];
596
      }
597
      if ($.type(existingValue) === 'string') {
598
        existingValue = existingValue.split('-');
599
      }
600
      if (existingValue.length === 3) {
601
        value = Date.UTC(existingValue[0], existingValue[1], existingValue[2]) / 1000;
602
      }
603
    }
604
    return value;
605
  };
606

    
607
  /**
608
   * Utility function to calculate a millisecond timestamp from a time field.
609
   */
610
  Drupal.webform.timeValue = function (element, existingValue) {
611
    var value = false;
612
    if (element) {
613
      var $element = $(element);
614
      if (Drupal.webform.isVisible($element)) {
615
        var hour = $element.find('[name*=hour]').val();
616
        var minute = $element.find('[name*=minute]').val();
617
        var ampm = $element.find('[name*=ampm]:checked').val();
618

    
619
        // Convert to integers if set.
620
        hour = (hour === '') ? hour : parseInt(hour);
621
        minute = (minute === '') ? minute : parseInt(minute);
622

    
623
        if (hour !== '') {
624
          hour = (hour < 12 && ampm == 'pm') ? hour + 12 : hour;
625
          hour = (hour === 12 && ampm == 'am') ? 0 : hour;
626
        }
627
        if (hour !== '' && minute !== '') {
628
          value = Date.UTC(1970, 0, 1, hour, minute) / 1000;
629
        }
630
      }
631
    }
632
    else {
633
      if ($.type(existingValue) === 'array' && existingValue.length) {
634
        existingValue = existingValue[0];
635
      }
636
      if ($.type(existingValue) === 'string') {
637
        existingValue = existingValue.split(':');
638
      }
639
      if (existingValue.length >= 2) {
640
        value = Date.UTC(1970, 0, 1, existingValue[0], existingValue[1]) / 1000;
641
      }
642
    }
643
    return value;
644
  };
645

    
646
  /**
647
   * Make a prop shim for jQuery < 1.9.
648
   */
649
  $.fn.webformProp = $.fn.webformProp || function (name, value) {
650
    if (value) {
651
      return $.fn.prop ? this.prop(name, true) : this.attr(name, true);
652
    }
653
    else {
654
      return $.fn.prop ? this.prop(name, false) : this.removeAttr(name);
655
    }
656
  };
657

    
658
  /**
659
   * Make a multi-valued val() function for setting checkboxes, radios, and select
660
   * elements.
661
   */
662
  $.fn.webformVal = function (values) {
663
    this.each(function () {
664
      var $this = $(this);
665
      var value = $this.val();
666
      var on = $.inArray($this.val(), values) != -1;
667
      if (this.nodeName == 'OPTION') {
668
        $this.webformProp('selected', on ? value : false);
669
      }
670
      else {
671
        $this.val(on ? [value] : false);
672
      }
673
    });
674
    return this;
675
  };
676

    
677
  /**
678
   * Given a table's DOM element, restripe the odd/even classes.
679
   */
680
  Drupal.webform.restripeTable = function (table) {
681
    // :even and :odd are reversed because jQuery counts from 0 and
682
    // we count from 1, so we're out of sync.
683
    // Match immediate children of the parent element to allow nesting.
684
    $('> tbody > tr, > tr', table)
685
      .filter(':visible:odd').filter('.odd')
686
        .removeClass('odd').addClass('even')
687
      .end().end()
688
      .filter(':visible:even').filter('.even')
689
        .removeClass('even').addClass('odd');
690
  };
691

    
692
})(jQuery);