Плагин для быстрой загрузки изображений в 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){// метод, который принимает на себя клик по кнопке в markItUpvar self =this;
 
        this.dialog= $(document).find(".inline_upload_container");
 
        if(!this.dialog.size()){// нашего скрытого div нету DOM'a, создаем// создаем форму с iframe внутри невидимого div и прикрепляем его к bodythis.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"})
 */publicfunction 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!',);}
 
    returnnew Response(json_encode($response));}

Таким образом мы получили простейший в использовании плагин. Юзер по клику на иконку картинки сразу получает диалог выбора файла и, после выбора, готовую строчку в редакторе.

Данный пример очень простой. По желанию его можно усложнить, например, вместо скрытой формы — показывать ее в диалоге, отправка на сервер нескольких картинок и т.д.