gt:         BorderPane tableEditor = loader.load();
context: package com.zestedesavoir.zestwriter.view;
  
  import com.zestedesavoir.zestwriter.MainApp;
  import com.zestedesavoir.zestwriter.model.Textual;
  import com.zestedesavoir.zestwriter.utils.Configuration;
  import com.zestedesavoir.zestwriter.utils.Corrector;
  import com.zestedesavoir.zestwriter.utils.FlipTable;
  import com.zestedesavoir.zestwriter.utils.readability.Readability;
  import com.zestedesavoir.zestwriter.view.com.*;
  import com.zestedesavoir.zestwriter.view.dialogs.FindReplaceDialog;
  import com.zestedesavoir.zestwriter.view.dialogs.ImageInputDialog;
  import javafx.application.Platform;
  import javafx.beans.property.BooleanPropertyBase;
  import javafx.beans.property.SimpleStringProperty;
  import javafx.beans.property.StringProperty;
  import javafx.beans.value.ObservableValue;
  import javafx.collections.ObservableList;
  import javafx.concurrent.Service;
  import javafx.concurrent.Task;
  import javafx.concurrent.Worker.State;
  import javafx.event.ActionEvent;
  import javafx.fxml.FXML;
  import javafx.fxml.FXMLLoader;
  import javafx.geometry.Insets;
  import javafx.scene.control.*;
  import javafx.scene.input.KeyCode;
  import javafx.scene.layout.BorderPane;
  import javafx.scene.layout.ColumnConstraints;
  import javafx.scene.layout.GridPane;
  import javafx.scene.web.WebEngine;
  import javafx.scene.web.WebView;
  import javafx.stage.Stage;
  import javafx.util.Pair;
  import netscape.javascript.JSException;
  import org.apache.commons.lang.StringEscapeUtils;
  import org.fxmisc.richtext.LineNumberFactory;
  import org.fxmisc.richtext.StyleClassedTextArea;
  import org.fxmisc.wellbehaved.event.EventHandlerHelper;
  import org.python.core.PyString;
  import org.python.util.PythonInterpreter;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.w3c.dom.DOMException;
  
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Optional;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  import static javafx.scene.input.KeyCombination.SHIFT_DOWN;
  import static javafx.scene.input.KeyCombination.SHORTCUT_DOWN;
  import static org.fxmisc.wellbehaved.event.EventPattern.keyPressed;
  import static org.fxmisc.wellbehaved.event.EventPattern.keyReleased;
  
  public class MdConvertController {
      private MainApp mainApp;
      private MdTextController mdBox;
      private Tab tab;
      private Textual extract;
      private Corrector corrector;
      private Service<String> renderTask;
      private final Logger logger;
      private int xRenderPosition = 0;
      private int yRenderPosition = 0;
      private StringProperty countChars = new SimpleStringProperty();
      private StringProperty countWords = new SimpleStringProperty();
      private BooleanPropertyBase isSaved = new BooleanPropertyBase(true) {
  
          @Override
          public String getName() {
              return null;
          }
  
          @Override
          public Object getBean() {
              return null;
          }
      };
  
      @FXML private WebView renderView;
      @FXML private Button SaveButton;
      @FXML private BorderPane BoxEditor;
      @FXML private BorderPane BoxRender;
      private CustomStyledClassedTextArea SourceText;
      public final static Pattern recognizeNumber = Pattern.compile("^(\\s*)([\\d][\\.]) (\\s*)(.*)");
      public final static Pattern recognizeBullet = Pattern.compile("^(\\s*)([*|-]) (\\s*)(.*)");
  
      public MdConvertController() {
          super();
          logger = LoggerFactory.getLogger(MdConvertController.class);
          SourceText = new CustomStyledClassedTextArea();
      }
  
      public MdTextController getMdBox() {
          return mdBox;
      }
  
      public StyleClassedTextArea getSourceText(){
          return SourceText;
      }
  
      public void setMdBox(MdTextController mdBox, Textual extract, Tab tab) throws IOException {
          this.mainApp = mdBox.getMainApp();
          this.mdBox = mdBox;
          this.tab = tab;
          this.extract = extract;
  
          mainApp.getPluginsManager().setPluginEditor(this);
  
          FXMLLoader loader = new CustomFXMLLoader(MainApp.class.getResource("fxml/Editor.fxml"));
          loader.load();
  
          if(MainApp.getConfig().getEditorToolbarView().equals("no")){
              BoxEditor.setTop(null);
              BoxRender.setTop(null);
          }
  
          BoxEditor.setCenter(SourceText);
          SourceText.setStyle("-fx-font-family: \"" + MainApp.getConfig().getEditorFont() + "\";-fx-font-size: " + MainApp.getConfig().getEditorFontsize() + ";");
          initRenderTask();
          Platform.runLater(() -> {
              SourceText.replaceText(extract.getMarkdown());
              initStats();
              SourceText.getUndoManager().forgetHistory();
              SourceText.textProperty().addListener((observableValue, s, s2) -> {
                  tab.setText("! " + extract.getTitle());
                  this.isSaved.setValue(false);
                  SourceText.getUndoManager().mark();
                  updateRender();
              });
              updateRender();
          });
  
          EventHandlerHelper.install(SourceText.onKeyPressedProperty(),
                  EventHandlerHelper.on(keyPressed(KeyCode.S, SHORTCUT_DOWN)).act( ev -> HandleSaveButtonAction(null)).create());
          EventHandlerHelper.install(SourceText.onKeyPressedProperty(),
                  EventHandlerHelper.on(keyPressed(KeyCode.G, SHORTCUT_DOWN)).act( ev -> HandleBoldButtonAction(null)).create());
          EventHandlerHelper.install(SourceText.onKeyPressedProperty(),
                  EventHandlerHelper.on(keyPressed(KeyCode.I, SHORTCUT_DOWN)).act( ev -> HandleItalicButtonAction(null)).create());
          EventHandlerHelper.install(SourceText.onKeyPressedProperty(),
                  EventHandlerHelper.on(keyPressed(KeyCode.B, SHORTCUT_DOWN)).act( ev -> HandleBarredButtonAction(null)).create());
          EventHandlerHelper.install(SourceText.onKeyPressedProperty(),
                  EventHandlerHelper.on(keyPressed(KeyCode.K, SHORTCUT_DOWN)).act( ev -> HandleTouchButtonAction(null)).create());
          EventHandlerHelper.install(SourceText.onKeyPressedProperty(),
                  EventHandlerHelper.on(keyPressed(KeyCode.PLUS, SHORTCUT_DOWN)).act( ev -> HandleExpButtonAction(null)).create());
          EventHandlerHelper.install(SourceText.onKeyPressedProperty(),
                  EventHandlerHelper.on(keyPressed(KeyCode.EQUALS, SHORTCUT_DOWN)).act( ev -> HandleIndButtonAction(null)).create());
          EventHandlerHelper.install(SourceText.onKeyPressedProperty(),
                  EventHandlerHelper.on(keyPressed(KeyCode.E, SHORTCUT_DOWN)).act( ev -> HandleCenterButtonAction(null)).create());
          EventHandlerHelper.install(SourceText.onKeyPressedProperty(),
                  EventHandlerHelper.on(keyPressed(KeyCode.D, SHIFT_DOWN, SHORTCUT_DOWN)).act( ev -> HandleRightButtonAction(null)).create());
          EventHandlerHelper.install(SourceText.onKeyPressedProperty(),
                  EventHandlerHelper.on(keyPressed(KeyCode.SPACE, SHORTCUT_DOWN)).act( ev -> HandleUnbreakableAction(null)).create());
          EventHandlerHelper.install(SourceText.onKeyPressedProperty(),
                  EventHandlerHelper.on(keyPressed(KeyCode.L, SHORTCUT_DOWN)).act( ev -> HandleGoToLineAction()).create());
          EventHandlerHelper.install(SourceText.onKeyPressedProperty(),
                  EventHandlerHelper.on(keyPressed(KeyCode.F, SHORTCUT_DOWN)).act( ev -> HandleFindReplaceDialog()).create());
          if(FunctionTreeFactory.isMacOs()) {
              EventHandlerHelper.install(SourceText.onKeyPressedProperty(),
                      EventHandlerHelper.on(keyPressed(KeyCode.Q, SHORTCUT_DOWN)).act( ev -> SourceText.selectAll()).create());
          }
          if(MainApp.getConfig().getEditorSmart().booleanValue()) {
              EventHandlerHelper.install(SourceText.onKeyReleasedProperty(),
                      EventHandlerHelper.on(keyReleased(KeyCode.TAB)).act(ev -> HandleSmartTab()).create());
              EventHandlerHelper.install(SourceText.onKeyReleasedProperty(),
                      EventHandlerHelper.on(keyReleased(KeyCode.ENTER)).act(ev -> HandleSmartEnter()).create());
          }
  
          tab.setOnSelectionChanged(t -> {
              if(tab.isSelected()) {
                  Platform.runLater(() -> {
                      SourceText.requestFocus();
                      initStats();
                  });
              }
          });
  
          Platform.runLater(() -> {
              SourceText.requestFocus();
          });
      }
  
  
      private void HandleSmartEnter() {
          int precLine = SourceText.getCurrentParagraph() - 1;
          if(precLine >= 0) {
              String line = SourceText.getParagraph(precLine).toString();
              Matcher matcher = recognizeBullet.matcher(line);
              //TODO : find how combine recognize bullet and number together for breaking following if
              if(!matcher.matches()) {
                  matcher = recognizeNumber.matcher(line);
              }
              if(matcher.matches()) {
                  if(matcher.group(4).trim().equals("")) {
                      int positionCaret = SourceText.getCaretPosition();
                      SourceText.deleteText(positionCaret-line.length() - 1, positionCaret);
                  } else {
                      SourceText.replaceSelection(matcher.group(1)+matcher.group(2)+" ");
                  }
              }
          }
      }
  
      private void HandleSmartTab() {
          int caseLine = SourceText.getCurrentParagraph();
          if(caseLine >= 0) {
              String line = SourceText.getParagraph(caseLine).toString();
              Matcher matcher = recognizeBullet.matcher(line);
              //TODO : find how combine recognize bullet and number together for breaking following if
              if(!matcher.matches()) {
                  matcher = recognizeNumber.matcher(line);
              }
              if(matcher.matches()) {
                  int positionCaret = SourceText.getCaretPosition();
                  int delta = matcher.group(1).length() + matcher.group(2).length() + matcher.group(3).length() + 1;
                  SourceText.replaceText(positionCaret-delta, positionCaret, "    "+matcher.group(2)+" "+matcher.group(4));
              }
          }
      }
  
  
      @FXML private void initialize() {
          SourceText.getStyleClass().add("markdown-editor");
          SourceText.getStylesheets().add(MainApp.class.getResource("css/editor.css").toExternalForm());
          SourceText.setParagraphGraphicFactory(LineNumberFactory.get(SourceText));
          SaveButton.disableProperty().bind(isSaved);
      }
  
      /*
       * Editor Toolbar Action
       */
  
      @FXML public void HandleSaveButtonAction(ActionEvent event) {
          extract.setMarkdown(SourceText.getText());
          extract.save();
          tab.setText(extract.getTitle());
          this.isSaved.setValue(true);
  
          SourceText.requestFocus();
      }
  
      @FXML private void HandleBoldButtonAction(ActionEvent event) {
          replaceAction("****", 2, "**", "**");
      }
  
      @FXML private void HandleItalicButtonAction(ActionEvent event) {
          replaceAction("**", 1, "*", "*");
      }
  
      @FXML private void HandleBarredButtonAction(ActionEvent event) {
          replaceAction("~~~~", 2, "~~", "~~");
      }
  
      @FXML private void HandleTouchButtonAction(ActionEvent event) {
          replaceAction("||||", 2, "||", "||");
      }
  
      @FXML private void HandleExpButtonAction(ActionEvent event) {
          replaceAction("^^", 1, "^", "^");
      }
  
      @FXML private void HandleIndButtonAction(ActionEvent event) {
          replaceAction("~~", 1, "~", "~");
      }
  
      @FXML private void HandleCenterButtonAction(ActionEvent event) {
          replaceAction("\n->  <-", 3, "\n-> ", " <-");
      }
  
      @FXML private void HandleRightButtonAction(ActionEvent event) {
          replaceAction("\n->  ->", 3, "\n-> ", " ->\n");
      }
  
      @FXML private void HandleImgButtonAction(ActionEvent event) {
          FXMLLoader loader = new CustomFXMLLoader(MainApp.class.getResource("fxml/ImageInput.fxml"));
  
          Stage dialogStage = new CustomStage(loader, Configuration.bundle.getString("ui.dialog.upload.img.title"));
  
          ImageInputDialog imageController = loader.getController();
          if(mainApp.getContents().size() > 0) {
              imageController.setSourceText(SourceText, MainApp.getZdsutils(), mainApp.getMenuController(), mainApp.getContents().get(0));
          } else {
              imageController.setSourceText(SourceText, MainApp.getZdsutils(), mainApp.getMenuController(), null);
          }
          imageController.setStage(dialogStage);
  
          dialogStage.show();
      }
      @FXML private void HandleBulletButtonAction(ActionEvent event) {
          if(SourceText.getSelectedText().isEmpty()){
              SourceText.replaceText(SourceText.getSelection(), "- ");
          }else{
              StringBuilder sb = new StringBuilder();
              String[] lines = SourceText.getSelectedText().split("\n");
              for(String line : lines){
                  sb.append("- ").append(line).append("\n");
              }
  
              SourceText.replaceText(SourceText.getSelection(), sb.toString());
          }
  
          SourceText.requestFocus();
      }
  
      @FXML private void HandleNumberedButtonAction(ActionEvent event) {
          if(SourceText.getSelectedText().isEmpty()){
              SourceText.replaceText(SourceText.getSelection(), "1. ");
          }else{
              StringBuilder sb = new StringBuilder();
              String[] lines = SourceText.getSelectedText().split("\n");
              int i = 1;
              for(String line : lines){
                  sb.append(i).append(". ").append(line).append("\n");
                  i++;
              }
  
              SourceText.replaceText(SourceText.getSelection(), sb.toString());
          }
  
          SourceText.requestFocus();
      }
  
      @FXML private void HandleHeaderButtonAction(ActionEvent event) {
          SourceText.replaceText(SourceText.getSelection(), "# " + SourceText.getSelectedText());
          SourceText.requestFocus();
      }
  
      @FXML private void HandleQuoteButtonAction(ActionEvent event) {
          replaceAction("> ", 0, "> ", "\n\n");
      }
  
      @FXML private void HandleBlocButtonAction(ActionEvent event) {
          String text = "";
          String[] lines = SourceText.getSelectedText().split("\n");
          for (String line : lines) {
              text += "| " + line + "\n";
          }
  
          List<String> choices = new ArrayList<>();
          choices.add("information");
          choices.add("question");
          choices.add("attention");
          choices.add("erreur");
  
          ChoiceDialog<String> dialog = new ChoiceDialog<>("information", choices);
          dialog.setTitle(Configuration.bundle.getString("ui.editor.dialog.bloc.title"));
          dialog.setHeaderText(Configuration.bundle.getString("ui.editor.dialog.bloc.header"));
          dialog.setContentText(Configuration.bundle.getString("ui.editor.dialog.bloc.text"));
  
          // Traditional way to get the response value.
          Optional<String> result = dialog.showAndWait();
          if (result.isPresent()) {
              SourceText.replaceText(SourceText.getSelection(), "\n[[" + result.get() + "]]\n" + text);
          }
  
          SourceText.requestFocus();
      }
  
      @FXML private void HandleTableButtonAction(ActionEvent event) throws IOException {
          // Create the custom dialog.
          Dialog<Pair<ObservableList, ObservableList<ZRow>>> dialog = new Dialog<>();
          dialog.setTitle(Configuration.bundle.getString("ui.editor.button.table"));
          dialog.setHeaderText("");
  
          // Set the button types.
          dialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);
  
          FXMLLoader loader = new CustomFXMLLoader(MainApp.class.getResource("fxml/TableEditor.fxml"));
  