"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _react = _interopRequireDefault(require("react"));
var _uuid = require("uuid");
var _madwizard = require("madwizard");
var _TextContent = require("@patternfly/react-core/dist/esm/components/Text/TextContent");
var _Capabilities = require("@kui-shell/core/mdist/api/Capabilities");
var _reactMarkdown = _interopRequireDefault(require("react-markdown"));
var _remarkGfm = _interopRequireDefault(require("remark-gfm"));
var _remarkMark = _interopRequireDefault(require("./remark-mark.js"));
var _remarkKeys = _interopRequireDefault(require("./remark-keys.js"));
var _remarkDirective = _interopRequireDefault(require("remark-directive"));
var _remarkEmoji = _interopRequireDefault(require("remark-emoji"));
var _remarkFrontmatter = _interopRequireDefault(require("remark-frontmatter"));
var _components = _interopRequireDefault(require("./components"));
var _rehypeRaw = _interopRequireDefault(require("rehype-raw"));
var _rehypeSlug = _interopRequireDefault(require("rehype-slug"));
var _rehypeIcons = _interopRequireDefault(require("./rehype-icons"));
var _frontmatter = require("./frontmatter");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/*
 * Copyright 2020 The Kubernetes Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var __awaiter = void 0 && (void 0).__awaiter || function (thisArg, _arguments, P, generator) {
  function adopt(value) {
    return value instanceof P ? value : new P(function (resolve) {
      resolve(value);
    });
  }
  return new (P || (P = Promise))(function (resolve, reject) {
    function fulfilled(value) {
      try {
        step(generator.next(value));
      } catch (e) {
        reject(e);
      }
    }
    function rejected(value) {
      try {
        step(generator["throw"](value));
      } catch (e) {
        reject(e);
      }
    }
    function step(result) {
      result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
    }
    step((generator = generator.apply(thisArg, _arguments || [])).next());
  });
};
const rehypePlugins = (uuid, choices, filepath) => [_madwizard.Parser.rehypeWizard, [_madwizard.Parser.rehypeTabbed, uuid, choices, {
  optimize: {
    aprioris: (0, _Capabilities.inElectron)()
  }
}], _madwizard.Parser.rehypeTip, [_madwizard.Parser.rehypeCodeIndexer, uuid, filepath, {}, undefined, true], _rehypeIcons.default, _rehypeRaw.default, _rehypeSlug.default];
const remarkPlugins = tab => [_remarkGfm.default, _remarkDirective.default, [_remarkFrontmatter.default, ['yaml', 'toml']], [_frontmatter.kuiFrontmatter, {
  tab
}], _remarkEmoji.default // [emojis, { emoticon: true }]
];

class Markdown extends _react.default.PureComponent {
  constructor(props) {
    super(props);
    this.mounted = false;
    /** Handlers to be called on unmount */
    this.cleaners = [];
    this.uuid = (0, _uuid.v4)();
    this.choices = this.props.choices || _madwizard.Choices.newChoiceState('default');
    /** This will be the `components` argument to `<ReactMarkdown/>` */
    this._components = (0, _components.default)({
      mdprops: this.props,
      repl: this.repl,
      uuid: this.uuid,
      choices: this.choices,
      codeBlockResponses: this.codeBlockResponseWithReplayedBit.bind(this)
    });
    this._remarkPlugins = remarkPlugins(this.props.tab);
    this._rehypePlugins = rehypePlugins(this.uuid, this.choices, this.props.filepath);
    this.state = {
      source: '',
      hasError: false
    };
  }
  /** We are going away. Invoke cleaners */
  componentWillUnmount() {
    this.mounted = false;
    this.cleaners.forEach(_ => _());
  }
  /** We are coming to life */
  componentDidMount() {
    this.mounted = true;
    this.prepareSource();
  }
  /** New props, so re-fetch inlined source if needed */
  componentDidUpdate(prevProps) {
    if (prevProps.source !== this.props.source) {
      this.prepareSource();
    }
  }
  /**
   * Base path for snippet inlining. Prefer `this.props`, which comes
   * from user/command line. Otherwise, look in the top matter of the
   * document.  Later, in `inlineSnippets` we will also look in each
   * include line in the document, which may also specify a
   * line-specific base path.
   */
  get snippetBasePath() {
    if (this.props.snippetBasePath) {
      return this.props.snippetBasePath;
    } else {
      const fm = (0, _frontmatter.tryFrontmatter)(this.props.source);
      if (fm.attributes && fm.attributes.snippets && fm.attributes.snippets.basePath) {
        return fm.attributes.snippets.basePath;
      }
    }
  }
  static getDerivedStateFromError() {
    return {
      hasError: true
    };
  }
  componentDidCatch(error, errorInfo) {
    console.error(error, errorInfo);
  }
  /**
   * To support snippet inlining (which may require an async file
   * fetch), we need to prepare the source from props into state. Yay
   * react.
   */
  prepareSource() {
    return __awaiter(this, void 0, void 0, function* () {
      if (!this.mounted) {
        return;
      }
      const sourcePriorToInlining = (yield this.source(this.props)).trim();
      if (this.props.tab && this.props.tab.REPL) {
        setTimeout(() => __awaiter(this, void 0, void 0, function* () {
          if (this.mounted) {
            const args = {
              REPL: this.repl
            };
            const {
              loadNotebook
            } = yield Promise.resolve().then(() => require('@kui-shell/plugin-client-common/notebook'));
            const fetcher = filepath => loadNotebook(filepath, args).then(_ => _.toString());
            const source = yield _madwizard.Parser.inlineSnippets({
              fetcher,
              madwizardOptions: {},
              snippetBasePath: this.snippetBasePath
            })(sourcePriorToInlining, this.props.filepath);
            this.setState({
              source: Markdown.hackSource(source)
            });
          }
        }));
      } else {
        this.setState({
          source: Markdown.hackSource(sourcePriorToInlining)
        });
      }
    });
  }
  codeBlockResponseWithReplayedBit(codeBlockIdx) {
    const fromProps = this.props.codeBlockResponses ? this.props.codeBlockResponses[codeBlockIdx] : undefined;
    // replayed if we have a response in props, because the commentary
    // controller has fetched this from a json from that stores
    // pre-recorded output
    const replayed = !!fromProps;
    // the current response to be displayed favors state, as these
    // come from re-executions in this session
    const response = fromProps || undefined;
    return Object.assign({
      replayed
    }, response);
  }
  /** `exec` controller */
  get repl() {
    return this.props.tab ? this.props.tab.REPL : undefined;
  }
  /** @return markdown source, as string in application/markdown format */
  source(props) {
    return __awaiter(this, void 0, void 0, function* () {
      if (props.contentType === 'text/html') {
        const [{
          default: TurndownService
        }, {
          gfm
        }] = yield Promise.all([Promise.resolve().then(() => require('turndown')), Promise.resolve().then(() => require('turndown-plugin-gfm'))]);
        const td = new TurndownService();
        td.use(gfm);
        return td.turndown(props.source);
      } else {
        return props.source;
      }
    });
  }
  /**
   * Deal with pymdown indentation syntax, and other not-worth-parsing
   * syntax from pymdown (such as target=_blank for links).
   */
  static hackSource(source) {
    return (0, _remarkKeys.default)((0, _remarkMark.default)(_madwizard.Parser.hackMarkdownSource(source))).trim().replace(/\){target=[^}]+}/g, ')').replace(/{draggable=(false|true)}/g, '');
  }
  get className() {
    return this.props.className || 'padding-content marked-content page-content ' + (this.props.extraClassName || '') + (!this.props.nested ? ' scrollable scrollable-x scrollable-auto' : '');
  }
  render() {
    if (this.props.onRender) {
      this.props.onRender();
    }
    if (this.state.hasError) {
      return _react.default.createElement(_TextContent.TextContent, null, "Internal Error");
    }
    const {
      source
    } = this.state;
    if (source.length === 0) {
      return _react.default.createElement(_react.default.Fragment, null);
    }
    return _react.default.createElement(_react.default.Suspense, {
      fallback: _react.default.createElement("div", null)
    }, _react.default.createElement(_TextContent.TextContent, null, _react.default.createElement(_reactMarkdown.default, {
      className: this.className,
      components: this._components(),
      remarkPlugins: this._remarkPlugins,
      rehypePlugins: this._rehypePlugins,
      "data-is-nested": this.props.nested || undefined
    }, source)));
  }
}
exports.default = Markdown;