搭建代码编辑器

ACE ,Code Mirror, monaco 三款代码编辑器,整体引用都差不多,monaco 是微软出品vscode也是采用的这个.它的智能提示会很方便,如果在nodejs环境下可以添加其它 type.d.ts 文件可以直接智能提示.

下面我是用vue搭的,有三种不同的模式,初始化直接设置就会自动加载,但是编辑器的资源需要你自己事先引入到页面中

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>

	<meta charset="utf-8" />
    <!--基础库-->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.4/ace.js"></script>
    <!--提示-->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.4/snippets/html.js"></script>
    <script src="editor.js"></script>
<style>
  
</style>
</head>
<body>
  
    <script>
        // codeMirror,monaco 
        initEditor('ace',{
            data:{
                theme:'ace/theme/chrome',
                themes:['ace/theme/chrome','ace/theme/monokai','ace/theme/solarized_dark']
            },
            mountedCallback:function(view){
               if (window.top !== window && window.top.registerEditor) {
                    window.top.registerEditor({
                        view: view
                    })
                }
            }
        })
      



    </script>
</body>
</html>

 

codemirror:

 

monaco: 

公共js:

(function(){


  function loadStyle(css){
      var style=document.createElement('style');
      try{
          style.appendChild(document.createTextNode(css));
      }catch(e){
          style.styleSheet.cssText=css;
      }
      document.getElementsByTagName('head')[0].appendChild(style);
  }
  function loadResource(url,resolve,reject){
      var type = url.match(/\.css$/) ? 'link' : 'script';
      var r = false;
      var h = document.getElementsByTagName('head')[0];
      var s = document.createElement(type);
  
      if (type === 'script') {
        s.type = 'text/javascript';
        s.src = url;
        s.async = true;
        s.onload = s.onreadystatechange = function onReadyStateChange() {
          // eslint-disable-line
          if (!r && (!s.readyState || s.readyState === 'complete')) {
            r = true;
            resolve(s);
          }
        };
        s.onerror = s.onabort = reject; // eslint-disable-line
      } else {
        s.setAttribute('rel', 'stylesheet');
        s.setAttribute('href', url);
        s.onload = function onLinkLoad() {
          // eslint-disable-line
          r = true;
          resolve(s);
        };
      }
      h.appendChild(s);
      
  }
  function createModules(modules){
      
      var loaded={};
      return function load(name,callback){
          var dependenciesStack=[];
          var set={};
          var mods=[];
          if(typeof name==='string'){
              name=[name];
          }
          for(var i=0;i<name.length;i++){
              add(name[i]);
              mods.push({
                  name:name[i],
                  dependenciesStack:dependenciesStack.slice(1)
              });
              dependenciesStack.length=0;
              set={};
          }
          function add(name){
              var m=modules[name];
              if(m&&!set.hasOwnProperty(name)){
                  var dependencies=m.dependencies||[];             
                  set[name]=true;
                  dependenciesStack.push(name)
                  if(typeof dependencies=='string'){
                      dependencies=[dependencies];
                  }
                  for(var i=0;i<dependencies.length;i++){
                      add(dependencies[i]);
                  }
              }
          }
       
          function loadResourceQueue(name,callback){
              var m=modules[name];
              if(loaded[name]===true){
                  callback();
                  return;
              }
              loaded[name]=true;
              var len=0,completeCount=0;
              function complete(){
                  completeCount++;
                  if(completeCount>=len&&callback){
                     callback()
                  }
              }
              if(m.js){
                  len++;
                  loadResource(m.js,complete,complete);
              }
               if(m.css){
                  len++;
                  loadResource(m.css,complete,complete);
              }
          }
  
          function load(m){
              var name=m.name,dependenciesStack=m.dependenciesStack;
  
              function complete(){
                  loadResourceQueue(name,finish)
              }
              function runLoad(){
                  var len=dependenciesStack.length;
                  if(len<=0){
                      complete();
                      return;
                  }
                  loadResourceQueue(dependenciesStack.pop(),runLoad)
              }
  
              runLoad();
          }
          var loadCount=0;
          function finish(){
              loadCount++;
              if(loadCount>=mods.length){
                  callback&&callback();
              }
          }
          for(var i=0;i<mods.length;i++){
              load(mods[i])
          }
      }
  }
  
  
  var loadModule=createModules({
       datgui:{
        js:"https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.js"
      },
     localforage:{
        js:"https://cdnjs.cloudflare.com/ajax/libs/localforage/1.7.3/localforage.js"
      },
      vconsole:{
          js:'https://cdnjs.cloudflare.com/ajax/libs/vConsole/3.3.0/vconsole.min.js'
      },
      logLevel:{
          js:'https://cdnjs.cloudflare.com/ajax/libs/loglevel/1.6.3/loglevel.min.js'
      },
      console:{
          js:'https://cdn.jsdelivr.net/npm/consola@2.9.0/dist/consola.browser.min.js'
      },
      store:{
          js:'https://cdnjs.cloudflare.com/ajax/libs/store.js/1.3.20/store.min.js'
      },
      jquery:{
          js:'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js'
      },
      vue:{
          js:'https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js',
          dependencies:[]
      },
      iview:{
          js:'https://cdnjs.cloudflare.com/ajax/libs/iview/3.4.1/iview.min.js',
          css:'https://cdnjs.cloudflare.com/ajax/libs/iview/3.4.1/styles/iview.css',
          dependencies:['vue','jquery']
      },
  
  })
  
  
  
  let template=`<div id="app">
  <Layout>
  <Layout>
      <Header class="header" :class="{'fixed-header':isFixed}">
     <Card>
     <Row type="flex" justify="end" align="middle" v-bind:style="{height:'40px',marginRight:'20px'}" v-bind:gutter="5">
     <i-col>
        <i-button @click="onClear">清空</i-button>
  </i-col>
   <i-col>
        <i-button @click="onAddResources">添加资源</i-button>
  </i-col>
  <i-col>
        <i-button @click="onRun">运行</i-button>
  </i-col>
  <i-col>
  <i-select  v-model="theme" @on-change="onThemeChange">
  <i-option v-for="name in themes" :key="name" :value="name">{{name | themeName}}</i-option>
  
  </i-select>
  </i-col>
  </Row>
     </Card>
      </Header>
      <Content :class="['content',isFixed?'fixed-content':'']">
      <Row >
      <i-col :xl="12" >
      <d-preview v-bind:resources="resources" ref="preview" v-bind:js="js" v-bind:css="css" v-bind:html="html"></d-preview>
      </i-col>
      <i-col :xl="12" >
          <Card>
        <Tabs v-bind:value="editorTab">
              <tab-pane label="html" name="html">
                  <d-editor v-model="html" v-on:on-change="onHtmlChange" v-bind:mode="htmlMode" v-bind:theme="theme"/>
              </tab-pane>
              <tab-pane label="javascript" name="javascript">
                      <d-editor v-model="js" v-on:on-change="onJsChange" v-bind:mode="jsMode"  v-bind:theme="theme" />
              </tab-pane>
              <tab-pane label="css" name="css">
                      <d-editor v-model="css" v-on:on-change="onCssChange" v-bind:mode="cssMode" v-bind:theme="theme" />
              </tab-pane>
       </Tabs>
      </Card>
      </i-col>
     
  </Row>
      </Content>
   <Footer>
   
   </Footer>
  </Layout>
   
  </Layout>
  
         <page-resource v-model="visibleResouce" @on-change="onResourcesChange" v-bind:value="resources" />
          </div>
  `;
  
  
  

     
function ShowAutocompletion(obj) { 
  // Disable default autocompletion for javascript
//  monaco.languages.typescript.javascriptDefaults.setCompilerOptions({ noLib: true  });

  // Helper function to return the monaco completion item type of a thing
  function getType(thing, isMember) {
    isMember =  (isMember == undefined) ? (typeof isMember == "boolean") ? isMember : false : false; // Give isMember a default value of false
  
    switch ((typeof thing).toLowerCase()) { 
      case "object": 
        return monaco.languages.CompletionItemKind.Class;

      case "function": 
        return (isMember) ? monaco.languages.CompletionItemKind.Method : monaco.languages.CompletionItemKind.Function;

      default: 
        return (isMember) ? monaco.languages.CompletionItemKind.Property : monaco.languages.CompletionItemKind.Variable;
    }
  }

  // Register object that will return autocomplete items 
  monaco.languages.registerCompletionItemProvider('javascript', {
    // Run this function when the period or open parenthesis is typed (and anything after a space)
    triggerCharacters: ['.', '('],

    // Function to generate autocompletion results
    provideCompletionItems: function(model, position, token) {
      // Split everything the user has typed on the current line up at each space, and only look at the last word
      var last_chars = model.getValueInRange({startLineNumber: position.lineNumber, startColumn: 0, endLineNumber: position.lineNumber, endColumn: position.column});
      var words = last_chars.replace("\t", "").split(" "); 
      var active_typing = words[words.length - 1]; // What the user is currently typing (everything after the last space)

      // If the last character typed is a period then we need to look at member objects of the obj object 
      var is_member = active_typing.charAt(active_typing.length - 1) == ".";

      // Array of autocompletion results
      var result = [];
          
      // Used for generic handling between member and non-member objects
      var last_token = obj; 
      var prefix = ''; 
    
      if (is_member) { 
        // Is a member, get a list of all members, and the prefix
        var parents = active_typing.substring(0, active_typing.length - 1).split("."); 
        last_token = obj[parents[0]]; 
        prefix = parents[0]; 

        // Loop through all the parents the current one will have (to generate prefix)
        for (var i = 1; i < parents.length; i++) { 
          if (last_token.hasOwnProperty(parents[i])) { 
            prefix += '.' + parents[i]; 
            last_token = last_token[parents[i]];
          } else { 
            // Not valid
            return result;
          }
        }
    
        prefix += '.';
      }
    
      // Get all the child properties of the last token
      for (var prop in last_token) { 
        // Do not show properites that begin with "__"
        if (last_token.hasOwnProperty(prop) && !prop.startsWith("__")) { 
          // Get the detail type (try-catch) incase object does not have prototype 
          var details = ''; 
          try { 
            details = last_token[prop].__proto__.constructor.name; 
          } catch (e) { 
            details = typeof last_token[prop]; 
          }
          
          // Create completion object
          var to_push = {
            label: prefix + prop,
            kind: getType(last_token[prop], is_member), 
            detail: details,     
            insertText: prop
          };

         

          // Change insertText and documentation for functions
          if (to_push.detail.toLowerCase() == 'function') { 
            to_push.insertText += "(";
            to_push.documentation = (last_token[prop].toString()).split("{")[0]; // Show function prototype in the documentation popup
          }

          // Add to final results
          result.push(to_push);
        }
      }

      return {
          suggestions:result
      }; 
    }
});
}
  
  function EditorBase(el){
      this.el=el;
      this.event=$({});
      this.theme='';
      this.mode='';
      this.value='';
  }
  EditorBase.prototype={
      constructor:EditorBase,
      setTheme:function(value){},
      setMode:function(value){},
      setValue:function(value){},
      getValue:function(){ return this.value;},
      clear:function(){},
      on:function(type,handler){
          this.event.on(type,function(e){
              handler.apply(this,Array.prototype.slice.call(arguments,1))
          })
      },
      emit:function(type){
          var args=Array.prototype.slice.call(arguments,1);
          this.event.triggerHandler(type,args)
      }
  }
  function inherits(childCtor, parentCtor) {
      childCtor.prototype = Object.create(parentCtor.prototype);
      childCtor.prototype.constructor = childCtor;
  }
  function extendClass(superClss,subProperties){
      function subClass(){
          superClss.apply(this,arguments);
          this.init&&this.init.apply(this,arguments)
      }
      inherits(subClass,superClss);
      Object.assign(subClass.prototype,subProperties);
      // for (let nextKey in nextSource) {
      //     // Avoid bugs when hasOwnProperty is shadowed
      //     if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
      //       to[nextKey] = nextSource[nextKey];
      //     }
      //   }
      return subClass;
  }
  
  var AceEditor=extendClass(EditorBase,{
      init:function(){
          var that=this;
          that.editor= ace.edit(this.el);
          this.editor.clearSelection();
          this.editor.on('change',function(a,editor){
              that.emit('change',that.editor.getValue())
          });
          ace.config.loadModule("ace/ext/language_tools", function() {
              that.editor.setOptions({
                      autoScrollEditorIntoView: true,
                      enableBasicAutocompletion: true,
                      enableSnippets: true,
                      enableLiveAutocompletion: true
              });
  
          });
      },
      setTheme:function(value){
          this.theme=value;
          this.editor.setTheme(value)
      },
      setMode:function(value){
         // console.log(value)
         var mode= this.mode=value;
          switch(this.mode){
              case "js":
                      mode='ace/mode/javascript';
                      break;
              case "css":
                      mode='ace/mode/css';
                      break;
              case "html":
                      mode= 'ace/mode/html';
                      break;
          }
          this.editor.session.setMode(mode);     
      },
      setValue:function(value){
          this.value=value;
          this.editor.setValue(value);
      },
      clear:function(){
          this.setValue('')
      }
  });
  var MonacoEditor=extendClass(EditorBase,{
      init:function(){
          var that=this;
          that.editor= monaco.editor.create(this.el,{
             // model:monaco.editor.createModel("","javascript")
          });
  
          this.editor.onDidChangeModelContent(function(a,editor){
              that.emit('change',that.editor.getValue())
          });
    
      },
      setTheme:function(value){
          this.theme=value;
          //this.editor.setTheme(value);
  
          monaco.editor.setTheme(value);
      },
      setMode:function(value){
         // console.log(value)
          var mode =this.mode=value;
          switch(this.mode){
              case "js":
                      mode='javascript';
                      break;
              case "css":
                      mode='css';
                      break;
              case "html":
                      mode= 'html';
                      break;
          }
          monaco.editor.setModelLanguage(this.editor.getModel(),mode)
          //this.editor.setModel(monaco.editor.createModel('',mode));     
      },
      setValue:function(value){
          this.value=value;
          this.editor.setValue(value);
      },
      clear:function(){
          this.setValue('')
      }
  })
  var CodeMirrorEditor=extendClass(EditorBase,{
      init:function(){
          var that=this;
          
          var editor=this.editor=CodeMirror(that.el,{
              extraKeys: {"Ctrl-.": "autocomplete"},
              showHint:{
                    // extraKeys:{".": "autocomplete"},
              },
              lineNumbers: true,
              mode:"text/html"
          });
          this.editor.on('change',function(a,editor){
              that.emit('change',that.editor.getValue())
          });
    
      },
      setTheme:function(value){
          this.theme=value;
          //this.editor.setTheme(value);
          var theme=value.split(/\s+/)[0];
          if(!$('link[data-theme="'+theme+'"]').length&&this.theme!='default'){
             var link= $('<link>',{
                 rel:"stylesheet",
                 href:"https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.46.0/theme/"+theme+".css"
             })
             $('head:first').append(link)
          }
          this.editor.setOption('theme',this.theme);
      },
      setMode:function(value){
         // console.log(value)
          var mode=this.mode=value;
          switch(this.mode){
              case "js":
                      mode='text/javascript';
                      break;
              case "css":
                      mode='text/css';
                      break;
              case "html":
                      mode= 'text/html';
                      break;
          }
          this.editor.setOption('mode',mode);   
      },
      setValue:function(value){
          this.value=value;
          this.editor.setValue(value);
      },
      clear:function(){
          this.setValue('')
      }
  })
  var scriptRources=[];
  
  var codeEditorTypes={
      ace:AceEditor,
      monaco:MonacoEditor,
      codeMirror:CodeMirrorEditor
  }
  window.initEditor=initEditor;
  function initEditorStyle(){
      loadStyle(`
        .exmaple{
            width:100%;
            height:80vh;
        }
        .exmaple  iframe {
          width: 100%;
          height: 100%;
         border:0;
        }
        .dx-editor{
          height:80vh;
        }
        .header{
  
        }
        .header .ivu-card-body{
          padding:5px;
        }
        .fixed-header{
            position:fixed;
            width:100%;
            z-index:2;
  
        }
        .content{
            margin:10px;
        }
        .fixed-content{
            margin-top:65px;
        }
      `);
  }
  function initEditor(type,options){
      loadModule(['jquery','iview','localforage'],function(){
          if(options&&options.onload){
              options.onload();
          }
          initEditorStyle();
          initEditor.init(type,options)
  
          
      })
  }
   
  initEditor.init=function _initEditor(type,options){
      options=options||{};
      Vue.use(iview);
      var root=$(template).appendTo(options.container?options.container:'body');
      var CodeEditor=codeEditorTypes[type];
      var Editor=Vue.extend({
          template:'<div ref="editor" class="dx-editor"></div>',
          props:{
              value:{
                  type:String,
                  default:""
              },
              mode:{
                  type:String,
                  default:"js",
                  // validator:function(val){
                  //     return ['js','html','css'].includes(val);
                  // }
              },
              theme:{
                  type:String,
                  default:""
              }
          },
          data:function(){
              return {
                currentValue:'',
                currentTheme:'',
                currentMode:''
              }
          },
          watch:{
              mode:{
                  immediate:false,
                  handler:function(v){
                       this.setMode(v)
                  }
              },
              theme:{
                  immediate:false,
                  handler:function(v){
                      this.setTheme(v);
                  }
              },
              value:function(v){
                  this.setValue(v);
              }
          },
          methods:{
              setTheme:function(theme){
                  if(!theme||theme===this.currentTheme){
                      return;
                  }
                  this.currentTheme=theme;
                  this.editor.setTheme(theme)
              },
              setMode:function(mode){
                  if(mode==this.currentMode){
                      return;
                    }
                   this.currentMode=mode;
                  this.editor.setMode(mode)
              },
              init:function(){
                  this.setMode(this.mode);
                  this.setTheme(this.theme);
                  this.setValue(this.value);
                  var that=this;
                  this.editor.on('change',function(value){
                      that.onChange(value);
                  });
              },
              setValue:function(value){
                  if(value==this.currentValue){
                    return;
                  }
                  this.currentValue=value;
                  this.editor.setValue(value);
              },
              onChange:function(v){
                  this.currentValue=v;
                  this.$emit('input',v);
                  this.$emit('on-change',v,this.editor);
              },
              clear:function(){
                  this.editor.setValue('');
                  this.onChange('');
              }
          },  
          beforeCreate:function(){
          },
          mounted:function(){
               this.editor = new CodeEditor(this.$refs.editor);
               this.init();
          }
      })
  
      Vue.component('PageResource',function(resolve){
          resolve({
          template:`<Drawer width="80vw" title="添加资源" :closable="true" :value="visible" @on-visible-change="onVisibleChange">
                    <i-form ref="form" :model="resourceForm"   :rules="validateRules">
                      <FormItem label="资源" prop="url">
                          <i-input  v-model="resourceForm.url"/>
                      </FormItem>
                      <FormItem>
                          <i-button @click="onAdd">添加</i-button>
                      </FormItem>
                  
                 </i-form>
                   <Card :bordered="true" :dis-hover="false" :shadow="true">
                              <p slot="title">依赖资源</p>
                              <i-table :data="tableData" :columns="columns"></i-table>
                          </Card>
           </Drawer>`,
          model: {
              prop: 'visible',
              event: 'change'
          },
          props:{
              visible:{
                  type:Boolean,
                  default:false
              },
              value:{
                  type:Array,
                  default:function(){
                       /**
                       version:"",
                      name:"",
                      url:"",
                      type:"js|css"
                    */
                    return [];
                  }
              }
          },
          data:function(){
            var that=this;
              return {
                  resourceForm:{
                      url:""
                  },
                  validateRules:{
                      url:[{
                          required:true,
                          message:"请添加资源引用路径"
                      },{
                        pattern:/\.(?:js|css)$/i,
                        message:"你的路径格式不正确"
                      }]
                  },
                  columns:[{
                      title:"名称",
                      key:"name"
                  },{
                      title:"版本",
                      key:"version"
                  }
                  ,{
                      title:"类型",
                      key:"type"
                  },{
                      title:"操作",
                      render:function(h,params){
                        return h('div', [
                          h('Button', {
                              props: {
                                  type: 'primary',
                                  size: 'small'
                              },
                              style: {
                                  marginRight: '5px'
                              },
                              on: {
                                  click:function(){
                                      this.show(params.index)
                                  }
                              }
                          }, 'view'),
                          h('Button', {
                              props: {
                                  type: 'error',
                                  size: 'small'
                              },
                              on: {
                                  click: function(){
                                      console.log('ff')
                                    that.remove(params.index)
                                  }
                              }
                          }, 'del')
                      ]); 
                      }
                  }],
                  tableData:this.value
              }; 
          },
          watch:{
              value:function(v){
                this.setCurrentValue(v)
              }
          },
          methods:{
              normalFile:function(url){    
                  if(typeof url!='string'){
                      return url
                  }         
                  var m=url.match(/((?:\d+\.)+\d+)|([^/]+)(?:(\.js|\.css)$)/g);
                  if(m){
                      var fileName=m.length>1?m[1]:m[0];
                      var fileNames=fileName.split('.');
                      return {
                          version:m.length>1?m[0]:"",
                          name:fileNames[0],
                          url:url,
                          type:fileNames[fileNames.length-1].toLowerCase()
                      }
                  }
              },
              remove:function(index){
                  this.tableData.splice(index,1)
                  this.onChange();
              },
              onAdd:function(){
                  var that=this;
                  this.$refs.form.validate(function(vali){
                      if(!vali){
                          return;
                      }
                     var file=that.normalFile(that.resourceForm.url);
                     if(file&&!that.tableData.some(function(d){
                         return d.name==file.name;
                     })){
                     that.tableData.push(file)
                     that.onChange();
                     }
                  })
              },
              onChange:function(){            
                  this.$emit('on-change',this.tableData);   
              },
              setCurrentValue:function(v){
                  if(v===this.tableData){
                      return;
                  }
                  this.tableData=v;
              },
              onVisibleChange:function(v){
                  this.$emit('change',v);
              },
              toResources:function(){
                      return [].concat(this.tableData.map(function(file){
                        if(file.type=="js"){
                          return `<script src="${file.url}" ><\/script>`;
                        }else{
                          return `<link rel="stylesheet" href="${file.url}" />`;
                        }
                      })).join('\n')
              }
          },
          /*beforeCreate
          created
          beforeMount
          mounted
          beforeUpdate
          updated
          activated
          deactivated
          beforeDestroy
          destroyed
          errorCaptured*/
          created:function(){
  
          },
          mounted:function(){
             // this.update();
          },
          beforeDestroy:function(){
  
          }
          
      });
      })
      var DPreview=Vue.extend({
          template:`<Card>
               <p slot="title">
            <Icon type="ios-film-outline"></Icon>
            Exmaple        
        </p>
        <template slot="extra" >
          <i-switch v-model="realTime"  size="large" >
          <span slot="open">实时</span>
          <span slot="close">关闭</span>  
          
            </i-switch>
        </template>
        <div class="exmaple"><iframe ref="previewFrame"></iframe></div>    
        
        </Card>`,
          props:{
              inline:{
                type:Boolean,
                default:false
              },
              js:String,
              css:String,
              html:String,
              resources:{
                type:Array,
                default:function(){
                  return [];
                }
              }
          },
          data:function(){
              return {
                realTime:false
              };
          },
          watch:{
              resources:function(v){
                  this.update('resources');
              },
              js:function(){
                this.update('js');
              },
              css:function(){
                this.update('css');
              },
              html:function(){
                this.update('html');
              }
          },
          methods:{
               init:function(){
                var previewFrame=this.$refs.previewFrame;
                var preview =  previewFrame.contentDocument ||  previewFrame.contentWindow.document;
                if(this.inline){
                    var elStyle=preview.createElement('style');
                    var elHtmlPlaceholder=preview.createComment('html-start');
                   
                    preview.getElementsByTagName('head')[0].appendChild(elStyle);
                    preview.body.appendChild(elHtmlPlaceholder)
  
                    this.elHtmlPlaceholder=elHtmlPlaceholder;
                    this.elStyle=elStyle;
                    this.htmls=[];
  
                }
                this.previewDoc=preview;
              },
  
              write:function(code){
                var previewFrame=this.$refs.previewFrame;
                var preview =  previewFrame.contentDocument ||  previewFrame.contentWindow.document;
              
               // preview.open();
                preview.write(code);
                preview.close();
              },
              update:function(type){
                    type=type||"*";
                    if(this.inline&&(type!="*"&&this.realTime||type=="*")){
                      if(type=="*"||type==='css'){
                        this.updateCss(this.css);
                      }
                      if(type=="*"||type==='resources'){
                        this.updateResources(this.resources);
                      }
                      if(type=="*"||type==='html'){
                        this.updateHtml(this.html);
                      }             
                      if(type=="*"||type==='js'){
                        this.updateJs(this.js);
                      }    
                    }else if(!this.inline&&this.realTime){
                      this.preview();
                    }
              },
              updateJs:function(value){
                  try{                  
                    var elScript=this.previewDoc.createElement('script');                  
                    elScript.text=value;
                    if(this.elScript){
                      this.previewDoc.body.replaceChild(elScript,this.elScript);
                    }else{
                        this.previewDoc.body.appendChild(elScript);
                    }
                    this.elScript=elScript;
       
                  }catch(e){
                    throw  e;
                  }
              },
              updateCss:function(value){
                  try{
                    this.elStyle.innerHTML=value;
                  }catch(e){
                    throw  e;
                  }
              },
              updateHtml:function(value){
                  try{
                   // var d=new DOMParser().parseFromString(value,'text/html')
                   // var m=d.body;
                    var d=this.previewDoc.createElement('div');
                    d.innerHTML=value;
                    var m=d;
                    var node;
                    var that=this,i=-1,len=that.htmls.length;
                    while(++i<len){
                       that.previewDoc.body.removeChild(that.htmls[i]);                    
                    }
                    that.htmls.length=0;
                    while((node=m.firstChild)){
                        that.htmls.push(node);
                        that.previewDoc.body.insertBefore(node,that.elHtmlPlaceholder);
                    }
                    
                  }catch(e){
                    throw  e;
                  }
              },
              updateResources:function(resources){
                  var that=this,previewDoc=that.previewDoc,
                  resourcesList=that.resourcesList,
                  cloneResourcesList=resourcesList.slice();
                  var alreadMap=new Map();
                  var resourcesMap=resources.reduce(function(map,s){
                       map.set(s.name,s);
                       return map;
                  },new Map())
  
                  cloneResourcesList.forEach(function(r,index){
                    if(!resourcesMap.has(r.name)){
                         resourcesList.splice(index,1);
                         previewDoc.removeChild(r.el);
                     }else{
                        alreadMap.set(r.name,r);
                     }
                  });
                  resources.forEach(function(r){
                      if(!alreadMap.has(r.name)){
                         var el;
                          if(r.type=="js"){
                             el=previewDoc.createElement('script');
                             el.src=r.url;
                          }else if(r,type=='css'){
                             el=previewDoc.createElement('style');
                             el.setAttribute('rel','stylesheet');
                             el.href=r.url;
                          }
                          previewDoc.getElementsByTagName('head')[0].appendChild(el);
                          resourcesList.push({
                            name:r.name,
                            el:el
                          })
                      }
                  })
  
              },
              toResources:function(){
                    return this.resources.map(function(file){
                      if(file.type=="js"){
                        return `<script src="${file.url}" ><\/script>`;
                      }else{
                        return `<link rel="stylesheet" href="${file.url}" />`;
                      }
                    }).join('\n')
              },
              preview:function(){
                if(this.inline){
                    this.update();
                }else{
                  this.write(this.template(this.html,this.css,this.js));
                }
              },
              template:function(html,style,js){
                  return `
                             <!DOCTYPE html>
                            <html>
                                <head>
                                        <title></title>
                                        ${this.toResources()}
                                        <style>${style}</style>
                                </head>
                                <body>
                                  ${html}
                                    <script>
                                         ${js}
                                    <\/script>
                                </body>
                             </html>
                        `;
              }
          },
          created:function(){
             this.resourcesList=[];
          },
          mounted:function(){
            this.init();
          },
          beforeDestroy:function(){
             
          }
       })
       var initData=$.extend({
          isFixed:true,// 是否固定头部
          css:'',
          js:'',
          html:'',
          htmlMode:'html',
          jsMode:'js',
          cssMode:'css',
          themes:[],
          theme:"",
          editorTab:"html",
          visibleResouce:false,
          resources:[]
     },options.data||{});
      var defaultResources=[{
             type:"js",
             name:"jquery",
             version:'3.4.1',
             url:'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js'
         }]
  //    initData.resources.unshift({
  //         type:"js",
  //         name:"consola",
  //         version:'2.9.0',
  //         url:'https://cdn.jsdelivr.net/npm/consola@2.9.0/dist/consola.browser.min.js'
  //    })
     initData.resources=defaultResources.concat(initData.resources);
      var currentStore = localforage.createInstance({
          name: window.top.location.pathname
      });
  
      var view=new Vue({
          el:"#app",
          components:{
             'd-editor':Editor,
             'd-preview':DPreview
          },
          data:initData,
          computed:{
              allResources:function(){
                  return defaultResources.concat(this.resources)
              }
          },
          watch:{
              resources:{
                  immediate:false,
                  handler:function(v){
                     // console.log(v);
                  }
              }
          },
          filters:{
              themeName:function(value){
                  var name=value.split('/')
                  return name[name.length-1];
              }
          },
          methods:{
               onResourcesChange:function(data){
                   this.setResources(data);
                   currentStore.setItem('resources',data)
               },  
               switchTab:function(tab){
                   this.editorTab=tab;
               },
               onThemeChange:function(){
                
               },
               setResources:function(resources){
                   if(resources==this.resources){
                       return;
                   }
                   this.resources=resources;
               },
               setJs:function(value){
                   this.js=value;
               },
               setCss:function(value){
                   this.css=value;
               },
               setHtml:function(value){
                   this.html=value;
               },
               onRun:function(){
                   this.$refs.preview.preview();
               },
               onAddResources:function(){
                  this.visibleResouce=!this.visibleResouce;
               },
               onClear:function(){
                   this.js='';
                   this.css='';
                   this.html='';
                   //console.log('ff')
               },
               onHtmlChange:function(value){
                  currentStore.setItem('html',value)
               },
               onJsChange:function(value){
                  currentStore.setItem('js',value)
               },
               onCssChange:function(value){
                  currentStore.setItem('css',value)
               }
          },
          created:function(){
              var that=this,js=this.js,css=this.css,html=this.html,resources=this.resources;
              currentStore.getItem('js').then(function(value){
                  if(js===''){
                     that.setJs(value)
                  }
              });
              currentStore.getItem('html').then(function(value){
                if(html===''){
                  that.setHtml(value)
               }
              })
              currentStore.getItem('css').then(function(value){
                if(css===''){
                  that.setCss(value)
               }
              });
              currentStore.getItem('resources').then(function(value){
                if(value.length>resources.length){
                  that.setResources(value)
               }
              })
          },
          mounted:function(){
  
             this.onThemeChange();        
             options.mountedCallback&&options.mountedCallback(this)
      
         
          }
      })
  
  }
  window.registerEditor=registerEditor;
  function registerEditor(options){
      
      loadModule(['jquery','iview'],function(){
          registerEditor.init(options);
      })
  }
  registerEditor.init=function(options){
      options=options||{};
      var container=options.container||document.body;
      var pageUID=options.pageUID||window.location.pathname;
      var el=$('<div v-pre>').appendTo(container);
      var root=$('<div><d-editor :css="css" :js="js"  :html="html" :resources="resources" ></d-editor></div>').appendTo(el);
      var view=new Vue({
          data:$.extend({
              css:"",
              js:"",
              html:"",
              resources:[]
          },options.data||{}),
          components:{
              'd-editor':EmapleEditor(Vue)
          }
      })
      view.$mount(root[0]);
      options.callback&&options.callback(view);
  }
  function EmapleEditor(vue){
       return vue.extend({
              template:` <iframe frameborder="0" scrolling="auto" ref="iframeAce" width="100%" height="100%" :src="src" :style="styles" @load="onLoadEditor"></iframe>`,
              props:{
                editorType:{
                  type:Number,
                  default:3
                },
                tab:{
                  type:String,
                  default:"html"
                },
                css:{
                  type:String,
                  default:""
                },
                js:{
                  type:String,
                  default:""
                },
                html:{
                  type:String,
                  default:""
                },
                resources:{
                  type:Array,
                  default:function(){
                    return [];
                  }
                },
                autocompletion:{
                    type:String,
                    default:''
                }
              },
              computed:{
                src:function(){
                  if(this.editorType===1){
                    return '/exmaple/userinterface/codeEditor/ace.html';
                  }else if(this.editorType===2){
                    return '/exmaple/userinterface/codeEditor/codeMirror.html';
                  }else if(this.editorType===3){
                    return '/exmaple/userinterface/codeEditor/monaco-editor.html';
                  }
                   return '/exmaple/userinterface/codeEditor/monaco-editor.html';
                }
              },
              data:function(){
                  return {
                    styles:{
                      minHeight:"100vh"
                    }
                  };
              },
              watch:{
                 resources:{
                    immediate:false,
                    handler:function(v){
                      this.update('resources');
                    }
                },
                css:{
                    immediate:false,
                    handler:function(v){
                      this.update('css');
                    }
                },
                html:{
                    immediate:false,
                    handler:function(v){
                      this.update('html');
                    }
                },
                js:{
                    immediate:false,
                    handler:function(v){
                      this.update('js');
                    }
                },
                tab:function(v){
                  this.switchTab()
                }
              },
              methods:{
                update:function(type){
                    type=type===undefined?"*":type;
                    var that=this,editorView=that.editorView;
                    if(!editorView){
                        return;
                    }
                 
                    if(type=="*"||type=="resources"){
                      editorView.setResources(this.resources)
                    }
                    if(type=="*"||type=="css"){
                      editorView.setCss(this.css)
                    }
                  if(type=="*"||type=="js"){
                      editorView.setJs(this.js)
                    }
                   
                    if(type=="*"||type=="html"){
                      editorView.setHtml(this.html)
                    }
                },
                switchTab:function(){
                   this.editorView.switchTab(this.tab)
                },
                init:function(){
                   this.update();
                   this.switchTab();
                },
                onLoadEditor:function(){
          
                }
              },
              created:function(){
                  var that=this;
                  that.editorView=null;
                  window.registerEditor=function registerEditor(d){
                        that.editorView=d.view;
                        that.init();               
                  }
              },
              mounted:function(){
                  this.update();
              }
            })
  }
  if(typeof Vue==='function'){
      Vue.component('d-edtior',EmapleEditor(Vue))
  }
  
  
     
  function createHtmlEditor(el,options){
      var editor = ace.edit(el);
    editor.session.setMode("ace/mode/html");
    return editor;
  }
  function createCssEditor(el,options){
       var editor = ace.edit(el);
     editor.session.setMode("ace/mode/css");
    return editor;
  }
  function createJavascriptEditor(el,options){
    var editor = ace.edit(el);
    editor.session.setMode("ace/mode/javascript");
  
    return editor;
  }
  function createPreview(wrapper){
        var previewFrame =document.createElement('iframe'); 
        wrapper.appendChild(previewFrame);
        //<iframe srcdoc="<p>这是HTML<p>" frameborder="0"></iframe>
       // <iframe src="data:text/html,<p>这是HTML<p>" frameborder="0"></iframe>
       /*
       <iframe src="javascript:void(function(){document.open();document.write('<p>这是HTML<p>');document.close();}())" frameborder="0">
    </iframe>
       */
        function updateView(code){
             var preview =  previewFrame.contentDocument ||  previewFrame.contentWindow.document;
             
             preview.open();
             preview.write(code);
             preview.close();
        }
        function createScript(){
            var script=document.createElement('script');
            script.type="text/javascript";
            return function(value){
                
            }
        }
        function createStyle(){
            var script=document.createElement('script');
            script.type="text/css";
        }
        function createHtml(){
            var script=document.createElement('script');
            script.type="text/javascript";
        }
        updateView.splitValue=function(html,css,js){
             var doc=document.implementation.createHTMLDocument();
             
        }
        return updateView;
  }
  function updatePreview() {
    var previewFrame =document.getElementById('preview');
    var preview =  previewFrame.contentDocument ||  previewFrame.contentWindow.document;
    preview.open();
    preview.write(editor.getValue());
    preview.close();
  }
  
  
  (function(DOMParser) {
  "use strict";
  
  var proto = DOMParser.prototype, 
    nativeParse = proto.parseFromString;
  
  // Firefox/Opera/IE throw errors on unsupported types
  try {
    // WebKit returns null on unsupported types
    if ((new DOMParser()).parseFromString("", "text/html")) {
        // text/html parsing is natively supported
        return;
    }
  } catch (ex) {}
  
  proto.parseFromString = function(markup, type) {
    if (/^\s*text\/html\s*(?:;|$)/i.test(type)) {
        var
          doc = document.implementation.createHTMLDocument("")
        ;
              if (markup.toLowerCase().indexOf('<!doctype') > -1) {
                doc.documentElement.innerHTML = markup;
              }
              else {
                doc.body.innerHTML = markup;
              }
        return doc;
    } else {
        return nativeParse.apply(this, arguments);
    }
  };
  }(DOMParser));
  })();

 

 


版权声明:本文为long5305350原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。