<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import reactMixin from 'react-mixin';
import autoBind from 'react-autobind';
import $ from 'jquery';
import Select from 'react-select';
import _ from 'underscore';
import DocumentTitle from 'react-document-title';
import SurveyScope from '../models/surveyScope';
import cascadeMixin from './cascadeMixin';
import AssetNavigator from './assetNavigator';
import {Link, hashHistory} from 'react-router';
import alertify from 'alertifyjs';
import {ProjectSettings} from '../components/formEditors';
import {
  surveyToValidJson,
  notify,
  assign,
  t,
  koboMatrixParser
} from '../utils';

import {
  ASSET_TYPES,
  AVAILABLE_FORM_STYLES,
  PROJECT_SETTINGS_CONTEXTS,
  update_states,
} from '../constants';

import ui from '../ui';
import bem from '../bem';
import stores from '../stores';
import actions from '../actions';
import dkobo_xlform from '../../xlform/src/_xlform.init';
import {dataInterface} from '../dataInterface';

const ErrorMessage = bem.create('error-message');
const ErrorMessage__strong = bem.create('error-message__header', '<strong>');
const ErrorMessage__link = bem.create('error-message__link', '<a>');

var webformStylesSupportUrl = 'http://help.kobotoolbox.org/creating-forms/formbuilder/using-alternative-enketo-web-form-styles';

class FormSettingsEditor extends React.Component {
  constructor(props) {
    super(props);
    autoBind(this);
  }

  render () {
    return (
      <bem.FormBuilderMeta>
        <bem.FormBuilderMeta__column>
          {this.props.meta.map((mtype) => {
            if (!mtype.key) {
              mtype.key = `meta-${mtype.name}`;
            }
            return (
              <FormCheckbox htmlFor={mtype} onChange={this.props.onCheckboxChange} {...mtype} />
            );
          })}
        </bem.FormBuilderMeta__column>
        <bem.FormBuilderMeta__column>
          {this.props.phoneMeta.map((mtype) => {
            if (!mtype.key) {
              mtype.key = `meta-${mtype.name}`;
            }
            return (
              <FormCheckbox htmlFor={mtype} onChange={this.props.onCheckboxChange} {...mtype} />
            );
          })}
        </bem.FormBuilderMeta__column>
      </bem.FormBuilderMeta>
    );
  }
  focusSelect () {
    this.refs.webformStyle.focus();
  }
};

class FormCheckbox extends React.Component {
  constructor(props) {
    super(props);
  }
  render () {
    // TODO: use better written (styled) checkboxes from https://github.com/kobotoolbox/kpi/pull/1864 after merge
    return (
      <bem.FormBuilderMeta__row>
        <input
          type='checkbox'
          id={this.props.name}
          checked={this.props.value}
          onChange={this.props.onChange}
        />
        <label htmlFor={this.props.name}>
          {this.props.label}
        </label>
      </bem.FormBuilderMeta__row>
    );
  }
};

class FormSettingsBox extends React.Component {
  constructor(props) {
    super(props);
    var formId = this.props.survey.settings.get('form_id');
    this.state = {
      xform_id_string: formId,
      meta: [],
      phoneMeta: []
    };
    this.META_PROPERTIES = ['start', 'end', 'today', 'deviceid'];
    this.PHONE_META_PROPERTIES = ['username', 'simserial', 'subscriberid', 'phonenumber'];
    autoBind(this);
  }

  componentDidMount() {
    this.updateState();
  }

  updateState(newState={}) {
    this.META_PROPERTIES.forEach(this.passValueIntoObj('meta', newState));
    this.PHONE_META_PROPERTIES.map(this.passValueIntoObj('phoneMeta', newState));
    this.setState(newState);
  }

  getSurveyDetail(sdId) {
    return this.props.survey.surveyDetails.filter(function(sd){
      return sd.attributes.name === sdId;
    })[0];
  }

  passValueIntoObj(category, newState) {
    newState[category] = [];
    return (id) => {
      var sd = this.getSurveyDetail(id);
      if (sd) {
        newState[category].push(assign({}, sd.attributes));
      }
    };
  }

  onCheckboxChange(evt) {
    this.getSurveyDetail(evt.target.id).set('value', evt.target.checked);
    this.updateState();
    if (typeof this.props.onChange === 'function') {
      this.props.onChange();
    }
  }

  onFieldChange(evt) {
    const fieldId = evt.target.id;
    const value = evt.target.value;

    if (fieldId === 'form_id') {
      this.props.survey.settings.set('form_id', value);
    }

    this.setState({
      xform_id_string: this.props.survey.settings.get('form_id')
    });
  }

  render() {
    return (
      <FormSettingsEditor {...this.state} onCheckboxChange={this.onCheckboxChange.bind(this)} />
    );
  }
};

export default assign({
  componentDidMount() {
    document.body.classList.add('hide-edge');

    if (this.state.editorState === 'existing') {
      let uid = this.props.params.assetid;
      stores.allAssets.whenLoaded(uid, (asset) => {
        this.setState({asset: asset});

        let translations = (
          asset.content &&
          asset.content.translations &&
          // slice makes shallow copy
          asset.content.translations.slice(0)
        ) || [];

        this.launchAppForSurveyContent(asset.content, {
          name: asset.name,
          translations: translations,
          settings__style: asset.settings__style,
          asset_uid: asset.uid,
          asset_type: asset.asset_type,
        });
      });
    } else {
      this.launchAppForSurveyContent();
    }

    document.querySelector('.page-wrapper__content').addEventListener('scroll', this.handleScroll);
    this.listenTo(stores.surveyState, this.surveyStateChanged);
  },

  componentWillUnmount () {
    if (this.app && this.app.survey) {
      document.querySelector('.page-wrapper__content').removeEventListener('scroll', this.handleScroll);
      this.app.survey.off('change');
    }
    this.unpreventClosingTab();
  },

  onFormSettingsBoxChange() {
    this.onSurveyChange();
  },

  onProjectDetailsChange({fieldName, fieldValue}) {
    const settingsNew = this.state.settingsNew || {};
    settingsNew[fieldName] = fieldValue;
    this.setState({
      settingsNew: settingsNew
    });
    this.onSurveyChange();
  },

  surveyStateChanged(state) {
    this.setState(state);
  },

  onStyleChange(evt) {
    let settingsStyle = null;
    if (evt !== null) {
      settingsStyle = evt.value;
    }

    this.setState({
      settings__style: settingsStyle
    });
    this.onSurveyChange();
  },

  onSurveyChange: _.debounce(function () {
    if (!this.state.asset_updated !== update_states.UNSAVED_CHANGES) {
      this.preventClosingTab();
    }
    this.setState({
      asset_updated: update_states.UNSAVED_CHANGES,
    });
  }, 200),

  preventClosingTab() {
    $(window).on('beforeunload.noclosetab', function(){
      return t('you have unsaved changes');
    });
  },

  unpreventClosingTab() {
    $(window).off('beforeunload.noclosetab');
  },

  nameChange(evt) {
    this.setState({
      name: evt.target.value,
    });
    this.onSurveyChange();
  },

  groupQuestions() {
    this.app.groupSelectedRows();
  },

  showAll(evt) {
    evt.preventDefault();
    evt.currentTarget.blur();
    this.app.expandMultioptions();
  },

  hasMetadataAndDetails() {
    return this.app && (
      this.state.asset_type === ASSET_TYPES.survey.id ||
      this.state.asset_type === ASSET_TYPES.template.id ||
      this.state.desiredAssetType === ASSET_TYPES.template.id
    );
  },

  needsSave() {
    return this.state.asset_updated === update_states.UNSAVED_CHANGES;
  },

  previewForm(evt) {
    if (evt && evt.preventDefault) {
      evt.preventDefault();
    }

    if (this.state.settings__style)
      this.app.survey.settings.set('style', this.state.settings__style);

    if (this.state.name)
      this.app.survey.settings.set('title', this.state.name);

    var params = {
      source: surveyToValidJson(this.app.survey),
    };

    params = koboMatrixParser(params);

    if (this.state.asset && this.state.asset.url) {
      params.asset = this.state.asset.url;
    }

    dataInterface.createAssetSnapshot(params).done((content) => {
      this.setState({
        enketopreviewOverlay: content.enketopreviewlink,
      });
    }).fail((jqxhr) => {
      let err;
      if (jqxhr && jqxhr.responseJSON && jqxhr.responseJSON.error) {
        err = jqxhr.responseJSON.error
      } else {
        err = t('Unknown Enketo preview error');
      }
      this.setState({
        enketopreviewError: err,
      });
    });
  },

  saveForm(evt) {
    if (evt && evt.preventDefault) {
      evt.preventDefault();
    }

    if (this.state.settings__style !== undefined) {
      this.app.survey.settings.set('style', this.state.settings__style);
    }

    let params = {
      content: surveyToValidJson(this.app.survey),
    };

    if (this.state.name) {
      params.name = this.state.name;
    }

    // handle settings update (if any changed)
    if (this.state.settingsNew) {
      let settings = {};
      if (this.state.asset) {
        settings = this.state.asset.settings;
      }

      if (this.state.settingsNew.description) {
        settings.description = this.state.settingsNew.description;
      }
      if (this.state.settingsNew.sector) {
        settings.sector = this.state.settingsNew.sector;
      }
      if (this.state.settingsNew.country) {
        settings.country = this.state.settingsNew.country;
      }
      if (this.state.settingsNew['share-metadata']) {
        settings['share-metadata'] = this.state.settingsNew['share-metadata'];
      }
      params.settings = JSON.stringify(settings);
    }

    params = koboMatrixParser(params);

    if (this.state.editorState === 'new') {
      // create new asset
      if (this.state.desiredAssetType) {
        params.asset_type = this.state.desiredAssetType;
      } else {
        params.asset_type = 'block';
      }
      actions.resources.createResource.triggerAsync(params)
        .then((asset) => {
          hashHistory.push('/library');
        })
    } else {
      // update existing asset
      var assetId = this.props.params.assetid;

      actions.resources.updateAsset.triggerAsync(assetId, params)
        .then(() => {
          this.unpreventClosingTab();
          this.setState({
            asset_updated: update_states.UP_TO_DATE,
            surveySaveFail: false,
          });
        })
        .catch((resp) => {
          var errorMsg = `${t('Your changes could not be saved, likely because of a lost internet connection.')}&nbsp;${t('Keep this window open and try saving again while using a better connection.')}`;
          if (resp.statusText != 'error')
            errorMsg = resp.statusText;

          alertify.defaults.theme.ok = 'ajs-cancel';
          let dialog = alertify.dialog('alert');
          let opts = {
            title: t('Error saving form'),
            message: errorMsg,
            label: t('Dismiss'),
          };
          dialog.set(opts).show();

          this.setState({
            surveySaveFail: true,
            asset_updated: update_states.SAVE_FAILED
          });
        });
    }
    this.setState({
      asset_updated: update_states.PENDING_UPDATE,
    });
  },

  handleScroll(evt) {
    var scrollTop = evt.target.scrollTop;
    if (!this.state.formHeaderFixed && scrollTop > 40) {
      var fhfh = $('.asset-view__row--header').height();
      this.setState({
        formHeaderFixed: true,
        formHeaderFixedHeight: fhfh,
      });
    } else if (this.state.formHeaderFixed && scrollTop <= 32) {
      this.setState({
        formHeaderFixed: false
      });
    }
  },

  buttonStates() {
    var ooo = {};
    if (!this.app) {
      ooo.allButtonsDisabled = true;
    } else {
      ooo.previewDisabled = true;
      if (this.app && this.app.survey) {
        ooo.previewDisabled = this.app.survey.rows.length < 1;
      }
      ooo.groupable = !!this.state.groupButtonIsActive;
      ooo.showAllOpen = !!this.state.multioptionsExpanded;
      ooo.showAllAvailable = (() => {
        var hasSelect = false;
        this.app.survey.forEachRow(function(row){
          if (row._isSelectQuestion()) {
            hasSelect = true;
          }
        });
        return hasSelect;
      })(); // todo: only true if survey has select questions
      ooo.name = this.state.name;
      ooo.hasSettings = this.state.backRoute === '/forms';
      ooo.styleValue = this.state.settings__style;
    }
    if (this.state.editorState === 'new') {
      ooo.saveButtonText = t('create');
    } else if (this.state.surveySaveFail) {
      ooo.saveButtonText = `${t('save')} (${t('retry')}) `;
    } else {
      ooo.saveButtonText = t('save');
    }
    return ooo;
  },

  toggleAsideLibrarySearch(evt) {
    evt.target.blur();
    this.setState({
      asideLayoutSettingsVisible: false,
      asideLibrarySearchVisible: !this.state.asideLibrarySearchVisible,
    });
  },

  toggleAsideLayoutSettings(evt) {
    evt.target.blur();
    this.setState({
      asideLayoutSettingsVisible: !this.state.asideLayoutSettingsVisible,
      asideLibrarySearchVisible: false
    });
  },

  hidePreview() {
    this.setState({
      enketopreviewOverlay: false
    });
  },

  hideCascade() {
    this.setState({
      showCascadePopup: false
    });
  },

  launchAppForSurveyContent(survey, _state={}) {
    if (_state.name) {
      _state.savedName = _state.name;
    }

    let isEmptySurvey = (
        survey &&
        Object.keys(survey.settings).length === 0 &&
        survey.survey.length === 0<commit_msg>keep aside settings in sessionStorage<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import reactMixin from 'react-mixin';
import autoBind from 'react-autobind';
import $ from 'jquery';
import Select from 'react-select';
import _ from 'underscore';
import DocumentTitle from 'react-document-title';
import SurveyScope from '../models/surveyScope';
import cascadeMixin from './cascadeMixin';
import AssetNavigator from './assetNavigator';
import {Link, hashHistory} from 'react-router';
import alertify from 'alertifyjs';
import {ProjectSettings} from '../components/formEditors';
import {
  surveyToValidJson,
  notify,
  assign,
  t,
  koboMatrixParser
} from '../utils';

import {
  ASSET_TYPES,
  AVAILABLE_FORM_STYLES,
  PROJECT_SETTINGS_CONTEXTS,
  update_states,
} from '../constants';

import ui from '../ui';
import bem from '../bem';
import stores from '../stores';
import actions from '../actions';
import dkobo_xlform from '../../xlform/src/_xlform.init';
import {dataInterface} from '../dataInterface';

const ErrorMessage = bem.create('error-message');
const ErrorMessage__strong = bem.create('error-message__header', '<strong>');
const ErrorMessage__link = bem.create('error-message__link', '<a>');

var webformStylesSupportUrl = 'http://help.kobotoolbox.org/creating-forms/formbuilder/using-alternative-enketo-web-form-styles';

class FormSettingsEditor extends React.Component {
  constructor(props) {
    super(props);
    autoBind(this);
  }

  render () {
    return (
      <bem.FormBuilderMeta>
        <bem.FormBuilderMeta__column>
          {this.props.meta.map((mtype) => {
            if (!mtype.key) {
              mtype.key = `meta-${mtype.name}`;
            }
            return (
              <FormCheckbox htmlFor={mtype} onChange={this.props.onCheckboxChange} {...mtype} />
            );
          })}
        </bem.FormBuilderMeta__column>
        <bem.FormBuilderMeta__column>
          {this.props.phoneMeta.map((mtype) => {
            if (!mtype.key) {
              mtype.key = `meta-${mtype.name}`;
            }
            return (
              <FormCheckbox htmlFor={mtype} onChange={this.props.onCheckboxChange} {...mtype} />
            );
          })}
        </bem.FormBuilderMeta__column>
      </bem.FormBuilderMeta>
    );
  }
  focusSelect () {
    this.refs.webformStyle.focus();
  }
};

class FormCheckbox extends React.Component {
  constructor(props) {
    super(props);
  }
  render () {
    // TODO: use better written (styled) checkboxes from https://github.com/kobotoolbox/kpi/pull/1864 after merge
    return (
      <bem.FormBuilderMeta__row>
        <input
          type='checkbox'
          id={this.props.name}
          checked={this.props.value}
          onChange={this.props.onChange}
        />
        <label htmlFor={this.props.name}>
          {this.props.label}
        </label>
      </bem.FormBuilderMeta__row>
    );
  }
};

class FormSettingsBox extends React.Component {
  constructor(props) {
    super(props);
    var formId = this.props.survey.settings.get('form_id');
    this.state = {
      xform_id_string: formId,
      meta: [],
      phoneMeta: []
    };
    this.META_PROPERTIES = ['start', 'end', 'today', 'deviceid'];
    this.PHONE_META_PROPERTIES = ['username', 'simserial', 'subscriberid', 'phonenumber'];
    autoBind(this);
  }

  componentDidMount() {
    this.updateState();
  }

  updateState(newState={}) {
    this.META_PROPERTIES.forEach(this.passValueIntoObj('meta', newState));
    this.PHONE_META_PROPERTIES.map(this.passValueIntoObj('phoneMeta', newState));
    this.setState(newState);
  }

  getSurveyDetail(sdId) {
    return this.props.survey.surveyDetails.filter(function(sd){
      return sd.attributes.name === sdId;
    })[0];
  }

  passValueIntoObj(category, newState) {
    newState[category] = [];
    return (id) => {
      var sd = this.getSurveyDetail(id);
      if (sd) {
        newState[category].push(assign({}, sd.attributes));
      }
    };
  }

  onCheckboxChange(evt) {
    this.getSurveyDetail(evt.target.id).set('value', evt.target.checked);
    this.updateState();
    if (typeof this.props.onChange === 'function') {
      this.props.onChange();
    }
  }

  onFieldChange(evt) {
    const fieldId = evt.target.id;
    const value = evt.target.value;

    if (fieldId === 'form_id') {
      this.props.survey.settings.set('form_id', value);
    }

    this.setState({
      xform_id_string: this.props.survey.settings.get('form_id')
    });
  }

  render() {
    return (
      <FormSettingsEditor {...this.state} onCheckboxChange={this.onCheckboxChange.bind(this)} />
    );
  }
};

const ASIDE_CACHE_NAME = 'kpi.editable-form.aside';

export default assign({
  componentDidMount() {
    document.body.classList.add('hide-edge');

    this.loadAsideSettings();

    if (this.state.editorState === 'existing') {
      let uid = this.props.params.assetid;
      stores.allAssets.whenLoaded(uid, (asset) => {
        this.setState({asset: asset});

        let translations = (
          asset.content &&
          asset.content.translations &&
          // slice makes shallow copy
          asset.content.translations.slice(0)
        ) || [];

        this.launchAppForSurveyContent(asset.content, {
          name: asset.name,
          translations: translations,
          settings__style: asset.settings__style,
          asset_uid: asset.uid,
          asset_type: asset.asset_type,
        });
      });
    } else {
      this.launchAppForSurveyContent();
    }

    document.querySelector('.page-wrapper__content').addEventListener('scroll', this.handleScroll);
    this.listenTo(stores.surveyState, this.surveyStateChanged);
  },

  componentWillUnmount () {
    if (this.app && this.app.survey) {
      document.querySelector('.page-wrapper__content').removeEventListener('scroll', this.handleScroll);
      this.app.survey.off('change');
    }
    this.unpreventClosingTab();
  },

  loadAsideSettings() {
    const asideSettings = sessionStorage.getItem(ASIDE_CACHE_NAME);
    if (asideSettings) {
      this.setState(JSON.parse(asideSettings));
    }
  },

  saveAsideSettings(asideSettings) {
    sessionStorage.setItem(ASIDE_CACHE_NAME, JSON.stringify(asideSettings));
  },

  onFormSettingsBoxChange() {
    this.onSurveyChange();
  },

  onProjectDetailsChange({fieldName, fieldValue}) {
    const settingsNew = this.state.settingsNew || {};
    settingsNew[fieldName] = fieldValue;
    this.setState({
      settingsNew: settingsNew
    });
    this.onSurveyChange();
  },

  surveyStateChanged(state) {
    this.setState(state);
  },

  onStyleChange(evt) {
    let settingsStyle = null;
    if (evt !== null) {
      settingsStyle = evt.value;
    }

    this.setState({
      settings__style: settingsStyle
    });
    this.onSurveyChange();
  },

  onSurveyChange: _.debounce(function () {
    if (!this.state.asset_updated !== update_states.UNSAVED_CHANGES) {
      this.preventClosingTab();
    }
    this.setState({
      asset_updated: update_states.UNSAVED_CHANGES,
    });
  }, 200),

  preventClosingTab() {
    $(window).on('beforeunload.noclosetab', function(){
      return t('you have unsaved changes');
    });
  },

  unpreventClosingTab() {
    $(window).off('beforeunload.noclosetab');
  },

  nameChange(evt) {
    this.setState({
      name: evt.target.value,
    });
    this.onSurveyChange();
  },

  groupQuestions() {
    this.app.groupSelectedRows();
  },

  showAll(evt) {
    evt.preventDefault();
    evt.currentTarget.blur();
    this.app.expandMultioptions();
  },

  hasMetadataAndDetails() {
    return this.app && (
      this.state.asset_type === ASSET_TYPES.survey.id ||
      this.state.asset_type === ASSET_TYPES.template.id ||
      this.state.desiredAssetType === ASSET_TYPES.template.id
    );
  },

  needsSave() {
    return this.state.asset_updated === update_states.UNSAVED_CHANGES;
  },

  previewForm(evt) {
    if (evt && evt.preventDefault) {
      evt.preventDefault();
    }

    if (this.state.settings__style)
      this.app.survey.settings.set('style', this.state.settings__style);

    if (this.state.name)
      this.app.survey.settings.set('title', this.state.name);

    var params = {
      source: surveyToValidJson(this.app.survey),
    };

    params = koboMatrixParser(params);

    if (this.state.asset && this.state.asset.url) {
      params.asset = this.state.asset.url;
    }

    dataInterface.createAssetSnapshot(params).done((content) => {
      this.setState({
        enketopreviewOverlay: content.enketopreviewlink,
      });
    }).fail((jqxhr) => {
      let err;
      if (jqxhr && jqxhr.responseJSON && jqxhr.responseJSON.error) {
        err = jqxhr.responseJSON.error
      } else {
        err = t('Unknown Enketo preview error');
      }
      this.setState({
        enketopreviewError: err,
      });
    });
  },

  saveForm(evt) {
    if (evt && evt.preventDefault) {
      evt.preventDefault();
    }

    if (this.state.settings__style !== undefined) {
      this.app.survey.settings.set('style', this.state.settings__style);
    }

    let params = {
      content: surveyToValidJson(this.app.survey),
    };

    if (this.state.name) {
      params.name = this.state.name;
    }

    // handle settings update (if any changed)
    if (this.state.settingsNew) {
      let settings = {};
      if (this.state.asset) {
        settings = this.state.asset.settings;
      }

      if (this.state.settingsNew.description) {
        settings.description = this.state.settingsNew.description;
      }
      if (this.state.settingsNew.sector) {
        settings.sector = this.state.settingsNew.sector;
      }
      if (this.state.settingsNew.country) {
        settings.country = this.state.settingsNew.country;
      }
      if (this.state.settingsNew['share-metadata']) {
        settings['share-metadata'] = this.state.settingsNew['share-metadata'];
      }
      params.settings = JSON.stringify(settings);
    }

    params = koboMatrixParser(params);

    if (this.state.editorState === 'new') {
      // create new asset
      if (this.state.desiredAssetType) {
        params.asset_type = this.state.desiredAssetType;
      } else {
        params.asset_type = 'block';
      }
      actions.resources.createResource.triggerAsync(params)
        .then((asset) => {
          hashHistory.push('/library');
        })
    } else {
      // update existing asset
      var assetId = this.props.params.assetid;

      actions.resources.updateAsset.triggerAsync(assetId, params)
        .then(() => {
          this.unpreventClosingTab();
          this.setState({
            asset_updated: update_states.UP_TO_DATE,
            surveySaveFail: false,
          });
        })
        .catch((resp) => {
          var errorMsg = `${t('Your changes could not be saved, likely because of a lost internet connection.')}&nbsp;${t('Keep this window open and try saving again while using a better connection.')}`;
          if (resp.statusText != 'error')
            errorMsg = resp.statusText;

          alertify.defaults.theme.ok = 'ajs-cancel';
          let dialog = alertify.dialog('alert');
          let opts = {
            title: t('Error saving form'),
            message: errorMsg,
            label: t('Dismiss'),
          };
          dialog.set(opts).show();

          this.setState({
            surveySaveFail: true,
            asset_updated: update_states.SAVE_FAILED
          });
        });
    }
    this.setState({
      asset_updated: update_states.PENDING_UPDATE,
    });
  },

  handleScroll(evt) {
    var scrollTop = evt.target.scrollTop;
    if (!this.state.formHeaderFixed && scrollTop > 40) {
      var fhfh = $('.asset-view__row--header').height();
      this.setState({
        formHeaderFixed: true,
        formHeaderFixedHeight: fhfh,
      });
    } else if (this.state.formHeaderFixed && scrollTop <= 32) {
      this.setState({
        formHeaderFixed: false
      });
    }
  },

  buttonStates() {
    var ooo = {};
    if (!this.app) {
      ooo.allButtonsDisabled = true;
    } else {
      ooo.previewDisabled = true;
      if (this.app && this.app.survey) {
        ooo.previewDisabled = this.app.survey.rows.length < 1;
      }
      ooo.groupable = !!this.state.groupButtonIsActive;
      ooo.showAllOpen = !!this.state.multioptionsExpanded;
      ooo.showAllAvailable = (() => {
        var hasSelect = false;
        this.app.survey.forEachRow(function(row){
          if (row._isSelectQuestion()) {
            hasSelect = true;
          }
        });
        return hasSelect;
      })(); // todo: only true if survey has select questions
      ooo.name = this.state.name;
      ooo.hasSettings = this.state.backRoute === '/forms';
      ooo.styleValue = this.state.settings__style;
    }
    if (this.state.editorState === 'new') {
      ooo.saveButtonText = t('create');
    } else if (this.state.surveySaveFail) {
      ooo.saveButtonText = `${t('save')} (${t('retry')}) `;
    } else {
      ooo.saveButtonText = t('save');
    }
    return ooo;
  },

  toggleAsideLibrarySearch(evt) {
    evt.target.blur();
    const asideSettings = {
      asideLayoutSettingsVisible: false,
      asideLibrarySearchVisible: !this.state.asideLibrarySearchVisible,
    };
    this.setState(asideSettings);
    this.saveAsideSettings(asideSettings);
  },

  toggleAsideLayoutSettings(evt) {
    evt.target.blur();
    const asideSettings = {
      asideLayoutSettingsVisible: !this.state.asideLayoutSettingsVisible,
      asideLibrarySearchVisible: false
    };
    this.setState(asideSettings);
    this.saveAsideSettings(asideSettings);
  },

  hidePreview() {
    this.setState({
      enketopreviewOverlay: false
    });
  },

  hideCascade() {
    this.setState({
      showCascadePopup: false
    });
  },

  launchAppForSurveyContent(survey, _state={}) {
    if (_state.name) {
      _state.savedName = _state.name;
    }

    let isEmptySurvey = (
        survey &&
        Object.keys(survey.settings).length === 0 &&
        survey.survey.length === 0
{'fraction_non_alphanumeric': 0.14171714606986108, 'fraction_numerical': 0.0011607240015959956, 'mean_word_length': 2.603921568627451, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 57, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1772201', 'n_tokens_mistral': 8661, 'n_tokens_neox': 8616, 'n_words': 1391}