
Порыскав в интернетах на предмет вразумительного редактора BB-тегов, обладающего с нормальным функционалом, дало парочку годных, включая один навороченный wysiwyg. Wisywyg я не хотел, предпочел обычное текстовое редактирование, без визуализации тегов на лету. Появилась тень челенджа, которая будоражила мозги до кончиков пальцев ). Порыскав еще минуты 3 – мысль окончательно взяла верх. На планете явно не хватает еще одного велосипеда, нельзя это так оставлять, нужен свой велосипед с цепью и педалями.
Задача проста – минимальный набор тегов жирный, курсив, подчеркнутый и т.д. Обязательно продвинутые теги типа цвет текста, ссылки или вставить картинку. И, конечно, обрезалкой текста до определенной границы, чтобы было похоже на превью текста c ссылкой «подробнее».
Написать плагин под фреймворк типа jQuery - слишком банально, хочется хардкора. Поэтому чистый JS.
Ну окай.
По ходу пьесы приходится изобретать много чего не нужного, например екстендера объектов типа jQuery.extend(o1,o2) увы нет.
function _extendOptions() {
var baseOptions = {
target: null,
tags: [{ tag: "b", separator: false },
{ tag: "s", separator: false },
{ tag: "i", separator: false },
{ tag: "u", separator: false },
{ separator: true }]
};
//merge options
for (var bO in baseOptions) {
if (!options[bO]) { options[bO] = baseOptions[bO]; }
}
if (!options.tags || !options.tags.length) {
options.tags = baseOptions.tags;
}
}
options – это объект полученный от пользователя. Набор минимальных свойств и объектов определен. Теперь, даже если они не были объявлены или правильно инициализированы и с ними пытаются что-то сделать - не возникнет ошибки. К тому же избавит от кучи проверок в и ловли исключений.
Далее, нужно создавать форму редактора. На чистом JS это более затратно и менее удобно, чем в любимом jQuery.
//format bbObject
function _formHTMLInput() {
_target = document.getElementById(options.target);
if (_target) {
var tag = {};
for (var t in options.tags) {
tag = document.createElement('div');
if (!options.tags[t].space) {
if (options.tags[t].caption) {
tag.innerHTML = options.tags[t].tag;
}
tag.className = 'tag';
tag.setAttribute('tag', options.tags[t].tag);
//store to tag.options
tag.BBEditor = options.tags[t];
}
else tag.className = 'tag space';
_data.tagElements.push(tag);
}
_data.wrap = document.createElement('div');
_data.wrap.className = 'bbeditor__wrap';
_data.tagElements_wrap = document.createElement('div');
_data.tagElements_wrap.className = 'tags__wrap';
_data.textElement_wrap = document.createElement('div');
_data.textElement_wrap.className = 'text__wrap';
_data.textElement = document.createElement('textarea');
_data.textElement.id = _target.id;
_data.textElement.name = _target.name;
_data.textElement.className = _target.className;
_data.textElement.innerHTML = _target.innerHTML;
}
}
_data и
_target – это глобальные объекты, которые находятся в области видимости.
Наконец самое интересное. Вставка тегов в текстовое поле, применение дополнительных опций тега, например url для или width для image. Тут использование нативного JS дало хоть какое-то преимущество, в виде отсутствия лишнего и ненужного.
function insertBB(tag, tagOptions, justInsertTag) {
var tagStart = tagOptions && tagOptions.length ? `[${tag} ${tagOptions.join(' ')}]` : `[${tag}]`,
tagEnd = `[/${tag}]`;
var textarea_val = _data.textElement.value;
var selectionStarPos = _data.textElement.selectionStart;
var selectionEndPos = _data.textElement.selectionEnd;
var selectedText = textarea_val.substring(selectionStarPos, selectionEndPos);
var cursorPosition = 0;
if (selectedText &&
!justInsertTag) {
cursorPosition = selectionEndPos + tagStart.length + tagEnd.length;
_data.textElement.value = `${textarea_val.substring(0, selectionStarPos)}${tagStart}${selectedText}${tagEnd}${textarea_val.substring(selectionEndPos)}`;
}
else {
cursorPosition = selectionStarPos + tagStart.length + tagEnd.length;
_data.textElement.value = `${textarea_val.substring(0, selectionStarPos)}${tagStart}${tagEnd}${textarea_val.substring(selectionStarPos)}`;
}
_data.textElement.setSelectionRange(cursorPosition, cursorPosition);
}
Я надеюсь, что уже все пользователи успели перейти на ecmascript 6. О чем намекает конструкция `${1==1}`.
Первое что делает функция это определяет открывающий и закрывающий BB-тег. В случае, если тег должен содержать определенные опции, типа url, width или что-то вроде, то происходит попытка сериализовать массив tagOptions (tagOptions.join(‘’));
Теперь нужно ловить место курсора, выделенную область текста.
var selectionStarPos = _data.textElement.selectionStart;
var selectionEndPos = _data.textElement.selectionEnd;
var selectedText = textarea_val.substring(selectionStarPos, selectionEndPos);
Есть. Если есть выделенный текст – окружить его тегами. Если нету – вставить в самый конец текста.
Для получения списка опций тега, как и их значений, нужна еще одна форма. Ее создание ничем не примечательно и описывать это точно не нужно.
Плагин доступен на гите -
https://github.com/zizzlezee/nativejsbb.
Форки приветствуются!
ЗЫ
Позднее я расскажу как собрать полученный bb-текст на сервере и перебросить его в html.