// ==UserScript==
// @name Dollchan Extension Tools
// @version 14.4.3.0
// @namespace http://www.freedollchan.org/scripts/*
// @author Sthephan Shinkufag @ FreeDollChan
// @copyright (C)2084, Bender Bending Rodriguez
// @description Doing some profit for imageboards
// @icon https://raw.github.com/SthephanShinkufag/Dollchan-Extension-Tools/master/Icon.png
// @updateURL https://raw.github.com/SthephanShinkufag/Dollchan-Extension-Tools/master/Dollchan_Extension_Tools.meta.js
// @run-at document-start
// @include http://*
// @include https://*
// ==/UserScript==
(function de_main_func(scriptStorage) {
'use strict';
var version = '14.4.3.0',
defaultCfg = {
'disabled': 0, // script enabled by default
'language': 0, // script language [0=ru, 1=en]
'hideBySpell': 1, // hide posts by spells
'spells': '', // user defined spells
'sortSpells': 0, // sort spells when applying
'menuHiddBtn': 1, // menu on hide button
'hideRefPsts': 0, // hide post with references to hidden posts
'delHiddPost': 0, // delete hidden posts
'ajaxUpdThr': 1, // auto update threads
'updThrDelay': 60, // threads update interval in sec
'noErrInTitle': 0, // don't show error number in title except 404
'favIcoBlink': 1, // favicon blinking, if new posts detected
'markNewPosts': 1, // new posts marking on page focus
'desktNotif': 0, // desktop notifications, if new posts detected
'expandPosts': 2, // expand shorted posts [0=off, 1=auto, 2=on click]
'postBtnsCSS': 2, // post buttons style [0=text, 1=classic, 2=solid grey]
'noSpoilers': 1, // open spoilers
'noPostNames': 0, // hide post names
'noPostScrl': 1, // no scroll in posts
'correctTime': 0, // correct time in posts
'timeOffset': '+0', // offset in hours
'timePattern': '', // find pattern
'timeRPattern': '', // replace pattern
'expandImgs': 2, // expand images by click [0=off, 1=in post, 2=by center]
'resizeImgs': 1, // resize large images
'maskImgs': 0, // mask images
'preLoadImgs': 0, // pre-load images
'findImgFile': 0, // detect built-in files in images
'openImgs': 0, // open images in posts
'openGIFs': 0, // open only GIFs in posts
'imgSrcBtns': 1, // add image search buttons
'linksNavig': 2, // navigation by >>links [0=off, 1=no map, 2=+refmap]
'linksOver': 100, // delay appearance in ms
'linksOut': 1500, // delay disappearance in ms
'markViewed': 0, // mark viewed posts
'strikeHidd': 0, // strike >>links to hidden posts
'noNavigHidd': 0, // don't show previews for hidden posts
'crossLinks': 0, // replace http: to >>/b/links
'insertNum': 1, // insert >>link on postnumber click
'addMP3': 1, // embed mp3 links
'addImgs': 0, // embed links to images
'addYouTube': 3, // embed YouTube links [0=off, 1=onclick, 2=player, 3=preview+player, 4=only preview]
'YTubeType': 0, // player type [0=flash, 1=HTML5]
'YTubeWidth': 360, // player width
'YTubeHeigh': 270, // player height
'YTubeHD': 0, // hd video quality
'YTubeTitles': 0, // convert links to titles
'addVimeo': 1, // embed vimeo links
'ajaxReply': 2, // posting with AJAX (0=no, 1=iframe, 2=HTML5)
'postSameImg': 1, // ability to post same images
'removeEXIF': 1, // remove EXIF data from JPEGs
'removeFName': 0, // remove file name
'addPostForm': 2, // postform displayed [0=at top, 1=at bottom, 2=hidden, 3=hanging]
'scrAfterRep': 0, // scroll to the bottom after reply
'favOnReply': 1, // add thread to favorites on reply
'addSageBtn': 1, // email field -> sage btn
'saveSage': 1, // remember sage
'sageReply': 0, // reply with sage
'warnSubjTrip': 0, // warn if subject field contains tripcode
'captchaLang': 1, // language input in captcha [0=off, 1=en, 2=ru]
'addTextBtns': 1, // text format buttons [0=off, 1=graphics, 2=text, 3=usual]
'txtBtnsLoc': 0, // located at [0=top, 1=bottom]
'passwValue': '', // user password value
'userName': 0, // user name
'nameValue': '', // value
'userSignat': 0, // user signature
'signatValue': '', // value
'noBoardRule': 1, // hide board rules
'noGoto': 1, // hide goto field
'noPassword': 1, // hide password field
'scriptStyle': 0, // script style [0=glass black, 1=glass blue, 2=solid grey]
'userCSS': 0, // user style
'userCSSTxt': '', // css text
'expandPanel': 0, // show full main panel
'attachPanel': 1, // attach main panel
'panelCounter': 1, // posts/images counter in script panel
'rePageTitle': 1, // replace page title in threads
'animation': 1, // animation in script
'closePopups': 0, // auto-close popups
'keybNavig': 1, // keyboard navigation
'loadPages': 1, // number of pages that are loaded on F5
'updScript': 1, // check for script's update
'scrUpdIntrv': 1, // check interval in days (every val+1 day)
'textaWidth': 500, // textarea width
'textaHeight': 160 // textarea height
},
Lng = {
cfg: {
'hideBySpell': ['Заклинания: ', 'Magic spells: '],
'sortSpells': ['Сортировать спеллы и удалять дубликаты', 'Sort spells and delete duplicates'],
'menuHiddBtn': ['Дополнительное меню кнопок скрытия ', 'Additional menu of hide buttons'],
'hideRefPsts': ['Скрывать ответы на скрытые посты*', 'Hide replies to hidden posts*'],
'delHiddPost': ['Удалять скрытые посты', 'Delete hidden posts'],
'ajaxUpdThr': ['AJAX обновление треда ', 'AJAX thread update '],
'updThrDelay': [' (сек)', ' (sec)'],
'noErrInTitle': ['Не показывать номер ошибки в заголовке', 'Don\'t show error number in title'],
'favIcoBlink': ['Мигать фавиконом при новых постах', 'Favicon blinking on new posts'],
'markNewPosts': ['Выделять новые посты при переключении на тред', 'Mark new posts on page focus'],
'desktNotif': ['Уведомления на рабочем столе', 'Desktop notifications'],
'expandPosts': {
sel: [['Откл.', 'Авто', 'По клику'], ['Disable', 'Auto', 'On click']],
txt: ['AJAX загрузка сокращенных постов*', 'AJAX upload of shorted posts*']
},
'postBtnsCSS': {
sel: [['Text', 'Classic', 'Solid grey'], ['Text', 'Classic', 'Solid grey']],
txt: ['Стиль кнопок постов*', 'Post buttons style*']
},
'noSpoilers': ['Открывать текстовые спойлеры', 'Open text spoilers'],
'noPostNames': ['Скрывать имена в постах', 'Hide names in posts'],
'noPostScrl': ['Без скролла в постах', 'No scroll in posts'],
'keybNavig': ['Навигация с помощью клавиатуры ', 'Navigation with keyboard '],
'loadPages': [' Количество страниц, загружаемых по F5', ' Number of pages that are loaded on F5 '],
'correctTime': ['Корректировать время в постах* ', 'Correct time in posts* '],
'timeOffset': [' Разница во времени', ' Time difference'],
'timePattern': [' Шаблон поиска', ' Find pattern'],
'timeRPattern': [' Шаблон замены', ' Replace pattern'],
'expandImgs': {
sel: [['Откл.', 'В посте', 'По центру'], ['Disable', 'In post', 'By center']],
txt: ['раскрывать изображения по клику', 'expand images on click']
},
'resizeImgs': ['Уменьшать в экран большие изображения', 'Resize large images to fit screen'],
'preLoadImgs': ['Предварительно загружать изображения*', 'Pre-load images*'],
'findImgFile': ['Распознавать встроенные файлы в изображениях*', 'Detect built-in files in images*'],
'openImgs': ['Скачивать полные версии изображений*', 'Download full version of images*'],
'openGIFs': ['Скачивать только GIFы*', 'Download GIFs only*'],
'imgSrcBtns': ['Добавлять кнопки для поиска изображений*', 'Add image search buttons*'],
'linksNavig': {
sel: [['Откл.', 'Без карты', 'С картой'], ['Disable', 'No map', 'With map']],
txt: ['навигация по >>ссылкам* ', 'navigation by >>links* ']
},
'linksOver': [' задержка появления (мс)', ' delay appearance (ms)'],
'linksOut': [' задержка пропадания (мс)', ' delay disappearance (ms)'],
'markViewed': ['Отмечать просмотренные посты*', 'Mark viewed posts*'],
'strikeHidd': ['Зачеркивать >>ссылки на скрытые посты', 'Strike >>links to hidden posts'],
'noNavigHidd': ['Не отображать превью для скрытых постов', 'Don\'t show previews for hidden posts'],
'crossLinks': ['Преобразовывать http:// в >>/b/ссылки*', 'Replace http:// with >>/b/links*'],
'insertNum': ['Вставлять >>ссылку по клику на №поста*', 'Insert >>link on №postnumber click*'],
'addMP3': ['Добавлять плейер к mp3 ссылкам* ', 'Add player to mp3 links* '],
'addVimeo': ['Добавлять плейер к Vimeo ссылкам* ', 'Add player to Vimeo links* '],
'addImgs': ['Загружать изображения к .jpg-, .png-, .gif-ссылкам*', 'Load images to .jpg-, .png-, .gif-links*'],
'addYouTube': {
sel: [['Ничего', 'Плейер по клику', 'Авто плейер', 'Превью+плейер', 'Только превью'], ['Nothing', 'On click player', 'Auto player', 'Preview+player', 'Only preview']],
txt: ['к YouTube-ссылкам* ', 'to YouTube-links* ']
},
'YTubeType': {
sel: [['Flash', 'HTML5'], ['Flash', 'HTML5']],
txt: ['', '']
},
'YTubeHD': ['HD ', 'HD '],
'YTubeTitles': ['Загружать названия к YouTube-ссылкам*', 'Load titles into YouTube-links*'],
'ajaxReply': {
sel: [['Откл.', 'Iframe', 'HTML5'], ['Disable', 'Iframe', 'HTML5']],
txt: ['AJAX отправка постов*', 'posting with AJAX*']
},
'postSameImg': ['Возможность отправки одинаковых изображений', 'Ability to post same images'],
'removeEXIF': ['Удалять EXIF из отправляемых JPEG-изображений', 'Remove EXIF from uploaded JPEG-images'],
'removeFName': ['Удалять имя из отправляемых файлов', 'Remove names from uploaded files'],
'addPostForm': {
sel: [['Сверху', 'Внизу', 'Скрытая', 'Отдельная'], ['At top', 'At bottom', 'Hidden', 'Hanging']],
txt: ['форма ответа в треде* ', 'reply form in thread* ']
},
'scrAfterRep': ['Перемещаться в конец треда после отправки', 'Scroll to the bottom after reply'],
'favOnReply': ['Добавлять тред в избранное при ответе', 'Add thread to favorites on reply'],
'addSageBtn': ['Sage вместо поля E-mail* ', 'Sage button instead of E-mail field* '],
'warnSubjTrip': ['Предупреждать при наличии трип-кода в поле тема', 'Warn if field subject contains trip-code'],
'saveSage': ['запоминать сажу', 'remember sage'],
'captchaLang': {
sel: [['Откл.', 'Eng', 'Rus'], ['Disable', 'Eng', 'Rus']],
txt: ['язык ввода капчи', 'language input in captcha']
},
'addTextBtns': {
sel: [['Откл.', 'Графич.', 'Упрощ.', 'Стандарт.'], ['Disable', 'As images', 'As text', 'Standard']],
txt: ['кнопки форматирования текста ', 'text format buttons ']
},
'txtBtnsLoc': ['внизу', 'at bottom'],
'userPassw': [' Постоянный пароль ', ' Fixed password '],
'userName': ['Постоянное имя', 'Fixed name'],
'userSignat': ['Постоянная подпись', 'Fixed signature'],
'noBoardRule': ['правила ', 'rules '],
'noGoto': ['поле goto ', 'goto field '],
'noPassword': ['пароль', 'password'],
'scriptStyle': {
sel: [['Glass black', 'Glass blue', 'Solid grey'], ['Glass black', 'Glass blue', 'Solid grey']],
txt: ['стиль скрипта', 'script style']
},
'userCSS': ['Пользовательский CSS ', 'User CSS '],
'attachPanel': ['Прикрепить главную панель', 'Attach main panel'],
'panelCounter': ['Счетчик постов/изображений на главной панели', 'Counter of posts/images on main panel'],
'rePageTitle': ['Название треда в заголовке вкладки*', 'Thread title in page tab*'],
'animation': ['CSS3 анимация в скрипте', 'CSS3 animation in script'],
'closePopups': ['Автоматически закрывать уведомления', 'Close popups automatically'],
'updScript': ['Автоматически проверять обновления скрипта', 'Check for script update automatically'],
'scrUpdIntrv': {
sel: [['Каждый день', 'Каждые 2 дня', 'Каждую неделю', 'Каждые 2 недели', 'Каждый месяц'], ['Every day', 'Every 2 days', 'Every week', 'Every 2 week', 'Every month']],
txt: ['', '']
},
'language': {
sel: [['Ru', 'En'], ['Ru', 'En']],
txt: ['', '']
}
},
txtBtn: [
['Жирный', 'Bold'],
['Наклонный', 'Italic'],
['Подчеркнутый', 'Underlined'],
['Зачеркнутый', 'Strike'],
['Спойлер', 'Spoiler'],
['Код', 'Code'],
['Верхний индекс', 'Superscript'],
['Нижний индекс', 'Subscript'],
['Цитировать выделенное', 'Quote selected']
],
cfgTab: {
'filters': ['Фильтры', 'Filters'],
'posts': ['Посты', 'Posts'],
'images': ['Картинки', 'Images'],
'links': ['Ссылки', 'Links'],
'form': ['Форма', 'Form'],
'common': ['Общее', 'Common'],
'info': ['Инфо', 'Info']
},
panelBtn: {
'attach': ['Прикрепить/Открепить', 'Attach/Detach'],
'settings': ['Настройки', 'Settings'],
'hidden': ['Скрытое', 'Hidden'],
'favor': ['Избранное', 'Favorites'],
'refresh': ['Обновить', 'Refresh'],
'goback': ['Назад', 'Go back'],
'gonext': ['Следующая', 'Next'],
'goup': ['Наверх', 'To the top'],
'godown': ['В конец', 'To the bottom'],
'expimg': ['Раскрыть картинки', 'Expand images'],
'preimg': ['Предзагрузка картинок', 'Preload images'],
'maskimg': ['Маскировать картинки', 'Mask images'],
'upd-on': ['Выключить автообновление треда', 'Disable thread autoupdate'],
'upd-off': ['Включить автообновление треда', 'Enable thread autoupdate'],
'audio-off':['Звуковое оповещение о новых постах', 'Sound notification about new posts'],
'catalog': ['Каталог', 'Catalog'],
'counter': ['Постов/Изображений в треде', 'Posts/Images in thread'],
'imgload': ['Сохранить изображения из треда', 'Save images from thread'],
'enable': ['Включить/выключить скрипт', 'Turn on/off the script']
},
selHiderMenu: {
'sel': ['Скрывать выделенное', 'Hide selected text'],
'name': ['Скрывать имя', 'Hide name'],
'trip': ['Скрывать трип-код', 'Hide with trip-code'],
'img': ['Скрывать изображение', 'Hide with image'],
'ihash': ['Скрывать схожие изобр.', 'Hide similar images'],
'text': ['Скрыть схожий текст', 'Hide similar text'],
'noimg': ['Скрывать без изображений', 'Hide without images'],
'notext': ['Скрывать без текста', 'Hide without text']
},
selExpandThrd: [
['5 постов', '15 постов', '30 постов', '50 постов', '100 постов'],
['5 posts', '15 posts', '30 posts', '50 posts', '100 posts']
],
selAjaxPages: [
['1 страница', '2 страницы', '3 страницы', '4 страницы', '5 страниц'],
['1 page', '2 pages', '3 pages', '4 pages', '5 pages']
],
selAudioNotif: [
['Каждые 30 сек.', 'Каждую минуту', 'Каждые 2 мин.', 'Каждые 5 мин.'],
['Every 30 sec.', 'Every minute', 'Every 2 min.', 'Every 5 min.']
],
keyNavEdit: [
'%l%i24 – предыдущая страница/изображение%/l' +
'%l%i217 – следующая страница/изображение%/l' +
'%l%i23 – скрыть текущий пост/тред%/l' +
'%l%i33 – раскрыть текущий тред%/l' +
'%l%i22 – быстрый ответ или создать тред%/l' +
'%l%i25t – отправить пост%/l' +
'%l%i21 – тред (на доске)/пост (в треде) ниже%/l' +
'%l%i20 – тред (на доске)/пост (в треде) выше%/l' +
'%l%i31 – пост (на доске) ниже%/l' +
'%l%i30 – пост (на доске) выше%/l' +
'%l%i32 – открыть тред%/l' +
'%l%i210 – открыть/закрыть настройки%/l' +
'%l%i26 – открыть/закрыть избранное%/l' +
'%l%i27 – открыть/закрыть скрытые посты%/l' +
'%l%i28 – открыть/закрыть панель%/l' +
'%l%i29 – включить/выключить маскировку изображений%/l' +
'%l%i40 – обновить тред%/l' +
'%l%i211 – раскрыть изображение текущего поста%/l' +
'%l%i212t – жирный%/l' +
'%l%i213t – курсив%/l' +
'%l%i214t – зачеркнутый%/l' +
'%l%i215t – спойлер%/l' +
'%l%i216t – код%/l',
'%l%i24 – previous page/image%/l' +
'%l%i217 – next page/image%/l' +
'%l%i23 – hide current post/thread%/l' +
'%l%i33 – expand current thread%/l' +
'%l%i22 – quick reply or create thread%/l' +
'%l%i25t – send post%/l' +
'%l%i21 – thread (on board)/post (in thread) below%/l' +
'%l%i20 – thread (on board)/post (in thread) above%/l' +
'%l%i31 – on board post below%/l' +
'%l%i30 – on board post above%/l' +
'%l%i32 – open thread%/l' +
'%l%i210 – open/close Settings%/l' +
'%l%i26 – open/close Favorites%/l' +
'%l%i27 – open/close Hidden Posts Table%/l' +
'%l%i28 – open/close the main panel%/l' +
'%l%i29 – turn on/off masking images%/l' +
'%l%i40 – update thread%/l' +
'%l%i211 – expand current post\'s images%/l' +
'%l%i212t – bold%/l' +
'%l%i213t – italic%/l' +
'%l%i214t – strike%/l' +
'%l%i215t – spoiler%/l' +
'%l%i216t – code%/l'
],
month: [
['янв', 'фев', 'мар', 'апр', 'мая', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'],
['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
],
fullMonth: [
['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'],
['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
],
week: [
['Вск', 'Пнд', 'Втр', 'Срд', 'Чтв', 'Птн', 'Сбт'],
['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
],
editor: {
cfg: ['Редактирование настроек:', 'Edit settings:'],
hidden: ['Редактирование скрытых тредов:', 'Edit hidden threads:'],
favor: ['Редактирование избранного:', 'Edit favorites:'],
css: ['Редактирование CSS', 'Edit CSS']
},
newPost: [
[' новый пост', ' новых поста', ' новых постов', '. Последний:'],
[' new post', ' new posts', ' new posts', '. Latest: ']
],
add: ['Добавить', 'Add'],
apply: ['Применить', 'Apply'],
clear: ['Очистить', 'Clear'],
refresh: ['Обновить', 'Refresh'],
load: ['Загрузить', 'Load'],
save: ['Сохранить', 'Save'],
edit: ['Правка', 'Edit'],
reset: ['Сброс', 'Reset'],
remove: ['Удалить', 'Remove'],
info: ['Инфо', 'Info'],
undo: ['Отмена', 'Undo'],
change: ['Сменить', 'Change'],
reply: ['Ответ', 'Reply'],
loading: ['Загрузка...', 'Loading...'],
checking: ['Проверка...', 'Checking...'],
deleting: ['Удаление...', 'Deleting...'],
error: ['Ошибка:', 'Error:'],
noConnect: ['Ошибка подключения', 'Connection failed'],
thrNotFound: ['Тред недоступен (№', 'Thread is unavailable (№'],
succDeleted: ['Успешно удалено!', 'Succesfully deleted!'],
errDelete: ['Не могу удалить:\n', 'Can\'t delete:\n'],
cTimeError: ['Неправильные настройки времени', 'Invalid time settings'],
noGlobalCfg: ['Глобальные настройки не найдены', 'Global config not found'],
postNotFound: ['Пост не найден', 'Post not found'],
dontShow: ['Не отображать: ', 'Do not show: '],
checkNow: ['Проверить сейчас', 'Check now'],
updAvail: ['Доступно обновление!', 'Update available!'],
haveLatest: ['У вас стоит самая последняя версия!', 'You have latest version!'],
storage: ['Хранение: ', 'Storage: '],
thrViewed: ['Тредов просмотрено: ', 'Threads viewed: '],
thrCreated: ['Тредов создано: ', 'Threads created: '],
thrHidden: ['Тредов скрыто: ', 'Threads hidden: '],
postsSent: ['Постов отправлено: ', 'Posts sent: '],
total: ['Всего: ', 'Total: '],
debug: ['Отладка', 'Debug'],
infoDebug: ['Информация для отладки', 'Information for debugging'],
loadGlobal: ['Загрузить глобальные настройки', 'Load global settings'],
saveGlobal: ['Сохранить настройки как глобальные', 'Save settings as global'],
editInTxt: ['Правка в текстовом формате', 'Edit in text format'],
resetCfg: ['Сбросить в настройки по умолчанию', 'Reset settings to defaults'],
conReset: ['Данное действие удалит все ваши настройки и закладки. Продолжить?', 'This will delete all your preferences and favourites. Continue?'],
clrSelected: ['Удалить выделенные записи', 'Remove selected notes'],
saveChanges: ['Сохранить внесенные изменения', 'Save your changes'],
infoCount: ['Обновить счетчики постов', 'Refresh posts counters'],
infoPage: ['Проверить актуальность тредов (до 5 страницы)', 'Check for threads actuality (up to 5 page)'],
clrDeleted: ['Очистить записи недоступных тредов', 'Clear notes of inaccessible threads'],
hiddenPosts: ['Скрытые посты на странице', 'Hidden posts on page'],
hiddenThrds: ['Скрытые треды', 'Hidden threads'],
noHidPosts: ['На этой странице нет скрытых постов...', 'No hidden posts on this page...'],
noHidThrds: ['Нет скрытых тредов...', 'No hidden threads...'],
expandAll: ['Раскрыть все', 'Expand all'],
invalidData: ['Некорректный формат данных', 'Incorrect data format'],
favThrds: ['Избранные треды:', 'Favorite threads:'],
noFavThrds: ['Нет избранных тредов...', 'Favorites is empty...'],
replyTo: ['Ответ в', 'Reply to'],
replies: ['Ответы:', 'Replies:'],
postsOmitted: ['Пропущено ответов: ', 'Posts omitted: '],
collapseThrd: ['Свернуть тред', 'Collapse thread'],
deleted: ['удалён', 'deleted'],
getNewPosts: ['Получить новые посты', 'Get new posts'],
page: ['Страница', 'Page'],
hiddenThrd: ['Скрытый тред:', 'Hidden thread:'],
makeThrd: ['Создать тред', 'Create thread'],
makeReply: ['Ответить', 'Make reply'],
hideForm: ['Закрыть форму', 'Hide form'],
search: ['Искать в ', 'Search in '],
wait: ['Ждите', 'Wait'],
addFile: ['+ файл', '+ file'],
helpAddFile: ['Добавить .ogg, .rar, .zip, или .7z к картинке', 'Add .ogg, .rar, .zip, or .7z to image '],
downloadFile: ['Скачать содержащийся в картинке файл', 'Download existing file from image'],
fileCorrupt: ['Файл повреждён: ', 'File is corrupted: '],
subjHasTrip: ['Поле "Тема" содержит трипкод', '"Subject" field contains tripcode'],
loadImage: ['Загружаются изображения: ', 'Loading images: '],
loadFile: ['Загружаются файлы: ', 'Loading files: '],
cantLoad: ['Не могу загрузить ', 'Can\'t load '],
willSavePview: ['Будет сохранено превью', 'Thumb will be saved'],
loadErrors: ['Во время загрузки произошли ошибки:', 'Warning:'],
errCorruptData: ['Ошибка: сервер отправил повреждённые данные', 'Error: server sent corrupted data'],
nextImg: ['Следующее изображение', 'Next image'],
prevImg: ['Предыдущее изображение', 'Previous image'],
seSyntaxErr: ['синтаксическая ошибка в аргументе спелла: %s', 'syntax error in argument of spell: %s'],
seUnknown: ['неизвестный спелл: %s', 'unknown spell: %s'],
seMissOp: ['пропущен оператор', 'missing operator'],
seMissArg: ['пропущен аргумент спелла: %s', 'missing argument of spell: %s'],
seMissSpell: ['пропущен спелл', 'missing spell'],
seErrRegex: ['синтаксическая ошибка в регулярном выражении: %s', 'syntax error in regular expression: %s'],
seUnexpChar: ['неожиданный символ: %s', 'unexpected character: %s'],
seMissClBkt: ['пропущена закрывающаяся скобка', 'missing ) in parenthetical'],
seRow: [' (строка ', ' (row '],
seCol: [', столбец ', ', column ']
},
doc = window.document, aProto = Array.prototype,
Cfg, comCfg, hThr, Favor, pByNum, sVis, bUVis, uVis, needScroll,
aib, nav, brd, TNum, pageNum, updater, youTube, keyNav, firstThr, lastThr, visPosts = 2,
pr, dForm, dummy, spells,
Images_ = {preloading: false, afterpreload: null, progressId: null, canvas: null},
oldTime, timeLog = [], dTime,
ajaxInterval, lang, quotetxt = '', liteMode, isExpImg, isPreImg,
$each = Function.prototype.call.bind(aProto.forEach),
emptyFn = function() {};
//============================================================================================================
// UTILITIES
//============================================================================================================
function $Q(path, root) {
return root.querySelectorAll(path);
}
function $q(path, root) {
return root.querySelector(path);
}
function $C(id, root) {
return root.getElementsByClassName(id);
}
function $c(id, root) {
return root.getElementsByClassName(id)[0];
}
function $id(id) {
return doc.getElementById(id);
}
function $T(id, root) {
return root.getElementsByTagName(id);
}
function $t(id, root) {
return root.getElementsByTagName(id)[0];
}
function $append(el, nodes) {
for(var i = 0, len = nodes.length; i < len; i++) {
if(nodes[i]) {
el.appendChild(nodes[i]);
}
}
}
function $before(el, node) {
el.parentNode.insertBefore(node, el);
}
function $after(el, node) {
el.parentNode.insertBefore(node, el.nextSibling);
}
function $add(html) {
dummy.innerHTML = html;
return dummy.firstChild;
}
function $new(tag, attr, events) {
var el = doc.createElement(tag);
if(attr) {
for(var key in attr) {
key === 'text' ? el.textContent = attr[key] :
key === 'value' ? el.value = attr[key] :
el.setAttribute(key, attr[key]);
}
}
if(events) {
for(var key in events) {
el.addEventListener(key, events[key], false);
}
}
return el;
}
function $New(tag, attr, nodes) {
var el = $new(tag, attr, null);
$append(el, nodes);
return el;
}
function $txt(el) {
return doc.createTextNode(el);
}
function $btn(val, ttl, Fn) {
return $new('input', {'type': 'button', 'value': val, 'title': ttl}, {'click': Fn});
}
function $script(text) {
$del(doc.head.appendChild($new('script', {'type': 'text/javascript', 'text': text}, null)));
}
function $css(text) {
return doc.head.appendChild($new('style', {'type': 'text/css', 'text': text}, null));
}
function $if(cond, el) {
return cond ? el : null;
}
function $disp(el) {
el.style.display = el.style.display === 'none' ? '' : 'none';
}
function $del(el) {
if(el) {
el.parentNode.removeChild(el);
}
}
function $DOM(html) {
var myDoc = doc.implementation.createHTMLDocument('');
myDoc.documentElement.innerHTML = html;
return myDoc;
}
function $getStyle(el, prop) {
return getComputedStyle(el).getPropertyValue(prop);
}
function $pd(e) {
e.preventDefault();
}
function $txtInsert(el, txt) {
var scrtop = el.scrollTop,
start = el.selectionStart;
el.value = el.value.substr(0, start) + txt + el.value.substr(el.selectionEnd);
el.setSelectionRange(start + txt.length, start + txt.length);
el.focus();
el.scrollTop = scrtop;
}
function $txtSelect() {
return (nav.Opera ? doc.getSelection() : window.getSelection()).toString();
}
function $isEmpty(obj) {
for(var i in obj) {
if(obj.hasOwnProperty(i)) {
return false;
}
}
return true;
}
function $log(txt) {
var newTime = Date.now(),
time = newTime - oldTime;
if(time > 1) {
timeLog.push(txt + ': ' + time + 'ms');
oldTime = newTime;
}
}
function $xhr(obj) {
var h, xhr = new window.XMLHttpRequest();
if(obj['onreadystatechange']) {
xhr.onreadystatechange = obj['onreadystatechange'].bind(window, xhr);
}
if(obj['onload']) {
xhr.onload = obj['onload'].bind(window, xhr);
}
xhr.open(obj['method'], obj['url'], true);
if(obj['responseType']) {
xhr.responseType = obj['responseType'];
}
for(h in obj['headers']) {
xhr.setRequestHeader(h, obj['headers'][h]);
}
xhr.send(obj['data'] || null);
return xhr;
}
function $queue(maxNum, Fn, endFn) {
this.array = [];
this.length = this.index = this.running = 0;
this.num = 1;
this.fn = Fn;
this.endFn = endFn;
this.max = maxNum;
this.freeSlots = [];
while(maxNum--) {
this.freeSlots.push(maxNum);
}
this.completed = this.paused = false;
}
$queue.prototype = {
run: function(data) {
if(this.paused || this.running === this.max) {
this.array.push(data);
this.length++;
} else {
this.fn(this.freeSlots.pop(), this.num++, data);
this.running++;
}
},
end: function(qIdx) {
if(!this.paused && this.index < this.length) {
this.fn(qIdx, this.num++, this.array[this.index++]);
return;
}
this.running--;
this.freeSlots.push(qIdx);
if(!this.paused && this.completed && this.running === 0) {
this.endFn();
}
},
complete: function() {
if(this.index >= this.length && this.running === 0) {
this.endFn();
} else {
this.completed = true;
}
},
pause: function() {
this.paused = true;
},
'continue': function() {
this.paused = false;
if(this.index >= this.length) {
if(this.completed) {
this.endFn();
}
return;
}
while(this.index < this.length && this.running !== this.max) {
this.fn(this.freeSlots.pop(), this.num++, this.array[this.index++]);
this.running++;
}
}
};
function $tar() {
this._data = [];
}
$tar.prototype = {
addFile: function(filepath, input) {
var i, checksum, nameLen, fileSize = input.length,
header = new Uint8Array(512);
for(i = 0, nameLen = Math.min(filepath.length, 100); i < nameLen; ++i) {
header[i] = filepath.charCodeAt(i) & 0xFF;
}
this._padSet(header, 100, '100777', 8); // fileMode
this._padSet(header, 108, '0', 8); // uid
this._padSet(header, 116, '0', 8); // gid
this._padSet(header, 124, fileSize.toString(8), 13); // fileSize
this._padSet(header, 136, Math.floor(Date.now() / 1000).toString(8), 12); // mtime
this._padSet(header, 148, ' ', 8); // checksum
header[156] = 0x30; // type ('0')
for(i = checksum = 0; i < 157; i++) {
checksum += header[i];
}
this._padSet(header, 148, checksum.toString(8), 8); // checksum
this._data.push(header);
this._data.push(input);
if((i = Math.ceil(fileSize / 512) * 512 - fileSize) !== 0) {
this._data.push(new Uint8Array(i));
}
},
addString: function(filepath, str) {
var i, len, data, sDat = unescape(encodeURIComponent(str));
for(i = 0, len = sDat.length, data = new Uint8Array(len); i < len; ++i) {
data[i] = sDat.charCodeAt(i) & 0xFF;
}
this.addFile(filepath, data);
},
get: function() {
this._data.push(new Uint8Array(1024));
return new Blob(this._data, {'type': 'application/x-tar'});
},
_padSet: function(data, offset, num, len) {
var i = 0, nLen = num.length;
len -= 2;
while(nLen < len) {
data[offset++] = 0x20; // ' '
len--;
}
while(i < nLen) {
data[offset++] = num.charCodeAt(i++);
}
data[offset] = 0x20; // ' '
}
};
function $workers(source, count) {
var i, wrk, wUrl;
if(nav.Firefox) {
wUrl = 'data:text/javascript,' + source;
wrk = unsafeWindow.Worker;
} else {
wUrl = window.URL.createObjectURL(new Blob([source], {'type': 'text/javascript'}));
this.url = wUrl;
wrk = Worker;
}
for(i = 0; i < count; ++i) {
this[i] = new wrk(wUrl);
}
}
$workers.prototype = {
url: null,
clear: function() {
if(this.url !== null) {
window.URL.revokeObjectURL(this.url);
}
}
};
function regQuote(str) {
return (str + '').replace(/([.?*+^$[\]\\(){}|\-])/g, '\\$1');
}
function getImages(el) {
return el.querySelectorAll('.thumb, .de-thumb, .ca_thumb, ' +
'img[src*="thumb"], img[src*="/spoiler"], img[src^="blob:"]');
}
function fixBrd(b) {
return '/' + b + (b ? '/' : '');
}
function getAbsLink(url) {
return url[1] === '/' ? aib.prot + url :
url[0] === '/' ? aib.prot + '//' + aib.host + url : url;
}
function getErrorMessage(eCode, eMsg) {
return eCode === 0 ? eMsg || Lng.noConnect[lang] : 'HTTP [' + eCode + '] ' + eMsg;
}
function getAncestor(el, tagName) {
do {
el = el.parentElement;
} while(el && el.tagName !== tagName);
return el;
}
function getPrettyErrorMessage(e) {
return e.stack ? (nav.WebKit ? e.stack :
e.name + ': ' + e.message + '\n' +
(nav.Firefox ? e.stack.replace(/^([^@]*).*\/(.+)$/gm, function(str, fName, line) {
return ' at ' + (fName ? fName + ' (' + line + ')' : line);
}) : e.stack)
) : e.name + ': ' + e.message;
}
function toRegExp(str, noG) {
var l = str.lastIndexOf('/'),
flags = str.substr(l + 1);
return new RegExp(str.substr(1, l - 1), noG ? flags.replace('g', '') : flags);
}
//============================================================================================================
// STORAGE & CONFIG
//============================================================================================================
function getStored(id) {
return nav.isGM ? GM_getValue(id) :
nav.isSStorage ? scriptStorage.getItem(id) :
localStorage.getItem(id);
}
function setStored(id, value) {
if(nav.isGM) {
GM_setValue(id, value);
} else if(nav.isSStorage) {
scriptStorage.setItem(id, value);
} else {
localStorage.setItem(id, value);
}
}
function delStored(id) {
if(nav.isGM) {
GM_deleteValue(id);
} else if(nav.isSStorage) {
scriptStorage.removeItem(id);
} else {
localStorage.removeItem(id);
}
}
function getStoredObj(id) {
try {
var data = JSON.parse(getStored(id));
} finally {
return data || {};
}
}
function saveComCfg(dm, obj) {
comCfg = getStoredObj('DESU_Config');
if(obj) {
comCfg[dm] = obj;
} else {
delete comCfg[dm];
}
setStored('DESU_Config', JSON.stringify(comCfg) || '');
}
function saveCfg(id, val) {
if(Cfg[id] !== val) {
Cfg[id] = val;
saveComCfg(aib.dm, Cfg);
}
}
function readCfg() {
comCfg = getStoredObj('DESU_Config');
if(!(aib.dm in comCfg) || $isEmpty(Cfg = comCfg[aib.dm])) {
Cfg = {};
if(nav.isGlobal) {
for(var i in comCfg['global']) {
Cfg[i] = comCfg['global'][i];
}
}
Cfg['captchaLang'] = aib.ru ? 2 : 1;
Cfg['correctTime'] = 0;
}
Cfg.__proto__ = defaultCfg;
if(!Cfg['timeOffset']) {
Cfg['timeOffset'] = '+0';
}
if(!Cfg['timePattern']) {
Cfg['timePattern'] = aib.timePattern;
}
if(nav.noBlob) {
Cfg['preLoadImgs'] = 0;
if(Cfg['ajaxReply'] === 2) {
Cfg['ajaxReply'] = 1;
}
}
if(aib.tiny && Cfg['ajaxReply'] === 2) {
Cfg['ajaxReply'] = 1;
}
if(!nav.Firefox) {
defaultCfg['favIcoBlink'] = 0;
}
if(!('Notification' in window)) {
Cfg['desktNotif'] = 0;
}
if(nav.Opera) {
if(nav.oldOpera) {
if(!nav.isGM) {
Cfg['YTubeTitles'] = 0;
}
Cfg['animation'] = 0;
}
if(Cfg['YTubeType'] === 2) {
Cfg['YTubeType'] = 1;
}
Cfg['preLoadImgs'] = 0;
Cfg['findImgFile'] = 0;
if(!nav.isGM) {
Cfg['updScript'] = 0;
}
}
if(Cfg['updThrDelay'] < 10) {
Cfg['updThrDelay'] = 10;
}
if(!Cfg['saveSage']) {
Cfg['sageReply'] = 0;
}
if(!Cfg['passwValue']) {
Cfg['passwValue'] = Math.round(Math.random() * 1e15).toString(32);
}
if(!Cfg['stats']) {
Cfg['stats'] = {'view': 0, 'op': 0, 'reply': 0};
}
if(TNum) {
Cfg['stats']['view']++;
}
saveComCfg(aib.dm, Cfg);
lang = Cfg['language'];
if(Cfg['correctTime']) {
dTime = new dateTime(Cfg['timePattern'], Cfg['timeRPattern'], Cfg['timeOffset'], lang, function(rp) {
saveCfg('timeRPattern', rp);
});
}
if(aib.dobr) {
aib.hDTFix = new dateTime(
'yyyy-nn-dd-hh-ii-ss',
'_d _M _y (_w) _h:_i ',
Cfg['timeOffset'] || 0,
Cfg['correctTime'] ? lang : 1,
null
);
}
}
function toggleCfg(id) {
saveCfg(id, +!Cfg[id]);
}
function readPosts() {
var data, str = TNum ? sessionStorage['de-hidden-' + brd + TNum] : null;
if(typeof str === 'string') {
data = str.split(',');
if(data.length === 4 && +data[0] === (Cfg['hideBySpell'] ? spells.hash : 0) &&
(data[1] in pByNum) && pByNum[data[1]].count === +data[2])
{
sVis = data[3].split('');
return;
}
}
sVis = [];
}
function readUserPosts() {
bUVis = getStoredObj('DESU_Posts_' + aib.dm);
uVis = bUVis[brd];
if(!uVis) {
bUVis[brd] = uVis = getStoredObj('DESU_Posts_' + aib.dm + '_' + brd);
delStored('DESU_Posts_' + aib.dm + '_' + brd);
}
hThr = getStoredObj('DESU_Threads_' + aib.dm);
if(!(brd in hThr)) {
hThr[brd] = {};
}
}
function savePosts() {
if(TNum) {
var lPost = firstThr.lastNotDeleted;
sessionStorage['de-hidden-' + brd + TNum] = (Cfg['hideBySpell'] ? spells.hash : '0') +
',' + lPost.num + ',' + lPost.count + ',' + sVis.join('');
}
saveHiddenThreads(false);
toggleContent('hid', true);
}
function saveUserPosts() {
var minDate, b, vis, key, str = JSON.stringify(bUVis);
if(str.length > 1e6) {
minDate = Date.now() - 5 * 24 * 3600 * 1000;
for(b in bUVis) {
if(bUVis.hasOwnProperty(b)) {
vis = bUVis[b];
for(key in vis) {
if(vis.hasOwnProperty(key) && vis[key][1] < minDate) {
delete vis[key];
}
}
}
}
str = JSON.stringify(bUVis);
}
setStored('DESU_Posts_' + aib.dm, str);
toggleContent('hid', true);
}
function saveHiddenThreads(updContent) {
setStored('DESU_Threads_' + aib.dm, JSON.stringify(hThr));
if(updContent) {
toggleContent('hid', true);
}
}
function readFavorites() {
Favor = getStoredObj('DESU_Favorites');
}
function saveFavorites() {
setStored('DESU_Favorites', JSON.stringify(Favor));
toggleContent('fav', true);
}
function removeFavorites(h, b, tNum) {
delete Favor[h][b][tNum];
if($isEmpty(Favor[h][b])) {
delete Favor[h][b];
}
if($isEmpty(Favor[h])) {
delete Favor[h];
}
if(pByNum[tNum]) {
($c('de-btn-fav-sel', pByNum[tNum].btns) || {}).className = 'de-btn-fav';
}
}
function toggleFavorites(post, btn) {
var h = aib.host,
b = brd,
tNum = post.num;
if(!btn) {
return;
}
readFavorites();
if(Favor[h] && Favor[h][b] && Favor[h][b][tNum]) {
removeFavorites(h, b, tNum);
saveFavorites();
return;
}
if(!Favor[h]) {
Favor[h] = {};
}
if(!Favor[h][b]) {
Favor[h][b] = {};
}
Favor[h][b][tNum] = {
'cnt': post.thr.pcount,
'txt': post.title,
'url': aib.getThrdUrl(brd, tNum)
};
btn.className = 'de-btn-fav-sel';
saveFavorites();
}
function readViewedPosts() {
if(Cfg['markViewed']) {
var data = sessionStorage['de-viewed'];
if(data) {
data.split(',').forEach(function(pNum) {
var post = pByNum[pNum];
if(post) {
post.el.classList.add('de-viewed');
post.viewed = true;
}
});
}
}
}
//============================================================================================================
// MAIN PANEL
//============================================================================================================
function pButton(id, href, hasHotkey) {
return '
';
}
function addPanel() {
var panel, evtObject, imgLen = getImages(dForm).length;
(pr && pr.pArea[0] || dForm).insertAdjacentHTML('beforebegin',
'' +
'
' +
'
' +
(Cfg['disabled'] ? '' :
'
' +
'
' +
'
'
) +
'
'
);
panel = $id('de-panel');
evtObject = {
attach: false,
odelay: 0,
panel: panel,
handleEvent: function(e) {
switch(e.type) {
case 'click':
switch(e.target.id) {
case 'de-btn-logo':
if(Cfg['expandPanel']) {
this.panel.lastChild.style.display = 'none';
this.attach = false;
} else {
this.attach = true;
}
toggleCfg('expandPanel');
return;
case 'de-btn-settings': this.attach = toggleContent('cfg', false); break;
case 'de-btn-hidden': this.attach = toggleContent('hid', false); break;
case 'de-btn-favor': this.attach = toggleContent('fav', false); break;
case 'de-btn-refresh': window.location.reload(); break;
case 'de-btn-goup': scrollTo(0, 0); break;
case 'de-btn-godown': scrollTo(0, doc.body.scrollHeight || doc.body.offsetHeight); break;
case 'de-btn-expimg':
isExpImg = !isExpImg;
$del($c('de-img-center', doc));
for(var post = firstThr.op; post; post = post.next) {
post.toggleImages(isExpImg);
}
break;
case 'de-btn-preimg':
isPreImg = !isPreImg;
preloadImages(null);
break;
case 'de-btn-maskimg':
toggleCfg('maskImgs');
updateCSS();
break;
case 'de-btn-upd-on':
case 'de-btn-upd-off':
case 'de-btn-upd-warn':
if(updater.enabled) {
updater.disable();
} else {
updater.enable();
}
break;
case 'de-btn-audio-on':
case 'de-btn-audio-off':
if(updater.toggleAudio(0)) {
updater.enable();
e.target.id = 'de-btn-audio-on';
} else {
e.target.id = 'de-btn-audio-off';
}
$del($c('de-menu', doc));
break;
case 'de-btn-imgload':
if($id('de-alert-imgload')) {
break;
}
if(Images_.preloading) {
$alert(Lng.loading[lang], 'imgload', true);
Images_.afterpreload = loadDocFiles.bind(null, true);
Images_.progressId = 'imgload';
} else {
loadDocFiles(true);
}
break;
case 'de-btn-enable':
toggleCfg('disabled');
window.location.reload();
break;
default: return;
}
$pd(e);
return;
case 'mouseover':
if(!Cfg['expandPanel']) {
clearTimeout(this.odelay);
this.panel.lastChild.style.display = '';
}
switch(e.target.id) {
case 'de-btn-settings': KeyEditListener.setTitle(e.target, 10); break;
case 'de-btn-hidden': KeyEditListener.setTitle(e.target, 7); break;
case 'de-btn-favor': KeyEditListener.setTitle(e.target, 6); break;
case 'de-btn-goback': KeyEditListener.setTitle(e.target, 4); break;
case 'de-btn-gonext': KeyEditListener.setTitle(e.target, 17); break;
case 'de-btn-maskimg': KeyEditListener.setTitle(e.target, 9); break;
case 'de-btn-refresh':
if(TNum) {
return;
}
case 'de-btn-audio-off': addMenu(e);
}
return;
default: // mouseout
if(!Cfg['expandPanel'] && !this.attach) {
this.odelay = setTimeout(function(obj) {
obj.panel.lastChild.style.display = 'none';
obj.attach = false;
}, 500, this);
}
switch(e.target.id) {
case 'de-btn-refresh':
case 'de-btn-audio-off': removeMenu(e); break;
}
}
}
};
panel.addEventListener('click', evtObject, true);
panel.addEventListener('mouseover', evtObject, false);
panel.addEventListener('mouseout', evtObject, false);
}
function toggleContent(name, isUpd) {
if(liteMode) {
return false;
}
var remove, el = $c('de-content', doc),
id = 'de-content-' + name;
if(!el) {
return false;
}
if(isUpd && el.id !== id) {
return true;
}
remove = !isUpd && el.id === id;
if(el.hasChildNodes() && Cfg['animation']) {
nav.animEvent(el, function(node) {
showContent(node, id, name, remove);
id = name = remove = null;
});
el.className = 'de-content de-cfg-close';
return !remove;
} else {
showContent(el, id, name, remove);
return !remove;
}
}
function addContentBlock(parent, title) {
return parent.appendChild($New('div', {'class': 'de-content-block'}, [
$new('input', {'type': 'checkbox'}, {'click': function() {
var el, res = this.checked, i = 0, els = $Q('.de-entry > div > input', this.parentNode);
for(; el = els[i++];) {
el.checked = res;
}
}}),
$new('b', {'text': title}, null)
]));
}
function showContent(cont, id, name, remove) {
var h, b, tNum, i, els, post, cln, block, temp, cfgTabId;
if(name === 'cfg' && !remove && (temp = $q('.de-cfg-tab-back[selected="true"] > .de-cfg-tab', cont))) {
cfgTabId = temp.getAttribute('info');
}
cont.innerHTML = cont.style.backgroundColor = '';
if(remove) {
cont.removeAttribute('id');
return;
}
cont.id = id;
if(name === 'cfg') {
addSettings(cont, cfgTabId);
} else if(Cfg['attachPanel']) {
cont.style.backgroundColor = $getStyle(doc.body, 'background-color');
}
if(name === 'hid') {
for(i = 0, els = $C('de-post-hid', dForm); post = els[i++];) {
if(post.isOp) {
continue;
}
(cln = post.cloneNode(true)).removeAttribute('id');
cln.style.display = '';
if(cln.classList.contains(aib.cRPost)) {
cln.classList.add('de-cloned-post');
} else {
cln.className = aib.cReply + ' de-cloned-post';
}
cln.post = Object.create(cln.clone = post.post);
cln.post.el = cln;
cln.btn = $q('.de-btn-hide, .de-btn-hide-user', cln);
cln.btn.parentNode.className = 'de-ppanel';
cln.btn.onclick = function() {
this.hideContent(this.hidden = !this.hidden);
}.bind(cln);
(block || (block = cont.appendChild(
$add('' + Lng.hiddenPosts[lang] + ':
')
))).appendChild($New('div', {'class': 'de-entry'}, [cln]));
}
if(block) {
$append(cont, [
$btn(Lng.expandAll[lang], '', function() {
$each($Q('.de-cloned-post', this.parentNode), function(el) {
var post = el.post;
post.hideContent(post.hidden = !post.hidden);
});
this.value = this.value === Lng.undo[lang] ? Lng.expandAll[lang] : Lng.undo[lang];
}),
$btn(Lng.save[lang], '', function() {
$each($Q('.de-cloned-post', this.parentNode), function(date, el) {
if(!el.post.hidden) {
el.clone.setUserVisib(false, date, true);
}
}.bind(null, Date.now()));
saveUserPosts();
})
]);
} else {
cont.appendChild($new('b', {'text': Lng.noHidPosts[lang]}, null));
}
$append(cont, [
doc.createElement('hr'),
$new('b', {'text': ($isEmpty(hThr) ? Lng.noHidThrds[lang] : Lng.hiddenThrds[lang] + ':')}, null)
]);
for(b in hThr) {
if(!$isEmpty(hThr[b])) {
block = addContentBlock(cont, '/' + b);
for(tNum in hThr[b]) {
block.insertAdjacentHTML('beforeend', '');
}
}
}
$append(cont, [
doc.createElement('hr'),
addEditButton('hidden', hThr, true, function(data) {
hThr = data;
if(!(brd in hThr)) {
hThr[brd] = {};
}
firstThr.updateHidden(hThr[brd]);
saveHiddenThreads(true);
localStorage['__de-threads'] = JSON.stringify(hThr);
localStorage.removeItem('__de-threads');
}),
$btn(Lng.clear[lang], Lng.clrDeleted[lang], function() {
$each($Q('.de-entry[info]', this.parentNode), function(el) {
var arr = el.getAttribute('info').split(';');
ajaxLoad(aib.getThrdUrl(arr[0], arr[1]), false, null, function(eCode, eMsg, xhr) {
if(eCode === 404) {
delete hThr[this[0]][this[1]];
saveHiddenThreads(true);
}
}.bind(arr));
});
}),
$btn(Lng.remove[lang], Lng.clrSelected[lang], function() {
$each($Q('.de-entry[info]', this.parentNode), function(date, el) {
var post, arr = el.getAttribute('info').split(';');
if($t('input', el).checked) {
if(arr[1] in pByNum) {
pByNum[arr[1]].setUserVisib(false, date, true);
} else {
localStorage['__de-post'] = JSON.stringify({
'brd': arr[0],
'date': date,
'isOp': true,
'num': arr[1],
'hide': false
});
localStorage.removeItem('__de-post');
}
delete hThr[arr[0]][arr[1]];
}
}.bind(null, Date.now()));
saveHiddenThreads(true);
})
]);
}
if(name === 'fav') {
readFavorites();
for(h in Favor) {
for(b in Favor[h]) {
block = addContentBlock(cont, h + '/' + b);
for(tNum in Favor[h][b]) {
i = Favor[h][b][tNum];
if(!i['url'].startsWith('http')) {
i['url'] = (h === aib.host ? aib.prot + '//' : 'http://') + h + i['url'];
}
block.appendChild($New('div', {'class': 'de-entry', 'info': h + ';' + b + ';' + tNum}, [
$New('div', {'class': aib.cReply}, [
$add(''),
$new('span', {'class': 'de-btn-expthr'}, {'click': loadFavorThread}),
$add('№' + tNum + ''),
$add(' - ' + i['txt'] + ''),
$add(''),
$add('[' +
i['cnt'] + ']')
])
]));
}
}
}
cont.insertAdjacentHTML('afterbegin', '' + (Lng[block ? 'favThrds' : 'noFavThrds'][lang]) + '');
$append(cont, [
doc.createElement('hr'),
addEditButton('favor', Favor, true, function(data) {
Favor = data;
setStored('DESU_Favorites', JSON.stringify(Favor));
toggleContent('fav', true);
}),
$btn(Lng.info[lang], Lng.infoCount[lang], function() {
$each($C('de-entry', doc), function(el) {
var c, arr = el.getAttribute('info').split(';'),
f = Favor[arr[0]][arr[1]][arr[2]];
if(arr[0] !== aib.host) {
return;
}
c = $c('de-fav-inf-posts', el).firstElementChild;
c.className = 'de-wait';
c.textContent = '';
ajaxLoad(aib.getThrdUrl(arr[1], arr[2]), true, function(form, xhr) {
var cnt = aib.getPosts(form).length + 1;
c.textContent = cnt;
if(cnt > f.cnt) {
c.className = 'de-fav-inf-new';
f.cnt = cnt;
setStored('DESU_Favorites', JSON.stringify(Favor));
} else {
c.className = 'de-fav-inf-old';
}
c = f = null;
}, function(eCode, eMsg, xhr) {
c.textContent = getErrorMessage(eCode, eMsg);
c.className = 'de-fav-inf-old';
c = null;
});
});
}),
$btn(Lng.page[lang], Lng.infoPage[lang], function() {
var i = 6,
loaded = 0;
$alert(Lng.loading[lang], 'load-pages', true);
while(i--) {
ajaxLoad(aib.getPageUrl(brd, i), true, function(idx, form, xhr) {
for(var arr, el, len = this.length, i = 0; i < len; ++i) {
arr = this[i].getAttribute('info').split(';');
if(arr[0] === aib.host && arr[1] === brd) {
el = $c('de-fav-inf-page', this[i]);
if((new RegExp('(?:№|No.|>)\\s*' + arr[2] + '\\s*<'))
.test(form.innerHTML))
{
el.innerHTML = '@' + idx;
} else if(loaded === 5 && !el.textContent.contains('@')) {
el.innerHTML = '@?';
}
}
}
if(loaded === 5) {
closeAlert($id('de-alert-load-pages'));
}
loaded++;
}.bind($C('de-entry', doc), i), function(eCode, eMsg, xhr) {
if(loaded === 5) {
closeAlert($id('de-alert-load-pages'));
}
loaded++;
});
}
}),
$btn(Lng.clear[lang], Lng.clrDeleted[lang], function() {
$each($C('de-entry', doc), function(el) {
var arr = el.getAttribute('info').split(';');
ajaxLoad(Favor[arr[0]][arr[1]][arr[2]]['url'], false, null, function(eCode, eMsg, xhr) {
if(eCode === 404) {
removeFavorites(arr[0], arr[1], arr[2]);
saveFavorites();
arr = null;
}
});
});
}),
$btn(Lng.remove[lang], Lng.clrSelected[lang], function() {
$each($C('de-entry', doc), function(el) {
var arr = el.getAttribute('info').split(';');
if($t('input', el).checked) {
removeFavorites(arr[0], arr[1], arr[2]);
}
});
saveFavorites();
})
]);
}
if(Cfg['animation']) {
cont.className = 'de-content de-cfg-open';
}
}
//============================================================================================================
// SETTINGS WINDOW
//============================================================================================================
function fixSettings() {
function toggleBox(state, arr) {
var i = arr.length,
nState = !state;
while(i--) {
($q(arr[i], doc) || {}).disabled = nState;
}
}
toggleBox(Cfg['ajaxUpdThr'], [
'input[info="noErrInTitle"]',
'input[info="favIcoBlink"]',
'input[info="markNewPosts"]',
'input[info="desktNotif"]'
]);
toggleBox(Cfg['expandImgs'], ['input[info="resizeImgs"]']);
toggleBox(Cfg['preLoadImgs'], ['input[info="findImgFile"]']);
toggleBox(Cfg['openImgs'], ['input[info="openGIFs"]']);
toggleBox(Cfg['linksNavig'], [
'input[info="linksOver"]',
'input[info="linksOut"]',
'input[info="markViewed"]',
'input[info="strikeHidd"]',
'input[info="noNavigHidd"]'
]);
toggleBox(Cfg['addYouTube'] && Cfg['addYouTube'] !== 4, [
'select[info="YTubeType"]', 'input[info="YTubeHD"]', 'input[info="addVimeo"]'
]);
toggleBox(Cfg['addYouTube'], [
'input[info="YTubeWidth"]', 'input[info="YTubeHeigh"]', 'input[info="YTubeTitles"]'
]);
toggleBox(Cfg['ajaxReply'] === 2, [
'input[info="postSameImg"]', 'input[info="removeEXIF"]', 'input[info="removeFName"]'
]);
toggleBox(Cfg['addTextBtns'], ['input[info="txtBtnsLoc"]']);
toggleBox(Cfg['updScript'], ['select[info="scrUpdIntrv"]']);
toggleBox(Cfg['keybNavig'], ['input[info="loadPages"]']);
}
function lBox(id, isBlock, Fn) {
var el = $new('input', {'info': id, 'type': 'checkbox'}, {'click': function() {
toggleCfg(this.getAttribute('info'));
fixSettings();
if(Fn) {
Fn(this);
}
}});
el.checked = Cfg[id];
return $New('label', isBlock ? {'class': 'de-block'} : null, [el, $txt(' ' + Lng.cfg[id][lang])]);
}
function inpTxt(id, size, Fn) {
return $new('input', {'info': id, 'type': 'text', 'size': size, 'value': Cfg[id]}, {
'keyup': Fn ? Fn : function() {
saveCfg(this.getAttribute('info'), this.value);
}
});
}
function optSel(id, isBlock, Fn) {
for(var i = 0, x = Lng.cfg[id], len = x.sel[lang].length, el, opt = ''; i < len; i++) {
opt += '';
}
el = $add('');
el.addEventListener('change', Fn || function() {
saveCfg(this.getAttribute('info'), this.selectedIndex);
fixSettings();
}, false);
el.selectedIndex = Cfg[id];
return $New('label', isBlock ? {'class': 'de-block'} : null, [el, $txt(' ' + x.txt[lang])]);
}
function cfgTab(name) {
return $New('div', {'class': aib.cReply + ' de-cfg-tab-back', 'selected': false}, [$new('div', {
'class': 'de-cfg-tab',
'text': Lng.cfgTab[name][lang],
'info': name}, {
'click': function() {
var el, id, pN = this.parentNode;
if(pN.getAttribute('selected') === 'true') {
return;
}
if(el = $c('de-cfg-body', doc)) {
el.className = 'de-cfg-unvis';
$q('.de-cfg-tab-back[selected="true"]', doc).setAttribute('selected', false);
}
pN.setAttribute('selected', true);
if(!(el = $id('de-cfg-' + (id = this.getAttribute('info'))))) {
$after($id('de-cfg-bar'), el =
id === 'filters' ? getCfgFilters() :
id === 'posts' ? getCfgPosts() :
id === 'images' ? getCfgImages() :
id === 'links' ? getCfgLinks() :
id === 'form' ? getCfgForm() :
id === 'common' ? getCfgCommon() :
getCfgInfo()
);
if(id === 'filters') {
updRowMeter.call($id('de-spell-edit'));
}
}
el.className = 'de-cfg-body';
if(id === 'filters') {
$id('de-spell-edit').value = spells.list;
}
fixSettings();
}
})]);
}
function updRowMeter() {
var str, top = this.scrollTop,
el = this.parentNode.previousSibling.firstChild,
num = el.numLines || 1,
i = 15;
if(num - i < ((top / 12) | 0 + 1)) {
str = '';
while(i--) {
str += num++ + '
';
}
el.insertAdjacentHTML('beforeend', str);
el.numLines = num;
}
el.scrollTop = top;
}
function getCfgFilters() {
return $New('div', {'class': 'de-cfg-unvis', 'id': 'de-cfg-filters'}, [
lBox('hideBySpell', false, toggleSpells),
$New('div', {'id': 'de-spell-panel'}, [
$new('a', {
'id': 'de-btn-addspell',
'text': Lng.add[lang],
'href': '#',
'class': 'de-abtn'}, {
'click': $pd,
'mouseover': addMenu,
'mouseout': removeMenu
}),
$new('a', {'text': Lng.apply[lang], 'href': '#', 'class': 'de-abtn'}, {'click': function(e) {
$pd(e);
saveCfg('hideBySpell', 1);
$q('input[info="hideBySpell"]', doc).checked = true;
toggleSpells();
}}),
$new('a', {'text': Lng.clear[lang], 'href': '#', 'class': 'de-abtn'}, {'click': function(e) {
$pd(e);
$id('de-spell-edit').value = '';
toggleSpells();
}}),
$add('[?]')
]),
$New('div', {'id': 'de-spell-div'}, [
$add(''),
$New('div', null, [$new('textarea', {'id': 'de-spell-edit', 'wrap': 'off'}, {
'keydown': updRowMeter,
'scroll': updRowMeter
})])
]),
lBox('sortSpells', true, function() {
if (Cfg['sortSpells']) {
toggleSpells();
}
}),
lBox('menuHiddBtn', true, null),
lBox('hideRefPsts', true, null),
lBox('delHiddPost', true, function() {
$each($C('de-post-hid', dForm), function(el) {
var wrap = el.post.wrap,
hide = !wrap.classList.contains('de-hidden');
if(hide) {
wrap.insertAdjacentHTML('beforebegin',
'');
} else {
$del(wrap.previousSibling);
}
wrap.classList.toggle('de-hidden');
});
updateCSS();
})
]);
}
function getCfgPosts() {
return $New('div', {'class': 'de-cfg-unvis', 'id': 'de-cfg-posts'}, [
lBox('ajaxUpdThr', false, TNum ? function() {
if(Cfg['ajaxUpdThr']) {
updater.enable();
} else {
updater.disable();
}
} : null),
$New('label', null, [
inpTxt('updThrDelay', 4, null),
$txt(Lng.cfg['updThrDelay'][lang])
]),
$New('div', {'class': 'de-cfg-depend'}, [
lBox('noErrInTitle', true, null),
lBox('favIcoBlink', true, null),
lBox('markNewPosts', true, function() {
firstThr.clearPostsMarks();
}),
$if('Notification' in window, lBox('desktNotif', true, function() {
if(Cfg['desktNotif']) {
Notification.requestPermission();
}
}))
]),
optSel('expandPosts', true, null),
optSel('postBtnsCSS', true, null),
lBox('noSpoilers', true, updateCSS),
lBox('noPostNames', true, updateCSS),
lBox('noPostScrl', true, updateCSS),
$New('div', null, [
lBox('correctTime', false, dateTime.toggleSettings),
$add('[?]')
]),
$New('div', {'class': 'de-cfg-depend'}, [
$New('div', null, [
inpTxt('timeOffset', 3, null),
$txt(Lng.cfg['timeOffset'][lang])
]),
$New('div', null, [
inpTxt('timePattern', 30, null),
$txt(Lng.cfg['timePattern'][lang])
]),
$New('div', null, [
inpTxt('timeRPattern', 30, null),
$txt(Lng.cfg['timeRPattern'][lang])
])
])
]);
}
function getCfgImages() {
return $New('div', {'class': 'de-cfg-unvis', 'id': 'de-cfg-images'}, [
optSel('expandImgs', true, null),
$New('div', {'style': 'padding-left: 25px;'}, [ lBox('resizeImgs', false, null)]),
$if(!nav.noBlob && !nav.Opera, lBox('preLoadImgs', true, null)),
$if(!nav.noBlob && !nav.Opera, $New('div', {'class': 'de-cfg-depend'}, [
lBox('findImgFile', true, null)
])),
lBox('openImgs', true, null),
$New('div', {'class': 'de-cfg-depend'}, [ lBox('openGIFs', false, null)]),
lBox('imgSrcBtns', true, null)
]);
}
function getCfgLinks() {
return $New('div', {'class': 'de-cfg-unvis', 'id': 'de-cfg-links'}, [
optSel('linksNavig', true, null),
$New('div', {'class': 'de-cfg-depend'}, [
$New('div', null, [
inpTxt('linksOver', 6, function() {
saveCfg('linksOver', +this.value | 0);
}),
$txt(Lng.cfg['linksOver'][lang])
]),
$New('div', null, [
inpTxt('linksOut', 6, function() {
saveCfg('linksOut', +this.value | 0);
}),
$txt(Lng.cfg['linksOut'][lang])
]),
lBox('markViewed', true, null),
lBox('strikeHidd', true, null),
lBox('noNavigHidd', true, null)
]),
lBox('crossLinks', true, null),
lBox('insertNum', true, null),
lBox('addMP3', true, null),
lBox('addImgs', true, null),
optSel('addYouTube', true, null),
$New('div', {'class': 'de-cfg-depend'}, [
$New('div', null, [
optSel('YTubeType', false, null),
inpTxt('YTubeWidth', 6, null),
$txt('×'),
inpTxt('YTubeHeigh', 6, null),
$txt(' '),
lBox('YTubeHD', false, null)
]),
$if(!nav.oldOpera || nav.isGM, lBox('YTubeTitles', false, null)),
lBox('addVimeo', true, null)
])
]);
}
function getCfgForm() {
return $New('div', {'class': 'de-cfg-unvis', 'id': 'de-cfg-form'}, [
optSel('ajaxReply', true, null),
$if(pr.form && !nav.noBlob, $New('div', {'class': 'de-cfg-depend'}, [
lBox('postSameImg', true, null),
lBox('removeEXIF', true, null),
lBox('removeFName', true, null)
])),
$if(pr.form, optSel('addPostForm', true, null)),
$if(pr.form, lBox('scrAfterRep', true, null)),
lBox('favOnReply', true, null),
$if(pr.mail, $New('div', null, [
lBox('addSageBtn', false, null),
lBox('saveSage', false, null)
])),
$if(pr.subj, lBox('warnSubjTrip', false, null)),
$if(pr.capTr, optSel('captchaLang', true, null)),
$if(pr.txta, $New('div', null, [
optSel('addTextBtns', false, function() {
saveCfg('addTextBtns', this.selectedIndex);
pr.addTextPanel();
}),
lBox('txtBtnsLoc', false, pr.addTextPanel.bind(pr))
])),
$if(pr.passw, $New('div', null, [
inpTxt('passwValue', 20, PostForm.setUserPassw),
$txt(Lng.cfg['userPassw'][lang]),
$btn(Lng.change[lang], '', function() {
$q('input[info="passwValue"]', doc).value = Math.round(Math.random() * 1e15).toString(32);
PostForm.setUserPassw();
})
])),
$if(pr.name, $New('div', null, [
inpTxt('nameValue', 20, PostForm.setUserName),
lBox('userName', false, PostForm.setUserName)
])),
$if(pr.txta, $New('div', null, [
inpTxt('signatValue', 20, null),
lBox('userSignat', false, null)
])),
$New('div', null, [
$txt(Lng.dontShow[lang]),
lBox('noBoardRule', false, updateCSS),
$if(pr.gothr, lBox('noGoto', false, function() {
$disp(pr.gothr);
})),
$if(pr.passw, lBox('noPassword', false, function() {
$disp(pr.passw.parentNode.parentNode);
}))
])
]);
}
function getCfgCommon() {
return $New('div', {'class': 'de-cfg-unvis', 'id': 'de-cfg-common'}, [
optSel('scriptStyle', true, function() {
saveCfg('scriptStyle', this.selectedIndex);
$id('de-main').lang = getThemeLang();
}),
$New('div', null, [
lBox('userCSS', false, updateCSS),
addEditButton('css', Cfg['userCSSTxt'], false, function() {
saveCfg('userCSSTxt', this.value);
updateCSS();
toggleContent('cfg', true);
})
]),
lBox('attachPanel', true, function() {
toggleContent('cfg', false);
updateCSS();
}),
lBox('panelCounter', true, updateCSS),
lBox('rePageTitle', true, null),
$if(nav.Anim, lBox('animation', true, null)),
lBox('closePopups', true, null),
$New('div', null, [
lBox('keybNavig', false, function() {
if(Cfg['keybNavig']) {
if(keyNav) {
keyNav.enable();
} else {
keyNav = new KeyNavigation();
}
} else if(keyNav) {
keyNav.disable();
}
}),
$btn(Lng.edit[lang], '', function(e) {
$pd(e);
if($id('de-alert-edit-keybnavig')) {
return;
}
var aEl, evtListener, keys = KeyNavigation.readKeys(),
temp = KeyEditListener.getEditMarkup(keys);
$alert(temp[1], 'edit-keybnavig', false);
aEl = $id('de-alert-edit-keybnavig');
evtListener = new KeyEditListener(aEl, keys, temp[0]);
aEl.addEventListener('focus', evtListener, true);
aEl.addEventListener('blur', evtListener, true);
aEl.addEventListener('click', evtListener, true);
aEl.addEventListener('keydown', evtListener, true);
aEl.addEventListener('keyup', evtListener, true);
})
]),
$New('div', {'class': 'de-cfg-depend'}, [
inpTxt('loadPages', 4, null),
$txt(Lng.cfg['loadPages'][lang])
]),
$if(!nav.Opera || nav.isGM, $New('div', null, [
lBox('updScript', true, null),
$New('div', {'class': 'de-cfg-depend'}, [
optSel('scrUpdIntrv', false, null),
$btn(Lng.checkNow[lang], '', function() {
var el = $id('de-cfg-updresult');
el.innerHTML = '' + Lng.checking[lang] + '';
checkForUpdates(true, function(html) {
el.innerHTML = html;
});
})
]),
$new('div', {'id': 'de-cfg-updresult'}, null)
]))
]);
}
function getCfgInfo() {
var getHiddenThrCount = function () {
var b, tNum, count = 0;
for(b in hThr) {
for(tNum in hThr[b]) {
count++;
}
}
return count;
}
return $New('div', {'class': 'de-cfg-unvis', 'id': 'de-cfg-info'}, [
$add(''),
$add('' +
Lng.thrViewed[lang] + Cfg['stats']['view'] + '
' +
Lng.thrCreated[lang] + Cfg['stats']['op'] + '
' +
Lng.thrHidden[lang] + getHiddenThrCount() + '
' +
Lng.postsSent[lang] + Cfg['stats']['reply'] + '
' +
'
' + timeLog.join('
') + '
'),
$btn(Lng.debug[lang], Lng.infoDebug[lang], function() {
$alert(Lng.infoDebug[lang] +
':', 'help-debug', false);
$id('de-debug-info').value = JSON.stringify({
'version': version,
'location': String(window.location),
'nav': nav,
'cfg': Cfg,
'sSpells': spells.list.split('\n'),
'oSpells': sessionStorage['de-spells-' + brd + TNum],
'perf': timeLog
}, function(key, value) {
if(key in defaultCfg) {
if(value === defaultCfg[key] || key === 'nameValue' || key === 'passwValue' ||
key === 'signatValue')
{
return void 0;
}
}
return key === 'stats' ? void 0 : value;
}, '\t');
})
]);
}
function addEditButton(name, val, isJSON, Fn) {
return $btn(Lng.edit[lang], Lng.editInTxt[lang], function() {
var ta = $new('textarea', {
'class': 'de-editor',
'value': isJSON ? JSON.stringify(val, null, '\t') : val
}, null);
$alert('', 'edit-' + name, false);
$append($c('de-alert-msg', $id('de-alert-edit-' + name)), [
$txt(Lng.editor[name][lang]),
ta,
$btn(Lng.save[lang], Lng.saveChanges[lang], isJSON ? function(fun, aName) {
var data;
try {
data = JSON.parse(this.value.trim().replace(/[\n\r\t]/g, '') || '{}');
} finally {
if(data) {
fun(data);
closeAlert($id('de-alert-edit-' + aName));
closeAlert($id('de-alert-err-invaliddata'));
} else {
$alert(Lng.invalidData[lang], 'err-invaliddata', false);
}
}
}.bind(ta, Fn, name) : Fn.bind(ta))
]);
});
}
function addSettings(Set, id) {
Set.appendChild($New('div', {'class': aib.cReply}, [
$new('div', {'id': 'de-cfg-head', 'text': 'Dollchan Extension Tools'}, null),
$New('div', {'id': 'de-cfg-bar'}, [
cfgTab('filters'),
cfgTab('posts'),
cfgTab('images'),
cfgTab('links'),
$if(pr.form || pr.oeForm, cfgTab('form')),
cfgTab('common'),
cfgTab('info')
]),
$New('div', {'id': 'de-cfg-btns'}, [
optSel('language', false, function() {
saveCfg('language', lang = this.selectedIndex);
$del($id('de-main'));
$del($id('de-css'));
$del($id('de-css-dynamic'));
scriptCSS();
addPanel();
toggleContent('cfg', false);
}),
$New('div', {'style': 'float: right;'}, [
addEditButton('cfg', Cfg, true, function(data) {
saveComCfg(aib.dm, data);
}),
$if(nav.isGlobal, $btn(Lng.load[lang], Lng.loadGlobal[lang], function() {
if(('global' in comCfg) && !$isEmpty(comCfg['global'])) {
saveComCfg(aib.dm, null);
window.location.reload();
} else {
$alert(Lng.noGlobalCfg[lang], 'err-noglobalcfg', false);
}
})),
$if(nav.isGlobal, $btn(Lng.save[lang], Lng.saveGlobal[lang], function() {
var i, obj = {},
com = comCfg[aib.dm];
for(i in com) {
if(com[i] !== defaultCfg[i] && i !== 'stats') {
obj[i] = com[i];
}
}
saveComCfg('global', obj);
toggleContent('cfg', true);
})),
$btn(Lng.reset[lang], Lng.resetCfg[lang], function() {
if(confirm(Lng.conReset[lang])) {
delStored('DESU_Config');
delStored('DESU_Favorites');
delStored('DESU_Threads');
delStored('DESU_keys');
window.location.reload();
}
})
]),
$new('div', {'style': 'clear: both;'}, null)
])
]));
$q('.de-cfg-tab[info="' + (id || 'filters') + '"]', Set).click();
}
//============================================================================================================
// MENUS & POPUPS
//============================================================================================================
function closeAlert(el) {
if(el) {
el.closeTimeout = null;
if(Cfg['animation']) {
nav.animEvent(el, function(node) {
var p = node && node.parentNode;
if(p) {
p.removeChild(node);
}
});
el.classList.add('de-close');
} else {
$del(el);
}
}
}
function $alert(txt, id, wait) {
var node, el = $id('de-alert-' + id),
cBtn = 'de-alert-btn' + (wait ? ' de-wait' : ''),
tBtn = wait ? '' : '\u2716 ';
if(el) {
$t('div', el).innerHTML = txt.trim();
node = $t('span', el);
node.className = cBtn;
node.textContent = tBtn;
clearTimeout(el.closeTimeout);
if(!wait && Cfg['animation']) {
nav.animEvent(el, function(node) {
node.classList.remove('de-blink');
});
el.classList.add('de-blink');
}
} else {
el = $id('de-alert').appendChild($New('div', {'class': aib.cReply, 'id': 'de-alert-' + id}, [
$new('span', {'class': cBtn, 'text': tBtn}, {'click': function() {
closeAlert(this.parentNode);
}}),
$add('' + txt.trim() + '
')
]));
if(Cfg['animation']) {
nav.animEvent(el, function(node) {
node.classList.remove('de-open');
});
el.classList.add('de-open');
}
}
if(Cfg['closePopups'] && !wait && !id.contains('help') && !id.contains('edit')) {
el.closeTimeout = setTimeout(closeAlert, 4e3, el);
}
}
function showMenu(el, html, inPanel, onclick) {
var y, pos, menu, cr = el.getBoundingClientRect();
if(Cfg['attachPanel'] && inPanel) {
pos = 'fixed';
y = 'bottom: 25';
} else {
pos = 'absolute';
y = 'top: ' + (window.pageYOffset + cr.bottom);
}
doc.body.insertAdjacentHTML('beforeend', '');
menu = doc.body.lastChild;
menu.addEventListener('mouseover', function(e) {
clearTimeout(e.currentTarget.odelay);
}, true);
menu.addEventListener('mouseout', removeMenu, true);
menu.addEventListener('click', function(e) {
var el = e.target;
if(el.className === 'de-menu-item') {
this(el);
do {
el = el.parentElement;
} while (!el.classList.contains('de-menu'));
$del(el);
}
}.bind(onclick), false);
}
function addMenu(e) {
e.target.odelay = setTimeout(function(el) {
switch(el.id) {
case 'de-btn-addspell': addSpellMenu(el); return;
case 'de-btn-refresh': addAjaxPagesMenu(el); return;
case 'de-btn-audio-off': addAudioNotifMenu(el); return;
}
}, Cfg['linksOver'], e.target);
}
function removeMenu(e) {
var el, rt = e.relatedTarget;
clearTimeout(e.target.odelay);
if(!rt || !nav.matchesSelector(rt, '.de-menu, .de-menu > div, .de-menu-item')) {
if(el = $c('de-menu', doc)) {
el.odelay = setTimeout($del, 75, el);
}
}
}
function addSpellMenu(el) {
showMenu(el, '' +
'
', false,
function(el) {
var exp = el.textContent,
idx = Spells.names.indexOf(exp.substr(1));
$txtInsert($id('de-spell-edit'), exp + (
TNum && exp !== '#op' && exp !== '#rep' && exp !== '#outrep' ? '[' + brd + ',' + TNum + ']' : ''
) + (Spells.needArg[idx] ? '(' : ''));
});
}
function addAjaxPagesMenu(el) {
showMenu(el, '', true,
function(el) {
loadPages(aProto.indexOf.call(el.parentNode.children, el) + 1);
});
}
function addAudioNotifMenu(el) {
showMenu(el, '', true,
function(el) {
var i = aProto.indexOf.call(el.parentNode.children, el);
updater.enable();
updater.toggleAudio(i === 0 ? 3e4 : i === 1 ? 6e4 : i === 2 ? 12e4 : 3e5);
$id('de-btn-audio-off').id = 'de-btn-audio-on';
});
}
//============================================================================================================
// KEYBOARD NAVIGATION
//============================================================================================================
function KeyNavigation() {
var keys = KeyNavigation.readKeys();
this.cPost = null;
this.enabled = true;
this.lastPage = pageNum;
this.lastPageOffset = 0;
this.gKeys = keys[2];
this.ntKeys = keys[3];
this.tKeys = keys[4];
doc.addEventListener('keydown', this, true);
}
KeyNavigation.version = 4;
KeyNavigation.readKeys = function() {
var tKeys, keys, str = getStored('DESU_keys');
if(!str) {
return KeyNavigation.getDefaultKeys();
}
try {
keys = JSON.parse(str);
} finally {
if(!keys) {
return KeyNavigation.getDefaultKeys();
}
if(keys[0] !== KeyNavigation.version) {
tKeys = KeyNavigation.getDefaultKeys();
switch(keys[0]) {
case 1:
keys[2][11] = tKeys[2][11];
keys[4] = tKeys[4];
case 2:
keys[2][12] = tKeys[2][12];
keys[2][13] = tKeys[2][13];
keys[2][14] = tKeys[2][14];
keys[2][15] = tKeys[2][15];
keys[2][16] = tKeys[2][16];
case 3:
keys[2][17] = keys[3][3];
keys[3][3] = keys[3].splice(4, 1)[0];
}
keys[0] = KeyNavigation.version;
setStored('DESU_keys', JSON.stringify(keys));
}
if(keys[1] ^ !!nav.Firefox) {
var mapFunc = nav.Firefox ? function mapFuncFF(key) {
switch(key) {
case 189: return 173;
case 187: return 61;
case 186: return 59;
default: return key;
}
} : function mapFuncNonFF(key) {
switch(key) {
case 173: return 189;
case 61: return 187;
case 59: return 186;
default: return key;
}
};
keys[1] = !!nav.Firefox;
keys[2] = keys[2].map(mapFunc);
keys[3] = keys[3].map(mapFunc);
setStored('DESU_keys', JSON.stringify(keys));
}
return keys;
}
};
KeyNavigation.getDefaultKeys = function() {
var isFirefox = !!nav.Firefox;
var globKeys = [
/* One post/thread above */ 0x004B /* = K */,
/* One post/thread below */ 0x004A /* = J */,
/* Reply or create thread */ 0x0052 /* = R */,
/* Hide selected thread/post */ 0x0048 /* = H */,
/* Open previous page/picture */ 0x1025 /* = Ctrl+Left */,
/* Send post (txt) */ 0xC00D /* = Alt+Enter */,
/* Open/close favorites posts */ 0x4046 /* = Alt+F */,
/* Open/close hidden posts */ 0x4048 /* = Alt+H */,
/* Open/close panel */ 0x0050 /* = P */,
/* Mask/unmask images */ 0x0042 /* = B */,
/* Open/close settings */ 0x4053 /* = Alt+S */,
/* Expand current image */ 0x0049 /* = I */,
/* Bold text */ 0xC042 /* = Alt+B */,
/* Italic text */ 0xC049 /* = Alt+I */,
/* Strike text */ 0xC054 /* = Alt+T */,
/* Spoiler text */ 0xC050 /* = Alt+P */,
/* Code text */ 0xC043 /* = Alt+C */,
/* Open next page/picture */ 0x1027 /* = Ctrl+Right */
];
var nonThrKeys = [
/* One post above */ 0x004D /* = M */,
/* One post below */ 0x004E /* = N */,
/* Open thread */ 0x0056 /* = V */,
/* Expand thread */ 0x0045 /* = E */
];
var thrKeys = [
/* Update thread */ 0x0055 /* = U */
];
return [KeyNavigation.version, isFirefox, globKeys, nonThrKeys, thrKeys];
};
KeyNavigation.prototype = {
paused: false,
clear: function(lastPage) {
this.cPost = null;
this.lastPage = lastPage;
this.lastPageOffset = 0;
},
disable: function() {
if(this.enabled) {
if(this.cPost) {
this.cPost.unselect();
}
doc.removeEventListener('keydown', this, true);
this.enabled = false;
}
},
enable: function() {
if(!this.enabled) {
this.clear(pageNum);
doc.addEventListener('keydown', this, true);
this.enabled = true;
}
},
handleEvent: function(e) {
if(this.paused) {
return;
}
var temp, post, scrollToThread, globIdx, idx, curTh = e.target.tagName,
kc = e.keyCode | (e.ctrlKey ? 0x1000 : 0) | (e.shiftKey ? 0x2000 : 0) |
(e.altKey ? 0x4000 : 0) | (curTh === 'TEXTAREA' ||
(curTh === 'INPUT' && e.target.type === 'text') ? 0x8000 : 0);
if(kc === 0x74 || kc === 0x8074) { // F5
if(TNum) {
return;
}
if(temp = this._fullImage) {
temp.click();
}
loadPages(+Cfg['loadPages']);
} else if(kc === 0x1B) { // ESC
if(temp = this._fullImage) {
temp.click();
return;
}
if(this.cPost) {
this.cPost.unselect();
this.cPost = null;
}
if(TNum) {
firstThr.clearPostsMarks();
}
this.lastPageOffset = 0;
} else if(kc === 0x801B) { // ESC (txt)
e.target.blur();
} else {
globIdx = this.gKeys.indexOf(kc);
switch(globIdx) {
case 2: // Reply or create thread
if(pr.form) {
if(!this.cPost && TNum && Cfg['addPostForm'] === 3) {
this.cPost = firstThr.op;
}
if(this.cPost) {
pr.showQuickReply(this.cPost, this.cPost.num, true);
} else {
pr.showMainReply(Cfg['addPostForm'] === 1, null);
}
}
break;
case 3: // Hide selected thread/post
post = this._getFirstVisPost(false, true) || this._getNextVisPost(null, true, false);
if(post) {
post.toggleUserVisib();
this._scroll(post, false, post.isOp);
}
break;
case 4: // Open previous page/picture
if(this._fullImage) {
$id('de-img-btn-prev').click();
} else if(TNum || pageNum !== aib.firstPage) {
window.location.pathname = aib.getPageUrl(brd, TNum ? 0 : pageNum - 1);
}
break;
case 5: // Send post (txt)
if(e.target !== pr.txta && e.target !== pr.cap) {
return;
}
pr.subm.click();
break;
case 6: // Open/close favorites posts
toggleContent('fav', false);
break;
case 7: // Open/close hidden posts
toggleContent('hid', false);
break;
case 8: // Open/close panel
$disp($id('de-panel').lastChild);
break;
case 9: // Mask/unmask images
toggleCfg('maskImgs');
updateCSS();
break;
case 10: // Open/close settings
toggleContent('cfg', false);
break;
case 11: // Expand current image
post = this._getFirstVisPost(false, true) || this._getNextVisPost(null, true, false);
if(post) {
post.toggleImages(!post.imagesExpanded);
}
break;
case 12: // Bold text (txt)
if(e.target !== pr.txta) {
return;
}
$id('de-btn-bold').click();
break;
case 13: // Italic text (txt)
if(e.target !== pr.txta) {
return;
}
$id('de-btn-italic').click();
break;
case 14: // Strike text (txt)
if(e.target !== pr.txta) {
return;
}
$id('de-btn-strike').click();
break;
case 15: // Spoiler text (txt)
if(e.target !== pr.txta) {
return;
}
$id('de-btn-spoil').click();
break;
case 16: // Code text (txt)
if(e.target !== pr.txta) {
return;
}
$id('de-btn-code').click();
break;
case 17: // Open next page/picture
if(this._fullImage) {
$id('de-img-btn-next').click();
} else if(!TNum && this.lastPage !== aib.lastPage) {
window.location.pathname = aib.getPageUrl(brd, this.lastPage + 1);
}
break;
case -1:
if(TNum) {
idx = this.tKeys.indexOf(kc);
if(idx === 0) { // Update thread
Thread.loadNewPosts(null);
break;
}
return;
}
idx = this.ntKeys.indexOf(kc);
if(idx === -1) {
return;
} else if(idx === 2) { // Open thread
post = this._getFirstVisPost(false, true) || this._getNextVisPost(null, true, false);
if(post) {
if(nav.Firefox) {
GM_openInTab(aib.getThrdUrl(brd, post.tNum), false, true);
} else {
window.open(aib.getThrdUrl(brd, post.tNum), '_blank');
}
}
break;
} else if(idx === 3) { // Expand/collapse thread
post = this._getFirstVisPost(false, true) || this._getNextVisPost(null, true, false);
if(post) {
if(post.thr.omitted === 0) {
temp = post.thr.nextNotHidden;
post.thr.load(visPosts, !!temp, null);
post = (temp || post.thr).op;
} else {
post.thr.load(1, false, null);
post = post.thr.op;
}
scrollTo(0, pageYOffset + post.topCoord);
if(this.cPost && this.cPost !== post) {
this.cPost.unselect();
this.cPost = post;
}
}
break;
}
default:
scrollToThread = !TNum && (globIdx === 0 || globIdx === 1);
this._scroll(this._getFirstVisPost(scrollToThread, false), globIdx === 0 || idx === 0,
scrollToThread);
}
}
e.stopPropagation();
$pd(e);
},
pause: function() {
this.paused = true;
},
resume: function(keys) {
this.gKeys = keys[2];
this.ntKeys = keys[3];
this.tKeys = keys[4];
this.paused = false;
},
get _fullImage() {
return $c('de-img-full de-img-center', doc);
},
_getFirstVisPost: function(getThread, getFull) {
var post, tPost;
if(this.lastPageOffset !== pageYOffset) {
post = getThread ? firstThr : firstThr.op;
while(post.topCoord < 1) {
tPost = post.next;
if(!tPost) {
break;
}
post = tPost;
}
if(this.cPost) {
this.cPost.unselect();
}
this.cPost = getThread ? getFull ? post.op : post.op.prev : getFull ? post : post.prev;
this.lastPageOffset = pageYOffset;
}
return this.cPost;
},
_getNextVisPost: function(cPost, isOp, toUp) {
var thr;
if(isOp) {
thr = cPost ? toUp ? cPost.thr.prevNotHidden : cPost.thr.nextNotHidden :
firstThr.hidden ? firstThr.nextNotHidden : firstThr;
return thr ? thr.op : null;
}
return cPost ? cPost.getAdjacentVisPost(toUp) : firstTht.hidden ||
firstThr.op.hidden ? firstThr.op.getAdjacentVisPost(toUp) : firstThr.op;
},
_scroll: function(post, toUp, toThread) {
var next = this._getNextVisPost(post, toThread, toUp);
if(!next) {
if(!TNum && (toUp ? pageNum > aib.firstPage : this.lastPage < aib.lastPage)) {
window.location.pathname = aib.getPageUrl(brd, toUp ? pageNum - 1 : this.lastPage + 1);
}
return;
}
if(post) {
post.unselect();
}
if(toThread) {
next.el.scrollIntoView();
} else {
scrollTo(0, pageYOffset + next.el.getBoundingClientRect().top -
Post.sizing.wHeight / 2 + next.el.clientHeight / 2);
}
this.lastPageOffset = pageYOffset;
next.select();
this.cPost = next;
}
}
function KeyEditListener(alertEl, keys, allKeys) {
var j, k, i, len, aInputs = aProto.slice.call($C('de-input-key', alertEl));
for(i = 0, len = allKeys.length; i < len; ++i) {
k = allKeys[i];
if(k !== 0) {
for(j = i + 1; j < len; ++j) {
if(k === allKeys[j]) {
aInputs[i].classList.add('de-error-key');
aInputs[j].classList.add('de-error-key');
break;
}
}
}
}
this.aEl = alertEl;
this.keys = keys;
this.initKeys = JSON.parse(JSON.stringify(keys));
this.allKeys = allKeys;
this.allInputs = aInputs;
this.errCount = $C('de-error-key', alertEl).length;
if(this.errCount !== 0) {
this.saveButton.disabled = true;
}
}
// Browsers have different codes for these keys (see KeyNavigation.readKeys):
// Firefox - '-' - 173, '=' - 61, ';' - 59
// Chrome/Opera: '-' - 189, '=' - 187, ';' - 186
KeyEditListener.keyCodes = ['',,,,,,,,'Backspace',/* Tab */,,,,'Enter',,,'Shift','Ctrl','Alt',
/* Pause/Break */,/* Caps Lock */,,,,,,,/* Escape */,,,,,'Space',/* Page Up */,
/* Page Down */,/* End */,/* Home */,'←','↑','→','↓',,,,,/* Insert */,/* Delete */,,'0','1','2',
'3','4','5','6','7','8','9',,';',,'=',,,,'A','B','C','D','E','F','G','H','I','J','K','L','M',
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',/* Left WIN Key */,/* Right WIN Key */,
/* Select key */,,,'Numpad 0','Numpad 1','Numpad 2','Numpad 3','Numpad 4','Numpad 5','Numpad 6',
'Numpad 7','Numpad 8','Numpad 9','Numpad *','Numpad +',,'Numpad -','Numpad .','Numpad /',
/* F1 */,/* F2 */,/* F3 */,/* F4 */,/* F5 */,/* F6 */,/* F7 */,/* F8 */,/* F9 */,/* F10 */,
/* F11 */,/* F12 */,,,,,,,,,,,,,,,,,,,,,/* Num Lock */,/* Scroll Lock */,,,,,,,,,,,,,,,,,,,,,,,,
,,,,'-',,,,,,,,,,,,,';','=',',','-','.','/','`',,,,,,,,,,,,,,,,,,,,,,,,,,,'[','\\',']','\''
];
KeyEditListener.getStrKey = function(key) {
var str = '';
if(key & 0x1000) {
str += 'Ctrl+';
}
if(key & 0x2000) {
str += 'Shift+';
}
if(key & 0x4000) {
str += 'Alt+';
}
str += KeyEditListener.keyCodes[key & 0xFFF];
return str;
};
KeyEditListener.getEditMarkup = function(keys) {
var allKeys = [];
var html = Lng.keyNavEdit[lang]
.replace(/%l/g, '')
.replace(/%i([2-4])([0-9]+)(t)?/g, function(aKeys, all, id1, id2, isText) {
var key = this[+id1][+id2];
aKeys.push(key);
return '';
}.bind(keys, allKeys)) +
'' +
'';
return [allKeys, html];
};
KeyEditListener.setTitle = function(el, idx) {
var title = el.getAttribute('de-title');
if(keyNav && idx !== -1) {
title += ' [' + KeyEditListener.getStrKey(keyNav.gKeys[idx]) + ']';
}
el.title = title;
};
KeyEditListener.prototype = {
cEl: null,
cKey: -1,
errorInput: false,
get saveButton() {
var val = $id('de-keys-save');
Object.defineProperty(this, 'saveButton', { value: val, configurable: true });
return val;
},
handleEvent: function(e) {
var key, keyStr, keys, str, id, temp, el = e.target;
switch(e.type) {
case 'blur':
if(keyNav && this.errCount === 0) {
keyNav.resume(this.keys);
}
this.cEl = null;
return;
case 'focus':
if(keyNav) {
keyNav.pause();
}
this.cEl = el;
return;
case 'click':
if(el.id === 'de-keys-reset') {
this.keys = KeyNavigation.getDefaultKeys();
this.initKeys = KeyNavigation.getDefaultKeys();
if(keyNav) {
keyNav.resume(this.keys);
}
temp = KeyEditListener.getEditMarkup(this.keys);
this.allKeys = temp[0];
$c('de-alert-msg', this.aEl).innerHTML = temp[1];
this.allInputs = aProto.slice.call($C('de-input-key', this.aEl));
this.errCount = 0;
delete this.saveButton;
break;
} else if(el.id === 'de-keys-save') {
keys = this.keys;
setStored('DESU_keys', JSON.stringify(keys));
} else if(el.className === 'de-alert-btn') {
keys = this.initKeys;
} else {
return;
}
if(keyNav) {
keyNav.resume(keys);
}
closeAlert($id('de-alert-edit-keybnavig'));
break;
case 'keydown':
if(!this.cEl) {
return;
}
key = e.keyCode;
if(key === 0x1B || key === 0x2E) { // ESC, DEL
this.cEl.value = '';
this.cKey = 0;
this.errorInput = false;
break;
}
keyStr = KeyEditListener.keyCodes[key];
if(keyStr == null) {
this.cKey = -1;
return;
}
str = '';
if(e.ctrlKey) {
str += 'Ctrl+';
}
if(e.shiftKey) {
str += 'Shift+';
}
if(e.altKey) {
str += 'Alt+';
}
if(key === 16 || key === 17 || key === 18) {
this.errorInput = true;
} else {
this.cKey = key | (e.ctrlKey ? 0x1000 : 0) | (e.shiftKey ? 0x2000 : 0) |
(e.altKey ? 0x4000 : 0) | (this.cEl.hasAttribute('de-text') ? 0x8000 : 0);
this.errorInput = false;
str += keyStr;
}
this.cEl.value = str;
break;
case 'keyup':
var idx, rIdx, oKey, rEl, isError, el = this.cEl,
key = this.cKey;
if(!el || key === -1) {
return;
}
isError = el.classList.contains('de-error-key');
if(!this.errorInput && key !== -1) {
idx = this.allInputs.indexOf(el);
oKey = this.allKeys[idx];
if(oKey === key) {
this.errorInput = false;
break;
}
rIdx = key === 0 ? -1 : this.allKeys.indexOf(key);
this.allKeys[idx] = key;
if(isError) {
idx = this.allKeys.indexOf(oKey);
if(idx !== -1 && this.allKeys.indexOf(oKey, idx + 1) === -1) {
rEl = this.allInputs[idx];
if(rEl.classList.contains('de-error-key')) {
this.errCount--;
rEl.classList.remove('de-error-key');
}
}
if(rIdx === -1) {
this.errCount--;
el.classList.remove('de-error-key');
}
}
if(rIdx === -1) {
this.keys[+el.getAttribute('de-id1')][+el.getAttribute('de-id2')] = key;
if(this.errCount === 0) {
this.saveButton.disabled = false;
}
this.errorInput = false;
break;
}
rEl = this.allInputs[rIdx];
if(!rEl.classList.contains('de-error-key')) {
this.errCount++;
rEl.classList.add('de-error-key');
}
}
if(!isError) {
this.errCount++;
el.classList.add('de-error-key');
}
if(this.errCount !== 0) {
this.saveButton.disabled = true;
}
}
$pd(e);
}
};
//============================================================================================================
// FORM SUBMIT
//============================================================================================================
function getSubmitResponse(dc, isFrame) {
var i, els, el, err = '', form = $q(aib.qDForm, dc);
if(dc.body.hasChildNodes() && !form) {
for(i = 0, els = $Q(aib.qError, dc); el = els[i++];) {
err += el.innerHTML + '\n';
}
if(!(err = err.replace(/]+>Назад.+| 1) {
$each(fileInputs, function(input, index) {
if(index > 0) {
$del(input.parentNode);
}
});
aib.btnSetFCntToOne.click();
}
}
}
if(pr.video) {
pr.video.value = '';
}
Cfg['stats'][pr.tNum ? 'reply' : 'op']++;
saveComCfg(aib.dm, Cfg);
if(!pr.tNum) {
window.location = response[0];
return;
}
if(TNum) {
firstThr.clearPostsMarks();
firstThr.loadNew(function(eCode, eMsg, np, xhr) {
infoLoadErrors(eCode, eMsg, 0);
closeAlert($id('de-alert-upload'));
if(Cfg['scrAfterRep']) {
scrollTo(0, pageYOffset + firstThr.last.el.getBoundingClientRect().top);
}
}, true);
} else {
pByNum[pr.tNum].thr.load(visPosts, false, closeAlert.bind(window, $id('de-alert-upload')));
}
pr.closeQReply();
pr.refreshCapImg(false);
}
function endDelete() {
var el = $id('de-alert-deleting');
if(el) {
closeAlert(el);
$alert(Lng.succDeleted[lang], 'deleted', false);
}
}
function checkDelete(response) {
if(response[1]) {
$alert(Lng.errDelete[lang] + response[1], 'deleting', false);
return;
}
var el, i, els, len, post, tNums = [],
num = (doc.location.hash.match(/\d+/) || [null])[0];
if(num && (post = pByNum[num])) {
if(!post.isOp) {
post.el.className = aib.cReply;
}
doc.location.hash = '';
}
for(i = 0, els = $Q('.' + aib.cRPost + ' input:checked', dForm), len = els.length; i < len; ++i) {
el = els[i];
el.checked = false;
if(!TNum && tNums.indexOf(num = aib.getPostEl(el).post.tNum) === -1) {
tNums.push(num);
}
}
if(TNum) {
firstThr.clearPostsMarks();
firstThr.loadNew(function(eCode, eMsg, np, xhr) {
infoLoadErrors(eCode, eMsg, 0);
endDelete();
}, false);
} else {
tNums.forEach(function(tNum) {
pByNum[tNum].thr.load(visPosts, false, endDelete);
});
}
}
function html5Submit(form, button, fn) {
this.boundary = '---------------------------' + Math.round(Math.random() * 1e11);
this.data = [];
this.busy = 0;
this.error = false;
this.url = form.action;
this.fn = fn;
$each($Q('input:not([type="submit"]):not([type="button"]), textarea, select', form),
this.append.bind(this));
this.append(button);
this.submit();
}
html5Submit.prototype = {
append: function(el) {
var file, fName, idx, fr,
pre = '--' + this.boundary + '\r\nContent-Disposition: form-data; name="' + el.name + '"';
if(el.type === 'file' && el.files.length > 0) {
file = el.files[0];
fName = file.name;
this.data.push(pre + '; filename="' + (
!Cfg['removeFName'] ? fName : ' ' + fName.substring(fName.lastIndexOf('.'))
) + '"\r\nContent-type: ' + file.type + '\r\n\r\n', null, '\r\n');
idx = this.data.length - 2;
if(!/^image\/(?:png|jpeg)$/.test(file.type)) {
this.data[idx] = file;
return;
}
fr = new FileReader();
fr.onload = function(name, e) {
var dat = this.clearImage(e.target.result, !!el.imgFile);
if(dat) {
if(el.imgFile) {
dat.push(el.imgFile);
}
if(Cfg['postSameImg']) {
dat.push(String(Math.round(Math.random() * 1e6)));
}
this.data[idx] = new Blob(dat);
this.busy--;
this.submit();
} else {
this.error = true;
$alert(Lng.fileCorrupt[lang] + name, 'upload', false);
}
}.bind(this, fName);
fr.readAsArrayBuffer(file);
this.busy++;
} else if(el.type !== 'checkbox' || el.checked) {
this.data.push(pre + '\r\n\r\n' + el.value + '\r\n');
}
},
submit: function() {
if(this.error || this.busy !== 0) {
return;
}
this.data.push('--' + this.boundary + '--\r\n');
$xhr({
'method': 'POST',
'headers': {'Content-type': 'multipart/form-data; boundary=' + this.boundary},
'data': new Blob(this.data),
'url': nav.fixLink(this.url),
'onreadystatechange': function(xhr) {
if(xhr.readyState === 4) {
if(xhr.status === 200) {
this(getSubmitResponse($DOM(xhr.responseText), false));
} else {
$alert(xhr.status === 0 ? Lng.noConnect[lang] :
'HTTP [' + xhr.status + '] ' + xhr.statusText, 'upload', false);
}
}
}.bind(this.fn)
});
},
readExif: function(data, off, len) {
var i, j, dE, tag, tgLen, xRes = 0,
yRes = 0,
resT = 0,
dv = new DataView(data, off),
le = String.fromCharCode(dv.getUint8(0), dv.getUint8(1)) !== 'MM';
if(dv.getUint16(2, le) !== 0x2A) {
return null;
}
i = dv.getUint32(4, le);
if(i > len) {
return null;
}
for(tgLen = dv.getUint16(i, le), j = 0; j < tgLen; j++) {
tag = dv.getUint16(dE = i + 2 + 12 * j, le);
if(tag !== 0x011A && tag !== 0x011B && tag !== 0x0128) {
continue;
}
if(tag === 0x0128) {
resT = dv.getUint16(dE + 8, le) - 1;
} else {
dE = dv.getUint32(dE + 8, le);
if(dE > len) {
return null;
}
if(tag === 0x11A) {
xRes = Math.round(dv.getUint32(dE, le) / dv.getUint32(dE + 4, le));
} else {
yRes = Math.round(dv.getUint32(dE, le) / dv.getUint32(dE + 4, le));
}
}
}
xRes = xRes || yRes;
yRes = yRes || xRes;
return new Uint8Array([resT, xRes >> 8, xRes & 0xFF, yRes >> 8, yRes & 0xFF]);
},
clearImage: function(data, delExtraData) {
var tmp, i, len, deep, rv, lIdx, jpgDat, img = new Uint8Array(data),
rExif = !!Cfg['removeEXIF'];
if(!Cfg['postSameImg'] && !rExif && !delExtraData) {
return [img];
}
if(img[0] === 0xFF && img[1] === 0xD8) {
for(i = 2, deep = 1, len = img.length - 1, rv = [null, null], lIdx = 2, jpgDat = null; i < len; ) {
if(img[i] === 0xFF) {
if(rExif) {
if(!jpgDat && deep === 1) {
if(img[i + 1] === 0xE1 && img[i + 4] === 0x45) {
jpgDat = this.readExif(data, i + 10, (img[i + 2] << 8) + img[i + 3]);
} else if(img[i + 1] === 0xE0 && img[i + 7] === 0x46) {
jpgDat = img.subarray(i + 11, i + 16);
}
}
if((img[i + 1] >> 4) === 0xE || img[i + 1] === 0xFE) {
if(lIdx !== i) {
rv.push(img.subarray(lIdx, i));
}
i += 2 + (img[i + 2] << 8) + img[i + 3];
lIdx = i;
continue;
}
} else if(img[i + 1] === 0xD8) {
deep++;
i++;
continue;
}
if(img[i + 1] === 0xD9 && --deep === 0) {
break;
}
}
i++;
}
i += 2;
if(!delExtraData && len - i > 75) {
i = len;
}
if(lIdx === 2) {
return i === len ? [img] : [new Uint8Array(data, 0, i)];
}
rv[0] = new Uint8Array([0xFF, 0xD8, 0xFF, 0xE0, 0, 0x0D, 0x4A, 0x46, 0x49, 0x46, 0, 1, 1]);
rv[1] = jpgDat || new Uint8Array([0, 0, 1, 0, 1]);
rv.push(img.subarray(lIdx, i));
return rv;
}
if(img[0] === 0x89 && img[1] === 0x50) {
for(i = 0, len = img.length - 7; i < len && (img[i] !== 0x49 ||
img[i + 1] !== 0x45 || img[i + 2] !== 0x4E || img[i + 3] !== 0x44); i++) {}
i += 8;
return i === len || (!delExtraData && len - i > 75) ? [img] : [new Uint8Array(data, 0, i)];
}
return null;
}
};
//============================================================================================================
// CONTENT FEATURES
//============================================================================================================
function initMessageFunctions() {
window.addEventListener('message', function(e) {
var temp, data = e.data.substring(1);
switch(e.data[0]) {
case 'A':
temp = data.split('$#$');
if(temp[0] === 'de-iframe-pform') {
checkUpload([temp[1], temp[2]]);
} else {
checkDelete([temp[1], temp[2]]);
}
$q('iframe[name="' + temp[0] + '"]', doc).src = 'about:blank';
return;
case 'B':
$del($id('de-fav-wait'));
$id('de-iframe-fav').style.height = data + 'px';
return;
}
}, false);
}
function detectImgFile(ab) {
var i, j, dat = new Uint8Array(ab),
len = dat.length;
/* JPG [ff d8 ff e0] = [яШяа] */
if(dat[0] === 0xFF && dat[1] === 0xD8) {
for(i = 0, j = 0; i < len - 1; i++) {
if(dat[i] === 0xFF) {
/* Built-in JPG */
if(dat[i + 1] === 0xD8) {
j++;
/* JPG end [ff d9] */
} else if(dat[i + 1] === 0xD9 && --j === 0) {
i += 2;
break;
}
}
}
/* PNG [89 50 4e 47] = [‰PNG] */
} else if(dat[0] === 0x89 && dat[1] === 0x50) {
for(i = 0; i < len - 7; i++) {
/* PNG end [49 45 4e 44 ae 42 60 82] */
if(dat[i] === 0x49 && dat[i + 1] === 0x45 && dat[i + 2] === 0x4E && dat[i + 3] === 0x44) {
i += 8;
break;
}
}
} else {
return {};
}
/* Ignore small files */
if(i !== len && len - i > 60) {
for(len = i + 90; i < len; i++) {
/* 7Z [37 7a bc af] = [7zјЇ] */
if(dat[i] === 0x37 && dat[i + 1] === 0x7A && dat[i + 2] === 0xBC) {
return {'type': 0, 'idx': i, 'data': ab};
/* ZIP [50 4b 03 04] = [PK..] */
} else if(dat[i] === 0x50 && dat[i + 1] === 0x4B && dat[i + 2] === 0x03) {
return {'type': 1, 'idx': i, 'data': ab};
/* RAR [52 61 72 21] = [Rar!] */
} else if(dat[i] === 0x52 && dat[i + 1] === 0x61 && dat[i + 2] === 0x72) {
return {'type': 2, 'idx': i, 'data': ab};
/* OGG [4f 67 67 53] = [OggS] */
} else if(dat[i] === 0x4F && dat[i + 1] === 0x67 && dat[i + 2] === 0x67) {
return {'type': 3, 'idx': i, 'data': ab};
/* MP3 [0x49 0x44 0x33] = [ID3] */
} else if(dat[i] === 0x49 && dat[i + 1] === 0x44 && dat[i + 2] === 0x33) {
return {'type': 4, 'idx': i, 'data': ab};
}
}
}
return {};
}
function workerQueue(mReqs, wrkFn, errFn) {
if(!nav.hasWorker) {
this.run = this._runSync.bind(wrkFn);
return;
}
this.queue = new $queue(mReqs, this._createWrk.bind(this), null);
this.run = this._runWrk;
this.wrks = new $workers('self.onmessage = function(e) {\
var info = (' + String(wrkFn) + ')(e.data[1]);\
if(info.data) {\
self.postMessage([e.data[0], info], [info.data]);\
} else {\
self.postMessage([e.data[0], info]);\
}\
}', mReqs);
this.errFn = errFn;
}
workerQueue.prototype = {
_runSync: function(data, transferObjs, Fn) {
Fn(this(data));
},
onMess: function(Fn, e) {
this.queue.end(e.data[0]);
Fn(e.data[1]);
},
onErr: function(qIdx, e) {
this.queue.end(qIdx);
this.errFn(e);
},
_runWrk: function(data, transObjs, Fn) {
this.queue.run([data, transObjs, this.onMess.bind(this, Fn)]);
},
_createWrk: function(qIdx, num, data) {
var w = this.wrks[qIdx];
w.onmessage = data[2];
w.onerror = this.onErr.bind(this, qIdx);
w.postMessage([qIdx, data[0]], data[1]);
},
clear: function() {
this.wrks.clear();
this.wrks = null;
}
};
function addImgFileIcon(fName, info) {
var app, ext, type = info['type'];
if(typeof type !== 'undefined') {
if(type === 2) {
app = 'application/x-rar-compressed';
ext = 'rar';
} else if(type === 1) {
app = 'application/zip';
ext = 'zip';
} else if(type === 0) {
app = 'application/x-7z-compressed';
ext = '7z';
} else if(type === 3) {
app = 'audio/ogg';
ext = 'ogg';
} else {
app = 'audio/mpeg';
ext = 'mp3';
}
this.insertAdjacentHTML('afterend', '.' + ext + ''
);
}
}
function downloadImgData(url, Fn) {
downloadObjInfo({
'method': 'GET',
'url': url,
'onreadystatechange': function onDownloaded(url, e) {
if(e.readyState !== 4) {
return;
}
var isAb = e.responseType === 'arraybuffer';
if(e.status === 0 && isAb) {
Fn(new Uint8Array(e.response));
} else if(e.status !== 200) {
if(e.status === 404 || !url) {
Fn(null);
} else {
downloadObjInfo({
'method': 'GET',
'url': url,
'onreadystatechange': onDownloaded.bind(null, null)
});
}
} else if(isAb) {
Fn(new Uint8Array(e.response));
} else {
for(var len, i = 0, txt = e.responseText, rv = new Uint8Array(len = txt.length); i < len; ++i) {
rv[i] = txt.charCodeAt(i) & 0xFF;
}
Fn(rv);
}
}.bind(null, url)
});
}
function downloadObjInfo(obj) {
if(nav.Firefox && aib.fch && !obj.url.startsWith('blob')) {
obj['overrideMimeType'] = 'text/plain; charset=x-user-defined';
GM_xmlhttpRequest(obj);
} else {
obj['responseType'] = 'arraybuffer';
try {
$xhr(obj);
} catch(e) {
Fn(null);
}
}
}
function preloadImages(post) {
if(!Cfg['preLoadImgs'] && !Cfg['openImgs'] && !isPreImg) {
return;
}
var lnk, url, iType, nExp, el, i, len, els, queue, mReqs = post ? 1 : 4, cImg = 1,
rjf = (isPreImg || Cfg['findImgFile']) && new workerQueue(mReqs, detectImgFile, function(e) {
console.error("FILE DETECTOR ERROR, line: " + e.lineno + " - " + e.message);
});
if(isPreImg || Cfg['preLoadImgs']) {
queue = new $queue(mReqs, function(qIdx, num, dat) {
downloadImgData(dat[0], function(idx, data) {
if(data) {
var a = this[1],
fName = this[0].substring(this[0].lastIndexOf("/") + 1),
aEl = $q(aib.qImgLink, aib.getImgWrap(a));
aEl.setAttribute('download', fName);
a.href = window.URL.createObjectURL(new Blob([data], {'type': this[2]}));
a.setAttribute('de-name', fName);
if(this[3]) {
this[3].src = a.href;
}
if(rjf) {
rjf.run(data.buffer, [data.buffer], addImgFileIcon.bind(aEl, fName));
}
}
queue.end(idx);
if(Images_.progressId) {
$alert(Lng.loadImage[lang] + cImg + '/' + len, Images_.progressId, true);
}
cImg++;
}.bind(dat, qIdx));
}, function() {
Images_.preloading = false
if(Images_.afterpreload) {
Images_.afterpreload();
Images_.afterpreload = Images_.progressId = null;
}
rjf && rjf.clear();
rjf = queue = cImg = len = null;
});
Images_.preloading = true;
}
for(i = 0, els = getImages(post || dForm), len = els.length; i < len; i++) {
if(lnk = getAncestor(el = els[i], 'A')) {
url = lnk.href;
nExp = !!Cfg['openImgs'];
if(/\.gif$/i.test(url)) {
iType = 'image/gif';
} else {
if(/\.jpe?g$/i.test(url)) {
iType = 'image/jpeg';
} else if(/\.png$/i.test(url)) {
iType = 'image/png';
} else {
continue;
}
nExp &= !Cfg['openGIFs'];
}
if(queue) {
queue.run([url, lnk, iType, nExp && el]);
} else if(nExp) {
el.src = url;
}
}
}
queue && queue.complete();
}
function getDataFromImg(img) {
var cnv = Images_.canvas || (Images_.canvas = doc.createElement('canvas'));
cnv.width = img.width;
cnv.height = img.height;
cnv.getContext('2d').drawImage(img, 0, 0);
return new Uint8Array(atob(cnv.toDataURL("image/png").split(',')[1]).split('').map(function(a) {
return a.charCodeAt();
}));
}
function loadDocFiles(imgOnly) {
var els, files, progress, counter, count = 0,
current = 1,
warnings = '',
tar = new $tar(),
dc = imgOnly ? doc : doc.documentElement.cloneNode(true);
Images_.queue = new $queue(4, function(qIdx, num, dat) {
downloadImgData(dat[0], function(idx, data) {
var name = this[1].replace(/[\\\/:*?"<>|]/g, '_'), el = this[2];
progress.value = current;
counter.innerHTML = current;
current++;
if(this[3]) {
if(!data) {
warnings += '
' + Lng.cantLoad[lang] + '' +
this[0] + '
' + Lng.willSavePview[lang];
$alert(Lng.loadErrors[lang] + warnings, 'floadwarn', false);
name = 'thumb-' + name.replace(/\.[a-z]+$/, '.png');
data = getDataFromImg(this[2]);
}
if(!imgOnly) {
el.classList.add('de-thumb');
el.src = this[3].href = $q(aib.qImgLink, aib.getImgWrap(this[3])).href =
name = 'images/' + name;
}
tar.addFile(name, data);
} else if(data && data.length > 0) {
tar.addFile(el.href = el.src = 'data/' + name, data);
} else {
$del(el);
}
Images_.queue.end(idx);
}.bind(dat, qIdx));
}, function() {
var u, a, dt;
if(!imgOnly) {
dt = doc.doctype;
$t('head', dc).insertAdjacentHTML('beforeend',
'');
tar.addString('data/dollscript.js', '(' + String(de_main_func) + ')(null, true);');
tar.addString(
TNum + '.html', '' + dc.outerHTML
);
}
u = window.URL.createObjectURL(tar.get());
a = $new('a', {'href': u, 'download': aib.dm + '-' + brd.replace(/[\\\/:*?"<>|]/g, '') +
'-t' + TNum + (imgOnly ? '-images.tar' : '.tar')}, null);
doc.body.appendChild(a);
a.click();
setTimeout(function(el, url) {
window.URL.revokeObjectURL(url);
$del(el);
}, 0, a, u);
$del($id('de-alert-filesload'));
Images_.queue = tar = warnings = count = current = imgOnly = progress = counter = null;
});
els = aProto.slice.call(getImages($q('[de-form]', dc)));
count += els.length;
els.forEach(function(el) {
var lnk, url;
if(lnk = getAncestor(el, 'A')) {
url = lnk.href;
Images_.queue.run([url, lnk.getAttribute('de-name') ||
url.substring(url.lastIndexOf("/") + 1), el, lnk]);
}
});
if(!imgOnly) {
files = [];
$each($Q('script, link[rel="alternate stylesheet"], span[class^="de-btn-"],' +
' #de-main > div, .de-parea, #de-qarea, ' + aib.qPostForm, dc), $del);
$each($T('a', dc), function(el) {
var num, tc = el.textContent;
if(tc[0] === '>' && tc[1] === '>' && (num = +tc.substr(2)) && (num in pByNum)) {
el.href = aib.anchor + num;
} else {
el.href = getAbsLink(el.href);
}
if(!el.classList.contains('de-preflink')) {
el.className = 'de-preflink ' + el.className;
}
});
$each($Q('.' + aib.cRPost, dc), function(post, i) {
post.setAttribute('de-num', i === 0 ? TNum : aib.getPNum(post));
});
$each($Q('link, *[src]', dc), function(el) {
if(els.indexOf(el) !== -1) {
return;
}
var temp, i, ext, name, url = el.tagName === 'LINK' ? el.href : el.src;
if(!this.test(url)) {
$del(el);
return;
}
name = url.substring(url.lastIndexOf("/") + 1).replace(/[\\\/:*?"<>|]/g, '_')
.toLowerCase();
if(files.indexOf(name) !== -1) {
temp = url.lastIndexOf('.');
ext = url.substring(temp);
url = url.substring(0, temp);
name = name.substring(0, name.lastIndexOf('.'));
for(i = 0; ; ++i) {
temp = name + '(' + i + ')' + ext;
if(files.indexOf(temp) === -1) {
break;
}
}
name = temp;
}
files.push(name);
Images_.queue.run([url, name, el, null]);
count++;
}.bind(new RegExp('^\\/\\/?|^https?:\\/\\/([^\\/]*\.)?' + regQuote(aib.dm) + '\\/', 'i')));
}
$alert((imgOnly ? Lng.loadImage[lang] : Lng.loadFile[lang]) +
'
1/' +
count, 'filesload', true);
progress = $id('de-loadprogress');
counter = progress.nextElementSibling;
Images_.queue.complete();
els = null;
}
//============================================================================================================
// TIME CORRECTION
//============================================================================================================
function dateTime(pattern, rPattern, diff, dtLang, onRPat) {
if(dateTime.checkPattern(pattern)) {
this.disabled = true;
return;
}
this.regex = pattern
.replace(/(?:[sihdny]\?){2,}/g, function() {
return '(?:' + arguments[0].replace(/\?/g, '') + ')?';
})
.replace(/\-/g, '[^<]')
.replace(/\+/g, '[^0-9]')
.replace(/([sihdny]+)/g, '($1)')
.replace(/[sihdny]/g, '\\d')
.replace(/m|w/g, '([a-zA-Zа-яА-Я]+)');
this.pattern = pattern.replace(/[\?\-\+]+/g, '').replace(/([a-z])\1+/g, '$1');
this.diff = parseInt(diff, 10);
this.sDiff = (this.diff < 0 ? '' : '+') + this.diff;
this.arrW = Lng.week[dtLang];
this.arrM = Lng.month[dtLang];
this.arrFM = Lng.fullMonth[dtLang];
this.rPattern = rPattern;
this.onRPat = onRPat;
}
dateTime.toggleSettings = function(el) {
if(el.checked && (!/^[+-]\d{1,2}$/.test(Cfg['timeOffset']) || dateTime.checkPattern(Cfg['timePattern']))) {
$alert(Lng.cTimeError[lang], 'err-correcttime', false);
saveCfg('correctTime', 0);
el.checked = false;
}
};
dateTime.checkPattern = function(val) {
return !val.contains('i') || !val.contains('h') || !val.contains('d') || !val.contains('y') ||
!(val.contains('n') || val.contains('m')) ||
/[^\?\-\+sihdmwny]|mm|ww|\?\?|([ihdny]\?)\1+/.test(val);
};
dateTime.prototype = {
getRPattern: function(txt) {
var k, p, a, str, i = 1,
j = 0,
m = txt.match(new RegExp(this.regex));
if(!m) {
this.disabled = true;
return false;
}
this.rPattern = '';
str = m[0];
while(a = m[i++]) {
p = this.pattern[i - 2];
if((p === 'm' || p === 'y') && a.length > 3) {
p = p.toUpperCase();
}
k = str.indexOf(a, j);
this.rPattern += str.substring(j, k) + '_' + p;
j = k + a.length;
}
this.onRPat && this.onRPat(this.rPattern);
return true;
},
pad2: function(num) {
return num < 10 ? '0' + num : num;
},
fix: function(txt) {
if(this.disabled || (!this.rPattern && !this.getRPattern(txt))) {
return txt;
}
return txt.replace(new RegExp(this.regex, 'g'), function() {
var i, a, t, second, minute, hour, day, month, year, dtime;
for(i = 1; i < 8; i++) {
a = arguments[i];
t = this.pattern[i - 1];
t === 's' ? second = a :
t === 'i' ? minute = a :
t === 'h' ? hour = a :
t === 'd' ? day = a :
t === 'n' ? month = a - 1 :
t === 'y' ? year = a :
t === 'm' && (
month =
/^янв|^jan/i.test(a) ? 0 :
/^фев|^feb/i.test(a) ? 1 :
/^мар|^mar/i.test(a) ? 2 :
/^апр|^apr/i.test(a) ? 3 :
/^май|^may/i.test(a) ? 4 :
/^июн|^jun/i.test(a) ? 5 :
/^июл|^jul/i.test(a) ? 6 :
/^авг|^aug/i.test(a) ? 7 :
/^сен|^sep/i.test(a) ? 8 :
/^окт|^oct/i.test(a) ? 9 :
/^ноя|^nov/i.test(a) ? 10 :
/^дек|^dec/i.test(a) && 11
);
}
dtime = new Date(year.length === 2 ? '20' + year : year, month, day, hour, minute, second || 0);
dtime.setHours(dtime.getHours() + this.diff);
return this.rPattern
.replace('_o', this.sDiff)
.replace('_s', this.pad2(dtime.getSeconds()))
.replace('_i', this.pad2(dtime.getMinutes()))
.replace('_h', this.pad2(dtime.getHours()))
.replace('_d', this.pad2(dtime.getDate()))
.replace('_w', this.arrW[dtime.getDay()])
.replace('_n', this.pad2(dtime.getMonth() + 1))
.replace('_m', this.arrM[dtime.getMonth()])
.replace('_M', this.arrFM[dtime.getMonth()])
.replace('_y', ('' + dtime.getFullYear()).substring(2))
.replace('_Y', dtime.getFullYear());
}.bind(this));
}
};
//============================================================================================================
// PLAYERS
//============================================================================================================
function initYouTube(embedType, videoType, width, height, isHD, loadTitles) {
var vData, vimReg = /^https?:\/\/(?:www\.)?vimeo\.com\/(?:[^\?]+\?clip_id=)?(\d+).*?$/,
ytReg = /^https?:\/\/(?:www\.|m\.)?youtu(?:be\.com\/(?:watch\?.*?v=|v\/|embed\/)|\.be\/)([^?]+).*?(?:t(?:ime)?=(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s?)?)?$/;
function addThumb(el, m, isYtube) {
var wh = ' width="' + width + '" height="' + height + '">';
if(isYtube) {
el.innerHTML = '' +
'' +
'';
if(isYtube) {
time = (m[2] ? m[2] * 3600 : 0) + (m[3] ? m[3] * 60 : 0) + (m[4] ? +m[4] : 0);
el.innerHTML = videoType === 1 ?
'