// ==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', '
    №' + tNum + ' - ' + hThr[b][tNum] + '
    '); } } } $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('
    ' + 'v' + version + ' | ' + 'Freedollchan | ' + 'Github
    '), $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', '
    ' + html + '
    '); 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, '
    ' + '' + ('#words,#exp,#exph,#imgn,#ihash,#subj,#name,#trip,#img,
    ') .split(',').join('
    ') + '
    ' + ('#sage,#op,#tlen,#all,#video,#vauthor,#num,#wipe,#rep,#outrep') .split(',').join('') + '
    ', 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, '' + Lng.selAjaxPages[lang].join('') + '', true, function(el) { loadPages(aProto.indexOf.call(el.parentNode.children, el) + 1); }); } function addAudioNotifMenu(el) { showMenu(el, '' + Lng.selAudioNotif[lang].join('') + '', 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 ? '' + '' ); dForm.target = 'de-iframe-dform'; dForm.onsubmit = function() { pr.closeQReply(); $alert(Lng.deleting[lang], 'deleting', true); }; } } function initThreadUpdater(title, enableUpdate) { var delay, checked404, loadTO, audioRep, currentXHR, audioEl, stateButton, hasAudio, initDelay, favIntrv, favNorm, favHref, notifGranted, enabled = false, disabledByUser = true, inited = false, lastECode = 200, newPosts = 0, aPlayers = 0, focused = true; if(enableUpdate) { init(); } if(focused && Cfg['desktNotif'] && ('permission' in Notification)) { switch(Notification.permission.toLowerCase()) { case 'default': requestNotifPermission(); break; case 'denied': saveCfg('desktNotif', 0); } } function init() { audioEl = null; stateButton = null; hasAudio = false; initDelay = Cfg['updThrDelay'] * 1e3; favIntrv = 0; favNorm = notifGranted = inited = true; favHref = ($q('head link[rel="shortcut icon"]', doc) || {}).href; focused = false; window.addEventListener('focus', onVis, false); window.addEventListener('blur', onBlur, false); window.addEventListener('mousemove', function mouseMove() { window.removeEventListener('mousemove', mouseMove, false); onVis(); }, false); enable(true); } function enable(startLoading) { enabled = true; checked404 = false; newPosts = 0; delay = initDelay; if(startLoading) { loadTO = setTimeout(loadPostsFun, delay); } } function disable(byUser) { disabledByUser = byUser; if(enabled) { clearTimeout(loadTO); enabled = hasAudio = false; setState('off'); var btn = $id('de-btn-audio-on'); if(btn) { btn.id = 'de-btn-audio-off'; } } } function toggleAudio(aRep) { if(!audioEl) { audioEl = $new('audio', { 'preload': 'auto', 'src': 'https://raw.github.com/SthephanShinkufag/Dollchan-Extension-Tools/master/signal.ogg' }, null); } audioRep = aRep; return hasAudio = !hasAudio; } function audioNotif() { if(focused) { hasAudio = false; } else { audioEl.play() setTimeout(audioNotif, audioRep); hasAudio = true; } } function requestNotifPermission() { notifGranted = false; Notification.requestPermission(function(state) { if(state.toLowerCase() === 'denied') { saveCfg('desktNotif', 0); } else { notifGranted = true; } }); } function loadPostsFun() { currentXHR = firstThr.loadNew(onLoaded, true); } function forceLoadPosts() { if(currentXHR) { currentXHR.abort(); } if(!enabled && !disabledByUser) { enable(false); } else { clearTimeout(loadTO); delay = initDelay; } loadPostsFun(); } function onLoaded(eCode, eMsg, lPosts, xhr) { if(currentXHR !== xhr && eCode === 0) { // Loading aborted return; } currentXHR = null; infoLoadErrors(eCode, eMsg, -1); if(eCode !== 200) { lastECode = eCode; if(!Cfg['noErrInTitle']) { updateTitle(); } if(eCode !== 0 && Math.floor(eCode / 500) === 0) { if(eCode === 404 && !checked404) { checked404 = true; } else { updateTitle(); disable(false); return; } } setState('warn'); if(enabled) { loadTO = setTimeout(loadPostsFun, delay); } return; } if(lastECode !== 200) { lastECode = 200; setState('on'); checked404 = false; if((focused || lPosts === 0) && !Cfg['noErrInTitle']) { updateTitle(); } } if(!focused) { if(lPosts !== 0) { if(Cfg['favIcoBlink'] && favHref && newPosts === 0) { favIntrv = setInterval(function() { $del($q('link[rel="shortcut icon"]', doc.head)); doc.head.insertAdjacentHTML('afterbegin', ''); favNorm = !favNorm; }, 800); } newPosts += lPosts; updateTitle(); if(Cfg['desktNotif'] && notifGranted) { var post = firstThr.last, imgs = post.images, notif = new Notification(aib.dm + '/' + brd + '/' + TNum + ': ' + newPosts + Lng.newPost[lang][lang !== 0 ? +(newPosts !== 1) : (newPosts % 10) > 4 || (newPosts % 10) === 0 || (((newPosts % 100) / 10) | 0) === 1 ? 2 : (newPosts % 10) === 1 ? 0 : 1] + Lng.newPost[lang][3], { 'body': post.text.substring(0, 250).replace(/\s+/g, ' '), 'tag': aib.dm + brd + TNum, 'icon': imgs.length === 0 ? favHref : imgs[0].src }); notif.onshow = function() { setTimeout(this.close.bind(this), 12e3); }; notif.onclick = function() { window.focus(); }; notif.onerror = function() { window.focus(); requestNotifPermission(); }; } if(hasAudio) { if(audioRep) { audioNotif(); } else { audioEl.play() } } delay = initDelay; } else if(delay !== 12e4) { delay = Math.min(delay + initDelay, 12e4); } } if(enabled) { loadTO = setTimeout(loadPostsFun, delay); } } function setState(state) { var btn = stateButton || (stateButton = $q('a[id^="de-btn-upd"]', doc)); btn.id = 'de-btn-upd-' + state; btn.title = Lng.panelBtn['upd-' + (state === 'off' ? 'off' : 'on')][lang]; } function onBlur() { focused = false; firstThr.clearPostsMarks(); } function onVis() { if(Cfg['favIcoBlink'] && favHref) { clearInterval(favIntrv); favNorm = true; $del($q('link[rel="shortcut icon"]', doc.head)); doc.head.insertAdjacentHTML('afterbegin', ''); } focused = true; newPosts = 0; setTimeout(function() { updateTitle(); if(enabled) { forceLoadPosts(); } }, 200); } function updateTitle() { doc.title = (aPlayers === 0 ? '' : '♫ ') + (lastECode === 200 ? '' : '{' + lastECode + '} ') + (newPosts === 0 ? '' : '[' + newPosts + '] ') + title; } function addPlayingTag() { aPlayers++; if(aPlayers === 1) { updateTitle(); } } function removePlayingTag() { aPlayers = Math.max(aPlayers - 1, 0); if(aPlayers === 0) { updateTitle(); } } return { get enabled() { return enabled; }, get focused() { return focused; }, forceLoad: forceLoadPosts, enable: function() { if(!inited) { init(); } else if(!enabled) { enable(true); } else { return; } setState('on'); }, disable: function() { disable(true); }, toggleAudio: toggleAudio, addPlayingTag: addPlayingTag, removePlayingTag: removePlayingTag }; } function initPage() { if(Cfg['updScript']) { checkForUpdates(false, function(html) { $alert(html, 'updavail', false); }); } if(TNum) { if(Cfg['rePageTitle']) { if(aib.abu) { window.addEventListener('load', function() { doc.title = '/' + brd + ' - ' + pByNum[TNum].title; }, false); } doc.title = '/' + brd + ' - ' + pByNum[TNum].title; } firstThr.el.insertAdjacentHTML('afterend', '
    >> []
    '); firstThr.el.nextSibling.addEventListener('click', Thread.loadNewPosts, false); } else if(needScroll) { setTimeout(window.scrollTo, 20, 0, 0); } updater = initThreadUpdater(doc.title, TNum && Cfg['ajaxUpdThr']); } //============================================================================================================ // MAIN //============================================================================================================ function addDelformStuff(isLog) { var pNum, post; preloadImages(null); isLog && (Cfg['preLoadImgs'] || Cfg['openImgs']) && $log('Preload images'); embedMP3Links(null); isLog && Cfg['addMP3'] && $log('MP3 links'); youTube.parseLinks(null); isLog && Cfg['addYouTube'] && $log('YouTube links'); if(Cfg['addImgs']) { embedImagesLinks(dForm); isLog && $log('Image links'); } if(Cfg['imgSrcBtns']) { addImagesSearch(dForm); isLog && $log('Sauce buttons'); } if(Cfg['linksNavig'] === 2) { genRefMap(pByNum, !!Cfg['hideRefPsts'], ''); for(pNum in pByNum) { post = pByNum[pNum]; if(post.hasRef) { addRefMap(post, ''); } } isLog && $log('Reflinks map'); } } function doScript(checkDomains) { var initTime = oldTime = Date.now(); if(!Initialization(checkDomains)) { return; } $log('Init'); readCfg(); if(Cfg['disabled']) { addPanel(); scriptCSS(); return; } spells = new Spells(!!Cfg['hideBySpell']); youTube = initYouTube(Cfg['addYouTube'], Cfg['YTubeType'], Cfg['YTubeWidth'], Cfg['YTubeHeigh'], Cfg['YTubeHD'], Cfg['YTubeTitles']); readFavorites(); $log('Read config'); $disp(doc.body); replaceDelform(); $log('Replace delform'); pr = new PostForm($q(aib.qPostForm, doc), false, !liteMode); pByNum = Object.create(null); try { parseDelform(dForm, $Q(aib.qThread, dForm)); } catch(e) { GM_log('DELFORM ERROR:\n' + getPrettyErrorMessage(e)); $disp(doc.body); return; } initDelformAjax(); readViewedPosts(); saveFavorites(); $log('Parse delform'); if(Cfg['keybNavig']) { keyNav = new KeyNavigation(); $log('Init keybinds'); } if(!liteMode) { initPage(); $log('Init page'); addPanel(); $log('Add panel'); } initMessageFunctions(); addDelformStuff(true); scriptCSS(); $disp(doc.body); $log('Apply CSS'); readPosts(); readUserPosts(); checkPostsVisib(); saveUserPosts(); $log('Apply spells'); timeLog.push(Lng.total[lang] + (Date.now() - initTime) + 'ms'); } if(doc.readyState === 'interactive' || doc.readyState === 'complete') { needScroll = false; doScript(true); } else { aib = getImageBoard(true, false); needScroll = true; doc.addEventListener(doc.onmousewheel !== undefined ? "mousewheel" : "DOMMouseScroll", function wheelFunc(e) { needScroll = false; doc.removeEventListener(e.type, wheelFunc, false); }, false); doc.addEventListener('DOMContentLoaded', doScript.bind(null, false), false); } })(window.opera && window.opera.scriptStorage);