From fe4a886ec4a8e4f6c06096ef8a2f2d0e1e47a818 Mon Sep 17 00:00:00 2001
From: Takashi Kato <tohosaku@users.osdn.me>
Date: Tue, 7 May 2024 22:29:58 +0000
Subject: [PATCH] Convert jstoolbar to ES Module

---
 .../javascripts/jstoolbar/common_mark.js      | 244 --------
 app/assets/javascripts/jstoolbar/jstoolbar.js | 568 ------------------
 .../jstoolbar/lang/jstoolbar-ar.js            |  20 -
 .../jstoolbar/lang/jstoolbar-az.js            |  20 -
 .../jstoolbar/lang/jstoolbar-bg.js            |  20 -
 .../jstoolbar/lang/jstoolbar-bs.js            |  18 -
 .../jstoolbar/lang/jstoolbar-ca.js            |  20 -
 .../jstoolbar/lang/jstoolbar-cs.js            |  20 -
 .../jstoolbar/lang/jstoolbar-da.js            |  20 -
 .../jstoolbar/lang/jstoolbar-de.js            |  20 -
 .../jstoolbar/lang/jstoolbar-en-gb.js         |  20 -
 .../jstoolbar/lang/jstoolbar-en.js            |  20 -
 .../jstoolbar/lang/jstoolbar-es-pa.js         |  20 -
 .../jstoolbar/lang/jstoolbar-es.js            |  20 -
 .../jstoolbar/lang/jstoolbar-et.js            |  37 --
 .../jstoolbar/lang/jstoolbar-eu.js            |  24 -
 .../jstoolbar/lang/jstoolbar-fa.js            |  20 -
 .../jstoolbar/lang/jstoolbar-fi.js            |  20 -
 .../jstoolbar/lang/jstoolbar-fr.js            |  20 -
 .../jstoolbar/lang/jstoolbar-gl.js            |  20 -
 .../jstoolbar/lang/jstoolbar-he.js            |  20 -
 .../jstoolbar/lang/jstoolbar-hr.js            |  20 -
 .../jstoolbar/lang/jstoolbar-hu.js            |  20 -
 .../jstoolbar/lang/jstoolbar-id.js            |  20 -
 .../jstoolbar/lang/jstoolbar-it.js            |  23 -
 .../jstoolbar/lang/jstoolbar-ja.js            |  20 -
 .../jstoolbar/lang/jstoolbar-ko.js            |  20 -
 .../jstoolbar/lang/jstoolbar-lt.js            |  20 -
 .../jstoolbar/lang/jstoolbar-lv.js            |  21 -
 .../jstoolbar/lang/jstoolbar-mk.js            |  20 -
 .../jstoolbar/lang/jstoolbar-mn.js            |  20 -
 .../jstoolbar/lang/jstoolbar-nl.js            |  20 -
 .../jstoolbar/lang/jstoolbar-no.js            |  20 -
 .../jstoolbar/lang/jstoolbar-pl.js            |  21 -
 .../jstoolbar/lang/jstoolbar-pt.js            |  21 -
 .../jstoolbar/lang/jstoolbar-ro.js            |  20 -
 .../jstoolbar/lang/jstoolbar-ru.js            |  20 -
 .../jstoolbar/lang/jstoolbar-sk.js            |  20 -
 .../jstoolbar/lang/jstoolbar-sl.js            |  20 -
 .../jstoolbar/lang/jstoolbar-sq.js            |  20 -
 .../jstoolbar/lang/jstoolbar-sr-yu.js         |  20 -
 .../jstoolbar/lang/jstoolbar-sr.js            |  20 -
 .../jstoolbar/lang/jstoolbar-sv.js            |  20 -
 .../jstoolbar/lang/jstoolbar-ta-in.js         |  20 -
 .../jstoolbar/lang/jstoolbar-th.js            |  20 -
 .../jstoolbar/lang/jstoolbar-tr.js            |  18 -
 .../jstoolbar/lang/jstoolbar-uk.js            |  20 -
 .../jstoolbar/lang/jstoolbar-vi.js            |  20 -
 .../jstoolbar/lang/jstoolbar-zh-tw.js         |  20 -
 .../jstoolbar/lang/jstoolbar-zh.js            |  20 -
 app/assets/javascripts/jstoolbar/textile.js   | 229 -------
 app/assets/stylesheets/jstoolbar.css          |  38 ++
 app/helpers/application_helper.rb             |  55 +-
 app/helpers/custom_fields_helper.rb           |  14 +-
 .../controllers/list_autofill_controller.js   | 124 ----
 .../controllers/precode_controller.js         |  52 ++
 .../controllers/table_generator_controller.js |  41 ++
 .../controllers/template_controller.js        |  18 +
 .../controllers/texteditor_controller.js      |  84 +++
 app/javascript/helper.js                      |  17 +
 app/javascript/jstoolbar.js                   | 550 +++++++++++++++++
 .../jstoolbar/formatting/common_mark.js       | 276 +++++++++
 .../jstoolbar/formatting/textile.js           | 236 ++++++++
 app/views/common/_code_highlighting.html.erb  |  12 +
 app/views/common/_table_generator.html.erb    |  14 +
 app/views/common/_texteditor.html.erb         |  17 +
 app/views/documents/_form.html.erb            |   8 +-
 app/views/issues/_edit.html.erb               |  13 +-
 app/views/issues/_form.html.erb               |  19 +-
 app/views/issues/_form_custom_fields.html.erb |   8 +-
 app/views/issues/bulk_edit.html.erb           |  10 +-
 app/views/journals/_notes_form.html.erb       |   7 +-
 app/views/messages/_form.html.erb             |   7 +-
 app/views/news/_form.html.erb                 |   8 +-
 app/views/news/show.html.erb                  |   9 +-
 app/views/projects/_form.html.erb             |   4 +-
 app/views/search/index.html.erb               |   2 +-
 app/views/settings/_general.html.erb          |   4 +-
 app/views/settings/_notifications.html.erb    |   6 +-
 app/views/timelog/_form.html.erb              |   6 +-
 app/views/timelog/bulk_edit.html.erb          |   4 +-
 app/views/wiki/edit.html.erb                  |   7 +-
 config/importmap.rb                           |   4 +
 config/locales/ar.yml                         |  20 +
 config/locales/az.yml                         |  20 +
 config/locales/bg.yml                         |  20 +
 config/locales/bs.yml                         |  18 +
 config/locales/ca.yml                         |  20 +
 config/locales/cs.yml                         |  20 +
 config/locales/da.yml                         |  20 +
 config/locales/de.yml                         |  20 +
 config/locales/el.yml                         |  20 +
 config/locales/en-GB.yml                      |  20 +
 config/locales/en.yml                         |  21 +
 config/locales/es-PA.yml                      |  20 +
 config/locales/es.yml                         |  20 +
 config/locales/et.yml                         |  20 +
 config/locales/eu.yml                         |  24 +
 config/locales/fa.yml                         |  20 +
 config/locales/fi.yml                         |  20 +
 config/locales/fr.yml                         |  20 +
 config/locales/gl.yml                         |  20 +
 config/locales/he.yml                         |  20 +
 config/locales/hr.yml                         |  20 +
 config/locales/hu.yml                         |  20 +
 config/locales/id.yml                         |  20 +
 config/locales/it.yml                         |  22 +
 config/locales/ja.yml                         |  21 +
 config/locales/ko.yml                         |  20 +
 config/locales/lt.yml                         |  20 +
 config/locales/lv.yml                         |  21 +
 config/locales/mk.yml                         |  20 +
 config/locales/mn.yml                         |  20 +
 config/locales/nl.yml                         |  20 +
 config/locales/no.yml                         |  20 +
 config/locales/pl.yml                         |  21 +
 config/locales/pt-BR.yml                      |  21 +
 config/locales/pt.yml                         |  21 +
 config/locales/ro.yml                         |  20 +
 config/locales/ru.yml                         |  20 +
 config/locales/sk.yml                         |  20 +
 config/locales/sl.yml                         |  20 +
 config/locales/sq.yml                         |  20 +
 config/locales/sr-YU.yml                      |  20 +
 config/locales/sr.yml                         |  20 +
 config/locales/sv.yml                         |  20 +
 config/locales/ta-IN.yml                      |  20 +
 config/locales/th.yml                         |  20 +
 config/locales/tr.yml                         |  18 +
 config/locales/uk.yml                         |  20 +
 config/locales/vi.yml                         |  20 +
 config/locales/zh-TW.yml                      |  20 +
 config/locales/zh.yml                         |  20 +
 lib/redmine/views/labelled_form_builder.rb    |  12 +
 lib/redmine/wiki_formatting.rb                |   8 +-
 .../wiki_formatting/common_mark/helper.rb     |  34 --
 lib/redmine/wiki_formatting/textile/helper.rb |  32 -
 test/helpers/application_helper_test.rb       |  33 +-
 test/integration/layout_test.rb               |   7 +-
 139 files changed, 2510 insertions(+), 2322 deletions(-)
 delete mode 100644 app/assets/javascripts/jstoolbar/common_mark.js
 delete mode 100644 app/assets/javascripts/jstoolbar/jstoolbar.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-ar.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-az.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-bg.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-bs.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-ca.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-cs.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-da.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-de.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-en-gb.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-en.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-es-pa.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-es.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-et.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-eu.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-fa.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-fi.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-fr.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-gl.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-he.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-hr.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-hu.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-id.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-it.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-ja.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-ko.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-lt.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-lv.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-mk.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-mn.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-nl.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-no.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-pl.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-pt.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-ro.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-ru.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-sk.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-sl.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-sq.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-sr-yu.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-sr.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-sv.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-ta-in.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-th.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-tr.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-uk.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-vi.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-zh-tw.js
 delete mode 100644 app/assets/javascripts/jstoolbar/lang/jstoolbar-zh.js
 delete mode 100644 app/assets/javascripts/jstoolbar/textile.js
 delete mode 100644 app/javascript/controllers/list_autofill_controller.js
 create mode 100644 app/javascript/controllers/precode_controller.js
 create mode 100644 app/javascript/controllers/table_generator_controller.js
 create mode 100644 app/javascript/controllers/template_controller.js
 create mode 100644 app/javascript/controllers/texteditor_controller.js
 create mode 100644 app/javascript/helper.js
 create mode 100644 app/javascript/jstoolbar.js
 create mode 100644 app/javascript/jstoolbar/formatting/common_mark.js
 create mode 100644 app/javascript/jstoolbar/formatting/textile.js
 create mode 100644 app/views/common/_code_highlighting.html.erb
 create mode 100644 app/views/common/_table_generator.html.erb
 create mode 100644 app/views/common/_texteditor.html.erb

diff --git a/app/assets/javascripts/jstoolbar/common_mark.js b/app/assets/javascripts/jstoolbar/common_mark.js
deleted file mode 100644
index 38467be4b..000000000
--- a/app/assets/javascripts/jstoolbar/common_mark.js
+++ /dev/null
@@ -1,244 +0,0 @@
-/**
- * This file is part of DotClear.
- * Copyright (c) 2005 Nicolas Martin & Olivier Meunier and contributors. All rights reserved.
- * This code is released under the GNU General Public License.
- *
- * Modified by JP LANG for common_mark formatting
- */
-
-// strong
-jsToolBar.prototype.elements.strong = {
-  type: 'button',
-  title: 'Strong',
-  shortcut: 'b',
-  fn: {
-    wiki: function() { this.singleTag('**') }
-  }
-}
-
-// em
-jsToolBar.prototype.elements.em = {
-  type: 'button',
-  title: 'Italic',
-  shortcut: 'i',
-  fn: {
-    wiki: function() { this.singleTag("*") }
-  }
-}
-
-// u
-jsToolBar.prototype.elements.ins = {
-  type: 'button',
-  title: 'Underline',
-  shortcut: 'u',
-  fn: {
-    wiki: function() { this.singleTag('<u>', '</u>') }
-  }
-}
-
-// del
-jsToolBar.prototype.elements.del = {
-  type: 'button',
-  title: 'Deleted',
-  fn: {
-    wiki: function() { this.singleTag('~~') }
-  }
-}
-
-// code
-jsToolBar.prototype.elements.code = {
-  type: 'button',
-  title: 'Code',
-  fn: {
-    wiki: function() { this.singleTag('`') }
-  }
-}
-
-// spacer
-jsToolBar.prototype.elements.space1 = {type: 'space'}
-
-// headings
-jsToolBar.prototype.elements.h1 = {
-  type: 'button',
-  title: 'Heading 1',
-  fn: {
-    wiki: function() {
-      this.encloseLineSelection('# ', '',function(str) {
-        str = str.replace(/^#+\s+/, '')
-        return str;
-      });
-    }
-  }
-}
-jsToolBar.prototype.elements.h2 = {
-  type: 'button',
-  title: 'Heading 2',
-  fn: {
-    wiki: function() {
-      this.encloseLineSelection('## ', '',function(str) {
-        str = str.replace(/^#+\s+/, '')
-        return str;
-      });
-    }
-  }
-}
-jsToolBar.prototype.elements.h3 = {
-  type: 'button',
-  title: 'Heading 3',
-  fn: {
-    wiki: function() {
-      this.encloseLineSelection('### ', '',function(str) {
-        str = str.replace(/^#+\s+/, '')
-        return str;
-      });
-    }
-  }
-}
-
-// spacer
-jsToolBar.prototype.elements.space2 = {type: 'space'}
-
-// ul
-jsToolBar.prototype.elements.ul = {
-  type: 'button',
-  title: 'Unordered list',
-  fn: {
-    wiki: function() {
-      this.encloseLineSelection('','',function(str) {
-        str = str.replace(/\r/g,'');
-        return str.replace(/(\n|^)[#-]?\s*/g,"$1* ");
-      });
-    }
-  }
-}
-
-// ol
-jsToolBar.prototype.elements.ol = {
-  type: 'button',
-  title: 'Ordered list',
-  fn: {
-    wiki: function() {
-      this.encloseLineSelection('','',function(str) {
-        str = str.replace(/\r/g,'');
-        return str.replace(/(\n|^)[*-]?\s*/g,"$11. ");
-      });
-    }
-  }
-}
-
-// tl
-jsToolBar.prototype.elements.tl = {
-  type: 'button',
-  title: 'Task list',
-  fn: {
-    wiki: function() {
-      this.encloseLineSelection('','',function(str) {
-        str = str.replace(/\r/g,'');
-        return str.replace(/(\n|^)[*-]?\s*/g,"$1* [ ] ");
-      });
-    }
-  }
-}
-
-// spacer
-jsToolBar.prototype.elements.space3 = {type: 'space'}
-
-// bq
-jsToolBar.prototype.elements.bq = {
-  type: 'button',
-  title: 'Quote',
-  fn: {
-    wiki: function() {
-      this.encloseLineSelection('','',function(str) {
-        str = str.replace(/\r/g,'');
-        return str.replace(/(\n|^)( *)([^\n]*)/g,"$1> $2$3");
-      });
-    }
-  }
-}
-
-// unbq
-jsToolBar.prototype.elements.unbq = {
-  type: 'button',
-  title: 'Unquote',
-  fn: {
-    wiki: function() {
-      this.encloseLineSelection('','',function(str) {
-        str = str.replace(/\r/g,'');
-        return str.replace(/(\n|^) *(> ?)?( *)([^\n]*)/g,"$1$3$4");
-      });
-    }
-  }
-}
-
-// table
-jsToolBar.prototype.elements.table = {
-  type: 'button',
-  title: 'Table',
-  fn: {
-    wiki: function() {
-      var This = this;
-      this.tableMenu(function(cols, rowCount){
-        This.encloseLineSelection(
-          '|'+cols.join(' |')+' |\n' +                                   // header
-          Array(cols.length+1).join('|--')+'|\n' +                       // second line
-          Array(rowCount+1).join(Array(cols.length+1).join('|  ')+'|\n') // cells
-        );
-      });
-    }
-  }
-}
-
-// pre
-jsToolBar.prototype.elements.pre = {
-  type: 'button',
-  title: 'Preformatted text',
-  fn: {
-    wiki: function() { this.encloseLineSelection('```\n', '\n```') }
-  }
-}
-
-// Code highlighting
-jsToolBar.prototype.elements.precode = {
-  type: 'button',
-  title: 'Highlighted code',
-  fn: {
-    wiki: function() {
-      var This = this;
-      this.precodeMenu(function(lang){
-        This.encloseLineSelection('``` ' + lang + '\n', '\n```\n');
-      });
-    }
-  }
-}
-
-// spacer
-jsToolBar.prototype.elements.space4 = {type: 'space'}
-
-// wiki page
-jsToolBar.prototype.elements.link = {
-  type: 'button',
-  title: 'Wiki link',
-  fn: {
-    wiki: function() { this.encloseSelection("[[", "]]") }
-  }
-}
-// image
-jsToolBar.prototype.elements.img = {
-  type: 'button',
-  title: 'Image',
-  fn: {
-    wiki: function() { this.encloseSelection("![](", ")") }
-  }
-}
-
-// spacer
-jsToolBar.prototype.elements.space5 = {type: 'space'}
-// help
-jsToolBar.prototype.elements.help = {
-  type: 'button',
-  title: 'Help',
-  fn: {
-    wiki: function() { window.open(this.help_link, '', 'resizable=yes, location=no, width=300, height=640, menubar=no, status=no, scrollbars=yes') }
-  }
-}
diff --git a/app/assets/javascripts/jstoolbar/jstoolbar.js b/app/assets/javascripts/jstoolbar/jstoolbar.js
deleted file mode 100644
index bae625c3b..000000000
--- a/app/assets/javascripts/jstoolbar/jstoolbar.js
+++ /dev/null
@@ -1,568 +0,0 @@
-/**
- * This file is part of DotClear.
- * Copyright (c) 2005 Nicolas Martin & Olivier Meunier and contributors. All rights reserved.
- * This code is released under the GNU General Public License.
- *
- * Modified by JP LANG for multiple text formatting
- */
-
-let lastJstPreviewed = null;
-const isMac = Boolean(navigator.platform.toLowerCase().match(/mac/));
-
-function jsToolBar(textarea) {
-  if (!document.createElement) { return; }
-
-  if (!textarea) { return; }
-
-  if ((typeof(document["selection"]) == "undefined")
-  && (typeof(textarea["setSelectionRange"]) == "undefined")) {
-    return;
-    }
-
-  this.textarea = textarea;
-
-  this.toolbarBlock = document.createElement('div');
-  this.toolbarBlock.className = 'jstBlock';
-  this.textarea.parentNode.insertBefore(this.toolbarBlock, this.textarea);
-
-  this.editor = document.createElement('div');
-  this.editor.className = 'jstEditor';
-
-  this.preview = document.createElement('div');
-  this.preview.className = 'wiki wiki-preview hidden';
-  this.preview.setAttribute('id', 'preview_' + textarea.getAttribute('id'));
-
-  this.editor.appendChild(this.textarea);
-  this.editor.appendChild(this.preview);
-
-  this.tabsBlock = document.createElement('div');
-  this.tabsBlock.className = 'jstTabs tabs';
-
-  var This = this;
-
-  this.textarea.onkeydown = function(event) { This.keyboardShortcuts.call(This, event); };
-
-  this.editTab = new jsTab('Edit', true);
-  this.editTab.onclick = function(event) { This.hidePreview.call(This, event); return false; };
-
-  this.previewTab = new jsTab('Preview');
-  this.previewTab.onclick = function(event) { This.showPreview.call(This, event); return false; };
-
-  var elementsTab = document.createElement('li');
-  elementsTab.classList = 'tab-elements';
-
-  var tabs = document.createElement('ul');
-  tabs.appendChild(this.editTab);
-  tabs.appendChild(this.previewTab);
-  tabs.appendChild(elementsTab);
-  this.tabsBlock.appendChild(tabs);
-
-  this.toolbar = document.createElement("div");
-  this.toolbar.className = 'jstElements';
-  elementsTab.appendChild(this.toolbar);
-
-  this.toolbarBlock.appendChild(this.tabsBlock);
-  this.toolbarBlock.appendChild(this.editor);
-
-  // Dragable resizing
-  if (this.editor.addEventListener && navigator.appVersion.match(/\bMSIE\b/))
-  {
-    this.handle = document.createElement('div');
-    this.handle.className = 'jstHandle';
-    var dragStart = this.resizeDragStart;
-    var This = this;
-    this.handle.addEventListener('mousedown',function(event) { dragStart.call(This,event); },false);
-    // fix memory leak in Firefox (bug #241518)
-    window.addEventListener('unload',function() {
-      var del = This.handle.parentNode.removeChild(This.handle);
-      delete(This.handle);
-    },false);
-
-    this.editor.parentNode.insertBefore(this.handle,this.editor.nextSibling);
-  }
-
-  this.context = null;
-  this.toolNodes = {}; // lorsque la toolbar est dessinée , cet objet est garni
-                       // de raccourcis vers les éléments DOM correspondants aux outils.
-}
-
-function jsTab(name, selected) {
-  selected = selected || false;
-  if(typeof jsToolBar.strings == 'undefined') {
-    var tabName = name || null;
-  } else {
-    var tabName = jsToolBar.strings[name] || name || null;
-  }
-
-  var tab = document.createElement('li');
-  var link = document.createElement('a');
-  link.setAttribute('href', '#');
-  link.innerText = tabName;
-  link.className = 'tab-' + name.toLowerCase();
-
-  if (selected == true) {
-    link.classList.add('selected');
-  }
-  tab.appendChild(link)
-
-  return tab;
-}
-function jsButton(title, fn, scope, className) {
-  if(typeof jsToolBar.strings == 'undefined') {
-    this.title = title || null;
-  } else {
-      this.title = jsToolBar.strings[title] || title || null;
-  }
-  this.fn = fn || function(){};
-  this.scope = scope || null;
-  this.className = className || null;
-}
-jsButton.prototype.draw = function() {
-  if (!this.scope) return null;
-
-  var button = document.createElement('button');
-  button.setAttribute('type','button');
-  button.tabIndex = 200;
-  if (this.className) button.className = this.className;
-  button.title = this.title;
-  var span = document.createElement('span');
-  span.appendChild(document.createTextNode(this.title));
-  button.appendChild(span);
-
-  if (this.icon != undefined) {
-    button.style.backgroundImage = 'url('+this.icon+')';
-  }
-
-  if (typeof(this.fn) == 'function') {
-    var This = this;
-    button.onclick = function() { try { This.fn.apply(This.scope, arguments) } catch (e) {} return false; };
-  }
-  return button;
-}
-
-function jsSpace(className) {
-  this.className = className || null;
-  this.width = null;
-}
-jsSpace.prototype.draw = function() {
-  var span = document.createElement('span');
-  span.appendChild(document.createTextNode(String.fromCharCode(160)));
-  span.className = 'jstSpacer' + (this.className ? ' ' + this.className : '');
-  if (this.width) span.style.marginRight = this.width+'px';
-
-  return span;
-}
-
-function jsCombo(title, options, scope, fn, className) {
-  this.title = title || null;
-  this.options = options || null;
-  this.scope = scope || null;
-  this.fn = fn || function(){};
-  this.className = className || null;
-}
-jsCombo.prototype.draw = function() {
-  if (!this.scope || !this.options) return null;
-
-  var select = document.createElement('select');
-  if (this.className) select.className = className;
-  select.title = this.title;
-
-  for (var o in this.options) {
-    //var opt = this.options[o];
-    var option = document.createElement('option');
-    option.value = o;
-    option.appendChild(document.createTextNode(this.options[o]));
-    select.appendChild(option);
-  }
-
-  var This = this;
-  select.onchange = function() {
-    try {
-      This.fn.call(This.scope, this.value);
-    } catch (e) { alert(e); }
-
-    return false;
-  }
-
-  return select;
-}
-
-
-jsToolBar.prototype = {
-  base_url: '',
-  mode: 'wiki',
-  elements: {},
-  help_link: '',
-  shortcuts: {},
-
-  getMode: function() {
-    return this.mode;
-  },
-
-  setMode: function(mode) {
-    this.mode = mode || 'wiki';
-  },
-
-  switchMode: function(mode) {
-    mode = mode || 'wiki';
-    this.draw(mode);
-  },
-
-  setHelpLink: function(link) {
-    this.help_link = link;
-  },
-
-  setPreviewUrl: function(url) {
-    this.previewTab.firstChild.setAttribute('data-url', url);
-  },
-
-  button: function(toolName) {
-    var tool = this.elements[toolName];
-    if (typeof tool.fn[this.mode] != 'function') return null;
-
-    const className = 'jstb_' + toolName;
-    let title = tool.title
-
-    if (tool.hasOwnProperty('shortcut')) {
-      this.shortcuts[tool.shortcut] = className;
-      title = this.buttonTitleWithShortcut(tool.title, tool.shortcut)
-    }
-
-    var b = new jsButton(title, tool.fn[this.mode], this, className);
-    if (tool.icon != undefined) b.icon = tool.icon;
-
-    return b;
-  },
-  buttonTitleWithShortcut: function(title, shortcutKey) {
-    if(typeof jsToolBar.strings == 'undefined') {
-      var i18nTitle = title || null;
-    } else {
-      var i18nTitle = jsToolBar.strings[title] || title || null;
-    }
-
-    if (isMac) {
-      return i18nTitle + " (⌘" + shortcutKey.toUpperCase() + ")";
-    } else {
-      return i18nTitle + " (Ctrl+" + shortcutKey.toUpperCase() + ")";
-    }
-  },
-  space: function(toolName) {
-    var tool = new jsSpace(toolName)
-    if (this.elements[toolName].width !== undefined)
-      tool.width = this.elements[toolName].width;
-    return tool;
-  },
-  combo: function(toolName) {
-    var tool = this.elements[toolName];
-    var length = tool[this.mode].list.length;
-
-    if (typeof tool[this.mode].fn != 'function' || length == 0) {
-      return null;
-    } else {
-      var options = {};
-      for (var i=0; i < length; i++) {
-        var opt = tool[this.mode].list[i];
-        options[opt] = tool.options[opt];
-      }
-      return new jsCombo(tool.title, options, this, tool[this.mode].fn);
-    }
-  },
-  draw: function(mode) {
-    this.setMode(mode);
-
-    // Empty toolbar
-    while (this.toolbar.hasChildNodes()) {
-      this.toolbar.removeChild(this.toolbar.firstChild)
-    }
-    this.toolNodes = {}; // vide les raccourcis DOM/**/
-
-    // Draw toolbar elements
-    var b, tool, newTool;
-
-    for (var i in this.elements) {
-      b = this.elements[i];
-
-      var disabled =
-      b.type == undefined || b.type == ''
-      || (b.disabled != undefined && b.disabled)
-      || (b.context != undefined && b.context != null && b.context != this.context);
-
-      if (!disabled && typeof this[b.type] == 'function') {
-        tool = this[b.type](i);
-        if (tool) newTool = tool.draw();
-        if (newTool) {
-          this.toolNodes[i] = newTool; //mémorise l'accès DOM pour usage éventuel ultérieur
-          this.toolbar.appendChild(newTool);
-        }
-      }
-    }
-  },
-
-  singleTag: function(stag,etag) {
-    stag = stag || null;
-    etag = etag || stag;
-
-    if (!stag || !etag) { return; }
-
-    this.encloseSelection(stag,etag);
-  },
-
-  encloseLineSelection: function(prefix, suffix, fn) {
-    this.textarea.focus();
-
-    prefix = prefix || '';
-    suffix = suffix || '';
-
-    var start, end, sel, scrollPos, subst, res;
-
-    if (typeof(document["selection"]) != "undefined") {
-      sel = document.selection.createRange().text;
-    } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
-      start = this.textarea.selectionStart;
-      end = this.textarea.selectionEnd;
-      scrollPos = this.textarea.scrollTop;
-      // go to the start of the line
-      start = this.textarea.value.substring(0, start).replace(/[^\r\n]*$/g,'').length;
-      // go to the end of the line
-      end = this.textarea.value.length - this.textarea.value.substring(end, this.textarea.value.length).replace(/^[^\r\n]*/, '').length;
-      sel = this.textarea.value.substring(start, end);
-    }
-
-    if (sel.match(/ $/)) { // exclude ending space char, if any
-      sel = sel.substring(0, sel.length - 1);
-      suffix = suffix + " ";
-    }
-
-    if (typeof(fn) == 'function') {
-      res = (sel) ? fn.call(this,sel) : fn('');
-    } else {
-      res = (sel) ? sel : '';
-    }
-
-    subst = prefix + res + suffix;
-
-    if (typeof(document["selection"]) != "undefined") {
-      document.selection.createRange().text = subst;
-      var range = this.textarea.createTextRange();
-      range.collapse(false);
-      range.move('character', -suffix.length);
-      range.select();
-    } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
-      this.textarea.value = this.textarea.value.substring(0, start) + subst +
-      this.textarea.value.substring(end);
-      if (sel || (!prefix && start === end)) {
-        this.textarea.setSelectionRange(start + subst.length, start + subst.length);
-      } else {
-        this.textarea.setSelectionRange(start + prefix.length, start + prefix.length);
-      }
-      this.textarea.scrollTop = scrollPos;
-    }
-  },
-
-  encloseSelection: function(prefix, suffix, fn) {
-    this.textarea.focus();
-    prefix = prefix || '';
-    suffix = suffix || '';
-
-    var start, end, sel, scrollPos, subst, res;
-
-    if (typeof(document["selection"]) != "undefined") {
-      sel = document.selection.createRange().text;
-    } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
-      start = this.textarea.selectionStart;
-      end = this.textarea.selectionEnd;
-      scrollPos = this.textarea.scrollTop;
-      sel = this.textarea.value.substring(start, end);
-      if (start > 0 && this.textarea.value.substr(start-1, 1).match(/\S/)) {
-        prefix = ' ' + prefix;
-      }
-      if (this.textarea.value.substr(end, 1).match(/\S/)) {
-        suffix = suffix + ' ';
-      }
-    }
-    if (sel.match(/ $/)) { // exclude ending space char, if any
-      sel = sel.substring(0, sel.length - 1);
-      suffix = suffix + " ";
-    }
-
-    if (typeof(fn) == 'function') {
-      res = (sel) ? fn.call(this,sel) : fn('');
-    } else {
-      res = (sel) ? sel : '';
-    }
-
-    subst = prefix + res + suffix;
-
-    if (typeof(document["selection"]) != "undefined") {
-      document.selection.createRange().text = subst;
-      var range = this.textarea.createTextRange();
-      range.collapse(false);
-      range.move('character', -suffix.length);
-      range.select();
-//      this.textarea.caretPos -= suffix.length;
-    } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
-      this.textarea.value = this.textarea.value.substring(0, start) + subst +
-      this.textarea.value.substring(end);
-      if (sel) {
-        this.textarea.setSelectionRange(start + subst.length, start + subst.length);
-      } else {
-        this.textarea.setSelectionRange(start + prefix.length, start + prefix.length);
-      }
-      this.textarea.scrollTop = scrollPos;
-    }
-  },
-  showPreview: function(event) {
-    if (event.target.classList.contains('selected')) { return; }
-    lastJstPreviewed = this.toolbarBlock;
-    this.preview.setAttribute('style', 'min-height: ' + this.textarea.clientHeight + 'px;')
-    this.toolbar.classList.add('hidden');
-    this.textarea.classList.add('hidden');
-    this.preview.classList.remove('hidden');
-    this.tabsBlock.querySelector('.tab-edit').classList.remove('selected');
-    event.target.classList.add('selected');
-  },
-  hidePreview: function(event) {
-    if (event.target.classList.contains('selected')) { return; }
-    this.toolbar.classList.remove('hidden');
-    this.textarea.classList.remove('hidden');
-    this.textarea.focus();
-    this.preview.classList.add('hidden');
-    this.tabsBlock.querySelector('.tab-preview').classList.remove('selected');
-    event.target.classList.add('selected');
-  },
-  keyboardShortcuts: function(e) {
-    let stop = false;
-    if (isToogleEditPreviewShortcut(e)) {
-      // Switch to preview only if Edit tab is selected when the event triggers.
-      if (this.tabsBlock.querySelector('.tab-edit.selected')) {
-        stop = true
-        this.tabsBlock.querySelector('.tab-preview').click();
-      }
-    }
-    if (isModifierKey(e) && this.shortcuts.hasOwnProperty(e.key.toLowerCase())) {
-      stop = true
-      this.toolbar.querySelector("." + this.shortcuts[e.key.toLowerCase()]).click();
-    }
-    if (stop) {
-      e.stopPropagation();
-      e.preventDefault();
-    }
-  },
-  stripBaseURL: function(url) {
-    if (this.base_url != '') {
-      var pos = url.indexOf(this.base_url);
-      if (pos == 0) {
-        url = url.substr(this.base_url.length);
-      }
-    }
-
-    return url;
-  }
-};
-
-/** Resizer
--------------------------------------------------------- */
-jsToolBar.prototype.resizeSetStartH = function() {
-  this.dragStartH = this.textarea.offsetHeight + 0;
-};
-jsToolBar.prototype.resizeDragStart = function(event) {
-  var This = this;
-  this.dragStartY = event.clientY;
-  this.resizeSetStartH();
-  document.addEventListener('mousemove', this.dragMoveHdlr=function(event){This.resizeDragMove(event);}, false);
-  document.addEventListener('mouseup', this.dragStopHdlr=function(event){This.resizeDragStop(event);}, false);
-};
-
-jsToolBar.prototype.resizeDragMove = function(event) {
-  this.textarea.style.height = (this.dragStartH+event.clientY-this.dragStartY)+'px';
-};
-
-jsToolBar.prototype.resizeDragStop = function(event) {
-  document.removeEventListener('mousemove', this.dragMoveHdlr, false);
-  document.removeEventListener('mouseup', this.dragStopHdlr, false);
-};
-
-/* Code highlighting menu */
-jsToolBar.prototype.precodeMenu = function(fn){
-  var hlLanguages = window.userHlLanguages;
-  var menu = $("<ul style='position:absolute;'></ul>");
-  for (var i = 0; i < hlLanguages.length; i++) {
-    var langItem = $('<div></div>').text(hlLanguages[i]);
-    $("<li></li>").html(langItem).appendTo(menu).mousedown(function(){
-      fn($(this).text());
-    });
-  }
-  $("body").append(menu);
-  menu.menu().width(150).position({
-    my: "left top",
-    at: "left bottom",
-    of: this.toolNodes['precode']
-  });
-  $(document).on("mousedown", function() {
-    menu.remove();
-  });
-  return false;
-};
-
-/* Table generator */
-jsToolBar.prototype.tableMenu = function(fn){
-  var alphabets = "ABCDEFGHIJ".split('');
-  var menu = $("<table class='table-generator'></table>");
-
-  for (var r = 1;  r <= 5;  r++) {
-    var row = $("<tr></tr>").appendTo(menu);
-    for (var c = 1;  c <= 10;  c++) {
-      $("<td data-row="+r+" data-col="+c+" title="+(c)+'&times;'+(r)+"></td>").mousedown(function(){
-        fn(alphabets.slice(0, $(this).data('col')), $(this).data('row'));
-      }).hover(function(){
-        var hoverRow = $(this).data('row');
-        var hoverCol = $(this).data('col');
-        $(this).closest('table').find('td').each(function(_index, element){
-          if ($(element).data('row') <= hoverRow && $(element).data('col') <= hoverCol){
-            $(element).addClass('selected-cell');
-          } else {
-            $(element).removeClass('selected-cell');
-          }
-        });
-      }).appendTo(row);
-    }
-  }
-  $("body").append(menu);
-  menu.position({
-    my: "left top",
-    at: "left bottom",
-    of: this.toolNodes['table']
-  });
-  $(document).on("mousedown", function() {
-    menu.remove();
-  });
-  return false;
-};
-
-$(document).keydown(function(e) {
-  if (isToogleEditPreviewShortcut(e)) {
-    if (lastJstPreviewed !== null) {
-      e.preventDefault();
-      e.stopPropagation();
-      lastJstPreviewed.querySelector('.tab-edit').click();
-      lastJstPreviewed = null;
-    }
-  }
-});
-
-function isToogleEditPreviewShortcut(e) {
-  if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key.toLowerCase() === 'p') {
-    return true;
-  } else {
-    return false;
-  }
-}
-function isModifierKey(e) {
-  if (isMac && e.metaKey) {
-    return true;
-  } else if (!isMac && e.ctrlKey) {
-    return true;
-  } else {
-    return false;
-  }
-}
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ar.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-ar.js
deleted file mode 100644
index 4a88b6b3e..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ar.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {}; 
-jsToolBar.strings['Strong'] = 'قوي';
-jsToolBar.strings['Italic'] = 'مائل';
-jsToolBar.strings['Underline'] = 'تسطير';
-jsToolBar.strings['Deleted'] = 'محذوف';
-jsToolBar.strings['Code'] = 'رمز ضمني';
-jsToolBar.strings['Heading 1'] = 'عنوان 1';
-jsToolBar.strings['Heading 2'] = 'عنوان 2';
-jsToolBar.strings['Heading 3'] = 'عنوان 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'قائمة غير مرتبة';
-jsToolBar.strings['Ordered list'] = 'قائمة مرتبة';
-jsToolBar.strings['Quote'] = 'اقتباس';
-jsToolBar.strings['Unquote'] = 'إزالة الاقتباس';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'نص مسبق التنسيق';
-jsToolBar.strings['Wiki link'] = 'رابط الى صفحة ويكي';
-jsToolBar.strings['Image'] = 'صورة';
-jsToolBar.strings['Edit'] = 'تعديل';
-jsToolBar.strings['Preview'] = 'معاينة';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-az.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-az.js
deleted file mode 100644
index a100460ab..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-az.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Strong';
-jsToolBar.strings['Italic'] = 'Italic';
-jsToolBar.strings['Underline'] = 'Underline';
-jsToolBar.strings['Deleted'] = 'Deleted';
-jsToolBar.strings['Code'] = 'Inline Code';
-jsToolBar.strings['Heading 1'] = 'Heading 1';
-jsToolBar.strings['Heading 2'] = 'Heading 2';
-jsToolBar.strings['Heading 3'] = 'Heading 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Unordered list';
-jsToolBar.strings['Ordered list'] = 'Ordered list';
-jsToolBar.strings['Quote'] = 'Quote';
-jsToolBar.strings['Unquote'] = 'Remove Quote';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Preformatted text';
-jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
-jsToolBar.strings['Image'] = 'Image';
-jsToolBar.strings['Edit'] = 'Redaktə etmək';
-jsToolBar.strings['Preview'] = 'İlkin baxış';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-bg.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-bg.js
deleted file mode 100644
index 913295286..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-bg.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Получер';
-jsToolBar.strings['Italic'] = 'Курсив';
-jsToolBar.strings['Underline'] = 'Подчертан';
-jsToolBar.strings['Deleted'] = 'Изтрит';
-jsToolBar.strings['Code'] = 'Вграден код';
-jsToolBar.strings['Heading 1'] = 'Заглавие 1';
-jsToolBar.strings['Heading 2'] = 'Заглавие 2';
-jsToolBar.strings['Heading 3'] = 'Заглавие 3';
-jsToolBar.strings['Highlighted code'] = 'Вграден код';
-jsToolBar.strings['Unordered list'] = 'Неподреден списък';
-jsToolBar.strings['Ordered list'] = 'Подреден списък';
-jsToolBar.strings['Quote'] = 'Цитат';
-jsToolBar.strings['Unquote'] = 'Премахване на цитат';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Форматиран текст';
-jsToolBar.strings['Wiki link'] = 'Връзка до Wiki страница';
-jsToolBar.strings['Image'] = 'Изображение';
-jsToolBar.strings['Edit'] = 'Редакция';
-jsToolBar.strings['Preview'] = 'Преглед';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-bs.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-bs.js
deleted file mode 100644
index 08f4fef9a..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-bs.js
+++ /dev/null
@@ -1,18 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Strong';
-jsToolBar.strings['Italic'] = 'Italic';
-jsToolBar.strings['Underline'] = 'Underline';
-jsToolBar.strings['Deleted'] = 'Deleted';
-jsToolBar.strings['Code'] = 'Inline Code';
-jsToolBar.strings['Heading 1'] = 'Heading 1';
-jsToolBar.strings['Heading 2'] = 'Heading 2';
-jsToolBar.strings['Heading 3'] = 'Heading 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Unordered list';
-jsToolBar.strings['Ordered list'] = 'Ordered list';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Preformatted text';
-jsToolBar.strings['Wiki link'] = 'Link na Wiki stranicu';
-jsToolBar.strings['Image'] = 'Slika';
-jsToolBar.strings['Edit'] = 'Ispravka';
-jsToolBar.strings['Preview'] = 'Pregled';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ca.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-ca.js
deleted file mode 100644
index abd543b10..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ca.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Negreta';
-jsToolBar.strings['Italic'] = 'Cursiva';
-jsToolBar.strings['Underline'] = 'Subratllat';
-jsToolBar.strings['Deleted'] = 'Barrat';
-jsToolBar.strings['Code'] = 'Codi en línia';
-jsToolBar.strings['Heading 1'] = 'Encapçalament 1';
-jsToolBar.strings['Heading 2'] = 'Encapçalament 2';
-jsToolBar.strings['Heading 3'] = 'Encapçalament 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Llista sense ordre';
-jsToolBar.strings['Ordered list'] = 'Llista ordenada';
-jsToolBar.strings['Quote'] = 'Cometes';
-jsToolBar.strings['Unquote'] = 'Sense cometes';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Text formatat';
-jsToolBar.strings['Wiki link'] = 'Enllaça a una pàgina Wiki';
-jsToolBar.strings['Image'] = 'Imatge';
-jsToolBar.strings['Edit'] = 'Editar';
-jsToolBar.strings['Preview'] = 'Previsualitzar';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-cs.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-cs.js
deleted file mode 100644
index 7df24289d..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-cs.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Tučné';
-jsToolBar.strings['Italic'] = 'Kurzíva';
-jsToolBar.strings['Underline'] = 'Podtržené';
-jsToolBar.strings['Deleted'] = 'Přeškrtnuté ';
-jsToolBar.strings['Code'] = 'Vnořený kód';
-jsToolBar.strings['Heading 1'] = 'Nadpis 1';
-jsToolBar.strings['Heading 2'] = 'Nadpis 2';
-jsToolBar.strings['Heading 3'] = 'Nadpis 3';
-jsToolBar.strings['Highlighted code'] = 'Zvýrazněný kód';
-jsToolBar.strings['Unordered list'] = 'Seznam';
-jsToolBar.strings['Ordered list'] = 'Uspořádaný seznam';
-jsToolBar.strings['Quote'] = 'Citace';
-jsToolBar.strings['Unquote'] = 'Odstranit citaci';
-jsToolBar.strings['Table'] = 'Tabulka';
-jsToolBar.strings['Preformatted text'] = 'Předformátovaný text';
-jsToolBar.strings['Wiki link'] = 'Vložit odkaz na Wiki stránku';
-jsToolBar.strings['Image'] = 'Vložit obrázek';
-jsToolBar.strings['Edit'] = 'Upravit';
-jsToolBar.strings['Preview'] = 'Náhled';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-da.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-da.js
deleted file mode 100644
index f4a848d44..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-da.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Fed';
-jsToolBar.strings['Italic'] = 'Kursiv';
-jsToolBar.strings['Underline'] = 'Understreget';
-jsToolBar.strings['Deleted'] = 'Slettet';
-jsToolBar.strings['Code'] = 'Inline-kode';
-jsToolBar.strings['Heading 1'] = 'Overskrift 1';
-jsToolBar.strings['Heading 2'] = 'Overskrift 2';
-jsToolBar.strings['Heading 3'] = 'Overskrift 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Unummereret liste';
-jsToolBar.strings['Ordered list'] = 'Nummereret liste';
-jsToolBar.strings['Quote'] = 'Citér';
-jsToolBar.strings['Unquote'] = 'Fjern citér';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Præformateret tekst';
-jsToolBar.strings['Wiki link'] = 'Link til en wiki-side';
-jsToolBar.strings['Image'] = 'Billede';
-jsToolBar.strings['Edit'] = 'Ret';
-jsToolBar.strings['Preview'] = 'Forhåndsvisning';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-de.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-de.js
deleted file mode 100644
index fe15f060c..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-de.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Fett';
-jsToolBar.strings['Italic'] = 'Kursiv';
-jsToolBar.strings['Underline'] = 'Unterstrichen';
-jsToolBar.strings['Deleted'] = 'Durchgestrichen';
-jsToolBar.strings['Code'] = 'Quelltext';
-jsToolBar.strings['Heading 1'] = 'Überschrift 1. Ordnung';
-jsToolBar.strings['Heading 2'] = 'Überschrift 2. Ordnung';
-jsToolBar.strings['Heading 3'] = 'Überschrift 3. Ordnung';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Aufzählungsliste';
-jsToolBar.strings['Ordered list'] = 'Nummerierte Liste';
-jsToolBar.strings['Quote'] = 'Zitat';
-jsToolBar.strings['Unquote'] = 'Zitat entfernen';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Präformatierter Text';
-jsToolBar.strings['Wiki link'] = 'Verweis (Link) zu einer Wiki-Seite';
-jsToolBar.strings['Image'] = 'Grafik';
-jsToolBar.strings['Edit'] = 'Bearbeiten';
-jsToolBar.strings['Preview'] = 'Vorschau';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-en-gb.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-en-gb.js
deleted file mode 100644
index e4b456a45..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-en-gb.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Strong';
-jsToolBar.strings['Italic'] = 'Italic';
-jsToolBar.strings['Underline'] = 'Underline';
-jsToolBar.strings['Deleted'] = 'Deleted';
-jsToolBar.strings['Code'] = 'Inline Code';
-jsToolBar.strings['Heading 1'] = 'Heading 1';
-jsToolBar.strings['Heading 2'] = 'Heading 2';
-jsToolBar.strings['Heading 3'] = 'Heading 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Unordered list';
-jsToolBar.strings['Ordered list'] = 'Ordered list';
-jsToolBar.strings['Quote'] = 'Quote';
-jsToolBar.strings['Unquote'] = 'Remove Quote';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Preformatted text';
-jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
-jsToolBar.strings['Image'] = 'Image';
-jsToolBar.strings['Edit'] = 'Edit';
-jsToolBar.strings['Preview'] = 'Preview';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-en.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-en.js
deleted file mode 100644
index e4b456a45..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-en.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Strong';
-jsToolBar.strings['Italic'] = 'Italic';
-jsToolBar.strings['Underline'] = 'Underline';
-jsToolBar.strings['Deleted'] = 'Deleted';
-jsToolBar.strings['Code'] = 'Inline Code';
-jsToolBar.strings['Heading 1'] = 'Heading 1';
-jsToolBar.strings['Heading 2'] = 'Heading 2';
-jsToolBar.strings['Heading 3'] = 'Heading 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Unordered list';
-jsToolBar.strings['Ordered list'] = 'Ordered list';
-jsToolBar.strings['Quote'] = 'Quote';
-jsToolBar.strings['Unquote'] = 'Remove Quote';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Preformatted text';
-jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
-jsToolBar.strings['Image'] = 'Image';
-jsToolBar.strings['Edit'] = 'Edit';
-jsToolBar.strings['Preview'] = 'Preview';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-es-pa.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-es-pa.js
deleted file mode 100644
index 50c55c98c..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-es-pa.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Negrita';
-jsToolBar.strings['Italic'] = 'Itálica';
-jsToolBar.strings['Underline'] = 'Subrayado';
-jsToolBar.strings['Deleted'] = 'Tachado';
-jsToolBar.strings['Code'] = 'Código fuente';
-jsToolBar.strings['Heading 1'] = 'Encabezado 1';
-jsToolBar.strings['Heading 2'] = 'Encabezado 2';
-jsToolBar.strings['Heading 3'] = 'Encabezado 3';
-jsToolBar.strings['Highlighted code'] = 'Código resaltado';
-jsToolBar.strings['Unordered list'] = 'Lista sin ordenar';
-jsToolBar.strings['Ordered list'] = 'Lista ordenada';
-jsToolBar.strings['Quote'] = 'Citar';
-jsToolBar.strings['Unquote'] = 'Quitar cita';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Texto con formato';
-jsToolBar.strings['Wiki link'] = 'Enlace a página Wiki';
-jsToolBar.strings['Image'] = 'Imagen';
-jsToolBar.strings['Edit'] = 'Modificar';
-jsToolBar.strings['Preview'] = 'Previsualizar';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-es.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-es.js
deleted file mode 100644
index 50c55c98c..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-es.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Negrita';
-jsToolBar.strings['Italic'] = 'Itálica';
-jsToolBar.strings['Underline'] = 'Subrayado';
-jsToolBar.strings['Deleted'] = 'Tachado';
-jsToolBar.strings['Code'] = 'Código fuente';
-jsToolBar.strings['Heading 1'] = 'Encabezado 1';
-jsToolBar.strings['Heading 2'] = 'Encabezado 2';
-jsToolBar.strings['Heading 3'] = 'Encabezado 3';
-jsToolBar.strings['Highlighted code'] = 'Código resaltado';
-jsToolBar.strings['Unordered list'] = 'Lista sin ordenar';
-jsToolBar.strings['Ordered list'] = 'Lista ordenada';
-jsToolBar.strings['Quote'] = 'Citar';
-jsToolBar.strings['Unquote'] = 'Quitar cita';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Texto con formato';
-jsToolBar.strings['Wiki link'] = 'Enlace a página Wiki';
-jsToolBar.strings['Image'] = 'Imagen';
-jsToolBar.strings['Edit'] = 'Modificar';
-jsToolBar.strings['Preview'] = 'Previsualizar';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-et.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-et.js
deleted file mode 100644
index b71c4c06b..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-et.js
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- Copyright (C) 2012 Kaitseministeerium
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- of the License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-*/
-
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Rõhutatult';
-jsToolBar.strings['Italic'] = 'Kaldkirjas';
-jsToolBar.strings['Underline'] = 'Allakriipsutatult';
-jsToolBar.strings['Deleted'] = 'Läbikriipsutatult';
-jsToolBar.strings['Code'] = 'Koodiblokk';
-jsToolBar.strings['Heading 1'] = '1. taseme pealkiri';
-jsToolBar.strings['Heading 2'] = '2. taseme pealkiri';
-jsToolBar.strings['Heading 3'] = '3. taseme pealkiri';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Täpitud nimekiri';
-jsToolBar.strings['Ordered list'] = 'Nummerdatud nimekiri';
-jsToolBar.strings['Quote'] = 'Tsitaat: aste juurde';
-jsToolBar.strings['Unquote'] = 'Tsitaat: aste madalamaks';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Eelvormindatud tekst';
-jsToolBar.strings['Wiki link'] = 'Vikilehe link';
-jsToolBar.strings['Image'] = 'Pilt';
-jsToolBar.strings['Edit'] = 'Muuda';
-jsToolBar.strings['Preview'] = 'Eelvaade';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-eu.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-eu.js
deleted file mode 100644
index 52e7877b9..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-eu.js
+++ /dev/null
@@ -1,24 +0,0 @@
-// jsToolBar EU language
-// Author: Ales Zabala Alava (Shagi), <shagi@gisa-elkartea.org>
-// 2010-01-25
-// Distributed under the same terms as the jsToolBar itself.
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Lodia';
-jsToolBar.strings['Italic'] = 'Etzana';
-jsToolBar.strings['Underline'] = 'Azpimarra';
-jsToolBar.strings['Deleted'] = 'Ezabatuta';
-jsToolBar.strings['Code'] = 'Inline Code';
-jsToolBar.strings['Heading 1'] = '1 Goiburua';
-jsToolBar.strings['Heading 2'] = '2 Goiburua';
-jsToolBar.strings['Heading 3'] = '3 Goiburua';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Ordenatu gabeko zerrenda';
-jsToolBar.strings['Ordered list'] = 'Ordenatutako zerrenda';
-jsToolBar.strings['Quote'] = 'Aipamena';
-jsToolBar.strings['Unquote'] = 'Aipamena kendu';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Aurrez formateatutako testua';
-jsToolBar.strings['Wiki link'] = 'Wiki orri baterako esteka';
-jsToolBar.strings['Image'] = 'Irudia';
-jsToolBar.strings['Edit'] = 'Editatu';
-jsToolBar.strings['Preview'] = 'Aurreikusi';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-fa.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-fa.js
deleted file mode 100644
index 92fb7d105..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-fa.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'پررنگ';
-jsToolBar.strings['Italic'] = 'کج';
-jsToolBar.strings['Underline'] = 'زیرخط';
-jsToolBar.strings['Deleted'] = 'برداشته شده';
-jsToolBar.strings['Code'] = 'کد درون خطی';
-jsToolBar.strings['Heading 1'] = 'سربرگ ۱';
-jsToolBar.strings['Heading 2'] = 'سربرگ ۲';
-jsToolBar.strings['Heading 3'] = 'سربرگ ۳';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'فهرست بدون شماره';
-jsToolBar.strings['Ordered list'] = 'فهرست با شماره';
-jsToolBar.strings['Quote'] = 'تو بردن';
-jsToolBar.strings['Unquote'] = 'بیرون آوردن';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'نوشته قالب بندی شده';
-jsToolBar.strings['Wiki link'] = 'پیوند به برگ ویکی';
-jsToolBar.strings['Image'] = 'عکس';
-jsToolBar.strings['Edit'] = 'ویرایش';
-jsToolBar.strings['Preview'] = 'پیش‌نمایش';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-fi.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-fi.js
deleted file mode 100644
index ed421a287..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-fi.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Lihavoitu';
-jsToolBar.strings['Italic'] = 'Kursivoitu';
-jsToolBar.strings['Underline'] = 'Alleviivattu';
-jsToolBar.strings['Deleted'] = 'Yliviivattu';
-jsToolBar.strings['Code'] = 'Koodi näkymä';
-jsToolBar.strings['Heading 1'] = 'Otsikko 1';
-jsToolBar.strings['Heading 2'] = 'Otsikko 2';
-jsToolBar.strings['Heading 3'] = 'Otsikko 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Järjestämätön lista';
-jsToolBar.strings['Ordered list'] = 'Järjestetty lista';
-jsToolBar.strings['Quote'] = 'Quote';
-jsToolBar.strings['Unquote'] = 'Remove Quote';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Ennaltamuotoiltu teksti';
-jsToolBar.strings['Wiki link'] = 'Linkki Wiki sivulle';
-jsToolBar.strings['Image'] = 'Kuva';
-jsToolBar.strings['Edit'] = 'Muokkaa';
-jsToolBar.strings['Preview'] = 'Esikatselu';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-fr.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-fr.js
deleted file mode 100644
index 996f55cd6..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-fr.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Gras';
-jsToolBar.strings['Italic'] = 'Italique';
-jsToolBar.strings['Underline'] = 'Souligné';
-jsToolBar.strings['Deleted'] = 'Rayé';
-jsToolBar.strings['Code'] = 'Code en ligne';
-jsToolBar.strings['Heading 1'] = 'Titre niveau 1';
-jsToolBar.strings['Heading 2'] = 'Titre niveau 2';
-jsToolBar.strings['Heading 3'] = 'Titre niveau 3';
-jsToolBar.strings['Highlighted code'] = 'Code colorisé';
-jsToolBar.strings['Unordered list'] = 'Liste à puces';
-jsToolBar.strings['Ordered list'] = 'Liste numérotée';
-jsToolBar.strings['Quote'] = 'Indenté';
-jsToolBar.strings['Unquote'] = 'Supprimer indentation';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Texte préformaté';
-jsToolBar.strings['Wiki link'] = 'Lien vers une page Wiki';
-jsToolBar.strings['Image'] = 'Image';
-jsToolBar.strings['Edit'] = 'Modifier';
-jsToolBar.strings['Preview'] = 'Prévisualiser';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-gl.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-gl.js
deleted file mode 100644
index 4e11cb1ed..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-gl.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Negriña';
-jsToolBar.strings['Italic'] = 'Itálica';
-jsToolBar.strings['Underline'] = 'Subliñado';
-jsToolBar.strings['Deleted'] = 'Tachado';
-jsToolBar.strings['Code'] = 'Código fonte';
-jsToolBar.strings['Heading 1'] = 'Encabezado 1';
-jsToolBar.strings['Heading 2'] = 'Encabezado 2';
-jsToolBar.strings['Heading 3'] = 'Encabezado 3';
-jsToolBar.strings['Highlighted code'] = 'Código resaltado';
-jsToolBar.strings['Unordered list'] = 'Lista sen ordenar';
-jsToolBar.strings['Ordered list'] = 'Lista ordenada';
-jsToolBar.strings['Quote'] = 'Citar';
-jsToolBar.strings['Unquote'] = 'Quitar cita';
-jsToolBar.strings['Table'] = 'Táboa';
-jsToolBar.strings['Preformatted text'] = 'Texto con formato';
-jsToolBar.strings['Wiki link'] = 'Ligazón a páxina Wiki';
-jsToolBar.strings['Image'] = 'Imaxe';
-jsToolBar.strings['Edit'] = 'Modificar';
-jsToolBar.strings['Preview'] = 'Vista previa';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-he.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-he.js
deleted file mode 100644
index 72d4b8db6..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-he.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Strong';
-jsToolBar.strings['Italic'] = 'Italic';
-jsToolBar.strings['Underline'] = 'Underline';
-jsToolBar.strings['Deleted'] = 'Deleted';
-jsToolBar.strings['Code'] = 'Inline Code';
-jsToolBar.strings['Heading 1'] = 'Heading 1';
-jsToolBar.strings['Heading 2'] = 'Heading 2';
-jsToolBar.strings['Heading 3'] = 'Heading 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Unordered list';
-jsToolBar.strings['Ordered list'] = 'Ordered list';
-jsToolBar.strings['Quote'] = 'Quote';
-jsToolBar.strings['Unquote'] = 'Remove Quote';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Preformatted text';
-jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
-jsToolBar.strings['Image'] = 'Image';
-jsToolBar.strings['Edit'] = 'ערוך';
-jsToolBar.strings['Preview'] = 'תצוגה מקדימה';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-hr.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-hr.js
deleted file mode 100644
index 8a864f4aa..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-hr.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Podebljano';
-jsToolBar.strings['Italic'] = 'Kurziv';
-jsToolBar.strings['Underline'] = 'Podcrtano';
-jsToolBar.strings['Deleted'] = 'Obrisano';
-jsToolBar.strings['Code'] = 'Inline Code';
-jsToolBar.strings['Heading 1'] = 'Naslov 1';
-jsToolBar.strings['Heading 2'] = 'Naslov 2';
-jsToolBar.strings['Heading 3'] = 'Naslov 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Graficke oznake';
-jsToolBar.strings['Ordered list'] = 'Numeriranje';
-jsToolBar.strings['Quote'] = 'Citat';
-jsToolBar.strings['Unquote'] = 'Ukloni citat';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Izveden tekst';
-jsToolBar.strings['Wiki link'] = 'Link na Wiki stranicu';
-jsToolBar.strings['Image'] = 'Slika';
-jsToolBar.strings['Edit'] = 'Uredi';
-jsToolBar.strings['Preview'] = 'Brzi pregled';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-hu.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-hu.js
deleted file mode 100644
index aec1e653e..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-hu.js
+++ /dev/null
@@ -1,20 +0,0 @@
-﻿jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Félkövér';
-jsToolBar.strings['Italic'] = 'Dőlt';
-jsToolBar.strings['Underline'] = 'Aláhúzott';
-jsToolBar.strings['Deleted'] = 'Törölt';
-jsToolBar.strings['Code'] = 'Kód sorok';
-jsToolBar.strings['Heading 1'] = 'Fejléc 1';
-jsToolBar.strings['Heading 2'] = 'Fejléc 2';
-jsToolBar.strings['Heading 3'] = 'Fejléc 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Felsorolás';
-jsToolBar.strings['Ordered list'] = 'Számozott lista';
-jsToolBar.strings['Quote'] = 'Quote';
-jsToolBar.strings['Unquote'] = 'Idézet eltávolítása';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Előreformázott szöveg';
-jsToolBar.strings['Wiki link'] = 'Link egy Wiki oldalra';
-jsToolBar.strings['Image'] = 'Kép';
-jsToolBar.strings['Edit'] = 'Szerkeszt';
-jsToolBar.strings['Preview'] = 'Előnézet';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-id.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-id.js
deleted file mode 100644
index ca5c71167..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-id.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Tebal';
-jsToolBar.strings['Italic'] = 'Miring';
-jsToolBar.strings['Underline'] = 'Garis bawah';
-jsToolBar.strings['Deleted'] = 'Dihapus';
-jsToolBar.strings['Code'] = 'Inline Code';
-jsToolBar.strings['Heading 1'] = 'Judul 1';
-jsToolBar.strings['Heading 2'] = 'Judul 2';
-jsToolBar.strings['Heading 3'] = 'Judul 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Daftar tak terurut';
-jsToolBar.strings['Ordered list'] = 'Daftar terurut';
-jsToolBar.strings['Quote'] = 'Kutipan';
-jsToolBar.strings['Unquote'] = 'Hapus kutipan';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Teks terformat';
-jsToolBar.strings['Wiki link'] = 'Tautkan ke halaman wiki';
-jsToolBar.strings['Image'] = 'Gambar';
-jsToolBar.strings['Edit'] = 'Sunting';
-jsToolBar.strings['Preview'] = 'Tinjauan';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-it.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-it.js
deleted file mode 100644
index 13ade13b2..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-it.js
+++ /dev/null
@@ -1,23 +0,0 @@
-// Italian translation
-// by Diego Pierotto (ita.translations@tiscali.it)
-
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Grassetto';
-jsToolBar.strings['Italic'] = 'Corsivo';
-jsToolBar.strings['Underline'] = 'Sottolineato';
-jsToolBar.strings['Deleted'] = 'Barrato';
-jsToolBar.strings['Code'] = 'Codice sorgente';
-jsToolBar.strings['Heading 1'] = 'Titolo 1';
-jsToolBar.strings['Heading 2'] = 'Titolo 2';
-jsToolBar.strings['Heading 3'] = 'Titolo 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Elenco puntato';
-jsToolBar.strings['Ordered list'] = 'Elenco numerato';
-jsToolBar.strings['Quote'] = 'Aumenta rientro';
-jsToolBar.strings['Unquote'] = 'Riduci rientro';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Testo preformattato';
-jsToolBar.strings['Wiki link'] = 'Collegamento a pagina Wiki';
-jsToolBar.strings['Image'] = 'Immagine';
-jsToolBar.strings['Edit'] = 'Modifica';
-jsToolBar.strings['Preview'] = 'Anteprima';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ja.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-ja.js
deleted file mode 100644
index 71a9c4313..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ja.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = '強調';
-jsToolBar.strings['Italic'] = '斜体';
-jsToolBar.strings['Underline'] = '下線';
-jsToolBar.strings['Deleted'] = '取り消し線';
-jsToolBar.strings['Code'] = 'コード';
-jsToolBar.strings['Heading 1'] = '見出し 1';
-jsToolBar.strings['Heading 2'] = '見出し 2';
-jsToolBar.strings['Heading 3'] = '見出し 3';
-jsToolBar.strings['Highlighted code'] = 'コードハイライト';
-jsToolBar.strings['Unordered list'] = '順不同リスト';
-jsToolBar.strings['Ordered list'] = '番号つきリスト';
-jsToolBar.strings['Quote'] = '引用';
-jsToolBar.strings['Unquote'] = '引用解除';
-jsToolBar.strings['Table'] = 'テーブル';
-jsToolBar.strings['Preformatted text'] = '整形済みテキスト';
-jsToolBar.strings['Wiki link'] = 'Wikiページへのリンク';
-jsToolBar.strings['Image'] = '画像';
-jsToolBar.strings['Edit'] = '編集';
-jsToolBar.strings['Preview'] = 'プレビュー';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ko.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-ko.js
deleted file mode 100644
index 601a83a84..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ko.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = '굵게';
-jsToolBar.strings['Italic'] = '기울임';
-jsToolBar.strings['Underline'] = '밑줄';
-jsToolBar.strings['Deleted'] = '취소선';
-jsToolBar.strings['Code'] = '코드';
-jsToolBar.strings['Heading 1'] = '제목 1';
-jsToolBar.strings['Heading 2'] = '제목 2';
-jsToolBar.strings['Heading 3'] = '제목 3';
-jsToolBar.strings['Highlighted code'] = '색상화한 코드';
-jsToolBar.strings['Unordered list'] = '글머리 기호';
-jsToolBar.strings['Ordered list'] = '번호 매기기';
-jsToolBar.strings['Quote'] = '인용';
-jsToolBar.strings['Unquote'] = '인용 취소';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = '있는 그대로 표현 (Preformatted text)';
-jsToolBar.strings['Wiki link'] = 'Wiki 페이지에 연결';
-jsToolBar.strings['Image'] = '그림';
-jsToolBar.strings['Edit'] = '편집';
-jsToolBar.strings['Preview'] = '미리보기';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-lt.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-lt.js
deleted file mode 100644
index d77b7ea08..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-lt.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Pastorinti';
-jsToolBar.strings['Italic'] = 'Kursyvu';
-jsToolBar.strings['Underline'] = 'Pabraukti';
-jsToolBar.strings['Deleted'] = 'Užbraukti';
-jsToolBar.strings['Code'] = 'Kodas';
-jsToolBar.strings['Heading 1'] = 'Antraštė 1';
-jsToolBar.strings['Heading 2'] = 'Antraštė 2';
-jsToolBar.strings['Heading 3'] = 'Antraštė 3';
-jsToolBar.strings['Highlighted code'] = 'Paryškintas kodas';
-jsToolBar.strings['Unordered list'] = 'Nenumeruotas sąrašas';
-jsToolBar.strings['Ordered list'] = 'Numeruotas sąrašas';
-jsToolBar.strings['Quote'] = 'Cituoti';
-jsToolBar.strings['Unquote'] = 'Pašalinti citavimą';
-jsToolBar.strings['Table'] = 'Lentelė';
-jsToolBar.strings['Preformatted text'] = 'Preformatuotas tekstas';
-jsToolBar.strings['Wiki link'] = 'Nuoroda į Wiki puslapį';
-jsToolBar.strings['Image'] = 'Paveikslėlis';
-jsToolBar.strings['Edit'] = 'Redaguoti';
-jsToolBar.strings['Preview'] = 'Peržiūra';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-lv.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-lv.js
deleted file mode 100644
index 5e43d128a..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-lv.js
+++ /dev/null
@@ -1,21 +0,0 @@
-// translated by Dzintars Bergs (dzintars.bergs@gmail.com)
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Treknraksts';
-jsToolBar.strings['Italic'] = 'Slīpraksts';
-jsToolBar.strings['Underline'] = 'Pasvītrojums';
-jsToolBar.strings['Deleted'] = 'Dzēsts';
-jsToolBar.strings['Code'] = 'Iekļauts kods';
-jsToolBar.strings['Heading 1'] = 'Virsraksts 1';
-jsToolBar.strings['Heading 2'] = 'Virsraksts 2';
-jsToolBar.strings['Heading 3'] = 'Virsraksts 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Nesakārtots saraksts';
-jsToolBar.strings['Ordered list'] = 'Sakārtots saraksts';
-jsToolBar.strings['Quote'] = 'Citēt';
-jsToolBar.strings['Unquote'] = 'Noņemt citātu';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Iepriekš formatēts teksts';
-jsToolBar.strings['Wiki link'] = 'Saite uz Wiki lapu';
-jsToolBar.strings['Image'] = 'Attēls';
-jsToolBar.strings['Edit'] = 'Labot';
-jsToolBar.strings['Preview'] = 'Priekšskatījums';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-mk.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-mk.js
deleted file mode 100644
index 01e8b7a51..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-mk.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Задебелен';
-jsToolBar.strings['Italic'] = 'Закосен';
-jsToolBar.strings['Underline'] = 'Подвлечен';
-jsToolBar.strings['Deleted'] = 'Прецртан';
-jsToolBar.strings['Code'] = 'Код';
-jsToolBar.strings['Heading 1'] = 'Заглавје 1';
-jsToolBar.strings['Heading 2'] = 'Заглавје 2';
-jsToolBar.strings['Heading 3'] = 'Заглавје 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Неподредена листа';
-jsToolBar.strings['Ordered list'] = 'Подредена листа';
-jsToolBar.strings['Quote'] = 'Цитат';
-jsToolBar.strings['Unquote'] = 'Отстрани цитат';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Форматиран текст';
-jsToolBar.strings['Wiki link'] = 'Врска до вики страна';
-jsToolBar.strings['Image'] = 'Слика';
-jsToolBar.strings['Edit'] = 'Уреди';
-jsToolBar.strings['Preview'] = 'Preview';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-mn.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-mn.js
deleted file mode 100644
index 82e80439b..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-mn.js
+++ /dev/null
@@ -1,20 +0,0 @@
-﻿jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Бүдүүн';
-jsToolBar.strings['Italic'] = 'Налуу';
-jsToolBar.strings['Underline'] = 'Доогуур зураас';
-jsToolBar.strings['Deleted'] = 'Устгагдсан';
-jsToolBar.strings['Code'] = 'Програмын код';
-jsToolBar.strings['Heading 1'] = 'Гарчиг 1';
-jsToolBar.strings['Heading 2'] = 'Гарчиг 2';
-jsToolBar.strings['Heading 3'] = 'Гарчиг 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Эрэмбэгүй жагсаалт';
-jsToolBar.strings['Ordered list'] = 'Эрэмбэтэй жагсаалт';
-jsToolBar.strings['Quote'] = 'Ишлэл';
-jsToolBar.strings['Unquote'] = 'Ишлэлийг устгах';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Өмнө нь хэлбэржсэн текст';
-jsToolBar.strings['Wiki link'] = 'Вики хуудас руу холбох';
-jsToolBar.strings['Image'] = 'Зураг';
-jsToolBar.strings['Edit'] = 'Засварлах';
-jsToolBar.strings['Preview'] = 'Ямар харагдахыг шалгах';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-nl.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-nl.js
deleted file mode 100644
index e28e8a144..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-nl.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Vetgedrukt';
-jsToolBar.strings['Italic'] = 'Cursief';
-jsToolBar.strings['Underline'] = 'Onderstreept';
-jsToolBar.strings['Deleted'] = 'Verwijderd';
-jsToolBar.strings['Code'] = 'Computercode';
-jsToolBar.strings['Heading 1'] = 'Hoofding 1';
-jsToolBar.strings['Heading 2'] = 'Hoofding 2';
-jsToolBar.strings['Heading 3'] = 'Hoofding 3';
-jsToolBar.strings['Highlighted code'] = 'Gemarkeerde code';
-jsToolBar.strings['Unordered list'] = 'Ongeordende lijst';
-jsToolBar.strings['Ordered list'] = 'Geordende lijst';
-jsToolBar.strings['Quote'] = 'Citaat';
-jsToolBar.strings['Unquote'] = 'Citaat verwijderen';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Vooropgemaakte tekst';
-jsToolBar.strings['Wiki link'] = 'Link naar een Wikipagina';
-jsToolBar.strings['Image'] = 'Afbeelding';
-jsToolBar.strings['Edit'] = 'Bewerken';
-jsToolBar.strings['Preview'] = 'Voorbeeldweergave';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-no.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-no.js
deleted file mode 100644
index 14f587922..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-no.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Fet';
-jsToolBar.strings['Italic'] = 'Kursiv';
-jsToolBar.strings['Underline'] = 'Understreking';
-jsToolBar.strings['Deleted'] = 'Slettet';
-jsToolBar.strings['Code'] = 'Kode';
-jsToolBar.strings['Heading 1'] = 'Overskrift 1';
-jsToolBar.strings['Heading 2'] = 'Overskrift 2';
-jsToolBar.strings['Heading 3'] = 'Overskrift 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Punktliste';
-jsToolBar.strings['Ordered list'] = 'Nummerert liste';
-jsToolBar.strings['Quote'] = 'Sitat';
-jsToolBar.strings['Unquote'] = 'Avslutt sitat';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Preformatert tekst';
-jsToolBar.strings['Wiki link'] = 'Lenke til Wiki-side';
-jsToolBar.strings['Image'] = 'Bilde';
-jsToolBar.strings['Edit'] = 'Endre';
-jsToolBar.strings['Preview'] = 'Forhåndsvis';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-pl.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-pl.js
deleted file mode 100644
index 28ecce816..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-pl.js
+++ /dev/null
@@ -1,21 +0,0 @@
-﻿// Keep this line in order to avoid problems with Windows Notepad UTF-8 EF-BB-BF idea...
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Pogrubienie';
-jsToolBar.strings['Italic'] = 'Kursywa';
-jsToolBar.strings['Underline'] = 'Podkreślenie';
-jsToolBar.strings['Deleted'] = 'Usunięte';
-jsToolBar.strings['Code'] = 'Kod';
-jsToolBar.strings['Heading 1'] = 'Nagłowek 1';
-jsToolBar.strings['Heading 2'] = 'Nagłówek 2';
-jsToolBar.strings['Heading 3'] = 'Nagłówek 3';
-jsToolBar.strings['Highlighted code'] = 'Kod z podświetlaniem składni';
-jsToolBar.strings['Unordered list'] = 'Nieposortowana lista';
-jsToolBar.strings['Ordered list'] = 'Posortowana lista';
-jsToolBar.strings['Quote'] = 'Cytat';
-jsToolBar.strings['Unquote'] = 'Usuń cytat';
-jsToolBar.strings['Table'] = 'Tabela';
-jsToolBar.strings['Preformatted text'] = 'Sformatowany tekst';
-jsToolBar.strings['Wiki link'] = 'Odnośnik do strony Wiki';
-jsToolBar.strings['Image'] = 'Obraz';
-jsToolBar.strings['Edit'] = 'Edytuj';
-jsToolBar.strings['Preview'] = 'Podgląd';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-pt.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-pt.js
deleted file mode 100644
index e296c8367..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-pt.js
+++ /dev/null
@@ -1,21 +0,0 @@
-// Translated by: Pedro Araújo <phcrva19@hotmail.com>
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Negrito';
-jsToolBar.strings['Italic'] = 'Itálico';
-jsToolBar.strings['Underline'] = 'Sublinhado';
-jsToolBar.strings['Deleted'] = 'Apagado';
-jsToolBar.strings['Code'] = 'Código Inline';
-jsToolBar.strings['Heading 1'] = 'Cabeçalho 1';
-jsToolBar.strings['Heading 2'] = 'Cabeçalho 2';
-jsToolBar.strings['Heading 3'] = 'Cabeçalho 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Lista não ordenada';
-jsToolBar.strings['Ordered list'] = 'Lista ordenada';
-jsToolBar.strings['Quote'] = 'Citação';
-jsToolBar.strings['Unquote'] = 'Remover citação';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Texto pré-formatado';
-jsToolBar.strings['Wiki link'] = 'Link para uma página da Wiki';
-jsToolBar.strings['Image'] = 'Imagem';
-jsToolBar.strings['Edit'] = 'Editar';
-jsToolBar.strings['Preview'] = 'Pré-visualizar';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ro.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-ro.js
deleted file mode 100644
index 8cdcad11d..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ro.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Bold';
-jsToolBar.strings['Italic'] = 'Italic';
-jsToolBar.strings['Underline'] = 'Subliniat';
-jsToolBar.strings['Deleted'] = 'Șters';
-jsToolBar.strings['Code'] = 'Fragment de cod';
-jsToolBar.strings['Heading 1'] = 'Heading 1';
-jsToolBar.strings['Heading 2'] = 'Heading 2';
-jsToolBar.strings['Heading 3'] = 'Heading 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Listă pe puncte';
-jsToolBar.strings['Ordered list'] = 'Listă ordonată';
-jsToolBar.strings['Quote'] = 'Citează';
-jsToolBar.strings['Unquote'] = 'Fără citat';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Text preformatat';
-jsToolBar.strings['Wiki link'] = 'Trimitere către o pagină wiki';
-jsToolBar.strings['Image'] = 'Imagine';
-jsToolBar.strings['Edit'] = 'Editează';
-jsToolBar.strings['Preview'] = 'Previzualizare';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ru.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-ru.js
deleted file mode 100644
index 8a6ef02a4..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ru.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Жирный';
-jsToolBar.strings['Italic'] = 'Курсив';
-jsToolBar.strings['Underline'] = 'Подчеркнутый';
-jsToolBar.strings['Deleted'] = 'Зачеркнутый';
-jsToolBar.strings['Code'] = 'Вставка кода';
-jsToolBar.strings['Heading 1'] = 'Заголовок 1';
-jsToolBar.strings['Heading 2'] = 'Заголовок 2';
-jsToolBar.strings['Heading 3'] = 'Заголовок 3';
-jsToolBar.strings['Highlighted code'] = 'Подсветка кода';
-jsToolBar.strings['Unordered list'] = 'Маркированный список';
-jsToolBar.strings['Ordered list'] = 'Нумерованный список';
-jsToolBar.strings['Quote'] = 'Цитата';
-jsToolBar.strings['Unquote'] = 'Удалить цитату';
-jsToolBar.strings['Table'] = 'Таблица';
-jsToolBar.strings['Preformatted text'] = 'Заранее форматированный текст';
-jsToolBar.strings['Wiki link'] = 'Ссылка на страницу в Wiki';
-jsToolBar.strings['Image'] = 'Вставка изображения';
-jsToolBar.strings['Edit'] = 'Редактирование';
-jsToolBar.strings['Preview'] = 'Предпросмотр';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sk.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-sk.js
deleted file mode 100644
index 25b1c1e2a..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sk.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Tučné';
-jsToolBar.strings['Italic'] = 'Kurzíva';
-jsToolBar.strings['Underline'] = 'Podčiarknuté';
-jsToolBar.strings['Deleted'] = 'Preškrtnuté';
-jsToolBar.strings['Code'] = 'Zobrazenie kódu';
-jsToolBar.strings['Heading 1'] = 'Nadpis 1';
-jsToolBar.strings['Heading 2'] = 'Nadpis 2';
-jsToolBar.strings['Heading 3'] = 'Nadpis 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Odrážkový zoznam';
-jsToolBar.strings['Ordered list'] = 'Číslovaný zoznam';
-jsToolBar.strings['Quote'] = 'Odsadenie';
-jsToolBar.strings['Unquote'] = 'Zrušiť odsadenie';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Predformátovaný text';
-jsToolBar.strings['Wiki link'] = 'Odkaz na wikistránku';
-jsToolBar.strings['Image'] = 'Obrázok';
-jsToolBar.strings['Edit'] = 'Upraviť';
-jsToolBar.strings['Preview'] = 'Náhľad';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sl.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-sl.js
deleted file mode 100644
index 090a55a9f..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sl.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Krepko';
-jsToolBar.strings['Italic'] = 'Poševno';
-jsToolBar.strings['Underline'] = 'Podčrtano';
-jsToolBar.strings['Deleted'] = 'Izbrisano';
-jsToolBar.strings['Code'] = 'Koda med vrsticami';
-jsToolBar.strings['Heading 1'] = 'Naslov 1';
-jsToolBar.strings['Heading 2'] = 'Naslov 2';
-jsToolBar.strings['Heading 3'] = 'Naslov 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Neurejen seznam';
-jsToolBar.strings['Ordered list'] = 'Urejen seznam';
-jsToolBar.strings['Quote'] = 'Citat';
-jsToolBar.strings['Unquote'] = 'Odstrani citat';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Predoblikovano besedilo';
-jsToolBar.strings['Wiki link'] = 'Povezava na Wiki stran';
-jsToolBar.strings['Image'] = 'Slika';
-jsToolBar.strings['Edit'] = 'Uredi';
-jsToolBar.strings['Preview'] = 'Predogled';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sq.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-sq.js
deleted file mode 100644
index e4b456a45..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sq.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Strong';
-jsToolBar.strings['Italic'] = 'Italic';
-jsToolBar.strings['Underline'] = 'Underline';
-jsToolBar.strings['Deleted'] = 'Deleted';
-jsToolBar.strings['Code'] = 'Inline Code';
-jsToolBar.strings['Heading 1'] = 'Heading 1';
-jsToolBar.strings['Heading 2'] = 'Heading 2';
-jsToolBar.strings['Heading 3'] = 'Heading 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Unordered list';
-jsToolBar.strings['Ordered list'] = 'Ordered list';
-jsToolBar.strings['Quote'] = 'Quote';
-jsToolBar.strings['Unquote'] = 'Remove Quote';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Preformatted text';
-jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
-jsToolBar.strings['Image'] = 'Image';
-jsToolBar.strings['Edit'] = 'Edit';
-jsToolBar.strings['Preview'] = 'Preview';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sr-yu.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-sr-yu.js
deleted file mode 100644
index 94d993d5a..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sr-yu.js
+++ /dev/null
@@ -1,20 +0,0 @@
-﻿jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Podebljano';
-jsToolBar.strings['Italic'] = 'Kurziv';
-jsToolBar.strings['Underline'] = 'Podvučeno';
-jsToolBar.strings['Deleted'] = 'Obrisano';
-jsToolBar.strings['Code'] = 'Ugrađeni kôd';
-jsToolBar.strings['Heading 1'] = 'Naslov 1';
-jsToolBar.strings['Heading 2'] = 'Naslov 2';
-jsToolBar.strings['Heading 3'] = 'Naslov 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Lista nabrajanja';
-jsToolBar.strings['Ordered list'] = 'Uređena lista';
-jsToolBar.strings['Quote'] = 'Pod navodnicima';
-jsToolBar.strings['Unquote'] = 'Ukloni navodnike';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Prethodno formatiran tekst';
-jsToolBar.strings['Wiki link'] = 'Veza prema Wiki strani';
-jsToolBar.strings['Image'] = 'Slika';
-jsToolBar.strings['Edit'] = 'Izmeni';
-jsToolBar.strings['Preview'] = 'Pregled';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sr.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-sr.js
deleted file mode 100644
index ab4ae0245..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sr.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Подебљано';
-jsToolBar.strings['Italic'] = 'Курзив';
-jsToolBar.strings['Underline'] = 'Подвучено';
-jsToolBar.strings['Deleted'] = 'Обрисано';
-jsToolBar.strings['Code'] = 'Уграђени кôд';
-jsToolBar.strings['Heading 1'] = 'Наслов 1';
-jsToolBar.strings['Heading 2'] = 'Наслов 2';
-jsToolBar.strings['Heading 3'] = 'Наслов 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Листа набрајања';
-jsToolBar.strings['Ordered list'] = 'Уређена листа';
-jsToolBar.strings['Quote'] = 'Под наводницима';
-jsToolBar.strings['Unquote'] = 'Уклони наводнике';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Претходно форматиран текст';
-jsToolBar.strings['Wiki link'] = 'Веза према Wiki страни';
-jsToolBar.strings['Image'] = 'Слика';
-jsToolBar.strings['Edit'] = 'Измени';
-jsToolBar.strings['Preview'] = 'Преглед';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sv.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-sv.js
deleted file mode 100644
index 7ef6c75cd..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-sv.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Fet';
-jsToolBar.strings['Italic'] = 'Kursiv';
-jsToolBar.strings['Underline'] = 'Understruken';
-jsToolBar.strings['Deleted'] = 'Genomstruken';
-jsToolBar.strings['Code'] = 'Kod';
-jsToolBar.strings['Heading 1'] = 'Rubrik 1';
-jsToolBar.strings['Heading 2'] = 'Rubrik 2';
-jsToolBar.strings['Heading 3'] = 'Rubrik 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Osorterad lista';
-jsToolBar.strings['Ordered list'] = 'Sorterad lista';
-jsToolBar.strings['Quote'] = 'Citat';
-jsToolBar.strings['Unquote'] = 'Ta bort citat';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Förformaterad text';
-jsToolBar.strings['Wiki link'] = 'Länk till en wikisida';
-jsToolBar.strings['Image'] = 'Bild';
-jsToolBar.strings['Edit'] = 'Ändra';
-jsToolBar.strings['Preview'] = 'Förhandsgranska';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ta-in.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-ta-in.js
deleted file mode 100644
index 533586a00..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-ta-in.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'வலுவான';
-jsToolBar.strings['Italic'] = 'சாய்வு';
-jsToolBar.strings['Underline'] = 'அடிக்கோடிட்டு';
-jsToolBar.strings['Deleted'] = 'நீக்கப்பட்டது';
-jsToolBar.strings['Code'] = 'இன்லைன் குறியீடு';
-jsToolBar.strings['Heading 1'] = 'தலைப்பு 1';
-jsToolBar.strings['Heading 2'] = 'தலைப்பு 2';
-jsToolBar.strings['Heading 3'] = 'தலைப்பு 3';
-jsToolBar.strings['Highlighted code'] = 'சிறப்பம்சமாக குறியீடு';
-jsToolBar.strings['Unordered list'] = 'வரிசைப்படுத்தப்படாத பட்டியல்';
-jsToolBar.strings['Ordered list'] = 'வரிசைப்படுத்தப்பட்ட பட்டியல்';
-jsToolBar.strings['Quote'] = 'மேற்கோள்';
-jsToolBar.strings['Unquote'] = 'மேற்கோளை அகற்று';
-jsToolBar.strings['Table'] = 'அட்டவணை';
-jsToolBar.strings['Preformatted text'] = 'முன்பே வடிவமைக்கப்பட்ட உரை';
-jsToolBar.strings['Wiki link'] = 'விக்கி பக்கத்துடன் இணைக்கவும்';
-jsToolBar.strings['Image'] = 'படம்';
-jsToolBar.strings['Edit'] = 'திருத்து';
-jsToolBar.strings['Preview'] = 'முன்னோட்டம்';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-th.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-th.js
deleted file mode 100644
index 1969ce0c4..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-th.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'หนา';
-jsToolBar.strings['Italic'] = 'เอียง';
-jsToolBar.strings['Underline'] = 'ขีดเส้นใต้';
-jsToolBar.strings['Deleted'] = 'ขีดฆ่า';
-jsToolBar.strings['Code'] = 'โค๊ดโปรแกรม';
-jsToolBar.strings['Heading 1'] = 'หัวข้อ 1';
-jsToolBar.strings['Heading 2'] = 'หัวข้อ 2';
-jsToolBar.strings['Heading 3'] = 'หัวข้อ 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'รายการ';
-jsToolBar.strings['Ordered list'] = 'ลำดับเลข';
-jsToolBar.strings['Quote'] = 'Quote';
-jsToolBar.strings['Unquote'] = 'Remove Quote';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'รูปแบบข้อความคงที่';
-jsToolBar.strings['Wiki link'] = 'เชื่อมโยงไปหน้า Wiki อื่น';
-jsToolBar.strings['Image'] = 'รูปภาพ';
-jsToolBar.strings['Edit'] = 'แก้ไข';
-jsToolBar.strings['Preview'] = 'ตัวอย่างก่อนจัดเก็บ';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-tr.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-tr.js
deleted file mode 100644
index 6d15a9c3e..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-tr.js
+++ /dev/null
@@ -1,18 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Kalın';
-jsToolBar.strings['Italic'] = 'İtalik';
-jsToolBar.strings['Underline'] = 'Altı çizgili';
-jsToolBar.strings['Deleted'] = 'Silinmiş';
-jsToolBar.strings['Code'] = 'Satır içi kod';
-jsToolBar.strings['Heading 1'] = 'Başlık 1';
-jsToolBar.strings['Heading 2'] = 'Başlık 2';
-jsToolBar.strings['Heading 3'] = 'Başlık 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Sırasız liste';
-jsToolBar.strings['Ordered list'] = 'Sıralı liste';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Preformatted text';
-jsToolBar.strings['Wiki link'] = 'Wiki sayfasına bağlantı';
-jsToolBar.strings['Image'] = 'Resim';
-jsToolBar.strings['Edit'] = 'Düzenle';
-jsToolBar.strings['Preview'] = 'Önizleme';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-uk.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-uk.js
deleted file mode 100644
index 55ca7c568..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-uk.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Жирний';
-jsToolBar.strings['Italic'] = 'Курсив';
-jsToolBar.strings['Underline'] = 'Підкреслений';
-jsToolBar.strings['Deleted'] = 'Закреслений';
-jsToolBar.strings['Code'] = 'Інлайн код';
-jsToolBar.strings['Heading 1'] = 'Заголовок 1';
-jsToolBar.strings['Heading 2'] = 'Заголовок 2';
-jsToolBar.strings['Heading 3'] = 'Заголовок 3';
-jsToolBar.strings['Highlighted code'] = 'Виділений код';
-jsToolBar.strings['Unordered list'] = 'Ненумерованний список';
-jsToolBar.strings['Ordered list'] = 'Нумерований список';
-jsToolBar.strings['Quote'] = 'Цитування';
-jsToolBar.strings['Unquote'] = 'Видалити цитування';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Попередньо відформатований текст';
-jsToolBar.strings['Wiki link'] = 'Посилання на сторінку Wiki';
-jsToolBar.strings['Image'] = 'Зображення';
-jsToolBar.strings['Edit'] = 'Редагувати';
-jsToolBar.strings['Preview'] = 'Попередній перегляд';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-vi.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-vi.js
deleted file mode 100644
index 30f74b69d..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-vi.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = 'Đậm';
-jsToolBar.strings['Italic'] = 'Nghiêng';
-jsToolBar.strings['Underline'] = 'Gạch chân';
-jsToolBar.strings['Deleted'] = 'Xóa';
-jsToolBar.strings['Code'] = 'Mã chung dòng';
-jsToolBar.strings['Heading 1'] = 'Tiêu đề 1';
-jsToolBar.strings['Heading 2'] = 'Tiêu đề 2';
-jsToolBar.strings['Heading 3'] = 'Tiêu đề 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = 'Danh sách không thứ tự';
-jsToolBar.strings['Ordered list'] = 'Danh sách có thứ tự';
-jsToolBar.strings['Quote'] = 'Trích dẫn';
-jsToolBar.strings['Unquote'] = 'Bỏ trích dẫn';
-jsToolBar.strings['Table'] = 'Table';
-jsToolBar.strings['Preformatted text'] = 'Mã nguồn';
-jsToolBar.strings['Wiki link'] = 'Liên kết đến trang wiki';
-jsToolBar.strings['Image'] = 'Ảnh';
-jsToolBar.strings['Edit'] = 'Sửa';
-jsToolBar.strings['Preview'] = 'Xem trước';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-zh-tw.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-zh-tw.js
deleted file mode 100644
index 98bde9219..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-zh-tw.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = '粗體';
-jsToolBar.strings['Italic'] = '斜體';
-jsToolBar.strings['Underline'] = '底線';
-jsToolBar.strings['Deleted'] = '刪除線';
-jsToolBar.strings['Code'] = '程式碼';
-jsToolBar.strings['Heading 1'] = '標題 1';
-jsToolBar.strings['Heading 2'] = '標題 2';
-jsToolBar.strings['Heading 3'] = '標題 3';
-jsToolBar.strings['Highlighted code'] = '反白程式碼';
-jsToolBar.strings['Unordered list'] = '項目清單';
-jsToolBar.strings['Ordered list'] = '編號清單';
-jsToolBar.strings['Quote'] = '引文';
-jsToolBar.strings['Unquote'] = '取消引文';
-jsToolBar.strings['Table'] = '表格';
-jsToolBar.strings['Preformatted text'] = '已格式文字';
-jsToolBar.strings['Wiki link'] = '連結至 Wiki 頁面';
-jsToolBar.strings['Image'] = '圖片';
-jsToolBar.strings['Edit'] = '編輯';
-jsToolBar.strings['Preview'] = '預覽';
diff --git a/app/assets/javascripts/jstoolbar/lang/jstoolbar-zh.js b/app/assets/javascripts/jstoolbar/lang/jstoolbar-zh.js
deleted file mode 100644
index 335270480..000000000
--- a/app/assets/javascripts/jstoolbar/lang/jstoolbar-zh.js
+++ /dev/null
@@ -1,20 +0,0 @@
-jsToolBar.strings = {};
-jsToolBar.strings['Strong'] = '粗体';
-jsToolBar.strings['Italic'] = '斜体';
-jsToolBar.strings['Underline'] = '下划线';
-jsToolBar.strings['Deleted'] = '删除线';
-jsToolBar.strings['Code'] = '程序代码';
-jsToolBar.strings['Heading 1'] = '标题 1';
-jsToolBar.strings['Heading 2'] = '标题 2';
-jsToolBar.strings['Heading 3'] = '标题 3';
-jsToolBar.strings['Highlighted code'] = 'Highlighted code';
-jsToolBar.strings['Unordered list'] = '无序列表';
-jsToolBar.strings['Ordered list'] = '排序列表';
-jsToolBar.strings['Quote'] = '引用';
-jsToolBar.strings['Unquote'] = '删除引用';
-jsToolBar.strings['Table'] = '表格';
-jsToolBar.strings['Preformatted text'] = '格式化文本';
-jsToolBar.strings['Wiki link'] = '连接到 Wiki 页面';
-jsToolBar.strings['Image'] = '图片';
-jsToolBar.strings['Edit'] = '编辑';
-jsToolBar.strings['Preview'] = '预览';
diff --git a/app/assets/javascripts/jstoolbar/textile.js b/app/assets/javascripts/jstoolbar/textile.js
deleted file mode 100644
index 6bc68382c..000000000
--- a/app/assets/javascripts/jstoolbar/textile.js
+++ /dev/null
@@ -1,229 +0,0 @@
-/**
- * This file is part of DotClear.
- * Copyright (c) 2005 Nicolas Martin & Olivier Meunier and contributors. All rights reserved.
- * This code is released under the GNU General Public License.
- *
- * Modified by JP LANG for textile formatting
- */
-
-// strong
-jsToolBar.prototype.elements.strong = {
-  type: 'button',
-  title: 'Strong',
-  shortcut: 'b',
-  fn: {
-    wiki: function() { this.singleTag('*') }
-  }
-}
-
-// em
-jsToolBar.prototype.elements.em = {
-  type: 'button',
-  title: 'Italic',
-  shortcut: 'i',
-  fn: {
-    wiki: function() { this.singleTag("_") }
-  }
-}
-
-// ins
-jsToolBar.prototype.elements.ins = {
-  type: 'button',
-  title: 'Underline',
-  shortcut: 'u',
-  fn: {
-    wiki: function() { this.singleTag('+') }
-  }
-}
-
-// del
-jsToolBar.prototype.elements.del = {
-  type: 'button',
-  title: 'Deleted',
-  fn: {
-    wiki: function() { this.singleTag('-') }
-  }
-}
-
-// code
-jsToolBar.prototype.elements.code = {
-  type: 'button',
-  title: 'Code',
-  fn: {
-    wiki: function() { this.singleTag('@') }
-  }
-}
-
-// spacer
-jsToolBar.prototype.elements.space1 = {type: 'space'}
-
-// headings
-jsToolBar.prototype.elements.h1 = {
-  type: 'button',
-  title: 'Heading 1',
-  fn: {
-    wiki: function() {
-      this.encloseLineSelection('h1. ', '',function(str) {
-        str = str.replace(/^h\d+\.\s+/, '')
-        return str;
-      });
-    }
-  }
-}
-jsToolBar.prototype.elements.h2 = {
-  type: 'button',
-  title: 'Heading 2',
-  fn: {
-    wiki: function() {
-      this.encloseLineSelection('h2. ', '',function(str) {
-        str = str.replace(/^h\d+\.\s+/, '')
-        return str;
-      });
-    }
-  }
-}
-jsToolBar.prototype.elements.h3 = {
-  type: 'button',
-  title: 'Heading 3',
-  fn: {
-    wiki: function() {
-      this.encloseLineSelection('h3. ', '',function(str) {
-        str = str.replace(/^h\d+\.\s+/, '')
-        return str;
-      });
-    }
-  }
-}
-
-// spacer
-jsToolBar.prototype.elements.space2 = {type: 'space'}
-
-// ul
-jsToolBar.prototype.elements.ul = {
-  type: 'button',
-  title: 'Unordered list',
-  fn: {
-    wiki: function() {
-      this.encloseLineSelection('','',function(str) {
-        str = str.replace(/\r/g,'');
-        return str.replace(/(\n|^)[#-]?\s*/g,"$1* ");
-      });
-    }
-  }
-}
-
-// ol
-jsToolBar.prototype.elements.ol = {
-  type: 'button',
-  title: 'Ordered list',
-  fn: {
-    wiki: function() {
-      this.encloseLineSelection('','',function(str) {
-        str = str.replace(/\r/g,'');
-        return str.replace(/(\n|^)[*-]?\s*/g,"$1# ");
-      });
-    }
-  }
-}
-
-// spacer
-jsToolBar.prototype.elements.space3 = {type: 'space'}
-
-// bq
-jsToolBar.prototype.elements.bq = {
-  type: 'button',
-  title: 'Quote',
-  fn: {
-    wiki: function() {
-      this.encloseLineSelection('','',function(str) {
-        str = str.replace(/\r/g,'');
-        return str.replace(/(\n|^)( *)([^\n]*)/g,"$1> $2$3");
-      });
-    }
-  }
-}
-
-// unbq
-jsToolBar.prototype.elements.unbq = {
-  type: 'button',
-  title: 'Unquote',
-  fn: {
-    wiki: function() {
-      this.encloseLineSelection('','',function(str) {
-        str = str.replace(/\r/g,'');
-        return str.replace(/(\n|^) *(> ?)?( *)([^\n]*)/g,"$1$3$4");
-      });
-    }
-  }
-}
-
-// table
-jsToolBar.prototype.elements.table = {
-  type: 'button',
-  title: 'Table',
-  fn: {
-    wiki: function() {
-      var This = this;
-      this.tableMenu(function(cols, rowCount){
-        This.encloseLineSelection(
-          '|_.'+cols.join('|_.')+'|\n' +                                 // header
-          Array(rowCount+1).join(Array(cols.length+1).join('|  ')+'|\n') // cells
-        );
-      });
-    }
-  }
-}
-
-// pre
-jsToolBar.prototype.elements.pre = {
-  type: 'button',
-  title: 'Preformatted text',
-  fn: {
-    wiki: function() { this.encloseLineSelection('<pre>\n', '\n</pre>') }
-  }
-}
-
-// Code highlighting
-jsToolBar.prototype.elements.precode = {
-  type: 'button',
-  title: 'Highlighted code',
-  fn: {
-    wiki: function() {
-      var This = this;
-      this.precodeMenu(function(lang){
-        This.encloseLineSelection('<pre><code class="' + lang + '">\n', '\n</code></pre>\n');
-      });
-    }
-  }
-}
-
-// spacer
-jsToolBar.prototype.elements.space4 = {type: 'space'}
-
-// wiki page
-jsToolBar.prototype.elements.link = {
-  type: 'button',
-  title: 'Wiki link',
-  fn: {
-    wiki: function() { this.encloseSelection("[[", "]]") }
-  }
-}
-// image
-jsToolBar.prototype.elements.img = {
-  type: 'button',
-  title: 'Image',
-  fn: {
-    wiki: function() { this.encloseSelection("!", "!") }
-  }
-}
-
-// spacer
-jsToolBar.prototype.elements.space5 = {type: 'space'}
-// help
-jsToolBar.prototype.elements.help = {
-  type: 'button',
-  title: 'Help',
-  fn: {
-    wiki: function() { window.open(this.help_link, '', 'resizable=yes, location=no, width=300, height=640, menubar=no, status=no, scrollbars=yes') }
-  }
-}
diff --git a/app/assets/stylesheets/jstoolbar.css b/app/assets/stylesheets/jstoolbar.css
index 3df219fcd..3f00f0798 100644
--- a/app/assets/stylesheets/jstoolbar.css
+++ b/app/assets/stylesheets/jstoolbar.css
@@ -164,3 +164,41 @@
 .jstb_help {
     background-image: url(/jstoolbar/help.svg);
 }
+
+.jst_menu {
+  border: 1px solid #c5c5c5;
+  border-radius: 2px;
+  background: #fff;
+  color: #333;
+  font-family: Arial,Helvetica,sans-serif;
+  font-size: 1em;
+  list-style: none;
+  padding: 0;
+  margin: 0;
+  display: block;
+  outline: 0;
+}
+
+.jst_menuitem-wrapper {
+  position: relative;
+  padding: 3px 1em 3px .4em;
+}
+
+.jst_menuitem {
+  padding: 4px 8px;
+  max-width: 500px;
+  cursor: pointer;
+  color: #333;
+  font-family: Arial,Helvetica,sans-serif;
+  font-size: 1em;
+  list-style: none;
+  list-style-image: none;
+}
+
+.jst_menuitem-active {
+  border: 1px solid #759fcf; background: #759fcf; font-weight: normal; color: #fff;
+}
+.jst_menuitem-active a { color: #fff; text-decoration: none;  }
+.jst_menuitem-active a:link { color: #fff; text-decoration: none;  }
+.jst_menuitem-active a:visited { color: #fff; text-decoration: none;  }
+
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index e64eb3616..28b5a85fb 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -30,9 +30,6 @@ module ApplicationHelper
   include Redmine::Helpers::URL
   include IconsHelper
 
-  extend Forwardable
-  def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
-
   # Return true if user is authorized for controller/action, otherwise false
   def authorize_for(controller, action)
     User.current.allowed_to?({:controller => controller, :action => action}, @project)
@@ -1438,16 +1435,6 @@ module ApplicationHelper
     end
   end
 
-  def list_autofill_data_attributes
-    return {} if Setting.text_formatting.blank?
-
-    {
-      controller: 'list-autofill',
-      action: 'beforeinput->list-autofill#handleBeforeInput',
-      list_autofill_text_formatting_param: Setting.text_formatting
-    }
-  end
-
   unless const_defined?(:MACROS_RE)
     MACROS_RE = /(
                   (!)?                        # escaping
@@ -1960,6 +1947,48 @@ module ApplicationHelper
     )
   end
 
+  def add_wiki_toolbar(data, &)
+    heads_for_wiki_formatter
+
+    preview_url = data.delete(:wiki_preview_url) || preview_text_path
+    data[:texteditor_target] = 'editor'
+    data[:action] = 'click->texteditor#shortcut beforeinput->texteditor#autofillListItem'
+
+    render layout: 'common/texteditor', locals: {preview_url: preview_url}, formats: [:html], &
+  end
+
+  def heads_for_wiki_formatter
+    unless @heads_for_wiki_formatter_included
+      toolbar_language_options = User.current && User.current.pref.toolbar_language_options
+      langs = if toolbar_language_options.nil?
+                UserPreference::DEFAULT_TOOLBAR_LANGUAGE_OPTIONS
+              else
+                toolbar_language_options.split(',')
+              end
+      content_for :header_tags do
+        javascript_tag(
+          "var wikiImageMimeTypes = #{Redmine::MimeType.by_type('image').to_json};") +
+        tag.script(raw(l(:jstoolbar).to_json), type: 'application/json', id: 'jstoolbar-locale') +
+        stylesheet_link_tag('jstoolbar') +
+        render(partial: 'common/table_generator') +
+        render(partial: 'common/code_highlighting', locals: {langs: langs})
+      end
+      @heads_for_wiki_formatter_included = true
+    end
+  end
+
+  def text_area_tag(name, content, options={})
+    data = options[:data] || {}
+    wiki_toolbar = data.delete(:wiki_toolbar)
+    if wiki_toolbar
+      add_wiki_toolbar(data) do
+        text_area_tag(name, content, options)
+      end
+    else
+      super
+    end
+  end
+
   private
 
   def wiki_helper
diff --git a/app/helpers/custom_fields_helper.rb b/app/helpers/custom_fields_helper.rb
index 14025b934..d20686e71 100644
--- a/app/helpers/custom_fields_helper.rb
+++ b/app/helpers/custom_fields_helper.rb
@@ -77,17 +77,16 @@ module CustomFieldsHelper
   end
 
   # Return custom field html tag corresponding to its format
-  def custom_field_tag(prefix, custom_value)
+  def custom_field_tag(prefix, custom_value, data = {})
     cf = custom_value.custom_field
     css = cf.css_classes
     placeholder = cf.description
     placeholder&.tr!("\n", ' ') if cf.field_format != 'text'
-    data = nil
     if cf.full_text_formatting?
       css += ' wiki-edit'
       data = {
         :auto_complete => true
-      }.merge(list_autofill_data_attributes)
+      }
     end
     cf.format.edit_tag(
       self,
@@ -120,7 +119,8 @@ module CustomFieldsHelper
 
   # Return custom field tag with its label tag
   def custom_field_tag_with_label(name, custom_value, options={})
-    tag = custom_field_tag(name, custom_value)
+    data = options.delete(:data) || {}
+    tag = custom_field_tag(name, custom_value, data)
     tag_id = nil
     ids = tag.scan(/ id="(.+?)"/)
     if ids.size == 1
@@ -130,14 +130,14 @@ module CustomFieldsHelper
   end
 
   # Returns the custom field tag for when bulk editing objects
-  def custom_field_tag_for_bulk_edit(prefix, custom_field, objects=nil, value='')
+  def custom_field_tag_for_bulk_edit(prefix, custom_field, objects=nil, value='', options={})
     css =  custom_field.css_classes
-    data = nil
+    data = options.delete(:data) || {}
     if custom_field.full_text_formatting?
       css += ' wiki-edit'
       data = {
         :auto_complete => true
-      }.merge(list_autofill_data_attributes)
+      }
     end
     custom_field.format.bulk_edit_tag(
       self,
diff --git a/app/javascript/controllers/list_autofill_controller.js b/app/javascript/controllers/list_autofill_controller.js
deleted file mode 100644
index 4db836ad6..000000000
--- a/app/javascript/controllers/list_autofill_controller.js
+++ /dev/null
@@ -1,124 +0,0 @@
-import { Controller } from '@hotwired/stimulus'
-
-class ListAutofillHandler {
-  constructor(inputElement, format) {
-    this.input = inputElement
-    this.format = format
-  }
-
-  run(event) {
-    const { selectionStart, value } = this.input
-
-    const beforeCursor = value.slice(0, selectionStart)
-    const lines = beforeCursor.split("\n")
-    const currentLine = lines[lines.length - 1]
-    const lineStartPos = beforeCursor.lastIndexOf("\n") + 1
-
-    let formatter
-    switch (this.format) {
-      case 'common_mark':
-        formatter = new CommonMarkListFormatter()
-        break
-      case 'textile':
-        formatter = new TextileListFormatter()
-        break
-      default:
-        return
-    }
-
-    const result = formatter.format(currentLine)
-
-    if (!result) return
-
-    switch (result.action) {
-      case 'remove':
-        event.preventDefault()
-        this.input.setRangeText('', lineStartPos, selectionStart, 'start')
-        break
-      case 'insert':
-        event.preventDefault()
-        const insertText = "\n" + result.text
-        const newValue = value.slice(0, selectionStart) + insertText + value.slice(selectionStart)
-        const newCursor = selectionStart + insertText.length
-        this.input.value = newValue
-        this.input.setSelectionRange(newCursor, newCursor)
-        break
-      default:
-        return
-    }
-  }
-}
-
-class CommonMarkListFormatter {
-  format(line) {
-    // Match list items in CommonMark syntax.
-    // Captures either an ordered list (e.g., '1. ' or '2) ') or an unordered list (e.g., '* ', '- ', '+ ').
-    // The regex structure:
-    // ^(\s*)               → leading whitespace
-    // (?:(\d+)([.)])       → an ordered list marker: number followed by '.' or ')'
-    // |([*+\-])            → OR an unordered list marker: '*', '+', or '-'
-    // (.*)$                → the actual list item content
-    //
-    // Examples:
-    // '2. ordered text'           → indent='',  number='2', delimiter='.', bullet=undefined, content='ordered text'
-    // '  3) nested ordered text'  → indent='  ', number='3', delimiter=')', bullet=undefined, content='nested ordered text'
-    // '* unordered text'          → indent='', number=undefined, delimiter=undefined, bullet='*', content='unordered text'
-    // '+ unordered text'          → indent='', number=undefined, delimiter=undefined, bullet='+', content='unordered text'
-    // '  - nested unordered text' → indent='  ', number=undefined, delimiter=undefined, bullet='-', content='nested unordered text'
-    const match = line.match(/^(\s*)(?:(\d+)([.)])|([*+\-])) (.*)$/)
-    if (!match) return null
-
-    const indent = match[1]
-    const number = match[2]
-    const delimiter = match[3]
-    const bullet = match[4]
-    const content = match[5]
-
-    if (content === '') {
-      return { action: 'remove' }
-    }
-
-    if (number) {
-      const nextNumber = parseInt(number, 10) + 1
-      return { action: 'insert', text: `${indent}${nextNumber}${delimiter} ` }
-    } else {
-      return { action: 'insert', text: `${indent}${bullet} ` }
-    }
-  }
-}
-
-class TextileListFormatter {
-  format(line) {
-    // Match list items in Textile syntax.
-    // Captures either an ordered list (using '#') or an unordered list (using '*').
-    // The regex structure:
-    // ^([*#]+)            → one or more list markers: '*' for unordered, '#' for ordered
-    // (.*)$               → the actual list item content
-    //
-    // Examples:
-    // '# ordered text'            → marker='#',  content='ordered text'
-    // '## nested ordered text'    → marker='##', content='nested ordered text'
-    // '* unordered text'          → marker='*',  content='unordered text'
-    // '** nested unordered text'  → marker='**', content='nested unordered text'
-    const match = line.match(/^([*#]+) (.*)$/)
-    if (!match) return null
-
-    const marker = match[1]
-    const content = match[2]
-
-    if (content === '') {
-      return { action: 'remove' }
-    }
-
-    return { action: 'insert', text: `${marker} ` }
-  }
-}
-
-export default class extends Controller {
-  handleBeforeInput(event) {
-    if (event.inputType != 'insertLineBreak') return
-
-    const format = event.params.textFormatting
-    new ListAutofillHandler(event.currentTarget, format).run(event)
-  }
-}
diff --git a/app/javascript/controllers/precode_controller.js b/app/javascript/controllers/precode_controller.js
new file mode 100644
index 000000000..406318bb6
--- /dev/null
+++ b/app/javascript/controllers/precode_controller.js
@@ -0,0 +1,52 @@
+/**
+ * Redmine - project management software
+ * Copyright (C) 2006-  Jean-Philippe Lang
+ * This code is released under the GNU General Public License.
+ */
+
+import { Controller } from "@hotwired/stimulus"
+
+// Connects to data-controller="precode"
+export default class extends Controller {
+  connect() {
+    this.currentElem = null;
+  }
+
+  select(e) {
+    if (this.currentElem) return;
+
+    let target = event.target.closest('.jst_menuitem');
+    if (!target || !this.element.contains(target)) return;
+
+    this.currentElem = target;
+    target.classList.add('jst_menuitem-active')
+  }
+
+  unselect(e) {
+    if (!this.currentElem) return;
+
+    let relatedTarget = event.relatedTarget;
+    if (relatedTarget) {
+      while (relatedTarget) {
+        if (relatedTarget == this.currentElem) return;
+        relatedTarget = relatedTarget.parentNode;
+      }
+    }
+    this.currentElem.classList.remove('jst_menuitem-active')
+    this.currentElem = null;
+  }
+
+  addMenu(e) {
+    this.element.addMenu(e.target.textContent)
+  }
+
+  remove(e) {
+    this.element.remove();
+  }
+
+  removeWhenOutside(e) {
+    if (!e.target.matches('.jstb_precode') && !this.element.contains(e.target)) {
+      this.remove();
+    }
+  }
+}
diff --git a/app/javascript/controllers/table_generator_controller.js b/app/javascript/controllers/table_generator_controller.js
new file mode 100644
index 000000000..9b50e2da1
--- /dev/null
+++ b/app/javascript/controllers/table_generator_controller.js
@@ -0,0 +1,41 @@
+/**
+ * Redmine - project management software
+ * Copyright (C) 2006-  Jean-Philippe Lang
+ * This code is released under the GNU General Public License.
+ */
+
+import { Controller } from "@hotwired/stimulus"
+
+// Connects to data-controller="table-generator"
+export default class extends Controller {
+
+  addMenu(e) {
+    const row = parseInt(e.target.dataset.row);
+    const col = parseInt(e.target.dataset.col);
+    const alphabets = this.element.dataset.alphabets.split('');
+    this.element.addMenu(alphabets.slice(0, col), row);
+  }
+
+  selectCell(e) {
+    const hoverRow = parseInt(e.target.dataset.row);
+    const hoverCol = parseInt(e.target.dataset.col);
+
+    e.currentTarget.querySelectorAll('td').forEach(element => {
+      if (parseInt(element.dataset.row) <= hoverRow && parseInt(element.dataset.col) <= hoverCol) {
+        element.classList.add('selected-cell');
+      } else {
+        element.classList.remove('selected-cell');
+      }
+    })
+  }
+
+  remove(e) {
+    this.element.remove();
+  }
+
+  removeWhenOutside(e) {
+    if (!e.target.matches('.jstb_table') && !this.element.contains(e.target)) {
+      this.remove();
+    }
+  }
+}
diff --git a/app/javascript/controllers/template_controller.js b/app/javascript/controllers/template_controller.js
new file mode 100644
index 000000000..89033d92d
--- /dev/null
+++ b/app/javascript/controllers/template_controller.js
@@ -0,0 +1,18 @@
+/**
+ * Redmine - project management software
+ * Copyright (C) 2006-  Jean-Philippe Lang
+ * This code is released under the GNU General Public License.
+ */
+
+import { Controller } from "@hotwired/stimulus"
+
+// Connects to data-controller="template"
+export default class extends Controller {
+  static targets = ['element']
+
+  elementTargetConnected(element) {
+    const clone = element.content.cloneNode(true);
+    this.element.prepend(clone);
+    element.remove()
+  }
+}
diff --git a/app/javascript/controllers/texteditor_controller.js b/app/javascript/controllers/texteditor_controller.js
new file mode 100644
index 000000000..7d80abf10
--- /dev/null
+++ b/app/javascript/controllers/texteditor_controller.js
@@ -0,0 +1,84 @@
+/**
+ * Redmine - project management software
+ * Copyright (C) 2006-  Jean-Philippe Lang
+ * This code is released under the GNU General Public License.
+ */
+
+import { Controller } from "@hotwired/stimulus"
+import { post } from '@rails/request.js'
+import { jsonContent } from 'helper'
+
+// Connects to data-controller="texteditor"
+export default class extends Controller {
+
+  static targets = ['editor', 'tabs', 'edittab', 'previewtab', 'elements', 'main', 'preview' ]
+  static values  = { 'previewUrl': String }
+
+  connect() {
+    const lang = jsonContent('jstoolbar-locale')
+
+    Promise.all([
+      import('jstoolbar'),
+      import(`jstoolbar/formatting/${this.element.dataset.formatting}`)
+    ])
+      .then(([mod, formatting]) => {
+         const { default: jsToolBar } = mod;
+         jsToolBar.strings = lang;
+         this.toolbar = new jsToolBar(this, formatting.default)
+         this.toolbar.setHelpLink(this.element.dataset.help)
+         this.toolbar.draw()
+     })
+  }
+
+  preview(e) {
+    e.preventDefault()
+    e.stopPropagation()
+
+    const data = { text: this.editorTarget.value };
+
+    const form = e.target.closest('form');
+    form.querySelectorAll('.attachments_fields input').forEach(el => (data[el.name] = el.value))
+
+    post(this.previewUrlValue, {
+      body: data
+    }).then(res => {
+      if (res.ok) {
+        return res.html
+      }
+    }).then(html => {
+      this.previewTarget.innerHTML = html;
+      setupWikiTableSortableHeader();
+    });
+  }
+
+  shortcut(e) {
+    e.preventDefault()
+    this.toolbar.keyboardShortcuts.call(this.toolbar, e);
+  }
+
+  hidePreview(e) {
+    e.preventDefault()
+    this.toolbar.hidePreview.call(this.toolbar, e);
+  }
+
+  showPreview(e) {
+    e.preventDefault()
+    this.toolbar.showPreview.call(this.toolbar, e);
+  }
+
+  keyboardShortcuts(e) {
+    this.toolbar.keyboardShortcuts.call(this.toolbar, e);
+  }
+
+  togglePreview(e) {
+    this.toolbar.togglePreview(e);
+  }
+
+  autofillListItem(e) {
+    this.toolbar.autofillListItem(e)
+  }
+
+  execute(e) {
+    this.toolbar.execute.call(this.toolbar, e);
+  }
+}
diff --git a/app/javascript/helper.js b/app/javascript/helper.js
new file mode 100644
index 000000000..945ccdb38
--- /dev/null
+++ b/app/javascript/helper.js
@@ -0,0 +1,17 @@
+/**
+ * Redmine - project management software
+ * Copyright (C) 2006-  Jean-Philippe Lang
+ * This code is released under the GNU General Public License.
+ */
+
+export function jsonContent(id) {
+  const json = document.getElementById(id);
+  if (json === null) throw new Error(`Element #${id} is not found`)
+  if (!(json.tagName === 'SCRIPT' && json.getAttribute('type', 'application/json'))) throw new Error(`Element #${id} should be script element`)
+
+  return JSON.parse(json.textContent)
+}
+
+export function isMac() {
+  return Boolean(navigator.platform.toLowerCase().match(/mac/));
+}
diff --git a/app/javascript/jstoolbar.js b/app/javascript/jstoolbar.js
new file mode 100644
index 000000000..e344d793f
--- /dev/null
+++ b/app/javascript/jstoolbar.js
@@ -0,0 +1,550 @@
+/**
+ * This file is part of DotClear.
+ * Copyright (c) 2005 Nicolas Martin & Olivier Meunier and contributors. All rights reserved.
+ * This code is released under the GNU General Public License.
+ *
+ * Modified by JP LANG for multiple text formatting
+ */
+import { isMac } from 'helper'
+
+export default class jsToolBar {
+  static lastJstPreviewed = null;
+
+  constructor(controller, formatter) {
+    if (!controller) { return; }
+
+    this.textarea     = controller.editorTarget;
+    this.toolbarBlock = controller.element
+    this.editor       = controller.mainTarget;
+    this.preview      = controller.previewTarget;
+    this.tabsBlock    = controller.tabsTarget;
+    this.formatter    = formatter;
+
+    const preview_id = `preview_${this.textarea.getAttribute('id')}`
+    this.preview.setAttribute('id', preview_id)
+
+    this.editTab = controller.edittabTarget;
+    jsTabname(this.editTab, 'Edit')
+
+    this.previewTab = controller.previewtabTarget;
+    jsTabname(this.previewTab, 'Preview')
+
+    this.toolbar = controller.elementsTarget;
+
+    // Dragable resizing
+    if (typeof this.editor !== 'undefined' && this.editor.addEventListener && navigator.appVersion.match(/\bMSIE\b/))
+    {
+      this.handle = document.createElement('div');
+      this.handle.className = 'jstHandle';
+      this.handle.addEventListener('mousedown',(event) => { this.resizeDragStart(event); },false);
+      this.editor.parentNode.insertBefore(this.handle,this.editor.nextSibling);
+    }
+
+    this.context = null;
+    this.toolNodes = {}; // lorsque la toolbar est dessinée , cet objet est garni
+                         // de raccourcis vers les éléments DOM correspondants aux outils.
+    this.base_url = '';
+    this.mode = 'wiki';
+    this.help_link = '';
+    this.shortcuts = {};
+    this.buttons = {}
+  }
+
+  getMode() {
+    return this.mode;
+  }
+
+  setMode(mode) {
+    this.mode = mode || 'wiki';
+  }
+
+  switchMode(mode) {
+    mode = mode || 'wiki';
+    this.draw(mode);
+  }
+
+  setHelpLink(link) {
+    this.help_link = link;
+  }
+
+  showHelp() {
+    window.open(this.help_link, '', 'resizable=yes, location=no, width=300, height=640, menubar=no, status=no, scrollbars=yes')
+  }
+
+  setPreviewUrl(url) {
+    this.previewTab.firstChild.setAttribute('data-url', url);
+  }
+
+  button(tool) {
+    if (typeof tool.fn[this.mode] != 'function') return null;
+
+    const className = 'jstb_' + tool.name;
+    let title = tool.title
+
+    if (tool.hasOwnProperty('shortcut')) {
+      this.shortcuts[tool.shortcut] = className;
+      title = this.buttonTitleWithShortcut(tool.title, tool.shortcut)
+    }
+
+    const b = new jsButton(title, tool.fn[this.mode], this, className, tool.name);
+    if (tool.icon != undefined) b.icon = tool.icon;
+
+    return b;
+  }
+
+  buttonTitleWithShortcut(title, shortcutKey) {
+    let i18nTitle
+    if(typeof jsToolBar.strings == 'undefined') {
+      i18nTitle = title || null;
+    } else {
+      i18nTitle = jsToolBar.strings[title.toLowerCase()] || title || null;
+    }
+
+    if (isMac()) {
+      return i18nTitle + " (⌘" + shortcutKey.toUpperCase() + ")";
+    } else {
+      return i18nTitle + " (Ctrl+" + shortcutKey.toUpperCase() + ")";
+    }
+  }
+
+  space(tool) {
+    const space = new jsSpace(tool.name)
+    if (tool.width !== undefined)
+      space.width = tool.width;
+    return space;
+  }
+
+  combo(toolName) {
+    const tool = this.formatter.elements[toolName];
+    const length = tool[this.mode].list.length;
+
+    if (typeof tool[this.mode].fn != 'function' || length == 0) {
+      return null;
+    } else {
+      const options = {};
+      for (let i=0; i < length; i++) {
+        const opt = tool[this.mode].list[i];
+        options[opt] = tool.options[opt];
+      }
+      return new jsCombo(tool.title, options, this, tool[this.mode].fn);
+    }
+  }
+
+  draw(mode) {
+    this.setMode(mode);
+
+    // Empty toolbar
+    while (this.toolbar.hasChildNodes()) {
+      this.toolbar.removeChild(this.toolbar.firstChild)
+    }
+    this.toolNodes = {}; // vide les raccourcis DOM/**/
+    this.buttons = {}
+
+    // Draw toolbar elements
+    let b, tool, newTool;
+
+    this.formatter.elements.forEach(b => {
+      const disabled =
+      b.type == undefined || b.type == ''
+      || (b.disabled != undefined && b.disabled)
+      || (b.context != undefined && b.context != null && b.context != this.context);
+
+      if (!disabled && typeof this[b.type] == 'function') {
+        tool = this[b.type](b);
+        if (tool) newTool = tool.draw();
+        if (newTool) {
+          this.toolNodes[b.name] = newTool; //mémorise l'accès DOM pour usage éventuel ultérieur
+          this.toolbar.appendChild(newTool);
+        }
+        if (b.type == 'button') {
+          this.buttons[b.name] = tool;
+        }
+      }
+    })
+  }
+
+  singleTag(stag,etag) {
+    stag = stag || null;
+    etag = etag || stag;
+
+    if (!stag || !etag) { return; }
+
+    this.encloseSelection(stag,etag);
+  }
+
+  encloseLineSelection(prefix, suffix, fn) {
+    this.textarea.focus();
+
+    prefix = prefix || '';
+    suffix = suffix || '';
+
+    let start, end, sel, scrollPos, subst, res;
+
+    if (typeof(document["selection"]) != "undefined") {
+      sel = document.selection.createRange().text;
+    } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
+      start = this.textarea.selectionStart;
+      end = this.textarea.selectionEnd;
+      scrollPos = this.textarea.scrollTop;
+      // go to the start of the line
+      start = this.textarea.value.substring(0, start).replace(/[^\r\n]*$/g,'').length;
+      // go to the end of the line
+      end = this.textarea.value.length - this.textarea.value.substring(end, this.textarea.value.length).replace(/^[^\r\n]*/, '').length;
+      sel = this.textarea.value.substring(start, end);
+    }
+
+    if (sel.match(/ $/)) { // exclude ending space char, if any
+      sel = sel.substring(0, sel.length - 1);
+      suffix = suffix + " ";
+    }
+
+    if (typeof(fn) == 'function') {
+      res = (sel) ? fn.call(this,sel) : fn('');
+    } else {
+      res = (sel) ? sel : '';
+    }
+
+    subst = prefix + res + suffix;
+
+    if (typeof(document["selection"]) != "undefined") {
+      document.selection.createRange().text = subst;
+      const range = this.textarea.createTextRange();
+      range.collapse(false);
+      range.move('character', -suffix.length);
+      range.select();
+    } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
+      this.textarea.value = this.textarea.value.substring(0, start) + subst +
+      this.textarea.value.substring(end);
+      if (sel || (!prefix && start === end)) {
+        this.textarea.setSelectionRange(start + subst.length, start + subst.length);
+      } else {
+        this.textarea.setSelectionRange(start + prefix.length, start + prefix.length);
+      }
+      this.textarea.scrollTop = scrollPos;
+    }
+  }
+
+  encloseSelection(prefix, suffix, fn) {
+    this.textarea.focus();
+    prefix = prefix || '';
+    suffix = suffix || '';
+
+    let start, end, sel, scrollPos, subst, res;
+
+    if (typeof(document["selection"]) != "undefined") {
+      sel = document.selection.createRange().text;
+    } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
+      start = this.textarea.selectionStart;
+      end = this.textarea.selectionEnd;
+      scrollPos = this.textarea.scrollTop;
+      sel = this.textarea.value.substring(start, end);
+      if (start > 0 && this.textarea.value.substr(start-1, 1).match(/\S/)) {
+        prefix = ' ' + prefix;
+      }
+      if (this.textarea.value.substr(end, 1).match(/\S/)) {
+        suffix = suffix + ' ';
+      }
+    }
+    if (sel.match(/ $/)) { // exclude ending space char, if any
+      sel = sel.substring(0, sel.length - 1);
+      suffix = suffix + " ";
+    }
+
+    if (typeof(fn) == 'function') {
+      res = (sel) ? fn.call(this,sel) : fn('');
+    } else {
+      res = (sel) ? sel : '';
+    }
+
+    subst = prefix + res + suffix;
+
+    if (typeof(document["selection"]) != "undefined") {
+      document.selection.createRange().text = subst;
+      const range = this.textarea.createTextRange();
+      range.collapse(false);
+      range.move('character', -suffix.length);
+      range.select();
+    } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
+      this.textarea.value = this.textarea.value.substring(0, start) + subst +
+      this.textarea.value.substring(end);
+      if (sel) {
+        this.textarea.setSelectionRange(start + subst.length, start + subst.length);
+      } else {
+        this.textarea.setSelectionRange(start + prefix.length, start + prefix.length);
+      }
+      this.textarea.scrollTop = scrollPos;
+    }
+  }
+
+  execute(event) {
+    const name = event.target.dataset.toolName;
+    const button = this.buttons[name];
+    if (typeof func !== undefined) {
+      button.execute()
+    }
+  }
+
+  showPreview(event) {
+    if (event.target.classList.contains('selected')) { return; }
+    jsToolBar.lastJstPreviewed = this.toolbarBlock;
+    this.preview.setAttribute('style', 'min-height: ' + this.textarea.clientHeight + 'px;')
+    this.toolbar.classList.add('hidden');
+    this.textarea.classList.add('hidden');
+    this.preview.classList.remove('hidden');
+    this.tabsBlock.querySelector('.tab-edit').classList.remove('selected');
+    event.target.classList.add('selected');
+  }
+
+  hidePreview(event) {
+    if (event.target.classList.contains('selected')) { return; }
+    this.toolbar.classList.remove('hidden');
+    this.textarea.classList.remove('hidden');
+    this.textarea.focus();
+    this.preview.classList.add('hidden');
+    this.tabsBlock.querySelector('.tab-preview').classList.remove('selected');
+    event.target.classList.add('selected');
+  }
+
+  keyboardShortcuts(e) {
+    let stop = false;
+    if (isToggleEditPreviewShortcut(e)) {
+      // Switch to preview only if Edit tab is selected when the event triggers.
+      if (this.tabsBlock.querySelector('.tab-edit.selected')) {
+        stop = true
+        this.tabsBlock.querySelector('.tab-preview').click();
+      }
+    }
+    if (isModifierKey(e) && this.shortcuts.hasOwnProperty(e.key.toLowerCase())) {
+      stop = true
+      this.toolbar.querySelector("." + this.shortcuts[e.key.toLowerCase()]).click();
+    }
+    if (stop) {
+      e.stopPropagation();
+      e.preventDefault();
+    }
+  }
+
+  stripBaseURL(url) {
+    if (this.base_url != '') {
+      const pos = url.indexOf(this.base_url);
+      if (pos == 0) {
+        url = url.substr(this.base_url.length);
+      }
+    }
+
+    return url;
+  }
+  /** Resizer
+  -------------------------------------------------------- */
+  resizeSetStartH() {
+    this.dragStartH = this.textarea.offsetHeight + 0;
+  }
+
+  resizeDragStart(event) {
+    this.dragStartY = event.clientY;
+    this.resizeSetStartH();
+    document.addEventListener('mousemove', this.dragMoveHdlr=(event) => {this.resizeDragMove(event);}, false);
+    document.addEventListener('mouseup', this.dragStopHdlr=(event) => {this.resizeDragStop(event);}, false);
+  }
+
+  resizeDragMove(event) {
+    this.textarea.style.height = (this.dragStartH+event.clientY-this.dragStartY)+'px';
+  }
+
+  resizeDragStop(event) {
+    document.removeEventListener('mousemove', this.dragMoveHdlr, false);
+    document.removeEventListener('mouseup', this.dragStopHdlr, false);
+  }
+
+  /* Code highlighting menu */
+  precodeMenu(fn){
+    this.createMenuElement('precode', 'code-highlighting-template', fn);
+    return false;
+  }
+
+  /* Table generator */
+  tableMenu(fn){
+    this.createMenuElement('table', 'table-generator-template', fn);
+    return false;
+  }
+
+  createMenuElement(name, templateId, fn) {
+    const template = document.getElementById(templateId);
+    const fragment = template.content.cloneNode(true);
+    const menu = document.body.appendChild(fragment.firstElementChild);
+
+    const rect = this.toolNodes[name].getBoundingClientRect();
+    menu.style.top  = `${rect.bottom + window.pageYOffset}px`;
+    menu.style.left = `${rect.left + window.pageXOffset}px`;
+    menu.addMenu = fn;
+
+    return menu;
+  }
+
+  togglePreview(e) {
+    if (isToggleEditPreviewShortcut(e)) {
+      if (jsToolBar.lastJstPreviewed !== null) {
+        e.preventDefault();
+        e.stopPropagation();
+        jsToolBar.lastJstPreviewed.querySelector('.tab-edit').click();
+        jsToolBar.lastJstPreviewed = null;
+      }
+    }
+  }
+
+  autofillListItem(e) {
+    if (e.inputType != 'insertLineBreak') return;
+
+    const target = e.currentTarget;
+    const { selectionStart, value } = target;
+
+    const beforeCursor = value.slice(0, selectionStart)
+    const lines = beforeCursor.split("\n")
+    const currentLine = lines[lines.length - 1]
+    const lineStartPos = beforeCursor.lastIndexOf("\n") + 1
+
+    const result = this.formatter.autofillListItem(currentLine)
+
+    if (!result) return
+
+    switch (result.action) {
+      case 'remove':
+        event.preventDefault()
+        target.setRangeText('', lineStartPos, selectionStart, 'start')
+        break
+      case 'insert':
+        event.preventDefault()
+        const insertText = "\n" + result.text
+        const newValue = value.slice(0, selectionStart) + insertText + value.slice(selectionStart)
+        const newCursor = selectionStart + insertText.length
+        target.value = newValue
+        target.setSelectionRange(newCursor, newCursor)
+        break
+      default:
+        return
+    }
+  }
+}
+
+function jsTabname(link, name) {
+  const tabName = jsToolBar.strings[name.toLowerCase()] || name || null;
+  if (typeof link !== 'undefined') {
+    link.innerText = tabName;
+  }
+}
+
+class jsButton {
+  constructor(title, fn, scope, className, name) {
+    if(typeof jsToolBar.strings == 'undefined') {
+      this.title = title || null;
+    } else {
+        this.title = jsToolBar.strings[name] || title || null;
+    }
+    this.fn = fn || function(){};
+    this.scope = scope || null;
+    this.className = className || null;
+    this.name = name
+  }
+
+  draw() {
+    if (!this.scope) return null;
+
+    const button = document.createElement('button');
+    button.setAttribute('type','button');
+    button.tabIndex = 200;
+    if (this.className) {
+      button.className = this.className;
+    }
+    button.title = this.title;
+    const span = document.createElement('span');
+    span.appendChild(document.createTextNode(this.title));
+    button.appendChild(span);
+    button.dataset.toolName = this.name;
+
+    if (this.icon != undefined) {
+      button.style.backgroundImage = `url(${this.icon})`;
+    }
+
+    return button;
+  }
+
+  execute() {
+    if (typeof(this.fn) == 'function') {
+      try {
+        this.fn.apply(this.scope, arguments)
+      } catch (e) {
+      }
+    }
+  }
+}
+
+class jsSpace {
+  constructor(className) {
+    this.className = className || null;
+    this.width = null;
+  }
+
+  draw() {
+    const span = document.createElement('span');
+    span.appendChild(document.createTextNode(String.fromCharCode(160)));
+    span.className = 'jstSpacer' + (this.className ? ' ' + this.className : '');
+    if (this.width) span.style.marginRight = this.width+'px';
+
+    return span;
+  }
+}
+
+class jsCombo {
+  constructor(title, options, scope, fn, className) {
+    this.title = title || null;
+    this.options = options || null;
+    this.scope = scope || null;
+    this.fn = fn || function(){};
+    this.className = className || null;
+  }
+
+  draw() {
+    if (!this.scope || !this.options) return null;
+
+    const select = document.createElement('select');
+    if (this.className) select.className = className;
+    select.title = this.title;
+
+    this.options.forEach(o => {
+      const option = document.createElement('option');
+      option.value = o;
+      option.appendChild(document.createTextNode(this.options[o]));
+      select.appendChild(option);
+    })
+
+    select.addEventListener('change', (e) =>  {
+      try {
+        this.fn.call(this.scope, e.currentTarget.value);
+      } catch (error) {
+        alert(error);
+      }
+
+      return false;
+    })
+
+    return select;
+  }
+}
+
+function isToggleEditPreviewShortcut(e) {
+  if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key.toLowerCase() === 'p') {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+function isModifierKey(e) {
+  if (isMac() && e.metaKey) {
+    return true;
+  } else if (!isMac() && e.ctrlKey) {
+    return true;
+  } else {
+    return false;
+  }
+}
diff --git a/app/javascript/jstoolbar/formatting/common_mark.js b/app/javascript/jstoolbar/formatting/common_mark.js
new file mode 100644
index 000000000..edd177a04
--- /dev/null
+++ b/app/javascript/jstoolbar/formatting/common_mark.js
@@ -0,0 +1,276 @@
+/**
+ * This file is part of DotClear.
+ * Copyright (c) 2005 Nicolas Martin & Olivier Meunier and contributors. All rights reserved.
+ * This code is released under the GNU General Public License.
+ *
+ * Modified by JP LANG for common_mark formatting
+ */
+
+export default {
+  autofillListItem: (line) => {
+    // Match list items in CommonMark syntax.
+    // Captures either an ordered list (e.g., '1. ' or '2) ') or an unordered list (e.g., '* ', '- ', '+ ').
+    // The regex structure:
+    // ^(\s*)               → leading whitespace
+    // (?:(\d+)([.)])       → an ordered list marker: number followed by '.' or ')'
+    // |([*+\-])            → OR an unordered list marker: '*', '+', or '-'
+    // (.*)$                → the actual list item content
+    //
+    // Examples:
+    // '2. ordered text'           → indent='',  number='2', delimiter='.', bullet=undefined, content='ordered text'
+    // '  3) nested ordered text'  → indent='  ', number='3', delimiter=')', bullet=undefined, content='nested ordered text'
+    // '* unordered text'          → indent='', number=undefined, delimiter=undefined, bullet='*', content='unordered text'
+    // '+ unordered text'          → indent='', number=undefined, delimiter=undefined, bullet='+', content='unordered text'
+    // '  - nested unordered text' → indent='  ', number=undefined, delimiter=undefined, bullet='-', content='nested unordered text'
+    const match = line.match(/^(\s*)(?:(\d+)([.)])|([*+\-])) (.*)$/)
+    if (!match) return null
+
+    const indent = match[1]
+    const number = match[2]
+    const delimiter = match[3]
+    const bullet = match[4]
+    const content = match[5]
+
+    if (content === '') {
+      return { action: 'remove' }
+    }
+
+    if (number) {
+      const nextNumber = parseInt(number, 10) + 1
+      return { action: 'insert', text: `${indent}${nextNumber}${delimiter} ` }
+    } else {
+      return { action: 'insert', text: `${indent}${bullet} ` }
+    }
+  },
+
+  elements: [
+    {
+      name: 'strong',
+      type: 'button',
+      title: 'Strong',
+      shortcut: 'b',
+      fn: {
+        wiki: function() { this.singleTag('**') }
+      }
+    },
+    {
+      name: 'em',
+      type: 'button',
+      title: 'Italic',
+      shortcut: 'i',
+      fn: {
+        wiki: function() { this.singleTag("*") }
+      }
+    },
+    {
+      name: 'ins',
+      type: 'button',
+      title: 'Underline',
+      shortcut: 'u',
+      fn: {
+        wiki: function() { this.singleTag('<u>', '</u>') }
+      }
+    },
+    {
+      name: 'del',
+      type: 'button',
+      title: 'Deleted',
+      fn: {
+        wiki: function() { this.singleTag('~~') }
+      }
+    },
+    {
+      name: 'code',
+      type: 'button',
+      title: 'Code',
+      fn: {
+        wiki: function() { this.singleTag('`') }
+      }
+    },
+    {
+      name: 'space1',
+      type: 'space'
+    },
+    {
+      name: 'h1',
+      type: 'button',
+      title: 'Heading 1',
+      fn: {
+        wiki: function() {
+          this.encloseLineSelection('# ', '',function(str) {
+            str = str.replace(/^#+\s+/, '')
+            return str;
+          });
+        }
+      }
+    },
+    {
+      name: 'h2',
+      type: 'button',
+      title: 'Heading 2',
+      fn: {
+        wiki: function() {
+          this.encloseLineSelection('## ', '',function(str) {
+            str = str.replace(/^#+\s+/, '')
+            return str;
+          });
+        }
+      }
+    },
+    {
+      name: 'h3', 
+      type: 'button',
+      title: 'Heading 3',
+      fn: {
+        wiki: function() {
+          this.encloseLineSelection('### ', '',function(str) {
+            str = str.replace(/^#+\s+/, '')
+            return str;
+          });
+        }
+      }
+    },
+    {
+      name: 'space2',
+      type: 'space'
+    },
+    {
+      name: 'ul',
+      type: 'button',
+      title: 'Unordered list',
+      fn: {
+        wiki: function() {
+          this.encloseLineSelection('','',function(str) {
+            str = str.replace(/\r/g,'');
+            return str.replace(/(\n|^)[#-]?\s*/g,"$1* ");
+          });
+        }
+      }
+    },
+    {
+      name: 'ol',
+      type: 'button',
+      title: 'Ordered list',
+      fn: {
+        wiki: function() {
+          this.encloseLineSelection('','',function(str) {
+            str = str.replace(/\r/g,'');
+            return str.replace(/(\n|^)[*-]?\s*/g,"$11. ");
+          });
+        }
+      }
+    },
+    {
+      name: 'tl',
+      type: 'button',
+      title: 'Task list',
+      fn: {
+        wiki: function() {
+          this.encloseLineSelection('','',function(str) {
+            str = str.replace(/\r/g,'');
+            return str.replace(/(\n|^)[*-]?\s*/g,"$1* [ ] ");
+          });
+        }
+      }
+    },
+    {
+      name: 'space3',
+      type: 'space'
+    },
+    {
+      name: 'bq',
+      type: 'button',
+      title: 'Quote',
+      fn: {
+        wiki: function() {
+          this.encloseLineSelection('','',function(str) {
+            str = str.replace(/\r/g,'');
+            return str.replace(/(\n|^)( *)([^\n]*)/g,"$1> $2$3");
+          });
+        }
+      }
+    },
+    {
+      name: 'unbq',
+      type: 'button',
+      title: 'Unquote',
+      fn: {
+        wiki: function() {
+          this.encloseLineSelection('','',function(str) {
+            str = str.replace(/\r/g,'');
+            return str.replace(/(\n|^) *(> ?)?( *)([^\n]*)/g,"$1$3$4");
+          });
+        }
+      }
+    },
+    {
+      name: 'table',
+      type: 'button',
+      title: 'Table',
+      fn: {
+        wiki: function() {
+          this.tableMenu((cols, rowCount) => {
+            this.encloseLineSelection(
+              '|'+cols.join(' |')+' |\n' +                                   // header
+              Array(cols.length+1).join('|--')+'|\n' +                       // second line
+              Array(rowCount+1).join(Array(cols.length+1).join('|  ')+'|\n') // cells
+            );
+          });
+        }
+      }
+    },
+    {
+      name: 'pre',
+      type: 'button',
+      title: 'Preformatted text',
+      fn: {
+        wiki: function() { this.encloseLineSelection('```\n', '\n```') }
+      }
+    },
+    {
+      name: 'precode',
+      type: 'button',
+      title: 'Highlighted code',
+      fn: {
+        wiki: function() {
+          var This = this;
+          this.precodeMenu(function(lang){
+            This.encloseLineSelection('``` ' + lang + '\n', '\n```\n');
+          });
+        }
+      }
+    },
+    {
+      name: 'space4',
+      type: 'space'
+    },
+    {
+      name: 'link',
+      type: 'button',
+      title: 'Wiki link',
+      fn: {
+        wiki: function() { this.encloseSelection("[[", "]]") }
+      }
+    },
+    {
+      name: 'img',
+      type: 'button',
+      title: 'Image',
+      fn: {
+        wiki: function() { this.encloseSelection("![](", ")") }
+      }
+    },
+    {
+      name: 'space5',
+      type: 'space'
+    },
+    {
+      name: 'help',
+      type: 'button',
+      title: 'Help',
+      fn: {
+        wiki: function() { this.showHelp() }
+      }
+    }
+  ]
+}
diff --git a/app/javascript/jstoolbar/formatting/textile.js b/app/javascript/jstoolbar/formatting/textile.js
new file mode 100644
index 000000000..e4b6f9e39
--- /dev/null
+++ b/app/javascript/jstoolbar/formatting/textile.js
@@ -0,0 +1,236 @@
+/**
+ * This file is part of DotClear.
+ * Copyright (c) 2005 Nicolas Martin & Olivier Meunier and contributors. All rights reserved.
+ * This code is released under the GNU General Public License.
+ *
+ * Modified by JP LANG for markdown formatting
+ */
+
+export default {
+  autofillListItem: (line) => {
+    // Match list items in Textile syntax.
+    // Captures either an ordered list (using '#') or an unordered list (using '*').
+    // The regex structure:
+    // ^([*#]+)            → one or more list markers: '*' for unordered, '#' for ordered
+    // (.*)$               → the actual list item content
+    //
+    // Examples:
+    // '# ordered text'            → marker='#',  content='ordered text'
+    // '## nested ordered text'    → marker='##', content='nested ordered text'
+    // '* unordered text'          → marker='*',  content='unordered text'
+    // '** nested unordered text'  → marker='**', content='nested unordered text'
+    const match = line.match(/^([*#]+) (.*)$/)
+    if (!match) return null
+
+    const marker = match[1]
+    const content = match[2]
+
+    if (content === '') {
+      return { action: 'remove' }
+    }
+
+    return { action: 'insert', text: `${marker} ` }
+  },
+
+  elements: [
+    {
+      name: 'strong',
+      type: 'button',
+      title: 'Strong',
+      shortcut: 'b',
+      fn: {
+        wiki: function() { this.singleTag('*') }
+      }
+    },
+    {
+      name: 'em',
+      type: 'button',
+      title: 'Italic',
+      shortcut: 'i',
+      fn: {
+        wiki: function() { this.singleTag("_") }
+      }
+    },
+    {
+      name: 'ins',
+      type: 'button',
+      title: 'Underline',
+      shortcut: 'u',
+      fn: {
+        wiki: function() { this.singleTag('+') }
+      }
+    },
+    {
+      name: 'del',
+      type: 'button',
+      title: 'Deleted',
+      fn: {
+        wiki: function() { this.singleTag('-') }
+      }
+    },
+    {
+      name: 'code',
+      type: 'button',
+      title: 'Code',
+      fn: {
+        wiki: function() { this.singleTag('@') }
+      }
+    },
+    { name: 'space1', type: 'space'},
+    {
+      name: 'h1',
+      type: 'button',
+      title: 'Heading 1',
+      fn: {
+        wiki: function() {
+          this.encloseLineSelection('h1. ', '',function(str) {
+            str = str.replace(/^h\d+\.\s+/, '')
+            return str;
+          });
+        }
+      }
+    },
+    {
+      name: 'h2',
+      type: 'button',
+      title: 'Heading 2',
+      fn: {
+        wiki: function() {
+          this.encloseLineSelection('h2. ', '',function(str) {
+            str = str.replace(/^h\d+\.\s+/, '')
+            return str;
+          });
+        }
+      }
+    },
+    {
+      name: 'h3',
+      type: 'button',
+      title: 'Heading 3',
+      fn: {
+        wiki: function() {
+          this.encloseLineSelection('h3. ', '',function(str) {
+            str = str.replace(/^h\d+\.\s+/, '')
+            return str;
+          });
+        }
+      }
+    },
+    { name: 'space2', type: 'space'},
+    {
+      name: 'ul',
+      type: 'button',
+      title: 'Unordered list',
+      fn: {
+        wiki: function() {
+          this.encloseLineSelection('','',function(str) {
+            str = str.replace(/\r/g,'');
+            return str.replace(/(\n|^)[#-]?\s*/g,"$1* ");
+          });
+        }
+      }
+    },
+    {
+      name: 'ol',
+      type: 'button',
+      title: 'Ordered list',
+      fn: {
+        wiki: function() {
+          this.encloseLineSelection('','',function(str) {
+            str = str.replace(/\r/g,'');
+            return str.replace(/(\n|^)[*-]?\s*/g,"$1# ");
+          });
+        }
+      }
+    },
+    { name: 'space3', type: 'space'},
+    {
+      name: 'bq',
+      type: 'button',
+      title: 'Quote',
+      fn: {
+        wiki: function() {
+          this.encloseLineSelection('','',function(str) {
+            str = str.replace(/\r/g,'');
+            return str.replace(/(\n|^)( *)([^\n]*)/g,"$1> $2$3");
+          });
+        }
+      }
+    },
+    {
+      name: 'unbq',
+      type: 'button',
+      title: 'Unquote',
+      fn: {
+        wiki: function() {
+          this.encloseLineSelection('','',function(str) {
+            str = str.replace(/\r/g,'');
+            return str.replace(/(\n|^) *(> ?)?( *)([^\n]*)/g,"$1$3$4");
+          });
+        }
+      }
+    },
+    {
+      name: 'table',
+      type: 'button',
+      title: 'Table',
+      fn: {
+        wiki: function() {
+          this.tableMenu((cols, rowCount) => {
+            this.encloseLineSelection(
+              '|_.'+cols.join('|_.')+'|\n' +                                 // header
+              Array(rowCount+1).join(Array(cols.length+1).join('|  ')+'|\n') // cells
+            );
+          });
+        }
+      }
+    },
+    {
+      name: 'pre',
+      type: 'button',
+      title: 'Preformatted text',
+      fn: {
+        wiki: function() { this.encloseLineSelection('<pre>\n', '\n</pre>') }
+      }
+    },
+    {
+      name: 'precode',
+      type: 'button',
+      title: 'Highlighted code',
+      fn: {
+        wiki: function() {
+          var This = this;
+          this.precodeMenu(function(lang){
+            This.encloseLineSelection('<pre><code class="' + lang + '">\n', '\n</code></pre>\n');
+          });
+        }
+      }
+    },
+    { name: 'space4', type: 'space'},
+    {
+      name: 'link',
+      type: 'button',
+      title: 'Wiki link',
+      fn: {
+        wiki: function() { this.encloseSelection("[[", "]]") }
+      }
+    },
+    {
+      name: 'img',
+      type: 'button',
+      title: 'Image',
+      fn: {
+        wiki: function() { this.encloseSelection("!", "!") }
+      }
+    },
+    { name: 'space5', type: 'space'},
+    {
+      name: 'help',
+      type: 'button',
+      title: 'Help',
+      fn: {
+        wiki: function() { this.showHelp() }
+      }
+    }
+  ]
+}
diff --git a/app/views/common/_code_highlighting.html.erb b/app/views/common/_code_highlighting.html.erb
new file mode 100644
index 000000000..c5aa8bb70
--- /dev/null
+++ b/app/views/common/_code_highlighting.html.erb
@@ -0,0 +1,12 @@
+<template id="code-highlighting-template">
+  <ul class="jst_menu"
+      style="position: absolute;width: 150px"
+      data-controller="precode"
+      data-action="pointerover->precode#select pointerout->precode#unselect pointerdown->precode#addMenu pointerdown->precode#remove click@window->precode#removeWhenOutside">
+    <% langs.each do |lang| %>
+      <li class="jst_menuitem">
+        <div class="jst_menuitem-wrapper"><%= lang %></div>
+      </li>
+     <% end %>
+  <ul>
+</template>
diff --git a/app/views/common/_table_generator.html.erb b/app/views/common/_table_generator.html.erb
new file mode 100644
index 000000000..1e2820001
--- /dev/null
+++ b/app/views/common/_table_generator.html.erb
@@ -0,0 +1,14 @@
+<template id="table-generator-template">
+  <table class="table-generator"
+         data-controller="table-generator"
+         data-action="pointerover->table-generator#selectCell pointerdown->table-generator#addMenu pointerdown->table-generator#remove click@window->table-generator#removeWhenOutside"
+         data-alphabets="ABCDEFGHIJ">
+    <% (1..5).each do |r| %>
+      <%= tag.tr do %>
+        <% (1..10).each do |c| %>
+          <%= tag.td data: {row: r, col: c, title: "#{c}&times;#{r}"} %>
+        <% end %>
+      <% end %>
+    <% end %>
+  </table>
+</template>
diff --git a/app/views/common/_texteditor.html.erb b/app/views/common/_texteditor.html.erb
new file mode 100644
index 000000000..41519177b
--- /dev/null
+++ b/app/views/common/_texteditor.html.erb
@@ -0,0 +1,17 @@
+<%= tag.div(class: 'jstBlock', data: { controller: 'texteditor',
+                                       formatting: Setting.text_formatting,
+                                       help: help_wiki_syntax_path,
+                                       texteditor_preview_url_value: preview_url,
+                                       action: 'keydown->texteditor#keyboardShortcuts keydown@window->texteditor#togglePreview' }) do %>
+  <div class="jstTabs tabs" data-texteditor-target="tabs">
+    <ul>
+      <li><%= link_to(nil, '#', class: 'tab-edit selected', data: { texteditor_target: 'edittab', action: 'texteditor#hidePreview' }) %></li>
+      <li><%= link_to(nil, '#', class: 'tab-preview', data: { texteditor_target: 'previewtab', action: 'texteditor#preview texteditor#showPreview' })%></li>
+      <li class="tab-elements"><div class="jstElements" data-texteditor-target="elements" data-action="click->texteditor#execute"></div><li>
+    </ul>
+  </div>
+  <div class="jstEditor" data-texteditor-target="main">
+    <%= yield %>
+    <div class="wiki wiki-preview hidden" data-texteditor-target="preview"></div>
+  </div>
+<% end %>
diff --git a/app/views/documents/_form.html.erb b/app/views/documents/_form.html.erb
index 82dcd9293..97bc96312 100644
--- a/app/views/documents/_form.html.erb
+++ b/app/views/documents/_form.html.erb
@@ -5,10 +5,10 @@
   <p><%= f.text_field :title, :required => true, :size => 60 %></p>
   <p><%= f.text_area :description, :cols => 60, :rows => 15, :class => 'wiki-edit',
                     :data => {
-                        :auto_complete => true
-                    }.merge(list_autofill_data_attributes)
+                        :auto_complete => true,
+                        :wiki_toolbar => Setting.text_formatting.present?
+                    }
                      %></p>
-
   <% @document.custom_field_values.each do |value| %>
     <p><%= custom_field_tag_with_label :document, value %></p>
   <% end %>
@@ -17,5 +17,3 @@
     <p><label><%=l(:label_attachment_plural)%></label><%= render :partial => 'attachments/form', :locals => {:container => @document} %></p>
   <% end %>
 </div>
-
-<%= wikitoolbar_for 'document_description' %>
diff --git a/app/views/issues/_edit.html.erb b/app/views/issues/_edit.html.erb
index 299efec7a..24b968738 100644
--- a/app/views/issues/_edit.html.erb
+++ b/app/views/issues/_edit.html.erb
@@ -29,12 +29,13 @@
     <% end %>
     <% if @issue.notes_addable? %>
       <fieldset id="add_notes"><legend><%= l(:field_notes) %></legend>
-      <%= f.text_area :notes, :cols => 60, :rows => 10, :class => 'wiki-edit',
-            :data => {
-                :auto_complete => true
-            }.merge(list_autofill_data_attributes),
-            :no_label => true %>
-      <%= wikitoolbar_for 'issue_notes', preview_issue_path(:project_id => @project, :issue_id => @issue) %>
+      <%= f.text_area :notes, cols: 60, rows: 10, class: 'wiki-edit',
+            data: {
+                auto_complete: true,
+                wiki_toolbar: Setting.text_formatting.present?,
+                wiki_preview_url: preview_issue_path(project_id: @project, issue_id: @issue)
+            },
+            no_label: true %>
 
       <% if @issue.safe_attribute? 'private_notes' %>
       <%= f.check_box :private_notes, :no_label => true %> <label for="issue_private_notes"><%= l(:field_private_notes) %></label>
diff --git a/app/views/issues/_form.html.erb b/app/views/issues/_form.html.erb
index 4e8e07856..bee1feee3 100644
--- a/app/views/issues/_form.html.erb
+++ b/app/views/issues/_form.html.erb
@@ -32,17 +32,20 @@
 <% if @issue.safe_attribute? 'description' %>
 <p>
   <%= f.label_for_field :description, :required => @issue.required_attribute?('description') %>
-  <%= content_tag 'span', :id => "issue_description_and_toolbar", :style => (@issue.new_record? ? nil : 'display:none') do %>
-    <%= f.text_area :description, :cols => 60, :accesskey => accesskey(:edit), :class => 'wiki-edit',
-                   :rows => [[10, @issue.description.to_s.length / 50].max, 20].min,
-                   :data => {
-                       :auto_complete => true
-                   }.merge(list_autofill_data_attributes),
-                   :no_label => true %>
+  <%= tag.span id: 'issue_description_and_toolbar', style: (@issue.new_record? ? nil : 'display:none'), data: {controller: 'template'} do %>
+    <template data-template-target="element">
+    <%= f.text_area :description, cols: 60, accesskey: accesskey(:edit), class: 'wiki-edit',
+                    rows: [[10, @issue.description.to_s.length / 50].max, 20].min,
+                    data: {
+                      auto_complete: true,
+                      wiki_toolbar: Setting.text_formatting.present?,
+                      wiki_preview_url: preview_issue_path(:project_id => @issue.project, :issue_id => @issue.id)
+                   },
+                   no_label: true %>
+    </template>
   <% end %>
   <%= link_to_function content_tag(:span, sprite_icon('edit', l(:button_edit)), :class => 'icon icon-edit'), '$(this).hide(); $("#issue_description_and_toolbar").show()' unless @issue.new_record? %>
 </p>
-<%= wikitoolbar_for 'issue_description', preview_issue_path(:project_id => @issue.project, :issue_id => @issue.id) %>
 <% end %>
 
 <div id="attributes" class="attributes">
diff --git a/app/views/issues/_form_custom_fields.html.erb b/app/views/issues/_form_custom_fields.html.erb
index fddcd742f..4e62debd4 100644
--- a/app/views/issues/_form_custom_fields.html.erb
+++ b/app/views/issues/_form_custom_fields.html.erb
@@ -19,6 +19,10 @@
 <% end %>
 
 <% custom_field_values_full_width.each do |value| %>
-  <p><%= custom_field_tag_with_label :issue, value, :required => @issue.required_attribute?(value.custom_field_id) %></p>
-  <%= wikitoolbar_for "issue_custom_field_values_#{value.custom_field_id}", preview_issue_path(:project_id => @issue.project, :issue_id => @issue.id) if value.custom_field.full_text_formatting? %>
+  <p>
+    <%= custom_field_tag_with_label :issue, value,
+      required: @issue.required_attribute?(value.custom_field_id),
+      data: { wiki_toolbar: value.custom_field.full_text_formatting?, wiki_preview_url: preview_issue_path(:project_id => @issue.project, :issue_id => @issue.id) }
+    %>
+  </p>
 <% end %>
diff --git a/app/views/issues/bulk_edit.html.erb b/app/views/issues/bulk_edit.html.erb
index 70582b735..e709f57fa 100644
--- a/app/views/issues/bulk_edit.html.erb
+++ b/app/views/issues/bulk_edit.html.erb
@@ -211,9 +211,9 @@
 <% custom_fields_full_width.each do |custom_field| %>
   <p>
     <label><%= custom_field.name %></label>
-    <%= custom_field_tag_for_bulk_edit('issue', custom_field, @issues, @issue_params[:custom_field_values][custom_field.id.to_s]) %>
+    <%= custom_field_tag_for_bulk_edit 'issue', custom_field, @issues, @issue_params[:custom_field_values][custom_field.id.to_s],
+          data: { wiki_toolbar: custom_field.full_text_formatting?, wiki_preview_url: preview_issue_path(project_id: @project, issue_id: nil) } %>
   </p>
-  <%= wikitoolbar_for "issue_custom_field_values_#{custom_field.id}", preview_issue_path(:project_id => @project, :issue_id => nil) if custom_field.full_text_formatting? %>
 <% end %>
 
 </fieldset>
@@ -222,10 +222,10 @@
 <legend><%= l(:field_notes) %></legend>
 <%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit',
                   :data => {
-                      :auto_complete => true
-                  }.merge(list_autofill_data_attributes)
+                      :auto_complete => true,
+                      :wiki_toolbar => Setting.text_formatting.present?
+                  }
 %>
-<%= wikitoolbar_for 'notes' %>
 
 <% if User.current.allowed_to?(:add_issue_watchers, nil, global: true)%>
   <%= update_data_sources_for_auto_complete({users: watchers_autocomplete_for_mention_path(q: '', object_type: 'issue', object_id: @issues.map(&:id) )}) %>
diff --git a/app/views/journals/_notes_form.html.erb b/app/views/journals/_notes_form.html.erb
index 45f20a6c4..244a33eef 100644
--- a/app/views/journals/_notes_form.html.erb
+++ b/app/views/journals/_notes_form.html.erb
@@ -6,8 +6,10 @@
     <%= text_area_tag 'journal[notes]', @journal.notes, :id => "journal_#{@journal.id}_notes", :class => 'wiki-edit',
           :rows => (@journal.notes.blank? ? 10 : [[10, @journal.notes.length / 50].max, 100].min),
           :data => {
-              :auto_complete => true
-          }.merge(list_autofill_data_attributes)
+              :auto_complete => true,
+              :wiki_toolbar => Setting.text_formatting.present?,
+              :wiki_preview_url => preview_issue_path(project_id: @project, issue_id: @journal.issue)
+          }
     %>
     <% if @journal.safe_attribute? 'private_notes' %>
       <%= hidden_field_tag 'journal[private_notes]', '0' %>
@@ -18,4 +20,3 @@
     <p><%= submit_tag l(:button_save) %>
     <%= link_to l(:button_cancel), '#', :onclick => "$('#journal-#{@journal.id}-form').remove(); $('#journal-#{@journal.id}-notes').show(); return false;" %></p>
 <% end %>
-<%= wikitoolbar_for "journal_#{@journal.id}_notes", preview_issue_path(:project_id => @project, :issue_id => @journal.issue) %>
diff --git a/app/views/messages/_form.html.erb b/app/views/messages/_form.html.erb
index 492390036..c738b4e4e 100644
--- a/app/views/messages/_form.html.erb
+++ b/app/views/messages/_form.html.erb
@@ -26,10 +26,11 @@
 <%= f.text_area :content, :cols => 80, :rows => 15, :class => 'wiki-edit', :id => 'message_content',
                 :accesskey => accesskey(:edit),
                 :data => {
-                    :auto_complete => true
-                }.merge(list_autofill_data_attributes)
+                    :auto_complete => true,
+                    :wiki_toolbar => Setting.text_formatting.present?,
+                    :wiki_preview_url => preview_board_message_path(:board_id => @board, :id => @message)
+                }
 %></p>
-<%= wikitoolbar_for 'message_content', preview_board_message_path(:board_id => @board, :id => @message) %>
 <!--[eoform:message]-->
 
 <p><%= l(:label_attachment_plural) %><br />
diff --git a/app/views/news/_form.html.erb b/app/views/news/_form.html.erb
index f86bba176..12a66b838 100644
--- a/app/views/news/_form.html.erb
+++ b/app/views/news/_form.html.erb
@@ -12,10 +12,10 @@
 <p><%= f.text_area :summary, :cols => 60, :rows => 2 %></p>
 <p><%= f.text_area :description, :required => true, :cols => 60, :rows => 15, :class => 'wiki-edit',
                    :data => {
-                       :auto_complete => true
-                   }.merge(list_autofill_data_attributes)
+                       :auto_complete => true,
+                       :wiki_toolbar => Setting.text_formatting.present?,
+                       :wiki_preview_url => preview_news_path(:project_id => @project, :id => @news)
+                   }
 %></p>
 <p id="attachments_form"><label><%= l(:label_attachment_plural) %></label><%= render :partial => 'attachments/form', :locals => {:container => @news} %></p>
 </div>
-
-<%= wikitoolbar_for 'news_description', preview_news_path(:project_id => @project, :id => @news) %>
diff --git a/app/views/news/show.html.erb b/app/views/news/show.html.erb
index 11126998e..c8517f924 100644
--- a/app/views/news/show.html.erb
+++ b/app/views/news/show.html.erb
@@ -67,12 +67,13 @@
 <p><%= toggle_link l(:label_comment_add), "add_comment_form", :focus => "comment_comments" %></p>
 <%= form_tag({:controller => 'comments', :action => 'create', :id => @news}, :id => "add_comment_form", :style => "display:none;") do %>
 <div class="box">
-    <%= text_area 'comment', 'comments', :cols => 80, :rows => 15, :class => 'wiki-edit',
+    <%= text_area_tag 'comment', nil, :cols => 80, :rows => 15, :class => 'wiki-edit',
                   :data => {
-                    :auto_complete => true
-                  }.merge(list_autofill_data_attributes)
+                    :auto_complete => true,
+                    :wiki_toolbar => Setting.text_formatting.present?,
+                    :wiki_preview_url => preview_news_path(:project_id => @project, :id => @news)
+                  }
     %>
-    <%= wikitoolbar_for 'comment_comments', preview_news_path(:project_id => @project, :id => @news) %>
 </div>
 <p><%= submit_tag l(:button_add) %></p>
 <% end %>
diff --git a/app/views/projects/_form.html.erb b/app/views/projects/_form.html.erb
index 0f8733e28..66dedb6b2 100644
--- a/app/views/projects/_form.html.erb
+++ b/app/views/projects/_form.html.erb
@@ -4,7 +4,7 @@
 <!--[form:project]-->
 <p><%= f.text_field :name, :required => true, :size => 60 %></p>
 
-<p><%= f.text_area :description, :rows => 8, :class => 'wiki-edit', :data => list_autofill_data_attributes %></p>
+<p><%= f.text_area :description, :rows => 8, :class => 'wiki-edit', :data => { :wiki_toolbar => Setting.text_formatting.present? } %></p>
 <p><%= f.text_field :identifier, :required => true, :size => 60, :disabled => @project.identifier_frozen?, :maxlength => Project::IDENTIFIER_MAX_LENGTH %>
 <% unless @project.identifier_frozen? %>
   <em class="info"><%= l(:text_length_between, :min => 1, :max => Project::IDENTIFIER_MAX_LENGTH) %> <%= l(:text_project_identifier_info).html_safe %></em>
@@ -23,8 +23,6 @@
 <p><%= f.check_box :inherit_members %></p>
 <% end %>
 
-<%= wikitoolbar_for 'project_description' %>
-
 <% @project.visible_custom_field_values.each do |value| %>
   <p><%= custom_field_tag_with_label :project, value %></p>
 <% end %>
diff --git a/app/views/search/index.html.erb b/app/views/search/index.html.erb
index b092bbae0..c17bbd8ea 100644
--- a/app/views/search/index.html.erb
+++ b/app/views/search/index.html.erb
@@ -6,7 +6,7 @@
 <p><%= text_field_tag 'q', @question, :size => 60, :id => 'search-input',
                       :data => {
                           :auto_complete => true
-                      }.merge(list_autofill_data_attributes) %>
+                      } %>
 <%= project_select_tag %>
 <%= hidden_field_tag 'all_words', '', :id => nil %>
 <label><%= check_box_tag 'all_words', 1, @all_words %> <%= l(:label_all_words) %></label>
diff --git a/app/views/settings/_general.html.erb b/app/views/settings/_general.html.erb
index 5afd1cb44..2e1ab42a6 100644
--- a/app/views/settings/_general.html.erb
+++ b/app/views/settings/_general.html.erb
@@ -3,9 +3,7 @@
 <div class="box tabular settings">
 <p><%= setting_text_field :app_title, :size => 30 %></p>
 
-<p><%= setting_text_area :welcome_text, :cols => 60, :rows => 5, :class => 'wiki-edit', :data => list_autofill_data_attributes %></p>
-<%= wikitoolbar_for 'settings_welcome_text' %>
-
+<p><%= setting_text_area :welcome_text, :cols => 60, :rows => 5, :class => 'wiki-edit', :data => { :wiki_toolbar => Setting.text_formatting.present? } %></p>
 
 <p><%= setting_text_field :per_page_options, :size => 20 %>
 <em class="info"><%= l(:text_comma_separated) %></em></p>
diff --git a/app/views/settings/_notifications.html.erb b/app/views/settings/_notifications.html.erb
index 9856d4fec..2514730f0 100644
--- a/app/views/settings/_notifications.html.erb
+++ b/app/views/settings/_notifications.html.erb
@@ -19,13 +19,11 @@
 </fieldset>
 
 <fieldset class="box"><legend><%= l(:setting_emails_header) %></legend>
-<%= setting_text_area :emails_header, :label => false, :class => 'wiki-edit', :rows => 5, :data => list_autofill_data_attributes %>
-<%= wikitoolbar_for 'settings_emails_header' %>
+<%= setting_text_area :emails_header, :label => false, :class => 'wiki-edit', :rows => 5, :data => { :wiki_toolbar => Setting.text_formatting.present? } %>
 </fieldset>
 
 <fieldset class="box"><legend><%= l(:setting_emails_footer) %></legend>
-<%= setting_text_area :emails_footer, :label => false, :class => 'wiki-edit', :rows => 5, :data => list_autofill_data_attributes %>
-<%= wikitoolbar_for 'settings_emails_footer' %>
+<%= setting_text_area :emails_footer, :label => false, :class => 'wiki-edit', :rows => 5, :data => { :wiki_toolbar => Setting.text_formatting.present? } %>
 </fieldset>
 
 <div style="float:right;">
diff --git a/app/views/timelog/_form.html.erb b/app/views/timelog/_form.html.erb
index 3a0543e7c..19d833abe 100644
--- a/app/views/timelog/_form.html.erb
+++ b/app/views/timelog/_form.html.erb
@@ -30,10 +30,8 @@
   <p><%= f.text_field :comments, :size => 100, :maxlength => 1024, :required => Setting.timelog_required_fields.include?('comments') %></p>
   <p><%= f.select :activity_id, activity_collection_for_select_options(@time_entry), :required => true %></p>
   <% @time_entry.editable_custom_field_values.each do |value| %>
-    <p><%= custom_field_tag_with_label :time_entry, value %></p>
-    <% if value.custom_field.full_text_formatting? %>
-      <%= wikitoolbar_for "time_entry_custom_field_values_#{value.custom_field_id}", preview_issue_path(:project_id => @project) %>
-    <% end %>
+    <p><%= custom_field_tag_with_label :time_entry, value,
+             data: { wiki_toolbar: value.custom_field.full_text_formatting?, wiki_preview_url: preview_issue_path(project_id: @project) } %></p>
   <% end %>
   <%= call_hook(:view_timelog_edit_form_bottom, { :time_entry => @time_entry, :form => f }) %>
 </div>
diff --git a/app/views/timelog/bulk_edit.html.erb b/app/views/timelog/bulk_edit.html.erb
index 4f496470a..32bbd5c45 100644
--- a/app/views/timelog/bulk_edit.html.erb
+++ b/app/views/timelog/bulk_edit.html.erb
@@ -71,9 +71,9 @@
     <% @custom_fields.each do |custom_field| %>
       <p>
         <label><%= h(custom_field.name) %></label>
-        <%= custom_field_tag_for_bulk_edit('time_entry', custom_field, @time_entries, @time_entry_params[:custom_field_values][custom_field.id.to_s]) %>
+        <%= custom_field_tag_for_bulk_edit 'time_entry', custom_field, @time_entries, @time_entry_params[:custom_field_values][custom_field.id.to_s],
+              data: { wiki_toolbar: custom_field.full_text_formatting?, wiki_preview_url: preview_issue_path(:project_id => @project) } %>
       </p>
-      <%= wikitoolbar_for "time_entry_custom_field_values_#{custom_field.id}", preview_issue_path(:project_id => @project) if custom_field.full_text_formatting? %>
     <% end %>
 
     <%= call_hook(:view_time_entries_bulk_edit_details_bottom, { :time_entries => @time_entries }) %>
diff --git a/app/views/wiki/edit.html.erb b/app/views/wiki/edit.html.erb
index 115b5b2c4..eaceb161a 100644
--- a/app/views/wiki/edit.html.erb
+++ b/app/views/wiki/edit.html.erb
@@ -16,8 +16,10 @@
 <%= text_area_tag 'content[text]', @text, :cols => 100, :rows => 25, :accesskey => accesskey(:edit),
                   :class => 'wiki-edit',
                   :data => {
-                      :auto_complete => true
-                  }.merge(list_autofill_data_attributes)
+                      :auto_complete => true,
+                      :wiki_toolbar => Setting.text_formatting.present?,
+                      :wiki_preview_url => preview_project_wiki_page_path(project_id: @project, id: @page.title)
+                  }
 %>
 
 <% if @page.safe_attribute_names.include?('parent_id') && @wiki.pages.any? %>
@@ -64,7 +66,6 @@
   <%= submit_tag l(:button_save) %>
   <%= link_to l(:button_cancel), wiki_page_edit_cancel_path(@page) %>
  </p>
-<%= wikitoolbar_for 'content_text', preview_project_wiki_page_path(:project_id => @project, :id => @page.title) %>
 
 <% if User.current.allowed_to?(:add_wiki_page_watchers, @project)%>
   <%= update_data_sources_for_auto_complete({users: watchers_autocomplete_for_mention_path(project_id: @project, q: '', object_type: 'wiki_page', object_id: @page.id)}) %>
diff --git a/config/importmap.rb b/config/importmap.rb
index 3560330c1..2314e905c 100644
--- a/config/importmap.rb
+++ b/config/importmap.rb
@@ -3,6 +3,10 @@
 # Pin npm packages by running ./bin/importmap
 
 pin "application"
+pin "helper"
+pin "jstoolbar", preload: false
+pin_all_from "app/javascript/jstoolbar/formatting", under: "jstoolbar/formatting", preload: false
+
 pin "@hotwired/stimulus", to: "stimulus.min.js"
 pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
 pin "turndown" # @7.2.0
diff --git a/config/locales/ar.yml b/config/locales/ar.yml
index 8aae80c97..a99bf888f 100644
--- a/config/locales/ar.yml
+++ b/config/locales/ar.yml
@@ -1511,6 +1511,26 @@ ar:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "قوي"
+    em: "مائل"
+    ins: "تسطير"
+    del: "محذوف"
+    code: "رمز ضمني"
+    h1: "عنوان 1"
+    h2: "عنوان 2"
+    h3: "عنوان 3"
+    precode: "Highlighted code"
+    ul: "قائمة غير مرتبة"
+    ol: "قائمة مرتبة"
+    bq: "اقتباس"
+    unbq: "إزالة الاقتباس"
+    table: "Table"
+    pre: "نص مسبق التنسيق"
+    link: "رابط الى صفحة ويكي"
+    img: "صورة"
+    edit: "تعديل"
+    preview: "معاينة"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/az.yml b/config/locales/az.yml
index e1f64c888..965f21a61 100644
--- a/config/locales/az.yml
+++ b/config/locales/az.yml
@@ -1602,6 +1602,26 @@ az:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Strong"
+    em: "Italic"
+    ins: "Underline"
+    del: "Deleted"
+    code: "Inline Code"
+    h1: "Heading 1"
+    h2: "Heading 2"
+    h3: "Heading 3"
+    precode: "Highlighted code"
+    ul: "Unordered list"
+    ol: "Ordered list"
+    bq: "Quote"
+    unbq: "Remove Quote"
+    table: "Table"
+    pre: "Preformatted text"
+    link: "Link to a Wiki page"
+    img: "Image"
+    edit: "Redaktə etmək"
+    preview: "İlkin baxış"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/bg.yml b/config/locales/bg.yml
index a5f3bee7f..3d141b616 100644
--- a/config/locales/bg.yml
+++ b/config/locales/bg.yml
@@ -1475,6 +1475,26 @@ bg:
   text_oauth_info_scopes: Select the scopes this application may request. The application
     will not be allowed to do more than what is selected here. It will also always be
     limited by the roles and project memberships of the user who authorized it.
+  jstoolbar:
+    strong: "Получер"
+    em: "Курсив"
+    ins: "Подчертан"
+    del: "Изтрит"
+    code: "Вграден код"
+    h1: "Заглавие 1"
+    h2: "Заглавие 2"
+    h3: "Заглавие 3"
+    precode: "Вграден код"
+    ul: "Неподреден списък"
+    ol: "Подреден списък"
+    bq: "Цитат"
+    unbq: "Премахване на цитат"
+    table: "Table"
+    pre: "Форматиран текст"
+    link: "Връзка до Wiki страница"
+    img: "Изображение"
+    edit: "Редакция"
+    preview: "Преглед"
   label_position: Position
   label_message: Message
   twofa_already_setup: Two-factor authentication already set up
diff --git a/config/locales/bs.yml b/config/locales/bs.yml
index f9e0f5838..c626ef75f 100644
--- a/config/locales/bs.yml
+++ b/config/locales/bs.yml
@@ -1497,6 +1497,24 @@ bs:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Strong"
+    em: "Italic"
+    ins: "Underline"
+    del: "Deleted"
+    code: "Inline Code"
+    h1: "Heading 1"
+    h2: "Heading 2"
+    h3: "Heading 3"
+    precode: "Highlighted code"
+    ul: "Unordered list"
+    ol: "Ordered list"
+    table: "Table"
+    pre: "Preformatted text"
+    link: "Link na Wiki stranicu"
+    img: "Slika"
+    edit: "Ispravka"
+    preview: "Pregled"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/ca.yml b/config/locales/ca.yml
index 719f5ebd0..7efae0d4e 100644
--- a/config/locales/ca.yml
+++ b/config/locales/ca.yml
@@ -1498,6 +1498,26 @@ ca:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Negreta"
+    em: "Cursiva"
+    ins: "Subratllat"
+    del: "Barrat"
+    code: "Codi en línia"
+    h1: "Encapçalament 1"
+    h2: "Encapçalament 2"
+    h3: "Encapçalament 3"
+    precode: "Highlighted code"
+    ul: "Llista sense ordre"
+    ol: "Llista ordenada"
+    bq: "Cometes"
+    unbq: "Sense cometes"
+    table: "Table"
+    pre: "Text formatat"
+    link: "Enllaça a una pàgina Wiki"
+    img: "Imatge"
+    edit: "Editar"
+    preview: "Previsualitzar"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/cs.yml b/config/locales/cs.yml
index cf65f5bcd..260af75fe 100644
--- a/config/locales/cs.yml
+++ b/config/locales/cs.yml
@@ -1493,6 +1493,26 @@ cs:
   reaction_text_x_other_users:
     one: 1 jiný
     other: "%{count} jiných"
+  jstoolbar:
+    strong: "Tučné"
+    em: "Kurzíva"
+    ins: "Podtržené"
+    del: "Přeškrtnuté "
+    code: "Vnořený kód"
+    h1: "Nadpis 1"
+    h2: "Nadpis 2"
+    h3: "Nadpis 3"
+    precode: "Zvýrazněný kód"
+    ul: "Seznam"
+    ol: "Uspořádaný seznam"
+    bq: "Citace"
+    unbq: "Odstranit citaci"
+    table: "Tabulka"
+    pre: "Předformátovaný text"
+    link: "Vložit odkaz na Wiki stránku"
+    img: "Vložit obrázek"
+    edit: "Upravit"
+    preview: "Náhled"
   text_setting_gravatar_default_initials_html: Uživatelovy iniciály jsou poslány do <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     k vygenerování avataru.
   permission_view_project: Zobrazení projektů
diff --git a/config/locales/da.yml b/config/locales/da.yml
index ac4b52bb7..82379b3d8 100644
--- a/config/locales/da.yml
+++ b/config/locales/da.yml
@@ -1528,6 +1528,26 @@ da:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Fed"
+    em: "Kursiv"
+    ins: "Understreget"
+    del: "Slettet"
+    code: "Inline-kode"
+    h1: "Overskrift 1"
+    h2: "Overskrift 2"
+    h3: "Overskrift 3"
+    precode: "Highlighted code"
+    ul: "Unummereret liste"
+    ol: "Nummereret liste"
+    bq: "Citér"
+    unbq: "Fjern citér"
+    table: "Table"
+    pre: "Præformateret tekst"
+    link: "Link til en wiki-side"
+    img: "Billede"
+    edit: "Ret"
+    preview: "Forhåndsvisning"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/de.yml b/config/locales/de.yml
index c4ea072d6..03c65b0ac 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -1478,6 +1478,26 @@ de:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Fett"
+    em: "Kursiv"
+    ins: "Unterstrichen"
+    del: "Durchgestrichen"
+    code: "Quelltext"
+    h1: "Überschrift 1. Ordnung"
+    h2: "Überschrift 2. Ordnung"
+    h3: "Überschrift 3. Ordnung"
+    precode: "Highlighted code"
+    ul: "Aufzählungsliste"
+    ol: "Nummerierte Liste"
+    bq: "Zitat"
+    unbq: "Zitat entfernen"
+    table: "Table"
+    pre: "Präformatierter Text"
+    link: "Verweis (Link) zu einer Wiki-Seite"
+    img: "Grafik"
+    edit: "Bearbeiten"
+    preview: "Vorschau"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   label_oauth_permission_admin: Administrator-Zugriff
diff --git a/config/locales/el.yml b/config/locales/el.yml
index 089958d0f..52034f913 100644
--- a/config/locales/el.yml
+++ b/config/locales/el.yml
@@ -1500,6 +1500,26 @@ el:
     one: "%{filename} and 1 file"
     other: "%{filename} and %{count} files"
   setting_wiki_tablesort_enabled: JavaScript based table sorting in wiki content
+  jstoolbar:
+    strong: "Strong"
+    em: "Italic"
+    ins: "Underline"
+    del: "Deleted"
+    code: "Inline Code"
+    h1: "Heading 1"
+    h2: "Heading 2"
+    h3: "Heading 3"
+    precode: "Highlighted code"
+    ul: "Unordered list"
+    ol: "Ordered list"
+    bq: "Quote"
+    unbq: "Remove Quote"
+    table: "Table"
+    pre: "Preformatted text"
+    link: "Link to a Wiki page"
+    img: "Image"
+    edit: "Edit"
+    preview: "Preview"
   label_progressbar: Progress bar
   error_spent_on_closed_issue: Cannot log time on a closed issue
   setting_timelog_accept_closed_issues: Accept time logs on closed issues
diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml
index c99e772b5..1ecf6cf87 100644
--- a/config/locales/en-GB.yml
+++ b/config/locales/en-GB.yml
@@ -1512,6 +1512,26 @@ en-GB:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Strong"
+    em: "Italic"
+    ins: "Underline"
+    del: "Deleted"
+    code: "Inline Code"
+    h1: "Heading 1"
+    h2: "Heading 2"
+    h3: "Heading 3"
+    precode: "Highlighted code"
+    ul: "Unordered list"
+    ol: "Ordered list"
+    bq: "Quote"
+    unbq: "Remove Quote"
+    table: "Table"
+    pre: "Preformatted text"
+    link: "Link to a Wiki page"
+    img: "Image"
+    edit: "Edit"
+    preview: "Preview"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/en.yml b/config/locales/en.yml
index f67d2d2f3..24f4d9e86 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -1456,3 +1456,24 @@ en:
   reaction_text_x_other_users:
     one: "1 other"
     other: "%{count} others"
+  jstoolbar:
+    strong: "Strong"
+    em: "Italic"
+    ins: "Underline"
+    del: "Deleted"
+    code: "Inline Code"
+    h1: "Heading 1"
+    h2: "Heading 2"
+    h3: "Heading 3"
+    precode: "Highlighted code"
+    ul: "Unordered list"
+    ol: "Ordered list"
+    tl: "Task List"
+    bq: "Quote"
+    unbq: "Remove Quote"
+    table: "Table"
+    pre: "Preformatted text"
+    link: "Link to a Wiki page"
+    img: "Image"
+    edit: "Edit"
+    preview: "Preview"
diff --git a/config/locales/es-PA.yml b/config/locales/es-PA.yml
index 4b4827349..45efa3ac3 100644
--- a/config/locales/es-PA.yml
+++ b/config/locales/es-PA.yml
@@ -1542,6 +1542,26 @@ es-PA:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Negrita"
+    em: "Itálica"
+    ins: "Subrayado"
+    del: "Tachado"
+    code: "Código fuente"
+    h1: "Encabezado 1"
+    h2: "Encabezado 2"
+    h3: "Encabezado 3"
+    precode: "Código resaltado"
+    ul: "Lista sin ordenar"
+    ol: "Lista ordenada"
+    bq: "Citar"
+    unbq: "Quitar cita"
+    table: "Table"
+    pre: "Texto con formato"
+    link: "Enlace a página Wiki"
+    img: "Imagen"
+    edit: "Modificar"
+    preview: "Previsualizar"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/es.yml b/config/locales/es.yml
index a4aa2dab7..bc7c0065d 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -1578,6 +1578,26 @@ es:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Negrita"
+    em: "Itálica"
+    ins: "Subrayado"
+    del: "Tachado"
+    code: "Código fuente"
+    h1: "Encabezado 1"
+    h2: "Encabezado 2"
+    h3: "Encabezado 3"
+    precode: "Código resaltado"
+    ul: "Lista sin ordenar"
+    ol: "Lista ordenada"
+    bq: "Citar"
+    unbq: "Quitar cita"
+    table: "Table"
+    pre: "Texto con formato"
+    link: "Enlace a página Wiki"
+    img: "Imagen"
+    edit: "Modificar"
+    preview: "Previsualizar"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/et.yml b/config/locales/et.yml
index b1ca9f248..3fc96a0f9 100644
--- a/config/locales/et.yml
+++ b/config/locales/et.yml
@@ -1516,6 +1516,26 @@ et:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Rõhutatult"
+    em: "Kaldkirjas"
+    ins: "Allakriipsutatult"
+    del: "Läbikriipsutatult"
+    code: "Koodiblokk"
+    h1: "1. taseme pealkiri"
+    h2: "2. taseme pealkiri"
+    h3: "3. taseme pealkiri"
+    precode: "Highlighted code"
+    ul: "Täpitud nimekiri"
+    ol: "Nummerdatud nimekiri"
+    bq: "Tsitaat: aste juurde"
+    unbq: "Tsitaat: aste madalamaks"
+    table: "Table"
+    pre: "Eelvormindatud tekst"
+    link: "Vikilehe link"
+    img: "Pilt"
+    edit: "Muuda"
+    preview: "Eelvaade"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/eu.yml b/config/locales/eu.yml
index 5f6229b2e..39797ed37 100644
--- a/config/locales/eu.yml
+++ b/config/locales/eu.yml
@@ -1512,6 +1512,30 @@ eu:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  # jsToolBar EU language
+  # Author: Ales Zabala Alava (Shagi), <shagi@gisa-elkartea.org>
+  # 2010-01-25
+  # Distributed under the same terms as the jsToolBar itself.
+  jstoolbar:
+    strong: "Lodia"
+    em: "Etzana"
+    ins: "Azpimarra"
+    del: "Ezabatuta"
+    code: "Inline Code"
+    h1: "1 Goiburua"
+    h2: "2 Goiburua"
+    h3: "3 Goiburua"
+    precode: "Highlighted code"
+    ul: "Ordenatu gabeko zerrenda"
+    ol: "Ordenatutako zerrenda"
+    bq: "Aipamena"
+    unbq: "Aipamena kendu"
+    table: "Table"
+    pre: "Aurrez formateatutako testua"
+    link: "Wiki orri baterako esteka"
+    img: "Irudia"
+    edit: "Editatu"
+    preview: "Aurreikusi"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/fa.yml b/config/locales/fa.yml
index e139004d8..a4fabf03b 100644
--- a/config/locales/fa.yml
+++ b/config/locales/fa.yml
@@ -1439,6 +1439,26 @@ fa:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "پررنگ"
+    em: "کج"
+    ins: "زیرخط"
+    del: "برداشته شده"
+    code: "کد درون خطی"
+    h1: "سربرگ ۱"
+    h2: "سربرگ ۲"
+    h3: "سربرگ ۳"
+    precode: "Highlighted code"
+    ul: "فهرست بدون شماره"
+    ol: "فهرست با شماره"
+    bq: "تو بردن"
+    unbq: "بیرون آوردن"
+    table: "Table"
+    pre: "نوشته قالب بندی شده"
+    link: "پیوند به برگ ویکی"
+    img: "عکس"
+    edit: "ویرایش"
+    preview: "پیش‌نمایش"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/fi.yml b/config/locales/fi.yml
index 5f1634aa2..b82e144e8 100644
--- a/config/locales/fi.yml
+++ b/config/locales/fi.yml
@@ -1529,6 +1529,26 @@ fi:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Lihavoitu"
+    em: "Kursivoitu"
+    ins: "Alleviivattu"
+    del: "Yliviivattu"
+    code: "Koodi näkymä"
+    h1: "Otsikko 1"
+    h2: "Otsikko 2"
+    h3: "Otsikko 3"
+    precode: "Highlighted code"
+    ul: "Järjestämätön lista"
+    ol: "Järjestetty lista"
+    bq: "Quote"
+    unbq: "Remove Quote"
+    table: "Table"
+    pre: "Ennaltamuotoiltu teksti"
+    link: "Linkki Wiki sivulle"
+    img: "Kuva"
+    edit: "Muokkaa"
+    preview: "Esikatselu"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index 6807ed9da..70c33371f 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -1488,6 +1488,26 @@ fr:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Gras"
+    em: "Italique"
+    ins: "Souligné"
+    del: "Rayé"
+    code: "Code en ligne"
+    h1: "Titre niveau 1"
+    h2: "Titre niveau 2"
+    h3: "Titre niveau 3"
+    precode: "Code colorisé"
+    ul: "Liste à puces"
+    ol: "Liste numérotée"
+    bq: "Indenté"
+    unbq: "Supprimer indentation"
+    table: "Table"
+    pre: "Texte préformaté"
+    link: "Lien vers une page Wiki"
+    img: "Image"
+    edit: "Modifier"
+    preview: "Prévisualiser"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/gl.yml b/config/locales/gl.yml
index 5cc6a7a1f..e56670076 100644
--- a/config/locales/gl.yml
+++ b/config/locales/gl.yml
@@ -1517,6 +1517,26 @@ gl:
   reaction_text_x_other_users:
     one: 1 outro
     other: "%{count} outros"
+  jstoolbar:
+    strong: "Negriña"
+    em: "Itálica"
+    ins: "Subliñado"
+    del: "Tachado"
+    code: "Código fonte"
+    h1: "Encabezado 1"
+    h2: "Encabezado 2"
+    h3: "Encabezado 3"
+    precode: "Código resaltado"
+    ul: "Lista sen ordenar"
+    ol: "Lista ordenada"
+    bq: "Citar"
+    unbq: "Quitar cita"
+    table: "Táboa"
+    pre: "Texto con formato"
+    link: "Ligazón a páxina Wiki"
+    img: "Imaxe"
+    edit: "Modificar"
+    preview: "Vista previa"
   text_setting_gravatar_default_initials_html: As iniciais dos usuarios son enviadas a <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     para xerar os seus avatares.
   permission_view_project: Ver proxectos
diff --git a/config/locales/he.yml b/config/locales/he.yml
index 019176bab..351f049b0 100644
--- a/config/locales/he.yml
+++ b/config/locales/he.yml
@@ -1516,6 +1516,26 @@ he:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Strong"
+    em: "Italic"
+    ins: "Underline"
+    del: "Deleted"
+    code: "Inline Code"
+    h1: "Heading 1"
+    h2: "Heading 2"
+    h3: "Heading 3"
+    precode: "Highlighted code"
+    ul: "Unordered list"
+    ol: "Ordered list"
+    bq: "Quote"
+    unbq: "Remove Quote"
+    table: "Table"
+    pre: "Preformatted text"
+    link: "Link to a Wiki page"
+    img: "Image"
+    edit: "ערוך"
+    preview: "תצוגה מקדימה"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/hr.yml b/config/locales/hr.yml
index 2e1541a7a..4b1203f04 100644
--- a/config/locales/hr.yml
+++ b/config/locales/hr.yml
@@ -1508,6 +1508,26 @@ hr:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Podebljano"
+    em: "Kurziv"
+    ins: "Podcrtano"
+    del: "Obrisano"
+    code: "Inline Code"
+    h1: "Naslov 1"
+    h2: "Naslov 2"
+    h3: "Naslov 3"
+    precode: "Highlighted code"
+    ul: "Graficke oznake"
+    ol: "Numeriranje"
+    bq: "Citat"
+    unbq: "Ukloni citat"
+    table: "Table"
+    pre: "Izveden tekst"
+    link: "Link na Wiki stranicu"
+    img: "Slika"
+    edit: "Uredi"
+    preview: "Brzi pregled"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/hu.yml b/config/locales/hu.yml
index 3bd3517bd..b2f010caf 100644
--- a/config/locales/hu.yml
+++ b/config/locales/hu.yml
@@ -1500,6 +1500,26 @@
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Félkövér"
+    em: "Dőlt"
+    ins: "Aláhúzott"
+    del: "Törölt"
+    code: "Kód sorok"
+    h1: "Fejléc 1"
+    h2: "Fejléc 2"
+    h3: "Fejléc 3"
+    precode: "Highlighted code"
+    ul: "Felsorolás"
+    ol: "Számozott lista"
+    bq: "Quote"
+    unbq: "Idézet eltávolítása"
+    table: "Table"
+    pre: "Előreformázott szöveg"
+    link: "Link egy Wiki oldalra"
+    img: "Kép"
+    edit: "Szerkeszt"
+    preview: "Előnézet"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/id.yml b/config/locales/id.yml
index 1fd49a338..c395f4251 100644
--- a/config/locales/id.yml
+++ b/config/locales/id.yml
@@ -1513,6 +1513,26 @@ id:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Tebal"
+    em: "Miring"
+    ins: "Garis bawah"
+    del: "Dihapus"
+    code: "Inline Code"
+    h1: "Judul 1"
+    h2: "Judul 2"
+    h3: "Judul 3"
+    precode: "Highlighted code"
+    ul: "Daftar tak terurut"
+    ol: "Daftar terurut"
+    bq: "Kutipan"
+    unbq: "Hapus kutipan"
+    table: "Table"
+    pre: "Teks terformat"
+    link: "Tautkan ke halaman wiki"
+    img: "Gambar"
+    edit: "Sunting"
+    preview: "Tinjauan"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/it.yml b/config/locales/it.yml
index ec096c0f6..b1819a574 100644
--- a/config/locales/it.yml
+++ b/config/locales/it.yml
@@ -1450,6 +1450,28 @@ it:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  # Italian translation
+  # by Diego Pierotto (ita.translations@tiscali.it)
+  jstoolbar:
+    strong: "Grassetto"
+    em: "Corsivo"
+    ins: "Sottolineato"
+    del: "Barrato"
+    code: "Codice sorgente"
+    h1: "Titolo 1"
+    h2: "Titolo 2"
+    h3: "Titolo 3"
+    precode: "Highlighted code"
+    ul: "Elenco puntato"
+    ol: "Elenco numerato"
+    bq: "Aumenta rientro"
+    unbq: "Riduci rientro"
+    table: "Table"
+    pre: "Testo preformattato"
+    link: "Collegamento a pagina Wiki"
+    img: "Immagine"
+    edit: "Modifica"
+    preview: "Anteprima"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/ja.yml b/config/locales/ja.yml
index d331bee3c..e0d02796a 100644
--- a/config/locales/ja.yml
+++ b/config/locales/ja.yml
@@ -1483,4 +1483,25 @@ ja:
   text_oauth_info_scopes: Select the scopes this application may request. The application
     will not be allowed to do more than what is selected here. It will also always be
     limited by the roles and project memberships of the user who authorized it.
+  jstoolbar:
+    strong: "強調"
+    em: "斜体"
+    ins: "下線"
+    del: "取り消し線"
+    code: "コード"
+    h1: "見出し 1"
+    h2: "見出し 2"
+    h3: "見出し 3"
+    precode: "コードハイライト"
+    ul: "順不同リスト"
+    ol: "番号つきリスト"
+    tl: "タスクリスト"
+    bq: "引用"
+    unbq: "引用解除"
+    table: "テーブル"
+    pre: "整形済みテキスト"
+    link: "Wikiページへのリンク"
+    img: "画像"
+    edit: "編集"
+    preview: "プレビュー"
   twofa_already_setup: Two-factor authentication already set up
diff --git a/config/locales/ko.yml b/config/locales/ko.yml
index f7ada81d2..3681a302a 100644
--- a/config/locales/ko.yml
+++ b/config/locales/ko.yml
@@ -1528,6 +1528,26 @@ ko:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "굵게"
+    em: "기울임"
+    ins: "밑줄"
+    del: "취소선"
+    code: "코드"
+    h1: "제목 1"
+    h2: "제목 2"
+    h3: "제목 3"
+    precode: "색상화한 코드"
+    ul: "글머리 기호"
+    ol: "번호 매기기"
+    bq: "인용"
+    unbq: "인용 취소"
+    table: "Table"
+    pre: "있는 그대로 표현 (Preformatted text)"
+    link: "Wiki 페이지에 연결"
+    img: "그림"
+    edit: "편집"
+    preview: "미리보기"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/lt.yml b/config/locales/lt.yml
index 1d0a428c2..360929c0a 100644
--- a/config/locales/lt.yml
+++ b/config/locales/lt.yml
@@ -1472,6 +1472,26 @@ lt:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Pastorinti"
+    em: "Kursyvu"
+    ins: "Pabraukti"
+    del: "Užbraukti"
+    code: "Kodas"
+    h1: "Antraštė 1"
+    h2: "Antraštė 2"
+    h3: "Antraštė 3"
+    precode: "Paryškintas kodas"
+    ul: "Nenumeruotas sąrašas"
+    ol: "Numeruotas sąrašas"
+    bq: "Cituoti"
+    unbq: "Pašalinti citavimą"
+    table: "Lentelė"
+    pre: "Preformatuotas tekstas"
+    link: "Nuoroda į Wiki puslapį"
+    img: "Paveikslėlis"
+    edit: "Redaguoti"
+    preview: "Peržiūra"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/lv.yml b/config/locales/lv.yml
index fd01d4ad6..7ad4b3b3b 100644
--- a/config/locales/lv.yml
+++ b/config/locales/lv.yml
@@ -1505,6 +1505,27 @@ lv:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  # translated by Dzintars Bergs (dzintars.bergs@gmail.com)
+  jstoolbar:
+    strong: "Treknraksts"
+    em: "Slīpraksts"
+    ins: "Pasvītrojums"
+    del: "Dzēsts"
+    code: "Iekļauts kods"
+    h1: "Virsraksts 1"
+    h2: "Virsraksts 2"
+    h3: "Virsraksts 3"
+    precode: "Highlighted code"
+    ul: "Nesakārtots saraksts"
+    ol: "Sakārtots saraksts"
+    bq: "Citēt"
+    unbq: "Noņemt citātu"
+    table: "Table"
+    pre: "Iepriekš formatēts teksts"
+    link: "Saite uz Wiki lapu"
+    img: "Attēls"
+    edit: "Labot"
+    preview: "Priekšskatījums"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/mk.yml b/config/locales/mk.yml
index 35b8af623..4021555d8 100644
--- a/config/locales/mk.yml
+++ b/config/locales/mk.yml
@@ -1511,6 +1511,26 @@ mk:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Задебелен"
+    em: "Закосен"
+    ins: "Подвлечен"
+    del: "Прецртан"
+    code: "Код"
+    h1: "Заглавје 1"
+    h2: "Заглавје 2"
+    h3: "Заглавје 3"
+    precode: "Highlighted code"
+    ul: "Неподредена листа"
+    ol: "Подредена листа"
+    bq: "Цитат"
+    unbq: "Отстрани цитат"
+    table: "Table"
+    pre: "Форматиран текст"
+    link: "Врска до вики страна"
+    img: "Слика"
+    edit: "Уреди"
+    preview: "Preview"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/mn.yml b/config/locales/mn.yml
index fc384de80..482646c09 100644
--- a/config/locales/mn.yml
+++ b/config/locales/mn.yml
@@ -1511,6 +1511,26 @@ mn:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Бүдүүн"
+    em: "Налуу"
+    ins: "Доогуур зураас"
+    del: "Устгагдсан"
+    code: "Програмын код"
+    h1: "Гарчиг 1"
+    h2: "Гарчиг 2"
+    h3: "Гарчиг 3"
+    precode: "Highlighted code"
+    ul: "Эрэмбэгүй жагсаалт"
+    ol: "Эрэмбэтэй жагсаалт"
+    bq: "Ишлэл"
+    unbq: "Ишлэлийг устгах"
+    table: "Table"
+    pre: "Өмнө нь хэлбэржсэн текст"
+    link: "Вики хуудас руу холбох"
+    img: "Зураг"
+    edit: "Засварлах"
+    preview: "Ямар харагдахыг шалгах"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/nl.yml b/config/locales/nl.yml
index a25b0f169..e1b3a9a05 100644
--- a/config/locales/nl.yml
+++ b/config/locales/nl.yml
@@ -1486,6 +1486,26 @@ nl:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Vetgedrukt"
+    em: "Cursief"
+    ins: "Onderstreept"
+    del: "Verwijderd"
+    code: "Computercode"
+    h1: "Hoofding 1"
+    h2: "Hoofding 2"
+    h3: "Hoofding 3"
+    precode: "Gemarkeerde code"
+    ul: "Ongeordende lijst"
+    ol: "Geordende lijst"
+    bq: "Citaat"
+    unbq: "Citaat verwijderen"
+    table: "Table"
+    pre: "Vooropgemaakte tekst"
+    link: "Link naar een Wikipagina"
+    img: "Afbeelding"
+    edit: "Bewerken"
+    preview: "Voorbeeldweergave"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/no.yml b/config/locales/no.yml
index 1269c1c34..d9ec2b449 100644
--- a/config/locales/no.yml
+++ b/config/locales/no.yml
@@ -1502,6 +1502,26 @@
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Fet"
+    em: "Kursiv"
+    ins: "Understreking"
+    del: "Slettet"
+    code: "Kode"
+    h1: "Overskrift 1"
+    h2: "Overskrift 2"
+    h3: "Overskrift 3"
+    precode: "Highlighted code"
+    ul: "Punktliste"
+    ol: "Nummerert liste"
+    bq: "Sitat"
+    unbq: "Avslutt sitat"
+    table: "Table"
+    pre: "Preformatert tekst"
+    link: "Lenke til Wiki-side"
+    img: "Bilde"
+    edit: "Endre"
+    preview: "Forhåndsvis"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/pl.yml b/config/locales/pl.yml
index 13e620bc9..fe86e85ed 100644
--- a/config/locales/pl.yml
+++ b/config/locales/pl.yml
@@ -1455,6 +1455,27 @@ pl:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  # Keep this line in order to avoid problems with Windows Notepad UTF-8 EF-BB-BF idea...
+  jstoolbar:
+    strong: "Pogrubienie"
+    em: "Kursywa"
+    ins: "Podkreślenie"
+    del: "Usunięte"
+    code: "Kod"
+    h1: "Nagłowek 1"
+    h2: "Nagłówek 2"
+    h3: "Nagłówek 3"
+    precode: "Kod z podświetlaniem składni"
+    ul: "Nieposortowana lista"
+    ol: "Posortowana lista"
+    bq: "Cytat"
+    unbq: "Usuń cytat"
+    table: "Tabela"
+    pre: "Sformatowany tekst"
+    link: "Odnośnik do strony Wiki"
+    img: "Obraz"
+    edit: "Edytuj"
+    preview: "Podgląd"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml
index 967e87fca..ab335a8fa 100644
--- a/config/locales/pt-BR.yml
+++ b/config/locales/pt-BR.yml
@@ -1516,6 +1516,27 @@ pt-BR:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  # Translated by: Alexandre da Silva <simpsomboy@gmail.com>
+  jstoolbar:
+    strong: "Negrito"
+    em: "Itálico"
+    ins: "Sublinhado"
+    del: "Excluído"
+    code: "Código Inline"
+    h1: "Cabeçalho 1"
+    h2: "Cabeçalho 2"
+    h3: "Cabeçalho 3"
+    precode: "Código destacado"
+    ul: "Lista não ordenada"
+    ol: "Lista ordenada"
+    bq: "Identar"
+    unbq: "Remover identação"
+    table: "Tabela"
+    pre: "Texto pré-formatado"
+    link: "Link para uma página Wiki"
+    img: "Imagem"
+    edit: "Editar"
+    preview: "Pré-visualizar"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/pt.yml b/config/locales/pt.yml
index 9e1a00ca0..3cffaf4b7 100644
--- a/config/locales/pt.yml
+++ b/config/locales/pt.yml
@@ -1504,6 +1504,27 @@ pt:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  # Translated by: Pedro Araújo <phcrva19@hotmail.com>
+  jstoolbar:
+    strong: "Negrito"
+    em: "Itálico"
+    ins: "Sublinhado"
+    del: "Apagado"
+    code: "Código Inline"
+    h1: "Cabeçalho 1"
+    h2: "Cabeçalho 2"
+    h3: "Cabeçalho 3"
+    precode: "Highlighted code"
+    ul: "Lista não ordenada"
+    ol: "Lista ordenada"
+    bq: "Citação"
+    unbq: "Remover citação"
+    table: "Table"
+    pre: "Texto pré-formatado"
+    link: "Link para uma página da Wiki"
+    img: "Imagem"
+    edit: "Editar"
+    preview: "Pré-visualizar"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/ro.yml b/config/locales/ro.yml
index 94282965b..e24f89aa4 100644
--- a/config/locales/ro.yml
+++ b/config/locales/ro.yml
@@ -1506,6 +1506,26 @@ ro:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Bold"
+    em: "Italic"
+    ins: "Subliniat"
+    del: "Șters"
+    code: "Fragment de cod"
+    h1: "Heading 1"
+    h2: "Heading 2"
+    h3: "Heading 3"
+    precode: "Highlighted code"
+    ul: "Listă pe puncte"
+    ol: "Listă ordonată"
+    bq: "Citează"
+    unbq: "Fără citat"
+    table: "Table"
+    pre: "Text preformatat"
+    link: "Trimitere către o pagină wiki"
+    img: "Imagine"
+    edit: "Editează"
+    preview: "Previzualizare"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/ru.yml b/config/locales/ru.yml
index b28035520..568b2dae2 100644
--- a/config/locales/ru.yml
+++ b/config/locales/ru.yml
@@ -1580,6 +1580,26 @@ ru:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Жирный"
+    em: "Курсив"
+    ins: "Подчеркнутый"
+    del: "Зачеркнутый"
+    code: "Вставка кода"
+    h1: "Заголовок 1"
+    h2: "Заголовок 2"
+    h3: "Заголовок 3"
+    precode: "Подсветка кода"
+    ul: "Маркированный список"
+    ol: "Нумерованный список"
+    bq: "Цитата"
+    unbq: "Удалить цитату"
+    table: "Таблица"
+    pre: "Заранее форматированный текст"
+    link: "Ссылка на страницу в Wiki"
+    img: "Вставка изображения"
+    edit: "Редактирование"
+    preview: "Предпросмотр"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/sk.yml b/config/locales/sk.yml
index 2e243cf5f..65fcc605d 100644
--- a/config/locales/sk.yml
+++ b/config/locales/sk.yml
@@ -1500,6 +1500,26 @@ sk:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Tučné"
+    em: "Kurzíva"
+    ins: "Podčiarknuté"
+    del: "Preškrtnuté"
+    code: "Zobrazenie kódu"
+    h1: "Nadpis 1"
+    h2: "Nadpis 2"
+    h3: "Nadpis 3"
+    precode: "Highlighted code"
+    ul: "Odrážkový zoznam"
+    ol: "Číslovaný zoznam"
+    bq: "Odsadenie"
+    unbq: "Zrušiť odsadenie"
+    table: "Table"
+    pre: "Predformátovaný text"
+    link: "Odkaz na wikistránku"
+    img: "Obrázok"
+    edit: "Upraviť"
+    preview: "Náhľad"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/sl.yml b/config/locales/sl.yml
index 66e26edce..5e5cfeb84 100644
--- a/config/locales/sl.yml
+++ b/config/locales/sl.yml
@@ -1511,6 +1511,26 @@ sl:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Krepko"
+    em: "Poševno"
+    ins: "Podčrtano"
+    del: "Izbrisano"
+    code: "Koda med vrsticami"
+    h1: "Naslov 1"
+    h2: "Naslov 2"
+    h3: "Naslov 3"
+    precode: "Highlighted code"
+    ul: "Neurejen seznam"
+    ol: "Urejen seznam"
+    bq: "Citat"
+    unbq: "Odstrani citat"
+    table: "Table"
+    pre: "Predoblikovano besedilo"
+    link: "Povezava na Wiki stran"
+    img: "Slika"
+    edit: "Uredi"
+    preview: "Predogled"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/sq.yml b/config/locales/sq.yml
index b4cba8884..fe789dc06 100644
--- a/config/locales/sq.yml
+++ b/config/locales/sq.yml
@@ -1473,6 +1473,26 @@ sq:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Strong"
+    em: "Italic"
+    ins: "Underline"
+    del: "Deleted"
+    code: "Inline Code"
+    h1: "Heading 1"
+    h2: "Heading 2"
+    h3: "Heading 3"
+    precode: "Highlighted code"
+    ul: "Unordered list"
+    ol: "Ordered list"
+    bq: "Quote"
+    unbq: "Remove Quote"
+    table: "Table"
+    pre: "Preformatted text"
+    link: "Link to a Wiki page"
+    img: "Image"
+    edit: "Edit"
+    preview: "Preview"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/sr-YU.yml b/config/locales/sr-YU.yml
index 5da905c8a..c138e3b59 100644
--- a/config/locales/sr-YU.yml
+++ b/config/locales/sr-YU.yml
@@ -1513,6 +1513,26 @@ sr-YU:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Podebljano"
+    em: "Kurziv"
+    ins: "Podvučeno"
+    del: "Obrisano"
+    code: "Ugrađeni kôd"
+    h1: "Naslov 1"
+    h2: "Naslov 2"
+    h3: "Naslov 3"
+    precode: "Highlighted code"
+    ul: "Lista nabrajanja"
+    ol: "Uređena lista"
+    bq: "Pod navodnicima"
+    unbq: "Ukloni navodnike"
+    table: "Table"
+    pre: "Prethodno formatiran tekst"
+    link: "Veza prema Wiki strani"
+    img: "Slika"
+    edit: "Izmeni"
+    preview: "Pregled"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/sr.yml b/config/locales/sr.yml
index 4ad7cb584..147acd598 100644
--- a/config/locales/sr.yml
+++ b/config/locales/sr.yml
@@ -1512,6 +1512,26 @@ sr:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Подебљано"
+    em: "Курзив"
+    ins: "Подвучено"
+    del: "Обрисано"
+    code: "Уграђени кôд"
+    h1: "Наслов 1"
+    h2: "Наслов 2"
+    h3: "Наслов 3"
+    precode: "Highlighted code"
+    ul: "Листа набрајања"
+    ol: "Уређена листа"
+    bq: "Под наводницима"
+    unbq: "Уклони наводнике"
+    table: "Table"
+    pre: "Претходно форматиран текст"
+    link: "Веза према Wiki страни"
+    img: "Слика"
+    edit: "Измени"
+    preview: "Преглед"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/sv.yml b/config/locales/sv.yml
index d165ecd88..4565900c0 100644
--- a/config/locales/sv.yml
+++ b/config/locales/sv.yml
@@ -1475,6 +1475,26 @@ sv:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Fet"
+    em: "Kursiv"
+    ins: "Understruken"
+    del: "Genomstruken"
+    code: "Kod"
+    h1: "Rubrik 1"
+    h2: "Rubrik 2"
+    h3: "Rubrik 3"
+    precode: "Highlighted code"
+    ul: "Osorterad lista"
+    ol: "Sorterad lista"
+    bq: "Citat"
+    unbq: "Ta bort citat"
+    table: "Table"
+    pre: "Förformaterad text"
+    link: "Länk till en wikisida"
+    img: "Bild"
+    edit: "Ändra"
+    preview: "Förhandsgranska"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/ta-IN.yml b/config/locales/ta-IN.yml
index 94870bfaa..9cbcfb622 100644
--- a/config/locales/ta-IN.yml
+++ b/config/locales/ta-IN.yml
@@ -1466,6 +1466,26 @@ ta-IN:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "வலுவான"
+    em: "சாய்வு"
+    ins: "அடிக்கோடிட்டு"
+    del: "நீக்கப்பட்டது"
+    code: "இன்லைன் குறியீடு"
+    h1: "தலைப்பு 1"
+    h2: "தலைப்பு 2"
+    h3: "தலைப்பு 3"
+    precode: "சிறப்பம்சமாக குறியீடு"
+    ul: "வரிசைப்படுத்தப்படாத பட்டியல்"
+    ol: "வரிசைப்படுத்தப்பட்ட பட்டியல்"
+    bq: "மேற்கோள்"
+    unbq: "மேற்கோளை அகற்று"
+    table: "அட்டவணை"
+    pre: "முன்பே வடிவமைக்கப்பட்ட உரை"
+    link: "விக்கி பக்கத்துடன் இணைக்கவும்"
+    img: "படம்"
+    edit: "திருத்து"
+    preview: "முன்னோட்டம்"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/th.yml b/config/locales/th.yml
index 992850c11..09926ebaa 100644
--- a/config/locales/th.yml
+++ b/config/locales/th.yml
@@ -1507,6 +1507,26 @@ th:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "หนา"
+    em: "เอียง"
+    ins: "ขีดเส้นใต้"
+    del: "ขีดฆ่า"
+    code: "โค๊ดโปรแกรม"
+    h1: "หัวข้อ 1"
+    h2: "หัวข้อ 2"
+    h3: "หัวข้อ 3"
+    precode: "Highlighted code"
+    ul: "รายการ"
+    ol: "ลำดับเลข"
+    bq: "Quote"
+    unbq: "Remove Quote"
+    table: "Table"
+    pre: "รูปแบบข้อความคงที่"
+    link: "เชื่อมโยงไปหน้า Wiki อื่น"
+    img: "รูปภาพ"
+    edit: "แก้ไข"
+    preview: "ตัวอย่างก่อนจัดเก็บ"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/tr.yml b/config/locales/tr.yml
index e59d90374..db7e22cee 100644
--- a/config/locales/tr.yml
+++ b/config/locales/tr.yml
@@ -1510,6 +1510,24 @@ tr:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Kalın"
+    em: "İtalik"
+    ins: "Altı çizgili"
+    del: "Silinmiş"
+    code: "Satır içi kod"
+    h1: "Başlık 1"
+    h2: "Başlık 2"
+    h3: "Başlık 3"
+    precode: "Highlighted code"
+    ul: "Sırasız liste"
+    ol: "Sıralı liste"
+    table: "Table"
+    pre: "Preformatted text"
+    link: "Wiki sayfasına bağlantı"
+    img: "Resim"
+    edit: "Düzenle"
+    preview: "Önizleme"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/uk.yml b/config/locales/uk.yml
index 5298e0a3d..05d2b17bc 100644
--- a/config/locales/uk.yml
+++ b/config/locales/uk.yml
@@ -1499,6 +1499,26 @@ uk:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Жирний"
+    em: "Курсив"
+    ins: "Підкреслений"
+    del: "Закреслений"
+    code: "Інлайн код"
+    h1: "Заголовок 1"
+    h2: "Заголовок 2"
+    h3: "Заголовок 3"
+    precode: "Виділений код"
+    ul: "Ненумерованний список"
+    ol: "Нумерований список"
+    bq: "Цитування"
+    unbq: "Видалити цитування"
+    table: "Table"
+    pre: "Попередньо відформатований текст"
+    link: "Посилання на сторінку Wiki"
+    img: "Зображення"
+    edit: "Редагувати"
+    preview: "Попередній перегляд"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/vi.yml b/config/locales/vi.yml
index beb52851c..23ae71f71 100644
--- a/config/locales/vi.yml
+++ b/config/locales/vi.yml
@@ -1515,6 +1515,26 @@ vi:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "Đậm"
+    em: "Nghiêng"
+    ins: "Gạch chân"
+    del: "Xóa"
+    code: "Mã chung dòng"
+    h1: "Tiêu đề 1"
+    h2: "Tiêu đề 2"
+    h3: "Tiêu đề 3"
+    precode: "Highlighted code"
+    ul: "Danh sách không thứ tự"
+    ol: "Danh sách có thứ tự"
+    bq: "Trích dẫn"
+    unbq: "Bỏ trích dẫn"
+    table: "Table"
+    pre: "Mã nguồn"
+    link: "Liên kết đến trang wiki"
+    img: "Ảnh"
+    edit: "Sửa"
+    preview: "Xem trước"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml
index 7aa6d50e1..5f1b282ed 100644
--- a/config/locales/zh-TW.yml
+++ b/config/locales/zh-TW.yml
@@ -1525,6 +1525,26 @@
   reaction_text_x_other_users:
     one: 1 位其他用戶
     other: "%{count} 位其他用戶"
+  jstoolbar:
+    strong: "粗體"
+    em: "斜體"
+    ins: "底線"
+    del: "刪除線"
+    code: "程式碼"
+    h1: "標題 1"
+    h2: "標題 2"
+    h3: "標題 3"
+    precode: "反白程式碼"
+    ul: "項目清單"
+    ol: "編號清單"
+    bq: "引文"
+    unbq: "取消引文"
+    table: "表格"
+    pre: "已格式文字"
+    link: "連結至 Wiki 頁面"
+    img: "圖片"
+    edit: "編輯"
+    preview: "預覽"
   text_setting_gravatar_default_initials_html: 用戶姓名的首字母縮寫將被送往 <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     用以產生其大頭貼.
   permission_view_project: 檢視專案
diff --git a/config/locales/zh.yml b/config/locales/zh.yml
index 34b89889e..b47cfb43f 100644
--- a/config/locales/zh.yml
+++ b/config/locales/zh.yml
@@ -1447,6 +1447,26 @@ zh:
   reaction_text_x_other_users:
     one: 1 other
     other: "%{count} others"
+  jstoolbar:
+    strong: "粗体"
+    em: "斜体"
+    ins: "下划线"
+    del: "删除线"
+    code: "程序代码"
+    h1: "标题 1"
+    h2: "标题 2"
+    h3: "标题 3"
+    precode: "Highlighted code"
+    ul: "无序列表"
+    ol: "排序列表"
+    bq: "引用"
+    unbq: "删除引用"
+    table: "表格"
+    pre: "格式化文本"
+    link: "连接到 Wiki 页面"
+    img: "图片"
+    edit: "编辑"
+    preview: "预览"
   text_setting_gravatar_default_initials_html: Users' initials are sent to <a href="https://www.gravatar.com">https://www.gravatar.com</a>
     to generate their avatars.
   permission_view_project: View projects
diff --git a/lib/redmine/views/labelled_form_builder.rb b/lib/redmine/views/labelled_form_builder.rb
index f364ca8e2..ecf199dd7 100644
--- a/lib/redmine/views/labelled_form_builder.rb
+++ b/lib/redmine/views/labelled_form_builder.rb
@@ -65,4 +65,16 @@ class Redmine::Views::LabelledFormBuilder < ActionView::Helpers::FormBuilder
                           :class => (@object && @object.errors[field].present? ? "error" : nil),
                           :for => (@object_name.to_s + "_" + field.to_s))
   end
+
+  def text_area(method, options={})
+    data = options[:data] || {}
+    wiki_toolbar = data.delete(:wiki_toolbar)
+    if wiki_toolbar
+      @template.add_wiki_toolbar(data) do
+        text_area(method, options)
+      end
+    else
+      super
+    end
+  end
 end
diff --git a/lib/redmine/wiki_formatting.rb b/lib/redmine/wiki_formatting.rb
index ce6097646..29e478842 100644
--- a/lib/redmine/wiki_formatting.rb
+++ b/lib/redmine/wiki_formatting.rb
@@ -89,7 +89,7 @@ module Redmine
       def to_html(format, text, options = {})
         text =
           if Setting.cache_formatted_text? && text.size > 2.kilobytes && cache_store &&
-              cache_key = cache_key_for(format, text, options[:object], options[:attribute])
+            cache_key = cache_key_for(format, text, options[:object], options[:attribute])
             # Text retrieved from the cache store may be frozen
             # We need to dup it so we can do in-place substitutions with gsub!
             cache_store.fetch cache_key do
@@ -141,12 +141,6 @@ module Redmine
       end
 
       module Helper
-        def wikitoolbar_for(field_id, preview_url = preview_text_path)
-        end
-
-        def heads_for_wiki_formatter
-        end
-
         def initial_page_content(page)
           page.pretty_title.to_s
         end
diff --git a/lib/redmine/wiki_formatting/common_mark/helper.rb b/lib/redmine/wiki_formatting/common_mark/helper.rb
index f75f1fc0e..a597afa39 100644
--- a/lib/redmine/wiki_formatting/common_mark/helper.rb
+++ b/lib/redmine/wiki_formatting/common_mark/helper.rb
@@ -21,43 +21,9 @@ module Redmine
   module WikiFormatting
     module CommonMark
       module Helper
-        def wikitoolbar_for(field_id, preview_url = preview_text_path)
-          heads_for_wiki_formatter
-
-          javascript_tag(
-            "var wikiToolbar = new jsToolBar(document.getElementById('#{field_id}')); " \
-            "wikiToolbar.setHelpLink('#{escape_javascript help_wiki_syntax_path}'); " \
-            "wikiToolbar.setPreviewUrl('#{escape_javascript preview_url}'); " \
-            "wikiToolbar.draw();"
-          )
-        end
-
         def initial_page_content(page)
           "# #{page.pretty_title}"
         end
-
-        def heads_for_wiki_formatter
-          unless @heads_for_wiki_formatter_included
-            toolbar_language_options = User.current && User.current.pref.toolbar_language_options
-            lang =
-              if toolbar_language_options.nil?
-                UserPreference::DEFAULT_TOOLBAR_LANGUAGE_OPTIONS
-              else
-                toolbar_language_options.split(',')
-              end
-            content_for :header_tags do
-              javascript_include_tag('jstoolbar/jstoolbar') +
-              javascript_include_tag('jstoolbar/common_mark') +
-              javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language.to_s.downcase}") +
-              javascript_tag(
-                "var wikiImageMimeTypes = #{Redmine::MimeType.by_type('image').to_json};" \
-                  "var userHlLanguages = #{lang.to_json};"
-              ) +
-              stylesheet_link_tag('jstoolbar')
-            end
-            @heads_for_wiki_formatter_included = true
-          end
-        end
       end
     end
   end
diff --git a/lib/redmine/wiki_formatting/textile/helper.rb b/lib/redmine/wiki_formatting/textile/helper.rb
index ad6e53992..5f01115b8 100644
--- a/lib/redmine/wiki_formatting/textile/helper.rb
+++ b/lib/redmine/wiki_formatting/textile/helper.rb
@@ -21,41 +21,9 @@ module Redmine
   module WikiFormatting
     module Textile
       module Helper
-        def wikitoolbar_for(field_id, preview_url = preview_text_path)
-          heads_for_wiki_formatter
-
-          javascript_tag(
-            "var wikiToolbar = new jsToolBar(document.getElementById('#{field_id}')); " \
-              "wikiToolbar.setHelpLink('#{escape_javascript help_wiki_syntax_path}'); " \
-              "wikiToolbar.setPreviewUrl('#{escape_javascript preview_url}'); wikiToolbar.draw();"
-          )
-        end
-
         def initial_page_content(page)
           "h1. #{page.pretty_title}"
         end
-
-        def heads_for_wiki_formatter
-          unless @heads_for_wiki_formatter_included
-            toolbar_language_options = User.current && User.current.pref.toolbar_language_options
-            lang =
-              if toolbar_language_options.nil?
-                UserPreference::DEFAULT_TOOLBAR_LANGUAGE_OPTIONS
-              else
-                toolbar_language_options.split(',')
-              end
-            content_for :header_tags do
-              javascript_include_tag('jstoolbar/jstoolbar') +
-              javascript_include_tag('jstoolbar/textile') +
-              javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language.to_s.downcase}") +
-              javascript_tag(
-                "var wikiImageMimeTypes = #{Redmine::MimeType.by_type('image').to_json};" \
-                  "var userHlLanguages = #{lang.to_json};") +
-              stylesheet_link_tag('jstoolbar')
-            end
-            @heads_for_wiki_formatter_included = true
-          end
-        end
       end
     end
   end
diff --git a/test/helpers/application_helper_test.rb b/test/helpers/application_helper_test.rb
index aad2c4542..749ae2026 100644
--- a/test/helpers/application_helper_test.rb
+++ b/test/helpers/application_helper_test.rb
@@ -2397,6 +2397,20 @@ class ApplicationHelperTest < Redmine::HelperTest
     assert_equal '', result
   end
 
+  def test_text_area_with_text_formatting
+    with_settings :text_formatting => 'textile' do
+      element = text_area_tag 'sample', '', data: {wiki_toolbar: Setting.text_formatting.present?}
+      assert_match 'texteditor', element
+    end
+  end
+
+  def test_text_area_without_text_formatting
+    with_settings :text_formatting => '' do
+      element = text_area_tag 'sample', '', data: {wiki_toolbar: Setting.text_formatting.present?}
+      assert_no_match 'texteditor', element
+    end
+  end
+
   private
 
   def wiki_links_with_special_characters
@@ -2427,23 +2441,4 @@ class ApplicationHelperTest < Redmine::HelperTest
                   :class => "wiki-page new"),
     }
   end
-
-  def test_list_autofill_data_attributes
-    with_settings :text_formatting => 'textile' do
-      expected = {
-        controller: "list-autofill",
-        action: "keydown->list-autofill#handleEnter",
-        list_autofill_target: "input",
-        list_autofill_text_formatting_param: "textile"
-      }
-
-      assert_equal expected, list_autofill_data_attributes
-    end
-  end
-
-  def test_list_autofill_data_attributes_with_blank_text_formatting
-    with_settings :text_formatting => '' do
-      assert_equal({}, list_autofill_data_attributes)
-    end
-  end
 end
diff --git a/test/integration/layout_test.rb b/test/integration/layout_test.rb
index a1c918ae0..3132396d0 100644
--- a/test/integration/layout_test.rb
+++ b/test/integration/layout_test.rb
@@ -57,8 +57,11 @@ class LayoutTest < Redmine::IntegrationTest
     Role.anonymous.add_permission! :add_issues
 
     get '/projects/ecookbook/issues/new'
-    assert_select "head script:match('src',?)", %r{/assets/jstoolbar/jstoolbar-\w+.js}
-    assert_include "var userHlLanguages = #{UserPreference::DEFAULT_TOOLBAR_LANGUAGE_OPTIONS.to_json};", response.body
+
+    text = Nokogiri::HTML5.fragment(response.body).css('script[type=importmap]').text
+    importmap = JSON.parse(text)
+    assert_match /jstoolbar-\w+/, importmap['imports']['jstoolbar']
+    assert_select 'template#code-highlighting-template'
   end
 
   def test_calendar_header_tags
-- 
2.39.5

