diff --git a/modules/ctools_views/ctools_views.libraries.yml b/modules/ctools_views/ctools_views.libraries.yml
new file mode 100644
index 0000000..37a5e51
--- /dev/null
+++ b/modules/ctools_views/ctools_views.libraries.yml
@@ -0,0 +1,8 @@
+autosubmit:
+  version: 1.0
+  js:
+    js/auto-submit.js: {}
+  dependencies:
+    - core/jquery
+    - core/jquery.once
+    - core/drupal
diff --git a/modules/ctools_views/js/auto-submit.js b/modules/ctools_views/js/auto-submit.js
new file mode 100644
index 0000000..58e4ff0
--- /dev/null
+++ b/modules/ctools_views/js/auto-submit.js
@@ -0,0 +1,95 @@
+/**
+ * To make a form auto submit, all you have to do is 3 things:
+ *
+ * $form['#attached']['library'][] = 'ctools_views/autosubmit';
+ *
+ * On gadgets you want to auto-submit when changed, add the views-auto-submit
+ * class. With FAPI, add:
+ * @code
+ *  '#attributes' => array('class' => array('views-auto-submit')),
+ * @endcode
+ *
+ * If you want to have auto-submit for every form element,
+ * add the views-auto-submit-full-form to the form. With FAPI, add:
+ * @code
+ *   '#attributes' => array('class' => array('views-auto-submit-full-form')),
+ * @endcode
+ *
+ * Finally, you have to identify which button you want clicked for autosubmit.
+ * The behavior of this button will be honored if it's ajaxy or not:
+ * @code
+ *  '#attributes' => array('class' => array('views-use-ajax', 'views-auto-submit-click')),
+ * @endcode
+ *
+ * Currently only 'select', 'radio', 'checkbox' and 'textfield' types are supported. We probably
+ * could use additional support for HTML5 input types.
+ */
+
+(function ($, drupalSettings) {
+
+Drupal.behaviors.ViewsAutoSubmit = {
+  attach: function(context) {
+    // 'this' references the form element
+    function triggerSubmit (e) {
+      var $this = $(this);
+      if (!$this.hasClass('views-ajaxing')) {
+        $this.find('.views-auto-submit-click').click();
+      }
+    }
+
+    // the change event bubbles so we only need to bind it to the outer form
+    $('form.views-auto-submit-full-form', context)
+      .add('.views-auto-submit', context)
+      .filter('form, select, input:not(:text, :submit)')
+      .once('views-auto-submit')
+      .change(function (e) {
+        if ($(e.target).is(':not(:text, :submit)')) {
+          triggerSubmit.call(e.target.form);
+        }
+      })
+      // In case of using selectmenu.js
+      .on('selectmenuchange', function (e,ui) {
+        // don't trigger on text change for full-form
+        if ($(e.target).is(':not(:text, :submit)')) {
+          triggerSubmit.call(e.target.form);
+        }
+      });
+
+    // e.keyCode: key
+    var discardKeyCode = [
+      16, // shift
+      17, // ctrl
+      18, // alt
+      20, // caps lock
+      33, // page up
+      34, // page down
+      35, // end
+      36, // home
+      37, // left arrow
+      38, // up arrow
+      39, // right arrow
+      40, // down arrow
+       9, // tab
+      13, // enter
+      27  // esc
+    ];
+    // Don't wait for change event on textfields
+    $('.views-auto-submit-full-form input:text, input:text.views-auto-submit', context)
+      .once('views-auto-submit', function () {
+        // each textinput element has his own timeout
+        var timeoutID = 0;
+        $(this)
+          .bind('keydown keyup', function (e) {
+            if ($.inArray(e.keyCode, discardKeyCode) === -1) {
+              timeoutID && clearTimeout(timeoutID);
+            }
+          })
+          .keyup(function(e) {
+            if ($.inArray(e.keyCode, discardKeyCode) === -1) {
+              timeoutID = setTimeout($.proxy(triggerSubmit, this.form), 500);
+            }
+          });
+      });
+  }
+}
+}(jQuery, drupalSettings));
diff --git a/modules/ctools_views/src/Plugin/views/exposed_form/Autosubmit.php b/modules/ctools_views/src/Plugin/views/exposed_form/Autosubmit.php
new file mode 100644
index 0000000..dfd604e
--- /dev/null
+++ b/modules/ctools_views/src/Plugin/views/exposed_form/Autosubmit.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\ctools_views\Plugin\views\exposed_form\Autosubmit.
+ */
+
+namespace Drupal\ctools_views\Plugin\views\exposed_form;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\views\Plugin\views\exposed_form\ExposedFormPluginBase;
+
+/**
+ * Extends the exposed form to provide an autosubmit functionality.
+ *
+ * @ingroup views_exposed_form_plugins
+ *
+ * @ViewsExposedForm(
+ *   id = "autosubmit",
+ *   title = @Translation("Autosubmit"),
+ *   help = @Translation("Exposed form with autosubmit.")
+ * )
+ */
+class Autosubmit extends ExposedFormPluginBase {
+  /**
+   * {@inheritdoc}.
+   */
+  protected function defineOptions() {
+    $options = parent::defineOptions();
+
+    $options['autosubmit_hide'] = array('default' => TRUE);
+
+    return $options;
+  }
+
+  /**
+   * {@inheritdoc}.
+   */
+  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
+    parent::buildOptionsForm($form, $form_state);
+
+    $form['autosubmit_hide'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Hide submit button'),
+      '#description' => t('Hide submit button if javascript is enabled.'),
+      '#default_value' => $this->options['autosubmit_hide'],
+    );
+  }
+
+  /**
+   * {@inheritdoc}.
+   */
+  public function exposedFormAlter(&$form, FormStateInterface $form_state) {
+    parent::exposedFormAlter($form, $form_state);
+    
+    // Apply autosubmit values.
+    $form = array_merge_recursive($form, array('#attributes' => array('class' => array('views-auto-submit-full-form'))));
+    $form['actions']['submit']['#attributes']['class'][] = 'views-use-ajax';
+    $form['actions']['submit']['#attributes']['class'][] = 'views-auto-submit-click';
+    $form['#attached']['library'][] = 'ctools_views/autosubmit';
+
+    if (!empty($this->options['autosubmit_hide'])) {
+      $form['actions']['submit']['#attributes']['class'][] = 'js-hide';
+    }
+  }
+
+}
