<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      輕量級富文本編輯器wangEditor源碼結構介紹

      1. 引言

        wangEditor——一款輕量級html富文本編輯器(開源軟件)

        從我發布wangEditor到現在,大概有七八個月了,隨著近期增加的插入視頻,表情,地圖這三個功能,目前為止基本的功能已經大體完善了。這期間也修改了幾個bug,都是各位網友反映的。至于程序是不是已經很穩定了,我不敢說。畢竟應用的人不是特別多,目前只有幾十個關注wangEditor的人在應用。他們會偶爾提出一些bug,不過只要告訴我,我會第一時間解決,至少大家對我修改bug增加功能的速度和態度,還是比較認可的。

        

        根據github記載,目前有105個commits,即我已經提交了105次代碼更新,這個數量也會繼續增加。大家有bug,有需求可以通過QQ群向我提交。

      2. 介紹源碼結構

        wangEditor.js源碼目前2200多行,用書寫文字書寫博客的方式介紹它的結構,還真不是一件簡單的事兒。所以,這里我就長話短說,盡量簡單的介紹一下重點,不要搞的太羅嗦,否則大家最后會不耐煩的。

        如果讓我自己對這個源碼的設計和架構做一個評價的話,我會打70分。它并不是完美的,但是它已經滿足了我基本的需求。比方說,我最近新增的幾個功能(插入視頻,地圖,表情)都是通過修改其中的配置項增加上去的,而沒有改動源碼中的核心部分。開放封閉原則——對擴展開放,對修改封閉,我想我已經基本做到了這一點。

        最后,我分享wangEditor源碼設計的目的,為的是讓大家給一些意見。提出一些疑問,一些建議,或者我目前還沒有意識到的一些問題。總之,我是希望這個軟件越做越好。

      3. 一個jQuery插件

        wangEditor是一款jQuery插件,也是基于jquery開發的(不理解jquery插件的同學,請自行補課,本文不講)。定義一個jquery插件其實很簡單,wangEditor.js源碼的最后幾十行定義了。

      //------------------------------------生成jquery插件------------------------------------
          $.fn.extend({
              /*
              * options: {
              *   $initContent: $elem, //配置要初始化內容
              *   menuConfig: [...],   //配置要顯示的菜單(menuConfig會覆蓋掉hideMenuConfig)
              *   onchange: function(){...},  //配置onchange事件,
              *   uploadUrl: 'string'  //圖片上傳的地址
              * }
              */
              'wangEditor': function(options){
                  if(this[0].nodeName !== 'TEXTAREA'){
                      //只支持textarea
                      alert('wangEditor提示:請使用textarea擴展富文本框。詳情可參見作者的demo.html');
                      return;
                  }
      
                  var options = options || {},
                      menuConfig = options.menuConfig,
                      $initContent = options.$initContent || $('<p><br/></p>'),
                      onchange = options.onchange,
                      uploadUrl = options.uploadUrl;
      
                  //獲取editor對象
                  var editor = $E(this, $initContent, menuConfig, onchange, uploadUrl);
      
                  //渲染editor,并隱藏textarea
                  this.before(editor.$editorContainer);
                  this.hide();
      
                  //頁面剛加載時,初始化selection
                  editor.initSelection();
      
                  return editor;
              }
          });

        以上代碼其實都很簡單,就是接受一些配置項然后調用一個 $E 函數,返回一個 editor 對象,最后渲染到頁面上。最關鍵的就是 $E 函數這一句話。

      //獲取editor對象
      var editor = $E(this, $initContent, menuConfig, onchange, uploadUrl);

        大家看這種方式是不是有點 var $div = $('div'); 的意思?——對了,這的設計我就是模仿著jquery來的。

      4. 仿jQuery的對象化設計

        上文中提到的 $E 函數是這樣定義的。

      //全局的構造函數
              $E = function($textarea, $initContent, menuConfig, onchange, uploadUrl){
                  return new $E.fn.init($textarea, $initContent, menuConfig, onchange, uploadUrl);
              };

        如上代碼,其實構造函數是 $E.fn.init 。$E 只不過是一個入口,返回這個構造函數 new 出來的一個對象。

        那么 $E.fn 是什么呢? ——它是 $E.prototype 的簡寫而已——好多js系統都喜歡這么干,我也就隨著高大上一些啦!

          //prototype簡寫為fn
          $E.fn = $E.prototype;

        既然 $E.fn.init 是構造函數,那么它 new 出來的對象(即上文中的 editor)的原型要指向:$E.fn.init.prototype ,這樣豈不是太長?不如來個簡單一些的,將原型指向 $E.fn 吧。

      $E.fn.init.prototype = $E.fn;

        到了這里,沒有看過jquery設計或者源碼的人,一定覺得繞暈了——那是很正常的。我一開始接觸jquery時,也是繞不過來。不過后來看多了,再后來自己用起來,還真覺得挺簡單易用。大家在做自己的js代碼時候,也不放試一試!

      5. 工具函數 & 對象函數

        其實這里也是仿照jquery來設計的。在jquery中,函數都是 $ 的屬性,例如 $.trim() ,對象函數都是 $.fn 的屬性,例如 $('div').html() 的 html 方法就是 $.fn.html 定義的。

        在wangEditor.js也一樣。有許多工具函數(例如log輸出,引號轉譯,url安全性檢查等)都是 $E 的屬性;許多對象函數(例如text,append,change等)都是 $E.fn 的屬性。

        為什么把函數定義在 $E.fn 上即可成為對象函數呢?——因為構造函數是 $E.fn.init ,而 $E.fn.init.prototype = $E.fn;  不知道大家明白了沒有?

      6. menu配置項

        wangEditor目前有28個功能菜單,不可能為每一個菜單都寫一遍執行代碼。因為我們是面向對象的編程,我們是遵循“開放封閉原則”的設計。

        還別說,在第一個版本中,我還真就是一個菜單寫一遍執行代碼,后來發現那樣根本無法擴展。現在我的宗旨是:寫一個菜單處理引擎(包括菜單初始化,頁面彈出關閉,命令執行),菜單的擴展通過配置項實現。這個菜單處理引擎今天就不在本文講解了,那塊挺麻煩的,有時間再通過視頻的方式跟大家分享吧。

        首先,我們需要把所有的菜單歸歸類,否則如何確定配置項啊?我把所有的菜單分為4類:

      • command類型:點擊按鈕即可執行命令,如“粗體”,“下劃線”
      • dropMenu類型:點擊按鈕彈出下拉menu,再選擇命令。如“字體”,“字號”
      • dropPanel類型:點擊按鈕彈出panel,再選擇命令。如“背景色”,“表情”
      • modal類型:點擊按鈕彈出對話框,需要填寫內容,再執行命令。如“插入圖片”,“插入地圖位置”

        下面是一個菜單按鈕配置時的說明:

      'menuId-1': {
          'title': (字符串,必須)標題,
          'type':(字符串,必須)類型,可以是 btn / dropMenu / dropPanel / modal,
          'txt': (字符串,必須)fontAwesome字體樣式,例如 'fa fa-head',
          'style': (字符串,可選)設置btn的樣式
          'hotKey':(字符串,可選)快捷鍵,如'ctrl + b', 'ctrl,shift + i', 'alt,meta + y'等,支持 ctrl, shift, alt, meta 四個功能鍵(只有type===btn才有效)
          'command':(字符串)document.execCommand的命令名,如'fontName';也可以是自定義的命令名,如“撤銷”、“插入表格”按鈕(type===modal時,command無效),
          'dropMenu': ($ul,可選)type===dropMenu時,要返回一個$ul,作為下拉菜單,
          'dropPanel':($div,可選)type===dropPanel是,要返回一個$div,作為彈出框
          'modal':($div,可選)type===modal是,要返回一個$div,作為彈出框,
          'callback':(函數,可選)回調函數,
      },

        再配置一個菜單時,必須要遵守這個規則,否則解析引擎無法正確解析配置項。在此,為每個類型的菜單按鈕,粘貼幾個簡單的配置項:

      'fontFamily': {
                          'title': '字體',
                          'type': 'dropMenu',
                          'txt': 'icon-wangEditor-font',
                          'command': 'fontName ', 
                          'dropMenu': function(){
                              var arr = [],
                                  //注意,此處commandValue必填項,否則程序不會跟蹤
                                  temp = '<li><a href="#" commandValue="${value}" style="font-family:${family};">${txt}</a></li>',
                                  $ul;
      
                              $.each($E.styleConfig.fontFamilyOptions, function(key, value){
                                  arr.push(
                                      temp.replace('${value}', value)
                                          .replace('${family}', value)
                                          .replace('${txt}', value)
                                  );
                              });
                              $ul = $( $E.htmlTemplates.dropMenu.replace('{content}', arr.join('')) );
                              return $ul; 
                          },
                          'callback': function(editor){
                              //console.log(editor);
                          }
                      },
      'bold': {
                          'title': '加粗',
                          'type': 'btn',
                          'hotKey': 'ctrl + b',
                          'txt':'icon-wangEditor-bold',
                          'command': 'bold',
                          'callback': function(editor){
                              //console.log(editor);
                          }
                      },
      'foreColor': {
                          'title': '前景色',
                          'type': 'dropPanel',
                          'txt': 'icon-wangEditor-pencil',   //如果要顏色: 'txt': 'fa fa-pencil|color:#4a7db1'
                          'style': 'color:blue;',
                          'command': 'foreColor',
                          'dropPanel': function(){
                              var arr = [],
                                  //注意,此處commandValue必填項,否則程序不會跟蹤
                                  temp = '<a href="#" commandValue="${value}" style="background-color:${color};" title="${txt}" class="forColorItem">&nbsp;</a>',
                                  $panel;
      
                              $.each($E.styleConfig.colorOptions, function(key, value){
                                  var floatItem = temp.replace('${value}', key)
                                                      .replace('${color}', key)
                                                      .replace('${txt}', value);
                                  arr.push(
                                      $E.htmlTemplates.dropPanel_floatItem.replace('{content}', floatItem)
                                  );
                              });
                              $panel = $( 
                                  $E.htmlTemplates.dropPanel.replace('{content}', arr.join('')) 
                              );
                              return $panel; 
                          }
                      },
      'createLink': {
                          'title': '插入鏈接',
                          'type': 'modal', 
                          'txt': 'icon-wangEditor-link',
                          'modal': function (editor) {
                              var urlTxtId = $E.getUniqeId(),
                                  titleTxtId = $E.getUniqeId(),
                                  blankCheckId = $E.getUniqeId(),
                                  btnId = $E.getUniqeId();
                                  content = '鏈接:<input id="' + urlTxtId + '" type="text" style="width:300px;"/><br />' +
                                              '標題:<input id="' + titleTxtId + '" type="text" style="width:300px;"/><br />' + 
                                              '新窗口:<input id="' + blankCheckId + '" type="checkbox" checked="checked"/><br />' +
                                              '<button id="' + btnId + '" type="button" class="wangEditor-modal-btn">插入鏈接</button>',
                                  $link_modal = $(
                                      $E.htmlTemplates.modalSmall.replace('{content}', content)
                                  );
                              $link_modal.find('#' + btnId).click(function(e){
                                  //注意,該方法中的 $link_modal 不要跟其他modal中的變量名重復!!否則程序會混淆
                                  //具體原因還未查證???
      
                                  var url = $.trim($('#' + urlTxtId).val()),
                                      title = $.trim($('#' + titleTxtId).val()),
                                      isBlank = $('#' + blankCheckId).is(':checked'),
                                      link_callback = function(){
                                          //create link callback
                                          $('#' + urlTxtId).val('');
                                          $('#' + titleTxtId).val('');
                                      };
      
                                  if(url !== ''){
                                      //xss過濾
                                      if($E.filterXSSForUrl(url) === false){
                                          alert('您的輸入內容有不安全字符,請重新輸入!')
                                          return;
                                      }
                                      if(title === '' && !isBlank){
                                          editor.command(e, 'createLink', url, link_callback);
                                      }else{
                                          editor.command(e, 'customCreateLink', {'url':url, 'title':title, 'isBlank':isBlank}, link_callback);
                                      }
                                  }
                              });
      
                              return $link_modal;
                          }
                      }

      7. 總結

        以上只是一些重點部分,其他的還有很多。例如富文本編輯器的核心技術:execCommand,如何支持IE6的fontIcon,菜單按鈕如何解析,以及表情,地圖是如何實現的。時間有限,就不一一說明了,大家有興趣可以去看源碼。

        最后還是歡迎大家多多指正!

      -------------------------------------------------------------------------------------------------------------

      歡迎關注我的教程:從設計到模式深入理解javascript原型和閉包系列》《css知多少》《微軟petshop4.0源碼解讀視頻》《json2.js源碼解讀視頻

      也歡迎關注我的開源項目——wangEditor,輕量化web富文本編輯器

      -------------------------------------------------------------------------------------------------------------

       

      posted @ 2015-05-11 08:24  王福朋  閱讀(15826)  評論(24)    收藏  舉報
      主站蜘蛛池模板: 国产精品亚洲аv无码播放| 少妇高潮喷水久久久影院| 无套内谢少妇毛片在线| 无码中文字幕热热久久| 漂亮人妻中文字幕丝袜| 亚洲18禁私人影院| 和黑人中出一区二区三区| 美女一区二区三区亚洲麻豆 | 亚洲国产成人av在线观看| 亚洲av男人电影天堂热app| 无遮高潮国产免费观看| 日本高清色WWW在线安全| 精品无码av无码免费专区| 干老熟女干老穴干老女人| 少妇高潮喷水正在播放| 国产一区二区三区色噜噜| www亚洲精品| 国产精品国产自产拍在线| 成人免费ā片在线观看| 久久热在线视频精品视频| 日本熟妇浓毛| 最新国产精品好看的精品| 一本久道中文无码字幕av| 伊人激情一区二区三区av| 日本熟妇XXXX潮喷视频| 国产成人欧美综合在线影院| 夜夜爽77777妓女免费看| 视频免费完整版在线播放| 亚洲AV日韩精品久久久久| 最新精品露脸国产在线| 日韩人妻不卡一区二区三区| 国产精品亚洲二区在线播放| 日本一区二区三区专线| 激情综合网激情国产av| 日韩欧美精品suv| 高清无码爆乳潮喷在线观看| 国产片一区二区三区视频| www成人国产高清内射| 欧美成本人视频免费播放| 国产精品色一区二区三区| 色伦专区97中文字幕|