Contact Form 7 - Version 3.3

Version Description

  • New: Introduce a new special mail tag [_user_agent] for user agent information.
  • New: Make WordPress Comment Blacklist applicable for inputs through contact forms.
  • New: Introduce new form of mail tag [_raw_{field name}]. This allows to output raw user input those have not been modified with pipes.
  • New: Make mail tags available in response messages.
  • New: Introduce new additional setting on_submit. It works like on_sent_ok and has one-line JavaScript code, but on_submit code is fired regardless of whether or not the mail has been sent successfully.
  • New: Introduce 5 new jQuery custom event triggers (invalid.wpcf7, spam.wpcf7, mailsent.wpcf7, mailfailed.wpcf7, submit.wpcf7).
  • Fix: Nonce used in a form have been changed to have no time limit.
  • Fix: Make every post metas key have underscore prefix.
  • The jQuery Form Plugin (jquery.form.js) has been updated to 3.15 and compressed with YUI compressor.
  • The required WordPress version has been changed and now requires WordPress 3.3 or higher. If you use WordPress 3.2, you will need to upgrade WordPress.
  • Translations for Arabic and German have been updated.

=

Download this release

Release Info

Developer takayukister
Plugin Icon 128x128 Contact Form 7
Version 3.3
Comparing to
See all releases

Code changes from version 3.2.1 to 3.3

admin/admin.php CHANGED
@@ -235,34 +235,6 @@ function wpcf7_admin_enqueue_scripts( $hook_suffix ) {
235
  'tagGenerators' => wpcf7_tag_generators() ) );
236
  }
237
 
238
- add_action( 'admin_print_footer_scripts', 'wpcf7_print_taggenerators_json', 20 );
239
-
240
- function wpcf7_print_taggenerators_json() { // for backward compatibility
241
- global $plugin_page, $wpcf7_tag_generators;
242
-
243
- if ( ! version_compare( get_bloginfo( 'version' ), '3.3-dev', '<' ) )
244
- return;
245
-
246
- if ( ! isset( $plugin_page ) || 'wpcf7' != $plugin_page )
247
- return;
248
-
249
- $taggenerators = array();
250
-
251
- foreach ( (array) $wpcf7_tag_generators as $name => $tg ) {
252
- $taggenerators[$name] = array_merge(
253
- (array) $tg['options'],
254
- array( 'title' => $tg['title'], 'content' => $tg['content'] ) );
255
- }
256
-
257
- ?>
258
- <script type="text/javascript">
259
- /* <![CDATA[ */
260
- _wpcf7.tagGenerators = <?php echo json_encode( $taggenerators ) ?>;
261
- /* ]]> */
262
- </script>
263
- <?php
264
- }
265
-
266
  function wpcf7_admin_management_page() {
267
  global $wpcf7_contact_form;
268
 
235
  'tagGenerators' => wpcf7_tag_generators() ) );
236
  }
237
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
  function wpcf7_admin_management_page() {
239
  global $wpcf7_contact_form;
240
 
includes/classes.php CHANGED
@@ -65,11 +65,14 @@ class WPCF7_ContactForm {
65
  $this->id = $post->ID;
66
  $this->title = $post->post_title;
67
 
68
- $this->form = get_post_meta( $post->ID, 'form', true );
69
- $this->mail = get_post_meta( $post->ID, 'mail', true );
70
- $this->mail_2 = get_post_meta( $post->ID, 'mail_2', true );
71
- $this->messages = get_post_meta( $post->ID, 'messages', true );
72
- $this->additional_settings = get_post_meta( $post->ID, 'additional_settings', true );
 
 
 
73
 
74
  $this->upgrade();
75
  }
@@ -77,6 +80,17 @@ class WPCF7_ContactForm {
77
  do_action_ref_array( 'wpcf7_contact_form', array( &$this ) );
78
  }
79
 
 
 
 
 
 
 
 
 
 
 
 
80
  // Return true if this form is the same one as currently POSTed.
81
  function is_posted() {
82
  if ( ! isset( $_POST['_wpcf7_unit_tag'] ) || empty( $_POST['_wpcf7_unit_tag'] ) )
@@ -119,16 +133,14 @@ class WPCF7_ContactForm {
119
  $class = 'wpcf7-form';
120
 
121
  if ( $this->is_posted() ) {
122
- if ( ! empty( $_POST['_wpcf7_validation_errors'] ) ) {
123
  $class .= ' invalid';
124
- } elseif ( ! empty( $_POST['_wpcf7_mail_sent'] ) ) {
125
- if ( ! empty( $_POST['_wpcf7_mail_sent']['spam'] ) )
126
- $class .= ' spam';
127
- elseif ( ! empty( $_POST['_wpcf7_mail_sent']['ok'] ) )
128
- $class .= ' sent';
129
- else
130
- $class .= ' failed';
131
- }
132
  }
133
 
134
  $class = apply_filters( 'wpcf7_form_class_attr', $class );
@@ -159,7 +171,7 @@ class WPCF7_ContactForm {
159
  '_wpcf7_unit_tag' => $this->unit_tag );
160
 
161
  if ( WPCF7_VERIFY_NONCE )
162
- $hidden_fields['_wpnonce'] = wpcf7_create_nonce( $this->unit_tag );
163
 
164
  $content = '';
165
 
@@ -177,20 +189,18 @@ class WPCF7_ContactForm {
177
  $content = '';
178
 
179
  if ( $this->is_posted() ) { // Post response output for non-AJAX
180
- if ( isset( $_POST['_wpcf7_mail_sent'] ) && $_POST['_wpcf7_mail_sent']['id'] == $this->id ) {
181
- if ( $_POST['_wpcf7_mail_sent']['ok'] ) {
182
- $class .= ' wpcf7-mail-sent-ok';
183
- $content = $_POST['_wpcf7_mail_sent']['message'];
184
- } else {
185
- $class .= ' wpcf7-mail-sent-ng';
186
- if ( $_POST['_wpcf7_mail_sent']['spam'] )
187
- $class .= ' wpcf7-spam-blocked';
188
- $content = $_POST['_wpcf7_mail_sent']['message'];
189
- }
190
- } elseif ( isset( $_POST['_wpcf7_validation_errors'] ) && $_POST['_wpcf7_validation_errors']['id'] == $this->id ) {
191
  $class .= ' wpcf7-validation-errors';
192
- $content = $this->message( 'validation_error' );
193
- }
 
 
 
 
 
 
 
194
  } else {
195
  $class .= ' wpcf7-display-none';
196
  }
@@ -204,17 +214,16 @@ class WPCF7_ContactForm {
204
  if ( ! $this->is_posted() )
205
  return '';
206
 
207
- if ( ! isset( $_POST['_wpcf7_validation_errors']['messages'][$name] ) )
208
  return '';
209
 
210
- $ve = trim( $_POST['_wpcf7_validation_errors']['messages'][$name] );
211
 
212
- if ( ! empty( $ve ) ) {
213
- $ve = '<span class="wpcf7-not-valid-tip-no-ajax">' . esc_html( $ve ) . '</span>';
214
- return apply_filters( 'wpcf7_validation_error', $ve, $name, $this );
215
- }
216
 
217
- return '';
 
218
  }
219
 
220
  /* Form Elements */
@@ -334,7 +343,8 @@ class WPCF7_ContactForm {
334
  'spam' => false,
335
  'message' => '',
336
  'mail_sent' => false,
337
- 'scripts_on_sent_ok' => null );
 
338
 
339
  $this->setup_posted_data();
340
 
@@ -371,6 +381,13 @@ class WPCF7_ContactForm {
371
  $result['message'] = $this->message( 'mail_sent_ng' );
372
  }
373
 
 
 
 
 
 
 
 
374
  // remove upload files
375
  foreach ( (array) $this->uploaded_files as $name => $path ) {
376
  @unlink( $path );
@@ -407,11 +424,24 @@ class WPCF7_ContactForm {
407
  if ( WPCF7_VERIFY_NONCE && ! $this->verify_nonce() )
408
  $spam = true;
409
 
 
 
 
410
  return apply_filters( 'wpcf7_spam', $spam );
411
  }
412
 
413
  function verify_nonce() {
414
- return wpcf7_verify_nonce( $_POST['_wpnonce'], $_POST['_wpcf7_unit_tag'] );
 
 
 
 
 
 
 
 
 
 
415
  }
416
 
417
  /* Mail */
@@ -464,24 +494,18 @@ class WPCF7_ContactForm {
464
  function compose_mail( $mail_template, $send = true ) {
465
  $this->mail_template_in_process = $mail_template;
466
 
467
- $regex = '/(\[?)\[\s*([a-zA-Z_][0-9a-zA-Z:._-]*)\s*\](\]?)/';
468
-
469
  $use_html = (bool) $mail_template['use_html'];
470
 
471
- $callback = array( &$this, 'mail_callback' );
472
- $callback_html = array( &$this, 'mail_callback_html' );
473
-
474
- $subject = preg_replace_callback( $regex, $callback, $mail_template['subject'] );
475
- $sender = preg_replace_callback( $regex, $callback, $mail_template['sender'] );
476
- $recipient = preg_replace_callback( $regex, $callback, $mail_template['recipient'] );
477
- $additional_headers =
478
- preg_replace_callback( $regex, $callback, $mail_template['additional_headers'] );
479
 
480
  if ( $use_html ) {
481
- $body = preg_replace_callback( $regex, $callback_html, $mail_template['body'] );
482
  $body = wpautop( $body );
483
  } else {
484
- $body = preg_replace_callback( $regex, $callback, $mail_template['body'] );
485
  }
486
 
487
  $attachments = array();
@@ -514,6 +538,17 @@ class WPCF7_ContactForm {
514
  return compact( 'subject', 'sender', 'body', 'recipient', 'headers', 'attachments' );
515
  }
516
 
 
 
 
 
 
 
 
 
 
 
 
517
  function mail_callback_html( $matches ) {
518
  return $this->mail_callback( $matches, true );
519
  }
@@ -536,12 +571,12 @@ class WPCF7_ContactForm {
536
  $replaced = wptexturize( $replaced );
537
  }
538
 
539
- $replaced = apply_filters( 'wpcf7_mail_tag_replaced', $replaced, $submitted );
540
 
541
  return stripslashes( $replaced );
542
  }
543
 
544
- if ( $special = apply_filters( 'wpcf7_special_mail_tags', '', $matches[2] ) )
545
  return $special;
546
 
547
  return $matches[0];
@@ -553,6 +588,8 @@ class WPCF7_ContactForm {
553
  $messages = $this->messages;
554
  $message = isset( $messages[$status] ) ? $messages[$status] : '';
555
 
 
 
556
  return apply_filters( 'wpcf7_display_message', $message, $status );
557
  }
558
 
@@ -610,16 +647,9 @@ class WPCF7_ContactForm {
610
  /* Save */
611
 
612
  function save() {
613
- $metas = array( 'form', 'mail', 'mail_2', 'messages', 'additional_settings' );
614
 
615
- $post_content = '';
616
-
617
- foreach ( $metas as $meta ) {
618
- $props = (array) $this->{$meta};
619
-
620
- foreach ( $props as $prop )
621
- $post_content .= "\n" . trim( $prop );
622
- }
623
 
624
  if ( $this->initial ) {
625
  $post_id = wp_insert_post( array(
@@ -636,8 +666,8 @@ class WPCF7_ContactForm {
636
  }
637
 
638
  if ( $post_id ) {
639
- foreach ( $metas as $meta )
640
- update_post_meta( $post_id, $meta, wpcf7_normalize_newline_deep( $this->{$meta} ) );
641
 
642
  if ( $this->initial ) {
643
  $this->initial = false;
@@ -658,11 +688,10 @@ class WPCF7_ContactForm {
658
  $new->initial = true;
659
  $new->title = $this->title . '_copy';
660
 
661
- $new->form = $this->form;
662
- $new->mail = $this->mail;
663
- $new->mail_2 = $this->mail_2;
664
- $new->messages = $this->messages;
665
- $new->additional_settings = $this->additional_settings;
666
 
667
  $new = apply_filters_ref_array( 'wpcf7_copy', array( &$new, &$this ) );
668
 
@@ -738,11 +767,10 @@ function wpcf7_get_contact_form_default_pack( $args = '' ) {
738
 
739
  $contact_form->title = ( $title ? $title : __( 'Untitled', 'wpcf7' ) );
740
 
741
- $contact_form->form = wpcf7_get_default_template( 'form' );
742
- $contact_form->mail = wpcf7_get_default_template( 'mail' );
743
- $contact_form->mail_2 = wpcf7_get_default_template( 'mail_2' );
744
- $contact_form->messages = wpcf7_get_default_template( 'messages' );
745
- $contact_form->additional_settings = wpcf7_get_default_template( 'additional_settings' );
746
 
747
  if ( isset( $mo_orig ) )
748
  $l10n['wpcf7'] = $mo_orig;
65
  $this->id = $post->ID;
66
  $this->title = $post->post_title;
67
 
68
+ $props = $this->get_properties();
69
+
70
+ foreach ( $props as $prop => $value ) {
71
+ if ( metadata_exists( 'post', $post->ID, '_' . $prop ) )
72
+ $this->{$prop} = get_post_meta( $post->ID, '_' . $prop, true );
73
+ else
74
+ $this->{$prop} = get_post_meta( $post->ID, $prop, true );
75
+ }
76
 
77
  $this->upgrade();
78
  }
80
  do_action_ref_array( 'wpcf7_contact_form', array( &$this ) );
81
  }
82
 
83
+ function get_properties() {
84
+ $prop_names = array( 'form', 'mail', 'mail_2', 'messages', 'additional_settings' );
85
+
86
+ $properties = array();
87
+
88
+ foreach ( $prop_names as $prop_name )
89
+ $properties[$prop_name] = isset( $this->{$prop_name} ) ? $this->{$prop_name} : '';
90
+
91
+ return apply_filters( 'wpcf7_contact_form_properties', $properties, $this );
92
+ }
93
+
94
  // Return true if this form is the same one as currently POSTed.
95
  function is_posted() {
96
  if ( ! isset( $_POST['_wpcf7_unit_tag'] ) || empty( $_POST['_wpcf7_unit_tag'] ) )
133
  $class = 'wpcf7-form';
134
 
135
  if ( $this->is_posted() ) {
136
+ if ( empty( $_POST['_wpcf7_result']['valid'] ) )
137
  $class .= ' invalid';
138
+ elseif ( ! empty( $_POST['_wpcf7_result']['spam'] ) )
139
+ $class .= ' spam';
140
+ elseif ( ! empty( $_POST['_wpcf7_result']['mail_sent'] ) )
141
+ $class .= ' sent';
142
+ else
143
+ $class .= ' failed';
 
 
144
  }
145
 
146
  $class = apply_filters( 'wpcf7_form_class_attr', $class );
171
  '_wpcf7_unit_tag' => $this->unit_tag );
172
 
173
  if ( WPCF7_VERIFY_NONCE )
174
+ $hidden_fields['_wpnonce'] = wpcf7_create_nonce( $this->id );
175
 
176
  $content = '';
177
 
189
  $content = '';
190
 
191
  if ( $this->is_posted() ) { // Post response output for non-AJAX
192
+
193
+ if ( empty( $_POST['_wpcf7_result']['valid'] ) )
 
 
 
 
 
 
 
 
 
194
  $class .= ' wpcf7-validation-errors';
195
+ elseif ( ! empty( $_POST['_wpcf7_result']['spam'] ) )
196
+ $class .= ' wpcf7-spam-blocked';
197
+ elseif ( ! empty( $_POST['_wpcf7_result']['mail_sent'] ) )
198
+ $class .= ' wpcf7-mail-sent-ok';
199
+ else
200
+ $class .= ' wpcf7-mail-sent-ng';
201
+
202
+ $content = $_POST['_wpcf7_result']['message'];
203
+
204
  } else {
205
  $class .= ' wpcf7-display-none';
206
  }
214
  if ( ! $this->is_posted() )
215
  return '';
216
 
217
+ if ( ! isset( $_POST['_wpcf7_result']['invalid_reasons'][$name] ) )
218
  return '';
219
 
220
+ $ve = trim( $_POST['_wpcf7_result']['invalid_reasons'][$name] );
221
 
222
+ if ( empty( $ve ) )
223
+ return '';
 
 
224
 
225
+ $ve = '<span class="wpcf7-not-valid-tip-no-ajax">' . esc_html( $ve ) . '</span>';
226
+ return apply_filters( 'wpcf7_validation_error', $ve, $name, $this );
227
  }
228
 
229
  /* Form Elements */
343
  'spam' => false,
344
  'message' => '',
345
  'mail_sent' => false,
346
+ 'scripts_on_sent_ok' => null,
347
+ 'scripts_on_submit' => null );
348
 
349
  $this->setup_posted_data();
350
 
381
  $result['message'] = $this->message( 'mail_sent_ng' );
382
  }
383
 
384
+ if ( $ajax ) {
385
+ $on_submit = $this->additional_setting( 'on_submit', false );
386
+
387
+ if ( ! empty( $on_submit ) )
388
+ $result['scripts_on_submit'] = array_map( 'wpcf7_strip_quote', $on_submit );
389
+ }
390
+
391
  // remove upload files
392
  foreach ( (array) $this->uploaded_files as $name => $path ) {
393
  @unlink( $path );
424
  if ( WPCF7_VERIFY_NONCE && ! $this->verify_nonce() )
425
  $spam = true;
426
 
427
+ if ( $this->blacklist_check() )
428
+ $spam = true;
429
+
430
  return apply_filters( 'wpcf7_spam', $spam );
431
  }
432
 
433
  function verify_nonce() {
434
+ return wpcf7_verify_nonce( $_POST['_wpnonce'], $this->id );
435
+ }
436
+
437
+ function blacklist_check() {
438
+ $target = wpcf7_array_flatten( $this->posted_data );
439
+ $target[] = $_SERVER['REMOTE_ADDR'];
440
+ $target[] = $_SERVER['HTTP_USER_AGENT'];
441
+
442
+ $target = implode( "\n", $target );
443
+
444
+ return wpcf7_blacklist_check( $target );
445
  }
446
 
447
  /* Mail */
494
  function compose_mail( $mail_template, $send = true ) {
495
  $this->mail_template_in_process = $mail_template;
496
 
 
 
497
  $use_html = (bool) $mail_template['use_html'];
498
 
499
+ $subject = $this->replace_mail_tags( $mail_template['subject'] );
500
+ $sender = $this->replace_mail_tags( $mail_template['sender'] );
501
+ $recipient = $this->replace_mail_tags( $mail_template['recipient'] );
502
+ $additional_headers = $this->replace_mail_tags( $mail_template['additional_headers'] );
 
 
 
 
503
 
504
  if ( $use_html ) {
505
+ $body = $this->replace_mail_tags( $mail_template['body'], true );
506
  $body = wpautop( $body );
507
  } else {
508
+ $body = $this->replace_mail_tags( $mail_template['body'] );
509
  }
510
 
511
  $attachments = array();
538
  return compact( 'subject', 'sender', 'body', 'recipient', 'headers', 'attachments' );
539
  }
540
 
541
+ function replace_mail_tags( $content, $html = false ) {
542
+ $regex = '/(\[?)\[\s*([a-zA-Z_][0-9a-zA-Z:._-]*)\s*\](\]?)/';
543
+
544
+ if ( $html )
545
+ $callback = array( &$this, 'mail_callback_html' );
546
+ else
547
+ $callback = array( &$this, 'mail_callback' );
548
+
549
+ return preg_replace_callback( $regex, $callback, $content );
550
+ }
551
+
552
  function mail_callback_html( $matches ) {
553
  return $this->mail_callback( $matches, true );
554
  }
571
  $replaced = wptexturize( $replaced );
572
  }
573
 
574
+ $replaced = apply_filters( 'wpcf7_mail_tag_replaced', $replaced, $submitted, $html );
575
 
576
  return stripslashes( $replaced );
577
  }
578
 
579
+ if ( $special = apply_filters( 'wpcf7_special_mail_tags', '', $matches[2], $html ) )
580
  return $special;
581
 
582
  return $matches[0];
588
  $messages = $this->messages;
589
  $message = isset( $messages[$status] ) ? $messages[$status] : '';
590
 
591
+ $message = $this->replace_mail_tags( $message, true );
592
+
593
  return apply_filters( 'wpcf7_display_message', $message, $status );
594
  }
595
 
647
  /* Save */
648
 
649
  function save() {
650
+ $props = $this->get_properties();
651
 
652
+ $post_content = implode( "\n", wpcf7_array_flatten( $props ) );
 
 
 
 
 
 
 
653
 
654
  if ( $this->initial ) {
655
  $post_id = wp_insert_post( array(
666
  }
667
 
668
  if ( $post_id ) {
669
+ foreach ( $props as $prop => $value )
670
+ update_post_meta( $post_id, '_' . $prop, wpcf7_normalize_newline_deep( $value ) );
671
 
672
  if ( $this->initial ) {
673
  $this->initial = false;
688
  $new->initial = true;
689
  $new->title = $this->title . '_copy';
690
 
691
+ $props = $this->get_properties();
692
+
693
+ foreach ( $props as $prop => $value )
694
+ $new->{$prop} = $value;
 
695
 
696
  $new = apply_filters_ref_array( 'wpcf7_copy', array( &$new, &$this ) );
697
 
767
 
768
  $contact_form->title = ( $title ? $title : __( 'Untitled', 'wpcf7' ) );
769
 
770
+ $props = $contact_form->get_properties();
771
+
772
+ foreach ( $props as $prop => $value )
773
+ $contact_form->{$prop} = wpcf7_get_default_template( $prop );
 
774
 
775
  if ( isset( $mo_orig ) )
776
  $l10n['wpcf7'] = $mo_orig;
includes/controller.php CHANGED
@@ -80,6 +80,9 @@ function wpcf7_ajax_json_echo() {
80
  if ( ! empty( $result['scripts_on_sent_ok'] ) )
81
  $items['onSentOk'] = $result['scripts_on_sent_ok'];
82
 
 
 
 
83
  $items = apply_filters( 'wpcf7_ajax_json_echo', $items, $result );
84
 
85
  $wpcf7_contact_form = null;
@@ -114,23 +117,10 @@ function wpcf7_submit_nonajax() {
114
 
115
  $id = (int) $_POST['_wpcf7'];
116
 
117
- if ( $wpcf7_contact_form = wpcf7_contact_form( $id ) ) {
118
- $result = $wpcf7_contact_form->submit();
119
-
120
- if ( ! $result['valid'] ) {
121
- $_POST['_wpcf7_validation_errors'] = array(
122
- 'id' => $id,
123
- 'messages' => $result['invalid_reasons'] );
124
- } else {
125
- $_POST['_wpcf7_mail_sent'] = array(
126
- 'id' => $id,
127
- 'ok' => $result['mail_sent'],
128
- 'message' => $result['message'],
129
- 'spam' => $result['spam'] );
130
- }
131
 
132
- $wpcf7_contact_form = null;
133
- }
134
  }
135
 
136
  add_action( 'the_post', 'wpcf7_the_post' );
@@ -237,8 +227,8 @@ function wpcf7_enqueue_scripts() {
237
  // so we need to deregister it and re-register the latest one
238
  wp_deregister_script( 'jquery-form' );
239
  wp_register_script( 'jquery-form',
240
- wpcf7_plugin_url( 'includes/js/jquery.form.js' ),
241
- array( 'jquery' ), '3.14', true );
242
 
243
  $in_footer = true;
244
  if ( 'header' === WPCF7_LOAD_JS )
80
  if ( ! empty( $result['scripts_on_sent_ok'] ) )
81
  $items['onSentOk'] = $result['scripts_on_sent_ok'];
82
 
83
+ if ( ! empty( $result['scripts_on_submit'] ) )
84
+ $items['onSubmit'] = $result['scripts_on_submit'];
85
+
86
  $items = apply_filters( 'wpcf7_ajax_json_echo', $items, $result );
87
 
88
  $wpcf7_contact_form = null;
117
 
118
  $id = (int) $_POST['_wpcf7'];
119
 
120
+ if ( $wpcf7_contact_form = wpcf7_contact_form( $id ) )
121
+ $_POST['_wpcf7_result'] = $wpcf7_contact_form->submit();
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
+ $wpcf7_contact_form = null;
 
124
  }
125
 
126
  add_action( 'the_post', 'wpcf7_the_post' );
227
  // so we need to deregister it and re-register the latest one
228
  wp_deregister_script( 'jquery-form' );
229
  wp_register_script( 'jquery-form',
230
+ wpcf7_plugin_url( 'includes/js/jquery.form.min.js' ),
231
+ array( 'jquery' ), '3.15', true );
232
 
233
  $in_footer = true;
234
  if ( 'header' === WPCF7_LOAD_JS )
includes/functions.php CHANGED
@@ -262,32 +262,48 @@ function wpcf7_ajax_loader() {
262
  return apply_filters( 'wpcf7_ajax_loader', $url );
263
  }
264
 
265
- /* Nonce functions: wpcf7_verify_nonce() and wpcf7_create_nonce()
266
- * For front-end use only.
267
- * Almost the same as wp_verify_nonce() and wp_create_nonce() except that $uid is always 0.
268
- */
269
-
270
  function wpcf7_verify_nonce( $nonce, $action = -1 ) {
271
- $i = wp_nonce_tick();
272
- $uid = 0;
 
 
 
 
 
 
 
 
 
 
273
 
274
- // Nonce generated 0-12 hours ago
275
- if ( substr( wp_hash( $i . $action . $uid, 'nonce' ), -12, 10 ) == $nonce )
276
- return 1;
277
 
278
- // Nonce generated 12-24 hours ago
279
- if ( substr( wp_hash( ( $i - 1 ) . $action . $uid, 'nonce' ), -12, 10 ) == $nonce )
280
- return 2;
 
 
 
 
 
 
 
 
281
 
282
- // Invalid nonce
283
  return false;
284
  }
285
 
286
- function wpcf7_create_nonce( $action = -1 ) {
287
- $i = wp_nonce_tick();
288
- $uid = 0;
 
 
 
 
 
289
 
290
- return substr( wp_hash( $i . $action . $uid, 'nonce' ), -12, 10 );
291
  }
292
 
293
  ?>
262
  return apply_filters( 'wpcf7_ajax_loader', $url );
263
  }
264
 
 
 
 
 
 
265
  function wpcf7_verify_nonce( $nonce, $action = -1 ) {
266
+ if ( substr( wp_hash( $action, 'nonce' ), -12, 10 ) == $nonce )
267
+ return true;
268
+
269
+ return false;
270
+ }
271
+
272
+ function wpcf7_create_nonce( $action = -1 ) {
273
+ return substr( wp_hash( $action, 'nonce' ), -12, 10 );
274
+ }
275
+
276
+ function wpcf7_blacklist_check( $target ) {
277
+ $mod_keys = trim( get_option( 'blacklist_keys' ) );
278
 
279
+ if ( empty( $mod_keys ) )
280
+ return false;
 
281
 
282
+ $words = explode( "\n", $mod_keys );
283
+
284
+ foreach ( (array) $words as $word ) {
285
+ $word = trim( $word );
286
+
287
+ if ( empty( $word ) )
288
+ continue;
289
+
290
+ if ( preg_match( '#' . preg_quote( $word, '#' ) . '#', $target ) )
291
+ return true;
292
+ }
293
 
 
294
  return false;
295
  }
296
 
297
+ function wpcf7_array_flatten( $input ) {
298
+ if ( ! is_array( $input ) )
299
+ return array( $input );
300
+
301
+ $output = array();
302
+
303
+ foreach ( $input as $value )
304
+ $output = array_merge( $output, wpcf7_array_flatten( $value ) );
305
 
306
+ return $output;
307
  }
308
 
309
  ?>
includes/js/jquery.form.dev.js DELETED
@@ -1,1089 +0,0 @@
1
- /*!
2
- * jQuery Form Plugin
3
- * version: 3.14 (30-JUL-2012)
4
- * @requires jQuery v1.3.2 or later
5
- *
6
- * Examples and documentation at: http://malsup.com/jquery/form/
7
- * Project repository: https://github.com/malsup/form
8
- * Dual licensed under the MIT and GPL licenses:
9
- * http://malsup.github.com/mit-license.txt
10
- * http://malsup.github.com/gpl-license-v2.txt
11
- */
12
- /*global ActiveXObject alert */
13
- ;(function($) {
14
- "use strict";
15
-
16
- /*
17
- Usage Note:
18
- -----------
19
- Do not use both ajaxSubmit and ajaxForm on the same form. These
20
- functions are mutually exclusive. Use ajaxSubmit if you want
21
- to bind your own submit handler to the form. For example,
22
-
23
- $(document).ready(function() {
24
- $('#myForm').on('submit', function(e) {
25
- e.preventDefault(); // <-- important
26
- $(this).ajaxSubmit({
27
- target: '#output'
28
- });
29
- });
30
- });
31
-
32
- Use ajaxForm when you want the plugin to manage all the event binding
33
- for you. For example,
34
-
35
- $(document).ready(function() {
36
- $('#myForm').ajaxForm({
37
- target: '#output'
38
- });
39
- });
40
-
41
- You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
42
- form does not have to exist when you invoke ajaxForm:
43
-
44
- $('#myForm').ajaxForm({
45
- delegation: true,
46
- target: '#output'
47
- });
48
-
49
- When using ajaxForm, the ajaxSubmit function will be invoked for you
50
- at the appropriate time.
51
- */
52
-
53
- /**
54
- * Feature detection
55
- */
56
- var feature = {};
57
- feature.fileapi = $("<input type='file'/>").get(0).files !== undefined;
58
- feature.formdata = window.FormData !== undefined;
59
-
60
- /**
61
- * ajaxSubmit() provides a mechanism for immediately submitting
62
- * an HTML form using AJAX.
63
- */
64
- $.fn.ajaxSubmit = function(options) {
65
- /*jshint scripturl:true */
66
-
67
- // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
68
- if (!this.length) {
69
- log('ajaxSubmit: skipping submit process - no element selected');
70
- return this;
71
- }
72
-
73
- var method, action, url, $form = this;
74
-
75
- if (typeof options == 'function') {
76
- options = { success: options };
77
- }
78
-
79
- method = this.attr('method');
80
- action = this.attr('action');
81
- url = (typeof action === 'string') ? $.trim(action) : '';
82
- url = url || window.location.href || '';
83
- if (url) {
84
- // clean url (don't include hash vaue)
85
- url = (url.match(/^([^#]+)/)||[])[1];
86
- }
87
-
88
- options = $.extend(true, {
89
- url: url,
90
- success: $.ajaxSettings.success,
91
- type: method || 'GET',
92
- iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
93
- }, options);
94
-
95
- // hook for manipulating the form data before it is extracted;
96
- // convenient for use with rich editors like tinyMCE or FCKEditor
97
- var veto = {};
98
- this.trigger('form-pre-serialize', [this, options, veto]);
99
- if (veto.veto) {
100
- log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
101
- return this;
102
- }
103
-
104
- // provide opportunity to alter form data before it is serialized
105
- if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
106
- log('ajaxSubmit: submit aborted via beforeSerialize callback');
107
- return this;
108
- }
109
-
110
- var traditional = options.traditional;
111
- if ( traditional === undefined ) {
112
- traditional = $.ajaxSettings.traditional;
113
- }
114
-
115
- var elements = [];
116
- var qx, a = this.formToArray(options.semantic, elements);
117
- if (options.data) {
118
- options.extraData = options.data;
119
- qx = $.param(options.data, traditional);
120
- }
121
-
122
- // give pre-submit callback an opportunity to abort the submit
123
- if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
124
- log('ajaxSubmit: submit aborted via beforeSubmit callback');
125
- return this;
126
- }
127
-
128
- // fire vetoable 'validate' event
129
- this.trigger('form-submit-validate', [a, this, options, veto]);
130
- if (veto.veto) {
131
- log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
132
- return this;
133
- }
134
-
135
- var q = $.param(a, traditional);
136
- if (qx) {
137
- q = ( q ? (q + '&' + qx) : qx );
138
- }
139
- if (options.type.toUpperCase() == 'GET') {
140
- options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
141
- options.data = null; // data is null for 'get'
142
- }
143
- else {
144
- options.data = q; // data is the query string for 'post'
145
- }
146
-
147
- var callbacks = [];
148
- if (options.resetForm) {
149
- callbacks.push(function() { $form.resetForm(); });
150
- }
151
- if (options.clearForm) {
152
- callbacks.push(function() { $form.clearForm(options.includeHidden); });
153
- }
154
-
155
- // perform a load on the target only if dataType is not provided
156
- if (!options.dataType && options.target) {
157
- var oldSuccess = options.success || function(){};
158
- callbacks.push(function(data) {
159
- var fn = options.replaceTarget ? 'replaceWith' : 'html';
160
- $(options.target)[fn](data).each(oldSuccess, arguments);
161
- });
162
- }
163
- else if (options.success) {
164
- callbacks.push(options.success);
165
- }
166
-
167
- options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
168
- var context = options.context || this ; // jQuery 1.4+ supports scope context
169
- for (var i=0, max=callbacks.length; i < max; i++) {
170
- callbacks[i].apply(context, [data, status, xhr || $form, $form]);
171
- }
172
- };
173
-
174
- // are there files to upload?
175
- var fileInputs = $('input:file:enabled[value]', this); // [value] (issue #113)
176
- var hasFileInputs = fileInputs.length > 0;
177
- var mp = 'multipart/form-data';
178
- var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
179
-
180
- var fileAPI = feature.fileapi && feature.formdata;
181
- log("fileAPI :" + fileAPI);
182
- var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
183
-
184
- // options.iframe allows user to force iframe mode
185
- // 06-NOV-09: now defaulting to iframe mode if file input is detected
186
- if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
187
- // hack to fix Safari hang (thanks to Tim Molendijk for this)
188
- // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
189
- if (options.closeKeepAlive) {
190
- $.get(options.closeKeepAlive, function() {
191
- fileUploadIframe(a);
192
- });
193
- }
194
- else {
195
- fileUploadIframe(a);
196
- }
197
- }
198
- else if ((hasFileInputs || multipart) && fileAPI) {
199
- fileUploadXhr(a);
200
- }
201
- else {
202
- $.ajax(options);
203
- }
204
-
205
- // clear element array
206
- for (var k=0; k < elements.length; k++)
207
- elements[k] = null;
208
-
209
- // fire 'notify' event
210
- this.trigger('form-submit-notify', [this, options]);
211
- return this;
212
-
213
- // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
214
- function fileUploadXhr(a) {
215
- var formdata = new FormData();
216
-
217
- for (var i=0; i < a.length; i++) {
218
- formdata.append(a[i].name, a[i].value);
219
- }
220
-
221
- if (options.extraData) {
222
- for (var p in options.extraData)
223
- if (options.extraData.hasOwnProperty(p))
224
- formdata.append(p, options.extraData[p]);
225
- }
226
-
227
- options.data = null;
228
-
229
- var s = $.extend(true, {}, $.ajaxSettings, options, {
230
- contentType: false,
231
- processData: false,
232
- cache: false,
233
- type: 'POST'
234
- });
235
-
236
- if (options.uploadProgress) {
237
- // workaround because jqXHR does not expose upload property
238
- s.xhr = function() {
239
- var xhr = jQuery.ajaxSettings.xhr();
240
- if (xhr.upload) {
241
- xhr.upload.onprogress = function(event) {
242
- var percent = 0;
243
- var position = event.loaded || event.position; /*event.position is deprecated*/
244
- var total = event.total;
245
- if (event.lengthComputable) {
246
- percent = Math.ceil(position / total * 100);
247
- }
248
- options.uploadProgress(event, position, total, percent);
249
- };
250
- }
251
- return xhr;
252
- };
253
- }
254
-
255
- s.data = null;
256
- var beforeSend = s.beforeSend;
257
- s.beforeSend = function(xhr, o) {
258
- o.data = formdata;
259
- if(beforeSend)
260
- beforeSend.call(this, xhr, o);
261
- };
262
- $.ajax(s);
263
- }
264
-
265
- // private function for handling file uploads (hat tip to YAHOO!)
266
- function fileUploadIframe(a) {
267
- var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
268
- var useProp = !!$.fn.prop;
269
-
270
- if ($(':input[name=submit],:input[id=submit]', form).length) {
271
- // if there is an input with a name or id of 'submit' then we won't be
272
- // able to invoke the submit fn on the form (at least not x-browser)
273
- alert('Error: Form elements must not have name or id of "submit".');
274
- return;
275
- }
276
-
277
- if (a) {
278
- // ensure that every serialized input is still enabled
279
- for (i=0; i < elements.length; i++) {
280
- el = $(elements[i]);
281
- if ( useProp )
282
- el.prop('disabled', false);
283
- else
284
- el.removeAttr('disabled');
285
- }
286
- }
287
-
288
- s = $.extend(true, {}, $.ajaxSettings, options);
289
- s.context = s.context || s;
290
- id = 'jqFormIO' + (new Date().getTime());
291
- if (s.iframeTarget) {
292
- $io = $(s.iframeTarget);
293
- n = $io.attr('name');
294
- if (!n)
295
- $io.attr('name', id);
296
- else
297
- id = n;
298
- }
299
- else {
300
- $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
301
- $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
302
- }
303
- io = $io[0];
304
-
305
-
306
- xhr = { // mock object
307
- aborted: 0,
308
- responseText: null,
309
- responseXML: null,
310
- status: 0,
311
- statusText: 'n/a',
312
- getAllResponseHeaders: function() {},
313
- getResponseHeader: function() {},
314
- setRequestHeader: function() {},
315
- abort: function(status) {
316
- var e = (status === 'timeout' ? 'timeout' : 'aborted');
317
- log('aborting upload... ' + e);
318
- this.aborted = 1;
319
- // #214
320
- if (io.contentWindow.document.execCommand) {
321
- try { // #214
322
- io.contentWindow.document.execCommand('Stop');
323
- } catch(ignore) {}
324
- }
325
- $io.attr('src', s.iframeSrc); // abort op in progress
326
- xhr.error = e;
327
- if (s.error)
328
- s.error.call(s.context, xhr, e, status);
329
- if (g)
330
- $.event.trigger("ajaxError", [xhr, s, e]);
331
- if (s.complete)
332
- s.complete.call(s.context, xhr, e);
333
- }
334
- };
335
-
336
- g = s.global;
337
- // trigger ajax global events so that activity/block indicators work like normal
338
- if (g && 0 === $.active++) {
339
- $.event.trigger("ajaxStart");
340
- }
341
- if (g) {
342
- $.event.trigger("ajaxSend", [xhr, s]);
343
- }
344
-
345
- if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
346
- if (s.global) {
347
- $.active--;
348
- }
349
- return;
350
- }
351
- if (xhr.aborted) {
352
- return;
353
- }
354
-
355
- // add submitting element to data if we know it
356
- sub = form.clk;
357
- if (sub) {
358
- n = sub.name;
359
- if (n && !sub.disabled) {
360
- s.extraData = s.extraData || {};
361
- s.extraData[n] = sub.value;
362
- if (sub.type == "image") {
363
- s.extraData[n+'.x'] = form.clk_x;
364
- s.extraData[n+'.y'] = form.clk_y;
365
- }
366
- }
367
- }
368
-
369
- var CLIENT_TIMEOUT_ABORT = 1;
370
- var SERVER_ABORT = 2;
371
-
372
- function getDoc(frame) {
373
- var doc = frame.contentWindow ? frame.contentWindow.document : frame.contentDocument ? frame.contentDocument : frame.document;
374
- return doc;
375
- }
376
-
377
- // Rails CSRF hack (thanks to Yvan Barthelemy)
378
- var csrf_token = $('meta[name=csrf-token]').attr('content');
379
- var csrf_param = $('meta[name=csrf-param]').attr('content');
380
- if (csrf_param && csrf_token) {
381
- s.extraData = s.extraData || {};
382
- s.extraData[csrf_param] = csrf_token;
383
- }
384
-
385
- // take a breath so that pending repaints get some cpu time before the upload starts
386
- function doSubmit() {
387
- // make sure form attrs are set
388
- var t = $form.attr('target'), a = $form.attr('action');
389
-
390
- // update form attrs in IE friendly way
391
- form.setAttribute('target',id);
392
- if (!method) {
393
- form.setAttribute('method', 'POST');
394
- }
395
- if (a != s.url) {
396
- form.setAttribute('action', s.url);
397
- }
398
-
399
- // ie borks in some cases when setting encoding
400
- if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
401
- $form.attr({
402
- encoding: 'multipart/form-data',
403
- enctype: 'multipart/form-data'
404
- });
405
- }
406
-
407
- // support timout
408
- if (s.timeout) {
409
- timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
410
- }
411
-
412
- // look for server aborts
413
- function checkState() {
414
- try {
415
- var state = getDoc(io).readyState;
416
- log('state = ' + state);
417
- if (state && state.toLowerCase() == 'uninitialized')
418
- setTimeout(checkState,50);
419
- }
420
- catch(e) {
421
- log('Server abort: ' , e, ' (', e.name, ')');
422
- cb(SERVER_ABORT);
423
- if (timeoutHandle)
424
- clearTimeout(timeoutHandle);
425
- timeoutHandle = undefined;
426
- }
427
- }
428
-
429
- // add "extra" data to form if provided in options
430
- var extraInputs = [];
431
- try {
432
- if (s.extraData) {
433
- for (var n in s.extraData) {
434
- if (s.extraData.hasOwnProperty(n)) {
435
- // if using the $.param format that allows for multiple values with the same name
436
- if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) {
437
- extraInputs.push(
438
- $('<input type="hidden" name="'+s.extraData[n].name+'">').attr('value',s.extraData[n].value)
439
- .appendTo(form)[0]);
440
- } else {
441
- extraInputs.push(
442
- $('<input type="hidden" name="'+n+'">').attr('value',s.extraData[n])
443
- .appendTo(form)[0]);
444
- }
445
- }
446
- }
447
- }
448
-
449
- if (!s.iframeTarget) {
450
- // add iframe to doc and submit the form
451
- $io.appendTo('body');
452
- if (io.attachEvent)
453
- io.attachEvent('onload', cb);
454
- else
455
- io.addEventListener('load', cb, false);
456
- }
457
- setTimeout(checkState,15);
458
- form.submit();
459
- }
460
- finally {
461
- // reset attrs and remove "extra" input elements
462
- form.setAttribute('action',a);
463
- if(t) {
464
- form.setAttribute('target', t);
465
- } else {
466
- $form.removeAttr('target');
467
- }
468
- $(extraInputs).remove();
469
- }
470
- }
471
-
472
- if (s.forceSync) {
473
- doSubmit();
474
- }
475
- else {
476
- setTimeout(doSubmit, 10); // this lets dom updates render
477
- }
478
-
479
- var data, doc, domCheckCount = 50, callbackProcessed;
480
-
481
- function cb(e) {
482
- if (xhr.aborted || callbackProcessed) {
483
- return;
484
- }
485
- try {
486
- doc = getDoc(io);
487
- }
488
- catch(ex) {
489
- log('cannot access response document: ', ex);
490
- e = SERVER_ABORT;
491
- }
492
- if (e === CLIENT_TIMEOUT_ABORT && xhr) {
493
- xhr.abort('timeout');
494
- return;
495
- }
496
- else if (e == SERVER_ABORT && xhr) {
497
- xhr.abort('server abort');
498
- return;
499
- }
500
-
501
- if (!doc || doc.location.href == s.iframeSrc) {
502
- // response not received yet
503
- if (!timedOut)
504
- return;
505
- }
506
- if (io.detachEvent)
507
- io.detachEvent('onload', cb);
508
- else
509
- io.removeEventListener('load', cb, false);
510
-
511
- var status = 'success', errMsg;
512
- try {
513
- if (timedOut) {
514
- throw 'timeout';
515
- }
516
-
517
- var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
518
- log('isXml='+isXml);
519
- if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
520
- if (--domCheckCount) {
521
- // in some browsers (Opera) the iframe DOM is not always traversable when
522
- // the onload callback fires, so we loop a bit to accommodate
523
- log('requeing onLoad callback, DOM not available');
524
- setTimeout(cb, 250);
525
- return;
526
- }
527
- // let this fall through because server response could be an empty document
528
- //log('Could not access iframe DOM after mutiple tries.');
529
- //throw 'DOMException: not available';
530
- }
531
-
532
- //log('response detected');
533
- var docRoot = doc.body ? doc.body : doc.documentElement;
534
- xhr.responseText = docRoot ? docRoot.innerHTML : null;
535
- xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
536
- if (isXml)
537
- s.dataType = 'xml';
538
- xhr.getResponseHeader = function(header){
539
- var headers = {'content-type': s.dataType};
540
- return headers[header];
541
- };
542
- // support for XHR 'status' & 'statusText' emulation :
543
- if (docRoot) {
544
- xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
545
- xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
546
- }
547
-
548
- var dt = (s.dataType || '').toLowerCase();
549
- var scr = /(json|script|text)/.test(dt);
550
- if (scr || s.textarea) {
551
- // see if user embedded response in textarea
552
- var ta = doc.getElementsByTagName('textarea')[0];
553
- if (ta) {
554
- xhr.responseText = ta.value;
555
- // support for XHR 'status' & 'statusText' emulation :
556
- xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
557
- xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
558
- }
559
- else if (scr) {
560
- // account for browsers injecting pre around json response
561
- var pre = doc.getElementsByTagName('pre')[0];
562
- var b = doc.getElementsByTagName('body')[0];
563
- if (pre) {
564
- xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
565
- }
566
- else if (b) {
567
- xhr.responseText = b.textContent ? b.textContent : b.innerText;
568
- }
569
- }
570
- }
571
- else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
572
- xhr.responseXML = toXml(xhr.responseText);
573
- }
574
-
575
- try {
576
- data = httpData(xhr, dt, s);
577
- }
578
- catch (e) {
579
- status = 'parsererror';
580
- xhr.error = errMsg = (e || status);
581
- }
582
- }
583
- catch (e) {
584
- log('error caught: ',e);
585
- status = 'error';
586
- xhr.error = errMsg = (e || status);
587
- }
588
-
589
- if (xhr.aborted) {
590
- log('upload aborted');
591
- status = null;
592
- }
593
-
594
- if (xhr.status) { // we've set xhr.status
595
- status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
596
- }
597
-
598
- // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
599
- if (status === 'success') {
600
- if (s.success)
601
- s.success.call(s.context, data, 'success', xhr);
602
- if (g)
603
- $.event.trigger("ajaxSuccess", [xhr, s]);
604
- }
605
- else if (status) {
606
- if (errMsg === undefined)
607
- errMsg = xhr.statusText;
608
- if (s.error)
609
- s.error.call(s.context, xhr, status, errMsg);
610
- if (g)
611
- $.event.trigger("ajaxError", [xhr, s, errMsg]);
612
- }
613
-
614
- if (g)
615
- $.event.trigger("ajaxComplete", [xhr, s]);
616
-
617
- if (g && ! --$.active) {
618
- $.event.trigger("ajaxStop");
619
- }
620
-
621
- if (s.complete)
622
- s.complete.call(s.context, xhr, status);
623
-
624
- callbackProcessed = true;
625
- if (s.timeout)
626
- clearTimeout(timeoutHandle);
627
-
628
- // clean up
629
- setTimeout(function() {
630
- if (!s.iframeTarget)
631
- $io.remove();
632
- xhr.responseXML = null;
633
- }, 100);
634
- }
635
-
636
- var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
637
- if (window.ActiveXObject) {
638
- doc = new ActiveXObject('Microsoft.XMLDOM');
639
- doc.async = 'false';
640
- doc.loadXML(s);
641
- }
642
- else {
643
- doc = (new DOMParser()).parseFromString(s, 'text/xml');
644
- }
645
- return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
646
- };
647
- var parseJSON = $.parseJSON || function(s) {
648
- /*jslint evil:true */
649
- return window['eval']('(' + s + ')');
650
- };
651
-
652
- var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
653
-
654
- var ct = xhr.getResponseHeader('content-type') || '',
655
- xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
656
- data = xml ? xhr.responseXML : xhr.responseText;
657
-
658
- if (xml && data.documentElement.nodeName === 'parsererror') {
659
- if ($.error)
660
- $.error('parsererror');
661
- }
662
- if (s && s.dataFilter) {
663
- data = s.dataFilter(data, type);
664
- }
665
- if (typeof data === 'string') {
666
- if (type === 'json' || !type && ct.indexOf('json') >= 0) {
667
- data = parseJSON(data);
668
- } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
669
- $.globalEval(data);
670
- }
671
- }
672
- return data;
673
- };
674
- }
675
- };
676
-
677
- /**
678
- * ajaxForm() provides a mechanism for fully automating form submission.
679
- *
680
- * The advantages of using this method instead of ajaxSubmit() are:
681
- *
682
- * 1: This method will include coordinates for <input type="image" /> elements (if the element
683
- * is used to submit the form).
684
- * 2. This method will include the submit element's name/value data (for the element that was
685
- * used to submit the form).
686
- * 3. This method binds the submit() method to the form for you.
687
- *
688
- * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
689
- * passes the options argument along after properly binding events for submit elements and
690
- * the form itself.
691
- */
692
- $.fn.ajaxForm = function(options) {
693
- options = options || {};
694
- options.delegation = options.delegation && $.isFunction($.fn.on);
695
-
696
- // in jQuery 1.3+ we can fix mistakes with the ready state
697
- if (!options.delegation && this.length === 0) {
698
- var o = { s: this.selector, c: this.context };
699
- if (!$.isReady && o.s) {
700
- log('DOM not ready, queuing ajaxForm');
701
- $(function() {
702
- $(o.s,o.c).ajaxForm(options);
703
- });
704
- return this;
705
- }
706
- // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
707
- log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
708
- return this;
709
- }
710
-
711
- if ( options.delegation ) {
712
- $(document)
713
- .off('submit.form-plugin', this.selector, doAjaxSubmit)
714
- .off('click.form-plugin', this.selector, captureSubmittingElement)
715
- .on('submit.form-plugin', this.selector, options, doAjaxSubmit)
716
- .on('click.form-plugin', this.selector, options, captureSubmittingElement);
717
- return this;
718
- }
719
-
720
- return this.ajaxFormUnbind()
721
- .bind('submit.form-plugin', options, doAjaxSubmit)
722
- .bind('click.form-plugin', options, captureSubmittingElement);
723
- };
724
-
725
- // private event handlers
726
- function doAjaxSubmit(e) {
727
- /*jshint validthis:true */
728
- var options = e.data;
729
- if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
730
- e.preventDefault();
731
- $(this).ajaxSubmit(options);
732
- }
733
- }
734
-
735
- function captureSubmittingElement(e) {
736
- /*jshint validthis:true */
737
- var target = e.target;
738
- var $el = $(target);
739
- if (!($el.is(":submit,input:image"))) {
740
- // is this a child element of the submit el? (ex: a span within a button)
741
- var t = $el.closest(':submit');
742
- if (t.length === 0) {
743
- return;
744
- }
745
- target = t[0];
746
- }
747
- var form = this;
748
- form.clk = target;
749
- if (target.type == 'image') {
750
- if (e.offsetX !== undefined) {
751
- form.clk_x = e.offsetX;
752
- form.clk_y = e.offsetY;
753
- } else if (typeof $.fn.offset == 'function') {
754
- var offset = $el.offset();
755
- form.clk_x = e.pageX - offset.left;
756
- form.clk_y = e.pageY - offset.top;
757
- } else {
758
- form.clk_x = e.pageX - target.offsetLeft;
759
- form.clk_y = e.pageY - target.offsetTop;
760
- }
761
- }
762
- // clear form vars
763
- setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
764
- }
765
-
766
-
767
- // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
768
- $.fn.ajaxFormUnbind = function() {
769
- return this.unbind('submit.form-plugin click.form-plugin');
770
- };
771
-
772
- /**
773
- * formToArray() gathers form element data into an array of objects that can
774
- * be passed to any of the following ajax functions: $.get, $.post, or load.
775
- * Each object in the array has both a 'name' and 'value' property. An example of
776
- * an array for a simple login form might be:
777
- *
778
- * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
779
- *
780
- * It is this array that is passed to pre-submit callback functions provided to the
781
- * ajaxSubmit() and ajaxForm() methods.
782
- */
783
- $.fn.formToArray = function(semantic, elements) {
784
- var a = [];
785
- if (this.length === 0) {
786
- return a;
787
- }
788
-
789
- var form = this[0];
790
- var els = semantic ? form.getElementsByTagName('*') : form.elements;
791
- if (!els) {
792
- return a;
793
- }
794
-
795
- var i,j,n,v,el,max,jmax;
796
- for(i=0, max=els.length; i < max; i++) {
797
- el = els[i];
798
- n = el.name;
799
- if (!n) {
800
- continue;
801
- }
802
-
803
- if (semantic && form.clk && el.type == "image") {
804
- // handle image inputs on the fly when semantic == true
805
- if(!el.disabled && form.clk == el) {
806
- a.push({name: n, value: $(el).val(), type: el.type });
807
- a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
808
- }
809
- continue;
810
- }
811
-
812
- v = $.fieldValue(el, true);
813
- if (v && v.constructor == Array) {
814
- if (elements)
815
- elements.push(el);
816
- for(j=0, jmax=v.length; j < jmax; j++) {
817
- a.push({name: n, value: v[j]});
818
- }
819
- }
820
- else if (feature.fileapi && el.type == 'file' && !el.disabled) {
821
- if (elements)
822
- elements.push(el);
823
- var files = el.files;
824
- if (files.length) {
825
- for (j=0; j < files.length; j++) {
826
- a.push({name: n, value: files[j], type: el.type});
827
- }
828
- }
829
- else {
830
- // #180
831
- a.push({ name: n, value: '', type: el.type });
832
- }
833
- }
834
- else if (v !== null && typeof v != 'undefined') {
835
- if (elements)
836
- elements.push(el);
837
- a.push({name: n, value: v, type: el.type, required: el.required});
838
- }
839
- }
840
-
841
- if (!semantic && form.clk) {
842
- // input type=='image' are not found in elements array! handle it here
843
- var $input = $(form.clk), input = $input[0];
844
- n = input.name;
845
- if (n && !input.disabled && input.type == 'image') {
846
- a.push({name: n, value: $input.val()});
847
- a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
848
- }
849
- }
850
- return a;
851
- };
852
-
853
- /**
854
- * Serializes form data into a 'submittable' string. This method will return a string
855
- * in the format: name1=value1&amp;name2=value2
856
- */
857
- $.fn.formSerialize = function(semantic) {
858
- //hand off to jQuery.param for proper encoding
859
- return $.param(this.formToArray(semantic));
860
- };
861
-
862
- /**
863
- * Serializes all field elements in the jQuery object into a query string.
864
- * This method will return a string in the format: name1=value1&amp;name2=value2
865
- */
866
- $.fn.fieldSerialize = function(successful) {
867
- var a = [];
868
- this.each(function() {
869
- var n = this.name;
870
- if (!n) {
871
- return;
872
- }
873
- var v = $.fieldValue(this, successful);
874
- if (v && v.constructor == Array) {
875
- for (var i=0,max=v.length; i < max; i++) {
876
- a.push({name: n, value: v[i]});
877
- }
878
- }
879
- else if (v !== null && typeof v != 'undefined') {
880
- a.push({name: this.name, value: v});
881
- }
882
- });
883
- //hand off to jQuery.param for proper encoding
884
- return $.param(a);
885
- };
886
-
887
- /**
888
- * Returns the value(s) of the element in the matched set. For example, consider the following form:
889
- *
890
- * <form><fieldset>
891
- * <input name="A" type="text" />
892
- * <input name="A" type="text" />
893
- * <input name="B" type="checkbox" value="B1" />
894
- * <input name="B" type="checkbox" value="B2"/>
895
- * <input name="C" type="radio" value="C1" />
896
- * <input name="C" type="radio" value="C2" />
897
- * </fieldset></form>
898
- *
899
- * var v = $(':text').fieldValue();
900
- * // if no values are entered into the text inputs
901
- * v == ['','']
902
- * // if values entered into the text inputs are 'foo' and 'bar'
903
- * v == ['foo','bar']
904
- *
905
- * var v = $(':checkbox').fieldValue();
906
- * // if neither checkbox is checked
907
- * v === undefined
908
- * // if both checkboxes are checked
909
- * v == ['B1', 'B2']
910
- *
911
- * var v = $(':radio').fieldValue();
912
- * // if neither radio is checked
913
- * v === undefined
914
- * // if first radio is checked
915
- * v == ['C1']
916
- *
917
- * The successful argument controls whether or not the field element must be 'successful'
918
- * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
919
- * The default value of the successful argument is true. If this value is false the value(s)
920
- * for each element is returned.
921
- *
922
- * Note: This method *always* returns an array. If no valid value can be determined the
923
- * array will be empty, otherwise it will contain one or more values.
924
- */
925
- $.fn.fieldValue = function(successful) {
926
- for (var val=[], i=0, max=this.length; i < max; i++) {
927
- var el = this[i];
928
- var v = $.fieldValue(el, successful);
929
- if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
930
- continue;
931
- }
932
- if (v.constructor == Array)
933
- $.merge(val, v);
934
- else
935
- val.push(v);
936
- }
937
- return val;
938
- };
939
-
940
- /**
941
- * Returns the value of the field element.
942
- */
943
- $.fieldValue = function(el, successful) {
944
- var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
945
- if (successful === undefined) {
946
- successful = true;
947
- }
948
-
949
- if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
950
- (t == 'checkbox' || t == 'radio') && !el.checked ||
951
- (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
952
- tag == 'select' && el.selectedIndex == -1)) {
953
- return null;
954
- }
955
-
956
- if (tag == 'select') {
957
- var index = el.selectedIndex;
958
- if (index < 0) {
959
- return null;
960
- }
961
- var a = [], ops = el.options;
962
- var one = (t == 'select-one');
963
- var max = (one ? index+1 : ops.length);
964
- for(var i=(one ? index : 0); i < max; i++) {
965
- var op = ops[i];
966
- if (op.selected) {
967
- var v = op.value;
968
- if (!v) { // extra pain for IE...
969
- v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
970
- }
971
- if (one) {
972
- return v;
973
- }
974
- a.push(v);
975
- }
976
- }
977
- return a;
978
- }
979
- return $(el).val();
980
- };
981
-
982
- /**
983
- * Clears the form data. Takes the following actions on the form's input fields:
984
- * - input text fields will have their 'value' property set to the empty string
985
- * - select elements will have their 'selectedIndex' property set to -1
986
- * - checkbox and radio inputs will have their 'checked' property set to false
987
- * - inputs of type submit, button, reset, and hidden will *not* be effected
988
- * - button elements will *not* be effected
989
- */
990
- $.fn.clearForm = function(includeHidden) {
991
- return this.each(function() {
992
- $('input,select,textarea', this).clearFields(includeHidden);
993
- });
994
- };
995
-
996
- /**
997
- * Clears the selected form elements.
998
- */
999
- $.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
1000
- var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
1001
- return this.each(function() {
1002
- var t = this.type, tag = this.tagName.toLowerCase();
1003
- if (re.test(t) || tag == 'textarea') {
1004
- this.value = '';
1005
- }
1006
- else if (t == 'checkbox' || t == 'radio') {
1007
- this.checked = false;
1008
- }
1009
- else if (tag == 'select') {
1010
- this.selectedIndex = -1;
1011
- }
1012
- else if (includeHidden) {
1013
- // includeHidden can be the value true, or it can be a selector string
1014
- // indicating a special test; for example:
1015
- // $('#myForm').clearForm('.special:hidden')
1016
- // the above would clean hidden inputs that have the class of 'special'
1017
- if ( (includeHidden === true && /hidden/.test(t)) ||
1018
- (typeof includeHidden == 'string' && $(this).is(includeHidden)) )
1019
- this.value = '';
1020
- }
1021
- });
1022
- };
1023
-
1024
- /**
1025
- * Resets the form data. Causes all form elements to be reset to their original value.
1026
- */
1027
- $.fn.resetForm = function() {
1028
- return this.each(function() {
1029
- // guard against an input with the name of 'reset'
1030
- // note that IE reports the reset function as an 'object'
1031
- if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
1032
- this.reset();
1033
- }
1034
- });
1035
- };
1036
-
1037
- /**
1038
- * Enables or disables any matching elements.
1039
- */
1040
- $.fn.enable = function(b) {
1041
- if (b === undefined) {
1042
- b = true;
1043
- }
1044
- return this.each(function() {
1045
- this.disabled = !b;
1046
- });
1047
- };
1048
-
1049
- /**
1050
- * Checks/unchecks any matching checkboxes or radio buttons and
1051
- * selects/deselects and matching option elements.
1052
- */
1053
- $.fn.selected = function(select) {
1054
- if (select === undefined) {
1055
- select = true;
1056
- }
1057
- return this.each(function() {
1058
- var t = this.type;
1059
- if (t == 'checkbox' || t == 'radio') {
1060
- this.checked = select;
1061
- }
1062
- else if (this.tagName.toLowerCase() == 'option') {
1063
- var $sel = $(this).parent('select');
1064
- if (select && $sel[0] && $sel[0].type == 'select-one') {
1065
- // deselect all other options
1066
- $sel.find('option').selected(false);
1067
- }
1068
- this.selected = select;
1069
- }
1070
- });
1071
- };
1072
-
1073
- // expose debug var
1074
- $.fn.ajaxSubmit.debug = false;
1075
-
1076
- // helper fn for console logging
1077
- function log() {
1078
- if (!$.fn.ajaxSubmit.debug)
1079
- return;
1080
- var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
1081
- if (window.console && window.console.log) {
1082
- window.console.log(msg);
1083
- }
1084
- else if (window.opera && window.opera.postError) {
1085
- window.opera.postError(msg);
1086
- }
1087
- }
1088
-
1089
- })(jQuery);