Greasy Fork is available in English.

Search posts by user

Search for posts by user(-s) in current topic

От 12.08.2018. Виж последната версия.

// ==UserScript==
// @name           Search posts by user
// @name:ru        Поиск постов пользователя
// @description    Search for posts by user(-s) in current topic
// @description:ru Найти посты пользователя(-ей) в текущей теме
// @version        3.0.3
// @date           12.08.2018
// @author         Halibut
// @namespace      https://greasyfork.org/en/users/145947-halibut
// @homepageURL    https://greasyfork.org/en/scripts/36319-search-posts-by-user
// @supportURL     https://forum.ru-board.com/topic.cgi?forum=2&topic=5673&glp
// @license        HUG-WARE
// @include        http*://forum.ru-board.com/topic.cgi?forum=*&topic=*
// @noframes
// @run-at         document-start
// @grant          none
// ==/UserScript==

/******************************************************************************
 * "THE HUG-WARE LICENSE" (Revision 2): As long as you retain this notice you *
 * can do whatever you want with this stuff. If we meet some day, and you     *
 * think this stuff is worth it, you can give me/us a hug.                    *
******************************************************************************/

window.addEventListener("DOMContentLoaded", function f() {
    'use strict';
    this.removeEventListener("DOMContentLoaded", f);
    const body = document.body,
          style = document.head.appendChild(document.createElement("style")),
          dats = [...document.getElementsByClassName("tb")].map(el => el.getElementsByClassName("dats")[0]),
          listener = {
              options: {
                  // <--- ОПЦИИ СКРИПТА --->
                  showAlerts: false // Показывать алерты по окончании поиска
                  , scrollToSearchResults: true  // Автоматически прокручивать страницу к результатам поиска
                                                 // (если showAlerts == true, то, при подтверждении, все-равно прокрутит к результатам)
                  , hideSearchButton: false // Показывпть кнопку поиска только при наведении курсора на пост
                  , autoOpenSpoilerWithResult: false // Раскрывать спойлер с результатами поиска автоматически
                  , reversPostSorting: true // обратить сортировку по дате для найденных постов (от новых к старым)
                  , headToResult: false // Включать шапку темы (если она создана выбранным пользоватем) в результаты поиска
                  , createCnMenu: true // Добавить в контекстное меню пункт запускающий поиск (только для FF)
                  , searchOnProfileLinksByRMB: true // Поиск при клике по ссылкам ведущим на профиль пользователя
                                                    // (только если нет выделенного текста и не нажаты клавиши модификаторы)
              }
              , names: []
              , txt: ''
              , sort: null
              , ttlShow: '\u25BA Показать результаты поиска'
              , ttlHide: '\u25BC Скрыть результаты поиска'
              , ttlLdng: '\u23F3 Поиск...'
              , ttlNotFnd: '\u26A0 Постов не найдено!'
              , get name() {
                  return this.name = this.getName()
              }
              , set name(str) {
                  return this.setName(str)
              }
              , get text() {
                  const text = this.txt;
                  delete this.txt;
                  return text;
              }
              , set text(str) {
                  if (str)
                      return this.txt = str;
              }
              , get win() {
                  delete this.win;
                  return this.win = window.top
              }
              , get loc() {
                  delete this.loc;
                  return this.loc = this.win.location.href
              }
              , get actnBox() {
                  delete this.actnBox;
                  return this.actnBox = [...document.getElementsByTagName("table")].find(el => el.querySelector("a[href^='post.cgi?action=new']")
                                                                                         || !el.getElementsByTagName('td')[0].children.length)
              }
              , get isFF() {
                  delete this.isFF;
                  return this.isFF = this.win.navigator && this.win.navigator.userAgent && this.win.navigator.userAgent.toLowerCase().includes("firefox")
              }
              , get spHd() {
                  delete this.spHd;
                  return this.spHd = this.getSpHd()
              }
              , get spBd() {
                  delete this.spBd;
                  return this.spBd = this.getSpBd()
              }
              , get spTTl() {
                  delete this.spTTl;
                  return this.spTTl = this.spHd && this.spHd.getElementsByTagName('td')[0]
              }
              , getSel() {
                  return this.win.getSelection && this.win.getSelection().toString() || ""
              }
              , spawn(gen) {
                  const continuer = (verb, arg) => {
                            let result;
                            try {
                                result = generator[verb](arg);
                            } catch (err) {
                                return Promise.reject(err);
                            }
                            if (result.done) {
                                return result.value;
                            } else {
                                return Promise.resolve(result.value).then(onFulfilled, onRejected);
                            };
                        },
                        generator = gen(),
                        onFulfilled = continuer.bind(continuer, 'next'),
                        onRejected = continuer.bind(continuer, 'throw');
                  return onFulfilled();
              }
              , prompt() {
                  return new Promise((res, rej) => {
                      delete this.sort;
                      body.appendChild(document.createElement('div')).outerHTML = html;
                      const mdlOverlay = document.getElementById('spu-modal'),
                            clsBtn = document.getElementById('spu-close'),
                            regChkbx = document.getElementById('spu-regexp'),
                            csChkbx = document.getElementById('spu-case'),
                            flgsFrm = document.getElementById('spu-flags'),
                            txtFrm = document.getElementById('spu-txtarea'),
                            nmsFrm = document.getElementById('spu-names'),
                            srchBtn = document.getElementById('spu-srch-strt'),
                            fromOld= document.getElementById('spu-fromold'),
                            fromNew= document.getElementById('spu-fromnew');
                      txtFrm.focus();
                      if (this.options.reversPostSorting)
                          fromNew.checked = true;
                      else
                          fromOld.checked = true;
                      clsBtn.onclick = () => {mdlOverlay.remove(); body.onwheel = null; res(null)};
                      mdlOverlay.onclick = mdlOverlay.oncontextmenu = mdlOverlay.onwheel = e => {
                          if (e.target != mdlOverlay) return;
                          e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
                      };
                      body.onwheel = e => {
                          e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
                      };
                      fromOld.onclick = fromNew.onclick = e => {
                          this.sort = fromNew.checked ? true : false;
                      };
                      regChkbx.onclick = e => {
                          flgsFrm.disabled = regChkbx.checked ? false : true;
                          !regChkbx.checked && (flgsFrm.value = '');
                      }
                      srchBtn.onclick = () => {
                          const val = {n: nmsFrm.value, t: txtFrm.value, rgxp: regChkbx.checked, flags: flgsFrm.value, cs: csChkbx.checked};
                          body.onwheel = null; mdlOverlay.remove();
                          res(val);
                      }
                  })
              }
              , getPosts([url, page], name, txt) {
                  return new Promise((res, rej) => {
                      let posts;
                      if (url == this.loc) {
                          posts = document.getElementsByClassName('tb');
                          if (posts && !!posts.length) {
                              posts = this.filterPosts(posts, name, txt, page);
                              return res(posts.map(el => el.cloneNode(true)));
                          }
                      };
                      const xhr = new XMLHttpRequest();
                      xhr.open("GET", url, true);
                      xhr.onload = re => {
                          if (!(xhr.readyState == 4 && xhr.status == 200)) return rej("error");
                          posts = xhr.responseXML.getElementsByClassName('tb');
                          if (posts && !!posts.length) {
                              posts = this.filterPosts(posts, name, txt, page);
                              return res(posts);
                          }
                      };
                      xhr.onerror = xhr.onabort = xhr.ontimeout = () => rej("error");
                      xhr.responseType = "document";
                      xhr.send();
                  })
              }
              , procesPosts(posts) {
                  return new Promise((res, rej) => {
                      posts = this.flatPosts(posts).filter(post => !!post);
                      if (posts && !!posts.length) {
                          if (typeof this.sort == "boolean")
                              this.sort && (posts = posts.reverse());
                          else if (this.options.reversPostSorting)
                              posts = posts.reverse();
                          posts = this.stylePosts(posts);
                          for (const post of posts)
                              this.spBd.appendChild(post);
                          res(posts.length)
                      }
                      else
                          rej("not found");
                  })
              }
              , filterPosts(posts, name, txt, page) {
                  return [...posts].filter(post => {
                      const isUser = name && name.includes(post.querySelector("td.dats b").textContent.toLowerCase().replace(/\s/g, '_')),
                            chkHead = this.options.headToResult && page == 1 || !post.querySelector('a.tpc[href$="&postno=1"]'),
                            isText = () => {
                                let pstEl = post.getElementsByClassName('post')[0],
                                    pstTxt = pstEl.textContent,
                                    txtToSearch = txt.val;
                                if (!(pstTxt && !!pstTxt.length)) return false;
                                if (txt.type != 'rgxp') {
                                    pstTxt = pstTxt.replace(/\s+/g, ' ');
                                    txtToSearch = txtToSearch.map(sntnc => sntnc.replace(/\s+/g, ' '));
                                    if (txt.cx) {
                                        pstTxt = pstTxt.toLowerCase();
                                        txtToSearch = txtToSearch.map(sntnc => sntnc.toLowerCase())
                                    }
                                };
                                switch (txt.type) {
                                    case 'all': {
                                        return txtToSearch.every(sntnc => pstTxt.indexOf(sntnc) != -1)
                                    }; break;
                                    case 'any': {
                                        return txtToSearch.some(sntnc => pstTxt.indexOf(sntnc) != -1)
                                    }; break;
                                    case 'rgxp': {
                                        return txtToSearch.test(pstTxt)
                                    }; break;
                                    case 'snt': {
                                        return pstTxt.indexOf(txtToSeach) != -1
                                    }; break;
                                    case 'some': {
                                        return txtToSearch.some(sntnc => pstTxt.indexOf(sntnc) != -1)
                                    }; break;
                                }
                            };
                      if (name) {
                          if (chkHead && isUser) {
                              return txt ? isText() : true
                          }
                      }
                      else {
                          return chkHead && isText()
                      }
                  });
              }
              , stylePosts(posts) {
                  const color1 = "#EEEEEE",
                        color2 = "#FFFFFF"
                  return posts.map((post,indx) => {
                      const tdEls =  post.getElementsByTagName("td");
                      for (const td of tdEls) {
                          td.bgColor && (td.bgColor = ((indx + 1) % 2) ? color1 : color2);
                      }
                      return post
                  })
              }
              , flatPosts(posts) {
                  return posts.reduce((a, b) => a.concat(b), [])
              }
              , getName() {
                  const name = this.getSel() || [...new Set(this.names)].join(",");
                  if (name)
                      return name.trim().toLowerCase().replace(/\s/g, "_").split(",")
              }
              , setName(str) {
                  if (str)
                      return this.names.push(str);
              }
              , getSearchQuery(str, cs) {
                  const query = {type: null, val: null, cs: cs};
                  if (Array.isArray(str)){
                      query.type = 'rgxp'; try {query.val = new RegExp(str[0], str[1])} catch(err) {return this.win.alert(err)}
                      return query
                  }
                  if (str.includes('OR')){
                      query.type = 'some'; query.val = str.split(/(?:\s+)?OR(?:\s+)?/);
                      return query
                  }
                  if (str.includes('AND')){
                      query.type = 'all'; query.val = str.split(/(?:\s+)?AND(?:\s+)?/);
                      return query
                  }
                  if (str.startsWith('"') && str.endsWith('"')) {
                      query.type = 'snt'; query.val = [str.slice(str.indexOf('"') + 1, str.lastIndexOf('"'))];
                      return query
                  }
                  else {
                      query.type = 'any'; query.val = str.trim().split(/\s+/)
                      return query
                  }
                  return null
              }
              , getPages() {
                  const paginator = [...document.getElementsByTagName("p")].find(el => el.textContent && el.textContent.startsWith("Страницы: "));
                  if (paginator)
                      return [...paginator.getElementsByTagName("td")[0].children].filter(el => !el.title).map((el, indx) => [el.href || this.loc, indx + 1]);
                  else
                      return [[this.loc, 1]];
              }
              , getSpHd() {
                  let spoilerHead = document.getElementById("spu-spoiler-head");
                  if (!spoilerHead) {
                      const dummyNode = this.actnBox.parentNode.insertBefore(document.createElement('div'), this.actnBox.nextElementSibling);
                      dummyNode.outerHTML = '<table id="spu-spoiler-head" width="95%" cellspacing="1" cellpadding="3" bgcolor="#999999" align="center" border="0"><tbody><tr><td valign="middle" bgcolor="#dddddd" align="left"></td></tr><td class="small" bgcolor="#dddddd" align="center">Найдено: <span id="spu-fndd"></span>   Ошибок: <span id="spu-errs"></span></td></tbody></table>';
                      spoilerHead = this.actnBox.nextElementSibling;
                      spoilerHead.id = "spu-spoiler-head";
                      spoilerHead.hidden = true;
                      const spTitle = spoilerHead.getElementsByTagName('td')[0];
                      spoilerHead.onclick = e => {
                          e.preventDefault(); e.stopPropagation();
                          const spoilerBody = this.spBd;
                          if (e.button != 0 || !spoilerBody || spoilerHead.hasAttribute("notready")) return;
                          spTitle.textContent = spoilerBody.hidden ? this.ttlHide : this.ttlShow;
                          spoilerBody.hidden = !spoilerBody.hidden;
                      }
                  }
                  return spoilerHead;
              }
              , getSpBd() {
                  let spoilerBody = document.getElementById("spu-spoiler-body");
                  if (!spoilerBody) {
                      const spoilerHead = this.spHd;
                      spoilerBody = spoilerHead.parentNode.insertBefore(document.createElement("table"), spoilerHead.nextElementSibling);
                      spoilerBody.id = "spu-spoiler-body";
                      spoilerBody.align = "center";
                      spoilerBody.width = "95%";
                      spoilerBody.hidden = true
                  }
                  return spoilerBody;
              }
              , endNotify(fndd,errs,ntfnd) {
                  if (fndd) {
                      const spoilerHead = this.spHd,
                            spoilerBody = this.spBd,
                            confirm = this.win.confirm("Поиск окончен!\nНайдено: " + fndd + (errs ? "\nОшибок: " + errs : "") + "\nПерейти к резульататам?");
                      if (confirm) {
                          spoilerHead.scrollIntoView({behavior:"smooth"});
                          if (this.options.autoOpenSpoilerWithResult) {
                              this.spTTl.textContent = this.ttlHide;
                              spoilerBody.hidden = false;
                          }
                      }
                  }
                  else
                      this.win.alert("Постов не найдено!" + (errs ? "\nОшибок: " + errs : ""));
              }
              , handleEvent(e, name, prmpt) {
                  e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
                  let cs;
                  // spawn function by Jake Archibald https://gist.github.com/jakearchibald/31b89cba627924972ad6
                  this.spawn(function*() {
                      if (name && !prmpt)  {
                          listener.name = name;
                          if (e.ctrlKey) return;
                      }
                      if (prmpt) {
                          const rVal = yield listener.prompt();
                          if (!rVal) return;
                          cs = rVal.cs;
                          listener.name = rVal.n;
                          listener.text = rVal.rgxp ? [rVal.t, rVal.flags] : rVal.t;
                      };
                  const nmsToSearch = listener.name,
                        txtToSearch = listener.text,
                        isNames = nmsToSearch && !!nmsToSearch.length,
                        isTexxt = txtToSearch && !!txtToSearch.length,
                        txtQuery = isTexxt && listener.getSearchQuery(txtToSearch, cs);
                  if (!isNames && !isTexxt || isTexxt && !txtQuery) return;
                  const pageLinks = listener.getPages(),
                        spoilerHead = listener.spHd,
                        spoilerBody = listener.spBd,
                        spoilerTitle = listener.spTTl,
                        errsLbl = document.getElementById("spu-errs"),
                        findedLbl = document.getElementById("spu-fndd");
                  spoilerHead.hidden = false; spoilerTitle.textContent = listener.ttlLdng;
                  spoilerHead.setAttribute("notready", "true"); spoilerBody.hidden = true;
                  listener.options.scrollToSearchResults && !listener.options.showAlerts && spoilerHead.scrollIntoView({behavior:"smooth"});
                  let errorsCntr = 0;
                  errsLbl.textContent = findedLbl.textContent = 0;
                  if (spoilerBody.children && !!spoilerBody.children.length)
                      for (const node of [...spoilerBody.children])
                          node.remove();
                  Promise.all(pageLinks.map(link => {
                      return listener.getPosts(link, isNames && nmsToSearch, isTexxt && txtQuery).catch(err => {
                          errorsCntr += 1; errsLbl.textContent = errorsCntr
                      })
                  })).then(
                      posts => {
                          return listener.procesPosts(posts)
                      }
                  ).then(
                      founded => {
                          findedLbl.textContent = founded;
                          spoilerTitle.textContent = listener.ttlShow;
                          spoilerHead.removeAttribute("notready");
                          if (listener.options.showAlerts)
                              listener.endNotify(founded, errorsCntr);
                          else if (listener.options.autoOpenSpoilerWithResult) {
                              spoilerBody.hidden = false
                              spoilerTitle.textContent = listener.ttlHide;
                          };
                          return
                      }
                  ).catch(err => {
                      if (err == "not found") {
                          spoilerTitle.textContent = listener.ttlNotFnd;
                          listener.options.showAlerts && listener.endNotify(null, errorsCntr, notfound);
                          return
                      }
                      errorsCntr += 1; errsLbl.textContent = errorsCntr
                  });
                  listener.names.length = 0;
                      });
              }
          },
          hlpNms = 'Чтобы искать посты для всех пользователей, по тексту, - оставьте поле имени пустым.\nИмена в форму поиска вводить без учета регистра, разделяя запятой (без пробела).\nИмена с пробелами - ищутся и с пробелом и с _.',
          hlpTxt =
          [
              'Чтобы искать только посты по именам пользователей - оставьте поле текста пустым.',
              '',
              'Регулярные выражения вводить включив чекбокс.',
              'Ьез начального и закрывающего слеша.',
              'Для ввода флагов - отдельное поле,\n',
              'Регулярки не проверяются на валидность.',
              '',
              'Поиск без регулярок:',
              '\tраз два три - искать любое из слов',
              '\tраз два OR три OR пять - искать любую из фраз: &quot;раз два&quot;, &quot;три&quot;, &quot;пять&quot;',
              '\tраз два AND три AND пять - искать все фразы',
              '\t&quot;раз два три&quot; - (в двойных кавычках) искать фразу целиком',
              'Комбинировать операторы пока нельзя.',
              '',
              'Поиск с учетом регистра по умолчанию. Для поиска без учета - включить чекбокс.',
              'При поиске без регулярок все пробелы, переносы, табуляции - заменяются на единичный пробел,',
              'как в задании для поиска, так и в тексте постов.',
              'Слово &quot;поле&quot; - найдется и в &quot;Наполеон&quot;.',
          ].join('\n'),
          questionMark = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAn1BMVEUAAAAAbfMAbfEAbe8AbfAAbfAAbfEAbu8AcO8AAP8Abu8AbfAAbfAAbfEAgP8AbPAAbfAAbPIAa/IAbvAAbfAAbPEAbe0AcO8AbPAAbfAAceMAbfAAbfAAbfAAbPAAbfAAbfAAZv8AafAAbfEAavQAVf8AbPEAcfEAbf8AbfAAbfAAbe8Abe8AbfAAbfAAbfAAa/IAbfAAbfAAbfAAAADuFkhRAAAAM3RSTlMAKn606eKxgCABcvj0bgK19ztMl+1cDhA0+gnw4fnI09sFEcMYA6ASB77xcJOvzc4T8kSdjffTAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAAN1wAADdcBQiibeAAAAAd0SU1FB+IIDBMlMtO6eB4AAAChSURBVBjTTY/pDoMgEIQHAVHE2lNr7/uyt+//bl3Ypun8YL5dks0MQBKRVDo2CVipzdogl3f83Cnan4qUFjlB1/T6gyGBBRJHPkJZYUyQCRiyelJOXYUZYYSY3vli2a7Wm5pQQvO57W5/8K6geHE8nYNryOCX5sofMSKG253dQISYDzw5LMW3Hl7vJixyX+U/OpfJHY+ZTb99ExNrJSPh+QOxnx6dvxBTegAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOC0wOC0xMlQxOTozNzo1MCswMjowMEnQ2mMAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTgtMDgtMTJUMTk6Mzc6NTArMDI6MDA4jWLfAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAABJRU5ErkJggg==',
          html =
          [
              '<div id="spu-modal">',
              '    <div>',
              '        <button class="spu" id="spu-close" title="Закрыть">X</button><br>',
              '        <fieldset>',
              '            <legend>Сортировать найденное</legend>',
              '            <label for="spu-fromnew">От новых к старым</label>',
              '            <input id="spu-fromnew" name="sort" type="radio">',
              '            <label for="spu-fromold">От старых к новым</label>',
              '            <input id="spu-fromold" name="sort" type="radio">',
              '        </fieldset>',
              '        <fieldset>',
              '            <legend>Введите имя(-ена)',
              '                <img src="' + questionMark + '" title="' + hlpNms + '">',
              '            </legend>',
              '            <input id="spu-names" autofocus="">',
              '        </fieldset>',
              '        <fieldset>',
              '            <legend>Введите текст',
              '                <img src="' + questionMark + '" title="' + hlpTxt + '">',
              '            </legend>',
              '            <fieldset>',
              '                <legend>Опции поиска</legend> ',
              '                <input id="spu-case" type="checkbox">',
              '                <label for="spu-case">Без учета регистра</label>',
              '                <input id="spu-regexp" type="checkbox">',
              '                <label for="spu-regexp">RegExp</label>',
              '                <label for="spu-flags">Флаги: </label>',
              '                <input id="spu-flags" size="6" disabled="" type="text">',
              '            </fieldset>',
              '            <textarea id="spu-txtarea"></textarea>',
              '        </fieldset><br>',
              '        <button id="spu-srch-strt" class="spu">Начать поиск</button>',
              '    </div>',
              '</div>'
          ].join('\n'),
          css =
          [
              '#spu-spoiler-head {',
              '    font-family: Segoe UI Emoji, bitstreamcyberbit, quivira, Verdana, Arial, Helvetica, sans-serif;',
              '    -moz-user-select: none !important;',
              '    -webkit-user-select: none !important;',
              '    -ms-user-select: none !important;',
              '    user-select: none !important;',
              '    cursor: pointer !important',
              '}',
              '#spu-spoiler-head[notready] {',
              '    cursor: not-allowed !important;',
              '}',
              '#spu-spoiler-body {',
              '    border: 1px solid black !important;',
              '    padding: 1em .5em !important;',
              '}',
              '#spu-spoiler-body button.spu' + (listener.options.hideSearchButton ? ', .tb:not(:hover) button.spu' : '') + '{',
              '    visibility: hidden !important;',
              '}',
              'button.spu {',
              '    padding: .05em .5em .1em !important;',
              '    background: rgb(251,251,251) !important;',
              '    color: rgb(51, 51, 51) !important;',
              '    border: 1px solid rgba(66,78,90,0.2) !important;',
              '    border-radius: 2px !important;',
              '    box-shadow: none !important;',
              '    font-size: 1em !important;',
              '}',
              'button.spu:hover {',
              '    background: rgb(235,235,235) !important;',
              '}',
              'button.spu:active {',
              '    background: rgb(225,225,225) !important;',
              '}',
              '#spu-modal {',
              '    position: fixed !important;',
              '    top: 0 !important;',
              '    right: 0 !important;',
              '    bottom: 0 !important;',
              '    left: 0 !important;',
              '    background: rgba(0,0,0,0.8) !important;',
              '    z-index: 2147483647 !important;',
              '}',
              '#spu-modal > div {',
              '    position: relative !important;',,
              '    width: 30vw;',
              '    display: flex !important;',
              '    flex-flow: column !important;',
              '    margin: 10vh auto !important;',
              '    padding: 20px !important;',
              '    border-radius: 3px !important;',
              '    background: rgb(251, 251, 251) !important;',
              '    resize: both !important;',
              '    overflow: auto !important;',
              '}',
              '#spu-modal fieldset {',
              '    display: flex !important;',
              '    flex-flow: column !important;',
              '    font-weight: 600 !important;',
              '}',
              '#spu-modal legend img {',
              '    cursor: help !important;',
              '    padding: 0 5px !important;',
              '    vertical-align: bottom !important;',
              '}',
              '#spu-modal fieldset fieldset,',
              '#spu-modal fieldset:first-of-type {',
              '    display: flex !important;',
              '    flex-flow: wrap !important;',
              '    align-items: center !important;',
              '    justify-content: space-evenly !important;',
              '}',
              '#spu-modal fieldset:nth-of-type(3) {',
              '    flex-grow: 1 !important;',
              '}',
              '#spu-modal label {',
              '    font-weight: 400 !important;',
              '}',
              '#spu-modal input:not([type]) {',
              '    height: 2.5em !important;',
              '}',
              '#spu-modal textarea {',
              '    resize: none !important;',
              '    flex-basis: 20vh !important;',
              '    flex-grow: 1 !important;',
              '    margin: 10px 2px 0 !important;',
              '}',
              '#spu-modal button {',
              '    font-weight: 600 !important;',
              '}',
              '#spu-modal #spu-close {',
              '    position: absolute !important;',
              '    top: 0 !important;',
              '    right: 0 !important;',
              '    padding: 0 15px !important;',
              '}',
              '#spu-modal #spu-srch-strt {',
              '    margin: 10px 0 0 !important;',
              '    padding: 5px 0 !important;',
              '}'
          ].join('\n');
    style.type = "text/css"; style.textContent = css;
    if (dats)
        for (const dat of dats) {
            const button = dat.appendChild(document.createElement("br")) && dat.appendChild(document.createElement("br"))
                               && dat.appendChild(document.createElement("button")),
                  name = dat.getElementsByTagName("b")[0].textContent;
            button.className = "spu";
            button.textContent = "Найти посты";
            button.title = "Ctrl + ЛКМ: Добавить этого пользователя в задание для поиска\nЛКМ: Поиск постов для пользователя(-ей)/для выделенного\nПКМ: Показать форму для поиска";
            button.onclick = button.oncontextmenu = e => listener.handleEvent(e, name, e.button == 2);
        }
    if (listener.options.searchOnProfileLinksByRMB)
        body.addEventListener("contextmenu", e => {
            const target = e.target.closest("a[href*='?action=show&member=']"),
                  name = target && target.search.split("&member=")[1];
            if (!target || !name || e.altKey || e.shiftKey || e.ctrlKey || e.metaKey
                || !!listener.getSel() || target.closest("#spu-spoiler-body")) return;
            listener.handleEvent(e, name)
        });
    if (listener.isFF && listener.options.createCnMenu) {
        const context = body.getAttribute("contextmenu"),
              menu = context
                        ? document.getElementById(context)
                        : body.appendChild(document.createElement("menu")),
              mitem = menu.appendChild(document.createElement("menuitem"));
        if (!context) {
            menu.id = "GM_page-actions";
            menu.type = "context";
            body.setAttribute("contextmenu", "GM_page-actions");
        };
        mitem.label = "Найти собщения пользователя(-ей)";
        mitem.onclick = e => listener.handleEvent(e, null, !listener.getSel());
    };
});