• Technical support is temporarily provided through the Discord

кастомизация чата/форума своими руками (userscript)

vxsw

Captain Of The Ark
Gold Founder
Joined
Feb 10, 2016
Messages
2,636
Likes
1,036
#1
товарищ бендер дал добро с оговоркой:
все чем вы попытаетесь заняться из этой темы, вы делаете на свой страх и риск.
претензии по работе форума/чата в режиме работы скрипта НЕ принимаются!
совсем.

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

собственно первый кирпич в огород, отложился с обновлением форума и чата в частности. и если по форуму нареканий в целом нет, то расположение и цветовая схема части элементов чата, вызывали внутреннюю дисгармонию. и тут возникли две проблемы:
- чат находится на стадии разработки, разрабатывает его не наш всемогущий, а часть кода закрыта и он в принципе ее менять не может. +выделение времени на фиксы мелких косяков фактически временной версии чата к-е возможно в следующих версиях уже будут исправлены не целесообразно.
- на вкус и цвет, все фломастеры разные. и что одному "услада для глаз", другому "разрыв шаблона". всем не угодишь.

как итог - пришлось искать напильник и вдаваться в поверхностное понимание его работы

оговорюсь сразу - я не профессионал, знания мои поверхностны и если где-то что-то ляпну не то или не так, не судите строго. +все описание пойдет по моему "стенду" (firefox 56.0.2), ибо разбираться по всем фронтам нет ни времени ни желания. но информация на самом деле актуальна для подавляющего большинства браузеров +-со своими нюансами, но не меняющими сути дела кардинально.


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

сначала, немного для понимании происходящего.
любая открытая страница любого сайта, представляет собой дерево элементов, со своими параметрами. будь то цвет, размер, положение или еще что.
это можно наглядно увидеть шлепнув правой мышкой по любому элементу страницы и нажав на "исследовать элемент":
1522398836703.png
при этом, выделенный в дереве элемент, будет подсвечен и на самой странице. что весьма удобно и облегчает поиск нужного.
в принципе, для базовых манипуляций, понимания этого более чем достаточно.

по большому счету, все изменения можно проводить прямо в этом окне. страница будет сразу меняться и можно получить желаемое прямо через этот инструмент. вот только при обновлении страницы, все ваши манипуляции пропадут и все придется делать заново.
процесс можно автоматизировать посредством т.н. "userscript". фактически, это последовательность команд, к-я выполняется автоматически после загрузки страницы. пугаться не стоит, все гораздо проще чем кажется.

если у вас не фаирфокс, а другой браузер, придется самостоятельно разобраться как использовать на нем userscript (может кто ниже распишет). если у вас фаирфокс - необходимо поставить аддон, добавляющий эту поддержку. наиболее распространенный (на сколько я понял) gracemonkey. последняя версия работает только с квантумом, для более старых, нужна соответственно более старая версия. спиок всех доступных для фаирфокса с возможностью установки, находится здесь: Greasemonkey :: Versions :: Add-ons for Firefox
листайте вниз до ближайшей подходящей и добавляйте.
после его установки (вроде потребует перезапуск браузера), в панели появится иконка мартышки:
1522400275837.png
тыкаем на комбик справа и нажимаем "создать скрипт".
появится меню с базовыми параметра скрипта, заполнить необходимо только первые два. остальное не принципиально и можно изменить прямо в теле самого скрипта.
откроется окно скрипта:
1522400830420.png
пустой скрипт, представляет собой ряд метаданных (строки начинающиеся с "// @". основные это:
- название скрипта (@name)
- описание (@description)
- автор (@author)
- пространство имен (@namespace)
- версия (@version)
- где применяется (@include)
- обязательный параметр "@grant", но нас он не интересует

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

дальше пойдет описание на примере модификации чата с такого:

до такого:


самое главное - надо ограничить область выполнение скрипта, чтобы он отрабатывал исключительно в нужном нам месте, в нужное время и нужное количество раз (за загрузку). для этого достаточно использовать шаблон. банально скопировав его и заменив все что уже есть в вашем свежесозданном скрипте. думать тут ни над чем не надо, но если оч. интересно, в комментариях указано что за что отвечает:
Code:
// ==UserScript==
// @name        genom_chat
// @description genom chat
// @author vx
// @namespace   NetherWorld
// @version 1.0
// @include     https://pgenom.com/community/
// @grant    none
// ==/UserScript==

// Оборачиваем скрипт в замыкание, для кроссбраузерности (opera, ie)
(function (window, undefined) {  // нормализуем window
    var w;
    if (typeof unsafeWindow != undefined) {
        w = unsafeWindow
    } else {
        w = window;
    }
    // не запускаем скрипт во фреймах
    // без этого условия скрипт будет запускаться несколько раз на странице с фреймами
    if (w.self != w.top) {
        return;
    }
    // дополнительная проверка наряду с @include
    if (/https:\/\/pgenom.com/.test(w.location.href)) {
        window.onload = function () // ждем окончания прогрузки страницы, чтобы гарантированно вылавливать все элементы
        { //Ниже идёт непосредственно код скрипта

        }
    }
})(window);
шаблон пересперт не помню с какого сайта и добавлено ожидание прогрузки страницы (пардон за авторские права если у кого имеются).

самое страшное и непонятное позади. теперь, все что нужно, это найти необходимые элементы и собственно произвести с ними какие-то действия.
основные способы поиска элементов:
- по его уникальному идентификатору (id)
- по имени класса
идеальный вариант - уникальный идентификатор. собственно по тому, что он уникальный, а следовательно единственный на странице, с таким обозначением. проблема в том, что не у каждого элемента он есть. в этом случае, на помощь приходит "имя класса". но тут есть другая проблема - элементов с одинаковыми именами классов, может быть бесконечно много. а у некоторых элементов, нет и его.

условно принимаем, что есть хотя-бы один опознавательный признак и исходя из этого, алгоритм поиска нужного элемента следующий:
- находим нужный элемент в дереве (правый тык, "исследовать элемент", доцеливаемся при необходимости до нужного в ручную)
- смотрим, есть-ли у него параметр "id=". если есть - ищем по нему, если нет:
- запоминаем имя класса найденного элемента "class=" и ищем вверх по дереву, ближайший элемент с имеющимся уникальным идентификатором ("id=")
- теперь задача сводится к нахождению этого элемента с уникальным идентификатором, а уже от него, нужно организовывать поиск элемента по имени класса

разберем дерево чата:
1522404646839.png
идентификаторы основных элементов, по которым будем их искать, подчеркнул зеленым. при наведении на эти строки мышкой, будут подсвечены эти элементы и все в них вложенные (ниже по ветке дерева). поводите по ним мышкой и сразу станет все понятно.

визуально нашли, теперь это надо все сделать посредством скрипта.
для удобства, каждому нужному нам элементу, назначаем переменную:
Code:
            var fr_chat = document.getElementById("siropuChat"); // собственно все окно чата. корень его дерева
            var fr_editor = document.getElementById("siropuChatEditor"); // контейнер с панелью форматирования и строкой ввода
            var fr_header = document.getElementById("siropuChatHeader"); // заголовок чата
            var fr_tabs = document.getElementById("siropuChatTabs"); // панель закладок комнат
            var fr_mlog = document.getElementById("siropuChatContent"); // окно лога сообщений
            var fr_edit = fr_editor.getElementsByClassName("fr-wrapper show-placeholder")[0]; // строка ввода
            var fr_tools = fr_editor.getElementsByClassName("fr-toolbar fr-ltr fr-desktop fr-top fr-basic")[0]; // панель форматирования
            var fr_banner = fr_editor.getElementsByClassName("siropuChatAds")[0]; // строка объявления. присутствует если собственно есть само объявление
            var fr_pholder = fr_edit.getElementsByClassName("fr-placeholder")[0]; // поле ввода в строке ввода
            var fr_bsend = fr_editor.getElementsByClassName("button--link button")[0]; // кнопка отправки сообщения (на данный момент отключена и ее нет)
все необходимые элементы найдены, производим манипуляции с ними:
Code:
            fr_editor.parentNode.insertBefore(fr_editor, fr_tabs); // переставляем ветку контейнера с панелью форматирования и строкой ввода
                                                                   //  над панелью закладок комнат
            fr_edit.parentNode.insertBefore(fr_edit, fr_tools); // поднимаем строку ввода над панелью форматирования

            fr_edit.style.backgroundColor = "#f0f0f0"; // задаем цвет фона строки ввода
            fr_pholder.style.color = "#000000"; // цвет шрифта надписи с предложением ввода сообщения
            fr_pholder.parentNode.children[0].style.color = "#000000"; // цвет шрифта набираемого текста

            if (typeof(fr_bsend) != 'undefined' && fr_bsend != null) // проверяем существование кнопки "отправить"
            {
                fr_bsend.style.zIndex = "10"; // задаем уровень слоя (поднимаем над другими)
                fr_bsend.style.bottom = "2px"; // корректируем расстояние от низа
                fr_bsend.style.right = "2px"; // от правого края
            }
            
            if (typeof(fr_banner) != 'undefined' && fr_banner != null) // проверяем существование объявления
            {
                fr_header.parentNode.insertBefore(fr_banner, fr_header); // поднимаем его над заголовком
            }
            
            fr_mlog.style.border = "thin solid #000000"; // добавляем тонкий черный бордюр окну лога сообщений

            fr_tabs.style.backgroundColor = "#3c3c3c"; // задаем фон панели закладок комнат
            for (var i = 0; i < fr_tabs.children.length-1; i++) // перебираем все закладки на этой панели кроме последней (надпись "приватные разговоры")
            {
                fr_tabs.children[i].style.border = "thin solid #000000"; // добавляем бордюр
                fr_tabs.children[i].style.backgroundColor = "#454545"; // изменяем фон
                fr_tabs.children[i].style.margin = "0px"; // убираем отступ между панелями
                fr_tabs.children[i].style.padding = "10px"; // задаем отступ границ панелей от текста
            }
совместив все вместе, сохранив скрипт и обновив страницу с окном чата, получаем желаемое:
Code:
// ==UserScript==
// @name        genom_chat
// @description genom chat
// @author vx
// @namespace   NetherWorld
// @version 1.0
// @include     https://pgenom.com/community/
// @grant    none
// ==/UserScript==

// Оборачиваем скрипт в замыкание, для кроссбраузерности (opera, ie)
(function (window, undefined) {  // нормализуем window
    var w;
    if (typeof unsafeWindow != undefined) {
        w = unsafeWindow
    } else {
        w = window;
    }
    // не запускаем скрипт во фреймах
    // без этого условия скрипт будет запускаться несколько раз на странице с фреймами
    if (w.self != w.top) {
        return;
    }
    // дополнительная проверка наряду с @include
    if (/https:\/\/pgenom.com/.test(w.location.href)) {
        window.onload = function () // ждем окончания прогрузки страницы, чтобы гарантированно вылавливать все элементы
        { //Ниже идёт непосредственно код скрипта

            var fr_chat = document.getElementById("siropuChat"); // собственно все окно чата. корень его дерева
            var fr_editor = document.getElementById("siropuChatEditor"); // контейнер с панелью форматирования и строкой ввода
            var fr_header = document.getElementById("siropuChatHeader"); // заголовок чата
            var fr_tabs = document.getElementById("siropuChatTabs"); // панель закладок комнат
            var fr_mlog = document.getElementById("siropuChatContent"); // окно лога сообщений
            var fr_edit = fr_editor.getElementsByClassName("fr-wrapper show-placeholder")[0]; // строка ввода
            var fr_tools = fr_editor.getElementsByClassName("fr-toolbar fr-ltr fr-desktop fr-top fr-basic")[0]; // панель форматирования
            var fr_banner = fr_editor.getElementsByClassName("siropuChatAds")[0]; // строка объявления. присутствует если собственно есть само объявление
            var fr_pholder = fr_edit.getElementsByClassName("fr-placeholder")[0]; // поле ввода в строке ввода
            var fr_bsend = fr_editor.getElementsByClassName("button--link button")[0]; // кнопка отправки сообщения (на данный момент отключена и ее нет)

            fr_editor.parentNode.insertBefore(fr_editor, fr_tabs); // переставляем ветку контейнера с панелью форматирования и строкой ввода
                                                                   //  над панелью закладок комнат
            fr_edit.parentNode.insertBefore(fr_edit, fr_tools); // поднимаем строку ввода над панелью форматирования

            fr_edit.style.backgroundColor = "#f0f0f0"; // задаем цвет фона строки ввода
            fr_pholder.style.color = "#000000"; // цвет шрифта надписи с предложением ввода сообщения
            fr_pholder.parentNode.children[0].style.color = "#000000"; // цвет шрифта набираемого текста

            if (typeof(fr_bsend) != 'undefined' && fr_bsend != null) // проверяем существование кнопки "отправить"
            {
                fr_bsend.style.zIndex = "10"; // задаем уровень слоя (поднимаем над другими)
                fr_bsend.style.bottom = "2px"; // корректируем расстояние от низа
                fr_bsend.style.right = "2px"; // от правого края
            }
            
            if (typeof(fr_banner) != 'undefined' && fr_banner != null) // проверяем существование объявления
            {
                fr_header.parentNode.insertBefore(fr_banner, fr_header); // поднимаем его над заголовком
            }
            
            fr_mlog.style.border = "thin solid #000000"; // добавляем тонкий черный бордюр окну лога сообщений

            fr_tabs.style.backgroundColor = "#3c3c3c"; // задаем фон панели закладок комнат
            for (var i = 0; i < fr_tabs.children.length-1; i++) // перебираем все закладки на этой панели кроме последней (надпись "приватные разговоры")
            {
                fr_tabs.children[i].style.border = "thin solid #000000"; // добавляем бордюр
                fr_tabs.children[i].style.backgroundColor = "#454545"; // изменяем фон
                fr_tabs.children[i].style.margin = "0px"; // убираем отступ между панелями
                fr_tabs.children[i].style.padding = "10px"; // задаем отступ границ панелей от текста
            }


        }
    }
})(window);


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

GDragon

Дракон
Gold Founder
Joined
Aug 26, 2015
Messages
105
Likes
23
#4
Есть небольшая (даже очень маленькая) вероятность, что текущего чата вообще не будет.
Вплотную занимаюсь дискордом...
хотеть.
 

vxsw

Captain Of The Ark
Gold Founder
Joined
Feb 10, 2016
Messages
2,636
Likes
1,036
#5
продолжаем развлекаться, углубляясь в тему.

вводная:
- нужна возможность вставки заранее определенных фрагментов текста. будь то ссылка на картинку обрамленная тегами, команда чата, текстовый смайл, или просто какая-то дежурная фраза.
- деление на категории
- максимальное упрощение добавления/редактирования как категорий так и их элементов

реализация:
самый простой и интуитивно понятный вариант, это аналог смайликов. кнопка в панели открывает блок со списком категорий смайликов, клик по каждой категории, отображает лист элементов-смайликов, клик по которым, добавляет в строку ввода либо сам смайлик, либо его текстовый эквивалент.
в общем, в моем представлении, выглядеть это должно как-то так:


начнем по порядку.
поскольку одна из целей "максимальное упрощение добавления/редактирования как категорий так и их элементов", то "жесткое" последовательное поэлементное добавление прямым текстом в коде нам не интересно. значит нужно сделать какую-то функцию, для автоматического создания категорий и элементов в них, вне зависимости от того, сколько их будет. чтобы такой алгоритм работал, данные должны подаваться строго структурированно. чтобы понять как это может выглядеть, просто представим данные, выделив их в группы. получим примерно такую картину:
1525427277029.png
по факту, имеем массив массивов. страшного в слове "массив" ни чего нет. это всего-лишь последовательность каких-либо элементов. в данном случае есть массив "данные" с элементами "категория". т.е. коричневый прямоугольник, это набор синих прямоугольников. а каждый синий прямоугольник, это набор элементов в зеленом. каждый конечный элемент, может иметь ряд своийст, таких как всплывающая подсказка, собственно то что должно быть вставлено и то, что должно отображаться в панели категории. на словах может и сложно, но в коде эта каша выглядит проще, для наглядности, обвел теми-же прямоугольниками, что и на предыдущем рисунке:
1525428142574.png
в данном случае, имеем две категории, по два элемента в каждой. все поля пустые ("").
такой массив просто обработать программно и вместе с тем, просто добавить как новую категорию, так и отдельный элемент.
для добавления категории надо просто скопировать блок выделенный синим, для добавления елемента, достаточно просто скопировать строку из зеленого блока.
обращаю внимание, что после закрывающей квадратной скобки каждого блока категории, запятая должна быть, но после последнего блока, ее быть не должно.

с данными разобрались. теперь, чтобы понять как должна работать функция по их обработке, надо понять, как все это должно располагаться графически на экране. очевидно, что нам нужен как минимум один контейнер, в котором все это хозяйство будет размещаться. чтобы категории не перемешивались с элементами, вложим еще один контейнер для названий категорий, а для каждой группы элементов категорий, будем создавать отдельный контейнер, но отображать будем только тот, к-й соответствует выбранной категории. схема примерно такая:
1525431222728.png
где,
- коричневый блок - наш контейнер, внедряемый в общий интерфейс.
- синий блок - контейнер для названий категорий
- зеленый блок - активный/видимый контейнер с элементами выбранной категории
- серые блоки - пассивные/скрытые контейнеры с элементами других категорий

построчно весь код расписывать в этот раз не буду, отмечу только, что все элементы являются кнопками и стилистически схожи, что как-бы шепчет о том, что сам процесс создания кнопки вынести в отдельную функцию будет весьма дачной идеей.
есть замечание по графике (картинки/смайлы). форум не показывает изображения напрямую с других сайтов, только проброшенные через себя. => для использования картинок со сторонних ресурсов, надо получить проксированную ссылку и использовать ее, вместо прямой. сделать это просто. вставляете нужную картинку в редакторе форума, нажимаете "предпросмотр" и в окне предпросмотра, запрашиваете информацию о нужной картинке (можно через "исследовать элемент"). ссылка на картинку, будет начинаться с "/community/proxy.php?image=". если есть начало "httрs://pgenom.com", то его просто затираете.

по коду кратко.
в основной функции скрипта.
добавляем три новых элемента:
Code:
            var sm_bttn = CreateButton("ツ","custom smiles"); //кнопка вызова панели категорий и элементов
                sm_bttn.id = "vx.chat.smile_button";
                sm_bttn.addEventListener("click", function(event){sm_bttn_onclick(event.target)},false)
                sm_bttn.style.width = fr_tools.children[0].offsetWidth + "px";
                sm_bttn.style.height = fr_tools.children[0].offsetHeight + "px";
                fr_tools.appendChild(fr_separator.cloneNode(true));
                fr_tools.appendChild(sm_bttn);
            var fr_cSmilePannel = document.createElement("div"); //наш блок в котором все будем размещать
                fr_cSmilePannel.id = "vx.chat.custom_smiles_pannel";
                fr_cSmilePannel.style.width = fr_tools.offsetWidth + "px";
                fr_cSmilePannel.style.border = "thin solid #000000";
                fr_edit.parentNode.insertBefore(fr_cSmilePannel, fr_edit);
                fr_cSmilePannel.style.display = "none";
            var fr_smileCatPannel = document.createElement("div"); //панель со списком категорий
                fr_smileCatPannel.id = "vx.chat.custom_smiles_cat_pannel";
                fr_smileCatPannel.style.width = fr_tools.offsetWidth + "px";
                fr_cSmilePannel.appendChild(fr_smileCatPannel);
теперь массив с элементами:
Code:
            var ar_smiles = [
//                                // шаблон
//                                [""   // отображаемое на вкладке название категории
//                                 , ["txt", "", "", ""] // текстовый [тип, подсказка, вставляемый текст, отображаемый текст]
//                                 , ["img", "", "", ""] // картинка  [тип, подсказка, ссылка на картинку, ссылка на иконку]
//                                ],    // конец категории
//                                // конец шаблона
                                ["текстовые"
                                 , ["txt", "" , "ツ✌", "ツ✌"]
                                 , ["txt", "" , "☠", "☠"]
                                 , ["txt", "" , "☢", "☢"]
                                 , ["txt", "" , "☣", "☣"]
                                 , ["txt", "" , "☕", "☕"]
                                 , ["txt", "" , "☭", "☭"]
                                 , ["txt", "" , "☮", "☮"]
                                 , ["txt", "" , "☯", "☯"]
//                                 , ["txt", "" , "", ""]
                                ],
                                ["команды чата"
                                 , ["txt", "", "/me", "/me"]
                                 , ["txt", "пм", "/whisper @", "/whisper"]
                                 , ["txt", "бросить кубик", "/roll", "/roll"]
                                 , ["txt", "задать статус", "/status", "/status"]
                                 , ["txt", "гифка", "/giphy", "/giphy"]
                                ],
                                ["loadout"
                                 , ["img","фак","https://drive.google.com/thumbnail?id=0BxNbonm8WQm6dW1sXzdQQ1c5TFU&sz=h32","/community/proxy.php?image=https%3A%2F%2Fdrive.google.com%2Fthumbnail%3Fid%3D0BxNbonm8WQm6dW1sXzdQQ1c5TFU%26sz%3Dh32&hash=03bc01e9514b4f2e82a208b9c116b90b"]
                                 , ["img","гольф","https://drive.google.com/thumbnail?id=0BxNbonm8WQm6YkQyZmZpSXY1Z3M&sz=h32","/community/proxy.php?image=https%3A%2F%2Fdrive.google.com%2Fthumbnail%3Fid%3D0BxNbonm8WQm6YkQyZmZpSXY1Z3M%26sz%3Dh32&hash=c22f6a001e3b2695e609a4eb1b856d9d"]
                                 , ["img","яйца","https://drive.google.com/thumbnail?id=0BxNbonm8WQm6VW8yTnJpZHpxOWM&sz=h32","/community/proxy.php?image=https%3A%2F%2Fdrive.google.com%2Fthumbnail%3Fid%3D0BxNbonm8WQm6VW8yTnJpZHpxOWM%26sz%3Dh32&hash=40c2ad7260a3b83a3f134bdf5206e3ed"]
                                 , ["img","джексон","https://drive.google.com/thumbnail?id=0BxNbonm8WQm6ck1PTW1ScU1XWTQ&sz=h32","/community/proxy.php?image=https%3A%2F%2Fdrive.google.com%2Fthumbnail%3Fid%3D0BxNbonm8WQm6ck1PTW1ScU1XWTQ%26sz%3Dh32&hash=4e093dbc6cd585574b0699a6270d379d"]
                                 , ["img","диско","https://drive.google.com/thumbnail?id=0BxNbonm8WQm6VC01aG5xYzFLVFk&sz=h32","/community/proxy.php?image=https%3A%2F%2Fdrive.google.com%2Fthumbnail%3Fid%3D0BxNbonm8WQm6VC01aG5xYzFLVFk%26sz%3Dh32&hash=2c294cb68a6c20dafa41cea4d15ab224"]
                                 , ["img","суммо","https://drive.google.com/thumbnail?id=0BxNbonm8WQm6OHNGOGhRMkZscDQ&sz=h32","/community/proxy.php?image=https%3A%2F%2Fdrive.google.com%2Fthumbnail%3Fid%3D0BxNbonm8WQm6OHNGOGhRMkZscDQ%26sz%3Dh32&hash=e5a2511c8295b075207c3de5ffb366cb"]
                                 , ["img","походка","https://drive.google.com/thumbnail?id=0BxNbonm8WQm6TV84VklZZjdDNVE&sz=h32","/community/proxy.php?image=https%3A%2F%2Fdrive.google.com%2Fthumbnail%3Fid%3D0BxNbonm8WQm6TV84VklZZjdDNVE%26sz%3Dh32&hash=bd0e5bbaa73682081f285bc671884b7d"]
                                 , ["img","кот","https://drive.google.com/thumbnail?id=0BxNbonm8WQm6VkNnUXRLV1lzN3c&sz=h32","/community/proxy.php?image=https%3A%2F%2Fdrive.google.com%2Fthumbnail%3Fid%3D0BxNbonm8WQm6VkNnUXRLV1lzN3c%26sz%3Dh32&hash=9086e33e925a6ab8a6571f4c13083d54"]
                                 , ["img","ствол","https://drive.google.com/thumbnail?id=0BxNbonm8WQm6MXotZFlRWV9NeVU&sz=h32","/community/proxy.php?image=https%3A%2F%2Fdrive.google.com%2Fthumbnail%3Fid%3D0BxNbonm8WQm6MXotZFlRWV9NeVU%26sz%3Dh32&hash=a929e37b15366b1d46bb06f1d518d37e"]
                                 , ["img","капуста","https://drive.google.com/thumbnail?id=0BxNbonm8WQm6eHdOMWtvVkRjN0E&sz=h32","/community/proxy.php?image=https%3A%2F%2Fdrive.google.com%2Fthumbnail%3Fid%3D0BxNbonm8WQm6eHdOMWtvVkRjN0E%26sz%3Dh32&hash=86e3e8aed3dee59bef1136edff55a688"]
                                 , ["img","каннам 1","https://drive.google.com/thumbnail?id=0BxNbonm8WQm6YzFIQU5sWjk5RmM&sz=h32","/community/proxy.php?image=https%3A%2F%2Fdrive.google.com%2Fthumbnail%3Fid%3D0BxNbonm8WQm6YzFIQU5sWjk5RmM%26sz%3Dh32&hash=2e872d5c843a4e5615978d68c24a671f"]
                                 , ["img","каннам 2","https://drive.google.com/thumbnail?id=0BxNbonm8WQm6ek5Cc1IwTlMyX1E&sz=h32","/community/proxy.php?image=https%3A%2F%2Fdrive.google.com%2Fthumbnail%3Fid%3D0BxNbonm8WQm6ek5Cc1IwTlMyX1E%26sz%3Dh32&hash=9c578f2416d362cc4e26ea212a217527"]
                                 , ["img","каннам 3","https://drive.google.com/thumbnail?id=0BxNbonm8WQm6djVRTXZtZW80THM&sz=h32","/community/proxy.php?image=https%3A%2F%2Fdrive.google.com%2Fthumbnail%3Fid%3D0BxNbonm8WQm6djVRTXZtZW80THM%26sz%3Dh32&hash=9d73f5e29de30d0768d2e814c6a47a00"]
                                 , ["img","гитара","https://drive.google.com/thumbnail?id=0BxNbonm8WQm6aFMwTXZoTklkRFk&sz=h32","/community/proxy.php?image=https%3A%2F%2Fdrive.google.com%2Fthumbnail%3Fid%3D0BxNbonm8WQm6aFMwTXZoTklkRFk%26sz%3Dh32&hash=afb3d126eab7313a3312a935ad445143"]
                                 , ["img","деревня","https://drive.google.com/thumbnail?id=0BxNbonm8WQm6Y2piUkZXTjFXdzQ&sz=h32","/community/proxy.php?image=https%3A%2F%2Fdrive.google.com%2Fthumbnail%3Fid%3D0BxNbonm8WQm6Y2piUkZXTjFXdzQ%26sz%3Dh32&hash=7dab52ca877a001ca9dd4df10fda62fa"]
                                 , ["img","твист 1","https://drive.google.com/thumbnail?id=0BxNbonm8WQm6U2VZbzUtQkZ3MzQ&sz=h32","/community/proxy.php?image=https%3A%2F%2Fdrive.google.com%2Fthumbnail%3Fid%3D0BxNbonm8WQm6U2VZbzUtQkZ3MzQ%26sz%3Dh32&hash=1ce2e2bd4248ebec5fc0b0757b46590c"]
                                 , ["img","твист 2","https://drive.google.com/thumbnail?id=0BxNbonm8WQm6SWoxV0hockdrWkE&sz=h32","/community/proxy.php?image=https%3A%2F%2Fdrive.google.com%2Fthumbnail%3Fid%3D0BxNbonm8WQm6SWoxV0hockdrWkE%26sz%3Dh32&hash=d81816e5912605e06941abea1f2a4e3f"]
                                 , ["img","апплодисменты","https://drive.google.com/thumbnail?id=0BxNbonm8WQm6R3M1SHRsa1BjYXc&sz=h32","/community/proxy.php?image=https%3A%2F%2Fdrive.google.com%2Fthumbnail%3Fid%3D0BxNbonm8WQm6R3M1SHRsa1BjYXc%26sz%3Dh32&hash=3542dc9e13f36563866652506513c2df"]
                                 , ["img","деревня 2","https://drive.google.com/thumbnail?id=0BxNbonm8WQm6V29tUVJUdHpiY2M&sz=h32","/community/proxy.php?image=https%3A%2F%2Fdrive.google.com%2Fthumbnail%3Fid%3D0BxNbonm8WQm6V29tUVJUdHpiY2M%26sz%3Dh32&hash=3759e5e67c6ba1aff33c7bfde20a4300"]
                                 , ["img","шест","https://drive.google.com/thumbnail?id=0BxNbonm8WQm6cjE3Uk5ra3B2ajQ&sz=h32","/community/proxy.php?image=https%3A%2F%2Fdrive.google.com%2Fthumbnail%3Fid%3D0BxNbonm8WQm6cjE3Uk5ra3B2ajQ%26sz%3Dh32&hash=766d4e23aa323c4c211efa0c1a2c7970"]
                                ]
                            ];
функция обработки массива, создающая и размещающая все необходимое (расположена в основной функции скрипта, сразу за массивом):
Code:
            for (var cat = 0; cat < ar_smiles.length; cat++)
                {
                    var tcat = CreateButton(ar_smiles[cat][0],"");
                        tcat.id = "vx.chat.smile_cat" + cat;
                        tcat.setAttribute("index", cat);
                        tcat.style.height = "32px";
                        tcat.style.fontWeight = "normal";
                        if (cat == 0)
                            {
                                tcat.style.borderBottom = "3px solid #76a3cf";
                                tcat.setAttribute("pressed", "true");
                                tcat.style.color = tcat.getAttribute("pressedColor");
                            }
                        tcat.addEventListener("click", function(event){sm_cat_onclick(event.target)},false)
                        fr_smileCatPannel.appendChild(tcat);
                    if (ar_smiles[cat].length >0)
                        {
                            var tspannel = document.createElement("div");
                                tspannel.id = "vx.chat.custom_smiles_smile_pannel" + cat;
                                tspannel.style.width = fr_tools.offsetWidth + "px";
                                if (cat == 0)
                                    {
                                        tspannel.style.display = "block";
                                    } else {
                                        tspannel.style.display = "none";
                                    }
                                fr_cSmilePannel.appendChild(tspannel);
                            for (var i = 1; i < ar_smiles[cat].length; i++)
                                {
                                    switch (ar_smiles[cat][i][0])
                                        {
                                            case "txt":
                                                var ti = CreateButton(ar_smiles[cat][i][3],ar_smiles[cat][i][1]);
                                                    ti.setAttribute("sType","txt");
                                                    ti.setAttribute("data",ar_smiles[cat][i][2]);
                                                    ti.style.fontWeight = "normal";
                                                    ti.addEventListener("click", function(event){sm_onclick(event.target)},false)
                                                    tspannel.appendChild(ti);
                                                break;
                                            case "img":
                                                var ti = CreateButton("",ar_smiles[cat][i][1]);
                                                    ti.setAttribute("sType","img");
                                                    ti.setAttribute("data",ar_smiles[cat][i][2]);
                                                    ti.style.height = "36px";
                                                    ti.style.padding = "2px";
                                                    ti.addEventListener("click", function(event){sm_onclick(event.target)},false);
                                                    var img = document.createElement("img");
                                                    img.innerHTML = "<img src='" + ar_smiles[cat][i][3] + "'/>";
                                                    ti.children[0].style.position = "absolute";
                                                    ti.children[0].style.top = "64px";
                                                    ti.appendChild(img);
                                                    tspannel.appendChild(ti);
                                                //break;
                                        }
                                }
                        }
                  
                }

теперь "отдельные" функции.
функция создания кнопки:
Code:
function CreateButton(txt,ttipText)
{
    var bttn = document.createElement("button");
    bttn.style.color = "#b7b7b7";
    bttn.setAttribute("type", "button");
    bttn.setAttribute("role", "button");
    bttn.setAttribute("pressed", "false");
    bttn.setAttribute("idleColor", "#b7b7b7");
    bttn.setAttribute("idleBackgroundColor", "transparent");
    bttn.setAttribute("idleHoverColor", "#e2e2e2");
    bttn.setAttribute("idleHoverBackgroundColor", "#464646");
    bttn.setAttribute("pressedColor", "#5d96b3");
    bttn.setAttribute("pressedBackgroundColor", "transparent");
    bttn.setAttribute("pressedHoverColor", "#5d96b3");
    bttn.setAttribute("pressedHoverBackgroundColor", "#464646");
    bttn.style.backgroundColor = "transparent";
    bttn.style.borderStyle = "none";
    bttn.appendChild(document.createTextNode(txt));
    bttn.style.fontWeight  = "bold";
    bttn.addEventListener("mouseover", function(event){ButtonHighlight(event.target,"true")},false);
    bttn.addEventListener("mouseout", function(event){ButtonHighlight(event.target,"false")},false);
    var ttip = document.createElement("span");
    ttip.innerHTML = ttipText;
    ttip.className = "tooltip";
    ttip.style.fontWeight  = "normal";
    ttip.style.backgroundColor = "#2f2f2f";
    ttip.style.color = "white";
    ttip.style.display = "none";
    bttn.appendChild(ttip);
    return bttn;
}
функция отвечающая за подсветку кнопок:
Code:
function ButtonHighlight(e, onHover)
{
    var ttip = e.getElementsByClassName("tooltip")
    if (onHover == "true")
        {
            if (ttip.length > 0){ttip[0].style.display = "block"};
            if (e.getAttribute("pressed") == "true")
                {
                  e.style.color = e.getAttribute("pressedHoverColor");
                  e.style.backgroundColor = e.getAttribute("pressedHoverBackgroundColor");
                } else {
                  e.style.color = e.getAttribute("idleHoverColor");
                  e.style.backgroundColor = e.getAttribute("idleHoverBackgroundColor");
                }
        } else {
            if (ttip.length > 0){ttip[0].style.display = "none"};
            if (e.getAttribute("pressed") == "true")
                {
                  e.style.color = e.getAttribute("pressedColor");
                  e.style.backgroundColor = e.getAttribute("pressedBackgroundColor");
                } else {
                  e.style.color = e.getAttribute("idleColor");
                  e.style.backgroundColor = e.getAttribute("idleBackgroundColor");
                }
        }
}
реакция на клик по кнопке отображения/скрытия панели:
Code:
function sm_bttn_onclick(e)
{
    if (e.getAttribute("pressed") == "true")
        {
            e.setAttribute("pressed", "false");
            ButtonHighlight(e,"true");
            document.getElementById("vx.chat.custom_smiles_pannel").style.display = "none";
        } else {
            e.setAttribute("pressed", "true");
            ButtonHighlight(e,"true");
            document.getElementById("vx.chat.custom_smiles_pannel").style.display = "block";
        }
}
клик по названиям категорий:
Code:
function sm_cat_onclick(e)
{
    var cIndex = e.getAttribute("index");
    for (var cat = 0; cat < e.parentNode.children.length; cat++)
        {
            if (cat == cIndex)
                {
                    e.parentNode.children[cat].setAttribute("pressed", "true");
                    e.parentNode.children[cat].style.borderBottom = "3px solid #76a3cf";
                    ButtonHighlight(e.parentNode.children[cat],"true");
                    document.getElementById("vx.chat.custom_smiles_smile_pannel" + cat).style.display = "block";
                } else {
                    e.parentNode.children[cat].setAttribute("pressed", "false");
                    e.parentNode.children[cat].style.borderBottom = "none";
                    ButtonHighlight(e.parentNode.children[cat],"false");
                    document.getElementById("vx.chat.custom_smiles_smile_pannel" + cat).style.display = "none";
                }
        }
}
клик по элементу категории (по смайлу):
Code:
function sm_onclick(e)
{
    var fr_editor = document.getElementById("siropuChatEditor"); // контейнер с панелью форматирования и строкой ввода
    var fr_edit = fr_editor.getElementsByClassName("fr-element fr-view")[0]; // строка ввода
    var fr_pholder = fr_editor.getElementsByClassName("fr-wrapper show-placeholder")[0]; // поле ввода в строке ввода
    var txt = fr_edit.children[0].innerHTML.replace("<br>","");
    if (txt.substr(txt.length -1) != " "){txt = txt + " "};
    switch (e.getAttribute("sType"))
        {
            case "txt":
                fr_edit.children[0].innerHTML = txt + e.getAttribute("data") + " ";
                break;
            case "img":
                fr_edit.children[0].innerHTML = txt + "[IMG]" + e.getAttribute("data") + "[/IMG] ";
                //break;
        }
    if (typeof(fr_pholder) != "undefined" && fr_pholder != null)
        {
            fr_pholder.classList.remove("show-placeholder");
        }
}

скрипт целиком не влез (длина поста ограничена), взять можно тут: genom chat

позже объясню, как такую кнопку организовать для редактора форума. в нем она более актуальна.
 

vxsw

Captain Of The Ark
Gold Founder
Joined
Feb 10, 2016
Messages
2,636
Likes
1,036
#6
добавил смайлики в редактор форума и начал заносить сами смайлики по группам:
1525772662627.png
функции используются те-же что и в чате, так-что расписывать не буду. текущие замечания:
- переделал структуру массива смайлов, дабы унифицировать блоки и не следить за наличием запятых после категорий и ее отсутствием после последней. теперь, шаблон категории можно копировать и вставлять в любое место не заморачиваясь.
- небольшие правки по стилю (в т.ч. и в скрипте чата), для большего соответствия "оригиналу".
- "женские" смайлики и все остальные, переразбиты ко категориям. логику не ищите, иногда ее просто нет. остальные категории добавлю позже, ибо смайлов на старом форуме было более 300.
- тултипы к смайлам пока не прописывал - не ходовая.
- возможны нестыковки в превьюшке смайла и тем, что на самом деле вылезет, ибо смайлов дофига, ссылки не явные и перепроверять каждый - застрелюсь. найдете косяк - отписывайтесь, поправлю.

о плохом:
- кнопка иногда пропадает. куда и при каких обстоятельствах - выяснить не удалось. перезагрузка страницы лечит.
- в упрощенном редакторе не работает (как и любые другие кнопки) и судя по всему, сделать работу в нем возможной не получится.
- добавляет пока только в конец текста, со вставкой по текущей позиции пока не заморачивался.
- смайлики вставляются в виде ссылки обрамленной тегами "имг". в посте будут отображаться нормально. для принудительной отрисовки в редакторе - переключите на упрощенный и обратно.

полный скрипт на форум тут genom forum

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

vxsw

Captain Of The Ark
Gold Founder
Joined
Feb 10, 2016
Messages
2,636
Likes
1,036
#7
к форумному:
- добавлена группа женских смайликов (83 шт.)
- ко всем панелям смайликов, добавлено ограничение по отображаемой высоте, с автопоказом скролла (переменная "cs_maxh"), по умолчанию выставил в 150 пикселей, что ~3 ряда.
 

vxsw

Captain Of The Ark
Gold Founder
Joined
Feb 10, 2016
Messages
2,636
Likes
1,036
#8
к форумному:
- закончил со всеми смайлами со старого форума (+109 шт)
- добавил кнопку "развернуть/свернуть". отображается только если высота панели смайлов превышает предельную. в связи с этим, предельная высота панели снижена до 100 пикселей (~2 ряда).
1528539594765.png
 
Top Bottom