1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/iceui-iceEditor

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
README.md 30 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 27.02.2025 14:57 f1bca47

IceEditor мощный текстовый редактор

Официальный

Описание

IceEditor — это минималистичный стильный редактор мощного текстового редактора с очень маленькими размерами, который не зависит от других библиотек. Весь редактор состоит всего из одного файла, но при этом имеет множество полезных функций! Минималистический дизайн, простой, быстрый, использование которого не требует включения jQuery, font, css... и других файлов, так как весь редактор представляет собой одну JavaScript-библиотеку, поддерживающую загрузку изображений и вложений! Поддерживает добавление музыки и видео! Группа IceEditor в официальном чате: 324415936

Преимущества

  • Чисто native разработка, без каких-либо зависимостей, прозрачность и честность
  • Откликчивый дизайн, адаптированный под любое разрешение экрана
  • Весь редактор состоит из одного файла, что делает его эффективным и удобным
  • Минималистический стильный дизайн, простой и быстрый

Последние обновления

IceEditor v1.1.9

  • 12 апреля 2022 года
  • [Улучшение] Загрузка изображений, добавлено поддержка установки ширины и высоты
  • 7 марта 2022 года
  • [Улучшение] Интерфейс стилей, исправлено создание лишних p-тегов при вставке
  • 23 октября 2021 года
  • [Добавлено] globalCss, используемое для настройки глобальных стилей
  • [Добавлено] globalIcon, используемое для настройки глобальных значков
  • 5 июля 2021 года
  • [Улучшение] Стили загрузки вложений
  • 23 июня 2021 года
  • [Добавлено] Настройка ajax.formData, filesUpload.formData, imgUpload.formData
  • [Добавлено] Настройка filesUpload.name, imgUpload.name для имени загружаемого контроллера
  • [Добавлено] Контроль данных после успешной отправки ajax, filesUpload, imgUpload
  • [Изменено] Архитектура изменена на ES6 класс

IceEditor v1.1.8

  • 10 мая 2021 года
  • [Исправлено] inputCallback слушатель ввода, добавлено слушатель вставки
  • 18 декабря 2020 года
  • [Добавлено] notMenu опция, которая позволяет скрывать меню из панели инструментов, например: e.notMenu['table']
  • 17 декабря 2020 года
  • [Исправлено] Дополнительный video-name тег при отправке формы
  • [Улучшено] Автоматическое выбор источника видео, поддерживается YouTube, Bilibili, Xigua, пользовательское видео
  • 20 ноября 2020 года
  • [Исправлено] Проблема конфигурации нескольких линий в меню
  • [Исправлено] Сохранение выделенного текста после применения стилей
  • 9 ноября 2020 года
  • [Исправлено] Проблема синтаксического анализа при вставке кода в режиме rich text
  • [Исправлено] Проблема вставки в режиме raw text
  • 6 ноября 2020 года
  • [Добавлено] Кнопка вставки текста
  • [Исправлено] Проблема создания лишних p, br тегов при вставке или загрузке файлов
  • [Исправлено] Исчезновение стиля разделителя
  • 23 октября 2020 года
  • [Добавлено] Фильтрация тегов
  • [Добавлено] Фильтрация стилей
  • [Добавлено] Вставка текста Word
  • [Добавлено] Вставка текста rich text
  • [Добавлено] Вставка изображений
  • [Добавлено] Автоматическая загрузка изображений из сети на сервер
  • [Добавлено] Слушатели событий загрузки изображений и вложений

IceEditor v1.1.7

  • 25 сентября 2020 года
  • [Исправлено] Проблема стиля line
  • 9 сентября 2020 года
  • [Добавлено] Возможность отключения ввода
  • [Добавлено] Возможность включения ввода
  • [Добавлено] Слушатель ввода
  • 2 сентября 2020 года
  • [Исправлено] Проблема прогресс-бара AJAX
  • 27 июля 2020 года
  • [Изменено] Все семантические теги, размеры шрифтов, цвета, жирный шрифт, зачеркивание, курсив... заменены на span теги, используемые для определения стилей через style
  • [Добавлено] Совпадение текущего положения курсора со светящимся пунктом меню
  • [Добавлено] Конфигурация AJAX
  • [Добавлено] Возможность вставки эмодзи и его конфигурация
  • 25 июля 2020 года
  • [Исправлено] Проблема смещения p-тегов в режиме raw text
  • [Исправлено] Проблема создания лишних p-тегов при вставке в режиме raw text
  • Просмотреть остальные обновления

Уведомление

Фреймворк IceUI уже включает этот редактор.

Внимание

Ссылка на iceEditor.js должна быть расположена вне тега head, рекомендуется располагать её внутри тега body или сразу после него!

Введение

Использование

<!-- Также можно использовать textarea, помещённую в форму, чтобы она могла быть отправлена -->
<!-- <textarea id="editor" name="content"> Добро пожаловать в IceEditor мощный текстовый редактор </textarea> -->
<div id="editor"> Добро пожаловать в IceEditor мощный текстовый редактор </div>
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/iceuinet/iceEditor/src/iceEditor.min.js"></script>
// Шаг 1: Создайте экземпляр объекта
ice.editor('content', function(e) {

  // Шаг 2: Настройте URL загрузки изображений или вложений
  // Если ваш проект написан на PHP, вы можете использовать upload.php
  // Для других языков программирования вам потребуется самостоятельно реализовать обработку multipart/form-data форм
  // Обратите внимание, что ответ должен содержать JSON-строку без лишней информации:
  // url: адрес файла
  // name: имя файла (включая расширение)
  // error: если загрузка прошла успешно, то значение должно быть 0, иначе сообщение об ошибке будет показано пользователю
  // Например, загрузка двух изображений:
  // [
  //   {url:'/upload/img/153444.jpg', name:'153444.jpg', error:0},
  //   {url:'/upload/img/153445.jpg', name:'153445.jpg', error:'Запрещён тип файла'}
  // ]
  e.uploadUrl = "/iceEditor/src/upload.php";

  // Шаг 3: Настройте меню (по умолчанию загружаются все пункты, поэтому настройка не требуется)
  e.menu = [
    'backColor',                 // Цвет фона текста
    'fontSize',                  // Размер шрифта
    'foreColor',                 // Цвет текста
    'bold',                      // Жирный шрифт
    'italic',                    // Курсив
    'underline',                 // Подчеркивание
    'strikeThrough',             // Зачёркивание
    'justifyLeft',               // Выравнивание по левому краю
    'justifyCenter',             // Выравнивание по центру
    'justifyRight',              // Выравнивание по правому краю
    'indent',                    // Увеличение отступа
    'outdent',                   // Уменьшение отступа
    'insertOrderedList',         // Вставка упорядоченного списка
    'insertUnorderedList',       // Вставка непронумерованного списка
    'superscript',               // Верхний индекс
    'subscript',                 // Нижний индекс
    'createLink',                // Создание гиперссылки
    'unlink',                    // Удаление гиперссылки
    'hr',                        // Горизонтальная черта
    'table',                     // Таблица
    'files',                     // Вложения
    'music',                     // Музыка
    'video',                     // Видео
    'insertImage',               // Изображение
    'removeFormat',              // Удаление формата
    'code',                      // Код
    'line'                       // Разделитель меню
  ];

  // Шаг 4: Создайте редактор, если нет специальных настроек, этот шаг не нужен
  e.create();

});
```#### Установка размеров редактора
```javascript
ice.editor('content', function(e) {
  e.width = '700px';   // Ширина
  e.height = '300px';  // Высота
  e.create();
});

Отключение редактора

// Отключение во время инициализации
ice.editor('content', function(e) {
  e.disabled = true;
  e.create();

  // Отключение ввода методом
  e.inputDisabled();

  // Включение ввода
  e.inputEnable();
});

Получение содержимого

ice.editor('content', function(e) {
  console.log(e.getHTML());  // Получение содержимого в формате HTML
  console.log(e.getText());  // Получение содержимого в формате текста
  console.log(e.getValue());  // То же самое, что getHTML(), но используется для удобства запоминания
});

Установка содержимого

ice.editor('content', function(e) {
  e.setValue('Привет мир!');
});

Добавление содержимого

ice.editor('content', function(e) {
  e.addValue('Привет мир!');
});

Слушатель ввода содержимого

ice.editor('content', function(e) {
  // html: формат HTML
  // text: чистый текст
  e.inputCallback(function(html, text) {
    // console.log(this.getHTML()) Метод this внутри равен e, html равно this.getHTML()
    console.log(html);
  });
});

Отключение возможности вставки скриншотов

ice.editor('content', function(e) {
  e.screenshot = false;
});

Отключение возможности автоматической загрузки скриншотов

// Отключение этого параметра приведёт к тому, что изображения будут отображаться в base64 формате
ice.editor('content', function(e) {
  e.screenshotUpload = false;
});

Автоматическая загрузка изображений из интернета на сервер

ice.editor('content', function(e) {
  e.imgAutoUpload = false;
});

Автоматическая загрузка изображений из интернета на сервер, белый список доменов

ice.editor('content', function(e) {
  // По умолчанию установлено значение false, автоматически фильтруется текущий домен, если изображение сохраняется на стороннем хранилище, например Qiniu, можно добавить этот домен, чтобы избежать повторной загрузки
  // Настройка других доменов в виде массива, например Qiniu
  e.imgDomain = ['www.qiniu.com'];
});

Включение вставки текста из Word

ice.editor('content', function(e) {
  e.pasteText = false;
});
```#### Настройка списка эмодзи
```javascript
ice.editor('content', function(e){  //type имеет два значения, img и text. Тип img представляет собой эмодзи в виде изображения, где content — это адрес изображения, а тип text представляет собой текстовое эмодзи, где content — это сам текстовый эмодзи.
  //Ниже приведены простые примеры, собранные с интернета и отредактированные одним из пользователей. Это только для справки. Если вы считаете, что ваши права нарушены, пожалуйста, свяжитесь со мной через QQ: 308018629, и я немедленно приму меры! Если у вас есть рекомендации по бесплатному использованию открытых эмодзи, вы можете связаться со мной или зайти в официальную группу QQ 324415936, и я внедрю эти эмодзи в редактор.
  e.face=[{
      title: 'Sina',
      type: 'img',
      list: [
        {title:'Хаха',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/0b/tootha_thumb.gif'},
        {title:'Хаха',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/6a/laugh.gif'},
        {title:'Милашка',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/14/tza_thumb.gif'},
        {title:'Печаль',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/af/kl_thumb.gif'},
        {title:'Копание носа',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/a0/kbsa_thumb.gif'},
        {title:'Удивление',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/f4/cj_thumb.gif'},
        {title:'Скромность',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/6e/shamea_thumb.gif'},
        {title:'Подмигивание',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/c3/zy_thumb.gif'},
        {title:'Закрытый рот',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/29/bz_thumb.gif'},
        {title:'Брезгливость',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/71/bs2_thumb.gif'},
        {title:'Любовь',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/6d/lovea_thumb.gif'},
        {title:'Расстройство',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/9d/sada_thumb.gif'},
        {title:'Тихий смех',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/19/heia_thumb.gif'},
        {title:'Поцелуй',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/8f/qq_thumb.gif'},
        {title:'Болезнь',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/b6/sb_thumb.gif'},
        {title:'Очень радостно',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/58/mb_thumb.gif'},
        {title:'Не хочу тебя знать',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/17/ldln_thumb.gif'},
        {title:'Правое хмыканье',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/98/yhh_thumb.gif'},
        {title:'Левое хмыканье',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/6d/zhh_thumb.gif'},
        {title:'Шшш',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/a6/x_thumb.gif'},
        {title:'Плохо',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/af/cry.gif'},
        {title:'Обидчивость',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/73/wq_thumb.gif'},
        {title:'Рвота',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/9e/t_thumb.gif'},
        {title:'Издевательство',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/f3/k_thumb.gif'},
        {title:'Обнимание',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/27/bba_thumb.gif'},
        {title:'Гнев',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/7c/angrya_thumb.gif'},
        {title:'Вопрос',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/5c/yw_thumb.gif'},
        {title:'Жадность',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/a5/cza_thumb.gif'},
        {title:'Прощай',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/70/88_thumb.gif'},
        {title:'Размышление',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/e9/sk_thumb.gif'},
        {title:'Пот',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/24/sweata_thumb.gif'},
        {title:'Усталость',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/7f/sleepya_thumb.gif'},
        {title:'Сон',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/6b/sleepa_thumb.gif'},
        {title:'Деньги',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/90/money_thumb.gif'},
        {title:'Отсутствие надежды',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/0c/sw_thumb.gif'},
        {title:'Крутость',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/40/cool_thumb.gif'},
        {title:'Цветочность',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/8c/hsa_thumb.gif'},
        {title:'Хмыкание',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/49/hatea_thumb.gif'},
        {title:'Аплодисменты',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/36/gza_thumb.gif'},
        {title:'Обморок',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/d9/dizzya_thumb.gif'},
        {title:'Грусть',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/1a/bs_thumb.gif'},
        {title:'Страх',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/62/crazya_thumb.gif'},
        {title:'Чёрная линия',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/91/h_thumb.gif'},
        {title:'Злобность',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/6d/yx_thumb.gif'},
        {title:'Гневная брань',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/89/nm_thumb.gif'},
        {title:'Сердце',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/40/hearta_thumb.gif'},
        {title:'Грустное сердце',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/ea/unheart.gif'},
        {title:'OK',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/d6/ok_thumb.gif'},
        {title:'Ура!',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/d9/ye_thumb.gif'},
        {title:'Хорошо',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/d8/good_thumb.gif'},
        {title:'Не надо',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/c7/no_thumb.gif'},
        {title:'Приятно',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/d0/z2_thumb.gif'},
        {title:'Приди',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/40/come_thumb.gif'},
        {title:'Слабость',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/d8/sad_thumb.gif'},
        {title:'Вечерний светильник',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/91/lazu_thumb.gif'},
        {title:'Торт',content:'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/6a/cake.gif'}
      ]
    }, {
      title: 'Текст',
      type: 'text',
      list: [
        {title:'Радость',content:'(^_^)'},
        {title:'Не могу больше',content:'(>_<)'},
        {title:'Брезгливость',content:'(¬、¬)'},
        {title:'Грусть',content:'(*>﹏<*)'},
        {title:'Милашка',content:'(。◕‿◕。)'},
        {title:'Напрасно',content:'╮(╯_╰)╭'},
        {title:'Удивление',content:'╰(*°▽°*)╯'},
        {title:'Музыка',content:'♪(^∇^*)'},
        {title:'Скромность',content:'(✿◡‿◡)'},
        {title:'Сон',content:'(∪。∪)..zzZ'},
        {title:'Эгоизм',content:'(o≖◡≖)'},
        {title:'Пот',content:'(ーー゛)'}
      ]
    }];
  e.create();
});



#### AJAX Callback
```javascript
ice.editor('content',function(e){
  //AJAX xhr settings
  e.ajax.xhr = function(xhr){};
  //AJAX formData settings
  e.ajax.formData = function(formData){};
  //AJAX timeout callback
  e.ajax.timeout = function(xhr){};
  //AJAX success callback
  e.ajax.success = function(res,xhr){};
  //AJAX error callback
  e.ajax.error = function(res,xhr){};
  //AJAX complete callback regardless of success or failure
  e.ajax.complete = function(res,xhr){};
  //AJAX progress callback
  e.ajax.progress = function(percent,evt,xhr){};
  //File upload
  e.filesUpload.name = 'file'; //upload control name setting, input's name
  e.filesUpload.formData = function(formData){}; //formData setting
  e.filesUpload.success = function(res){}; //success
  e.filesUpload.error = function(res,xhr){}; //failure
  e.filesUpload.complete = function(res,xhr){}; //callback regardless of success or failure
  //Image upload
  e.imgUpload.name = 'file'; //upload control name setting, input's name
  e.imgUpload.formData = function(formData){}; //formData setting
  e.imgUpload.success = function(res){}; //success
  e.imgUpload.error = function(res,xhr){}; //failure
  e.imgUpload.complete = function(res,xhr){}; //callback regardless of success or failure
});
```#### Разработка плагинов
```javascript
ice.editor('content',function(e){
  e.addValue('Привет мир!');
  //┌────────────────────────────────────────────────────────────────────────
  //│ e.plugin(options) параметры объяснены
  //│────────────────────────────────────────────────────────────────────────
  //│ options     {json}
  //│  ├ name     {строка}   [обязательно] Уникальное имя для пункта меню, может быть настроено для отображения и порядка в меню
  //│  ├ menu     {строка}   [обязательно] Кнопка, отображаемая на панели меню, может быть значком или текстом
  //│  ├ data     {строка}   Команда для execCommand
  //│  ├ id       {строка}   ID для кнопки меню
  //│  ├ css      {строка}   Класс для кнопки меню
  //│  ├ style    {строка}   Стили для этого плагина, записанные как CSS стили
  //│  ├ dropdown {строка}   HTML для выпадающего меню, если задано, то это параметр невалиден
  //│  ├ popup    {json} JSON для всплывающего окна
  //│  │    ├ width   {число}    Ширина всплывающего окна
  //│  │    ├ height  {число}    Высота всплывающего окна
  //│  │    ├ title   {строка}   Название всплывающего окна
  //│  │    └ content {строка}   Содержание всплывающего окна, может быть HTML
  //│  ├ click   {функция} Событие нажатия для кнопки
  //│  └ success {функция} Метод автоматически выполняется после успешной установки плагина
  //└────────────────────────────────────────────────────────────────────────
  //Тип выпадающего меню
  e.plugin({
      menu:'Язык',
      name:'codeLanguages',
      dropdown:`
          <div class="iceEditor-codeLanguages" style="padding:10px 20px;">
              <div>Передняя часть, пожалуйста, обратитесь к iceCode.js</div>
              <select>
                  <option disabled selected>Язык программирования</option>
                  <option value ="php">PHP</option>
                  <option value ="js">JavaScript</option>
                  <option value="html">HTML</option>
                  <option value="java">Java</option>
              </select>
          </div>`,
      success:function(e,z){
          //Получаем кнопку из содержимого
          var select = e.getElementsByTagName('select')[0];
          //Устанавливаем событие нажатия
          select.onchange=function(){
              var str = z.getSelectHTML().replace(/<\s*\/p\s*>/ig,"\r\n").replace(/<[^>]+>/ig,'').trim();
              var pre = z.c('pre');
              pre.className = 'iceCode:'+select.value;
              pre.innerHTML = str.length?str:"\r\n";
              z.setHTML(pre,true);
              select.getElementsByTagName('option')[0].selected = true;
          }   
      }
  });
```  //Функциональный способ
  e.plugin({
    menu: 'функциональный способ',
    name: 'click',
    click: function(e, z) {
      z.setText('привет мир');
    }
  });
  //Команда execCommand
  e.plugin({
    menu: 'команда удаления',
    name: 'del',
    data: 'delete'
  });
  //Тип выпадающего меню
  e.plugin({
    menu: 'выпадающее меню',
    name: 'dropdown',
    dropdown: '<div class="iceEditor-exec" data="copy" style="padding:10px 20px;">Копировать выделенный текст</div>'
  });
  //Тип всплывающего окна
  e.plugin({
    menu: 'всплывающий пример',
    name: 'popup',
    style: '.demo-p {margin-bottom: 10px}.demo-button {padding: 0 10px}',
    popup: {
      width: 230,
      height: 120,
      title: 'Я являюсь demo',
      content: '<p class="demo-p">Вставить "привет мир!" в позицию курсора.</p><button class="demo-button" type="button">OK</button>',
    },
    success: function(e, z) {
      //Получаем кнопку внутри контента
      var btn = e.getElementsByTagName('button')[0];
      //Устанавливаем событие клика
      btn.onclick = function() {
        z.setText('привет мир');
        //Закрываем это всплывающее окно
        e.close()
      }  
    }
  });
  e.create();
});

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/iceui-iceEditor.git
git@api.gitlife.ru:oschina-mirror/iceui-iceEditor.git
oschina-mirror
iceui-iceEditor
iceui-iceEditor
master