Плагін для швидкого завантаження зображень у MarkItUp

Поговоримо про написання плагіна для WYSIWYG-редактора markItUp. Завдання плагіна повинно полягати в швидкому та легкому завантаженні зображень на сервер, щоб їх можна було вставити в текст пізніше (у формі тегу img).

Все почалося з запиту «Завантаження зображень у markItUp». Пошук звичайних готових рішень майже ні до чого не призвів. Єдине рішення, яке спрацювало – це плагін https://gist.github.com/783934, але у нього є кілька недоліків:

  • Багато залежностей від «додаткових» плагінів jQuery.
  • Занадто громіздкі JS та CSS.

Отже, ми могли або змінити (або спростити) цей плагін, або написати свій. Після початку роботи в перший спосіб, я майже непомітно перейшов до другого і в результаті з'явився цей JS-код:

var InlineUpload ={
    dialog:null,
    options:{
        form_class:'inline_upload_form',// клас форми
        action:'/posts/upload',// URL, на який буде надіслано POST із завантаженим файлом
        iframe:'inline_upload_iframe'// ім'я iframe},
    display:function(hash){// метод, який передбачає клік на кнопку в markItUpvar self =this;
 
        this.dialog= $(document).find(".inline_upload_container");
 
        if(!this.dialog.size()){// в нашому прихованому div немає DOM, давайте створимо його// форма з iframe всередині невидимого div та прикріпимо її до тіла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// щоб показати системний діалог вибору файлу
        $("input[name='form[inlineUploadFile]']").focus();// Хак для chrome та інших
        $("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="'%20+%20response.src%20+%20'" 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):

/**
 * Завантажити фото
 *
 * @return string
 * @Route("/posts/upload", name="blog_post_upload_image")
 * @Method({"POST"})
 */publicfunction uploadImageAction(){// Створити валідатор$collectionConstraint=new Collection(array('inlineUploadFile'=&gt;new Image(array('mimeTypes'=&gt;array("image/png","image/jpeg","image/gif"))),));
 
    // Створити "на льоту" форму з нашим валідатором$form=$this-&gt;createFormBuilder(null,array('csrf_protection'=&gt;false,'validation_constraint'=&gt;$collectionConstraint))-&gt;add('inlineUploadFile','file')-&gt;getForm();
 
    $form-&gt;bindRequest($this-&gt;get('request'));if($form-&gt;isValid()){// Обробка файлу$file=$form-&gt;get('inlineUploadFile')-&gt;getData();$ext=$file-&gt;guessExtension();
 
        if($ext==''){$response=array('msg'=&gt;'Файл виглядає трохи підозріло! Я не хочу його приймати.',);}else{// Зберегти файл$uploadDir=realpath($this-&gt;get('kernel')-&gt;getRootDir().'/../web/uploads/images');$newName=uniqid().'.'.$ext;$file-&gt;move($uploadDir,$newName);$info=getImageSize($uploadDir.'/'.$newName);
 
            // Повернути таку відповідь, якщо все добре$response=array('status'=&gt;'успіх','src'=&gt;'/uploads/images/'.$newName,'width'=&gt;$info[0],'height'=&gt;$info[1],);}}else{// Повернути таку відповідь, якщо все погано$response=array('msg'=&gt;'Ваш файл недійсний!',);}
 
    returnnew Response(json_encode($response));}

Таким чином, ми отримали найпростіший у використанні плагін. Користувач, натискаючи на іконку зображення, одразу отримує діалог вибору файлу, а після вибору має готовий рядок в редакторі.

Цей приклад дуже простий. Якщо бажаєте, його можна ускладнити, наприклад, замість прихованих форм - показати їх у діалозі, надіславши серверу кілька зображень тощо.