diff --git a/.gitignore b/.gitignore index dcfba0ff9..2a6c6bd39 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,11 @@ node_modules/ *.db tmp-crx/ tmp-userscript/ +<<<<<<< HEAD +======= +testbuilds/ +builds/4chan-X.zip +>>>>>>> v3 Gruntfile.js builds/4chan-* -Gruntfile.js \ No newline at end of file +Gruntfile.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 089f6f986..d958a8900 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,26 @@ + +**ccd0** +- `Loop in New Tab` (enabled by default) causes videos opened in a separate tab to loop, and applies your settings for inline expanded videos to them. +- Add WebM support to gallery (currently no controls). +- Add PDF support to gallery, disabled by default. Enable with `PDF in Gallery`. +- Fix behavior of .webm videos expanded within inline quotes. +- Contract thumbnails in quoted previews to avoid crashes caused by videos in quoted previews on some systems. +- Change interface when both `Autoplay` and `Show Controls` are unchecked. In this mode, videos are now activated by clicking on them. The first click expands the video, the second click plays the video, and the third click contracts it. +- Add item `Expand videos` in `Image Expansion` menu, which enables expansion of videos by `Expand All Images`. Disabled by default. Previously videos were expanded. +- Disable autoplay for videos expanded by `Expand All Images`. +- Fix minimum width bug. + +**fgts** +- Update archive list. + +**Nebukazar** +- `Quote Threading` disabled by default +- Added missing titles to Header icons + +**Zixaphir**: +- WebM Thumbnail Replacement. Use at your own risk. +- Bugfixes. + ### v2.9.15 *2014-04-05* @@ -187,8 +210,8 @@ **Spittie** - Check image dimension before uploading -**Vampiricwulf** -- Flash embedding and other Flash features. +## v1.7.0 +*2014-04-06* **Zixaphir** - Update Custom Navigation legend to reflect index mode changes. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8333cd9b7..7890d2777 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,6 @@ ## Reporting bugs and suggestions +<<<<<<< HEAD Reporting bugs: (note that some of these links refer to Mayhem's 4chan X repo to avoid duplication of information resources. Bugs MUST be filed on [our issue tracker](https://github.com/zixaphir/appchan-x/issues), not Mayhem's) 1. Make sure both your **browser** and **Appchan X** are up to date.
@@ -12,6 +13,19 @@ Reporting bugs: (note that some of these links refer to Mayhem's 4chan X repo to 1. Precise steps to reproduce the problem, with the expected and actual results. 2. [Console errors](https://github.com/MayhemYDG/4chan-x/wiki/FAQ#console-errors), if any. 3. Appchan X version, browser variant, browser version, and Greasemonkey version if you are using it. +======= +Reporting bugs: + +1. Make sure both your **browser** and **4chan X** are up to date.
+ Only **Chrome**, **Firefox** and **Opera** are supported.
+ **SRWare Iron**, **Firefox ESR**, **Pale Moon**, **Waterfox**, and other derivatives are not supported, use them at your own risk. +2. Look at the list of [known problems and solutions](https://github.com/MayhemYDG/4chan-x/wiki/FAQ#known-problems). +3. Disable your other extensions & scripts to identify conflicts. +4. If your issue persists, open a [new issue](https://github.com/ccd0/4chan-x/issues) with the following information: + 1. Precise steps to reproduce the problem, with the expected and actual results. + 2. [Console errors](https://github.com/MayhemYDG/4chan-x/wiki/FAQ#console-errors), if any. + 3. 4chan X version, browser variant, browser version, and Greasemonkey version if you are using it. +>>>>>>> v3 4. Your exported settings. If your settings contains sensible information (e.g. personas), edit the text file manually. Respect these guidelines: diff --git a/Gruntfile.coffee b/Gruntfile.coffee index cc725392f..00e7b52a9 100755 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -44,8 +44,7 @@ module.exports = (grunt) -> meta: files: - 'LICENSE': 'src/General/meta/banner.js', - 'latest.js': 'src/General/meta/latest.js' + 'LICENSE': 'src/General/meta/banner.js' crx: files: diff --git a/LICENSE b/LICENSE index 45ddb7f1a..851b8fae1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* -* appchan x - Version 2.9.15 - 2014-04-05 +* appchan x - Version 2.9.15 - 2014-04-11 * * Licensed under the MIT license. * https://github.com/zixaphir/appchan-x/blob/master/LICENSE diff --git a/builds/4chan-X.meta.js b/builds/4chan-X.meta.js index 636db037a..70021551c 100755 --- a/builds/4chan-X.meta.js +++ b/builds/4chan-X.meta.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan X -// @version 1.5.1 +// @version 1.7.7 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X diff --git a/builds/appchan-x.user.js b/builds/appchan-x.user.js index 3d612f04c..59c767c54 100644 --- a/builds/appchan-x.user.js +++ b/builds/appchan-x.user.js @@ -25,7 +25,7 @@ // ==/UserScript== /* -* appchan x - Version 2.9.15 - 2014-04-05 +* appchan x - Version 2.9.15 - 2014-04-11 * * Licensed under the MIT license. * https://github.com/zixaphir/appchan-x/blob/master/LICENSE @@ -113,7 +113,7 @@ 'use strict'; (function() { - var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, Callbacks, CatalogLinks, CatalogThread, Clone, Color, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Flash, Fourchan, Gallery, Get, GlobalMessage, Header, IDColor, ImageExpand, ImageHover, ImageLoader, Index, InfiniScroll, JSColor, Keybinds, Labels, Linkify, Main, MascotTools, Mascots, Menu, Nav, Navigate, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteInline, QuoteMarkers, QuotePreview, QuoteStrikeThrough, QuoteThreading, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Rice, Sauce, Settings, SimpleDict, Style, ThemeTools, Themes, Thread, ThreadExcerpt, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, editMascot, editTheme, g, userNavigation, + var $, $$, Anonymize, ArchiveLink, Banner, Board, Build, Callbacks, CatalogLinks, CatalogThread, Clone, Color, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Flash, Fourchan, Gallery, Get, GlobalMessage, Header, IDColor, ImageExpand, ImageHover, ImageLoader, Index, InfiniScroll, JSColor, Keybinds, Labels, Linkify, Main, MascotTools, Mascots, Menu, Nav, Navigate, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteInline, QuoteMarkers, QuotePreview, QuoteStrikeThrough, QuoteThreading, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Rice, Sauce, Settings, SimpleDict, Style, ThemeTools, Themes, Thread, ThreadExcerpt, ThreadStats, ThreadUpdater, ThreadWatcher, Time, TrashQueue, UI, Unread, Video, c, d, doc, editMascot, editTheme, g, userNavigation, __slice = [].slice, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, __hasProp = {}.hasOwnProperty, @@ -138,7 +138,7 @@ Config = { main: { 'Miscellaneous': { - 'JSON Navigation': [true, 'Use JSON for loading the Board Index and Threads. Also allows searching and sorting the board index and infinite scolling.'], + 'JSON Navigation': [false, 'Use JSON for loading the Board Index and Threads. Also allows searching and sorting the board index and infinite scolling.'], 'Catalog Links': [true, 'Add toggle link in header menu to turn Navigation links into links to each board\'s catalog.'], 'External Catalog': [false, 'Link to external catalog instead of the internal one.'], 'Desktop Notifications': [false, 'Enables desktop notifications across various appchan x features.'], @@ -154,7 +154,8 @@ 'Show Dice Roll': [true, 'Show dice that were entered into the email field.'], 'Color User IDs': [false, 'Assign unique colors to user IDs on boards that use them'], 'Remove Spoilers': [false, 'Remove all spoilers in text.'], - 'Reveal Spoilers': [false, 'Indicate spoilers if Remove Spoilers is enabled, or make the text appear hovered if Remove Spoiler is disabled.'] + 'Reveal Spoilers': [false, 'Indicate spoilers if Remove Spoilers is enabled, or make the text appear hovered if Remove Spoiler is disabled.'], + 'Show Support Message': [true, 'Warn if your browser is unsupported. 4chan X may not operate correctly on unsupported browser versions.'] }, 'Linkification': { 'Linkify': [true, 'Convert text into links where applicable.'], @@ -175,17 +176,20 @@ 'Image Hover': [true, 'Show full image / video on mouseover.'], 'Image Hover in Catalog': [false, 'Show a floating expanded image on hover in the catalog.'], 'Gallery': [true, 'Adds a simple and cute image gallery.'], + 'PDF in Gallery': [false, 'Show PDF files in gallery.'], 'Sauce': [true, 'Add sauce links to images.'], 'Reveal Spoiler Thumbnails': [false, 'Replace spoiler thumbnails with the original image.'], - 'Replace GIF': [false, 'Replace thumbnail of gifs with its actual image.'], - 'Replace PNG': [false, 'Replace pngs.'], - 'Replace JPG': [false, 'Replace jpgs.'], + 'Replace GIF': [false, 'Replace gif thumbnails with the actual image.'], + 'Replace JPG': [false, 'Replace jpg thumbnails with the actual image.'], + 'Replace PNG': [false, 'Replace png thumbnails with the actual image.'], + 'Replace WEBM': [false, 'Replace webm thumbnails with the actual webm video. Probably will degrade browser performance ;)'], 'Image Prefetching': [false, 'Preload images'], 'Fappe Tyme': [false, 'Hide posts without images when toggled. *hint* *hint*'], 'Werk Tyme': [false, 'Hide all post images when toggled.'], - 'Autoplay': [true, 'Videos begin playing immediately when opened inline.'], - 'Show Controls': [true, 'Show native seek and volume controls on videos. Contract videos when dragged to the left.'], - 'Allow Sound': [true, 'Allow sound in inline videos.'] + 'Autoplay': [true, 'Videos begin playing immediately when opened.'], + 'Show Controls': [true, 'Show controls on videos expanded inline. Turn this off if you want to contract videos by clicking on them.'], + 'Allow Sound': [true, 'Allow sound in videos.'], + 'Loop in New Tab': [true, 'Loop videos opened in their own tabs, and apply settings for inline expanded videos to them.'] }, 'Menu': { 'Menu': [true, 'Add a drop-down menu to posts.'], @@ -244,6 +248,7 @@ 'Fit width': [false, ''], 'Fit height': [false, ''], 'Expand spoilers': [true, 'Expand all images along with spoilers.'], + 'Expand videos': [false, 'Expand all images also expands videos (no autoplay).'], 'Expand from here': [false, 'Expand all images only from current position to thread end.'], 'Advance on contract': [false, 'Advance to next post when contracting an expanded image.'] }, @@ -3611,8 +3616,8 @@ return this.board.posts.rm(this); }; - Post.prototype.addClone = function(context) { - return new Clone(this, context); + Post.prototype.addClone = function(context, contractThumb) { + return new Clone(this, context, contractThumb); }; Post.prototype.rmClone = function(index) { @@ -3632,8 +3637,8 @@ Clone = (function(_super) { __extends(Clone, _super); - function Clone(origin, context) { - var file, info, inline, inlined, key, nodes, post, root, val, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3; + function Clone(origin, context, contractThumb) { + var file, info, inline, inlined, key, nodes, post, root, val, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3, _ref4, _ref5; this.origin = origin; this.context = context; _ref = ['ID', 'fullID', 'board', 'thread', 'info', 'quotes', 'isReply']; @@ -3642,7 +3647,7 @@ this[key] = origin[key]; } nodes = origin.nodes; - root = nodes.root.cloneNode(true); + root = contractThumb ? this.cloneWithoutVideo(nodes.root) : nodes.root.cloneNode(true); post = $('.post', root); info = $('.postInfo', post); this.nodes = { @@ -3702,6 +3707,17 @@ this.file.text = file.firstElementChild; this.file.thumb = $('img[data-md5]', file); this.file.fullImage = $('.full-image', file); + if (contractThumb) { + $.rmClass(root, 'expanded-image'); + $.rmClass(this.file.thumb, 'expanding'); + } + this.file.isExpanded = $.hasClass(root, 'expanded-image'); + if ((_ref4 = this.file.fullImage) != null) { + _ref4.removeAttribute('id'); + } + if ((_ref5 = $('.video-controls', this.file.text)) != null) { + _ref5.remove(); + } } if (origin.isDead) { this.isDead = true; @@ -3710,6 +3726,23 @@ root.dataset.clone = origin.clones.push(this) - 1; } + Clone.prototype.cloneWithoutVideo = function(node) { + var child, clone, _i, _len, _ref; + if (node.tagName === 'VIDEO') { + return []; + } else if (node.nodeType === Node.ELEMENT_NODE && $('video', node)) { + clone = node.cloneNode(false); + _ref = node.childNodes; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + child = _ref[_i]; + $.add(clone, this.cloneWithoutVideo(child)); + } + return clone; + } else { + return node.cloneNode(true); + } + }; + return Clone; })(Post); @@ -4580,6 +4613,11 @@ } return bottom; }, + isNodeVisible: function(node) { + var height; + height = node.getBoundingClientRect().height; + return Header.getTopOf(node) + height >= 0 && Header.getBottomOf(node) + height >= 0; + }, isHidden: function() { var top; top = Header.bar.getBoundingClientRect().top; @@ -4850,8 +4888,8 @@ if (Index.req || Conf['Index Mode'] !== 'infinite' || (window.scrollY <= doc.scrollHeight - (300 + window.innerHeight)) || g.VIEW === 'thread') { return; } - Index.pageNum = (Index.pageNum || Index.getCurrentPage()) + 1; - if (Index.pageNum >= Index.pagesNum) { + Index.currentPage = (Index.currentPage || Index.getCurrentPage()) + 1; + if (Index.currentPage >= Index.pagesNum) { return Index.endNotice(); } return Index.buildIndex(true); @@ -5190,8 +5228,8 @@ return Header.scrollToIfNeeded(Index.navLinks); }, getCurrentPage: function() { - if (Conf['Index Mode'] === 'infinite' && Index.pageNum) { - return Index.pageNum; + if (Conf['Index Mode'] === 'infinite' && Index.currentPage) { + return Index.currentPage; } return +window.location.pathname.split('/')[2]; }, @@ -5253,7 +5291,10 @@ }, setPage: function(pageNum) { var a, href, maxPageNum, next, pagesRoot, prev, strong; - pageNum || (pageNum = Index.getCurrentPage()); + if (pageNum == null) { + pageNum = Index.getCurrentPage(); + } + Index.currentPage = pageNum; maxPageNum = Index.getMaxPageNum(); pagesRoot = $('.pages', Index.pagelist); prev = pagesRoot.previousSibling.firstChild; @@ -5314,7 +5355,7 @@ if (!(d.readyState === 'loading' || Index.root.parentElement)) { $.replace($('.board'), Index.root); } - delete Index.pageNum; + Index.currentPage = 0; if ((_ref = Index.req) != null) { _ref.abort(); } @@ -5375,10 +5416,15 @@ } Navigate.title(); try { + pageNum || (pageNum = 0); if (req.status === 200) { Index.parse(req.response, pageNum); } else if (req.status === 304) { - Index.pageNav(pageNum || 0); + if (Index.currentPage === pageNum) { + Index.buildIndex(); + } else { + Index.pageNav(pageNum); + } } } catch (_error) { err = _error; @@ -5401,7 +5447,7 @@ Index.parseThreadList(pages); Index.buildThreads(); Index.sort(); - if (pageNum != null) { + if ((pageNum != null) && Index.currentPage !== pageNum) { Index.pageNav(pageNum); return; } @@ -5627,16 +5673,12 @@ buildIndex: function(infinite) { var i, max, nodes, pageNum, sortedThreads, thread, threads, threadsPerPage; sortedThreads = Index.sortedThreads; + nodes = []; switch (Conf['Index Mode']) { case 'paged': case 'infinite': pageNum = Index.getCurrentPage(); - if (pageNum > Index.getMaxPageNum()) { - Index.pageNav(Index.getMaxPageNum()); - return; - } threadsPerPage = Index.getThreadsNumPerPage(); - nodes = []; threads = []; i = threadsPerPage * pageNum; max = i + threadsPerPage; @@ -5652,7 +5694,6 @@ nodes = Index.buildCatalogViews(); break; default: - nodes = []; i = 0; while (thread = sortedThreads[i++]) { nodes.push(thread.OP.nodes.root.parentNode, $.el('hr')); @@ -5677,9 +5718,11 @@ if (!Index.searchInput.dataset.searching) { Index.searchInput.dataset.searching = 1; Index.pageBeforeSearch = Index.getCurrentPage(); - pageNum = 0; + Index.setPage(pageNum = 0); } else { - pageNum = Index.getCurrentPage(); + if (Conf['Index Mode'] !== 'infinite') { + pageNum = Index.getCurrentPage(); + } } } else { if (!Index.searchInput.dataset.searching) { @@ -6128,7 +6171,7 @@ if (!root.parentNode) { return; } - clone = post.addClone(context); + clone = post.addClone(context, $.hasClass(root, 'dialog')); Clone.callbacks.execute([clone]); nodes = clone.nodes; $.rmAll(nodes.root); @@ -6151,6 +6194,7 @@ $.cache(url, function() { return Get.archivedPost(this, boardID, postID, root, context); }, { + responseType: 'json', withCredentials: url.archive.withCredentials }); } else { @@ -6632,8 +6676,8 @@ return $.set("" + this.id + ".position", this.style.cssText); }; hoverstart = function(_arg) { - var asapTest, cb, el, endEvents, latestEvent, o, offsetX, offsetY, root; - root = _arg.root, el = _arg.el, latestEvent = _arg.latestEvent, endEvents = _arg.endEvents, asapTest = _arg.asapTest, cb = _arg.cb, offsetX = _arg.offsetX, offsetY = _arg.offsetY; + var asapTest, cb, el, endEvents, latestEvent, noRemove, o, offsetX, offsetY, root; + root = _arg.root, el = _arg.el, latestEvent = _arg.latestEvent, endEvents = _arg.endEvents, asapTest = _arg.asapTest, cb = _arg.cb, offsetX = _arg.offsetX, offsetY = _arg.offsetY, noRemove = _arg.noRemove; o = { root: root, el: el, @@ -6645,7 +6689,8 @@ clientHeight: doc.clientHeight, clientWidth: doc.clientWidth, offsetX: offsetX || 45, - offsetY: offsetY || -120 + offsetY: offsetY || -120, + noRemove: noRemove }; o.hover = hover.bind(o); o.hoverend = hoverend.bind(o); @@ -6665,7 +6710,7 @@ $.on(root, 'mousemove', o.hover); o.workaround = function(e) { if (!root.contains(e.target)) { - return o.hoverend(); + return o.hoverend(e); } }; return $.on(doc, 'mousemove', o.workaround); @@ -8952,7 +8997,7 @@ return $.addClass(QR.nodes.el, 'dump'); }, handleBlob: function(urlBlob, header, url) { - var blob, end, endnl, endsc, mime, name, name_end, name_start, start, _ref; + var blob, end, endnl, endsc, mime, name, name_end, name_start, start; name = url.substr(url.lastIndexOf('/') + 1, url.length); start = header.indexOf("Content-Type: ") + 14; endsc = header.substr(start, header.length).indexOf(";"); @@ -8974,9 +9019,6 @@ if (blob.type === null) { return QR.error("Unsupported file type."); } - if (_ref = blob.type, __indexOf.call(QR.mimeTypes, _ref) < 0) { - return QR.error("Unsupported file type."); - } return QR.handleFiles([blob]); }, handleUrl: function() { @@ -9462,6 +9504,9 @@ } QR.status(); QR.error(err); + if (QR.captcha.isEnabled) { + QR.captcha.setup(); + } return; } h1 = $('h1', resDoc); @@ -9492,9 +9537,15 @@ }); postsCount = QR.posts.length - 1; QR.cooldown.auto = postsCount && isReply; + if (QR.captcha.isEnabled && QR.cooldown.auto) { + QR.captcha.setup(); + } if (!(Conf['Persistent QR'] || QR.cooldown.auto)) { QR.close(); } else { + if (QR.posts.length > 1) { + QR.captcha.setup(); + } post.rm(); } QR.cooldown.set({ @@ -9953,9 +10004,6 @@ $.rmClass(QR.nodes.el, 'dump'); } else if (this === QR.selected) { (QR.posts[index - 1] || QR.posts[index + 1]).select(); - if (QR.captcha.isEnabled) { - QR.captcha.setup(); - } } QR.posts.splice(index, 1); return QR.status(); @@ -10232,62 +10280,6 @@ })(); - AutoGIF = { - init: function() { - var _ref; - if (!Conf['Auto-GIF'] || ((_ref = g.BOARD.ID) === 'gif' || _ref === 'wsg')) { - return; - } - Post.callbacks.push({ - name: 'Auto-GIF', - cb: this.node - }); - return CatalogThread.callbacks.push({ - name: 'Auto-GIF', - cb: this.catalogNode - }); - }, - node: function() { - var URL, style, thumb, _ref, _ref1; - if (this.isClone || this.isHidden || this.thread.isHidden || !((_ref = this.file) != null ? _ref.isImage : void 0)) { - return; - } - _ref1 = this.file, thumb = _ref1.thumb, URL = _ref1.URL; - if (!(/gif$/.test(URL) && !/spoiler/.test(thumb.src))) { - return; - } - if (this.file.isSpoiler) { - style = thumb.style; - style.maxHeight = style.maxWidth = this.isReply ? '125px' : '250px'; - } - return AutoGIF.replaceThumbnail(thumb, URL); - }, - catalogNode: function() { - var OP, URL, _ref; - OP = this.thread.OP; - if (!((_ref = OP.file) != null ? _ref.isImage : void 0)) { - return; - } - URL = OP.file.URL; - if (!/gif$/.test(URL)) { - return; - } - return AutoGIF.replaceThumbnail(this.nodes.thumb, URL, true); - }, - replaceThumbnail: function(thumb, URL, isBackground) { - var gif; - gif = $.el('img'); - $.on(gif, 'load', function() { - if (isBackground) { - return thumb.style.backgroundImage = "url(" + URL + ")"; - } else { - return thumb.src = URL; - } - }); - return gif.src = URL; - } - }; - FappeTyme = { init: function() { var el, lc, type, _i, _len, _ref; @@ -10364,8 +10356,7 @@ }); }, node: function() { - var _ref; - if (!((_ref = this.file) != null ? _ref.isImage : void 0)) { + if (!this.file) { return; } if (Gallery.nodes) { @@ -10377,12 +10368,12 @@ } }, build: function(image) { - var cb, createSubEntry, dialog, el, file, files, i, key, menuButton, name, nodes, value, _ref; + var cb, createSubEntry, dialog, el, file, i, key, menuButton, name, nodes, value, _i, _len, _ref, _ref1; Gallery.images = []; nodes = Gallery.nodes = {}; nodes.el = dialog = $.el('div', { id: 'a-gallery', - innerHTML: "
\n \n \uf107\n \uf00d\n \n \n \n / \n \n
\n
\n \n
\n
\n
\n
" + innerHTML: "
\n \n \n ×\n \n \n \n / \n \n
\n
\n \n
\n
\n
\n
" }); _ref = { frame: '.gal-image', @@ -10401,8 +10392,7 @@ nodes.menu = new UI.Menu('gallery'); cb = Gallery.cb; $.on(nodes.frame, 'click', cb.blank); - $.on(nodes.current, 'click', cb.download); - $.on(nodes.next, 'click', cb.next); + $.on(nodes.next, 'click', cb.advance); $.on($('.gal-prev', dialog), 'click', cb.prev); $.on($('.gal-next', dialog), 'click', cb.next); $.on($('.gal-close', dialog), 'click', cb.close); @@ -10420,13 +10410,12 @@ } $.on(d, 'keydown', cb.keybinds); $.off(d, 'keydown', Keybinds.keydown); - i = 0; - files = $$('.post .file'); - while (file = files[i++]) { - if ($('.fileDeletedRes, .fileDeleted', file)) { - continue; + _ref1 = $$('.post .file'); + for (i = _i = 0, _len = _ref1.length; _i < _len; i = ++_i) { + file = _ref1[i]; + if (!$('.fileDeletedRes, .fileDeleted', file)) { + Gallery.generateThumb(file); } - Gallery.generateThumb(file); } $.add(d.body, dialog); nodes.thumbs.scrollTop = 0; @@ -10436,18 +10425,26 @@ return nodes.total.textContent = --i; }, generateThumb: function(file) { - var dupe, post, thumb, title; + var post, thumb, thumbImg, title; post = Get.postFromNode(file); - title = ($('.fileText a', file)).textContent; - thumb = post.file.thumb.parentNode.cloneNode(true); - if (dupe = $('img + img', thumb)) { - $.rm(dupe); + if (!(post.file && (post.file.isImage || post.file.isVideo || Conf['PDF in Gallery']))) { + return; } - thumb.className = 'gal-thumb'; - thumb.title = title; + title = ($('.fileText a', file)).textContent; + thumb = $.el('a', { + className: 'gal-thumb', + href: post.file.URL, + target: '_blank', + title: title + }); thumb.dataset.id = Gallery.images.length; thumb.dataset.post = $('a[title="Highlight this post"]', post.nodes.info).href; - thumb.firstElementChild.style.cssText = ''; + if (post.file.isVideo) { + thumb.dataset.isVideo = true; + } + thumbImg = post.file.thumb.cloneNode(false); + thumbImg.style.cssText = ''; + $.add(thumb, thumbImg); $.on(thumb, 'click', Gallery.cb.open); Gallery.images.push(thumb); return $.add(Gallery.nodes.thumbs, thumb); @@ -10464,8 +10461,9 @@ case Conf['Open Gallery']: return Gallery.cb.close; case 'Right': - case 'Enter': return Gallery.cb.next; + case 'Enter': + return Gallery.cb.advance; case 'Left': case '': return Gallery.cb.prev; @@ -10479,7 +10477,7 @@ return cb(); }, open: function(e) { - var el, img, name, nodes, rect, top; + var el, elType, file, name, nodes, rect, top, _base; if (e) { e.preventDefault(); } @@ -10492,14 +10490,22 @@ $.rmClass(el, 'gal-highlight'); } $.addClass(this, 'gal-highlight'); - img = $.el('img', { + elType = this.dataset.isVideo ? 'video' : /\.pdf$/.test(this.href) ? 'iframe' : 'img'; + $[elType === 'iframe' ? 'addClass' : 'rmClass'](nodes.el, 'gal-pdf'); + file = $.el(elType, { src: name.href = this.href, title: name.download = name.textContent = this.title }); - $.extend(img.dataset, this.dataset); - $.replace(nodes.current, img); + $.extend(file.dataset, this.dataset); + if (typeof (_base = nodes.current).pause === "function") { + _base.pause(); + } + $.replace(nodes.current, file); + if (this.dataset.isVideo) { + Video.configure(file); + } nodes.count.textContent = +this.dataset.id + 1; - nodes.current = img; + nodes.current = file; nodes.frame.scrollTop = 0; nodes.next.focus(); rect = this.getBoundingClientRect(); @@ -10511,8 +10517,8 @@ } } nodes.thumbs.scrollTop += top; - return $.on(img, 'error', function() { - return Gallery.cb.error(img, thumb); + return $.on(file, 'error', function() { + return Gallery.cb.error(file, thumb); }); }, image: function(e) { @@ -10521,13 +10527,13 @@ return Gallery.build(this); }, error: function(img, thumb) { - var URL, post, revived, src; + var URL, post, src; post = Get.postFromLink($.el('a', { href: img.dataset.post })); delete post.file.fullImage; src = this.src.split('/'); - if (src[2] === 'images.4chan.org') { + if (src[2] === 'i.4cdn.org') { URL = Redirect.to('file', { boardID: src[3], filename: src[5] @@ -10537,19 +10543,14 @@ if (Gallery.nodes.current !== img) { return; } - revived = $.el('img', { - src: URL, - title: img.title - }); - $.extend(revived.dataset, img.dataset); - $.replace(img, revived); + img.src = URL; return; } if (g.DEAD || post.isDead || post.file.isDead) { return; } } - return $.ajax("//api.4chan.org/" + post.board + "/res/" + post.thread + ".json", { + return $.ajax("//a.4cdn.org/" + post.board + "/res/" + post.thread + ".json", { onload: function() { var i, postObj, posts; if (this.status !== 200) { @@ -10585,7 +10586,27 @@ return Gallery.cb.close(); } }, + advance: function() { + if (Gallery.nodes.current.controls) { + return; + } + if (Gallery.nodes.current.paused) { + return Gallery.nodes.current.play(); + } + return Gallery.cb.next(); + }, + pause: function() { + var current; + current = Gallery.nodes.current; + if (current.nodeType === 'VIDEO') { + return current[current.paused ? 'play' : 'pause'](); + } + }, close: function() { + var _base; + if (typeof (_base = Gallery.nodes.current).pause === "function") { + _base.pause(); + } $.rm(Gallery.nodes.el); delete Gallery.nodes; d.body.style.overflow = ''; @@ -10639,7 +10660,7 @@ ImageExpand = { init: function() { - if (!Conf['Image Expansion']) { + if (g.VIEW === 'catalog' || !Conf['Image Expansion']) { return; } this.EAI = $.el('a', { @@ -10656,54 +10677,73 @@ }); }, node: function() { - var thumb; - if (!(this.file && (this.file.isImage || this.file.isVideo))) { + var clone, thumb, _ref, _ref1; + if (!(((_ref = this.file) != null ? _ref.isImage : void 0) || ((_ref1 = this.file) != null ? _ref1.isVideo : void 0))) { return; } thumb = this.file.thumb; $.on(thumb.parentNode, 'click', ImageExpand.cb.toggle); - if (this.isClone && $.hasClass(thumb, 'expanding')) { - ImageExpand.contract(this); - ImageExpand.expand(this); - return; - } - if (ImageExpand.on && !this.isHidden && (Conf['Expand spoilers'] || !this.file.isSpoiler)) { - return ImageExpand.expand(this); + if (this.isClone) { + if ($.hasClass(thumb, 'expanding')) { + ImageExpand.contract(this); + ImageExpand.expand(this); + } else if (this.file.isExpanded && this.file.isVideo) { + clone = this; + ImageExpand.setupVideoControls(clone); + if (!clone.origin.file.fullImage.paused) { + $.queueTask(function() { + return Video.start(clone.file.fullImage); + }); + } + } + } else if (ImageExpand.on && !this.isHidden && (Conf['Expand spoilers'] || !this.file.isSpoiler) && (Conf['Expand videos'] || !this.file.isVideo)) { + return ImageExpand.expand(this, null, true); } }, cb: { toggle: function(e) { + var post, _ref; if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) { return; } + post = Get.postFromNode(this); + if (post.file.isExpanded && ((_ref = post.file.fullImage) != null ? _ref.controls : void 0)) { + return; + } e.preventDefault(); - return ImageExpand.toggle(Get.postFromNode(this)); + return ImageExpand.toggle(post); }, toggleAll: function() { - var func; + var func, toggle; $.event('CloseMenu'); + toggle = function(post) { + var file; + file = post.file; + if (!(file && (file.isImage || file.isVideo) && doc.contains(post.nodes.root))) { + return; + } + if (ImageExpand.on && (!Conf['Expand spoilers'] && file.isSpoiler || !Conf['Expand videos'] && file.isVideo || Conf['Expand from here'] && Header.getTopOf(file.thumb) < 0)) { + return; + } + return $.queueTask(func, post); + }; if (ImageExpand.on = $.hasClass(ImageExpand.EAI, 'expand-all-shortcut')) { ImageExpand.EAI.className = 'contract-all-shortcut a-icon'; ImageExpand.EAI.title = 'Contract All Images'; - func = ImageExpand.expand; + func = function(post) { + return ImageExpand.expand(post, null, true); + }; } else { ImageExpand.EAI.className = 'expand-all-shortcut a-icon'; ImageExpand.EAI.title = 'Expand All Images'; func = ImageExpand.contract; } return g.posts.forEach(function(post) { - var file, _i, _len, _ref; - _ref = [post].concat(post.clones); + var _i, _len, _ref; + _ref = [post].concat(__slice.call(post.clones)); for (_i = 0, _len = _ref.length; _i < _len; _i++) { post = _ref[_i]; - file = post.file; - if (!(file && (file.isImage || file.isVideo) && doc.contains(post.nodes.root))) { - return; - } - if (ImageExpand.on && !post.isHidden && (!Conf['Expand spoilers'] && file.isSpoiler || Conf['Expand from here'] && Header.getTopOf(file.thumb) < 0)) { - return; - } - $.queueTask(func, post); + toggle(post); } }); }, @@ -10712,9 +10752,8 @@ } }, toggle: function(post) { - var headRect, left, root, thumb, top, x, y, _ref; - thumb = post.file.thumb; - if (!(post.file.isExpanded || $.hasClass(thumb, 'expanding'))) { + var headRect, left, root, top, x, y, _ref; + if (!(post.file.isExpanded || $.hasClass(post.file.thumb, 'expanding'))) { ImageExpand.expand(post); return; } @@ -10746,85 +10785,126 @@ return ImageExpand.contract(post); }, contract: function(post) { - var _ref; + var cb, eventName, thumb, video, _ref; + thumb = post.file.thumb; + if (post.file.isVideo && (video = post.file.fullImage)) { + video.pause(); + TrashQueue.add(video, post); + thumb.parentNode.href = video.src; + thumb.parentNode.target = '_blank'; + _ref = ImageExpand.videoCB; + for (eventName in _ref) { + cb = _ref[eventName]; + $.off(video, eventName, cb); + } + $.rm(post.file.videoControls); + delete post.file.videoControls; + } $.rmClass(post.nodes.root, 'expanded-image'); - $.rmClass(post.file.thumb, 'expanding'); - post.file.isExpanded = false; - if (post.file.isVideo) { - post.file.fullImage.pause(); - } - if ((_ref = post.file.videoControls) != null) { - _ref.map($.rm); - } - return delete post.file.videoControls; + $.rmClass(thumb, 'expanding'); + return post.file.isExpanded = false; }, - expand: function(post, src) { - var file, isVideo, thumb, _ref; + expand: function(post, src, disableAutoplay) { + var el, isVideo, thumb, _ref; _ref = post.file, thumb = _ref.thumb, isVideo = _ref.isVideo; if (post.isHidden || post.file.isExpanded || $.hasClass(thumb, 'expanding')) { return; } $.addClass(thumb, 'expanding'); - if (post.file.fullImage) { - $.asap((function() { - var file; - return (file = post.file.fullImage) && (file.videoHeight || file.naturalHeight); - }), function() { - return ImageExpand.completeExpand(post); + if (el = post.file.fullImage) { + TrashQueue.remove(el); + } else { + el = post.file.fullImage = $.el((isVideo ? 'video' : 'img'), { + className: 'full-image' }); - return; - } - file = $.el((post.file.isImage ? 'img' : 'video'), { - className: 'full-image', - src: src || post.file.URL - }); - post.file.fullImage = file; - if (isVideo) { - file.loop = true; - file.controls = Conf['Show Controls']; - } - $.on(file, 'error', ImageExpand.error); - $.asap((function() { - return (file = post.file.fullImage) && (file.videoHeight || file.naturalHeight); - }), function() { + $.on(el, 'error', ImageExpand.error); + el.src = src || post.file.URL; if (isVideo) { - file.style.maxHeight = file.videoHeight + "px"; - file.style.maxWidth = file.videoWidth + "px"; + el.loop = true; } - return ImageExpand.completeExpand(post); + } + if (el !== thumb.nextSibling) { + $.after(thumb, el); + } + return $.asap((function() { + return el.videoHeight || el.naturalHeight; + }), function() { + return ImageExpand.completeExpand(post, disableAutoplay); }); - return $.after((file.controls ? thumb.parentNode : thumb), file); }, - completeExpand: function(post) { - var bottom, thumb; - thumb = post.file.thumb; - if (!$.hasClass(thumb, 'expanding')) { + completeExpand: function(post, disableAutoplay) { + var bottom; + if (!$.hasClass(post.file.thumb, 'expanding')) { return; } - post.file.isExpanded = true; - if (post.file.isVideo) { - ImageExpand.setupVideo(post); - } - $.addClass(post.nodes.root, 'expanded-image'); - $.rmClass(post.file.thumb, 'expanding'); if (!post.nodes.root.parentNode) { + ImageExpand.completeExpand2(post); return; } bottom = post.nodes.root.getBoundingClientRect().bottom; return $.queueTask(function() { + ImageExpand.completeExpand2(post, disableAutoplay); if (!(bottom <= 0)) { return; } return window.scrollBy(0, post.nodes.root.getBoundingClientRect().bottom - bottom); }); }, - setupVideo: function(post) { - var contract, file, play, video; + completeExpand2: function(post, disableAutoplay) { + $.addClass(post.nodes.root, 'expanded-image'); + $.rmClass(post.file.thumb, 'expanding'); + post.file.isExpanded = true; + if (post.file.isVideo) { + ImageExpand.setupVideoControls(post); + return Video.configure(post.file.fullImage, disableAutoplay); + } + }, + videoCB: (function() { + var mousedown; + mousedown = false; + return { + mouseover: function() { + return mousedown = false; + }, + mousedown: function(e) { + if (e.button === 0) { + return mousedown = true; + } + }, + mouseup: function(e) { + if (e.button === 0) { + return mousedown = false; + } + }, + mouseout: function(e) { + if (mousedown && e.clientX <= this.getBoundingClientRect().left) { + return ImageExpand.contract(Get.postFromNode(this)); + } + }, + click: function(e) { + if (this.paused && !this.controls) { + this.play(); + return e.preventDefault(); + } + } + }; + })(), + setupVideoControls: function(post) { + var cb, contract, eventName, file, thumb, video, _ref; file = post.file; + thumb = file.thumb; video = file.fullImage; - file.videoControls = []; - video.muted = !Conf['Allow Sound']; - if (video.controls) { + file.thumb.parentNode.removeAttribute('href'); + file.thumb.parentNode.removeAttribute('target'); + _ref = ImageExpand.videoCB; + for (eventName in _ref) { + cb = _ref[eventName]; + $.on(video, eventName, cb); + } + file.videoControls = $.el('span', { + className: 'video-controls' + }); + if (Conf['Show Controls']) { contract = $.el('a', { textContent: 'contract', href: 'javascript:;', @@ -10833,39 +10913,7 @@ $.on(contract, 'click', function(e) { return ImageExpand.contract(post); }); - file.videoControls.push($.tn('\u00A0'), contract); - file.mousedown = false; - $.on(video, 'mousedown', function(e) { - if (e.button === 0) { - return file.mousedown = true; - } - }); - $.on(video, 'mouseup', function(e) { - if (e.button === 0) { - return file.mousedown = false; - } - }); - $.on(video, 'mouseover', function(e) { - return file.mousedown = false; - }); - $.on(video, 'mouseout', function(e) { - if (file.mousedown && e.clientX <= video.getBoundingClientRect().left) { - return ImageExpand.contract(post); - } - }); - } - if (Conf['Autoplay']) { - video.play(); - } else if (!video.controls) { - play = $.el('a', { - textContent: 'play', - href: 'javascript:;' - }); - $.on(play, 'click', function(e) { - video[this.textContent](); - return this.textContent = this.textContent === 'play' ? 'pause' : 'play'; - }); - file.videoControls.push($.tn('\u00A0'), play); + $.add(file.videoControls, [$.tn('\u00A0'), contract]); } return $.add(file.text, file.videoControls); }, @@ -10919,7 +10967,7 @@ menu: { init: function() { var conf, createSubEntry, el, name, subEntries, _ref; - if (!Conf['Image Expansion']) { + if (g.VIEW === 'catalog' || !Conf['Image Expansion']) { return; } el = $.el('span', { @@ -10976,40 +11024,68 @@ } }, node: function() { - if (!(this.file && (this.file.isImage || this.file.isVideo))) { + var _ref, _ref1; + if (!(((_ref = this.file) != null ? _ref.isImage : void 0) || ((_ref1 = this.file) != null ? _ref1.isVideo : void 0))) { return; } return $.on(this.file.thumb, 'mouseover', ImageHover.mouseover); }, catalogNode: function() { - if (!(this.thread.OP.file && (this.thread.OP.file.isImage || this.thread.OP.file.isVideo))) { + var file; + if (!((file = this.thread.OP.file) && (file.isImage || file.isVideo))) { return; } return $.on(this.nodes.thumb, 'mouseover', ImageHover.mouseover); }, mouseover: function(e) { - var el, post; + var el, isVideo, post, thumb; post = $.hasClass(this, 'thumb') ? g.posts[this.parentNode.dataset.fullID] : Get.postFromNode(this); - el = post.file.isImage ? $.el('img', { - id: 'ihover', - src: post.file.URL - }) : $.el('video', { - controls: false, - id: 'ihover', - src: post.file.URL, - autoplay: Conf['Autoplay'], - muted: !Conf['Allow Sound'], - loop: true - }); - $.add(Header.hover, el); + isVideo = post.file.isVideo; + if (post.file.fullImage) { + el = post.file.fullImage; + TrashQueue.remove(el); + } else { + el = $.el((isVideo ? 'video' : 'img'), { + className: 'full-image', + src: post.file.URL + }); + post.file.fullImage = el; + thumb = post.file.thumb; + } + if (d.body.contains(thumb)) { + if (el !== thumb.nextSibling) { + $.after(thumb, el); + } + } else { + if (el.parentNode !== Header.hover) { + $.add(Header.hover, el); + } + } + el.id = 'ihover'; el.dataset.fullID = post.fullID; + if (isVideo) { + el.loop = true; + el.controls = false; + el.muted = !Conf['Allow Sound']; + if (Conf['Autoplay']) { + el.play(); + } + } UI.hover({ root: this, el: el, latestEvent: e, endEvents: 'mouseout click', asapTest: function() { - return post.file.isVideo || el.naturalHeight; + return el.videoHeight || el.naturalHeight; + }, + noRemove: true, + cb: function() { + if (isVideo) { + el.pause(); + TrashQueue.add(el, post); + } + return el.removeAttribute('id'); } }); return $.on(el, 'error', ImageHover.error); @@ -11067,7 +11143,7 @@ ImageLoader = { init: function() { var prefetch; - if (!(Conf["Image Prefetching"] || Conf["Replace JPG"] || Conf["Replace PNG"] || Conf["Replace GIF"])) { + if (!(Conf["Image Prefetching"] || Conf["Replace JPG"] || Conf["Replace PNG"] || Conf["Replace GIF"] || Conf["Replace WEBM"])) { return; } Post.callbacks.push({ @@ -11078,9 +11154,6 @@ name: 'Image Replace', cb: this.thread }); - if (!(Conf['Image Prefetching'] && g.VIEW === 'thread')) { - return; - } prefetch = $.el('label', { innerHTML: ' Prefetch Images' }); @@ -11096,31 +11169,59 @@ return ImageLoader.thread = this; }, node: function() { - var URL, img, string, style, thumb, type, _ref, _ref1; - if (this.isClone || this.isHidden || this.thread.isHidden || !((_ref = this.file) != null ? _ref.isImage : void 0)) { + var URL, cb, file, isImage, isVideo, match, replace, style, thumb, type, _ref, _ref1; + if (!this.file) { + return; + } + _ref = this.file, isImage = _ref.isImage, isVideo = _ref.isVideo; + if (this.isClone || this.isHidden || this.thread.isHidden || !(isImage || isVideo)) { return; } _ref1 = this.file, thumb = _ref1.thumb, URL = _ref1.URL; - if (!((Conf[string = "Replace " + ((type = (URL.match(/\w{3}$/))[0].toUpperCase()) === 'PEG' ? 'JPG' : type)] && !/spoiler/.test(thumb.src)) || Conf['prefetch'])) { + style = thumb.style; + type = (match = URL.match(/\.([^.]+)$/)[1].toUpperCase()) === 'JPEG' ? 'JPG' : match; + replace = "Replace " + type; + if (!((Conf[replace] && !/spoiler/.test(thumb.src)) || (Conf['prefetch'] && g.VIEW === 'thread'))) { return; } if (this.file.isSpoiler) { - style = thumb.style; style.maxHeight = style.maxWidth = this.isReply ? '125px' : '250px'; } - img = $.el('img'); - if (Conf[string]) { - $.on(img, 'load', function() { - return thumb.src = URL; - }); + file = $.el(isImage ? 'img' : 'video'); + if (Conf[replace]) { + if (isVideo) { + file.alt = thumb.alt; + file.dataset.md5 = thumb.dataset.md5; + file.style.height = style.height; + file.style.width = style.width; + file.style.maxHeight = style.maxHeight; + file.style.maxWidth = style.maxWidth; + file.loop = true; + file.autoplay = Conf['Autoplay']; + if (Conf['Image Hover']) { + $.on(file, 'mouseover', ImageHover.mouseover); + } + } + cb = (function(_this) { + return function() { + $.off(file, 'load loadedmetadata', cb); + if (isVideo) { + $.replace(thumb, file); + _this.file.thumb = file; + return; + } + return thumb.src = URL; + }; + })(this); + $.on(file, 'load loadedmetadata', cb); } - return img.src = URL; + return file.src = URL; }, toggle: function() { var enabled; enabled = Conf['prefetch'] = this.checked; if (enabled) { - ImageLoader.thread.posts.forEach(ImageLoader.node.call); + g.BOARD.posts.forEach(ImageLoader.node.call); } } }; @@ -11207,6 +11308,55 @@ } }; + TrashQueue = { + init: function() {}, + add: function(video, post) { + var _ref, _ref1; + if (this.killNext && video !== this.killNext) { + if ((_ref = this.killNextPost) != null) { + if ((_ref1 = _ref.file) != null) { + delete _ref1.fullImage; + } + } + $.rm(this.killNext); + } + this.killNext = video; + return this.killNextPost = post; + }, + remove: function(video) { + if (video === this.killNext) { + return delete this.killNext; + } + } + }; + + Video = { + configure: function(video, disableAutoplay) { + video.loop = true; + video.muted = !Conf['Allow Sound']; + video.controls = Conf['Show Controls']; + video.autoplay = false; + if (Conf['Autoplay'] && !disableAutoplay) { + return Video.start(video); + } else { + return video.pause(); + } + }, + start: function(video) { + var controls; + controls = video.controls; + video.controls = false; + video.play(); + if (controls) { + return $.asap((function() { + return (video.readyState >= 3 && video.currentTime <= Math.max(0.1, video.duration - 0.5)) || !d.contains(video); + }), function() { + return video.controls = true; + }, 500); + } + } + }; + ArchiveLink = { init: function() { var div, entry, type, _i, _len, _ref; @@ -12940,7 +13090,7 @@ } return Redirect.data = o; }, - archives: [{"uid":0,"name":"Foolz","domain":"archive.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["a","biz","co","diy","gd","jp","m","sci","sp","tg","tv","v","vg","vp","vr","wsg"],"files":["a","biz","gd","diy","jp","m","sci","tg","vg","vp","vr","wsg"]},{"uid":1,"name":"NSFW Foolz","domain":"nsfw.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["u"],"files":["u"]},{"uid":2,"name":"The Dark Cave","domain":"archive.thedarkcave.org","http":true,"https":true,"software":"foolfuuka","boards":["c","int","out","po"],"files":["c","po"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":18,"name":"4plebs Flash Archive","domain":"flash.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["f"],"files":["f"]},{"uid":4,"name":"Nyafuu","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["d","i"],"files":["d","i"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":true,"https":true,"software":"fuuka","boards":["cgl","g","mu","w"],"files":["cgl","g","mu","w"]},{"uid":9,"name":"Heinessen","domain":"archive.heinessen.com","http":true,"https":false,"software":"fuuka","boards":["an","fit","k","mlp","r9k","toy"],"files":["an","fit","k","r9k","toy"]},{"uid":10,"name":"warosu","domain":"fuuka.warosu.org","http":false,"https":true,"software":"fuuka","boards":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"],"files":["3","biz","cgl","ck","diy","fa","ic","jp","lit","sci","tg","vr"]},{"uid":15,"name":"fgts","domain":"fgts.eu","http":true,"https":true,"software":"foolfuuka","boards":["cm","hm","r","soc","y"],"files":["cm","hm","r","soc","y"]},{"uid":16,"name":"maware","domain":"archive.mawa.re","http":true,"https":false,"software":"foolfuuka","boards":["t"],"files":["t"]},{"uid":17,"name":"installgentoo.com","domain":"chan.installgentoo.com","http":true,"https":false,"software":"foolfuuka","boards":["g","t"],"files":["g","t"]},{"uid":13,"name":"Foolz Beta","domain":"beta.foolz.us","http":true,"https":true,"withCredentials":true,"software":"foolfuuka","boards":["a","biz","co","d","diy","gd","jp","m","s4s","sci","sp","tg","tv","u","v","vg","vp","vr","wsg"],"files":["a","biz","d","diy","gd","jp","m","s4s","sci","tg","u","vg","vp","vr","wsg"]}], + archives: [{"uid":0,"name":"Foolz","domain":"archive.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["a","biz","co","diy","gd","jp","m","sci","sp","tg","tv","v","vg","vp","vr","wsg"],"files":["a","biz","gd","diy","jp","m","sci","tg","vg","vp","vr","wsg"]},{"uid":1,"name":"NSFW Foolz","domain":"nsfw.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["u"],"files":["u"]},{"uid":2,"name":"The Dark Cave","domain":"archive.thedarkcave.org","http":true,"https":true,"software":"foolfuuka","boards":["c","int","out","po"],"files":["c","po"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":18,"name":"4plebs Flash Archive","domain":"flash.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["f"],"files":["f"]},{"uid":4,"name":"Nyafuu","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["d","i"],"files":["d","i"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":true,"https":true,"software":"fuuka","boards":["cgl","g","mu","w"],"files":["cgl","g","mu","w"]},{"uid":9,"name":"Heinessen","domain":"archive.heinessen.com","http":true,"https":false,"software":"fuuka","boards":["an","fit","k","mlp","r9k","toy"],"files":["an","fit","k","r9k","toy"]},{"uid":10,"name":"warosu","domain":"fuuka.warosu.org","http":false,"https":true,"software":"fuuka","boards":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"],"files":["3","biz","cgl","ck","diy","fa","ic","jp","lit","sci","tg","vr"]},{"uid":15,"name":"fgts","domain":"fgts.eu","http":true,"https":true,"software":"foolfuuka","boards":["cm","h","hc","hm","r","s","soc","y"],"files":["cm","h","hc","hm","r","s","soc","y"]},{"uid":16,"name":"maware","domain":"archive.mawa.re","http":true,"https":false,"software":"foolfuuka","boards":["t"],"files":["t"]},{"uid":17,"name":"installgentoo.com","domain":"chan.installgentoo.com","http":true,"https":false,"software":"foolfuuka","boards":["g","t"],"files":["g","t"]},{"uid":13,"name":"Foolz Beta","domain":"beta.foolz.us","http":true,"https":true,"withCredentials":true,"software":"foolfuuka","boards":["a","biz","co","d","diy","gd","jp","m","s4s","sci","sp","tg","tv","u","v","vg","vp","vr","wsg"],"files":["a","biz","d","diy","gd","jp","m","s4s","sci","tg","u","vg","vp","vr","wsg"]}], to: function(dest, data) { var archive; archive = (dest === 'search' || dest === 'board' ? Redirect.data.thread : Redirect.data[dest])[data.boardID]; @@ -13094,8 +13244,8 @@ "http": true, "https": true, "software": "foolfuuka", - "boards": ["cm", "hm", "r", "soc", "y"], - "files": ["cm", "hm", "r", "soc", "y"] + "boards": ["cm", "h", "hc", "hm", "r", "s", "soc", "y"], + "files": ["cm", "h", "hc", "hm", "r", "s", "soc", "y"] }, { "uid": 16, "name": "maware", @@ -15027,7 +15177,7 @@ } a = $.el('a', { textContent: ExpandThread.text.apply(ExpandThread, ['+'].concat(__slice.call(summary.textContent.match(/\d+/g)))), - href: "" + thread.board.ID + "/res/" + thread.ID, + href: "res/" + thread.ID, className: 'summary' }); $.on(a, 'click', ExpandThread.cbToggle); @@ -16353,17 +16503,22 @@ $.addClass(doc, view); ({ index: function() { + var _ref; delete g.THREADID; if (Conf['Index Mode'] === 'catalog') { - return Index.cb.toggleCatalogMode(); + Index.cb.toggleCatalogMode(); } + return (_ref = QR.posts[0]) != null ? _ref.thread = 'new' : void 0; }, thread: function() { + var _ref; if (Conf['Index Mode'] === 'catalog') { - return $.rmClass(doc, 'catalog-mode'); + $.rmClass(doc, 'catalog-mode'); } + return (_ref = QR.posts[0]) != null ? _ref.thread = g.THREADID : void 0; } })[view](); + QR.status(); return g.VIEW = view; }, updateBoard: function(boardID) { @@ -16646,6 +16801,7 @@ var addSection, arr, check, el, settings, _i, _len, _ref; el = $.el('a', { className: 'settings-link', + title: 'Appchan X Settings', href: 'javascript:;', textContent: 'Settings' }); @@ -17771,7 +17927,7 @@ }); }, initFeatures: function() { - var href, init, _ref; + var href, init, video, _ref; Favicon.el.type = 'image/x-icon'; href = Favicon.el.href; Favicon.SFW = /ws\.ico$/.test(href); @@ -17800,6 +17956,18 @@ Report.init(); return; case 'i.4cdn.org': + if (Conf['Loop in New Tab'] && (video = $('video'))) { + Video.configure(video); + $.on(video, 'click', function() { + if (!video.controls) { + if (video.paused) { + return video.play(); + } else { + return video.pause(); + } + } + }); + } $.ready(function() { var URL, pathname, _ref1; if (Conf['404 Redirect'] && ((_ref1 = d.title) === '4chan - Temporarily Offline' || _ref1 === '4chan - 404 Not Found')) { @@ -17924,7 +18092,7 @@ $.event('4chanXInitFinished'); test = $.el('span'); test.classList.add('a', 'b'); - if (test.className !== 'a b') { + if (test.className !== 'a b' && Conf['Show Support Message']) { new Notice('warning', "Your version of Firefox is outdated (v26 minimum) and appchan x may not operate correctly.", 30); } GMver = GM_info.version.split('.'); diff --git a/builds/crx.crx b/builds/crx.crx index a2a647a0a..a908dabcc 100644 Binary files a/builds/crx.crx and b/builds/crx.crx differ diff --git a/builds/crx/script.js b/builds/crx/script.js index 0d9178bc7..4143357d2 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript /* -* appchan x - Version 2.9.15 - 2014-04-05 +* appchan x - Version 2.9.15 - 2014-04-11 * * Licensed under the MIT license. * https://github.com/zixaphir/appchan-x/blob/master/LICENSE @@ -88,7 +88,7 @@ 'use strict'; (function() { - var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, Callbacks, CatalogLinks, CatalogThread, Clone, Color, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Flash, Fourchan, Gallery, Get, GlobalMessage, Header, IDColor, ImageExpand, ImageHover, ImageLoader, Index, InfiniScroll, JSColor, Keybinds, Labels, Linkify, Main, MascotTools, Mascots, Menu, Nav, Navigate, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteInline, QuoteMarkers, QuotePreview, QuoteStrikeThrough, QuoteThreading, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Rice, Sauce, Settings, SimpleDict, Style, ThemeTools, Themes, Thread, ThreadExcerpt, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, editMascot, editTheme, g, userNavigation, + var $, $$, Anonymize, ArchiveLink, Banner, Board, Build, Callbacks, CatalogLinks, CatalogThread, Clone, Color, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Flash, Fourchan, Gallery, Get, GlobalMessage, Header, IDColor, ImageExpand, ImageHover, ImageLoader, Index, InfiniScroll, JSColor, Keybinds, Labels, Linkify, Main, MascotTools, Mascots, Menu, Nav, Navigate, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteInline, QuoteMarkers, QuotePreview, QuoteStrikeThrough, QuoteThreading, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Rice, Sauce, Settings, SimpleDict, Style, ThemeTools, Themes, Thread, ThreadExcerpt, ThreadStats, ThreadUpdater, ThreadWatcher, Time, TrashQueue, UI, Unread, Video, c, d, doc, editMascot, editTheme, g, userNavigation, __slice = [].slice, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, __hasProp = {}.hasOwnProperty, @@ -113,7 +113,7 @@ Config = { main: { 'Miscellaneous': { - 'JSON Navigation': [true, 'Use JSON for loading the Board Index and Threads. Also allows searching and sorting the board index and infinite scolling.'], + 'JSON Navigation': [false, 'Use JSON for loading the Board Index and Threads. Also allows searching and sorting the board index and infinite scolling.'], 'Catalog Links': [true, 'Add toggle link in header menu to turn Navigation links into links to each board\'s catalog.'], 'External Catalog': [false, 'Link to external catalog instead of the internal one.'], 'Desktop Notifications': [false, 'Enables desktop notifications across various appchan x features.'], @@ -129,7 +129,8 @@ 'Show Dice Roll': [true, 'Show dice that were entered into the email field.'], 'Color User IDs': [false, 'Assign unique colors to user IDs on boards that use them'], 'Remove Spoilers': [false, 'Remove all spoilers in text.'], - 'Reveal Spoilers': [false, 'Indicate spoilers if Remove Spoilers is enabled, or make the text appear hovered if Remove Spoiler is disabled.'] + 'Reveal Spoilers': [false, 'Indicate spoilers if Remove Spoilers is enabled, or make the text appear hovered if Remove Spoiler is disabled.'], + 'Show Support Message': [true, 'Warn if your browser is unsupported. 4chan X may not operate correctly on unsupported browser versions.'] }, 'Linkification': { 'Linkify': [true, 'Convert text into links where applicable.'], @@ -150,17 +151,20 @@ 'Image Hover': [true, 'Show full image / video on mouseover.'], 'Image Hover in Catalog': [false, 'Show a floating expanded image on hover in the catalog.'], 'Gallery': [true, 'Adds a simple and cute image gallery.'], + 'PDF in Gallery': [false, 'Show PDF files in gallery.'], 'Sauce': [true, 'Add sauce links to images.'], 'Reveal Spoiler Thumbnails': [false, 'Replace spoiler thumbnails with the original image.'], - 'Replace GIF': [false, 'Replace thumbnail of gifs with its actual image.'], - 'Replace PNG': [false, 'Replace pngs.'], - 'Replace JPG': [false, 'Replace jpgs.'], + 'Replace GIF': [false, 'Replace gif thumbnails with the actual image.'], + 'Replace JPG': [false, 'Replace jpg thumbnails with the actual image.'], + 'Replace PNG': [false, 'Replace png thumbnails with the actual image.'], + 'Replace WEBM': [false, 'Replace webm thumbnails with the actual webm video. Probably will degrade browser performance ;)'], 'Image Prefetching': [false, 'Preload images'], 'Fappe Tyme': [false, 'Hide posts without images when toggled. *hint* *hint*'], 'Werk Tyme': [false, 'Hide all post images when toggled.'], - 'Autoplay': [true, 'Videos begin playing immediately when opened inline.'], - 'Show Controls': [true, 'Show native seek and volume controls on videos. Contract videos when dragged to the left.'], - 'Allow Sound': [true, 'Allow sound in inline videos.'] + 'Autoplay': [true, 'Videos begin playing immediately when opened.'], + 'Show Controls': [true, 'Show controls on videos expanded inline. Turn this off if you want to contract videos by clicking on them.'], + 'Allow Sound': [true, 'Allow sound in videos.'], + 'Loop in New Tab': [true, 'Loop videos opened in their own tabs, and apply settings for inline expanded videos to them.'] }, 'Menu': { 'Menu': [true, 'Add a drop-down menu to posts.'], @@ -220,6 +224,7 @@ 'Fit width': [false, ''], 'Fit height': [false, ''], 'Expand spoilers': [true, 'Expand all images along with spoilers.'], + 'Expand videos': [false, 'Expand all images also expands videos (no autoplay).'], 'Expand from here': [false, 'Expand all images only from current position to thread end.'], 'Advance on contract': [false, 'Advance to next post when contracting an expanded image.'] }, @@ -3667,8 +3672,8 @@ return this.board.posts.rm(this); }; - Post.prototype.addClone = function(context) { - return new Clone(this, context); + Post.prototype.addClone = function(context, contractThumb) { + return new Clone(this, context, contractThumb); }; Post.prototype.rmClone = function(index) { @@ -3688,8 +3693,8 @@ Clone = (function(_super) { __extends(Clone, _super); - function Clone(origin, context) { - var file, info, inline, inlined, key, nodes, post, root, val, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3; + function Clone(origin, context, contractThumb) { + var file, info, inline, inlined, key, nodes, post, root, val, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3, _ref4, _ref5; this.origin = origin; this.context = context; _ref = ['ID', 'fullID', 'board', 'thread', 'info', 'quotes', 'isReply']; @@ -3698,7 +3703,7 @@ this[key] = origin[key]; } nodes = origin.nodes; - root = nodes.root.cloneNode(true); + root = contractThumb ? this.cloneWithoutVideo(nodes.root) : nodes.root.cloneNode(true); post = $('.post', root); info = $('.postInfo', post); this.nodes = { @@ -3758,6 +3763,17 @@ this.file.text = file.firstElementChild; this.file.thumb = $('img[data-md5]', file); this.file.fullImage = $('.full-image', file); + if (contractThumb) { + $.rmClass(root, 'expanded-image'); + $.rmClass(this.file.thumb, 'expanding'); + } + this.file.isExpanded = $.hasClass(root, 'expanded-image'); + if ((_ref4 = this.file.fullImage) != null) { + _ref4.removeAttribute('id'); + } + if ((_ref5 = $('.video-controls', this.file.text)) != null) { + _ref5.remove(); + } } if (origin.isDead) { this.isDead = true; @@ -3766,6 +3782,23 @@ root.dataset.clone = origin.clones.push(this) - 1; } + Clone.prototype.cloneWithoutVideo = function(node) { + var child, clone, _i, _len, _ref; + if (node.tagName === 'VIDEO') { + return []; + } else if (node.nodeType === Node.ELEMENT_NODE && $('video', node)) { + clone = node.cloneNode(false); + _ref = node.childNodes; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + child = _ref[_i]; + $.add(clone, this.cloneWithoutVideo(child)); + } + return clone; + } else { + return node.cloneNode(true); + } + }; + return Clone; })(Post); @@ -4640,6 +4673,11 @@ } return bottom; }, + isNodeVisible: function(node) { + var height; + height = node.getBoundingClientRect().height; + return Header.getTopOf(node) + height >= 0 && Header.getBottomOf(node) + height >= 0; + }, isHidden: function() { var top; top = Header.bar.getBoundingClientRect().top; @@ -4910,8 +4948,8 @@ if (Index.req || Conf['Index Mode'] !== 'infinite' || (window.scrollY <= doc.scrollHeight - (300 + window.innerHeight)) || g.VIEW === 'thread') { return; } - Index.pageNum = (Index.pageNum || Index.getCurrentPage()) + 1; - if (Index.pageNum >= Index.pagesNum) { + Index.currentPage = (Index.currentPage || Index.getCurrentPage()) + 1; + if (Index.currentPage >= Index.pagesNum) { return Index.endNotice(); } return Index.buildIndex(true); @@ -5250,8 +5288,8 @@ return Header.scrollToIfNeeded(Index.navLinks); }, getCurrentPage: function() { - if (Conf['Index Mode'] === 'infinite' && Index.pageNum) { - return Index.pageNum; + if (Conf['Index Mode'] === 'infinite' && Index.currentPage) { + return Index.currentPage; } return +window.location.pathname.split('/')[2]; }, @@ -5313,7 +5351,10 @@ }, setPage: function(pageNum) { var a, href, maxPageNum, next, pagesRoot, prev, strong; - pageNum || (pageNum = Index.getCurrentPage()); + if (pageNum == null) { + pageNum = Index.getCurrentPage(); + } + Index.currentPage = pageNum; maxPageNum = Index.getMaxPageNum(); pagesRoot = $('.pages', Index.pagelist); prev = pagesRoot.previousSibling.firstChild; @@ -5374,7 +5415,7 @@ if (!(d.readyState === 'loading' || Index.root.parentElement)) { $.replace($('.board'), Index.root); } - delete Index.pageNum; + Index.currentPage = 0; if ((_ref = Index.req) != null) { _ref.abort(); } @@ -5435,10 +5476,15 @@ } Navigate.title(); try { + pageNum || (pageNum = 0); if (req.status === 200) { Index.parse(req.response, pageNum); } else if (req.status === 304) { - Index.pageNav(pageNum || 0); + if (Index.currentPage === pageNum) { + Index.buildIndex(); + } else { + Index.pageNav(pageNum); + } } } catch (_error) { err = _error; @@ -5461,7 +5507,7 @@ Index.parseThreadList(pages); Index.buildThreads(); Index.sort(); - if (pageNum != null) { + if ((pageNum != null) && Index.currentPage !== pageNum) { Index.pageNav(pageNum); return; } @@ -5687,16 +5733,12 @@ buildIndex: function(infinite) { var i, max, nodes, pageNum, sortedThreads, thread, threads, threadsPerPage; sortedThreads = Index.sortedThreads; + nodes = []; switch (Conf['Index Mode']) { case 'paged': case 'infinite': pageNum = Index.getCurrentPage(); - if (pageNum > Index.getMaxPageNum()) { - Index.pageNav(Index.getMaxPageNum()); - return; - } threadsPerPage = Index.getThreadsNumPerPage(); - nodes = []; threads = []; i = threadsPerPage * pageNum; max = i + threadsPerPage; @@ -5712,7 +5754,6 @@ nodes = Index.buildCatalogViews(); break; default: - nodes = []; i = 0; while (thread = sortedThreads[i++]) { nodes.push(thread.OP.nodes.root.parentNode, $.el('hr')); @@ -5737,9 +5778,11 @@ if (!Index.searchInput.dataset.searching) { Index.searchInput.dataset.searching = 1; Index.pageBeforeSearch = Index.getCurrentPage(); - pageNum = 0; + Index.setPage(pageNum = 0); } else { - pageNum = Index.getCurrentPage(); + if (Conf['Index Mode'] !== 'infinite') { + pageNum = Index.getCurrentPage(); + } } } else { if (!Index.searchInput.dataset.searching) { @@ -6188,7 +6231,7 @@ if (!root.parentNode) { return; } - clone = post.addClone(context); + clone = post.addClone(context, $.hasClass(root, 'dialog')); Clone.callbacks.execute([clone]); nodes = clone.nodes; $.rmAll(nodes.root); @@ -6211,6 +6254,7 @@ $.cache(url, function() { return Get.archivedPost(this, boardID, postID, root, context); }, { + responseType: 'json', withCredentials: url.archive.withCredentials }); } else { @@ -6692,8 +6736,8 @@ return $.set("" + this.id + ".position", this.style.cssText); }; hoverstart = function(_arg) { - var asapTest, cb, el, endEvents, latestEvent, o, offsetX, offsetY, root; - root = _arg.root, el = _arg.el, latestEvent = _arg.latestEvent, endEvents = _arg.endEvents, asapTest = _arg.asapTest, cb = _arg.cb, offsetX = _arg.offsetX, offsetY = _arg.offsetY; + var asapTest, cb, el, endEvents, latestEvent, noRemove, o, offsetX, offsetY, root; + root = _arg.root, el = _arg.el, latestEvent = _arg.latestEvent, endEvents = _arg.endEvents, asapTest = _arg.asapTest, cb = _arg.cb, offsetX = _arg.offsetX, offsetY = _arg.offsetY, noRemove = _arg.noRemove; o = { root: root, el: el, @@ -6705,7 +6749,8 @@ clientHeight: doc.clientHeight, clientWidth: doc.clientWidth, offsetX: offsetX || 45, - offsetY: offsetY || -120 + offsetY: offsetY || -120, + noRemove: noRemove }; o.hover = hover.bind(o); o.hoverend = hoverend.bind(o); @@ -9015,7 +9060,7 @@ return $.addClass(QR.nodes.el, 'dump'); }, handleBlob: function(urlBlob, header, url) { - var blob, end, endnl, endsc, mime, name, name_end, name_start, start, _ref; + var blob, end, endnl, endsc, mime, name, name_end, name_start, start; name = url.substr(url.lastIndexOf('/') + 1, url.length); start = header.indexOf("Content-Type: ") + 14; endsc = header.substr(start, header.length).indexOf(";"); @@ -9037,9 +9082,6 @@ if (blob.type === null) { return QR.error("Unsupported file type."); } - if (_ref = blob.type, __indexOf.call(QR.mimeTypes, _ref) < 0) { - return QR.error("Unsupported file type."); - } return QR.handleFiles([blob]); }, handleUrl: function() { @@ -9506,6 +9548,9 @@ } QR.status(); QR.error(err); + if (QR.captcha.isEnabled) { + QR.captcha.setup(); + } return; } h1 = $('h1', resDoc); @@ -9536,9 +9581,15 @@ }); postsCount = QR.posts.length - 1; QR.cooldown.auto = postsCount && isReply; + if (QR.captcha.isEnabled && QR.cooldown.auto) { + QR.captcha.setup(); + } if (!(Conf['Persistent QR'] || QR.cooldown.auto)) { QR.close(); } else { + if (QR.posts.length > 1) { + QR.captcha.setup(); + } post.rm(); } QR.cooldown.set({ @@ -9991,9 +10042,6 @@ $.rmClass(QR.nodes.el, 'dump'); } else if (this === QR.selected) { (QR.posts[index - 1] || QR.posts[index + 1]).select(); - if (QR.captcha.isEnabled) { - QR.captcha.setup(); - } } QR.posts.splice(index, 1); return QR.status(); @@ -10270,62 +10318,6 @@ })(); - AutoGIF = { - init: function() { - var _ref; - if (!Conf['Auto-GIF'] || ((_ref = g.BOARD.ID) === 'gif' || _ref === 'wsg')) { - return; - } - Post.callbacks.push({ - name: 'Auto-GIF', - cb: this.node - }); - return CatalogThread.callbacks.push({ - name: 'Auto-GIF', - cb: this.catalogNode - }); - }, - node: function() { - var URL, style, thumb, _ref, _ref1; - if (this.isClone || this.isHidden || this.thread.isHidden || !((_ref = this.file) != null ? _ref.isImage : void 0)) { - return; - } - _ref1 = this.file, thumb = _ref1.thumb, URL = _ref1.URL; - if (!(/gif$/.test(URL) && !/spoiler/.test(thumb.src))) { - return; - } - if (this.file.isSpoiler) { - style = thumb.style; - style.maxHeight = style.maxWidth = this.isReply ? '125px' : '250px'; - } - return AutoGIF.replaceThumbnail(thumb, URL); - }, - catalogNode: function() { - var OP, URL, _ref; - OP = this.thread.OP; - if (!((_ref = OP.file) != null ? _ref.isImage : void 0)) { - return; - } - URL = OP.file.URL; - if (!/gif$/.test(URL)) { - return; - } - return AutoGIF.replaceThumbnail(this.nodes.thumb, URL, true); - }, - replaceThumbnail: function(thumb, URL, isBackground) { - var gif; - gif = $.el('img'); - $.on(gif, 'load', function() { - if (isBackground) { - return thumb.style.backgroundImage = "url(" + URL + ")"; - } else { - return thumb.src = URL; - } - }); - return gif.src = URL; - } - }; - FappeTyme = { init: function() { var el, lc, type, _i, _len, _ref; @@ -10402,8 +10394,7 @@ }); }, node: function() { - var _ref; - if (!((_ref = this.file) != null ? _ref.isImage : void 0)) { + if (!this.file) { return; } if (Gallery.nodes) { @@ -10415,12 +10406,12 @@ } }, build: function(image) { - var cb, createSubEntry, dialog, el, file, files, i, key, menuButton, name, nodes, value, _ref; + var cb, createSubEntry, dialog, el, file, i, key, menuButton, name, nodes, value, _i, _len, _ref, _ref1; Gallery.images = []; nodes = Gallery.nodes = {}; nodes.el = dialog = $.el('div', { id: 'a-gallery', - innerHTML: "
\n \n \uf107\n \uf00d\n \n \n \n / \n \n
\n
\n \n
\n
\n
\n
" + innerHTML: "
\n \n \n ×\n \n \n \n / \n \n
\n
\n \n
\n
\n
\n
" }); _ref = { frame: '.gal-image', @@ -10439,8 +10430,7 @@ nodes.menu = new UI.Menu('gallery'); cb = Gallery.cb; $.on(nodes.frame, 'click', cb.blank); - $.on(nodes.current, 'click', cb.download); - $.on(nodes.next, 'click', cb.next); + $.on(nodes.next, 'click', cb.advance); $.on($('.gal-prev', dialog), 'click', cb.prev); $.on($('.gal-next', dialog), 'click', cb.next); $.on($('.gal-close', dialog), 'click', cb.close); @@ -10458,13 +10448,12 @@ } $.on(d, 'keydown', cb.keybinds); $.off(d, 'keydown', Keybinds.keydown); - i = 0; - files = $$('.post .file'); - while (file = files[i++]) { - if ($('.fileDeletedRes, .fileDeleted', file)) { - continue; + _ref1 = $$('.post .file'); + for (i = _i = 0, _len = _ref1.length; _i < _len; i = ++_i) { + file = _ref1[i]; + if (!$('.fileDeletedRes, .fileDeleted', file)) { + Gallery.generateThumb(file); } - Gallery.generateThumb(file); } $.add(d.body, dialog); nodes.thumbs.scrollTop = 0; @@ -10474,18 +10463,26 @@ return nodes.total.textContent = --i; }, generateThumb: function(file) { - var dupe, post, thumb, title; + var post, thumb, thumbImg, title; post = Get.postFromNode(file); - title = ($('.fileText a', file)).textContent; - thumb = post.file.thumb.parentNode.cloneNode(true); - if (dupe = $('img + img', thumb)) { - $.rm(dupe); + if (!(post.file && (post.file.isImage || post.file.isVideo || Conf['PDF in Gallery']))) { + return; } - thumb.className = 'gal-thumb'; - thumb.title = title; + title = ($('.fileText a', file)).textContent; + thumb = $.el('a', { + className: 'gal-thumb', + href: post.file.URL, + target: '_blank', + title: title + }); thumb.dataset.id = Gallery.images.length; thumb.dataset.post = $('a[title="Highlight this post"]', post.nodes.info).href; - thumb.firstElementChild.style.cssText = ''; + if (post.file.isVideo) { + thumb.dataset.isVideo = true; + } + thumbImg = post.file.thumb.cloneNode(false); + thumbImg.style.cssText = ''; + $.add(thumb, thumbImg); $.on(thumb, 'click', Gallery.cb.open); Gallery.images.push(thumb); return $.add(Gallery.nodes.thumbs, thumb); @@ -10502,8 +10499,9 @@ case Conf['Open Gallery']: return Gallery.cb.close; case 'Right': - case 'Enter': return Gallery.cb.next; + case 'Enter': + return Gallery.cb.advance; case 'Left': case '': return Gallery.cb.prev; @@ -10517,7 +10515,7 @@ return cb(); }, open: function(e) { - var el, img, name, nodes, rect, top; + var el, elType, file, name, nodes, rect, top, _base; if (e) { e.preventDefault(); } @@ -10530,14 +10528,22 @@ $.rmClass(el, 'gal-highlight'); } $.addClass(this, 'gal-highlight'); - img = $.el('img', { + elType = this.dataset.isVideo ? 'video' : /\.pdf$/.test(this.href) ? 'iframe' : 'img'; + $[elType === 'iframe' ? 'addClass' : 'rmClass'](nodes.el, 'gal-pdf'); + file = $.el(elType, { src: name.href = this.href, title: name.download = name.textContent = this.title }); - $.extend(img.dataset, this.dataset); - $.replace(nodes.current, img); + $.extend(file.dataset, this.dataset); + if (typeof (_base = nodes.current).pause === "function") { + _base.pause(); + } + $.replace(nodes.current, file); + if (this.dataset.isVideo) { + Video.configure(file); + } nodes.count.textContent = +this.dataset.id + 1; - nodes.current = img; + nodes.current = file; nodes.frame.scrollTop = 0; nodes.next.focus(); rect = this.getBoundingClientRect(); @@ -10549,8 +10555,8 @@ } } nodes.thumbs.scrollTop += top; - return $.on(img, 'error', function() { - return Gallery.cb.error(img, thumb); + return $.on(file, 'error', function() { + return Gallery.cb.error(file, thumb); }); }, image: function(e) { @@ -10559,13 +10565,13 @@ return Gallery.build(this); }, error: function(img, thumb) { - var URL, post, revived, src; + var URL, post, src; post = Get.postFromLink($.el('a', { href: img.dataset.post })); delete post.file.fullImage; src = this.src.split('/'); - if (src[2] === 'images.4chan.org') { + if (src[2] === 'i.4cdn.org') { URL = Redirect.to('file', { boardID: src[3], filename: src[5] @@ -10575,19 +10581,14 @@ if (Gallery.nodes.current !== img) { return; } - revived = $.el('img', { - src: URL, - title: img.title - }); - $.extend(revived.dataset, img.dataset); - $.replace(img, revived); + img.src = URL; return; } if (g.DEAD || post.isDead || post.file.isDead) { return; } } - return $.ajax("//api.4chan.org/" + post.board + "/res/" + post.thread + ".json", { + return $.ajax("//a.4cdn.org/" + post.board + "/res/" + post.thread + ".json", { onload: function() { var i, postObj, posts; if (this.status !== 200) { @@ -10623,7 +10624,27 @@ return Gallery.cb.close(); } }, + advance: function() { + if (Gallery.nodes.current.controls) { + return; + } + if (Gallery.nodes.current.paused) { + return Gallery.nodes.current.play(); + } + return Gallery.cb.next(); + }, + pause: function() { + var current; + current = Gallery.nodes.current; + if (current.nodeType === 'VIDEO') { + return current[current.paused ? 'play' : 'pause'](); + } + }, close: function() { + var _base; + if (typeof (_base = Gallery.nodes.current).pause === "function") { + _base.pause(); + } $.rm(Gallery.nodes.el); delete Gallery.nodes; d.body.style.overflow = ''; @@ -10677,7 +10698,7 @@ ImageExpand = { init: function() { - if (!Conf['Image Expansion']) { + if (g.VIEW === 'catalog' || !Conf['Image Expansion']) { return; } this.EAI = $.el('a', { @@ -10694,54 +10715,73 @@ }); }, node: function() { - var thumb; - if (!(this.file && (this.file.isImage || this.file.isVideo))) { + var clone, thumb, _ref, _ref1; + if (!(((_ref = this.file) != null ? _ref.isImage : void 0) || ((_ref1 = this.file) != null ? _ref1.isVideo : void 0))) { return; } thumb = this.file.thumb; $.on(thumb.parentNode, 'click', ImageExpand.cb.toggle); - if (this.isClone && $.hasClass(thumb, 'expanding')) { - ImageExpand.contract(this); - ImageExpand.expand(this); - return; - } - if (ImageExpand.on && !this.isHidden && (Conf['Expand spoilers'] || !this.file.isSpoiler)) { - return ImageExpand.expand(this); + if (this.isClone) { + if ($.hasClass(thumb, 'expanding')) { + ImageExpand.contract(this); + ImageExpand.expand(this); + } else if (this.file.isExpanded && this.file.isVideo) { + clone = this; + ImageExpand.setupVideoControls(clone); + if (!clone.origin.file.fullImage.paused) { + $.queueTask(function() { + return Video.start(clone.file.fullImage); + }); + } + } + } else if (ImageExpand.on && !this.isHidden && (Conf['Expand spoilers'] || !this.file.isSpoiler) && (Conf['Expand videos'] || !this.file.isVideo)) { + return ImageExpand.expand(this, null, true); } }, cb: { toggle: function(e) { + var post, _ref; if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) { return; } + post = Get.postFromNode(this); + if (post.file.isExpanded && ((_ref = post.file.fullImage) != null ? _ref.controls : void 0)) { + return; + } e.preventDefault(); - return ImageExpand.toggle(Get.postFromNode(this)); + return ImageExpand.toggle(post); }, toggleAll: function() { - var func; + var func, toggle; $.event('CloseMenu'); + toggle = function(post) { + var file; + file = post.file; + if (!(file && (file.isImage || file.isVideo) && doc.contains(post.nodes.root))) { + return; + } + if (ImageExpand.on && (!Conf['Expand spoilers'] && file.isSpoiler || !Conf['Expand videos'] && file.isVideo || Conf['Expand from here'] && Header.getTopOf(file.thumb) < 0)) { + return; + } + return $.queueTask(func, post); + }; if (ImageExpand.on = $.hasClass(ImageExpand.EAI, 'expand-all-shortcut')) { ImageExpand.EAI.className = 'contract-all-shortcut a-icon'; ImageExpand.EAI.title = 'Contract All Images'; - func = ImageExpand.expand; + func = function(post) { + return ImageExpand.expand(post, null, true); + }; } else { ImageExpand.EAI.className = 'expand-all-shortcut a-icon'; ImageExpand.EAI.title = 'Expand All Images'; func = ImageExpand.contract; } return g.posts.forEach(function(post) { - var file, _i, _len, _ref; - _ref = [post].concat(post.clones); + var _i, _len, _ref; + _ref = [post].concat(__slice.call(post.clones)); for (_i = 0, _len = _ref.length; _i < _len; _i++) { post = _ref[_i]; - file = post.file; - if (!(file && (file.isImage || file.isVideo) && doc.contains(post.nodes.root))) { - return; - } - if (ImageExpand.on && !post.isHidden && (!Conf['Expand spoilers'] && file.isSpoiler || Conf['Expand from here'] && Header.getTopOf(file.thumb) < 0)) { - return; - } - $.queueTask(func, post); + toggle(post); } }); }, @@ -10750,9 +10790,8 @@ } }, toggle: function(post) { - var headRect, left, root, thumb, top, x, y, _ref; - thumb = post.file.thumb; - if (!(post.file.isExpanded || $.hasClass(thumb, 'expanding'))) { + var headRect, left, root, top, x, y, _ref; + if (!(post.file.isExpanded || $.hasClass(post.file.thumb, 'expanding'))) { ImageExpand.expand(post); return; } @@ -10784,85 +10823,126 @@ return ImageExpand.contract(post); }, contract: function(post) { - var _ref; + var cb, eventName, thumb, video, _ref; + thumb = post.file.thumb; + if (post.file.isVideo && (video = post.file.fullImage)) { + video.pause(); + TrashQueue.add(video, post); + thumb.parentNode.href = video.src; + thumb.parentNode.target = '_blank'; + _ref = ImageExpand.videoCB; + for (eventName in _ref) { + cb = _ref[eventName]; + $.off(video, eventName, cb); + } + $.rm(post.file.videoControls); + delete post.file.videoControls; + } $.rmClass(post.nodes.root, 'expanded-image'); - $.rmClass(post.file.thumb, 'expanding'); - post.file.isExpanded = false; - if (post.file.isVideo) { - post.file.fullImage.pause(); - } - if ((_ref = post.file.videoControls) != null) { - _ref.map($.rm); - } - return delete post.file.videoControls; + $.rmClass(thumb, 'expanding'); + return post.file.isExpanded = false; }, - expand: function(post, src) { - var file, isVideo, thumb, _ref; + expand: function(post, src, disableAutoplay) { + var el, isVideo, thumb, _ref; _ref = post.file, thumb = _ref.thumb, isVideo = _ref.isVideo; if (post.isHidden || post.file.isExpanded || $.hasClass(thumb, 'expanding')) { return; } $.addClass(thumb, 'expanding'); - if (post.file.fullImage) { - $.asap((function() { - var file; - return (file = post.file.fullImage) && (file.videoHeight || file.naturalHeight); - }), function() { - return ImageExpand.completeExpand(post); + if (el = post.file.fullImage) { + TrashQueue.remove(el); + } else { + el = post.file.fullImage = $.el((isVideo ? 'video' : 'img'), { + className: 'full-image' }); - return; - } - file = $.el((post.file.isImage ? 'img' : 'video'), { - className: 'full-image', - src: src || post.file.URL - }); - post.file.fullImage = file; - if (isVideo) { - file.loop = true; - file.controls = Conf['Show Controls']; - } - $.on(file, 'error', ImageExpand.error); - $.asap((function() { - return (file = post.file.fullImage) && (file.videoHeight || file.naturalHeight); - }), function() { + $.on(el, 'error', ImageExpand.error); + el.src = src || post.file.URL; if (isVideo) { - file.style.maxHeight = file.videoHeight + "px"; - file.style.maxWidth = file.videoWidth + "px"; + el.loop = true; } - return ImageExpand.completeExpand(post); + } + if (el !== thumb.nextSibling) { + $.after(thumb, el); + } + return $.asap((function() { + return el.videoHeight || el.naturalHeight; + }), function() { + return ImageExpand.completeExpand(post, disableAutoplay); }); - return $.after((file.controls ? thumb.parentNode : thumb), file); }, - completeExpand: function(post) { - var bottom, thumb; - thumb = post.file.thumb; - if (!$.hasClass(thumb, 'expanding')) { + completeExpand: function(post, disableAutoplay) { + var bottom; + if (!$.hasClass(post.file.thumb, 'expanding')) { return; } - post.file.isExpanded = true; - if (post.file.isVideo) { - ImageExpand.setupVideo(post); - } - $.addClass(post.nodes.root, 'expanded-image'); - $.rmClass(post.file.thumb, 'expanding'); if (!post.nodes.root.parentNode) { + ImageExpand.completeExpand2(post); return; } bottom = post.nodes.root.getBoundingClientRect().bottom; return $.queueTask(function() { + ImageExpand.completeExpand2(post, disableAutoplay); if (!(bottom <= 0)) { return; } return window.scrollBy(0, post.nodes.root.getBoundingClientRect().bottom - bottom); }); }, - setupVideo: function(post) { - var contract, file, play, video; + completeExpand2: function(post, disableAutoplay) { + $.addClass(post.nodes.root, 'expanded-image'); + $.rmClass(post.file.thumb, 'expanding'); + post.file.isExpanded = true; + if (post.file.isVideo) { + ImageExpand.setupVideoControls(post); + return Video.configure(post.file.fullImage, disableAutoplay); + } + }, + videoCB: (function() { + var mousedown; + mousedown = false; + return { + mouseover: function() { + return mousedown = false; + }, + mousedown: function(e) { + if (e.button === 0) { + return mousedown = true; + } + }, + mouseup: function(e) { + if (e.button === 0) { + return mousedown = false; + } + }, + mouseout: function(e) { + if (mousedown && e.clientX <= this.getBoundingClientRect().left) { + return ImageExpand.contract(Get.postFromNode(this)); + } + }, + click: function(e) { + if (this.paused && !this.controls) { + this.play(); + return e.preventDefault(); + } + } + }; + })(), + setupVideoControls: function(post) { + var cb, contract, eventName, file, thumb, video, _ref; file = post.file; + thumb = file.thumb; video = file.fullImage; - file.videoControls = []; - video.muted = !Conf['Allow Sound']; - if (video.controls) { + file.thumb.parentNode.removeAttribute('href'); + file.thumb.parentNode.removeAttribute('target'); + _ref = ImageExpand.videoCB; + for (eventName in _ref) { + cb = _ref[eventName]; + $.on(video, eventName, cb); + } + file.videoControls = $.el('span', { + className: 'video-controls' + }); + if (Conf['Show Controls']) { contract = $.el('a', { textContent: 'contract', href: 'javascript:;', @@ -10871,39 +10951,7 @@ $.on(contract, 'click', function(e) { return ImageExpand.contract(post); }); - file.videoControls.push($.tn('\u00A0'), contract); - file.mousedown = false; - $.on(video, 'mousedown', function(e) { - if (e.button === 0) { - return file.mousedown = true; - } - }); - $.on(video, 'mouseup', function(e) { - if (e.button === 0) { - return file.mousedown = false; - } - }); - $.on(video, 'mouseover', function(e) { - return file.mousedown = false; - }); - $.on(video, 'mouseout', function(e) { - if (file.mousedown && e.clientX <= video.getBoundingClientRect().left) { - return ImageExpand.contract(post); - } - }); - } - if (Conf['Autoplay']) { - video.play(); - } else if (!video.controls) { - play = $.el('a', { - textContent: 'play', - href: 'javascript:;' - }); - $.on(play, 'click', function(e) { - video[this.textContent](); - return this.textContent = this.textContent === 'play' ? 'pause' : 'play'; - }); - file.videoControls.push($.tn('\u00A0'), play); + $.add(file.videoControls, [$.tn('\u00A0'), contract]); } return $.add(file.text, file.videoControls); }, @@ -10931,7 +10979,7 @@ } } timeoutID = setTimeout(ImageExpand.expand, 10000, post); - return $.ajax(post.file.URL, { + return $.ajax(this.src, { onloadend: function() { if (this.status !== 404) { return; @@ -10946,7 +10994,7 @@ menu: { init: function() { var conf, createSubEntry, el, name, subEntries, _ref; - if (!Conf['Image Expansion']) { + if (g.VIEW === 'catalog' || !Conf['Image Expansion']) { return; } el = $.el('span', { @@ -11003,40 +11051,68 @@ } }, node: function() { - if (!(this.file && (this.file.isImage || this.file.isVideo))) { + var _ref, _ref1; + if (!(((_ref = this.file) != null ? _ref.isImage : void 0) || ((_ref1 = this.file) != null ? _ref1.isVideo : void 0))) { return; } return $.on(this.file.thumb, 'mouseover', ImageHover.mouseover); }, catalogNode: function() { - if (!(this.thread.OP.file && (this.thread.OP.file.isImage || this.thread.OP.file.isVideo))) { + var file; + if (!((file = this.thread.OP.file) && (file.isImage || file.isVideo))) { return; } return $.on(this.nodes.thumb, 'mouseover', ImageHover.mouseover); }, mouseover: function(e) { - var el, post; + var el, isVideo, post, thumb; post = $.hasClass(this, 'thumb') ? g.posts[this.parentNode.dataset.fullID] : Get.postFromNode(this); - el = post.file.isImage ? $.el('img', { - id: 'ihover', - src: post.file.URL - }) : $.el('video', { - controls: false, - id: 'ihover', - src: post.file.URL, - autoplay: Conf['Autoplay'], - muted: !Conf['Allow Sound'], - loop: true - }); - $.add(Header.hover, el); + isVideo = post.file.isVideo; + if (post.file.fullImage) { + el = post.file.fullImage; + TrashQueue.remove(el); + } else { + el = $.el((isVideo ? 'video' : 'img'), { + className: 'full-image', + src: post.file.URL + }); + post.file.fullImage = el; + thumb = post.file.thumb; + } + if (d.body.contains(thumb)) { + if (el !== thumb.nextSibling) { + $.after(thumb, el); + } + } else { + if (el.parentNode !== Header.hover) { + $.add(Header.hover, el); + } + } + el.id = 'ihover'; el.dataset.fullID = post.fullID; + if (isVideo) { + el.loop = true; + el.controls = false; + el.muted = !Conf['Allow Sound']; + if (Conf['Autoplay']) { + el.play(); + } + } UI.hover({ root: this, el: el, latestEvent: e, endEvents: 'mouseout click', asapTest: function() { - return post.file.isVideo || el.naturalHeight; + return el.videoHeight || el.naturalHeight; + }, + noRemove: true, + cb: function() { + if (isVideo) { + el.pause(); + TrashQueue.add(el, post); + } + return el.removeAttribute('id'); } }); return $.on(el, 'error', ImageHover.error); @@ -11083,7 +11159,7 @@ ImageLoader = { init: function() { var prefetch; - if (!(Conf["Image Prefetching"] || Conf["Replace JPG"] || Conf["Replace PNG"] || Conf["Replace GIF"])) { + if (!(Conf["Image Prefetching"] || Conf["Replace JPG"] || Conf["Replace PNG"] || Conf["Replace GIF"] || Conf["Replace WEBM"])) { return; } Post.callbacks.push({ @@ -11094,9 +11170,6 @@ name: 'Image Replace', cb: this.thread }); - if (!(Conf['Image Prefetching'] && g.VIEW === 'thread')) { - return; - } prefetch = $.el('label', { innerHTML: ' Prefetch Images' }); @@ -11112,31 +11185,59 @@ return ImageLoader.thread = this; }, node: function() { - var URL, img, string, style, thumb, type, _ref, _ref1; - if (this.isClone || this.isHidden || this.thread.isHidden || !((_ref = this.file) != null ? _ref.isImage : void 0)) { + var URL, cb, file, isImage, isVideo, match, replace, style, thumb, type, _ref, _ref1; + if (!this.file) { + return; + } + _ref = this.file, isImage = _ref.isImage, isVideo = _ref.isVideo; + if (this.isClone || this.isHidden || this.thread.isHidden || !(isImage || isVideo)) { return; } _ref1 = this.file, thumb = _ref1.thumb, URL = _ref1.URL; - if (!((Conf[string = "Replace " + ((type = (URL.match(/\w{3}$/))[0].toUpperCase()) === 'PEG' ? 'JPG' : type)] && !/spoiler/.test(thumb.src)) || Conf['prefetch'])) { + style = thumb.style; + type = (match = URL.match(/\.([^.]+)$/)[1].toUpperCase()) === 'JPEG' ? 'JPG' : match; + replace = "Replace " + type; + if (!((Conf[replace] && !/spoiler/.test(thumb.src)) || (Conf['prefetch'] && g.VIEW === 'thread'))) { return; } if (this.file.isSpoiler) { - style = thumb.style; style.maxHeight = style.maxWidth = this.isReply ? '125px' : '250px'; } - img = $.el('img'); - if (Conf[string]) { - $.on(img, 'load', function() { - return thumb.src = URL; - }); + file = $.el(isImage ? 'img' : 'video'); + if (Conf[replace]) { + if (isVideo) { + file.alt = thumb.alt; + file.dataset.md5 = thumb.dataset.md5; + file.style.height = style.height; + file.style.width = style.width; + file.style.maxHeight = style.maxHeight; + file.style.maxWidth = style.maxWidth; + file.loop = true; + file.autoplay = Conf['Autoplay']; + if (Conf['Image Hover']) { + $.on(file, 'mouseover', ImageHover.mouseover); + } + } + cb = (function(_this) { + return function() { + $.off(file, 'load loadedmetadata', cb); + if (isVideo) { + $.replace(thumb, file); + _this.file.thumb = file; + return; + } + return thumb.src = URL; + }; + })(this); + $.on(file, 'load loadedmetadata', cb); } - return img.src = URL; + return file.src = URL; }, toggle: function() { var enabled; enabled = Conf['prefetch'] = this.checked; if (enabled) { - ImageLoader.thread.posts.forEach(ImageLoader.node.call); + g.BOARD.posts.forEach(ImageLoader.node.call); } } }; @@ -11223,6 +11324,55 @@ } }; + TrashQueue = { + init: function() {}, + add: function(video, post) { + var _ref, _ref1; + if (this.killNext && video !== this.killNext) { + if ((_ref = this.killNextPost) != null) { + if ((_ref1 = _ref.file) != null) { + delete _ref1.fullImage; + } + } + $.rm(this.killNext); + } + this.killNext = video; + return this.killNextPost = post; + }, + remove: function(video) { + if (video === this.killNext) { + return delete this.killNext; + } + } + }; + + Video = { + configure: function(video, disableAutoplay) { + video.loop = true; + video.muted = !Conf['Allow Sound']; + video.controls = Conf['Show Controls']; + video.autoplay = false; + if (Conf['Autoplay'] && !disableAutoplay) { + return Video.start(video); + } else { + return video.pause(); + } + }, + start: function(video) { + var controls; + controls = video.controls; + video.controls = false; + video.play(); + if (controls) { + return $.asap((function() { + return (video.readyState >= 3 && video.currentTime <= Math.max(0.1, video.duration - 0.5)) || !d.contains(video); + }), function() { + return video.controls = true; + }, 500); + } + } + }; + ArchiveLink = { init: function() { var div, entry, type, _i, _len, _ref; @@ -12955,7 +13105,7 @@ } return Redirect.data = o; }, - archives: [{"uid":0,"name":"Foolz","domain":"archive.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["a","biz","co","diy","gd","jp","m","sci","sp","tg","tv","v","vg","vp","vr","wsg"],"files":["a","biz","gd","diy","jp","m","sci","tg","vg","vp","vr","wsg"]},{"uid":1,"name":"NSFW Foolz","domain":"nsfw.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["u"],"files":["u"]},{"uid":2,"name":"The Dark Cave","domain":"archive.thedarkcave.org","http":true,"https":true,"software":"foolfuuka","boards":["c","int","out","po"],"files":["c","po"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":18,"name":"4plebs Flash Archive","domain":"flash.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["f"],"files":["f"]},{"uid":4,"name":"Nyafuu","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["d","i"],"files":["d","i"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":true,"https":true,"software":"fuuka","boards":["cgl","g","mu","w"],"files":["cgl","g","mu","w"]},{"uid":9,"name":"Heinessen","domain":"archive.heinessen.com","http":true,"https":false,"software":"fuuka","boards":["an","fit","k","mlp","r9k","toy"],"files":["an","fit","k","r9k","toy"]},{"uid":10,"name":"warosu","domain":"fuuka.warosu.org","http":false,"https":true,"software":"fuuka","boards":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"],"files":["3","biz","cgl","ck","diy","fa","ic","jp","lit","sci","tg","vr"]},{"uid":15,"name":"fgts","domain":"fgts.eu","http":true,"https":true,"software":"foolfuuka","boards":["cm","hm","r","soc","y"],"files":["cm","hm","r","soc","y"]},{"uid":16,"name":"maware","domain":"archive.mawa.re","http":true,"https":false,"software":"foolfuuka","boards":["t"],"files":["t"]},{"uid":17,"name":"installgentoo.com","domain":"chan.installgentoo.com","http":true,"https":false,"software":"foolfuuka","boards":["g","t"],"files":["g","t"]},{"uid":13,"name":"Foolz Beta","domain":"beta.foolz.us","http":true,"https":true,"withCredentials":true,"software":"foolfuuka","boards":["a","biz","co","d","diy","gd","jp","m","s4s","sci","sp","tg","tv","u","v","vg","vp","vr","wsg"],"files":["a","biz","d","diy","gd","jp","m","s4s","sci","tg","u","vg","vp","vr","wsg"]}], + archives: [{"uid":0,"name":"Foolz","domain":"archive.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["a","biz","co","diy","gd","jp","m","sci","sp","tg","tv","v","vg","vp","vr","wsg"],"files":["a","biz","gd","diy","jp","m","sci","tg","vg","vp","vr","wsg"]},{"uid":1,"name":"NSFW Foolz","domain":"nsfw.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["u"],"files":["u"]},{"uid":2,"name":"The Dark Cave","domain":"archive.thedarkcave.org","http":true,"https":true,"software":"foolfuuka","boards":["c","int","out","po"],"files":["c","po"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":18,"name":"4plebs Flash Archive","domain":"flash.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["f"],"files":["f"]},{"uid":4,"name":"Nyafuu","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["d","i"],"files":["d","i"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":true,"https":true,"software":"fuuka","boards":["cgl","g","mu","w"],"files":["cgl","g","mu","w"]},{"uid":9,"name":"Heinessen","domain":"archive.heinessen.com","http":true,"https":false,"software":"fuuka","boards":["an","fit","k","mlp","r9k","toy"],"files":["an","fit","k","r9k","toy"]},{"uid":10,"name":"warosu","domain":"fuuka.warosu.org","http":false,"https":true,"software":"fuuka","boards":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"],"files":["3","biz","cgl","ck","diy","fa","ic","jp","lit","sci","tg","vr"]},{"uid":15,"name":"fgts","domain":"fgts.eu","http":true,"https":true,"software":"foolfuuka","boards":["cm","h","hc","hm","r","s","soc","y"],"files":["cm","h","hc","hm","r","s","soc","y"]},{"uid":16,"name":"maware","domain":"archive.mawa.re","http":true,"https":false,"software":"foolfuuka","boards":["t"],"files":["t"]},{"uid":17,"name":"installgentoo.com","domain":"chan.installgentoo.com","http":true,"https":false,"software":"foolfuuka","boards":["g","t"],"files":["g","t"]},{"uid":13,"name":"Foolz Beta","domain":"beta.foolz.us","http":true,"https":true,"withCredentials":true,"software":"foolfuuka","boards":["a","biz","co","d","diy","gd","jp","m","s4s","sci","sp","tg","tv","u","v","vg","vp","vr","wsg"],"files":["a","biz","d","diy","gd","jp","m","s4s","sci","tg","u","vg","vp","vr","wsg"]}], to: function(dest, data) { var archive; archive = (dest === 'search' || dest === 'board' ? Redirect.data.thread : Redirect.data[dest])[data.boardID]; @@ -13109,8 +13259,8 @@ "http": true, "https": true, "software": "foolfuuka", - "boards": ["cm", "hm", "r", "soc", "y"], - "files": ["cm", "hm", "r", "soc", "y"] + "boards": ["cm", "h", "hc", "hm", "r", "s", "soc", "y"], + "files": ["cm", "h", "hc", "hm", "r", "s", "soc", "y"] }, { "uid": 16, "name": "maware", @@ -15048,7 +15198,7 @@ } a = $.el('a', { textContent: ExpandThread.text.apply(ExpandThread, ['+'].concat(__slice.call(summary.textContent.match(/\d+/g)))), - href: "" + thread.board.ID + "/res/" + thread.ID, + href: "res/" + thread.ID, className: 'summary' }); $.on(a, 'click', ExpandThread.cbToggle); @@ -16379,17 +16529,22 @@ $.addClass(doc, view); ({ index: function() { + var _ref; delete g.THREADID; if (Conf['Index Mode'] === 'catalog') { - return Index.cb.toggleCatalogMode(); + Index.cb.toggleCatalogMode(); } + return (_ref = QR.posts[0]) != null ? _ref.thread = 'new' : void 0; }, thread: function() { + var _ref; if (Conf['Index Mode'] === 'catalog') { - return $.rmClass(doc, 'catalog-mode'); + $.rmClass(doc, 'catalog-mode'); } + return (_ref = QR.posts[0]) != null ? _ref.thread = g.THREADID : void 0; } })[view](); + QR.status(); return g.VIEW = view; }, updateBoard: function(boardID) { @@ -16672,6 +16827,7 @@ var addSection, arr, check, el, settings, _i, _len, _ref; el = $.el('a', { className: 'settings-link', + title: 'Appchan X Settings', href: 'javascript:;', textContent: 'Settings' }); @@ -17789,7 +17945,7 @@ }); }, initFeatures: function() { - var href, init, _ref; + var href, init, video, _ref; Favicon.el.type = 'image/x-icon'; href = Favicon.el.href; Favicon.SFW = /ws\.ico$/.test(href); @@ -17818,6 +17974,18 @@ Report.init(); return; case 'i.4cdn.org': + if (Conf['Loop in New Tab'] && (video = $('video'))) { + Video.configure(video); + $.on(video, 'click', function() { + if (!video.controls) { + if (video.paused) { + return video.play(); + } else { + return video.pause(); + } + } + }); + } $.ready(function() { var URL, pathname, _ref1; if (Conf['404 Redirect'] && ((_ref1 = d.title) === '4chan - Temporarily Offline' || _ref1 === '4chan - 404 Not Found')) { diff --git a/builds/updates.xml b/builds/updates.xml index b25f40d4d..5c043194a 100644 --- a/builds/updates.xml +++ b/builds/updates.xml @@ -1,7 +1,7 @@ - + diff --git a/builds/xpi/4chanx.xpi b/builds/xpi/4chanx.xpi deleted file mode 100644 index 896dfe79e..000000000 Binary files a/builds/xpi/4chanx.xpi and /dev/null differ diff --git a/builds/xpi/data/4chan-X.user.js b/builds/xpi/data/4chan-X.user.js deleted file mode 100644 index 257f532b1..000000000 --- a/builds/xpi/data/4chan-X.user.js +++ /dev/null @@ -1,13534 +0,0 @@ -// Generated by CoffeeScript -// ==UserScript== -// @name 4chan X -// @version 1.4.1 -// @minGMVer 1.14 -// @minFFVer 26 -// @namespace 4chan-X -// @description Cross-browser userscript for maximum lurking on 4chan. -// @license MIT; https://github.com/Spittie/4chan-x/blob/master/LICENSE -// @match *://boards.4chan.org/* -// @match *://sys.4chan.org/* -// @match *://a.4cdn.org/* -// @match *://i.4cdn.org/* -// @grant GM_getValue -// @grant GM_setValue -// @grant GM_deleteValue -// @grant GM_listValues -// @grant GM_openInTab -// @grant GM_xmlhttpRequest -// @run-at document-start -// @updateURL https://github.com/Spittie/4chan-x/raw/stable/builds/4chan-X.meta.js -// @downloadURL https://github.com/Spittie/4chan-x/raw/stable/builds/4chan-X.user.js -// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAF5JREFUeNrtkTESABAQxPD/R6tsE2dUGYUtFJvLDKf93KevHJAjpBorAQWSBIKqFASC4G0pCAkm4GfaEvgYXl0T6HBaE97f0vmnfYHbZOMLZCx9ISdKWwjOWZSC8GYm4SUGwfYgqI4AAAAASUVORK5CYII= -// ==/UserScript== - -/* -* 4chan X - Version 1.4.1 - 2014-03-01 -* -* Licensed under the MIT license. -* https://github.com/Spittie/4chan-x/blob/master/LICENSE -* -* Appchan X Copyright © 2013-2013 Zixaphir -* http://zixaphir.github.io/appchan-x/ -* 4chan x Copyright © 2009-2011 James Campos -* https://github.com/aeosynth/4chan-x -* 4chan x Copyright © 2012-2014 Nicolas Stepien -* https://4chan-x.just-believe.in/ -* 4chan x Copyright © 2013-2014 Jordan Bates -* http://seaweedchan.github.io/4chan-x/ -* 4chan x Copyright © 2012-2014 ihavenoface -* http://ihavenoface.github.io/4chan-x/ -* 4chan SS Copyright © 2011-2013 Ahodesuka -* https://github.com/ahodesuka/4chan-Style-Script/ -* -* Permission is hereby granted, free of charge, to any person -* obtaining a copy of this software and associated documentation -* files (the "Software"), to deal in the Software without -* restriction, including without limitation the rights to use, -* copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the -* Software is furnished to do so, subject to the following -* conditions: -* -* The above copyright notice and this permission notice shall be -* included in all copies or substantial portions of the Software. -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -* OTHER DEALINGS IN THE SOFTWARE. -* -* Contributors: -* aeosynth -* mayhemydg -* noface -* !K.WeEabo0o -* blaise -* that4chanwolf -* desuwa -* seaweed -* e000 -* ahodesuka -* Shou -* ferongr -* xat -* Ongpot -* thisisanon -* Anonymous -* Seiba -* herpaderpderp -* WakiMiko -* btmcsweeney -* AppleBloom -* detharonil -* -* All the people who've taken the time to write bug reports. -* -* Thank you. -*/ - -/* -* Contains data from external sources: -* -* audio/beep.wav from http://freesound.org/people/pierrecartoons1979/sounds/90112/ -* cc-by-nc-3.0 -* -* 4chan/4chan-JS (https://github.com/4chan/4chan-JS) -* Copyright (c) 2012-2013, 4chan LLC -* All rights reserved. -* -* license: https://github.com/4chan/4chan-JS/blob/master/LICENSE -*/ -'use strict'; - -(function() { - var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, Callbacks, CatalogLinks, Clone, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, Header, IDColor, ImageExpand, ImageHover, ImageLoader, Index, InfiniScroll, Keybinds, Linkify, Main, Menu, Nav, Navigate, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, SimpleDict, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, g, - __slice = [].slice, - __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, - __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; - - Array.prototype.indexOf = function(val, i) { - var len; - i || (i = 0); - len = this.length; - while (i < len) { - if (this[i] === val) { - return i; - } - i++; - } - return -1; - }; - - __indexOf = [].indexOf; - - Config = { - main: { - 'Miscellaneous': { - 'JSON Navigation': [true, 'Use JSON for loading the Board Index and Threads. Also allows searching and sorting the board index and infinite scolling.'], - 'Catalog Links': [true, 'Add toggle link in header menu to turn Navigation links into links to each board\'s catalog.'], - 'External Catalog': [false, 'Link to external catalog instead of the internal one.'], - 'QR Shortcut': [false, 'Adds a small [QR] link in the header.'], - 'Announcement Hiding': [true, 'Add button to hide 4chan announcements.'], - 'Desktop Notifications': [false, 'Enables desktop notifications across various 4chan X features.'], - '404 Redirect': [true, 'Redirect dead threads and images.'], - 'Keybinds': [true, 'Bind actions to keyboard shortcuts.'], - 'Time Formatting': [true, 'Localize and format timestamps.'], - 'Relative Post Dates': [true, 'Display dates like "3 minutes ago". Tooltip shows the timestamp.'], - 'File Info Formatting': [true, 'Reformat the file information.'], - 'Thread Expansion': [true, 'Add buttons to expand threads.'], - 'Index Navigation': [false, 'Add buttons to navigate between threads.'], - 'Reply Navigation': [false, 'Add buttons to navigate to top / bottom of thread.'], - 'Show Dice Roll': [true, 'Show dice that were entered into the email field.'], - 'Custom Board Titles': [true, 'Allow editing of the board title and subtitle by ctrl+clicking them'], - 'Persistent Custom Board Titles': [false, 'Force custom board titles to be persistent, even if moot updates the board titles.'], - 'Show Updated Notifications': [true, 'Show notifications when 4chan X is successfully updated.'], - 'Emoji': [false, 'Adds icons next to names for different emails'], - 'Color User IDs': [false, 'Assign unique colors to user IDs on boards that use them'], - 'Remove Spoilers': [false, 'Remove all spoilers in text.'], - 'Reveal Spoilers': [false, 'Indicate spoilers if Remove Spoilers is enabled, or make the text appear hovered if Remove Spoiler is disabled.'] - }, - 'Linkification': { - 'Linkify': [true, 'Convert text into links where applicable.'], - 'Embedding': [true, 'Embed supported services.'], - 'Auto-embed': [false, 'Auto-embed Linkify Embeds.'], - 'Link Title': [true, 'Replace the link of a supported site with its actual title. Currently Supported: YouTube, Vimeo, SoundCloud, and Github gists'] - }, - 'Filtering': { - 'Anonymize': [false, 'Make everyone Anonymous.'], - 'Filter': [true, 'Self-moderation placebo.'], - 'Recursive Hiding': [true, 'Hide replies of hidden posts, recursively.'], - 'Thread Hiding Buttons': [false, 'Add buttons to hide entire threads.'], - 'Reply Hiding Buttons': [false, 'Add buttons to hide single replies.'], - 'Filtered Backlinks': [true, 'When enabled, shows backlinks to filtered posts with a line-through decoration. Otherwise, hides the backlinks.'], - 'Stubs': [true, 'Show stubs of hidden threads / replies.'] - }, - 'Images': { - 'Image Expansion': [true, 'Expand images.'], - 'Image Hover': [true, 'Show full image on mouseover.'], - 'Gallery': [true, 'Adds a simple and cute image gallery.'], - 'Sauce': [true, 'Add sauce links to images.'], - 'Reveal Spoiler Thumbnails': [false, 'Replace spoiler thumbnails with the original image.'], - 'Replace GIF': [false, 'Replace thumbnail of gifs with its actual image.'], - 'Replace PNG': [false, 'Replace pngs.'], - 'Replace JPG': [false, 'Replace jpgs.'], - 'Image Prefetching': [false, 'Preload images'], - 'Fappe Tyme': [false, 'Hide posts without images. *hint* *hint*'], - 'Werk Tyme': [false, 'Hide all post images.'] - }, - 'Menu': { - 'Menu': [true, 'Add a drop-down menu to posts.'], - 'Report Link': [true, 'Add a report link to the menu.'], - 'Thread Hiding Link': [true, 'Add a link to hide entire threads.'], - 'Reply Hiding Link': [true, 'Add a link to hide single replies.'], - 'Delete Link': [true, 'Add post and image deletion links to the menu.'], - 'Archive Link': [true, 'Add an archive link to the menu.'] - }, - 'Monitoring': { - 'Thread Updater': [true, 'Fetch and insert new replies. Has more options in its own dialog.'], - 'Unread Count': [true, 'Show the unread posts count in the tab title.'], - 'Hide Unread Count at (0)': [false, 'Hide the unread posts count in the tab title when it reaches 0.'], - 'Unread Favicon': [true, 'Show a different favicon when there are unread posts.'], - 'Unread Line': [true, 'Show a line to distinguish read posts from unread ones.'], - 'Scroll to Last Read Post': [true, 'Scroll back to the last read post when reopening a thread.'], - 'Thread Excerpt': [true, 'Show an excerpt of the thread in the tab title.'], - 'Thread Stats': [true, 'Display reply and image count.'], - 'Page Count in Stats': [false, 'Display the page count in the thread stats as well.'], - 'Updater and Stats in Header': [true, 'Places the thread updater and thread stats in the header instead of floating them.'], - 'Thread Watcher': [true, 'Bookmark threads.'], - 'Toggleable Thread Watcher': [true, 'Adds a shortcut for the thread watcher, hides the watcher by default, and makes it scroll with the page.'] - }, - 'Posting': { - 'Quick Reply': [true, 'All-in-one form to reply, create threads, automate dumping and more.'], - 'Persistent QR': [true, 'The Quick reply won\'t disappear after posting.'], - 'Auto Hide QR': [true, 'Automatically hide the quick reply when posting.'], - 'Open Post in New Tab': [true, 'Open new threads or replies to a thread from the index in a new tab.'], - 'Remember Subject': [false, 'Remember the subject field, instead of resetting after posting.'], - 'Remember QR Size': [false, 'Remember the size of the Quick reply.'], - 'Remember Spoiler': [false, 'Remember the spoiler state, instead of resetting after posting.'], - 'Hide Original Post Form': [true, 'Hide the normal post form.'], - 'Cooldown': [true, 'Indicate the remaining time before posting again.'], - 'Cooldown Prediction': [true, 'Decrease the cooldown time by taking into account upload speed. Disable it if it\'s inaccurate for you.'], - 'Posting Success Notifications': [true, 'Show notifications on successful post creation or file uploading.'], - 'Captcha Warning Notifications': [true, 'When disabled, shows a red border on the CAPTCHA input until a key is pressed instead of a notification.'], - 'Auto-load captcha': [false, 'Automatically load the captcha when you open a thread'] - }, - 'Quote Links': { - 'Quote Backlinks': [true, 'Add quote backlinks.'], - 'OP Backlinks': [true, 'Add backlinks to the OP.'], - 'Quote Inlining': [true, 'Inline quoted post on click.'], - 'Quote Hash Navigation': [false, 'Include an extra link after quotes for autoscrolling to quoted posts.'], - 'Forward Hiding': [true, 'Hide original posts of inlined backlinks.'], - 'Quote Previewing': [true, 'Show quoted post on hover.'], - 'Quote Highlighting': [true, 'Highlight the previewed post.'], - 'Resurrect Quotes': [true, 'Link dead quotes to the archives.'], - 'Mark Quotes of You': [true, 'Add \'(You)\' to quotes linking to your posts.'], - 'Quoted Title': [false, 'Change the page title to reflect you\'ve been quoted.'], - 'Highlight Posts Quoting You': [false, 'Highlights any posts that contain a quote to your post.'], - 'Highlight Own Posts': [false, 'Highlights own posts if Mark Quotes of You is enabled.'], - 'Mark OP Quotes': [true, 'Add \'(OP)\' to OP quotes.'], - 'Mark Cross-thread Quotes': [true, 'Add \'(Cross-thread)\' to cross-threads quotes.'], - 'Quote Threading': [true, 'Thread conversations'] - } - }, - imageExpansion: { - 'Fit width': [false, ''], - 'Fit height': [false, ''], - 'Expand spoilers': [true, 'Expand all images along with spoilers.'], - 'Expand from here': [false, 'Expand all images only from current position to thread end.'], - 'Advance on contract': [false, 'Advance to next post when contracting an expanded image.'] - }, - gallery: { - 'Hide Thumbnails': [false], - 'Fit Width': [true], - 'Fit Height': [true] - }, - threadWatcher: { - 'Current Board': [false, 'Only show watched threads from the current board.'], - 'Auto Watch': [true, 'Automatically watch threads you start.'], - 'Auto Watch Reply': [false, 'Automatically watch threads you reply to.'], - 'Auto Prune': [false, 'Automatically prune 404\'d threads.'] - }, - filter: { - name: "# Filter any namefags:\n#/^(?!Anonymous$)/", - uniqueID: "# Filter a specific ID:\n#/Txhvk1Tl/", - tripcode: "# Filter any tripfag\n#/^!/", - capcode: "# Set a custom class for mods:\n#/Mod$/;highlight:mod;op:yes\n# Set a custom class for moot:\n#/Admin$/;highlight:moot;op:yes", - email: "", - subject: "# Filter Generals on /v/:\n#/general/i;boards:v;op:only", - comment: "# Filter Stallman copypasta on /g/:\n#/what you\'re refer+ing to as linux/i;boards:g", - flag: '', - filename: '', - dimensions: "# Highlight potential wallpapers:\n#/1920x1080/;op:yes;highlight;top:no;boards:w,wg", - filesize: '', - MD5: '' - }, - sauces: "https://www.google.com/searchbyimage?image_url=%TURL\nhttp://iqdb.org/?url=%TURL\n#//tineye.com/search?url=%TURL\n#http://saucenao.com/search.php?url=%TURL\n#http://3d.iqdb.org/?url=%TURL\n#http://regex.info/exif.cgi?imgurl=%URL\n# uploaders:\n#http://imgur.com/upload?url=%URL;text:Upload to imgur\n#http://ompldr.org/upload?url1=%URL;text:Upload to ompldr\n# \"View Same\" in archives:\n#//archive.foolz.us/_/search/image/%MD5/;text:View same on foolz\n#//archive.foolz.us/%board/search/image/%MD5/;text:View same on foolz /%board/\n#//archive.installgentoo.net/%board/image/%MD5;text:View same on installgentoo /%board/", - FappeT: { - fappe: false, - werk: false - }, - 'sageEmoji': '4chan SS', - 'emojiPos': 'before', - 'Custom CSS': false, - Index: { - 'Index Mode': 'paged', - 'Index Sort': 'bump', - 'Show Replies': true, - 'Anchor Hidden Threads': true, - 'Refreshed Navigation': false - }, - Header: { - 'Fixed Header': true, - 'Header auto-hide': false, - 'Header auto-hide on scroll': false, - 'Bottom Header': false, - 'Centered links': false, - 'Header catalog links': false, - 'Bottom Board List': true, - 'Shortcut Icons': true, - 'Custom Board Navigation': true - }, - boardnav: "[ toggle-all ]\na-replace\nc-replace\ng-replace\nk-replace\nv-replace\nvg-replace\nvr-replace\nck-replace\nco-replace\nfit-replace\njp-replace\nmu-replace\nsp-replace\ntv-replace\nvp-replace\n[external-text:\"FAQ\",\"https://github.com/seaweedchan/4chan-x/wiki/Frequently-Asked-Questions\"]", - QR: { - 'QR.personas': "#email:\"sage\";boards:jp;always" - }, - time: '%m/%d/%y(%a)%H:%M:%S', - backlink: '>>%id', - fileInfo: '%L (%p%s, %r)', - favicon: 'ferongr', - usercss: '', - hotkeys: { - 'Toggle board list': ['Ctrl+b', 'Toggle the full board list.'], - 'Toggle header': ['Shift+h', 'Toggle the auto-hide option of the header.'], - 'Open empty QR': ['i', 'Open QR without post number inserted.'], - 'Open QR': ['Shift+i', 'Open QR with post number inserted.'], - 'Open settings': ['Alt+o', 'Open Settings.'], - 'Close': ['Esc', 'Close Settings, Notifications or QR.'], - 'Spoiler tags': ['Ctrl+s', 'Insert spoiler tags.'], - 'Code tags': ['Alt+c', 'Insert code tags.'], - 'Eqn tags': ['Alt+e', 'Insert eqn tags.'], - 'Math tags': ['Alt+m', 'Insert math tags.'], - 'Toggle sage': ['Alt+s', 'Toggle sage in email field'], - 'Submit QR': ['Ctrl+Enter', 'Submit post.'], - 'Watch': ['w', 'Watch thread.'], - 'Update': ['r', 'Update the thread now.'], - 'Expand image': ['Shift+e', 'Expand selected image.'], - 'Expand images': ['e', 'Expand all images.'], - 'Open Gallery': ['g', 'Opens the gallery.'], - 'fappeTyme': ['f', 'Fappe Tyme.'], - 'werkTyme': ['Shift+w', 'Werk Tyme'], - 'Front page': ['0', 'Jump to page 0.'], - 'Open front page': ['Shift+0', 'Open page 0 in a new tab.'], - 'Next page': ['Shift+Right', 'Jump to the next page.'], - 'Previous page': ['Shift+Left', 'Jump to the previous page.'], - 'Open catalog': ['Shift+c', 'Open the catalog of the current board'], - 'Search form': ['Ctrl+Alt+s', 'Focus the search field on the board index.'], - 'Next thread': ['Shift+Down', 'See next thread.'], - 'Previous thread': ['Shift+Up', 'See previous thread.'], - 'Expand thread': ['Ctrl+e', 'Expand thread.'], - 'Open thread': ['o', 'Open thread in current tab.'], - 'Open thread tab': ['Shift+o', 'Open thread in new tab.'], - 'Next reply': ['j', 'Select next reply.'], - 'Previous reply': ['k', 'Select previous reply.'], - 'Deselect reply': ['Shift+d', 'Deselect reply.'], - 'Hide': ['x', 'Hide thread.'], - 'Previous Post Quoting You': ['Alt+Up', 'Scroll to the previous post that quotes you.'], - 'Next Post Quoting You': ['Alt+Down', 'Scroll to the next post that quotes you.'] - }, - updater: { - checkbox: { - 'Beep': [false, 'Beep on new post to completely read thread.'], - 'Auto Scroll': [false, 'Scroll updated posts into view. Only enabled at bottom of page.'], - 'Bottom Scroll': [false, 'Always scroll to the bottom, not the first new post. Useful for event threads.'], - 'Scroll BG': [false, 'Auto-scroll background tabs.'], - 'Auto Update': [true, 'Automatically fetch new posts.'], - 'Optional Increase': [false, 'Increase the intervals between updates on threads without new posts.'] - }, - 'Interval': 30 - } - }; - - Conf = {}; - - c = console; - - d = document; - - doc = d.documentElement; - - g = { - VERSION: '1.4.1', - NAMESPACE: '4chan X.', - boards: {} - }; - - $ = function(selector, root) { - if (root == null) { - root = d.body; - } - return root.querySelector(selector); - }; - - $.extend = function(obj, prop) { - var key, val; - for (key in prop) { - val = prop[key]; - if (prop.hasOwnProperty(key)) { - obj[key] = val; - } - } - }; - - $.DAY = 24 * ($.HOUR = 60 * ($.MINUTE = 60 * ($.SECOND = 1000))); - - $.id = function(id) { - return d.getElementById(id); - }; - - $.ready = function(fc) { - var cb; - if (d.readyState !== 'loading') { - $.queueTask(fc); - return; - } - cb = function() { - $.off(d, 'DOMContentLoaded', cb); - return fc(); - }; - return $.on(d, 'DOMContentLoaded', cb); - }; - - $.formData = function(form) { - var fd, key, val; - if (form instanceof HTMLFormElement) { - return new FormData(form); - } - fd = new FormData(); - for (key in form) { - val = form[key]; - if (val) { - if (typeof val === 'object' && 'newName' in val) { - fd.append(key, val, val.newName); - } else { - fd.append(key, val); - } - } - } - return fd; - }; - - $.extend = function(object, properties) { - var key, val; - for (key in properties) { - val = properties[key]; - object[key] = val; - } - }; - - $.ajax = (function() { - var lastModified; - lastModified = {}; - return function(url, options, extra) { - var form, r, sync, type, upCallbacks, whenModified; - if (extra == null) { - extra = {}; - } - type = extra.type, whenModified = extra.whenModified, upCallbacks = extra.upCallbacks, form = extra.form, sync = extra.sync; - r = new XMLHttpRequest(); - type || (type = form && 'post' || 'get'); - r.open(type, url, !sync); - if (whenModified) { - if (url in lastModified) { - r.setRequestHeader('If-Modified-Since', lastModified[url]); - } - $.on(r, 'load', function() { - return lastModified[url] = r.getResponseHeader('Last-Modified'); - }); - } - if (/\.json$/.test(url)) { - r.responseType = 'json'; - } - $.extend(r, options); - $.extend(r.upload, upCallbacks); - r.send(form); - return r; - }; - })(); - - $.cache = (function() { - var reqs; - reqs = {}; - return function(url, cb, options) { - var err, req, rm; - if (req = reqs[url]) { - if (req.readyState === 4) { - cb.call(req, req.evt); - } else { - req.callbacks.push(cb); - } - return; - } - rm = function() { - return delete reqs[url]; - }; - try { - req = $.ajax(url, options); - } catch (_error) { - err = _error; - return; - } - $.on(req, 'load', function(e) { - var _i, _len, _ref; - _ref = this.callbacks; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - cb = _ref[_i]; - cb.call(this, e); - } - this.evt = e; - return delete this.callbacks; - }); - $.on(req, 'abort error', rm); - req.callbacks = [cb]; - return reqs[url] = req; - }; - })(); - - $.cb = { - checked: function() { - $.set(this.name, this.checked); - return Conf[this.name] = this.checked; - }, - value: function() { - $.set(this.name, this.value.trim()); - return Conf[this.name] = this.value; - } - }; - - $.asap = function(test, cb) { - if (test()) { - return cb(); - } else { - return setTimeout($.asap, 25, test, cb); - } - }; - - $.addStyle = function(css, id) { - var style; - style = $.el('style', { - id: id, - textContent: css - }); - $.asap((function() { - return d.head; - }), function() { - return $.add(d.head, style); - }); - return style; - }; - - $.x = function(path, root) { - root || (root = d.body); - return d.evaluate(path, root, null, 8, null).singleNodeValue; - }; - - $.X = function(path, root) { - root || (root = d.body); - return d.evaluate(path, root, null, 7, null); - }; - - $.addClass = function() { - var className, el, _ref; - el = arguments[0], className = 2 <= arguments.length ? __slice.call(arguments, 1) : []; - return (_ref = el.classList).add.apply(_ref, className); - }; - - $.rmClass = function() { - var className, el, _ref; - el = arguments[0], className = 2 <= arguments.length ? __slice.call(arguments, 1) : []; - return (_ref = el.classList).remove.apply(_ref, className); - }; - - $.toggleClass = function(el, className) { - return el.classList.toggle(className); - }; - - $.hasClass = function(el, className) { - return __indexOf.call(el.classList, className) >= 0; - }; - - $.rm = function(el) { - return el.remove(); - }; - - $.rmAll = function(root) { - return root.textContent = null; - }; - - $.tn = function(s) { - return d.createTextNode(s); - }; - - $.frag = function() { - return d.createDocumentFragment(); - }; - - $.nodes = function(nodes) { - var frag, node, _i, _len; - if (!(nodes instanceof Array)) { - return nodes; - } - frag = $.frag(); - for (_i = 0, _len = nodes.length; _i < _len; _i++) { - node = nodes[_i]; - frag.appendChild(node); - } - return frag; - }; - - $.add = function(parent, el) { - return parent.appendChild($.nodes(el)); - }; - - $.prepend = function(parent, el) { - return parent.insertBefore($.nodes(el), parent.firstChild); - }; - - $.after = function(root, el) { - return root.parentNode.insertBefore($.nodes(el), root.nextSibling); - }; - - $.before = function(root, el) { - return root.parentNode.insertBefore($.nodes(el), root); - }; - - $.replace = function(root, el) { - return root.parentNode.replaceChild($.nodes(el), root); - }; - - $.el = function(tag, properties) { - var el; - el = d.createElement(tag); - if (properties) { - $.extend(el, properties); - } - return el; - }; - - $.on = function(el, events, handler) { - var event, _i, _len, _ref; - _ref = events.split(' '); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - event = _ref[_i]; - el.addEventListener(event, handler, false); - } - }; - - $.off = function(el, events, handler) { - var event, _i, _len, _ref; - _ref = events.split(' '); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - event = _ref[_i]; - el.removeEventListener(event, handler, false); - } - }; - - $.event = function(event, detail, root) { - if (root == null) { - root = d; - } - return root.dispatchEvent(new CustomEvent(event, { - bubbles: true, - detail: detail - })); - }; - - $.open = GM_openInTab; - - $.debounce = function(wait, fn) { - var args, exec, lastCall, that, timeout; - lastCall = 0; - timeout = null; - that = null; - args = null; - exec = function() { - lastCall = Date.now(); - return fn.apply(that, args); - }; - return function() { - args = arguments; - that = this; - if (lastCall < Date.now() - wait) { - return exec(); - } - clearTimeout(timeout); - return timeout = setTimeout(exec, wait); - }; - }; - - $.queueTask = (function() { - var execTask, taskChannel, taskQueue; - taskQueue = []; - execTask = function() { - var args, func, task; - task = taskQueue.shift(); - func = task[0]; - args = Array.prototype.slice.call(task, 1); - return func.apply(func, args); - }; - if (window.MessageChannel) { - taskChannel = new MessageChannel(); - taskChannel.port1.onmessage = execTask; - return function() { - taskQueue.push(arguments); - return taskChannel.port2.postMessage(null); - }; - } else { - return function() { - taskQueue.push(arguments); - return setTimeout(execTask, 0); - }; - } - })(); - - $.globalEval = function(code) { - var script; - script = $.el('script', { - textContent: code - }); - $.add(d.head || doc, script); - return $.rm(script); - }; - - $.bytesToString = function(size) { - var unit; - unit = 0; - while (size >= 1024) { - size /= 1024; - unit++; - } - size = unit > 1 ? Math.round(size * 100) / 100 : Math.round(size); - return "" + size + " " + ['B', 'KB', 'MB', 'GB'][unit]; - }; - - $.minmax = function(value, min, max) { - return (value < min ? min : value > max ? max : value); - }; - - $.item = function(key, val) { - var item; - item = {}; - item[key] = val; - return item; - }; - - $.syncing = {}; - - $.sync = (function() { - $.on(window, 'storage', function(_arg) { - var cb, key, newValue; - key = _arg.key, newValue = _arg.newValue; - if (cb = $.syncing[key]) { - return cb(JSON.parse(newValue), key); - } - }); - return function(key, cb) { - return $.syncing[g.NAMESPACE + key] = cb; - }; - })(); - - $.desync = function(key) { - return delete $.syncing[g.NAMESPACE + key]; - }; - - $["delete"] = function(keys) { - var key, _i, _len; - if (!(keys instanceof Array)) { - keys = [keys]; - } - for (_i = 0, _len = keys.length; _i < _len; _i++) { - key = keys[_i]; - key = g.NAMESPACE + key; - localStorage.removeItem(key); - GM_deleteValue(key); - } - }; - - $.get = function(key, val, cb) { - var items; - if (typeof cb === 'function') { - items = $.item(key, val); - } else { - items = key; - cb = val; - } - return $.queueTask(function() { - for (key in items) { - if (val = GM_getValue(g.NAMESPACE + key)) { - items[key] = JSON.parse(val); - } - } - return cb(items); - }); - }; - - $.set = (function() { - var set; - set = function(key, val) { - key = g.NAMESPACE + key; - val = JSON.stringify(val); - if (key in $.syncing) { - localStorage.setItem(key, val); - } - return GM_setValue(key, val); - }; - return function(keys, val) { - var key; - if (typeof keys === 'string') { - set(keys, val); - return; - } - for (key in keys) { - val = keys[key]; - set(key, val); - } - }; - })(); - - $.clear = function(cb) { - $["delete"](GM_listValues().map(function(key) { - return key.replace(g.NAMESPACE, ''); - })); - return typeof cb === "function" ? cb() : void 0; - }; - - $$ = function(selector, root) { - if (root == null) { - root = d.body; - } - return __slice.call(root.querySelectorAll(selector)); - }; - - Callbacks = (function() { - function Callbacks(type) { - this.type = type; - this.keys = []; - } - - Callbacks.prototype.push = function(_arg) { - var cb, name; - name = _arg.name, cb = _arg.cb; - if (this[name]) { - this.connect(name); - } - if (!this[name]) { - this.keys.push(name); - } - return this[name] = cb; - }; - - Callbacks.prototype.connect = function(name) { - if (this[name].disconnected) { - return delete this[name].disconnected; - } - }; - - Callbacks.prototype.disconnect = function(name) { - if (this[name]) { - return this[name].disconnected = true; - } - }; - - Callbacks.prototype.execute = function(node) { - var err, errors, name, _i, _len, _ref; - _ref = this.keys; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - name = _ref[_i]; - try { - if (!this[name].disconnected) { - this[name].call(node); - } - } catch (_error) { - err = _error; - if (!errors) { - errors = []; - } - errors.push({ - message: ['"', name, '" crashed on node ', this.type, ' No.', node.ID, ' (', node.board, ').'].join(''), - error: err - }); - } - } - if (errors) { - return Main.handleErrors(errors); - } - }; - - return Callbacks; - - })(); - - Board = (function() { - Board.prototype.toString = function() { - return this.ID; - }; - - function Board(ID) { - this.ID = ID; - this.threads = new SimpleDict; - this.posts = new SimpleDict; - g.boards[this] = this; - } - - return Board; - - })(); - - Thread = (function() { - Thread.callbacks = new Callbacks('Thread'); - - Thread.prototype.toString = function() { - return this.ID; - }; - - function Thread(ID, board) { - this.ID = ID; - this.board = board; - this.fullID = "" + this.board + "." + this.ID; - this.posts = new SimpleDict; - this.isSticky = false; - this.isClosed = false; - this.postLimit = false; - this.fileLimit = false; - g.threads.push(this.fullID, board.threads.push(this, this)); - } - - Thread.prototype.setPage = function(pageNum) { - var icon, key, _i, _len, _ref; - icon = $('.page-num', this.OP.nodes.post); - _ref = ['title', 'textContent']; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - key = _ref[_i]; - icon[key] = icon[key].replace(/\d+/, pageNum); - } - }; - - Thread.prototype.setStatus = function(type, status) { - var icon, name, root, typeLC; - name = "is" + type; - if (this[name] === status) { - return; - } - this[name] = status; - if (!this.OP) { - return; - } - typeLC = type.toLowerCase(); - if (!status) { - $.rm($("." + typeLC + "Icon", this.OP.nodes.info)); - return; - } - icon = $.el('img', { - src: "//s.4cdn.org/image/" + typeLC + (window.devicePixelRatio >= 2 ? '@2x' : '') + ".gif", - alt: type, - title: type, - className: "" + typeLC + "Icon" - }); - root = type === 'Closed' && this.isSticky ? $('.stickyIcon', this.OP.nodes.info) : g.VIEW === 'index' ? $('.page-num', this.OP.nodes.info) : $('[title="Quote this post"]', this.OP.nodes.info); - return $.after(root, [$.tn(' '), icon]); - }; - - Thread.prototype.kill = function() { - this.isDead = true; - return this.timeOfDeath = Date.now(); - }; - - Thread.prototype.collect = function() { - this.posts.forEach(function(post) { - return post.collect(); - }); - g.threads.rm(this.fullID); - return this.board.threads.rm(this); - }; - - return Thread; - - })(); - - Post = (function() { - Post.callbacks = new Callbacks('Post'); - - Post.prototype.toString = function() { - return this.ID; - }; - - function Post(root, thread, board, that) { - var capcode, date, email, flag, info, name, post, subject, tripcode, uniqueID; - this.thread = thread; - this.board = board; - if (that == null) { - that = {}; - } - this.ID = +root.id.slice(2); - this.fullID = "" + this.board + "." + this.ID; - if (that.isOriginalMarkup) { - this.cleanup(root); - } - post = $('.post', root); - info = $('.postInfo', post); - this.nodes = { - root: root, - post: post, - info: info, - comment: $('.postMessage', post), - links: [], - quotelinks: [], - backlinks: info.getElementsByClassName('backlink') - }; - if (!(this.isReply = $.hasClass(post, 'reply'))) { - this.thread.OP = this; - this.thread.isSticky = !!$('.stickyIcon', info); - this.thread.isClosed = !!$('.closedIcon', info); - } - this.info = {}; - if (subject = $('.subject', info)) { - this.nodes.subject = subject; - this.info.subject = subject.textContent; - } - if (name = $('.name', info)) { - this.nodes.name = name; - this.info.name = name.textContent; - } - if (email = $('.useremail', info)) { - this.nodes.email = email; - this.info.email = decodeURIComponent(email.href.slice(7)); - } - if (tripcode = $('.postertrip', info)) { - this.nodes.tripcode = tripcode; - this.info.tripcode = tripcode.textContent; - } - if (uniqueID = $('.posteruid', info)) { - this.nodes.uniqueID = uniqueID; - this.info.uniqueID = uniqueID.firstElementChild.textContent; - } - if (capcode = $('.capcode.hand', info)) { - this.nodes.capcode = capcode; - this.info.capcode = capcode.textContent.replace('## ', ''); - } - if (flag = $('.flag, .countryFlag', info)) { - this.nodes.flag = flag; - this.info.flag = flag.title; - } - if (date = $('.dateTime', info)) { - this.nodes.date = date; - this.info.date = new Date(date.dataset.utc * 1000); - } - this.parseComment(); - this.parseQuotes(); - this.parseFile(that); - this.clones = []; - g.posts.push(this.fullID, thread.posts.push(this, board.posts.push(this, this))); - if (that.isArchived) { - this.kill(); - } - } - - Post.prototype.parseComment = function() { - var bq, i, node, nodes, text; - this.nodes.comment.normalize(); - bq = this.nodes.comment.cloneNode(true); - nodes = $$('.abbr, .exif, b', bq); - i = 0; - while (node = nodes[i++]) { - $.rm(node); - } - text = ""; - nodes = $.X('.//br|.//text()', bq); - i = 0; - while (node = nodes.snapshotItem(i++)) { - text += node.data || '\n'; - } - return this.info.comment = text.trim().replace(/\s+$/gm, ''); - }; - - Post.prototype.parseQuotes = function() { - var quotelink, _i, _len, _ref; - this.quotes = []; - _ref = $$('.quotelink', this.nodes.comment); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - quotelink = _ref[_i]; - this.parseQuote(quotelink); - } - }; - - Post.prototype.parseQuote = function(quotelink) { - var fullID, match; - if (!(match = quotelink.href.match(/boards\.4chan\.org\/([^\/]+)\/res\/\d+#p(\d+)$/))) { - return; - } - this.nodes.quotelinks.push(quotelink); - if (this.isClone) { - return; - } - fullID = "" + match[1] + "." + match[2]; - if (__indexOf.call(this.quotes, fullID) < 0) { - return this.quotes.push(fullID); - } - }; - - Post.prototype.parseFile = function(that) { - var anchor, fileEl, fileText, nameNode, size, thumb, unit; - if (!((fileEl = $('.file', this.nodes.post)) && (thumb = $('img[data-md5]', fileEl)))) { - return; - } - anchor = thumb.parentNode; - fileText = fileEl.firstElementChild; - this.file = { - text: fileText, - thumb: thumb, - URL: anchor.href, - size: thumb.alt.match(/[\d.]+\s\w+/)[0], - MD5: thumb.dataset.md5, - isSpoiler: $.hasClass(anchor, 'imgspoiler') - }; - size = +this.file.size.match(/[\d.]+/)[0]; - unit = ['B', 'KB', 'MB', 'GB'].indexOf(this.file.size.match(/\w+$/)[0]); - while (unit-- > 0) { - size *= 1024; - } - this.file.sizeInBytes = size; - this.file.thumbURL = that.isArchived ? thumb.src : "" + location.protocol + "//t.4cdn.org/" + this.board + "/thumb/" + (this.file.URL.match(/(\d+)\./)[1]) + "s.jpg"; - this.file.name = (nameNode = $('span', fileText)) ? nameNode.title || nameNode.textContent : fileText.title; - if (this.file.isImage = /(jpg|png|gif)$/i.test(this.file.name)) { - return this.file.dimensions = fileText.textContent.match(/\d+x\d+/)[0]; - } - }; - - Post.prototype.cleanup = function(root) { - var node, _i, _j, _len, _len1, _ref, _ref1; - _ref = $$('.mobile', root); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - node = _ref[_i]; - $.rm(node); - } - _ref1 = $$('.desktop', root); - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - node = _ref1[_j]; - $.rmClass(node, 'desktop'); - } - }; - - Post.prototype.kill = function(file, now) { - var clone, quotelink, strong, _i, _j, _len, _len1, _ref, _ref1; - now || (now = new Date()); - if (file) { - if (this.file.isDead) { - return; - } - this.file.isDead = true; - this.file.timeOfDeath = now; - $.addClass(this.nodes.root, 'deleted-file'); - } else { - if (this.isDead) { - return; - } - this.isDead = true; - this.timeOfDeath = now; - $.addClass(this.nodes.root, 'deleted-post'); - } - if (!(strong = $('strong.warning', this.nodes.info))) { - strong = $.el('strong', { - className: 'warning', - textContent: this.isReply ? '[Deleted]' : '[Dead]' - }); - $.after($('input', this.nodes.info), strong); - } - strong.textContent = file ? '[File deleted]' : '[Deleted]'; - if (this.isClone) { - return; - } - _ref = this.clones; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - clone = _ref[_i]; - clone.kill(file, now); - } - if (file) { - return; - } - _ref1 = Get.allQuotelinksLinkingTo(this); - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - quotelink = _ref1[_j]; - if (!(!$.hasClass(quotelink, 'deadlink'))) { - continue; - } - quotelink.textContent = quotelink.textContent + '\u00A0(Dead)'; - $.addClass(quotelink, 'deadlink'); - } - }; - - Post.prototype.resurrect = function() { - var clone, quotelink, strong, _i, _j, _len, _len1, _ref, _ref1; - delete this.isDead; - delete this.timeOfDeath; - $.rmClass(this.nodes.root, 'deleted-post'); - strong = $('strong.warning', this.nodes.info); - if (this.file && this.file.isDead) { - strong.textContent = '[File deleted]'; - } else { - $.rm(strong); - } - if (this.isClone) { - return; - } - _ref = this.clones; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - clone = _ref[_i]; - clone.resurrect(); - } - _ref1 = Get.allQuotelinksLinkingTo(this); - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - quotelink = _ref1[_j]; - if ($.hasClass(quotelink, 'deadlink')) { - quotelink.textContent = quotelink.textContent.replace('\u00A0(Dead)', ''); - $.rmClass(quotelink, 'deadlink'); - } - } - }; - - Post.prototype.collect = function() { - this.kill(); - g.posts.rm(this.fullID); - this.thread.posts.rm(this); - return this.board.posts.rm(this); - }; - - Post.prototype.addClone = function(context) { - return new Clone(this, context); - }; - - Post.prototype.rmClone = function(index) { - var clone, _i, _len, _ref; - this.clones.splice(index, 1); - _ref = this.clones.slice(index); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - clone = _ref[_i]; - clone.nodes.root.dataset.clone = index++; - } - }; - - return Post; - - })(); - - Clone = (function(_super) { - __extends(Clone, _super); - - function Clone(origin, context) { - var file, info, inline, inlined, key, nodes, post, root, val, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3; - this.origin = origin; - this.context = context; - _ref = ['ID', 'fullID', 'board', 'thread', 'info', 'quotes', 'isReply']; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - key = _ref[_i]; - this[key] = origin[key]; - } - nodes = origin.nodes; - root = nodes.root.cloneNode(true); - post = $('.post', root); - info = $('.postInfo', post); - this.nodes = { - root: root, - post: post, - info: info, - comment: $('.postMessage', post), - quotelinks: [], - backlinks: info.getElementsByClassName('backlink') - }; - _ref1 = $$('.inline', post); - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - inline = _ref1[_j]; - $.rm(inline); - } - _ref2 = $$('.inlined', post); - for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { - inlined = _ref2[_k]; - $.rmClass(inlined, 'inlined'); - } - root.hidden = false; - $.rmClass(root, 'forwarded'); - $.rmClass(post, 'highlight'); - if (nodes.subject) { - this.nodes.subject = $('.subject', info); - } - if (nodes.name) { - this.nodes.name = $('.name', info); - } - if (nodes.email) { - this.nodes.email = $('.useremail', info); - } - if (nodes.tripcode) { - this.nodes.tripcode = $('.postertrip', info); - } - if (nodes.uniqueID) { - this.nodes.uniqueID = $('.posteruid', info); - } - if (nodes.capcode) { - this.nodes.capcode = $('.capcode', info); - } - if (nodes.flag) { - this.nodes.flag = $('.countryFlag', info); - } - if (nodes.date) { - this.nodes.date = $('.dateTime', info); - } - this.parseQuotes(); - if (origin.file) { - this.file = {}; - _ref3 = origin.file; - for (key in _ref3) { - val = _ref3[key]; - this.file[key] = val; - } - file = $('.file', post); - this.file.text = file.firstElementChild; - this.file.thumb = $('img[data-md5]', file); - this.file.fullImage = $('.full-image', file); - } - if (origin.isDead) { - this.isDead = true; - } - this.isClone = true; - root.dataset.clone = origin.clones.push(this) - 1; - } - - return Clone; - - })(Post); - - DataBoard = (function() { - DataBoard.keys = ['hiddenThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts', 'watchedThreads']; - - function DataBoard(key, sync, dontClean) { - var init, - _this = this; - this.key = key; - this.onSync = __bind(this.onSync, this); - this.data = Conf[key]; - $.sync(key, this.onSync); - if (!dontClean) { - this.clean(); - } - if (!sync) { - return; - } - init = function() { - $.off(d, '4chanXInitFinished', init); - return _this.sync = sync; - }; - $.on(d, '4chanXInitFinished', init); - } - - DataBoard.prototype.save = function() { - return $.set(this.key, this.data); - }; - - DataBoard.prototype["delete"] = function(_arg) { - var boardID, postID, threadID; - boardID = _arg.boardID, threadID = _arg.threadID, postID = _arg.postID; - if (postID) { - delete this.data.boards[boardID][threadID][postID]; - this.deleteIfEmpty({ - boardID: boardID, - threadID: threadID - }); - } else if (threadID) { - delete this.data.boards[boardID][threadID]; - this.deleteIfEmpty({ - boardID: boardID - }); - } else { - delete this.data.boards[boardID]; - } - return this.save(); - }; - - DataBoard.prototype.deleteIfEmpty = function(_arg) { - var boardID, threadID; - boardID = _arg.boardID, threadID = _arg.threadID; - if (threadID) { - if (!Object.keys(this.data.boards[boardID][threadID]).length) { - delete this.data.boards[boardID][threadID]; - return this.deleteIfEmpty({ - boardID: boardID - }); - } - } else if (!Object.keys(this.data.boards[boardID]).length) { - return delete this.data.boards[boardID]; - } - }; - - DataBoard.prototype.set = function(_arg) { - var boardID, postID, threadID, val, _base, _base1, _base2; - boardID = _arg.boardID, threadID = _arg.threadID, postID = _arg.postID, val = _arg.val; - if (postID !== void 0) { - ((_base = ((_base1 = this.data.boards)[boardID] || (_base1[boardID] = {})))[threadID] || (_base[threadID] = {}))[postID] = val; - } else if (threadID !== void 0) { - ((_base2 = this.data.boards)[boardID] || (_base2[boardID] = {}))[threadID] = val; - } else { - this.data.boards[boardID] = val; - } - return this.save(); - }; - - DataBoard.prototype.get = function(_arg) { - var ID, board, boardID, defaultValue, postID, thread, threadID, val, _i, _len; - boardID = _arg.boardID, threadID = _arg.threadID, postID = _arg.postID, defaultValue = _arg.defaultValue; - if (board = this.data.boards[boardID]) { - if (!threadID) { - if (postID) { - for (thread = _i = 0, _len = board.length; _i < _len; thread = ++_i) { - ID = board[thread]; - if (postID in thread) { - val = thread[postID]; - break; - } - } - } else { - val = board; - } - } else if (thread = board[threadID]) { - val = postID ? thread[postID] : thread; - } - } - return val || defaultValue; - }; - - DataBoard.prototype.clean = function() { - var boardID, now, val, _ref; - _ref = this.data.boards; - for (boardID in _ref) { - val = _ref[boardID]; - this.deleteIfEmpty({ - boardID: boardID - }); - } - now = Date.now(); - if ((this.data.lastChecked || 0) < now - 2 * $.HOUR) { - this.data.lastChecked = now; - for (boardID in this.data.boards) { - this.ajaxClean(boardID); - } - } - return this.save(); - }; - - DataBoard.prototype.ajaxClean = function(boardID) { - var _this = this; - return $.cache("//a.4cdn.org/" + boardID + "/threads.json", function(e) { - var board, page, thread, threads, _i, _j, _len, _len1, _ref, _ref1; - if (e.target.status !== 200) { - if (e.target.status === 404) { - _this["delete"](boardID); - } - return; - } - board = _this.data.boards[boardID]; - threads = {}; - _ref = e.target.response; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - page = _ref[_i]; - _ref1 = page.threads; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - thread = _ref1[_j]; - if (thread.no in board) { - threads[thread.no] = board[thread.no]; - } - } - } - _this.data.boards[boardID] = threads; - _this.deleteIfEmpty({ - boardID: boardID - }); - return _this.save(); - }); - }; - - DataBoard.prototype.onSync = function(data) { - this.data = data || { - boards: {} - }; - return typeof this.sync === "function" ? this.sync() : void 0; - }; - - DataBoard.prototype.disconnect = function() { - $.desync(this.key); - delete this.sync; - return delete this.data; - }; - - return DataBoard; - - })(); - - Notice = (function() { - function Notice(type, content, timeout) { - this.timeout = timeout; - this.close = __bind(this.close, this); - this.add = __bind(this.add, this); - this.el = $.el('div', { - innerHTML: '
' - }); - this.el.style.opacity = 0; - this.setType(type); - $.on(this.el.firstElementChild, 'click', this.close); - if (typeof content === 'string') { - content = $.tn(content); - } - $.add(this.el.lastElementChild, content); - $.ready(this.add); - } - - Notice.prototype.setType = function(type) { - return this.el.className = "notification " + type; - }; - - Notice.prototype.add = function() { - if (d.hidden) { - $.on(d, 'visibilitychange', this.add); - return; - } - $.off(d, 'visibilitychange', this.add); - $.add(Header.noticesRoot, this.el); - this.el.clientHeight; - this.el.style.opacity = 1; - if (this.timeout) { - return setTimeout(this.close, this.timeout * $.SECOND); - } - }; - - Notice.prototype.close = function() { - $.off(d, 'visibilitychange', this.add); - return $.rm(this.el); - }; - - return Notice; - - })(); - - RandomAccessList = (function() { - function RandomAccessList(items) { - var item, _i, _len; - this.length = 0; - if (items) { - for (_i = 0, _len = items.length; _i < _len; _i++) { - item = items[_i]; - this.push(item); - } - } - } - - RandomAccessList.prototype.push = function(data) { - var ID, item, last; - ID = data.ID; - ID || (ID = data.id); - if (this[ID]) { - return; - } - last = this.last; - this[ID] = item = { - prev: last, - next: null, - data: data, - ID: ID - }; - item.prev = last; - this.last = last ? last.next = item : this.first = item; - return this.length++; - }; - - RandomAccessList.prototype.before = function(root, item) { - var prev; - if (item.next === root) { - return; - } - this.rmi(item); - prev = root.prev; - root.prev = item; - item.next = root; - item.prev = prev; - if (prev) { - return prev.next = item; - } - }; - - RandomAccessList.prototype.after = function(root, item) { - var next; - if (item.prev === root) { - return; - } - this.rmi(item); - next = root.next; - root.next = item; - item.prev = root; - item.next = next; - if (next) { - return next.prev = item; - } - }; - - RandomAccessList.prototype.prepend = function(item) { - var first; - first = this.first; - if (item === first || !this[item.ID]) { - return; - } - this.rmi(item); - item.next = first; - first.prev = item; - this.first = item; - return delete item.prev; - }; - - RandomAccessList.prototype.shift = function() { - return this.rm(this.first.ID); - }; - - RandomAccessList.prototype.order = function() { - var item, order; - order = [item = this.first]; - while (item = item.next) { - order.push(item); - } - return order; - }; - - RandomAccessList.prototype.rm = function(ID) { - var item; - item = this[ID]; - if (!item) { - return; - } - delete this[ID]; - this.length--; - this.rmi(item); - delete item.next; - return delete item.prev; - }; - - RandomAccessList.prototype.rmi = function(item) { - var next, prev; - prev = item.prev, next = item.next; - if (prev) { - prev.next = next; - } else { - this.first = next; - } - if (next) { - return next.prev = prev; - } else { - return this.last = prev; - } - }; - - return RandomAccessList; - - })(); - - SimpleDict = (function() { - function SimpleDict() { - this.keys = []; - } - - SimpleDict.prototype.push = function(key, data) { - key = "" + key; - if (!this[key]) { - this.keys.push(key); - } - return this[key] = data; - }; - - SimpleDict.prototype.rm = function(key) { - var i; - key = "" + key; - if ((i = this.keys.indexOf(key)) !== -1) { - this.keys.splice(i, 1); - return delete this[key]; - } - }; - - SimpleDict.prototype.forEach = function(fn) { - var key, _i, _len, _ref, _results; - _ref = __slice.call(this.keys); - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - key = _ref[_i]; - _results.push(fn(this[key])); - } - return _results; - }; - - return SimpleDict; - - })(); - - Polyfill = { - init: function() {}, - notificationPermission: function() { - if (!window.Notification || 'permission' in Notification || !window.webkitNotifications) { - return; - } - return Object.defineProperty(Notification, 'permission', { - get: function() { - switch (webkitNotifications.checkPermission()) { - case 0: - return 'granted'; - case 1: - return 'default'; - case 2: - return 'denied'; - } - } - }); - }, - toBlob: function() { - var _base; - return (_base = HTMLCanvasElement.prototype).toBlob || (_base.toBlob = function(cb) { - var data, i, l, ui8a, _i; - data = atob(this.toDataURL().slice(22)); - l = data.length; - ui8a = new Uint8Array(l); - for (i = _i = 0; _i < l; i = _i += 1) { - ui8a[i] = data.charCodeAt(i); - } - return cb(new Blob([ui8a], { - type: 'image/png' - })); - }); - }, - visibility: function() { - if ('visibilityState' in d) { - return; - } - Object.defineProperties(HTMLDocument.prototype, { - visibilityState: { - get: function() { - return this.webkitVisibilityState; - } - }, - hidden: { - get: function() { - return this.webkitHidden; - } - } - }); - return $.on(d, 'webkitvisibilitychange', function() { - return $.event('visibilitychange'); - }); - } - }; - - Header = { - init: function() { - var barFixedToggler, barPositionToggler, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, menuButton, scrollHeaderToggler, shortcutToggler, - _this = this; - this.menu = new UI.Menu('header'); - menuButton = $.el('span', { - className: 'menu-button', - innerHTML: '' - }); - barFixedToggler = $.el('label', { - innerHTML: ' Fixed Header' - }); - headerToggler = $.el('label', { - innerHTML: ' Auto-hide header' - }); - scrollHeaderToggler = $.el('label', { - innerHTML: ' Auto-hide header on scroll' - }); - barPositionToggler = $.el('label', { - innerHTML: ' Bottom header' - }); - linkJustifyToggler = $.el('label', { - innerHTML: " Centered links" - }); - customNavToggler = $.el('label', { - innerHTML: ' Custom board navigation' - }); - footerToggler = $.el('label', { - innerHTML: " Hide bottom board list" - }); - shortcutToggler = $.el('label', { - innerHTML: " Shortcut Icons" - }); - editCustomNav = $.el('a', { - textContent: 'Edit custom board navigation', - href: 'javascript:;' - }); - this.barFixedToggler = barFixedToggler.firstElementChild; - this.scrollHeaderToggler = scrollHeaderToggler.firstElementChild; - this.barPositionToggler = barPositionToggler.firstElementChild; - this.linkJustifyToggler = linkJustifyToggler.firstElementChild; - this.headerToggler = headerToggler.firstElementChild; - this.footerToggler = footerToggler.firstElementChild; - this.shortcutToggler = shortcutToggler.firstElementChild; - this.customNavToggler = customNavToggler.firstElementChild; - $.on(menuButton, 'click', this.menuToggle); - $.on(this.headerToggler, 'change', this.toggleBarVisibility); - $.on(this.barFixedToggler, 'change', this.toggleBarFixed); - $.on(this.barPositionToggler, 'change', this.toggleBarPosition); - $.on(this.scrollHeaderToggler, 'change', this.toggleHideBarOnScroll); - $.on(this.linkJustifyToggler, 'change', this.toggleLinkJustify); - $.on(this.headerToggler, 'change', this.toggleBarVisibility); - $.on(this.footerToggler, 'change', this.toggleFooterVisibility); - $.on(this.shortcutToggler, 'change', this.toggleShortcutIcons); - $.on(this.customNavToggler, 'change', this.toggleCustomNav); - $.on(editCustomNav, 'click', this.editCustomNav); - this.setBarFixed(Conf['Fixed Header']); - this.setHideBarOnScroll(Conf['Header auto-hide on scroll']); - this.setBarVisibility(Conf['Header auto-hide']); - this.setLinkJustify(Conf['Centered links']); - this.setShortcutIcons(Conf['Shortcut Icons']); - $.sync('Fixed Header', this.setBarFixed); - $.sync('Header auto-hide on scroll', this.setHideBarOnScroll); - $.sync('Bottom Header', this.setBarPosition); - $.sync('Shortcut Icons', this.setShortcutIcons); - $.sync('Header auto-hide', this.setBarVisibility); - $.sync('Centered links', this.setLinkJustify); - this.addShortcut(menuButton); - $.event('AddMenuEntry', { - type: 'header', - el: $.el('span', { - textContent: 'Header' - }), - order: 107, - subEntries: [ - { - el: barFixedToggler - }, { - el: headerToggler - }, { - el: scrollHeaderToggler - }, { - el: barPositionToggler - }, { - el: linkJustifyToggler - }, { - el: footerToggler - }, { - el: shortcutToggler - }, { - el: customNavToggler - }, { - el: editCustomNav - } - ] - }); - $.on(window, 'load hashchange', Header.hashScroll); - $.on(d, 'CreateNotification', this.createNotification); - $.asap((function() { - return d.body; - }), function() { - if (!Main.isThisPageLegit()) { - return; - } - $.asap((function() { - return $.id('boardNavMobile') || d.readyState !== 'loading'; - }), Header.setBoardList); - $.prepend(d.body, _this.bar); - $.add(d.body, Header.hover); - _this.setBarPosition(Conf['Bottom Header']); - return _this; - }); - $.ready(function() { - var a, cs, footer, _i, _len, _ref; - _this.footer = footer = $.id('boardNavDesktopFoot'); - if (Conf['JSON Navigation']) { - _ref = $$('a', footer); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - a = _ref[_i]; - $.on(a, 'click', Navigate.navigate); - } - } - if (a = $("a[href*='/" + g.BOARD + "/']", footer)) { - a.className = 'current'; - } - cs = $.el('a', { - id: 'settingsWindowLink', - href: 'javascript:;', - textContent: 'Catalog Settings' - }); - if (g.VIEW === 'catalog') { - _this.addShortcut(cs); - } - Header.setFooterVisibility(Conf['Bottom Board List']); - return $.sync('Bottom Board List', Header.setFooterVisibility); - }); - return this.enableDesktopNotifications(); - }, - bar: $.el('div', { - id: 'header-bar' - }), - noticesRoot: $.el('div', { - id: 'notifications' - }), - shortcuts: $.el('span', { - id: 'shortcuts' - }), - hover: $.el('div', { - id: 'hoverUI' - }), - toggle: $.el('div', { - id: 'scroll-marker' - }), - initReady: function() { - Header.setBoardList(); - return Header.addNav(); - }, - setBoardList: function() { - var a, boardList, btn, fourchannav, fullBoardList, _i, _len, _ref; - fourchannav = $.id('boardNavDesktop'); - Header.boardList = boardList = $.el('span', { - id: 'board-list', - innerHTML: "" - }); - _ref = $$('a', boardList); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - a = _ref[_i]; - if (Conf['JSON Navigation']) { - $.on(a, 'click', Navigate.navigate); - } - if (a.pathname.split('/')[1] === g.BOARD.ID) { - a.className = 'current'; - } - } - fullBoardList = $('#full-board-list', boardList); - btn = $('.hide-board-list-button', fullBoardList); - $.on(btn, 'click', Header.toggleBoardList); - $.rm($('#navtopright', fullBoardList)); - $.add(boardList, fullBoardList); - $.add(Header.bar, [Header.boardList, Header.shortcuts, Header.noticesRoot, Header.toggle]); - Header.setCustomNav(Conf['Custom Board Navigation']); - Header.generateBoardList(Conf['boardnav'].replace(/(\r\n|\n|\r)/g, ' ')); - $.sync('Custom Board Navigation', Header.setCustomNav); - return $.sync('boardnav', Header.generateBoardList); - }, - generateBoardList: function(text) { - var as, list, nodes; - list = $('#custom-board-list', Header.boardList); - $.rmAll(list); - if (!text) { - return; - } - as = $$('#full-board-list a[title]', Header.boardList); - nodes = text.match(/[\w@]+((-(all|title|replace|full|index|catalog|url:"[^"]+[^"]"|text:"[^"]+")|\,"[^"]+[^"]"))*|[^\w@]+/g).map(function(t) { - var a, board, m, _i, _len; - if (/^[^\w@]/.test(t)) { - return $.tn(t); - } - if (/^toggle-all/.test(t)) { - a = $.el('a', { - className: 'show-board-list-button', - textContent: (t.match(/-text:"(.+)"/) || [null, '+'])[1], - href: 'javascript:;' - }); - $.on(a, 'click', Header.toggleBoardList); - return a; - } - if (/^external/.test(t)) { - a = $.el('a', { - href: (t.match(/\,"(.+)"/) || [null, '+'])[1], - textContent: (t.match(/-text:"(.+)"\,/) || [null, '+'])[1], - className: 'external' - }); - return a; - } - board = /^current/.test(t) ? g.BOARD.ID : t.match(/^[^-]+/)[0]; - for (_i = 0, _len = as.length; _i < _len; _i++) { - a = as[_i]; - if (a.textContent === board) { - a = a.cloneNode(true); - if (Conf['JSON Navigation']) { - $.on(a, 'click', Navigate.navigate); - } - a.textContent = /-title/.test(t) || /-replace/.test(t) && $.hasClass(a, 'current') ? a.title : /-full/.test(t) ? "/" + board + "/ - " + a.title : (m = t.match(/-text:"(.+)"/)) ? m[1] : a.textContent; - if (m = t.match(/-(index|catalog)/)) { - a.dataset.only = m[1]; - a.href = "//boards.4chan.org/" + board + "/"; - if (m[1] === 'catalog') { - if (Conf['External Catalog']) { - a.href = CatalogLinks.external(board); - } else { - a.href += 'catalog'; - } - $.addClass(a, 'catalog'); - } - } - if (board === '@') { - $.addClass(a, 'navSmall'); - } - return a; - } - } - return $.tn(t); - }); - return $.add(list, nodes); - }, - toggleBoardList: function() { - var bar, custom, full, showBoardList; - bar = Header.bar; - custom = $('#custom-board-list', bar); - full = $('#full-board-list', bar); - showBoardList = !full.hidden; - custom.hidden = !showBoardList; - return full.hidden = showBoardList; - }, - setLinkJustify: function(centered) { - Header.linkJustifyToggler.checked = centered; - if (centered) { - return $.addClass(doc, 'centered-links'); - } else { - return $.rmClass(doc, 'centered-links'); - } - }, - toggleLinkJustify: function() { - var centered; - $.event('CloseMenu'); - centered = this.nodeName === 'INPUT' ? this.checked : void 0; - Header.setLinkJustify(centered); - return $.set('Centered links', centered); - }, - setBarFixed: function(fixed) { - Header.barFixedToggler.checked = fixed; - if (fixed) { - $.addClass(doc, 'fixed'); - return $.addClass(Header.bar, 'dialog'); - } else { - $.rmClass(doc, 'fixed'); - return $.rmClass(Header.bar, 'dialog'); - } - }, - toggleBarFixed: function() { - $.event('CloseMenu'); - Header.setBarFixed(this.checked); - Conf['Fixed Header'] = this.checked; - return $.set('Fixed Header', this.checked); - }, - setShortcutIcons: function(show) { - Header.shortcutToggler.checked = show; - if (show) { - return $.addClass(doc, 'shortcut-icons'); - } else { - return $.rmClass(doc, 'shortcut-icons'); - } - }, - toggleShortcutIcons: function() { - $.event('CloseMenu'); - Header.setShortcutIcons(this.checked); - Conf['Shortcut Icons'] = this.checked; - return $.set('Shortcut Icons', this.checked); - }, - setBarVisibility: function(hide) { - Header.headerToggler.checked = hide; - $.event('CloseMenu'); - (hide ? $.addClass : $.rmClass)(Header.bar, 'autohide'); - return (hide ? $.addClass : $.rmClass)(doc, 'autohide'); - }, - toggleBarVisibility: function() { - var hide, message; - hide = this.nodeName === 'INPUT' ? this.checked : !$.hasClass(Header.bar, 'autohide'); - this.checked = hide; - $.set('Header auto-hide', Conf['Header auto-hide'] = hide); - Header.setBarVisibility(hide); - message = "The header bar will " + (hide ? 'automatically hide itself.' : 'remain visible.'); - return new Notice('info', message, 2); - }, - setHideBarOnScroll: function(hide) { - Header.scrollHeaderToggler.checked = hide; - if (hide) { - $.on(window, 'scroll', Header.hideBarOnScroll); - return; - } - $.off(window, 'scroll', Header.hideBarOnScroll); - $.rmClass(Header.bar, 'scroll'); - if (!Conf['Header auto-hide']) { - return $.rmClass(Header.bar, 'autohide'); - } - }, - toggleHideBarOnScroll: function(e) { - var hide; - hide = this.checked; - $.cb.checked.call(this); - return Header.setHideBarOnScroll(hide); - }, - hideBarOnScroll: function() { - var offsetY; - offsetY = window.pageYOffset; - if (offsetY > (Header.previousOffset || 0)) { - $.addClass(Header.bar, 'autohide', 'scroll'); - } else { - $.rmClass(Header.bar, 'autohide', 'scroll'); - } - return Header.previousOffset = offsetY; - }, - setBarPosition: function(bottom) { - var args; - Header.barPositionToggler.checked = bottom; - $.event('CloseMenu'); - args = bottom ? ['bottom-header', 'top-header', 'bottom', 'after'] : ['top-header', 'bottom-header', 'top', 'add']; - $.addClass(doc, args[0]); - $.rmClass(doc, args[1]); - Header.bar.parentNode.className = args[2]; - return $[args[3]](Header.bar, Header.noticesRoot); - }, - toggleBarPosition: function() { - $.cb.checked.call(this); - return Header.setBarPosition(this.checked); - }, - setFooterVisibility: function(hide) { - Header.footerToggler.checked = hide; - return Header.footer.hidden = hide; - }, - toggleFooterVisibility: function() { - var hide, message; - $.event('CloseMenu'); - hide = this.nodeName === 'INPUT' ? this.checked : !!Header.footer.hidden; - Header.setFooterVisibility(hide); - $.set('Bottom Board List', hide); - message = hide ? 'The bottom navigation will now be hidden.' : 'The bottom navigation will remain visible.'; - return new Notice('info', message, 2); - }, - setCustomNav: function(show) { - var btn, cust, full, _ref; - Header.customNavToggler.checked = show; - cust = $('#custom-board-list', Header.bar); - full = $('#full-board-list', Header.bar); - btn = $('.hide-board-list-button', full); - return _ref = show ? [false, true] : [true, false], cust.hidden = _ref[0], full.hidden = _ref[1], _ref; - }, - toggleCustomNav: function() { - $.cb.checked.call(this); - return Header.setCustomNav(this.checked); - }, - editCustomNav: function() { - var settings; - Settings.open('Advanced'); - settings = $.id('fourchanx-settings'); - return $('input[name=boardnav]', settings).focus(); - }, - hashScroll: function() { - var hash, post; - hash = this.location.hash.slice(1); - if (!(/^p\d+$/.test(hash) && (post = $.id(hash)))) { - return; - } - if ((Get.postFromRoot(post)).isHidden) { - return; - } - return Header.scrollTo(post); - }, - scrollTo: function(root, down, needed) { - var height, x; - if (down) { - x = Header.getBottomOf(root); - if (Conf['Header auto-hide on scroll'] && Conf['Bottom header']) { - height = Header.bar.getBoundingClientRect().height; - if (x <= 0) { - if (!Header.isHidden()) { - x += height; - } - } else { - if (Header.isHidden()) { - x -= height; - } - } - } - if (!(needed && x >= 0)) { - return window.scrollBy(0, -x); - } - } else { - x = Header.getTopOf(root); - if (Conf['Header auto-hide on scroll'] && !Conf['Bottom header']) { - height = Header.bar.getBoundingClientRect().height; - if (x >= 0) { - if (!Header.isHidden()) { - x += height; - } - } else { - if (Header.isHidden()) { - x -= height; - } - } - } - if (!(needed && x >= 0)) { - return window.scrollBy(0, x); - } - } - }, - scrollToIfNeeded: function(root, down) { - return Header.scrollTo(root, down, true); - }, - getTopOf: function(root) { - var headRect, top; - top = root.getBoundingClientRect().top; - if (Conf['Fixed Header'] && !Conf['Bottom Header']) { - headRect = Header.toggle.getBoundingClientRect(); - top -= headRect.top + headRect.height; - } - return top; - }, - getBottomOf: function(root) { - var bottom, clientHeight, headRect; - clientHeight = doc.clientHeight; - bottom = clientHeight - root.getBoundingClientRect().bottom; - if (Conf['Bottom Header']) { - headRect = Header.toggle.getBoundingClientRect(); - bottom -= clientHeight - headRect.bottom + headRect.height; - } - return bottom; - }, - isHidden: function() { - var top; - top = Header.bar.getBoundingClientRect().top; - if (Conf['Bottom header']) { - return top === doc.clientHeight; - } else { - return top < 0; - } - }, - addShortcut: function(el) { - var shortcut; - shortcut = $.el('span', { - className: 'shortcut brackets-wrap' - }); - $.add(shortcut, el); - return $.prepend(Header.shortcuts, shortcut); - }, - rmShortcut: function(el) { - return $.rm(el.parentElement); - }, - menuToggle: function(e) { - return Header.menu.toggle(e, this, g); - }, - createNotification: function(e) { - var cb, content, lifetime, notice, type, _ref; - _ref = e.detail, type = _ref.type, content = _ref.content, lifetime = _ref.lifetime, cb = _ref.cb; - notice = new Notice(type, content, lifetime); - if (cb) { - return cb(notice); - } - }, - areNotificationsEnabled: false, - enableDesktopNotifications: function() { - var authorize, disable, el, notice, _ref; - if (!(window.Notification && Conf['Desktop Notifications'])) { - return; - } - switch (Notification.permission) { - case 'granted': - Header.areNotificationsEnabled = true; - return; - case 'denied': - return; - } - el = $.el('span', { - innerHTML: "Desktop notification permissions are not granted.\n[FAQ]
\n or " - }); - _ref = $$('button', el), authorize = _ref[0], disable = _ref[1]; - $.on(authorize, 'click', function() { - return Notification.requestPermission(function(status) { - Header.areNotificationsEnabled = status === 'granted'; - if (status === 'default') { - return; - } - return notice.close(); - }); - }); - $.on(disable, 'click', function() { - $.set('Desktop Notifications', false); - return notice.close(); - }); - return notice = new Notice('info', el); - } - }; - - Index = { - init: function() { - var anchorEntry, input, label, modeEntry, name, refNavEntry, repliesEntry, sortEntry, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2; - if (g.BOARD.ID === 'f' || g.VIEW === 'catalog' || !Conf['JSON Navigation']) { - return; - } - this.board = "" + g.BOARD; - this.button = $.el('a', { - className: 'index-refresh-shortcut fa fa-refresh', - title: 'Refresh', - href: 'javascript:;', - textContent: 'Refresh Index' - }); - $.on(this.button, 'click', this.update); - Header.addShortcut(this.button, 1); - modeEntry = { - el: $.el('span', { - textContent: 'Index mode' - }), - subEntries: [ - { - el: $.el('label', { - innerHTML: ' Paged' - }) - }, { - el: $.el('label', { - innerHTML: ' Infinite scrolling' - }) - }, { - el: $.el('label', { - innerHTML: ' All threads' - }) - } - ] - }; - _ref = modeEntry.subEntries; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - label = _ref[_i]; - input = label.el.firstChild; - input.checked = Conf['Index Mode'] === input.value; - $.on(input, 'change', $.cb.value); - $.on(input, 'change', this.cb.mode); - } - sortEntry = { - el: $.el('span', { - textContent: 'Sort by' - }), - subEntries: [ - { - el: $.el('label', { - innerHTML: ' Bump order' - }) - }, { - el: $.el('label', { - innerHTML: ' Last reply' - }) - }, { - el: $.el('label', { - innerHTML: ' Creation date' - }) - }, { - el: $.el('label', { - innerHTML: ' Reply count' - }) - }, { - el: $.el('label', { - innerHTML: ' File count' - }) - } - ] - }; - _ref1 = sortEntry.subEntries; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - label = _ref1[_j]; - input = label.el.firstChild; - input.checked = Conf['Index Sort'] === input.value; - $.on(input, 'change', $.cb.value); - $.on(input, 'change', this.cb.sort); - } - repliesEntry = { - el: $.el('label', { - innerHTML: ' Show replies' - }) - }; - anchorEntry = { - el: $.el('label', { - innerHTML: ' Anchor hidden threads', - title: 'Move hidden threads at the end of the index.' - }) - }; - refNavEntry = { - el: $.el('label', { - innerHTML: ' Refreshed navigation', - title: 'Refresh index when navigating through pages.' - }) - }; - _ref2 = [repliesEntry, anchorEntry, refNavEntry]; - for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { - label = _ref2[_k]; - input = label.el.firstChild; - name = input.name; - input.checked = Conf[name]; - $.on(input, 'change', $.cb.checked); - switch (name) { - case 'Show Replies': - $.on(input, 'change', this.cb.replies); - break; - case 'Anchor Hidden Threads': - $.on(input, 'change', this.cb.sort); - } - } - $.event('AddMenuEntry', { - type: 'header', - el: $.el('span', { - textContent: 'Index Navigation' - }), - order: 98, - subEntries: [repliesEntry, anchorEntry, refNavEntry, modeEntry, sortEntry] - }); - $.addClass(doc, 'index-loading'); - this.root = $.el('div', { - className: 'board' - }); - this.pagelist = $.el('div', { - className: 'pagelist', - hidden: true, - innerHTML: "
" - }); - this.navLinks = $.el('div', { - className: 'navLinks', - innerHTML: "Return Catalog Bottom ×" - }); - this.searchInput = $('#index-search', this.navLinks); - this.currentPage = this.getCurrentPage(); - $.on(d, 'scroll', Index.scroll); - $.on(this.pagelist, 'click', this.cb.pageNav); - $.on(this.searchInput, 'input', this.onSearchInput); - $.on($('#index-search-clear', this.navLinks), 'click', this.clearSearch); - $.on($('#returnlink a', this.navLinks), 'click', Navigate.navigate); - $.on($('#cataloglink a', this.navLinks), 'click', function() { - return window.location = "//boards.4chan.org/" + g.BOARD + "/catalog"; - }); - if (g.VIEW === 'index') { - this.update(); - } - $.asap((function() { - return $('.board', doc) || d.readyState !== 'loading'; - }), function() { - var board, navLink, _l, _len3, _ref3; - if (g.VIEW === 'index') { - board = $('.board'); - $.replace(board, Index.root); - d.implementation.createDocument(null, null, null).appendChild(board); - } - _ref3 = $$('.navLinks'); - for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) { - navLink = _ref3[_l]; - $.rm(navLink); - } - $.after($.x('child::form/preceding-sibling::hr[1]'), Index.navLinks); - return $.rmClass(doc, 'index-loading'); - }); - return $.asap((function() { - return $('.pagelist', doc) || d.readyState !== 'loading'; - }), function() { - var pagelist; - if (pagelist = $('.pagelist')) { - return $.replace(pagelist, Index.pagelist); - } else { - return $.after($.id('delform'), Index.pagelist); - } - }); - }, - scroll: $.debounce(100, function() { - var nodes, pageNum; - if (Index.req || Conf['Index Mode'] !== 'infinite' || (doc.scrollTop <= doc.scrollHeight - (300 + window.innerHeight)) || g.VIEW === 'thread') { - return; - } - if (Index.pageNum == null) { - Index.pageNum = Index.getCurrentPage(); - } - pageNum = Index.pageNum++; - if (pageNum >= Index.pagesNum) { - return Index.endNotice(); - } - nodes = Index.buildSinglePage(pageNum); - if (Conf['Show Replies']) { - Index.buildReplies(nodes); - } - Index.buildStructure(nodes); - return Index.setPage(pageNum); - }), - endNotice: (function() { - var notify, reset; - notify = false; - reset = function() { - return notify = false; - }; - return function() { - if (notify) { - return; - } - notify = true; - new Notice('info', "Last page reached.", 2); - return setTimeout(reset, 3 * $.SECOND); - }; - })(), - cb: { - mode: function() { - Index.togglePagelist(); - return Index.buildIndex(); - }, - sort: function() { - Index.sort(); - return Index.buildIndex(); - }, - replies: function() { - Index.buildThreads(); - Index.sort(); - return Index.buildIndex(); - }, - pageNav: function(e) { - var a; - if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) { - return; - } - switch (e.target.nodeName) { - case 'BUTTON': - a = e.target.parentNode; - break; - case 'A': - a = e.target; - break; - default: - return; - } - if (a.textContent === 'Catalog') { - return; - } - e.preventDefault(); - return Index.userPageNav(+a.pathname.split('/')[2]); - } - }, - scrollToIndex: function() { - return Header.scrollToIfNeeded(Index.root); - }, - getCurrentPage: function() { - return +window.location.pathname.split('/')[2]; - }, - userPageNav: function(pageNum) { - if (Conf['Refreshed Navigation'] && Conf['Index Mode'] !== 'all pages') { - return Index.update(pageNum); - } else { - return Index.pageNav(pageNum); - } - }, - pageNav: function(pageNum) { - if (Index.currentPage === pageNum) { - return; - } - history.pushState(null, '', pageNum === 0 ? './' : pageNum); - return Index.pageLoad(pageNum); - }, - pageLoad: function(pageNum) { - Index.currentPage = pageNum; - if (Conf['Index Mode'] === 'all pages') { - return; - } - Index.buildIndex(); - Index.setPage(); - return Index.scrollToIndex(); - }, - getPagesNum: function() { - if (Index.isSearching) { - return Math.ceil((Index.sortedNodes.length / 2) / Index.threadsNumPerPage); - } else { - return Index.pagesNum; - } - }, - getMaxPageNum: function() { - return Math.max(0, Index.getPagesNum() - 1); - }, - togglePagelist: function() { - return Index.pagelist.hidden = Conf['Index Mode'] !== 'paged'; - }, - buildPagelist: function() { - var a, i, maxPageNum, nodes, pagesRoot, _i; - pagesRoot = $('.pages', Index.pagelist); - maxPageNum = Index.getMaxPageNum(); - if (pagesRoot.childElementCount !== maxPageNum + 1) { - nodes = []; - for (i = _i = 0; _i <= maxPageNum; i = _i += 1) { - a = $.el('a', { - textContent: i, - href: i ? i : './' - }); - nodes.push($.tn('['), a, $.tn('] ')); - } - $.rmAll(pagesRoot); - $.add(pagesRoot, nodes); - } - return Index.togglePagelist(); - }, - setPage: function(pageNum) { - var a, href, maxPageNum, next, pagesRoot, prev, strong; - pageNum || (pageNum = Index.getCurrentPage()); - maxPageNum = Index.getMaxPageNum(); - pagesRoot = $('.pages', Index.pagelist); - prev = pagesRoot.previousSibling.firstChild; - next = pagesRoot.nextSibling.firstChild; - href = Math.max(pageNum - 1, 0); - prev.href = href === 0 ? './' : href; - prev.firstChild.disabled = href === pageNum; - href = Math.min(pageNum + 1, maxPageNum); - next.href = href === 0 ? './' : href; - next.firstChild.disabled = href === pageNum; - if (strong = $('strong', pagesRoot)) { - if (+strong.textContent === pageNum) { - return; - } - $.replace(strong, strong.firstChild); - } else { - strong = $.el('strong'); - } - a = pagesRoot.children[pageNum]; - $.before(a, strong); - return $.add(strong, a); - }, - update: function(pageNum) { - var now, onload, _ref, _ref1; - if (!navigator.onLine) { - return; - } - if (g.VIEW === 'thread') { - if (Conf['Thread Updater']) { - return ThreadUpdater.update(); - } - return; - } - if (!(d.readyState === 'loading' || Index.root.parentElement)) { - $.replace($('.board'), Index.root); - } - delete Index.pageNum; - if ((_ref = Index.req) != null) { - _ref.abort(); - } - if ((_ref1 = Index.notice) != null) { - _ref1.close(); - } - now = Date.now(); - $.ready(function() { - return Index.nTimeout = setTimeout((function() { - if (Index.req && !Index.notice) { - return Index.notice = new Notice('info', 'Refreshing index...', 2); - } - }), 3 * $.SECOND - (Date.now() - now)); - }); - if (typeof pageNum !== 'number') { - pageNum = null; - } - onload = function(e) { - return Index.load(e, pageNum); - }; - Index.req = $.ajax("//a.4cdn.org/" + g.BOARD + "/catalog.json", { - onabort: onload, - onloadend: onload - }, { - whenModified: Index.board === ("" + g.BOARD) - }); - return $.addClass(Index.button, 'fa-spin'); - }, - load: function(e, pageNum) { - var err, nTimeout, notice, req, timeEl, _ref; - $.rmClass(Index.button, 'fa-spin'); - req = Index.req, notice = Index.notice, nTimeout = Index.nTimeout; - if (nTimeout) { - clearTimeout(nTimeout); - } - delete Index.nTimeout; - delete Index.req; - delete Index.notice; - if (e.type === 'abort') { - req.onloadend = null; - notice.close(); - return; - } - if ((_ref = req.status) !== 200 && _ref !== 304) { - err = "Index refresh failed. Error " + req.statusText + " (" + req.status + ")"; - if (notice) { - notice.setType('warning'); - notice.el.lastElementChild.textContent = err; - setTimeout(notice.close, $.SECOND); - } else { - new Notice('warning', err, 1); - } - return; - } - Navigate.title(); - Index.board = "" + g.BOARD; - try { - if (req.status === 200) { - Index.parse(req.response, pageNum); - } else if (req.status === 304 && (pageNum != null)) { - Index.pageNav(pageNum); - } - } catch (_error) { - err = _error; - c.error("Index failure: " + err.message, err.stack); - if (notice) { - notice.setType('error'); - notice.el.lastElementChild.textContent = 'Index refresh failed.'; - setTimeout(notice.close, $.SECOND); - } else { - new Notice('error', 'Index refresh failed.', 1); - } - return; - } - timeEl = $('#index-last-refresh time', Index.navLinks); - timeEl.dataset.utc = Date.parse(req.getResponseHeader('Last-Modified')); - RelativeDates.update(timeEl); - return Index.scrollToIndex(); - }, - parse: function(pages, pageNum) { - Index.parseThreadList(pages); - Index.buildThreads(); - Index.sort(); - Index.buildPagelist(); - if (pageNum != null) { - Index.pageNav(pageNum); - return; - } - Index.buildIndex(); - return Index.setPage(); - }, - parseThreadList: function(pages) { - Index.pagesNum = pages.length; - Index.threadsNumPerPage = pages[0].threads.length; - Index.liveThreadData = pages.reduce((function(arr, next) { - return arr.concat(next.threads); - }), []); - Index.liveThreadIDs = Index.liveThreadData.map(function(data) { - return data.no; - }); - g.BOARD.threads.forEach(function(thread) { - var _ref; - if (_ref = thread.ID, __indexOf.call(Index.liveThreadIDs, _ref) < 0) { - return thread.collect(); - } - }); - }, - buildThreads: function() { - var err, errors, i, posts, thread, threadData, threadRoot, threads, _i, _len, _ref; - Index.nodes = []; - threads = []; - posts = []; - _ref = Index.liveThreadData; - for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { - threadData = _ref[i]; - try { - threadRoot = Build.thread(g.BOARD, threadData); - if (thread = g.BOARD.threads[threadData.no]) { - thread.setPage(Math.floor(i / Index.threadsNumPerPage)); - thread.setStatus('Sticky', !!threadData.sticky); - thread.setStatus('Closed', !!threadData.closed); - } else { - thread = new Thread(threadData.no, g.BOARD); - threads.push(thread); - } - Index.nodes.push(threadRoot); - if (thread.ID in thread.posts) { - continue; - } - posts.push(new Post($('.opContainer', threadRoot), thread, g.BOARD)); - } catch (_error) { - err = _error; - if (!errors) { - errors = []; - } - errors.push({ - message: "Parsing of Thread No." + thread + " failed. Thread will be skipped.", - error: err - }); - } - } - if (errors) { - Main.handleErrors(errors); - } - $.nodes(Index.nodes); - Main.callbackNodes(Thread, threads); - Main.callbackNodes(Post, posts); - return $.event('IndexRefresh'); - }, - buildReplies: function(threadRoots) { - var data, err, errors, i, lastReplies, node, nodes, post, posts, thread, threadRoot, _i, _j, _len, _len1; - posts = []; - for (_i = 0, _len = threadRoots.length; _i < _len; _i++) { - threadRoot = threadRoots[_i]; - thread = Get.threadFromRoot(threadRoot); - i = Index.liveThreadIDs.indexOf(thread.ID); - if (!(lastReplies = Index.liveThreadData[i].last_replies)) { - continue; - } - nodes = []; - for (_j = 0, _len1 = lastReplies.length; _j < _len1; _j++) { - data = lastReplies[_j]; - if (post = thread.posts[data.no]) { - nodes.push(post.nodes.root); - continue; - } - nodes.push(node = Build.postFromObject(data, thread.board.ID)); - try { - posts.push(new Post(node, thread, thread.board)); - } catch (_error) { - err = _error; - if (!errors) { - errors = []; - } - errors.push({ - message: "Parsing of Post No." + data.no + " failed. Post will be skipped.", - error: err - }); - } - } - $.add(threadRoot, nodes); - } - if (errors) { - Main.handleErrors(errors); - } - return Main.callbackNodes(Post, posts); - }, - sort: function() { - var cnd, fn, i, item, items, liveThreadData, liveThreadIDs, nodes, sortedNodes, sortedThreadIDs, threadID, _i, _len; - liveThreadIDs = Index.liveThreadIDs, liveThreadData = Index.liveThreadData; - sortedThreadIDs = { - lastreply: __slice.call(liveThreadData).sort(function(a, b) { - var num; - if ((num = a.last_replies)) { - a = num[num.length - 1]; - } - if ((num = b.last_replies)) { - b = num[num.length - 1]; - } - return b.no - a.no; - }).map(function(post) { - return post.no; - }), - bump: liveThreadIDs, - birth: __slice.call(liveThreadIDs).sort(function(a, b) { - return b - a; - }), - replycount: __slice.call(liveThreadData).sort(function(a, b) { - return b.replies - a.replies; - }).map(function(post) { - return post.no; - }), - filecount: __slice.call(liveThreadData).sort(function(a, b) { - return b.images - a.images; - }).map(function(post) { - return post.no; - }) - }[Conf['Index Sort']]; - Index.sortedNodes = sortedNodes = new RandomAccessList; - nodes = Index.nodes; - for (_i = 0, _len = sortedThreadIDs.length; _i < _len; _i++) { - threadID = sortedThreadIDs[_i]; - sortedNodes.push(nodes[Index.liveThreadIDs.indexOf(threadID)]); - } - if (Index.isSearching && (nodes = Index.querySearch(Index.searchInput.value))) { - Index.sortedNodes = new RandomAccessList(nodes); - } - items = [ - { - fn: function(thread) { - return thread.isSticky; - }, - cnd: true - }, { - fn: function(thread) { - return thread.isOnTop; - }, - cnd: Conf['Filter'] - }, { - fn: function(thread) { - return !thread.isHidden; - }, - cnd: Conf['Anchor Hidden Threads'] - } - ]; - i = 0; - while (item = items[i++]) { - fn = item.fn, cnd = item.cnd; - if (cnd) { - Index.sortOnTop(fn); - } - } - }, - sortOnTop: function(match) { - var j, offset, sortedNodes, target, threadRoot; - offset = 0; - sortedNodes = Index.sortedNodes; - threadRoot = sortedNodes.first; - while (threadRoot) { - if (match(Get.threadFromRoot(threadRoot.data))) { - target = sortedNodes.first; - j = 0; - while (j++ < offset) { - target = target.next; - } - if (threadRoot !== target) { - offset++; - sortedNodes.before(target, threadRoot); - } - } - threadRoot = threadRoot.next; - } - }, - buildIndex: function() { - var nodes, target; - if (Conf['Index Mode'] !== 'all pages') { - nodes = Index.buildSinglePage(Index.getCurrentPage()); - } else { - nodes = [(target = Index.sortedNodes.first).data]; - while (target = target.next) { - nodes.push(target.data); - } - } - $.rmAll(Index.root); - $.rmAll(Header.hover); - if (Conf['Show Replies']) { - Index.buildReplies(nodes); - } - return Index.buildStructure(nodes); - }, - buildSinglePage: function(pageNum) { - var end, nodes, nodesPerPage, offset, target; - nodes = []; - nodesPerPage = Index.threadsNumPerPage; - offset = nodesPerPage * pageNum; - end = offset + nodesPerPage; - target = Index.sortedNodes.order()[offset]; - Index.sortedNodes; - while ((offset++ <= end) && target) { - nodes.push(target.data); - target = target.next; - } - return nodes; - }, - buildStructure: function(nodes) { - var hr, i, node, result, _i, _len, _ref; - result = $.frag(); - i = 0; - while (node = nodes[i++]) { - $.add(result, [node, $.el('hr')]); - } - $.add(Index.root, result); - _ref = $$('hr + hr', Index.root); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - hr = _ref[_i]; - $.rm(hr); - } - return $.event('IndexBuild', result); - }, - isSearching: false, - clearSearch: function() { - Index.searchInput.value = null; - Index.onSearchInput(); - return Index.searchInput.focus(); - }, - onSearchInput: function() { - var pageNum; - if (Index.isSearching = !!Index.searchInput.value.trim()) { - if (!Index.searchInput.dataset.searching) { - Index.searchInput.dataset.searching = 1; - Index.pageBeforeSearch = Index.getCurrentPage(); - pageNum = 0; - } else { - pageNum = Index.getCurrentPage(); - } - } else { - if (!Index.searchInput.dataset.searching) { - return; - } - pageNum = Index.pageBeforeSearch; - delete Index.pageBeforeSearch; - Index.searchInput.removeAttribute('data-searching'); - } - Index.sort(); - if (Conf['Index Mode'] !== 'all pages') { - pageNum = Math.min(pageNum, Index.getMaxPageNum()); - } - Index.buildPagelist(); - if (Index.currentPage === pageNum) { - Index.buildIndex(); - return Index.setPage(); - } else { - return Index.pageNav(pageNum); - } - }, - querySearch: function(query) { - var keywords; - if (!(keywords = query.toLowerCase().match(/\S+/g))) { - return; - } - return Index.search(keywords); - }, - search: function(keywords) { - var data, found, target; - found = []; - target = Index.sortedNodes.first; - while (target) { - data = target.data; - if (Index.searchMatch(Get.threadFromRoot(data), keywords)) { - found.push(data); - } - target = target.next; - } - return found; - }, - searchMatch: function(thread, keywords) { - var file, info, key, keyword, text, _i, _j, _len, _len1, _ref, _ref1; - _ref = thread.OP, info = _ref.info, file = _ref.file; - text = []; - _ref1 = ['comment', 'subject', 'name', 'tripcode', 'email']; - for (_i = 0, _len = _ref1.length; _i < _len; _i++) { - key = _ref1[_i]; - if (key in info) { - text.push(info[key]); - } - } - if (file) { - text.push(file.name); - } - text = text.join(' ').toLowerCase(); - for (_j = 0, _len1 = keywords.length; _j < _len1; _j++) { - keyword = keywords[_j]; - if (-1 === text.indexOf(keyword)) { - return false; - } - } - return true; - } - }; - - Build = { - staticPath: '//s.4cdn.org/image/', - gifIcon: window.devicePixelRatio >= 2 ? '@2x.gif' : '.gif', - spoilerRange: {}, - shortFilename: function(filename, isReply) { - var threshold; - threshold = isReply ? 30 : 40; - if (filename.length - 4 > threshold) { - return "" + filename.slice(0, threshold - 5) + "(...)." + filename.slice(-3); - } else { - return filename; - } - }, - thumbRotate: (function() { - var n; - n = 0; - return function() { - return n = (n + 1) % 3; - }; - })(), - postFromObject: function(data, boardID) { - var o; - o = { - postID: data.no, - threadID: data.resto || data.no, - boardID: boardID, - name: data.name, - capcode: data.capcode, - tripcode: data.trip, - uniqueID: data.id, - email: data.email ? encodeURI(data.email.replace(/"/g, '"')) : '', - subject: data.sub, - flagCode: data.country, - flagName: data.country_name, - date: data.now, - dateUTC: data.time, - comment: data.com, - isSticky: !!data.sticky, - isClosed: !!data.closed - }; - if (data.ext || data.filedeleted) { - o.file = { - name: data.filename + data.ext, - timestamp: "" + data.tim + data.ext, - url: boardID === 'f' ? "//i.4cdn.org/" + boardID + "/src/" + data.filename + data.ext : "//i.4cdn.org/" + boardID + "/src/" + data.tim + data.ext, - height: data.h, - width: data.w, - MD5: data.md5, - size: data.fsize, - turl: "//" + (Build.thumbRotate()) + ".t.4cdn.org/" + boardID + "/thumb/" + data.tim + "s.jpg", - theight: data.tn_h, - twidth: data.tn_w, - isSpoiler: !!data.spoiler, - isDeleted: !!data.filedeleted - }; - } - return Build.post(o); - }, - post: function(o, isArchived) { - /* - This function contains code from 4chan-JS (https://github.com/4chan/4chan-JS). - @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE - */ - - var a, boardID, capcode, capcodeClass, capcodeIcon, capcodeStart, closed, comment, container, date, dateUTC, email, emailEnd, emailStart, file, fileDims, fileHTML, fileInfo, fileSize, fileThumb, filename, flag, flagCode, flagName, gifIcon, href, imgSrc, isClosed, isOP, isSticky, name, pageIcon, pageNum, postID, quote, replyLink, shortFilename, spoilerRange, staticPath, sticky, subject, threadID, tripcode, uniqueID, userID, _i, _len, _ref; - postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, comment = o.comment, file = o.file; - isOP = postID === threadID; - staticPath = Build.staticPath, gifIcon = Build.gifIcon; - tripcode = tripcode ? " " + tripcode + "" : ''; - if (email) { - emailStart = ''; - emailEnd = ''; - } else { - emailStart = ''; - emailEnd = ''; - } - switch (capcode) { - case 'admin': - case 'admin_highlight': - capcodeClass = " capcodeAdmin"; - capcodeStart = " ## Admin"; - capcodeIcon = (" "; - break; - case 'mod': - capcodeClass = " capcodeMod"; - capcodeStart = " ## Mod"; - capcodeIcon = (" "; - break; - case 'developer': - capcodeClass = " capcodeDeveloper"; - capcodeStart = " ## Developer"; - capcodeIcon = (" "; - break; - default: - capcodeClass = ''; - capcodeStart = ''; - capcodeIcon = ''; - } - userID = !capcode && uniqueID ? (" (ID: ") + ("" + uniqueID + ") ") : ''; - flag = !flagCode ? '' : boardID === 'pol' ? "  + flagCode + " : " "; - if (file != null ? file.isDeleted : void 0) { - fileHTML = isOP ? ("
") + ("File deleted.") + "
" : ("
") + ("File deleted.") + "
"; - } else if (file) { - fileSize = $.bytesToString(file.size); - fileThumb = file.turl; - if (file.isSpoiler) { - fileSize = "Spoiler Image, " + fileSize; - if (!isArchived) { - fileThumb = "" + staticPath + "spoiler"; - if (spoilerRange = Build.spoilerRange[boardID]) { - fileThumb += ("-" + boardID) + Math.floor(1 + spoilerRange * Math.random()); - } - fileThumb += '.png'; - file.twidth = file.theight = 100; - } - } - imgSrc = boardID === 'f' ? '' : ("") + ("" + fileSize + "") + ""; - a = $.el('a', { - innerHTML: file.name - }); - filename = a.textContent.replace(/%22/g, '"'); - a.textContent = Build.shortFilename(filename); - shortFilename = a.innerHTML; - a.textContent = filename; - filename = a.innerHTML.replace(/'/g, '''); - fileDims = file.name.slice(-3) === 'pdf' ? 'PDF' : "" + file.width + "x" + file.height; - fileInfo = ("
File: " + file.timestamp + "") + ("-(" + fileSize + ", " + fileDims + (file.isSpoiler ? '' : ", " + shortFilename + "")) + ")
"; - fileHTML = "
" + fileInfo + imgSrc + "
"; - } else { - fileHTML = ''; - } - sticky = isSticky ? " Sticky" : ''; - closed = isClosed ? " Closed" : ''; - if (isOP && g.VIEW === 'index') { - pageNum = Math.floor(Index.liveThreadIDs.indexOf(postID) / Index.threadsNumPerPage); - pageIcon = " [" + pageNum + "]"; - replyLink = "   [Reply]"; - } else { - pageIcon = replyLink = ''; - } - container = $.el('div', { - id: "pc" + postID, - className: "postContainer " + (isOP ? 'op' : 'reply') + "Container", - innerHTML: "" + (isOP ? '' : "
>>
") + "
" + (isOP ? fileHTML : '') + "" + (isOP ? '' : fileHTML) + "
" + (comment || '') + "
" + ' ' + "
" - }); - _ref = $$('.quotelink', container); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - quote = _ref[_i]; - href = quote.getAttribute('href'); - if (href[0] === '/') { - continue; - } - quote.href = "/" + boardID + "/res/" + href; - } - return container; - }, - summary: function(boardID, threadID, posts, files) { - var text; - text = []; - text.push("" + posts + " post" + (posts > 1 ? 's' : '')); - if (files) { - text.push("and " + files + " image repl" + (files > 1 ? 'ies' : 'y')); - } - text.push('omitted.'); - return $.el('a', { - className: 'summary', - textContent: text.join(' '), - href: "/" + boardID + "/res/" + threadID - }); - }, - thread: function(board, data, full) { - var OP, root; - Build.spoilerRange[board] = data.custom_spoiler; - if ((OP = board.posts[data.no]) && (root = OP.nodes.root.parentNode)) { - $.rmAll(root); - } else { - root = $.el('div', { - className: 'thread', - id: "t" + data.no - }); - } - $.add(root, Build[full ? 'fullThread' : 'excerptThread'](board, data, OP)); - return root; - }, - excerptThread: function(board, data, OP) { - var files, nodes, posts, _ref; - nodes = [OP ? OP.nodes.root : Build.postFromObject(data, board.ID)]; - if (data.omitted_posts || !Conf['Show Replies'] && data.replies) { - _ref = Conf['Show Replies'] ? [data.omitted_posts, data.omitted_images] : [ - data.replies, data.omitted_images + data.last_replies.filter(function(data) { - return !!data.ext; - }).length - ], posts = _ref[0], files = _ref[1]; - nodes.push(Build.summary(board.ID, data.no, posts, files)); - } - return nodes; - }, - fullThread: function(board, data) { - return Build.postFromObject(data, board.ID); - } - }; - - Get = { - threadExcerpt: function(thread) { - var OP, excerpt, _ref; - OP = thread.OP; - excerpt = ("/" + thread.board + "/ - ") + (((_ref = OP.info.subject) != null ? _ref.trim() : void 0) || OP.info.comment.replace(/\n+/g, ' // ') || Conf['Anonymize'] && 'Anonymous' || $('.nameBlock', OP.nodes.info).textContent.trim()); - if (excerpt.length > 73) { - return "" + excerpt.slice(0, 70) + "..."; - } - return excerpt; - }, - threadFromRoot: function(root) { - return g.threads["" + g.BOARD + "." + root.id.slice(1)]; - }, - threadFromNode: function(node) { - return Get.threadFromRoot($.x('ancestor::div[@class="thread"]', node)); - }, - postFromRoot: function(root) { - var boardID, index, link, post, postID; - link = $('a[title="Highlight this post"]', root); - boardID = link.pathname.split('/')[1]; - postID = link.hash.slice(2); - index = root.dataset.clone; - post = g.posts["" + boardID + "." + postID]; - if (index) { - return post.clones[index]; - } else { - return post; - } - }, - postFromNode: function(root) { - return Get.postFromRoot($.x('(ancestor::div[contains(@class,"postContainer")][1]|following::div[contains(@class,"postContainer")][1])', root)); - }, - contextFromNode: function(node) { - return Get.postFromRoot($.x('ancestor::div[parent::div[@class="thread"]][1]', node)); - }, - postDataFromLink: function(link) { - var boardID, path, postID, threadID, _ref; - if (link.hostname === 'boards.4chan.org') { - path = link.pathname.split('/'); - boardID = path[1]; - threadID = path[3]; - postID = link.hash.slice(2); - } else { - _ref = link.dataset, boardID = _ref.boardID, threadID = _ref.threadID, postID = _ref.postID; - threadID || (threadID = 0); - } - return { - boardID: boardID, - threadID: +threadID, - postID: +postID - }; - }, - allQuotelinksLinkingTo: function(post) { - var fullID, handleQuotes, posts, qPost, quote, quotelinks, _i, _len, _ref; - quotelinks = []; - posts = g.posts; - fullID = { - post: post - }; - handleQuotes = function(qPost, type) { - var clone, _i, _len, _ref; - quotelinks.push.apply(quotelinks, qPost.nodes[type]); - _ref = qPost.clones; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - clone = _ref[_i]; - quotelinks.push.apply(quotelinks, clone.nodes[type]); - } - }; - posts.forEach(function(qPost) { - if (__indexOf.call(qPost.quotes, fullID) >= 0) { - return handleQuotes(qPost, 'quotelinks'); - } - }); - if (Conf['Quote Backlinks']) { - _ref = post.quotes; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - quote = _ref[_i]; - if (qPost = posts[quote]) { - handleQuotes(qPost, 'backlinks'); - } - } - } - return quotelinks.filter(function(quotelink) { - var boardID, postID, _ref1; - _ref1 = Get.postDataFromLink(quotelink), boardID = _ref1.boardID, postID = _ref1.postID; - return boardID === post.board.ID && postID === post.ID; - }); - }, - postClone: function(boardID, threadID, postID, root, context) { - var post, url; - if (post = g.posts["" + boardID + "." + postID]) { - Get.insert(post, root, context); - return; - } - root.textContent = "Loading post No." + postID + "..."; - if (threadID) { - return $.cache("//a.4cdn.org/" + boardID + "/res/" + threadID + ".json", function() { - return Get.fetchedPost(this, boardID, threadID, postID, root, context); - }); - } else if (url = Redirect.to('post', { - boardID: boardID, - postID: postID - })) { - return $.cache(url, function() { - return Get.archivedPost(this, boardID, postID, root, context); - }, { - responseType: 'json', - withCredentials: url.archive.withCredentials - }); - } - }, - insert: function(post, root, context) { - var clone, nodes; - if (!root.parentNode) { - return; - } - clone = post.addClone(context); - Main.callbackNodes(Clone, [clone]); - nodes = clone.nodes; - $.rmAll(nodes.root); - $.add(nodes.root, nodes.post); - $.rmAll(root); - return $.add(root, nodes.root); - }, - fetchedPost: function(req, boardID, threadID, postID, root, context) { - var board, post, posts, status, thread, url, _i, _len; - if (post = g.posts["" + boardID + "." + postID]) { - Get.insert(post, root, context); - return; - } - status = req.status; - if (status !== 200 && status !== 304) { - if (url = Redirect.to('post', { - boardID: boardID, - postID: postID - })) { - $.cache(url, function() { - return Get.archivedPost(this, boardID, postID, root, context); - }, { - withCredentials: url.archive.withCredentials - }); - } else { - $.addClass(root, 'warning'); - root.textContent = status === 404 ? "Thread No." + threadID + " 404'd." : "Error " + req.statusText + " (" + req.status + ")."; - } - return; - } - posts = req.response.posts; - Build.spoilerRange[boardID] = posts[0].custom_spoiler; - for (_i = 0, _len = posts.length; _i < _len; _i++) { - post = posts[_i]; - if (post.no === postID) { - break; - } - } - if (post.no !== postID) { - if (url = Redirect.to('post', { - boardID: boardID, - postID: postID - })) { - $.cache(url, function() { - return Get.archivedPost(this, boardID, postID, root, context); - }, { - withCredentials: url.archive.withCredentials - }); - } else { - $.addClass(root, 'warning'); - root.textContent = "Post No." + postID + " was not found."; - } - return; - } - board = g.boards[boardID] || new Board(boardID); - thread = g.threads["" + boardID + "." + threadID] || new Thread(threadID, board); - post = new Post(Build.postFromObject(post, boardID), thread, board); - Main.callbackNodes(Post, [post]); - return Get.insert(post, root, context); - }, - archivedPost: function(req, boardID, postID, root, context) { - var board, bq, comment, data, o, post, thread, threadID, _ref; - if (post = g.posts["" + boardID + "." + postID]) { - Get.insert(post, root, context); - return; - } - data = req.response; - if (data.error) { - $.addClass(root, 'warning'); - root.textContent = data.error; - return; - } - bq = $.el('blockquote', { - textContent: data.comment - }); - bq.innerHTML = bq.innerHTML.replace(/\n|\[\/?[a-z]+(:lit)?\]/g, Get.parseMarkup); - comment = bq.innerHTML.replace(/(^|>)(>[^<$]*)(<|$)/g, '$1$2$3').replace(/((>){2}(>\/[a-z\d]+\/)?\d+)/g, '$1'); - threadID = +data.thread_num; - o = { - postID: postID, - threadID: threadID, - boardID: boardID, - name: data.name_processed, - capcode: (function() { - switch (data.capcode) { - case 'M': - return 'mod'; - case 'A': - return 'admin'; - case 'D': - return 'developer'; - } - })(), - tripcode: data.trip, - uniqueID: data.poster_hash, - email: data.email ? encodeURI(data.email) : '', - subject: data.title_processed, - flagCode: data.poster_country, - flagName: data.poster_country_name_processed, - date: data.fourchan_date, - dateUTC: data.timestamp, - comment: comment - }; - if ((_ref = data.media) != null ? _ref.media_filename : void 0) { - o.file = { - name: data.media.media_filename_processed, - timestamp: data.media.media_orig, - url: data.media.media_link || data.media.remote_media_link, - height: data.media.media_h, - width: data.media.media_w, - MD5: data.media.media_hash, - size: data.media.media_size, - turl: data.media.thumb_link || ("//t.4cdn.org/" + boardID + "/thumb/" + data.media.preview_orig), - theight: data.media.preview_h, - twidth: data.media.preview_w, - isSpoiler: data.media.spoiler === '1' - }; - } - board = g.boards[boardID] || new Board(boardID); - thread = g.threads["" + boardID + "." + threadID] || new Thread(threadID, board); - post = new Post(Build.post(o, true), thread, board, { - isArchived: true - }); - Main.callbackNodes(Post, [post]); - return Get.insert(post, root, context); - }, - parseMarkup: function(text) { - return { - '\n': '
', - '[b]': '', - '[/b]': '', - '[spoiler]': '', - '[/spoiler]': '', - '[code]': '
',
-        '[/code]': '
', - '[moot]': '
', - '[/moot]': '
', - '[banned]': '', - '[/banned]': '' - }[text] || text.replace(':lit', ''); - } - }; - - UI = (function() { - var Menu, dialog, drag, dragend, dragstart, hover, hoverend, hoverstart, touchend, touchmove; - dialog = function(id, position, html) { - var child, el, move, _i, _len, _ref; - el = $.el('div', { - className: 'dialog', - innerHTML: html, - id: id - }); - el.style.cssText = position; - $.get("" + id + ".position", position, function(item) { - return el.style.cssText = item["" + id + ".position"]; - }); - move = $('.move', el); - $.on(move, 'touchstart mousedown', dragstart); - _ref = move.children; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - child = _ref[_i]; - if (!child.tagName) { - continue; - } - $.on(child, 'touchstart mousedown', function(e) { - return e.stopPropagation(); - }); - } - return el; - }; - Menu = (function() { - var currentMenu, lastToggledButton; - - currentMenu = null; - - lastToggledButton = null; - - function Menu(type) { - this.type = type; - this.rmEntry = __bind(this.rmEntry, this); - this.addEntry = __bind(this.addEntry, this); - this.onFocus = __bind(this.onFocus, this); - this.keybinds = __bind(this.keybinds, this); - this.close = __bind(this.close, this); - $.on(d, 'AddMenuEntry', this.addEntry); - $.on(d, 'rmMenuEntry', this.rmEntry); - this.entries = []; - } - - Menu.prototype.makeMenu = function() { - var menu; - menu = $.el('div', { - className: 'dialog', - id: 'menu', - tabIndex: 0 - }); - $.on(menu, 'click', function(e) { - return e.stopPropagation(); - }); - $.on(menu, 'keydown', this.keybinds); - return menu; - }; - - Menu.prototype.toggle = function(e, button, data) { - var previousButton; - e.preventDefault(); - e.stopPropagation(); - if (currentMenu) { - previousButton = lastToggledButton; - this.close(); - if (previousButton === button) { - return; - } - } - if (!this.entries.length) { - return; - } - return this.open(button, data); - }; - - Menu.prototype.open = function(button, data) { - var bLeft, bRect, bTop, bottom, cHeight, cWidth, entry, left, mRect, menu, right, style, top, _i, _len, _ref, _ref1, _ref2; - menu = this.makeMenu(); - currentMenu = menu; - lastToggledButton = button; - this.entries.sort(function(first, second) { - return first.order - second.order; - }); - _ref = this.entries; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - entry = _ref[_i]; - this.insertEntry(entry, menu, data); - } - $.addClass(lastToggledButton, 'active'); - $.on(d, 'click', this.close); - $.on(d, 'CloseMenu', this.close); - $.add(Header.hover, menu); - mRect = menu.getBoundingClientRect(); - bRect = button.getBoundingClientRect(); - bTop = window.scrollY + bRect.top; - bLeft = window.scrollX + bRect.left; - cHeight = doc.clientHeight; - cWidth = doc.clientWidth; - _ref1 = bRect.top + bRect.height + mRect.height < cHeight ? [bRect.bottom, null] : [null, cHeight - bRect.top], top = _ref1[0], bottom = _ref1[1]; - _ref2 = bRect.left + mRect.width < cWidth ? [bRect.left, null] : [null, cWidth - bRect.right], left = _ref2[0], right = _ref2[1]; - style = menu.style; - style.top = "" + top + "px"; - style.right = "" + right + "px"; - style.bottom = "" + bottom + "px"; - style.left = "" + left + "px"; - if (right) { - $.addClass(menu, 'left'); - } - entry = $('.entry', menu); - this.focus(entry); - return menu.focus(); - }; - - Menu.prototype.insertEntry = function(entry, parent, data) { - var subEntry, submenu, _i, _len, _ref; - if (typeof entry.open === 'function') { - if (!entry.open(data)) { - return; - } - } - $.add(parent, entry.el); - if (!entry.subEntries) { - return; - } - if (submenu = $('.submenu', entry.el)) { - $.rm(submenu); - } - submenu = $.el('div', { - className: 'dialog submenu' - }); - _ref = entry.subEntries; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - subEntry = _ref[_i]; - this.insertEntry(subEntry, submenu, data); - } - $.add(entry.el, submenu); - }; - - Menu.prototype.close = function() { - $.rm(currentMenu); - $.rmClass(lastToggledButton, 'active'); - currentMenu = null; - lastToggledButton = null; - return $.off(d, 'click CloseMenu', this.close); - }; - - Menu.prototype.findNextEntry = function(entry, direction) { - var entries; - entries = __slice.call(entry.parentNode.children); - entries.sort(function(first, second) { - return first.style.order - second.style.order; - }); - return entries[entries.indexOf(entry) + direction]; - }; - - Menu.prototype.keybinds = function(e) { - var entry, next, nextPrev, subEntry, submenu; - entry = $('.focused', currentMenu); - while (subEntry = $('.focused', entry)) { - entry = subEntry; - } - switch (e.keyCode) { - case 27: - lastToggledButton.focus(); - this.close(); - break; - case 13: - case 32: - entry.click(); - break; - case 38: - if (next = this.findNextEntry(entry, -1)) { - this.focus(next); - } - break; - case 40: - if (next = this.findNextEntry(entry, +1)) { - this.focus(next); - } - break; - case 39: - if ((submenu = $('.submenu', entry)) && (next = submenu.firstElementChild)) { - while (nextPrev = this.findNextEntry(next, -1)) { - next = nextPrev; - } - this.focus(next); - } - break; - case 37: - if (next = $.x('parent::*[contains(@class,"submenu")]/parent::*', entry)) { - this.focus(next); - } - break; - default: - return; - } - e.preventDefault(); - return e.stopPropagation(); - }; - - Menu.prototype.onFocus = function(e) { - e.stopPropagation(); - return this.focus(e.target); - }; - - Menu.prototype.focus = function(entry) { - var bottom, cHeight, cWidth, eRect, focused, left, right, sRect, style, submenu, top, _i, _len, _ref, _ref1, _ref2; - while (focused = $.x('parent::*/child::*[contains(@class,"focused")]', entry)) { - $.rmClass(focused, 'focused'); - } - _ref = $$('.focused', entry); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - focused = _ref[_i]; - $.rmClass(focused, 'focused'); - } - $.addClass(entry, 'focused'); - if (!(submenu = $('.submenu', entry))) { - return; - } - sRect = submenu.getBoundingClientRect(); - eRect = entry.getBoundingClientRect(); - cHeight = doc.clientHeight; - cWidth = doc.clientWidth; - _ref1 = eRect.top + sRect.height < cHeight ? ['0px', 'auto'] : ['auto', '0px'], top = _ref1[0], bottom = _ref1[1]; - _ref2 = eRect.right + sRect.width < cWidth - 150 ? ['100%', 'auto'] : ['auto', '100%'], left = _ref2[0], right = _ref2[1]; - style = submenu.style; - style.top = top; - style.bottom = bottom; - style.left = left; - return style.right = right; - }; - - Menu.prototype.addEntry = function(e) { - var entry; - entry = e.detail; - if (entry.type !== this.type) { - return; - } - this.parseEntry(entry); - return this.entries.push(entry); - }; - - Menu.prototype.rmEntry = function(e) { - var entry, index; - entry = e.detail; - if (entry.type !== this.type) { - return; - } - index = this.entries.indexOf(entry); - return this.entries.splice(index, 1); - }; - - Menu.prototype.parseEntry = function(entry) { - var el, subEntries, subEntry, _i, _len; - el = entry.el, subEntries = entry.subEntries; - $.addClass(el, 'entry'); - $.on(el, 'focus mouseover', this.onFocus); - el.style.order = entry.order || 100; - if (!subEntries) { - return; - } - $.addClass(el, 'has-submenu'); - for (_i = 0, _len = subEntries.length; _i < _len; _i++) { - subEntry = subEntries[_i]; - this.parseEntry(subEntry); - } - }; - - return Menu; - - })(); - dragstart = function(e) { - var el, isTouching, o, rect, screenHeight, screenWidth, _ref; - if (e.type === 'mousedown' && e.button !== 0) { - return; - } - e.preventDefault(); - if (isTouching = e.type === 'touchstart') { - e = e.changedTouches[e.changedTouches.length - 1]; - } - el = $.x('ancestor::div[contains(@class,"dialog")][1]', this); - rect = el.getBoundingClientRect(); - screenHeight = doc.clientHeight; - screenWidth = doc.clientWidth; - o = { - id: el.id, - style: el.style, - dx: e.clientX - rect.left, - dy: e.clientY - rect.top, - height: screenHeight - rect.height, - width: screenWidth - rect.width, - screenHeight: screenHeight, - screenWidth: screenWidth, - isTouching: isTouching - }; - _ref = Conf['Header auto-hide'] || !Conf['Fixed Header'] ? [0, 0] : Conf['Bottom Header'] ? [0, Header.bar.getBoundingClientRect().height] : [Header.bar.getBoundingClientRect().height, 0], o.topBorder = _ref[0], o.bottomBorder = _ref[1]; - if (isTouching) { - o.identifier = e.identifier; - o.move = touchmove.bind(o); - o.up = touchend.bind(o); - $.on(d, 'touchmove', o.move); - return $.on(d, 'touchend touchcancel', o.up); - } else { - o.move = drag.bind(o); - o.up = dragend.bind(o); - $.on(d, 'mousemove', o.move); - return $.on(d, 'mouseup', o.up); - } - }; - touchmove = function(e) { - var touch, _i, _len, _ref; - _ref = e.changedTouches; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - touch = _ref[_i]; - if (touch.identifier === this.identifier) { - drag.call(this, touch); - return; - } - } - }; - drag = function(e) { - var bottom, clientX, clientY, left, right, style, top; - clientX = e.clientX, clientY = e.clientY; - left = clientX - this.dx; - left = left < 10 ? 0 : this.width - left < 10 ? null : left / this.screenWidth * 100 + '%'; - top = clientY - this.dy; - top = top < (10 + this.topBorder) ? this.topBorder + 'px' : this.height - top < (10 + this.bottomBorder) ? null : top / this.screenHeight * 100 + '%'; - right = left === null ? 0 : null; - bottom = top === null ? this.bottomBorder + 'px' : null; - style = this.style; - style.left = left; - style.right = right; - style.top = top; - return style.bottom = bottom; - }; - touchend = function(e) { - var touch, _i, _len, _ref; - _ref = e.changedTouches; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - touch = _ref[_i]; - if (touch.identifier === this.identifier) { - dragend.call(this); - return; - } - } - }; - dragend = function() { - if (this.isTouching) { - $.off(d, 'touchmove', this.move); - $.off(d, 'touchend touchcancel', this.up); - } else { - $.off(d, 'mousemove', this.move); - $.off(d, 'mouseup', this.up); - } - return $.set("" + this.id + ".position", this.style.cssText); - }; - hoverstart = function(_arg) { - var asapTest, cb, el, endEvents, latestEvent, o, root; - root = _arg.root, el = _arg.el, latestEvent = _arg.latestEvent, endEvents = _arg.endEvents, asapTest = _arg.asapTest, cb = _arg.cb; - o = { - root: root, - el: el, - style: el.style, - cb: cb, - endEvents: endEvents, - latestEvent: latestEvent, - clientHeight: doc.clientHeight, - clientWidth: doc.clientWidth - }; - o.hover = hover.bind(o); - o.hoverend = hoverend.bind(o); - $.asap(function() { - return !el.parentNode || asapTest(); - }, function() { - if (el.parentNode) { - return o.hover(o.latestEvent); - } - }); - $.on(root, endEvents, o.hoverend); - if ($.x('ancestor::div[contains(@class,"inline")][1]', root)) { - $.on(d, 'keydown', o.hoverend); - } - $.on(root, 'mousemove', o.hover); - o.workaround = function(e) { - if (!root.contains(e.target)) { - return o.hoverend(); - } - }; - return $.on(doc, 'mousemove', o.workaround); - }; - hover = function(e) { - var clientX, clientY, height, left, right, style, top, _ref; - this.latestEvent = e; - height = this.el.offsetHeight; - clientX = e.clientX, clientY = e.clientY; - top = clientY - 120; - top = this.clientHeight <= height || top <= 0 ? 0 : top + height >= this.clientHeight ? this.clientHeight - height : top; - _ref = clientX <= this.clientWidth - 400 ? [clientX + 45 + 'px', null] : [null, this.clientWidth - clientX + 45 + 'px'], left = _ref[0], right = _ref[1]; - style = this.style; - style.top = top + 'px'; - style.left = left; - return style.right = right; - }; - hoverend = function(e) { - if (e.type === 'keydown' && e.keyCode !== 13 || e.target.nodeName === "TEXTAREA") { - return; - } - $.rm(this.el); - $.off(this.root, this.endEvents, this.hoverend); - $.off(d, 'keydown', this.hoverend); - $.off(this.root, 'mousemove', this.hover); - $.off(doc, 'mousemove', this.workaround); - if (this.cb) { - return this.cb.call(this); - } - }; - return { - dialog: dialog, - Menu: Menu, - hover: hoverstart - }; - })(); - - Anonymize = { - init: function() { - if (g.VIEW === 'catalog' || !Conf['Anonymize']) { - return; - } - return Post.callbacks.push({ - name: 'Anonymize', - cb: this.node - }); - }, - node: function() { - var email, name, tripcode, _ref; - if (this.info.capcode || this.isClone) { - return; - } - _ref = this.nodes, name = _ref.name, tripcode = _ref.tripcode, email = _ref.email; - if (this.info.name !== 'Anonymous') { - name.textContent = 'Anonymous'; - } - if (tripcode) { - $.rm(tripcode); - delete this.nodes.tripcode; - } - if (this.info.email) { - $.replace(email, name); - return delete this.nodes.email; - } - } - }; - - Filter = { - filters: {}, - init: function() { - var boards, err, filter, hl, key, op, regexp, stub, top, _i, _len, _ref, _ref1, _ref2, _ref3, _ref4, _ref5; - if (g.VIEW === 'catalog' || !Conf['Filter']) { - return; - } - if (!Conf['Filtered Backlinks']) { - $.addClass(doc, 'hide-backlinks'); - } - for (key in Config.filter) { - this.filters[key] = []; - _ref = Conf[key].split('\n'); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - filter = _ref[_i]; - if (filter[0] === '#') { - continue; - } - if (!(regexp = filter.match(/\/(.+)\/(\w*)/))) { - continue; - } - filter = filter.replace(regexp[0], ''); - boards = ((_ref1 = filter.match(/boards:([^;]+)/)) != null ? _ref1[1].toLowerCase() : void 0) || 'global'; - if (boards !== 'global' && (_ref2 = g.BOARD.ID, __indexOf.call(boards.split(','), _ref2) < 0)) { - continue; - } - if (key === 'uniqueID' || key === 'MD5') { - regexp = regexp[1]; - } else { - try { - regexp = RegExp(regexp[1], regexp[2]); - } catch (_error) { - err = _error; - new Notice('warning', err.message, 60); - continue; - } - } - op = ((_ref3 = filter.match(/[^t]op:(yes|no|only)/)) != null ? _ref3[1] : void 0) || 'yes'; - stub = (function() { - var _ref4; - switch ((_ref4 = filter.match(/stub:(yes|no)/)) != null ? _ref4[1] : void 0) { - case 'yes': - return true; - case 'no': - return false; - default: - return Conf['Stubs']; - } - })(); - if (hl = /highlight/.test(filter)) { - hl = ((_ref4 = filter.match(/highlight:(\w+)/)) != null ? _ref4[1] : void 0) || 'filter-highlight'; - top = ((_ref5 = filter.match(/top:(yes|no)/)) != null ? _ref5[1] : void 0) || 'yes'; - top = top === 'yes'; - } - this.filters[key].push(this.createFilter(regexp, op, stub, hl, top)); - } - if (!this.filters[key].length) { - delete this.filters[key]; - } - } - if (!Object.keys(this.filters).length) { - return; - } - return Post.callbacks.push({ - name: 'Filter', - cb: this.node - }); - }, - createFilter: function(regexp, op, stub, hl, top) { - var settings, test; - test = typeof regexp === 'string' ? function(value) { - return regexp === value; - } : function(value) { - return regexp.test(value); - }; - settings = { - hide: !hl, - stub: stub, - "class": hl, - top: top - }; - return function(value, isReply) { - if (isReply && op === 'only' || !isReply && op === 'no') { - return false; - } - if (!test(value)) { - return false; - } - return settings; - }; - }, - node: function() { - var filter, key, result, value, _i, _len, _ref; - if (this.isClone) { - return; - } - for (key in Filter.filters) { - value = Filter[key](this); - if (value === false) { - continue; - } - _ref = Filter.filters[key]; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - filter = _ref[_i]; - if (!(result = filter(value, this.isReply))) { - continue; - } - if (result.hide) { - if (this.isReply) { - PostHiding.hide(this, result.stub); - } else if (g.VIEW === 'index') { - ThreadHiding.hide(this.thread, result.stub); - } else { - continue; - } - return; - } - $.addClass(this.nodes.root, result["class"]); - if (!this.isReply && result.top) { - this.thread.isOnTop = true; - } - } - } - }, - name: function(post) { - if ('name' in post.info) { - return post.info.name; - } - return false; - }, - uniqueID: function(post) { - if ('uniqueID' in post.info) { - return post.info.uniqueID; - } - return false; - }, - tripcode: function(post) { - if ('tripcode' in post.info) { - return post.info.tripcode; - } - return false; - }, - capcode: function(post) { - if ('capcode' in post.info) { - return post.info.capcode; - } - return false; - }, - email: function(post) { - if ('email' in post.info) { - return post.info.email; - } - return false; - }, - subject: function(post) { - if ('subject' in post.info) { - return post.info.subject || false; - } - return false; - }, - comment: function(post) { - if ('comment' in post.info) { - return post.info.comment; - } - return false; - }, - flag: function(post) { - if ('flag' in post.info) { - return post.info.flag; - } - return false; - }, - filename: function(post) { - if (post.file) { - return post.file.name; - } - return false; - }, - dimensions: function(post) { - if (post.file && post.file.isImage) { - return post.file.dimensions; - } - return false; - }, - filesize: function(post) { - if (post.file) { - return post.file.size; - } - return false; - }, - MD5: function(post) { - if (post.file) { - return post.file.MD5; - } - return false; - }, - menu: { - init: function() { - var div, entry, type, _i, _len, _ref; - if (g.VIEW === 'catalog' || !Conf['Menu'] || !Conf['Filter']) { - return; - } - div = $.el('div', { - textContent: 'Filter' - }); - entry = { - type: 'post', - el: div, - order: 50, - open: function(post) { - Filter.menu.post = post; - return true; - }, - subEntries: [] - }; - _ref = [['Name', 'name'], ['Unique ID', 'uniqueID'], ['Tripcode', 'tripcode'], ['Capcode', 'capcode'], ['E-mail', 'email'], ['Subject', 'subject'], ['Comment', 'comment'], ['Flag', 'flag'], ['Filename', 'filename'], ['Image dimensions', 'dimensions'], ['Filesize', 'filesize'], ['Image MD5', 'MD5']]; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - type = _ref[_i]; - entry.subEntries.push(Filter.menu.createSubEntry(type[0], type[1])); - } - return $.event('AddMenuEntry', entry); - }, - createSubEntry: function(text, type) { - var el; - el = $.el('a', { - href: 'javascript:;', - textContent: text - }); - el.dataset.type = type; - $.on(el, 'click', Filter.menu.makeFilter); - return { - el: el, - open: function(post) { - var value; - value = Filter[type](post); - return value !== false; - } - }; - }, - makeFilter: function() { - var re, type, value; - type = this.dataset.type; - value = Filter[type](Filter.menu.post); - re = type === 'uniqueID' || type === 'MD5' ? value : value.replace(/\/|\\|\^|\$|\n|\.|\(|\)|\{|\}|\[|\]|\?|\*|\+|\|/g, function(c) { - if (c === '\n') { - return '\\n'; - } else if (c === '\\') { - return '\\\\'; - } else { - return "\\" + c; - } - }); - re = type === 'uniqueID' || type === 'MD5' ? "/" + re + "/" : "/^" + re + "$/"; - return $.get(type, Conf[type], function(item) { - var save, section, select, ta, tl; - save = item[type]; - save = save ? "" + save + "\n" + re : re; - $.set(type, save); - Settings.open('Filter'); - section = $('.section-container'); - select = $('select[name=filter]', section); - select.value = type; - Settings.selectFilter.call(select); - ta = $('textarea', section); - tl = ta.textLength; - ta.setSelectionRange(tl, tl); - return ta.focus(); - }); - } - } - }; - - PostHiding = { - init: function() { - if (g.VIEW === 'catalog' || !Conf['Reply Hiding Buttons'] && !Conf['Reply Hiding Link']) { - return; - } - if (Conf['Reply Hiding Buttons']) { - $.addClass(doc, "reply-hide"); - } - this.db = new DataBoard('hiddenPosts'); - return Post.callbacks.push({ - name: 'Reply Hiding', - cb: this.node - }); - }, - node: function() { - var data; - if (!this.isReply || this.isClone) { - return; - } - if (data = PostHiding.db.get({ - boardID: this.board.ID, - threadID: this.thread.ID, - postID: this.ID - })) { - if (data.thisPost) { - PostHiding.hide(this, data.makeStub, data.hideRecursively); - } else { - Recursive.apply(PostHiding.hide, this, data.makeStub, true); - Recursive.add(PostHiding.hide, this, data.makeStub, true); - } - } - if (!Conf['Reply Hiding Buttons']) { - return; - } - return $.replace($('.sideArrows', this.nodes.root), PostHiding.makeButton(this, 'hide')); - }, - menu: { - init: function() { - var apply, div, hideStubLink, makeStub, replies, thisPost; - if (g.VIEW === 'catalog' || !Conf['Menu'] || !Conf['Reply Hiding Link']) { - return; - } - div = $.el('div', { - className: 'hide-reply-link', - textContent: 'Hide reply' - }); - apply = $.el('a', { - textContent: 'Apply', - href: 'javascript:;' - }); - $.on(apply, 'click', PostHiding.menu.hide); - thisPost = $.el('label', { - innerHTML: ' This post' - }); - replies = $.el('label', { - innerHTML: " Hide replies" - }); - makeStub = $.el('label', { - innerHTML: " Make stub" - }); - $.event('AddMenuEntry', { - type: 'post', - el: div, - order: 20, - open: function(post) { - if (!post.isReply || post.isClone || post.isHidden) { - return false; - } - PostHiding.menu.post = post; - return true; - }, - subEntries: [ - { - el: apply - }, { - el: thisPost - }, { - el: replies - }, { - el: makeStub - } - ] - }); - div = $.el('div', { - className: 'show-reply-link', - textContent: 'Show reply' - }); - apply = $.el('a', { - textContent: 'Apply', - href: 'javascript:;' - }); - $.on(apply, 'click', PostHiding.menu.show); - thisPost = $.el('label', { - innerHTML: ' This post' - }); - replies = $.el('label', { - innerHTML: " Show replies" - }); - hideStubLink = $.el('a', { - textContent: 'Hide stub', - href: 'javascript:;' - }); - $.on(hideStubLink, 'click', PostHiding.menu.hideStub); - $.event('AddMenuEntry', { - type: 'post', - el: div, - order: 20, - open: function(post) { - var data; - if (!post.isReply || post.isClone || !post.isHidden) { - return false; - } - if (!(data = PostHiding.db.get({ - boardID: post.board.ID, - threadID: post.thread.ID, - postID: post.ID - }))) { - return false; - } - PostHiding.menu.post = post; - thisPost.firstChild.checked = post.isHidden; - replies.firstChild.checked = (data != null ? data.hideRecursively : void 0) != null ? data.hideRecursively : Conf['Recursive Hiding']; - return true; - }, - subEntries: [ - { - el: apply - }, { - el: thisPost - }, { - el: replies - } - ] - }); - return $.event('AddMenuEntry', { - type: 'post', - el: hideStubLink, - order: 15, - open: function(post) { - var data; - if (!post.isReply || post.isClone || !post.isHidden) { - return false; - } - if (!(data = PostHiding.db.get({ - boardID: post.board.ID, - threadID: post.thread.ID, - postID: post.ID - }))) { - return false; - } - return PostHiding.menu.post = post; - } - }); - }, - hide: function() { - var makeStub, parent, post, replies, thisPost; - parent = this.parentNode; - thisPost = $('input[name=thisPost]', parent).checked; - replies = $('input[name=replies]', parent).checked; - makeStub = $('input[name=makeStub]', parent).checked; - post = PostHiding.menu.post; - if (thisPost) { - PostHiding.hide(post, makeStub, replies); - } else if (replies) { - Recursive.apply(PostHiding.hide, post, makeStub, true); - Recursive.add(PostHiding.hide, post, makeStub, true); - } else { - return; - } - PostHiding.saveHiddenState(post, true, thisPost, makeStub, replies); - return $.event('CloseMenu'); - }, - show: function() { - var data, parent, post, replies, thisPost; - parent = this.parentNode; - thisPost = $('input[name=thisPost]', parent).checked; - replies = $('input[name=replies]', parent).checked; - post = PostHiding.menu.post; - if (thisPost) { - PostHiding.show(post, replies); - } else if (replies) { - Recursive.apply(PostHiding.show, post, true); - Recursive.rm(PostHiding.hide, post, true); - } else { - return; - } - if (data = PostHiding.db.get({ - boardID: post.board.ID, - threadID: post.thread.ID, - postID: post.ID - })) { - PostHiding.saveHiddenState(post, !(thisPost && replies), !thisPost, data.makeStub, !replies); - } - return $.event('CloseMenu'); - }, - hideStub: function() { - var post; - post = PostHiding.menu.post; - post.nodes.root.hidden = true; - $.event('CloseMenu'); - } - }, - makeButton: function(post, type) { - var a, span; - span = $.el('span', { - className: "fa fa-" + (type === 'hide' ? 'minus' : 'plus') + "-square-o", - textContent: "" - }); - a = $.el('a', { - className: "" + type + "-reply-button", - href: 'javascript:;' - }); - $.add(a, span); - $.on(a, 'click', PostHiding.toggle); - return a; - }, - saveHiddenState: function(post, isHiding, thisPost, makeStub, hideRecursively) { - var data; - data = { - boardID: post.board.ID, - threadID: post.thread.ID, - postID: post.ID - }; - if (isHiding) { - data.val = { - thisPost: thisPost !== false, - makeStub: makeStub, - hideRecursively: hideRecursively - }; - return PostHiding.db.set(data); - } else { - return PostHiding.db["delete"](data); - } - }, - toggle: function() { - var post; - post = Get.postFromNode(this); - PostHiding[(post.isHidden ? 'show' : 'hide')](post); - return PostHiding.saveHiddenState(post, post.isHidden); - }, - hide: function(post, makeStub, hideRecursively) { - var a, postInfo, quotelink, _i, _len, _ref; - if (makeStub == null) { - makeStub = Conf['Stubs']; - } - if (hideRecursively == null) { - hideRecursively = Conf['Recursive Hiding']; - } - if (post.isHidden) { - return; - } - post.isHidden = true; - if (hideRecursively) { - Recursive.apply(PostHiding.hide, post, makeStub, true); - Recursive.add(PostHiding.hide, post, makeStub, true); - } - _ref = Get.allQuotelinksLinkingTo(post); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - quotelink = _ref[_i]; - $.addClass(quotelink, 'filtered'); - } - if (!makeStub) { - post.nodes.root.hidden = true; - return; - } - a = PostHiding.makeButton(post, 'show'); - postInfo = Conf['Anonymize'] ? 'Anonymous' : $('.nameBlock', post.nodes.info).textContent; - $.add(a, $.tn(" " + postInfo)); - post.nodes.stub = $.el('div', { - className: 'stub' - }); - $.add(post.nodes.stub, a); - if (Conf['Menu']) { - $.add(post.nodes.stub, Menu.makeButton()); - } - return $.prepend(post.nodes.root, post.nodes.stub); - }, - show: function(post, showRecursively) { - var quotelink, _i, _len, _ref; - if (showRecursively == null) { - showRecursively = Conf['Recursive Hiding']; - } - if (post.nodes.stub) { - $.rm(post.nodes.stub); - delete post.nodes.stub; - } else { - post.nodes.root.hidden = false; - } - post.isHidden = false; - if (showRecursively) { - Recursive.apply(PostHiding.show, post, true); - Recursive.rm(PostHiding.hide, post); - } - _ref = Get.allQuotelinksLinkingTo(post); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - quotelink = _ref[_i]; - $.rmClass(quotelink, 'filtered'); - } - } - }; - - Recursive = { - recursives: {}, - init: function() { - if (g.VIEW === 'catalog') { - return; - } - return Post.callbacks.push({ - name: 'Recursive', - cb: this.node - }); - }, - node: function() { - var i, obj, quote, recursive, _i, _j, _len, _len1, _ref, _ref1; - if (this.isClone) { - return; - } - _ref = this.quotes; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - quote = _ref[_i]; - if (obj = Recursive.recursives[quote]) { - _ref1 = obj.recursives; - for (i = _j = 0, _len1 = _ref1.length; _j < _len1; i = ++_j) { - recursive = _ref1[i]; - recursive.apply(null, [this].concat(__slice.call(obj.args[i]))); - } - } - } - }, - add: function() { - var args, obj, post, recursive, _base, _name; - recursive = arguments[0], post = arguments[1], args = 3 <= arguments.length ? __slice.call(arguments, 2) : []; - obj = (_base = Recursive.recursives)[_name = post.fullID] || (_base[_name] = { - recursives: [], - args: [] - }); - obj.recursives.push(recursive); - return obj.args.push(args); - }, - rm: function(recursive, post) { - var i, obj, rec, _i, _len, _ref; - if (!(obj = Recursive.recursives[post.fullID])) { - return; - } - _ref = obj.recursives; - for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { - rec = _ref[i]; - if (rec === recursive) { - obj.recursives.splice(i, 1); - obj.args.splice(i, 1); - } - } - }, - apply: function() { - var args, fullID, post, recursive; - recursive = arguments[0], post = arguments[1], args = 3 <= arguments.length ? __slice.call(arguments, 2) : []; - fullID = post.fullID; - return g.posts.forEach(function(post) { - if (__indexOf.call(post.quotes, fullID) >= 0) { - return recursive.apply(null, [post].concat(__slice.call(args))); - } - }); - } - }; - - ThreadHiding = { - init: function() { - if (g.VIEW !== 'index' || !Conf['Thread Hiding Buttons'] && !Conf['Thread Hiding Link']) { - return; - } - this.db = new DataBoard('hiddenThreads'); - this.syncCatalog(); - $.on(d, 'IndexBuild', this.onIndexBuild); - return Thread.callbacks.push({ - name: 'Thread Hiding', - cb: this.node - }); - }, - node: function() { - var data; - if (data = ThreadHiding.db.get({ - boardID: this.board.ID, - threadID: this.ID - })) { - ThreadHiding.hide(this, data.makeStub); - } - if (!Conf['Thread Hiding Buttons']) { - return; - } - return $.prepend(this.OP.nodes.root, ThreadHiding.makeButton(this, 'hide')); - }, - onIndexBuild: function(_arg) { - var i, nodes, root, thread, _i, _len; - nodes = _arg.detail; - for (i = _i = 0, _len = nodes.length; _i < _len; i = _i += 2) { - root = nodes[i]; - thread = Get.threadFromRoot(root); - if (!thread.isHidden) { - continue; - } - if (!thread.stub) { - nodes[i + 1].hidden = true; - } else if (!root.contains(thread.stub)) { - ThreadHiding.makeStub(thread, root); - } - } - }, - syncCatalog: function() { - var hiddenThreads, hiddenThreadsOnCatalog, threadID; - hiddenThreads = ThreadHiding.db.get({ - boardID: g.BOARD.ID, - defaultValue: {} - }); - hiddenThreadsOnCatalog = JSON.parse(localStorage.getItem("4chan-hide-t-" + g.BOARD)) || {}; - for (threadID in hiddenThreadsOnCatalog) { - if (!(threadID in hiddenThreads)) { - hiddenThreads[threadID] = {}; - } - } - for (threadID in hiddenThreads) { - if (!(threadID in hiddenThreadsOnCatalog)) { - delete hiddenThreads[threadID]; - } - } - if ((ThreadHiding.db.data.lastChecked || 0) > Date.now() - $.MINUTE) { - ThreadHiding.cleanCatalog(hiddenThreadsOnCatalog); - } - return ThreadHiding.db.set({ - boardID: g.BOARD.ID, - val: hiddenThreads - }); - }, - cleanCatalog: function(hiddenThreadsOnCatalog) { - return $.cache("//a.4cdn.org/" + g.BOARD + "/threads.json", function() { - var page, thread, threads, _i, _j, _len, _len1, _ref, _ref1; - if (this.status !== 200) { - return; - } - threads = {}; - _ref = this.response; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - page = _ref[_i]; - _ref1 = page.threads; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - thread = _ref1[_j]; - if (thread.no in hiddenThreadsOnCatalog) { - threads[thread.no] = hiddenThreadsOnCatalog[thread.no]; - } - } - } - if (Object.keys(threads).length) { - return localStorage.setItem("4chan-hide-t-" + g.BOARD, JSON.stringify(threads)); - } else { - return localStorage.removeItem("4chan-hide-t-" + g.BOARD); - } - }); - }, - menu: { - init: function() { - var apply, div, hideStubLink, makeStub; - if (g.VIEW !== 'index' || !Conf['Menu'] || !Conf['Thread Hiding Link']) { - return; - } - div = $.el('div', { - className: 'hide-thread-link', - textContent: 'Hide thread' - }); - apply = $.el('a', { - textContent: 'Apply', - href: 'javascript:;' - }); - $.on(apply, 'click', ThreadHiding.menu.hide); - makeStub = $.el('label', { - innerHTML: " Make stub" - }); - $.event('AddMenuEntry', { - type: 'post', - el: div, - order: 20, - open: function(_arg) { - var isReply, thread; - thread = _arg.thread, isReply = _arg.isReply; - if (isReply || thread.isHidden) { - return false; - } - ThreadHiding.menu.thread = thread; - return true; - }, - subEntries: [ - { - el: apply - }, { - el: makeStub - } - ] - }); - div = $.el('a', { - className: 'show-thread-link', - textContent: 'Show thread', - href: 'javascript:;' - }); - $.on(div, 'click', ThreadHiding.menu.show); - $.event('AddMenuEntry', { - type: 'post', - el: div, - order: 20, - open: function(_arg) { - var isReply, thread; - thread = _arg.thread, isReply = _arg.isReply; - if (isReply || !thread.isHidden) { - return false; - } - ThreadHiding.menu.thread = thread; - return true; - } - }); - hideStubLink = $.el('a', { - textContent: 'Hide stub', - href: 'javascript:;' - }); - $.on(hideStubLink, 'click', ThreadHiding.menu.hideStub); - return $.event('AddMenuEntry', { - type: 'post', - el: hideStubLink, - order: 15, - open: function(_arg) { - var isReply, thread; - thread = _arg.thread, isReply = _arg.isReply; - if (isReply || !thread.isHidden) { - return false; - } - return ThreadHiding.menu.thread = thread; - } - }); - }, - hide: function() { - var makeStub, thread; - makeStub = $('input', this.parentNode).checked; - thread = ThreadHiding.menu.thread; - ThreadHiding.hide(thread, makeStub); - ThreadHiding.saveHiddenState(thread, makeStub); - return $.event('CloseMenu'); - }, - show: function() { - var thread; - thread = ThreadHiding.menu.thread; - ThreadHiding.show(thread); - ThreadHiding.saveHiddenState(thread); - return $.event('CloseMenu'); - }, - hideStub: function() { - var thread; - thread = ThreadHiding.menu.thread; - ThreadHiding.hide(thread, false); - $.event('CloseMenu'); - } - }, - makeButton: function(thread, type) { - var a; - a = $.el('a', { - className: "" + type + "-thread-button", - innerHTML: "", - href: 'javascript:;' - }); - a.dataset.fullID = thread.fullID; - $.on(a, 'click', ThreadHiding.toggle); - return a; - }, - makeStub: function(thread, root) { - var a, numReplies, opInfo, summary; - numReplies = $$('.thread > .replyContainer', root).length; - if (summary = $('.summary', root)) { - numReplies += +summary.textContent.match(/\d+/); - } - opInfo = Conf['Anonymize'] ? 'Anonymous' : $('.nameBlock', thread.OP.nodes.info).textContent; - a = ThreadHiding.makeButton(thread, 'show'); - $.add(a, $.tn(" " + opInfo + " (" + (numReplies === 1 ? '1 reply' : "" + numReplies + " replies") + ")")); - thread.stub = $.el('div', { - className: 'stub' - }); - if (Conf['Menu']) { - $.add(thread.stub, [a, Menu.makeButton()]); - } else { - $.add(thread.stub, a); - } - return $.prepend(root, thread.stub); - }, - saveHiddenState: function(thread, makeStub) { - var hiddenThreadsOnCatalog; - hiddenThreadsOnCatalog = JSON.parse(localStorage.getItem("4chan-hide-t-" + g.BOARD)) || {}; - if (thread.isHidden) { - ThreadHiding.db.set({ - boardID: thread.board.ID, - threadID: thread.ID, - val: { - makeStub: makeStub - } - }); - hiddenThreadsOnCatalog[thread] = true; - } else { - ThreadHiding.db["delete"]({ - boardID: thread.board.ID, - threadID: thread.ID - }); - delete hiddenThreadsOnCatalog[thread]; - } - return localStorage.setItem("4chan-hide-t-" + g.BOARD, JSON.stringify(hiddenThreadsOnCatalog)); - }, - toggle: function(thread) { - if (!(thread instanceof Thread)) { - thread = g.threads[this.dataset.fullID]; - } - if (thread.isHidden) { - ThreadHiding.show(thread); - } else { - ThreadHiding.hide(thread); - } - return ThreadHiding.saveHiddenState(thread); - }, - hide: function(thread, makeStub) { - var threadRoot; - if (makeStub == null) { - makeStub = Conf['Stubs']; - } - if (thread.isHidden) { - return; - } - threadRoot = thread.OP.nodes.root.parentNode; - thread.isHidden = true; - if (!makeStub) { - return threadRoot.hidden = threadRoot.nextElementSibling.hidden = true; - } - return ThreadHiding.makeStub(thread, threadRoot); - }, - show: function(thread) { - var threadRoot; - if (thread.stub) { - $.rm(thread.stub); - delete thread.stub; - } - threadRoot = thread.OP.nodes.root.parentNode; - return threadRoot.nextElementSibling.hidden = threadRoot.hidden = thread.isHidden = false; - } - }; - - QuoteBacklink = { - containers: {}, - init: function() { - var format; - if (g.VIEW === 'catalog' || !Conf['Quote Backlinks']) { - return; - } - format = Conf['backlink'].replace(/%id/g, "' + id + '"); - this.funk = Function('id', "return '" + format + "'"); - Post.callbacks.push({ - name: 'Quote Backlinking Part 1', - cb: this.firstNode - }); - return Post.callbacks.push({ - name: 'Quote Backlinking Part 2', - cb: this.secondNode - }); - }, - firstNode: function() { - var a, clone, container, containers, link, nodes, post, quote, _i, _j, _k, _len, _len1, _len2, _ref, _ref1; - if (this.isClone || !this.quotes.length) { - return; - } - a = $.el('a', { - href: "/" + this.board + "/res/" + this.thread + "#p" + this, - className: this.isHidden ? 'filtered backlink' : 'backlink', - textContent: (QuoteBacklink.funk(this.ID)) + (Conf['Mark Quotes of You'] && this.info.yours ? '\u00A0(You)' : '') - }); - _ref = this.quotes; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - quote = _ref[_i]; - containers = [QuoteBacklink.getContainer(quote)]; - if ((post = g.posts[quote]) && post.nodes.backlinkContainer) { - _ref1 = post.clones; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - clone = _ref1[_j]; - containers.push(clone.nodes.backlinkContainer); - } - } - for (_k = 0, _len2 = containers.length; _k < _len2; _k++) { - container = containers[_k]; - nodes = [$.tn(' '), link = a.cloneNode(true)]; - if (Conf['Quote Previewing']) { - $.on(link, 'mouseover', QuotePreview.mouseover); - } - if (Conf['Quote Inlining']) { - $.on(link, 'click', QuoteInline.toggle); - if (Conf['Quote Hash Navigation']) { - nodes.push(QuoteInline.qiQuote(link, $.hasClass(link, 'filtered'))); - } - } - $.add(container, nodes); - } - } - }, - secondNode: function() { - var container; - if (this.isClone && (this.origin.isReply || Conf['OP Backlinks'])) { - this.nodes.backlinkContainer = $('.container', this.nodes.info); - return; - } - if (!(this.isReply || Conf['OP Backlinks'])) { - return; - } - container = QuoteBacklink.getContainer(this.fullID); - this.nodes.backlinkContainer = container; - return $.add(this.nodes.info, container); - }, - getContainer: function(id) { - var _base; - return (_base = this.containers)[id] || (_base[id] = $.el('span', { - className: 'container' - })); - } - }; - - QuoteCT = { - init: function() { - if (g.VIEW === 'catalog' || !Conf['Mark Cross-thread Quotes']) { - return; - } - if (Conf['Comment Expansion']) { - ExpandComment.callbacks.push(this.node); - } - this.text = '\u00A0(Cross-thread)'; - return Post.callbacks.push({ - name: 'Mark Cross-thread Quotes', - cb: this.node - }); - }, - node: function() { - var board, boardID, quotelink, thread, threadID, _i, _len, _ref, _ref1, _ref2; - if (this.isClone && this.thread === this.context.thread) { - return; - } - _ref = this.isClone ? this.context : this, board = _ref.board, thread = _ref.thread; - _ref1 = this.nodes.quotelinks; - for (_i = 0, _len = _ref1.length; _i < _len; _i++) { - quotelink = _ref1[_i]; - _ref2 = Get.postDataFromLink(quotelink), boardID = _ref2.boardID, threadID = _ref2.threadID; - if (!threadID) { - continue; - } - if (this.isClone) { - quotelink.textContent = quotelink.textContent.replace(QuoteCT.text, ''); - } - if (boardID === board.ID && threadID !== thread.ID) { - $.add(quotelink, $.tn(QuoteCT.text)); - } - } - } - }; - - QuoteInline = { - init: function() { - if (g.VIEW === 'catalog' || !Conf['Quote Inlining']) { - return; - } - this.process = Conf['Quote Hash Navigation'] ? function(link, clone) { - if (!clone) { - $.after(link, QuoteInline.qiQuote(link, $.hasClass(link, 'filtered'))); - } - return $.on(link, 'click', QuoteInline.toggle); - } : function(link) { - return $.on(link, 'click', QuoteInline.toggle); - }; - if (Conf['Comment Expansion']) { - ExpandComment.callbacks.push(this.node); - } - return Post.callbacks.push({ - name: 'Quote Inlining', - cb: this.node - }); - }, - node: function() { - var isClone, link, process, _i, _j, _len, _len1, _ref, _ref1; - process = QuoteInline.process; - isClone = this.isClone; - _ref = this.nodes.quotelinks; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - link = _ref[_i]; - process(link, isClone); - } - _ref1 = this.nodes.backlinks; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - link = _ref1[_j]; - process(link, isClone); - } - }, - qiQuote: function(link, hidden) { - return $.el('a', { - className: "hashlink" + (hidden ? ' filtered' : ''), - textContent: '#', - href: link.href - }); - }, - toggle: function(e) { - var boardID, context, postID, threadID, _ref; - if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) { - return; - } - e.preventDefault(); - _ref = Get.postDataFromLink(this), boardID = _ref.boardID, threadID = _ref.threadID, postID = _ref.postID; - context = Get.contextFromNode(this); - if ($.hasClass(this, 'inlined')) { - QuoteInline.rm(this, boardID, threadID, postID, context); - } else { - if ($.x("ancestor::div[@id='p" + postID + "']", this)) { - return; - } - QuoteInline.add(this, boardID, threadID, postID, context); - } - return this.classList.toggle('inlined'); - }, - findRoot: function(quotelink, isBacklink) { - if (isBacklink) { - return quotelink.parentNode.parentNode; - } else { - return $.x('ancestor-or-self::*[parent::blockquote][1]', quotelink); - } - }, - add: function(quotelink, boardID, threadID, postID, context) { - var inline, isBacklink, post, qroot, root; - isBacklink = $.hasClass(quotelink, 'backlink'); - inline = $.el('div', { - id: "i" + postID, - className: 'inline' - }); - root = QuoteInline.findRoot(quotelink, isBacklink); - $.after(root, inline); - qroot = $.x('ancestor::*[contains(@class,"postContainer")][1]', root); - $.addClass(qroot, 'hasInline'); - Get.postClone(boardID, threadID, postID, inline, context); - if (!((post = g.posts["" + boardID + "." + postID]) && context.thread === post.thread)) { - return; - } - if (isBacklink && Conf['Forward Hiding']) { - $.addClass(post.nodes.root, 'forwarded'); - post.forwarded++ || (post.forwarded = 1); - } - if (!Unread.posts) { - return; - } - return Unread.readSinglePost(post); - }, - rm: function(quotelink, boardID, threadID, postID, context) { - var el, inlined, isBacklink, post, qroot, root, _ref; - isBacklink = $.hasClass(quotelink, 'backlink'); - root = QuoteInline.findRoot(quotelink, isBacklink); - root = $.x("following-sibling::div[@id='i" + postID + "'][1]", root); - qroot = $.x('ancestor::*[contains(@class,"postContainer")][1]', root); - $.rm(root); - if (!$('.inline', qroot)) { - $.rmClass(qroot, 'hasInline'); - } - if (!(el = root.firstElementChild)) { - return; - } - post = g.posts["" + boardID + "." + postID]; - post.rmClone(el.dataset.clone); - if (Conf['Forward Hiding'] && isBacklink && context.thread === g.threads["" + boardID + "." + threadID] && !--post.forwarded) { - delete post.forwarded; - $.rmClass(post.nodes.root, 'forwarded'); - } - while (inlined = $('.inlined', el)) { - _ref = Get.postDataFromLink(inlined), boardID = _ref.boardID, threadID = _ref.threadID, postID = _ref.postID; - QuoteInline.rm(inlined, boardID, threadID, postID, context); - $.rmClass(inlined, 'inlined'); - } - } - }; - - QuoteOP = { - init: function() { - if (g.VIEW === 'catalog' || !Conf['Mark OP Quotes']) { - return; - } - if (Conf['Comment Expansion']) { - ExpandComment.callbacks.push(this.node); - } - this.text = '\u00A0(OP)'; - return Post.callbacks.push({ - name: 'Mark OP Quotes', - cb: this.node - }); - }, - node: function() { - var boardID, fullID, i, postID, quotelink, quotelinks, quotes, _ref, _ref1; - if (this.isClone && this.thread === this.context.thread) { - return; - } - if (!(quotes = this.quotes).length) { - return; - } - quotelinks = this.nodes.quotelinks; - if (this.isClone && (_ref = this.thread.fullID, __indexOf.call(quotes, _ref) >= 0)) { - i = 0; - while (quotelink = quotelinks[i++]) { - quotelink.textContent = quotelink.textContent.replace(QuoteOP.text, ''); - } - } - fullID = (this.isClone ? this.context : this).thread.fullID; - if (__indexOf.call(quotes, fullID) < 0) { - return; - } - i = 0; - while (quotelink = quotelinks[i++]) { - _ref1 = Get.postDataFromLink(quotelink), boardID = _ref1.boardID, postID = _ref1.postID; - if (("" + boardID + "." + postID) === fullID) { - $.add(quotelink, $.tn(QuoteOP.text)); - } - } - } - }; - - QuotePreview = { - init: function() { - if (g.VIEW === 'catalog' || !Conf['Quote Previewing']) { - return; - } - if (Conf['Comment Expansion']) { - ExpandComment.callbacks.push(this.node); - } - return Post.callbacks.push({ - name: 'Quote Previewing', - cb: this.node - }); - }, - node: function() { - var link, _i, _len, _ref; - _ref = this.nodes.quotelinks.concat(__slice.call(this.nodes.backlinks)); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - link = _ref[_i]; - $.on(link, 'mouseover', QuotePreview.mouseover); - } - }, - mouseover: function(e) { - var boardID, clone, origin, post, postID, posts, qp, quote, quoterID, threadID, _i, _j, _len, _len1, _ref, _ref1; - if ($.hasClass(this, 'inlined')) { - return; - } - _ref = Get.postDataFromLink(this), boardID = _ref.boardID, threadID = _ref.threadID, postID = _ref.postID; - qp = $.el('div', { - id: 'qp', - className: 'dialog' - }); - $.add(Header.hover, qp); - Get.postClone(boardID, threadID, postID, qp, Get.contextFromNode(this)); - UI.hover({ - root: this, - el: qp, - latestEvent: e, - endEvents: 'mouseout click', - cb: QuotePreview.mouseout, - asapTest: function() { - return qp.firstElementChild; - } - }); - if (!(origin = g.posts["" + boardID + "." + postID])) { - return; - } - if (Conf['Quote Highlighting']) { - posts = [origin].concat(origin.clones); - posts.pop(); - for (_i = 0, _len = posts.length; _i < _len; _i++) { - post = posts[_i]; - $.addClass(post.nodes.post, 'qphl'); - } - } - quoterID = $.x('ancestor::*[@id][1]', this).id.match(/\d+$/)[0]; - clone = Get.postFromRoot(qp.firstChild); - _ref1 = clone.nodes.quotelinks.concat(__slice.call(clone.nodes.backlinks)); - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - quote = _ref1[_j]; - if (quote.hash.slice(2) === quoterID) { - $.addClass(quote, 'forwardlink'); - } - } - }, - mouseout: function() { - var clone, post, root, _i, _len, _ref; - if (!(root = this.el.firstElementChild)) { - return; - } - clone = Get.postFromRoot(root); - post = clone.origin; - post.rmClone(root.dataset.clone); - if (!Conf['Quote Highlighting']) { - return; - } - _ref = [post].concat(post.clones); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - post = _ref[_i]; - $.rmClass(post.nodes.post, 'qphl'); - } - } - }; - - QuoteStrikeThrough = { - init: function() { - if (g.VIEW === 'catalog' || !Conf['Reply Hiding Buttons'] && !Conf['Reply Hiding Link'] && !Conf['Filter']) { - return; - } - return Post.callbacks.push({ - name: 'Strike-through Quotes', - cb: this.node - }); - }, - node: function() { - var boardID, postID, quotelink, _i, _len, _ref, _ref1, _ref2; - if (this.isClone) { - return; - } - _ref = this.nodes.quotelinks; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - quotelink = _ref[_i]; - _ref1 = Get.postDataFromLink(quotelink), boardID = _ref1.boardID, postID = _ref1.postID; - if ((_ref2 = g.posts["" + boardID + "." + postID]) != null ? _ref2.isHidden : void 0) { - $.addClass(quotelink, 'filtered'); - } - } - } - }; - - /* - <3 aeosynth - */ - - - QuoteThreading = { - init: function() { - var input; - if (!(Conf['Quote Threading'] && g.VIEW === 'thread')) { - return; - } - this.enabled = true; - this.controls = $.el('span', { - innerHTML: '' - }); - input = $('input', this.controls); - $.on(input, 'change', this.toggle); - $.event('AddMenuEntry', this.entry = { - type: 'header', - el: this.controls, - order: 98 - }); - if (!Conf['Unread Count']) { - $.on(d, '4chanXInitFinished', this.ready); - } - return Post.callbacks.push({ - name: 'Quote Threading', - cb: this.node - }); - }, - disconnect: function() { - var input; - if (!(Conf['Quote Threading'] && g.VIEW === 'thread')) { - return; - } - input = $('input', this.controls); - $.off(input, 'change', this.toggle); - $.event('rmMenuEntry', this.entry); - delete this.enabled; - delete this.controls; - delete this.entry; - return Post.callbacks.disconnect('Quote Threading'); - }, - ready: function() { - $.off(d, '4chanXInitFinished', QuoteThreading.ready); - return QuoteThreading.force(); - }, - force: function() { - g.posts.forEach(function(post) { - if (post.cb) { - return post.cb(true); - } - }); - if (Conf['Unread Count'] && Unread.thread.OP.nodes.root.parentElement.parentElement) { - Unread.read(); - return Unread.update(); - } - }, - node: function() { - var keys, len, posts, quote, _i, _len, _ref; - posts = g.posts; - if (this.isClone || !QuoteThreading.enabled) { - return; - } - if (Conf['Unread Count']) { - Unread.posts.push(this); - } - if (this.thread.OP === this || this.isHidden) { - return; - } - keys = []; - len = g.BOARD.ID.length + 1; - _ref = this.quotes; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - quote = _ref[_i]; - if ((quote.slice(len) < this.ID) && quote in posts) { - keys.push(quote); - } - } - if (keys.length !== 1) { - return; - } - this.threaded = keys[0]; - return this.cb = QuoteThreading.nodeinsert; - }, - nodeinsert: function(force) { - var bottom, height, post, posts, root, threadContainer, top, _ref; - post = g.posts[this.threaded]; - if (this.thread.OP === post) { - return false; - } - posts = Unread.posts; - root = post.nodes.root; - if (!force) { - height = doc.clientHeight; - _ref = root.getBoundingClientRect(), bottom = _ref.bottom, top = _ref.top; - if (!((Conf['Unread Count'] && posts[post.ID]) || ((bottom < height) && (top > 0)))) { - return false; - } - } - if ($.hasClass(root, 'threadOP')) { - threadContainer = root.nextElementSibling; - post = Get.postFromRoot($.x('descendant::div[contains(@class,"postContainer")][last()]', threadContainer)); - $.add(threadContainer, this.nodes.root); - } else { - threadContainer = $.el('div', { - className: 'threadContainer' - }); - $.add(threadContainer, this.nodes.root); - $.after(root, threadContainer); - $.addClass(root, 'threadOP'); - } - if (!Conf['Unread Count']) { - return true; - } - if (post = posts[post.ID]) { - posts.after(post, posts[this.ID]); - } else { - posts.prepend(posts[this.ID]); - } - return true; - }, - toggle: function() { - var container, containers, nodes, post, posts, thread, _i, _j, _k, _len, _len1, _len2, _ref; - if (QuoteThreading.enabled = this.checked) { - QuoteThreading.force(); - } else { - thread = $('.thread'); - posts = []; - nodes = []; - g.posts.forEach(function(post) { - if (!(post === post.thread.OP || post.isClone)) { - return posts.push(post); - } - }); - posts.sort(function(a, b) { - return a.ID - b.ID; - }); - for (_i = 0, _len = posts.length; _i < _len; _i++) { - post = posts[_i]; - nodes.push(post.nodes.root); - } - $.add(thread, nodes); - containers = $$('.threadContainer', thread); - for (_j = 0, _len1 = containers.length; _j < _len1; _j++) { - container = containers[_j]; - $.rm(container); - } - _ref = $$('.threadOP'); - for (_k = 0, _len2 = _ref.length; _k < _len2; _k++) { - post = _ref[_k]; - $.rmClass(post, 'threadOP'); - } - } - }, - kb: function() { - var control; - control = $.id('threadingControl'); - control.checked = !control.checked; - return QuoteThreading.toggle.call(control); - } - }; - - QuoteYou = { - init: function() { - if (!(g.VIEW !== 'catalog' && Conf['Mark Quotes of You'] && Conf['Quick Reply'])) { - return; - } - if (Conf['Highlight Own Posts']) { - $.addClass(doc, 'highlight-own'); - } - if (Conf['Highlight Posts Quoting You']) { - $.addClass(doc, 'highlight-you'); - } - if (Conf['Comment Expansion']) { - ExpandComment.callbacks.push(this.node); - } - this.text = '\u00A0(You)'; - return Post.callbacks.push({ - name: 'Mark Quotes of You', - cb: this.node - }); - }, - node: function() { - var quotelink, _i, _len, _ref; - if (this.isClone) { - return; - } - if (QR.db.get({ - boardID: this.board.ID, - threadID: this.thread.ID, - postID: this.ID - })) { - $.addClass(this.nodes.root, 'yourPost'); - } - if (!this.quotes.length) { - return; - } - _ref = this.nodes.quotelinks; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - quotelink = _ref[_i]; - if (!(QR.db.get(Get.postDataFromLink(quotelink)))) { - continue; - } - $.add(quotelink, $.tn(QuoteYou.text)); - $.addClass(quotelink, 'you'); - $.addClass(this.nodes.root, 'quotesYou'); - } - }, - cb: { - seek: function(type) { - var highlight, post, posts, result, str; - if (!(Conf['Mark Quotes of You'] && Conf['Quick Reply'])) { - return; - } - if (highlight = $('.highlight')) { - $.rmClass(highlight, 'highlight'); - } - if (!QuoteYou.lastRead) { - if (!(post = QuoteYou.lastRead = $('.quotesYou'))) { - new Notice('warning', 'No posts are currently quoting you, loser.', 20); - return; - } - if (QuoteYou.cb.scroll(post)) { - return; - } - } else { - post = QuoteYou.lastRead; - } - str = "" + type + "::div[contains(@class,'quotesYou')]"; - while (post = (result = $.X(str, post)).snapshotItem(type === 'preceding' ? result.snapshotLength - 1 : 0)) { - if (QuoteYou.cb.scroll(post)) { - return; - } - } - posts = $$('.quotesYou'); - return QuoteYou.cb.scroll(posts[type === 'following' ? 0 : posts.length - 1]); - }, - scroll: function(post) { - if (Get.postFromRoot(post).isHidden) { - return false; - } else { - QuoteYou.lastRead = post; - window.location = "#" + post.id; - Header.scrollToPost(post); - $.addClass($('.post', post), 'highlight'); - return true; - } - } - } - }; - - Quotify = { - init: function() { - if (g.VIEW === 'catalog' || !Conf['Resurrect Quotes']) { - return; - } - if (Conf['Comment Expansion']) { - ExpandComment.callbacks.push(this.node); - } - return Post.callbacks.push({ - name: 'Resurrect Quotes', - cb: this.node - }); - }, - node: function() { - var deadlink, _i, _len, _ref; - _ref = $$('.deadlink', this.nodes.comment); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - deadlink = _ref[_i]; - if (this.isClone) { - if ($.hasClass(deadlink, 'quotelink')) { - this.nodes.quotelinks.push(deadlink); - } - } else { - Quotify.parseDeadlink.call(this, deadlink); - } - } - }, - parseDeadlink: function(deadlink) { - var a, boardID, m, post, postID, quote, quoteID, redirect, _ref; - if ($.hasClass(deadlink.parentNode, 'prettyprint')) { - Quotify.fixDeadlink(deadlink); - return; - } - quote = deadlink.textContent; - if (!(postID = (_ref = quote.match(/\d+$/)) != null ? _ref[0] : void 0)) { - return; - } - if (postID[0] === '0') { - Quotify.fixDeadlink(deadlink); - return; - } - boardID = (m = quote.match(/^>>>\/([a-z\d]+)/)) ? m[1] : this.board.ID; - quoteID = "" + boardID + "." + postID; - if (post = g.posts[quoteID]) { - if (!post.isDead) { - a = $.el('a', { - href: "/" + boardID + "/res/" + post.thread + "#p" + postID, - className: 'quotelink', - textContent: quote - }); - } else { - a = $.el('a', { - href: "/" + boardID + "/res/" + post.thread + "#p" + postID, - className: 'quotelink deadlink', - target: '_blank', - textContent: "" + quote + "\u00A0(Dead)" - }); - $.extend(a.dataset, { - boardID: boardID, - threadID: post.thread.ID, - postID: postID - }); - } - } else if (redirect = Redirect.to('thread', { - boardID: boardID, - threadID: 0, - postID: postID - })) { - a = $.el('a', { - href: redirect, - className: 'deadlink', - target: '_blank', - textContent: "" + quote + "\u00A0(Dead)" - }); - if (Redirect.to('post', { - boardID: boardID, - postID: postID - })) { - $.addClass(a, 'quotelink'); - $.extend(a.dataset, { - boardID: boardID, - postID: postID - }); - } - } - if (__indexOf.call(this.quotes, quoteID) < 0) { - this.quotes.push(quoteID); - } - if (!a) { - return deadlink.textContent = "" + quote + "\u00A0(Dead)"; - } - $.replace(deadlink, a); - if ($.hasClass(a, 'quotelink')) { - return this.nodes.quotelinks.push(a); - } - }, - fixDeadlink: function(deadlink) { - var el, green; - if (!(el = deadlink.previousSibling) || el.nodeName === 'BR') { - green = $.el('span', { - className: 'quote' - }); - $.before(deadlink, green); - $.add(green, deadlink); - } - return $.replace(deadlink, __slice.call(deadlink.childNodes)); - } - }; - - QR = { - mimeTypes: ['image/jpeg', 'image/png', 'image/gif', 'application/pdf', 'application/x-shockwave-flash', ''], - init: function() { - var sc; - if (!Conf['Quick Reply']) { - return; - } - this.db = new DataBoard('yourPosts'); - this.posts = []; - if (Conf['QR Shortcut']) { - sc = $.el('a', { - className: "qr-shortcut fa fa-comment-o " + (!Conf['Persistent QR'] ? 'disabled' : ''), - textContent: 'QR', - title: 'Quick Reply', - href: 'javascript:;' - }); - $.on(sc, 'click', function() { - if (Conf['Persistent QR'] || !QR.nodes || QR.nodes.el.hidden) { - $.event('CloseMenu'); - QR.open(); - QR.nodes.com.focus(); - return $.rmClass(this, 'disabled'); - } else { - QR.close(); - return $.addClass(this, 'disabled'); - } - }); - Header.addShortcut(sc); - } - if (Conf['Hide Original Post Form']) { - $.asap((function() { - return doc; - }), function() { - return $.addClass(doc, 'hide-original-post-form'); - }); - } - $.ready(this.initReady); - if (Conf['Persistent QR']) { - if (!(g.BOARD.ID === 'f' && g.VIEW === 'index')) { - $.on(d, '4chanXInitFinished', this.persist); - } else { - $.ready(this.persist); - } - } - return Post.callbacks.push({ - name: 'Quick Reply', - cb: this.node - }); - }, - initReady: function() { - var link; - QR.postingIsEnabled = !!$.id('postForm'); - if (!QR.postingIsEnabled) { - return; - } - link = $.el('h1', { - innerHTML: "" + (g.VIEW === 'thread' ? 'Reply to Thread' : 'Start a Thread') + "", - className: "qr-link-container" - }); - QR.link = link.firstElementChild; - $.on(link.firstChild, 'click', function() { - $.event('CloseMenu'); - QR.open(); - QR.nodes.com.focus(); - if (Conf['QR Shortcut']) { - return $.rmClass($('.qr-shortcut'), 'disabled'); - } - }); - $.before($.id('postForm'), link); - $.on(d, 'QRGetSelectedPost', function(_arg) { - var cb; - cb = _arg.detail; - return cb(QR.selected); - }); - $.on(d, 'QRAddPreSubmitHook', function(_arg) { - var cb; - cb = _arg.detail; - return QR.preSubmitHooks.push(cb); - }); - $.on(d, 'dragover', QR.dragOver); - $.on(d, 'drop', QR.dropFile); - $.on(d, 'dragstart dragend', QR.drag); - return { - catalog: function() { - if (Conf["Persistent QR"]) { - QR.open(); - } - if (Conf['Auto Hide QR']) { - return QR.hide(); - } - }, - index: function() { - return $.on(d, 'IndexRefresh', QR.generatePostableThreadsList); - }, - thread: function() { - return $.on(d, 'ThreadUpdate', QR.statusCheck); - } - }[g.VIEW](); - }, - statusCheck: function() { - if (g.DEAD) { - return QR.abort(); - } else { - return QR.status(); - } - }, - node: function() { - return $.on($('a[title="Quote this post"]', this.nodes.info), 'click', QR.quote); - }, - persist: function() { - if (!QR.postingIsEnabled) { - return; - } - QR.open(); - if (Conf['Auto Hide QR'] || g.VIEW === 'catalog') { - return QR.hide(); - } - }, - open: function() { - var err; - if (QR.nodes) { - QR.nodes.el.hidden = false; - QR.unhide(); - return; - } - try { - return QR.dialog(); - } catch (_error) { - err = _error; - delete QR.nodes; - return Main.handleErrors({ - message: 'Quick Reply dialog creation crashed.', - error: err - }); - } - }, - close: function() { - var post, _i, _len, _ref; - if (QR.req) { - QR.abort(); - return; - } - QR.nodes.el.hidden = true; - QR.cleanNotifications(); - d.activeElement.blur(); - $.rmClass(QR.nodes.el, 'dump'); - if (!Conf['Captcha Warning Notifications']) { - if (QR.captcha.isEnabled) { - $.rmClass(QR.captcha.nodes.input, 'error'); - } - } - if (Conf['QR Shortcut']) { - $.toggleClass($('.qr-shortcut'), 'disabled'); - } - new QR.post(true); - _ref = QR.posts.splice(0, QR.posts.length - 1); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - post = _ref[_i]; - post["delete"](); - } - QR.cooldown.auto = false; - return QR.status(); - }, - focusin: function() { - return $.addClass(QR.nodes.el, 'focus'); - }, - focusout: function() { - return $.rmClass(QR.nodes.el, 'focus'); - }, - hide: function() { - d.activeElement.blur(); - $.addClass(QR.nodes.el, 'autohide'); - return QR.nodes.autohide.checked = true; - }, - unhide: function() { - $.rmClass(QR.nodes.el, 'autohide'); - return QR.nodes.autohide.checked = false; - }, - toggleHide: function() { - if (this.checked) { - return QR.hide(); - } else { - return QR.unhide(); - } - }, - error: function(err) { - var el; - QR.open(); - if (typeof err === 'string') { - el = $.tn(err); - } else { - el = err; - el.removeAttribute('style'); - } - if (QR.captcha.isEnabled && /captcha|verification/i.test(el.textContent)) { - QR.captcha.nodes.input.focus(); - if (Conf['Captcha Warning Notifications'] && !d.hidden) { - QR.notify(el); - } else { - $.addClass(QR.captcha.nodes.input, 'error'); - $.on(QR.captcha.nodes.input, 'keydown', function() { - return $.rmClass(QR.captcha.nodes.input, 'error'); - }); - } - } else { - QR.notify(el); - } - if (d.hidden) { - return alert(el.textContent); - } - }, - notify: function(el) { - var notice, notif; - notice = new Notice('warning', el); - if (!(Header.areNotificationsEnabled && d.hidden)) { - return QR.notifications.push(notice); - } else { - notif = new Notification(el.textContent, { - body: el.textContent, - icon: Favicon.logo - }); - return notif.onclick = function() { - return window.focus(); - }; - } - }, - notifications: [], - cleanNotifications: function() { - var notification, _i, _len, _ref; - _ref = QR.notifications; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - notification = _ref[_i]; - notification.close(); - } - return QR.notifications = []; - }, - status: function() { - var disabled, status, thread, value; - if (!QR.nodes) { - return; - } - thread = QR.posts[0].thread; - if (thread !== 'new' && g.threads["" + g.BOARD + "." + thread].isDead) { - value = 404; - disabled = true; - QR.cooldown.auto = false; - } - value = QR.req ? QR.req.progress : QR.cooldown.seconds || value; - status = QR.nodes.status; - status.value = !value ? 'Submit' : QR.cooldown.auto ? "Auto " + value : value; - return status.disabled = disabled || false; - }, - quote: function(e) { - var caretPos, com, index, post, range, s, sel, text, thread, _ref; - if (e != null) { - e.preventDefault(); - } - if (!QR.postingIsEnabled) { - return; - } - sel = d.getSelection(); - post = Get.postFromNode(this); - text = ">>" + post + "\n"; - if ((s = sel.toString().trim()) && post === Get.postFromNode(sel.anchorNode)) { - s = s.replace(/\n/g, '\n>'); - text += ">" + s + "\n"; - } - QR.open(); - if (QR.selected.isLocked) { - index = QR.posts.indexOf(QR.selected); - (QR.posts[index + 1] || new QR.post()).select(); - $.addClass(QR.nodes.el, 'dump'); - QR.cooldown.auto = true; - } - _ref = QR.nodes, com = _ref.com, thread = _ref.thread; - if (!com.value) { - thread.value = Get.threadFromNode(this); - } - caretPos = com.selectionStart; - com.value = com.value.slice(0, caretPos) + text + com.value.slice(com.selectionEnd); - range = caretPos + text.length; - com.setSelectionRange(range, range); - com.focus(); - QR.selected.save(com); - QR.selected.save(thread); - if (Conf['QR Shortcut']) { - return $.rmClass($('.qr-shortcut'), 'disabled'); - } - }, - characterCount: function() { - var count, counter; - counter = QR.nodes.charCount; - count = QR.nodes.com.textLength; - counter.textContent = count; - counter.hidden = count < 1000; - return (count > 1500 ? $.addClass : $.rmClass)(counter, 'warning'); - }, - drag: function(e) { - var toggle; - toggle = e.type === 'dragstart' ? $.off : $.on; - toggle(d, 'dragover', QR.dragOver); - return toggle(d, 'drop', QR.dropFile); - }, - dragOver: function(e) { - e.preventDefault(); - return e.dataTransfer.dropEffect = 'copy'; - }, - dropFile: function(e) { - if (!e.dataTransfer.files.length) { - return; - } - e.preventDefault(); - QR.open(); - return QR.handleFiles(e.dataTransfer.files); - }, - paste: function(e) { - var blob, files, item, _i, _len, _ref; - files = []; - _ref = e.clipboardData.items; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - item = _ref[_i]; - if (!(item.kind === 'file')) { - continue; - } - blob = item.getAsFile(); - blob.name = 'file'; - if (blob.type) { - blob.name += '.' + blob.type.split('/')[1]; - } - files.push(blob); - } - if (!files.length) { - return; - } - QR.open(); - QR.handleFiles(files); - return $.addClass(QR.nodes.el, 'dump'); - }, - handleBlob: function(urlBlob, header, url) { - var blob, end, endnl, endsc, mime, name, name_end, name_start, start, _ref; - name = url.substr(url.lastIndexOf('/') + 1, url.length); - start = header.indexOf("Content-Type: ") + 14; - endsc = header.substr(start, header.length).indexOf(";"); - endnl = header.substr(start, header.length).indexOf("\n") - 1; - end = endnl; - if (endsc !== -1 && endsc < endnl) { - end = endsc; - } - mime = header.substr(start, end); - blob = new Blob([urlBlob], { - type: mime - }); - blob.name = url.substr(url.lastIndexOf('/') + 1, url.length); - name_start = header.indexOf('name="') + 6; - if (name_start - 6 !== -1) { - name_end = header.substr(name_start, header.length).indexOf('"'); - blob.name = header.substr(name_start, name_end); - } - if (blob.type === null) { - return QR.error("Unsupported file type."); - } - if (_ref = blob.type, __indexOf.call(QR.mimeTypes, _ref) < 0) { - return QR.error("Unsupported file type."); - } - return QR.handleFiles([blob]); - }, - handleUrl: function() { - var url; - url = prompt("Insert an url:"); - if (url === null) { - return; - } - GM_xmlhttpRequest({ - method: "GET", - url: url, - overrideMimeType: "text/plain; charset=x-user-defined", - onload: function(xhr) { - var data, i, r; - r = xhr.responseText; - data = new Uint8Array(r.length); - i = 0; - while (i < r.length) { - data[i] = r.charCodeAt(i); - i++; - } - QR.handleBlob(data, xhr.responseHeaders, url); - return; - return { - onerror: function(xhr) { - return QR.error("Can't load image."); - } - }; - } - }); - }, - handleFiles: function(files) { - var file, isSingle, max, _i, _len; - if (this !== QR) { - files = __slice.call(this.files); - this.value = null; - } - if (!files.length) { - return; - } - max = QR.nodes.fileInput.max; - isSingle = files.length === 1; - QR.cleanNotifications(); - for (_i = 0, _len = files.length; _i < _len; _i++) { - file = files[_i]; - QR.checkDimensions(file, isSingle, max); - } - if (!isSingle) { - return $.addClass(QR.nodes.el, 'dump'); - } - }, - checkDimensions: function(file, isSingle, max) { - var img, - _this = this; - img = new Image(); - img.onload = function() { - var height, width; - height = img.height, width = img.width; - if (height > QR.max_heigth || width > QR.max_heigth) { - return QR.error("" + file.name + ": Image too large (image: " + img.height + "x" + img.width + "px, max: " + QR.max_heigth + "x" + QR.max_width + "px)"); - } - if (height < QR.min_heigth || width < QR.min_heigth) { - return QR.error("" + file.name + ": Image too small (image: " + img.height + "x" + img.width + "px, min: " + QR.min_heigth + "x" + QR.min_width + "px)"); - } - return QR.handleFile(file, isSingle, max); - }; - return img.src = URL.createObjectURL(file); - }, - handleFile: function(file, isSingle, max) { - var post, _ref; - if (file.size > max) { - QR.error("" + file.name + ": File too large (file: " + ($.bytesToString(file.size)) + ", max: " + ($.bytesToString(max)) + ")."); - return; - } else if (_ref = file.type, __indexOf.call(QR.mimeTypes, _ref) < 0) { - if (!/^text/.test(file.type)) { - QR.error("" + file.name + ": Unsupported file type."); - return; - } - if (isSingle) { - post = QR.selected; - } else if ((post = QR.posts[QR.posts.length - 1]).com) { - post = new QR.post(); - } - post.pasteText(file); - return; - } - if (isSingle) { - post = QR.selected; - } else if ((post = QR.posts[QR.posts.length - 1]).file) { - post = new QR.post(); - } - return post.setFile(file); - }, - openFileInput: function(e) { - var _ref; - e.stopPropagation(); - if (e.shiftKey && e.type === 'click') { - return QR.selected.rmFile(); - } - if (e.ctrlKey && e.type === 'click') { - $.addClass(QR.nodes.filename, 'edit'); - QR.nodes.filename.focus(); - return $.on(QR.nodes.filename, 'blur', function() { - return $.rmClass(QR.nodes.filename, 'edit'); - }); - } - if (e.target.nodeName === 'INPUT' || (e.keyCode && ((_ref = e.keyCode) !== 32 && _ref !== 13)) || e.ctrlKey) { - return; - } - e.preventDefault(); - return QR.nodes.fileInput.click(); - }, - generatePostableThreadsList: function() { - var list, options, thread, val, _i, _len, _ref; - if (!QR.nodes) { - return; - } - list = QR.nodes.thread; - options = [list.firstChild]; - _ref = g.BOARD.threads.keys; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - thread = _ref[_i]; - options.push($.el('option', { - value: thread, - textContent: "Thread No." + thread - })); - } - val = list.value; - $.rmAll(list); - $.add(list, options); - list.value = val; - if (!list.value) { - return; - } - return list.value = g.VIEW === 'thread' ? g.THREADID : 'new'; - }, - dialog: function() { - var dialog, elm, event, i, items, key, max_heigth, max_width, min_heigth, min_width, name, node, nodes, rules, save, tmp_dim, value, _ref; - QR.nodes = nodes = { - el: dialog = UI.dialog('qr', 'top:0;right:0;', "
×
No selected file
") - }; - _ref = { - move: '.move', - autohide: '#autohide', - thread: 'select', - threadPar: '#qr-thread-select', - close: '.close', - form: 'form', - dumpButton: '#dump-button', - urlButton: '#url-button', - name: '[data-name=name]', - email: '[data-name=email]', - sub: '[data-name=sub]', - com: '[data-name=com]', - dumpList: '#dump-list', - addPost: '#add-post', - charCount: '#char-count', - fileSubmit: '#file-n-submit', - filename: '#qr-filename', - fileContainer: '#qr-filename-container', - fileRM: '#qr-filerm', - fileExtras: '#qr-extras-container', - spoiler: '#qr-file-spoiler', - spoilerPar: '#qr-spoiler-label', - status: '[type=submit]', - fileInput: '[type=file]' - }; - for (key in _ref) { - value = _ref[key]; - nodes[key] = $(value, dialog); - } - rules = $('ul.rules').textContent.trim(); - try { - tmp_dim = rules.match(/.+smaller than (\d+)x(\d+).+/); - min_width = tmp_dim[1]; - min_heigth = tmp_dim[2]; - } catch (_error) { - min_width = 1; - min_heigth = 1; - } - try { - tmp_dim = rules.match(/.+greater than (\d+)x(\d+).+/); - max_width = tmp_dim[1]; - max_heigth = tmp_dim[2]; - } catch (_error) { - QR.max_width = 5000; - QR.max_heigth = 5000; - } - nodes.fileInput.max = $('input[name=MAX_FILE_SIZE]').value; - QR.spoiler = !!$('input[name=spoiler]'); - if (QR.spoiler) { - $.addClass(QR.nodes.el, 'has-spoiler'); - } else { - nodes.spoiler.parentElement.hidden = true; - } - if (g.BOARD.ID === 'f') { - nodes.flashTag = $.el('select', { - name: 'filetag', - innerHTML: "\n\n\n\n\n\n" - }); - nodes.flashTag.dataset["default"] = '4'; - $.add(nodes.form, nodes.flashTag); - } - QR.flagsInput(); - $.on(nodes.filename.parentNode, 'click keydown', QR.openFileInput); - items = $$('*', QR.nodes.el); - i = 0; - while (elm = items[i++]) { - $.on(elm, 'blur', QR.focusout); - $.on(elm, 'focus', QR.focusin); - } - $.on(nodes.autohide, 'change', QR.toggleHide); - $.on(nodes.close, 'click', QR.close); - $.on(nodes.dumpButton, 'click', function() { - return nodes.el.classList.toggle('dump'); - }); - $.on(nodes.urlButton, 'click', QR.handleUrl); - $.on(nodes.addPost, 'click', function() { - return new QR.post(true); - }); - $.on(nodes.form, 'submit', QR.submit); - $.on(nodes.fileRM, 'click', function() { - return QR.selected.rmFile(); - }); - $.on(nodes.fileExtras, 'click', function(e) { - return e.stopPropagation(); - }); - $.on(nodes.spoiler, 'change', function() { - return QR.selected.nodes.spoiler.click(); - }); - $.on(nodes.fileInput, 'change', QR.handleFiles); - items = ['name', 'email', 'sub', 'com', 'filename', 'flag']; - i = 0; - save = function() { - return QR.selected.save(this); - }; - while (name = items[i++]) { - if (!(node = nodes[name])) { - continue; - } - event = node.nodeName === 'SELECT' ? 'change' : 'input'; - $.on(nodes[name], event, save); - } - if (Conf['Remember QR Size']) { - $.get('QR Size', '', function(item) { - return nodes.com.style.cssText = item['QR Size']; - }); - $.on(nodes.com, 'mouseup', function(e) { - if (e.button !== 0) { - return; - } - return $.set('QR Size', this.style.cssText); - }); - } - QR.generatePostableThreadsList(); - QR.persona.init(); - new QR.post(true); - QR.status(); - QR.cooldown.init(); - QR.captcha.init(); - $.add(d.body, dialog); - return $.event('QRDialogCreation', null, dialog); - }, - flags: function() { - var flag, fn, select, _i, _len, _ref; - fn = function(val) { - return $.el('option', { - value: val[0], - textContent: val[1] - }); - }; - select = $.el('select', { - name: 'flag', - className: 'flagSelector' - }); - _ref = [['0', 'None'], ['US', 'American'], ['KP', 'Best Korean'], ['BL', 'Black Nationalist'], ['CM', 'Communist'], ['CF', 'Confederate'], ['RE', 'Conservative'], ['EU', 'European'], ['GY', 'Gay'], ['PC', 'Hippie'], ['IL', 'Israeli'], ['DM', 'Liberal'], ['RP', 'Libertarian'], ['MF', 'Muslim'], ['NZ', 'Nazi'], ['OB', 'Obama'], ['PR', 'Pirate'], ['RB', 'Rebel'], ['TP', 'Tea Partier'], ['TX', 'Texan'], ['TR', 'Tree Hugger'], ['WP', 'White Supremacist']]; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - flag = _ref[_i]; - $.add(select, fn(flag)); - } - return select; - }, - flagsInput: function() { - var flag, nodes; - nodes = QR.nodes; - if (nodes.flagSelector) { - $.rm(nodes.flagSelector); - delete nodes.flagSelector; - } - if (g.BOARD.ID === 'pol') { - flag = QR.flags(); - flag.dataset.name = 'flag'; - flag.dataset["default"] = '0'; - nodes.flag = flag; - return $.add(nodes.form, flag); - } - }, - preSubmitHooks: [], - submit: function(e) { - var challenge, err, extra, filetag, formData, hook, options, post, response, textOnly, thread, threadID, _i, _len, _ref, _ref1; - if (e != null) { - e.preventDefault(); - } - if (QR.req) { - QR.abort(); - return; - } - if (QR.cooldown.seconds) { - QR.cooldown.auto = !QR.cooldown.auto; - QR.status(); - return; - } - post = QR.posts[0]; - post.forceSave(); - if (g.BOARD.ID === 'f') { - filetag = QR.nodes.flashTag.value; - } - threadID = post.thread; - thread = g.BOARD.threads[threadID]; - if (threadID === 'new') { - threadID = null; - if (g.BOARD.ID === 'vg' && !post.sub) { - err = 'New threads require a subject.'; - } else if (!(post.file || (textOnly = !!$('input[name=textonly]', $.id('postForm'))))) { - err = 'No file selected.'; - } - } else if (g.BOARD.threads[threadID].isClosed) { - err = 'You can\'t reply to this thread anymore.'; - } else if (!(post.com || post.file)) { - err = 'No file selected.'; - } else if (post.file && thread.fileLimit) { - err = 'Max limit of image replies has been reached.'; - } else { - _ref = QR.preSubmitHooks; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - hook = _ref[_i]; - if (err = hook(post, thread)) { - break; - } - } - } - if (QR.captcha.isEnabled && !err) { - _ref1 = QR.captcha.getOne(), challenge = _ref1.challenge, response = _ref1.response; - if (!response) { - err = 'No valid captcha.'; - } - } - QR.cleanNotifications(); - if (err) { - QR.cooldown.auto = false; - QR.status(); - QR.error(err); - return; - } - QR.cooldown.auto = QR.posts.length > 1; - if (Conf['Auto Hide QR'] && !QR.cooldown.auto) { - QR.hide(); - } - if (!QR.cooldown.auto && $.x('ancestor::div[@id="qr"]', d.activeElement)) { - d.activeElement.blur(); - } - post.lock(); - formData = { - resto: threadID, - name: post.name, - email: post.email, - sub: post.sub, - com: post.com, - upfile: post.file, - filetag: filetag, - spoiler: post.spoiler, - flag: post.flag, - textonly: textOnly, - mode: 'regist', - pwd: QR.persona.pwd, - recaptcha_challenge_field: challenge, - recaptcha_response_field: response - }; - options = { - responseType: 'document', - withCredentials: true, - onload: QR.response, - onerror: function(err, url, line) { - delete QR.req; - post.unlock(); - QR.cooldown.auto = false; - QR.status(); - console.log(err); - console.log(url); - console.log(line); - return QR.error($.el('span', { - innerHTML: "4chan X encountered an error while posting. \n[Banned?] [More info]" - })); - } - }; - extra = { - form: $.formData(formData), - upCallbacks: { - onload: function() { - QR.req.isUploadFinished = true; - QR.req.uploadEndTime = Date.now(); - QR.req.progress = '...'; - return QR.status(); - }, - onprogress: function(e) { - QR.req.progress = "" + (Math.round(e.loaded / e.total * 100)) + "%"; - return QR.status(); - } - } - }; - QR.req = $.ajax("https://sys.4chan.org/" + g.BOARD + "/post", options, extra); - QR.req.uploadStartTime = Date.now(); - QR.req.progress = '...'; - return QR.status(); - }, - response: function() { - var URL, ban, board, captchasCount, err, h1, isReply, m, notif, post, postID, postsCount, req, resDoc, threadID, _, _ref, _ref1; - req = QR.req; - delete QR.req; - post = QR.posts[0]; - post.unlock(); - resDoc = req.response; - if (ban = $('.banType', resDoc)) { - board = $('.board', resDoc).innerHTML; - err = $.el('span', { - innerHTML: ban.textContent.toLowerCase() === 'banned' ? "You are banned on " + board + "! ;_;
\nClick here to see the reason." : "You were issued a warning on " + board + " as " + ($('.nameBlock', resDoc).innerHTML) + ".
\nReason: " + ($('.reason', resDoc).innerHTML) - }); - } else if (err = resDoc.getElementById('errmsg')) { - if ((_ref = $('a', err)) != null) { - _ref.target = '_blank'; - } - } else if (resDoc.title !== 'Post successful!') { - err = 'Connection error with sys.4chan.org.'; - } else if (req.status !== 200) { - err = "Error " + req.statusText + " (" + req.status + ")"; - } - if (err) { - if (/captcha|verification/i.test(err.textContent) || err === 'Connection error with sys.4chan.org.') { - if (/mistyped/i.test(err.textContent)) { - err = 'You seem to have mistyped the CAPTCHA.'; - } else if (/expired/i.test(err.textContent)) { - err = 'This CAPTCHA is no longer valid because it has expired.'; - } - QR.cooldown.auto = QR.captcha.isEnabled ? !!QR.captcha.captchas.length : err === 'Connection error with sys.4chan.org.' ? true : false; - QR.cooldown.set({ - delay: 2 - }); - } else if (err.textContent && (m = err.textContent.match(/wait\s+(\d+)\s+second/i))) { - QR.cooldown.auto = QR.captcha.isEnabled ? !!QR.captcha.captchas.length : true; - QR.cooldown.set({ - delay: m[1] - }); - } else { - QR.cooldown.auto = false; - } - QR.status(); - QR.error(err); - return; - } - h1 = $('h1', resDoc); - QR.cleanNotifications(); - if (Conf['Posting Success Notifications']) { - QR.notifications.push(new Notice('success', h1.textContent, 5)); - } - QR.persona.set(post); - _ref1 = h1.nextSibling.textContent.match(/thread:(\d+),no:(\d+)/), _ = _ref1[0], threadID = _ref1[1], postID = _ref1[2]; - postID = +postID; - threadID = +threadID || postID; - isReply = threadID !== postID; - QR.db.set({ - boardID: g.BOARD.ID, - threadID: threadID, - postID: postID, - val: true - }); - ThreadUpdater.postID = postID; - $.event('QRPostSuccessful', { - board: g.BOARD, - threadID: threadID, - postID: postID - }); - $.event('QRPostSuccessful_', { - threadID: threadID, - postID: postID - }); - postsCount = QR.posts.length - 1; - QR.cooldown.auto = postsCount && isReply; - if (QR.cooldown.auto && QR.captcha.isEnabled && (captchasCount = QR.captcha.captchas.length) < 3 && captchasCount < postsCount) { - notif = new Notification('Quick reply warning', { - body: "You are running low on cached captchas. Cache count: " + captchasCount + ".", - icon: Favicon.logo - }); - notif.onclick = function() { - QR.open(); - QR.captcha.nodes.input.focus(); - return window.focus(); - }; - notif.onshow = function() { - return setTimeout(function() { - return notif.close(); - }, 7 * $.SECOND); - }; - } - if (!(Conf['Persistent QR'] || QR.cooldown.auto)) { - QR.close(); - } else { - post.rm(); - } - QR.cooldown.set({ - req: req, - post: post, - isReply: isReply, - threadID: threadID - }); - URL = threadID === postID ? "/" + g.BOARD + "/res/" + threadID : g.VIEW === 'index' && !QR.cooldown.auto && Conf['Open Post in New Tab'] ? "/" + g.BOARD + "/res/" + threadID + "#p" + postID : void 0; - if (URL) { - if (Conf['Open Post in New Tab']) { - $.open(URL); - } else { - window.location = URL; - } - } - return QR.status(); - }, - abort: function() { - if (QR.req && !QR.req.isUploadFinished) { - QR.req.abort(); - delete QR.req; - QR.posts[0].unlock(); - QR.cooldown.auto = false; - QR.notifications.push(new Notice('info', 'QR upload aborted.', 5)); - } - return QR.status(); - } - }; - - QR.captcha = { - init: function() { - var container, imgContainer, input; - if (d.cookie.indexOf('pass_enabled=1') >= 0) { - return; - } - container = $.id('captchaContainer'); - if (!(this.isEnabled = !!container)) { - return; - } - if (Conf['Auto-load captcha']) { - $.globalEval('loadRecaptcha()'); - } - imgContainer = $.el('div', { - className: 'captcha-img', - title: 'Reload reCAPTCHA', - innerHTML: '', - hidden: true - }); - input = $.el('input', { - className: 'captcha-input field', - title: 'Verification', - placeholder: 'Focus to load reCAPTCHA', - autocomplete: 'off', - spellcheck: false, - tabIndex: 45 - }); - this.nodes = { - img: imgContainer.firstChild, - input: input - }; - $.on(input, 'focus', this.setup); - $.on(input, 'blur', QR.focusout); - $.on(input, 'focus', QR.focusin); - $.addClass(QR.nodes.el, 'has-captcha'); - $.after(QR.nodes.com.parentNode, [imgContainer, input]); - this.setupObserver = new MutationObserver(this.afterSetup); - this.setupObserver.observe(container, { - childList: true - }); - return this.afterSetup(); - }, - setup: function() { - return $.globalEval('loadRecaptcha()'); - }, - afterSetup: function() { - var challenge, img, input, setLifetime, _ref; - if (!(challenge = $.id('recaptcha_challenge_field_holder'))) { - return; - } - QR.captcha.setupObserver.disconnect(); - delete QR.captcha.setupObserver; - setLifetime = function(e) { - return QR.captcha.lifetime = e.detail; - }; - $.on(window, 'captcha:timeout', setLifetime); - $.globalEval('window.dispatchEvent(new CustomEvent("captcha:timeout", {detail: RecaptchaState.timeout}))'); - $.off(window, 'captcha:timeout', setLifetime); - _ref = QR.captcha.nodes, img = _ref.img, input = _ref.input; - img.parentNode.hidden = false; - $.off(input, 'focus', QR.captcha.setup); - $.on(input, 'keydown', QR.captcha.keydown.bind(QR.captcha)); - $.on(img.parentNode, 'click', QR.captcha.reload.bind(QR.captcha)); - $.get('captchas', [], function(_arg) { - var captchas; - captchas = _arg.captchas; - return QR.captcha.sync(captchas); - }); - $.sync('captchas', QR.captcha.sync); - QR.captcha.nodes.challenge = challenge; - new MutationObserver(QR.captcha.load.bind(QR.captcha)).observe(challenge, { - childList: true - }); - return QR.captcha.load(); - }, - sync: function(captchas) { - QR.captcha.captchas = captchas; - return QR.captcha.count(); - }, - getOne: function() { - var captcha, challenge, response; - this.clear(); - if (captcha = this.captchas.shift()) { - challenge = captcha.challenge, response = captcha.response; - this.count(); - $.set('captchas', this.captchas); - } else { - challenge = this.nodes.img.alt; - if (response = this.nodes.input.value) { - this.reload(); - } - } - if (response) { - response = response.trim(); - if (!/\s/.test(response)) { - response = "" + response + " " + response; - } - } - return { - challenge: challenge, - response: response - }; - }, - save: function() { - var response; - if (!(response = this.nodes.input.value.trim())) { - return; - } - this.captchas.push({ - challenge: this.nodes.img.alt, - response: response, - timeout: this.timeout - }); - this.count(); - this.reload(); - return $.set('captchas', this.captchas); - }, - clear: function() { - var captcha, i, now, _i, _len, _ref; - if (!this.captchas.length) { - return; - } - now = Date.now(); - _ref = this.captchas; - for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { - captcha = _ref[i]; - if (captcha.timeout > now) { - break; - } - } - if (!i) { - return; - } - this.captchas = this.captchas.slice(i); - this.count(); - return $.set('captchas', this.captchas); - }, - load: function() { - var challenge; - if (!this.nodes.challenge.firstChild) { - return; - } - this.timeout = Date.now() + this.lifetime * $.SECOND - $.MINUTE; - challenge = this.nodes.challenge.firstChild.value; - this.nodes.img.alt = challenge; - this.nodes.img.src = "//www.google.com/recaptcha/api/image?c=" + challenge; - this.nodes.input.value = null; - return this.clear(); - }, - count: function() { - var count; - count = this.captchas ? this.captchas.length : 0; - this.nodes.input.placeholder = (function() { - switch (count) { - case 0: - return 'Verification (Shift + Enter to cache)'; - case 1: - return 'Verification (1 cached captcha)'; - default: - return "Verification (" + count + " cached captchas)"; - } - })(); - return this.nodes.input.alt = count; - }, - reload: function(focus) { - $.globalEval('Recaptcha.reload("t")'); - if (focus) { - return this.nodes.input.focus(); - } - }, - keydown: function(e) { - if (e.keyCode === 8 && !this.nodes.input.value) { - this.reload(); - } else if (e.keyCode === 13 && e.shiftKey) { - this.save(); - } else { - return; - } - return e.preventDefault(); - } - }; - - QR.cooldown = { - init: function() { - var key, setTimers, type, - _this = this; - if (!Conf['Cooldown']) { - return; - } - setTimers = function(e) { - return QR.cooldown.types = e.detail; - }; - $.on(window, 'cooldown:timers', setTimers); - $.globalEval('window.dispatchEvent(new CustomEvent("cooldown:timers", {detail: cooldowns}))'); - $.off(window, 'cooldown:timers', setTimers); - for (type in QR.cooldown.types) { - QR.cooldown.types[type] = +QR.cooldown.types[type]; - } - QR.cooldown.upSpd = 0; - QR.cooldown.upSpdAccuracy = .5; - key = "cooldown." + g.BOARD; - $.get(key, {}, function(item) { - QR.cooldown.cooldowns = item[key]; - return QR.cooldown.start(); - }); - return $.sync(key, QR.cooldown.sync); - }, - start: function() { - if (!Conf['Cooldown']) { - return; - } - if (QR.cooldown.isCounting) { - return; - } - QR.cooldown.isCounting = true; - return QR.cooldown.count(); - }, - sync: function(cooldowns) { - var id; - for (id in cooldowns) { - QR.cooldown.cooldowns[id] = cooldowns[id]; - } - return QR.cooldown.start(); - }, - set: function(data) { - var cooldown, delay, isReply, post, req, start, threadID, upSpd; - if (!Conf['Cooldown']) { - return; - } - req = data.req, post = data.post, isReply = data.isReply, threadID = data.threadID, delay = data.delay; - start = req ? req.uploadEndTime : Date.now(); - if (delay) { - cooldown = { - delay: delay - }; - } else { - if (post.file) { - upSpd = post.file.size / ((start - req.uploadStartTime) / $.SECOND); - QR.cooldown.upSpdAccuracy = ((upSpd > QR.cooldown.upSpd * .9) + QR.cooldown.upSpdAccuracy) / 2; - QR.cooldown.upSpd = upSpd; - } - cooldown = { - isReply: isReply, - threadID: threadID - }; - } - QR.cooldown.cooldowns[start] = cooldown; - $.set("cooldown." + g.BOARD, QR.cooldown.cooldowns); - return QR.cooldown.start(); - }, - unset: function(id) { - delete QR.cooldown.cooldowns[id]; - if (Object.keys(QR.cooldown.cooldowns).length) { - return $.set("cooldown." + g.BOARD, QR.cooldown.cooldowns); - } else { - return $["delete"]("cooldown." + g.BOARD); - } - }, - count: function() { - var cooldown, cooldowns, elapsed, hasFile, isReply, maxTimer, now, post, seconds, start, type, types, upSpd, upSpdAccuracy, update, _ref; - if (!Object.keys(QR.cooldown.cooldowns).length) { - $["delete"]("" + g.BOARD + ".cooldown"); - delete QR.cooldown.isCounting; - delete QR.cooldown.seconds; - QR.status(); - return; - } - clearTimeout(QR.cooldown.timeout); - QR.cooldown.timeout = setTimeout(QR.cooldown.count, $.SECOND); - now = Date.now(); - post = QR.posts[0]; - isReply = post.thread !== 'new'; - hasFile = !!post.file; - seconds = null; - _ref = QR.cooldown, types = _ref.types, cooldowns = _ref.cooldowns, upSpd = _ref.upSpd, upSpdAccuracy = _ref.upSpdAccuracy; - for (start in cooldowns) { - cooldown = cooldowns[start]; - if ('delay' in cooldown) { - if (cooldown.delay) { - seconds = Math.max(seconds, cooldown.delay--); - } else { - seconds = Math.max(seconds, 0); - QR.cooldown.unset(start); - } - continue; - } - if (isReply === cooldown.isReply) { - elapsed = Math.floor((now - start) / $.SECOND); - if (elapsed < 0) { - continue; - } - type = !isReply ? 'thread' : hasFile ? 'image' : 'reply'; - maxTimer = Math.max(types[type] || 0, types[type + '_intra'] || 0); - if (!((start <= now && now <= start + maxTimer * $.SECOND))) { - QR.cooldown.unset(start); - } - if (isReply && +post.thread === cooldown.threadID) { - type += '_intra'; - } - seconds = Math.max(seconds, types[type] - elapsed); - } - } - if (seconds && Conf['Cooldown Prediction'] && hasFile && upSpd) { - seconds -= Math.floor(post.file.size / upSpd * upSpdAccuracy); - seconds = seconds > 0 ? seconds : 0; - } - update = seconds !== null || !!QR.cooldown.seconds; - QR.cooldown.seconds = seconds; - if (update) { - QR.status(); - } - if (seconds === 0 && QR.cooldown.auto && !QR.req) { - return QR.submit(); - } - } - }; - - QR.persona = { - pwd: '', - always: {}, - init: function() { - QR.persona.getPassword(); - return $.get('QR.personas', Conf['QR.personas'], function(_arg) { - var arr, item, personas, type, types, _i, _len, _ref; - personas = _arg['QR.personas']; - types = { - name: [], - email: [], - sub: [] - }; - _ref = personas.split('\n'); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - item = _ref[_i]; - QR.persona.parseItem(item.trim(), types); - } - for (type in types) { - arr = types[type]; - QR.persona.loadPersonas(type, arr); - } - }); - }, - parseItem: function(item, types) { - var boards, match, type, val, _ref, _ref1, _ref2; - if (item[0] === '#') { - return; - } - if (!(match = item.match(/(name|email|subject|password):"(.*)"/i))) { - return; - } - _ref = match, match = _ref[0], type = _ref[1], val = _ref[2]; - item = item.replace(match, ''); - boards = ((_ref1 = item.match(/boards:([^;]+)/i)) != null ? _ref1[1].toLowerCase() : void 0) || 'global'; - if (boards !== 'global' && (_ref2 = g.BOARD.ID, __indexOf.call(boards.split(','), _ref2) < 0)) { - return; - } - if (type === 'password') { - QR.persona.pwd = val; - return; - } - if (type === 'subject') { - type = 'sub'; - } - if (/always/i.test(item)) { - QR.persona.always[type] = val; - } - if (__indexOf.call(types[type], val) < 0) { - return types[type].push(val); - } - }, - loadPersonas: function(type, arr) { - var list, val, _i, _len; - list = $("#list-" + type, QR.nodes.el); - for (_i = 0, _len = arr.length; _i < _len; _i++) { - val = arr[_i]; - if (val) { - $.add(list, $.el('option', { - textContent: val - })); - } - } - }, - getPassword: function() { - var input, m; - if (!QR.persona.pwd) { - QR.persona.pwd = (m = d.cookie.match(/4chan_pass=([^;]+)/)) ? decodeURIComponent(m[1]) : (input = $.id('postPassword')) ? input.value : $.id('delPassword').value; - } - return QR.persona.pwd; - }, - get: function(cb) { - return $.get('QR.persona', {}, function(_arg) { - var persona; - persona = _arg['QR.persona']; - return cb(persona); - }); - }, - set: function(post) { - return $.get('QR.persona', {}, function(_arg) { - var persona; - persona = _arg['QR.persona']; - persona = { - name: post.name, - email: /^sage$/.test(post.email) ? persona.email : post.email, - sub: Conf['Remember Subject'] ? post.sub : void 0, - flag: post.flag - }; - return $.set('QR.persona', persona); - }); - } - }; - - QR.post = (function() { - function _Class(select) { - this.select = __bind(this.select, this); - var el, elm, event, prev, _i, _j, _len, _len1, _ref, _ref1, - _this = this; - el = $.el('a', { - className: 'qr-preview', - draggable: true, - href: 'javascript:;', - innerHTML: '' - }); - this.nodes = { - el: el, - rm: el.firstChild, - label: $('label', el), - spoiler: $('input', el), - span: el.lastChild - }; - _ref = $$('*', el); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - elm = _ref[_i]; - $.on(elm, 'blur', QR.focusout); - $.on(elm, 'focus', QR.focusin); - } - $.on(el, 'click', this.select); - $.on(this.nodes.rm, 'click', function(e) { - e.stopPropagation(); - return _this.rm(); - }); - $.on(this.nodes.label, 'click', function(e) { - return e.stopPropagation(); - }); - $.on(this.nodes.spoiler, 'change', function(e) { - _this.spoiler = e.target.checked; - if (_this === QR.selected) { - return QR.nodes.spoiler.checked = _this.spoiler; - } - }); - $.add(QR.nodes.dumpList, el); - _ref1 = ['dragStart', 'dragEnter', 'dragLeave', 'dragOver', 'dragEnd', 'drop']; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - event = _ref1[_j]; - $.on(el, event.toLowerCase(), this[event]); - } - this.thread = g.VIEW === 'thread' ? g.THREADID : 'new'; - prev = QR.posts[QR.posts.length - 1]; - QR.posts.push(this); - this.nodes.spoiler.checked = this.spoiler = prev && Conf['Remember Spoiler'] ? prev.spoiler : false; - QR.persona.get(function(persona) { - _this.name = 'name' in QR.persona.always ? QR.persona.always.name : prev ? prev.name : persona.name; - _this.email = 'email' in QR.persona.always ? QR.persona.always.email : prev && !/^sage$/.test(prev.email) ? prev.email : persona.email; - _this.sub = 'sub' in QR.persona.always ? QR.persona.always.sub : Conf['Remember Subject'] ? prev ? prev.sub : persona.sub : ''; - if (QR.nodes.flag) { - _this.flag = prev ? prev.flag : persona.flag; - } - if (QR.selected === _this) { - return _this.load(); - } - }); - if (select) { - this.select(); - } - this.unlock(); - } - - _Class.prototype.rm = function() { - var index; - this["delete"](); - index = QR.posts.indexOf(this); - if (QR.posts.length === 1) { - new QR.post(true); - $.rmClass(QR.nodes.el, 'dump'); - } else if (this === QR.selected) { - (QR.posts[index - 1] || QR.posts[index + 1]).select(); - } - QR.posts.splice(index, 1); - return QR.status(); - }; - - _Class.prototype["delete"] = function() { - $.rm(this.nodes.el); - return URL.revokeObjectURL(this.URL); - }; - - _Class.prototype.lock = function(lock) { - var name, node, _i, _len, _ref; - if (lock == null) { - lock = true; - } - this.isLocked = lock; - if (this !== QR.selected) { - return; - } - _ref = ['thread', 'name', 'email', 'sub', 'com', 'fileButton', 'filename', 'spoiler', 'flag']; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - name = _ref[_i]; - if (node = QR.nodes[name]) { - node.disabled = lock; - } - } - this.nodes.rm.style.visibility = lock ? 'hidden' : ''; - (lock ? $.off : $.on)(QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput); - this.nodes.spoiler.disabled = lock; - return this.nodes.el.draggable = !lock; - }; - - _Class.prototype.unlock = function() { - return this.lock(false); - }; - - _Class.prototype.select = function() { - var rectEl, rectList; - if (QR.selected) { - QR.selected.nodes.el.id = null; - QR.selected.forceSave(); - } - QR.selected = this; - this.lock(this.isLocked); - this.nodes.el.id = 'selected'; - rectEl = this.nodes.el.getBoundingClientRect(); - rectList = this.nodes.el.parentNode.getBoundingClientRect(); - this.nodes.el.parentNode.scrollLeft += rectEl.left + rectEl.width / 2 - rectList.left - rectList.width / 2; - this.load(); - return $.event('QRPostSelection', this); - }; - - _Class.prototype.load = function() { - var name, node, _i, _len, _ref; - _ref = ['thread', 'name', 'email', 'sub', 'com', 'filename', 'flag']; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - name = _ref[_i]; - if (!(node = QR.nodes[name])) { - continue; - } - node.value = this[name] || node.dataset["default"] || null; - } - this.showFileData(); - return QR.characterCount(); - }; - - _Class.prototype.save = function(input) { - var name, _ref; - if (input.type === 'checkbox') { - this.spoiler = input.checked; - return; - } - name = input.dataset.name; - this[name] = input.value || input.dataset["default"] || null; - switch (name) { - case 'thread': - return QR.status(); - case 'com': - this.nodes.span.textContent = this.com; - QR.characterCount(); - if (QR.cooldown.auto && this === QR.posts[0] && (0 < (_ref = QR.cooldown.seconds) && _ref <= 5)) { - return QR.cooldown.auto = false; - } - break; - case 'filename': - if (!this.file) { - return; - } - this.file.newName = this.filename.replace(/[/\\]/g, '-'); - if (!/\.(jpe?g|png|gif|pdf|swf)$/i.test(this.filename)) { - this.file.newName += '.jpg'; - } - return this.updateFilename(); - } - }; - - _Class.prototype.forceSave = function() { - var name, node, _i, _len, _ref; - if (this !== QR.selected) { - return; - } - _ref = ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler', 'flag']; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - name = _ref[_i]; - if (!(node = QR.nodes[name])) { - continue; - } - this.save(node); - } - }; - - _Class.prototype.setFile = function(file) { - this.file = file; - this.filename = file.name; - this.filesize = $.bytesToString(file.size); - if (QR.spoiler) { - this.nodes.label.hidden = false; - } - URL.revokeObjectURL(this.URL); - if (this === QR.selected) { - this.showFileData(); - } - if (!/^image/.test(file.type)) { - this.nodes.el.style.backgroundImage = null; - return; - } - return this.setThumbnail(); - }; - - _Class.prototype.setThumbnail = function() { - var fileURL, img, - _this = this; - img = $.el('img'); - img.onload = function() { - var cv, height, s, width; - s = 90 * 2 * window.devicePixelRatio; - if (_this.file.type === 'image/gif') { - s *= 3; - } - height = img.height, width = img.width; - if (height < s || width < s) { - _this.URL = fileURL; - _this.nodes.el.style.backgroundImage = "url(" + _this.URL + ")"; - return; - } - if (height <= width) { - width = s / height * width; - height = s; - } else { - height = s / width * height; - width = s; - } - cv = $.el('canvas'); - cv.height = img.height = height; - cv.width = img.width = width; - cv.getContext('2d').drawImage(img, 0, 0, width, height); - URL.revokeObjectURL(fileURL); - return cv.toBlob(function(blob) { - _this.URL = URL.createObjectURL(blob); - return _this.nodes.el.style.backgroundImage = "url(" + _this.URL + ")"; - }); - }; - fileURL = URL.createObjectURL(this.file); - return img.src = fileURL; - }; - - _Class.prototype.rmFile = function() { - if (this.isLocked) { - return; - } - delete this.file; - delete this.filename; - delete this.filesize; - this.nodes.el.title = null; - QR.nodes.fileContainer.title = ''; - this.nodes.el.style.backgroundImage = null; - if (QR.spoiler) { - this.nodes.label.hidden = true; - } - this.showFileData(); - return URL.revokeObjectURL(this.URL); - }; - - _Class.prototype.updateFilename = function() { - var long; - long = "" + this.filename + " (" + this.filesize + ")\nCtrl+click to edit filename. Shift+click to clear."; - this.nodes.el.title = long; - if (this !== QR.selected) { - return; - } - return QR.nodes.fileContainer.title = long; - }; - - _Class.prototype.showFileData = function() { - if (this.file) { - this.updateFilename(); - QR.nodes.filename.value = this.filename; - QR.nodes.spoiler.checked = this.spoiler; - return $.addClass(QR.nodes.fileSubmit, 'has-file'); - } else { - return $.rmClass(QR.nodes.fileSubmit, 'has-file'); - } - }; - - _Class.prototype.pasteText = function(file) { - var reader, - _this = this; - reader = new FileReader(); - reader.onload = function(e) { - var text; - text = e.target.result; - if (_this.com) { - _this.com += "\n" + text; - } else { - _this.com = text; - } - if (QR.selected === _this) { - QR.nodes.com.value = _this.com; - } - return _this.nodes.span.textContent = _this.com; - }; - return reader.readAsText(file); - }; - - _Class.prototype.dragStart = function(e) { - e.dataTransfer.setDragImage(this, e.layerX, e.layerY); - return $.addClass(this, 'drag'); - }; - - _Class.prototype.dragEnd = function() { - return $.rmClass(this, 'drag'); - }; - - _Class.prototype.dragEnter = function() { - return $.addClass(this, 'over'); - }; - - _Class.prototype.dragLeave = function() { - return $.rmClass(this, 'over'); - }; - - _Class.prototype.dragOver = function(e) { - e.preventDefault(); - return e.dataTransfer.dropEffect = 'move'; - }; - - _Class.prototype.drop = function() { - var el, index, newIndex, oldIndex, post; - $.rmClass(this, 'over'); - if (!this.draggable) { - return; - } - el = $('.drag', this.parentNode); - index = function(el) { - return __slice.call(el.parentNode.children).indexOf(el); - }; - oldIndex = index(el); - newIndex = index(this); - (oldIndex < newIndex ? $.after : $.before)(this, el); - post = QR.posts.splice(oldIndex, 1)[0]; - QR.posts.splice(newIndex, 0, post); - return QR.status(); - }; - - return _Class; - - })(); - - AutoGIF = { - init: function() { - var _ref; - if (g.VIEW === 'catalog' || !Conf['Auto-GIF'] || ((_ref = g.BOARD.ID) === 'gif' || _ref === 'wsg')) { - return; - } - return Post.callbacks.push({ - name: 'Auto-GIF', - cb: this.node - }); - }, - node: function() { - var URL, gif, style, thumb, _ref, _ref1; - if (this.isClone || this.isHidden || this.thread.isHidden || !((_ref = this.file) != null ? _ref.isImage : void 0)) { - return; - } - _ref1 = this.file, thumb = _ref1.thumb, URL = _ref1.URL; - if (!(/gif$/.test(URL) && !/spoiler/.test(thumb.src))) { - return; - } - if (this.file.isSpoiler) { - style = thumb.style; - style.maxHeight = style.maxWidth = this.isReply ? '125px' : '250px'; - } - gif = $.el('img'); - $.on(gif, 'load', function() { - return thumb.src = URL; - }); - return gif.src = URL; - } - }; - - FappeTyme = { - init: function() { - var el, input, lc, type, _i, _len, _ref; - if (!(Conf['Fappe Tyme'] || Conf['Werk Tyme']) || g.VIEW === 'catalog' || g.BOARD === 'f') { - return; - } - _ref = ["Fappe", "Werk"]; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - type = _ref[_i]; - if (!Conf["" + type + " Tyme"]) { - continue; - } - lc = type.toLowerCase(); - el = $.el('label', { - innerHTML: " " + type + " Tyme", - title: "" + type + " Tyme" - }); - FappeTyme[lc] = input = el.firstElementChild; - $.on(input, 'change', FappeTyme.cb.toggle.bind(input)); - $.event('AddMenuEntry', { - type: 'header', - el: el, - order: 97 - }); - if (Conf[lc]) { - FappeTyme.cb.set(lc); - } - } - return Post.callbacks.push({ - name: 'Fappe Tyme', - cb: this.node - }); - }, - node: function() { - if (this.file) { - return; - } - return $.addClass(this.nodes.root, "noFile"); - }, - cb: { - set: function(type) { - FappeTyme[type].checked = Conf[type]; - return $["" + (Conf[type] ? 'add' : 'rm') + "Class"](doc, "" + type + "Tyme"); - }, - toggle: function() { - Conf[this.name] = !Conf[this.name]; - FappeTyme.cb.set(this.name); - return $.cb.checked.call(FappeTyme[this.name]); - } - } - }; - - Gallery = { - init: function() { - var el; - if (g.VIEW === 'catalog' || g.BOARD === 'f' || !Conf['Gallery']) { - return; - } - el = $.el('a', { - href: 'javascript:;', - id: 'appchan-gal', - title: 'Gallery', - className: 'fa fa-picture-o', - textContent: 'Gallery' - }); - $.on(el, 'click', this.cb.toggle); - Header.addShortcut(el); - return Post.callbacks.push({ - name: 'Gallery', - cb: this.node - }); - }, - node: function() { - var _ref; - if (!((_ref = this.file) != null ? _ref.isImage : void 0)) { - return; - } - if (Gallery.nodes) { - Gallery.generateThumb($('.file', this.nodes.root)); - Gallery.nodes.total.textContent = Gallery.images.length; - } - if (!Conf['Image Expansion']) { - return $.on(this.file.thumb.parentNode, 'click', Gallery.cb.image); - } - }, - build: function(image) { - var cb, createSubEntry, dialog, el, file, files, i, key, menuButton, name, nodes, value, _ref; - Gallery.images = []; - nodes = Gallery.nodes = {}; - nodes.el = dialog = $.el('div', { - id: 'a-gallery', - innerHTML: "
\n \n \n ×\n \n \n / \n
\n
\n \n
\n
\n
\n
" - }); - _ref = { - frame: '.gal-image', - name: '.gal-name', - count: '.count', - total: '.total', - thumbs: '.gal-thumbnails', - next: '.gal-image a', - current: '.gal-image img' - }; - for (key in _ref) { - value = _ref[key]; - nodes[key] = $(value, dialog); - } - menuButton = $('.menu-button', dialog); - nodes.menu = new UI.Menu('gallery'); - cb = Gallery.cb; - $.on(nodes.frame, 'click', cb.blank); - $.on(nodes.current, 'click', cb.download); - $.on(nodes.next, 'click', cb.next); - $.on($('.gal-prev', dialog), 'click', cb.prev); - $.on($('.gal-next', dialog), 'click', cb.next); - $.on($('.gal-close', dialog), 'click', cb.close); - $.on(menuButton, 'click', function(e) { - return nodes.menu.toggle(e, this, g); - }); - createSubEntry = Gallery.menu.createSubEntry; - for (name in Config.gallery) { - el = createSubEntry(name).el; - $.event('AddMenuEntry', { - type: 'gallery', - el: el, - order: 0 - }); - } - $.on(d, 'keydown', cb.keybinds); - $.off(d, 'keydown', Keybinds.keydown); - i = 0; - files = $$('.post .file'); - while (file = files[i++]) { - if ($('.fileDeletedRes, .fileDeleted', file)) { - continue; - } - Gallery.generateThumb(file); - } - $.add(d.body, dialog); - nodes.thumbs.scrollTop = 0; - nodes.current.parentElement.scrollTop = 0; - Gallery.cb.open.call(image ? $("[href='" + (image.href.replace(/https?:/, '')) + "']", nodes.thumbs) : Gallery.images[0]); - d.body.style.overflow = 'hidden'; - return nodes.total.textContent = --i; - }, - generateThumb: function(file) { - var double, post, thumb, title; - post = Get.postFromNode(file); - title = ($('.fileText a', file)).textContent; - thumb = post.file.thumb.parentNode.cloneNode(true); - if (double = $('img + img', thumb)) { - $.rm(double); - } - thumb.className = 'gal-thumb'; - thumb.title = title; - thumb.dataset.id = Gallery.images.length; - thumb.dataset.post = $('a[title="Highlight this post"]', post.nodes.info).href; - thumb.firstElementChild.style.cssText = ''; - $.on(thumb, 'click', Gallery.cb.open); - Gallery.images.push(thumb); - return $.add(Gallery.nodes.thumbs, thumb); - }, - cb: { - keybinds: function(e) { - var cb, key; - if (!(key = Keybinds.keyCode(e))) { - return; - } - cb = (function() { - switch (key) { - case 'Esc': - case Conf['Open Gallery']: - return Gallery.cb.close; - case 'Right': - case 'Enter': - return Gallery.cb.next; - case 'Left': - case '': - return Gallery.cb.prev; - } - })(); - if (!cb) { - return; - } - e.stopPropagation(); - e.preventDefault(); - return cb(); - }, - open: function(e) { - var el, img, name, nodes, rect, top; - if (e) { - e.preventDefault(); - } - if (!this) { - return; - } - nodes = Gallery.nodes; - name = nodes.name; - if (el = $('.gal-highlight', Gallery.thumbs)) { - $.rmClass(el, 'gal-highlight'); - } - $.addClass(this, 'gal-highlight'); - img = $.el('img', { - src: name.href = this.href, - title: name.download = name.textContent = this.title - }); - $.extend(img.dataset, this.dataset); - $.replace(nodes.current, img); - nodes.count.textContent = +this.dataset.id + 1; - nodes.current = img; - nodes.frame.scrollTop = 0; - nodes.next.focus(); - rect = this.getBoundingClientRect(); - top = rect.top; - if (top > 0) { - top += rect.height - doc.clientHeight; - if (top < 0) { - return; - } - } - nodes.thumbs.scrollTop += top; - return $.on(img, 'error', function() { - return Gallery.cb.error(img, thumb); - }); - }, - image: function(e) { - e.preventDefault(); - e.stopPropagation(); - return Gallery.build(this); - }, - error: function(img, thumb) { - var URL, post, revived, src; - post = Get.postFromLink($.el('a', { - href: img.dataset.post - })); - delete post.file.fullImage; - src = this.src.split('/'); - if (src[2] === 'images.4chan.org') { - URL = Redirect.to('file', { - boardID: src[3], - filename: src[5] - }); - if (URL) { - thumb.href = URL; - if (Gallery.nodes.current !== img) { - return; - } - revived = $.el('img', { - src: URL, - title: img.title - }); - $.extend(revived.dataset, img.dataset); - $.replace(img, revived); - return; - } - if (g.DEAD || post.isDead || post.file.isDead) { - return; - } - } - return $.ajax("//api.4chan.org/" + post.board + "/res/" + post.thread + ".json", { - onload: function() { - var i, postObj, posts; - if (this.status !== 200) { - return; - } - i = 0; - posts = this.response.posts; - while (postObj = posts[i++]) { - if (postObj.no === post.ID) { - break; - } - } - if (!postObj.no) { - return post.kill(); - } - if (postObj.filedeleted) { - return post.kill(true); - } - } - }); - }, - prev: function() { - return Gallery.cb.open.call(Gallery.images[+Gallery.nodes.current.dataset.id - 1]); - }, - next: function() { - return Gallery.cb.open.call(Gallery.images[+Gallery.nodes.current.dataset.id + 1]); - }, - toggle: function() { - return (Gallery.nodes ? Gallery.cb.close : Gallery.build)(); - }, - blank: function(e) { - if (e.target === this) { - return Gallery.cb.close(); - } - }, - close: function() { - $.rm(Gallery.nodes.el); - delete Gallery.nodes; - d.body.style.overflow = ''; - $.off(d, 'keydown', Gallery.cb.keybinds); - return $.on(d, 'keydown', Keybinds.keydown); - }, - setFitness: function() { - return (this.checked ? $.addClass : $.rmClass)(doc, "gal-" + (this.name.toLowerCase().replace(/\s+/g, '-'))); - } - }, - menu: { - init: function() { - var createSubEntry, el, name, subEntries; - if (g.VIEW === 'catalog' || !Conf['Gallery']) { - return; - } - el = $.el('span', { - textContent: 'Gallery', - className: 'gallery-link' - }); - createSubEntry = Gallery.menu.createSubEntry; - subEntries = []; - for (name in Config.gallery) { - subEntries.push(createSubEntry(name)); - } - return $.event('AddMenuEntry', { - type: 'header', - el: el, - order: 105, - subEntries: subEntries - }); - }, - createSubEntry: function(name) { - var input, label; - label = $.el('label', { - innerHTML: " " + name - }); - input = label.firstElementChild; - if (name === 'Fit Width' || name === 'Fit Height' || name === 'Hide Thumbnails') { - $.on(input, 'change', Gallery.cb.setFitness); - } - input.checked = Conf[name]; - $.event('change', null, input); - $.on(input, 'change', $.cb.checked); - return { - el: label - }; - } - } - }; - - ImageExpand = { - init: function() { - if (g.VIEW === 'catalog' || !Conf['Image Expansion']) { - return; - } - this.EAI = $.el('a', { - className: 'expand-all-shortcut fa fa-expand', - textContent: 'EAI', - title: 'Expand All Images', - href: 'javascript:;' - }); - $.on(this.EAI, 'click', ImageExpand.cb.toggleAll); - Header.addShortcut(this.EAI, 3); - return Post.callbacks.push({ - name: 'Image Expansion', - cb: this.node - }); - }, - node: function() { - var thumb, _ref; - if (!((_ref = this.file) != null ? _ref.isImage : void 0)) { - return; - } - thumb = this.file.thumb; - $.on(thumb.parentNode, 'click', ImageExpand.cb.toggle); - if (this.isClone && $.hasClass(thumb, 'expanding')) { - ImageExpand.contract(this); - ImageExpand.expand(this); - return; - } - if (ImageExpand.on && !this.isHidden && (Conf['Expand spoilers'] || !this.file.isSpoiler)) { - return ImageExpand.expand(this); - } - }, - cb: { - toggle: function(e) { - if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) { - return; - } - e.preventDefault(); - return ImageExpand.toggle(Get.postFromNode(this)); - }, - toggleAll: function() { - var func, toggle; - $.event('CloseMenu'); - toggle = function(post) { - var file; - file = post.file; - if (!(file && file.isImage && doc.contains(post.nodes.root))) { - return; - } - if (ImageExpand.on && (!Conf['Expand spoilers'] && file.isSpoiler || Conf['Expand from here'] && Header.getTopOf(file.thumb) < 0)) { - return; - } - return $.queueTask(func, post); - }; - if (ImageExpand.on = $.hasClass(ImageExpand.EAI, 'expand-all-shortcut')) { - ImageExpand.EAI.className = 'contract-all-shortcut fa fa-compress'; - ImageExpand.EAI.title = 'Contract All Images'; - func = ImageExpand.expand; - } else { - ImageExpand.EAI.className = 'expand-all-shortcut fa fa-expand'; - ImageExpand.EAI.title = 'Expand All Images'; - func = ImageExpand.contract; - } - return g.posts.forEach(function(post) { - var _i, _len, _ref; - toggle(post); - _ref = post.clones; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - post = _ref[_i]; - toggle(post); - } - }); - }, - setFitness: function() { - return (this.checked ? $.addClass : $.rmClass)(doc, this.name.toLowerCase().replace(/\s+/g, '-')); - } - }, - toggle: function(post) { - var headRect, left, root, thumb, top, x, y, _ref; - thumb = post.file.thumb; - if (!(post.file.isExpanded || $.hasClass(thumb, 'expanding'))) { - ImageExpand.expand(post); - return; - } - root = post.nodes.root; - _ref = (Conf['Advance on contract'] ? (function() { - var next; - next = root; - while (next = $.x("following::div[contains(@class,'postContainer')][1]", next)) { - if ($('.stub', next) || next.offsetHeight === 0) { - continue; - } - return next; - } - return root; - })() : root).getBoundingClientRect(), top = _ref.top, left = _ref.left; - if (top < 0) { - y = top; - if (Conf['Fixed Header'] && !Conf['Bottom Header']) { - headRect = Header.bar.getBoundingClientRect(); - y -= headRect.top + headRect.height; - } - } - if (left < 0) { - x = -window.scrollX; - } - if (x || y) { - window.scrollBy(x, y); - } - return ImageExpand.contract(post); - }, - contract: function(post) { - $.rmClass(post.nodes.root, 'expanded-image'); - $.rmClass(post.file.thumb, 'expanding'); - return post.file.isExpanded = false; - }, - expand: function(post, src) { - var img, thumb; - thumb = post.file.thumb; - if (post.isHidden || post.file.isExpanded || $.hasClass(thumb, 'expanding')) { - return; - } - $.addClass(thumb, 'expanding'); - if (post.file.fullImage) { - $.asap((function() { - return post.file.fullImage.naturalHeight; - }), function() { - return ImageExpand.completeExpand(post); - }); - return; - } - post.file.fullImage = img = $.el('img', { - className: 'full-image', - src: src || post.file.URL - }); - $.on(img, 'error', ImageExpand.error); - $.asap((function() { - return post.file.fullImage.naturalHeight; - }), function() { - return ImageExpand.completeExpand(post); - }); - return $.after(thumb, img); - }, - completeExpand: function(post) { - var bottom, thumb; - thumb = post.file.thumb; - if (!$.hasClass(thumb, 'expanding')) { - return; - } - post.file.isExpanded = true; - if (!post.nodes.root.parentNode) { - $.addClass(post.nodes.root, 'expanded-image'); - $.rmClass(post.file.thumb, 'expanding'); - return; - } - bottom = post.nodes.root.getBoundingClientRect().bottom; - return $.queueTask(function() { - $.addClass(post.nodes.root, 'expanded-image'); - $.rmClass(post.file.thumb, 'expanding'); - if (!(bottom <= 0)) { - return; - } - return window.scrollBy(0, post.nodes.root.getBoundingClientRect().bottom - bottom); - }); - }, - error: function() { - var URL, post, src, timeoutID; - post = Get.postFromNode(this); - $.rm(this); - delete post.file.fullImage; - if (!($.hasClass(post.file.thumb, 'expanding') || $.hasClass(post.nodes.root, 'expanded-image'))) { - return; - } - ImageExpand.contract(post); - src = this.src.split('/'); - if (src[2] === 'i.4cdn.org') { - URL = Redirect.to('file', { - boardID: src[3], - filename: src[5] - }); - if (URL) { - setTimeout(ImageExpand.expand, 10000, post, URL); - return; - } - if (g.DEAD || post.isDead || post.file.isDead) { - return; - } - } - timeoutID = setTimeout(ImageExpand.expand, 10000, post); - return $.ajax("//a.4cdn.org/" + post.board + "/res/" + post.thread + ".json", { - onload: function() { - var postObj, _i, _len, _ref; - if (this.status !== 200) { - return; - } - _ref = this.response.posts; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - postObj = _ref[_i]; - if (postObj.no === post.ID) { - break; - } - } - if (postObj.no !== post.ID) { - clearTimeout(timeoutID); - return post.kill(); - } else if (postObj.filedeleted) { - clearTimeout(timeoutID); - return post.kill(true); - } - } - }); - }, - menu: { - init: function() { - var conf, createSubEntry, el, name, subEntries, _ref; - if (g.VIEW === 'catalog' || !Conf['Image Expansion']) { - return; - } - el = $.el('span', { - textContent: 'Image Expansion', - className: 'image-expansion-link' - }); - createSubEntry = ImageExpand.menu.createSubEntry; - subEntries = []; - _ref = Config.imageExpansion; - for (name in _ref) { - conf = _ref[name]; - subEntries.push(createSubEntry(name, conf[1])); - } - return $.event('AddMenuEntry', { - type: 'header', - el: el, - order: 105, - subEntries: subEntries - }); - }, - createSubEntry: function(name, desc) { - var input, label; - label = $.el('label', { - innerHTML: " " + name, - title: desc - }); - input = label.firstElementChild; - if (name === 'Fit width' || name === 'Fit height') { - $.on(input, 'change', ImageExpand.cb.setFitness); - } - input.checked = Conf[name]; - $.event('change', null, input); - $.on(input, 'change', $.cb.checked); - return { - el: label - }; - } - } - }; - - ImageHover = { - init: function() { - if (g.VIEW === 'catalog' || !Conf['Image Hover']) { - return; - } - return Post.callbacks.push({ - name: 'Image Hover', - cb: this.node - }); - }, - node: function() { - var _ref; - if (!((_ref = this.file) != null ? _ref.isImage : void 0)) { - return; - } - return $.on(this.file.thumb, 'mouseover', ImageHover.mouseover); - }, - mouseover: function(e) { - var el, post; - post = Get.postFromNode(this); - el = $.el('img', { - id: 'ihover', - src: post.file.URL - }); - el.dataset.fullID = post.fullID; - $.add(Header.hover, el); - UI.hover({ - root: this, - el: el, - latestEvent: e, - endEvents: 'mouseout click', - asapTest: function() { - return el.naturalHeight; - } - }); - return $.on(el, 'error', ImageHover.error); - }, - error: function() { - var URL, post, src, timeoutID, - _this = this; - if (!doc.contains(this)) { - return; - } - post = g.posts[this.dataset.fullID]; - src = this.src.split('/'); - if (src[2] === 'i.4cdn.org') { - URL = Redirect.to('file', { - boardID: src[3], - filename: src[5].replace(/\?.+$/, '') - }); - if (URL) { - this.src = URL; - return; - } - if (g.DEAD || post.isDead || post.file.isDead) { - return; - } - } - timeoutID = setTimeout((function() { - return _this.src = post.file.URL + '?' + Date.now(); - }), 3000); - return $.ajax("//a.4cdn.org/" + post.board + "/res/" + post.thread + ".json", { - onload: function() { - var postObj, _i, _len, _ref; - if (this.status !== 200) { - return; - } - _ref = this.response.posts; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - postObj = _ref[_i]; - if (postObj.no === post.ID) { - break; - } - } - if (postObj.no !== post.ID) { - clearTimeout(timeoutID); - return post.kill(); - } else if (postObj.filedeleted) { - clearTimeout(timeoutID); - return post.kill(true); - } - } - }); - } - }; - - ImageLoader = { - init: function() { - var prefetch; - if (g.VIEW === 'catalog') { - return; - } - if (!(Conf["Image Prefetching"] || Conf["Replace JPG"] || Conf["Replace PNG"] || Conf["Replace GIF"])) { - return; - } - Post.callbacks.push({ - name: 'Image Replace', - cb: this.node - }); - Thread.callbacks.push({ - name: 'Image Replace', - cb: this.thread - }); - if (!(Conf['Image Prefetching'] && g.VIEW === 'thread')) { - return; - } - prefetch = $.el('label', { - innerHTML: ' Prefetch Images' - }); - this.el = prefetch.firstElementChild; - $.on(this.el, 'change', this.toggle); - return $.event('AddMenuEntry', { - type: 'header', - el: prefetch, - order: 104 - }); - }, - thread: function() { - return ImageLoader.thread = this; - }, - node: function() { - var URL, img, string, style, thumb, type, _ref, _ref1; - if (this.isClone || this.isHidden || this.thread.isHidden || !((_ref = this.file) != null ? _ref.isImage : void 0)) { - return; - } - _ref1 = this.file, thumb = _ref1.thumb, URL = _ref1.URL; - if (!((Conf[string = "Replace " + ((type = (URL.match(/\w{3}$/))[0].toUpperCase()) === 'PEG' ? 'JPG' : type)] && !/spoiler/.test(thumb.src)) || Conf['prefetch'])) { - return; - } - if (this.file.isSpoiler) { - style = thumb.style; - style.maxHeight = style.maxWidth = this.isReply ? '125px' : '250px'; - } - img = $.el('img'); - if (Conf[string]) { - $.on(img, 'load', function() { - return thumb.src = URL; - }); - } - return img.src = URL; - }, - toggle: function() { - var enabled; - enabled = Conf['prefetch'] = this.checked; - if (enabled) { - ImageLoader.thread.posts.forEach(ImageLoader.node.call); - } - } - }; - - RevealSpoilers = { - init: function() { - if (g.VIEW === 'catalog' || !Conf['Reveal Spoiler Thumbnails']) { - return; - } - return Post.callbacks.push({ - cb: this.node - }); - }, - node: function() { - var thumb, _ref; - if (this.isClone || !((_ref = this.file) != null ? _ref.isSpoiler : void 0)) { - return; - } - thumb = this.file.thumb; - thumb.removeAttribute('style'); - return thumb.src = this.file.thumbURL; - } - }; - - Sauce = { - init: function() { - var err, link, links, _i, _len, _ref; - if (g.VIEW === 'catalog' || !Conf['Sauce']) { - return; - } - links = []; - _ref = Conf['sauces'].split('\n'); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - link = _ref[_i]; - try { - if (link[0] !== '#') { - links.push(this.createSauceLink(link.trim())); - } - } catch (_error) { - err = _error; - } - } - if (!links.length) { - return; - } - this.links = links; - this.link = $.el('a', { - target: '_blank' - }); - return Post.callbacks.push({ - name: 'Sauce', - cb: this.node - }); - }, - createSauceLink: function(link) { - var m, text; - link = link.replace(/%(T?URL|MD5|board|name)/g, function(parameter) { - var type; - return ((type = { - '%TURL': 'post.file.thumbURL', - '%URL': 'post.file.URL', - '%MD5': 'post.file.MD5', - '%board': 'post.board', - '%name': 'post.file.name' - }[parameter]) ? "' + encodeURIComponent(" + type + ") + '" : parameter); - }); - text = (m = link.match(/;text:(.+)$/)) ? m[1] : link.match(/(\w+)\.\w+\//)[1]; - link = link.replace(/;text:.+$/, ''); - return Function('post', 'a', "a.href = '" + link + "';\na.textContent = '" + text + "';\nreturn a;"); - }, - node: function() { - var link, nodes, _i, _len, _ref; - if (this.isClone || !this.file) { - return; - } - nodes = []; - _ref = Sauce.links; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - link = _ref[_i]; - nodes.push($.tn('\u00A0'), link(this, Sauce.link.cloneNode(true))); - } - return $.add(this.file.text, nodes); - } - }; - - Linkify = { - init: function() { - if (g.VIEW === 'catalog' || !Conf['Linkify']) { - return; - } - this.regString = /((https?|mailto|git|magnet|ftp|irc):([a-z\d%\/])|[-a-z\d]+[.](aero|asia|biz|cat|com|coop|info|int|jobs|mobi|museum|name|net|org|post|pro|tel|travel|xxx|edu|gov|mil|[a-z]{2})(\/|(?!.))|[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}|[-\w\d.@]+@[a-z\d.-]+\.[a-z\d])/i; - if (Conf['Comment Expansion']) { - ExpandComment.callbacks.push(this.node); - } - if (Conf['Title Link']) { - $.sync('CachedTitles', Linkify.titleSync); - } - return Post.callbacks.push({ - name: 'Linkify', - cb: this.node - }); - }, - node: function() { - var data, el, end, endNode, i, index, items, length, link, links, node, result, saved, snapshot, space, test, word, _i, _len, _ref; - if (this.isClone) { - if (Conf['Embedding']) { - i = 0; - items = $$('.embed', this.nodes.comment); - while (el = items[i++]) { - $.on(el, 'click', Linkify.cb.toggle); - if ($.hasClass(el, 'embedded')) { - Linkify.cb.toggle.call(el); - } - } - } - return; - } - test = /[^\s'"]+/g; - space = /[\s'"]/; - snapshot = $.X('.//br|.//text()', this.nodes.comment); - i = 0; - links = []; - while (node = snapshot.snapshotItem(i++)) { - data = node.data; - if (node.parentElement.nodeName === "A" || !data) { - continue; - } - while (result = test.exec(data)) { - index = result.index; - endNode = node; - word = result[0]; - if ((length = index + word.length) === data.length) { - test.lastIndex = 0; - while ((saved = snapshot.snapshotItem(i++))) { - if (saved.nodeName === 'BR') { - break; - } - endNode = saved; - data = saved.data; - word += data; - length = data.length; - if (end = space.exec(data)) { - test.lastIndex = length = end.index; - i--; - break; - } - } - } - if (Linkify.regString.exec(word)) { - links.push(Linkify.makeRange(node, endNode, index, length)); - } - if (!(test.lastIndex && node === endNode)) { - break; - } - } - } - _ref = links.reverse(); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - link = _ref[_i]; - this.nodes.links.push(Linkify.makeLink(link, this)); - link.detach(); - } - if (!(Conf['Embedding'] || Conf['Link Title'])) { - return; - } - links = this.nodes.links; - i = 0; - while (link = links[i++]) { - if (data = Linkify.services(link)) { - if (Conf['Embedding']) { - Linkify.embed(data); - } - if (Conf['Link Title']) { - Linkify.title(data); - } - } - } - }, - makeRange: function(startNode, endNode, startOffset, endOffset) { - var range; - range = document.createRange(); - range.setStart(startNode, startOffset); - range.setEnd(endNode, endOffset); - return range; - }, - makeLink: function(range) { - var a, char, i, text; - text = range.toString(); - i = 0; - while (/[(\[{<>]/.test(text.charAt(i))) { - i++; - } - if (i) { - text = text.slice(i); - while (range.startOffset + i >= range.startContainer.data.length) { - i--; - } - if (i) { - range.setStart(range.startContainer, range.startOffset + i); - } - } - i = 0; - while (/[)\]}>.,]/.test(char = text.charAt(text.length - (1 + i)))) { - if (!(/[.,]/.test(char) || (text.match(/[()\[\]{}<>]/g)).length % 2)) { - break; - } - i++; - } - if (i) { - text = text.slice(0, -i); - while (range.endOffset - i < 0) { - i--; - } - if (i) { - range.setEnd(range.endContainer, range.endOffset - i); - } - } - if (!/(https?|mailto|git|magnet|ftp|irc):/.test(text)) { - text = (/@/.test(text) ? 'mailto:' : 'http://') + text; - } - a = $.el('a', { - className: 'linkify', - rel: 'nofollow noreferrer', - target: '_blank', - href: text - }); - $.add(a, range.extractContents()); - range.insertNode(a); - return a; - }, - services: function(link) { - var href, key, match, type, _ref; - href = link.href; - _ref = Linkify.types; - for (key in _ref) { - type = _ref[key]; - if (!(match = type.regExp.exec(href))) { - continue; - } - return [key, match[1], match[2], link]; - } - }, - embed: function(data) { - var embed, href, key, link, name, options, uid, value, _ref; - key = data[0], uid = data[1], options = data[2], link = data[3]; - href = link.href; - embed = $.el('a', { - className: 'embedder', - href: 'javascript:;', - textContent: '(embed)' - }); - _ref = { - key: key, - href: href, - uid: uid, - options: options - }; - for (name in _ref) { - value = _ref[name]; - embed.dataset[name] = value; - } - embed.dataset.nodedata = link.innerHTML; - $.addClass(link, "" + embed.dataset.key); - $.on(embed, 'click', Linkify.cb.toggle); - $.after(link, [$.tn(' '), embed]); - if (Conf['Auto-embed']) { - Linkify.cb.toggle.call(embed); - } - data.push(embed); - }, - title: function(data) { - var embed, err, key, link, options, service, title, titles, uid; - key = data[0], uid = data[1], options = data[2], link = data[3], embed = data[4]; - if (!(service = Linkify.types[key].title)) { - return; - } - titles = Conf['CachedTitles']; - if (title = titles[uid]) { - if (link) { - link.textContent = title[0]; - } - if (Conf['Embedding']) { - return embed.dataset.title = title[0]; - } - } else { - try { - $.cache(service.api(uid), function() { - return title = Linkify.cb.title(this, data); - }, { - responseType: 'json' - }); - } catch (_error) { - err = _error; - if (link) { - link.innerHTML = "[" + key + "] Title Link Blocked (are you using NoScript?)"; - } - return; - } - if (title) { - titles[uid] = [title, Date.now()]; - return $.set('CachedTitles', titles); - } - } - }, - titleSync: function(value) { - return Conf['CachedTitles'] = value; - }, - cb: { - toggle: function() { - var string, _ref; - _ref = $.hasClass(this, "embedded") ? ['unembed', '(embed)'] : ['embed', '(unembed)'], string = _ref[0], this.textContent = _ref[1]; - $.replace(this.previousElementSibling, Linkify.cb[string](this)); - return $.toggleClass(this, 'embedded'); - }, - embed: function(a) { - var el, style, type; - el = (type = Linkify.types[a.dataset.key]).el(a); - el.style.cssText = (style = type.style) ? style : "border: 0; width: 640px; height: 390px"; - return el; - }, - unembed: function(a) { - var el; - el = $.el('a', { - rel: 'nofollow noreferrer', - target: 'blank', - className: 'linkify', - href: a.dataset.href, - innerHTML: a.dataset.title || a.dataset.nodedata - }); - $.addClass(el, a.dataset.key); - return el; - }, - title: function(response, data) { - var embed, key, link, options, service, text, uid; - key = data[0], uid = data[1], options = data[2], link = data[3], embed = data[4]; - service = Linkify.types[key].title; - switch (response.status) { - case 200: - case 304: - text = "" + (service.text(response.response)); - if (Conf['Embedding']) { - embed.dataset.title = text; - } - break; - case 404: - text = "[" + key + "] Not Found"; - break; - case 403: - text = "[" + key + "] Forbidden or Private"; - break; - default: - text = "[" + key + "] " + this.status + "'d"; - } - if (link) { - return link.textContent = text; - } - } - }, - types: { - audio: { - regExp: /(.*\.(mp3|ogg|wav))$/, - el: function(a) { - return $.el('audio', { - controls: 'controls', - preload: 'auto', - src: a.dataset.uid - }); - } - }, - gist: { - regExp: /.*(?:gist.github.com.*\/)([^\/][^\/]*)$/, - el: function(a) { - var div; - return div = $.el('iframe', { - src: "http://www.purplegene.com/script?url=https://gist.github.com/" + a.dataset.uid + ".js" - }); - }, - title: { - api: function(uid) { - return "https://api.github.com/gists/" + uid; - }, - text: function(_arg) { - var file, files; - files = _arg.files; - for (file in files) { - if (files.hasOwnProperty(file)) { - return file; - } - } - } - } - }, - image: { - regExp: /(http|www).*\.(gif|png|jpg|jpeg|bmp)$/, - style: 'border: 0; width: auto; height: auto;', - el: function(a) { - return $.el('div', { - innerHTML: "" - }); - } - }, - InstallGentoo: { - regExp: /.*(?:paste.installgentoo.com\/view\/)([0-9a-z_]+)/, - el: function(a) { - return $.el('iframe', { - src: "http://paste.installgentoo.com/view/embed/" + a.dataset.uid - }); - } - }, - Twitter: { - regExp: /.*twitter.com\/(.+\/status\/\d+)/, - el: function(a) { - return $.el('iframe', { - src: "https://twitframe.com/show?url=https://twitter.com/" + a.dataset.uid - }); - } - }, - LiveLeak: { - regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/, - el: function(a) { - var el; - el = $.el('iframe', { - width: "640", - height: "360", - src: "http://www.liveleak.com/ll_embed?i=" + a.dataset.uid, - frameborder: "0" - }); - el.setAttribute("allowfullscreen", "true"); - return el; - } - }, - MediaCrush: { - regExp: /.*(?:mediacru.sh\/)([0-9a-z_]+)/i, - style: 'border: 0;', - el: function(a) { - var el; - el = $.el('div'); - $.cache("https://mediacru.sh/" + a.dataset.uid + ".json", function() { - var embed, file, files, status, type, _i, _j, _len, _len1, _ref; - status = this.status; - if (status !== 200 && status !== 304) { - return div.innerHTML = "ERROR " + status; - } - files = this.response.files; - _ref = ['video/mp4', 'video/ogv', 'image/svg+xml', 'image/png', 'image/gif', 'image/jpeg', 'image/svg', 'audio/mpeg']; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - type = _ref[_i]; - for (_j = 0, _len1 = files.length; _j < _len1; _j++) { - file = files[_j]; - if (file.type === type) { - embed = file; - break; - } - } - if (embed) { - break; - } - } - if (!embed) { - return div.innerHTML = "ERROR: Not a valid filetype"; - } - return el.innerHTML = (function() { - switch (embed.type) { - case 'video/mp4': - case 'video/ogv': - return ""; - case 'image/png': - case 'image/gif': - case 'image/jpeg': - return ""; - case 'image/svg': - case 'image/svg+xml': - return ""; - case 'audio/mpeg': - return ""; - default: - return "ERROR: No valid filetype."; - } - })(); - }); - return el; - } - }, - pastebin: { - regExp: /.*(?:pastebin.com\/(?!u\/))([^#\&\?]*).*/, - el: function(a) { - var div; - return div = $.el('iframe', { - src: "http://pastebin.com/embed_iframe.php?i=" + a.dataset.uid - }); - } - }, - gfycat: { - regExp: /.*gfycat.com\/(?:iframe\/)?(\S*)/, - el: function(a) { - var div; - return div = $.el('iframe', { - src: "http://gfycat.com/iframe/" + a.dataset.uid - }); - } - }, - SoundCloud: { - regExp: /.*(?:soundcloud.com\/|snd.sc\/)([^#\&\?]*).*/, - style: 'height: auto; width: 500px; display: inline-block;', - el: function(a) { - var div; - div = $.el('div', { - className: "soundcloud", - name: "soundcloud" - }); - $.ajax("//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + a.dataset.uid, { - onloadend: function() { - return div.innerHTML = JSON.parse(this.responseText).html; - } - }, false); - return div; - }, - title: { - api: function(uid) { - return "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + uid; - }, - text: function(_) { - return _.title; - } - } - }, - StrawPoll: { - regExp: /strawpoll\.me\/(?:embed_\d+\/)?(\d+)/, - style: 'border: 0; width: 600px; height: 406px;', - el: function(a) { - return $.el('iframe', { - src: "http://strawpoll.me/embed_1/" + a.dataset.uid - }); - } - }, - TwitchTV: { - regExp: /.*(?:twitch.tv\/)([^#\&\?]*).*/, - style: "border: none; width: 640px; height: 360px;", - el: function(a) { - var channel, chapter, result, _; - if (result = /(\w+)\/(?:[a-z]\/)?(\d+)/i.exec(a.dataset.uid)) { - _ = result[0], channel = result[1], chapter = result[2]; - return $.el('object', { - data: 'http://www.twitch.tv/widgets/archive_embed_player.swf', - innerHTML: "\n" - }); - } else { - channel = (/(\w+)/.exec(a.dataset.uid))[0]; - return $.el('object', { - data: "http://www.twitch.tv/widgets/live_embed_player.swf?channel=" + channel, - innerHTML: "\n\n" - }); - } - } - }, - Vocaroo: { - regExp: /.*(?:vocaroo.com\/)([^#\&\?]*).*/, - style: 'border: 0; width: 150px; height: 45px;', - el: function(a) { - return $.el('object', { - innerHTML: "" - }); - } - }, - Vimeo: { - regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/, - el: function(a) { - return $.el('iframe', { - src: "//player.vimeo.com/video/" + a.dataset.uid + "?wmode=opaque" - }); - }, - title: { - api: function(uid) { - return "https://vimeo.com/api/oembed.json?url=http://vimeo.com/" + uid; - }, - text: function(_) { - return _.title; - } - } - }, - Vine: { - regExp: /.*(?:vine.co\/)([^#\&\?]*).*/, - style: 'border: none; width: 500px; height: 500px;', - el: function(a) { - return $.el('iframe', { - src: "https://vine.co/" + a.dataset.uid + "/card" - }); - } - }, - YouTube: { - regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/, - el: function(a) { - var el; - el = $.el('iframe', { - src: "//www.youtube.com/embed/" + a.dataset.uid + (a.dataset.option ? '#' + a.dataset.option : '') + "?wmode=opaque" - }); - el.setAttribute("allowfullscreen", "true"); - return el; - }, - title: { - api: function(uid) { - return "https://gdata.youtube.com/feeds/api/videos/" + uid + "?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode"; - }, - text: function(data) { - return data.entry.title.$t; - } - } - } - } - }; - - ArchiveLink = { - init: function() { - var div, entry, type, _i, _len, _ref; - if (g.VIEW === 'catalog' || !Conf['Menu'] || !Conf['Archive Link']) { - return; - } - div = $.el('div', { - textContent: 'Archive' - }); - entry = { - type: 'post', - el: div, - order: 90, - open: function(_arg) { - var ID, board, thread; - ID = _arg.ID, thread = _arg.thread, board = _arg.board; - return !!Redirect.to('thread', { - postID: ID, - threadID: thread.ID, - boardID: board.ID - }); - }, - subEntries: [] - }; - _ref = [['Post', 'post'], ['Name', 'name'], ['Tripcode', 'tripcode'], ['E-mail', 'email'], ['Subject', 'subject'], ['Filename', 'filename'], ['Image MD5', 'MD5']]; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - type = _ref[_i]; - entry.subEntries.push(this.createSubEntry(type[0], type[1])); - } - return $.event('AddMenuEntry', entry); - }, - createSubEntry: function(text, type) { - var el, open; - el = $.el('a', { - textContent: text, - target: '_blank' - }); - open = type === 'post' ? function(_arg) { - var ID, board, thread; - ID = _arg.ID, thread = _arg.thread, board = _arg.board; - el.href = Redirect.to('thread', { - postID: ID, - threadID: thread.ID, - boardID: board.ID - }); - return true; - } : function(post) { - var value; - value = Filter[type](post); - if (!value) { - return false; - } - el.href = Redirect.to('search', { - boardID: post.board.ID, - type: type, - value: value, - isSearch: true - }); - return true; - }; - return { - el: el, - open: open - }; - } - }; - - DeleteLink = { - init: function() { - var div, fileEl, fileEntry, postEl, postEntry; - if (g.VIEW === 'catalog' || !Conf['Menu'] || !Conf['Delete Link']) { - return; - } - div = $.el('div', { - className: 'delete-link', - textContent: 'Delete' - }); - postEl = $.el('a', { - className: 'delete-post', - href: 'javascript:;' - }); - fileEl = $.el('a', { - className: 'delete-file', - href: 'javascript:;' - }); - postEntry = { - el: postEl, - open: function() { - postEl.textContent = 'Post'; - $.on(postEl, 'click', DeleteLink["delete"]); - return true; - } - }; - fileEntry = { - el: fileEl, - open: function(_arg) { - var file; - file = _arg.file; - if (!file || file.isDead) { - return false; - } - fileEl.textContent = 'File'; - $.on(fileEl, 'click', DeleteLink["delete"]); - return true; - } - }; - return $.event('AddMenuEntry', { - type: 'post', - el: div, - order: 40, - open: function(post) { - var node; - if (post.isDead) { - return false; - } - DeleteLink.post = post; - node = div.firstChild; - node.textContent = 'Delete'; - DeleteLink.cooldown.start(post, node); - return true; - }, - subEntries: [postEntry, fileEntry] - }); - }, - "delete": function() { - var fileOnly, form, link, post; - post = DeleteLink.post; - if (DeleteLink.cooldown.counting === post) { - return; - } - $.off(this, 'click', DeleteLink["delete"]); - fileOnly = $.hasClass(this, 'delete-file'); - this.textContent = "Deleting " + (fileOnly ? 'file' : 'post') + "..."; - form = { - mode: 'usrdel', - onlyimgdel: fileOnly, - pwd: QR.persona.getPassword() - }; - form[post.ID] = 'delete'; - link = this; - return $.ajax($.id('delform').action.replace("/" + g.BOARD + "/", "/" + post.board + "/"), { - responseType: 'document', - withCredentials: true, - onload: function() { - return DeleteLink.load(link, post, fileOnly, this.response); - }, - onerror: function() { - return DeleteLink.error(link); - } - }, { - form: $.formData(form) - }); - }, - load: function(link, post, fileOnly, resDoc) { - var msg, s; - if (resDoc.title === '4chan - Banned') { - s = 'Banned!'; - } else if (msg = resDoc.getElementById('errmsg')) { - s = msg.textContent; - $.on(link, 'click', DeleteLink["delete"]); - } else { - if (resDoc.title === 'Updating index...') { - (post.origin || post).kill(fileOnly); - } - s = 'Deleted'; - } - return link.textContent = s; - }, - error: function(link) { - link.textContent = 'Connection error, please retry.'; - return $.on(link, 'click', DeleteLink["delete"]); - }, - cooldown: { - start: function(post, node) { - var length, seconds, _ref; - if (!((_ref = QR.db) != null ? _ref.get({ - boardID: post.board.ID, - threadID: post.thread.ID, - postID: post.ID - }) : void 0)) { - delete DeleteLink.cooldown.counting; - return; - } - DeleteLink.cooldown.counting = post; - length = 60; - seconds = Math.ceil((length * $.SECOND - (Date.now() - post.info.date)) / $.SECOND); - return DeleteLink.cooldown.count(post, seconds, length, node); - }, - count: function(post, seconds, length, node) { - if (DeleteLink.cooldown.counting !== post) { - return; - } - if (!((0 <= seconds && seconds <= length))) { - if (DeleteLink.cooldown.counting === post) { - node.textContent = 'Delete'; - delete DeleteLink.cooldown.counting; - } - return; - } - setTimeout(DeleteLink.cooldown.count, 1000, post, seconds - 1, length, node); - return node.textContent = "Delete (" + seconds + ")"; - } - } - }; - - DownloadLink = { - init: function() { - var a; - if (g.VIEW === 'catalog' || !Conf['Menu'] || !Conf['Download Link']) { - return; - } - a = $.el('a', { - className: 'download-link', - textContent: 'Download file' - }); - return $.event('AddMenuEntry', { - type: 'post', - el: a, - order: 100, - open: function(_arg) { - var file; - file = _arg.file; - if (!file) { - return false; - } - a.href = file.URL; - a.download = file.name; - return true; - } - }); - } - }; - - Menu = { - init: function() { - if (g.VIEW === 'catalog' || !Conf['Menu']) { - return; - } - this.menu = new UI.Menu('post'); - return Post.callbacks.push({ - name: 'Menu', - cb: this.node - }); - }, - node: function() { - if (this.isClone) { - $.on($('.menu-button', this.nodes.info), 'click', Menu.toggle); - return; - } - return $.add(this.nodes.info, Menu.makeButton()); - }, - makeButton: (function() { - var a; - a = $.el('a', { - className: 'menu-button', - innerHTML: '', - href: 'javascript:;' - }); - return function() { - var button; - button = a.cloneNode(true); - $.on(button, 'click', Menu.toggle); - return button; - }; - })(), - toggle: function(e) { - var post; - post = Get.postFromNode(this); - return Menu.menu.toggle(e, this, post); - } - }; - - ReportLink = { - init: function() { - var a; - if (g.VIEW === 'catalog' || !Conf['Menu'] || !Conf['Report Link']) { - return; - } - a = $.el('a', { - className: 'report-link', - href: 'javascript:;', - textContent: 'Report this post' - }); - $.on(a, 'click', ReportLink.report); - return $.event('AddMenuEntry', { - type: 'post', - el: a, - order: 10, - open: function(post) { - ReportLink.post = post; - return !post.isDead; - } - }); - }, - report: function() { - var id, post, set, url; - post = ReportLink.post; - url = "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post; - id = Date.now(); - set = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=1,width=685,height=200"; - return window.open(url, id, set); - } - }; - - Favicon = { - init: function() { - return $.asap((function() { - return Favicon.el = $('link[rel="shortcut icon"]', d.head); - }), Favicon.initAsap); - }, - initAsap: function() { - var href; - Favicon.el.type = 'image/x-icon'; - href = Favicon.el.href; - Favicon.SFW = /ws\.ico$/.test(href); - Favicon["default"] = href; - return Favicon["switch"](); - }, - "switch": function() { - var f, funreadDeadY, i, items, t; - items = { - ferongr: ['iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAFVBMVEX///9zBQC/AADpDAP/gID/q6voCwJJTwpOAAAAAXRSTlMAQObYZgAAAGJJREFUeF5Fi7ENg0AQBCfa/AFdDh2gdwPIogMK2E2+/xLslwOvdqRJhv+GQQPUCtJM7svankLrq/I+TY5e6Ueh1jyBMX7AFJi9vwfyVO4CbbO6jNYpp9GyVPbdkFhVgAQ2H0NOE5jk9DT8AAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAxUlEQVR42q1TOwrCQBB9s0FRtJI0WoqFtSLYegoP4gVSeJsUHsHSI3iFeIqRXXgwrhlXwYHHhLwPTB7B36abBCV+0pA4DUBQUNZYQptGtW3jtoKyxgoe0yrBCoyZfL/5ioQ3URZOXW9I341l3oo+NXEZiW4CEuIzvPECopED4OaZ3RNmeAm4u+a8Jr5f17VyVoL8fr8qcltzwlyyj2iqcgPOQ9ExkHAITgD75bYBe0A5S4H/P9htuWMF3QXoQpwaKeT+lnsC6JE5I6aq6fEAAAAASUVORK5CYII=', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAFVBMVEX///8AcH4AtswA2PJ55fKi6fIA1/FtpPADAAAAAXRSTlMAQObYZgAAAGJJREFUeF5Fi7ENg0AQBCfa/AFdDh2gdwPIogMK2E2+/xLslwOvdqRJhv+GQQPUCtJM7svankLrq/I+TY5e6Ueh1jyBMX7AFJi9vwfyVO4CbbO6jNYpp9GyVPbdkFhVgAQ2H0NOE5jk9DT8AAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAxElEQVQ4y2NgoBq4/vE/HJOsBiRQUIfA2AzBqQYqUfn00/9FLz+BaQxDCKqBmX7jExijKEDSDJPHrnnbGQhGV4RmOFwdVkNwhQMheYwQxhaIi7b9Z9A3gWAQm2BUoQOgRhgA8o7j1ozLC4LCyAZcx6kZI5qg4kLKqggDFFWxJySsUQVzlb4pwgAJaTRvokcVNgOqOv8zcHBCsL07DgNg8YsczzA5MxtUL+DMD8g0slxI/H8GQ/P/DJKyeKIRpglXZsIiBwBhP5O+VbI/JgAAAABJRU5ErkJggg==', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAFVBMVEX///8oeQBJ3ABV/wHM/7Lu/+ZU/gAqUP3dAAAAAXRSTlMAQObYZgAAAGJJREFUeF5Fi7ENg0AQBCfa/AFdDh2gdwPIogMK2E2+/xLslwOvdqRJhv+GQQPUCtJM7svankLrq/I+TY5e6Ueh1jyBMX7AFJi9vwfyVO4CbbO6jNYpp9GyVPbdkFhVgAQ2H0NOE5jk9DT8AAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAx0lEQVQ4y2NgoBYI+cfwH4ZJVgMS0KhEYGyG4FQDkzjzf9P/d/+fgWl0QwiqgSkI/c8IxsgKkDXD5LFq9rwDweiK0A2HqcNqCK5wICSPEcLYAtH+AMN/IXMIBrEJRie6OEgjDAC5x3FqxuUFNiEUA67j1IweTTBxBQ1puAG86jgSEraogskJWSBcwCGF5k30qMJmgMFEhv/MXBAs5oLDAFj8IsczTE7UEeECbhU8+QGZRpaTi2b4L2zF8J9TGk80wjThykzY5AAW/2O1C2mIbgAAAABJRU5ErkJggg=='], - 'xat-': ['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'], - Mayhem: ['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'], - '4chanJS': ['iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAD1BMVEUBAAAAAAD/AABnZ2f///8nFk05AAAAAXRSTlMAQObYZgAAAEFJREFUeNqNjgEKACAMAjvX/98cAkkxgmSgO8Bt/Ai4ApJ6KKhzF3OiEMDASrGB/QWgPEHsUpN+Ng9xAETMYhDrWmeHAMcmvycWAAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAD1BMVEUBAAAAAAD/AAD///9nZ2f77Y6hAAAAAXRSTlMAQObYZgAAAEBJREFUeF6NjQEKACAMAnfW/98cAxFiBIngOsTqR8B1IGkeG9p5i7XabgAGZNigXgA8aoCUxvzWAIcBItGiSEwdccYA3BuRAWkAAAAASUVORK5CYII=', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAD1BMVEUBAAAAAAAul8NnZ2f////82iC9AAAAAXRSTlMAQObYZgAAAEFJREFUeNqNjgEKACAMAjvX/98cAkkxgmSgO8Bt/Ai4ApJ6KKhzF3OiEMDASrGB/QWgPEHsUpN+Ng9xAETMYhDrWmeHAMcmvycWAAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAD1BMVEUBAAAAAAAul8P///9nZ2cgIeMlAAAAAXRSTlMAQObYZgAAAEBJREFUeF6NjQEKACAMAnfW/98cAxFiBIngOsTqR8B1IGkeG9p5i7XabgAGZNigXgA8aoCUxvzWAIcBItGiSEwdccYA3BuRAWkAAAAASUVORK5CYII=', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAElBMVEUBAAAAAABmzDNlyjJnZ2f///+6o7dfAAAAAXRSTlMAQObYZgAAAERJREFUeF6NjkEKADEIA51o///lJZfQxUsHITogWi8AvwZJuxmYa25xDooBLEwOWFTYAsYVhdorLZt9Ng9xCUTCUCQ2H3F4ANrZ2WNiAAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAElBMVEUBAAAAAABmzDP///9lyjJnZ2cIHys9AAAAAXRSTlMAQObYZgAAAENJREFUeF6NjUEKwEAMAjNm9/9fLkEslFwqgjoEUn8EfAqSdrkwzj6ieyyTkQEVGWRvANfO1iEX620AjgBEwqR4Y+sBeGAA6d+vQ4IAAAAASUVORK5CYII='], - Original: ['iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAADFBMVEX/////AAD///8AAABBZmS3AAAAAXRSTlMAQObYZgAAAExJREFUeF4tyrENgDAMAMFXKuQswQLBG3mOlBnFS1gwDfIYLpEivvjq2MlqjmYvYg5jWEzCwtDSQlwcXKCVLrpFbvLvvSf9uZJ2HusDtJAY7Tkn1oYAAAAASUVORK5CYII=', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAhElEQVR42q1RwQnAMAjMu5M4guAKXa4j5dUROo5tipSDcrFChUONd0di2m/hEGVOHDyIPufgwAFASDkpoSzmBrkJ2UMyR9LsJ3rvrqo3Rt1YMIMhhNnOxLMnoMFBxHyJAr2IOBFzA8U+6pLBdmEJTA0aMVjpDd6Loks0s5HZNwYx8tfZCZ0kll7ORffZAAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAADFBMVEX///8ul8P///8AAACaqgkzAAAAAXRSTlMAQObYZgAAAExJREFUeF4tyrENgDAMAMFXKuQswQLBG3mOlBnFS1gwDfIYLpEivvjq2MlqjmYvYg5jWEzCwtDSQlwcXKCVLrpFbvLvvSf9uZJ2HusDtJAY7Tkn1oYAAAAASUVORK5CYII=', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAALVBMVEUAAAAAAAAAAAAAAAABBQcHFx4KISoNLToaVW4oKCgul8M4ODg7OzvBwcH///8uS/CdAAAAA3RSTlMAx9dmesIgAAAAV0lEQVR42m2NWw6AIBAD1eILZO5/XI0UAgm7H9tOsu0yGWAQSOoFijHOxOANGqm/LczpOaXs4gISrPZ+gc2+hO5w2xdwgOjBFUIF+sEJrhUl9JFr+badFwR+BfqlmGUJAAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAADFBMVEX///9mzDP///8AAACT0n1lAAAAAXRSTlMAQObYZgAAAExJREFUeF4tyrENgDAMAMFXKuQswQLBG3mOlBnFS1gwDfIYLpEivvjq2MlqjmYvYg5jWEzCwtDSQlwcXKCVLrpFbvLvvSf9uZJ2HusDtJAY7Tkn1oYAAAAASUVORK5CYII=', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAALVBMVEUAAAAAAAAAAAAAAAAECAIQIAgWLAsePA8oKCg4ODg6dB07OztmzDPBwcH///+rsf3XAAAAA3RSTlMAx9dmesIgAAAAV0lEQVR42m2NWw6AIBAD1eIDhbn/cTVSCCTsfmw7ybbLZIBBIKkXKKU0E4M3aKT+tjCn5xiziwuIsNr7BTb7ErrDZV/AAaIHdwgV6AcnuFaU0Eeu5dt2XiUyBjCQ2bIrAAAAAElFTkSuQmCC'], - 'Metro': ['iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAC/AABrZQDiAAAAAXRSTlMAQObYZgAAABJJREFUCB1jZGBgrMNAQEEc4gCSfAX5bRw/NQAAAABJRU5ErkJggg==', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAAAAAAAAAAHAAAdAAApAAAsAAA4AABsAACQAAC/AAD///9SVhtjAAAAA3RSTlMAPse+s4iwAAAAM0lEQVQIW2NggAGuVasWgDBpDDAQUoSaob0Jao73lgVojOitUEazBZRRvR3KmJa5AO4KAGBtLuMAuhIIAAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAAA1/GhpCidAAAAAXRSTlMAQObYZgAAABJJREFUCB1jZGBgrMNAQEEc4gCSfAX5bRw/NQAAAABJRU5ErkJggg==', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAAAAAAAAAAACAkAISUALzQAMTcAQEcAeokAorYA1/H///8BrzTFAAAAA3RSTlMAPse+s4iwAAAAM0lEQVQIW2NggAGuVasWgDBpDDAQUoSaob0Jao73lgVojOitUEazBZRRvR3KmJa5AO4KAGBtLuMAuhIIAAAAAElFTkSuQmCC', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAABV/wErM5hwAAAAAXRSTlMAQObYZgAAABJJREFUCB1jZGBgrMNAQEEc4gCSfAX5bRw/NQAAAABJRU5ErkJggg==', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAAAAAAAAAADCgANKAASOAATOwAZTAAwkQBAwQBV/wH////+Fmy4AAAAA3RSTlMAPse+s4iwAAAAM0lEQVQIW2NggAGuVasWgDBpDDAQUoSaob0Jao73lgVojOitUEazBZRRvR3KmJa5AO4KAGBtLuMAuhIIAAAAAElFTkSuQmCC'] - }[Conf['favicon']]; - f = Favicon; - t = 'data:image/png;base64,'; - i = 0; - while (items[i]) { - items[i] = t + items[i++]; - } - f.unreadDead = items[0], funreadDeadY = items[1], f.unreadSFW = items[2], f.unreadSFWY = items[3], f.unreadNSFW = items[4], f.unreadNSFWY = items[5]; - return f.update(); - }, - update: function() { - if (this.SFW) { - this.unread = this.unreadSFW; - return this.unreadY = this.unreadSFWY; - } else { - this.unread = this.unreadNSFW; - return this.unreadY = this.unreadNSFWY; - } - }, - dead: 'data:image/gif;base64,R0lGODlhEAAQAKECAAAAAP8AAP///////yH5BAEKAAIALAAAAAAQABAAAAIvlI+pq+D9DAgUoFkPDlbs7lFZKIJOJJ3MyraoB14jFpOcVMpzrnF3OKlZYsMWowAAOw==', - logo: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAgMAAAC+UIlYAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAGlJREFUWMPtlkEKADEIA/tJP9lXLttQto2yHxgDHozTi0ToGK2WKZZ+HAQQMZc+xBwI4EZ+wAC2IfPuSIDOZJrSZQEAX9eVJhhwIuUYAnQe8rhAEMAZlTI2MID9f5Clyh0JeE1V1ZEAvB4qDfwuJTSGRAAAAABJRU5ErkJggg==' - }; - - ThreadExcerpt = { - init: function() { - if (g.VIEW !== 'thread' || !Conf['Thread Excerpt']) { - return; - } - return Thread.callbacks.push({ - name: 'Thread Excerpt', - cb: this.node - }); - }, - node: function() { - return d.title = Get.threadExcerpt(this); - }, - disconnect: function() { - if (g.VIEW !== 'thread' || !Conf['Thread Excerpt']) { - return; - } - return Thread.callbacks.disconnect('Thread Excerpt'); - } - }; - - ThreadStats = { - init: function() { - var sc, - _this = this; - if (g.VIEW !== 'thread' || !Conf['Thread Stats']) { - return; - } - if (Conf['Updater and Stats in Header']) { - this.dialog = sc = $.el('span', { - innerHTML: "0 / 0" + (Conf["Page Count in Stats"] ? " / 0" : ""), - id: 'thread-stats', - title: 'Post Count / File Count' + (Conf["Page Count in Stats"] ? " / Page Count" : "") - }); - $.ready(function() { - return Header.addShortcut(sc); - }); - } else { - this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', "
0 / 0" + (Conf["Page Count in Stats"] ? " / 0" : "") + "
"); - $.ready(function() { - return $.add(d.body, sc); - }); - } - this.postCountEl = $('#post-count', sc); - this.fileCountEl = $('#file-count', sc); - this.pageCountEl = $('#page-count', sc); - return Thread.callbacks.push({ - name: 'Thread Stats', - cb: this.node - }); - }, - node: function() { - var fileCount, postCount; - postCount = 0; - fileCount = 0; - this.posts.forEach(function(post) { - postCount++; - if (post.file) { - return fileCount++; - } - }); - ThreadStats.thread = this; - ThreadStats.fetchPage(); - ThreadStats.update(postCount, fileCount); - return $.on(d, 'ThreadUpdate', ThreadStats.onUpdate); - }, - disconnect: function() { - if (g.VIEW !== 'thread' || !Conf['Thread Stats']) { - return; - } - if (Conf['Updater and Stats in Header']) { - Header.rmShortcut(this.dialog); - } else { - $.rm(d.body, sc); - } - clearTimeout(this.timeout); - delete this.timeout; - delete this.thread; - delete this.postCountEl; - delete this.fileCountEl; - delete this.pageCountEl; - Thread.callbacks.disconnect('Thread Stats'); - return $.off(d, 'ThreadUpdate', ThreadStats.onUpdate); - }, - onUpdate: function(e) { - var fileCount, postCount, _ref; - if (e.detail[404]) { - return; - } - _ref = e.detail, postCount = _ref.postCount, fileCount = _ref.fileCount; - return ThreadStats.update(postCount, fileCount); - }, - update: function(postCount, fileCount) { - var fileCountEl, postCountEl, thread; - thread = ThreadStats.thread, postCountEl = ThreadStats.postCountEl, fileCountEl = ThreadStats.fileCountEl; - postCountEl.textContent = postCount; - fileCountEl.textContent = fileCount; - (thread.postLimit && !thread.isSticky ? $.addClass : $.rmClass)(postCountEl, 'warning'); - return (thread.fileLimit && !thread.isSticky ? $.addClass : $.rmClass)(fileCountEl, 'warning'); - }, - fetchPage: function() { - if (!Conf["Page Count in Stats"]) { - return; - } - if (ThreadStats.thread.isDead) { - ThreadStats.pageCountEl.textContent = 'Dead'; - $.addClass(ThreadStats.pageCountEl, 'warning'); - return; - } - ThreadStats.timeout = setTimeout(ThreadStats.fetchPage, 2 * $.MINUTE); - return $.ajax("//a.4cdn.org/" + ThreadStats.thread.board + "/threads.json", { - onload: ThreadStats.onThreadsLoad - }, { - whenModified: true - }); - }, - onThreadsLoad: function() { - var page, thread, _i, _j, _len, _len1, _ref, _ref1; - if (!(Conf["Page Count in Stats"] && this.status === 200)) { - return; - } - _ref = this.response; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - page = _ref[_i]; - _ref1 = page.threads; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - thread = _ref1[_j]; - if (!(thread.no === ThreadStats.thread.ID)) { - continue; - } - ThreadStats.pageCountEl.textContent = page.page; - (page.page === this.response.length - 1 ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning'); - return; - } - } - } - }; - - ThreadUpdater = { - init: function() { - var checked, conf, el, input, name, sc, subEntries, _ref, - _this = this; - if (g.VIEW !== 'thread' || !Conf['Thread Updater']) { - return; - } - if (Conf['Updater and Stats in Header']) { - this.dialog = sc = $.el('span', { - innerHTML: "", - id: 'updater' - }); - $.ready(function() { - return Header.addShortcut(sc); - }); - } else { - this.dialog = sc = UI.dialog('updater', 'bottom: 0px; left: 0px;', "
"); - $.addClass(doc, 'float'); - $.ready(function() { - $.addClass(doc, 'float'); - return $.add(d.body, sc); - }); - } - this.checkPostCount = 0; - this.timer = $('#update-timer', sc); - this.status = $('#update-status', sc); - this.isUpdating = Conf['Auto Update']; - $.on(this.timer, 'click', this.update); - $.on(this.status, 'click', this.update); - subEntries = []; - _ref = Config.updater.checkbox; - for (name in _ref) { - conf = _ref[name]; - checked = Conf[name] ? 'checked' : ''; - el = $.el('label', { - title: "" + conf[1], - innerHTML: " " + name - }); - input = el.firstElementChild; - $.on(input, 'change', $.cb.checked); - if (input.name === 'Scroll BG') { - $.on(input, 'change', this.cb.scrollBG); - this.cb.scrollBG(); - } else if (input.name === 'Auto Update') { - $.on(input, 'change', this.cb.update); - } - subEntries.push({ - el: el - }); - } - this.settings = $.el('span', { - innerHTML: 'Interval' - }); - $.on(this.settings, 'click', this.intervalShortcut); - subEntries.push({ - el: this.settings - }); - $.event('AddMenuEntry', this.entry = { - type: 'header', - el: $.el('span', { - textContent: 'Updater' - }), - order: 110, - subEntries: subEntries - }); - return Thread.callbacks.push({ - name: 'Thread Updater', - cb: this.node - }); - }, - disconnect: function() { - var el, entry, input, name, _i, _j, _len, _len1, _ref, _ref1; - if (g.VIEW !== 'thread' || !Conf['Thread Updater']) { - return; - } - $.off(this.timer, 'click', this.update); - $.off(this.status, 'click', this.update); - if (this.timeoutID) { - clearTimeout(this.timeoutID); - } - _ref = this.entry.subEntries; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - entry = _ref[_i]; - el = entry.el; - input = el.firstElementChild; - $.off(input, 'change', $.cb.checked); - $.off(input, 'change', this.cb.scrollBG); - $.off(input, 'change', this.cb.update); - } - $.off(this.settings, 'click', this.intervalShortcut); - $.off(window, 'online offline', this.cb.online); - $.off(d, 'QRPostSuccessful', this.cb.checkpost); - $.off(d, 'visibilitychange', this.cb.visibility); - this.set('timer', null); - this.set('status', 'Offline'); - $.event('rmMenuEntry', this.entry); - if (Conf['Updater and Stats in Header']) { - Header.rmShortcut(this.dialog); - } else { - $.rmClass(doc, 'float'); - $.rm(this.dialog); - } - _ref1 = ['checkPostCount', 'timer', 'status', 'isUpdating', 'entry', 'dialog', 'thread', 'root', 'lastPost', 'outdateCount', 'online', 'seconds', 'timeoutID']; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - name = _ref1[_j]; - delete this[name]; - } - return Thread.callbacks.disconnect('Thread Updater'); - }, - node: function() { - ThreadUpdater.thread = this; - ThreadUpdater.root = this.OP.nodes.root.parentNode; - ThreadUpdater.lastPost = +ThreadUpdater.root.lastElementChild.id.match(/\d+/)[0]; - ThreadUpdater.outdateCount = 0; - ThreadUpdater.cb.interval.call($.el('input', { - value: Conf['Interval'] - })); - $.on(window, 'online offline', ThreadUpdater.cb.online); - $.on(d, 'QRPostSuccessful', ThreadUpdater.cb.checkpost); - $.on(d, 'visibilitychange', ThreadUpdater.cb.visibility); - return ThreadUpdater.cb.online(); - }, - /* - http://freesound.org/people/pierrecartoons1979/sounds/90112/ - cc-by-nc-3.0 - */ - - beep: 'data:audio/wav;base64,UklGRjQDAABXQVZFZm10IBAAAAABAAEAgD4AAIA+AAABAAgAc21wbDwAAABBAAADAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkYXRhzAIAAGMms8em0tleMV4zIpLVo8nhfSlcPR102Ki+5JspVEkdVtKzs+K1NEhUIT7DwKrcy0g6WygsrM2k1NpiLl0zIY/WpMrjgCdbPhxw2Kq+5Z4qUkkdU9K1s+K5NkVTITzBwqnczko3WikrqM+l1NxlLF0zIIvXpsnjgydZPhxs2ay95aIrUEkdUdC3suK8N0NUIjq+xKrcz002WioppdGm091pK1w0IIjYp8jkhydXPxxq2K295aUrTkoeTs65suK+OUFUIzi7xqrb0VA0WSoootKm0t5tKlo1H4TYqMfkiydWQBxm16+85actTEseS8y7seHAPD9TIza5yKra01QyWSson9On0d5wKVk2H4DYqcfkjidUQB1j1rG75KsvSkseScu8seDCPz1TJDW2yara1FYxWSwnm9Sn0N9zKVg2H33ZqsXkkihSQR1g1bK65K0wSEsfR8i+seDEQTxUJTOzy6rY1VowWC0mmNWoz993KVc3H3rYq8TklSlRQh1d1LS647AyR0wgRMbAsN/GRDpTJTKwzKrX1l4vVy4lldWpzt97KVY4IXbUr8LZljVPRCxhw7W3z6ZISkw1VK+4sMWvXEhSPk6buay9sm5JVkZNiLWqtrJ+TldNTnquqbCwilZXU1BwpKirrpNgWFhTaZmnpquZbFlbVmWOpaOonHZcXlljhaGhpZ1+YWBdYn2cn6GdhmdhYGN3lp2enIttY2Jjco+bnJuOdGZlZXCImJqakHpoZ2Zug5WYmZJ/bGlobX6RlpeSg3BqaW16jZSVkoZ0bGtteImSk5KIeG5tbnaFkJKRinxxbm91gY2QkIt/c3BwdH6Kj4+LgnZxcXR8iI2OjIR5c3J0e4WLjYuFe3VzdHmCioyLhn52dHR5gIiKioeAeHV1eH+GiYqHgXp2dnh9hIiJh4J8eHd4fIKHiIeDfXl4eHyBhoeHhH96eHmA', - cb: { - online: function() { - if (ThreadUpdater.online = navigator.onLine) { - ThreadUpdater.outdateCount = 0; - ThreadUpdater.setInterval(); - ThreadUpdater.set('status', null, null); - return; - } - ThreadUpdater.set('timer', null); - return ThreadUpdater.set('status', 'Offline', 'warning'); - }, - post: function(e) { - if (!(ThreadUpdater.isUpdating && e.detail.threadID === ThreadUpdater.thread.ID)) { - return; - } - ThreadUpdater.outdateCount = 0; - if (ThreadUpdater.seconds > 2) { - return setTimeout(ThreadUpdater.update, 1000); - } - }, - checkpost: function(e) { - if (!ThreadUpdater.checkPostCount) { - if (e.detail.threadID !== ThreadUpdater.thread.ID) { - return; - } - ThreadUpdater.seconds = 0; - ThreadUpdater.outdateCount = 0; - ThreadUpdater.set('timer', '...'); - } - if (!(g.DEAD || ThreadUpdater.foundPost || ThreadUpdater.checkPostCount >= 5)) { - return setTimeout(ThreadUpdater.update, ++ThreadUpdater.checkPostCount * $.SECOND); - } - ThreadUpdater.setInterval(); - ThreadUpdater.checkPostCount = 0; - delete ThreadUpdater.foundPost; - return delete ThreadUpdater.postID; - }, - visibility: function() { - if (d.hidden) { - return; - } - ThreadUpdater.outdateCount = 0; - if (ThreadUpdater.seconds > ThreadUpdater.interval) { - return ThreadUpdater.setInterval(); - } - }, - scrollBG: function() { - return ThreadUpdater.scrollBG = Conf['Scroll BG'] ? function() { - return true; - } : function() { - return !d.hidden; - }; - }, - interval: function() { - var val; - val = parseInt(this.value, 10); - if (val < 1) { - val = 1; - } - ThreadUpdater.interval = this.value = val; - return $.cb.value.call(this); - }, - load: function(e) { - var klass, req, text, _ref; - req = ThreadUpdater.req; - switch (req.status) { - case 200: - g.DEAD = false; - ThreadUpdater.parse(req.response.posts); - ThreadUpdater.setInterval(); - break; - case 404: - g.DEAD = true; - ThreadUpdater.set('timer', null); - ThreadUpdater.set('status', '404', 'warning'); - clearTimeout(ThreadUpdater.timeoutID); - ThreadUpdater.thread.kill(); - $.event('ThreadUpdate', { - 404: true, - thread: ThreadUpdater.thread - }); - break; - default: - ThreadUpdater.outdateCount++; - ThreadUpdater.setInterval(); - _ref = req.status === 304 ? [null, null] : ["" + req.statusText + " (" + req.status + ")", 'warning'], text = _ref[0], klass = _ref[1]; - ThreadUpdater.set('status', text, klass); - } - if (ThreadUpdater.postID) { - return ThreadUpdater.cb.checkpost(); - } - } - }, - setInterval: function() { - var cur, i, j, limit; - i = ThreadUpdater.interval + 1; - if (Conf['Optional Increase']) { - cur = ThreadUpdater.outdateCount || 1; - limit = d.hidden ? 7 : 10; - j = cur <= limit ? cur : limit; - cur = (Math.floor(i * 0.1) || 1) * j * j; - ThreadUpdater.seconds = cur > i ? cur <= 300 ? cur : 300 : i; - } else { - ThreadUpdater.seconds = i; - } - ThreadUpdater.set('timer', ThreadUpdater.seconds); - return ThreadUpdater.count(true); - }, - intervalShortcut: function() { - var settings; - Settings.open('Advanced'); - settings = $.id('fourchanx-settings'); - return $('input[name=Interval]', settings).focus(); - }, - set: function(name, text, klass) { - var el, node; - el = ThreadUpdater[name]; - if (node = el.firstChild) { - node.data = text; - } else { - el.textContent = text; - } - if (klass !== void 0) { - return el.className = klass; - } - }, - count: function(start) { - clearTimeout(ThreadUpdater.timeoutID); - if (start && ThreadUpdater.isUpdating && navigator.onLine) { - return ThreadUpdater.timeout(); - } - }, - timeout: function() { - var n; - ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.timeout, 1000); - if (!(n = --ThreadUpdater.seconds)) { - return ThreadUpdater.update(); - } else if (n <= -60) { - ThreadUpdater.set('status', 'Retrying', null); - return ThreadUpdater.update(); - } else if (n > 0) { - return ThreadUpdater.set('timer', n); - } - }, - update: function() { - var url, _ref; - if (!navigator.onLine) { - return; - } - ThreadUpdater.count(); - if (Conf['Auto Update']) { - ThreadUpdater.set('timer', '...'); - } else { - ThreadUpdater.set('timer', 'Update'); - } - if ((_ref = ThreadUpdater.req) != null) { - _ref.abort(); - } - url = "//a.4cdn.org/" + ThreadUpdater.thread.board + "/res/" + ThreadUpdater.thread + ".json"; - return ThreadUpdater.req = $.ajax(url, { - onloadend: ThreadUpdater.cb.load - }, { - whenModified: true - }); - }, - updateThreadStatus: function(type, status) { - var change, hasChanged; - if (!(hasChanged = ThreadUpdater.thread["is" + type] !== status)) { - return; - } - ThreadUpdater.thread.setStatus(type, status); - change = type === 'Sticky' ? status ? 'now a sticky' : 'not a sticky anymore' : status ? 'now closed' : 'not closed anymore'; - return new Notice('info', "The thread is " + change + ".", 30); - }, - parse: function(postObjects) { - var OP, count, deletedFiles, deletedPosts, files, index, node, num, post, postObject, posts, root, scroll, _i, _j, _len, _len1; - OP = postObjects[0]; - Build.spoilerRange[ThreadUpdater.thread.board] = OP.custom_spoiler; - ThreadUpdater.updateThreadStatus('Sticky', !!OP.sticky); - ThreadUpdater.updateThreadStatus('Closed', !!OP.closed); - ThreadUpdater.thread.postLimit = !!OP.bumplimit; - ThreadUpdater.thread.fileLimit = !!OP.imagelimit; - posts = []; - index = []; - files = []; - count = 0; - for (_i = 0, _len = postObjects.length; _i < _len; _i++) { - postObject = postObjects[_i]; - num = postObject.no; - index.push(num); - if (postObject.fsize) { - files.push(num); - } - if (num <= ThreadUpdater.lastPost) { - continue; - } - count++; - node = Build.postFromObject(postObject, ThreadUpdater.thread.board.ID); - posts.push(new Post(node, ThreadUpdater.thread, ThreadUpdater.thread.board)); - } - deletedPosts = []; - deletedFiles = []; - ThreadUpdater.thread.posts.forEach(function(post) { - var ID; - ID = +post.ID; - if (__indexOf.call(index, ID) < 0) { - post.kill(); - deletedPosts.push(post); - } else if (post.isDead) { - post.resurrect(); - } else if (post.file && !(post.file.isDead || __indexOf.call(files, ID) >= 0)) { - post.kill(true); - deletedFiles.push(post); - } - if (ThreadUpdater.postID && ThreadUpdater.postID === ID) { - return ThreadUpdater.foundPost = true; - } - }); - if (!count) { - ThreadUpdater.set('status', null, null); - ThreadUpdater.outdateCount++; - } else { - ThreadUpdater.set('status', "+" + count, 'new'); - ThreadUpdater.outdateCount = 0; - if (Conf['Beep'] && d.hidden && Unread.posts && !Unread.posts.length) { - if (!ThreadUpdater.audio) { - ThreadUpdater.audio = $.el('audio', { - src: ThreadUpdater.beep - }); - } - ThreadUpdater.audio.play(); - } - ThreadUpdater.lastPost = posts[count - 1].ID; - Main.callbackNodes(Post, posts); - scroll = Conf['Auto Scroll'] && ThreadUpdater.scrollBG() && ThreadUpdater.root.getBoundingClientRect().bottom - doc.clientHeight < 25; - for (_j = 0, _len1 = posts.length; _j < _len1; _j++) { - post = posts[_j]; - root = post.nodes.root; - if (post.cb) { - if (!post.cb()) { - $.add(ThreadUpdater.root, root); - } - } else { - $.add(ThreadUpdater.root, root); - } - } - if (scroll) { - if (Conf['Bottom Scroll']) { - window.scrollTo(0, d.body.clientHeight); - } else { - if (root) { - Header.scrollTo(root); - } - } - } - $.queueTask(function() { - var length, threadID; - threadID = ThreadUpdater.thread.ID; - length = $$('.thread > .postContainer', ThreadUpdater.root).length; - return Fourchan.parseThread(threadID, length - count, length); - }); - } - return $.event('ThreadUpdate', { - 404: false, - thread: ThreadUpdater.thread, - newPosts: posts, - deletedPosts: deletedPosts, - deletedFiles: deletedFiles, - postCount: OP.replies + 1, - fileCount: OP.images + (!!ThreadUpdater.thread.OP.file && !ThreadUpdater.thread.OP.file.isDead) - }); - } - }; - - ThreadWatcher = { - init: function() { - var now, sc; - if (!Conf['Thread Watcher']) { - return; - } - this.shortcut = sc = $.el('a', { - id: 'watcher-link', - textContent: 'Watcher', - href: 'javascript:;', - className: 'disabled fa fa-eye' - }); - this.db = new DataBoard('watchedThreads', this.refresh, true); - this.dialog = UI.dialog('thread-watcher', 'top: 50px; left: 0px;', "
Thread Watcher ×
"); - this.status = $('#watcher-status', this.dialog); - this.list = this.dialog.lastElementChild; - $.on(d, 'QRPostSuccessful', this.cb.post); - if (g.VIEW === 'thread') { - $.on(d, 'ThreadUpdate', this.cb.threadUpdate); - } - $.on(sc, 'click', this.toggleWatcher); - $.on($('.move>.close', ThreadWatcher.dialog), 'click', this.toggleWatcher); - $.on(d, '4chanXInitFinished', this.ready); - switch (g.VIEW) { - case 'index': - $.on(d, 'IndexRefresh', this.cb.onIndexRefresh); - break; - case 'thread': - $.on(d, 'ThreadUpdate', this.cb.onThreadRefresh); - } - if (Conf['Toggleable Thread Watcher']) { - Header.addShortcut(sc); - $.addClass(doc, 'fixed-watcher'); - } - now = Date.now(); - if ((this.db.data.lastChecked || 0) < now - 2 * $.HOUR) { - this.db.data.lastChecked = now; - ThreadWatcher.fetchAllStatus(); - this.db.save(); - } - return Thread.callbacks.push({ - name: 'Thread Watcher', - cb: this.node - }); - }, - node: function() { - var toggler; - toggler = $.el('img', { - className: 'watch-thread-link' - }); - $.on(toggler, 'click', ThreadWatcher.cb.toggle); - return $.before($('input', this.OP.nodes.post), toggler); - }, - ready: function() { - $.off(d, '4chanXInitFinished', ThreadWatcher.ready); - if (!Main.isThisPageLegit()) { - return; - } - ThreadWatcher.refresh(); - $.add(d.body, ThreadWatcher.dialog); - if (Conf['Toggleable Thread Watcher']) { - ThreadWatcher.dialog.hidden = true; - } - if (!Conf['Auto Watch']) { - return; - } - return $.get('AutoWatch', 0, function(_arg) { - var AutoWatch, thread; - AutoWatch = _arg.AutoWatch; - if (!(thread = g.BOARD.threads[AutoWatch])) { - return; - } - ThreadWatcher.add(thread); - return $["delete"]('AutoWatch'); - }); - }, - toggleWatcher: function() { - $.toggleClass(ThreadWatcher.shortcut, 'disabled'); - return ThreadWatcher.dialog.hidden = !ThreadWatcher.dialog.hidden; - }, - cb: { - openAll: function() { - var a, _i, _len, _ref; - if ($.hasClass(this, 'disabled')) { - return; - } - _ref = $$('a[title]', ThreadWatcher.list); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - a = _ref[_i]; - $.open(a.href); - } - return $.event('CloseMenu'); - }, - checkThreads: function() { - if ($.hasClass(this, 'disabled')) { - return; - } - return ThreadWatcher.fetchAllStatus(); - }, - pruneDeads: function() { - var boardID, data, threadID, _i, _len, _ref, _ref1; - if ($.hasClass(this, 'disabled')) { - return; - } - _ref = ThreadWatcher.getAll(); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - _ref1 = _ref[_i], boardID = _ref1.boardID, threadID = _ref1.threadID, data = _ref1.data; - if (!data.isDead) { - continue; - } - delete ThreadWatcher.db.data.boards[boardID][threadID]; - ThreadWatcher.db.deleteIfEmpty({ - boardID: boardID - }); - } - ThreadWatcher.db.save(); - ThreadWatcher.refresh(); - return $.event('CloseMenu'); - }, - toggle: function() { - return ThreadWatcher.toggle(Get.postFromNode(this).thread); - }, - rm: function() { - var boardID, threadID, _ref; - _ref = this.parentNode.dataset.fullID.split('.'), boardID = _ref[0], threadID = _ref[1]; - return ThreadWatcher.rm(boardID, +threadID); - }, - post: function(e) { - var board, postID, threadID, _ref; - _ref = e.detail, board = _ref.board, postID = _ref.postID, threadID = _ref.threadID; - if (postID === threadID) { - if (Conf['Auto Watch']) { - return $.set('AutoWatch', threadID); - } - } else if (Conf['Auto Watch Reply']) { - return ThreadWatcher.add(board.threads[threadID]); - } - }, - onIndexRefresh: function() { - var boardID, data, db, threadID, _ref; - db = ThreadWatcher.db; - boardID = g.BOARD.ID; - _ref = db.data.boards[boardID]; - for (threadID in _ref) { - data = _ref[threadID]; - if (!data.isDead && !(threadID in g.BOARD.threads)) { - if (Conf['Auto Prune']) { - ThreadWatcher.db["delete"]({ - boardID: boardID, - threadID: threadID - }); - } else { - data.isDead = true; - ThreadWatcher.db.set({ - boardID: boardID, - threadID: threadID, - val: data - }); - } - } - } - return ThreadWatcher.refresh(); - }, - onThreadRefresh: function(e) { - var thread; - thread = e.detail.thread; - if (!(e.detail[404] && ThreadWatcher.db.get({ - boardID: thread.board.ID, - threadID: thread.ID - }))) { - return; - } - return ThreadWatcher.add(thread); - } - }, - fetchCount: { - fetched: 0, - fetching: 0 - }, - fetchAllStatus: function() { - var thread, threads, _i, _len; - if (!(threads = ThreadWatcher.getAll()).length) { - return; - } - ThreadWatcher.status.textContent = '...'; - for (_i = 0, _len = threads.length; _i < _len; _i++) { - thread = threads[_i]; - ThreadWatcher.fetchStatus(thread); - } - }, - fetchStatus: function(_arg) { - var boardID, data, fetchCount, threadID; - boardID = _arg.boardID, threadID = _arg.threadID, data = _arg.data; - if (data.isDead) { - return; - } - fetchCount = ThreadWatcher.fetchCount; - fetchCount.fetching++; - return $.ajax("//a.4cdn.org/" + boardID + "/res/" + threadID + ".json", { - onloadend: function() { - var status; - fetchCount.fetched++; - if (fetchCount.fetched === fetchCount.fetching) { - fetchCount.fetched = 0; - fetchCount.fetching = 0; - status = ''; - } else { - status = "" + (Math.round(fetchCount.fetched / fetchCount.fetching * 100)) + "%"; - } - ThreadWatcher.status.textContent = status; - if (this.status !== 404) { - return; - } - if (Conf['Auto Prune']) { - ThreadWatcher.db["delete"]({ - boardID: boardID, - threadID: threadID - }); - } else { - data.isDead = true; - ThreadWatcher.db.set({ - boardID: boardID, - threadID: threadID, - val: data - }); - } - return ThreadWatcher.refresh(); - } - }, { - type: 'head' - }); - }, - getAll: function() { - var all, boardID, data, threadID, threads, _ref; - all = []; - _ref = ThreadWatcher.db.data.boards; - for (boardID in _ref) { - threads = _ref[boardID]; - if (Conf['Current Board'] && boardID !== g.BOARD.ID) { - continue; - } - for (threadID in threads) { - data = threads[threadID]; - all.push({ - boardID: boardID, - threadID: threadID, - data: data - }); - } - } - return all; - }, - makeLine: function(boardID, threadID, data) { - var div, fullID, href, link, x; - x = $.el('a', { - className: 'fa fa-times', - href: 'javascript:;' - }); - $.on(x, 'click', ThreadWatcher.cb.rm); - if (data.isDead) { - href = Redirect.to('thread', { - boardID: boardID, - threadID: threadID - }); - } - link = $.el('a', { - href: href || ("/" + boardID + "/res/" + threadID), - textContent: data.excerpt, - title: data.excerpt - }); - div = $.el('div'); - fullID = "" + boardID + "." + threadID; - div.dataset.fullID = fullID; - if (g.VIEW === 'thread' && fullID === ("" + g.BOARD + "." + g.THREADID)) { - $.addClass(div, 'current'); - } - if (data.isDead) { - $.addClass(div, 'dead-thread'); - } - $.add(div, [x, $.tn(' '), link]); - return div; - }, - refresh: function() { - var boardID, data, helper, list, nodes, refresher, thread, threadID, threads, toggler, watched, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3; - nodes = []; - _ref = ThreadWatcher.getAll(); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - _ref1 = _ref[_i], boardID = _ref1.boardID, threadID = _ref1.threadID, data = _ref1.data; - nodes.push(ThreadWatcher.makeLine(boardID, threadID, data)); - } - list = ThreadWatcher.list; - $.rmAll(list); - $.add(list, nodes); - threads = g.BOARD.threads; - _ref2 = threads.keys; - for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) { - threadID = _ref2[_j]; - thread = threads[threadID]; - toggler = $('.watch-thread-link', thread.OP.nodes.post); - watched = ThreadWatcher.db.get({ - boardID: thread.board.ID, - threadID: threadID - }); - helper = watched ? ['addClass', 'Unwatch'] : ['rmClass', 'Watch']; - $[helper[0]](toggler, 'watched'); - toggler.title = "" + helper[1] + " Thread"; - } - _ref3 = ThreadWatcher.menu.refreshers; - for (_k = 0, _len2 = _ref3.length; _k < _len2; _k++) { - refresher = _ref3[_k]; - refresher(); - } - }, - toggle: function(thread) { - var boardID, threadID; - boardID = thread.board.ID; - threadID = thread.ID; - if (ThreadWatcher.db.get({ - boardID: boardID, - threadID: threadID - })) { - return ThreadWatcher.rm(boardID, threadID); - } else { - return ThreadWatcher.add(thread); - } - }, - add: function(thread) { - var boardID, data, threadID; - data = {}; - boardID = thread.board.ID; - threadID = thread.ID; - if (thread.isDead) { - if (Conf['Auto Prune'] && ThreadWatcher.db.get({ - boardID: boardID, - threadID: threadID - })) { - ThreadWatcher.rm(boardID, threadID); - return; - } - data.isDead = true; - } - data.excerpt = Get.threadExcerpt(thread); - ThreadWatcher.db.set({ - boardID: boardID, - threadID: threadID, - val: data - }); - return ThreadWatcher.refresh(); - }, - rm: function(boardID, threadID) { - ThreadWatcher.db["delete"]({ - boardID: boardID, - threadID: threadID - }); - return ThreadWatcher.refresh(); - }, - convert: function(oldFormat) { - var boardID, data, newFormat, threadID, threads; - newFormat = {}; - for (boardID in oldFormat) { - threads = oldFormat[boardID]; - for (threadID in threads) { - data = threads[threadID]; - (newFormat[boardID] || (newFormat[boardID] = {}))[threadID] = { - excerpt: data.textContent - }; - } - } - return newFormat; - }, - menu: { - refreshers: [], - init: function() { - var menu; - if (!Conf['Thread Watcher']) { - return; - } - menu = new UI.Menu('thread watcher'); - $.on($('.menu-button', ThreadWatcher.dialog), 'click', function(e) { - return menu.toggle(e, this, ThreadWatcher); - }); - this.addHeaderMenuEntry(); - return this.addMenuEntries(); - }, - addHeaderMenuEntry: function() { - var entryEl; - if (g.VIEW !== 'thread') { - return; - } - entryEl = $.el('a', { - href: 'javascript:;' - }); - $.event('AddMenuEntry', { - type: 'header', - el: entryEl, - order: 60 - }); - $.on(entryEl, 'click', function() { - return ThreadWatcher.toggle(g.threads["" + g.BOARD + "." + g.THREADID]); - }); - return this.refreshers.push(function() { - var addClass, rmClass, text, _ref; - _ref = $('.current', ThreadWatcher.list) ? ['unwatch-thread', 'watch-thread', 'Unwatch thread'] : ['watch-thread', 'unwatch-thread', 'Watch thread'], addClass = _ref[0], rmClass = _ref[1], text = _ref[2]; - $.addClass(entryEl, addClass); - $.rmClass(entryEl, rmClass); - return entryEl.textContent = text; - }); - }, - addMenuEntries: function() { - var cb, conf, entries, entry, name, refresh, subEntries, _i, _len, _ref, _ref1; - entries = []; - entries.push({ - cb: ThreadWatcher.cb.openAll, - entry: { - type: 'thread watcher', - el: $.el('a', { - textContent: 'Open all threads' - }) - }, - refresh: function() { - return (ThreadWatcher.list.firstElementChild ? $.rmClass : $.addClass)(this.el, 'disabled'); - } - }); - entries.push({ - cb: ThreadWatcher.cb.checkThreads, - entry: { - type: 'thread watcher', - el: $.el('a', { - textContent: 'Check 404\'d threads' - }) - }, - refresh: function() { - return ($('div:not(.dead-thread)', ThreadWatcher.list) ? $.rmClass : $.addClass)(this.el, 'disabled'); - } - }); - entries.push({ - cb: ThreadWatcher.cb.pruneDeads, - entry: { - type: 'thread watcher', - el: $.el('a', { - textContent: 'Prune 404\'d threads' - }) - }, - refresh: function() { - return ($('.dead-thread', ThreadWatcher.list) ? $.rmClass : $.addClass)(this.el, 'disabled'); - } - }); - subEntries = []; - _ref = Config.threadWatcher; - for (name in _ref) { - conf = _ref[name]; - subEntries.push(this.createSubEntry(name, conf[1])); - } - entries.push({ - entry: { - type: 'thread watcher', - el: $.el('span', { - textContent: 'Settings' - }), - subEntries: subEntries - } - }); - for (_i = 0, _len = entries.length; _i < _len; _i++) { - _ref1 = entries[_i], entry = _ref1.entry, cb = _ref1.cb, refresh = _ref1.refresh; - if (entry.el.nodeName === 'A') { - entry.el.href = 'javascript:;'; - } - if (cb) { - $.on(entry.el, 'click', cb); - } - if (refresh) { - this.refreshers.push(refresh.bind(entry)); - } - $.event('AddMenuEntry', entry); - } - }, - createSubEntry: function(name, desc) { - var entry, input; - entry = { - type: 'thread watcher', - el: $.el('label', { - innerHTML: " " + name, - title: desc - }) - }; - input = entry.el.firstElementChild; - input.checked = Conf[name]; - $.on(input, 'change', $.cb.checked); - if (name === 'Current Board') { - $.on(input, 'change', ThreadWatcher.refresh); - } - return entry; - } - } - }; - - Unread = { - init: function() { - if (g.VIEW !== 'thread' || !Conf['Unread Count'] && !Conf['Unread Favicon'] && !Conf['Desktop Notifications']) { - return; - } - this.db = new DataBoard('lastReadPosts', this.sync); - this.hr = $.el('hr', { - id: 'unread-line' - }); - this.posts = new RandomAccessList; - this.postsQuotingYou = []; - return Thread.callbacks.push({ - name: 'Unread', - cb: this.node - }); - }, - disconnect: function() { - var hr, name, _i, _len, _ref; - if (g.VIEW !== 'thread' || !Conf['Unread Count'] && !Conf['Unread Favicon'] && !Conf['Desktop Notifications']) { - return; - } - Unread.db.disconnect(); - if (hr = Unread.hr, Unread) { - $.rm(hr); - } - _ref = ['db', 'hr', 'posts', 'postsQuotingYou', 'thread', 'title', 'lastReadPost']; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - name = _ref[_i]; - delete this[name]; - } - $.off(d, '4chanXInitFinished', this.ready); - $.off(d, 'ThreadUpdate', this.onUpdate); - $.off(d, 'scroll visibilitychange', this.read); - if (Conf['Unread Line']) { - $.off(d, 'visibilitychange', this.setLine); - } - return Thread.callbacks.disconnect('Unread'); - }, - node: function() { - Unread.thread = this; - Unread.title = d.title; - Unread.lastReadPost = Unread.db.get({ - boardID: this.board.ID, - threadID: this.ID, - defaultValue: 0 - }); - $.on(d, '4chanXInitFinished', Unread.ready); - $.on(d, 'ThreadUpdate', Unread.onUpdate); - $.on(d, 'scroll visibilitychange', Unread.read); - if (Conf['Unread Line']) { - return $.on(d, 'visibilitychange', Unread.setLine); - } - }, - ready: function() { - var posts; - $.off(d, '4chanXInitFinished', Unread.ready); - if (!Conf['Quote Threading']) { - posts = []; - Unread.thread.posts.forEach(function(post) { - if (post.isReply) { - return posts.push(post); - } - }); - Unread.addPosts(posts); - } - if (Conf['Quote Threading']) { - QuoteThreading.force(); - } - if (Conf['Scroll to Last Read Post']) { - return Unread.scroll(); - } - }, - scroll: function() { - var down, hash, keys, post, posts, root; - if ((hash = location.hash.match(/\d+/)) && hash[0] in Unread.thread.posts) { - return; - } - if (post = Unread.posts.first) { - while (root = $.x('preceding-sibling::div[contains(@class,"replyContainer")][1]', post.data.nodes.root)) { - if (!(post = Get.postFromRoot(root)).isHidden) { - break; - } - } - if (!root) { - return; - } - down = true; - } else { - posts = Unread.thread.posts; - keys = posts.keys; - root = posts[keys[keys.length - 1]].nodes.root; - } - if (Header.getBottomOf(root) < 0) { - return Header.scrollTo(root, down); - } - }, - sync: function() { - var ID, lastReadPost, post; - lastReadPost = Unread.db.get({ - boardID: Unread.thread.board.ID, - threadID: Unread.thread.ID, - defaultValue: 0 - }); - if (!(Unread.lastReadPost < lastReadPost)) { - return; - } - Unread.lastReadPost = lastReadPost; - post = Unread.posts.first; - while (post) { - if ((ID = post.ID, post) > Unread.lastReadPost) { - break; - } - post = post.next; - Unread.posts.rm(ID); - } - Unread.readArray(Unread.postsQuotingYou); - if (Conf['Unread Line']) { - Unread.setLine(); - } - return Unread.update(); - }, - addPosts: function(posts) { - var ID, post, _i, _len, _ref, _ref1; - for (_i = 0, _len = posts.length; _i < _len; _i++) { - post = posts[_i]; - ID = post.ID; - if (ID <= Unread.lastReadPost || post.isHidden || QR.db.get({ - boardID: post.board.ID, - threadID: post.thread.ID, - postID: ID - })) { - continue; - } - Unread.posts.push(post); - Unread.addPostQuotingYou(post); - } - if (Conf['Unread Line']) { - Unread.setLine((_ref = (_ref1 = Unread.posts.first) != null ? _ref1.data : void 0, __indexOf.call(posts, _ref) >= 0)); - } - Unread.read(); - return Unread.update(); - }, - addPostQuotingYou: function(post) { - var quotelink, _i, _len, _ref; - _ref = post.nodes.quotelinks; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - quotelink = _ref[_i]; - if (!(QR.db.get(Get.postDataFromLink(quotelink)))) { - continue; - } - Unread.postsQuotingYou.push(post); - Unread.openNotification(post); - return; - } - }, - openNotification: function(post) { - var name, notif; - if (!Header.areNotificationsEnabled) { - return; - } - name = Conf['Anonymize'] ? 'Anonymous' : $('.nameBlock', post.nodes.info).textContent.trim(); - notif = new Notification("" + name + " replied to you", { - body: post.info.comment, - icon: Favicon.logo - }); - notif.onclick = function() { - Header.scrollToIfNeeded(post.nodes.root, true); - return window.focus(); - }; - return notif.onshow = function() { - return setTimeout(function() { - return notif.close(); - }, 7 * $.SECOND); - }; - }, - onUpdate: function(e) { - if (e.detail[404]) { - return Unread.update(); - } else if (!Conf['Quote Threading']) { - return Unread.addPosts(e.detail.newPosts); - } else { - Unread.read(); - return Unread.update(); - } - }, - readSinglePost: function(post) { - var ID, i, posts; - ID = post.ID; - posts = Unread.posts; - if (!posts[ID]) { - return; - } - if (post === posts.first) { - Unread.lastReadPost = ID; - Unread.saveLastReadPost(); - } - posts.rm(ID); - if ((i = Unread.postsQuotingYou.indexOf(post)) !== -1) { - Unread.postsQuotingYou.splice(i, 1); - } - return Unread.update(); - }, - readArray: function(arr) { - var i, post, _i, _len; - for (i = _i = 0, _len = arr.length; _i < _len; i = ++_i) { - post = arr[i]; - if (post.ID > Unread.lastReadPost) { - break; - } - } - return arr.splice(0, i); - }, - read: $.debounce(100, function(e) { - var ID, data, height, post, posts; - if (d.hidden || !Unread.posts.length) { - return; - } - height = doc.clientHeight; - posts = Unread.posts; - while (post = posts.first) { - if (!(Header.getBottomOf(post.data.nodes.root) > -1)) { - break; - } - ID = post.ID, data = post.data; - posts.rm(ID); - if (Conf['Mark Quotes of You'] && QR.db.get({ - boardID: data.board.ID, - threadID: data.thread.ID, - postID: ID - })) { - QuoteYou.lastRead = data.nodes.root; - } - } - if (!ID) { - return; - } - if (Unread.lastReadPost < ID || !Unread.lastReadPost) { - Unread.lastReadPost = ID; - } - Unread.saveLastReadPost(); - Unread.readArray(Unread.postsQuotingYou); - if (e) { - return Unread.update(); - } - }), - saveLastReadPost: $.debounce(2 * $.SECOND, function() { - if (Unread.thread.isDead) { - return; - } - return Unread.db.set({ - boardID: Unread.thread.board.ID, - threadID: Unread.thread.ID, - val: Unread.lastReadPost - }); - }), - setLine: function(force) { - var post; - if (!(d.hidden || force === true)) { - return; - } - if (!(post = Unread.posts.first)) { - return $.rm(Unread.hr); - } - if ($.x('preceding-sibling::div[contains(@class,"replyContainer")]', post.data.nodes.root)) { - return $.before(post.data.nodes.root, Unread.hr); - } - }, - update: function() { - var count; - count = Unread.posts.length; - if (Conf['Unread Count']) { - d.title = "" + (Conf['Quoted Title'] && Unread.postsQuotingYou.length ? '(!) ' : '') + (count || !Conf['Hide Unread Count at (0)'] ? "(" + count + ") " : '') + (g.DEAD ? "/" + g.BOARD + "/ - 404" : "" + Unread.title); - } - if (!Conf['Unread Favicon']) { - return; - } - Favicon.el.href = g.DEAD ? Unread.postsQuotingYou[0] ? Favicon.unreadDeadY : count ? Favicon.unreadDead : Favicon.dead : count ? Unread.postsQuotingYou[0] ? Favicon.unreadY : Favicon.unread : Favicon["default"]; - return $.add(d.head, Favicon.el); - } - }; - - Redirect = { - init: function() { - var archive, archives, boardID, boards, data, files, id, name, o, record, software, type, _i, _j, _len, _len1, _ref, _ref1, _ref2; - o = { - thread: {}, - post: {}, - file: {} - }; - archives = {}; - _ref = Redirect.archives; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - _ref1 = _ref[_i], name = _ref1.name, boards = _ref1.boards, files = _ref1.files, data = _ref1.data; - archives[name] = { - boards: boards, - files: files, - data: data - }; - software = data.software; - for (_j = 0, _len1 = boards.length; _j < _len1; _j++) { - boardID = boards[_j]; - if (!(boardID in o.thread)) { - o.thread[boardID] = data; - } - if (!(boardID in o.post || software !== 'foolfuuka')) { - o.post[boardID] = data; - } - if (!(boardID in o.file || __indexOf.call(files, boardID) < 0)) { - o.file[boardID] = data; - } - } - } - _ref2 = Conf['selectedArchives']; - for (boardID in _ref2) { - record = _ref2[boardID]; - for (type in record) { - id = record[type]; - if (!((archive = archives[id]))) { - continue; - } - boards = type === 'file' ? archive.files : archive.boards; - if (__indexOf.call(boards, boardID) < 0) { - continue; - } - o[type][boardID] = archive.data; - } - } - return Redirect.data = o; - }, - archives: [ - { - name: "Foolz", - boards: ["a", "biz", "co", "diy", "gd", "jp", "m", "sci", "sp", "tg", "tv", "v", "vg", "vp", "vr", "wsg"], - files: ["a", "biz", "diy", "gd", "jp", "m", "sci", "tg", "vg", "vp", "vr", "wsg"], - data: { - domain: "archive.foolz.us", - http: false, - https: true, - software: "foolfuuka" - } - }, { - name: "NSFW Foolz", - boards: ["u"], - files: ["u"], - data: { - domain: "nsfw.foolz.us", - http: false, - https: true, - software: "foolfuuka" - } - }, { - name: "The Dark Cave", - boards: ["c", "int", "out", "po"], - files: ["c", "po"], - data: { - domain: "archive.thedarkcave.org", - http: true, - https: true, - software: "foolfuuka" - } - }, { - name: "4plebs", - boards: ["adv", "hr", "o", "pol", "s4s", "tg", "tv", "x"], - files: ["adv", "hr", "o", "pol", "s4s", "tg", "tv", "x"], - data: { - domain: "archive.4plebs.org", - http: true, - https: true, - software: "foolfuuka" - } - }, { - name: "Nyafuu", - boards: ["c", "e", "w", "wg"], - files: ["c", "e", "w", "wg"], - data: { - domain: "archive.nyafuu.org", - http: true, - https: true, - software: "foolfuuka" - } - }, { - name: "Love is Over", - boards: ["d", "i"], - files: ["d", "i"], - data: { - domain: "loveisover.me", - http: true, - https: true, - software: "foolfuuka" - } - }, { - name: "Rebecca Black Tech", - boards: ["cgl", "g", "mu", "w"], - files: ["cgl", "g", "mu", "w"], - data: { - domain: "archive.rebeccablacktech.com", - http: true, - https: true, - software: "fuuka" - } - }, { - name: "Heinessen", - boards: ["an", "fit", "k", "mlp", "r9k", "toy"], - files: ["an", "fit", "k", "r9k", "toy"], - data: { - domain: "archive.heinessen.com", - http: true, - software: "fuuka" - } - }, { - name: "warosu", - boards: ["3", "biz", "cgl", "ck", "diy", "fa", "g", "ic", "jp", "lit", "sci", "tg", "vr"], - files: ["3", "biz", "cgl", "ck", "diy", "fa", "ic", "jp", "lit", "sci", "tg", "vr"], - data: { - domain: "fuuka.warosu.org", - https: true, - software: "fuuka" - } - }, { - name: "fgts", - boards: ["r", "soc"], - files: ["r", "soc"], - data: { - domain: "fgst.eu", - http: true, - https: true, - software: "foolfuuka" - } - }, { - name: "maware", - boards: ["t"], - files: ["t"], - data: { - domain: "archive.mawa.re", - http: true, - software: "foolfuuka" - } - }, { - name: "InstallGentoo", - boards: ["g", "t"], - files: ["g", "t"], - data: { - domain: "chan.installgentoo.com", - http: true, - software: "foolfuuka" - } - }, { - name: "Foolz Beta", - boards: ["a", "biz", "co", "d", "diy", "gd", "jp", "m", "mlp", "s4s", "sci", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"], - files: ["a", "biz", "d", "diy", "gd", "jp", "m", "s4s", "sci", "tg", "u", "vg", "vp", "vr", "wsg"], - data: { - domain: "beta.foolz.us", - http: true, - https: true, - withCredentials: true, - software: "foolfuuka" - } - } - ], - to: function(dest, data) { - var archive; - archive = (dest === 'search' ? Redirect.data.thread : Redirect.data[dest])[data.boardID]; - if (!archive) { - return ''; - } - return Redirect[dest](archive, data); - }, - protocol: function(archive) { - var protocol; - protocol = location.protocol; - if (!archive[protocol.slice(0, -1)]) { - protocol = protocol === 'https:' ? 'http:' : 'https:'; - } - return "" + protocol + "//"; - }, - thread: function(archive, _arg) { - var boardID, path, postID, threadID; - boardID = _arg.boardID, threadID = _arg.threadID, postID = _arg.postID; - path = threadID ? "" + boardID + "/thread/" + threadID : "" + boardID + "/post/" + postID; - if (archive.software === 'foolfuuka') { - path += '/'; - } - if (threadID && postID) { - path += archive.software === 'foolfuuka' ? "#" + postID : "#p" + postID; - } - return "" + (Redirect.protocol(archive)) + archive.domain + "/" + path; - }, - post: function(archive, _arg) { - var URL, boardID, postID; - boardID = _arg.boardID, postID = _arg.postID; - URL = new String("" + (Redirect.protocol(archive)) + archive.domain + "/_/api/chan/post/?board=" + boardID + "&num=" + postID); - URL.archive = archive; - return URL; - }, - file: function(archive, _arg) { - var boardID, filename; - boardID = _arg.boardID, filename = _arg.filename; - return "" + (Redirect.protocol(archive)) + archive.domain + "/" + boardID + "/full_image/" + filename; - }, - search: function(archive, _arg) { - var boardID, path, type, value; - boardID = _arg.boardID, type = _arg.type, value = _arg.value; - type = type === 'name' ? 'username' : type === 'MD5' ? 'image' : type; - value = encodeURIComponent(value); - path = archive.software === 'foolfuuka' ? "" + boardID + "/search/" + type + "/" + value : "" + boardID + "/?task=search2&search_" + (type === 'image' ? 'media_hash' : type) + "=" + value; - return "" + (Redirect.protocol(archive)) + archive.domain + "/" + path; - } - }; - - PSAHiding = { - init: function() { - if (!Conf['Announcement Hiding']) { - return; - } - $.addClass(doc, 'hide-announcement'); - return $.on(d, '4chanXInitFinished', this.setup); - }, - setup: function() { - var btn, entry, psa; - $.off(d, '4chanXInitFinished', PSAHiding.setup); - if (!(psa = $.id('globalMessage'))) { - $.rmClass(doc, 'hide-announcement'); - return; - } - entry = { - type: 'header', - el: $.el('a', { - textContent: 'Show announcement', - className: 'show-announcement', - href: 'javascript:;' - }), - order: 50, - open: function() { - return psa.hidden; - } - }; - $.event('AddMenuEntry', entry); - $.on(entry.el, 'click', PSAHiding.toggle); - PSAHiding.btn = btn = $.el('span', { - innerHTML: '[Dismiss]', - title: 'Mark announcement as read and hide.', - className: 'hide-announcement', - href: 'javascript:;' - }); - $.on(btn, 'click', PSAHiding.toggle); - $.get('hiddenPSA', 0, function(_arg) { - var hiddenPSA; - hiddenPSA = _arg.hiddenPSA; - PSAHiding.sync(hiddenPSA); - $.add(psa, btn); - return $.rmClass(doc, 'hide-announcement'); - }); - return $.sync('hiddenPSA', PSAHiding.sync); - }, - toggle: function(e) { - var UTC; - if ($.hasClass(this, 'hide-announcement')) { - UTC = +$.id('globalMessage').dataset.utc; - $.set('hiddenPSA', UTC); - } else { - $.event('CloseMenu'); - $["delete"]('hiddenPSA'); - } - return PSAHiding.sync(UTC); - }, - sync: function(UTC) { - var hr, psa; - psa = $.id('globalMessage'); - psa.hidden = PSAHiding.btn.hidden = UTC && UTC >= +psa.dataset.utc ? true : false; - if ((hr = psa.nextElementSibling) && hr.nodeName === 'HR') { - return hr.hidden = psa.hidden; - } - } - }; - - Banner = { - init: function() { - return $.asap((function() { - return d.body; - }), function() { - return $.asap((function() { - return $('.abovePostForm'); - }), Banner.ready); - }); - }, - ready: function() { - var banner, child, children, i; - banner = $(".boardBanner"); - children = banner.children; - i = 0; - while (child = children[i++]) { - if (i === 1) { - child.id = "Banner"; - child.title = "Click to change"; - $.on(child, 'click', Banner.cb.toggle); - continue; - } - if (Conf['Custom Board Titles']) { - Banner.custom(child).title = "Ctrl+click to edit board " + (i === 3 ? 'sub' : '') + "title"; - child.spellcheck = false; - } - } - }, - cb: { - toggle: (function() { - var types; - types = { - jpg: 227, - png: 270, - gif: 253 - }; - return function() { - var num, type; - type = Object.keys(types)[Math.floor(3 * Math.random())]; - num = Math.floor(types[type] * Math.random()); - return this.src = "//static.4chan.org/image/title/" + num + "." + type; - }; - })(), - click: function(e) { - if (e.ctrlKey) { - this.contentEditable = true; - return this.focus(); - } - }, - keydown: function(e) { - e.stopPropagation(); - if (!e.shiftKey && e.keyCode === 13) { - return this.blur(); - } - }, - focus: function() { - var items, string, string2; - this.textContent = this.innerHTML; - string = "" + g.BOARD + "." + this.className; - string2 = "" + string + ".orig"; - items = { - title: this.innerHTML - }; - items[string] = ''; - items[string2] = false; - $.get(items, function(items) { - if (!(items[string2] && items.title === items[string])) { - return $.set(string2, items.title); - } - }); - }, - blur: function() { - this.innerHTML = this.textContent; - this.contentEditable = false; - return $.set("" + g.BOARD + "." + this.className, this.textContent); - } - }, - custom: function(child) { - var cachedTest, string; - cachedTest = child.innerHTML; - string = "" + g.BOARD + "." + child.className; - $.on(child, 'click keydown focus blur', function(e) { - return Banner.cb[e.type].apply(this, [e]); - }); - $.get(string, cachedTest, function(item) { - var string2, title; - if (!(title = item[string])) { - return; - } - if (Conf['Persistent Custom Board Titles']) { - return child.innerHTML = title; - } - string2 = "" + string + ".orig"; - return $.get(string2, cachedTest, function(itemb) { - if (cachedTest === itemb[string2]) { - return child.innerHTML = title; - } else { - $.set(string, cachedTest); - return $.set(string2, cachedTest); - } - }); - }); - return child; - } - }; - - CatalogLinks = { - init: function() { - var el, input; - if (!Conf['Catalog Links']) { - return; - } - CatalogLinks.el = el = $.el('label', { - id: 'toggleCatalog', - href: 'javascript:;', - innerHTML: " Catalog Links" - }); - input = $('input', el); - $.on(input, 'change', this.toggle); - $.sync('Header catalog links', CatalogLinks.set); - $.event('AddMenuEntry', { - type: 'header', - el: el, - order: 95 - }); - return $.on(d, '4chanXInitFinished', function() { - return CatalogLinks.set(Conf['Header catalog links']); - }); - }, - toggle: function() { - $.event('CloseMenu'); - $.set('Header catalog links', this.checked); - return CatalogLinks.set(this.checked); - }, - set: function(useCatalog) { - var a, board, generateURL, path, _i, _len, _ref, _ref1; - path = useCatalog ? 'catalog' : ''; - generateURL = useCatalog && Conf['External Catalog'] ? CatalogLinks.external : function(board) { - return a.href = "/" + board + "/" + path; - }; - _ref = $$("#board-list a:not(.catalog), #boardNavDesktopFoot a"); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - a = _ref[_i]; - if (((_ref1 = a.hostname) !== 'boards.4chan.org' && _ref1 !== 'catalog.neet.tv' && _ref1 !== '4index.gropes.us') || !(board = a.pathname.split('/')[1]) || (board === 'f' || board === 'status' || board === '4chan')) { - continue; - } - a.href = generateURL(board); - } - return CatalogLinks.el.title = "Turn catalog links " + (useCatalog ? 'off' : 'on') + "."; - }, - external: function(board) { - switch (board) { - case 'a': - case 'c': - case 'g': - case 'co': - case 'k': - case 'm': - case 'o': - case 'p': - case 'v': - case 'vg': - case 'w': - case 'cm': - case '3': - case 'adv': - case 'an': - case 'cgl': - case 'ck': - case 'diy': - case 'fa': - case 'fit': - case 'int': - case 'jp': - case 'mlp': - case 'lit': - case 'mu': - case 'n': - case 'po': - case 'sci': - case 'toy': - case 'trv': - case 'tv': - case 'vp': - case 'x': - case 'q': - return "http://catalog.neet.tv/" + board; - case 'd': - case 'e': - case 'gif': - case 'h': - case 'hr': - case 'hc': - case 'r9k': - case 's': - case 'pol': - case 'soc': - case 'u': - case 'i': - case 'ic': - case 'hm': - case 'r': - case 'w': - case 'wg': - case 'wsg': - case 't': - case 'y': - return "http://4index.gropes.us/" + board; - default: - return "/" + board + "/catalog"; - } - } - }; - - CustomCSS = { - init: function() { - if (!Conf['Custom CSS']) { - return; - } - return this.addStyle(); - }, - addStyle: function() { - return this.style = $.addStyle(Conf['usercss']); - }, - rmStyle: function() { - if (this.style) { - $.rm(this.style); - return delete this.style; - } - }, - update: function() { - if (!this.style) { - this.addStyle(); - } - return this.style.textContent = Conf['usercss']; - } - }; - - Dice = { - init: function() { - if (g.BOARD.ID !== 'tg' || g.VIEW === 'catalog' || !Conf['Show Dice Roll']) { - return; - } - return Post.callbacks.push({ - name: 'Show Dice Roll', - cb: this.node - }); - }, - node: function() { - var dicestats, roll, _ref; - if (this.isClone || !(dicestats = (_ref = this.info.email) != null ? _ref.match(/dice[+\s](\d+)d(\d+)/) : void 0)) { - return; - } - roll = $('b', this.nodes.comment).firstChild; - return roll.data = "Rolled " + dicestats[1] + "d" + dicestats[2] + ": " + (roll.data.slice(7)); - } - }; - - Emoji = { - init: function() { - var css, icon, name, pos, _ref; - if (!Conf['Emoji']) { - return; - } - pos = Conf['emojiPos']; - css = ["a.useremail[href]:last-of-type::" + pos + " {\n vertical-align: top;\n margin-" + (pos === "before" ? "right" : "left") + ": 5px;\n}\n"]; - this.icons["PlanNine"] = Emoji.icons["Plan9"]; - this.icons['Sage'] = Emoji.sage[Conf['sageEmoji']]; - _ref = this.icons; - for (name in _ref) { - icon = _ref[name]; - if (!this.icons.hasOwnProperty(name)) { - continue; - } - css.push("a.useremail[href*='" + name + "']:last-of-type::" + pos + ",\na.useremail[href*='" + (name.toLowerCase()) + "']:last-of-type::" + pos + ",\na.useremail[href*='" + (name.toUpperCase()) + "']:last-of-type::" + pos + " {\n content: url('data:image/png;base64," + icon + "');\n}\n"); - } - return $.addStyle(css.join(""), 'emoji'); - }, - sage: { - '4chan SS': 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAANCAMAAACuAq9NAAABIFBMVEUAAAAAXwAAOAAAVQAAKgAAOgAALwAAagAATwAAdAAAYAAAYwAARAAAcgAANwAAOAABcwEBZAEBXwEAQwABbwEBaQEBWgEBTwECdAICaQIIcwgBWQEIXAcARAAALgACdAICbQICdAICcAIBVQEBTgEAQgAAQwAkjCIcexomgSIcbRtCnj9IpUNEmT5LoUNYtFE9lDtClD5dtVJqwmNCmEFMoEh1zGcnfCYnfCc6jzc7kDs9kjxAlUBDmEFInUNLoEpMoExOo0tPpExQpU1Rpk1Sp0pSp1JXrFVZrlhar1Rar1pes1xftFhhtmFit19juFxkuVxovWRrwGBuw2Juw2Nuw2Ruw2V0yWx1ym14zWt6z2980W6A1XGD2HSD2XSI3XdgUJhRAAAAN3RSTlMACAkJDBobHyBERUVHR3KIiYyNkJmanZ6rrq+ws7S5vL29vsLFxsfP0dLU5eXn5+vt7e34+fn5LB88GQAAAI1JREFUGFdNzjsSwjAMRdGn2PngmZAUVHQshP0vArYQYCYDlmxLos3tTncx4xjdAMCEhR1ApLup+bPxtgsQzZ2Mr4iPYROEU129g6it0jJCv6xqFJlpKbl2kr21Zsl/Mo0IBpmrqg7ZnPfgSnKuqhrKwO+AVrSUOjmo5VcEuHzH9CEAXaTDYZ88HGh++QNCDFZ4bvbHSQAAAABJRU5ErkJggg==', - 'appchan': 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAABa1BMVEUAAACqrKiCgYIAAAAAAAAAAACHmX5pgl5NUEx/hnx4hXRSUVMiIyKwrbFzn19SbkZ1d3OvtqtpaWhcX1ooMyRsd2aWkZddkEV8vWGcpZl+kHd7jHNdYFuRmI4bHRthaV5WhUFsfGZReUBFZjdJazpGVUBnamYfHB9TeUMzSSpHgS1cY1k1NDUyOC8yWiFywVBoh1lDSEAZHBpucW0ICQgUHhBjfFhCRUA+QTtEQUUBAQFyo1praWspKigWFRZHU0F6j3E9Oz5VWFN0j2hncWONk4sAAABASDxJWkJKTUgAAAAvNC0fJR0DAwMAAAA9QzoWGhQAAAA8YytvrFOJsnlqyT9oqExqtkdrsExpsUsqQx9rpVJDbzBBbi5utk9jiFRuk11iqUR64k5Wf0JIZTpadk5om1BkyjmF1GRNY0FheFdXpjVXhz86XSp2yFJwslR3w1NbxitbtDWW5nNnilhFXTtYqDRwp1dSijiJ7H99AAAAUnRSTlMAJTgNGQml71ypu3cPEN/RDh8HBbOwQN7wVg4CAQZ28vs9EDluXjo58Ge8xwMy0P3+rV8cT73sawEdTv63NAa3rQwo4cUdAl3hWQSWvS8qqYsjEDiCzAAAAIVJREFUeNpFx7GKAQAYAOD/A7GbZVAWZTBZFGQw6LyCF/MIkiTdcOmWSzYbJVE2u1KX0J1v+8QDv/EkyS0yXF/NgeEILiHfyc74mICTQltqYXBeAWU9HGxU09YqqEvAElGjyZYjPyLqitjzHSEiGkrsfMWr0VLe+oy/djGP//YwfbeP8bN3Or0bkqEVblAAAAAASUVORK5CYII=' - }, - icons: { - 'Plan9': 'iVBORw0KGgoAAAANSUhEUgAAAAwAAAAPCAYAAAGn5h7fAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AoYAzE15J1s7QAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAACAElEQVQoz3WSz4sSARTHvzMjygpqYg3+KIhkE83DKtKlf0C9SrTRuZNEx0VowU6CuSeJNlwwpEO2kJ6SQBiIUAzFjRDF4wrjKosnGx3HmdehFDfpe/2+z/s++D5gU7IsEwRByICIiAEAIiIAYAFAXsjYVr/fLxMRNVvN+prJ5/OA3+/XERFNf02JyeVyDx0OxyvLNQsnimLKfcf9KRQKXQAAnE6nlf5qMpnQycnbP/kAoKoqsSwLAJhOp+AAwOv1otvtpqxWq73dbt/r9XqvEQ6HUalUEvF4XLd5IpvNZqlerzd5nlf6/f6tTCZjBACk0+nb+XxeW4UrikLJZPImAGA0Gq0NIqJyuSyyANDr9Q5Wu1utFvR6/SULAI1G4+vK8Pv90DTtGwsAJpPpaGUYDAZ0Op3PHAAEg8H3tVqtbrtu21sqyxuRSOQJk0ql9IvF4r7b7f7pcrlejkaj57IsH58Pzp8dvjhc/lsBk0gkbLFYrFqtVvd27+4qOk733ePxPDCbzVBVFfP5fCiK4rvhxfDN/qP9wSasGwwGMv1HiqJQsVg8ZlfTHMepkiR1t05gGJBGmM/nMBqNj9nN9kql0lNN064ARISzH2cQBAGz2ewLu2na7XYLwzBbvxYIBBCNRrFj3BmsAZ/PZ+J5/kOhUIAkSVeA8XiMZqt5efrx9OA3GfcgvyVno9cAAAAASUVORK5CYII=', - 'Neko': 'iVBORw0KGgoAAAANSUhEUgAAABMAAAARCAMAAAAIRmf1AAACoFBMVEUAAABnUFZoUVddU1T6+PvFwLzn4eFXVlT/+vZpZGCgm5dKU1Cfnpz//flbWljr5uLp5OCalpNZWFb//f3r6+n28ff9+PRaVVH59Pr//vr38vj57/Dp7eyjn5zq8O5aVVJbYV9nVFhjUFRiWFlZVlFgZGOboJzm5uZhamfz9/bt8fDw6+drb26bl5j/8/lkX1z06uldWFS5r61UT0tfWlbDwr3Ew76moqNRTU7Mx8P75OpeY19pWl1XW1qzr6x5eHaLiojv7+1UT0xIU0uzqadVS0nV0MxkZGT5+PPk497///ra29Xq5eFtY2H28e2hnJignJlUUE1dXV2vrqxkY2FkYF/m3d5vZmfDuruhl5aZlJHx8O75+PZWVVP29vT/9fTj3trv6ubh5eRdXFqTkpBOTUtqZmX88/RMQ0T78vPEvr7HwcHDwsDq6ef///3Gx8H++fXEv7tZWVedmZZXXVudnJp0c3FZU1f79fnb1dlXUVVjXWFrZmy8t7359/qLj455e3q4s69vamZjX1zy4+avpaReWFz/+f1NR0vu6Ozp4+f48/lnYmi8ur3Iw7/69fHz7+xbV1SZmJZVUk1ZV1zq5ez++f/c196uqbDn4uj9+P7z7vRVVVXt6ORiXl/OycXHw8CPi4ihoJ5aWF3/+v/k3+axrLOsp67LzMZYU1m2sq9dWF5WUU1WUk/Au7eYlJGqpqObmphYVV749f7p5Or38fPu6OpiXFz38fH79vLz7urv6+hhYF5cWWKal6D//f/Z09Xg29exraqbl5RqaW6kpKTq5uPv7Of/+PDj29D//vP18Ozs5+OloJymoZ1ZVVJZWVlkYF2hnpmblIyspJmVjYKQi4enop5STUlRTUpcWUhqY1BgWT9ZUjhcV1NiXVkkhke3AAAABHRSTlMA5vjapJ+a9wAAAP9JREFUGBk9wA1EAwEAhuHv3dTQAkLiUlJFJWF0QDLFYDRXIMkomBgxNIYxhOk4wwCqQhQjxgxSGIsALFA5BiYbMZHajz1oJlx51sBJpf6Gd3zONcrqm/r1W8ByK0r+XV1LXyOLLnjW6hMGpu0u1IzPSdO17DgrGC6AadrVodGcDQYbhguP6wAvAaC0BRZQalkUQ8UQDz5tAof0XbejOFcV5xiUoCfjj3O/nf0ZbqAMPYmzU18KSDaRQ08qnfw+B2JNdAEQt2O5vctUGjhoIBU4ygPsj2Vh5zYopDK73hsirdkPTwGCbSHpiYFwYVVC/17pCFSBeUmoqwYQuZtWxx+BVEz0LeVKIQAAAABJRU5ErkJggg==', - 'Madotsuki': 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAMAAADTRh9nAAAALVBMVEUAAAC3iopWLTtWPkHnvqUcBxx5GCZyAAARERGbdXJrRUyGRUyYbY23coZFGDRFGEYfAAAAAXRSTlMAQObYZgAAAGdJREFUeNpVywkOwCAQAkAXtPb+/3PLWklTiIlMtDiH4rvpVM22K+WvY+7Z/dOxZ2xkLmYpNWo6RoKMUQJ8SYiozEYiZAuLbCZQsGB+/hC4SwZsdV2rTjSR0+J9tzXL0B4RW5f9VbE94skEEpHbpw8AAAAASUVORK5CYII=', - 'Sega': 'iVBORw0KGgoAAAANSUhEUgAAACwAAAALBAMAAAD2A3K8AAAAMFBMVEUAAACMjpOChImytLmdnqMrKzDIyM55dnkODQ94foQ7PkXm5Olsb3VUUVVhZmw8Sl6klHLxAAAAAXRSTlMAQObYZgAAANFJREFUGJVjYIACRiUlJUUGDHBk4syTkxQwhO3/rQ/4ZYsuymi3YEFUqAhC4LCJZJGIi1uimKKjk3KysbOxsaMnAwNLyqoopaXhttf2it1anrJqke1pr1DlBAZhicLnM5YXZ4RWlIYoezx0zrjYqG6czCDsYRzxIko6Q/qFaKy0690Ij0MxN8K2MIhJXF+hsfxJxuwdpYGVaUU3Mm5bqgKFOZOFit3Vp23J3pgsqLxFUXpLtlD5bgcGBs45794dn6mkOVFQUOjNmXPPz8ysOcAAANw6SHLtrqolAAAAAElFTkSuQmCC', - 'Sakamoto': 'iVBORw0KGgoAAAANSUhEUgAAABEAAAAQCAYAAADwMZRfAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAxVJREFUOE+Nk19IU1EYwK+GQQTVQ39egh6ibKlzw91z7rn3bvfOmddNszl1bjKXc5rJJGmBUr7Yg9qTD0IalFgRBEYg6EDQQB+GovQyQgiaUZsoLcgHMcr069w7MgcGXfi453zn+37fv3MYZt/n99e76tzVj4JN/hP79fvXnV3hnNabwUBjoOHcgTYOu/JQspgTzsqKgn9BfD4vkWTzur287PqLVy+zM+yePB7KsRXLywTjnSpnZctBkPCdW8ccDuU55vBO8RXbkC/oP5ph19V5+7LIky0OY1BKbZEbLcFSt7u6pN7jLmltCVrr3DV5jY3+KovFEsccB1KJNVpefe10BqS2tqqO4/AuphBB4L/LkrRqNgtJs1lMypLls1kU38mytMLz/E8VIlutqVqX6/weZG52OttRXjbE0cP/FYLRlpVjDXuQ/r77x2XZPKkCHA4HBAIBkCQpAygIAvh8Pu2MZgO0Lz+QSa/sQfwN9RfpVN66XC6Ynp6GhYUFGBwczAC1t7fD0tISxONx6O7upgHILmsqvLcHodOggfiV/v5+SCaT4HQ6IRaLgdfr1bIRRREmJyfBZrNBNBqF+fl5sNsdgE2GiAbp6bmbdbXC7qWQbxMTE7C2tgY6nQ5SqRSEw2ENopaoZpCXlwdTU1NaoECgCbgiU6y8QH+ECYWaTymK7TWdys7MzIwGaWtrg42NDejo6AB1WjU1NZo+FArB2NgYrK6uQrAlCASxn2z6wkuMp87VIAhkE2MEAwMDkEgkYHx8HBYXF0HtkQpRy1BLiEQisLy8rPVNKSsFjEzrXH4+z1hlS4xDhKadNu7t7YPR0VHweDzAEVWfHru6HxkZgeHhYVAURYNjkylVWKArZjjMzqmdVi+QCsLUkQiEjvDvncEkvU7/qQ0Vgukeo48Go87IiCJnZNmipxiz7wXEbVDnbUxQOgM12h9n6qTq6NvapRdtkwaP0XK8RmPuYSbxYfaQ/sJJhjfknuFRURUi7AMOozcCwl94hLZp5F+EioDQVwqYI6jomZU1NFtM+rOSxZjVazcyvwHr/p/Kws1jegAAAABJRU5ErkJggg==', - 'Baka': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAA0pJREFUOE91k3tI01EUx39JOpA0H4jNx0pbD3XTalISWf8YFlEgldqDsBLLyqjEKBCiLLWiggh6/KEV1WZ7OaelLZvDdDafNW1JFraWe/32+01FrUZ9uy4ylLpw4Z5z7/nc77n3HIqaMRIjZJyEcNX+uFCFeGmI/GZciEIsCFJUTvoAzDz+1y7K76MSwhX5hXl6z+WSbrzU2KB8YEGDwgrTaxZ3b7xHcaHhR3xw7Z5/UviB1ReP5XSg3+TAqYJOxMzWISFIC0GQDomhTVA9skCnsaAwp/vnMq66dBokNuBR9uFd7T9Z1zCunjci0qcRJUVdoJ3DYOhRnC/qBZ+jQbfeCc+37yjY2UEg0iwvJE0k9l8Z+8xqHmTgot0QLdQgTaQFQ2AsOzlHvOu1S5pwOLsHHo8HjHMCq2MazNvTlByKHyrJLDvdR25jMWRxYx5HjeMH2r1BDOOeguRua4OI14jx8a8YH5tA+al3EHKlW6mYOapb2oZBOOwMbEMseAE12L+jjUh3w+VipyAZ65oxn1NP/GMYGR6Ftn4Qsf7qa9S82Y/l/X122G0uL2TbxmZEz1WhXW8mUol8moXu+SCi/OoQ6VsDh3UUwyQ1k9GOaI5MTkX4yWTGHutvgI1F28sviAlRgxeoRm62HvsyW8En9pZ1TYgi6TntoyQtFm86rVgUoJZRvDnKMmXVAGxWmkAYOBwudBqGcHCvHulrGpGT2Uy+z4yT+QYsCXtCUpp8GxbKhx8gDK0ro+KjJGvzdjfDZnN6VdisLD5/JjArQ2zW66PJOj2lEZtStaBphkwah7K6kMJ/GEulp1bMWhAmMbTozOQRaWRtfoZVgjo4iRra4SYgGi26TwjxVeDKhR7Y7U606ixICq9tr7hd7+OthRWL7yUnJ1WPmXotqLhpRICPHCePtuFV6xdUPTAhcWEtRHEqfHpPyto4hPXLXnzflSEJnFaN3OCKDcsFsrEntR9RUmxARLAUgT5iBPuJsXWDBj0dZjRU9yNV+PTbpjTp9OA/pOSk24nRkXf1J462oPxcJ65f6ULlHSMulepRerYDgvj7A0cKpNz/tyTZqbzXO4t0ZZGQJ34RH11lFHIlA8LIqreCCMUZRY3cd2bwL/5/RmjNSXqtAAAAAElFTkSuQmCC', - 'Ponyo': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAACAVBMVEUAAAAAAAA/AAC/AADMAACqAAC2AADGcQBMGQCyZgDMGRnEYgDMERHJGhrOGBjHeQvSISFPGgjNgyDQHh7MHx9QHgqBGhHLeQzQHh7OGRnSHR3WJCTTJCTQGxvQHx9jLBbKfxjPGxvPHh7QOB7TJCRrLRrTJCRZJhHbljjeqmN3OibaLS1+NyXYMDC8NzPaKyvYKyvaLS3YOincQEDorFvbKCjdNjbaKCjuypjZMjLcKyveLi6GRi/hOzvhMTHrs2ftxpHXLix/OCODOCeGSjOHRy2IPyqJQSqKSy6PTjmQQS6XSDGZMSefWUKgTzehUz2jVT2kVDqkbVSlWUGmUTema02qZUqrbkyrckCsMSmwNyywbEyyQTm0XkC1YkS1ak+1gVK2Qzu3iVq5Ni26Qjy6oHy9JiG9ZUnCb0vENCLFxcXGdVLGpIzJdVDKfFbMd1HNqILQysXScU7Sck/TwrDXRS3YSEbcS0rdLy7d0cHd3Nref1XfSknfflTfhFnf3tzgwp3hLi7hgFXhglfiLi7iRUTiTU3ihVnjMTHjSknkMDDlODjlOjrlOzvmPDzmrFzm0bbnTU3oT0/oUlLpU1PpVlbpWFjp1r7qW1vqYGDrvoHs1rnt0q7vwH/yz6HzypLz2bb306L43bn50Zr62Kv637r82Kf83LD83rb837f84b0dlQysAAAAQnRSTlMAAQQEBQYHCQoKCg0PExUXFx0fISgzOTtCRUVMTVJSXFxcXGNqa3BykJegp6mqtLS1t7/Jz9DU1trb3eTn8/X2/P3IgXZJAAAA30lEQVQoz2NggAJmSUUlPgYEYDNs6es150II6HfNmNZhIAznizlOmdzdONFWCsLlENVyTixPKZs004oFxFe3CfDLyI5zi2mb2iPHwCCoqhPYXNvU1FQd7FNjJsLAwCSfl5+bXldU3B6bJsHMwKCr5+Tu6hHtHxbqHR+pBjRAu9U3YmFCeFLOPBfPKBVeRgbZipCCqoblKxdVlgQlG1uyMrCYZqbWz148f0JWoVennTRQj4DR3AUrlixdMX1OqbUM2FncJstWAMEqC2UeqLv5Nez7ZzloCiH5lV1cgRPKBAApxz0bK1ScOQAAAABJRU5ErkJggg==', - 'Rabite': 'iVBORw0KGgoAAAANSUhEUgAAABIAAAAQCAYAAAAbBi9cAAAD7klEQVQYGQXBW2xTZQDA8f93zmlPe7qOrVvXyzY2B6ybTLksIAImanTiDGimb/pAohISEx980TffTHwjPviiDxiNokajMSAQIwEGIwzYTWAFcZdudOt9bdfbuXz+fuLHrz9935bFnbr3yY/6Y/3O9PQkXk8T6UyeYHuY/u1RCvkyqWyBJo+XQyOvIRQFKSVut4fJG5cO5HPJlCZz42+MjvaM3l5ZayoWu0+oiuqABAAAAEAIQa1aIbV6D6/PQAiFdCrbkU3/cjGTN08rfp/fzvyVY/fTm+9WauN/bhTKfqFogAQAAEAgsC2bSmWT1rYmfD4vdXPy84NDQX9fwN2t+CND91qsIMnzizx/xBjp27p0pVjI90g0EAIABCBAKIJisYLV0Hl0f/zkth3F40rCjaH6DEUx+s5uRgO0LfeycPY+I0cCu/cNzk+a5eWxekMiFDf1ukO10sBsSBqmws3xX4+0G1dPifU6SsJNplJaF7OTE5jJ76/2lwcPJ2cSbPTcIjbSiyUMfv69fLE3Eo6HQuYeR5HNlbJVmJlOmh2e1cMHXxzUzXMmLX1Bvl2MHxdrS3GSq/cPqInTl2P1w+6V63PM1xL49qrsOjaEt70d3edDShBCYtXr5JaWuPDVHZ5zQhT36PM388PD6rN795LLOyu37q7+W639cTTQoWsD/X6697fiHxhkYibJb+cfs2egA3DQNDdnrqXpjUWZW5h2HlpPvR3eOhhXXEKnyevG0zJwZka+PjSxmV1pGi6jx2IgdC7NrvLxTxe4Mb2I5nKDojC3lOaLKw/YMbLPaQ/FUp2dIbRgWzvegA8j2Ar19LaO8q0uJTRITfFQz+c4OTbM0PYou3aGKaSKlPLwyZu7KFmwtUVqldTsCc32faA4AjKJxy814tlvlq/+8GVvp4rt8iGlpF4s0N7i5dj+CLn4fzz6J0N18wXSCYXekI7LMGjeYh1cWiihrS/GXx20/Oe6up9hqlQDKRGlDKrmwohEsUwbKTSiQ9vp1lQya5fpDBgIVUG4VFrD/sj12wt+TatUXwn6fdSLj2iYFrViCelKoQuB1aghjS0I3UCoIJEEu7xIBxzLQqNCannt8e3ZjZJmmtWas3YXuzGHzG7hYXOGAXcWt2Hgskzk5gZ2Uyu4dLBNcGxUu4HiVMklkszOej47dGAf6tjYsXRzw3onpOGOqC1MzVSZym8kvdYGml3SPR6JapZQagWURhmlkqeWzfFgPr/x953wh31Do99FoxE0f7BrZt7leXliauI9vSH1dS0Qb+s9eupCfKa1K8tb5sT8bpdab7Vte7MuXDZaOJ+tBK5LredsOPpEMdLVjWWZ/A+8EtJREuofIwAAAABJRU5ErkJggg==', - 'Arch': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABCFBMVEUAAAAA//8rqtVAqtUQj88tpdIYks46otwVldUbktEaldMjldM2qNcXk9IWktQZkdIYlc8mnNUXlNEZktEZlNIYktIWlNMXktE7o9klmdMXktFHqdkXk9EWk9EYk9IlmtQXlNEXktAWk9AWlNEYlNFDptkZldMYk9E4otg/p9kXktEXk9AXlNA4otclmdQXk9IYktEXlNEwn9YXk9IXk9FFp9o3otgXk9FPrdwXk9E2otdCptkXk9E/ptkcldIXk9Edl9IXk9EjmdUXk9EXk9EXk9EbldIcldIjmdMmmtQsndUvntYyn9YyoNYzoNc0odc1odc2odc6pNg7pNg9pdlDp9pJqttOrdzlYlFbAAAARXRSTlMAAQYMEBEVFhgcHR0mLS8zNTY3PT4/RU1kdXp6e3+Cg4WIiYqMjZGXl5mbnqSnrbS3zMzV3OPk7Ozv8fT29vf4+fz8/f7SyXIjAAAAlUlEQVQYV1XNQwIDAQBD0dS2bdvmNLV5/5t0UU52728CvGayQLx8UWz1eKoXhdBqmRaF6mbdVfzZXWgetomfpY3b4Hruqb7B97hf9rtT5mNZ+7ggyaHuHTxzzqIxgUy+LG+RWSBFjrQAgAhJF+Ak6ykA0PRJOgAj2QlKAOTISkADKMM1Mg4YJmXr585cEozw2vE3m/8J5h8V7jsI1XAAAAAASUVORK5CYII=', - 'CentOS': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAB5lBMVEUAAADy8tng4Ovs9tnk5O3c7bX44LLduNO1tdDh7r/eutj43q2kocX23az07N+qqsvUqcmXl7331ZXJj7r40o/Pn8T42qP63KjNw9n21p3Y387Ml7732JzR55z05MSxtMLGn8TC4Hx8eqt8e62Af6/B4HnG4oPC4HzH44fBf7LCgbOkoMTcsrmtn8PWqcfFtKrj4Jvs2ZOz2FnMqLXT3KfY5p60Z6NUU5XRuqHzwWSywqDn3JaiiLWahrWhkry5zJjRmqm1Z6P1wmb1y319fK632mK5cKi5nH+73Gu73Gy73W283W+9eK17e6y1yZS3aqRZWJdcW5ldXJplXZppaKBwb6VwcKV5eKswL306OYNPTpGkfK+m0kGpUJWq1EnEqIuXK3+Xh7ahP4qhkryMfK6BgK+CdpGMaKKMa6O9ea2+eq6+oYW/eq+NbqWVlL2Wlr7AjanA4HnA4HrBkqbBlafB33rCgbLCmKjCxIzC1mSs1UytV5mtxIWt1lCuz2evWpuvXJywxYzHjrvH4oXIjrrN2HXO5pTO5pXUlYnUlYvVl5Hb0G7e0XTg03rhr5fpzHPpzXTp0Hvtz3/wrDHytknyt0zyuE3yuVHzvVr0wGP1x3T1yHf1yXe0ZaL2zYP30o730pD31ZeRIcF5AAAAQ3RSTlMAFBkbHEhJS0xMTk5UWWBsd4SEiIiPkJCVlZaam6CjpK29wMPDxMTFxcnK193e3+Dg4uTn5+fo6e/v8/P4+fn7/P7+J4XBAAAAANNJREFUGFdjYIAAcW4oA0rJOFnywkVk1VVNWyf1OehpaQqABTW8213jJna5lfnECoMF1NqaC2MmdM5tyfKHCJi4FpV69nc05VTXK4D40hVVtR5ehjqZDUkJNjwMDKKOBeUlxcZ8EnbJiSnB5hwM7GbRuRnpulJyFvHZ7mlKLAwMXLZhofnh9tYLF8ycrs8EMkQ7Nc830K93/jznOZJgW1RcfIMiG3tmOM+aKgIWUI4KCYio6Z42e8pkiAC/oKC8VV2lgZiQEBvcP6xGioyo3uVkhvIBH9A0EWEgTIIAAAAASUVORK5CYII=', - 'Debian': 'iVBORw0KGgoAAAANSUhEUgAAAA0AAAAQCAYAAADNo/U5AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAZ5JREFUOE+Nkk0oBHEYxv8fu5GQj3JwcaDkIAc5IpR87M7MKnIVJVKclaIQ5Sy5OLkgR7n5OigcSNpmd2c2Vyfl4KT8/muWiVU79TTv+7zv837NCBF6PG1X+NpZyEYSD9mIc+tHnBPe23B9xKrCuTmbQA/JKfABrhBswa1hH4A38IwfOxPdX1qcjiCQxO5NyrjKV70TnSbeRPwJvGN3i4yyqnEucPY8ZZX9GSEgGK+RvFfyjk2VKZxzBNG8wJWWgh/xtDOeUXZ7Slr6TrSLYL9N4SMgYTTcwdc2ArvJcElhSVcM6mCNSV8n9hA59yTU5UWMG6HIbLhIWlglgWiC2L4Z79qTdo40D6ISuOWwKCWHyk9Fv8ldpUHOuGTuynwSBUynddPdlbEosVpP9Eu4FnOsRzUYNTsdmZN/d5LDiqM0w+2CMdAFFsFGWgfXxZnheqe/z+0puwEM0HHYV3Z9Sgz8TEz7GkQvpuJ/36ggj2AaHLrSlkULWV5x+h2E8xkZL16YVjGNaAUscfZ/f6c/k9ywLKI2MMcRWl0RLy007idmRbQJ7RIfDAAAAABJRU5ErkJggg==', - 'Elementary': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAACXBIWXMAAABIAAAASABGyWs+AAAAAmJLR0QA/4ePzL8AAAFuSURBVCiRfVG/S8NQEH4VimAHcXKS+otIsNb2vpc4W0QXF8FBqKOiIEgQKTgEK/gXBKEOIoIIgmMo6KSIkxYXHRxcqhjQQpQKOojES9K0mxwc7919d/fdd8IToemKLMKGw2bLoq5E8dDFyIALixZlAWvsd/BBhog1ACKGMiqkyiVU5SGO8EQzmV66QNmHMICrK4hjHXUt49dgHM+D7ekELslggK7AJVUbxicmo7l4yY56Yqwbrq4IpmZx6FweN9MdcOVQ8CrRpoBNedGGLzkXAWgFpyF13soWcHhAP7xsMkyPdOFRpoL6DXzDYYDehwF4NBUEO+UNbQd9cvhhCANsbYK/1zA9oWm4xbKf1nrwii3K8wgmWeLKVdSxhzfK+Wk5ixoOmIPFJHnNGit3D4/tShZol1Wp0jR3VYM1A6F+YWaTNI8T3OEMZjrBPeOsrtGS+iFUsbmqyn0iqRvHepf7WJApUpmaxeq2jvX/uf8A9h7IjHC1AQkAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTMtMTAtMTVUMjI6MjM6NTQrMDg6MDCQ664gAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDEzLTEwLTE1VDIyOjIzOjU0KzA4OjAw4bYWnAAAAABJRU5ErkJggg==', - 'Fedora': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABPlBMVEUAAAApQXIpQXIpQXIqQ3UpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIqQ3QpQXIpQXIqRHYpQXIpQXIqQ3QqRHYpQXI8brT///8uTYMpQnM5Zqg5ZqnS1+I4ZaY4ZactSn8uRnYrQ3MrRXgsRHUsR3s8bbM8brMtSX4wUosxVI01XZw2X50vUIguToQvR3c6X5o6aKs6aq08Un8qQnM9VIFDWINJXohKcKlXapEqQ3UvUIc2X55bhcBdcJVgcpdhfapmd5tuk8dxgqJ1hKR5jbB6iah/m8Shudq3v9C4wNG/x9bFy9nFzNnFzNrIz9zK0NzK0t/O2+3P1eA2YaDU2eTb3+jb4Oje4urj6fHm6e/s7/Tz9fj3+fz7/P38/f3+/v83YaEa/NNxAAAAHnRSTlMABAoVGyY1SVlpeIuQsLfDzdHW4+3y8/b39/n6+vr4+ns8AAAAxklEQVQYGSXBhUJCQRRF0SNYgIooiPJ0zwXE7k6wu7s7//8HdGAteeFoPNne2lhfpYpIioqWWnkNeNv3X+87HXWSIni73/b2updvq1E4hffys8/ag5toUhRv0QpAn5tCcbyiXQBZN4mSQG62ZDf9Q2PdbhplyPacmPe56TZAwcyIOy4828fj7cp4DhTk3ToU7YoKdbleoGiXlKXVOToPbNkpZTEFv25uefXJDvASIWWGF+7M7GyJf4lqqZnBw+vzowEgHQtJfyetJP7BfFOIAAAAAElFTkSuQmCC', - 'FreeBSD': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADXklEQVQ4EQXBS2wUZQDA8f83j33M9rF9d7u4loaWklaDpkSo9KDGaIKUaGxshD2YSPRiuDVeTDyhBxosJCoa40ktpAkPDcUqAYVIpUSUPrAulEdD2bbb7e7ObGcfM/P5+4kwKDvq6yJ1FYYcvb+YAkqAHo/HQ7FYrFIoCiurq9ZXJ06YSOkA+kBzfX06bys3zHxS9EL0tXDVyZfefacqV+X/ZSJx5+qLbx98LhaL9RiGEZWlEsWC/Thd9q6Pf3vs2u6Orc83rFsvTwwfLf5obgywT1Vjh2Hh+rbNsnTssJdNLedK5aIrpSuldKVXKsnH4+Pyn6FDXn5tMef9O+3NvdkvP1V4+EYw2AoQ+KSx8dRYS6NXXnwovaItXduSrrkinWxGOmZWJi9OyOK9m1LmsjIz9IH8QUMOd3WfAQwNKCy2tJwbHB5+XasPaxIHmc4g7WWEZ1MquBiRFlJTf1E7+Tl/H/8asavPzTY1nWd2ZkMDRPeBeHPz5ojwsilEQCBvTSKunCF3M8FSNkBGVTHDYYrLj8jVNhDZ2SMa2zo3MTamaIC/u6Ojr3DtrOrvP0BpdATnyBeIhTxpR5ABUlKSUlXS1dWstbVxdz6hPL0l1quGqkLaKwNvVcjEXNRd/4mit4Z19DjefBEPyCKxgQJQcF28dBrHNDGTSZSezsjeff0hraa2Vs2vrvit81O4vj9xLJcC4ADrQA7YAGqBGsAql/EtLdFQE/L7dF1XZmdnSrbPMJfXoLDmolQK8gJyQBowgQhQDRQBD+hsraVhd4e5MH+/oExfvWLJ9q3/3S7qMpNH2hsS40kFS4EUUAMA2IANRIBXv4uzuO67c2PykqkA5YmZ6bN18YPi0Yoknxc4AsJPCMLVAk2BLKDosCWqs/PZaulkuxk9fekcUBAAQGDks5FT0W++3NuYuC0DVUL4DIEdlIQDAj0IRkigaMjArkFx0tf523sffrQHyKsAgHPhwoXLL+yP9/kePNhk5ExUTyKFkJVAUAiCFZrQup4Rv9ftuLV/6ONBYBVABQAArMvJ5MXW7duD6P62sD8UrPAFRU1TpeCpCnGvPZr7WW///v0jpw+VC9ZdAAABAAAAAMLo7drWrmQyPWG/r8tnaGIjaM05ujr16x/ZBFh5AACA/wGZnIuwraa4ZgAAAABJRU5ErkJggg==', - 'Gentoo': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAB9VBMVEUAAAD///+AgICqqv+AgIC/v9+Ojqqii9GAgKptYZKQkOmPj/ddUYBgW4eVjeCTgfiWjO5wbJaZkvPBvepkXomYkNldV4Bzbpl6dJ+Uj7ynoO6Vi+1qZI63se2mnudjXYjOy+GCfaqZjvWlm/Pc2e+Oh7NeWIOWjfeXjeW1sd+gl+diXIfp5/KHgKnn5/F2cZx6c6ZgWoXc2e6dltrAvNu0scrX1eTOyujCvup4c5qpovVpY43///+6uPPJyPXq6fvm5vrz8/z8/P7+/v/d3PixqvmxrPSyrfe0sPO0sfS3tMve2/3r6vy6ufPz8/3d3fi3tM63tPO4tsu5tsu5tvO6tfe6t/Vva5KRjKy7tvW7t/W9vPO/vM+/vvPCwfPEw/TFwvTFxOfGxfTGxvTHxvTIx/TJx/aTiOrNzPXNzfXQzfnRzuHS0fbS0vbT0uHU0e/U0uTU0/bW0+zW1ffX1vfY1/jZ2Pjb2/jc2uSTiemVkLSlnvbe3PTe3vng3fzg3f3g4Pnh4Pnh4fri4enj4/nk5Prl5Prm4/ymn/bn5vro5/rp6O/p6funoPWsqs3t7Pvt7fXv7vzv7v3w7/nx7/3y8f3y8v3z8vytqPWuqPX09P319P319P719f339v739/34+P35+f37+/+uqev9/f6vqvSwrPQAR0dcAAAAPHRSTlMAAQIDBAgJCwwVFyAsNUFHSVBneH+Bh4mVmZmanKCxsrK2tr3ExtDW19rb4ODl5u3t7u/w8/T6+/z9/f4MkNJ1AAAA7UlEQVQYGQXBA2IDABAAwU1t27aNi1Pbtm0rtW277+wMgEN05nRWjBMAgCJgVUTed+sibQEg9EZEvm7V8x05LgCOJSKi1+8XdKmUhT5AyIuIvHUOLDWoyvKb/MG3uVRExuOTzvqUf6fDrthEfc/diXwczXbX/h7kpYCle+qETrQ7Y+1VDysbaYAiTER3bhhsKXpcn/QG8zgR0e7N9Cjrr0bCLTBNEJHXk4Whtv77ymArCBL5eVKvjfZuHS97mQEZn8+XhxVThuviRGcA0ss1xk3NRXW2nzUAeNZsL7Y25gbaAwCYuMUmR3jYAQDwDzDCPrxVMnjZAAAAAElFTkSuQmCC', - 'Mint': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAACVVBMVEUAAADh4eEAAAAAAAAAAAAAAAAAAAAsLCyXl5dgYGCnp6eTk5N3d3fBwcGqqqq8vLzNzc3Ozs7Ozs7Pz8/Pz9DQ0NHR0dLS0tLS0tPT09Pf3t/Pz8/i4eLb29vZ2drZ2tna2dra2trf3t/u7O/u7e/u7O/r6+vt7O/w7/Lw8PDy8fTz8fXz8fbx8fHz8/P19fb49/j49/n6+vuPxlmWyGOx437h9NDr9eD6/fj////+/v75/vTA5Jv6/fb7/fnL5bDL5q+AxjeDxUCEzTyGxUaGzjyHxkiHzz6J0D+Kxk6K0kCLyE2M00WNy06P00mSz1OUyF+W2FGX1FiY0F6Z02CZ21ac0Wiez2yfz2+f2mOh4GCi4GOi4WKi4mOk12+k3Wul32um1Hin0nun4G6n5Gin5Wmo23Op2Huq1n+q43Cr526s4Hit23+v6XSw34Cw34Gw6nWx4IKy4IOy44Cy63ez146z34az4IWz4YW03Y217nu38H2625e645G74pK83pu98Iq984W+4ZjA4px0tzDA5ZrB8ZDC5p7D55/E947F6KHF+JHH4qvH6qTI46/K5LLL5LN1tzLL5bN1uTDL57DM5bPM6qzM66/N5rTP6LbP6bTR6rfS573T67vT7LrV7r3X68XX7MHX773Y77/Y9rvZ8cHa7cjd88bi88/j8tTk8djk9tHm8trn89vo89zo9N3p9N3p9d7p9tvq9d/s+93s/dzy+erz+O73+vT4/PX5/fT5/fX5/vN1uzB3vTD6/ff6/fh5uTj8/fv9/vr9/vx8wjV/xDmrMRH0AAAAOXRSTlMAAAECAwQJDzk/RUlNU3F0kpSVlpeYmpucnaKjpKWqqqqtu8LExMTEzdTU1NXY4evy8vP+/v7+/v6LaR1mAAABDUlEQVQYGWPgk5QQFxMVERYSFODnYGZgYJA7vMfa2nrXbltbiyOW0mwMDApbuzsbq6sKslJiok0tFYECW9oalqwuyU2NjQoNjLfkYpC3tCxevDE5c/nShBUdfmZSDJw8Rr3zN0Ru2n/AaptlXa0G0FiDrnnrgizLsg95LVqfp8PAwGTQPnutv2VOmp3P9M352iABm5lrfC3T4+w8pkEEDJtmrPJeGBY8y9VmWSFQBYN+c892TzcXp2POlfVFIAGllQGWc2qSIsIz+kMqQALclsalOywXTJjUl+heDhJg1d1pcnBiy+S9+446tmoBBRjY9azMp9rbOzhMmWulycAIFGHhlVFWUVFRVVVXkwUAyhJUc5MwaMIAAAAASUVORK5CYII=', - 'OpenSUSE': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gIKDigueojqlAAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAADEklEQVQ4y5WTTWxUZRSGn/PdO9PpdKYjbRmmrWmm/00ptlS02FqjEgpRNCxo3Eg0auJCrJqYmGA0hEiC7ghroy40Rk0wppYiIbEBjQSlFEJFWksTqv21vwydmTv3Oy5GFho2PvvzvHnf5MA/XP5jqPPMtU8fWFr6q5P/MDT+uTM1PybcBZlbmD0dL9u0c2j8Cx2YPEqkoBzR7G9V0Y61RyueW0+Eq38NRt2XAX6c+Lqos2Zv6qcb/Wyv3pMXvNgfT9fGHioImcLcjbWLbtAEyPheNmtX1bMeG4IVwZayHmku6Z6LBst7Nld2jNxJPz/Zj/QNNqnFs1bVuOKiRgCLWBcRyNoUgqvhQETAob1sz8i+pkM7bq6Mpms3bUm5CAgB46IggBVEDKIWS44d9x6gK/6MpHLLCA7vXXysJuCEFnq3vC2fXTpkXMjfIfmNREBV8cjQXvo0YSfGwQttVIab8TTDW+2D0cMXunKTc+NfJeN1+8xdlxUH3+boLn+B01PHqSraxpttA4SdUlYyMySLt+pHV19qBMgLVFEFweD56yAWEWFm/TqJwnrm02O8+3MH4ytniQbiZHO3AxinZXhy8GEDoAgGy7pdpbfuKBFTguBwYuIw+xuP0VKyk0RBLW+0DnBl8Ttu+7dYSU+xnFvYbe70tgKFJsrVxZO88+AP7K0+SNau88HwEySKGmgt28XZ6Y85N/0hjjgE3Cglwcpl6TvVpCiIKJYcaqHAjdBbe4Sme7q5tPgtv8yeYCkzjxEl6ERoiHVmHql4/lpiY2WbvDrYqAiIku+uDhYla1PEguVUFNbzeNUrJEI1qCqz6d859+cn3Exfp654a6f0napXUSdvEIO1ihGLIICDMYbl7AxqBas59TUjyeJtNMa2Dz973/v75cDJWn/NW8w6xsVXD9cUEDAhsKDGx/dz+GTElbBJRlsDDRu6ZkNOZNdTza+NALgVRZu/fzL5ejTslup8eoLp1BhTq5fxxCNkwsTD1SSj9/vVxW23NpZW9sAAX145UgTwzegxUNUg/4P+0eP/euu/AVF+N0gj+MWXAAAAAElFTkSuQmCC', - 'Osx': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABrVBMVEUAAAD///////+qqqr///+ZmZn///+qqqqAgID///////+tra339/eAgICoqKjx8fGMjIzm5ubh4eGPj4/g4ODIyMiAgICSkpKLi4vS1tbPz8+Xl5eMjIypqanIyMjW1tZ2dnbR0dGamprFxcV3d3d+fn60tbV3d3dcXFx3d3epqal7fHxxcXF+foCnp6hYWFhyc3Ojo6SMjI5fX196enp+fn6Li4xERERqamqgoKFpaWmFhoeen6A/Pz9QUFCWlpeSk5SUlZWUlZaOjo+Tk5RHR0cuLi5YWFgwMDAeHh40NDQ3Nzc6OjpcXF1rbG0XFxdSU1NVVVVXV1dZWVlbW1tnZ2lwcHABAQEEBAQXFxchISI+P0BISUpaW1xHR0kNDg4qKyszNDU1NTY9Pj8NDQ1cXF4XFxhSU1QSEhIDAwMrKywtLS4uLi4wMDFHSElISEggISE0NDVJSktNTU1FRUVWVlhGRkYEBAVBQUE0NTZQUVJQUVMFBQUqKitWV1lXV1daWlpaWlw+Pj8bGxtcXV9dXV1fX19fYGFgYGBkZGRlZmhpaWlsbGxwcHB2dna844Y9AAAAV3RSTlMAAQIDAwUFBggMDhkeICMkKCgqMDIzPj9ERFBib4CCg4iMjZCcnp+jqamrw83W1tvb3ePl6Ojp6+vs7u7v8PHy9PT09PT3+vr7/f39/f39/v7+/v7+/v50ou7NAAAA30lEQVR4XkXIY3vDYABG4SepMdq2bRSz/capzdm2fvOuDO397Rw0Ly4tz2QAQPbcxuZ2E/STJwfxPhWgG355fRrVAIVb1zeP9UDLfiSwkAcADe8fn7tFxWuEXFRDoer/OgoMTRBCumj8yJwPBo8Zhpk14U856/HI8n0ZUtpZ1udrSzfVneA4roNKjdrwpcMRilb8d8G60+lKnrpWcn9bO+B23w2O8Tzfq4aiNSZJqzn5O4Kw16h06fPZ+VUlUHfo97+VAEb7rSh2UgDd4/U+TBlQY7FMj5gBIGvcarVVfQPVPTG94D0j9QAAAABJRU5ErkJggg==', - 'Rhel': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABj1BMVEUAAAD///////8AAAD///////8AAAD///8AAAD///////8AAAD///8AAAD+/v4AAAAAAAAAAAArKysAAAD///////8AAAAAAAAAAAAAAAD///8AAAAAAAAAAAD///8AAAD///8AAAAAAAAAAAAAAAB5eXn+/v5JSUnKysrS0tJ5eXmqqqqxsrL+/v4ZCgknJyeHh4eIiIjo6OgZCAdOTk7t7e3///8GCwwPAAArKyv19fX29vb9/f0EAAD////+/v4AAAAGBgYHAAAJAAAMAAANAQAPAQAVAQFyCQV9fX2pIRzmEQjn5+cBAAAFAAAAAADnEQjvEgn////uEQjyEgnsEQjzEgnxEgljBwPaEAj9EwnwEglHBQJHBQNNBQIBAAB3CQR5CQSHCgWLCgWRCgWTCwadDAWmDAapDAa/DgfKDwjWEAgGAADh4eHiEQjmEQjmEQkKAADoEQgLAQDtEQgMAQDuEQnvEQjvEQkPAQAfAgEuAwEvAwE8BAL1Egn3Egn4Egn6Egk+BAL+/v5CBQJrB0muAAAAT3RSTlMAAAMEBAkYGhsbMTRLUmpvcHeIjLe6vcHCxM3P0NbW3Ojp6u/w9ff5+fn6+vr6+/v7+/v8/Pz9/f39/f39/f7+/v7+/v7+/v7+/v7+/v7+Q8UoNAAAAO5JREFUeF4tiwVPA0EYRL9SXIsWl+LuxfcOd2Z3764quLu788NZNrxkksmbDP2R7vH6GioLs+iffEzNXd4+TqPErUUpVqMOvwgdzMPn1rv5vPsVeufBTaBK/bH2FPvkEUuIG5jIIc+sHYn/HJ3dC/Hxuo4y8s44dzwBbFkisHN8bVIdXs6jb+H97aCwbHEIqgcml64CD7YllNkAVQC940MLYe5YzvIeQAXNrd19Roc5MdzfdQLUUKaUYyuG9I8y1g4gj6hIak4X5cBIT2MquZJrJdOqpY11ZpAiqVwbY/C7KY1cRCrZxX4pWXVuiuq/hs49kg4OyP4AAAAASUVORK5CYII=', - 'Sabayon': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABvFBMVEUAAAAcUaYdVKwAAAAAAAUABAwWRY4YSZYhZtIhaNYHDx0KCgoFDBcKCgoRMmYSNm0fXL0fXb8AAAAYS5gaTp8fXLwgXsEGBgYFBQUZSpgZTZ4JFSgODg4IEiIOJkwOKVIkW7EnXbQLGzUTExMKGC8LHjwMIkITExMiIiIPEBEPJ00QEhMXOXAaPncOJEgoXbApXbEcHBwwMDAEAgAfHRgQDgo3NC8AAAAHBwcKCgoLCwsJCQkaGhofHx8lJSUwMDA0NDQ4ODiRkZEICQocHBweHh4GBgYHCg8mJiYnJycpKSkrKystLS0uLi4ICAgODg43NzcRERF1dXUUFBSjo6O1tbUbGxsEBAMLGS8MDA0iIiIjIyMkJCQNDQ0NHTYKCQkoKCgPDw8QEBArMDkKCgkRERIREhMxMTEyMjISIz00Njk1NTU2NjYCAgIVFRU5OTo5P0c8PD0+Pj4/QURAQEBHR0dKSkpMTExSUlJiYmJlZWVnZ2cWFhZ2dnZ4eHh8fHx9fX2FhYUXFxeVlZWXl5eYmJiZmZmcnJwZGRmlpaWrq6usrKyvr68KFiq/v7/FxcXY2Nji4uLn5+ft7e0yif9uAAAAN3RSTlMAAAApKSkqKioqg4OEhISEhoa1tra3t7y9vr7S09PT09TU+Pj5+fn5+/v7+/v7+/v7/v7+/v7+70RY/wAAAPlJREFUGBktwQNbQ2EYANC3lt3NtazltvDh+s52tm1z2f7Dfe3pHPiTllfT1V2bnw5xCVDUPruyub271VEMicCUTfQ6XEtritq/XA5MwVvw7NFydOB0e+WhQoCUzh5MxmcWPRZxxNHXmgo5doyxDd3ESPhaCNtzocrsY9BXFPHU7zdXQ+McTwhZ//lAhPC+ySZoIBuUUv77HVGbNTJYB5X4SnZh8hlBQuhYHq6ArPnphxdtP/p88vQqBBcyIKnFaD29vdO0+0tlwNiWDMCJ0ujOeejicEySRA6YUqfJs7qnur2mqRKI4wxKQFUDioGDf7psfXO9PlMHzC/HlDtslvM8zQAAAABJRU5ErkJggg==', - 'Slackware': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AcEDi0qZWWDgAAAAx1JREFUOMt9kktoXHUchb/ffc1M7rySSdJMOknFPMRitLgoNKKI8ZHGKkgrjU8SitidimSh2UkXoQmoO1dGQSxJjdvOtqSaqlR0USEGSjVJGxuSmWR6M3fu4/93YX0g4rc9HA6cc4Q7DI+fpzz7PA8++2mxvZAeBZ4xhHtFcJRmXWsWvb36/OLcyxf5B/KHeYHy7DmGx1+YSDjmWTdlobTGMAStQGkNoLXS4tXDq7u7tUcWz49tA8jR8QUuzB5n5NTCV13F9JEo1JJwTLKuzU61QiOMcd0UDb+BncwQK3Rl15eNja3ui/Njq8aF2eMcO/XlBz0H8oO2ZUkum6A13WB99TtyzXlaCi24SaFa+ZFCzsG2DNnfkdbFjsI1APPhk+d6ujqznycdCxFozadYWvyMpx47wa+bPkGksKwUNnsk3TaCGASRXDZh5LpHXPPg4Rcni+3uYBxrtBbQghlscOVKmYHeEm0ZIZ9xyLffw41ND6VAa43SmjiMByzHYtjzwr9arfshxf5jOKlvKZfn8es77N2uks24PPfSFD/9Uvt7AtPKWmEU9d645eHYJo5tcKi/FX/zG+zmQxQH+rANk862DOW5N/hhaY64cJSa5xNFCgDDILZACMKYWAmh73HmzFsMlBQJ06LeiMinE1S3KzRCm5rXIIoUIoKIYCVM36urZFbEoiBLNMIhAE6/NsSB7h6SKZdL8xsUOnpx9j1KbTdARACIowArYe1ergfNT2i0mIbJys0GI6PT3N1/hJvrPxOFdRJNBQIy/FapI4Bpgohgcjuw+jq8jy8tV55MNBWI4ohS802CpizKv8q+FgALZAfYgSyAZtNro1oLaU1VvxCA029Oraxs7u/tKnXiNjn8HyKwur6lI++6vPK4V7IA7u+1Dyu1tr183ddNbkHuXP8/zEIYeFqiLRl6YO/p0bHJdflT/PD9qZa1W+ry99fcvlAlcZwUpuUAglIRYVgnDEIOlna4q0M/NPnuO1/PzMwg/045O/XeibUt5/Xangx6viSVFpK2jtMpvdyWCz+5ryf10clX3/amp6eZmJjgd441URWWJY8BAAAAAElFTkSuQmCC', - 'Trisquel': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABjFBMVEX///8AAAAAAAAAAAAAADMAAGYAAAAAHFUAGWYAF10AImYAIGAAHloAHGMAKGsAGmYAJmYAJGEAKnUAJ1gAMXYAJnEAJGQAI2EAK28AK3cAGTEAMHgALXEALXgALG0AFUAAI2oAK3EAMngANoYALXMANIAAM4IANIIAL3gANIcANokANoQANYQAOY0ANIYANooAN4kAN40AOY0APZMANIUAOY0AO5AAPZUAPJAAP5MAPpQAQJUAOYsAPpYANoUAPpoAPpUAM4AAQJkAPZIAPJEAQpgAN4cAPpQAPZUAPJEAO4oAOosAOo8AQJoAOYsAO44AQpsAO48AQp0AP5UAQpoARJwAQ58ARaAAQZgAQ54AQ50AQpgARaIARqMARaMARaIAR6QARaIARaEASakARKEAR6MASqsARKEASKcAR6MARqYAR6UATbEATa8ARqUARKAAR6oARqMASKgATK8AR6QATbIATbAASq0AR6cASKgASqwAR6UASKcATa8ASqoASqwAS6wASKoAS60ATbHn4CTpAAAAhHRSTlMAAQIFBQUGCQoLDxAREhMUFBUYGhobHB0eHh8gIiIjJCQkJCYoLC0xMTE0NDo6Oz1BQUNHSUxOVFVVVldaWl5iY2RkZWZoamtsb3FycnR1ent9f4KDhIiJioyNkJGYm5+foqOkpqamqKmqrKytsLKzs7e4uLy8v8TFxcXGx8rO0NXY2eZc4XYcAAAAzUlEQVQYV2NggAMWdX12BiQglJ+SXqIMYqmHxQdJMzAEmXLKuCcyMTBYNWYmNTVYMARzMToqlAsycFYYSBppFFVp6nqa2VkVcDCIFXK7GfrrlWWISKjqpAUwMPDkibIz+Inn5iSHxxQn8AHN9Ig1d7DRzimtrasO5QJZwqhmrMoQ2Rwix8WIcANrTaqAT6AWQoC33t5Flj1CCS7AUenqrMJk7YRQEpXFb6LoawvjMjIJZ8dZ+maKMcHMZWST8or2lmdD2MPILKYlxgziAwCs9yR3GXo0vAAAAABJRU5ErkJggg==', - 'Ubuntu': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABKVBMVEX////ojFzplGf1zbnqnHLvs5P10b3yuZv1xKrytZXvtJXys5LysI32waT0n3HxiVHwg0jxhk31kFn0h0zxf0P0hUrveTv2iU3yfkD1hEfyejv5eDLybSX0aR7zZxvyayH6ZxnxZBj4YhH7XAb5WALlUQLeTwHgUAHeTgHfTwD65NzdTQDdTQHdTgD31MfcTgLcTADcTQD////xt5/31Mf54dfmfE/dUAbeVQ/jcUDcTgHeWBnnflHohFvpjGbqkGztnX342Mz53dLgXiP65d399PHdUgrtoYLyu6Xzvaf76eLfXB/rkm/fWhvupojwrpTeVhTgYSfgYynzwa30xbL1ybnngFT31snngljhZS3539XhZzDiajbibDn77OX88Ovrl3X99vTjbz1fisGCAAAAMHRSTlMABgYGBwcHJiorMDA1NXGHjY2Nl5mZmZyfn6O5u8XHzc3X193j9fj4+vr6/f39/f08OUojAAAAxklEQVQYGS3BBULDQABFwY8WLdbiHjzI201Sd8Hd3e5/CJKWGUVio0u77vJYTP/iG7RsxhXpmDe0BDsHc12SpgzkyscnhVojZ8algT34KD5YGxTq4PYpabh+es3fXJSbXy8wIgeO7Dehkr2HFZnLn1SQIXToXcGWtivN7GmayO8brGsNKjZFKGs9WNWsIVP182fp58ZnHSY0ZKBYurO29ngLbr80Y4Bzz3v3fUhI6l2krbrPQqdCPdOGtmS32oYTjjHO5KBCf5XaKpkxFPzRAAAAAElFTkSuQmCC', - 'Windows': 'iVBORw0KGgoAAAANSUhEUgAAABIAAAAQCAYAAAAbBi9cAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAA+pJREFUOE+F0n84FHYcB3CWSsL9ojo/6ik64c6PnTjmSS0limmrpBm2G002y++xzXRz6zE0R4nbw+RnTj/WD4sbanLkkAe55ccYlyNme4SrO9u9d13PI3/saZ+/vs/3831ez+f9eb5aWsuqy2mjRYeNUa7YmtjfTico7jNJ8z0eG24NB9vvnDrvufzpq89Npnr8VjMddNmuRh9rDfp36mFg91oM7qPIc5JdbDJq3An/JfCu7Hl53W2lpS220pP2OuniN299jAYbYizSENIoAgbCTdrTKtxOJVdvGo8psUwKy7Vxe4ez1YEVudGP8YEZzyveInFJ6mZRHHqYazDspw/pJwTIuERM5JIwmUdGdyo9K7/BszGzzg6fXzZHGJ8KvzQqXKOpoIeZLjofWR++BPWyCEnPY4xFGEKWQcLjMjKmr1MwfcMYwmz/Y4KOgNki0V5k1dkjUWCK93Kp2PMFFawos8cm1gZ2GqjLXktL4mbQPHLQ4B9ZDFE5+S356fQlyuJMqzH++HnTo6ui2OO1ko9Ul+4fxfd3d4F7k4YTReqpuFS88bGZUE2QNNDobuIq8Q5CduHb7lFJaTnvnym9ergjMWD/FG8zf+aKS3G9JO5C01Asah6wUXrvALKEDoitMMHhDKrKJdg8RU2s0EB2EWWur8dd7PDPFv6dUC0Gv3kAN36VPRGP/5k5NS6lljWxG0TDiSr1VKhoPwhevRMSqkwRxDObc/DavGtpP6zoi8XOyZfhnyNEvKANBU0P8VPfI/wyNCGXSn7wlEmyA9KrgmOKGth3eDVvPfyywq2dnUEv2R9qG2rLsH7xJXziKnWcI8tlTvEC7Mu8hROlImTU9aKqcwQ1vWOihWFu+sJknmph5CvxQh87c7bNh/NXo03hrMCosyvLmMNgMF7TQL6J1dsZIUVwjKqEO+cajp5vxPN439U/gKBt8PTcYHzL/BgHCyOf4unAISj6mFC2bYC82kB5Ls460NHRUVsDeYSXpGw7UgC7sAtwShDgzdM38W7BbURXtqpqhfmB8sEQuXwoCM/6faGQuGCxyxyKWhIm+PrSD495WL3cT0hhi8Whc3NbAs9KaOyCTvrJ8qkdX19XBeTUDU00+55USFzVU2yHstcaix0mUAjJkJeuRU868Ucmk0lcguiBnMAVxjbbdHV1yeq8+u4Hgo22huSG+iQXp83ftaxW3lsPZcs6KG5T8OwaAfJiPcxlrVRVRhvF02i0F/t5VbHZ7JWDfErKTLnhE3mFPuRFepg/uxqz6TqLv6euGj3ut87t/4ylvre3t3ZehOWWO1zjSFEqMVP4GfGb/DBykJcjmaZOoLsc+hcVY/LaAgcTQAAAAABJRU5ErkJggg==', - 'OpenBSD': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AoYAykIPu64pQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAADTklEQVQ4y32RXUxTdxjGn3N6eujoKT3SUkuk3VoBGfVjgFXAsZ7WkipyYXQbuu3CzUXZmGRbssnFEseFkWzgcGzGXky9MWL8TsC4IeFgtK4oAqOnG5vMVl1pCMVWQD7b/y5M6jLdflfvxfPked/nBQA0NDSChqnGVrLuGkES742NhJAdhAKAuk9yyUs5Gry7RQMZAARCWgivpQiPe71P5DUfH0xaqTL7m/iiLkJmphawa+e4SM2PvUyC4yUIBu8CnAQKAK53rCA5OUtQtStVpJ4Gw/FOBddZVKhCfq4MP4n6+at+DUsJm/e0G9JZzYEvI2tHwlEYjDxomkZ+3nG8WroRtHihZVOhVlorDQzh0okhcByDP4ZGcf+X9XAsvY5/RsBa7Kq5H/CqLctKyl/g08S2i6fq8W/MS3P34T9wNDVYSeDX1eTD9xhiLXbtB/Akwmmv6Kr+ICFkLpGhtNSM3qsSstS3oX8lSsmsxS6ZVn3j6PvVVqhUcvC8AtPxVPxwygVKvngN89WOjgVprggGA4eenjB4nsXsTASpC63I0wVTZYPR11FoKRB8Ax54PCFk6BhMTk5CPR3GSbHouGzknr/bYFq9EAvfc9Tu1sLjHcXNKxLuTOTgzOlOe7IHBc/beAXWpWmXlz8a84nhcLQ+ecVzsAEQrMWuMX+f9HZF2YPZ28FVSNfoPWqOzMUmqYMAJm7+/OOzXQFwHGpyEV+vi+yvtxBC9pDmpgJC4tvI3mo9GTitIxvW24nT7ug67HY/3eDs2bbyrVsrY2day70rV6kRfDAHk5lDLJqAmmeRiD9GJDKHvwb74R8G0mkTPjrQTTG122xkTTbwaV2b1H4u16JQKXGr7yG2b8/H1MQ09IsTSEmRwzf4CCwzD+dmE1re8CI7wwi5XNlFf9vaTXX4dWJg4LLl7h05fpNGwNAMWpp9CIVYNO/tRCzGwpDFQaVMQTS2CKY0BWr3GVGWNSXKACDDaA4Mh976pq9f5Sy09GgKlmeAMIBKzUKpU+BFoxJecRhUfAbMxDi4eADfHVmE79v7q575gvvYeVvjZ58LD5mwsKUyX0hnf0feslnQCWD4zxnc6reKisxsfH2oscqcmTmK/+Ow252cna7K52r+Bky6PqmoT5HBAAAAAElFTkSuQmCC', - 'Gnu': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAHC/Qd8AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AoYAywUV5gQrwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAAAmJLR0QA/4ePzL8AAAHHSURBVBgZZcFPSJNxAAbgt28uanSRapCsy0KK2CWCUYecB5HBolNsglZEsmAwaUvHaIu2YYty1bKGMKmgHIRbGEhURIEY/VHUQ+uwZcxEJSL5Ft+W5ubv7TMkip4HJglrPhHeUmAchWprBK+Kni9pukoAJAOF04j4y6Z9dZtu3sIT32lxXwxeRepwidP0mAF0p5JKPfBsLOMBQs/dJ3pehzcnQ+jc6SfFKlN8KMgEodLiQJWTFOyPY52mUbLmzANY4zro+xG8DVXn8UjOvg2WFSB69Oxo8Hx3F3CM2KsE2wO8LKbp5gWvtYK78zHKooM/eZFu9t0AUOCYIIf4IRfvOrIBKn12vEJyjtDhtx3QfV+dYPgO/qipOfeAgA0qn+UaW+TE9ZQjE0g63uhrpZh2yJyOPXKFe9uWrmTxtmpaSOwCgk5bWUfQywyt3MOOHmB4f9MyBu610ii3fRvlvBgRMrOc4mPmxQs2yPpylABObQ9FJxVyhAqXuEiyyOFiQcnT6TipBWDf6k9fGjwUWZgjF8V7PmVOlPmxkNmNvxhR136muELOclZ85QR73fiHBgZshM1e+UzBdzOmRvxPgmqLlKgl8mjGul/jG/ctzIC/LwAAAABJRU5ErkJggg==', - 'CrunchBang': 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAQCAQAAAC45EetAAAA8ElEQVR4XnWOsUpCYQBGz1TIHYu2Qix6g0DEtSeQu/UIISJtUS8gJq61F1wcdMohcBDxKUR8hsz1xA/y44/cs3znbB+RJ0Skl3pSkeFQbUs79VAPzrwPFRmN1Ja0Ug/16I93+1oi4lKte+zMXv32WuoAm43lXMrqzbFncgWw21lORf4+/PREKpAhYqZuPXZ+T/3yXbZEajV1JavUQ104sRcq0myqc5mnHurWqc/7yhExVwuPncl+C4Bu13L60ueAwcByOtLhgAIRCzU38fRGTmSxUBvSSD3Ui1NvQkXWa7Uq1dRD9R17HiqyRUSy1NP6B7e1Yu2GtlUKAAAAAElFTkSuQmCC', - 'Yuno': 'iVBORw0KGgoAAAANSUhEUgAAABgAAAAPCAYAAAD+pA/bAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAABDtJREFUOE+FlHtMm1UYxrtsi8aEgCb+oTFmZur+WNS5RaPERU10C2qGaBgb6hgwLwMmHTIKlIKlQIHSQrmU24BSSmnpBVooUmihtEC5yKWDjVu5uOkcEca4lG5E93j6EQmELX7Jky/fOed9fu973vMdGu0xT3Cgz57yXMZLDdXcy821PFWLKmuA6HqLMqtLX5POl4iYb2ukWW8IOOFe/qfe3/M4n0eOjwyZD8//bldODOk37N1yDJgl+LVdjEGLFKO9KkzZm8hbje7mIrTXZ7sMtTydrJh15H8hHW11XvN/jGS7VudcD5w34ZZzeQYb67fwYO03LN4exo1+LWzNxbA05O5QuzbHqRYn+++CHDx4YK9WLfaedfQzV5em54g5Zbi8OIml+VFMDLWQ7GXoaSmFWZsDZVGCO2u0EbkhHTrhFqi9PmelSsQ8tAtSVch60dpUeGe4kxgZxegzVkBzlQ2NKBG2+iJIMqMok9r8OLRIMqApToSqmAWTmk9B2+o2YW79oshU7ABcuvAFrVGWXkVKpBYoSaBSxIS2mINpiwbjZiUMZRloVfJQyaXDKObBpimBScpHFe8KmmXpaKhK3arGrBVuVBclHN2CiPNin1OVs1tVJYlQlyZBxA6DviQVo6ZaOKd7sTplw53BVugruBBzfsRslw7rZPxaczWutSpQV/gzJPxo1JexyfaxKBBpuiEx+tw+CpKdEvGWTprGlhcwqbIzL5/DYKMYndpK3L1hxf3ZfkrzwybUZjPhnOqmvlcmutFF1jis9QSShOrcWNSXJ1MA0ou/NZWc8Ddfe4VGO3bk0JON1dyMMlK+gmxNrZCFhZF2Kng7YNO0awt4b7wLNp2EqtAsF6ImP56SG0B6siovTYpIjg15gapCVhAfJRUyIBFEo6k8AyuTtkcC/qvG/XbDexulWJvqgYH0o0nKhVHFJ40XwFQnWM5OCX+XMg86c3KvVMSMapCmPpSTIygTxGKZZOcOXhrr3Mp4uzkFuG6B3ajE3TELDDU8qEmsmvRATxquKkxAnSTFjwKEfv3JU9JC5unG6rQ1bTkbQ4Yq/DVgxOqwBWt2K9Yne3ZCZvrgHO2k5paHzOhSiVCZSkdNTgzy40JRlPgDhDHBCxUZdCs91G8fLeK87zOl6XSOICZYXMGNhDqX9fDP/mbK2DXVi/szm03eLpejl5pzOfqwOt4JBT8OeYwQt/4R/BR0OzXiLCM5LOCji/4nXt46rpywgG+zor5RxgSdupBzJdglSY+5ZZbl3XNY6mbn7W0Lcx06zBg1WBjtcC6OmG+OmRTrFrnIUZESZeVeCpwh8TpiPsQ47/tloM97T+/6m8mg55mT3tStyL54mhlwwtszNvjzD8/6HH8i7PvvPPRioZdRWuDBZUR6pEWG7I8P9Xs1Jsj36MfvvO5J/+rTw58dP7afJPfBgeef3XGz/gskFVpJc4HwGwAAAABJRU5ErkJggg==' - } - }; - - ExpandComment = { - init: function() { - if (g.VIEW !== 'index' || !Conf['Comment Expansion']) { - return; - } - if (g.BOARD.ID === 'g') { - this.callbacks.push(Fourchan.code); - } - if (g.BOARD.ID === 'sci') { - this.callbacks.push(Fourchan.math); - } - return Post.callbacks.push({ - name: 'Comment Expansion', - cb: this.node - }); - }, - node: function() { - var a; - if (a = $('.abbr > a:not([onclick])', this.nodes.comment)) { - return $.on(a, 'click', ExpandComment.cb); - } - }, - callbacks: [], - cb: function(e) { - e.preventDefault(); - return ExpandComment.expand(Get.postFromNode(this)); - }, - expand: function(post) { - var a; - if (post.nodes.longComment && !post.nodes.longComment.parentNode) { - $.replace(post.nodes.shortComment, post.nodes.longComment); - post.nodes.comment = post.nodes.longComment; - return; - } - if (!(a = $('.abbr > a', post.nodes.comment))) { - return; - } - a.textContent = "Post No." + post + " Loading..."; - return $.cache("//api.4chan.org" + a.pathname + ".json", function() { - return ExpandComment.parse(this, a, post); - }); - }, - contract: function(post) { - var a; - if (!post.nodes.shortComment) { - return; - } - a = $('.abbr > a', post.nodes.shortComment); - a.textContent = 'here'; - $.replace(post.nodes.longComment, post.nodes.shortComment); - return post.nodes.comment = post.nodes.shortComment; - }, - parse: function(req, a, post) { - var callback, clone, comment, href, postObj, posts, quote, spoilerRange, status, _i, _j, _k, _len, _len1, _len2, _ref, _ref1; - status = req.status; - if (status !== 200 && status !== 304) { - a.textContent = "Error " + req.statusText + " (" + status + ")"; - return; - } - posts = JSON.parse(req.response).posts; - if (spoilerRange = posts[0].custom_spoiler) { - Build.spoilerRange[g.BOARD] = spoilerRange; - } - for (_i = 0, _len = posts.length; _i < _len; _i++) { - postObj = posts[_i]; - if (postObj.no === post.ID) { - break; - } - } - if (postObj.no !== post.ID) { - a.textContent = "Post No." + post + " not found."; - return; - } - comment = post.nodes.comment; - clone = comment.cloneNode(false); - clone.innerHTML = postObj.com; - _ref = $$('.quotelink', clone); - for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { - quote = _ref[_j]; - href = quote.getAttribute('href'); - if (href[0] === '/') { - continue; - } - quote.href = "/" + post.board + "/res/" + href; - } - post.nodes.shortComment = comment; - $.replace(comment, clone); - post.nodes.comment = post.nodes.longComment = clone; - post.parseComment(); - post.parseQuotes(); - _ref1 = ExpandComment.callbacks; - for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { - callback = _ref1[_k]; - callback.call(post); - } - } - }; - - ExpandThread = { - statuses: {}, - init: function() { - if (g.VIEW === 'thread' || !Conf['Thread Expansion']) { - return; - } - return $.on(d, 'IndexRefresh', this.onIndexRefresh); - }, - setButton: function(thread) { - var a; - if (!(a = $.x('following-sibling::a[contains(@class,"summary")][1]', thread.OP.nodes.root))) { - return; - } - a.textContent = ExpandThread.text.apply(ExpandThread, ['+'].concat(__slice.call(a.textContent.match(/\d+/g)))); - return $.on(a, 'click', ExpandThread.cbToggle); - }, - disconnect: function(refresh) { - var status, threadID, _ref, _ref1; - if (g.VIEW === 'thread' || !Conf['Thread Expansion']) { - return; - } - _ref = ExpandThread.statuses; - for (threadID in _ref) { - status = _ref[threadID]; - if ((_ref1 = status.req) != null) { - _ref1.abort(); - } - delete ExpandThread.statuses[threadID]; - } - if (!refresh) { - return $.off(d, 'IndexRefresh', this.onIndexRefresh); - } - }, - onIndexRefresh: function() { - ExpandThread.disconnect(true); - return g.BOARD.threads.forEach(function(thread) { - return ExpandThread.setButton(thread); - }); - }, - text: function(status, posts, files) { - return ("" + status + " " + posts + " post" + (posts > 1 ? 's' : '')) + (+files ? " and " + files + " image repl" + (files > 1 ? 'ies' : 'y') : "") + (" " + (status === '-' ? 'shown' : 'omitted') + "."); - }, - cbToggle: function(e) { - if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) { - return; - } - e.preventDefault(); - return ExpandThread.toggle(Get.threadFromNode(this)); - }, - toggle: function(thread) { - var a, threadRoot; - threadRoot = thread.OP.nodes.root.parentNode; - if (!(a = $('.summary', threadRoot))) { - return; - } - if (thread.ID in ExpandThread.statuses) { - return ExpandThread.contract(thread, a, threadRoot); - } else { - return ExpandThread.expand(thread, a, threadRoot); - } - }, - expand: function(thread, a, threadRoot) { - var status; - ExpandThread.statuses[thread] = status = {}; - a.textContent = ExpandThread.text.apply(ExpandThread, ['...'].concat(__slice.call(a.textContent.match(/\d+/g)))); - return status.req = $.cache("//a.4cdn.org/" + thread.board + "/res/" + thread + ".json", function() { - delete status.req; - return ExpandThread.parse(this, thread, a); - }); - }, - contract: function(thread, a, threadRoot) { - var filesCount, inlined, num, postsCount, replies, reply, status, _i, _len; - status = ExpandThread.statuses[thread]; - delete ExpandThread.statuses[thread]; - if (status.req) { - status.req.abort(); - if (a) { - a.textContent = ExpandThread.text.apply(ExpandThread, ['+'].concat(__slice.call(a.textContent.match(/\d+/g)))); - } - return; - } - replies = $$('.thread > .replyContainer', threadRoot); - if (Conf['Show Replies']) { - num = (function() { - if (thread.isSticky) { - return 1; - } else { - switch (g.BOARD.ID) { - case 'b': - case 'vg': - return 3; - case 't': - return 1; - default: - return 5; - } - } - })(); - replies = replies.slice(0, -num); - } - postsCount = 0; - filesCount = 0; - for (_i = 0, _len = replies.length; _i < _len; _i++) { - reply = replies[_i]; - if (Conf['Quote Inlining']) { - while (inlined = $('.inlined', reply)) { - inlined.click(); - } - } - postsCount++; - if ('file' in Get.postFromRoot(reply)) { - filesCount++; - } - $.rm(reply); - } - return a.textContent = ExpandThread.text('+', postsCount, filesCount); - }, - parse: function(req, thread, a) { - var filesCount, post, postData, posts, postsCount, postsRoot, root, _i, _len, _ref, _ref1; - if ((_ref = req.status) !== 200 && _ref !== 304) { - a.textContent = "Error " + req.statusText + " (" + req.status + ")"; - return; - } - Build.spoilerRange[thread.board] = req.response.posts[0].custom_spoiler; - posts = []; - postsRoot = []; - filesCount = 0; - _ref1 = req.response.posts; - for (_i = 0, _len = _ref1.length; _i < _len; _i++) { - postData = _ref1[_i]; - if (postData.no === thread.ID) { - continue; - } - if (post = thread.posts[postData.no]) { - if ('file' in post) { - filesCount++; - } - postsRoot.push(post.nodes.root); - continue; - } - root = Build.postFromObject(postData, thread.board.ID); - post = new Post(root, thread, thread.board); - if ('file' in post) { - filesCount++; - } - posts.push(post); - postsRoot.push(root); - } - Main.callbackNodes(Post, posts); - $.after(a, postsRoot); - postsCount = postsRoot.length; - a.textContent = ExpandThread.text('-', postsCount, filesCount); - return Fourchan.parseThread(thread.ID, 1, postsCount); - } - }; - - FileInfo = { - init: function() { - if (g.VIEW === 'catalog' || !Conf['File Info Formatting']) { - return; - } - this.funk = this.createFunc(Conf['fileInfo']); - return Post.callbacks.push({ - name: 'File Info Formatting', - cb: this.node - }); - }, - node: function() { - if (!this.file || this.isClone) { - return; - } - return this.file.text.innerHTML = "" + (FileInfo.funk(FileInfo, this)) + ""; - }, - createFunc: function(format) { - var code; - code = format.replace(/%(.)/g, function(s, c) { - if (c in FileInfo.formatters) { - return "' + FileInfo.formatters." + c + ".call(post) + '"; - } else { - return s; - } - }); - return Function('FileInfo', 'post', "return '" + code + "'"); - }, - convertUnit: function(size, unit) { - var i; - if (unit === 'B') { - return "" + (size.toFixed()) + " Bytes"; - } - i = 1 + ['KB', 'MB'].indexOf(unit); - while (i--) { - size /= 1024; - } - size = unit === 'MB' ? Math.round(size * 100) / 100 : size.toFixed(); - return "" + size + " " + unit; - }, - escape: function(name) { - return name.replace(/<|>/g, function(c) { - return c === '<' && '<' || '>'; - }); - }, - formatters: { - t: function() { - return this.file.URL.match(/\d+\..+$/)[0]; - }, - T: function() { - return "" + (FileInfo.formatters.t.call(this)) + ""; - }, - l: function() { - return "" + (FileInfo.formatters.n.call(this)) + ""; - }, - L: function() { - return "" + (FileInfo.formatters.N.call(this)) + ""; - }, - n: function() { - var fullname, shortname; - fullname = this.file.name; - shortname = Build.shortFilename(this.file.name, this.isReply); - if (fullname === shortname) { - return FileInfo.escape(fullname); - } else { - return "" + (FileInfo.escape(shortname)) + "" + (FileInfo.escape(fullname)) + ""; - } - }, - N: function() { - return FileInfo.escape(this.file.name); - }, - p: function() { - if (this.file.isSpoiler) { - return 'Spoiler, '; - } else { - return ''; - } - }, - s: function() { - return this.file.size; - }, - B: function() { - return FileInfo.convertUnit(this.file.sizeInBytes, 'B'); - }, - K: function() { - return FileInfo.convertUnit(this.file.sizeInBytes, 'KB'); - }, - M: function() { - return FileInfo.convertUnit(this.file.sizeInBytes, 'MB'); - }, - r: function() { - if (this.file.isImage) { - return this.file.dimensions; - } else { - return 'PDF'; - } - } - } - }; - - Fourchan = { - init: function() { - var board; - if (g.VIEW === 'catalog') { - return; - } - board = g.BOARD.ID; - if (board === 'g') { - $.globalEval("window.addEventListener('prettyprint', function(e) {\n window.dispatchEvent(new CustomEvent('prettyprint:cb', {\n detail: prettyPrintOne(e.detail)\n }));\n}, false);"); - Post.callbacks.push({ - name: 'Parse /g/ code', - cb: this.code - }); - } - if (board === 'sci') { - $.globalEval("window.addEventListener('jsmath', function(e) {\n if (jsMath.loaded) {\n // process one post\n jsMath.ProcessBeforeShowing(e.detail);\n } else {\n // load jsMath and process whole document\n jsMath.Autoload.Script.Push('ProcessBeforeShowing', [null]);\n jsMath.Autoload.LoadJsMath();\n }\n}, false);"); - return Post.callbacks.push({ - name: 'Parse /sci/ math', - cb: this.math - }); - } - }, - code: function() { - var apply, pre, _i, _len, _ref; - if (this.isClone) { - return; - } - apply = function(e) { - return pre.innerHTML = e.detail; - }; - $.on(window, 'prettyprint:cb', apply); - _ref = $$('.prettyprint:not(.prettyprinted)', this.nodes.comment); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - pre = _ref[_i]; - $.event('prettyprint', pre.innerHTML, window); - } - $.off(window, 'prettyprint:cb', apply); - }, - math: function() { - if (this.isClone || !$('.math', this.nodes.comment)) { - return; - } - return $.event('jsmath', this.nodes.post, window); - }, - parseThread: function(threadID, offset, limit) { - return $.event('4chanParsingDone', { - threadId: threadID, - offset: offset, - limit: limit - }); - } - }; - - IDColor = { - init: function() { - if (g.VIEW === 'catalog' || !Conf['Color User IDs']) { - return; - } - this.ids = {}; - return Post.callbacks.push({ - name: 'Color User IDs', - cb: this.node - }); - }, - node: function() { - var rgb, span, style, uid; - if (this.isClone || !(uid = this.info.uniqueID)) { - return; - } - span = $('.hand', this.nodes.uniqueID); - if (!(span && span.nodeName === 'SPAN')) { - return; - } - rgb = IDColor.compute(uid); - style = span.style; - style.color = rgb[3]; - style.backgroundColor = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")"; - $.addClass(span, 'painted'); - return span.title = 'Highlight posts by this ID'; - }, - compute: function(uid) { - var hash, rgb; - if (IDColor.ids[uid]) { - return IDColor.ids[uid]; - } - hash = IDColor.hash(uid); - rgb = [(hash >> 24) & 0xFF, (hash >> 16) & 0xFF, (hash >> 8) & 0xFF]; - rgb[3] = (rgb[0] * 0.299 + rgb[1] * 0.587 + rgb[2] * 0.114) > 125 ? '#000' : '#fff'; - return this.ids[uid] = rgb; - }, - hash: function(uid) { - var i, msg; - msg = 0; - i = 0; - while (i < 8) { - msg = (msg << 5) - msg + uid.charCodeAt(i++); - } - return msg; - } - }; - - InfiniScroll = { - init: function() { - if (!(Conf['Infinite Scrolling'] && g.VIEW === 'index' && g.BOARD !== 'f')) { - return; - } - this.threads = g.threads; - return $.on(d, '4chanXInitFinished', this.ready); - }, - ready: function() { - $.off(d, '4chanXInitFinished', InfiniScroll.ready); - $.on(d, 'scroll', InfiniScroll.scroll); - return InfiniScroll.scroll(); - }, - scroll: $.debounce(100, function() { - var url; - if (InfiniScroll.isFetching || ((d.body.scrollTop || doc.scrollTop) <= doc.scrollHeight - (300 + window.innerHeight))) { - return; - } - if (InfiniScroll.isDead) { - return InfiniScroll.notice(); - } - if (InfiniScroll.cache && InfiniScroll.cache.time > Date.now() - $.MINUTE) { - return InfiniScroll.parse(InfiniScroll.cache); - } - new Notice('info', "Fetching next page.", 2); - InfiniScroll.isFetching = true; - url = "//api.4chan.org/" + g.BOARD + "/catalog.json"; - return $.ajax(url, { - onloadend: InfiniScroll.cb.load - }, { - whenModified: true - }); - }), - parse: function(response) { - var botPostForm, el, nodes, omitted_images, omitted_posts, op, post, postlink, posts, replylink, thread, threadID, threadNodes, threads, _i, _j, _len, _len1, _ref; - threads = InfiniScroll.parsePages(response); - threadNodes = []; - nodes = []; - if (!threads.length) { - InfiniScroll.notice(); - return InfiniScroll.isDead = true; - } - for (_i = 0, _len = threads.length; _i < _len; _i++) { - thread = threads[_i]; - posts = []; - omitted_posts = thread.omitted_posts, omitted_images = thread.omitted_images; - threadID = thread.no; - el = $.el('div', { - className: 'thread', - id: "t" + threadID - }); - op = Build.postFromObject(thread, g.BOARD); - posts.push(op); - replylink = $.el('a', { - href: "res/" + threadID, - className: 'replylink', - textContent: 'Reply' - }); - postlink = $.el('div', { - className: "postLink mobile", - innerHTML: "View Thread" - }); - if (omitted_posts) { - posts.push($.el('span', { - className: 'summary desktop', - innerHTML: "" + omitted_posts + " posts " + (omitted_images ? "and " + omitted_images + " image replies" : void 0) + " omitted. Click here to view." - })); - $.prepend(postlink, $.el('span', { - className: 'info', - innerHTML: "" + omitted_posts + " posts omitted" + (omitted_images ? "
(" + omitted_images + " have images)" : "") - })); - } - $.add($('.postInfo', op), [$.tn('\u00A0\u00A0\u00A0['), replylink, $.tn(']\u00A0')]); - $.add(op, postlink); - if (thread.last_replies) { - _ref = thread.last_replies; - for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { - post = _ref[_j]; - posts.push(Build.postFromObject(post, g.BOARD)); - } - } - $.add(el, posts); - threadNodes.push(el); - nodes.push(el); - nodes.push($.el('hr')); - } - InfiniScroll.features(threadNodes); - if (botPostForm = $('.board > .mobile.center')) { - return $.before(botPostForm, nodes); - } - }, - parsePages: function(response) { - var newThreads, number, page, pages, thread, threads, _i, _len; - pages = JSON.parse(response); - newThreads = []; - for (number in pages) { - page = pages[number]; - if (!(pages.hasOwnProperty(number))) { - continue; - } - threads = page.threads; - for (_i = 0, _len = threads.length; _i < _len; _i++) { - thread = threads[_i]; - if (g.threads["" + g.BOARD + "." + thread.no]) { - continue; - } - newThreads.push(thread); - if (newThreads.length === 15) { - return newThreads; - } - } - } - return newThreads; - }, - features: function(threadNodes) { - var err, errors, post, posts, thread, threadRoot, threads, _i, _j, _len, _len1, _ref; - posts = []; - threads = []; - for (_i = 0, _len = threadNodes.length; _i < _len; _i++) { - threadRoot = threadNodes[_i]; - thread = new Thread(+threadRoot.id.slice(1), g.BOARD); - threads.push(thread); - _ref = $$('.thread > .postContainer', threadRoot); - for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { - post = _ref[_j]; - try { - posts.push(new Post(post, thread, g.BOARD)); - } catch (_error) { - err = _error; - if (!errors) { - errors = []; - } - errors.push({ - message: "Parsing of Post No." + (postRoot.id.match(/\d+/)) + " failed. Post will be skipped.", - error: err - }); - } - } - } - if (errors) { - Main.handleErrors(errors); - } - Main.callbackNodes(Thread, threads); - return Main.callbackNodes(Post, posts); - }, - notice: (function() { - var notify, reset; - notify = false; - reset = function() { - return notify = false; - }; - return function() { - if (notify) { - return; - } - notify = true; - new Notice('info', "Last page reached.", 2); - return setTimeout(reset, 3 * $.SECOND); - }; - })(), - cb: { - load: function() { - InfiniScroll.isFetching = false; - if (this.status !== 200) { - return; - } - InfiniScroll.cache = new String(this.response); - InfiniScroll.cache.time = Date.now(); - return InfiniScroll.parse(this.response); - } - } - }; - - Keybinds = { - init: function() { - var hotkey, init; - if (g.VIEW === 'catalog' || !Conf['Keybinds']) { - return; - } - for (hotkey in Conf.hotkeys) { - $.sync(hotkey, Keybinds.sync); - } - init = function() { - var node, _i, _len, _ref; - $.off(d, '4chanXInitFinished', init); - $.on(d, 'keydown', Keybinds.keydown); - _ref = $$('[accesskey]'); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - node = _ref[_i]; - node.removeAttribute('accesskey'); - } - }; - return $.on(d, '4chanXInitFinished', init); - }, - sync: function(key, hotkey) { - return Conf[hotkey] = key; - }, - keydown: function(e) { - var key, notification, notifications, op, target, thread, threadRoot, _i, _len, _ref; - if (!(key = Keybinds.keyCode(e))) { - return; - } - target = e.target; - if ((_ref = target.nodeName) === 'INPUT' || _ref === 'TEXTAREA') { - if (!/(Esc|Alt|Ctrl|Meta|Shift\+\w{2,})/.test(key)) { - return; - } - } - threadRoot = Nav.getThread(); - if (op = $('.op', threadRoot)) { - thread = Get.postFromNode(op).thread; - } - switch (key) { - case Conf['Toggle board list']: - if (Conf['Custom Board Navigation']) { - Header.toggleBoardList(); - } - break; - case Conf['Toggle header']: - Header.toggleBarVisibility(); - break; - case Conf['Open empty QR']: - Keybinds.qr(threadRoot); - break; - case Conf['Open QR']: - Keybinds.qr(threadRoot, true); - break; - case Conf['Open settings']: - Settings.open(); - break; - case Conf['Close']: - if (Settings.dialog) { - Settings.close(); - } else if ((notifications = $$('.notification')).length) { - for (_i = 0, _len = notifications.length; _i < _len; _i++) { - notification = notifications[_i]; - $('.close', notification).click(); - } - } else if (QR.nodes) { - if (Conf['Persistent QR']) { - QR.hide(); - } else { - QR.close(); - } - } - break; - case Conf['Spoiler tags']: - if (target.nodeName !== 'TEXTAREA') { - return; - } - Keybinds.tags('spoiler', target); - break; - case Conf['Code tags']: - if (target.nodeName !== 'TEXTAREA') { - return; - } - Keybinds.tags('code', target); - break; - case Conf['Eqn tags']: - if (target.nodeName !== 'TEXTAREA') { - return; - } - Keybinds.tags('eqn', target); - break; - case Conf['Math tags']: - if (target.nodeName !== 'TEXTAREA') { - return; - } - Keybinds.tags('math', target); - break; - case Conf['Toggle sage']: - if (QR.nodes) { - Keybinds.sage(); - } - break; - case Conf['Submit QR']: - if (QR.nodes && !QR.status()) { - QR.submit(); - } - break; - case Conf['Update']: - switch (g.VIEW) { - case 'thread': - ThreadUpdater.update(); - break; - case 'index': - Index.update(); - } - break; - case Conf['Watch']: - ThreadWatcher.toggle(thread); - break; - case Conf['Expand image']: - Keybinds.img(threadRoot); - break; - case Conf['Expand images']: - Keybinds.img(threadRoot, true); - break; - case Conf['Open Gallery']: - Gallery.cb.toggle(); - break; - case Conf['fappeTyme']: - FappeTyme.cb.toggle.call({ - name: 'fappe' - }); - break; - case Conf['werkTyme']: - FappeTyme.cb.toggle.call({ - name: 'werk' - }); - break; - case Conf['Front page']: - if (g.VIEW === 'index') { - Index.userPageNav(0); - } else { - window.location = "/" + g.BOARD + "/"; - } - break; - case Conf['Open front page']: - $.open("/" + g.BOARD + "/"); - break; - case Conf['Next page']: - if (!(g.VIEW === 'index' && Conf['Index Mode'] !== 'all pages')) { - return; - } - $('.next button', Index.pagelist).click(); - break; - case Conf['Previous page']: - if (!(g.VIEW === 'index' && Conf['Index Mode'] !== 'all pages')) { - return; - } - $('.prev button', Index.pagelist).click(); - break; - case Conf['Search form']: - Index.searchInput.focus(); - break; - case Conf['Open catalog']: - if (Conf['External Catalog']) { - window.location = CatalogLinks.external(g.BOARD.ID); - } else { - window.location = "/" + g.BOARD + "/catalog"; - } - break; - case Conf['Next thread']: - if (g.VIEW !== 'index') { - return; - } - Nav.scroll(+1); - break; - case Conf['Previous thread']: - if (g.VIEW !== 'index') { - return; - } - Nav.scroll(-1); - break; - case Conf['Expand thread']: - ExpandThread.toggle(thread); - break; - case Conf['Open thread']: - Keybinds.open(thread); - break; - case Conf['Open thread tab']: - Keybinds.open(thread, true); - break; - case Conf['Next reply']: - Keybinds.hl(+1, threadRoot); - break; - case Conf['Previous reply']: - Keybinds.hl(-1, threadRoot); - break; - case Conf['Deselect reply']: - Keybinds.hl(0, threadRoot); - break; - case Conf['Hide']: - if (ThreadHiding.db) { - ThreadHiding.toggle(thread); - } - break; - case Conf['Previous Post Quoting You']: - QuoteYou.cb.seek('preceding'); - break; - case Conf['Next Post Quoting You']: - QuoteYou.cb.seek('following'); - break; - default: - return; - } - e.preventDefault(); - return e.stopPropagation(); - }, - keyCode: function(e) { - var kc, key; - key = (function() { - switch (kc = e.keyCode) { - case 8: - return ''; - case 13: - return 'Enter'; - case 27: - return 'Esc'; - case 37: - return 'Left'; - case 38: - return 'Up'; - case 39: - return 'Right'; - case 40: - return 'Down'; - default: - if ((48 <= kc && kc <= 57) || (65 <= kc && kc <= 90)) { - return String.fromCharCode(kc).toLowerCase(); - } else { - return null; - } - } - })(); - if (key) { - if (e.altKey) { - key = 'Alt+' + key; - } - if (e.ctrlKey) { - key = 'Ctrl+' + key; - } - if (e.metaKey) { - key = 'Meta+' + key; - } - if (e.shiftKey) { - key = 'Shift+' + key; - } - } - return key; - }, - qr: function(thread, quote) { - if (!(Conf['Quick Reply'] && QR.postingIsEnabled)) { - return; - } - QR.open(); - if (quote) { - QR.quote.call($('input', $('.post.highlight', thread) || thread)); - } - QR.nodes.com.focus(); - if (Conf['QR Shortcut']) { - return $.rmClass($('.qr-shortcut'), 'disabled'); - } - }, - tags: function(tag, ta) { - var range, selEnd, selStart, value; - value = ta.value; - selStart = ta.selectionStart; - selEnd = ta.selectionEnd; - ta.value = value.slice(0, selStart) + ("[" + tag + "]") + value.slice(selStart, selEnd) + ("[/" + tag + "]") + value.slice(selEnd); - range = ("[" + tag + "]").length + selEnd; - ta.setSelectionRange(range, range); - return $.event('input', null, ta); - }, - sage: function() { - var isSage; - isSage = /sage/i.test(QR.nodes.email.value); - return QR.nodes.email.value = isSage ? "" : "sage"; - }, - img: function(thread, all) { - var post; - if (all) { - return ImageExpand.cb.toggleAll(); - } else { - post = Get.postFromNode($('.post.highlight', thread) || $('.op', thread)); - return ImageExpand.toggle(post); - } - }, - open: function(thread, tab) { - var url; - if (g.VIEW !== 'index') { - return; - } - url = "/" + thread.board + "/res/" + thread; - if (tab) { - return $.open(url); - } else { - return location.href = url; - } - }, - hl: function(delta, thread) { - var axis, height, next, postEl, replies, reply, root, _i, _len; - postEl = $('.reply.highlight', thread); - if (!delta) { - if (postEl) { - $.rmClass(postEl, 'highlight'); - } - return; - } - if (postEl) { - height = postEl.getBoundingClientRect().height; - if (Header.getTopOf(postEl) >= -height && Header.getBottomOf(postEl) >= -height) { - root = postEl.parentNode; - axis = delta === +1 ? 'following' : 'preceding'; - if (!(next = $.x("" + axis + "-sibling::div[contains(@class,'replyContainer') and not(@hidden) and not(child::div[@class='stub'])][1]/child::div[contains(@class,'reply')]", root))) { - return; - } - Header.scrollToIfNeeded(next, delta === +1); - this.focus(next); - $.rmClass(postEl, 'highlight'); - return; - } - $.rmClass(postEl, 'highlight'); - } - replies = $$('.reply', thread); - if (delta === -1) { - replies.reverse(); - } - for (_i = 0, _len = replies.length; _i < _len; _i++) { - reply = replies[_i]; - if (delta === +1 && Header.getTopOf(reply) > 0 || delta === -1 && Header.getBottomOf(reply) > 0) { - this.focus(reply); - return; - } - } - }, - focus: function(post) { - return $.addClass(post, 'highlight'); - } - }; - - Nav = { - init: function() { - var append, next, prev, span; - switch (g.VIEW) { - case 'index': - if (!Conf['Index Navigation']) { - return; - } - break; - case 'thread': - if (!Conf['Reply Navigation']) { - return; - } - break; - default: - return; - } - span = $.el('span', { - id: 'navlinks' - }); - prev = $.el('a', { - textContent: '▲', - href: 'javascript:;' - }); - next = $.el('a', { - textContent: '▼', - href: 'javascript:;' - }); - $.on(prev, 'click', this.prev); - $.on(next, 'click', this.next); - $.add(span, [prev, $.tn(' '), next]); - append = function() { - $.off(d, '4chanXInitFinished', append); - return $.add(d.body, span); - }; - return $.on(d, '4chanXInitFinished', append); - }, - prev: function() { - if (g.VIEW === 'thread') { - return window.scrollTo(0, 0); - } else { - return Nav.scroll(-1); - } - }, - next: function() { - if (g.VIEW === 'thread') { - return window.scrollTo(0, d.body.scrollHeight); - } else { - return Nav.scroll(+1); - } - }, - getThread: function() { - var thread, threadRoot, _i, _len, _ref; - _ref = $$('.thread'); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - threadRoot = _ref[_i]; - thread = Get.threadFromRoot(threadRoot); - if (thread.isHidden && !thread.stub) { - continue; - } - if (Header.getTopOf(threadRoot) >= -threadRoot.getBoundingClientRect().height) { - return threadRoot; - } - } - return $('.board'); - }, - scroll: function(delta) { - var axis, next, thread, top; - thread = Nav.getThread(); - axis = delta === +1 ? 'following' : 'preceding'; - if (next = $.x("" + axis + "-sibling::div[contains(@class,'thread') and not(@hidden)][1]", thread)) { - top = Header.getTopOf(thread); - if (delta === +1 && top < 5 || delta === -1 && top > -5) { - thread = next; - } - } - return Header.scrollTo(thread); - } - }; - - RelativeDates = { - INTERVAL: $.MINUTE / 2, - init: function() { - switch (g.VIEW) { - case 'index': - this.flush(); - $.on(d, 'visibilitychange', this.flush); - if (!Conf['Relative Post Dates']) { - return; - } - break; - case 'thread': - if (!Conf['Relative Post Dates']) { - return; - } - this.flush(); - if (g.VIEW === 'thread') { - $.on(d, 'visibilitychange ThreadUpdate', this.flush); - } - break; - default: - return; - } - return Post.callbacks.push({ - name: 'Relative Post Dates', - cb: this.node - }); - }, - node: function() { - var dateEl; - if (this.isClone) { - return; - } - dateEl = this.nodes.date; - dateEl.title = dateEl.textContent; - return RelativeDates.update(this); - }, - relative: function(diff, now, date) { - var days, months, number, rounded, unit, years; - unit = (number = diff / $.DAY) >= 1 ? (years = now.getYear() - date.getYear(), months = now.getMonth() - date.getMonth(), days = now.getDate() - date.getDate(), years > 1 ? (number = years - (months < 0 || months === 0 && days < 0), 'year') : years === 1 && (months > 0 || months === 0 && days >= 0) ? (number = years, 'year') : (months = (months + 12) % 12) > 1 ? (number = months - (days < 0), 'month') : months === 1 && days >= 0 ? (number = months, 'month') : 'day') : (number = diff / $.HOUR) >= 1 ? 'hour' : (number = diff / $.MINUTE) >= 1 ? 'minute' : (number = Math.max(0, diff) / $.SECOND, 'second'); - rounded = Math.round(number); - if (rounded !== 1) { - unit += 's'; - } - return "" + rounded + " " + unit + " ago"; - }, - stale: [], - flush: function() { - var data, now, _i, _len, _ref; - if (d.hidden) { - return; - } - now = new Date(); - _ref = RelativeDates.stale; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - data = _ref[_i]; - RelativeDates.update(data, now); - } - RelativeDates.stale = []; - clearTimeout(RelativeDates.timeout); - return RelativeDates.timeout = setTimeout(RelativeDates.flush, RelativeDates.INTERVAL); - }, - update: function(data, now) { - var date, diff, isPost, relative, singlePost, _i, _len, _ref; - isPost = data instanceof Post; - date = isPost ? data.info.date : new Date(+data.dataset.utc); - now || (now = new Date()); - diff = now - date; - relative = RelativeDates.relative(diff, now, date); - if (isPost) { - _ref = [data].concat(data.clones); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - singlePost = _ref[_i]; - singlePost.nodes.date.firstChild.textContent = relative; - } - } else { - data.firstChild.textContent = relative; - } - return RelativeDates.setOwnTimeout(diff, data); - }, - setOwnTimeout: function(diff, data) { - var delay; - delay = diff < $.MINUTE ? $.SECOND - (diff + $.SECOND / 2) % $.SECOND : diff < $.HOUR ? $.MINUTE - (diff + $.MINUTE / 2) % $.MINUTE : diff < $.DAY ? $.HOUR - (diff + $.HOUR / 2) % $.HOUR : $.DAY - (diff + $.DAY / 2) % $.DAY; - return setTimeout(RelativeDates.markStale, delay, data); - }, - markStale: function(data) { - if (__indexOf.call(RelativeDates.stale, data) >= 0) { - return; - } - if (data instanceof Post && !g.posts[data.fullID]) { - return; - } - return RelativeDates.stale.push(data); - } - }; - - RemoveSpoilers = { - init: function() { - if (Conf['Reveal Spoilers'] && !Conf['Remove Spoilers']) { - $.addClass(doc, 'reveal-spoilers'); - } - if (!Conf['Remove Spoilers']) { - return; - } - if (Conf['Reveal Spoilers']) { - this.wrapper = function(text) { - return "[spoiler]" + text + "[/spoiler]"; - }; - } - return Post.callbacks.push({ - name: 'Reveal Spoilers', - cb: this.node - }); - }, - wrapper: function(text) { - return text; - }, - node: function(post) { - var spoiler, spoilers, _i, _len; - spoilers = $$('s', this.nodes.comment); - for (_i = 0, _len = spoilers.length; _i < _len; _i++) { - spoiler = spoilers[_i]; - $.replace(spoiler, $.tn(RemoveSpoilers.wrapper(spoiler.textContent))); - } - } - }; - - Report = { - init: function() { - if (!/report/.test(location.search)) { - return; - } - return $.asap((function() { - return $.id('recaptcha_response_field'); - }), Report.ready); - }, - ready: function() { - var field; - field = $.id('recaptcha_response_field'); - $.on(field, 'keydown', function(e) { - if (e.keyCode === 8 && !field.value) { - return $.globalEval('Recaptcha.reload("t")'); - } - }); - return $.on($('form'), 'submit', function(e) { - var response; - e.preventDefault(); - response = field.value.trim(); - if (!/\s/.test(response)) { - field.value = "" + response + " " + response; - } - return this.submit(); - }); - } - }; - - Time = { - init: function() { - if (g.VIEW === 'catalog' || !Conf['Time Formatting']) { - return; - } - this.funk = this.createFunc(Conf['time']); - return Post.callbacks.push({ - name: 'Time Formatting', - cb: this.node - }); - }, - node: function() { - if (this.isClone) { - return; - } - return this.nodes.date.textContent = Time.funk(Time, this.info.date); - }, - createFunc: function(format) { - var code; - code = format.replace(/%([A-Za-z])/g, function(s, c) { - if (c in Time.formatters) { - return "' + Time.formatters." + c + ".call(date) + '"; - } else { - return s; - } - }); - return Function('Time', 'date', "return '" + code + "'"); - }, - day: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], - month: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], - zeroPad: function(n) { - if (n < 10) { - return "0" + n; - } else { - return n; - } - }, - formatters: { - a: function() { - return Time.day[this.getDay()].slice(0, 3); - }, - A: function() { - return Time.day[this.getDay()]; - }, - b: function() { - return Time.month[this.getMonth()].slice(0, 3); - }, - B: function() { - return Time.month[this.getMonth()]; - }, - d: function() { - return Time.zeroPad(this.getDate()); - }, - e: function() { - return this.getDate(); - }, - H: function() { - return Time.zeroPad(this.getHours()); - }, - I: function() { - return Time.zeroPad(this.getHours() % 12 || 12); - }, - k: function() { - return this.getHours(); - }, - l: function() { - return this.getHours() % 12 || 12; - }, - m: function() { - return Time.zeroPad(this.getMonth() + 1); - }, - M: function() { - return Time.zeroPad(this.getMinutes()); - }, - p: function() { - if (this.getHours() < 12) { - return 'AM'; - } else { - return 'PM'; - } - }, - P: function() { - if (this.getHours() < 12) { - return 'am'; - } else { - return 'pm'; - } - }, - S: function() { - return Time.zeroPad(this.getSeconds()); - }, - y: function() { - return this.getFullYear().toString().slice(2); - }, - Y: function() { - return this.getFullYear(); - } - } - }; - - Navigate = { - path: window.location.pathname, - init: function() { - if (g.VIEW === 'catalog' || g.BOARD.ID === 'f' || !Conf['JSON Navigation']) { - return; - } - $.ready(function() { - return $.on(window, 'popstate', Navigate.popstate); - }); - this.title = function() {}; - Thread.callbacks.push({ - name: 'Navigate', - cb: this.thread - }); - return Post.callbacks.push({ - name: 'Navigate', - cb: this.post - }); - }, - thread: function() { - var replyLink; - if (g.VIEW === 'thread') { - return; - } - replyLink = $('a.replylink', this.OP.nodes.info); - return $.on(replyLink, 'click', Navigate.navigate); - }, - post: function() { - var hashlink, postlink, _i, _len, _ref; - if (g.VIEW === 'thread' && this.thread.ID === g.THREADID) { - return; - } - postlink = $('a[title="Highlight this post"]', this.nodes.info); - $.on(postlink, 'click', Navigate.navigate); - if (!Conf['Quote Hash Navigation']) { - return; - } - _ref = $$('.hashlink', this.nodes.comment); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - hashlink = _ref[_i]; - $.on(hashlink, 'click', Navigate.navigate); - } - }, - clean: function() { - g.threads.forEach(function(thread) { - return thread.collect(); - }); - QuoteBacklink.containers = {}; - return $.rmAll($('.board')); - }, - features: [['Thread Excerpt', ThreadExcerpt], ['Unread Count', Unread], ['Quote Threading', QuoteThreading], ['Thread Stats', ThreadStats], ['Thread Updater', ThreadUpdater], ['Thread Expansion', ExpandThread]], - disconnect: function() { - var err, errors, feature, name, _i, _len, _ref, _ref1; - _ref = Navigate.features; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - _ref1 = _ref[_i], name = _ref1[0], feature = _ref1[1]; - try { - feature.disconnect(); - } catch (_error) { - err = _error; - if (!errors) { - errors = []; - } - errors.push({ - message: "Failed to disconnect feature " + name + ".", - error: err - }); - } - if (errors) { - Main.handleErrors(errors); - } - } - }, - reconnect: function() { - var err, errors, feature, name, _i, _len, _ref, _ref1; - _ref = Navigate.features; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - _ref1 = _ref[_i], name = _ref1[0], feature = _ref1[1]; - try { - feature.init(); - } catch (_error) { - err = _error; - if (!errors) { - errors = []; - } - errors.push({ - message: "Failed to reconnect feature " + name + ".", - error: err - }); - } - } - if (errors) { - Main.handleErrors(errors); - } - }, - ready: function(name, feature, condition) { - var err, error; - try { - if (condition) { - feature(); - } - } catch (_error) { - err = _error; - error = [ - { - message: "" + name + " Failed.", - error: err - } - ]; - } - if (error) { - Main.handleErrors(error); - } - return QR.generatePostableThreadsList(); - }, - updateContext: function(view) { - var oldView; - g.DEAD = false; - if (view !== g.VIEW) { - $.rmClass(doc, g.VIEW); - $.addClass(doc, view); - } - oldView = g.VIEW; - g.VIEW = view; - return { - index: function() { - if (oldView === g.VIEW) { - return; - } - delete g.THREADID; - QR.link.textContent = 'Start a Thread'; - $.off(d, 'ThreadUpdate', QR.statusCheck); - return $.on(d, 'IndexRefresh', QR.generatePostableThreadsList); - }, - thread: function() { - g.THREADID = +window.location.pathname.split('/')[3]; - if (oldView === g.VIEW) { - return; - } - QR.link.textContent = 'Reply to Thread'; - $.on(d, 'ThreadUpdate', QR.statusCheck); - return $.off(d, 'IndexRefresh', QR.generatePostableThreadsList); - } - }[g.VIEW](); - }, - updateBoard: function(boardID) { - var fullBoardList, onload, req; - fullBoardList = $('#full-board-list', Header.boardList); - $.rmClass($('.current', fullBoardList), 'current'); - $.addClass($("a[href*='/" + boardID + "/']", fullBoardList), 'current'); - Header.generateBoardList(Conf['boardnav'].replace(/(\r\n|\n|\r)/g, ' ')); - QR.flagsInput(); - onload = function(e) { - var aboard, board, err, _i, _len, _ref; - if (e.type === 'abort') { - req.onloadend = null; - return; - } - if (req.status !== 200) { - return; - } - try { - _ref = req.response.boards; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - aboard = _ref[_i]; - if (!(aboard.board === boardID)) { - continue; - } - board = aboard; - break; - } - } catch (_error) { - err = _error; - Main.handleErrors([ - { - message: "Navigation failed to update board name.", - error: err - } - ]); - return false; - } - if (!board) { - return; - } - Navigate.updateTitle(board); - return Navigate.updateSFW(!!board.ws_board); - }; - return req = $.ajax('//a.4cdn.org/boards.json', { - onabort: onload, - onloadend: onload - }); - }, - updateSFW: function(sfw) { - var findStyle, style; - Favicon.el.href = "//s.4cdn.org/image/favicon" + (sfw ? '-ws' : '') + ".ico"; - $.add(d.head, Favicon.el); - if (Favicon.SFW === sfw) { - return; - } - Favicon.SFW = sfw; - Favicon.update(); - findStyle = function(type, base) { - var style; - style = d.cookie.match(new RegExp("\b" + type + "\_style\=([^;]+);\b")); - return ["" + type + "_style", (style ? style[1] : base)]; - }; - style = findStyle.apply(null, (sfw ? ['ws', 'Yotsuba B New'] : ['nws', 'Yotsuba New'])); - $.globalEval("var style_group = '" + style[0] + "'"); - $('link[title=switch]', d.head).href = $("link[title='" + style[1] + "']", d.head).href; - return Main.setClass(); - }, - updateTitle: function(_arg) { - var board, subtitle, title; - board = _arg.board, title = _arg.title; - if (subtitle = $('.boardSubtitle')) { - $.rm(subtitle); - } - return $('.boardTitle').textContent = d.title = "/" + board + "/ - " + title; - }, - navigate: function(e) { - var boardID, load, pageNum, path, threadID, view; - if (this.hostname !== 'boards.4chan.org' || window.location.hostname === 'rs.4chan.org' || (e && (e.shiftKey || e.ctrlKey || (e.type === 'click' && e.button !== 0)))) { - return; - } - $.addClass(Index.button, 'fa-spin'); - path = this.pathname.split('/'); - if (path[0] === '') { - path.shift(); - } - boardID = path[0], view = path[1], threadID = path[2]; - if (view === 'catalog' || ('f' === boardID || 'f' === g.BOARD.ID)) { - return; - } - if (e) { - e.preventDefault(); - } - Navigate.title = function() {}; - delete Index.pageNum; - path = this.pathname; - if (this.hash) { - path += this.hash; - } - if (this.id !== 'popState') { - history.pushState(null, '', path); - } - Navigate.path = this.pathname; - if (threadID) { - view = 'thread'; - } else { - pageNum = view; - view = 'index'; - } - if (view === g.VIEW && boardID === g.BOARD.ID) { - Navigate.updateContext(view); - } else { - Navigate.disconnect(); - Navigate.updateContext(view); - Navigate.clean(); - Navigate.reconnect(); - } - if (boardID === g.BOARD.ID) { - Navigate.title = function() { - if (view === 'index') { - return d.title = $('.boardTitle').textContent; - } - }; - } else { - g.BOARD = new Board(boardID); - Navigate.title = function() { - return Navigate.updateBoard(boardID); - }; - } - if (view === 'index') { - return Index.update(pageNum); - } else { - Navigate.updateSFW(Favicon.SFW); - load = Navigate.load; - Navigate.req = $.ajax("//a.4cdn.org/" + boardID + "/res/" + threadID + ".json", { - onabort: load, - onloadend: load - }); - return setTimeout((function() { - if (Navigate.req && !Navigate.notice) { - return Navigate.notice = new Notice('info', 'Loading thread...'); - } - }), 3 * $.SECOND); - } - }, - load: function(e) { - var err, notice, req; - $.rmClass(Index.button, 'fa-spin'); - req = Navigate.req, notice = Navigate.notice; - if (notice != null) { - notice.close(); - } - delete Navigate.req; - delete Navigate.notice; - if (e.type === 'abort' || req.status !== 200) { - req.onloadend = null; - new Notice('warning', "Failed to load thread." + (req.status ? " " + req.status : '')); - return; - } - Navigate.title(); - try { - return Navigate.parse(req.response.posts); - } catch (_error) { - err = _error; - console.error('Navigate failure:'); - console.log(err); - if (notice) { - notice.setType('error'); - notice.el.lastElementChild.textContent = 'Navigation Failed.'; - setTimeout(notice.close, 2 * $.SECOND); - } else { - new Notice('error', 'Navigation Failed.', 2); - } - } - }, - parse: function(data) { - var OP, board, errors, makePost, obj, post, posts, thread, threadRoot, _i, _len; - board = g.BOARD; - Navigate.threadRoot = threadRoot = Build.thread(board, OP = data.shift(), true); - thread = new Thread(OP.no, board); - posts = []; - errors = null; - makePost = function(postNode) { - var err; - try { - return posts.push(new Post(postNode, thread, board)); - } catch (_error) { - err = _error; - if (!errors) { - errors = []; - } - return errors.push({ - message: "Parsing of Post No." + thread.ID + " failed. Post will be skipped.", - error: err - }); - } - }; - makePost($('.opContainer', threadRoot)); - for (_i = 0, _len = data.length; _i < _len; _i++) { - obj = data[_i]; - post = Build.postFromObject(obj, board); - makePost(post); - $.add(threadRoot, post); - } - if (errors) { - Main.handleErrors(errors); - } - Main.callbackNodes(Thread, [thread]); - Main.callbackNodes(Post, posts); - Navigate.ready('Quote Threading', QuoteThreading.force, Conf['Quote Threading'] && !Conf['Unread Count']); - Navigate.buildThread(); - return Header.hashScroll.call(window); - }, - buildThread: function() { - var board; - board = $('.board'); - $.rmAll(board); - $.add(board, [Navigate.threadRoot, $.el('hr')]); - if (Conf['Unread Count']) { - return Navigate.ready('Unread Count', Unread.ready, Conf['Unread Count']); - } - }, - popstate: function() { - var a; - if (window.location.pathname === Navigate.path) { - return; - } - a = $.el('a', { - href: window.location, - id: 'popState' - }); - return Navigate.navigate.call(a); - } - }; - - Settings = { - init: function() { - var link, settings; - link = $.el('a', { - className: 'settings-link fa fa-wrench', - textContent: 'Settings', - href: 'javascript:;' - }); - $.on(link, 'click', Settings.open); - Header.addShortcut(link); - Settings.addSection('Main', Settings.main); - Settings.addSection('Filter', Settings.filter); - Settings.addSection('Sauce', Settings.sauce); - Settings.addSection('Advanced', Settings.advanced); - Settings.addSection('Keybinds', Settings.keybinds); - $.on(d, 'AddSettingsSection', Settings.addSection); - $.on(d, 'OpenSettings', function(e) { - return Settings.open(e.detail); - }); - settings = JSON.parse(localStorage.getItem('4chan-settings')) || {}; - if (settings.disableAll) { - return; - } - settings.disableAll = true; - return localStorage.setItem('4chan-settings', JSON.stringify(settings)); - }, - open: function(openSection) { - var dialog, html, link, links, overlay, section, sectionToOpen, _i, _len, _ref; - if (Settings.dialog) { - return; - } - $.event('CloseMenu'); - html = "
"; - Settings.overlay = overlay = $.el('div', { - id: 'overlay' - }); - Settings.dialog = dialog = $.el('div', { - id: 'fourchanx-settings', - className: 'dialog', - innerHTML: html - }); - $.on($('.export', Settings.dialog), 'click', Settings["export"]); - $.on($('.import', Settings.dialog), 'click', Settings["import"]); - $.on($('.reset', Settings.dialog), 'click', Settings.reset); - $.on($('input', Settings.dialog), 'change', Settings.onImport); - links = []; - _ref = Settings.sections; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - section = _ref[_i]; - link = $.el('a', { - className: "tab-" + section.hyphenatedTitle, - textContent: section.title, - href: 'javascript:;' - }); - $.on(link, 'click', Settings.openSection.bind(section)); - links.push(link, $.tn(' | ')); - if (section.title === openSection) { - sectionToOpen = link; - } - } - links.pop(); - $.add($('.sections-list', dialog), links); - (sectionToOpen ? sectionToOpen : links[0]).click(); - $.on($('.close', dialog), 'click', Settings.close); - $.on(overlay, 'click', Settings.close); - $.add(d.body, [overlay, dialog]); - return $.event('OpenSettings', null, dialog); - }, - close: function() { - if (!Settings.dialog) { - return; - } - $.rm(Settings.overlay); - $.rm(Settings.dialog); - delete Settings.overlay; - return delete Settings.dialog; - }, - sections: [], - addSection: function(title, open) { - var hyphenatedTitle, _ref; - if (typeof title !== 'string') { - _ref = title.detail, title = _ref.title, open = _ref.open; - } - hyphenatedTitle = title.toLowerCase().replace(/\s+/g, '-'); - return Settings.sections.push({ - title: title, - hyphenatedTitle: hyphenatedTitle, - open: open - }); - }, - openSection: function() { - var section, selected; - if (selected = $('.tab-selected', Settings.dialog)) { - $.rmClass(selected, 'tab-selected'); - } - $.addClass($(".tab-" + this.hyphenatedTitle, Settings.dialog), 'tab-selected'); - section = $('section', Settings.dialog); - $.rmAll(section); - section.className = "section-" + this.hyphenatedTitle; - this.open(section, g); - section.scrollTop = 0; - return $.event('OpenSettings', null, section); - }, - main: function(section) { - var arr, button, description, div, fs, input, inputs, items, key, obj, _ref; - items = {}; - inputs = {}; - _ref = Config.main; - for (key in _ref) { - obj = _ref[key]; - fs = $.el('fieldset', { - innerHTML: "" + key + "" - }); - for (key in obj) { - arr = obj[key]; - description = arr[1]; - div = $.el('div', { - innerHTML: ": " + description + "" - }); - input = $('input', div); - $.on(input, 'change', $.cb.checked); - items[key] = Conf[key]; - inputs[key] = input; - $.add(fs, div); - } - $.add(section, fs); - } - $.get(items, function(items) { - var val; - for (key in items) { - val = items[key]; - inputs[key].checked = val; - } - }); - div = $.el('div', { - innerHTML: ": Clear manually-hidden threads and posts on all boards. Reload the page to apply." - }); - button = $('button', div); - $.get({ - hiddenThreads: {}, - hiddenPosts: {} - }, function(_arg) { - var ID, board, hiddenNum, hiddenPosts, hiddenThreads, thread, _ref1, _ref2; - hiddenThreads = _arg.hiddenThreads, hiddenPosts = _arg.hiddenPosts; - hiddenNum = 0; - _ref1 = hiddenThreads.boards; - for (ID in _ref1) { - board = _ref1[ID]; - hiddenNum += Object.keys(board).length; - } - _ref2 = hiddenPosts.boards; - for (ID in _ref2) { - board = _ref2[ID]; - for (ID in board) { - thread = board[ID]; - hiddenNum += Object.keys(thread).length; - } - } - return button.textContent = "Hidden: " + hiddenNum; - }); - $.on(button, 'click', function() { - this.textContent = 'Hidden: 0'; - return $.get('hiddenThreads', {}, function(_arg) { - var boardID, hiddenThreads; - hiddenThreads = _arg.hiddenThreads; - for (boardID in hiddenThreads.boards) { - localStorage.removeItem("4chan-hide-t-" + boardID); - } - return $["delete"](['hiddenThreads', 'hiddenPosts']); - }); - }); - return $.after($('input[name="Stubs"]', section).parentNode.parentNode, div); - }, - "export": function() { - return $.get(Conf, function(Conf) { - delete Conf['archives']; - return Settings.downloadExport({ - version: g.VERSION, - date: Date.now(), - Conf: Conf - }); - }); - }, - downloadExport: function(data) { - var a, p; - a = $.el('a', { - download: "4chan X v" + g.VERSION + "-" + data.date + ".json", - href: "data:application/json;base64," + (btoa(unescape(encodeURIComponent(JSON.stringify(data, null, 2))))) - }); - p = $('.imp-exp-result', Settings.dialog); - $.rmAll(p); - $.add(p, a); - return a.click(); - }, - "import": function() { - return $('input', this.parentNode).click(); - }, - onImport: function() { - var file, output, reader; - if (!(file = this.files[0])) { - return; - } - output = $('.imp-exp-result'); - if (!confirm('Your current settings will be entirely overwritten, are you sure?')) { - output.textContent = 'Import aborted.'; - return; - } - reader = new FileReader(); - reader.onload = function(e) { - var err; - try { - Settings.loadSettings(JSON.parse(e.target.result)); - if (confirm('Import successful. Reload now?')) { - return window.location.reload(); - } - } catch (_error) { - err = _error; - output.textContent = 'Import failed due to an error.'; - return c.error(err.stack); - } - }; - return reader.readAsText(file); - }, - loadSettings: function(data) { - var convertSettings, key, val, version, _ref; - version = data.version.split('.'); - if (version[0] === '2') { - convertSettings = function(data, map) { - var newKey, prevKey; - for (prevKey in map) { - newKey = map[prevKey]; - if (newKey) { - data.Conf[newKey] = data.Conf[prevKey]; - } - delete data.Conf[prevKey]; - } - return data; - }; - data = Settings.convertSettings(data, { - 'Disable 4chan\'s extension': '', - 'Catalog Links': '', - 'Reply Navigation': '', - 'Show Stubs': 'Stubs', - 'Image Auto-Gif': 'Auto-GIF', - 'Expand From Current': '', - 'Unread Tab Icon': 'Unread Favicon', - 'Post in Title': 'Thread Excerpt', - 'Auto Hide QR': '', - 'Open Reply in New Tab': '', - 'Remember QR size': '', - 'Quote Inline': 'Quote Inlining', - 'Quote Preview': 'Quote Previewing', - 'Indicate OP quote': 'Mark OP Quotes', - 'Indicate Cross-thread Quotes': 'Mark Cross-thread Quotes', - 'Reply Hiding': 'Reply Hiding Buttons', - 'Thread Hiding': 'Thread Hiding Buttons', - 'uniqueid': 'uniqueID', - 'mod': 'capcode', - 'country': 'flag', - 'md5': 'MD5', - 'openEmptyQR': 'Open empty QR', - 'openQR': 'Open QR', - 'openOptions': 'Open settings', - 'close': 'Close', - 'spoiler': 'Spoiler tags', - 'code': 'Code tags', - 'submit': 'Submit QR', - 'watch': 'Watch', - 'update': 'Update', - 'unreadCountTo0': '', - 'expandAllImages': 'Expand images', - 'expandImage': 'Expand image', - 'zero': 'Front page', - 'nextPage': 'Next page', - 'previousPage': 'Previous page', - 'nextThread': 'Next thread', - 'previousThread': 'Previous thread', - 'expandThread': 'Expand thread', - 'openThreadTab': 'Open thread', - 'openThread': 'Open thread tab', - 'nextReply': 'Next reply', - 'previousReply': 'Previous reply', - 'hide': 'Hide', - 'Scrolling': 'Auto Scroll', - 'Verbose': '' - }); - data.Conf.sauces = data.Conf.sauces.replace(/\$\d/g, function(c) { - switch (c) { - case '$1': - return '%TURL'; - case '$2': - return '%URL'; - case '$3': - return '%MD5'; - case '$4': - return '%board'; - default: - return c; - } - }); - _ref = Config.hotkeys; - for (key in _ref) { - val = _ref[key]; - if (key in data.Conf) { - data.Conf[key] = data.Conf[key].replace(/ctrl|alt|meta/g, function(s) { - return "" + (s[0].toUpperCase()) + s.slice(1); - }).replace(/(^|.+\+)[A-Z]$/g, function(s) { - return "Shift+" + s.slice(0, -1) + (s.slice(-1).toLowerCase()); - }); - } - } - data.Conf['WatchedThreads'] = data.WatchedThreads; - } - if (data.Conf['WatchedThreads']) { - data.Conf['watchedThreads'] = { - boards: ThreadWatcher.convert(data.Conf['WatchedThreads']) - }; - delete data.Conf['WatchedThreads']; - } - return $.set(data.Conf); - }, - reset: function() { - if (confirm('Your current settings will be entirely wiped, are you sure?')) { - return $.clear(function() { - if (confirm('Reset successful. Reload now?')) { - return window.location.reload(); - } - }); - } - }, - filter: function(section) { - var select; - section.innerHTML = "
"; - select = $('select', section); - $.on(select, 'change', Settings.selectFilter); - return Settings.selectFilter.call(select); - }, - selectFilter: function() { - var div, name, ta; - div = this.nextElementSibling; - if ((name = this.value) !== 'guide') { - $.rmAll(div); - ta = $.el('textarea', { - name: name, - className: 'field', - spellcheck: false - }); - $.get(name, Conf[name], function(item) { - return ta.value = item[name]; - }); - $.on(ta, 'change', $.cb.value); - $.add(div, ta); - return; - } - return div.innerHTML = "
Filter is disabled.

Use regular expressions, one per line.
Lines starting with a # will be ignored.
For example, /weeaboo/i will filter posts containing the string `weeaboo`, case-insensitive.
MD5 filtering uses exact string matching, not regular expressions.

    You can use these settings with each regular expression, separate them with semicolons:
  • Per boards, separate them with commas. It is global if not specified.
    For example: boards:a,jp;.
  • Filter OPs only along with their threads (`only`), replies only (`no`), or both (`yes`, this is default).
    For example: op:only;, op:no; or op:yes;.
  • Overrule the `Show Stubs` setting if specified: create a stub (`yes`) or not (`no`).
    For example: stub:yes; or stub:no;.
  • Highlight instead of hiding. You can specify a class name to use with a userstyle.
    For example: highlight; or highlight:wallpaper;.
  • Highlighted OPs will have their threads put on top of the board index by default.
    For example: top:yes; or top:no;.
"; - }, - sauce: function(section) { - var ta; - section.innerHTML = "
Sauce is disabled.
Lines starting with a # will be ignored.
You can specify a display text by appending ;text:[text] to the URL.
    These parameters will be replaced by their corresponding values:
  • %TURL: Thumbnail URL.
  • %URL: Full image URL.
  • %MD5: MD5 hash.
  • %name: Original file name.
  • %board: Current board.
"; - ta = $('textarea', section); - $.get('sauces', Conf['sauces'], function(item) { - return ta.value = item['sauces']; - }); - return $.on(ta, 'change', $.cb.value); - }, - advanced: function(section) { - var archBoards, boardID, boardOptions, boardSelect, boards, data, event, files, input, inputs, item, items, name, o, row, rows, ta, table, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m, _ref, _ref1, _ref2, _ref3, _ref4; - section.innerHTML = "
Archiver
404 Redirect is disabled.
Thread redirectionPost fetchingFile redirection
Disabled selections indicate that only one archive is available for that board and redirection type.
Custom Board Navigation
New lines will be converted into spaces.

In the following examples for /g/, g can be changed to a different board ID (a, b, etc...), the current board (current), or the Twitter link (@).
Board link: g
Title link: g-title
Board link (Replace with title when on that board): g-replace
Full text link: g-full
Custom text link: g-text:\"Install Gentoo\"
Index-only link: g-index
Catalog-only link: g-catalog
External link: external-text:\"Google\",\"http://www.google.com\"
Combinations are possible: g-index-text:\"Technology Index\"
Full board list toggle: toggle-all

[ toggle-all ] [current-title] [g-title / a-title / jp-title] [x / wsg / h] [t-text:\"Piracy\"]
will give you
[ + ] [Technology] [Technology / Anime & Manga / Otaku Culture] [x / wsg / h] [Piracy]
if you are on /g/.
Time Formatting is disabled.
:
Day: %a, %A, %d, %e
Month: %m, %b, %B
Year: %y, %Y
Hour: %k, %H, %l, %I, %p, %P
Minute: %M
Second: %S
Quote Backlinks formatting is disabled.
:
File Info Formatting is disabled.
:
Link: %l (truncated), %L (untruncated), %T (Unix timestamp)
Original file name: %n (truncated), %N (untruncated), %t (Unix timestamp)
Spoiler indicator: %p
Size: %B (Bytes), %K (KB), %M (MB), %s (4chan default)
Resolution: %r (Displays 'PDF' for PDF files)
Quick Reply Personas

One item per line.
Items will be added in the relevant input's auto-completion list.
Password items will always be used, since there is no password input.
Lines starting with a # will be ignored.

    You can use these settings with each item, separate them with semicolons:
  • Possible items are: name, email, subject and password.
  • Wrap values of items with quotes, like this: email:\"sage\".
  • Force values as defaults with the always keyword, for example: email:\"sage\";always.
  • Select specific boards for an item, separated with commas, for example: email:\"sage\";boards:jp;always.
Unread Favicon is disabled.
Emoji is disabled.
Sage Icon:
Position:
Thread Updater is disabled.
Interval:
"; - items = {}; - inputs = {}; - _ref = ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'sageEmoji', 'emojiPos', 'usercss']; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - name = _ref[_i]; - input = $("[name=" + name + "]", section); - items[name] = Conf[name]; - inputs[name] = input; - event = name === 'favicon' || name === 'usercss' || name === 'sageEmoji' || name === 'emojiPos' ? 'change' : 'input'; - $.on(input, event, $.cb.value); - } - ta = $('.personafield', section); - $.get('QR.personas', Conf['QR.personas'], function(item) { - return ta.value = item['QR.personas']; - }); - $.on(ta, 'change', $.cb.value); - $.get(items, function(items) { - var key, val; - for (key in items) { - val = items[key]; - if (key === 'emojiPos') { - continue; - } - input = inputs[key]; - input.value = val; - if (key === 'usercss') { - continue; - } - $.on(input, event, Settings[key]); - Settings[key].call(input); - } - }); - $.on($('input[name=Interval]', section), 'change', ThreadUpdater.cb.interval); - $.on($('input[name="Custom CSS"]', section), 'change', Settings.togglecss); - $.on($.id('apply-css'), 'click', Settings.usercss); - archBoards = {}; - _ref1 = Redirect.archives; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - _ref2 = _ref1[_j], name = _ref2.name, boards = _ref2.boards, files = _ref2.files, data = _ref2.data; - for (_k = 0, _len2 = boards.length; _k < _len2; _k++) { - boardID = boards[_k]; - o = archBoards[boardID] || (archBoards[boardID] = { - thread: [], - post: [], - file: [] - }); - o.thread.push(name); - if (data.software === 'foolfuuka') { - o.post.push(name); - } - if (__indexOf.call(files, boardID) >= 0) { - o.file.push(name); - } - } - } - rows = []; - boardOptions = []; - _ref3 = Object.keys(archBoards).sort(); - for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) { - boardID = _ref3[_l]; - row = $.el('tr', { - className: "board-" + boardID - }); - row.hidden = boardID !== g.BOARD.ID; - boardOptions.push($.el('option', { - textContent: "/" + boardID + "/", - value: "board-" + boardID, - selected: boardID === g.BOARD.ID - })); - o = archBoards[boardID]; - _ref4 = ['thread', 'post', 'file']; - for (_m = 0, _len4 = _ref4.length; _m < _len4; _m++) { - item = _ref4[_m]; - $.add(row, Settings.addArchiveCell(boardID, o, item)); - } - rows.push(row); - } - $.add($('tbody', section), rows); - boardSelect = $('#archive-board-select', section); - $.add(boardSelect, boardOptions); - table = $.id('archive-table'); - $.on(boardSelect, 'change', function() { - $('tbody > :not([hidden])', table).hidden = true; - return $("tbody > ." + this.value, table).hidden = false; - }); - $.get('selectedArchives', Conf['selectedArchives'], function(_arg) { - var option, selectedArchives, type; - selectedArchives = _arg.selectedArchives; - for (boardID in selectedArchives) { - data = selectedArchives[boardID]; - for (type in data) { - name = data[type]; - if (option = $("select[data-boardid='" + boardID + "'][data-type='" + type + "'] > option[value='" + name + "']", section)) { - option.selected = true; - } - } - } - }); - }, - addArchiveCell: function(boardID, data, type) { - var archive, i, length, options, select, td; - length = data[type].length; - td = $.el('td', { - className: 'archive-cell' - }); - if (!length) { - td.textContent = '--'; - return td; - } - options = []; - i = 0; - while (i < length) { - archive = data[type][i++]; - options.push($.el('option', { - textContent: archive, - value: archive - })); - } - td.innerHTML = ''; - select = td.firstElementChild; - if (!(select.disabled = length === 1)) { - select.setAttribute('data-boardid', boardID); - select.setAttribute('data-type', type); - $.on(select, 'change', Settings.saveSelectedArchive); - } - $.add(select, options); - return td; - }, - saveSelectedArchive: function() { - var _this = this; - return $.get('selectedArchives', Conf['selectedArchives'], function(_arg) { - var selectedArchives, _name; - selectedArchives = _arg.selectedArchives; - (selectedArchives[_name = _this.dataset.boardid] || (selectedArchives[_name] = {}))[_this.dataset.type] = _this.value; - return $.set('selectedArchives', selectedArchives); - }); - }, - boardnav: function() { - return Header.generateBoardList(this.value); - }, - time: function() { - var funk; - funk = Time.createFunc(this.value); - return this.nextElementSibling.textContent = funk(Time, new Date()); - }, - backlink: function() { - return this.nextElementSibling.textContent = this.value.replace(/%id/, '123456789'); - }, - fileInfo: function() { - var data, funk; - data = { - isReply: true, - file: { - URL: '//i.4cdn.org/g/src/1334437723720.jpg', - name: 'd9bb2efc98dd0df141a94399ff5880b7.jpg', - size: '276 KB', - sizeInBytes: 276 * 1024, - dimensions: '1280x720', - isImage: true, - isSpoiler: true - } - }; - funk = FileInfo.createFunc(this.value); - return this.nextElementSibling.innerHTML = funk(FileInfo, data); - }, - favicon: function() { - Favicon["switch"](); - if (g.VIEW === 'thread' && Conf['Unread Favicon']) { - Unread.update(); - } - return this.nextElementSibling.innerHTML = "\n\n\n"; - }, - sageEmoji: function() { - return this.nextElementSibling.innerHTML = ""; - }, - togglecss: function() { - if ($('textarea[name=usercss]', $.x('ancestor::fieldset[1]', this)).disabled = !this.checked) { - CustomCSS.rmStyle(); - } else { - CustomCSS.addStyle(); - } - return $.cb.checked.call(this); - }, - usercss: function() { - return CustomCSS.update(); - }, - keybinds: function(section) { - var arr, input, inputs, items, key, tbody, tr, _ref; - section.innerHTML = "
Keybinds are disabled.
Allowed keys: a-z, 0-9, Ctrl, Shift, Alt, Meta, Enter, Esc, Up, Down, Right, Left.
Press Backspace to disable a keybind.
ActionsKeybinds
"; - tbody = $('tbody', section); - items = {}; - inputs = {}; - _ref = Config.hotkeys; - for (key in _ref) { - arr = _ref[key]; - tr = $.el('tr', { - innerHTML: "" + arr[1] + "" - }); - input = $('input', tr); - input.name = key; - input.spellcheck = false; - items[key] = Conf[key]; - inputs[key] = input; - $.on(input, 'keydown', Settings.keybind); - $.add(tbody, tr); - } - return $.get(items, function(items) { - var val; - for (key in items) { - val = items[key]; - inputs[key].value = val; - } - }); - }, - keybind: function(e) { - var key; - if (e.keyCode === 9) { - return; - } - e.preventDefault(); - e.stopPropagation(); - if ((key = Keybinds.keyCode(e)) == null) { - return; - } - this.value = key; - return $.cb.value.call(this); - } - }; - - Main = { - init: function() { - var db, flatten, pathname, _i, _len, _ref, _ref1; - g.threads = new SimpleDict; - g.posts = new SimpleDict; - pathname = location.pathname.split('/'); - g.BOARD = new Board(pathname[1]); - if ((_ref = g.BOARD.ID) === 'z' || _ref === 'fk') { - return; - } - g.VIEW = (function() { - switch (pathname[2]) { - case 'res': - return 'thread'; - case 'catalog': - return 'catalog'; - default: - return 'index'; - } - })(); - if (g.VIEW === 'thread') { - g.THREADID = +pathname[3]; - } - flatten = function(parent, obj) { - var key, val; - if (obj instanceof Array) { - Conf[parent] = obj[0]; - } else if (typeof obj === 'object') { - for (key in obj) { - val = obj[key]; - flatten(key, val); - } - } else { - Conf[parent] = obj; - } - }; - flatten(null, Config); - _ref1 = DataBoard.keys; - for (_i = 0, _len = _ref1.length; _i < _len; _i++) { - db = _ref1[_i]; - Conf[db] = { - boards: {} - }; - } - Conf['selectedArchives'] = {}; - Conf['CachedTitles'] = []; - $.get(Conf, function(items) { - $.extend(Conf, items); - return Main.initFeatures(); - }); - return $.on(d, '4chanMainInit', Main.initStyle); - }, - initFeatures: function() { - var err, feature, name, _i, _len, _ref, _ref1; - switch (location.hostname) { - case 'a.4cdn.org': - return; - case 'sys.4chan.org': - Report.init(); - return; - case 'i.4cdn.org': - $.ready(function() { - var URL, pathname, _ref; - if (Conf['404 Redirect'] && ((_ref = d.title) === '4chan - Temporarily Offline' || _ref === '4chan - 404 Not Found')) { - Redirect.init(); - pathname = location.pathname.split('/'); - URL = Redirect.to('file', { - boardID: g.BOARD.ID, - filename: pathname[pathname.length - 1] - }); - if (URL) { - return location.replace(URL); - } - } - }); - return; - } - _ref = Main.features; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - _ref1 = _ref[_i], name = _ref1[0], feature = _ref1[1]; - try { - feature.init(); - } catch (_error) { - err = _error; - Main.handleErrors({ - message: "\"" + name + "\" initialization crashed.", - error: err - }); - } - } - $.on(d, 'AddCallback', Main.addCallback); - return $.ready(Main.initReady); - }, - initStyle: function() { - var _ref; - $.off(d, '4chanMainInit', Main.initStyle); - if (!Main.isThisPageLegit() || $.hasClass(doc, 'fourchan-x')) { - return; - } - if ((_ref = $('link[href*=mobile]', d.head)) != null) { - _ref.disabled = true; - } - $.addClass(doc, 'fourchan-x', 'seaweedchan', g.VIEW, 'gecko'); - $.addStyle(Main.css); - return Main.setClass(); - }, - setClass: function() { - var mainStyleSheet, setStyle, style, styleSheets; - if (g.VIEW === 'catalog') { - $.addClass(doc, $.id('base-css').href.match(/catalog_(\w+)/)[1].replace('_new', '').replace(/_+/g, '-')); - return; - } - style = 'yotsuba-b'; - mainStyleSheet = $('link[title=switch]', d.head); - styleSheets = $$('link[rel="alternate stylesheet"]', d.head); - setStyle = function() { - var styleSheet, _i, _len; - $.rmClass(doc, style); - for (_i = 0, _len = styleSheets.length; _i < _len; _i++) { - styleSheet = styleSheets[_i]; - if (styleSheet.href === mainStyleSheet.href) { - style = styleSheet.title.toLowerCase().replace('new', '').trim().replace(/\s+/g, '-'); - break; - } - } - return $.addClass(doc, style); - }; - setStyle(); - if (!mainStyleSheet) { - return; - } - return new MutationObserver(setStyle).observe(mainStyleSheet, { - attributes: true, - attributeFilter: ['href'] - }); - }, - initReady: function() { - var GMver, err, href, i, passLink, styleSelector, test, v, _i, _len, _ref, _ref1; - if ((_ref = d.title) === '4chan - Temporarily Offline' || _ref === '4chan - 404 Not Found') { - if (Conf['404 Redirect'] && g.VIEW === 'thread') { - href = Redirect.to('thread', { - boardID: g.BOARD.ID, - threadID: g.THREADID, - postID: +location.hash.match(/\d+/) - }); - location.replace(href || ("/" + g.BOARD + "/")); - } - return; - } - Main.initStyle(); - if (styleSelector = $.id('styleSelector')) { - passLink = $.el('a', { - textContent: '4chan Pass', - href: 'javascript:;' - }); - $.on(passLink, 'click', function() { - return window.open('//sys.4chan.org/auth', 'This will steal your data.', 'left=0,top=0,width=500,height=255,toolbar=0,resizable=0'); - }); - $.before(styleSelector.previousSibling, [$.tn('['), passLink, $.tn(']\u00A0\u00A0')]); - } - if (!(Conf['JSON Navigation'] && g.VIEW === 'index')) { - Main.initThread(); - } else { - $.event('4chanXInitFinished'); - } - test = $.el('span'); - test.classList.add('a', 'b'); - if (test.className !== 'a b') { - new Notice('warning', "Your version of Firefox is outdated (v26 minimum) and 4chan X may not operate correctly.", 30); - } - GMver = GM_info.version.split('.'); - _ref1 = "1.14".split('.'); - for (i = _i = 0, _len = _ref1.length; _i < _len; i = ++_i) { - v = _ref1[i]; - if (v === GMver[i]) { - continue; - } - (v < GMver[i]) || new Notice('warning', "Your version of Greasemonkey is outdated (v" + GM_info.version + " instead of v1.14 minimum) and 4chan X may not operate correctly.", 30); - break; - } - try { - return localStorage.getItem('4chan-settings'); - } catch (_error) { - err = _error; - return new Notice('warning', 'Cookies need to be enabled on 4chan for 4chan X to operate properly.', 30); - } - }, - initThread: function() { - var board, err, errors, postRoot, posts, thread, threadRoot, threads, _i, _j, _len, _len1, _ref, _ref1; - if (board = $('.board')) { - threads = []; - posts = []; - _ref = $$('.board > .thread', board); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - threadRoot = _ref[_i]; - thread = new Thread(+threadRoot.id.slice(1), g.BOARD); - threads.push(thread); - _ref1 = $$('.thread > .postContainer', threadRoot); - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - postRoot = _ref1[_j]; - try { - posts.push(new Post(postRoot, thread, g.BOARD)); - } catch (_error) { - err = _error; - if (!errors) { - errors = []; - } - errors.push({ - message: "Parsing of Post No." + (postRoot.id.match(/\d+/)) + " failed. Post will be skipped.", - error: err - }); - } - } - } - if (errors) { - Main.handleErrors(errors); - } - Main.callbackNodes(Thread, threads); - Main.callbackNodesDB(Post, posts, function() { - return $.event('4chanXInitFinished'); - }); - } - return $.get('previousversion', null, function(_arg) { - var changelog, el, previousversion; - previousversion = _arg.previousversion; - if (previousversion === g.VERSION) { - return; - } - if (previousversion) { - changelog = 'https://github.com/Spittie/4chan-x/blob/master/CHANGELOG.md'; - el = $.el('span', { - innerHTML: "4chan X has been updated to version " + g.VERSION + "." - }); - new Notice('info', el, 15); - } else { - Settings.open(); - } - return $.set('previousversion', g.VERSION); - }); - }, - callbackNodes: function(klass, nodes) { - var cb, i, node; - i = 0; - cb = klass.callbacks; - while (node = nodes[i++]) { - cb.execute(node); - } - }, - callbackNodesDB: function(klass, nodes, cb) { - var cbs, fn, i, softTask; - i = 0; - cbs = klass.callbacks; - fn = function() { - var node; - if (!(node = nodes[i])) { - return false; - } - cbs.execute(node); - return ++i % 25; - }; - softTask = function() { - while (fn()) { - continue; - } - if (!nodes[i]) { - if (cb) { - cb(); - } - return; - } - return setTimeout(softTask, 0); - }; - return softTask(); - }, - addCallback: function(e) { - var Klass, obj; - obj = e.detail; - if (typeof obj.callback.name !== 'string') { - throw new Error("Invalid callback name: " + obj.callback.name); - } - switch (obj.type) { - case 'Post': - Klass = Post; - break; - case 'Thread': - Klass = Thread; - break; - default: - return; - } - obj.callback.isAddon = true; - return Klass.callbacks.push(obj.callback); - }, - handleErrors: function(errors) { - var div, error, logs, _i, _len; - if (!(errors instanceof Array)) { - error = errors; - } else if (errors.length === 1) { - error = errors[0]; - } - if (error) { - new Notice('error', Main.parseError(error), 15); - return; - } - div = $.el('div', { - innerHTML: "" + errors.length + " errors occurred. [show]" - }); - $.on(div.lastElementChild, 'click', function() { - var _ref; - return _ref = this.textContent === 'show' ? ['hide', false] : ['show', true], this.textContent = _ref[0], logs.hidden = _ref[1], _ref; - }); - logs = $.el('div', { - hidden: true - }); - for (_i = 0, _len = errors.length; _i < _len; _i++) { - error = errors[_i]; - $.add(logs, Main.parseError(error)); - } - return new Notice('error', [div, logs], 30); - }, - parseError: function(data) { - var error, message; - c.error(data.message, data.error.stack); - message = $.el('div', { - textContent: data.message - }); - error = $.el('div', { - textContent: data.error - }); - return [message, error]; - }, - isThisPageLegit: function() { - var _ref; - if (!('thisPageIsLegit' in Main)) { - Main.thisPageIsLegit = location.hostname === 'boards.4chan.org' && !$('link[href*="favicon-status.ico"]', d.head) && ((_ref = d.title) !== '4chan - Temporarily Offline' && _ref !== '4chan - Error' && _ref !== '504 Gateway Time-out'); - } - return Main.thisPageIsLegit; - }, - css: "/*! * Font Awesome 4.0.3 * the iconic font designed for Bootstrap * ------------------------------------------------------------------------------ * The full suite of pictographic icons, examples, and documentation can be * found at http://fontawesome.io. Stay up to date on Twitter at * http://twitter.com/fontawesome. * * License * ------------------------------------------------------------------------------ * - The Font Awesome font is licensed under SIL OFL 1.1 - * http://scripts.sil.org/OFL * - Font Awesome CSS, LESS, and SASS files are licensed under MIT License - * http://opensource.org/licenses/mit-license.html * - Font Awesome documentation licensed under CC BY 3.0 - * http://creativecommons.org/licenses/by/3.0/ * - Attribution is no longer required in Font Awesome 3.0, but much appreciated: * \"Font Awesome by Dave Gandy - http://fontawesome.io\" * * Author - Dave Gandy * ------------------------------------------------------------------------------ * Email: dave@fontawesome.io * Twitter: http://twitter.com/davegandy * Work: Lead Product Designer @ Kyruus - http://kyruus.com */ @font-face{font-family:FontAwesome;src:url('data:application/font-woff;base64,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') format('woff');font-weight:400;font-style:normal}.fa::before{font-family:FontAwesome;font-weight:400;font-style:normal;-webkit-font-smoothing:antialiased;*margin-right:.3em;text-decoration:inherit;display:none;speak:none} :root.shortcut-icons .fa::before, .menu-button .fa::before, .hide-reply-button .fa::before, .hide-thread-button .fa::before {display:inline-block;font-size:13px;visibility:visible} :root.shortcut-icons #shortcuts .fa::before{font-size:15px!important;margin-top:-3px!important;position:relative;top:1px} :root.shortcut-icons .fa, .menu-button .fa{font-size:0;visibility:hidden} :root.shortcut-icons .shortcut.brackets-wrap::after,:root.shortcut-icons .shortcut.brackets-wrap::before{display:none} :root.shortcut-icons a .fa, .menu-button .fa, .hide-reply-button .fa, .hide-thread-button .fa {display:inline} /* Update this line only */ .fa-glass:before{content:\"\\f000\"}.fa-music:before{content:\"\\f001\"}.fa-search:before{content:\"\\f002\"}.fa-envelope-o:before{content:\"\\f003\"}.fa-heart:before{content:\"\\f004\"}.fa-star:before{content:\"\\f005\"}.fa-star-o:before{content:\"\\f006\"}.fa-user:before{content:\"\\f007\"}.fa-film:before{content:\"\\f008\"}.fa-th-large:before{content:\"\\f009\"}.fa-th:before{content:\"\\f00a\"}.fa-th-list:before{content:\"\\f00b\"}.fa-check:before{content:\"\\f00c\"}.fa-times:before{content:\"\\f00d\"}.fa-search-plus:before{content:\"\\f00e\"}.fa-search-minus:before{content:\"\\f010\"}.fa-power-off:before{content:\"\\f011\"}.fa-signal:before{content:\"\\f012\"}.fa-gear:before,.fa-cog:before{content:\"\\f013\"}.fa-trash-o:before{content:\"\\f014\"}.fa-home:before{content:\"\\f015\"}.fa-file-o:before{content:\"\\f016\"}.fa-clock-o:before{content:\"\\f017\"}.fa-road:before{content:\"\\f018\"}.fa-download:before{content:\"\\f019\"}.fa-arrow-circle-o-down:before{content:\"\\f01a\"}.fa-arrow-circle-o-up:before{content:\"\\f01b\"}.fa-inbox:before{content:\"\\f01c\"}.fa-play-circle-o:before{content:\"\\f01d\"}.fa-rotate-right:before,.fa-repeat:before{content:\"\\f01e\"}.fa-refresh:before{content:\"\\f021\"}.fa-list-alt:before{content:\"\\f022\"}.fa-lock:before{content:\"\\f023\"}.fa-flag:before{content:\"\\f024\"}.fa-headphones:before{content:\"\\f025\"}.fa-volume-off:before{content:\"\\f026\"}.fa-volume-down:before{content:\"\\f027\"}.fa-volume-up:before{content:\"\\f028\"}.fa-qrcode:before{content:\"\\f029\"}.fa-barcode:before{content:\"\\f02a\"}.fa-tag:before{content:\"\\f02b\"}.fa-tags:before{content:\"\\f02c\"}.fa-book:before{content:\"\\f02d\"}.fa-bookmark:before{content:\"\\f02e\"}.fa-print:before{content:\"\\f02f\"}.fa-camera:before{content:\"\\f030\"}.fa-font:before{content:\"\\f031\"}.fa-bold:before{content:\"\\f032\"}.fa-italic:before{content:\"\\f033\"}.fa-text-height:before{content:\"\\f034\"}.fa-text-width:before{content:\"\\f035\"}.fa-align-left:before{content:\"\\f036\"}.fa-align-center:before{content:\"\\f037\"}.fa-align-right:before{content:\"\\f038\"}.fa-align-justify:before{content:\"\\f039\"}.fa-list:before{content:\"\\f03a\"}.fa-dedent:before,.fa-outdent:before{content:\"\\f03b\"}.fa-indent:before{content:\"\\f03c\"}.fa-video-camera:before{content:\"\\f03d\"}.fa-picture-o:before{content:\"\\f03e\"}.fa-pencil:before{content:\"\\f040\"}.fa-map-marker:before{content:\"\\f041\"}.fa-adjust:before{content:\"\\f042\"}.fa-tint:before{content:\"\\f043\"}.fa-edit:before,.fa-pencil-square-o:before{content:\"\\f044\"}.fa-share-square-o:before{content:\"\\f045\"}.fa-check-square-o:before{content:\"\\f046\"}.fa-arrows:before{content:\"\\f047\"}.fa-step-backward:before{content:\"\\f048\"}.fa-fast-backward:before{content:\"\\f049\"}.fa-backward:before{content:\"\\f04a\"}.fa-play:before{content:\"\\f04b\"}.fa-pause:before{content:\"\\f04c\"}.fa-stop:before{content:\"\\f04d\"}.fa-forward:before{content:\"\\f04e\"}.fa-fast-forward:before{content:\"\\f050\"}.fa-step-forward:before{content:\"\\f051\"}.fa-eject:before{content:\"\\f052\"}.fa-chevron-left:before{content:\"\\f053\"}.fa-chevron-right:before{content:\"\\f054\"}.fa-plus-circle:before{content:\"\\f055\"}.fa-minus-circle:before{content:\"\\f056\"}.fa-times-circle:before{content:\"\\f057\"}.fa-check-circle:before{content:\"\\f058\"}.fa-question-circle:before{content:\"\\f059\"}.fa-info-circle:before{content:\"\\f05a\"}.fa-crosshairs:before{content:\"\\f05b\"}.fa-times-circle-o:before{content:\"\\f05c\"}.fa-check-circle-o:before{content:\"\\f05d\"}.fa-ban:before{content:\"\\f05e\"}.fa-arrow-left:before{content:\"\\f060\"}.fa-arrow-right:before{content:\"\\f061\"}.fa-arrow-up:before{content:\"\\f062\"}.fa-arrow-down:before{content:\"\\f063\"}.fa-mail-forward:before,.fa-share:before{content:\"\\f064\"}.fa-expand:before{content:\"\\f065\"}.fa-compress:before{content:\"\\f066\"}.fa-plus:before{content:\"\\f067\"}.fa-minus:before{content:\"\\f068\"}.fa-asterisk:before{content:\"\\f069\"}.fa-exclamation-circle:before{content:\"\\f06a\"}.fa-gift:before{content:\"\\f06b\"}.fa-leaf:before{content:\"\\f06c\"}.fa-fire:before{content:\"\\f06d\"}.fa-eye:before{content:\"\\f06e\"}.fa-eye-slash:before{content:\"\\f070\"}.fa-warning:before,.fa-exclamation-triangle:before{content:\"\\f071\"}.fa-plane:before{content:\"\\f072\"}.fa-calendar:before{content:\"\\f073\"}.fa-random:before{content:\"\\f074\"}.fa-comment:before{content:\"\\f075\"}.fa-magnet:before{content:\"\\f076\"}.fa-chevron-up:before{content:\"\\f077\"}.fa-chevron-down:before{content:\"\\f078\"}.fa-retweet:before{content:\"\\f079\"}.fa-shopping-cart:before{content:\"\\f07a\"}.fa-folder:before{content:\"\\f07b\"}.fa-folder-open:before{content:\"\\f07c\"}.fa-arrows-v:before{content:\"\\f07d\"}.fa-arrows-h:before{content:\"\\f07e\"}.fa-bar-chart-o:before{content:\"\\f080\"}.fa-twitter-square:before{content:\"\\f081\"}.fa-facebook-square:before{content:\"\\f082\"}.fa-camera-retro:before{content:\"\\f083\"}.fa-key:before{content:\"\\f084\"}.fa-gears:before,.fa-cogs:before{content:\"\\f085\"}.fa-comments:before{content:\"\\f086\"}.fa-thumbs-o-up:before{content:\"\\f087\"}.fa-thumbs-o-down:before{content:\"\\f088\"}.fa-star-half:before{content:\"\\f089\"}.fa-heart-o:before{content:\"\\f08a\"}.fa-sign-out:before{content:\"\\f08b\"}.fa-linkedin-square:before{content:\"\\f08c\"}.fa-thumb-tack:before{content:\"\\f08d\"}.fa-external-link:before{content:\"\\f08e\"}.fa-sign-in:before{content:\"\\f090\"}.fa-trophy:before{content:\"\\f091\"}.fa-github-square:before{content:\"\\f092\"}.fa-upload:before{content:\"\\f093\"}.fa-lemon-o:before{content:\"\\f094\"}.fa-phone:before{content:\"\\f095\"}.fa-square-o:before{content:\"\\f096\"}.fa-bookmark-o:before{content:\"\\f097\"}.fa-phone-square:before{content:\"\\f098\"}.fa-twitter:before{content:\"\\f099\"}.fa-facebook:before{content:\"\\f09a\"}.fa-github:before{content:\"\\f09b\"}.fa-unlock:before{content:\"\\f09c\"}.fa-credit-card:before{content:\"\\f09d\"}.fa-rss:before{content:\"\\f09e\"}.fa-hdd-o:before{content:\"\\f0a0\"}.fa-bullhorn:before{content:\"\\f0a1\"}.fa-bell:before{content:\"\\f0f3\"}.fa-certificate:before{content:\"\\f0a3\"}.fa-hand-o-right:before{content:\"\\f0a4\"}.fa-hand-o-left:before{content:\"\\f0a5\"}.fa-hand-o-up:before{content:\"\\f0a6\"}.fa-hand-o-down:before{content:\"\\f0a7\"}.fa-arrow-circle-left:before{content:\"\\f0a8\"}.fa-arrow-circle-right:before{content:\"\\f0a9\"}.fa-arrow-circle-up:before{content:\"\\f0aa\"}.fa-arrow-circle-down:before{content:\"\\f0ab\"}.fa-globe:before{content:\"\\f0ac\"}.fa-wrench:before{content:\"\\f0ad\"}.fa-tasks:before{content:\"\\f0ae\"}.fa-filter:before{content:\"\\f0b0\"}.fa-briefcase:before{content:\"\\f0b1\"}.fa-arrows-alt:before{content:\"\\f0b2\"}.fa-group:before,.fa-users:before{content:\"\\f0c0\"}.fa-chain:before,.fa-link:before{content:\"\\f0c1\"}.fa-cloud:before{content:\"\\f0c2\"}.fa-flask:before{content:\"\\f0c3\"}.fa-cut:before,.fa-scissors:before{content:\"\\f0c4\"}.fa-copy:before,.fa-files-o:before{content:\"\\f0c5\"}.fa-paperclip:before{content:\"\\f0c6\"}.fa-save:before,.fa-floppy-o:before{content:\"\\f0c7\"}.fa-square:before{content:\"\\f0c8\"}.fa-bars:before{content:\"\\f0c9\"}.fa-list-ul:before{content:\"\\f0ca\"}.fa-list-ol:before{content:\"\\f0cb\"}.fa-strikethrough:before{content:\"\\f0cc\"}.fa-underline:before{content:\"\\f0cd\"}.fa-table:before{content:\"\\f0ce\"}.fa-magic:before{content:\"\\f0d0\"}.fa-truck:before{content:\"\\f0d1\"}.fa-pinterest:before{content:\"\\f0d2\"}.fa-pinterest-square:before{content:\"\\f0d3\"}.fa-google-plus-square:before{content:\"\\f0d4\"}.fa-google-plus:before{content:\"\\f0d5\"}.fa-money:before{content:\"\\f0d6\"}.fa-caret-down:before{content:\"\\f0d7\"}.fa-caret-up:before{content:\"\\f0d8\"}.fa-caret-left:before{content:\"\\f0d9\"}.fa-caret-right:before{content:\"\\f0da\"}.fa-columns:before{content:\"\\f0db\"}.fa-unsorted:before,.fa-sort:before{content:\"\\f0dc\"}.fa-sort-down:before,.fa-sort-asc:before{content:\"\\f0dd\"}.fa-sort-up:before,.fa-sort-desc:before{content:\"\\f0de\"}.fa-envelope:before{content:\"\\f0e0\"}.fa-linkedin:before{content:\"\\f0e1\"}.fa-rotate-left:before,.fa-undo:before{content:\"\\f0e2\"}.fa-legal:before,.fa-gavel:before{content:\"\\f0e3\"}.fa-dashboard:before,.fa-tachometer:before{content:\"\\f0e4\"}.fa-comment-o:before{content:\"\\f0e5\"}.fa-comments-o:before{content:\"\\f0e6\"}.fa-flash:before,.fa-bolt:before{content:\"\\f0e7\"}.fa-sitemap:before{content:\"\\f0e8\"}.fa-umbrella:before{content:\"\\f0e9\"}.fa-paste:before,.fa-clipboard:before{content:\"\\f0ea\"}.fa-lightbulb-o:before{content:\"\\f0eb\"}.fa-exchange:before{content:\"\\f0ec\"}.fa-cloud-download:before{content:\"\\f0ed\"}.fa-cloud-upload:before{content:\"\\f0ee\"}.fa-user-md:before{content:\"\\f0f0\"}.fa-stethoscope:before{content:\"\\f0f1\"}.fa-suitcase:before{content:\"\\f0f2\"}.fa-bell-o:before{content:\"\\f0a2\"}.fa-coffee:before{content:\"\\f0f4\"}.fa-cutlery:before{content:\"\\f0f5\"}.fa-file-text-o:before{content:\"\\f0f6\"}.fa-building-o:before{content:\"\\f0f7\"}.fa-hospital-o:before{content:\"\\f0f8\"}.fa-ambulance:before{content:\"\\f0f9\"}.fa-medkit:before{content:\"\\f0fa\"}.fa-fighter-jet:before{content:\"\\f0fb\"}.fa-beer:before{content:\"\\f0fc\"}.fa-h-square:before{content:\"\\f0fd\"}.fa-plus-square:before{content:\"\\f0fe\"}.fa-angle-double-left:before{content:\"\\f100\"}.fa-angle-double-right:before{content:\"\\f101\"}.fa-angle-double-up:before{content:\"\\f102\"}.fa-angle-double-down:before{content:\"\\f103\"}.fa-angle-left:before{content:\"\\f104\"}.fa-angle-right:before{content:\"\\f105\"}.fa-angle-up:before{content:\"\\f106\"}.fa-angle-down:before{content:\"\\f107\"}.fa-desktop:before{content:\"\\f108\"}.fa-laptop:before{content:\"\\f109\"}.fa-tablet:before{content:\"\\f10a\"}.fa-mobile-phone:before,.fa-mobile:before{content:\"\\f10b\"}.fa-circle-o:before{content:\"\\f10c\"}.fa-quote-left:before{content:\"\\f10d\"}.fa-quote-right:before{content:\"\\f10e\"}.fa-spinner:before{content:\"\\f110\"}.fa-circle:before{content:\"\\f111\"}.fa-mail-reply:before,.fa-reply:before{content:\"\\f112\"}.fa-github-alt:before{content:\"\\f113\"}.fa-folder-o:before{content:\"\\f114\"}.fa-folder-open-o:before{content:\"\\f115\"}.fa-smile-o:before{content:\"\\f118\"}.fa-frown-o:before{content:\"\\f119\"}.fa-meh-o:before{content:\"\\f11a\"}.fa-gamepad:before{content:\"\\f11b\"}.fa-keyboard-o:before{content:\"\\f11c\"}.fa-flag-o:before{content:\"\\f11d\"}.fa-flag-checkered:before{content:\"\\f11e\"}.fa-terminal:before{content:\"\\f120\"}.fa-code:before{content:\"\\f121\"}.fa-reply-all:before{content:\"\\f122\"}.fa-mail-reply-all:before{content:\"\\f122\"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:\"\\f123\"}.fa-location-arrow:before{content:\"\\f124\"}.fa-crop:before{content:\"\\f125\"}.fa-code-fork:before{content:\"\\f126\"}.fa-unlink:before,.fa-chain-broken:before{content:\"\\f127\"}.fa-question:before{content:\"\\f128\"}.fa-info:before{content:\"\\f129\"}.fa-exclamation:before{content:\"\\f12a\"}.fa-superscript:before{content:\"\\f12b\"}.fa-subscript:before{content:\"\\f12c\"}.fa-eraser:before{content:\"\\f12d\"}.fa-puzzle-piece:before{content:\"\\f12e\"}.fa-microphone:before{content:\"\\f130\"}.fa-microphone-slash:before{content:\"\\f131\"}.fa-shield:before{content:\"\\f132\"}.fa-calendar-o:before{content:\"\\f133\"}.fa-fire-extinguisher:before{content:\"\\f134\"}.fa-rocket:before{content:\"\\f135\"}.fa-maxcdn:before{content:\"\\f136\"}.fa-chevron-circle-left:before{content:\"\\f137\"}.fa-chevron-circle-right:before{content:\"\\f138\"}.fa-chevron-circle-up:before{content:\"\\f139\"}.fa-chevron-circle-down:before{content:\"\\f13a\"}.fa-html5:before{content:\"\\f13b\"}.fa-css3:before{content:\"\\f13c\"}.fa-anchor:before{content:\"\\f13d\"}.fa-unlock-alt:before{content:\"\\f13e\"}.fa-bullseye:before{content:\"\\f140\"}.fa-ellipsis-h:before{content:\"\\f141\"}.fa-ellipsis-v:before{content:\"\\f142\"}.fa-rss-square:before{content:\"\\f143\"}.fa-play-circle:before{content:\"\\f144\"}.fa-ticket:before{content:\"\\f145\"}.fa-minus-square:before{content:\"\\f146\"}.fa-minus-square-o:before{content:\"\\f147\"}.fa-level-up:before{content:\"\\f148\"}.fa-level-down:before{content:\"\\f149\"}.fa-check-square:before{content:\"\\f14a\"}.fa-pencil-square:before{content:\"\\f14b\"}.fa-external-link-square:before{content:\"\\f14c\"}.fa-share-square:before{content:\"\\f14d\"}.fa-compass:before{content:\"\\f14e\"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:\"\\f150\"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:\"\\f151\"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:\"\\f152\"}.fa-euro:before,.fa-eur:before{content:\"\\f153\"}.fa-gbp:before{content:\"\\f154\"}.fa-dollar:before,.fa-usd:before{content:\"\\f155\"}.fa-rupee:before,.fa-inr:before{content:\"\\f156\"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:\"\\f157\"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:\"\\f158\"}.fa-won:before,.fa-krw:before{content:\"\\f159\"}.fa-bitcoin:before,.fa-btc:before{content:\"\\f15a\"}.fa-file:before{content:\"\\f15b\"}.fa-file-text:before{content:\"\\f15c\"}.fa-sort-alpha-asc:before{content:\"\\f15d\"}.fa-sort-alpha-desc:before{content:\"\\f15e\"}.fa-sort-amount-asc:before{content:\"\\f160\"}.fa-sort-amount-desc:before{content:\"\\f161\"}.fa-sort-numeric-asc:before{content:\"\\f162\"}.fa-sort-numeric-desc:before{content:\"\\f163\"}.fa-thumbs-up:before{content:\"\\f164\"}.fa-thumbs-down:before{content:\"\\f165\"}.fa-youtube-square:before{content:\"\\f166\"}.fa-youtube:before{content:\"\\f167\"}.fa-xing:before{content:\"\\f168\"}.fa-xing-square:before{content:\"\\f169\"}.fa-youtube-play:before{content:\"\\f16a\"}.fa-dropbox:before{content:\"\\f16b\"}.fa-stack-overflow:before{content:\"\\f16c\"}.fa-instagram:before{content:\"\\f16d\"}.fa-flickr:before{content:\"\\f16e\"}.fa-adn:before{content:\"\\f170\"}.fa-bitbucket:before{content:\"\\f171\"}.fa-bitbucket-square:before{content:\"\\f172\"}.fa-tumblr:before{content:\"\\f173\"}.fa-tumblr-square:before{content:\"\\f174\"}.fa-long-arrow-down:before{content:\"\\f175\"}.fa-long-arrow-up:before{content:\"\\f176\"}.fa-long-arrow-left:before{content:\"\\f177\"}.fa-long-arrow-right:before{content:\"\\f178\"}.fa-apple:before{content:\"\\f179\"}.fa-windows:before{content:\"\\f17a\"}.fa-android:before{content:\"\\f17b\"}.fa-linux:before{content:\"\\f17c\"}.fa-dribbble:before{content:\"\\f17d\"}.fa-skype:before{content:\"\\f17e\"}.fa-foursquare:before{content:\"\\f180\"}.fa-trello:before{content:\"\\f181\"}.fa-female:before{content:\"\\f182\"}.fa-male:before{content:\"\\f183\"}.fa-gittip:before{content:\"\\f184\"}.fa-sun-o:before{content:\"\\f185\"}.fa-moon-o:before{content:\"\\f186\"}.fa-archive:before{content:\"\\f187\"}.fa-bug:before{content:\"\\f188\"}.fa-vk:before{content:\"\\f189\"}.fa-weibo:before{content:\"\\f18a\"}.fa-renren:before{content:\"\\f18b\"}.fa-pagelines:before{content:\"\\f18c\"}.fa-stack-exchange:before{content:\"\\f18d\"}.fa-arrow-circle-o-right:before{content:\"\\f18e\"}.fa-arrow-circle-o-left:before{content:\"\\f190\"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:\"\\f191\"}.fa-dot-circle-o:before{content:\"\\f192\"}.fa-wheelchair:before{content:\"\\f193\"}.fa-vimeo-square:before{content:\"\\f194\"}.fa-turkish-lira:before,.fa-try:before{content:\"\\f195\"}.fa-plus-square-o:before{content:\"\\f196\"} /* */ .fa-spin::before{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}\n/* General */ .dialog { border: 1px solid; display: block; } .dialog:not(#qr):not(#thread-watcher):not(#header-bar) { box-shadow: 0 1px 2px rgba(0, 0, 0, .15); } #qr, #thread-watcher { box-shadow: -1px 2px 2px rgba(0, 0, 0, 0.25); } .captcha-img, .field { background-color: #FFF; border: 1px solid #CCC; -moz-box-sizing: border-box; box-sizing: border-box; color: #333; font: 13px sans-serif; outline: none; transition: color .25s, border-color .25s; transition: color .25s, border-color .25s; } .field::-moz-placeholder, .field:hover::-moz-placeholder { color: #AAA !important; font-size: 13px !important; opacity: 1.0 !important; } .captch-img:hover, .field:hover { border-color: #999; } .field:hover, .field:focus { color: #000; } .field[disabled] { background-color: #F2F2F2; color: #888; } .field::-webkit-search-decoration { display: none; } .move { cursor: move; overflow: hidden; } label, .watch-thread-link { cursor: pointer; } a[href=\"javascript:;\"] { text-decoration: none; } .warning { color: red; } #boardNavDesktop { display: none !important; } a { outline: none !important; } .painted { border-radius: 3px; padding: 0px 2px; } body>hr, .ad-plea-bottom + hr { display: none; } .board > hr:last-of-type { border-top-color: transparent !important; } div.navLinks { margin-bottom: -10px !important; } .ad-plea { display: none; } .ad-cnt { margin: 10px !important; } /* 4chan style fixes */ .opContainer, .op { display: block !important; overflow: visible !important; } .reply > .file > .fileText { margin: 0 20px; } .hashlink::before { content: ' '; visibility: hidden; } .inline + .hashlink, [hidden] { display: none !important; } div.center:not(.ad-cnt) { display: none !important; } .page-num { margin-right: -8px; } /* fixed, z-index */ #overlay, #fourchanx-settings, #qp, #ihover, #navlinks, .fixed #header-bar, :root.float #updater, :root.float #thread-stats, #qr { position: fixed; } #fourchanx-settings { z-index: 999; } #overlay { z-index: 900; } #notifications { z-index: 70; } #qp, #ihover { z-index: 60; } #menu { z-index: 50; } #navlinks, #updater, #thread-stats { z-index: 40; } .fixed #header-bar.autohide { z-index: 35; } #qr { z-index: 30; } #thread-watcher { z-index: 8; } :root.fixed-watcher #thread-watcher { z-index: 20; } .fixed #header-bar { z-index: 10; } /* Header */ .fixed.top-header body { padding-top: 2em; } .fixed.bottom-header body { padding-bottom: 2em; } .fixed #header-bar { right: 0; left: 0; padding: 3px 4px 4px; } .fixed.top-header #header-bar { top: 0; } .fixed.bottom-header #header-bar { bottom: 0; } #header-bar { border-width: 0; transition: all .1s .05s ease-in-out; } :root.fixed #header-bar { box-shadow: -5px 1px 10px rgba(0, 0, 0, 0.20); } #custom-board-list .current { padding: 1px 1px 4px 1px; } :root.centered-links #shortcuts { width: 300px; text-align: right; } :root.centered-links #header-bar { text-align: center; } #board-list { font-size: 13px; } :root.centered-links #custom-board-list { position: relative; left: 150px; } .fixed.top-header #header-bar { border-bottom-width: 1px; } .fixed.bottom-header #header-bar { box-shadow: 0 -1px 2px rgba(0, 0, 0, .15); border-top-width: 1px; } .fixed.bottom-header #header-bar .menu-button i { border-top: none; border-bottom: 6px solid; } #board-list { text-align: center; } .fixed #header-bar.autohide:not(:hover) { box-shadow: none; transition: all .8s .6s cubic-bezier(.55, .055, .675, .19); } .fixed.top-header #header-bar.autohide:not(:hover) { margin-bottom: -1em; -webkit-transform: translateY(-100%); transform: translateY(-100%); } .fixed.bottom-header #header-bar.autohide:not(:hover) { -webkit-transform: translateY(100%); transform: translateY(100%); } #scroll-marker { left: 0; right: 0; height: 10px; position: absolute; } :root:not(.autohide) #scroll-marker { pointer-events: none; } #header-bar #scroll-marker { display: none; } .fixed #header-bar #scroll-marker { display: block; } .fixed.top-header #header-bar #scroll-marker { top: 100%; } .fixed.bottom-header #header-bar #scroll-marker { bottom: 100%; } #header-bar a:not(.entry):not(.close) { text-decoration: none; } #header-bar a:not(.entry):not(.close):not(.current) { padding: 1px; } #header-bar input { margin: 0; vertical-align: bottom; } #shortcuts:empty { display: none; } .brackets-wrap::before { content: \"\\00a0[\"; } .brackets-wrap::after { content: \"]\\00a0\"; } .dead-thread, .disabled { opacity: .45; } #shortcuts { float: right; } .shortcut { margin-left: 3px; } #navbotright, #navtopright { display: none; } #toggleMsgBtn { display: none !important; } .current { font-weight: bold; } /* 4chan X link brackets */ .brackets-wrap::before { content: \"[\"; } .brackets-wrap::after { content: \"]\"; } /* Notifications */ #notifications { position: fixed; top: 0; height: 0; text-align: center; right: 0; left: 0; transition: all .8s .6s cubic-bezier(.55, .055, .675, .19); } .fixed.top-header #header-bar #notifications { position: absolute; top: 100%; } .notification { color: #FFF; font-weight: 700; text-shadow: 0 1px 2px rgba(0, 0, 0, .5); box-shadow: 0 1px 2px rgba(0, 0, 0, .15); border-radius: 2px; margin: 1px auto; width: 500px; max-width: 100%; position: relative; transition: all .25s ease-in-out; } .notification.error { background-color: hsla(0, 100%, 38%, .9); } .notification.warning { background-color: hsla(36, 100%, 38%, .9); } .notification.info { background-color: hsla(200, 100%, 38%, .9); } .notification.success { background-color: hsla(104, 100%, 38%, .9); } .notification a { color: white; } .notification > .close { padding: 7px; top: 0px; right: 5px; position: absolute; } .notification > .fa-times::before { font-size: 11px !important; } .message { -moz-box-sizing: border-box; box-sizing: border-box; padding: 6px 20px; max-height: 200px; width: 100%; overflow: auto; } /* Settings */ :root.fourchan-x body { -moz-box-sizing: border-box; box-sizing: border-box; } #overlay { background-color: rgba(0, 0, 0, .5); top: 0; left: 0; height: 100%; width: 100%; } #fourchanx-settings { -moz-box-sizing: border-box; box-sizing: border-box; box-shadow: 0 0 15px rgba(0, 0, 0, .15); height: 600px; max-height: 100%; width: 900px; max-width: 100%; margin: auto; padding: 3px; top: 50%; left: 50%; -moz-transform: translate(-50%, -50%); -webkit-transform: translate(-50%, -50%); transform: translate(-50%, -50%); } #fourchanx-settings > nav { padding: 2px 2px 0; height: 15px; } #fourchanx-settings > nav a { text-decoration: underline; } #fourchanx-settings > nav a.close { text-decoration: none; padding: 0 2px; } .section-container { overflow: auto; position: absolute; top: 2.1em; right: 5px; bottom: 5px; left: 5px; padding-right: 5px; } .sections-list { padding: 0 3px; float: left; } .credits { float: right; } .tab-selected { font-weight: 700; } .section-sauce ul, .section-advanced ul { list-style: none; margin: 0; } .section-sauce ul { padding: 8px; } .section-advanced ul { padding: 0px; } .section-sauce li, .section-advanced li { padding-left: 4px; } .section-main label { text-decoration: underline; } .section-filter ul { padding: 0; } .section-filter li { margin: 10px 40px; } .section-filter textarea { height: 500px; } .section-sauce textarea { height: 350px; } .section-advanced .field[name=\"boardnav\"] { width: 100%; } .section-advanced textarea { height: 150px; } .section-advanced .archive-cell { min-width: 160px; text-align: center; } .section-advanced #archive-board-select { position: absolute; } .section-advanced .note { font-size: 0.8em; font-style: italic; margin-left: 10px; } .section-advanced .note code { font-style: normal; font-size: 11px; } .section-keybinds .field { font-family: monospace; } #fourchanx-settings fieldset { border: 1px solid; border-radius: 3px; } #fourchanx-settings legend { font-weight: 700; } #fourchanx-settings textarea { font-family: monospace; min-width: 100%; max-width: 100%; } #fourchanx-settings code { color: #000; background-color: #FFF; padding: 0 2px; } .unscroll { overflow: hidden; } /* Index */ :root.index-loading .navLinks, :root.index-loading .board, :root.index-loading .pagelist { display: none; } #index-search { padding-right: 1.5em; width: 100px; transition: color .25s, border-color .25s, width .25s; } #index-search:focus, #index-search[data-searching] { width: 200px; } #index-search-clear { color: gray; margin-left: -1em; } #index-search:not([data-searching]) + #index-search-clear { display: none; } .summary { text-decoration: none; } .index #returnlink, .index #bottomlink, .thread #index-last-refresh, .thread #index-search-clear, .thread #index-search { display: none; } /* Announcement Hiding */ :root.hide-announcement #globalMessage { display: none; } span.hide-announcement { font-size: 11px; position: relative; bottom: 5px; } .globalMessage, h2, h3 { color: inherit !important; font-size: 13px; font-weight: 100; } /* Unread */ #unread-line { margin: 0; border-color: rgb(255,0,0); } /* Thread Updater */ #updater { background: none; border: none; box-shadow: none; } #updater > .move { padding: 5px 3px 0px; margin-bottom: -3px; } #updater > div:last-child { text-align: center; } #updater input[type=number] { width: 4em; } :root.float #updater { padding: 0px 3px; } .new { color: limegreen; } #update-status.new { margin-right: 5px; } #update-timer { cursor: pointer; } /* Thread Watcher */ #thread-watcher { position: absolute; } #thread-watcher { padding-bottom: 3px; padding-left: 3px; overflow: hidden; white-space: nowrap; min-width: 136px; max-height: 92%; overflow-y: auto; } #thread-watcher .menu-button { bottom: 1px; } :root.fixed-watcher #thread-watcher { position: fixed; } :root:not(.fixed-watcher) #thread-watcher:not(:hover) { max-height: 210px; overflow-y: hidden; } #thread-watcher > .move { padding-top: 3px; } #watched-threads > div { max-width: 250px; overflow: hidden; padding-left: 3px; padding-right: 3px; text-overflow: ellipsis; } #thread-watcher a { text-decoration: none; } #thread-watcher .move>.close { position: absolute; right: 0px; top: 0px; padding: 0px 4px; } .watch-thread-link { padding-top: 18px; width: 18px; height: 0px; display: inline-block; background-repeat: no-repeat; opacity: 0.2; position: relative; top: 1px; } .watch-thread-link.watched { opacity: 1; } /* Thread Stats */ #thread-stats { background: none; border: none; box-shadow: none; } :root.float #post-count, :root.float #file-count { pointer-events: none; } :root.float #thread-stats { padding: 0px 3px; } /* Quote */ .deadlink { text-decoration: none !important; } .backlink.deadlink:not(.forwardlink), .quotelink.deadlink:not(.forwardlink) { text-decoration: underline !important; } .inlined { opacity: .5; } #qp input, .forwarded { display: none; } .quotelink.forwardlink, .backlink.forwardlink { text-decoration: none; border-bottom: 1px dashed; } @supports (text-decoration-style: dashed) or (-moz-text-decoration-style: dashed) { .quotelink.forwardlink, .backlink.forwardlink { text-decoration: underline; -moz-text-decoration-style: dashed; text-decoration-style: dashed; border-bottom: none; } } .filtered { text-decoration: underline line-through; } :root.hide-backlinks .backlink.filtered { display: none; } .inline { border: 1px solid; display: table; margin: 2px 0; } .inline .post { border: 0 !important; background-color: transparent !important; display: table !important; margin: 0 !important; padding: 1px 2px !important; } #qp > .opContainer::after { content: ''; clear: both; display: table; } #qp .post { border: none; margin: 0; padding: 2px 2px 5px; } #qp img { max-height: 80vh; max-width: 50vw; } .qphl { outline: 2px solid rgba(216, 94, 49, .7); } :root.highlight-own .yourPost > .reply, :root.highlight-you .quotesYou > .reply { border-left: 2px solid rgba(221,0,0,.5); } /* Quote Threading */ .threadContainer { margin-left: 20px; border-left: 1px solid rgba(128,128,128,.3); } .threadOP { clear: both; } /* File */ .fileText:hover .fntrunc, .fileText:not(:hover) .fnfull, .expanded-image > .post > .file > .fileThumb > img[data-md5], :not(.expanded-image) > .post > .file > .fileThumb > .full-image { display: none; } .expanding { opacity: .5; } :root.fit-height .full-image { max-height: 100vh; } :root.fit-width .full-image { max-width: 100%; } :root.gecko.fit-width .full-image { width: 100%; } #ihover { -moz-box-sizing: border-box; box-sizing: border-box; max-height: 100%; max-width: 75%; padding-bottom: 16px; } /* Fappe Tyme */ .fappeTyme .thread > .noFile, .fappeTyme .threadContainer > .noFile { display: none; } /* Werk Tyme */ .werkTyme .post .file { display: none; } /* Index/Reply Navigation */ #navlinks { font-size: 16px; top: 25px; right: 10px; } /* Filter */ .opContainer.filter-highlight { box-shadow: inset 5px 0 rgba(255, 0, 0, .5); } .filter-highlight > .reply { box-shadow: -5px 0 rgba(255, 0, 0, .5); } /* Spoiler text */ :root.reveal-spoilers s { color: white !important; } /* Thread & Reply Hiding */ .hide-thread-button, .hide-reply-button { float: left; margin-right: 4px; padding: 2px; } .hide-thread-button:not(:hover), .hide-reply-button:not(:hover) { opacity: 0.4; } .threadContainer .hide-reply-button { margin-left: 2px !important; position: relative; left: 1px; } .hide-thread-button { margin-top: -1px; } .stub ~ * { display: none !important; } .stub input { display: inline-block; } /* QR */ :root.hide-original-post-form #postForm, :root.hide-original-post-form .postingMode, :root.hide-original-post-form #togglePostForm, #qr.autohide:not(.has-focus):not(:hover) > form, .thread #qr select[data-name=thread], #file-n-submit:not(.has-file) #qr-filerm { display: none; } #qr select, #dump-button, #url-button, .remove, .captcha-img { cursor: pointer; } #qr { z-index: 20; position: fixed; padding: 1px; border: 1px solid transparent; min-width: 300px; border-radius: 3px 3px 0 0; } #qrtab { border-radius: 3px 3px 0 0; } #qrtab { margin-bottom: 1px; } #qr .close { float: right; padding: 0 3px; } #qr .warning { min-height: 1.6em; vertical-align: middle; padding: 0 1px; border-width: 1px; border-style: solid; } .qr-link-container { text-align: center; } .qr-link { border-radius: 3px; padding: 6px 10px 5px; font-weight: bold; vertical-align: middle; border-style: solid; border-width: 1px; font-size: 10pt; } .persona { width: 248px; max-width: 100%; min-width: 100%; } #dump-button { width: 10%; margin: 0; margin-right: 4px; font: 13px sans-serif; padding: 1px 0px 2px; opacity: 0.6; } #url-button { width: 10%; margin: 0; margin-right: 4px; font: 13px sans-serif; padding: 1px 0px 2px; opacity: 0.6; } .persona .field:not(#dump) { width: 95px; min-width: 33.3%; max-width: 33.3%; } #qr textarea.field { height: 14.8em; min-height: 9em; } #qr.has-captcha textarea.field { height: 9em; } input.field.tripped:not(:hover):not(:focus) { color: transparent !important; text-shadow: none !important; } #qr textarea { resize: both; } .captcha-img { margin: 0px; text-align: center; background-image: #fff; font-size: 0px; min-height: 59px; min-width: 302px; } .captcha-input{ width: 100%; margin: 1px 0 0; } .captcha-input.error:focus { border-color: rgb(255,0,0) !important; } .field { -moz-box-sizing: border-box; margin: 0px; padding: 2px 4px 3px; } #qr textarea { min-width: 100%; } #qr [type='submit'] { width: 25%; vertical-align: top; } :root.webkit #qr [type='submit'] { height: 24px; } #qr label input[type=\"checkbox\"] { position: relative; top: 2px; } /* Fake File Input */ input#qr-filename { border: none !important; width: 80%; padding: 0px 4px; position: relative; bottom: 1px; background: none !important; } input#qr-filename:not(.edit) { pointer-events: none; } #qr-filename, #qr-filesize, .has-file #qr-no-file { display: none; } #qr-no-file, .has-file #qr-filename, .has-file #qr-filesize { display: inline-block; margin: 0 0 2px; overflow: hidden; text-overflow: ellipsis; vertical-align: top; } #qr-no-file { color: #AAA; padding: 1px 4px; } #qr-filename-container { -moz-box-sizing: border-box; display: inline-block; position: relative; width: 100px; min-width: 74.6%; max-width: 74.6%; margin-right: 0.4%; margin-top: 1px; overflow: hidden; padding: 2px 1px 0; height: 22px; } #qr-filename-container:hover { cursor: text; } #qr-extras-container { position: absolute; right: 0px; } #qr-filerm { margin-right: 3px; z-index: 2; } #file-n-submit { height: 23px; } #qr input[type=file] { visibility: hidden; position: absolute; } /* Thread Select / Spoiler Label */ #qr select[data-name=thread] { float: right; } #qr.has-spoiler .has-file #qr-spoiler-label { width: 6.7%; min-width: 6.7%; max-width: 6.7%; display: inline-block; text-align: center; vertical-align: top; } #qr.has-spoiler #file-n-submit:not(.has-file) #qr-spoiler-label { display: none; } #qr.has-spoiler .has-file #qr-filename-container { max-width: 67.9%; min-width: 67.9%; } #qr-spoiler-label input { position: relative; top: 3px; } /* Dumping UI */ .dump #dump-list-container { display: block; } #dump-list-container { display: none; position: relative; overflow-y: hidden; margin-top: 1px; } #dump-list { overflow-x: auto; overflow-y: hidden; white-space: nowrap; width: 248px; max-width: 100%; min-width: 100%; } #dump-list:hover { overflow-x: auto; } .qr-preview { -moz-box-sizing: border-box; counter-increment: thumbnails; cursor: move; display: inline-block; height: 90px; width: 90px; padding: 2px; opacity: .5; overflow: hidden; position: relative; text-shadow: 0 0 2px #000; -moz-transition: opacity .25s ease-in-out; vertical-align: top; background-size: cover; } .qr-preview:hover, .qr-preview:focus { opacity: .9; } .qr-preview::before { content: counter(thumbnails); color: #fff; position: absolute; top: 3px; right: 3px; text-shadow: 0 0 3px #000, 0 0 8px #000; } .qr-preview#selected { opacity: 1; } .qr-preview.drag { box-shadow: 0 0 10px rgba(0,0,0,.5); } .qr-preview.over { border-color: #fff; } .qr-preview > span { color: #fff; } .remove { background: none; color: #e00; padding: 1px; } a:only-of-type > .remove { display: none; } .remove:hover::after { content: \" Remove\"; } .qr-preview > label { background: rgba(0,0,0,.5); color: #fff; right: 0; bottom: 0; left: 0; position: absolute; text-align: center; } .qr-preview > label > input { margin: 0; } #add-post { cursor: pointer; font-size: 2em; position: absolute; top: 50%; right: 10px; -moz-transform: translateY(-50%); } .textarea { position: relative; } :root.webkit .textarea { margin-bottom: -2px; } #char-count { color: #000; background: hsla(0, 0%, 100%, .5); font-size: 8pt; position: absolute; bottom: 1px; right: 1px; pointer-events: none; } /* Menu */ .menu-button:not(.fa-bars) { display: inline-block; position: relative; cursor: pointer; } .menu-button i { border-top: 6px solid; border-right: 4px solid transparent; border-left: 4px solid transparent; display: inline-block; margin: 2px; vertical-align: middle; } .reply .menu-button, .op .menu-button, #thread-watcher .menu-button { margin-left: -1px !important; position: relative; } .op .menu-button, #thread-watcher .menu-button { top: 1px; } :root.blink .reply .menu-button { position: relative; top: 2px; } :root.blink .op .menu-button, :root.blink #thread-watcher .menu-button { top: 3px; } .menu-button + .container:not(:empty) { margin-left: -5px !important; } #menu { position: fixed; outline: none; } #menu, .submenu { border-radius: 3px; padding-top: 1px; padding-bottom: 3px; } .entry { cursor: pointer; display: block; outline: none; padding: 2px 10px; position: relative; text-decoration: none; white-space: nowrap; min-width: 70px; } .left>.entry.has-submenu { padding-right: 17px !important; } .entry input[type=\"checkbox\"], .entry input[type=\"radio\"] { margin: 0px; position: relative; top: 2px; } .has-submenu::after { content: \"\"; border-left: .5em solid; border-top: .3em solid transparent; border-bottom: .3em solid transparent; display: inline-block; margin: .3em; position: absolute; right: 3px; } .left .has-submenu::after { border-left: 0; border-right: .5em solid; } .submenu { display: none; position: absolute; left: 100%; top: -1px; margin-left: 0px; margin-top: -2px; } .focused > .submenu { display: block; } .imp-exp-result { position: absolute; text-align: center; margin: auto; right: 0px; left: 0px; width: 200px; } .export, .import, .reset { cursor: pointer; text-decoration: none !important; } /* Custom Board Titles */ .boardTitle[contenteditable=\"true\"], .boardSubtitle[contenteditable=\"true\"] { cursor: text !important; } div.boardTitle { font-weight: 400 !important; } /* Link Title Favicons */ .linkify.YouTube { background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAYAAABr5z2BAAABIklEQVQoz53LvUrDUBjG8bOoOammSf1IoBSvoCB4JeIqOHgBLt6AIMRBBQelWurQ2kERnMRBsBUcIp5FJSBI5oQsJVkkUHh8W0o5nhaFHvjBgef/Mq+Q46RJBMkI/vE+aOus956tnEswIZe1LV0QyJ5sE2GzgZfVMtRNIdiDpccEssdlB1mW4bvTwdvWJtRdErM7U+8S/FJykCRJX5qm+KpVce8UMNLRLbulz4iSjTAMh6Iowsd5BeNadp3nUF0VlxAEwZBotXC0Usa4ll3meZdA1iguwvf9vpvDA2wvmKgYGtSud8suDB4TyGr2PF49D/vra9jRZ1BVdknMzgwuCGSnZEObwu6sBnVTCHZiaC7BhFx2PKdxUidiAH/4lLo9Mv0DELVs9qsOHXwAAAAASUVORK5CYII=') center left no-repeat!important; padding-left: 18px; } .linkify.Vimeo { background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAYFBMVEUAAAAIdZUKh6sLlLkLmr4LmsAMp88NrdYVW3MZj7Acstkrt9s1e5E7vN5EfI9JvdtKwuBijp5kpbl30eiDt8aG1uqRr7qTyNehxM+k4PCy3enB3OTg6Ovv9PXw+fz////L9U5WAAAAAXRSTlMAQObYZgAAAIFJREFUeNplz90OwiAMBWAQpAoyxclkP3je/y0H2AQXz0WT8100rRD6kNI9/cRroemQL3hXhoujZYj4OHoAmBvYGcBISwbWBvfXCrytnIDUQMkbsBpagMA7zhtQdyTFQAmIG7IkYniiZuh3XGsPqoOZkMOJOpAcLqUzNFGGu/57fwc1hgtp0mVSyQAAAABJRU5ErkJggg==') center left no-repeat!important; padding-left: 18px; } .linkify.SoundCloud { background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABsklEQVQ4y5WTy2pUQRCGv2rbzDjJeAlIBmOyipGIIJqFEBDElwh4yULGeRFXPoEIBl/AvQ/gC2RnxCAoxijiwks852S6+3dxzslcHJCpTXVX11/Xv0097gLPgVNMJxnQNfX4zsqleWbnpoMf/oa9d988MM9MC/rp+E0a+A0dsVobMNMCOO8B6McRoABJI+A6gJmN3D2A8jgEBCEkSEMBrcrsDAzDWWn3AjgKFaDMmgRqniGFgsaDp1jrLOngDf1XT1D+A1dFc4MKAkkiCVKjjVu7g9+4Rzx4i1u6hjXbuMWr0O5QPNvCu7IaCZwEKQukLGDrm5x8uI0tr6MkiGlkiv7yLfzN+6S5i6QsIMABkEfcxhbWWYMkVAOjxvYAjc3HNHrbKI9VBQBFwF25XQKSBjqIf1YBuAurEMrczgDygD6/x2LCpFLXLUyQ+PoldphhBhYfIX09XU1+Flaukz7uYqs3SHs7cG4BmTsmkBUF9mmXEwa28BNLPaQPLepuNcbGSWQquQC2/Kdcox1FUGkcB0ykck1nA2+wTzMs8stGnP4rbWGw74EuS/GFQWfK7/wF6P4F7fzIAYkdmdEAAAAASUVORK5CYII=') center left no-repeat!important; padding-left: 18px; } .linkify.audio { background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAitJREFUOE9jYCAWKJWwavr0KyXWb/FIbDtUFFyzJx6nVofE2Xo5nXsj0rqPNSR0nVkR2Hjmgmfd+U9Otdf+m5Vf/6+SfeU/R9ChVVgNYDRtlfJuuPA/rPfe/4QpD/6nznj0P27Kw/9unff/69Xf+69c/+C/SO7N/0z+OAxgMmmRCe++/r9i3ev/KWvf/vdY8PK/bt/9/wrNV3/IN5y/IVt1YqNg4pGTTP4HsbuA2bhZ2qvpyn+xjIObxAp3VwqlrgngLFyryVy5nhPmZJHANS2cwYexG8BmVC/pWn3hP4NZlzWuQDJI3dIiFnUUuwEsQAOcq87jNcC7fHeLUtJxHF4AGmBWeAavAWH1+1rUUk7giAWjOknllON4DXAs2NEiG4/DBQxAF/CFHfrPYI4jDFSLuJVjNrUJhB/B7gIGo1pJRt99GAZYJK7wLJ1z7Xzl4vu/7aqv/GRBj0bjqAX2qb0nJ7mXH17C4HcUxQA+hymWtSue/C5a9up/9Ozn/7Vr7v1nRY7GqMb91T3b3v6vWvPmf/S0p/9ZQk+DDLCBRSOz06Jqk+o7/21nvfqvsebDf7kZL/5zBaxphkezd+OFn7HzXvz3Wvjmv9a8N//5Ek//ZTBpVYUrMG2X5wjcdl68+uI/wa5Lr3hSNjczGFeywOVZ/bbcVGp//F9izfv/Ql03f3P4LC/HSEQquYwMFnUCDJ7dzBhyjGZNQpye89M5gpfnMvtNUyE2h4PUAQBovvT7lyNljwAAAABJRU5ErkJggg==') center left no-repeat!important; padding-left: 18px; } .linkify.LiveLeak { background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAlNJREFUGBkFwU2LVmUYAODrPu8Z5x1xSpRBXQyFoLsBE+wfiO5atJOgnf9DUPwFgtGinUgEaQsRhHYuMtpEiEWuG5iNjuOcj+c8z911xXcXL/68c3Dw1fzhg0QgEQAAEYGUKXFie9vxlSs/xk/rdavjGEkmkWSih65z4osv9GfOiK6LzEyZ2uGh4dUrmzs72ddlUUhkoiMr4PT167589Mh6c1N0nSRlqrX67dat+PDyZXRT19m5edPnt28rGFHxMcJ6d9fprS1/37tneP3aemPD1uamUydPOru3p5DdGOH0tWsu3LhhxIQJM2qEpRT/Pn3q/du3AhARSmvGTH0lplKMrVkiYpVpQaJlighzhDkzhmEA0fcWoqAfyaFW4zTlgCABxlrNmY4ylUzLsiREprFWc0T2M+ZSjKWY0AEaltZUjJixZJIpuk5pTWlNP2BYFvOyKJkCAKU1tTXHrZlqVWolUxdhxsfVSj9FmJfFMM9GdICGGa01HyMstYpMIFPJVNDPmYZSTOPoOEKHzNRlKpmWWh1j6TpLa2SKTKVWU6Z+Qolwdm/P9QcPZKa2LH69e9eIMs+WCL/cv2/98CGZPrt61am+V9APq1X89eyZ/968obVYaiXT4dGREgG+vnPHeHgYMsH2+fP+efEihtVKv7SWw/6+9/v7KYLMhIywTJPamvOXLomukyRsrNf+ePzYkpl9dJ3SWgSCSCQCfz5/7pMLF2yfO6eLiAQcHRz4/cmT+HR7O+Ob3d0fNt69+7a2BiICQCJbA0EgE5lpvbXl1OXL3/8Pfax4+6SjSukAAAAASUVORK5CYII=') center left no-repeat!important; padding-left: 18px; } .linkify.Vocaroo { background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAw9JREFUOE9jYMABuMwYmCyTJKUCGlSnFSy02TTzeOyCiQcDViX26qVz2TAyYtWmEMwuoZ3M7V40LcB79pHkc0svpvzY8jD//87nxf+3Pyn8v/ZO8v+VNyP/2mZJumI1QCWSI8232Hjumitlfw5+qPp/9l8TCt76JP//xkdx/wsXWCzjtWFkwTCkbWFe9plPk/+ga4Txz/xt/D/hkN//gMXif21a+NbyWjIwoRiy6GDT5rP/mlFsPfyp5n/NpOj/22+0gMUXXIz/H7hC/L/bFKFbPDZMrHAD5H35OPt2J9zacDv/f3V7xv9FhwrBGubsT/1//Pjx/1GJ/mD+/nfl/1v3Ovy3KRJNQbHdOlXCvOO03/+pm1P/v3v37n90hhtYw9HPtf8Xb2v937cmHswHeWPRxYj/LvkK3igGKARwicTO07118H3V/5kbi/4vPZMJtK3s/6YH2f+Pfq1B8VbjWrdnMu5s4nAD9CNFhKwz5DTUvLl419zKvAcLtG1P84BRl/b/5M/6/6f/NPzf/qzo84yj0Uus0xUU4Zor54bm9+4OfZG02OCuoAMTb9ZkC9ull1Nvrr2Z+XvRpaRfc65H/68F+jl9svEhzyLFWoccWVc+eyTHq/twydjlKRln7jX9bNMkMJnbhoFRL1xCqmKx6/yi2fYXa/c5/e846PV/5fW0/7OPx/yfcjzop34ulxdGGvDuU8mMXaX507lBuiN6ueadmQeT/p/93vf/1O+G//sP5fw/eL3o/5JLif8zVxs+Tlir9S26UyeFQQvJGBE7FvaFZ9LfN+1y+WjbItSb3GmXvXd15v8zroH/HxgE/D+aGPx/18vi/z07PeZNPRKxe/Kh0Ae8toxscCO4zBkYXArk9C1SxJUYjBkYPPIVtbbuTftz3cz//2O9wP/75iSAXdO72/dt2HL5F6YlfBW4MiJYXMiBiW3t7azHBx+V/t89N+H/8a+1//e9K/9attDp5LQjYX8SuvVL8RoAkmxa65299Erq1FnHo0qrl7t4BddriIs4MrM3rfWcFd+pGwVSAwBZ0bKP8yrZPAAAAABJRU5ErkJggg==') center left no-repeat!important; padding-left: 18px; } .linkify.pastebin { background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAB1FBMVEUAAAAAAAAAAABWYWwAAABbY3BbYm5dZnFdZXJeZnMEBAQHCAhYYGpdZnFdZnBgaHIlJyomKCooKi09QkdESU5eZGtdYmhdYmleY2lrcXdqb3Rqb3Rqb3SSmJ+SlJeWmJutr7GtrrCWm6ChpKhbW1tmZmZvb290dHR3d3d4eHh5eXl6enp8fHx+gIJ/f3+CgoKDg4OEhISFhYWHh4eKioqKjI2Li4uMjIyOjo6Pj4+QkJCRkZGSkpKUlJSVl5mWlpaYmZqZm52ampqbm5ucnJydnZ2enp6fn5+hoaGioqKkpKSkpaalpaWmp6mmp6qnqauoqKioqquoqq2qqqqrrK2srKysra6srrCsrrGurq6vr6+wsLCxsbGysrKztLa0tLS1t7m2tra3t7e4uLi5ubm6urq7u7u8vLy9vb2+vr7AwMDAwsTBwcHExcfFxcXFxsnGxsbHx8fIyMjJycnMzMzNzc3Ozs7O0NLPz8/Q0NDR0dHR09XT09PV1dXV1dbV1tfV19rW1tbX19fX19jY2tzZ2dnZ2tva2tra3N3a3N7c3Nze3t7f39/f4OHg4ODi4uLl5+jm5ubs7Ozs7e3u7u7v7+/v8PDw8PDx8fHy8vLz8/P29vYSoLMZAAAAJHRSTlMABAUGCwsNHCAiLzMzMzZEYGJwgIuOnJycnqmqq9bc3+/w8fkZ0N/uAAAA/klEQVQoU2NgYGDl5YMDdgYGBmZZ3964CYFtIR3e9Q7K/AwMHI55KfaFmcHWMy3K3MwlGRg4wz0zdYpcorRbNbL0LaWAAp3ts2umV8wo6MupTauQBgqUG03VL7W3sfZSb1erAgm02M+yzYrVCXUy6zapAQlUx/dEdyX3J3ZHVUYVywAF8o2rDNN1Go2jzGLMokAC2QbuSc42mXmaOXop9iAtCXrJ5qXWjT59Abl2ESJAAX/tSIMMiyrrqQ3T6uS5gQK6kSqpqkUermGTexQFmYACflqR+hlWZSamzQpCLEDPsSmVVDT1TJw0JUhOAMRnYOARFRMTE5cQF+ZiBPIAII5B3EVG0b4AAAAASUVORK5CYII=') center left no-repeat!important; padding-left: 18px; } .linkify.gist { background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABblBMVEXc3NykpKTW1tbb29ugoKCdnZ0AAAACAgIEDRcKCgoMDAwODg4QIzYRDAoTExMUDwwVAg0WICsaEw8aGhoiCBklGxUmERwwKCQ7LSU7Ozs8LSZFLyNINi1JNyxJNy1KSklMOi5VR1FXV1daQTRkZGRseYZwU0F4eHh7dnR8bWV/YE6IdGiKcGCKkJaNgYeNjY2RdGOScWCUcWCZmZmhoaGkpKSoqKirfmaurq6xsbG1tbW6urq+vr7AbmzBb23CwsLGxsbHx8fHyMjJycnJysrMzMzOiYbPi4fQ0NDRoYbT09PU1NTW1tbY2NjZqIzZ2dnb29vd3d3f39/i4uLktZrk5OTl5eXm5ubn5+fo6Ojq6urs7OzttKLu7u7wuqbw8PDx8fHz8/P4+Pj5+fn7uZj8vpz9ya79ybD/tZf/upr/wZ//w6H/xKH/xaL/xrH/yqj/y7T/zqv/z7D/07D/17n/2Lv/2Lz/3L//38n/4Mk3Q/ZuAAAABnRSTlMSFcbGzc5MNKFvAAAA1klEQVQoz2NgYPZHAswMDEwRSclwkBTBxOARn4gE4j0YXBOiJNUDg7y8Ar1UlOITXBkcY73Z2Li42dg42dn4wmIdGeyjQ7nZoEA4PNqewSZKlw0O9KJsGKwjBdl4ZeWkJGQUhNjEIq0ZrMI5+D0ri7Jz8itCRAXCrRgsQ3mUy+xicrPSbfO0REItGSyCVaVL3ONSU9LcCtQUgy0YzIJ85M1LizMzCsv9xF2CzBhMAwN99TV1DI0MtDWcAgNNGUycA5CAswkDi5kDwrMOZiwMjKzGSICVEQDhZj0UQV7PewAAAABJRU5ErkJggg==') center left no-repeat!important; padding-left: 18px; } .linkify.image { background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAs5JREFUOE+lk/tvi1EYx98/xT8gW4REIpGFMEQWl2FiM9ZMZhm2xRAyOsmujFFmdFRHu0tWm87UypxStr69zPauN5e5rHVp3IYhbOvHy+wHEQlxkm+ek+d8nm9OznkeSfrfldmgJC7QyUlTymsJTfuTZ25z4HdWYwyLreYhtpgekGPw0+kKvo1Eo+IXRSIiEhkWZuc9tqnsJD9EqTUopCxjSGTpB0iueczSo1HyW8cpsExQ1DbxI2pt45j9cXpexul4FEd79RnZphAa/SD7WvuFtO6UItbU9LC+YQxNI2w0wwYT5LRAdhOU3oBTIXC9gXP3oUSGgz2vST3gYHejR0jptT1C332f8yrUEYHrz8CgxDnpm6DKCUfc0KnmXa/AEVPPwnDcD0cvetA2uYRk67Ive/lpjO7YBO1PPuF8Df3vwf4cbNE4tqdw7YVq8HYyHx6FvhE1hkMEg8HDUqvFkjT4aIjMqkqyqkswDSrcfBfH+Q561YLAZ/B+BLda6FXlU/cPv0AoEPhuoP1h4Av7Wbh9E/Py15NWWUjeSR3nZDfeN+N0DY9hG/7K1eGP3P0S5/EYRFUF/IOTBrUXHPm9fT6mr1xEwupkZqxbzLyiDJYUZ5NSnkdqdSHpxyrYdFpPgdmAsdfJwPMI/Yr65bf7tZLGGBQ7DNdJWFtIYvoOZmbuZE7OXpIKKli86zAr9p9gTVktWTVnKTI2U95uRWe3U2IJUDbVB5p6hVm5x5m9Vc/cnedZUNzC8lILaQesZBy6hEZ3maKzgvJWFzVWD9XtXvVGQbSWASFtMATVRlJIKbOTWtlJXaeXepuPM1f6MNp9GLt8mLvvYLmp0OhQ2Fwvk6m7xaqDTvY0eYWUVtcnllXfYlGpnfklVuraHHg8HjxuN+6fktUHlWWZPaZeUo/ILK0UKttBcbNbSB9GP0yLxWJJUxoZGUn80zD9C/vXQ/4NHY10h3M1zmQAAAAASUVORK5CYII=') center left no-repeat!important; padding-left: 18px; } .linkify.InstallGentoo { background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABcVBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB3dIYAAAAAAAAAAAAbGh4BBAcCBgoBBgoCBwsCCQ/QzucCCA7MyuXZ1eUBBQmTh8fo5/i9svIAAADh3vQAAAACCA0CCQ8CCQ4DDBQbGCUDChDr6vgAAAAAAAAREBIDCxK6tdfe2fTv7/cDCxIDDBQEDRUHDhgMJjXk4PZdXWdLUFoUNEYOKDgSMUMRLUBneI4eTGj08/QmW3onW3rTzvfOx/giU3IiVHMkWHdEaYJobHv3+PokWHpua6TNy9xZgZ+1quz8/foQKj0XPFInWn0nW38tZ4o6fqg8gq48grA9hrU/i7pAhrNAiLdBjLtEjr1FksNIjr5Il8pImMtKWnNqhL97odKFqti5q/q5rPq60+nCt/vLw/vPx/jV0vHY0/rc1/rg2/vh3fzn4fzu6/vx8vf19Pv19Pz49/v5+Pv8/Pv8/fr9/vv+/frziVtUAAAAT3RSTlMABQYHCAoNDhARGRobL0ZOV1xdXV5fYGBmZnB0eX2MjZSaoaGio6mqqqustLq7zubo6Ojo6evt7u/x8fLy9/f4+Pj5+vr6+vr6+/39/v7+XKgUSwAAAMhJREFUKM9jYGDg4OZmZgABKINT1dBAhBHIYFMxMBIDisjbhoZbCTExsCu5hoeY8DEwcOkEx8fY6MqpucTGB0izglVEplcU5/gmRYWBVQDNMK+s0hN3SvMyBpsBNJxXw0NfwTEjVQZqHQMHj5RfWW5mliSEC7TPzK6yJD/bXZQRzGdXcisqLy309okA2Q4Eis4peQWmstqBCdGW/CABraC45ERBBs3A6Fh/AbAKTwsHa34QZW8NVsGuLqwswQSjQICTmYMFQaEDAAF8JHLfKGswAAAAAElFTkSuQmCC') center left no-repeat!important; padding-left: 18px; } /* Gallery */ #a-gallery { position: fixed; top: 0; bottom: 0; left: 0; right: 0; z-index: 30; display: flex; flex-direction: row; background: rgba(0,0,0,0.7); } .gal-viewport { display: flex; align-items: stretch; flex-direction: row; flex: 1 1 auto; } .gal-thumbnails { flex: 0 0 150px; overflow-y: auto; display: flex; flex-direction: column; align-items: stretch; text-align: center; background: rgba(0,0,0,.5); border-left: 1px solid #222; } .gal-hide-thumbnails .gal-thumbnails { display: none; } .gal-thumb img { max-width: 125px; max-height: 125px; height: auto; width: auto; } .gal-thumb { flex: 0 0 auto; padding: 3px; line-height: 0; transition: background .2s linear; } .gal-highlight { background: rgba(0, 190, 255,.8); } .gal-prev { order: 0; border-right: 1px solid #222; } .gal-next { order: 2; border-left: 1px solid #222; } .gal-prev, .gal-next { flex: 0 0 20px; position: relative; cursor: pointer; opacity: 0.7; background-color: rgba(0, 0, 0, 0.3); } .gal-prev:hover, .gal-next:hover { opacity: 1; } .gal-prev::after, .gal-next::after { position: absolute; top: 48.6%; transform: translateY(-50%) display: inline-block; border-top: 11px solid transparent; border-bottom: 11px solid transparent; content: \"\"; } .gal-prev::after { border-right: 12px solid #fff; right: 5px; } .gal-next::after { border-left: 12px solid #fff; right: 3px; } .gal-image { order: 1; flex: 1 0 auto; display: flex; align-items: flex-start; justify-content: space-around; overflow: hidden; /* Flex > Non-Flex child max-width and overflow fix (Firefox only?) */ width: 1%; } :root:not(.gal-fit-height) .gal-image { overflow-y: scroll !important; } :root:not(.gal-fit-width) .gal-image { overflow-x: scroll !important; } .gal-image a { margin: auto; line-height: 0; } .gal-fit-width .gal-image img { max-width: 100%; } .gal-fit-height .gal-image img { /* Chrome doesn't support viewpoint units in calc() http://bugs.chromium.org/168840 \"It looks like the original author of viewport units in WebKit is not coming back to fix this stuff.\" Well, fuck. */ max-height: 95vh; max-height: calc(100vh - 25px); } .gal-buttons { font-size: 2em; margin-right: 10px; top: 5px; } .gal-buttons i { vertical-align: baseline; border-top-width: .4em; border-right-width: .25em; border-left-width: .25em; } .gal-buttons .menu-button { bottom: 2px; color: #ffffff; text-shadow: 0px 0px 1px #000000; } .gal-close { color: #ffffff; text-shadow: 0px 0px 1px #000000; } .gal-buttons, .gal-name, .gal-count { position: fixed; right: 178px; } .gal-hide-thumbnails .gal-buttons, .gal-hide-thumbnails .gal-count, .gal-hide-thumbnails .gal-name { right: 28px; } .gal-name { bottom: 6px; background: rgba(0,0,0,0.6) !important; border-radius: 3px; padding: 1px 5px 2px 5px; text-decoration: none !important; color: white !important; } .gal-name:hover, .gal-close:hover, .gal-buttons .menu-button:hover { color: rgb(95, 95, 101) !important; } .gal-count { bottom: 27px; background: rgba(0,0,0,0.6) !important; border-radius: 3px; padding: 1px 5px 2px 5px; color: #ffffff !important; } :root:not(.gal-fit-width) .gal-name { bottom: 23px !important; } :root:not(.gal-fit-width) .gal-count { bottom: 44px !important; } :root:not(.gal-fit-height):not(.gal-hide-thumbnails) .gal-buttons, :root:not(.gal-fit-height):not(.gal-hide-thumbnails) .gal-name, :root:not(.gal-fit-height):not(.gal-hide-thumbnails) .gal-count { right: 195px !important; } :root.gal-hide-thumbnails:not(.gal-fit-height) .gal-buttons, :root.gal-hide-thumbnails:not(.gal-fit-height) .gal-name, :root.gal-hide-thumbnails:not(.gal-fit-height) .gal-count { right: 44px !important; } @media screen and (resolution: 1dppx) { .fa-bars { font-size: 14px; } #shortcuts .fa-bars { vertical-align: -1px; } }\n/* General */ :root.yotsuba .dialog { background-color: #F0E0D6; border-color: #D9BFB7; } :root.yotsuba .field:focus { border-color: #EA8; } /* Header */ :root.yotsuba #header-bar.dialog { background-color: rgba(240,224,214,0.98); } :root.yotsuba #header-bar, :root.yotsuba #notifications { font-size: 9pt; color: #B86; } :root.yotsuba #board-list a, :root.yotsuba #shortcuts a { color: #800000; } :root.yotsuba.fixed #custom-board-list a.current { border-bottom: 1px solid rgba(178,0,0,0.2); } :root.yotsuba.fixed #custom-board-list .current:hover { border-bottom-color: rgba(255,0,0,0.2); } /* Settings */ :root.yotsuba #fourchanx-settings fieldset { border-color: #D9BFB7; } /* Quote */ :root.yotsuba .backlink.deadlink { color: #00E !important; } :root.yotsuba .inline { border-color: #D9BFB7; background-color: rgba(255, 255, 255, .14); } /* QR */ .yotsuba #dump-list::-webkit-scrollbar-thumb { background-color: #F0E0D6; border-color: #D9BFB7; } :root.yotsuba .qr-preview { background-color: rgba(0, 0, 0, .15); } :root.yotsuba .qr-link { border-color: rgb(225, 209, 199) rgb(225, 209, 199) rgb(210, 194, 184); background: linear-gradient(#FFEFE5, #F0E0D6) repeat scroll 0% 0% transparent; } :root.yotsuba .qr-link:hover { background: #F0E0D6; } /* Menu */ :root.yotsuba #menu { color: #800000; } :root.yotsuba .entry { font-size: 10pt; } :root.yotsuba .focused.entry { background: rgba(255, 255, 255, .33); } /* Watcher Favicon */ :root.yotsuba .watch-thread-link { background-image: url(\"data:image/svg+xml,\"); } /* Board Title */ :root.yotsuba div.boardTitle { font-family: sans-serif !important; text-shadow: 1px 1px 1px rgba(100,0,0,0.6); }\n/* General */ :root.yotsuba-b .dialog { background-color: #D6DAF0; border-color: #B7C5D9; } :root.yotsuba-b .field:focus { border-color: #98E; } /* Header */ :root.yotsuba-b #header-bar.dialog { background-color: rgba(214,218,240,0.98); } :root.yotsuba-b #header-bar, :root.yotsuba-b #notifications { font-size: 9pt; color: #89A; } :root.yotsuba-b #board-list a, :root.yotsuba-b #shortcuts a { color: #34345C; } :root.yotsuba-b.fixed #custom-board-list .current { border-bottom: 1px solid rgba(30, 30, 255, 0.2); } :root.yotsuba-b.fixed #custom-board-list .current:hover { border-bottom-color: rgba(255,0,0,0.2); } /* Settings */ :root.yotsuba-b #fourchanx-settings fieldset { border-color: #B7C5D9; } /* Quote */ :root.yotsuba-b .backlink.deadlink { color: #34345C !important; } :root.yotsuba-b .inline { border-color: #B7C5D9; background-color: rgba(255, 255, 255, .14); } /* QR */ .yotsuba-b #dump-list::-webkit-scrollbar-thumb { background-color: #D6DAF0; border-color: #B7C5D9; } :root.yotsuba-b .qr-preview { background-color: rgba(0, 0, 0, .15); } :root.yotsuba-b .qr-link { border-color: rgb(199, 203, 225) rgb(199, 203, 225) rgb(184, 188, 210); background: linear-gradient(#E5E9FF, #D6DAF0) repeat scroll 0% 0% transparent; } :root.yotsuba-b .qr-link:hover { background: #D9DDF3; } /* Menu */ :root.yotsuba-b #menu { color: #000; } :root.yotsuba-b .entry { font-size: 10pt; } :root.yotsuba-b .focused.entry { background: rgba(255, 255, 255, .33); } /* Watcher Favicon */ :root.yotsuba-b .watch-thread-link { background-image: url(\"data:image/svg+xml,\"); } /* Board Title */ :root.yotsuba-b div.boardTitle { font-family: sans-serif !important; text-shadow: 1px 1px 1px rgba(105,10,15,0.6); }\n/* General */ :root.futaba .dialog { background-color: #F0E0D6; border-color: #D9BFB7; } :root.futaba .field:focus { border-color: #EA8; } /* Header */ :root.futaba #header-bar.dialog { background-color: rgba(240,224,214,0.98); } :root.futaba #header-bar, :root.futaba #notifications { font-size: 11pt; color: #B86; } :root.futaba #header-bar a, :root.futaba #notifications a { color: #800000; } :root.futaba.fixed #custom-board-list a.current { border-bottom: 1px solid rgba(178,0,0,0.2); } :root.futaba.fixed #custom-board-list .current:hover { border-bottom-color: rgba(255,0,0,0.2); } /* Settings */ :root.futaba #fourchanx-settings fieldset { border-color: #D9BFB7; } /* Quote */ :root.futaba .backlink.deadlink { color: #00E !important; } :root.futaba .inline { border-color: #D9BFB7; background-color: rgba(255, 255, 255, .14); } /* QR */ .futaba #dump-list::-webkit-scrollbar-thumb { background-color: #F0E0D6; border-color: #D9BFB7; } :root.futaba .qr-preview { background-color: rgba(0, 0, 0, .15); } :root.futaba .qr-link { border-color: rgb(225, 209, 199) rgb(225, 209, 199) rgb(210, 194, 184); background: linear-gradient(#FFEFE5, #F0E0D6) repeat scroll 0% 0% transparent; } :root.futaba .qr-link:hover { background: #F0E0D6; } /* Menu */ :root.futaba #menu { color: #800000; } :root.futaba .entry { font-size: 12pt; } :root.futaba .focused.entry { background: rgba(255, 255, 255, .33); } /* Watcher Favicon */ :root.futaba .watch-thread-link { background-image: url(\"data:image/svg+xml,\"); }\n/* General */ :root.burichan .dialog { background-color: #D6DAF0; border-color: #B7C5D9; } :root.burichan .field:focus { border-color: #98E; } /* Header */ :root.burichan #header-bar.dialog { background-color: rgba(214,218,240,0.98); } :root.burichan #header-bar, :root.burichan #header-bar #notifications { font-size: 11pt; color: #89A; } :root.burichan #header-bar a, :root.burichan #header-bar #notifications a { color: #34345C; } :root.burichan.fixed #custom-board-list .current { border-bottom: 1px solid rgba(30, 30, 255, 0.2); } :root.burichan.fixed #custom-board-list .current:hover { border-bottom-color: rgba(255,0,0,0.2); } /* Settings */ :root.burichan #fourchanx-settings fieldset { border-color: #B7C5D9; } /* Quote */ :root.burichan .backlink.deadlink { color: #34345C !important; } :root.burichan .inline { border-color: #B7C5D9; background-color: rgba(255, 255, 255, .14); } /* QR */ .burichan #dump-list::-webkit-scrollbar-thumb { background-color: #D6DAF0; border-color: #B7C5D9; } :root.burichan .qr-preview { background-color: rgba(0, 0, 0, .15); } :root.burichan .qr-link { border-color: rgb(199, 203, 225) rgb(199, 203, 225) rgb(184, 188, 210); background: linear-gradient(#E5E9FF, #D6DAF0) repeat scroll 0% 0% transparent; } :root.burichan .qr-link:hover { background: #D9DDF3; } /* Menu */ :root.burichan #menu { color: #000000; } :root.burichan .entry { font-size: 12pt; } :root.burichan .focused.entry { background: rgba(255, 255, 255, .33); } /* Watcher Favicon */ :root.burichan .watch-thread-link { background-image: url(\"data:image/svg+xml,\"); }\n/* General */ :root.tomorrow .dialog { background-color: #282A2E; border-color: #111; } /* Header */ :root.tomorrow #header-bar.dialog { background-color: rgba(40,42,46,0.9); } :root.tomorrow #header-bar, :root.tomorrow #notifications { font-size: 9pt; color: #C5C8C6; } :root.tomorrow #header-bar a, :root.tomorrow #notifications a { color: #81A2BE; } :root.tomorrow.fixed #custom-board-list a.current { border-bottom: 1px solid rgba(83,124,160,0.4); } :root.tomorrow.fixed #custom-board-list .current:hover { border-bottom-color: rgba(95,137,172,0.4); } /* Settings */ :root.tomorrow #fourchanx-settings fieldset { border-color: #111; } /* Quote */ :root.tomorrow .backlink.deadlink { color: #81A2BE !important; } :root.tomorrow .inline { border-color: #111; background-color: rgba(0, 0, 0, .14); } /* QR */ .tomorrow #dump-list::-webkit-scrollbar-thumb { background-color: #282A2E; border-color: #111; } :root.tomorrow .qr-preview { background-color: rgba(255, 255, 255, .15); } :root.tomorrow #qr .field { background-color: rgb(26, 27, 29); color: rgb(197,200,198); border-color: rgb(40, 41, 42); } :root.tomorrow #qr .field:focus { border-color: rgb(129, 162, 190) !important; background-color: rgb(30,32,36); } :root.tomorrow .qr-link { border-color: rgb(25, 27, 31) rgb(25, 27, 31) rgb(10, 12, 16); background: linear-gradient(#37393D, #282A2E) repeat scroll 0% 0% transparent; } :root.tomorrow .qr-link:hover { background: #282A2E; } /* Menu */ :root.tomorrow #menu { color: #C5C8C6; } :root.tomorrow .entry { font-size: 10pt; } :root.tomorrow .focused.entry { background: rgba(0, 0, 0, .33); } /* Watcher Favicon */ :root.tomorrow .watch-thread-link { background-image: url(\"data:image/svg+xml,\"); } /* Board Title */ :root.tomorrow div.boardTitle { font-family: sans-serif !important; text-shadow: 1px 1px 1px rgba(167,170,168,0.6); }\n/* General */ :root.photon .dialog { background-color: #DDD; border-color: #CCC; } :root.photon .field:focus { border-color: #EA8; } /* Header */ :root.photon #header-bar.dialog { background-color: rgba(221,221,221,0.98); } :root.photon #header-bar, :root.photon #notifications { font-size: 9pt; color: #333; } :root.photon #header-bar a, :root.photon #notifications a { color: #FF6600; } :root.photon.fixed #custom-board-list a.current { border-bottom: 1px solid rgba(0,74,153,0.2); } :root.photon.fixed #custom-board-list .current:hover { border-bottom-color: rgba(255,51,0,0.2); } /* Settings */ :root.photon #fourchanx-settings fieldset { border-color: #CCC; } /* Quote */ :root.photon .backlink.deadlink { color: #F60 !important; } :root.photon .inline { border-color: #CCC; background-color: rgba(255, 255, 255, .14); } /* QR */ .photon #dump-list::-webkit-scrollbar-thumb { background-color: #DDD; border-color: #CCC; } :root.photon .qr-preview { background-color: rgba(0, 0, 0, .15); } :root.photon .qr-link { border-color: rgb(206, 206, 206) rgb(206, 206, 206) rgb(191, 191, 191); background: linear-gradient(#ECECEC, #DDD) repeat scroll 0% 0% transparent; } :root.photon .qr-link:hover { background: #DDDDDD; } /* Menu */ :root.photon #menu { color: #333; } :root.photon .entry { font-size: 10pt; } :root.photon .focused.entry { background: rgba(255, 255, 255, .33); } /* Watcher Favicon */ :root.photon .watch-thread-link { background-image: url(\"data:image/svg+xml,\"); } /* Board Title */ :root.photon div.boardTitle { font-family: sans-serif !important; text-shadow: 1px 1px 1px rgba(0,74,153,0.6); }", - features: [['Polyfill', Polyfill], ['Redirect', Redirect], ['Header', Header], ['Catalog Links', CatalogLinks], ['Settings', Settings], ['Index Generator', Index], ['Announcement Hiding', PSAHiding], ['Fourchan thingies', Fourchan], ['Emoji', Emoji], ['Color User IDs', IDColor], ['Custom CSS', CustomCSS], ['Linkify', Linkify], ['Reveal Spoilers', RemoveSpoilers], ['Resurrect Quotes', Quotify], ['Filter', Filter], ['Thread Hiding Buttons', ThreadHiding], ['Reply Hiding Buttons', PostHiding], ['Recursive', Recursive], ['Strike-through Quotes', QuoteStrikeThrough], ['Quick Reply', QR], ['Menu', Menu], ['Report Link', ReportLink], ['Thread Hiding (Menu)', ThreadHiding.menu], ['Reply Hiding (Menu)', PostHiding.menu], ['Delete Link', DeleteLink], ['Filter (Menu)', Filter.menu], ['Download Link', DownloadLink], ['Archive Link', ArchiveLink], ['Quote Inlining', QuoteInline], ['Quote Previewing', QuotePreview], ['Quote Backlinks', QuoteBacklink], ['Mark Quotes of You', QuoteYou], ['Mark OP Quotes', QuoteOP], ['Mark Cross-thread Quotes', QuoteCT], ['Anonymize', Anonymize], ['Time Formatting', Time], ['Relative Post Dates', RelativeDates], ['File Info Formatting', FileInfo], ['Fappe Tyme', FappeTyme], ['Gallery', Gallery], ['Gallery (menu)', Gallery.menu], ['Sauce', Sauce], ['Image Expansion', ImageExpand], ['Image Expansion (Menu)', ImageExpand.menu], ['Reveal Spoiler Thumbnails', RevealSpoilers], ['Image Loading', ImageLoader], ['Image Hover', ImageHover], ['Thread Expansion', ExpandThread], ['Thread Excerpt', ThreadExcerpt], ['Favicon', Favicon], ['Unread', Unread], ['Quote Threading', QuoteThreading], ['Thread Stats', ThreadStats], ['Thread Updater', ThreadUpdater], ['Thread Watcher', ThreadWatcher], ['Thread Watcher (Menu)', ThreadWatcher.menu], ['Index Navigation', Nav], ['Keybinds', Keybinds], ['Show Dice Roll', Dice], ['Banner', Banner], ['Navigate', Navigate]] - }; - - Main.init(); - -}).call(this); diff --git a/builds/xpi/data/greaseshim.js b/builds/xpi/data/greaseshim.js deleted file mode 100644 index 0d89a981f..000000000 --- a/builds/xpi/data/greaseshim.js +++ /dev/null @@ -1,62 +0,0 @@ -function GM_openInTab(_url) { - self.port.emit("GM_openInTab", _url); - return; // Should return the Window object -}; - -function GM_setValue(_name, _value) { - localStorage[_name] = _value; - return; -}; - -function GM_getValue(_name, _default) { - if (localStorage[_name] === null && _default === null) return null; - return (localStorage[_name] || _default); -}; - -function GM_deleteValue(_name) { - localStorage.removeItem(_name); - return; -}; - -function GM_listValues() { - return Object.keys(localStorage); -}; - -function GM_setClipboard(_text) { - self.port.emit("GM_setClipboard", _text); -}; - -//Deprecated -function GM_log(_message) { - console.log(_message); - return; -}; - -function GM_xmlhttpRequest(_details) { - //Ugly hack? Race condition? Memory leak? - _onload = _details.onload; - _context = _details.context; - self.port.emit("GM_xmlhttpRequest", _details); -}; - -self.port.on("callback_GM_xmlhttpRequest", function(_response) { - _response.context = _context; - _onload(_response); -}); - -function GM_addStyle(_css) { - self.port.emit("GM_addStyle", _css); -} - -var GM_info = new Object(); -GM_info.version = '1.15'; -GM_info.scriptWillUpdate = true; - -//To do -function GM_registerMenuCommand(_caption, _commandFunc, _accessKey) { - return; -} - -self.port.on("load-userscript", function(_script) { - eval(_script); -}); diff --git a/builds/xpi/doc/main.md b/builds/xpi/doc/main.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/builds/xpi/icon.png b/builds/xpi/icon.png deleted file mode 100755 index 6b9265ec9..000000000 Binary files a/builds/xpi/icon.png and /dev/null differ diff --git a/builds/xpi/icon64.png b/builds/xpi/icon64.png deleted file mode 100755 index 80b72bf13..000000000 Binary files a/builds/xpi/icon64.png and /dev/null differ diff --git a/builds/xpi/lib/main.js b/builds/xpi/lib/main.js deleted file mode 100644 index 43db1bef7..000000000 --- a/builds/xpi/lib/main.js +++ /dev/null @@ -1,82 +0,0 @@ -var data = require("sdk/self").data; -var Request = require("sdk/request").Request; -var tabs = require("sdk/tabs"); -var system = require("sdk/system"); -if (system.platform !== 'android') { - // Clipboard is not supported on Android - var clipboard = require("sdk/clipboard"); -} -var pageMod = require("sdk/page-mod"); -pageMod.PageMod({ - include: ["*.4chan.org", "*.4cdn.org"], - contentScriptFile: data.url("greaseshim.js"), - contentScriptWhen: "start", - onAttach: function(worker) { - worker.port.emit("load-userscript", data.load("4chan-X.user.js")); - - //GM_openInTab - worker.port.on("GM_openInTab", function(url) { - tabs.open(url); - }); - - //GM_setClipboard - worker.port.on("GM_setClipboard", function(text) { - if (system.platform !== 'android') { - // Clipboard is not supported on Android - clipboard.set(text); - } - }); - - //GM_xmlhttpRequest - worker.port.on("GM_xmlhttpRequest", function(details) { - request = new Object(); - request.url = details.url; - if (details.headers) { - request.headers = details.headers; - if (details.headers["Content-Type"]) {request.contentType = details.headers["Content-Type"]}; - }; - if (details.data) {request.content = encodeURIComponent(details.data)}; - if (details.overrideMimeType) {request.overrideMimeType = details.overrideMimeType}; - - request.onComplete = function(response) { - response.finalUrl = details.url; - response.responseText = response.text; - for (var headerName in response.headers) { - _string = headerName + ": " + response.headers[headerName] + " \n"; - response.responseHeaders += _string; - } - response.readyState = 4; - worker.port.emit("callback_GM_xmlhttpRequest", response); - } - - xhr = Request(request); - - switch(details.method){ - case "GET": - xhr.get(); - break; - case "POST": - xhr.post(); - break; - case "HEAD": - xhr.head(); - break; - case "PUT": - xhr.put(); - break; - default: xhr.get(); - } - }); - - //GM_addStyle - worker.port.on("GM_addStyle", function(css) { - tabs.activeTab.attach({ - contentScript: "var style = document.createElement('style');" + - "style.type = 'text/css';" + - "style.innerHTML = '" + css + "';" + - "document.head.appendChild(style);" - }); - }); - - } -}); \ No newline at end of file diff --git a/builds/xpi/package.json b/builds/xpi/package.json deleted file mode 100644 index 916a4ee38..000000000 --- a/builds/xpi/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "4chanx", - "title": "4chan X", - "id": "72DAF86E-9689-11E3-8BA3-F4B66188709B", - "description": "Adds various features to 4chan.", - "author": "Spittie", - "license": "MIT", - "version": "1.4.1" -} diff --git a/builds/xpi/test/test-main.js b/builds/xpi/test/test-main.js deleted file mode 100644 index 147f98adf..000000000 --- a/builds/xpi/test/test-main.js +++ /dev/null @@ -1,12 +0,0 @@ -var main = require("./main"); - -exports["test main"] = function(assert) { - assert.pass("Unit test running!"); -}; - -exports["test main async"] = function(assert, done) { - assert.pass("async Unit test running!"); - done(); -}; - -require("sdk/test").run(exports); diff --git a/css/style.css b/css/style.css deleted file mode 100644 index 11857b167..000000000 --- a/css/style.css +++ /dev/null @@ -1,1068 +0,0 @@ -/* General */ -.dialog { - box-shadow: 0 1px 2px rgba(0, 0, 0, .15); - border: 1px solid; - display: block; - padding: 0; -} -.field { - background-color: #FFF; - border: 1px solid #CCC; - -moz-box-sizing: border-box; - box-sizing: border-box; - color: #333; - font-family: inherit; - font-size: 13px; - margin: 0; - padding: 2px 4px 3px; - outline: none; - transition: color .25s, border-color .25s, flex .25s; -} -.field::-moz-placeholder, -.field:hover::-moz-placeholder { - color: #AAA !important; -} -.field:hover { - border-color: #999; -} -.field:hover, .field:focus { - color: #000; -} -.field[disabled] { - background-color: #F2F2F2; - color: #888; -} -.field::-webkit-search-decoration { - display: none; -} -.move { - cursor: move; -} -label, .watcher-toggler { - cursor: pointer; -} -a[href="javascript:;"] { - text-decoration: none; -} -.warning { - color: red; -} - -/* 4chan style fixes */ -.opContainer, .op { - display: block !important; -} -.post { - overflow: visible !important; -} -.reply > .file > .fileText { - margin: 0 20px; -} -[hidden] { - display: none !important; -} - -/* fixed, z-index */ -#overlay, -#qp, #ihover, -#updater, #thread-stats, -#navlinks, #header, -#qr { - position: fixed; -} -#overlay { - z-index: 999; -} -#notifications { - z-index: 70; -} -#qp, #ihover { - z-index: 60; -} -#menu { - z-index: 50; -} -#navlinks, #updater, #thread-stats { - z-index: 40; -} -#qr { - z-index: 30; -} -#thread-watcher:hover { - z-index: 20; -} -#header { - z-index: 10; -} -#thread-watcher { - z-index: 5; -} - -/* Header */ -:root.top-header body { - margin-top: 2em; -} -:root.bottom-header body { - margin-bottom: 2em; -} -:root.fourchan-x #navtopright, -:root.fourchan-x #navbotright, -:root.fourchan-x:not(.show-original-top-board-list) #boardNavDesktop, -:root.fourchan-x:not(.show-original-bot-board-list) #boardNavDesktopFoot { - display: none !important; -} -#header { - right: 0; - left: 0; - pointer-events: none; -} -#header.top { - top: 0; -} -#header.bottom { - bottom: 0; -} -#header-bar { - border-width: 0; - display: flex; - padding: 3px; - position: relative; - transition: all .1s .05s ease-in-out; - pointer-events: initial; -} -#header.top #header-bar { - border-bottom-width: 1px; -} -#header.bottom #header-bar { - box-shadow: 0 -1px 2px rgba(0, 0, 0, .15); - border-top-width: 1px; -} -#board-list { - flex: 1; - align-self: center; - text-align: center; -} -#header-bar.autohide:not(:hover) { - box-shadow: none; - transition: all .8s .6s cubic-bezier(.55, .055, .675, .19); -} -#header-bar.scroll:not(:hover) { - transition: -webkit-transform .2s !important; - transition: transform .2s !important; -} -#header.top #header-bar.autohide:not(:hover) { - margin-bottom: -1em; - -webkit-transform: translateY(-100%); - transform: translateY(-100%); -} -#header.bottom #header-bar.autohide:not(:hover) { - -webkit-transform: translateY(100%); - transform: translateY(100%); -} -#header-bar-hitzone { - left: 0; - right: 0; - height: 10px; - position: absolute; -} -#header-bar:not(.autohide) #header-bar-hitzone { - display: none; -} -#header.top #header-bar-hitzone { - bottom: -10px; -} -#header.bottom #header-bar-hitzone { - top: -10px; -} -#header-bar a:not(.entry) { - text-decoration: none; - padding: 1px; -} -.shortcut:not(:last-child)::after { - content: " / "; -} -.brackets-wrap::before { - content: " [ "; -} -.brackets-wrap::after { - content: " ] "; -} - -/* Notifications */ -#notifications { - height: 0; - text-align: center; - pointer-events: initial; -} -#header.bottom #notifications { - position: fixed; - top: 0; - left: 0; - width: 100%; -} -.notification { - color: #FFF; - font-weight: 700; - text-shadow: 0 1px 2px rgba(0, 0, 0, .5); - box-shadow: 0 1px 2px rgba(0, 0, 0, .15); - border-radius: 2px; - margin: 1px auto; - width: 500px; - max-width: 100%; - position: relative; - transition: all .25s ease-in-out; -} -.notification.error { - background-color: hsla(0, 100%, 38%, .9); -} -.notification.warning { - background-color: hsla(36, 100%, 38%, .9); -} -.notification.info { - background-color: hsla(200, 100%, 38%, .9); -} -.notification.success { - background-color: hsla(104, 100%, 38%, .9); -} -.notification a { - color: white; -} -.notification > .close { - padding: 7px; - top: 0; - right: 0; - position: absolute; -} -.message { - -moz-box-sizing: border-box; - box-sizing: border-box; - padding: 6px 20px; - max-height: 200px; - width: 100%; - overflow: auto; -} - -/* Settings */ -:root.fourchan-x body { - -moz-box-sizing: border-box; - box-sizing: border-box; -} -#overlay { - background-color: rgba(0, 0, 0, .5); - display: flex; - position: fixed; - top: 0; - left: 0; - height: 100%; - width: 100%; -} -#fourchanx-settings { - -moz-box-sizing: border-box; - box-sizing: border-box; - box-shadow: 0 0 15px rgba(0, 0, 0, .15); - height: 600px; - max-height: 100%; - width: 900px; - max-width: 100%; - margin: auto; - padding: 3px; - display: flex; - flex-direction: column; -} -#fourchanx-settings > nav { - display: flex; - padding: 2px 2px .5em; -} -#fourchanx-settings > nav a { - text-decoration: underline; -} -#fourchanx-settings > nav a.close { - text-decoration: none; - padding: 0 2px; -} -.sections-list { - flex: 1; -} -.tab-selected { - font-weight: 700; -} -#fourchanx-settings > section { - flex: 1; - overflow: auto; -} -.section-sauce ul, -.section-rice ul { - list-style: none; - margin: 0; - padding: 8px; -} -.section-sauce li, -.section-rice li { - padding-left: 4px; -} -.section-main label { - text-decoration: underline; -} -.section-filter ul, -.section-qr ul { - padding: 0; -} -.section-filter li, -.section-qr li { - margin: 10px 40px; -} -.section-filter textarea { - height: 500px; -} -.section-qr textarea { - height: 200px; -} -.section-sauce textarea { - height: 350px; -} -.section-rice .field[name="boardnav"] { - width: 100%; -} -.section-rice textarea { - height: 150px; -} -.section-archives table { - width: 100%; -} -.section-archives th:not(:first-child) { - width: 30%; -} -.section-archives td { - text-align: center; -} -.section-archives select { - width: 90%; -} -.section-keybinds .field { - font-family: monospace; -} -#fourchanx-settings fieldset { - border: 1px solid; - border-radius: 3px; -} -#fourchanx-settings legend { - font-weight: 700; -} -#fourchanx-settings textarea { - font-family: monospace; - min-width: 100%; - max-width: 100%; -} -#fourchanx-settings code { - color: #000; - background-color: #FFF; - padding: 0 2px; -} -.unscroll { - overflow: hidden; -} - -/* Index */ -:root.index-loading .navLinks, -:root.index-loading .board, -:root.index-loading .pagelist, -:root:not(.catalog-mode) #index-size { - display: none; -} -#nav-links { - display: flex; - align-items: baseline; -} -#index-search { - padding-right: 1.5em; - width: 100px; - transition: color .25s, border-color .25s, width .25s; -} -#index-search:focus, -#index-search[data-searching] { - width: 200px; -} -#index-search-clear { - color: gray; - position: relative; - left: -1.25em; - width: 0; -} -<% if (type === 'crx') { %> -/* ``::-webkit-*'' selectors break selector lists on Firefox. */ -#index-search::-webkit-search-cancel-button, -<% } %> -#index-search:not([data-searching]) + #index-search-clear { - display: none; -} -.summary { - text-decoration: none; -} -.catalog-mode .board { - text-align: center; -} -.catalog-thread { - display: inline-flex; - text-align: left; - flex-direction: column; - align-items: center; - margin: 0 2px 5px; - word-break: break-word; - vertical-align: top; -} -.catalog-small .catalog-thread { - width: 165px; - max-height: 320px; -} -.catalog-large .catalog-thread { - width: 270px; - max-height: 410px; -} -.thumb { - flex-shrink: 0; - position: relative; - background-size: 100% 100%; - background-repeat: no-repeat; - background-position: center; - border-radius: 2px; - box-shadow: 0 0 5px rgba(0, 0, 0, .25); -} -.thumb:not(.deleted-file):not(.no-file) { - min-width: 30px; - min-height: 30px; -} -.thumb.spoiler-file { - background-size: 100px; - width: 100px; - height: 100px; -} -.thumb.deleted-file { - background-size: 127px 13px; - width: 127px; - height: 13px; - padding: 20px 11px; -} -.thumb.no-file { - background-size: 77px 13px; - width: 77px; - height: 13px; - padding: 20px 36px; -} -.thread-icons > img { - width: 1em; - height: 1em; - margin: 0; - vertical-align: text-top; -} -.thumb:not(:hover):not(:focus) > .menu-button:not(.open):not(:focus) > i { - display: none; -} -.thumb > .menu-button { - position: absolute; - top: 0; - right: 0; -} -.thumb > .menu-button > i { - width: 1em; - height: 1em; - padding: 1px; - border-radius: 0 2px 0 2px; - font-size: 14px; - text-align: center; -<% if (type === 'userscript') { %> - line-height: normal; -<% } %> -} -.thread-stats { - flex-shrink: 0; - cursor: help; - font-size: 10px; - font-weight: 700; - margin-top: 2px; -} -.catalog-thread > .subject { - flex-shrink: 0; - font-weight: 700; - line-height: 1; - text-align: center; -} -.catalog-thread > .comment { - flex-shrink: 1; - align-self: stretch; - overflow: hidden; - text-align: center; -} -.thread-info { - position: fixed; - background-color: inherit; - padding: 2px; - border-radius: 2px; - box-shadow: 0 0 5px rgba(0, 0, 0, .25); -} -.thread-info .post { - margin: 0; -} - -/* Announcement Hiding */ -:root.hide-announcement #globalMessage, -:root.hide-announcement-enabled #toggleMsgBtn { - display: none; -} -a.hide-announcement { - float: left; - font-size: 14px; -} - -/* Unread */ -#unread-line { - margin: 0; -} - -/* Thread Updater */ -#updater:not(:hover) { - background: none; - border: none; - box-shadow: none; -} -#updater > .move { - padding: 0 3px; -} -#updater > div:last-child { - text-align: center; -} -#updater input[type="number"] { - width: 4em; -} -#updater:not(:hover) > div:not(.move) { - display: none; -} -#updater input[type="button"] { - width: 100%; -} -.new { - color: limegreen; -} - -/* Thread Watcher */ -#thread-watcher { - max-width: 200px; - min-width: 150px; - padding: 3px; - position: absolute; -} -#thread-watcher > div:first-child { - display: flex; - align-items: center; -} -#thread-watcher .move { - flex: 1; -} -#watcher-status:not(:empty)::before { - content: "("; -} -#watcher-status:not(:empty)::after { - content: ")"; -} -#watched-threads:not(:hover) { - max-height: 150px; - overflow: hidden; -} -#watched-threads div { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -#watched-threads .current { - font-weight: 700; -} -#watched-threads a { - text-decoration: none; -} -#watched-threads .dead-thread a[title] { - text-decoration: line-through; -} - -/* Thread Stats */ -#thread-stats { - background: none; - border: none; - box-shadow: none; -} - -/* Quote */ -.deadlink { - text-decoration: none !important; -} -.backlink.deadlink:not(.forwardlink), -.quotelink.deadlink:not(.forwardlink) { - text-decoration: underline !important; -} -.inlined { - opacity: .5; -} -#qp input, .forwarded { - display: none; -} -.quotelink.forwardlink, -.backlink.forwardlink { - text-decoration: none; - border-bottom: 1px dashed; -} -@supports (text-decoration-style: dashed) or (-moz-text-decoration-style: dashed) { - .quotelink.forwardlink, - .backlink.forwardlink { - text-decoration: underline; - -moz-text-decoration-style: dashed; - text-decoration-style: dashed; - border-bottom: none; - } -} -.filtered { - text-decoration: underline line-through; -} -.inline { - border: 1px solid; - display: table; - margin: 2px 0; -} -.inline .post { - border: 0 !important; - background-color: transparent !important; - display: table !important; - margin: 0 !important; - padding: 1px 2px !important; -} -#qp > .opContainer::after { - content: ''; - clear: both; - display: table; -} -#qp .post { - border: none; - margin: 0; - padding: 2px 2px 5px; -} -#qp img { - max-height: 80vh; - max-width: 50vw; -} -.qphl { - outline: 2px solid rgba(216, 94, 49, .7); -} - -/* File */ -.file-info:hover .fntrunc, -.file-info:not(:hover) .fnfull, -.expanded-image > .post > .file > .fileThumb > img[data-md5], -:not(.expanded-image) > .post > .file > .fileThumb > .full-image { - display: none; -} -.expanding { - opacity: .5; -} -.expanded-image { - clear: both; -} -.expanded-image > .op > .file::after { - content: ''; - clear: both; - display: table; -} -:root.fit-height .full-image { - max-height: 100vh; -} -:root.fit-width .full-image { - max-width: 100%; -} -:root.gecko.fit-width .full-image { - width: 100%; -} -#ihover { - -moz-box-sizing: border-box; - box-sizing: border-box; - max-height: 100%; - max-width: 75%; - padding-bottom: 16px; -} - -/* Index/Reply Navigation */ -#navlinks { - font-size: 16px; - top: 25px; - right: 10px; -} - -/* Filter */ -.opContainer.filter-highlight { - box-shadow: inset 5px 0 rgba(255, 0, 0, .5); -} -.filter-highlight > .reply { - box-shadow: -5px 0 rgba(255, 0, 0, .5); -} -.pinned .thumb, -.filter-highlight .thumb { - border: 2px solid rgba(255, 0, 0, .5); -} - -/* Post Hiding */ -.hide-post-button, -.show-post-button { - font-size: 14px; - line-height: 12px; /* Prevent the floating effect from affecting the thumbnail too */ -} -.opContainer > .show-post-button, -.hide-post-button { - float: left; - margin-right: 3px; -} -.stub input { - display: inline-block; -} - -/* QR */ -:root.hide-original-post-form #postForm, -:root.hide-original-post-form .postingMode, -:root.hide-original-post-form #togglePostForm, -#qr.autohide:not(.has-focus):not(:hover) > form { - display: none; -} -#qr select, #dump-button, .remove, .captcha-img { - cursor: pointer; -} -#qr > div { - min-width: 300px; - display: flex; - align-items: center; -} -#qr .move { - align-self: stretch; - flex: 1; -} -#qr select[data-name=thread] { - margin: 0; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - border: none; - background: none; - font: inherit; -} -#qr option { - color: #000; - background-color: #F7F7F7; -} -#qr .close { - padding: 0 3px; -} -#qr > form { - display: flex; - flex-direction: column; -} -.persona { - display: flex; -} -.persona .field { - flex: 1; - width: 0; -} -.persona .field:hover, -.persona .field:focus { - flex: 3; -} -#dump-button { - background: linear-gradient(#EEE, #CCC); - border: 1px solid #CCC; - margin: 0; - padding: 2px 4px 3px; - outline: none; - width: 30px; -} -#dump-button:hover, -#dump-button:focus { - background: linear-gradient(#FFF, #DDD); -} -#dump-button:active, -.dump #dump-button:not(:hover):not(:focus) { - background: linear-gradient(#CCC, #DDD); -} -:root.gecko #dump-button { - padding: 0; -} -#qr:not(.dump) #dump-list, -#qr:not(.dump) #add-post { - display: none; -} -#dump-list { - counter-reset: qrpreviews; - width: 0; - min-width: 100%; - overflow: hidden; - white-space: nowrap; - position: relative; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; -} -#dump-list:hover { - padding-bottom: 12px; - margin-bottom: -12px; - overflow-x: auto; - z-index: 1; -} -#dump-list::-webkit-scrollbar { - height: 12px; -} -#dump-list::-webkit-scrollbar-thumb { - border: 1px solid; -} -.qr-preview { - background-position: 50% 20%; - background-size: cover; - border: 1px solid #808080; - color: #FFF !important; - font-size: 12px; - -moz-box-sizing: border-box; - box-sizing: border-box; - cursor: move; - display: inline-block; - height: 92px; - width: 92px; - margin: 4px; - padding: 2px; - opacity: .6; - outline: none; - overflow: hidden; - position: relative; - text-shadow: 0 0 2px #000; - transition: opacity .25s ease-in-out; - vertical-align: top; - white-space: pre; -} -.qr-preview:hover, -.qr-preview:focus { - opacity: .9; - color: #FFF !important; -} -.qr-preview#selected { - opacity: 1; -} -.qr-preview::before { - counter-increment: qrpreviews; - content: counter(qrpreviews); - font-weight: 700; - text-shadow: 0 0 3px #000, 0 0 5px #000; - position: absolute; - top: 3px; - right: 3px; -} -.qr-preview.drag { - border-color: red; - border-style: dashed; - opacity: 1; -} -.qr-preview.over { - border-color: #FFF; - border-style: dashed; - opacity: 1; -} -a.remove { - color: #E00 !important; - padding: 1px; -} -.qr-preview > label { - background: rgba(0, 0, 0, .5); - right: 0; - bottom: 0; - left: 0; - position: absolute; - text-align: center; -} -.qr-preview > label > input { - margin: 1px 0; - vertical-align: bottom; -} -#add-post { - align-self: flex-end; - font-size: 20px; - width: 1em; - margin-top: -1em; - text-align: center; - z-index: 1; -} -#qr textarea { - min-height: 160px; - min-width: 100%; - display: block; -} -#qr.has-captcha textarea { - min-height: 120px; -} -.textarea { - position: relative; -} -#char-count { - color: #000; - background: hsla(0, 0%, 100%, .5); - font-size: 8pt; - position: absolute; - bottom: 1px; - right: 1px; - pointer-events: none; -} -#char-count.warning { - color: red; -} -.captcha-img { - background: #FFF; - outline: 1px solid #CCC; - outline-offset: -1px; -} -.captcha-img > img { - display: block; - height: 57px; - width: 300px; -} -#file-n-submit { - display: flex; - align-items: center; -} -#file-n-submit input { - margin: 0; -} -#file-n-submit input[type='submit'] { - order: 1; -} -#file-n-submit.has-file #qr-no-file, -#file-n-submit:not(.has-file) #qr-filename, -#file-n-submit:not(.has-file) #qr-filesize, -#file-n-submit:not(.has-file) #qr-file-spoiler, -#file-n-submit:not(.has-file) #qr-filerm, -#qr-filename:focus ~ #qr-filesize { - display: none; -} -#qr-no-file, -#qr-filename, -#qr-filesize, -#qr-filerm, -#qr-file-spoiler { - margin: 0 1px !important; -} -#qr-no-file { - cursor: default; - flex: 1; -} -#qr-filename { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background: none; - border: none; - color: inherit; - font: inherit; - flex: 1; - width: 0; - padding: 0; - text-overflow: ellipsis; -} -#qr-filesize { - font-size: .8em; -} -#qr-filesize::before { - content: "("; -} -#qr-filesize::after { - content: ")"; -} - -/* Menu */ -.menu-button { - position: relative; -} -@media screen and (resolution: 1dppx) { - .fa-bars { - font-size: 14px; - } - #shortcuts .fa-bars { - vertical-align: -1px; - } -} -#menu { - border-bottom: 0; - display: flex; - margin: 2px 0; - flex-direction: column; - position: absolute; - outline: none; -} -#menu.top { - top: 100%; -} -#menu.bottom { - bottom: 100%; -} -#menu.left { - left: 0; -} -#menu.right { - right: 0; -} -.entry { - cursor: pointer; - outline: none; - padding: 3px 7px; - position: relative; - text-decoration: none; - white-space: nowrap; -} -.entry.disabled { - color: graytext !important; -} -.entry.has-submenu { - padding-right: 20px; -} -.has-submenu::after { - content: ''; - border-left: 6px solid; - border-top: 4px solid transparent; - border-bottom: 4px solid transparent; - display: inline-block; - margin: 4px; - position: absolute; - right: 3px; -} -.has-submenu:not(.focused) > .submenu { - display: none; -} -.submenu { - border-bottom: 0; - display: flex; - flex-direction: column; - position: absolute; - margin: -1px 0; -} -.submenu.top { - top: 0; -} -.submenu.bottom { - bottom: 0; -} -.submenu.left { - left: 100%; -} -.submenu.right { - right: 100%; -} -.entry input { - margin: 0; -} - -/* Other */ -.linkified { - word-break: break-all; -} -.posteruid.painted { - padding: 0 5px; - border-radius: 1em; - font-size: 0.8em; - cursor: pointer; -} diff --git a/json/archives.json b/json/archives.json deleted file mode 100644 index 73b76cd14..000000000 --- a/json/archives.json +++ /dev/null @@ -1,128 +0,0 @@ -[{ - "uid": 0, - "name": "Foolz", - "domain": "archive.foolz.us", - "http": true, - "https": true, - "software": "foolfuuka", - "boards": ["a", "biz", "co", "diy", "gd", "jp", "m", "sci", "sp", "tg", "tv", "v", "vg", "vp", "vr", "wsg"], - "files": ["a", "biz", "gd", "diy", "jp", "m", "sci", "tg", "vg", "vp", "vr", "wsg"] -}, { - "uid": 1, - "name": "NSFW Foolz", - "domain": "nsfw.foolz.us", - "http": true, - "https": true, - "software": "foolfuuka", - "boards": ["u"], - "files": ["u"] -}, { - "uid": 2, - "name": "The Dark Cave", - "domain": "archive.thedarkcave.org", - "http": true, - "https": true, - "software": "foolfuuka", - "boards": ["c", "int", "out", "po"], - "files": ["c", "po"] -}, { - "uid": 3, - "name": "4plebs Archive", - "domain": "archive.4plebs.org", - "http": true, - "https": true, - "software": "foolfuuka", - "boards": ["adv", "hr", "o", "pol", "s4s", "tg", "trv", "tv", "x"], - "files": ["adv", "hr", "o", "pol", "s4s", "tg", "trv", "tv", "x"] -}, { - "uid": 18, - "name": "4plebs Flash Archive", - "domain": "flash.4plebs.org", - "http": true, - "https": true, - "software": "foolfuuka", - "boards": ["f"], - "files": ["f"] -}, { - "uid": 4, - "name": "Nyafuu", - "domain": "archive.nyafuu.org", - "http": true, - "https": true, - "software": "foolfuuka", - "boards": ["c", "e", "w", "wg"], - "files": ["c", "e", "w", "wg"] -}, { - "uid": 5, - "name": "Love is Over", - "domain": "loveisover.me", - "http": true, - "https": true, - "software": "foolfuuka", - "boards": ["d", "i"], - "files": ["d", "i"] -}, { - "uid": 8, - "name": "Rebecca Black Tech", - "domain": "archive.rebeccablacktech.com", - "http": true, - "https": true, - "software": "fuuka", - "boards": ["cgl", "g", "mu", "w"], - "files": ["cgl", "g", "mu", "w"] -}, { - "uid": 9, - "name": "Heinessen", - "domain": "archive.heinessen.com", - "http": true, - "https": false, - "software": "fuuka", - "boards": ["an", "fit", "k", "mlp", "r9k", "toy"], - "files": ["an", "fit", "k", "r9k", "toy"] -}, { - "uid": 10, - "name": "warosu", - "domain": "fuuka.warosu.org", - "http": false, - "https": true, - "software": "fuuka", - "boards": ["3", "biz", "cgl", "ck", "diy", "fa", "g", "ic", "jp", "lit", "sci", "tg", "vr"], - "files": ["3", "biz", "cgl", "ck", "diy", "fa", "ic", "jp", "lit", "sci", "tg", "vr"] -}, { - "uid": 15, - "name": "fgts", - "domain": "fgts.eu", - "http": true, - "https": true, - "software": "foolfuuka", - "boards": ["cm", "hm", "r", "soc", "y"], - "files": ["cm", "hm", "r", "soc", "y"] -}, { - "uid": 16, - "name": "maware", - "domain": "archive.mawa.re", - "http": true, - "https": false, - "software": "foolfuuka", - "boards": ["t"], - "files": ["t"] -}, { - "uid": 17, - "name": "installgentoo.com", - "domain": "chan.installgentoo.com", - "http": true, - "https": false, - "software": "foolfuuka", - "boards": ["g", "t"], - "files": ["g", "t"] -}, { - "uid": 13, - "name": "Foolz Beta", - "domain": "beta.foolz.us", - "http": true, - "https": true, - "withCredentials": true, - "software": "foolfuuka", - "boards": ["a", "biz", "co", "d", "diy", "gd", "jp", "m", "s4s", "sci", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"], - "files": ["a", "biz", "d", "diy", "gd", "jp", "m", "s4s", "sci", "tg", "u", "vg", "vp", "vr", "wsg"] -}] diff --git a/src/Archive/archives.json b/src/Archive/archives.json index 8918ef6c0..a5d3039bb 100644 --- a/src/Archive/archives.json +++ b/src/Archive/archives.json @@ -95,8 +95,8 @@ "http": true, "https": true, "software": "foolfuuka", - "boards": ["cm", "hm", "r", "soc", "y"], - "files": ["cm", "hm", "r", "soc", "y"] + "boards": ["cm", "h", "hc", "hm", "r", "s", "soc", "y"], + "files": ["cm", "h", "hc", "hm", "r", "s", "soc", "y"] }, { "uid": 16, "name": "maware", diff --git a/src/General/Config.coffee b/src/General/Config.coffee index 7a0b79695..8aa4d1eaf 100644 --- a/src/General/Config.coffee +++ b/src/General/Config.coffee @@ -2,7 +2,7 @@ Config = main: 'Miscellaneous': 'JSON Navigation' : [ - true + false 'Use JSON for loading the Board Index and Threads. Also allows searching and sorting the board index and infinite scolling.' ] 'Catalog Links': [ @@ -69,6 +69,10 @@ Config = false 'Indicate spoilers if Remove Spoilers is enabled, or make the text appear hovered if Remove Spoiler is disabled.' ] + 'Show Support Message': [ + true + 'Warn if your browser is unsupported. 4chan X may not operate correctly on unsupported browser versions.' + ] 'Linkification': 'Linkify': [ @@ -131,6 +135,10 @@ Config = true 'Adds a simple and cute image gallery.' ] + 'PDF in Gallery': [ + false + 'Show PDF files in gallery.' + ] 'Sauce': [ true 'Add sauce links to images.' @@ -141,15 +149,19 @@ Config = ] 'Replace GIF': [ false - 'Replace thumbnail of gifs with its actual image.' - ] - 'Replace PNG': [ - false - 'Replace pngs.' + 'Replace gif thumbnails with the actual image.' ] 'Replace JPG': [ false - 'Replace jpgs.' + 'Replace jpg thumbnails with the actual image.' + ] + 'Replace PNG': [ + false + 'Replace png thumbnails with the actual image.' + ] + 'Replace WEBM': [ + false + 'Replace webm thumbnails with the actual webm video. Probably will degrade browser performance ;)' ] 'Image Prefetching': [ false @@ -165,15 +177,19 @@ Config = ] 'Autoplay': [ true - 'Videos begin playing immediately when opened inline.' + 'Videos begin playing immediately when opened.' ] 'Show Controls': [ true - 'Show native seek and volume controls on videos. Contract videos when dragged to the left.' + 'Show controls on videos expanded inline. Turn this off if you want to contract videos by clicking on them.' ] 'Allow Sound': [ true - 'Allow sound in inline videos.' + 'Allow sound in videos.' + ] + 'Loop in New Tab': [ + true + 'Loop videos opened in their own tabs, and apply settings for inline expanded videos to them.' ] 'Menu': @@ -366,6 +382,7 @@ Config = 'Add \'(Cross-thread)\' to cross-threads quotes.' 'Highlights own posts if Quote Markers are enabled.' ] + imageExpansion: 'Fit width': [ false @@ -379,6 +396,10 @@ Config = true 'Expand all images along with spoilers.' ] + 'Expand videos': [ + false + 'Expand all images also expands videos (no autoplay).' + ] 'Expand from here': [ false 'Expand all images only from current position to thread end.' diff --git a/src/General/Get.coffee b/src/General/Get.coffee index 3e7367753..3e7cdda7f 100755 --- a/src/General/Get.coffee +++ b/src/General/Get.coffee @@ -47,7 +47,7 @@ Get = # get all their backlinks. posts.forEach (qPost) -> if fullID in qPost.quotes - handleQuotes qPost, 'quotelinks' + handleQuotes qPost, 'quotelinks' # Second: # If we have quote backlinks: @@ -62,6 +62,7 @@ Get = quotelinks.filter (quotelink) -> {boardID, postID} = Get.postDataFromLink quotelink boardID is post.board.ID and postID is post.ID + postClone: (boardID, threadID, postID, root, context) -> if post = g.posts["#{boardID}.#{postID}"] Get.insert post, root, context @@ -80,7 +81,7 @@ Get = insert: (post, root, context) -> # Stop here if the container has been removed while loading. return unless root.parentNode - clone = post.addClone context + clone = post.addClone context, ($.hasClass root, 'dialog') Clone.callbacks.execute [clone] # Get rid of the side arrows/stubs. @@ -104,6 +105,7 @@ Get = $.cache url, -> Get.archivedPost @, boardID, postID, root, context , + responseType: 'json' withCredentials: url.archive.withCredentials else $.addClass root, 'warning' diff --git a/src/General/Header.coffee b/src/General/Header.coffee index 893bfc293..4fde28582 100644 --- a/src/General/Header.coffee +++ b/src/General/Header.coffee @@ -362,6 +362,10 @@ Header = bottom -= clientHeight - headRect.bottom + headRect.height - 10 bottom + isNodeVisible: (node) -> + {height} = node.getBoundingClientRect() + Header.getTopOf(node) + height >= 0 and Header.getBottomOf(node) + height >= 0 + isHidden: -> {top} = Header.bar.getBoundingClientRect() if Conf['Bottom header'] diff --git a/src/General/Index.coffee b/src/General/Index.coffee index 4d8241077..a402c8f0c 100644 --- a/src/General/Index.coffee +++ b/src/General/Index.coffee @@ -171,9 +171,9 @@ Index = scroll: -> return if Index.req or Conf['Index Mode'] isnt 'infinite' or (window.scrollY <= doc.scrollHeight - (300 + window.innerHeight)) or g.VIEW is 'thread' - Index.pageNum = (Index.pageNum or Index.getCurrentPage()) + 1 # Avoid having to pushState to keep track of the current page + Index.currentPage = (Index.currentPage or Index.getCurrentPage()) + 1 # Avoid having to pushState to keep track of the current page - return Index.endNotice() if Index.pageNum >= Index.pagesNum + return Index.endNotice() if Index.currentPage >= Index.pagesNum Index.buildIndex true @@ -419,8 +419,8 @@ Index = Header.scrollToIfNeeded Index.navLinks getCurrentPage: -> - if Conf['Index Mode'] is 'infinite' and Index.pageNum - return Index.pageNum + if Conf['Index Mode'] is 'infinite' and Index.currentPage + return Index.currentPage +window.location.pathname.split('/')[2] userPageNav: (pageNum) -> @@ -469,8 +469,8 @@ Index = $.add pagesRoot, nodes Index.togglePagelist() - setPage: (pageNum) -> - pageNum or= Index.getCurrentPage() + setPage: (pageNum = Index.getCurrentPage()) -> + Index.currentPage = pageNum maxPageNum = Index.getMaxPageNum() pagesRoot = $ '.pages', Index.pagelist # Previous/Next buttons @@ -513,7 +513,7 @@ Index = return unless d.readyState is 'loading' or Index.root.parentElement $.replace $('.board'), Index.root - delete Index.pageNum + Index.currentPage = 0 Index.req?.abort() Index.notice?.close() @@ -564,10 +564,14 @@ Index = Navigate.title() try + pageNum or= 0 if req.status is 200 Index.parse req.response, pageNum else if req.status is 304 - Index.pageNav pageNum or 0 + if Index.currentPage is pageNum + Index.buildIndex() + else + Index.pageNav pageNum catch err c.error "Index failure: #{err.message}", err.stack # network error or non-JSON content for example. @@ -588,7 +592,7 @@ Index = Index.parseThreadList pages Index.buildThreads() Index.sort() - if pageNum? + if pageNum? and Index.currentPage isnt pageNum Index.pageNav pageNum return Index.buildIndex() @@ -752,16 +756,12 @@ Index = buildIndex: (infinite) -> {sortedThreads} = Index + nodes = [] switch Conf['Index Mode'] when 'paged', 'infinite' pageNum = Index.getCurrentPage() - if pageNum > Index.getMaxPageNum() - # Go to the last available page if we were past the limit. - Index.pageNav Index.getMaxPageNum() - return threadsPerPage = Index.getThreadsNumPerPage() - nodes = [] threads = [] i = threadsPerPage * pageNum max = i + threadsPerPage @@ -777,8 +777,7 @@ Index = nodes = Index.buildCatalogViews() else - nodes = [] - i = 0 + i = 0 while thread = sortedThreads[i++] nodes.push thread.OP.nodes.root.parentNode, $.el 'hr' Index.buildReplies thread @@ -799,9 +798,11 @@ Index = unless Index.searchInput.dataset.searching Index.searchInput.dataset.searching = 1 Index.pageBeforeSearch = Index.getCurrentPage() - pageNum = 0 + Index.setPage pageNum = 0 else - pageNum = Index.getCurrentPage() + unless Conf['Index Mode'] is 'infinite' + pageNum = Index.getCurrentPage() + else return unless Index.searchInput.dataset.searching pageNum = Index.pageBeforeSearch @@ -832,7 +833,6 @@ Index = filtered.push thread if Index.searchMatch thread, keywords Index.sortedThreads = filtered - searchMatch: (thread, keywords) -> {info, file} = thread.OP text = [] diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 63395b6fa..983981c07 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -83,6 +83,11 @@ Main = Report.init() return when 'i.4cdn.org' + if Conf['Loop in New Tab'] and video = $ 'video' + Video.configure video + $.on video, 'click', -> + if !video.controls + if video.paused then video.play() else video.pause() $.ready -> if Conf['404 Redirect'] and d.title in ['4chan - Temporarily Offline', '4chan - 404 Not Found'] Redirect.init() @@ -204,7 +209,7 @@ Main = <% if (type === 'userscript') { %> test = $.el 'span' test.classList.add 'a', 'b' - if test.className isnt 'a b' + if test.className isnt 'a b' and Conf['Show Support Message'] new Notice 'warning', "Your version of Firefox is outdated (v<%= meta.min.firefox %> minimum) and <%= meta.name %> may not operate correctly.", 30 GMver = GM_info.version.split '.' diff --git a/src/General/Navigate.coffee b/src/General/Navigate.coffee index f116b97a7..36b701ede 100644 --- a/src/General/Navigate.coffee +++ b/src/General/Navigate.coffee @@ -115,11 +115,15 @@ Navigate = index: -> delete g.THREADID Index.cb.toggleCatalogMode() if Conf['Index Mode'] is 'catalog' + QR.posts[0]?.thread = 'new' thread: -> $.rmClass doc, 'catalog-mode' if Conf['Index Mode'] is 'catalog' + QR.posts[0]?.thread = g.THREADID }[view]() + QR.status() # Re-enable the QR in the case of a 404'd thread or something. + g.VIEW = view updateBoard: (boardID) -> diff --git a/src/General/Settings.coffee b/src/General/Settings.coffee index b1563cb11..1a6e450fe 100755 --- a/src/General/Settings.coffee +++ b/src/General/Settings.coffee @@ -3,6 +3,7 @@ Settings = # Appchan X settings link el = $.el 'a', className: 'settings-link' + title: 'Appchan X Settings' href: 'javascript:;' textContent: 'Settings' $.on el, 'click', @open diff --git a/src/General/UI.coffee b/src/General/UI.coffee index 0e9ebf418..7a2c512a0 100755 --- a/src/General/UI.coffee +++ b/src/General/UI.coffee @@ -309,7 +309,7 @@ UI = do -> $.off d, 'mouseup', @up $.set "#{@id}.position", @style.cssText - hoverstart = ({root, el, latestEvent, endEvents, asapTest, cb, offsetX, offsetY}) -> + hoverstart = ({root, el, latestEvent, endEvents, asapTest, cb, offsetX, offsetY, noRemove}) -> o = { root el @@ -322,6 +322,7 @@ UI = do -> clientWidth: doc.clientWidth offsetX: offsetX or 45 offsetY: offsetY or -120 + noRemove } o.hover = hover.bind o o.hoverend = hoverend.bind o @@ -338,7 +339,7 @@ UI = do -> $.on root, 'mousemove', o.hover <% if (type === 'userscript') { %> # Workaround for https://github.com/MayhemYDG/4chan-x/issues/377 - o.workaround = (e) -> o.hoverend() unless root.contains e.target + o.workaround = (e) -> o.hoverend(e) unless root.contains e.target $.on doc, 'mousemove', o.workaround <% } %> diff --git a/src/General/css/style.css b/src/General/css/style.css index 28bc9791d..67089d6a8 100755 --- a/src/General/css/style.css +++ b/src/General/css/style.css @@ -223,7 +223,7 @@ div.center:not(.ad-cnt) { height: 10px; position: absolute; } -:root:not(.autohide) #scroll-marker { +:root #header-bar:not(.autohide) #scroll-marker { pointer-events: none; } #header-bar #scroll-marker { @@ -824,20 +824,26 @@ span.hide-announcement { /* File */ .fileText:hover .fntrunc, .fileText:not(:hover) .fnfull, -.expanded-image > .post > .file > .fileThumb > img[data-md5], -:not(.expanded-image) > .post > .file .full-image { +.expanded-image > .post > .file > .fileThumb > video[data-md5], +.expanded-image > .post > .file > .fileThumb > img[data-md5] { display: none; } +.full-image:not(#ihover) { + display: none; +} +.expanded-image > .post > .file > .fileThumb > .full-image:not(#ihover) { + display: inline; +} .expanding { opacity: .5; } -:root.fit-height .full-image { +:root.fit-height .full-image:not(#ihover) { max-height: 100vh; } -:root.fit-width .full-image { +:root.fit-width .full-image:not(#ihover) { max-width: 100%; } -:root.gecko.fit-width .full-image { +:root.gecko.fit-width .full-image:not(#ihover) { width: 100%; } #ihover { @@ -1459,34 +1465,44 @@ div.boardTitle { /* Flex > Non-Flex child max-width and overflow fix (Firefox only?) */ width: 1%; } -:root:not(.gal-fit-height) .gal-image { +:root:not(.gal-fit-height):not(.gal-pdf) .gal-image { overflow-y: scroll !important; } -:root:not(.gal-fit-width) .gal-image { +:root:not(.gal-fit-width):not(.gal-pdf) .gal-image { overflow-x: scroll !important; } .gal-image a { margin: auto; line-height: 0; } +:root.gal-pdf .gal-image a { + width: 100%; + height: 100%; +} +.gal-fit-width .gal-image video, .gal-fit-width .gal-image img { max-width: 100%; } +.gal-fit-height .gal-image video, .gal-fit-height .gal-image img { - /* - Chrome doesn't support viewpoint units in calc() - http://bugs.chromium.org/168840 - "It looks like the original author of viewport units in WebKit is not coming back to fix this stuff." - Well, fuck. - */ max-height: 95vh; - max-height: calc(100vh - 25px); +} +.gal-image iframe { + width: 100%; + height: 100%; } .gal-buttons { font-size: 2em; - margin-right: 10px; + margin-right: 3px; + padding-left: 7px; + padding-right: 7px; top: 5px; } +.gal-pdf .gal-buttons { + top: 40px; + background: rgba(0,0,0,0.6) !important; + border-radius: 3px; +} .gal-buttons i { vertical-align: baseline; border-top-width: .4em; @@ -1506,12 +1522,12 @@ div.boardTitle { .gal-name, .gal-count { position: fixed; - right: 178px; + right: 195px; } .gal-hide-thumbnails .gal-buttons, .gal-hide-thumbnails .gal-count, .gal-hide-thumbnails .gal-name { - right: 28px; + right: 44px; } .gal-name { bottom: 6px; @@ -1526,6 +1542,10 @@ div.boardTitle { .gal-buttons .menu-button:hover { color: rgb(95, 95, 101) !important; } +:root.gal-pdf .gal-close:hover, +:root.gal-pdf .gal-buttons .menu-button:hover { + color: rgb(204, 204, 204) !important; +} .gal-count { bottom: 27px; background: rgba(0,0,0,0.6) !important; @@ -1533,21 +1553,21 @@ div.boardTitle { padding: 1px 5px 2px 5px; color: #ffffff !important; } -:root:not(.gal-fit-width) .gal-name { +:root:not(.gal-fit-width):not(.gal-pdf) .gal-name { bottom: 23px !important; } -:root:not(.gal-fit-width) .gal-count { +:root:not(.gal-fit-width):not(.gal-pdf) .gal-count { bottom: 44px !important; } -:root:not(.gal-fit-height):not(.gal-hide-thumbnails) .gal-buttons, -:root:not(.gal-fit-height):not(.gal-hide-thumbnails) .gal-name, -:root:not(.gal-fit-height):not(.gal-hide-thumbnails) .gal-count { - right: 195px !important; +:root.gal-fit-height:not(.gal-pdf):not(.gal-hide-thumbnails) .gal-buttons, +:root.gal-fit-height:not(.gal-pdf):not(.gal-hide-thumbnails) .gal-name, +:root.gal-fit-height:not(.gal-pdf):not(.gal-hide-thumbnails) .gal-count { + right: 178px !important; } -:root.gal-hide-thumbnails:not(.gal-fit-height) .gal-buttons, -:root.gal-hide-thumbnails:not(.gal-fit-height) .gal-name, -:root.gal-hide-thumbnails:not(.gal-fit-height) .gal-count { - right: 44px !important; +:root.gal-hide-thumbnails:.gal-fit-height:not(.gal-pdf) .gal-buttons, +:root.gal-hide-thumbnails:.gal-fit-height:not(.gal-pdf) .gal-name, +:root.gal-hide-thumbnails:.gal-fit-height:not(.gal-pdf) .gal-count { + right: 28px !important; } @media screen and (resolution: 1dppx) { .fa-bars { diff --git a/src/General/html/Settings/Settings.html b/src/General/html/Settings/Settings.html index 4388940a0..68fa74330 100755 --- a/src/General/html/Settings/Settings.html +++ b/src/General/html/Settings/Settings.html @@ -13,4 +13,4 @@
-
\ No newline at end of file +
diff --git a/img/changelog/3.16.0/0.png b/src/General/img/changelog/1.6.0.png similarity index 100% rename from img/changelog/3.16.0/0.png rename to src/General/img/changelog/1.6.0.png diff --git a/src/General/lib/clone.class b/src/General/lib/clone.class index 1d71dabb6..6c686a6c1 100755 --- a/src/General/lib/clone.class +++ b/src/General/lib/clone.class @@ -1,11 +1,14 @@ class Clone extends Post - constructor: (@origin, @context) -> + constructor: (@origin, @context, contractThumb) -> for key in ['ID', 'fullID', 'board', 'thread', 'info', 'quotes', 'isReply'] # Copy or point to the origin's key value. @[key] = origin[key] {nodes} = origin - root = nodes.root.cloneNode true + root = if contractThumb + @cloneWithoutVideo nodes.root + else + nodes.root.cloneNode true post = $ '.post', root info = $ '.postInfo', post @nodes = @@ -56,6 +59,29 @@ class Clone extends Post @file.thumb = $ 'img[data-md5]', file @file.fullImage = $ '.full-image', file + # Contract thumbnails in quote preview + if contractThumb + $.rmClass root, 'expanded-image' + $.rmClass @file.thumb, 'expanding' + @file.isExpanded = $.hasClass root, 'expanded-image' + + # Remove any #ihover ID + @file.fullImage?.removeAttribute 'id' + + # Remove video controls. + ($ '.video-controls', @file.text)?.remove() + @isDead = true if origin.isDead @isClone = true root.dataset.clone = origin.clones.push(@) - 1 + + cloneWithoutVideo: (node) -> + if node.tagName is 'VIDEO' + [] + else if node.nodeType is Node.ELEMENT_NODE and $ 'video', node + clone = node.cloneNode false + $.add clone, @cloneWithoutVideo child for child in node.childNodes + clone + else + node.cloneNode true + diff --git a/src/General/lib/post.class b/src/General/lib/post.class index 729d3abd5..ab3b89750 100755 --- a/src/General/lib/post.class +++ b/src/General/lib/post.class @@ -290,8 +290,8 @@ class Post @thread.posts.rm @ @board.posts.rm @ - addClone: (context) -> - new Clone @, context + addClone: (context, contractThumb) -> + new Clone @, context, contractThumb rmClone: (index) -> @clones.splice index, 1 diff --git a/src/General/meta/latest.js b/src/General/meta/latest.js deleted file mode 100755 index d4611d0c3..000000000 --- a/src/General/meta/latest.js +++ /dev/null @@ -1 +0,0 @@ -postMessage({version:'<%= version %>'},'*') diff --git a/src/Images/AutoGIF.coffee b/src/Images/AutoGIF.coffee deleted file mode 100644 index 70acc351c..000000000 --- a/src/Images/AutoGIF.coffee +++ /dev/null @@ -1,34 +0,0 @@ -AutoGIF = - init: -> - return if !Conf['Auto-GIF'] or g.BOARD.ID in ['gif', 'wsg'] - - Post.callbacks.push - name: 'Auto-GIF' - cb: @node - CatalogThread.callbacks.push - name: 'Auto-GIF' - cb: @catalogNode - node: -> - return if @isClone or @isHidden or @thread.isHidden or !@file?.isImage - {thumb, URL} = @file - return unless /gif$/.test(URL) and !/spoiler/.test thumb.src - if @file.isSpoiler - # Revealed spoilers do not have height/width set, this fixes auto-gifs dimensions. - {style} = thumb - style.maxHeight = style.maxWidth = if @isReply then '125px' else '250px' - AutoGIF.replaceThumbnail thumb, URL - catalogNode: -> - {OP} = @thread - return unless OP.file?.isImage - {URL} = OP.file - return unless /gif$/.test URL - AutoGIF.replaceThumbnail @nodes.thumb, URL, true - replaceThumbnail: (thumb, URL, isBackground) -> - gif = $.el 'img' - $.on gif, 'load', -> - # Replace the thumbnail once the GIF has finished loading. - if isBackground - thumb.style.backgroundImage = "url(#{URL})" - else - thumb.src = URL - gif.src = URL diff --git a/src/Images/Gallery.coffee b/src/Images/Gallery.coffee index d892e3771..aee0c66e8 100644 --- a/src/Images/Gallery.coffee +++ b/src/Images/Gallery.coffee @@ -15,10 +15,10 @@ Gallery = Post.callbacks.push name: 'Gallery' - cb: @node + cb: @node node: -> - return unless @file?.isImage + return unless @file if Gallery.nodes Gallery.generateThumb $ '.file', @nodes.root Gallery.nodes.total.textContent = Gallery.images.length @@ -33,23 +33,23 @@ Gallery = nodes.el = dialog = $.el 'div', id: 'a-gallery' innerHTML: """ -
- - \uf107 - \uf00d - - - - / - -
-
- -
-
-
-
-""" +
+ + + × + + + + / + +
+
+ +
+
+
+
+ """ nodes[key] = $ value, dialog for key, value of { frame: '.gal-image' @@ -65,12 +65,11 @@ Gallery = nodes.menu = new UI.Menu 'gallery' {cb} = Gallery - $.on nodes.frame, 'click', cb.blank - $.on nodes.current, 'click', cb.download - $.on nodes.next, 'click', cb.next - $.on ($ '.gal-prev', dialog), 'click', cb.prev - $.on ($ '.gal-next', dialog), 'click', cb.next - $.on ($ '.gal-close', dialog), 'click', cb.close + $.on nodes.frame, 'click', cb.blank + $.on nodes.next, 'click', cb.advance + $.on $('.gal-prev', dialog), 'click', cb.prev + $.on $('.gal-next', dialog), 'click', cb.next + $.on $('.gal-close', dialog), 'click', cb.close $.on menuButton, 'click', (e) -> nodes.menu.toggle e, @, g @@ -86,12 +85,7 @@ Gallery = $.on d, 'keydown', cb.keybinds $.off d, 'keydown', Keybinds.keydown - - i = 0 - files = $$ '.post .file' - while file = files[i++] - continue if $ '.fileDeletedRes, .fileDeleted', file - Gallery.generateThumb file + Gallery.generateThumb file for file, i in $$ '.post .file' when !$ '.fileDeletedRes, .fileDeleted', file $.add d.body, dialog nodes.thumbs.scrollTop = 0 @@ -107,16 +101,22 @@ Gallery = generateThumb: (file) -> post = Get.postFromNode file + return unless post.file and (post.file.isImage or post.file.isVideo or Conf['PDF in Gallery']) title = ($ '.fileText a', file).textContent - thumb = post.file.thumb.parentNode.cloneNode true - if dupe = $ 'img + img', thumb - $.rm dupe - thumb.className = 'gal-thumb' - thumb.title = title - thumb.dataset.id = Gallery.images.length - thumb.dataset.post = $('a[title="Highlight this post"]', post.nodes.info).href - thumb.firstElementChild.style.cssText = '' + thumb = $.el 'a', + className: 'gal-thumb' + href: post.file.URL + target: '_blank' + title: title + + thumb.dataset.id = Gallery.images.length + thumb.dataset.post = $('a[title="Highlight this post"]', post.nodes.info).href + thumb.dataset.isVideo = true if post.file.isVideo + + thumbImg = post.file.thumb.cloneNode false + thumbImg.style.cssText = '' + $.add thumb, thumbImg $.on thumb, 'click', Gallery.cb.open @@ -130,8 +130,10 @@ Gallery = cb = switch key when 'Esc', Conf['Open Gallery'] Gallery.cb.close - when 'Right', 'Enter' + when 'Right' Gallery.cb.next + when 'Enter' + Gallery.cb.advance when 'Left', '' Gallery.cb.prev @@ -150,15 +152,20 @@ Gallery = $.rmClass el, 'gal-highlight' if el = $ '.gal-highlight', nodes.thumbs $.addClass @, 'gal-highlight' - img = $.el 'img', + elType = if @dataset.isVideo then 'video' else if /\.pdf$/.test(@href) then 'iframe' else 'img' + $[if elType is 'iframe' then 'addClass' else 'rmClass'] nodes.el, 'gal-pdf' + + file = $.el elType, src: name.href = @href title: name.download = name.textContent = @title - $.extend img.dataset, @dataset - $.replace nodes.current, img + $.extend file.dataset, @dataset + nodes.current.pause?() + $.replace nodes.current, file + Video.configure file if @dataset.isVideo nodes.count.textContent = +@dataset.id + 1 - nodes.current = img - nodes.frame.scrollTop = 0 + nodes.current = file + nodes.frame.scrollTop = 0 nodes.next.focus() # Scroll @@ -169,9 +176,9 @@ Gallery = return if top < 0 nodes.thumbs.scrollTop += top - - $.on img, 'error', -> - Gallery.cb.error img, thumb + + $.on file, 'error', -> + Gallery.cb.error file, thumb image: (e) -> e.preventDefault() @@ -183,24 +190,20 @@ Gallery = delete post.file.fullImage src = @src.split '/' - if src[2] is 'images.4chan.org' + if src[2] is 'i.4cdn.org' URL = Redirect.to 'file', boardID: src[3] filename: src[5] if URL thumb.href = URL return unless Gallery.nodes.current is img - revived = $.el 'img', - src: URL - title: img.title - $.extend revived.dataset, img.dataset - $.replace img, revived + img.src = URL return if g.DEAD or post.isDead or post.file.isDead return # XXX CORS for images.4chan.org WHEN? - $.ajax "//api.4chan.org/#{post.board}/res/#{post.thread}.json", onload: -> + $.ajax "//a.4cdn.org/#{post.board}/res/#{post.thread}.json", onload: -> return if @status isnt 200 i = 0 {posts} = @response @@ -211,12 +214,22 @@ Gallery = if postObj.filedeleted post.kill true - prev: -> Gallery.cb.open.call Gallery.images[+Gallery.nodes.current.dataset.id - 1] - next: -> Gallery.cb.open.call Gallery.images[+Gallery.nodes.current.dataset.id + 1] - toggle: -> (if Gallery.nodes then Gallery.cb.close else Gallery.build)() + prev: -> Gallery.cb.open.call Gallery.images[+Gallery.nodes.current.dataset.id - 1] + next: -> Gallery.cb.open.call Gallery.images[+Gallery.nodes.current.dataset.id + 1] + toggle: -> (if Gallery.nodes then Gallery.cb.close else Gallery.build)() blank: (e) -> Gallery.cb.close() if e.target is @ + advance: -> + if Gallery.nodes.current.controls then return + if Gallery.nodes.current.paused then return Gallery.nodes.current.play() + Gallery.cb.next() + + pause: -> + {current} = Gallery.nodes + current[if current.paused then 'play' else 'pause']() if current.nodeType is 'VIDEO' + close: -> + Gallery.nodes.current.pause?() $.rm Gallery.nodes.el delete Gallery.nodes d.body.style.overflow = '' diff --git a/src/Images/ImageExpand.coffee b/src/Images/ImageExpand.coffee index 68798a7f4..f99d6cfd2 100644 --- a/src/Images/ImageExpand.coffee +++ b/src/Images/ImageExpand.coffee @@ -1,6 +1,6 @@ ImageExpand = init: -> - return if !Conf['Image Expansion'] + return if g.VIEW is 'catalog' or !Conf['Image Expansion'] @EAI = $.el 'a', id: 'img-controls' @@ -17,51 +17,66 @@ ImageExpand = cb: @node node: -> - return unless @file and (@file.isImage or @file.isVideo) + return unless @file?.isImage or @file?.isVideo {thumb} = @file $.on thumb.parentNode, 'click', ImageExpand.cb.toggle - if @isClone and $.hasClass thumb, 'expanding' - # If we clone a post where the image is still loading, - # make it loading in the clone too. - ImageExpand.contract @ - ImageExpand.expand @ + if @isClone + if $.hasClass thumb, 'expanding' + # If we clone a post where the image is still loading, + # make it loading in the clone too. + ImageExpand.contract @ + ImageExpand.expand @ + + else if @file.isExpanded and @file.isVideo + clone = @ + ImageExpand.setupVideoControls clone + unless clone.origin.file.fullImage.paused + $.queueTask -> Video.start clone.file.fullImage + return - if ImageExpand.on and !@isHidden and (Conf['Expand spoilers'] or !@file.isSpoiler) - ImageExpand.expand @ + + else if ImageExpand.on and !@isHidden and + (Conf['Expand spoilers'] or !@file.isSpoiler) and + (Conf['Expand videos'] or !@file.isVideo) + ImageExpand.expand @, null, true cb: toggle: (e) -> return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0 + post = Get.postFromNode @ + return if post.file.isExpanded and post.file.fullImage?.controls e.preventDefault() - ImageExpand.toggle Get.postFromNode @ + ImageExpand.toggle post toggleAll: -> $.event 'CloseMenu' + toggle = (post) -> + {file} = post + return unless file and (file.isImage or file.isVideo) and doc.contains post.nodes.root + if ImageExpand.on and + (!Conf['Expand spoilers'] and file.isSpoiler or + !Conf['Expand videos'] and file.isVideo or + Conf['Expand from here'] and Header.getTopOf(file.thumb) < 0) + return + $.queueTask func, post + if ImageExpand.on = $.hasClass ImageExpand.EAI, 'expand-all-shortcut' ImageExpand.EAI.className = 'contract-all-shortcut a-icon' ImageExpand.EAI.title = 'Contract All Images' - func = ImageExpand.expand + func = (post) -> ImageExpand.expand post, null, true else ImageExpand.EAI.className = 'expand-all-shortcut a-icon' ImageExpand.EAI.title = 'Expand All Images' func = ImageExpand.contract g.posts.forEach (post) -> - for post in [post].concat post.clones - {file} = post - return unless file and (file.isImage or file.isVideo) and doc.contains post.nodes.root - if ImageExpand.on and !post.isHidden and - (!Conf['Expand spoilers'] and file.isSpoiler or - Conf['Expand from here'] and Header.getTopOf(file.thumb) < 0) - return - $.queueTask func, post + toggle post for post in [post, post.clones...] return setFitness: -> (if @checked then $.addClass else $.rmClass) doc, @name.toLowerCase().replace /\s+/g, '-' toggle: (post) -> - {thumb} = post.file - unless post.file.isExpanded or $.hasClass thumb, 'expanding' + unless post.file.isExpanded or $.hasClass post.file.thumb, 'expanding' ImageExpand.expand post return @@ -90,87 +105,94 @@ ImageExpand = ImageExpand.contract post contract: (post) -> + {thumb} = post.file + if post.file.isVideo and video = post.file.fullImage + video.pause() + TrashQueue.add video, post + thumb.parentNode.href = video.src + thumb.parentNode.target = '_blank' + for eventName, cb of ImageExpand.videoCB + $.off video, eventName, cb + $.rm post.file.videoControls + delete post.file.videoControls $.rmClass post.nodes.root, 'expanded-image' - $.rmClass post.file.thumb, 'expanding' + $.rmClass thumb, 'expanding' post.file.isExpanded = false - post.file.fullImage.pause() if post.file.isVideo - post.file.videoControls?.map($.rm) - delete post.file.videoControls - expand: (post, src) -> + expand: (post, src, disableAutoplay) -> # Do not expand images of hidden/filtered replies, or already expanded pictures. {thumb, isVideo} = post.file return if post.isHidden or post.file.isExpanded or $.hasClass thumb, 'expanding' $.addClass thumb, 'expanding' - if post.file.fullImage + if el = post.file.fullImage # Expand already-loaded/ing picture. - $.asap (-> (file = post.file.fullImage) and (file.videoHeight or file.naturalHeight)), -> - ImageExpand.completeExpand post - return - file = - $.el (if post.file.isImage then 'img' else 'video'), + TrashQueue.remove el + else + el = post.file.fullImage = $.el (if isVideo then 'video' else 'img'), className: 'full-image' - src: src or post.file.URL - post.file.fullImage = file - if isVideo - file.loop = true - file.controls = Conf['Show Controls'] - $.on file, 'error', ImageExpand.error - $.asap (-> (file = post.file.fullImage) and (file.videoHeight or file.naturalHeight)), -> + $.on el, 'error', ImageExpand.error + el.src = src or post.file.URL if isVideo - # XXX Firefox doesn't seem to size videos correctly? - file.style.maxHeight = file.videoHeight + "px" - file.style.maxWidth = file.videoWidth + "px" - ImageExpand.completeExpand post - $.after (if file.controls then thumb.parentNode else thumb), file + el.loop = true + $.after thumb, el unless el is thumb.nextSibling + $.asap (-> el.videoHeight or el.naturalHeight), -> + ImageExpand.completeExpand post, disableAutoplay - completeExpand: (post) -> - {thumb} = post.file - return unless $.hasClass thumb, 'expanding' # contracted before the image loaded - post.file.isExpanded = true - ImageExpand.setupVideo post if post.file.isVideo - $.addClass post.nodes.root, 'expanded-image' - $.rmClass post.file.thumb, 'expanding' + completeExpand: (post, disableAutoplay) -> + return unless $.hasClass post.file.thumb, 'expanding' # contracted before the image loaded unless post.nodes.root.parentNode # Image might start/finish loading before the post is inserted. # Don't scroll when it's expanded in a QP for example. + ImageExpand.completeExpand2 post return {bottom} = post.nodes.root.getBoundingClientRect() $.queueTask -> + ImageExpand.completeExpand2 post, disableAutoplay return unless bottom <= 0 window.scrollBy 0, post.nodes.root.getBoundingClientRect().bottom - bottom - setupVideo: (post) -> - {file} = post - video = file.fullImage - file.videoControls = [] - video.muted = not Conf['Allow Sound'] - if video.controls - # contract link in file info + completeExpand2: (post, disableAutoplay) -> + $.addClass post.nodes.root, 'expanded-image' + $.rmClass post.file.thumb, 'expanding' + post.file.isExpanded = true + if post.file.isVideo + ImageExpand.setupVideoControls post + Video.configure post.file.fullImage, disableAutoplay + + videoCB: do -> + # dragging to the left contracts the video + mousedown = false + mouseover: -> mousedown = false + mousedown: (e) -> mousedown = true if e.button is 0 + mouseup: (e) -> mousedown = false if e.button is 0 + mouseout: (e) -> ImageExpand.contract(Get.postFromNode @) if mousedown and e.clientX <= @getBoundingClientRect().left + click: (e) -> + if @paused and not @controls + @play() + e.preventDefault() + + setupVideoControls: (post) -> + {file} = post + {thumb} = file + video = file.fullImage + + # disable link to file so native controls can work + file.thumb.parentNode.removeAttribute 'href' + file.thumb.parentNode.removeAttribute 'target' + + # setup callbacks on video element + $.on video, eventName, cb for eventName, cb of ImageExpand.videoCB + + # setup controls in file info + file.videoControls = $.el 'span', + className: 'video-controls' + if Conf['Show Controls'] contract = $.el 'a', textContent: 'contract' href: 'javascript:;' title: 'You can also contract the video by dragging it to the left.' $.on contract, 'click', (e) -> ImageExpand.contract post - file.videoControls.push $.tn('\u00A0'), contract - # drag left to contract - file.mousedown = false - $.on video, 'mousedown', (e) -> file.mousedown = true if e.button is 0 - $.on video, 'mouseup', (e) -> file.mousedown = false if e.button is 0 - $.on video, 'mouseover', (e) -> file.mousedown = false - $.on video, 'mouseout', (e) -> - if file.mousedown and e.clientX <= video.getBoundingClientRect().left - ImageExpand.contract post - if Conf['Autoplay'] - video.play() - else unless video.controls - play = $.el 'a', - textContent: 'play' - href: 'javascript:;' - $.on play, 'click', (e) -> - video[@textContent]() - @textContent = if @textContent is 'play' then 'pause' else 'play' - file.videoControls.push $.tn('\u00A0'), play + $.add file.videoControls, [$.tn('\u00A0'), contract] $.add file.text, file.videoControls error: -> @@ -198,7 +220,7 @@ ImageExpand = timeoutID = setTimeout ImageExpand.expand, 10000, post <% if (type === 'crx') { %> - $.ajax post.file.URL, + $.ajax @src, onloadend: -> return if @status isnt 404 clearTimeout timeoutID @@ -221,7 +243,7 @@ ImageExpand = menu: init: -> - return if !Conf['Image Expansion'] + return if g.VIEW is 'catalog' or !Conf['Image Expansion'] el = $.el 'span', textContent: 'Image Expansion' diff --git a/src/Images/ImageHover.coffee b/src/Images/ImageHover.coffee index 2a1633e01..f216a7ab6 100755 --- a/src/Images/ImageHover.coffee +++ b/src/Images/ImageHover.coffee @@ -9,36 +9,50 @@ ImageHover = name: 'Image Hover' cb: @catalogNode node: -> - return unless @file and (@file.isImage or @file.isVideo) + return unless @file?.isImage or @file?.isVideo $.on @file.thumb, 'mouseover', ImageHover.mouseover catalogNode: -> - return unless @thread.OP.file and (@thread.OP.file.isImage or @thread.OP.file.isVideo) + return unless (file = @thread.OP.file) and (file.isImage or file.isVideo) $.on @nodes.thumb, 'mouseover', ImageHover.mouseover mouseover: (e) -> post = if $.hasClass @, 'thumb' g.posts[@parentNode.dataset.fullID] else Get.postFromNode @ - el = if post.file.isImage - $.el 'img', - id: 'ihover' - src: post.file.URL + {isVideo} = post.file + if post.file.fullImage + el = post.file.fullImage + TrashQueue.remove el else - $.el 'video', - controls: false - id: 'ihover' + el = $.el (if isVideo then 'video' else 'img'), + className: 'full-image' src: post.file.URL - autoplay: Conf['Autoplay'] - muted: !Conf['Allow Sound'] - loop: true - $.add Header.hover, el + post.file.fullImage = el + {thumb} = post.file + + if d.body.contains thumb + $.after thumb, el unless el is thumb.nextSibling + else + $.add Header.hover, el if el.parentNode isnt Header.hover + el.id = 'ihover' el.dataset.fullID = post.fullID + if isVideo + el.loop = true + el.controls = false + el.muted = not Conf['Allow Sound'] + el.play() if Conf['Autoplay'] UI.hover root: @ el: el latestEvent: e endEvents: 'mouseout click' - asapTest: -> post.file.isVideo or el.naturalHeight + asapTest: -> (el.videoHeight or el.naturalHeight) + noRemove: true + cb: -> + if isVideo + el.pause() + TrashQueue.add el, post + el.removeAttribute 'id' $.on el, 'error', ImageHover.error error: -> return unless doc.contains @ diff --git a/src/Images/ImageLoader.coffee b/src/Images/ImageLoader.coffee index e3c51bffa..e9b638b20 100755 --- a/src/Images/ImageLoader.coffee +++ b/src/Images/ImageLoader.coffee @@ -1,17 +1,15 @@ ImageLoader = init: -> - return unless Conf["Image Prefetching"] or Conf["Replace JPG"] or Conf["Replace PNG"] or Conf["Replace GIF"] + return unless Conf["Image Prefetching"] or Conf["Replace JPG"] or Conf["Replace PNG"] or Conf["Replace GIF"] or Conf["Replace WEBM"] Post.callbacks.push name: 'Image Replace' cb: @node - + Thread.callbacks.push name: 'Image Replace' cb: @thread - - return unless Conf['Image Prefetching'] and g.VIEW is 'thread' - + prefetch = $.el 'label', innerHTML: ' Prefetch Images' @@ -22,27 +20,49 @@ ImageLoader = type: 'header' el: prefetch order: 104 - + thread: -> ImageLoader.thread = @ node: -> - return if @isClone or @isHidden or @thread.isHidden or !@file?.isImage + return unless @file + {isImage, isVideo} = @file + return if @isClone or @isHidden or @thread.isHidden or !(isImage or isVideo) {thumb, URL} = @file - return unless (Conf[string = "Replace #{if (type = (URL.match /\w{3}$/)[0].toUpperCase()) is 'PEG' then 'JPG' else type}"] and !/spoiler/.test thumb.src) or Conf['prefetch'] + {style} = thumb + type = if (match = URL.match(/\.([^.]+)$/)[1].toUpperCase()) is 'JPEG' then 'JPG' else match + replace = "Replace #{type}" + return unless (Conf[replace] and !/spoiler/.test thumb.src) or (Conf['prefetch'] and g.VIEW is 'thread') if @file.isSpoiler # Revealed spoilers do not have height/width set, this fixes the image's dimensions. - {style} = thumb style.maxHeight = style.maxWidth = if @isReply then '125px' else '250px' - img = $.el 'img' - if Conf[string] - $.on img, 'load', -> - # Replace the thumbnail once the GIF has finished loading. + file = $.el if isImage then 'img' else 'video' + if Conf[replace] + if isVideo + + file.alt = thumb.alt + file.dataset.md5 = thumb.dataset.md5 + file.style.height = style.height + file.style.width = style.width + file.style.maxHeight = style.maxHeight + file.style.maxWidth = style.maxWidth + file.loop = true + file.autoplay = Conf['Autoplay'] + if Conf['Image Hover'] + $.on file, 'mouseover', ImageHover.mouseover + cb = => + $.off file, 'load loadedmetadata', cb + # Replace the thumbnail once the file has finished loading. + if isVideo + $.replace thumb, file + @file.thumb = file # XXX expanding requires the post.file.thumb node. + return thumb.src = URL - img.src = URL + $.on file, 'load loadedmetadata', cb + file.src = URL toggle: -> enabled = Conf['prefetch'] = @checked if enabled - ImageLoader.thread.posts.forEach ImageLoader.node.call + g.BOARD.posts.forEach ImageLoader.node.call return \ No newline at end of file diff --git a/src/Images/TrashQueue.coffee b/src/Images/TrashQueue.coffee new file mode 100644 index 000000000..f7425d906 --- /dev/null +++ b/src/Images/TrashQueue.coffee @@ -0,0 +1,14 @@ +TrashQueue = + init: -> return + + add: (video, post) -> + if @killNext and video isnt @killNext + delete @killNextPost?.file?.fullImage + $.rm @killNext + @killNext = video + @killNextPost = post + + remove: (video) -> + if video is @killNext + delete @killNext + diff --git a/src/Images/Video.coffee b/src/Images/Video.coffee new file mode 100644 index 000000000..6ee8ef4da --- /dev/null +++ b/src/Images/Video.coffee @@ -0,0 +1,21 @@ +Video = + configure: (video, disableAutoplay) -> + video.loop = true + video.muted = !Conf['Allow Sound'] + video.controls = Conf['Show Controls'] + video.autoplay = false + if Conf['Autoplay'] and not disableAutoplay + Video.start video + else + video.pause() + + start: (video) -> + {controls} = video + video.controls = false + video.play() + # Hacky workaround for Firefox forever-loading bug for very short videos + if controls + $.asap (-> (video.readyState >= 3 and video.currentTime <= Math.max 0.1, (video.duration - 0.5)) or !d.contains video), -> + video.controls = true + , 500 + diff --git a/src/Miscellaneous/ExpandThread.coffee b/src/Miscellaneous/ExpandThread.coffee index 77c5fdc6d..4f4059fe2 100755 --- a/src/Miscellaneous/ExpandThread.coffee +++ b/src/Miscellaneous/ExpandThread.coffee @@ -8,7 +8,7 @@ ExpandThread = return unless summary = $.x 'following-sibling::*[contains(@class,"summary")][1]', thread.OP.nodes.root a = $.el 'a', textContent: ExpandThread.text '+', summary.textContent.match(/\d+/g)... - href: "#{thread.board.ID}/res/#{thread.ID}" + href: "res/#{thread.ID}" className: 'summary' $.on a, 'click', ExpandThread.cbToggle $.replace summary, a diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index 375bc4351..26f58027c 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -299,13 +299,11 @@ QR = blob.name = url.substr(url.lastIndexOf('/')+1, url.length) name_start = header.indexOf('name="') + 6 if (name_start - 6 != -1) - name_end = header.substr(name_start, header.length).indexOf('"') + name_end = header.substr(name_start, header.length).indexOf('"') blob.name = header.substr(name_start, name_end) return if blob.type is null QR.error "Unsupported file type." - return unless blob.type in QR.mimeTypes - QR.error "Unsupported file type." QR.handleFiles([blob]) handleUrl: -> @@ -778,6 +776,7 @@ QR = QR.cooldown.auto = false QR.status() QR.error err + QR.captcha.setup() if QR.captcha.isEnabled return h1 = $ 'h1', resDoc @@ -812,10 +811,13 @@ QR = # Enable auto-posting if we have stuff left to post, disable it otherwise. postsCount = QR.posts.length - 1 QR.cooldown.auto = postsCount and isReply + QR.captcha.setup() if QR.captcha.isEnabled and QR.cooldown.auto unless Conf['Persistent QR'] or QR.cooldown.auto QR.close() else + if QR.posts.length > 1 + QR.captcha.setup() post.rm() QR.cooldown.set {req, post, isReply, threadID} diff --git a/src/Posting/QR.post.coffee b/src/Posting/QR.post.coffee index c1e4d9547..799c17e9f 100644 --- a/src/Posting/QR.post.coffee +++ b/src/Posting/QR.post.coffee @@ -80,8 +80,6 @@ QR.post = class $.rmClass QR.nodes.el, 'dump' else if @ is QR.selected (QR.posts[index-1] or QR.posts[index+1]).select() - if QR.captcha.isEnabled - QR.captcha.setup() QR.posts.splice index, 1 QR.status() delete: ->