Плагин для быстрой загрузки изображений в MarkItUp

Поговорим о написании плагина для WYSIWYG редактора markItUp. Задачей плагина будет быстрая и удобная загрузка картинок на сервер с последующей вставкой в текст (в виде тега img).
Все началось с тикета «Аплоад картинок в markItUp». Поиск готовых решений почти ничего не дал. Единственное решение, которое работало - это плагин https://gist.github.com/783934, но у него есть несколько минусов:
- Много зависимостей от «лишних» jQuery плагинов.
- Громоздкий JS и CSS.
Можно было или переделать (упростить) этот плагин или написать свой. Начав идти по первому пути я незаметно перешел ко второму и в итоге получился вот такой JS код:
var InlineUpload = { dialog: null, options: { form_class: 'inline_upload_form', // class формы action: '/posts/upload', // урл на который будет отправлен POST c загруженым файлом iframe: 'inline_upload_iframe' // имя iframe }, display: function(hash) { // метод, который принимает на себя клик по кнопке в markItUp var self = this; this.dialog = $(document).find(".inline_upload_container"); if (!this.dialog.size()) { // нашего скрытого div нету DOM'a, создаем // создаем форму с iframe внутри невидимого div и прикрепляем его к body this.dialog = $([ '<div style="opacity:0;position:absolute;" class="inline_upload_container"><form class="',this.options.form_class,'" action="',this.options.action,'" target="',this.options.iframe,'" method="post" enctype="multipart/form-data">', '<input name="form[inlineUploadFile]" type="file" /></form>' + '<iframe id="',this.options.iframe,'" name="',this.options.iframe,'" class="',this.options.iframe,'" src="about:blank" width="0" height="0"></iframe></div>', ].join('')); this.dialog.appendTo(document.body); } // делаем клик по input[type=file] в скрытом div'e // чтобы показать системный диалог выборки файла $("input[name='form[inlineUploadFile]']").focus(); // хак для хрома и прочих $("input[name='form[inlineUploadFile]']").trigger('click'); // после того, как файл был выбран, отправляем нашу скрытую форму на сервер $("input[name='form[inlineUploadFile]']").live('change', function(){ if ($(this).val() != '') { // если файл небыл выбран, ничего страшного не произойдет $('.' + self.options.form_class).submit(); } }); // ответ будет отдан в скрытый iframe $('.' + this.options.iframe).bind('load', function() { var responseJSONStr = $(this).contents().find('body').html(); if (responseJSONStr != '') { // сервер вернул нам ответ, будем его парсить var response = $.parseJSON(responseJSONStr); if (response.status == 'success') { // если все хорошо var block = ['<img src="' + response.src + '" width="' + response.width + '" height="' + response.height + '" alt="" class=""/>']; $.markItUp({replaceWith: block.join('')} ); // добавляем тег img в текст } else { alert(response.msg); // сообщение об ошибке } self.cleanUp(); // cleanUp() убирает скрытый div с формой и iframe из DOM } }); }, cleanUp: function() { $("input[name='form[inlineUploadFile]']").die('change'); this.dialog.remove(); } };
Чтобы подключить наш плагин, нужно в настройках панелей markItUp добавить новую кнопку:
{ name:'PictureUpload', key:'P', beforeInsert: function(markItUp) { InlineUpload.display(markItUp) } },
А на стороне бэкэнда добавить обработчик для загрузки файлов (пример экшена для Symfony2):
/** * Upload photo * * @return string * @Route("/posts/upload", name="blog_post_upload_image") * @Method({"POST"}) */ public function uploadImageAction() { // создаем валидатор $collectionConstraint = new Collection(array( 'inlineUploadFile' => new Image(array('mimeTypes' => array("image/png", "image/jpeg", "image/gif"))), )); // создаем "на лету" форму с нашим валидатором $form = $this->createFormBuilder(null, array( 'csrf_protection' => false, 'validation_constraint' => $collectionConstraint )) ->add('inlineUploadFile', 'file') ->getForm(); $form->bindRequest($this->get('request')); if ($form->isValid()) { // обрабатываем файл $file = $form->get('inlineUploadFile')->getData(); $ext = $file->guessExtension(); if ($ext == '') { $response = array( 'msg' => 'Какой-то подозрительный файл! Я не хочу его принимать.', ); } else { // сохраняем файл $uploadDir = realpath($this->get('kernel')->getRootDir() . '/../web/uploads/images'); $newName = uniqid() . '.' . $ext; $file->move($uploadDir, $newName); $info = getImageSize($uploadDir . '/' . $newName); // возвращаем такой ответ, если все хорошо $response = array( 'status' => 'success', 'src' => '/uploads/images/' . $newName, 'width' => $info[0], 'height' => $info[1], ); } } else { // возвращаем такой ответ, если все плохо $response = array( 'msg' => 'Your file is not valid!', ); } return new Response(json_encode($response)); }
Таким образом мы получили простейший в использовании плагин. Юзер по клику на иконку картинки сразу получает диалог выбора файла и, после выбора, готовую строчку в редакторе.
Данный пример очень простой. По желанию его можно усложнить, например, вместо скрытой формы — показывать ее в диалоге, отправка на сервер нескольких картинок и т.д.