text: import {VBase} from './base';
  import appConfig from '../../config';
  import {expandParams} from './action_parameter';
  import {encode} from './encode';
  import {getEventTarget} from '../get_event_target';
  import {buildFormData} from '../../utils/form-data'
  
  // Replaces a given element with the contents of the call to the url.
  // parameters are appended.
  export class VPosts extends VBase {
      constructor(options, url, params, method, event, root) {
          super(options, root);
          this.url = url;
          this.params = params;
          this.method = method;
          this.event = event;
          this.headers = options.headers;
      }
  
      call(results, eventParams) {
          this.clearErrors();
          let method = this.method;
  
          const ev = new CustomEvent('V:postStarted', {
              bubbles: true,
              cancelable: false,
              detail: this,
              composed: true,
          });
  
          this.dispatchLifecycleEvent(this.event, ev);
  
          // Manually build the FormData.
          // Passing in a <form> element (if available) would skip over
          // unchecked toggle elements, which would be unexpected if the user
          // has specified a value for the toggle's `off_value` attribute.
          const formData = new FormData();
  
          // NB: `inputValues` will appropriately handle `input_tag`.
          for (const [name, value] of this.inputValues()) {
              formData.append(name, value);
          }
  
          if (eventParams){
              buildFormData(formData, eventParams);
          }
  
          // Add CSRF authenticity token if present
          const csrf_meta_token = document.querySelector('meta[name=csrf-token]');
          const csrf_meta_param = document.querySelector('meta[name=csrf-param]');
          if (csrf_meta_token && csrf_meta_param) {
              formData.append(csrf_meta_param.content, csrf_meta_token.content);
          }
  
          // Add params from presenter:
          const expandedParams = expandParams(results, this.params);
  
          for (const [name, value] of Object.entries(expandedParams)) {
              formData.append(name, encode(value));
          }
  
          if (this.paramCount(formData) < 1) {
              console.warn(
                  'Creating request with no data!'
                  + ' Are you sure you\'ve hooked everything up correctly?');
          }
  
          let errors = this.validate(formData);
          if (errors.length > 0) {
              return new Promise(function(_, reject) {
                  results.push({
                      action: 'posts',
                      method: method,
                      statusCode: 400,
                      contentType: 'v/errors',
                      content: errors,
                  });
                  reject(results);
              });
          }
  
          const httpRequest = new XMLHttpRequest();
          const url = this.url;
          const callHeaders = this.headers;
          const root = this.root;
          const vEvent = this;
          if (!httpRequest) {
              throw new Error(
                  'Cannot talk to server! Please upgrade your browser to one that supports XMLHttpRequest.');
          }
  
          if (formData.has('rich_text_payload')) {
              callHeaders['X-Rich-Text-Payload'] = true;
          }
  
          const snackbarCallback = function(contentType, response) {
              const element = root.querySelector('.mdc-snackbar');
  
              if (!(element && element.vComponent)) {
                  return;
              }
  
              const snackbar = element.vComponent;
  
              if (contentType && contentType.includes('application/json')) {
                  const messages = JSON.parse(response).message;
                  if (messages && messages.snackbar) {
                      const message = messages.snackbar.join('<br/>');
  
                      if (message !== '') {
                          snackbar.display(message);
                      }
                  }
              }
          };
  
          return new Promise((resolve, reject) => {
              httpRequest.onreadystatechange = (event) => {
                  if (httpRequest.readyState === XMLHttpRequest.DONE) {
                      const contentType = httpRequest.getResponseHeader('content-type');
                      console.debug(httpRequest.status + ':' + contentType);
  
                      const result = {
                          action: 'posts',
                          method: httpRequest.method,
                          statusCode: httpRequest.status,
                          contentType: contentType,
                          content: httpRequest.responseText,
                          responseURL: httpRequest.responseURL,
                      };
  
  
                      var postFailed = httpRequest.status >= 400;
                      const ev = new CustomEvent(postFailed ? 'V:postFailed' : 'V:postSucceeded', {
                          bubbles: true,
                          cancelable: false,
                          detail: {event: vEvent, result: result},
                          composed: true,
                      });
                      this.dispatchLifecycleEvent(this.event, ev);
  
                      if (httpRequest.status >= 200 && httpRequest.status < 300) {
                          results.push(result);
                          snackbarCallback(contentType,
                              httpRequest.responseText);
                          resolve(results);
                      }
                      // Response is an html error page
                      else if (contentType && contentType.indexOf('text/html') !== -1) {
                          root.open(contentType);
                          root.write(httpRequest.responseText);
                          root.close();
                          results.push(result);
                          resolve(results);
                      }
                      else {
                          results.push(result);
                          reject(results);
                      }
                      const evFinished = new CustomEvent('V:postFinished', {
                          bubbles: true,
                          cancelable: false,
                          detail: {event: vEvent, result: result},
                          composed: true,
                      });
                      this.dispatchLifecycleEvent(this.event, evFinished);
                  }
              };
              // Set up our request
              httpRequest.open(method, url);
  
              const configHeaders = appConfig.get('request.headers.POST', {});
  
              for (const [key, value] of Object.entries(configHeaders)) {
                  httpRequest.setRequestHeader(key, value);
              }
  
              if (callHeaders) {
                  for (const [key, value] of Object.entries(callHeaders)) {
                      httpRequest.setRequestHeader(key, value);
                  }
              }
  
              // Send our FormData object; HTTP headers are set automatically
              // Rack 2.2 will throw an exception https://github.com/rack/rack/issues/1603
              // if we set the header as multi-part form data with no data in the body
              // So we set the body to null in this case.
              httpRequest.send(this.paramCount(formData) < 1 ? null : formData);
          });
      }
  
      paramCount(formData){
          return Array.from(formData).length;
      }
  
      isForm() {
          var parentElement = this.parentElement();
          return parentElement && parentElement.elements;
      }
  
      form() {
          if (this.isForm()) {
              return this.parentElement();
          }
          return null;
      }
  
      dispatchLifecycleEvent(domEvent, lifecycleEvent) {
          let target = getEventTarget(domEvent);
  
          if (!target || !target.isConnected) {
              // If an action has hidden `target` or its parent (via e.g.
              // `hides :some_element`), it will no longer be connected to the DOM
              // and its dispatched lifecycle events won't make it up to the root.
              // Instead, dispatch straight from the root instead of bubbling up
              // from the DOM event's target.
              target = this.root;
          }
  
          return target.dispatchEvent(lifecycleEvent);
      }
  }
  