## Introduction

This migration guide includes all information regarding changes users must be aware of when updating from TOAST UI Editor 2.x to TOAST UI Editor 3.0. 

TOAST UI Editor (hereafter referred to as the 'Editor') has removed the original [CodeMirror](https://codemirror.net/), squire, and to-mark dependencies and has modified the editor to use abstract models through Prosemirror. Since the core module API and plugin usages were changed, it is advised that users consult the migration guide carefully. The table of contents is as follows and refers to the 'Changes' in the order enumerated when updating. 

## Table of Contents

- [Changes](#changes)
  1. [Installation and Usages](#1-installation-and-usages)
  2. [Customizing the Toolbar](#2-customizing-the-toolbar)
  3. [Defining Plugins](#3-defining-plugins)
  4. [APIs and Events](#4-APIs-and-events)
  5. [Supported Browsers](#5-supported-browsers)
- [Removed Features](#removed-features)
  1. [Removed jQuery Wrapper](#1-removed-jquery-wrapper)
  2. [Removed Dependencies](#2-removed-dependencies)
  3. [Removed APIs](#3-removed-apis)

## Changes

### 1. Installation and Usages

To use the Editor, use the [scoped package](https://docs.npmjs.com/using-npm/scope.html) to install the `@toast-ui/editor` package as you did for the previous v2.x. The following is an example of using the npm command to install the Editor. 

```sh
$ npm install @toast-ui/editor
$ npm install @toast-ui/editor@<version>
```

#### Usages

```js
const Editor = require('@toast-ui/editor'); /* CommonJS */
import Editor from '@toast-ui/editor'; /* ES6 Module */
```

Furthermore, v3.0 provides an `EditorCore` module as named export for those wanting to implement their own unique UI instead of using the default UI. This module will create the markdown editor, markdown preview, and WYSIWYG editor, and the user can use the `getEditorElements()` method to add the editor to desired UI. This module does not create external editor UIs like toolbars, toolbar popups, and switch tabs.

```js
import { EditorCore } from '@toast-ui/editor'; /* ES6 Module */

const editorCore = new EditorCore({
  el
  // ...
});

const { mdEditor, mdPreview, wwEditor } = editorCore.getEditorElements();

// ...
```

#### Bundle Structure

Aside from the original v2.x bundle content, two new items were added to v3.0. 

In addition to the original legacy support bundle and the cdn bundle, ESM bundle is included. ESM bundle is lightweight due to the fact that there is no complex module compatibility statement, and it also provides the bundle with the added benefit of tree shaking via static analysis.

Secondly, the `theme/toastui-editor-dark.css` is added for the dark theme support. The dark theme will be covered more in depth in [Added Dark Theme](#-added-dark-theme). 

The bundle structure for v3.0 is as follows.

```
- dist/
   ├─ cdn/...
   ├─ i18n/...
   ├─ esm/
   │    ├─ index.js
   │    └─ index.js.map
   ├─ theme/
   │    └─ toastui-editor-dark.css
   │
   ├─ toastui-editor-only.css
   ├─ toastui-editor-viewer.css
   ├─ toastui-editor.css
   ├─ toastui-editor.js
   └─ toastui-editor-viewer.js
```

Furthermore, the ESM bundle is included in the v3.0, and the package.json file has been updated accordingly.
The original UMD bundle file is defined in the main field, and the ESM bundle file is defined in the exports field.

```json
{
  "main": "dist/toastui-editor.js",
  "module": "dist/esm/",
  "exports": {
    ".": {
      "import": "./dist/esm/index.js",
      "require": "./dist/toastui-editor.js"
    },
    "./viewer": {
      "import": "./dist/esm/indexViewer.js",
      "require": "./dist/toastui-editor-viewer.js"
    }
  }
}
```

#### Added Dark Theme
v3.0 ships with dark theme included. To apply the dark theme, add the `theme/toastui-editor-dark.css` and set the editor's `theme` option to be `dark`. Currently, in v3.0, only the dark theme is supported, but the `theme` option was added to support more diverse combinations of themes in the future. 

```js
import Editor from '@toast-ui/editor';
import '@toast-ui/editor/dist/toastui-editor.css';
import '@toast-ui/editor/dist/theme/toastui-editor-dark.css';

const editor = new Editor({
  el: document.querySelector('#editor'),
  previewStyle: 'vertical',
  height: '500px',
  initialValue: content,
  theme: 'dark',
});
```

![image](https://user-images.githubusercontent.com/37766175/120954138-73ab8680-c789-11eb-8445-87bf15842482.png)

#### Changes in Dependencies

Editor 3.0 no longer requires some of the dependent modules that were needed for v2.x. If you are using the CDN for development, the [CodeMirror](https://codemirror.net/) dependencies required for v2.x are no longer necessary and should be removed. The v3.0 requires Prosemirror and its related modules, but the change is reflected in the CDN, so there is nothing for the user to add. 

**v2.0**

```html
<head>
  ...
  <link
    rel="stylesheet"
    href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.48.4/codemirror.min.css"
  />
  <link rel="stylesheet" href="https://uicdn.toast.com/editor/latest/toastui-editor.css" />
  ...
</head>
<body>
  ...
  <script src="https://uicdn.toast.com/editor/latest/toastui-editor-all.min.js"></script>
  ...
</body>
```

**v3.0**

```html
<head>
  ...
  <link rel="stylesheet" href="https://uicdn.toast.com/editor/latest/toastui-editor.css" />
  ...
</head>
<body>
  ...
  <script src="https://uicdn.toast.com/editor/latest/toastui-editor-all.min.js"></script>
  ...
</body>
```

### 2. Customizing the Toolbar

The `toolbarItems` option has been reworked to be more concise and declarative compared to the v2.x. In v3.0, each toolbar item and toolbar groups are defined as options in **2D array** format. This method removes the need to define the `divider` for differentiating different groups, making the final code much more intuitive. 

**v2.0**

```js
const editor = new Editor({
  el: document.querySelector('#editor'),
  toolbarItems: [
    'heading',
    'bold',
    'italic',
    'strike',
    // The divider element had to be added to differentiate different groups. 
    'divider',
    'hr',
    'quote',
    'divider',
    // ...
  ],
  // ...
});
```

**v3.0**

```js
const editor = new Editor({
  el: document.querySelector('#editor'),
  toolbarItems: [
    ['heading', 'bold', 'italic', 'strike'],
    ['hr', 'quote'],
    // ...
  ],
  // ...
});
```

By looking at the example code above, it is clear that the v3.0 code is more concise and that the group separation is made easier.

#### Customization

The method of customization has changed. In v2.x, when displaying or hiding a popup on toolbar item click, the coupling between the editor's `eventManager` and other UI instances were intricate. This forced the users to be familiar with the editor's internal implementations when customizing from the user's or from the plugin's perspective. It made customization difficult and created unnecessary control codes. In v3.0, the UI control codes have been capsulated internally in order to decrease the level of coupling, and users can now customize the toolbar items just by configuring the options. 

**v2.0**

```js
const popup = editor.getUI().createPopup({
  header: false,
  title: null,
  content: colorPickerContainer,
  className: 'tui-popup-color',
  target: editor.getUI().getToolbar().el,
  css: {
    width: 'auto',
    position: 'absolute'
  }
});

editor.eventManager.listen('focus', () => {
  popup.hide();

  // ...
});

editor.eventManager.listen('colorButtonClicked', () => {
  // ...
});

editor.eventManager.listen('closeAllPopup', () => {
  // ...
});
```

The code above is an example of customizing the toolbar's color picker in v2.x. In order to customize how the toolbar popup functions, users had to use API that were dependent on the editor's internal implementations like `editor.getUI().createPopup()` and `editor.getUI().getToolbar()`. Such internal dependencies make flexible customization difficult. It is not just the API. In order to manipulate the popup, users had to register multiple events to the `eventManager`. 


**v3.0**

```js
const popup = {
  name: 'color',
  tooltip: 'Text color',
  className: 'toastui-editor-toolbar-icons color',
  popup: {
    className: 'toastui-editor-popup-color',
    body: colorPickerContainer,
    style: { width: 'auto' },
  },
};
```

Few bits of codes have been intentionally left out, but in v3.0, users can create and control the popup UI through a simple option object. Users no longer need to be familiar with the internal UI modules and just have to define the `popup` option object's `className`, `style`, and `body` properties to trigger a popup on click of a toolbar button. For more information regarding customizing the toolbar, refer to [this link](https://github.com/nhn/tui.editor/tree/master/docs/en/toolbar.md).

![image](https://user-images.githubusercontent.com/37766175/120915630-b6b11f80-c6df-11eb-8094-b264ca9312a1.gif)

### 3. Defining Plugins

The biggest change of the v3.0 is in defining plugins. In v2.x, plugins were also incredibly dependent on the editor's internal modules as seen in the previous toolbar section. For plugins, the users had to be especially familiar with the markdown editor, WYSIWYG editor, converter, and editor's other internal instances and how they function. In v3.0, in order to address this issue, defining plugins have been reworked to include clearly defined options for customizing each feature. This guide will briefly discuss the options, and for in depth guide on defining plugins can be found [here](https://github.com/nhn/tui.editor/tree/master/docs/en/plugin.md). 

#### Registering Commands

Users can register markdown and WYSIWYG commands through `markdownCommands` and `wysiwygCommands` options for plugins.

```js
return {
  markdownCommands: {
    myCommand: (payload, state, dispatch) => {
      // ...
    },
  },
  wysiwygCommands: {
    myCommand: (payload, state, dispatch) => {
      // ...
    },
  },
};
```

Each command takes `payload`, `state`, and `dispatch` as inputs, and these three parameters can be used to control the internal functionalities of Prosemirror based editor. This method also requires that users be familiar with Prosemirror. However, the Editor will continue to provide our own basic commands, which will prevent users having to work with the internal implementations directly. 

#### Converting

Users can now change the result of a render that happens when a certain element is converted from markdown to preview or from markdown editor to WYSIWYG editor. The same is true in reverse. Users can now redefine the text of the element that is converted from WYSIWYG editor to markdown editor. The `toHTMLRenderers` and `toMarkdownRenderers` options can be used to define what happens during the conversion from markdown to WYSIWYG and from WYSIWYG to markdown.

```ts
return {
  toHTMLRenderers: {
    // ...
    tableCell(node: MergedTableCellMdNode, { entering, origin }) {
      const result = origin!();

      // ...
      
      return result;
    },
  },
  toMarkdownRenderers: {
    // ...
    tableHead(nodeInfo) {
      const row = (nodeInfo.node as ProsemirrorNode).firstChild;

      let delim = '';

      if (row) {
        row.forEach(({ textContent, attrs }) => {
          const headDelim = createTableHeadDelim(textContent, attrs.align);

          delim += `| ${headDelim} `;

          // ...
        });
      }
      return { delim };
    },
  },
};
```

The above code is an example of merged table plugin. The `tableCell`, defined in `toHTMLRenderers`, node's return value is used for the markdown preview and WYSIWYG editor conversion, and the `tableHead`, defined in `toMarkdownRenderers` node's text value is used for markdown editor conversion. Any process during each editor's conversion can be defined per node through options.

#### Registering Toolbar Items

The method of registering toolbar items in plugins have changed. The options are similar to the previously explained toolbar customization options. The user just needs to configure the index of the to be added group. 

```ts
return {
  // ...
  toolbarItems: [
    {
      groupIndex: 0,
      itemIndex: 3,
      item: toolbarItem,
    },
  ],
};
```

As the code above shows, users can configure which item to add to the `toolbarItems` array. Each option object has `groupIndex`, `itemIndex`, and `item` properties and serves the following purpose. 

* `groupIndex`: Defines the index of the group that the item will be added to. 
* `itemIndex`: Defines the index of the item to be placed in the determined group. 
* `item`: Defines the toolbar item to be added. 

Following the example code, the `toolbarItems` option will make it so that the toolbar item will be added to the first toolbar group's fourth index.

Additionally, there are options that this document does not cover including registering markdown and WYSIWYG editor's Prosemirror plugin, using the `eventEmitter` for communication between the editor and the plugin. For more information, it is recommended that users consult the [Guide to Using Plugins](https://github.com/nhn/tui.editor/tree/master/docs/en/plugin.md).

### 4. APIs and Events

The following are the API signatures and event names that have changed as of v3.0. 

#### Commands
The options of the commands to be registered are now passed in as individual inputs instead of an object consisting of the name and the handler. Furthermore, the input format of the method that executes the command has changed as well. 

**v2.x**

| Method Signature             | Returned Type         |
| ----------------- | ------------ |
| `addCommand(type: string, props: { name: string; exec: Command }` | `void` |
| `exec(name: string, ...args: any[]`) | `void` |

**v3.0**

| Method Signature              | Returned Type     |
| ----------------- | ------------ |
| `addCommand(type: string, name: string, command: CommandFn)` | `void` |
| `exec(name: string, payload?: Object)` | `void` |

#### Text Manipulation API
Originally, the `getTextObject()` API was used to insert or change text from the editor. However, in order to use this, users had to be familiar with the structure of the instance returned by the `getTextObject()` API. In v3.0, the `getTextObject()` API has been replaced with individual APIs that gets, replaces, and deletes text. 

**v2.x**

`TextObject`'s Interface

```ts
interface TextObject {
  setRange(range): void;
  setEndBeforeRange(range): void;
  expandStartOffset(): void;
  expandEndOffset(): void;
  getTextContent(): string;
  replaceContent(content) : void;
  deleteContent(): void;
  peekStartBeforeOffset(offset): Range;
}
```

**v3.0**

| Method Signature              | Returned Type         | Notes        |
| ----------------- | ------------ | ------------ |
| `replaceSelection(text: string, start?: EditorPos, end?: EditorPos) ` | `void` | Replaces the text at the given range. If the range is not provided, the text present in current editor's selected range is replaced. |
| `deleteSelection(start?: EditorPos, end?: EditorPos)` | `void` | Deletes the text at the given range. If the range is not provided, the text present in current editor's selected range is replaced. |
| `getSelectedText(start?: EditorPos, end?: EditorPos)` | `string` | Gets the text at the given range. If the range is not provided, the text present in current editor's selected range is retrieved. |

The above APIs' positional information (`EditorPos`) differs from markdown editor to WYSIWYG editor, and has the following format. This is because markdown and WYSIWYG have different ways to calculate position. Markdown calculates position based on the line, and the WYSIWYG calculates the offset from the start of the document.

```ts
// Markdown's Position Information
type EditorPos = [line: number, charactorOffset: number];
// WYSIWYG's Position Information
type EditorPos = number; // 오프셋
// Offset
```

#### Changed Instance Constructing Options and Methods

There were changes in options and methods that were not named properly or that did not clearly indicate its feature. 

* Instance Constructing Option

| v2 | v3 |
| --- | --- |
| `linkAttribute` | `linkAttributes` |

* Instance Methods

| v2 | v3 |
| --- | --- |
| `setHtml` | `setHTML` |
| `getHtml` | `getHTML` |
| `minHeight` | `setMinHeight`, `getMinHeight` |
| `height` | `setHeight`, `getHeight` |
| `getRange` | `getSelection` |
| `remove` | `destroy` |

#### Changed Event Names

Some events were renamed to represent their meaning more clearly. 

| v2 | v3 |
| --- | --- |
| `stateChange` | `caretChange` |
| `convertorAfterMarkdownToHtmlConverted` | `beforePreviewRender` |
| `convertorAfterHtmlToMarkdownConverted` | `beforeConvertWysiwygToMarkdown` |

### 5. Supported Browsers

From v3.0, only the browsers **above Internet Explorer (IE) 11** will be supported. The previous version supported IE 10 and above, but the support range has been changed due to the low browser share and Prosemirror core module support. 

## Removed Features

### 1. Removed jQuery Wrapper

From v3.0, the jQuery Wrapper has been removed. To use jQuery, the user must wrap the `@toast-ui/editor` package separately.

### 2. Removed Dependencies

Because original CodeMirror, squire, and to-mark dependencies were all removed, any code that accesses these modules directly or indirectly will no longer work. Most of the required features were added to the editor instance's API, and it is recommended that users use the according APIs.

**v2.x**

```js
const editor = new Editor(/* */);

console.log(editor.getCodeMirror()); // CodeMirror Instance
console.log(editor.getSquire()); // squire Instance
```

**v3.0**

```js
const editor = new Editor(/* */);

console.log(editor.getCodeMirror()); // Uncaught TypeError
console.log(editor.getSquire()); // Uncaught TypeError
```

### 3. Removed APIs

Lastly, the following is a list of APIs removed from the v3.0.

#### Static Properties

| Name                  | Type               |
| --------------------- | ------------------ |
| `isViewer`            | `{boolean}` |
| `codeBlockManager`    | `{CodeBlockManager}` |
| `WwCodeBlockManager`  | `{Class.<WwCodeBlockManager>}` |
| `WwTableManager`      | `{Class.<WwTableManager>}` |
| `WwTableSelectionManager` | `{Class.<WwTableSelectionManager>}` |
| `CommandManager` | `{Class.<CommandManager>}` |

#### Static Methods

| Name              | Type         |
| ----------------- | ------------ |
| `getInstances` | `{function}` |


#### Instance Constructing Option

| Name              | Type                       |
| -------------------- | -------------------------- |
| `useDefaultHTMLSanitizer` | `boolean`         |  


#### Instance Method

| Name     | Type         |
| ---------- | ------------ |
| `setCodeBlockLanguages`  | `{function}` |
| `afterAddedCommand` | `{function}` |
| `getCodeMirror` | `{function}` |
| `getSquire` | `{function}` |
| `getCurrentModeEditor` | `{function}` |
| `getUI` | `{function}` |
