TiddlyWiki — очень хорошая штука и я давно ею пользуюсь. Тем не менее, некоторых вещей в ней нет, и это минус. Но её можно творчески допилить напильником, и это плюс.

Понадобился мне прогрессбар. Применений у него куча: показывать степень готовности поста, например. Или количество выполненных дел по сравнению с оставшимися. Да и вообще, наглядная штука.

Поскольку прогрессбара из коробки нет, начал я думать, как бы его добавить. И, для начала, сформулировал к нему требования. В прогрессбаре должна быть возможность задать следующие параметры:

  • цвет заполнения;
  • размер;
  • подсказку при наведении.

Если какой-то параметр не задан, то он должен вычисляться автоматически.

Для решения подобных задач в TiddlyWiki существуют макросы. Это заранее заданный фрагмент текста, куда можно передать параметры и подставить их в нужные места.

В рамках эксперимента, я набросал пробный макрос, выводящий прогрессбар:

\define progressbar(count:"0" total:"10")
<svg width="$total$5" height="15">
<g>
<title>$count$ из $total$</title>
<rect x="1.5" y="4.5" height="10" width="$count$0" rx="3" ry="3" style="fill: green; stroke: none" />
<rect x="1.5" y="4.5" height="10" width="$total$0" rx="3" ry="3" style="fill: none; stroke: green" />
</g>
</svg>
\end

В том же тиддлере (так в TiddlyWiki называется фрагмент текста) дописал внизу вызов этого макроса:

Прогрессбар: <<progressbar 3 4>>

Сохранил тиддлер и полюбовался результатом:

image

Схема оказалась принципиально рабочей. Но имела кучу несоответствий исходным требованиям. И исправить это было пока нельзя из-за крайней ограниченности создания макросов с помощью формата WikiText. Пришлось разбираться дальше.

В документации обнаружилось упоминание, что макросы можно писать и на JavaScript. Для этого тиддлер макроса должен содержать поле module-type со значением macro (поля в TiddlyWiki представляют собой метаданные и могут назначаться любому тиддлеру). Скрипт макроса должен экспортировать следующие свойства:

  • name: Строка, представляющая собой имя, по которому будут вызывать макрос
  • params: Массив объектов со следующими свойствами:
    • name: имя параметра
    • default: (необязательно) значение параметра по умолчанию
  • run: Функция, которая будет вызываться при запуске макроса. Параметры извлекаются из вызова макроса и располагаются согласно массиву params. Функция run должна вернуть строковое значение макроса. При вызове this указывает на узел виджета, вызвавшего макрос.

Если массив params пуст или отсутствует, то все параметры будут просто переданы в метод run().

Это уже было хорошо. Но в TiddlyWiki нельзя просто взять и сделать тиддлер с куском JavaScript-а. Вернее, можно, но работать не будет. Чтобы конструкция заработала, её следует надлежащим образом оформить. Например, сделать плагин.

Что есть плагин в терминологии TiddlyWiki? Это некая совокупность тиддлеров, собранная в единое целое, которая помечена как скрытая и не отображается в обычном списке тиддлеров, может встраиваться в систему и запускаться на выполнение, а также может импортироваться из одной wiki в другую парой щелчков.

Первоначально для написания плагинов требовалось развернуть довольно сложную инфраструктуру с использованием node.js. Я когда-то пробовал это делать, но не получилось (не помню уже, по какой причине). Однако, на данный момент разработчик добавил возможность писать плагины прямо в браузере, чем я воспользовался и сейчас расскажу вам, как это делается.

Итак, уважаемые читатели, давайте приступим к созданию плагина. Сверяясь с документацией, скачаем чистый файл TiddlyWiki и начнём делать из него заготовку для плагина.

Первое, что надо сделать, это создать тиддлер HelloThere, настроить его автоматическое отображение при открытии страницы и прописать в нём ссылки на тиддлеры плагина.

Путь к плагину, согласно документации, должен выглядеть так:

$:/plugins/ваше_имя/название_плагина

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

$:/plugins/ваше_имя/название_плагина/имя_скрипта.js

Вот так выглядит мой вариант тиддлера HelloThere:

* [[$:/plugins/morthan/progress]]
* [[$:/plugins/morthan/progress/progressbar.js]]

Прогрессбар получает следующие параметры:

; `count`
: Количество выполненного
; `total`
: Количество всего
; `width`
: Если указано --- длина прогрессбара в пикселях
; `color`
: Цвет заполнения (стандартно --- зелёный)
; `title`
: Подсказка, которая выводится при наведении мыши (по умолчанию --- '{count} из {total}')

<<progressbar 30 42 80>>

А это он же в отрисованном виде:

image

Стоит обратить внимание на две вещи. Во-первых, ссылки даны курсивом, потому что ни плагина, ни скрипта, входящего в его состав, пока не существует. Во-вторых, прогрессбар тоже пока не отрисован — по той же причине.

Чтобы настроить автоматическое отображение тиддлера при открытии страницы надо добавить его в список Default tiddlers. Его можно обнаружить либо в открывающемся по умолчанию тиддлере GettingStarted, либо найти в сайдбаре пиктограмму в виде шестерёнки и нажать её. В появившейся панели настроек ищем Default tiddlers и прописываем туда наш HelloThere.

image

Всё, первая часть готова. Теперь надо создать тиддлеры плагина и скрипта. Начнём с плагина.

Чтобы создать тиддлер, достаточно щёлкнуть по ссылке на него. Появляется заготовка тиддлера, нажимаем «Редактировать» и редактируем.

Как подсказывает документация, в плагине должны быть прописаны такие поля:
Поле Значение
dependents Список разделённых пробелами плагинов, от которых зависит наш плагин
(для названий с пробелами используйте квадратные скобки)
description Описание плагина
plugin-type Для обычного плагина «plugin», для темы «theme», а для языкового пакета «language»
type Задайте «application/json»
version Номер версии плагина (например, «0.0.1»)
Заполняем поля, а в теле плагина пишем:

{"tiddlers": {}}

image

Сохраняем тиддлер. Треть работы выполнена.

Аналогичным образом поступаем со второй ссылкой, скриптом макроса. Щёлкаем по ней, нажимаем «Редактировать», заполняем поля:
Поле Значение
type «application/javascript»
module-type «macro»
Теперь само тело скрипта (под спойлером):

Показать код
/*title: $:/plugins/morthan/progress/progressbar.js
type: application/javascript
module-type: macro

Macro to display progressbar

\*/
(function(){

/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";

/*
Information about this macro
*/

exports.name = "progressbar";

exports.params = [
    {name: "count"},
    {name: "total"},
    {name: "width",
     default: ""},
    {name: "color",
     default: "green"},
    {name: "title",
     default: "{count} из {total}"}
];

/*
Run the macro
*/
exports.run = function(count, total, width, color, title) {
    count = parseInt(count);
    total = parseInt(total);
    width = (width == '') ? total * 10 : parseInt(width);
    var html = [svg(width), '<g>', tagTitle(count, total, title),
        innerRect(count, total, width, color), outerRect(width), '</g>',
        '</svg>'];
    return html.join("\n");
};

function svg(width) {
    width += 5;
    return '<svg width="' + width + '" height="15">';
};

function rect(width, fill) {
    return '<rect x="1.5" y="4.5" height="10" width="' + width +
        '" rx="3" ry="3" style="fill: ' + fill + '; stroke: green" />';
};

function outerRect(width) {
    return rect(width, 'none');
};

function innerRect(count, total, width, color) {
    var dx = 0;
    if (count > 0 && count != total) {
        dx = count * width / total;
    }
    else if (count == total) {
        dx = width;
    }
    return rect(dx, color);
};

function tagTitle(count, total, title) {
    if (title == '') return '';
    return '<title>' + title.replace('{count}', count).replace('{total}', total) + '</title>';
};

})();


Я джаваскриптер не настоящий, пишу на этом языке от случая к случаю, поскольку идиотский сишный синтаксис с фигурными скобками меня очень раздражает. И вообще, это только пример написания плагина.

В общем, сохраняем готовый тиддлер. Всё, что нужно, уже написано. Но не работает, потому что не оформлено.

А сейчас — магия! Открываем консоль JavaScript в браузере и пишем там вот что:

$tw.utils.repackPlugin('$:/plugins/morthan/progress', ['$:/plugins/morthan/progress/progressbar.js'])

Первый аргумент это имя плагина, который подлежит перепаковке. Второй — список тиддлеров, которые будут включены в плагин. Нажимаем Enter. Браузер говорит, что всё прошло успешно и для того, чтобы изменения возымели действие, надо сохранить и перезагрузить wiki.

Так и сделаем. После переоткрытия в тиддлере HelloThere внезапно появляется красивый зелёненький прогрессбар.

Заключение


В новой версии TiddlyWiki появилась возможность писать плагины прямо не выходя из бассейна браузера. Это позволяет творчески доработать систему под себя, открывает неограниченные горизонты и ещё на один маленький шаг приближает нас к мировому господству.

PS: не уверен в правильности выбора хабов, ибо не совсем понимаю, зачем они нужны. Если что-то сделал неправильно, буду благодарен за разъяснения в комментариях.

Комментарии (4)


  1. RPG
    22.06.2015 08:30

    Всё же 1.5 МБ для пустой вики, в которой поиск работает криво — это перебор. И это они ещё jQuery выкинули!

    Если интересует, у меня есть форк ветки 2.8 с некоторыми изменениями в части разделения движка на составные части (стиль и скрипт отдельными файлами, допилил до удобства использования под Android, а также пытаюсь выбросить зависимость jQuery), быстрый поиск с подсветкой и подсчётом релевантности, улучшенная разметка, CSS3 и т.п. Она пока проходит обкатку, но через некоторое время это можно будет выложить на github.


    1. Morthan Автор
      22.06.2015 09:01

      Я так понимаю, автор TiddlyWiki развращён зарубежными провайдерами и считает, что в наш век электроники и пластилина пустая вики на полтора метра — это ничего особенного. В принципе, его можно понять (где-то видел новость, что средний размер веб-страницы уже два метра, на этом фоне TiddlyWiki ещё прилично смотрится).

      От форка не откажусь, но сейчас банально нет времени копаться в чём-то новом. Однако, буду благодарен за ссылку на github, когда оный форк там появится.


      1. RPG
        22.06.2015 10:06

        Дело не в провайдерах — эта вики создана быть локальной (правда, бекапы растут в геометрической прогрессии — каждый по 2 МБ). Дело в том, что новая версия откровенно тормоз (я ещё учитываю андроид и ноутбуки). Для меня основная суть базы знаний — наличие хорошего поиска. А в TW5 его просто нет, то что они там сделали — поиском назвать сложно. Последний гвоздь в крышку гроба — сложность хакинга. Для 5 версии вон, пришлось целую статью написать, а под 2.х.х просто тонны плагинов придумано, потому что порог вхождения минимальный, у меня пару-тройку вечеров ушло на то, чтобы переделать там поиск, русифицировать, выкинуть поддержку древних браузеров, добавить свою разметку по вкусу и поправить некоторые косяки.

        P.S. Если у сообщества появится интерес к развитию 2 ветки — можно сделать полноценный форк с тестами и сборкой.


        1. Morthan Автор
          22.06.2015 10:35

          Я от бекапов вообще отказался, держу вики в дропбоксе и полагаюсь на возможность восстановить предыдущую версию. Про тормоза соглашусь. Заставить по-человечески работать андроидную версию у меня пока не получилось.