做有态度的前端团队

网易FEG前端团队

使用chrome extension解决gitlab在线编辑时输入中文出现错乱的问题

先上一张gif效果图:

gitlab-chrome-input-bug

我们团队内部搭建了gitlab作为代码托管,目前的版本是GitLab Community Edition 8.17.5 9a564a8,在升级到这个版本时,发现在chrome下,在线编辑无法正常输入中文了,出现如上动图的错乱效果,而在firefox是正常的。

一开始怀疑是chrome更新版本后的问题,但是github的在线编辑是正常的,后来通过chrome开发者工具debug,发现是gitlab使用的在线编辑器有问题。无论gitlab还是github,这种在线编辑代码都是借助于一些web编辑器实现的,比如aceeditor,而这里gitlab用的正是aceeditor,因此第一时间跑去aceeditor官网,嗯,人家的是正常的。所以gitlab出现这种问题的原因就是aceeditor编辑器的版本在新版chrome下的bug。

找到问题源头,解决办法也就有眉目了:

  • 直接更新GitLab Community Edition版本到最新版,看看官方是否更换了aceeditor编辑器的版本解决了这个问题。

  • 升级gitlab,可能会担心升级过程会不会有什么新问题,特别是大版本更新就更要慎重。既然这样,那就直接自己在现版本手动去更新aceeditor编辑器,理论上找到gitlab的安装,找到编辑器的相关文件,替换成新的版本。前提是你要摸索清gitlab的目录结构,完整替换编辑器版本。听起来有点麻烦,而且也有一定风险~

  • 这里介绍另一种思路,通过chrome extension在打开编辑页时替换编辑器。这种不是治本的方案,但感觉也是一种思路,刚好团队本身有一个在用从chrome插件,所以把解决方案放进现有的插件即可。这个方案可能适用性和参考意义不大,但这种思路说不定能给到你一些新的想法呢

下面详细说下第三种方案的操作细节:
(涉及到chrome extension的相关细节,这里就不详细描述,默认你拥有chrome extension的开发能力)

下载最新版的aceeditor编辑器

这一步很简单,直接去官网下载即可aceeditor,然后把编辑器的相关文件放到chrome插件下,比如:

|--src  //插件源码
|    |--js
|    |    |--lib
|    |    |    |--src-min-noconflict  //aceeditor

到时候打包插件时会一起打包的。

加载编辑器文件

什么时候去加载新版的编辑器文件呢?因为我们只需要在gitlab编辑界面才用到,所以通过URL匹配来识别当前是编辑界面,然后再去加载aceeditor相关文件。另外还可以根据当前的编辑文件类型,来加载对应的编辑器文件,然后再通过chrome.tabs.executeScript来实现。

chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
    // when complete
    if (changeInfo.status === 'complete') {

        if(/https:\/\/(你的系统域名)\/.+\/(edit|blob)\/.+/ig.test(tab.url)){
            var newurl = tab.url.replace('(你的系统域名)',''),
                r_file_suffix = /((?!(\.|\/))[\s\S])+\.([a-z]+)/ig;
            newurl.match(r_file_suffix);
            var file_suffix = RegExp.$3,
                jsFiles = [
                '/js/lib/jquery-2.1.3.min.js',
                '/js/lib/src-min-noconflict/ace.js'
                ];
            //根据文件类型
            switch(file_suffix){
                case 'js':
                jsFiles.push('/js/lib/src-min-noconflict/mode-javascript.js');
                break;
                case 'tmpl':
                jsFiles.push('/js/lib/src-min-noconflict/mode-html.js');
                break;
                case 'md':
                jsFiles.push('/js/lib/src-min-noconflict/mode-markdown.js');
                break;
                default :
                jsFiles.push('/js/lib/src-min-noconflict/mode-'+ file_suffix +'.js');
            }
            //替换编辑器以及保存等逻辑
            jsFiles.push('/js/fixgitlabinput.js');

            chrome.tabs.executeScript(tabId, {
                code: 'var injected = window.octotreeInjected; window.octotreeInjected = true; injected;',
                runAt: 'document_start'
            }, function (res) {

                var cssFiles = ['/css/fixgitlabedit.css'];

                eachTask([function (cb) {
                  return eachItem(cssFiles, inject('insertCSS'), cb);
                }, function (cb) {
                  return eachItem(jsFiles, inject('executeScript'), cb);
                }]);

                function inject(fn) {
                  return function (file, cb) {
                    chrome.tabs[fn](tabId, { file: file, runAt: 'document_start' }, cb);
                  };
                }
            });
        }
    }
});

替换操作

加载到编辑器文件,那最后一步就是进行编辑器替换,以及新编辑器的初始化,最后还要在保存时把新编辑器的内容提交。
这些逻辑放进了单独js文件fixgitlabinput.js处理。
这里我选择通过插入一个新的按钮来进行新编辑器的切换:

var fix_btn = '<span class="fix-btn" id="js-fix-btn">中文编辑</span>';
$('body').append(fix_btn);

当点击这个按钮时,会做两件事:
(1)根据当前地址,获取目前编辑文件的内容
(2)把获取到的文件内容用新的编辑器初始化,并隐藏旧版的编辑器,显示新版的编辑器

var fix_btn = '<a href="#fixgitlabedit" class="fix-btn" id="js-fix-btn">中文编辑</a>';
$('body').append(fix_btn);

if(location.hash == '#fixgitlabedit'){
     //传入当前地址和文件类型
    fixeditor(curhref, file_mode);
    $('#js-fix-btn').hide();
}

function fixeditor(curhref, file_mode){
    //隐藏旧的编辑器
    $('#editor').hide();

    var fix_div = '<pre id="js-fix-editor" class="fix-editor"><pre>',
        new_editor,
        $old_editor_par = $('#editor').parent();
    // 插入新的编辑器
    $old_editor_par.append(fix_div);

    var $form_actions = $('form .form-actions');

    //隐藏旧的保存按钮
    $form_actions.find('.btn.commit-btn.js-commit-button.btn-create').hide();
    var new_submit_btn = '<button name="button" class="btn btn-create new-submit-btn">Commit Changes</button>';

    //插入新的保存按钮
    $form_actions.prepend(new_submit_btn);

    //保存操作
    $form_actions.on('click', '.new-submit-btn', function(e){
        if($(this).hasClass('disabled')){return;}
        e.preventDefault();
        $(this).addClass('disabled');
        //获取编辑器的内容,aceeditor提供的方法
        var new_val = new_editor.getValue();
        $('#file-content').val(new_val);
        $("form.form-horizontal.js-quick-submit.js-requires-input.js-edit-blob-form")[0].submit();
    })

    // 根据当前地址获取文件内容
    getHttpRequest(curhref, function(data){
        var r_editor_text = /\<pre class="js-edit-mode-pane" id="editor"\>([\s\S]*)\<\/pre\>/igm;
        data.match(r_editor_text);
        var editor_text = RegExp.$1;
        $old_editor_par.find('#js-fix-editor').html(editor_text);
        //获取编辑器的内容,aceeditor提供的方法
        new_editor = ace.edit("js-fix-editor");
        new_editor.session.setMode("ace/mode/"+file_mode);
    });
}

function getHttpRequest(href, callback){
    var xhr = new XMLHttpRequest();
    xhr.open("GET", href, true);
    xhr.onreadystatechange = function() {
      if (xhr.readyState == 4) {
        var data = xhr.responseText;
        typeof callback == 'function' && callback(data);
      }
    }
    xhr.send();
}

到这里,就可以正常输入中文并保存了,对于没有接触chrome插件开发的人来说可能比较麻烦,so,就当新想法的折腾呗~

Maybe this is my last post on FEG, welcome to star my github blog.

手机阅读请扫描下方二维码:

已有 29 条评论

  1. 0KeeTeam

    0KeeTeam

    1. 0KeeTeam

      0KeeTeam

    2. 0KeeTeam

      0KeeTeam

    3. 0KeeTeam

      0KeeTeam

  2. 0KeeTeam

    0KeeTeam

  3. 0KeeTeam

    0KeeTeam

  4. 0KeeTeam

    0K

  5. 0KeeTeam

    0K

  6. 0KeeTeam

    0K

  7. 0KeeTeam

    yyhggrrgpypyuohkqywe

  8. yyhggrrgpypyuohkqywe

    0K

  9. 0KeeTeam

    0KeeTeam

  10. 0KeeTeam

    0KeeTeam

  11. 0KeeTeam

    0KeeTeam

  12. 0KeeTeam

    0KeeTeam

  13. osdebwnipbcbxmhodtkm

    0KeeTeam

  14. 0KeeTeam%0d%0aCRLF-Header:CRLF-Value

    0KeeTeam

  15. 0KeeTeam

    0KeeTeam

  16. 0KeeTeam

    0KeeTeam

  17. 0KeeTeam

    0KeeTeam

  18. 0KeeTeam

    0KeeTeam

  19. 0KeeTeam

    0KeeTeam

  20. 0KeeTeam

    0KeeTeam

  21. 0KeeTeam

    0KeeTeam

  22. 0KeeTeam

    0KeeTeam

  23. 0KeeTeam

    0KeeTeam

  24. rqwwisreqbxsdkyjpcky

    0KeeTeam

  25. 0KeeTeam|expr 921763908 + 865164635

    0KeeTeam

  26. 0KeeTeam%0d%0aCRLF-Header:CRLF-Value

    0KeeTeam

添加新评论

ali-40.gifali-41.gifali-42.gifali-43.gifali-44.gifali-45.gifali-46.gifali-47.gifali-48.gifali-49.gifali-50.gifali-51.gifali-52.gifali-53.gifali-54.gifali-55.gifali-56.gifali-57.gifali-58.gifali-59.gifali-60.gifali-61.gif