Search code examples
javascriptjquerytwitter-bootstrapcodemirror

Bootstrap tabs with CodeMirror


I have a pretty complex page where I have a number of instances of CodeMirror in hidden tabs within tabs. To then make it even more complex I remember the last active tabs.

I've manage to get it half working (http://codepen.io/anon/pen/LheaF) the problems are with the Second Editor tabs:

  1. Its loading the Second tabs before the main Code Mirror tabs has been clicked. When you do click the Code Mirror tab it doesn't load the editor correctly either, until you click twice.
  2. I want the second tabs to call the refresh() method if its already been initiated, like I do for the main editor.
  3. Bug where its duplicating the secondary editors

(function($) {
    var mainEditor;

    function initMainCodeEditor() {
        if (mainEditor instanceof CodeMirror) {
            mainEditor.refresh();
        } else {
            // Load main editor
            var el = document.getElementById("codifyme");
            mainEditor = CodeMirror.fromTextArea(el, {
                lineNumbers: true
            });
            mainEditor.setSize('100%', 50);
        }
    }

    function initSecondaryCodeEditor() {
        var $active = $('#code_mirror_editors > .active > a');
        var $sec_tab = $($active.data('target'));

        CodeMirror.fromTextArea($sec_tab.find('textarea')[0], {
            lineNumbers: true
        });
    }

    $(document).ready(function() {

        // Only load editors if tab has been clicked
        $('#maintabs > li > a[data-target="#codemirror"]').on('shown.bs.tab', function(e) {
            initMainCodeEditor();
        });

        $('#code_mirror_editors > li > a[data-toggle="tab"]').on('shown.bs.tab', function(e) {
            initSecondaryCodeEditor();
        });

        // Remember tabs
        var json, tabsState;
        $('a[data-toggle="tab"]').on('shown.bs.tab', function(e) {
            tabsState = localStorage.getItem("tabs-state");
            json = JSON.parse(tabsState || "{}");
            json[$(e.target).parents("ul.nav.nav-pills, ul.nav.nav-tabs").attr("id")] = $(e.target).data('target');

            localStorage.setItem("tabs-state", JSON.stringify(json));
        });

        tabsState = localStorage.getItem("tabs-state");

        json = JSON.parse(tabsState || "{}");
        $.each(json, function(containerId, target) {
            return $("#" + containerId + " a[data-target=" + target + "]").tab('show');
        });

        $("ul.nav.nav-pills, ul.nav.nav-tabs").each(function() {
            var $this = $(this);
            if (!json[$this.attr("id")]) {
                return $this.find("a[data-toggle=tab]:first, a[data-toggle=pill]:first").tab("show");
            }
        });

    }); // doc.ready
})(jQuery);

Solution

  • The problems:

    1. it might happen that you create the CodeMirror on an element which is not visible (one of it's parents has display: none). This breaks various calculations done by CodeMirror
    2. by getting the CodeMirror instance right from the CodeMirror container element enables us to call refresh everytime you want (by finding the .CodeMirror next to your textarea)
    3. fixed as a side effect of 2.