'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

exports.default = CodeExporterWrapper;

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _copyToClipboard = require('copy-to-clipboard');

var _copyToClipboard2 = _interopRequireDefault(_copyToClipboard);

var _graphql = require('graphql');

var _prismjs = require('prismjs');

var _prismjs2 = _interopRequireDefault(_prismjs);

var _snippets = require('./snippets');

var _snippets2 = _interopRequireDefault(_snippets);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

// TODO: can we use plain graphiql nodes or do we need @onegraph?
// import GraphiQL from '@onegraph/graphiql';

// TODO: not sure if we should include all snippets by default


function formatVariableName(name) {
  var uppercasePattern = /[A-Z]/g;

  return name.charAt(0).toUpperCase() + name.slice(1).replace(uppercasePattern, '_$&').toUpperCase();
}

var copyIcon = _react2.default.createElement(
  'svg',
  {
    xmlns: 'http://www.w3.org/2000/svg',
    width: '20',
    height: '20',
    viewBox: '0 0 24 24' },
  _react2.default.createElement('path', { fill: 'none', d: 'M0 0h24v24H0V0z' }),
  _react2.default.createElement('path', { d: 'M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm-1 4H8c-1.1 0-1.99.9-1.99 2L6 21c0 1.1.89 2 1.99 2H19c1.1 0 2-.9 2-2V11l-6-6zM8 21V7h6v5h5v9H8z' })
);

var getInitialOptions = function getInitialOptions(snippet) {
  return snippet.options.reduce(function (newOptions, option) {
    newOptions[option.id] = {
      label: option.label,
      value: option.initial
    };

    return newOptions;
  }, {});
};

var getOperations = function getOperations(query) {
  var operations = [];
  try {
    var _iteratorNormalCompletion = true;
    var _didIteratorError = false;
    var _iteratorError = undefined;

    try {
      for (var _iterator = (0, _graphql.parse)(query).definitions[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
        var def = _step.value;

        if (def.kind === 'OperationDefinition' && def.operation !== 'subscription') {
          operations.push(def);
        }
      }
    } catch (err) {
      _didIteratorError = true;
      _iteratorError = err;
    } finally {
      try {
        if (!_iteratorNormalCompletion && _iterator.return) {
          _iterator.return();
        }
      } finally {
        if (_didIteratorError) {
          throw _iteratorError;
        }
      }
    }

    return operations;
  } catch (e) {
    return [];
  }
};

var getUsedVariables = function getUsedVariables(variables, operation) {
  return (operation.variableDefinitions || []).reduce(function (usedVariables, variable) {
    var variableName = variable.variable.name.value;
    if (variables[variableName]) {
      usedVariables[variableName] = variables[variableName];
    }

    return usedVariables;
  }, {});
};

var getOperationName = function getOperationName(operation) {
  return operation.name ? operation.name.value : operation.operation;
};

var getOperationDisplayName = function getOperationDisplayName(operation) {
  return operation.name ? operation.name.value : '<Unnamed:' + operation.operation + '>';
};

var CodeExporter = function (_Component) {
  _inherits(CodeExporter, _Component);

  function CodeExporter(props, context) {
    _classCallCheck(this, CodeExporter);

    var _this = _possibleConstructorReturn(this, (CodeExporter.__proto__ || Object.getPrototypeOf(CodeExporter)).call(this, props, context));

    _this.setSnippet = function (name) {
      var snippet = _this.props.snippets.find(function (snippet) {
        return snippet.name === name && snippet.language === _this.state.snippet.language;
      });
      if (snippet) {
        _this.setState({
          options: getInitialOptions(snippet),
          snippet: snippet
        });
      }
    };

    _this.setLanguage = function (language) {
      var snippet = _this.props.snippets.find(function (snippet) {
        return snippet.language === language;
      });

      if (snippet) {
        _this.setState({
          options: getInitialOptions(snippet),
          snippet: snippet
        });
      }
    };

    _this.defaultSetOption = function (id, value) {
      return _this.setState({
        options: _extends({}, _this.state.options, _defineProperty({}, id, _extends({}, _this.state.options[id], {
          value: value
        })))
      });
    };

    var initialSnippet = props.initialSnippet;


    _this.state = {
      languages: [],
      showCopiedTooltip: false,
      options: getInitialOptions(initialSnippet),
      snippet: initialSnippet,
      query: props.query
    };
    return _this;
  }

  _createClass(CodeExporter, [{
    key: 'componentDidMount',
    value: function componentDidMount() {
      var _this2 = this;

      var style = document.createElement('link');
      style.setAttribute('rel', 'stylesheet');
      style.setAttribute('href', 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.15.0/themes/' + this.props.theme + '.min.css');

      document.head ? document.head.appendChild(style) : null;
      this.style = style;

      var langs = this.props.snippets.map(function (snippet) {
        return snippet.prismLanguage || snippet.language.toLowerCase();
      });

      langs.forEach(function (lang) {
        if (_this2.state.languages.indexOf(lang) === -1) {
          // $FlowFixMe: No dynamic requires (can have the snippet require this)
          require('prismjs/components/prism-' + lang);
        }
      });

      this.setState(function (prevState) {
        return {
          languages: [].concat(_toConsumableArray(prevState.languages), _toConsumableArray(langs))
        };
      });
    }
  }, {
    key: 'componentWillUnmount',
    value: function componentWillUnmount() {
      this.style && this.style.remove();
    }
  }, {
    key: 'render',
    value: function render() {
      var _this3 = this;

      var _props = this.props,
          serverUrl = _props.serverUrl,
          snippets = _props.snippets,
          _props$context = _props.context,
          context = _props$context === undefined ? {} : _props$context,
          _props$variables = _props.variables,
          variables = _props$variables === undefined ? {} : _props$variables,
          _props$headers = _props.headers,
          headers = _props$headers === undefined ? {} : _props$headers,
          _props$setOption = _props.setOption,
          setOption = _props$setOption === undefined ? this.defaultSetOption : _props$setOption;
      var _state = this.state,
          snippet = _state.snippet,
          options = _state.options,
          operations = _state.operations,
          showCopiedTooltip = _state.showCopiedTooltip;


      if (!operations || operations.length === 0 || !snippet) {
        return null;
      }

      var name = snippet.name,
          language = snippet.language,
          prismLanguage = snippet.prismLanguage,
          generate = snippet.generate;


      var operationList = operations.map(function (operation) {
        return {
          query: (0, _graphql.print)(operation),
          name: getOperationName(operation),
          displayName: getOperationDisplayName(operation),
          type: operation.operation,
          variableName: formatVariableName(getOperationName(operation)),
          variables: getUsedVariables(variables, operation),
          operation: operation
        };
      });

      var codeSnippet = generate({
        serverUrl: serverUrl,
        headers: headers,
        context: context,
        operations: operationList,
        options: Object.keys(options).reduce(function (flags, id) {
          flags[id] = options[id].value;
          return flags;
        }, {})
      });

      var rawSnippet = codeSnippet;
      // we use a try catch here because otherwise highlight might break the render
      try {
        var lang = prismLanguage || language.toLowerCase();
        codeSnippet = _prismjs2.default.highlight(codeSnippet, _prismjs2.default.languages[lang], lang);
      } catch (e) {}

      return _react2.default.createElement(
        'div',
        { style: { minWidth: 410 } },
        _react2.default.createElement(
          'div',
          {
            style: {
              fontFamily: 'system, -apple-system, San Francisco, Helvetica Neue, arial, sans-serif'
            } },
          _react2.default.createElement(
            'div',
            { style: { padding: '12px 7px 8px' } },
            _react2.default.createElement(
              'div',
              { label: language, title: 'Language' },
              snippets.map(function (snippet) {
                return snippet.language;
              }).filter(function (lang, index, arr) {
                return arr.indexOf(lang) === index;
              }).sort(function (a, b) {
                return a.localeCompare(b);
              }).map(function (lang) {
                return _react2.default.createElement(
                  'li',
                  { onClick: function onClick() {
                      return _this3.setLanguage(lang);
                    } },
                  lang
                );
              })
            ),
            _react2.default.createElement(
              'div',
              { label: name, title: 'Mode' },
              snippets.filter(function (snippet) {
                return snippet.language === language;
              }).map(function (snippet) {
                return snippet.name;
              }).sort(function (a, b) {
                return a.toLowerCase().localeCompare(b.toLowerCase());
              }).map(function (snippetName) {
                return _react2.default.createElement(
                  'li',
                  { onClick: function onClick() {
                      return _this3.setSnippet(snippetName);
                    } },
                  snippetName
                );
              })
            )
          ),
          snippet.options.length > 0 ? _react2.default.createElement(
            'div',
            { style: { padding: '0px 11px 10px' } },
            _react2.default.createElement(
              'div',
              {
                style: {
                  fontWeight: 700,
                  color: 'rgb(177, 26, 4)',
                  fontVariant: 'small-caps',
                  textTransform: 'lowercase'
                } },
              'Options'
            ),
            Object.keys(options).sort(function (a, b) {
              return a.localeCompare(b);
            }).map(function (optionId) {
              return _react2.default.createElement(
                'div',
                { key: optionId },
                _react2.default.createElement('input', {
                  id: optionId,
                  type: 'checkbox',
                  style: { position: 'relative', top: -1 },
                  checked: options[optionId].value,
                  onChange: function onChange() {
                    return setOption(optionId, !options[optionId].value);
                  }
                }),
                _react2.default.createElement(
                  'label',
                  { 'for': optionId, style: { paddingLeft: 5 } },
                  options[optionId].label
                )
              );
            })
          ) : _react2.default.createElement('div', { style: { minHeight: 8 } })
        ),
        _react2.default.createElement(
          'button',
          {
            className: 'toolbar-button',
            style: {
              fontSize: '1.2em',
              padding: 0,
              position: 'absolute',
              left: 340,
              marginTop: -20,
              width: 40,
              height: 40,
              backgroundColor: 'white',
              borderRadius: 40,
              border: 'none'
            },
            type: 'link',
            onClick: function onClick() {
              (0, _copyToClipboard2.default)(rawSnippet);
              _this3.setState({ showCopiedTooltip: true }, function () {
                return setTimeout(function () {
                  return _this3.setState({ showCopiedTooltip: false });
                }, 450);
              });
            } },
          _react2.default.createElement(
            'div',
            {
              style: {
                position: 'absolute',
                top: '-30px',
                left: '-15px',
                fontSize: 'small',
                padding: '6px 8px',
                color: '#fff',
                textAlign: 'left',
                textDecoration: 'none',
                wordWrap: 'break-word',
                backgroundColor: 'rgba(0,0,0,0.75)',
                borderRadius: '4px',
                boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
                display: showCopiedTooltip ? 'block' : 'none'
              },
              pointerEvents: 'none' },
            'Copied!'
          ),
          copyIcon
        ),
        _react2.default.createElement(
          'pre',
          {
            style: {
              borderTop: '1px solid rgb(220, 220, 220)',
              padding: '15px 12px',
              margin: 0
            } },
          _react2.default.createElement('code', {
            style: {
              fontFamily: 'Dank Monk, Fira Code, Hack, Consolas, Inconsolata, Droid Sans Mono, Monaco, monospace',
              textRendering: 'optimizeLegibility',
              fontSize: 12
            },
            dangerouslySetInnerHTML: {
              __html: codeSnippet
            }
          }),
          _react2.default.createElement('div', { style: { minHeight: 10 } })
        )
      );
    }
  }], [{
    key: 'getDerivedStateFromProps',
    value: function getDerivedStateFromProps(props, state) {
      // for now we do not support subscriptions, might add those later
      var operations = getOperations(props.query).filter(function (op) {
        return op.operation !== 'subscription';
      });

      return {
        operations: operations,
        query: props.query
      };
    }
  }]);

  return CodeExporter;
}(_react.Component);

var ErrorBoundary = function (_React$Component) {
  _inherits(ErrorBoundary, _React$Component);

  function ErrorBoundary() {
    var _ref;

    var _temp, _this4, _ret;

    _classCallCheck(this, ErrorBoundary);

    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    return _ret = (_temp = (_this4 = _possibleConstructorReturn(this, (_ref = ErrorBoundary.__proto__ || Object.getPrototypeOf(ErrorBoundary)).call.apply(_ref, [this].concat(args))), _this4), _this4.state = { hasError: false }, _temp), _possibleConstructorReturn(_this4, _ret);
  }

  _createClass(ErrorBoundary, [{
    key: 'componentDidCatch',
    value: function componentDidCatch(error, info) {
      this.setState({ hasError: true });
      console.error('Error in component', error, info);
    }
  }, {
    key: 'render',
    value: function render() {
      if (this.state.hasError) {
        return _react2.default.createElement(
          'div',
          null,
          'Something went wrong on our side while generating a snippet, sorry about that!'
        );
      }
      return this.props.children;
    }
  }]);

  return ErrorBoundary;
}(_react2.default.Component);

// we borrow class names from graphiql's CSS as the visual appearance is the same
// yet we might want to change that at some point in order to have a self-contained standalone
function CodeExporterWrapper(_ref2) {
  var query = _ref2.query,
      serverUrl = _ref2.serverUrl,
      variables = _ref2.variables,
      _ref2$context = _ref2.context,
      context = _ref2$context === undefined ? {} : _ref2$context,
      _ref2$headers = _ref2.headers,
      headers = _ref2$headers === undefined ? {} : _ref2$headers,
      _ref2$theme = _ref2.theme,
      theme = _ref2$theme === undefined ? 'prism' : _ref2$theme,
      _ref2$hideCodeExporte = _ref2.hideCodeExporter,
      hideCodeExporter = _ref2$hideCodeExporte === undefined ? function () {} : _ref2$hideCodeExporte,
      _ref2$snippets = _ref2.snippets,
      snippets = _ref2$snippets === undefined ? _snippets2.default : _ref2$snippets;

  var jsonVariables = {};

  try {
    var parsedVariables = JSON.parse(variables);
    if ((typeof parsedVariables === 'undefined' ? 'undefined' : _typeof(parsedVariables)) === 'object') {
      jsonVariables = parsedVariables;
    }
  } catch (e) {}

  return _react2.default.createElement(
    'div',
    {
      className: 'historyPaneWrap',
      style: {
        width: 440,
        minWidth: 440,
        zIndex: 7
      } },
    _react2.default.createElement(
      'div',
      { className: 'history-title-bar' },
      _react2.default.createElement(
        'div',
        { className: 'history-title' },
        'Code Exporter'
      ),
      _react2.default.createElement(
        'div',
        { className: 'doc-explorer-rhs' },
        _react2.default.createElement(
          'div',
          { className: 'docExplorerHide', onClick: hideCodeExporter },
          '\u2715'
        )
      )
    ),
    _react2.default.createElement(
      'div',
      {
        className: 'history-contents',
        style: { borderTop: '1px solid #d6d6d6' } },
      _react2.default.createElement(
        ErrorBoundary,
        null,
        _react2.default.createElement(CodeExporter, {
          query: query,
          serverUrl: serverUrl,
          snippets: snippets,
          initialSnippet: snippets[0],
          theme: theme,
          context: context,
          headers: headers,
          variables: jsonVariables
        })
      )
    )
  );
}