Как реализовать тег кодового блока с вкладками для Hexo

Я пытаюсь создать блоки кода с вкладками (как плагин тегов) в шестнадцатеричном формате, но я не могу понять, куда поместить мою функцию js. Я думал, что смогу загрузить функцию, используя помощник js, но я не знаю, куда включить помощник. Я попытался и не смог добавить его в плагин тега. Это код плагина тега (сохраненный как testtag.js):

hexo.extend.tag.register('testtag', function(args, content){
  var className =  args.join(' ');

  var result = '';
  result += "<\%- js('\\themes\\bootstrap-blog\\scripts\\tab.js') \%>"
  result += '<div class="tabs">';
  result += '<ul>';
  result += '<li class="li_tab1" onclick="tab(&apos;tab1&apos;)"><a>Tab 1</a></li>';
  result += '<li class="li_tab2" onclick="tab(&apos;tab2&apos;)"><a>Tab 2</a></li>';
  result += '</ul>';
  result += '<div class="contentarea">';
  result += '<div id="tab1">';
  result += '<p>' + content + '</p>';
  result += '</div>';
  result += '<div id="tab2" style="display: none;">'
  result += '<p>This is the text for tab 2.</p>'
  result += '</div>'
  result += '</div>'
  result += '</div>'

  return result;

}, {ends: true});

что работает. Однако событие onclick тегов просто вызывает ошибку, что не может найти функцию tab. Обратите внимание, что первая строка result выше была моей неудачной попыткой использовать помощник.

Это моя функция tab, tab.js:

function tab(tab) {
document.getElementById('tab1').style.display = 'none';
document.getElementById('tab2').style.display = 'none';
document.getElementById('li_tab1').setAttribute("class", "");
document.getElementById('li_tab2').setAttribute("class", "");
document.getElementById(tab).style.display = 'block';
document.getElementById('li_'+tab).setAttribute("class", "active");
}

И tab.js, и testtag.js сохранены в папке *\themes\bootstrap-blog\scripts*.

Я видел этот ответ, который, хотя и мог бы помочь, но я не могу понять, что такое представление. Я не смог найти ничего о представлениях в документах Hexo.


person Dan    schedule 01.02.2016    source источник
comment
Эй, мужик. Я пытаюсь понять вашу проблему. В вашем коде есть несколько проблем. Что ты пытаешься сделать? Мне нужно это знать, чтобы дать вам разумный ответ. представления/шаблоны хранятся в theme/theme_name/layout   -  person Louis Barranqueiro    schedule 05.02.2016
comment
Я хочу сделать плагин тега, который представляет собой кодовый блок с вкладками. например, я хочу опубликовать один и тот же код на нескольких языках и иметь возможность переключаться между языками с помощью вкладок. Например, что-то вроде это, но также с раскраской кода. Обратите внимание, что я только что нашел эту ссылку прямо сейчас, и, поскольку в ней нет js, это может быть для меня хорошим шагом вперед. Но меня все еще интересует ответ на вопрос в его нынешнем виде. Элементы моего списка отображаются с помощью onclick="tab('tab2'), поэтому мне нужно написать эту функцию tab(), но я не уверен, где разместить для нее код.   -  person Dan    schedule 05.02.2016
comment
Я лучше сделаю это в теме.   -  person Leo    schedule 25.02.2016
comment
@Leo Можете ли вы объяснить, как это сделать в теме? Я обнаружил, что документов сильно не хватает...   -  person Dan    schedule 25.02.2016
comment
@Leo Привет, ребята, я только что опубликовал полный пример, объясняющий процесс.   -  person Louis Barranqueiro    schedule 02.03.2016
comment
В дополнение к феноменальному ответу Луи ниже, эта стенограмма чата показывает более подробную информацию о том, как это реализовать, а также отвечает на мой первоначальный вопрос о том, куда я могу поместить js: chat.stackoverflow.com/transcript/105190   -  person Dan    schedule 03.03.2016


Ответы (2)


В вашем коде слишком много ошибок, поэтому я предпочитаю привести полный пример с пояснениями.

Вот что нам нужно:

  1. Пользовательский тег для создания структуры HTML этого нескольких кодовых блоков.
  2. Файл CSS для стилизации блока кода и окраски кода.
  3. Скрипт JS для анимации блока кода (вкладки)

Пользовательский тег: m_codeblock (на стороне сервера JS)

Нам нужно разрешить пользователю определять:

  • имя или ссылка
  • несколько кодовых блоков на этой вкладке, поэтому мы определили тег для разделения и перечисления каждой вкладки

Вот синтаксис:

{% m_codeblock [name] [link] %}
    <!-- tab [lang] -->
        source_code
    <!-- endtab -->
{% endm_codeblock %}

и пример:

{% m_codeblock stack overflow https://example.fr %}
    <!-- tab html -->
        <html>
            <body>
                <h1>Hey dan</h1>
            </body>
        </html>
    <!-- endtab -->
    <!-- tab css -->
        h1 {
            color:red;
        }
    <!-- endtab -->
{% endm_codeblock %}        

Установите эти зависимости в папку вашего блога (не в папку темы):

  • беги npm install jsdom --save
  • беги npm install jquery --save

а вот исходный код этого пользовательского тега, помещенного в themes/theme_name/scripts/m_codeblock.js:

'use strict';

var util = require('hexo-util');
var highlight = util.highlight;
var stripIndent = require('strip-indent');
var rCaptionUrl = /(\S[\S\s]*)\s+(https?:\/\/)(\S+)/i;
var rCaption = /(\S[\S\s]*)/;
var rTab = /<!--\s*tab (\w*)\s*-->\n([\w\W\s\S]*?)<!--\s*endtab\s*-->/g;

// create a window with a document to use jQuery library
require("jsdom").env("", function(err, window) {
    if (err) {
        console.error(err);
        return;
    }

    var $ = require("jquery")(window);

    /**
     * Multi code block
     * @param args
     * @param content
     * @returns {string}
     */
    function multiCodeBlock(args, content) {
        var arg = args.join(' ');
        // get blog config
        var config = hexo.config.highlight || {};

        if (!config.enable) {
            return '<pre><code>' + content + '</code></pre>';
        }

        var html;
        var matches = [];
        var match;
        var caption = '';
        var codes = '';

        // extract languages and source codes
        while (match = rTab.exec(content)) {
            matches.push(match[1]);
            matches.push(match[2]);
        }
        // create tabs and tabs content
        for (var i = 0; i < matches.length; i += 2) {
            var lang = matches[i];
            var code = matches[i + 1];
            var $code;
            // trim code
            code = stripIndent(code).trim();
            // add tab
            // active the first tab
            if (i == 0) {
                caption += '<li class="tab active">' + lang + '</li>';
            }
            else {
                caption += '<li class="tab">' + lang + '</li>';
            }
            // highlight code
            code = highlight(code, {
                lang: lang,
                gutter: config.line_number,
                tab: config.tab_replace,
                autoDetect: config.auto_detect
            });
            // used to parse HTML code and ease DOM manipulation
            // display the first code block
            $code = $('<div>').append(code).find('>:first-child');
            if (i == 0) {
                $code.css('display', 'block');
            }
            else {
                $code.css('display', 'none');
            }

            codes += $code.prop('outerHTML');
        }
        // build caption
        caption = '<ul class="tabs">' + caption + '</ul>';
        // add caption title
        if (rCaptionUrl.test(arg)) {
            match = arg.match(rCaptionUrl);
            caption = '<a href="' + match[2] + match[3] + '">' + match[1] + '</a>' + caption;
        }
        else if (rCaption.test(arg)) {
            match = arg.match(rCaption);
            caption = '<span>' + match[1] + '</span>' + caption;
        }
        codes = '<div class="tabs-content">' + codes + '</div>';
        // wrap caption
        caption = '<figcaption>' + caption + '</figcaption>';
        html = '<figure class="highlight multi">' + caption + codes + '</figure>';
        return html;
    }

    /**
     * Multi code block tag
     *
     * Syntax:
     *   {% m_codeblock %}
     *   <!-- tab [lang] -->
     *       content
     *   <!-- endtab -->
     *   {% endm_codeblock %}
     * E.g:
     *   {% m_codeblock %}
     *   <!-- tab js -->
     *       var test = 'test';
     *   <!-- endtab -->
     *   <!-- tab css -->
     *       .btn {
     *           color: red;
     *       }
     *   <!-- endtab -->
     *   {% endm_codeblock %}
     */
    hexo.extend.tag.register('m_codeblock', multiCodeBlock, {ends: true});
});

Прочтите комментарий, чтобы понять код.

Все, что вам нужно сделать, это поместить ваши файлы JavaScript в папку scripts, и Hexo загрузит их во время инициализации.

Стилизовать блок кода

По умолчанию отображается только первая вкладка, а остальные скрыты, и мы сделали это в исходном коде пользовательского тега здесь:

$code = $('<div>').append(code).find('>:first-child');
if (i == 0) {
  $code.css('display', 'block');
}
else {
  $code.css('display', 'none');
}

Так что вам просто нужно больше CSS для улучшения пользовательского интерфейса и окраски кода. Поместите этот файл в theme/theme_name/assets/css/style.css и свяжите его с макетом.

Анимируйте блок кода (сторона клиента JS)

Нам нужен javascript для анимации вкладки. Когда мы нажимаем на вкладку, все содержимое вкладки должно быть скрыто и отображаться только правая вкладка. Поместите этот скрипт в theme/theme_name/assets/js/script.js и свяжите его с макетом.

$(document).ready(function() {
  $('.highlight.multi').find('.tab').click(function() {
    var $codeblock = $(this).parent().parent().parent();
    var $tab = $(this);
    // remove `active` css class on all tabs
    $tab.siblings().removeClass('active');
    // add `active` css class on the clicked tab
    $tab.addClass('active');
    // hide all tab contents
    $codeblock.find('.highlight').hide();
    // show only the right one
    $codeblock.find('.highlight.' + $tab.text()).show();
  });  
});

Ваша проблема заключалась в возможности создать этот собственный тег, и я собираюсь интегрировать его в следующий выпуск темы hexo (Tranquilpeak), который я разработал.

Вот результат: результат

Проверьте его в реальном времени на JSFiddle

person Louis Barranqueiro    schedule 02.03.2016
comment
Спасибо большое! Я пробовал это, но когда я запускаю сервер, он выдает ошибку Error: Cannot find module 'jsdom'. Нужно ли как-то сначала устанавливать модуль? Кроме того, можете ли вы объяснить, как связать что-то с макетом? - person Dan; 02.03.2016

Вы также можете создавать внутренние теги, которые читаются родительским.

{% code_with_tabs %}
  {% code js title="something" class_name="info" %}
    1. aosjaojsdoajsdoajsd
  {% endcode %}
  {% code js title="something" class_name="info" %}
    2. aosjaojsdoajsdoajsd
  {% endcode %}
{% endcode_with_tabs %}

И используйте парсер для чтения сгенерированного внутреннего HTML.

person matsko    schedule 06.06.2016