9

Markdown编辑器选用https://simplemde.com
它是一款纯js实现的markdown编辑器。缺点不支持图片上传。那我们就得改造它。
simplemde是基于codemirror编辑器的.
先介绍基本:
codemirror文档:http://codemirror.net/doc/man...
simplemde文档:https://github.com/NextStepWe...
API文档:
拖拽:
https://developer.mozilla.org...
https://developer.mozilla.org...

粘贴:
https://developer.mozilla.org...
https://developer.mozilla.org...

注意一点:目前firefox与chrome比较新的版本都实现了这些API。

paste事件

绑定的元素不一定是input,普通的div也是可以绑定的,如果是给document绑定了,就相当于全局了,任何时候的粘贴操作都会触发。
获取事件对象ClipboardEvent
先写一下事件绑定的代码

pasteEle.addEventListener("paste", function (e){
    if ( !(e.clipboardData && e.clipboardData.items) ) {
        return ;
    }
 
    for (var i = 0, len = e.clipboardData.items.length; i < len; i++) {
        var item = e.clipboardData.items[i];
 
        if (item.kind === "string") {
            item.getAsString(function (str) {
                // str 是获取到的字符串
            })
        } else if (item.kind === "file") {
            var pasteFile = item.getAsFile();
            // pasteFile就是获取到的文件
        }
    }
});

粘贴事件提供了一个clipboardData的属性,如果该属性有items属性,那么就可以查看items中是否有图片类型的数据了。
clipboardData介绍
介绍一下clipboardData对象,它实际上是一个DataTransfer类型的对象,DataTransfer 是拖动产生的一个对象,但实际上粘贴事件也是它。
属性介绍
dropEffect String 默认是 none
effectAllowed String 默认是 uninitialized
files FileList 在粘贴操作时为空List
items DataTransferItemList 剪切板中的各项数据
types Array 剪切板中的数据类型。

DataTransferItem
items是一个DataTransferItemList对象,自然里面都是DataTransferItem类型的数据了。
DataTransferItem有两个属性kind和type

kind 一般为string或者file
type 具体的数据类型,例如具体是哪种类型字符串或者哪种类型的文件,即MIME-Type,常见的值有text/plain、text/html、Files。
方法

getAsFile 空 如果kind是file,可以用该方法获取到文件
getAsString 回调函数 如果kind是string,可以用该方法获取到字符串,字符串需要用回调函数得到,回调函数的第一个参数就是剪切板中的字符串
综合

// demo 程序将粘贴事件绑定到 document 上
document.addEventListener("paste", function (e) {
    var cbd = e.clipboardData;
    //var ua = window.navigator.userAgent;
 
    for(var i = 0; i < cbd.items.length; i++) {
        var item = cbd.items[i];
        if(item.kind == "file"){
            var blob = item.getAsFile();
            if (blob.size === 0) {
                return;
            }
            // blob 就是从剪切板获得的文件 可以进行上传或其他操作
        }
    }
}, false);

drop事件

DragEvent
DragEvent.dataTransfer

dropEffect String 默认是 none
effectAllowed String 默认是 uninitialized
files FileList
items DataTransferItemList 剪切板中的各项数据
types Array 剪切板中的数据类型。
DataTransferItem
items是一个DataTransferItemList对象,自然里面都是DataTransferItem类型的数据了。

DataTransferItem有两个属性kind和type

kind 一般为string或者file
type 具体的数据类型,例如具体是哪种类型字符串或者哪种类型的文件,即MIME-Type,常见的值有images/*、text/plain、text/html、Files。
方法

getAsFile 空 如果kind是file,可以用该方法获取到文件
getAsString 回调函数 如果kind是string,可以用该方法获取到字符串,字符串需要用回调函数得到,回调函数的第一个参数就是剪切板中的字符串

dropEle.addEventListener("drop", function (e){
    var data = new FormData();
    var files = event.dataTransfer.files;
    var i = 0;
    var len = files.length;
    while (i < len){
        data.append("file" + i, files[i]);
         i++;
    }
    var xhr = new XMLHttpRequest();
    xhr.open("post", "/upload", true);
    xhr.onreadystatechange = function(){
         if (xhr.readyState == 4){
             alert(xhr.responseText);
         }
     };
     xhr.send(data);
});

阻止浏览器默认打开拖拽文件的行为:参考这里

window.addEventListener("drop",function(e){
  e = e || event;
  console.log(e);
  //e.preventDefault();
  if (e.target.tagName != "textarea") {  // check wich element is our target
    e.preventDefault();
  }  
},false);

理论知识说完了。下面开始实验改造codemirror

codemirror支持粘贴和拖拽上传

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>codemirror</title>
    <link rel="stylesheet" href="codemirror.css">
    <script src="codemirror.js"></script>
</head>
<body>
    <textarea name="aaa" id="aaa"></textarea>
    <script>
    var textarea=document.getElementById("aaa")
      var editor = CodeMirror.fromTextArea(textarea, {
        lineNumbers: true
      });
  editor.on("paste",function(editor,e){
      // console.log(e.clipboardData)
      if(!(e.clipboardData&&e.clipboardData.items)){
          alert("该浏览器不支持操作");
          return;
      }
      for (var i = 0, len = e.clipboardData.items.length; i < len; i++) {
        var item = e.clipboardData.items[i];
       // console.log(item.kind+":"+item.type);
        if (item.kind === "string") {
            item.getAsString(function (str) {
                // str 是获取到的字符串
            })
        } else if (item.kind === "file") {
            var pasteFile = item.getAsFile();
            // pasteFile就是获取到的文件
            console.log(pasteFile);
            fileUpload(pasteFile);
        }
    }
  });
  editor.on("drop",function(editor,e){
      // console.log(e.dataTransfer.files[0]);
      if(!(e.dataTransfer&&e.dataTransfer.files)){
          alert("该浏览器不支持操作");
          return;
      }
      for(var i=0;i<e.dataTransfer.files.length;i++){
          console.log(e.dataTransfer.files[i]);
          fileUpload(e.dataTransfer.files[i]);
      }
      e.preventDefault();
  });
  //文件上传
  function fileUpload(fileObj){
      var data = new FormData();
      data.append("file",fileObj);
      var xhr = new XMLHttpRequest();
    xhr.open("post", "/upload", true);
    xhr.onreadystatechange = function(){
         if (xhr.readyState == 4){
             alert(xhr.responseText);
         }
     };
     xhr.send(data);
  }
  //阻止浏览器默认打开拖拽文件的行为
  window.addEventListener("drop",function(e){
  e = e || event;
  e.preventDefault();
  if (e.target.tagName == "textarea") {  // check wich element is our target
    e.preventDefault();
  } 
},false);
</script>
</html>

附Codemirror常用事件与方法
参考这里
1.onChange(instance,changeObj):codeMirror文本被修改后触发。
instance是一个当前的codemirror对象,changeObj是一个{from,to,text,removed}对象。其中from,to分别表示起始行对象和结束行对象,行对象包括ch:改变位置距离行头的间隔字符,line:改变的行数。text是一个字符串数组表示被修改的文本内容,即你输入的内容。

2.onBeforeChange(instance,changObj):内容改变前被调用
3.onCursorActivity(instance):当鼠标点击内容区、选中内容、修改内容时被触发
4.onKeyHandled:(instance,name,event):当一个都dom元素的事件触发时调用,name为操作名称。
5.onInputRead(insatance,changeObj):当一个新的input从隐藏的textara读取出时调用
6.onBeforeSelectionChange(instance,obj):当选中的区域被改变时调用,obj对象是选择的范围和改变的内容(本人未测试成功)
7.onUpdate(instance):编辑器内容被改变时触发
8.onFocus(instance):编辑器获得焦点式触发
9.onBlur(instance):编辑器失去焦点时触发

常用方法:
getValue():获取编辑器文本内容
setValue(text):设置编辑器文本内容
getRange({line,ch},{line,ch}):获取指定范围内的文本内容第一个对象是起始坐标,第二个是结束坐标
replaceRange(replaceStr,{line,ch},{line,ch}):替换指定区域的内容
getLine(line):获取指定行的文本内容
lineCount():统计编辑器内容行数
firstLine():获取第一行行数,默认为0,从开始计数
lastLine():获取最后一行行数
getLineHandle(line):根据行号获取行句柄
getSelection():获取鼠标选中区域的代码
replaceSelection(str):替换选中区域的代码
setSelection({line:num,ch:num1},{line:num2,ch:num3}):设置一个区域被选中
somethingSelected():判断是否被选择
getEditor():获取CodeMirror对像
undo():撤销
redo():回退

simplemde支持粘贴和拖拽上传

var simplemde = new SimpleMDE({ element: document.getElementById("MyID") });
simplemde.codemirror.on("drop", function(editor,e){
    ...
});
simplemde.codemirror.on("paste",function(editor,e){
...
});

Blob对象转File对象

为了使用WebUploader这个文件上传组件,需要将粘贴得到的Blob对象转为File对象。
Blob 对象是包含有只读原始数据的类文件对象.File 接口基于 Blob,继承了 Blob 的功能,并且扩展支持用户计算机上的本地文件。

var blob=new Blob();
var file = new File([blob], "image.png", {type:"image/png"});

File构造器的第一个参数必须是数组

WebUploader文件上传

http://fex.baidu.com/webuploa...
创建Uploader对象

var uploader = WebUploader.Uploader({
    swf: 'path_of_swf/Uploader.swf',
 
    // 开起分片上传。
    chunked: true
});

监听fileQueued事件来实现进度UI构造:

// 当有文件被添加进队列的时候
uploader.on( 'fileQueued', function( file ) {
    var $list=$("#list");
    $list.append( '<div id="' + file.id + '" class="item">' +
        '<h4 class="info">' + file.name + '</h4>' +
        '<p class="state">等待上传...</p>' +
    '</div>' );
});

文件上传进度:

// 文件上传过程中创建进度条实时显示。
uploader.on( 'uploadProgress', function( file, percentage ) {
    var $li = $( '#'+file.id ),
    $percent = $li.find('.progress .progress-bar');
    // 避免重复创建
    if ( !$percent.length ) {
        $percent = $('<div class="progress progress-striped active">' +
          '<div class="progress-bar" role="progressbar" style="width: 0%">' +
          '</div>' +
        '</div>').appendTo( $li ).find('.progress-bar');
    }
 
    $li.find('p.state').text('上传中');
    $percent.css( 'width', percentage * 100 + '%' );
});

文件成功、失败处理:

uploader.on( 'uploadSuccess', function( file,data ) {
    $( '#'+file.id ).find('p.state').text('已上传');
});
 
uploader.on( 'uploadError', function( file ) {
    $( '#'+file.id ).find('p.state').text('上传出错');
});
 
uploader.on( 'uploadComplete', function( file ) {
    $( '#'+file.id ).find('.progress').fadeOut();
});

添加文件到队列并上传:

uploader.addFiles( file ) 
uploader.addFiles( [file1, file2 …] ) 

开始上传:

uploader.upload() 
//uploader.upload( file | fileId)

其他参考:
js获取剪切板内容,js控制图片粘贴
在线代码编辑器 CODEMIRROR 事件说明
javascript.ruanyifeng.com
https://developer.mozilla.org...


xbynet
1k 声望124 粉丝

不雨花犹落,无风絮自飞