четверг, 13 мая 2010 г.

Создание плагина к онлайн HTML редактору TinyMCE

Положение дел с существующими онлайн WYSIWYG редакторами таково, что не один из них не работает идеально. Глючат, вставляют лишние переводы строк и т д. А самое главное не делают некоторых нужных вещей, или делают не так. Но разве это проблема? мы ведь с вами программисты, а код редакторов открыт!
Правда, решая эту проблему некоторые мои коллеги бодро лезут в код редактора, очень бысто успокаиваясь в нагромождении сжатого JavaScript. Как правило лучше программа от такого хирургического вмешательства работать не начинала. Между тем есть вполне нормальный способ добавить в редактор нужный функционал - написать к нему плагин. Чем сейчас и займёмся.

Сама задача - обеспечить возможность вставки втекст гиперссылки. Конечно, в TinyMCE она давно решена, с помощью плагина advlink, номне нужны были специальные ссылки из ограниченного набора url. Впрочем на этом я останавливаться не буду, давайте сначала просто реализуем вставку ссылок. Плагин будет называться mics_link.

Структура плагина для TinyMCE, находящегося в папке tiny_mce/plugins/, следующая:
/css
  Специфичные для данного плагина CSS.
/img
Тут всё понятно.
/js
Скрипты, обрабатывающие данные диалоговой формы плагина. Можно сказать реализация бизнес логики =).
/langs
Словари для локализации. каждому языку тут соответствуют два файла:
 en.js - здесь содержиться описание плагина, появляющееся при наведении курсора на его иконку в интерфейсе редактора.
 en_dlg.js - здесь все языковые опции в надписях форм, диалогов, в общем всё остальное.
/editor_plugin.js
Скрипт управляющий загрузкой плагина и поведением диалогового окна. (код в сжатом виде)
/editor_plugin_src.js
Тоже самое, только не в сжатом виде.
/somedialog.htm
HTML представления диалогового окна плагина. 
Из всего этого единственным обязательным файлом является editor_plugin.js. При работе плагина выполняется именно он, но как было сказано выше JavaScript в нём упакован и работать с таким кодом довольно затруднительно. Поэтому и существует его несжаты й двойник - editor_plugin_src.js.
Теперь хорошая новость - все эти папки и файлы нам с нуля создавать не надо. В один из плагинов  TinyMCE - example, представляет собой шаблон для изготовления своего плагина.
Сохраним папку plugins/exampl поднужным нам названием (у меня plugins/mics_link) и займёмся редактированием editor_plugin_src.js
Шаблон для плагина, в упрощённом виде выглядит следующим образом:


(function() {
 // Lзагружаем языковый пакет
 tinymce.PluginManager.requireLangPack('mics_link');
 tinymce.create('tinymce.plugins.MicsLink', {
  /**
   * инициализируем плагин 
   * ed - экземляр редактора 
   * url - абсолютный url плагина 
   */
  init : function(ed, url) {
   // Регестрирум комаду, для открытия окна плагина
    ed.addCommand('mceMics', function() {
    //  параметры окна
    ed.windowManager.open({
     file: url + '/dialog.htm',
     width: 320 + ed.getLang('mics_link.delta_width', 0),
     height: 120 + ed.getLang('mics_link.delta_height', 0),
     inline: 1
    }, {
     // данные для плагина - его абсолютный URL
     // и аргументы (ели они нужны)
     plugin_url : url,
     some_custom_arg : 'custom arg'
    });
   });

   // регестрируем иконку плагина
   ed.addButton('mics_link', {
    title: 'mics_link.desc',
    cmd: 'mceMics',
    image: url + '/img/mics_link.gif'
   });

    ed.onNodeChange.add(function(ed, cm, n) {
    cm.setActive('mics_link', n.nodeName == 'IMG');
   });
  },

   createControl : function(n, cm) {
   return null;
  },

  // "Выходные данные" плагина
  getInfo : function() {
   return {
    longname : 'Mics Link Plugin',
    author: 'Geol',
    authorurl: 'http://webgeol.blogspot.com/',
    version: "1.0"
   };
  }
 });

 // Регестрируем плагин
   tinymce.PluginManager.add('mics_link', tinymce.plugins.MicsPlugin);
})();

Небольшие пояснения (хотя я почти всё написал комментариях).
Сначала мы загружаем файлы локализации. Потом вызывается метод tinymce.create(), который создает экземпляр класса нашего плагина. Затем, в финкции, вызываемой при инициализации плагина, регестрируем комманду mceMics, загружающею плагин. методом addButton мы добавляем пиктограммудля панели инструментов редаактора и связываем с ний команду (разумеется иконка пиктограммы должна быть создана и помещена в папку image/ плагина.
Секцию createControl  мы сейчас расмтривать не будем (она нужна для создания элементов управления более сложных чем кнопки), ну а в getInfo помещаются сведения об авторе и версии плагина.
Теперь нужно превратить этот скрипт  в неудобоваримый вид, то есть сжать JavaScript и сохранить под именем editor_plugin.js (исполняться будет именно он, незжатый аналог нужен только для удобного редактирования). Для этого мы воспользуемся утилитой  JavaScript Comressor (http://javascriptcompressor.com/ Рис 1)


Рис 1 
"Компилируем" код

Теперь редактируем somedialog.htm. Для начала переименуем его в mics_link.php, в соответствии с названием плагина (строго говоря это не обязательно, но таковы рекомендации). Теперь верстаем нашу форму.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
 <title>{#micslink_dlg.title}</title>
 <script type="text/javascript" src="../../tiny_mce_popup.js">
</script>
 <script type="text/javascript" src="js/dialog.js">
</script>
</head>
<body>

<form onsubmit="MicslincDialog.insert();return false;" action="#">
<p>Вставка гиперссылки.</p>
<p>Текст ссылки: 
<input id="mtext" name="mtext" type="text" class="text" /></p>
<p>URL: 
<input id="mhref" name="mhref" type="text" class="text" /></p>
<select id="targ" name="targ"
onchange="this.form.targ.value=this.options[this.selectedIndex].value;">
     <option value="1"> В этом фрейме </option>
     <option value="2"> В новом окне </option>
      </select>
 <div class="mceActionPanel">
  <input type="button" id="insert" name="insert" value="{#insert}" onclick="ExampleDialog.insert();" >
  <input type="button" id="cancel" name="cancel" value="{#cancel}" onclick="tinyMCEPopup.close();" />
 </div>
</form>

</body>
</html>

Теперь нужно написать обработчик формы. Он довольно прост и находится в в файле /js/dialog.js:


tinyMCEPopup.requireLangPack();

var MicsDialog = {
init : function() {
 var f = document.forms[0];
 f.mtext.value = tinyMCEPopup.editor.selection.getContent({
 format : 'text'
 });
 f.mhref.value = '';
 },

 insert : function() {
  if(document.forms[0].targ.value==1){
  tr='target=_self';  }
  else  {
  tr='target=_blank';
  }
  mlink= "<a "+tr+" href="+document.forms[0].mhref.value+">";
  mlink+=  document.forms[0].mtext.value+"</a>";
  tinyMCEPopup.editor.execCommand('mceInsertContent', false, mlink);
  tinyMCEPopup.close();
 }
};
tinyMCEPopup.onInit.add(MicsDialog.init, MicsDialog);

Что мы натворили (а точнее слегка изменили шаблонный скрипт)?
Последней строкой мы добавляем в обработчик onInit, вызываемый при инициализации объекта tinyMCEPopup, вызов метода init, только что созданного объекта MicsDialog. Этот метод устанавливает начальные значения полей формы. В поле текста помещает выделенный в редакторе текст, а полю URL присваивает пустое значение. (Зачем нужно последнее? В дальнейшем (за пределами этой статьи), мы неприменно расширим функционал пплагина для работы по изменению уже созданных гиперссылок.)
Метод inser, вызываемый по нажатию соответствующей кнопки на нашей форме, формирует гиперссылку. и вызывает два метода tinyMCEPopup. Это tinyMCEPopup.editor.execCommand(), вставляющий сформированный контент в текст документа, и tinyMCEPopup.close(), закрывающий вслывающее окошко с нашей формой.
Всё, плагин готов. Осталось не забыть про него при вызове редактора. Для этого, в код вызова редактора включаем соответсвующие пункты:

tinyMCE.init({
  // General options
  mode : "textareas",
  theme : "advanced",
  plugins : "mics_link, safari,syntaxhl,pagebreak,spellchecker,style,layer,..."

Тут мы включили наш плагин, теперь разместим кнопку вызова на панели редактора:
// Theme options
theme_advanced_buttons1 : "spellchecker,syntaxhl,mics_link,|,...",
theme_advanced_buttons2 : "cut,cop ..."
Теперь запускаем редактор. На панели инструментов должна отобразиться новая иконка (Рис 2)

Рис 2
Иконка запуска плагина появилась на панели инструментов

При клике на неё откроется и сам плагин (рис 3)

14 комментариев:

  1. Хорошая статейка, помогла в работе ;)

    ОтветитьУдалить
  2. Жду продолжения... ))

    ОтветитьУдалить
  3. Постараюсь разродиться на этой неделе.

    ОтветитьУдалить
  4. сделал, точнее скопировал, по Вашей статье плагин.
    в опере перестает отображать все плагины ВООБЩЕ, а в IE просто тупо его нет )=
    опера: http://savepic.org/762630.htm
    IE: http://savepic.org/740127.htm
    а вот и код: http://savepic.org/743199.htm

    ОтветитьУдалить
  5. Судя по всему какая-то беда с подключением плагина. Есть пль JavaScript ошибки? Изчезает -ли пробле ма в опере, если подключить плагин, но не помещать его в тулбар?

    ОтветитьУдалить
  6. И доступна-ли иконка? вроде дело именно в этом.

    ОтветитьУдалить
  7. проблема не исчезла.tinyMCE скачен с офсайта

    ОтветитьУдалить
  8. и иконка не доступна

    ОтветитьУдалить
  9. так может она неправипльно прописана? или просто прав на файл не хватает?

    ОтветитьУдалить
  10. прав не хватает? о_О

    ОтветитьУдалить
  11. Ну вы ведь закачивали иконку дпля своего плагина? Достаточно ли прав у веб сервера, чтобы отдавать её?

    ОтветитьУдалить
  12. Спасибо очень большое!!! Код по какой то причине не заработал?!! Но статья в целом подсказала куда копать. Уже написал плагин для вставки изображений. Всё кратко и лаконично. Побольше бы таких статей!!!

    ОтветитьУдалить
  13. zZiron: У тебя в самом начале ошибочка. Ты регаешь MicsPlugin а создаешь MicsLink, исправь. Спасибо большое за пример, по трахаться конечно пришлось, так как я вообще не веб разработчик, а просто решил прикрутить TinyMCE к 1C, но всё равно огромное спасибо

    ОтветитьУдалить
  14. Кому еще актуально вот где проблема была MicsLinkPlugin (а конкретней Plugin)

    tinymce.create('tinymce.plugins.MicsLinkPlugin'

    tinymce.PluginManager.add('mics_link', tinymce.plugins.MicsLinkPlugin);

    вроде после такого как раз должен заработать

    ОтветитьУдалить