diff --git a/Gruntfile.coffee b/Gruntfile.coffee index a2aea0425..541087ccc 100755 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -1,26 +1,34 @@ module.exports = (grunt) -> + importHTML = (filename) -> + "\"\"\"#{grunt.file.read("src/General/html/#{filename}.html").replace(/^\s+|\s+$ grunt.config 'pkg' + get: -> + pkg = grunt.config 'pkg' + pkg.importHTML = importHTML + pkg enumerable: true ) coffee: src: [ + 'src/General/Cheats.coffee' 'src/General/Config.coffee' 'src/General/Globals.coffee' 'src/General/lib/*.coffee' 'src/General/Header.coffee' + 'src/General/Index.coffee' 'src/General/Build.coffee' 'src/General/Get.coffee' 'src/General/UI.coffee' 'src/General/Notice.coffee' 'src/Filtering/**/*' 'src/Quotelinks/**/*' - 'src/Linkification/**/*' + 'src/Posting/QR.coffee' 'src/Posting/**/*' 'src/Images/**/*' 'src/Linkification/**/*' @@ -90,6 +98,8 @@ module.exports = (grunt) -> stdout: true stderr: true failOnError: true + checkout: + command: 'git checkout <%= pkg.meta.mainBranch %>' commit: command: """ git commit -am "Release <%= pkg.meta.name %> v<%= pkg.version %>." diff --git a/LICENSE b/LICENSE index 1cddedec4..254faf90d 100755 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* -* 4chan X - Version 1.2.45 - 2014-01-07 +* 4chan X - Version 1.2.45 - 2014-01-08 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE diff --git a/builds/4chan-X.meta.js b/builds/4chan-X.meta.js index 457562051..b9e7832a1 100755 --- a/builds/4chan-X.meta.js +++ b/builds/4chan-X.meta.js @@ -2,7 +2,7 @@ // @name 4chan X // @version 1.2.45 // @minGMVer 1.13 -// @minFFVer 22 +// @minFFVer 26 // @namespace 4chan-X // @description Cross-browser userscript for maximum lurking on 4chan. // @license MIT; https://github.com/seaweedchan/4chan-x/blob/master/LICENSE diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 22f27e060..0e60c522d 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -3,7 +3,7 @@ // @name 4chan X // @version 1.2.45 // @minGMVer 1.13 -// @minFFVer 22 +// @minFFVer 26 // @namespace 4chan-X // @description Cross-browser userscript for maximum lurking on 4chan. // @license MIT; https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -22,7 +22,7 @@ // ==/UserScript== /* -* 4chan X - Version 1.2.45 - 2014-01-07 +* 4chan X - Version 1.2.45 - 2014-01-08 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -104,12 +104,26 @@ 'use strict'; (function() { - var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, CatalogLinks, Clone, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, Header, IDColor, ImageExpand, ImageHover, ImageLoader, InfiniScroll, Keybinds, Linkify, Main, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, g, + 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, 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, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, g, + __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; }, __slice = [].slice, __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) { + var i; + i = this.length; + while (i--) { + if (this[i] === val) { + return i; + } + } + return i; + }; + + __indexOf = [].indexOf; + Config = { main: { 'Miscellaneous': { @@ -123,7 +137,6 @@ '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.'], - 'Comment Expansion': [true, 'Add buttons to expand long comments.'], '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.'], @@ -134,8 +147,7 @@ '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.'], - 'Infinite Scrolling': [false, 'Add new posts to the board index upon reaching the bottom of the board.'] + '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.'], @@ -252,12 +264,24 @@ 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, @@ -299,6 +323,7 @@ '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.'], @@ -340,29 +365,6 @@ posts: {} }; - String.prototype.capitalize = function() { - return this.charAt(0).toUpperCase() + this.slice(1); - }; - - String.prototype.contains = function(string) { - return this.indexOf(string) > -1; - }; - - Array.prototype.contains = function(object) { - return this.indexOf(object) > -1; - }; - - Array.prototype.indexOf = function(object) { - var i; - i = this.length; - while (i--) { - if (this[i] === object) { - return i; - } - } - return i; - }; - $ = function(selector, root) { if (root == null) { root = d.body; @@ -370,14 +372,13 @@ return root.querySelector(selector); }; - $.extend = function(object, properties) { + $.extend = function(obj, prop) { var key, val; - for (key in properties) { - val = properties[key]; - if (!properties.hasOwnProperty(key)) { - continue; + for (key in prop) { + val = prop[key]; + if (prop.hasOwnProperty(key)) { + obj[key] = val; } - object[key] = val; } }; @@ -440,7 +441,9 @@ type || (type = form && 'post' || 'get'); r.open(type, url, !sync); if (whenModified) { - r.setRequestHeader('If-Modified-Since', lastModified[url] || '0'); + if (url in lastModified) { + r.setRequestHeader('If-Modified-Since', lastModified[url]); + } $.on(r, 'load', function() { return lastModified[url] = r.getResponseHeader('Last-Modified'); }); @@ -546,7 +549,7 @@ }; $.hasClass = function(el, className) { - return el.classList.contains(className); + return __indexOf.call(el.classList, className) >= 0; }; $.rm = (function() { @@ -563,8 +566,10 @@ })(); $.rmAll = function(root) { - var node; - while (node = root.firstChild) { + var node, _i, _len, _ref; + _ref = __slice.call(root.childNodes); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + node = _ref[_i]; root.removeChild(node); } }; @@ -729,10 +734,11 @@ $.syncing = {}; $.sync = (function() { - $.on(window, 'storage', function(e) { - var cb; - if (cb = $.syncing[e.key]) { - return cb(JSON.parse(e.newValue)); + $.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) { @@ -801,6 +807,55 @@ return __slice.call(root.querySelectorAll(selector)); }; + Callbacks = (function() { + function Callbacks() {} + + Callbacks.prototype.push = function(_arg) { + var cb, name; + name = _arg.name, cb = _arg.cb; + return this[name] = cb; + }; + + Callbacks.prototype.clean = function() { + var name; + for (name in this) { + if (this.hasOwnProperty(name)) { + this.rm(name); + } + } + }; + + Callbacks.prototype.rm = function(name) { + return delete this[name]; + }; + + Callbacks.prototype.execute = function(node) { + var err, errors, name; + for (name in this) { + if (this.hasOwnProperty(name)) { + try { + this[name].call(node); + } catch (_error) { + err = _error; + if (!errors) { + errors = []; + } + errors.push({ + message: ['"', name, '" crashed on node No.', node, ' (', node.board, ').'].join(''), + error: err + }); + } + } + } + if (errors) { + return Main.handleErrors(errors); + } + }; + + return Callbacks; + + })(); + Board = (function() { Board.prototype.toString = function() { return this.ID; @@ -818,7 +873,7 @@ })(); Thread = (function() { - Thread.callbacks = []; + Thread.callbacks = new Callbacks(); Thread.prototype.toString = function() { return this.ID; @@ -829,20 +884,70 @@ this.board = board; this.fullID = "" + this.board + "." + this.ID; this.posts = {}; + this.isSticky = false; + this.isClosed = false; + this.postLimit = false; + this.fileLimit = false; g.threads[this.fullID] = board.threads[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() { + var post, postID, _i, _len, _ref; + _ref = this.posts; + for (post = _i = 0, _len = _ref.length; _i < _len; post = ++_i) { + postID = _ref[post]; + post.collect(); + } + delete g.threads[this.fullID]; + return delete this.board.threads[this]; + }; + return Thread; })(); Post = (function() { - Post.callbacks = []; + Post.callbacks = new Callbacks(); Post.prototype.toString = function() { return this.ID; @@ -857,6 +962,9 @@ } 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 = { @@ -961,7 +1069,7 @@ return; } fullID = "" + match[1] + "." + match[2]; - if (!this.quotes.contains(fullID)) { + if (__indexOf.call(this.quotes, fullID) < 0) { return this.quotes.push(fullID); } }; @@ -994,6 +1102,20 @@ } }; + 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()); @@ -1071,6 +1193,13 @@ } }; + Post.prototype.collect = function() { + this.kill(); + delete g.posts[this.fullID]; + delete this.thread.posts[this]; + return delete this.board.posts[this]; + }; + Post.prototype.addClone = function(context) { return new Clone(this, context); }; @@ -1296,6 +1425,9 @@ 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]; @@ -1336,7 +1468,7 @@ this.close = __bind(this.close, this); this.add = __bind(this.add, this); this.el = $.el('div', { - innerHTML: '×
' + innerHTML: '
' }); this.el.style.opacity = 0; this.setType(type); @@ -1358,7 +1490,7 @@ return; } $.off(d, 'visibilitychange', this.add); - $.add($.id('notifications'), this.el); + $.add(Header.noticesRoot, this.el); this.el.clientHeight; this.el.style.opacity = 1; if (this.timeout) { @@ -1375,10 +1507,90 @@ })(); + RandomAccessList = (function() { + function RandomAccessList() { + this.length = 0; + } + + RandomAccessList.prototype.push = function(item) { + var ID, last; + ID = item.ID; + if (this[ID]) { + return; + } + last = this.last; + item.prev = last; + this[ID] = item; + this.last = last ? last.next = item : this.first = item; + return this.length++; + }; + + 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; + 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.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; + + })(); + Polyfill = { init: function() {}, notificationPermission: function() { - if (!window.Notification || 'permission' in Notification) { + if (!window.Notification || 'permission' in Notification || !window.webkitNotifications) { return; } return Object.defineProperty(Notification, 'permission', { @@ -1410,7 +1622,7 @@ }); }, visibility: function() { - if (!('webkitHidden' in document)) { + if ('visibilityState' in d) { return; } Object.defineProperties(HTMLDocument.prototype, { @@ -1433,7 +1645,7 @@ Header = { init: function() { - var barFixedToggler, barPositionToggler, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, menuButton, shortcutToggler, + var barFixedToggler, barPositionToggler, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, menuButton, scrollHeaderToggler, shortcutToggler, _this = this; this.menu = new UI.Menu('header'); menuButton = $.el('span', { @@ -1446,8 +1658,11 @@ headerToggler = $.el('label', { innerHTML: ' Auto-hide header' }); + scrollHeaderToggler = $.el('label', { + innerHTML: ' Auto-hide header on scroll' + }); barPositionToggler = $.el('label', { - innerHTML: ' Bottom header' + innerHTML: ' Bottom header' }); linkJustifyToggler = $.el('label', { innerHTML: " Centered links" @@ -1466,6 +1681,7 @@ href: 'javascript:;' }); this.barFixedToggler = barFixedToggler.firstElementChild; + this.scrollHeaderToggler = scrollHeaderToggler.firstElementChild; this.barPositionToggler = barPositionToggler.firstElementChild; this.linkJustifyToggler = linkJustifyToggler.firstElementChild; this.headerToggler = headerToggler.firstElementChild; @@ -1473,8 +1689,10 @@ 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); @@ -1482,14 +1700,16 @@ $.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', Header.setBarFixed); - $.sync('Bottom Header', Header.setBarPosition); - $.sync('Shortcut Icons', Header.setShortcutIcons); - $.sync('Header auto-hide', Header.setBarVisibility); - $.sync('Centered links', Header.setLinkJustify); + $.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', @@ -1502,6 +1722,8 @@ el: barFixedToggler }, { el: headerToggler + }, { + el: scrollHeaderToggler }, { el: barPositionToggler }, { @@ -1534,10 +1756,11 @@ return _this; }); $.ready(function() { - var a, cs; - _this.footer = $.id('boardNavDesktopFoot'); - if (a = $("a[href*='/" + g.BOARD + "/']", $.id('boardNavDesktopFoot'))) { + var a, cs, footer; + _this.footer = footer = $.id('boardNavDesktopFoot'); + if (a = $("a[href*='/" + g.BOARD + "/']", footer)) { a.className = 'current'; + $.on(a, 'click', Index.cb.link); } cs = $.el('a', { id: 'settingsWindowLink', @@ -1555,7 +1778,7 @@ bar: $.el('div', { id: 'header-bar' }), - notify: $.el('div', { + noticesRoot: $.el('div', { id: 'notifications' }), shortcuts: $.el('span', { @@ -1570,19 +1793,20 @@ setBoardList: function() { var a, boardList, btn, fourchannav, fullBoardList; fourchannav = $.id('boardNavDesktop'); - if (a = $("a[href*='/" + g.BOARD + "/']", fourchannav)) { - a.className = 'current'; - } boardList = $.el('span', { id: 'board-list', innerHTML: "" }); + if (a = $("a[href*='/" + g.BOARD + "/']", boardList)) { + a.className = 'current'; + $.on(a, 'click', Index.cb.link); + } 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, [boardList, Header.shortcuts, Header.notify, Header.toggle]); + $.add(Header.bar, [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); @@ -1597,7 +1821,7 @@ } as = $$('#full-board-list a[title]', Header.bar); nodes = text.match(/[\w@]+((-(all|title|replace|full|index|catalog|url:"[^"]+[^"]"|text:"[^"]+")|\,"[^"]+[^"]"))*|[^\w@]+/g).map(function(t) { - var a, board, m, _i, _len; + var a, board, current, m, _i, _len; if (/^[^\w@]/.test(t)) { return $.tn(t); } @@ -1623,7 +1847,11 @@ a = as[_i]; if (a.textContent === board) { a = a.cloneNode(true); - 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; + current = $.hasClass(a, 'current'); + if (current) { + $.on(a, 'click', Index.cb.link); + } + a.textContent = /-title/.test(t) || /-replace/.test(t) && 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 + "/"; @@ -1655,18 +1883,6 @@ custom.hidden = !showBoardList; return full.hidden = showBoardList; }, - setBarPosition: function(bottom) { - Header.barPositionToggler.checked = bottom; - if (bottom) { - $.rmClass(doc, 'top'); - $.addClass(doc, 'bottom'); - return $.after(Header.bar, Header.notify); - } else { - $.rmClass(doc, 'bottom'); - $.addClass(doc, 'top'); - return $.add(Header.bar, Header.notify); - } - }, setLinkJustify: function(centered) { Header.linkJustifyToggler.checked = centered; if (centered) { @@ -1675,12 +1891,6 @@ return $.rmClass(doc, 'centered-links'); } }, - toggleBarPosition: function() { - $.event('CloseMenu'); - Header.setBarPosition(this.checked); - Conf['Bottom Header'] = this.checked; - return $.set('Bottom Header', this.checked); - }, toggleLinkJustify: function() { var centered; $.event('CloseMenu'); @@ -1733,6 +1943,50 @@ 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; + $.set('Header auto-hide on scroll', hide); + return Header.setHideBarOnScroll(hide); + }, + hideBarOnScroll: function() { + var offsetY; + offsetY = window.pageYOffset; + if (offsetY > (Header.previousOffset || 0)) { + $.addClass(Header.bar, 'autohide'); + $.addClass(Header.bar, 'scroll'); + } else { + $.rmClass(Header.bar, 'autohide'); + $.rmClass(Header.bar, '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.notify); + }, + toggleBarPosition: function() { + $.cb.checked.call(this); + return Header.setBarPosition(this.checked); + }, setFooterVisibility: function(hide) { Header.footerToggler.checked = hide; return Header.footer.hidden = hide; @@ -1766,22 +2020,50 @@ }, hashScroll: function() { var hash, post; - if (!((hash = this.location.hash.slice(1)) && (post = $.id(hash)))) { + hash = this.location.hash.slice(1); + if (!(/^p\d+$/.test(hash) && (post = $.id(hash)))) { return; } if ((Get.postFromRoot(post)).isHidden) { return; } - return Header.scrollToPost(post); + return Header.scrollTo(post); }, - scrollToPost: function(post) { + scrollTo: function(root, down, needed) { + var x; + if (down) { + x = Header.getBottomOf(root); + if (!(needed && x >= 0)) { + return window.scrollBy(0, -x); + } + } else { + x = Header.getTopOf(root); + 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 = post.getBoundingClientRect().top; + top = root.getBoundingClientRect().top; if (Conf['Fixed Header'] && !Conf['Bottom Header']) { - headRect = Header.bar.getBoundingClientRect(); + headRect = Header.toggle.getBoundingClientRect(); top -= headRect.top + headRect.height; } - return window.scrollBy(0, top); + 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; }, addShortcut: function(el) { var shortcut; @@ -1795,11 +2077,11 @@ return Header.menu.toggle(e, this, g); }, createNotification: function(e) { - var cb, content, lifetime, notif, type, _ref; + var cb, content, lifetime, notice, type, _ref; _ref = e.detail, type = _ref.type, content = _ref.content, lifetime = _ref.lifetime, cb = _ref.cb; - notif = new Notice(type, content, lifetime); + notice = new Notice(type, content, lifetime); if (cb) { - return cb(notif); + return cb(notice); } }, areNotificationsEnabled: false, @@ -1836,7 +2118,651 @@ } }; + Index = { + init: function() { + var anchorEntry, input, label, modeEntry, name, refNavEntry, repliesEntry, sortEntry, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2; + if (g.VIEW !== 'index' || g.BOARD.ID === 'f') { + return; + } + this.button = $.el('a', { + className: 'index-refresh-shortcut fa fa-refresh', + title: 'Refresh Index', + 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: ' 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: 90, + subEntries: [modeEntry, sortEntry, repliesEntry, anchorEntry, refNavEntry] + }); + $.addClass(doc, 'index-loading'); + this.update(); + this.root = $.el('div', { + className: 'board' + }); + this.pagelist = $.el('div', { + className: 'pagelist', + hidden: true, + innerHTML: "
Catalog
" + }); + this.navLinks = $.el('div', { + className: 'navLinks', + innerHTML: "[Catalog] [" + }); + this.searchInput = $('#index-search', this.navLinks); + this.currentPage = this.getCurrentPage(); + $.on(window, 'popstate', this.cb.popstate); + $.on(this.pagelist, 'click', this.cb.pageNav); + $.on(this.searchInput, 'input', this.onSearchInput); + $.on($('#index-search-clear', this.navLinks), 'click', this.clearSearch); + return $.asap((function() { + return $('.board', doc) || d.readyState !== 'loading'; + }), function() { + var board, navLink, _l, _len3, _ref3; + 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); + $.rmClass(doc, 'index-loading'); + return $.asap((function() { + return $('.pagelist') || d.readyState !== 'loading'; + }), function() { + return $.replace($('.pagelist'), Index.pagelist); + }); + }); + }, + cb: { + mode: function() { + Index.togglePagelist(); + return Index.buildIndex(); + }, + sort: function() { + Index.sort(); + return Index.buildIndex(); + }, + replies: function() { + Index.buildThreads(); + Index.sort(); + return Index.buildIndex(); + }, + popstate: function(e) { + var pageNum; + pageNum = Index.getCurrentPage(); + if (Index.currentPage !== pageNum) { + return Index.pageLoad(pageNum); + } + }, + 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]); + }, + link: function(e) { + if (g.VIEW !== 'index' || /catalog/.test(this.href)) { + return; + } + e.preventDefault(); + return Index.update(); + } + }, + 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'] === 'paged') { + 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'] !== 'paged') { + 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() { + var a, href, maxPageNum, next, pageNum, pagesRoot, prev, strong; + 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 ((_ref = Index.req) != null) { + _ref.abort(); + } + if ((_ref1 = Index.notice) != null) { + _ref1.close(); + } + if (d.readyState !== 'loading') { + Index.notice = new Notice('info', 'Refreshing index...'); + } else { + now = Date.now(); + $.ready(function() { + return setTimeout((function() { + if (!(Index.req && !Index.notice)) { + return; + } + return Index.notice = new Notice('info', 'Refreshing index...'); + }), 5 * $.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: true + }); + return $.addClass(Index.button, 'fa-spin'); + }, + load: function(e, pageNum) { + var err, notice, req, timeEl; + $.rmClass(Index.button, 'fa-spin'); + req = Index.req, notice = Index.notice; + delete Index.req; + delete Index.notice; + if (e.type === 'abort') { + req.onloadend = null; + notice.close(); + return; + } + try { + if (req.status === 200) { + Index.parse(JSON.parse(req.response), pageNum); + } else if (req.status === 304 && (pageNum != null)) { + Index.pageNav(pageNum); + } + } catch (_error) { + err = _error; + c.error('Index failure:', err.stack); + if (notice) { + notice.setType('error'); + notice.el.lastElementChild.textContent = 'Index refresh failed.'; + setTimeout(notice.close, 2 * $.SECOND); + } else { + new Notice('error', 'Index refresh failed.', 2); + } + return; + } + if (notice) { + notice.setType('success'); + notice.el.lastElementChild.textContent = 'Index refreshed!'; + setTimeout(notice.close, $.SECOND); + } + timeEl = $('#index-last-refresh', 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) { + var thread, threadID, _ref, _ref1; + 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; + }); + _ref = g.BOARD.threads; + for (threadID in _ref) { + thread = _ref[threadID]; + if (_ref1 = thread.ID, __indexOf.call(Index.liveThreadIDs, _ref1) < 0) { + 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]; + threadRoot = Build.thread(g.BOARD, threadData); + Index.nodes.push(threadRoot, $.el('hr')); + 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); + } + if (thread.ID in thread.posts) { + continue; + } + try { + posts.push(new Post($('.opContainer', threadRoot), thread, g.BOARD)); + } catch (_error) { + err = _error; + if (!errors) { + errors = []; + } + errors.push({ + message: "Parsing of Post No." + thread + " failed. Post 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 += 2) { + 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 i, sortedThreadIDs, threadID, _i, _len; + switch (Conf['Index Sort']) { + case 'bump': + sortedThreadIDs = Index.liveThreadIDs; + break; + case 'lastreply': + sortedThreadIDs = __slice.call(Index.liveThreadData).sort(function(a, b) { + if ('last_replies' in a) { + a = a.last_replies[a.last_replies.length - 1]; + } + if ('last_replies' in b) { + b = b.last_replies[b.last_replies.length - 1]; + } + return b.no - a.no; + }).map(function(data) { + return data.no; + }); + break; + case 'birth': + sortedThreadIDs = __slice.call(Index.liveThreadIDs).sort(function(a, b) { + return b - a; + }); + break; + case 'replycount': + sortedThreadIDs = __slice.call(Index.liveThreadData).sort(function(a, b) { + return b.replies - a.replies; + }).map(function(data) { + return data.no; + }); + break; + case 'filecount': + sortedThreadIDs = __slice.call(Index.liveThreadData).sort(function(a, b) { + return b.images - a.images; + }).map(function(data) { + return data.no; + }); + } + Index.sortedNodes = []; + for (_i = 0, _len = sortedThreadIDs.length; _i < _len; _i++) { + threadID = sortedThreadIDs[_i]; + i = Index.liveThreadIDs.indexOf(threadID) * 2; + Index.sortedNodes.push(Index.nodes[i], Index.nodes[i + 1]); + } + if (Index.isSearching) { + Index.sortedNodes = Index.querySearch(Index.searchInput.value) || Index.sortedNodes; + } + Index.sortOnTop(function(thread) { + return thread.isSticky; + }); + if (Conf['Filter']) { + Index.sortOnTop(function(thread) { + return thread.isOnTop; + }); + } + if (Conf['Anchor Hidden Threads']) { + return Index.sortOnTop(function(thread) { + return !thread.isHidden; + }); + } + }, + sortOnTop: function(match) { + var i, offset, threadRoot, _i, _len, _ref, _ref1; + offset = 0; + _ref = Index.sortedNodes; + for (i = _i = 0, _len = _ref.length; _i < _len; i = _i += 2) { + threadRoot = _ref[i]; + if (match(Get.threadFromRoot(threadRoot))) { + (_ref1 = Index.sortedNodes).splice.apply(_ref1, [offset++ * 2, 0].concat(__slice.call(Index.sortedNodes.splice(i, 2)))); + } + } + }, + buildIndex: function() { + var nodes, nodesPerPage, pageNum; + if (Conf['Index Mode'] === 'paged') { + pageNum = Index.getCurrentPage(); + nodesPerPage = Index.threadsNumPerPage * 2; + nodes = Index.sortedNodes.slice(nodesPerPage * pageNum, nodesPerPage * (pageNum + 1)); + } else { + nodes = Index.sortedNodes; + } + $.rmAll(Index.root); + if (Conf['Show Replies']) { + Index.buildReplies(nodes); + } + $.event('IndexBuild', nodes); + return $.add(Index.root, nodes); + }, + 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 { + pageNum = Index.pageBeforeSearch; + delete Index.pageBeforeSearch; + Index.searchInput.removeAttribute('data-searching'); + } + Index.sort(); + if (Conf['Index Mode'] === 'paged') { + 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 found, i, threadRoot, _i, _len, _ref; + found = []; + _ref = Index.sortedNodes; + for (i = _i = 0, _len = _ref.length; _i < _len; i = _i += 2) { + threadRoot = _ref[i]; + if (Index.searchMatch(Get.threadFromRoot(threadRoot), keywords)) { + found.push(Index.sortedNodes[i], Index.sortedNodes[i + 1]); + } + } + 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; @@ -1847,6 +2773,13 @@ return filename; } }, + thumbRotate: (function() { + var n; + n = 0; + return function() { + return n = (n + 1) % 3; + }; + })(), postFromObject: function(data, boardID) { var o; o = { @@ -1876,7 +2809,7 @@ width: data.w, MD5: data.md5, size: data.fsize, - turl: "//t.4cdn.org/" + boardID + "/thumb/" + data.tim + "s.jpg", + turl: "//" + (Build.thumbRotate()) + ".t.4cdn.org/" + boardID + "/thumb/" + data.tim + "s.jpg", theight: data.tn_h, twidth: data.tn_w, isSpoiler: !!data.spoiler, @@ -1891,10 +2824,11 @@ @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE */ - var a, boardID, capcode, capcodeClass, capcodeStart, closed, comment, container, date, dateUTC, email, emailEnd, emailStart, ext, file, fileDims, fileHTML, fileInfo, fileSize, fileThumb, filename, flag, flagCode, flagName, href, imgSrc, isClosed, isOP, isSticky, name, postID, quote, shortFilename, spoilerRange, staticPath, sticky, subject, threadID, tripcode, uniqueID, userID, _i, _len, _ref; + 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 = '//s.4cdn.org/image/'; + staticPath = Build.staticPath, gifIcon = Build.gifIcon; + tripcode = tripcode ? " " + tripcode + "" : ''; if (email) { emailStart = ''; emailEnd = ''; @@ -1902,39 +2836,33 @@ emailStart = ''; emailEnd = ''; } - subject = " " + (subject || '') + " "; - userID = !capcode && uniqueID ? (" (ID: ") + ("" + uniqueID + ") ") : ''; switch (capcode) { case 'admin': case 'admin_highlight': capcodeClass = " capcodeAdmin"; capcodeStart = " ## Admin"; - capcode = (" "; + capcodeIcon = (" "; break; case 'mod': capcodeClass = " capcodeMod"; capcodeStart = " ## Mod"; - capcode = (" "; + capcodeIcon = (" "; break; case 'developer': capcodeClass = " capcodeDeveloper"; capcodeStart = " ## Developer"; - capcode = (" "; + capcodeIcon = (" "; break; default: capcodeClass = ''; capcodeStart = ''; - capcode = ''; + capcodeIcon = ''; } + userID = !capcode && uniqueID ? (" (ID: ") + ("" + uniqueID + ") ") : ''; flag = !flagCode ? '' : boardID === 'pol' ? "  + flagCode + " : " "; if (file != null ? file.isDeleted : void 0) { - fileHTML = isOP ? ("
") + ("File deleted.") + "
" : ("
") + ("File deleted.") + "
"; + fileHTML = isOP ? ("
") + ("File deleted.") + "
" : ("
") + ("File deleted.") + "
"; } else if (file) { - ext = file.name.slice(-3); - if (!file.twidth && !file.theight && ext === 'gif') { - file.twidth = file.width; - file.theight = file.height; - } fileSize = $.bytesToString(file.size); fileThumb = file.turl; if (file.isSpoiler) { @@ -1957,19 +2885,25 @@ shortFilename = a.innerHTML; a.textContent = filename; filename = a.innerHTML.replace(/'/g, '''); - fileDims = ext === 'pdf' ? 'PDF' : "" + file.width + "x" + file.height; + 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 = ''; } - tripcode = tripcode ? " " + tripcode + "" : ''; - sticky = isSticky ? " Sticky" : ''; - closed = isClosed ? " Closed" : ''; + 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 ? '' : "
>>
") + "
" + (name || '') + "" + (tripcode + capcodeStart + capcode + userID + flag + sticky + closed) + "
" + subject + "
" + date + "No." + postID + "
" + (isOP ? fileHTML : '') + "
" + subject + "" + emailStart + "" + (name || '') + "" + (tripcode + capcodeStart + emailEnd + capcode + userID + flag + sticky + closed) + "" + " " + "" + date + "" + " " + "No." + postID + "
" + (isOP ? '' : fileHTML) + "
" + (comment || '') + "
" + " " + "
" + innerHTML: "" + (isOP ? '' : "
>>
") + "
" + (isOP ? fileHTML : '') + "
" + ' ' + "" + (subject || '') + "" + ' ' + "" + emailStart + "" + (name || '') + "" + (tripcode + capcodeStart + emailEnd + capcodeIcon + userID + flag) + "" + " " + "" + date + "" + ' ' + "No." + postID + "" + (pageIcon + sticky + closed + replyLink) + "
" + (isOP ? '' : fileHTML) + "
" + (comment || '') + "
" + ' ' + "
" }); _ref = $$('.quotelink', container); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -1981,6 +2915,43 @@ 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) { + var OP, files, nodes, posts, root, _ref; + 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 + }); + } + 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)); + } + $.add(root, nodes); + return root; } }; @@ -2037,36 +3008,36 @@ }; }, allQuotelinksLinkingTo: function(post) { - var ID, quote, quotedPost, quotelinks, quoterPost, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3; + var ID, quote, quotedPost, quotelinks, quoterPost, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3, _ref4; quotelinks = []; _ref = g.posts; for (ID in _ref) { quoterPost = _ref[ID]; - if (quoterPost.quotes.contains(post.fullID)) { - _ref1 = [quoterPost].concat(quoterPost.clones); - for (_i = 0, _len = _ref1.length; _i < _len; _i++) { - quoterPost = _ref1[_i]; + if (_ref1 = post.fullID, __indexOf.call(quoterPost.quotes, _ref1) >= 0) { + _ref2 = [quoterPost].concat(quoterPost.clones); + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + quoterPost = _ref2[_i]; quotelinks.push.apply(quotelinks, quoterPost.nodes.quotelinks); } } } if (Conf['Quote Backlinks']) { - _ref2 = post.quotes; - for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) { - quote = _ref2[_j]; + _ref3 = post.quotes; + for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) { + quote = _ref3[_j]; if (!(quotedPost = g.posts[quote])) { continue; } - _ref3 = [quotedPost].concat(quotedPost.clones); - for (_k = 0, _len2 = _ref3.length; _k < _len2; _k++) { - quotedPost = _ref3[_k]; + _ref4 = [quotedPost].concat(quotedPost.clones); + for (_k = 0, _len2 = _ref4.length; _k < _len2; _k++) { + quotedPost = _ref4[_k]; quotelinks.push.apply(quotelinks, __slice.call(quotedPost.nodes.backlinks)); } } } return quotelinks.filter(function(quotelink) { - var boardID, postID, _ref4; - _ref4 = Get.postDataFromLink(quotelink), boardID = _ref4.boardID, postID = _ref4.postID; + var boardID, postID, _ref5; + _ref5 = Get.postDataFromLink(quotelink), boardID = _ref5.boardID, postID = _ref5.postID; return boardID === post.board.ID && postID === post.ID; }); }, @@ -2112,7 +3083,7 @@ return; } status = req.status; - if (![200, 304].contains(status)) { + if (status !== 200 && status !== 304) { if (url = Redirect.to('post', { boardID: boardID, postID: postID @@ -2177,8 +3148,8 @@ 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, + postID: postID, + threadID: threadID, boardID: boardID, name: data.name_processed, capcode: (function() { @@ -2472,7 +3443,7 @@ 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 ? ['100%', 'auto'] : ['auto', '100%'], left = _ref2[0], right = _ref2[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; @@ -2697,7 +3668,7 @@ Filter = { filters: {}, init: function() { - var boards, err, filter, hl, key, op, regexp, stub, top, _i, _len, _ref, _ref1, _ref2, _ref3, _ref4; + 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; } @@ -2717,10 +3688,10 @@ } filter = filter.replace(regexp[0], ''); boards = ((_ref1 = filter.match(/boards:([^;]+)/)) != null ? _ref1[1].toLowerCase() : void 0) || 'global'; - if (boards !== 'global' && !(boards.split(',')).contains(g.BOARD.ID)) { + if (boards !== 'global' && (_ref2 = g.BOARD.ID, __indexOf.call(boards.split(','), _ref2) < 0)) { continue; } - if (['uniqueID', 'MD5'].contains(key)) { + if (key === 'uniqueID' || key === 'MD5') { regexp = regexp[1]; } else { try { @@ -2731,10 +3702,10 @@ continue; } } - op = ((_ref2 = filter.match(/[^t]op:(yes|no|only)/)) != null ? _ref2[1] : void 0) || 'yes'; + op = ((_ref3 = filter.match(/[^t]op:(yes|no|only)/)) != null ? _ref3[1] : void 0) || 'yes'; stub = (function() { - var _ref3; - switch ((_ref3 = filter.match(/stub:(yes|no)/)) != null ? _ref3[1] : void 0) { + var _ref4; + switch ((_ref4 = filter.match(/stub:(yes|no)/)) != null ? _ref4[1] : void 0) { case 'yes': return true; case 'no': @@ -2744,8 +3715,8 @@ } })(); if (hl = /highlight/.test(filter)) { - hl = ((_ref3 = filter.match(/highlight:(\w+)/)) != null ? _ref3[1] : void 0) || 'filter-highlight'; - top = ((_ref4 = filter.match(/top:(yes|no)/)) != null ? _ref4[1] : void 0) || 'yes'; + 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)); @@ -2786,7 +3757,7 @@ }; }, node: function() { - var filter, firstThread, key, result, thisThread, value, _i, _len, _ref; + var filter, key, result, value, _i, _len, _ref; if (this.isClone) { return; } @@ -2812,13 +3783,8 @@ return; } $.addClass(this.nodes.root, result["class"]); - if (!this.isReply && result.top && g.VIEW === 'index') { - thisThread = this.nodes.root.parentNode; - if (firstThread = $('div[class="postContainer opContainer"]')) { - if (firstThread !== this.nodes.root) { - $.before(firstThread.parentNode, [thisThread, thisThread.nextElementSibling]); - } - } + if (!this.isReply && result.top) { + this.thread.isOnTop = true; } } } @@ -2942,7 +3908,7 @@ var re, type, value; type = this.dataset.type; value = Filter[type](Filter.menu.post); - re = ['uniqueID', 'MD5'].contains(type) ? value : value.replace(/\/|\\|\^|\$|\n|\.|\(|\)|\{|\}|\[|\]|\?|\*|\+|\|/g, function(c) { + re = type === 'uniqueID' || type === 'MD5' ? value : value.replace(/\/|\\|\^|\$|\n|\.|\(|\)|\{|\}|\[|\]|\?|\*|\+|\|/g, function(c) { if (c === '\n') { return '\\n'; } else if (c === '\\') { @@ -2951,7 +3917,7 @@ return "\\" + c; } }); - re = ['uniqueID', 'MD5'].contains(type) ? "/" + re + "/" : "/^" + re + "$/"; + re = type === 'uniqueID' || type === 'MD5' ? "/" + re + "/" : "/^" + re + "$/"; return $.get(type, Conf[type], function(item) { var save, section, select, ta, tl; save = item[type]; @@ -3174,12 +4140,16 @@ } }, makeButton: function(post, type) { - var a; + var a, span; + span = $.el('span', { + className: "brackets-wrap", + textContent: "\u00A0" + (type === 'hide' ? '-' : '+') + "\u00A0" + }); a = $.el('a', { className: "" + type + "-reply-button", - innerHTML: " " + (type === 'hide' ? '-' : '+') + " ", href: 'javascript:;' }); + $.add(a, span); $.on(a, 'click', PostHiding.toggle); return a; }, @@ -3208,7 +4178,7 @@ return PostHiding.saveHiddenState(post, post.isHidden); }, hide: function(post, makeStub, hideRecursively) { - var a, button, postInfo, quotelink, _i, _len, _ref; + var a, postInfo, quotelink, _i, _len, _ref; if (makeStub == null) { makeStub = Conf['Stubs']; } @@ -3238,7 +4208,10 @@ post.nodes.stub = $.el('div', { className: 'stub' }); - $.add(post.nodes.stub, Conf['Menu'] ? [a, $.tn(' '), button = Menu.makeButton(post)] : a); + $.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) { @@ -3324,7 +4297,7 @@ _ref = g.posts; for (ID in _ref) { post = _ref[ID]; - if (post.quotes.contains(fullID)) { + if (__indexOf.call(post.quotes, fullID) >= 0) { recursive.apply(null, [post].concat(__slice.call(args))); } } @@ -3338,6 +4311,7 @@ } this.db = new DataBoard('hiddenThreads'); this.syncCatalog(); + $.on(d, 'IndexBuild', this.onIndexBuild); return Thread.callbacks.push({ name: 'Thread Hiding', cb: this.node @@ -3356,6 +4330,22 @@ } 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({ @@ -3517,6 +4507,25 @@ $.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)) || {}; @@ -3550,27 +4559,19 @@ return ThreadHiding.saveHiddenState(thread); }, hide: function(thread, makeStub) { - var OP, a, numReplies, opInfo, span, threadRoot; + var threadRoot; if (makeStub == null) { makeStub = Conf['Stubs']; } - OP = thread.OP; - threadRoot = OP.nodes.root.parentNode; - thread.isHidden = true; - if (!makeStub) { - threadRoot.hidden = threadRoot.nextElementSibling.hidden = true; + if (thread.isHidden) { return; } - numReplies = ((span = $('.summary', threadRoot)) ? +span.textContent.match(/\d+/) : 0) + $$('.opContainer ~ .replyContainer', threadRoot).length; - numReplies = numReplies === 1 ? '1 reply' : "" + (numReplies || 'No') + " replies"; - opInfo = Conf['Anonymize'] ? 'Anonymous' : $('.nameBlock', OP.nodes.info).textContent; - a = ThreadHiding.makeButton(thread, 'show'); - $.add(a, $.tn(" " + opInfo + " (" + numReplies + ")")); - thread.stub = $.el('div', { - className: 'stub' - }); - $.add(thread.stub, Conf['Menu'] ? [a, $.tn(' '), Menu.makeButton()] : a); - return $.prepend(threadRoot, thread.stub); + 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; @@ -3831,7 +4832,7 @@ }); }, node: function() { - var boardID, fullID, i, postID, quotelink, quotelinks, quotes, _ref; + var boardID, fullID, i, postID, quotelink, quotelinks, quotes, _ref, _ref1; if (this.isClone && this.thread === this.context.thread) { return; } @@ -3839,19 +4840,19 @@ return; } quotelinks = this.nodes.quotelinks; - if (this.isClone && quotes.contains(this.thread.fullID)) { + 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 (!quotes.contains(fullID)) { + if (__indexOf.call(quotes, fullID) < 0) { return; } i = 0; while (quotelink = quotelinks[i++]) { - _ref = Get.postDataFromLink(quotelink), boardID = _ref.boardID, postID = _ref.postID; + _ref1 = Get.postDataFromLink(quotelink), boardID = _ref1.boardID, postID = _ref1.postID; if (("" + boardID + "." + postID) === fullID) { $.add(quotelink, $.tn(QuoteOP.text)); } @@ -3984,127 +4985,138 @@ innerHTML: '' }); input = $('input', this.controls); - $.on(input, 'change', QuoteThreading.toggle); + $.on(input, 'change', this.toggle); $.event('AddMenuEntry', { type: 'header', el: this.controls, order: 98 }); - $.on(d, '4chanXInitFinished', this.setup); + if (!Conf['Unread Count']) { + $.on(d, '4chanXInitFinished', this.setup); + } return Post.callbacks.push({ name: 'Quote Threading', cb: this.node }); }, setup: function() { - var ID, post, posts; $.off(d, '4chanXInitFinished', QuoteThreading.setup); - posts = g.posts; - for (ID in posts) { - post = posts[ID]; + return QuoteThreading.force(); + }, + force: function() { + var ID, post, _ref; + _ref = g.posts; + for (ID in _ref) { + post = _ref[ID]; if (post.cb) { - post.cb.call(post); + post.cb(true); } } - return QuoteThreading.hasRun = true; }, node: function() { - var ID, fullID, keys, len, post, posts, qid, quote, quotes, uniq, _i, _len; - if (this.isClone || !QuoteThreading.enabled || this.thread.OP === this) { - return; - } - quotes = this.quotes, ID = this.ID, fullID = this.fullID; + var keys, len, post, posts, quote, _i, _len, _ref; posts = g.posts; - if (!(post = posts[fullID]) || post.isHidden) { + if (this.isClone || !QuoteThreading.enabled) { return; } - uniq = {}; - len = ("" + g.BOARD).length + 1; - for (_i = 0, _len = quotes.length; _i < _len; _i++) { - quote = quotes[_i]; - qid = quote; - if (!(qid.slice(len) < ID)) { - continue; - } - if (qid in posts) { - uniq[qid.slice(len)] = true; + if (Conf['Unread Count']) { + Unread.posts.push(this); + } + if (this.thread.OP === this || !(post = posts[this.fullID]) || post.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); } } - keys = Object.keys(uniq); if (keys.length !== 1) { return; } - this.threaded = "" + g.BOARD + "." + keys[0]; + this.threaded = keys[0]; return this.cb = QuoteThreading.nodeinsert; }, - nodeinsert: function() { - var bottom, height, qpost, qroot, threadContainer, top, _ref; - qpost = g.posts[this.threaded]; - delete this.threaded; - delete this.cb; - if (this.thread.OP === qpost) { + nodeinsert: function(force) { + var bottom, height, post, posts, root, threadContainer, top, _ref; + post = g.posts[this.threaded]; + if (this.thread.OP === post) { return false; } - if (QuoteThreading.hasRun) { + posts = Unread.posts; + root = post.nodes.root; + if (!force) { height = doc.clientHeight; - _ref = qpost.nodes.root.getBoundingClientRect(), bottom = _ref.bottom, top = _ref.top; - if (!(Unread.posts.contains(qpost) || ((bottom < height) && (top > 0)))) { + _ref = root.getBoundingClientRect(), bottom = _ref.bottom, top = _ref.top; + if (!((Conf['Unread Count'] && posts[post.ID]) || ((bottom < height) && (top > 0)))) { return false; } } - qroot = qpost.nodes.root; - if (!$.hasClass(qroot, 'threadOP')) { - $.addClass(qroot, 'threadOP'); + 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' }); - $.after(qroot, threadContainer); - } else { - threadContainer = qroot.nextSibling; + $.add(threadContainer, this.nodes.root); + $.after(root, threadContainer); + $.addClass(root, 'threadOP'); + } + if (!Conf['Unread Count']) { + return true; + } + if (posts[post.ID]) { + posts.after(post, this); + } else { + posts.prepend(this); } - $.add(threadContainer, this.nodes.root); return true; }, toggle: function() { - var container, containers, node, post, replies, reply, thread, _i, _j, _k, _len, _len1, _len2, _ref; - thread = $('.thread'); - replies = $$('.thread > .replyContainer, .threadContainer > .replyContainer', thread); - QuoteThreading.enabled = this.checked; - if (this.checked) { - QuoteThreading.hasRun = false; - for (_i = 0, _len = replies.length; _i < _len; _i++) { - reply = replies[_i]; - QuoteThreading.node.call(node = Get.postFromRoot(reply)); - if (node.cb) { - node.cb(); + var ID, container, containers, nodes, post, posts, thread, _i, _j, _k, _len, _len1, _len2, _ref, _ref1; + if (QuoteThreading.enabled = this.checked) { + QuoteThreading.force(); + } else { + thread = $('.thread'); + posts = []; + nodes = []; + _ref = g.posts; + for (ID in _ref) { + post = _ref[ID]; + if (!(post === post.thread.OP || post.isClone)) { + posts.push(post); } } - QuoteThreading.hasRun = true; - } else { - replies.sort(function(a, b) { - var aID, bID; - aID = Number(a.id.slice(2)); - bID = Number(b.id.slice(2)); - return aID - bID; + posts.sort(function(a, b) { + return a.ID - b.ID; }); - $.add(thread, replies); + 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]; + _ref1 = $$('.threadOP'); + for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { + post = _ref1[_k]; $.rmClass(post, 'threadOP'); } } - return Unread.update(true); }, kb: function() { var control; control = $.id('threadingControl'); - return control.click(); + control.checked = !control.checked; + return QuoteThreading.toggle.call(control); } }; @@ -4278,12 +5290,11 @@ }); } } - if (!this.quotes.contains(quoteID)) { + if (__indexOf.call(this.quotes, quoteID) < 0) { this.quotes.push(quoteID); } if (!a) { - deadlink.textContent = "" + quote + "\u00A0(Dead)"; - return; + return deadlink.textContent = "" + quote + "\u00A0(Dead)"; } $.replace(deadlink, a); if ($.hasClass(a, 'quotelink')) { @@ -4303,6 +5314,2379 @@ } }; + QR = { + 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" + }); + $.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); + switch (g.VIEW) { + case 'index': + return $.on(d, 'IndexRefresh', QR.generatePostableThreadsList); + case 'thread': + return $.on(d, 'ThreadUpdate', 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, 'has-focus'); + }, + focusout: function() { + return $.queueTask(function() { + if ($.x('ancestor::div[@id="qr"]', d.activeElement)) { + return; + } + return $.rmClass(QR.nodes.el, 'has-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'); + }, + 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.handleFile(file, isSingle, max); + } + if (!isSingle) { + return $.addClass(QR.nodes.el, 'dump'); + } + }, + 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; + if (!QR.nodes) { + return; + } + list = QR.nodes.thread; + options = [list.firstChild]; + for (thread in g.BOARD.threads) { + 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 check, dialog, elm, event, flagSelector, i, items, key, mimeTypes, name, node, nodes, save, 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', + 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); + } + check = { + jpg: 'image/jpeg', + pdf: 'application/pdf', + swf: 'application/x-shockwave-flash' + }; + mimeTypes = $('ul.rules > li').textContent.trim().match(/: (.+)/)[1].toLowerCase().replace(/\w+/g, function(type) { + return check[type] || ("image/" + type); + }); + QR.mimeTypes = mimeTypes.split(', '); + QR.mimeTypes.push(''); + 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); + } + if (flagSelector = $('.flagSelector')) { + nodes.flag = flagSelector.cloneNode(true); + nodes.flag.dataset.name = 'flag'; + nodes.flag.dataset["default"] = '0'; + $.add(nodes.form, nodes.flag); + } + $.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(dialog, 'focusin', QR.focusin); + $.on(dialog, 'focusout', QR.focusout); + $.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.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); + }, + 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() { + delete QR.req; + post.unlock(); + QR.cooldown.auto = false; + QR.status(); + 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($.id('postForm').parentNode.action, 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.'; + } + 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+)\ssecond/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() { + if (d.cookie.indexOf('pass_enabled=1') >= 0) { + return; + } + if (!(this.isEnabled = !!$.id('captchaFormPart'))) { + return; + } + return $.asap((function() { + return $.id('recaptcha_challenge_field_holder'); + }), this.ready.bind(this)); + }, + ready: function() { + var imgContainer, input, setLifetime, + _this = this; + setLifetime = function(e) { + return _this.lifetime = e.detail; + }; + $.on(window, 'captcha:timeout', setLifetime); + $.globalEval('window.dispatchEvent(new CustomEvent("captcha:timeout", {detail: RecaptchaState.timeout}))'); + $.off(window, 'captcha:timeout', setLifetime); + imgContainer = $.el('div', { + className: 'captcha-img', + title: 'Reload reCAPTCHA', + innerHTML: '' + }); + input = $.el('input', { + className: 'captcha-input field', + title: 'Verification', + autocomplete: 'off', + spellcheck: false, + tabIndex: 55 + }); + this.nodes = { + challenge: $.id('recaptcha_challenge_field_holder'), + img: imgContainer.firstChild, + input: input + }; + new MutationObserver(this.load.bind(this)).observe(this.nodes.challenge, { + childList: true + }); + $.on(imgContainer, 'click', this.reload.bind(this)); + $.on(input, 'keydown', this.keydown.bind(this)); + $.on(input, 'focus', function() { + return $.addClass(QR.nodes.el, 'focus'); + }); + $.on(input, 'blur', function() { + return $.rmClass(QR.nodes.el, 'focus'); + }); + $.get('captchas', [], function(_arg) { + var captchas; + captchas = _arg.captchas; + return _this.sync(captchas); + }); + $.sync('captchas', this.sync); + this.reload(); + $.on(input, 'blur', QR.focusout); + $.on(input, 'focus', QR.focusin); + $.addClass(QR.nodes.el, 'has-captcha'); + return $.after(QR.nodes.com.parentNode, [imgContainer, input]); + }, + 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; + 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.length; + 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', + 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; + if (this.status !== 200) { + return; + } + i = 0; + while (postObj = JSON.parse(this.response).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 ID, file, func, post, _i, _len, _ref, _ref1; + $.event('CloseMenu'); + 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; + } + _ref = g.posts; + for (ID in _ref) { + post = _ref[ID]; + _ref1 = [post].concat(post.clones); + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + post = _ref1[_i]; + file = post.file; + if (!(file && file.isImage && doc.contains(post.nodes.root))) { + continue; + } + if (ImageExpand.on && (!Conf['Expand spoilers'] && file.isSpoiler || Conf['Expand from here'] && Header.getTopOf(file.thumb) < 0)) { + continue; + } + $.queueTask(func, 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 = JSON.parse(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 = JSON.parse(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 + }); + 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 + }); + }, + 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, id, post, _ref; + enabled = Conf['prefetch'] = this.checked; + if (enabled) { + _ref = g.threads["" + g.BOARD.ID + "." + g.THREADID].posts; + for (id in _ref) { + post = _ref[id]; + ImageLoader.node.call(post); + } + } + } + }; + + 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)/ig, function(parameter) { + switch (parameter) { + case '%TURL': + return "' + encodeURIComponent(post.file.thumbURL) + '"; + case '%URL': + return "' + encodeURIComponent(post.file.URL) + '"; + case '%MD5': + return "' + encodeURIComponent(post.file.MD5) + '"; + case '%board': + return "' + encodeURIComponent(post.board) + '"; + default: + return 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']) { @@ -4628,9 +8012,15 @@ LiveLeak: { regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/, el: function(a) { - return $.el('object', { - innerHTML: "" + 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: { @@ -4642,7 +8032,7 @@ $.cache("https://mediacru.sh/" + a.dataset.uid + ".json", function() { var embed, file, files, status, type, _i, _j, _len, _len1, _ref; status = this.status; - if (![200, 304].contains(status)) { + if (status !== 200 && status !== 304) { return div.innerHTML = "ERROR " + status; } files = JSON.parse(this.response).files; @@ -4785,9 +8175,12 @@ YouTube: { regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/, el: function(a) { - return $.el('iframe', { + 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) { @@ -4801,2362 +8194,6 @@ } }; - QR = { - init: function() { - var sc; - if (!Conf['Quick Reply']) { - return; - } - this.db = new DataBoard('yourPosts'); - if (Conf['QR Shortcut']) { - sc = $.el('a', { - className: "qr-shortcut fourchanx-icon icon-comment " + (!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" - }); - $.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 $.on(d, 'ThreadUpdate', 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, 'has-focus'); - }, - focusout: function() { - return $.queueTask(function() { - if ($.x('ancestor::div[@id="qr"]', d.activeElement)) { - return; - } - return $.rmClass(QR.nodes.el, 'has-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; - }, - 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; - 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' && !((boards.split(',')).contains(g.BOARD.ID))) { - 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 (!types[type].contains(val)) { - 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); - }); - } - }, - cooldown: { - init: function() { - var key, setTimers, type, _base, - _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}))'); - (_base = QR.cooldown).types || (_base.types = {}); - $.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 ('timeout' in cooldown) { - 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(); - } - } - }, - 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'); - }, - 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.handleFile(file, isSingle, max); - } - if (!isSingle) { - return $.addClass(QR.nodes.el, 'dump'); - } - }, - handleFile: function(file, isSingle, max) { - var post; - if (file.size > max) { - QR.error("" + file.name + ": File too large (file: " + ($.bytesToString(file.size)) + ", max: " + ($.bytesToString(max)) + ")."); - return; - } else if (!QR.mimeTypes.contains(file.type)) { - 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) { - 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 && ![32, 13].contains(e.keyCode)) || e.ctrlKey) { - return; - } - e.preventDefault(); - return QR.nodes.fileInput.click(); - }, - posts: [], - 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; - 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; - - })(), - captcha: { - init: function() { - if (d.cookie.indexOf('pass_enabled=1') >= 0) { - return; - } - if (!(this.isEnabled = !!$.id('captchaFormPart'))) { - return; - } - return $.asap((function() { - return $.id('recaptcha_challenge_field_holder'); - }), this.ready.bind(this)); - }, - ready: function() { - var imgContainer, input, setLifetime, - _this = this; - setLifetime = function(e) { - return _this.lifetime = e.detail; - }; - $.on(window, 'captcha:timeout', setLifetime); - $.globalEval('window.dispatchEvent(new CustomEvent("captcha:timeout", {detail: RecaptchaState.timeout}))'); - $.off(window, 'captcha:timeout', setLifetime); - imgContainer = $.el('div', { - className: 'captcha-img', - title: 'Reload', - innerHTML: '' - }); - input = $.el('input', { - className: 'captcha-input field', - title: 'Verification', - autocomplete: 'off', - spellcheck: false, - tabIndex: 55 - }); - this.nodes = { - challenge: $.id('recaptcha_challenge_field_holder'), - img: imgContainer.firstChild, - input: input - }; - new MutationObserver(this.load.bind(this)).observe(this.nodes.challenge, { - childList: true - }); - $.on(imgContainer, 'click', this.reload.bind(this)); - $.on(input, 'keydown', this.keydown.bind(this)); - $.on(input, 'focus', function() { - return $.addClass(QR.nodes.el, 'focus'); - }); - $.on(input, 'blur', function() { - return $.rmClass(QR.nodes.el, 'focus'); - }); - $.get('captchas', [], function(_arg) { - var captchas; - captchas = _arg.captchas; - return _this.sync(captchas); - }); - $.sync('captchas', this.sync); - this.reload(); - $.on(input, 'blur', QR.focusout); - $.on(input, 'focus', QR.focusin); - $.addClass(QR.nodes.el, 'has-captcha'); - return $.after(QR.nodes.com.parentNode, [imgContainer, input]); - }, - 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; - 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.length; - 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(); - } - }, - dialog: function() { - var check, dialog, elm, event, flagSelector, i, items, key, mimeTypes, name, node, nodes, save, thread, 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', - 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); - } - check = { - jpg: 'image/jpeg', - pdf: 'application/pdf', - swf: 'application/x-shockwave-flash' - }; - mimeTypes = $('ul.rules > li').textContent.trim().match(/: (.+)/)[1].toLowerCase().replace(/\w+/g, function(type) { - return check[type] || ("image/" + type); - }); - QR.mimeTypes = mimeTypes.split(', '); - QR.mimeTypes.push(''); - 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); - } - if (flagSelector = $('.flagSelector')) { - nodes.flag = flagSelector.cloneNode(true); - nodes.flag.dataset.name = 'flag'; - nodes.flag.dataset["default"] = '0'; - $.add(nodes.form, nodes.flag); - } - for (thread in g.BOARD.threads) { - $.add(nodes.thread, $.el('option', { - value: thread, - textContent: "Thread No." + thread - })); - } - $.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(dialog, 'focusin', QR.focusin); - $.on(dialog, 'focusout', QR.focusout); - $.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.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.persona.init(); - new QR.post(true); - QR.status(); - QR.cooldown.init(); - QR.captcha.init(); - $.add(d.body, dialog); - return $.event('QRDialogCreation', null, dialog); - }, - 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() { - delete QR.req; - post.unlock(); - QR.cooldown.auto = false; - QR.status(); - 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($.id('postForm').parentNode.action, 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.'; - } - 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+)\ssecond/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(); - } - }; - - 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; - if (!(Conf['Fappe Tyme'] || Conf['Werk Tyme']) || g.VIEW === 'catalog' || g.BOARD === 'f') { - return; - } - if (Conf['Fappe Tyme']) { - el = $.el('label', { - innerHTML: " Fappe Tyme", - title: 'Fappe Tyme' - }); - FappeTyme.fappe = input = el.firstElementChild; - $.on(input, 'change', FappeTyme.cb.fappe); - $.event('AddMenuEntry', { - type: 'header', - el: el, - order: 97 - }); - } - if (Conf['Werk Tyme']) { - el = $.el('label', { - innerHTML: " Werk Tyme", - title: 'Werk Tyme' - }); - FappeTyme.werk = input = el.firstElementChild; - $.on(input, 'change', FappeTyme.cb.werk); - $.event('AddMenuEntry', { - type: 'header', - el: el, - order: 98 - }); - } - return Post.callbacks.push({ - name: 'Fappe Tyme', - cb: this.node - }); - }, - node: function() { - if (this.file) { - return; - } - return $.addClass(this.nodes.root, "noFile"); - }, - cb: { - fappe: function() { - $.toggleClass(doc, 'fappeTyme'); - return FappeTyme.fappe.checked = $.hasClass(doc, 'fappeTyme'); - }, - werk: function() { - $.toggleClass(doc, 'werkTyme'); - return FappeTyme.werk.checked = $.hasClass(doc, 'werkTyme'); - } - } - }; - - 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: 'fourchanx-icon icon-picture', - 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; - if (this.status !== 200) { - return; - } - i = 0; - while (postObj = JSON.parse(this.response).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 (['Fit Width', 'Fit Height', 'Hide Thumbnails'].contains(name)) { - $.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 fourchanx-icon icon-resize-full', - textContent: 'EAI', - title: 'Expand All Images', - href: 'javascript:;' - }); - $.on(this.EAI, 'click', ImageExpand.cb.toggleAll); - Header.addShortcut(this.EAI, 2); - 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 ID, file, func, post, _i, _len, _ref, _ref1; - $.event('CloseMenu'); - if (ImageExpand.on = $.hasClass(ImageExpand.EAI, 'expand-all-shortcut')) { - ImageExpand.EAI.className = 'contract-all-shortcut fourchanx-icon icon-resize-small'; - ImageExpand.EAI.title = 'Contract All Images'; - func = ImageExpand.expand; - } else { - ImageExpand.EAI.className = 'expand-all-shortcut fourchanx-icon icon-resize-full'; - ImageExpand.EAI.title = 'Expand All Images'; - func = ImageExpand.contract; - } - _ref = g.posts; - for (ID in _ref) { - post = _ref[ID]; - _ref1 = [post].concat(post.clones); - for (_i = 0, _len = _ref1.length; _i < _len; _i++) { - post = _ref1[_i]; - file = post.file; - if (!(file && file.isImage && doc.contains(post.nodes.root))) { - continue; - } - if (ImageExpand.on && (!Conf['Expand spoilers'] && file.isSpoiler || Conf['Expand from here'] && file.thumb.getBoundingClientRect().top < 0)) { - continue; - } - $.queueTask(func, post); - } - } - }, - setFitness: function() { - return (this.checked ? $.addClass : $.rmClass)(doc, this.name.toLowerCase().replace(/\s+/g, '-')); - } - }, - toggle: function(post) { - var headRect, rect, root, thumb, x, y; - thumb = post.file.thumb; - if (!(post.file.isExpanded || $.hasClass(thumb, 'expanding'))) { - ImageExpand.expand(post); - return; - } - root = post.nodes.root; - rect = (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(); - if (rect.top < 0) { - y = rect.top; - if (Conf['Fixed Header'] && !Conf['Bottom Header']) { - headRect = Header.bar.getBoundingClientRect(); - y -= headRect.top + headRect.height; - } - } - if (rect.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 prev, 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; - } - prev = post.nodes.root.getBoundingClientRect(); - return $.queueTask(function() { - var curr; - $.addClass(post.nodes.root, 'expanded-image'); - $.rmClass(post.file.thumb, 'expanding'); - if (!(prev.top + prev.height <= 0)) { - return; - } - curr = post.nodes.root.getBoundingClientRect(); - return window.scrollBy(0, curr.height - prev.height + curr.top - prev.top); - }); - }, - 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 = JSON.parse(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 = JSON.parse(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 - }); - 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 - }); - }, - 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, id, post, _ref; - enabled = Conf['prefetch'] = this.checked; - if (enabled) { - _ref = g.threads["" + g.BOARD.ID + "." + g.THREADID].posts; - for (id in _ref) { - post = _ref[id]; - ImageLoader.node.call(post); - } - } - } - }; - - 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)/ig, function(parameter) { - switch (parameter) { - case '%TURL': - return "' + encodeURIComponent(post.file.thumbURL) + '"; - case '%URL': - return "' + encodeURIComponent(post.file.URL) + '"; - case '%MD5': - return "' + encodeURIComponent(post.file.MD5) + '"; - case '%board': - return "' + encodeURIComponent(post.board) + '"; - default: - return 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); - } - }; - ArchiveLink = { init: function() { var div, entry, type, _i, _len, _ref; @@ -7403,23 +8440,28 @@ }, node: function() { if (this.isClone) { - return $.on($('.menu-button', this.nodes.info), 'click', Menu.toggle); - } else { - return $.add(this.nodes.info, [$.tn('\u00A0'), Menu.makeButton()]); + $.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 brackets-wrap', - innerHTML: '', - href: 'javascript:;' - }); + var frag; + frag = null; return function() { - var button; - button = a.cloneNode(true); - $.on(button, 'click', Menu.toggle); - return button; + var clone; + if (frag == null) { + frag = $.nodes([ + $.tn(' '), $.el('a', { + className: 'menu-button', + innerHTML: '[]', + href: 'javascript:;' + }) + ]); + } + clone = frag.cloneNode(true); + $.on(clone.lastElementChild, 'click', Menu.toggle); + return clone; }; })(), toggle: function(e) { @@ -7868,7 +8910,7 @@ } }, update: function() { - var url; + var url, _ref; if (!navigator.onLine) { return; } @@ -7878,8 +8920,8 @@ } else { ThreadUpdater.set('timer', 'Update'); } - if (ThreadUpdater.req) { - ThreadUpdater.req.abort(); + if ((_ref = ThreadUpdater.req) != null) { + _ref.abort(); } url = "//a.4cdn.org/" + ThreadUpdater.thread.board + "/res/" + ThreadUpdater.thread + ".json"; return ThreadUpdater.req = $.ajax(url, { @@ -7888,38 +8930,21 @@ whenModified: true }); }, - updateThreadStatus: function(title, OP) { - var icon, message, root, titleLC; - titleLC = title.toLowerCase(); - if (ThreadUpdater.thread["is" + title] === !!OP[titleLC]) { + updateThreadStatus: function(type, status) { + var change, hasChanged; + if (!(hasChanged = ThreadUpdater.thread["is" + type] !== status)) { return; } - if (!(ThreadUpdater.thread["is" + title] = !!OP[titleLC])) { - message = title === 'Sticky' ? 'The thread is not a sticky anymore.' : 'The thread is not closed anymore.'; - new Notice('info', message, 30); - $.rm($("." + titleLC + "Icon", ThreadUpdater.thread.OP.nodes.info)); - return; - } - message = title === 'Sticky' ? 'The thread is now a sticky.' : 'The thread is now closed.'; - new Notice('info', message, 30); - icon = $.el('img', { - src: "//static.4chan.org/image/" + titleLC + ".gif", - alt: title, - title: title, - className: "" + titleLC + "Icon" - }); - root = $('[title="Quote this post"]', ThreadUpdater.thread.OP.nodes.info); - if (title === 'Closed') { - root = $('.stickyIcon', ThreadUpdater.thread.OP.nodes.info) || root; - } - return $.after(root, [$.tn(' '), icon]); + 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 ID, OP, count, deletedFiles, deletedPosts, files, index, key, node, num, post, postObject, posts, root, scroll, _i, _len, _ref; OP = postObjects[0]; Build.spoilerRange[ThreadUpdater.thread.board] = OP.custom_spoiler; - ThreadUpdater.updateThreadStatus('Sticky', OP); - ThreadUpdater.updateThreadStatus('Closed', OP); + ThreadUpdater.updateThreadStatus('Sticky', !!OP.sticky); + ThreadUpdater.updateThreadStatus('Closed', !!OP.closed); ThreadUpdater.thread.postLimit = !!OP.bumplimit; ThreadUpdater.thread.fileLimit = !!OP.imagelimit; posts = []; @@ -7946,12 +8971,12 @@ for (ID in _ref) { post = _ref[ID]; ID = +ID; - if (post.isDead && index.contains(ID)) { - post.resurrect(); - } else if (!index.contains(ID)) { + if (__indexOf.call(index, ID) < 0) { post.kill(); deletedPosts.push(post); - } else if (post.file && !post.file.isDead && !files.contains(ID)) { + } else if (post.isDead) { + post.resurrect(); + } else if (post.file && !(post.file.isDead || __indexOf.call(files, ID) >= 0)) { post.kill(true); deletedFiles.push(post); } @@ -7983,7 +9008,7 @@ } root = post.nodes.root; if (post.cb) { - if (!post.cb.call(post)) { + if (!post.cb()) { $.add(ThreadUpdater.root, root); } } else { @@ -7995,7 +9020,7 @@ window.scrollTo(0, d.body.clientHeight); } else { if (root) { - Header.scrollToPost(root); + Header.scrollTo(root); } } } @@ -8028,7 +9053,7 @@ id: 'watcher-link', textContent: 'Watcher', href: 'javascript:;', - className: 'disabled fourchanx-icon icon-eye-open' + className: 'disabled fa fa-eye-open' }); this.db = new DataBoard('watchedThreads', this.refresh, true); this.dialog = UI.dialog('thread-watcher', 'top: 50px; left: 0px;', "
Thread Watcher ×
"); @@ -8041,6 +9066,13 @@ $.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'); @@ -8149,7 +9181,32 @@ return ThreadWatcher.add(board.threads[threadID]); } }, - threadUpdate: function(e) { + 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({ @@ -8200,7 +9257,10 @@ return; } if (Conf['Auto Prune']) { - ThreadWatcher.rm(boardID, threadID); + ThreadWatcher.db["delete"]({ + boardID: boardID, + threadID: threadID + }); } else { data.isDead = true; ThreadWatcher.db.set({ @@ -8238,7 +9298,7 @@ makeLine: function(boardID, threadID, data) { var div, fullID, href, link, x; x = $.el('a', { - textContent: '×', + className: 'fa fa-times', href: 'javascript:;' }); $.on(x, 'click', ThreadWatcher.cb.rm); @@ -8390,7 +9450,7 @@ }); }, addMenuEntries: function() { - var cb, conf, entries, entry, name, refresh, subEntries, _i, _len, _ref, _ref1, _results; + var cb, conf, entries, entry, name, refresh, subEntries, _i, _len, _ref, _ref1; entries = []; entries.push({ cb: ThreadWatcher.cb.openAll, @@ -8443,7 +9503,6 @@ subEntries: subEntries } }); - _results = []; 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') { @@ -8455,9 +9514,8 @@ if (refresh) { this.refreshers.push(refresh.bind(entry)); } - _results.push($.event('AddMenuEntry', entry)); + $.event('AddMenuEntry', entry); } - return _results; }, createSubEntry: function(name, desc) { var entry, input; @@ -8488,7 +9546,7 @@ this.hr = $.el('hr', { id: 'unread-line' }); - this.posts = []; + this.posts = new RandomAccessList; this.postsQuotingYou = []; return Thread.callbacks.push({ name: 'Unread', @@ -8522,17 +9580,19 @@ } } Unread.addPosts(posts); - return Unread.scroll(); + if (Conf['Quote Threading']) { + QuoteThreading.force(); + } + if (Conf['Scroll to Last Read Post']) { + return Unread.scroll(); + } }, scroll: function() { - var checkPosition, hash, onload, post, posts, root; - if (!Conf['Scroll to Last Read Post']) { - return; - } + var down, hash, post, posts, root; if ((hash = location.hash.match(/\d+/)) && hash[0] in Unread.thread.posts) { return; } - if (post = Unread.posts[0]) { + if (post = Unread.posts.first) { while (root = $.x('preceding-sibling::div[contains(@class,"replyContainer")][1]', post.nodes.root)) { if (!(post = Get.postFromRoot(root)).isHidden) { break; @@ -8541,27 +9601,17 @@ if (!root) { return; } - onload = function() { - if (checkPosition(root)) { - return root.scrollIntoView(false); - } - }; + down = true; } else { posts = Object.keys(Unread.thread.posts); root = Unread.thread.posts[posts[posts.length - 1]].nodes.root; - onload = function() { - if (checkPosition(root)) { - return Header.scrollToPost(root); - } - }; } - checkPosition = function(target) { - return target.getBoundingClientRect().bottom > doc.clientHeight; - }; - return $.on(window, 'load', onload); + if (Header.getBottomOf(root) < 0) { + return Header.scrollTo(root, down); + } }, sync: function() { - var lastReadPost; + var ID, lastReadPost, post; lastReadPost = Unread.db.get({ boardID: Unread.thread.board.ID, threadID: Unread.thread.ID, @@ -8571,7 +9621,14 @@ return; } Unread.lastReadPost = lastReadPost; - Unread.readArray(Unread.posts); + 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(); @@ -8579,28 +9636,24 @@ return Unread.update(); }, addPosts: function(posts) { - var ID, data, post, _i, _len; + var ID, post, _i, _len, _ref; for (_i = 0, _len = posts.length; _i < _len; _i++) { post = posts[_i]; ID = post.ID; - if (ID <= Unread.lastReadPost || post.isHidden) { + if (ID <= Unread.lastReadPost || post.isHidden || QR.db.get({ + boardID: post.board.ID, + threadID: post.thread.ID, + postID: ID + })) { continue; } - if (QR.db) { - data = { - boardID: post.board.ID, - threadID: post.thread.ID, - postID: post.ID - }; - if (QR.db.get(data)) { - continue; - } + if (!(post.prev || post.next)) { + Unread.posts.push(post); } - Unread.posts.push(post); Unread.addPostQuotingYou(post); } if (Conf['Unread Line']) { - Unread.setLine(posts.contains(Unread.posts[0])); + Unread.setLine((_ref = Unread.posts.first, __indexOf.call(posts, _ref) >= 0)); } Unread.read(); return Unread.update(); @@ -8632,7 +9685,7 @@ icon: Favicon.logo }); notif.onclick = function() { - Header.scrollToPost(post.nodes.root); + Header.scrollToIfNeeded(post.nodes.root, true); return window.focus(); }; return notif.onshow = function() { @@ -8649,15 +9702,16 @@ } }, readSinglePost: function(post) { - var i; - if ((i = Unread.posts.indexOf(post)) === -1) { + var ID, i; + ID = post.ID; + if (!Unread.posts[ID]) { return; } - Unread.posts.splice(i, 1); - if (i === 0) { - Unread.lastReadPost = post.ID; + if (post === Unread.posts.first) { + Unread.lastReadPost = ID; Unread.saveLastReadPost(); } + Unread.posts.rm(ID); if ((i = Unread.postsQuotingYou.indexOf(post)) !== -1) { Unread.postsQuotingYou.splice(i, 1); } @@ -8673,35 +9727,22 @@ } return arr.splice(0, i); }, - read: $.debounce(50, function(e) { - var ID, height, i, post, posts; + read: $.debounce(100, function(e) { + var ID, height, post, posts; if (d.hidden || !Unread.posts.length) { return; } height = doc.clientHeight; posts = Unread.posts; - i = 0; - while (post = posts[i]) { - if (post.nodes.root.getBoundingClientRect().bottom < height) { - ID = post.ID; - if (Conf['Mark Quotes of You']) { - if (post.info.yours) { - QuoteYou.lastRead = post.nodes.root; - } - } - if (Conf['Quote Threading']) { - posts.splice(i, 1); - continue; - } - } else { - if (!Conf['Quote Threading']) { - break; - } + while (post = posts.first) { + if (!(Header.getBottomOf(post.nodes.root) > -1)) { + break; + } + ID = post.ID; + posts.rm(ID); + if (Conf['Mark Quotes of You'] && post.info.yours) { + QuoteYou.lastRead = post.nodes.root; } - i++; - } - if (i && !Conf['Quote Threading']) { - posts.splice(0, i); } if (!ID) { return; @@ -8730,7 +9771,7 @@ if (!(d.hidden || force === true)) { return; } - if (!(post = Unread.posts[0])) { + if (!(post = Unread.posts.first)) { return $.rm(Unread.hr); } if ($.x('preceding-sibling::div[contains(@class,"replyContainer")]', post.nodes.root)) { @@ -8752,125 +9793,143 @@ }; Redirect = { - data: { - thread: {}, - post: {}, - file: {} - }, init: function() { - var archive, boardID, boards, data, id, name, type, _i, _len, _ref, _ref1, _ref2; + var archive, archives, boardID, data, id, name, o, type, _i, _len, _ref, _ref1; + o = { + thread: {}, + post: {}, + file: {} + }; + archives = Redirect.archives; _ref = Conf['selectedArchives']; for (boardID in _ref) { data = _ref[boardID]; for (type in data) { id = data[type]; - if (archive = Redirect.archives[id]) { - boards = archive[type] || archive['boards']; - if (!boards.contains(boardID)) { - continue; - } - Redirect.data[type][boardID] = archive; + if ((archive = archives[id]) && __indexOf.call(archive[type] || archive['boards'], boardID) >= 0) { + o[type][boardID] = archive.data; } } } - _ref1 = Redirect.archives; - for (name in _ref1) { - archive = _ref1[name]; - _ref2 = archive.boards; - for (_i = 0, _len = _ref2.length; _i < _len; _i++) { - boardID = _ref2[_i]; - if (!(boardID in Redirect.data.thread)) { - Redirect.data.thread[boardID] = archive; + for (name in archives) { + archive = archives[name]; + _ref1 = archive.boards; + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + boardID = _ref1[_i]; + data = archive.data; + if (!(boardID in o.thread)) { + o.thread[boardID] = data; } - if (!(boardID in Redirect.data.post || archive.software !== 'foolfuuka')) { - Redirect.data.post[boardID] = archive; + if (!(boardID in o.post || archive.software !== 'foolfuuka')) { + o.post[boardID] = data; } - if (!(boardID in Redirect.data.file || !archive.files.contains(boardID))) { - Redirect.data.file[boardID] = archive; + if (!(boardID in o.file || __indexOf.call(archive.files, boardID) < 0)) { + o.file[boardID] = data; } } } + return Redirect.data = o; }, archives: { "Foolz": { - domain: "archive.foolz.us", - http: false, - https: true, - software: "foolfuuka", boards: ["a", "co", "gd", "jp", "m", "sp", "tg", "tv", "v", "vg", "vp", "vr", "wsg"], - files: ["a", "gd", "jp", "m", "tg", "vg", "vp", "vr", "wsg"] + files: ["a", "gd", "jp", "m", "tg", "vg", "vp", "vr", "wsg"], + data: { + domain: "archive.foolz.us", + http: false, + https: true, + software: "foolfuuka" + } }, "NSFW Foolz": { - domain: "nsfw.foolz.us", - http: false, - https: true, - software: "foolfuuka", boards: ["u"], - files: ["u"] + files: ["u"], + data: { + domain: "nsfw.foolz.us", + http: false, + https: true, + software: "foolfuuka" + } }, "The Dark Cave": { - domain: "archive.thedarkcave.org", - http: true, - https: true, - software: "foolfuuka", boards: ["c", "int", "out", "po"], - files: ["c", "po"] + files: ["c", "po"], + data: { + domain: "archive.thedarkcave.org", + http: true, + https: true, + software: "foolfuuka" + } }, "4plebs": { - domain: "archive.4plebs.org", - http: true, - https: true, - software: "foolfuuka", boards: ["hr", "pol", "s4s", "tg", "tv", "x"], - files: ["hr", "pol", "s4s", "tg", "tv", "x"] + files: ["hr", "pol", "s4s", "tg", "tv", "x"], + data: { + domain: "archive.4plebs.org", + http: true, + https: true, + software: "foolfuuka" + } }, "Nyafuu": { - domain: "archive.nyafuu.org", - http: true, - https: true, - software: "foolfuuka", boards: ["c", "w", "wg"], - files: ["c", "w", "wg"] + files: ["c", "w", "wg"], + data: { + domain: "archive.nyafuu.org", + http: true, + https: true, + software: "foolfuuka" + } }, "Install Gentoo": { - domain: "archive.installgentoo.net", - http: false, - https: true, - software: "fuuka", boards: ["diy", "g", "sci"], - files: [] + files: [], + data: { + domain: "archive.installgentoo.net", + http: false, + https: true, + software: "fuuka" + } }, "Rebecca Black Tech": { - domain: "rbt.asia", - http: true, - https: true, - software: "fuuka", boards: ["cgl", "g", "mu", "w"], - files: ["cgl", "g", "mu", "w"] + files: ["cgl", "g", "mu", "w"], + data: { + domain: "rbt.asia", + http: true, + https: true, + software: "fuuka" + } }, "Heinessen": { - domain: "archive.heinessen.com", - http: true, - software: "fuuka", boards: ["an", "fit", "k", "mlp", "r9k", "toy"], - files: ["an", "fit", "k", "r9k", "toy"] + files: ["an", "fit", "k", "r9k", "toy"], + data: { + domain: "archive.heinessen.com", + http: true, + software: "fuuka" + } }, "warosu": { - domain: "fuuka.warosu.org", - http: true, - https: true, - software: "fuuka", boards: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"], - files: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"] + files: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"], + data: { + domain: "fuuka.warosu.org", + http: true, + https: true, + software: "fuuka" + } }, "Foolz Beta": { - domain: "beta.foolz.us", - http: true, - https: true, - withCredentials: true, - software: "foolfuuka", - boards: ["a", "co", "gd", "jp", "m", "s4s", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"], - files: ["a", "gd", "jp", "m", "s4s", "tg", "u", "vg", "vp", "vr", "wsg"] + boards: ["a", "co", "d", "gd", "h", "jp", "m", "mlp", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"], + files: ["a", "d", "gd", "h", "jp", "m", "tg", "u", "vg", "vp", "vr", "wsg"], + data: { + domain: "beta.foolz.us", + http: true, + https: true, + withCredentials: true, + software: "foolfuuka" + } } }, to: function(dest, data) { @@ -9101,11 +10160,10 @@ if (!Conf['Catalog Links']) { return; } - el = $.el('label', { + CatalogLinks.el = el = $.el('label', { id: 'toggleCatalog', href: 'javascript:;', - innerHTML: " Catalog Links", - title: "Turn catalog links " + (Conf['Header catalog links'] ? 'off' : 'on') + "." + innerHTML: " Catalog Links" }); input = $('input', el); $.on(input, 'change', this.toggle); @@ -9120,31 +10178,87 @@ }); }, toggle: function() { - var useCatalog; $.event('CloseMenu'); - $.set('Header catalog links', useCatalog = this.checked); - return CatalogLinks.set(useCatalog); + $.set('Header catalog links', this.checked); + return CatalogLinks.set(this.checked); }, set: function(useCatalog) { - var a, board, path, _i, _len, _ref; + var a, board, generateURL, path, _i, _len, _ref, _ref1; path = useCatalog ? 'catalog' : ''; - _ref = $$("#board-list a:not(.catalog),\n#boardNavDesktopFoot a"); + 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]; - board = a.pathname.split('/')[1]; - if (['f', 'status', '4chan'].contains(board) || !board) { + 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; } - if (Conf['External Catalog']) { - a.href = useCatalog ? CatalogLinks.external(board) : "/" + board + "/"; - } else { - a.pathname = "/" + board + "/" + path; - } + a.href = generateURL(board); } - return this.title = "Turn catalog links " + (useCatalog ? 'off' : 'on') + "."; + return CatalogLinks.el.title = "Turn catalog links " + (useCatalog ? 'off' : 'on') + "."; }, external: function(board) { - return (['a', 'c', 'g', 'co', 'k', 'm', 'o', 'p', 'v', 'vg', 'w', 'cm', '3', 'adv', 'an', 'cgl', 'ck', 'diy', 'fa', 'fit', 'int', 'jp', 'mlp', 'lit', 'mu', 'n', 'po', 'sci', 'toy', 'trv', 'tv', 'vp', 'x', 'q'].contains(board) ? "http://catalog.neet.tv/" + board : ['d', 'e', 'gif', 'h', 'hr', 'hc', 'r9k', 's', 'pol', 'soc', 'u', 'i', 'ic', 'hm', 'r', 'w', 'wg', 'wsg', 't', 'y'].contains(board) ? "http://4index.gropes.us/" + board : "/" + board + "/catalog"); + 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"; + } } }; @@ -9302,7 +10416,7 @@ 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 (![200, 304].contains(status)) { + if (status !== 200 && status !== 304) { a.textContent = "Error " + req.statusText + " (" + status + ")"; return; } @@ -9350,146 +10464,140 @@ if (g.VIEW !== 'index' || !Conf['Thread Expansion']) { return; } - return Thread.callbacks.push({ - name: 'Thread Expansion', - cb: this.node - }); + this.statuses = {}; + return $.on(d, 'IndexRefresh', this.onIndexRefresh); }, - node: function() { - var a, files, posts, span, _ref; - if (!(span = $.x('following-sibling::span[contains(@class,"summary")][1]', this.OP.nodes.root))) { + setButton: function(thread) { + var a; + if (!(a = $.x('following-sibling::a[contains(@class,"summary")][1]', thread.OP.nodes.root))) { return; } - _ref = span.textContent.match(/\d+/g), posts = _ref[0], files = _ref[1]; - a = $.el('a', { - textContent: ExpandThread.text('+', posts, files), - className: 'summary', - href: 'javascript:;' - }); - $.on(a, 'click', ExpandThread.cbToggle); - return $.replace(span, a); + a.textContent = ExpandThread.text.apply(ExpandThread, ['+'].concat(__slice.call(a.textContent.match(/\d+/g)))); + return $.on(a, 'click', ExpandThread.cbToggle); + }, + onIndexRefresh: function() { + var status, thread, threadID, _ref, _ref1, _ref2; + _ref = ExpandThread.statuses; + for (threadID in _ref) { + status = _ref[threadID]; + if ((_ref1 = status.req) != null) { + _ref1.abort(); + } + delete ExpandThread.statuses[threadID]; + } + _ref2 = g.BOARD.threads; + for (threadID in _ref2) { + thread = _ref2[threadID]; + 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() { + 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, files, filesCount, inlined, num, post, posts, postsCount, reply, threadRoot, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3, _ref4; + var a, threadRoot; threadRoot = thread.OP.nodes.root.parentNode; - a = $('.summary', threadRoot); - switch (thread.isExpanded) { - case false: - case void 0: - _ref = $$('.thread > .postContainer', threadRoot); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - post = _ref[_i]; - ExpandComment.expand(Get.postFromRoot(post)); - } - if (!a) { - thread.isExpanded = true; - return; - } - thread.isExpanded = 'loading'; - _ref1 = a.textContent.match(/\d+/g), posts = _ref1[0], files = _ref1[1]; - a.textContent = ExpandThread.text('...', posts, files); - $.cache("//a.4cdn.org/" + thread.board + "/res/" + thread + ".json", function() { - return ExpandThread.parse(this, thread, a); - }); - break; - case 'loading': - thread.isExpanded = false; - if (!a) { - return; - } - _ref2 = a.textContent.match(/\d+/g), posts = _ref2[0], files = _ref2[1]; - a.textContent = ExpandThread.text('+', posts, files); - break; - case true: - thread.isExpanded = false; - 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; - } - } - })(); - posts = $$(".thread > .replyContainer", threadRoot); - _ref3 = [thread.OP.nodes.root].concat(posts.slice(-num)); - for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) { - post = _ref3[_j]; - ExpandComment.contract(Get.postFromRoot(post)); - } - if (!a) { - return; - } - postsCount = 0; - filesCount = 0; - _ref4 = posts.slice(0, -num); - for (_k = 0, _len2 = _ref4.length; _k < _len2; _k++) { - reply = _ref4[_k]; - if (Conf['Quote Inlining']) { - while (inlined = $('.inlined', reply)) { - inlined.click(); - } - } - postsCount++; - if ('file' in Get.postFromRoot(reply)) { - filesCount++; - } - $.rm(reply); - } - a.textContent = ExpandThread.text('+', postsCount, filesCount); + 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, link, post, posts, postsCount, postsObj, postsRoot, reply, root, spoilerRange, _i, _len; - if (a.textContent[0] === '+') { - return; - } - if (![200, 304].contains(req.status)) { + var data, filesCount, post, postData, posts, postsCount, postsRoot, root, _i, _len, _ref; + if ((_ref = req.status) !== 200 && _ref !== 304) { a.textContent = "Error " + req.statusText + " (" + req.status + ")"; - $.off(a, 'click', ExpandThread.cbToggle); return; } - thread.isExpanded = true; - posts = JSON.parse(req.response).posts; - if (spoilerRange = posts.shift().custom_spoiler) { - Build.spoilerRange[thread.board] = spoilerRange; - } - postsObj = []; + data = JSON.parse(req.response).posts; + Build.spoilerRange[thread.board] = data.shift().custom_spoiler; + posts = []; postsRoot = []; filesCount = 0; - for (_i = 0, _len = posts.length; _i < _len; _i++) { - reply = posts[_i]; - if (post = thread.posts[reply.no]) { + for (_i = 0, _len = data.length; _i < _len; _i++) { + postData = data[_i]; + if (post = thread.posts[postData.no]) { if ('file' in post) { filesCount++; } postsRoot.push(post.nodes.root); continue; } - root = Build.postFromObject(reply, thread.board.ID); + root = Build.postFromObject(postData, thread.board.ID); post = new Post(root, thread, thread.board); - link = $('a[title="Highlight this post"]', root); - link.href = "res/" + thread + "#p" + post; - link.nextSibling.href = "res/" + thread + "#q" + post; if ('file' in post) { filesCount++; } - postsObj.push(post); + posts.push(post); postsRoot.push(root); } - Main.callbackNodes(Post, postsObj); + Main.callbackNodes(Post, posts); $.after(a, postsRoot); postsCount = postsRoot.length; a.textContent = ExpandThread.text('-', postsCount, filesCount); @@ -9512,7 +10620,7 @@ if (!this.file || this.isClone) { return; } - return this.file.text.innerHTML = FileInfo.funk(FileInfo, this); + return this.file.text.innerHTML = "" + (FileInfo.funk(FileInfo, this)) + ""; }, createFunc: function(format) { var code; @@ -9605,7 +10713,7 @@ } board = g.BOARD.ID; if (board === 'g') { - $.globalEval("window.addEventListener('prettyprint', function(e) {\n var pre = e.detail;\n pre.innerHTML = prettyPrintOne(pre.innerHTML);\n}, false);"); + $.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 @@ -9620,16 +10728,20 @@ } }, code: function() { - var pre, _i, _len, _ref; + 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, window); - $.addClass(pre, 'prettyprinted'); + $.event('prettyprint', pre.innerHTML, window); } + $.off(window, 'prettyprint:cb', apply); }, math: function() { if (this.isClone || !$('.math', this.nodes.comment)) { @@ -9869,10 +10981,13 @@ Keybinds = { init: function() { - var init; + 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); @@ -9885,14 +11000,17 @@ }; return $.on(d, '4chanXInitFinished', init); }, + sync: function(key, hotkey) { + return Conf[hotkey] = key; + }, keydown: function(e) { - var form, key, notification, notifications, op, target, thread, threadRoot, _i, _len; + var key, notification, notifications, op, target, thread, threadRoot, _i, _len, _ref; if (!(key = Keybinds.keyCode(e))) { return; } target = e.target; - if (['INPUT', 'TEXTAREA'].contains(target.nodeName)) { - if (!/(Esc|Alt|Ctrl|Meta)/.test(key)) { + if ((_ref = target.nodeName) === 'INPUT' || _ref === 'TEXTAREA') { + if (!/(Esc|Alt|Ctrl|Meta|Shift\+\w{2,})/.test(key)) { return; } } @@ -9968,12 +11086,18 @@ 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['Update']: - ThreadUpdater.update(); - break; case Conf['Expand image']: Keybinds.img(threadRoot); break; @@ -9984,32 +11108,39 @@ Gallery.cb.toggle(); break; case Conf['fappeTyme']: - FappeTyme.cb.fappe(); + FappeTyme.cb.toggle.call({ + name: 'fappe' + }); break; case Conf['werkTyme']: - FappeTyme.cb.werk(); + FappeTyme.cb.toggle.call({ + name: 'werk' + }); break; case Conf['Front page']: - window.location = "/" + g.BOARD + "/0#delform"; + if (g.VIEW === 'index') { + Index.userPageNav(0); + } else { + window.location = "/" + g.BOARD + "/"; + } break; case Conf['Open front page']: - $.open("/" + g.BOARD + "/#delform"); + $.open("/" + g.BOARD + "/"); break; case Conf['Next page']: - if (g.VIEW === 'thread') { + if (!(g.VIEW === 'index' && Conf['Index Mode'] === 'paged')) { return; } - if (form = $('.next form')) { - window.location = form.action; - } + $('.next button', Index.pagelist).click(); break; case Conf['Previous page']: - if (g.VIEW === 'thread') { + if (!(g.VIEW === 'index' && Conf['Index Mode'] === 'paged')) { return; } - if (form = $('.prev form')) { - window.location = form.action; - } + $('.prev button', Index.pagelist).click(); + break; + case Conf['Search form']: + Index.searchInput.focus(); break; case Conf['Open catalog']: if (Conf['External Catalog']) { @@ -10049,7 +11180,7 @@ Keybinds.hl(0, threadRoot); break; case Conf['Hide']: - if (g.VIEW === 'index') { + if (ThreadHiding.db) { ThreadHiding.toggle(thread); } break; @@ -10157,44 +11288,28 @@ } }, hl: function(delta, thread) { - var axe, headRect, next, postEl, rect, replies, reply, root, topMargin, _i, _len; + var axis, height, next, postEl, replies, reply, root, _i, _len; + postEl = $('.reply.highlight', thread); if (!delta) { - if (postEl = $('.reply.highlight', thread)) { + if (postEl) { $.rmClass(postEl, 'highlight'); } return; } - if (Conf['Fixed Header'] && Conf['Bottom header']) { - topMargin = 0; - } else { - headRect = Header.bar.getBoundingClientRect(); - topMargin = headRect.top + headRect.height; - } - if (postEl = $('.reply.highlight', thread)) { - $.rmClass(postEl, 'highlight'); - rect = postEl.getBoundingClientRect(); - if (rect.bottom >= topMargin && rect.top <= doc.clientHeight) { + if (postEl) { + height = postEl.getBoundingClientRect().height; + if (Header.getTopOf(postEl) >= -height && Header.getBottomOf(postEl) >= -height) { root = postEl.parentNode; - axe = delta === +1 ? 'following' : 'preceding'; - next = $.x("" + axe + "-sibling::div[contains(@class,'replyContainer')][1]/child::div[contains(@class,'reply')]", root); - if (!next) { - this.focus(postEl); + 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; } - if (!(g.VIEW === 'thread' || $.x('ancestor::div[parent::div[@class="board"]]', next) === thread)) { - return; - } - rect = next.getBoundingClientRect(); - if (rect.top < 0 || rect.bottom > doc.clientHeight) { - if (delta === -1) { - window.scrollBy(0, rect.top - topMargin); - } else { - next.scrollIntoView(false); - } - } + Header.scrollToIfNeeded(next, delta === +1); this.focus(next); + $.rmClass(postEl, 'highlight'); return; } + $.rmClass(postEl, 'highlight'); } replies = $$('.reply', thread); if (delta === -1) { @@ -10202,8 +11317,7 @@ } for (_i = 0, _len = replies.length; _i < _len; _i++) { reply = replies[_i]; - rect = reply.getBoundingClientRect(); - if (delta === +1 && rect.top >= topMargin || delta === -1 && rect.bottom <= doc.clientHeight) { + if (delta === +1 && Header.getTopOf(reply) > 0 || delta === -1 && Header.getBottomOf(reply) > 0) { this.focus(reply); return; } @@ -10265,50 +11379,58 @@ return Nav.scroll(+1); } }, - getThread: function(full) { - var headRect, i, rect, thread, threads, topMargin, _i, _len; - if (Conf['Bottom header'] || !Conf['Fixed Header']) { - topMargin = 0; - } else { - headRect = Header.bar.getBoundingClientRect(); - topMargin = headRect.top + headRect.height; - } - threads = $$('.thread').filter(function(thread) { - thread = Get.threadFromRoot(thread); - return !(thread.isHidden && !thread.stub); - }); - for (i = _i = 0, _len = threads.length; _i < _len; i = ++_i) { - thread = threads[i]; - rect = thread.getBoundingClientRect(); - if (rect.bottom > topMargin) { - if (full) { - return [threads, thread, i, rect, topMargin]; - } else { - return thread; - } + 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 i, rect, thread, threads, top, topMargin, _ref, _ref1; - _ref = Nav.getThread(true), threads = _ref[0], thread = _ref[1], i = _ref[2], rect = _ref[3], topMargin = _ref[4]; - top = rect.top - topMargin; - if ((delta === -1 && top > -5) || (delta === +1 && top < 5)) { - top = ((_ref1 = threads[i + delta]) != null ? _ref1.getBoundingClientRect().top : void 0) - topMargin; + 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 window.scrollBy(0, top); + return Header.scrollTo(thread); } }; RelativeDates = { INTERVAL: $.MINUTE / 2, init: function() { - if (g.VIEW === 'catalog' || !Conf['Relative Post Dates']) { - return; + 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; } - $.on(d, 'visibilitychange ThreadUpdate', this.flush); - this.flush(); return Post.callbacks.push({ name: 'Relative Post Dates', cb: this.node @@ -10321,7 +11443,7 @@ } dateEl = this.nodes.date; dateEl.title = dateEl.textContent; - return RelativeDates.setUpdate(this); + return RelativeDates.update(this); }, relative: function(diff, now, date) { var days, months, number, rounded, unit, years; @@ -10334,43 +11456,51 @@ }, stale: [], flush: function() { - var now, update, _i, _len, _ref; + 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++) { - update = _ref[_i]; - update(now); + data = _ref[_i]; + RelativeDates.update(data, now); } RelativeDates.stale = []; clearTimeout(RelativeDates.timeout); return RelativeDates.timeout = setTimeout(RelativeDates.flush, RelativeDates.INTERVAL); }, - setUpdate: function(post) { - var markStale, setOwnTimeout, update; - setOwnTimeout = function(diff) { - 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(markStale, delay); - }; - update = function(now) { - var date, diff, relative, singlePost, _i, _len, _ref; - date = post.info.date; - diff = now - date; - relative = RelativeDates.relative(diff, now, date); - _ref = [post].concat(post.clones); + 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; } - return setOwnTimeout(diff); - }; - markStale = function() { - return RelativeDates.stale.push(update); - }; - return update(new Date()); + } 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); } }; @@ -10538,7 +11668,7 @@ init: function() { var link, settings; link = $.el('a', { - className: 'settings-link fourchanx-icon icon-wrench', + className: 'settings-link fa fa-wrench', textContent: 'Settings', href: 'javascript:;' }); @@ -10585,7 +11715,7 @@ return; } $.event('CloseMenu'); - html = "
"; + html = "
"; Settings.overlay = overlay = $.el('div', { id: 'overlay' }); @@ -10687,7 +11817,7 @@ } }); div = $.el('div', { - innerHTML: ": Clear manually-hidden threads and posts on all boards. Refresh the page to apply." + innerHTML: ": Clear manually-hidden threads and posts on all boards. Reload the page to apply." }); button = $('button', div); hiddenNum = 0; @@ -10788,7 +11918,7 @@ try { data = JSON.parse(e.target.result); Settings.loadSettings(data); - if (confirm('Import successful. Refresh now?')) { + if (confirm('Import successful. Reload now?')) { return window.location.reload(); } } catch (_error) { @@ -10921,11 +12051,11 @@ $.add(div, ta); return; } - return div.innerHTML = "
Filter is disabled.

\nUse regular expressions, one per line.
\nLines starting with a # will be ignored.
\nFor example, /weeaboo/i will filter posts containing the string `weeaboo`, case-insensitive.
\nMD5 filtering uses exact string matching, not regular expressions.\n

"; + 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.

"; }, 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.
"; + 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.
"; ta = $('textarea', section); $.get('sauces', Conf['sauces'], function(item) { return ta.value = item['sauces']; @@ -10934,7 +12064,7 @@ }, advanced: function(section) { var archive, boardID, boardOptions, boardSelect, boards, data, event, input, inputs, item, items, name, row, rows, ta, table, _i, _j, _k, _l, _len, _len1, _len2, _len3, _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\"]
\n will give you
[ + ] [Technology] [Technology / Anime & Manga / Otaku Culture] [x / wsg / h] [Piracy]
\n if you are on /g/.\n
Time Formatting is disabled.
:
Supported format specifiers:
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 is disabled.

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

    You can use these settings with each item, separate them with semicolons:\n
  • 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.
\n Sage Icon:
\n Position:
Thread Updater is disabled.
\n Interval:
"; + 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.
:
Supported format specifiers:
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 is disabled.

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']; @@ -10943,7 +12073,7 @@ input = $("[name=" + name + "]", section); items[name] = Conf[name]; inputs[name] = input; - event = ['favicon', 'usercss', 'sageEmoji', 'emojiPos'].contains(name) ? 'change' : 'input'; + event = name === 'favicon' || name === 'usercss' || name === 'sageEmoji' || name === 'emojiPos' ? 'change' : 'input'; $.on(input, event, $.cb.value); } ta = $('.personafield', section); @@ -10955,7 +12085,7 @@ var key, val; for (key in items) { val = items[key]; - if (['emojiPos'].contains(key)) { + if (key === 'emojiPos') { continue; } input = inputs[key]; @@ -10986,7 +12116,7 @@ if (archive.software === 'foolfuuka') { data.post.push(name); } - if (archive.files.contains(boardID)) { + if (__indexOf.call(archive.files, boardID) >= 0) { data.file.push(name); } } @@ -11167,38 +12297,7 @@ Main = { init: function() { - var db, flatten, _i, _len, _ref; - 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); - _ref = DataBoard.keys; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - db = _ref[_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 init, pathname, _ref; + var db, flatten, pathname, _i, _len, _ref, _ref1; pathname = location.pathname.split('/'); g.BOARD = new Board(pathname[1]); if ((_ref = g.BOARD.ID) === 'z' || _ref === 'fk') { @@ -11217,6 +12316,37 @@ 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 init; switch (location.hostname) { case 'a.4cdn.org': return; @@ -11225,8 +12355,8 @@ return; case 'i.4cdn.org': $.ready(function() { - var URL; - if (Conf['404 Redirect'] && ['4chan - Temporarily Offline', '4chan - 404 Not Found'].contains(d.title)) { + 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', { @@ -11261,6 +12391,7 @@ 'Header': Header, 'Catalog Links': CatalogLinks, 'Settings': Settings, + 'Index Generator': Index, 'Announcement Hiding': PSAHiding, 'Fourchan thingies': Fourchan, 'Emoji': Emoji, @@ -11302,7 +12433,6 @@ 'Reveal Spoiler Thumbnails': RevealSpoilers, 'Image Loading': ImageLoader, 'Image Hover': ImageHover, - 'Comment Expansion': ExpandComment, 'Thread Expansion': ExpandThread, 'Thread Excerpt': ThreadExcerpt, 'Favicon': Favicon, @@ -11315,8 +12445,7 @@ 'Index Navigation': Nav, 'Keybinds': Keybinds, 'Show Dice Roll': Dice, - 'Banner': Banner, - 'Infinite Scrolling': InfiniScroll + 'Banner': Banner }); $.on(d, 'AddCallback', Main.addCallback); return $.ready(Main.initReady); @@ -11364,8 +12493,8 @@ }); }, initReady: function() { - var board, err, errors, href, passLink, postRoot, posts, styleSelector, thread, threadRoot, threads, _i, _j, _len, _len1, _ref, _ref1; - if (['4chan - Temporarily Offline', '4chan - 404 Not Found'].contains(d.title)) { + var GMver, err, errors, href, i, passLink, post, postRoot, posts, styleSelector, thread, threadRoot, v, _i, _j, _len, _len1, _ref, _ref1, _ref2; + 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, @@ -11377,35 +12506,31 @@ return; } Main.initStyle(); - if (board = $('.board')) { - threads = []; + if (g.VIEW === 'thread' && (threadRoot = $('.thread'))) { + thread = new Thread(+threadRoot.id.slice(1), g.BOARD); 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 - }); + _ref1 = $$('.thread > .postContainer', threadRoot); + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + postRoot = _ref1[_i]; + try { + posts.push(post = new Post(postRoot, thread, g.BOARD, { + isOriginalMarkup: true + })); + } 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.callbackNodes(Thread, [thread]); Main.callbackNodesDB(Post, posts, function() { return $.event('4chanXInitFinished'); }); @@ -11421,92 +12546,54 @@ } return; } + GMver = GM_info.version.split('.'); + _ref2 = "1.13".split('.'); + for (i = _j = 0, _len1 = _ref2.length; _j < _len1; i = ++_j) { + v = _ref2[i]; + if (v < GMver[i]) { + break; + } + if (v === GMver[i]) { + continue; + } + new Notice('warning', "Your version of Greasemonkey is outdated (v" + GM_info.version + " instead of v1.13 minimum) and 4chan X may not operate correctly.", 30); + break; + } try { - localStorage.getItem('4chan-settings'); + return localStorage.getItem('4chan-settings'); } catch (_error) { err = _error; - new Notice('warning', 'Cookies need to be enabled on 4chan for 4chan X to properly function.', 30); - Main.disableReports = true; + return new Notice('warning', 'Cookies need to be enabled on 4chan for 4chan X to operate properly.', 30); } - return $.event('4chanXInitFinished'); }, callbackNodes: function(klass, nodes) { - var callback, err, errors, i, len, node, _i, _len, _ref; - len = nodes.length; - _ref = klass.callbacks; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - callback = _ref[_i]; - i = 0; - while (i < len) { - node = nodes[i++]; - try { - callback.cb.call(node); - } catch (_error) { - err = _error; - if (!errors) { - errors = []; - } - errors.push({ - message: "\"" + callback.name + "\" crashed on " + klass.name + " No." + node + " (/" + node.board + "/).", - error: err - }); - } - } - } - if (errors) { - return Main.handleErrors(errors); + var cb, i, node; + i = 0; + cb = klass.callbacks; + while (node = nodes[i++]) { + cb.execute(node); } }, callbackNodesDB: function(klass, nodes, cb) { - var errors, func, i, len, node, queue, softTask; - queue = []; + var callbacks, errors, i, len, softTask; errors = null; - func = function(node) { - var callback, err, _i, _len, _ref; - _ref = klass.callbacks; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - callback = _ref[_i]; - try { - callback.cb.call(node); - } catch (_error) { - err = _error; - if (!errors) { - errors = []; - } - errors.push({ - message: "\"" + callback.name + "\" crashed on " + klass.name + " No." + node + " (/" + node.board + "/).", - error: err - }); - } - } - if (!queue.length) { - if (errors) { - Main.handleErrors(errors); - } - if (cb) { - return cb(); - } - } - }; + len = 0; + i = 0; + callbacks = klass.callbacks; softTask = function() { var node; - node = queue.shift(); - func(node); - if (!queue.length) { - return; + node = nodes[i++]; + callbacks.execute(node); + if (len === i && cb) { + return cb(); } - if (!(queue.length % 7)) { + if (!(i % 7)) { return setTimeout(softTask, 0); } else { return softTask(); } }; len = nodes.length; - i = 0; - while (i < len) { - node = nodes[i++]; - queue.push(node); - } return softTask(); }, addCallback: function(e) { @@ -11557,7 +12644,7 @@ }, parseError: function(data) { var error, message; - Main.logError(data); + c.error(data.message, data.error.stack); message = $.el('div', { textContent: data.message }); @@ -11566,11 +12653,6 @@ }); return [message, error]; }, - errors: [], - logError: function(data) { - c.error(data.message, data.error.stack); - return Main.errors.push(data); - }, isThisPageLegit: function() { var _ref; if (!('thisPageIsLegit' in Main)) { @@ -11578,7 +12660,7 @@ } return Main.thisPageIsLegit; }, - css: "/*! * Font Awesome 3.2.1 * 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: normal; font-style: normal; } .fourchanx-icon::before { font-family: FontAwesome; font-weight: normal; font-style: normal; text-decoration: inherit; -webkit-font-smoothing: antialiased; *margin-right: .3em; text-decoration: inherit; display: none; speak: none; } :root.shortcut-icons .fourchanx-icon::before { display: inline-block; font-size: 13px; visibility: visible; } :root.shortcut-icons #shortcuts .fourchanx-icon::before { font-size: 15px !important; margin-top: -3px !important; position: relative; top: 1px; } :root.shortcut-icons .fourchanx-icon { font-size: 0; visibility: hidden; } :root.shortcut-icons .shortcut.brackets-wrap::before, :root.shortcut-icons .shortcut.brackets-wrap::after { display: none; } /* makes sure icons active on rollover in links */ :root.shortcut-icons a .fourchanx-icon { display: inline; } /* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen readers do not read off random characters that represent icons */ .icon-glass::before { content: \"\\f000\"; } .icon-music::before { content: \"\\f001\"; } .icon-search::before { content: \"\\f002\"; } .icon-envelope-alt::before { content: \"\\f003\"; } .icon-heart::before { content: \"\\f004\"; } .icon-star::before { content: \"\\f005\"; } .icon-star-empty::before { content: \"\\f006\"; } .icon-user::before { content: \"\\f007\"; } .icon-film::before { content: \"\\f008\"; } .icon-th-large::before { content: \"\\f009\"; } .icon-th::before { content: \"\\f00a\"; } .icon-th-list::before { content: \"\\f00b\"; } .icon-ok::before { content: \"\\f00c\"; } .icon-remove::before { content: \"\\f00d\"; } .icon-zoom-in::before { content: \"\\f00e\"; } .icon-zoom-out::before { content: \"\\f010\"; } .icon-power-off:before, .icon-off::before { content: \"\\f011\"; } .icon-signal::before { content: \"\\f012\"; } .icon-gear:before, .icon-cog::before { content: \"\\f013\"; } .icon-trash::before { content: \"\\f014\"; } .icon-home::before { content: \"\\f015\"; } .icon-file-alt::before { content: \"\\f016\"; } .icon-time::before { content: \"\\f017\"; } .icon-road::before { content: \"\\f018\"; } .icon-download-alt::before { content: \"\\f019\"; } .icon-download::before { content: \"\\f01a\"; } .icon-upload::before { content: \"\\f01b\"; } .icon-inbox::before { content: \"\\f01c\"; } .icon-play-circle::before { content: \"\\f01d\"; } .icon-rotate-right:before, .icon-repeat::before { content: \"\\f01e\"; } .icon-refresh::before { content: \"\\f021\"; } .icon-list-alt::before { content: \"\\f022\"; } .icon-lock::before { content: \"\\f023\"; } .icon-flag::before { content: \"\\f024\"; } .icon-headphones::before { content: \"\\f025\"; } .icon-volume-off::before { content: \"\\f026\"; } .icon-volume-down::before { content: \"\\f027\"; } .icon-volume-up::before { content: \"\\f028\"; } .icon-qrcode::before { content: \"\\f029\"; } .icon-barcode::before { content: \"\\f02a\"; } .icon-tag::before { content: \"\\f02b\"; } .icon-tags::before { content: \"\\f02c\"; } .icon-book::before { content: \"\\f02d\"; } .icon-bookmark::before { content: \"\\f02e\"; } .icon-print::before { content: \"\\f02f\"; } .icon-camera::before { content: \"\\f030\"; } .icon-font::before { content: \"\\f031\"; } .icon-bold::before { content: \"\\f032\"; } .icon-italic::before { content: \"\\f033\"; } .icon-text-height::before { content: \"\\f034\"; } .icon-text-width::before { content: \"\\f035\"; } .icon-align-left::before { content: \"\\f036\"; } .icon-align-center::before { content: \"\\f037\"; } .icon-align-right::before { content: \"\\f038\"; } .icon-align-justify::before { content: \"\\f039\"; } .icon-list::before { content: \"\\f03a\"; } .icon-indent-left::before { content: \"\\f03b\"; } .icon-indent-right::before { content: \"\\f03c\"; } .icon-facetime-video::before { content: \"\\f03d\"; } .icon-picture::before { content: \"\\f03e\"; } .icon-pencil::before { content: \"\\f040\"; } .icon-map-marker::before { content: \"\\f041\"; } .icon-adjust::before { content: \"\\f042\"; } .icon-tint::before { content: \"\\f043\"; } .icon-edit::before { content: \"\\f044\"; } .icon-share::before { content: \"\\f045\"; } .icon-check::before { content: \"\\f046\"; } .icon-move::before { content: \"\\f047\"; } .icon-step-backward::before { content: \"\\f048\"; } .icon-fast-backward::before { content: \"\\f049\"; } .icon-backward::before { content: \"\\f04a\"; } .icon-play::before { content: \"\\f04b\"; } .icon-pause::before { content: \"\\f04c\"; } .icon-stop::before { content: \"\\f04d\"; } .icon-forward::before { content: \"\\f04e\"; } .icon-fast-forward::before { content: \"\\f050\"; } .icon-step-forward::before { content: \"\\f051\"; } .icon-eject::before { content: \"\\f052\"; } .icon-chevron-left::before { content: \"\\f053\"; } .icon-chevron-right::before { content: \"\\f054\"; } .icon-plus-sign::before { content: \"\\f055\"; } .icon-minus-sign::before { content: \"\\f056\"; } .icon-remove-sign::before { content: \"\\f057\"; } .icon-ok-sign::before { content: \"\\f058\"; } .icon-question-sign::before { content: \"\\f059\"; } .icon-info-sign::before { content: \"\\f05a\"; } .icon-screenshot::before { content: \"\\f05b\"; } .icon-remove-circle::before { content: \"\\f05c\"; } .icon-ok-circle::before { content: \"\\f05d\"; } .icon-ban-circle::before { content: \"\\f05e\"; } .icon-arrow-left::before { content: \"\\f060\"; } .icon-arrow-right::before { content: \"\\f061\"; } .icon-arrow-up::before { content: \"\\f062\"; } .icon-arrow-down::before { content: \"\\f063\"; } .icon-mail-forward:before, .icon-share-alt::before { content: \"\\f064\"; } .icon-resize-full::before { content: \"\\f065\"; } .icon-resize-small::before { content: \"\\f066\"; } .icon-plus::before { content: \"\\f067\"; } .icon-minus::before { content: \"\\f068\"; } .icon-asterisk::before { content: \"\\f069\"; } .icon-exclamation-sign::before { content: \"\\f06a\"; } .icon-gift::before { content: \"\\f06b\"; } .icon-leaf::before { content: \"\\f06c\"; } .icon-fire::before { content: \"\\f06d\"; } .icon-eye-open::before { content: \"\\f06e\"; } .icon-eye-close::before { content: \"\\f070\"; } .icon-warning-sign::before { content: \"\\f071\"; } .icon-plane::before { content: \"\\f072\"; } .icon-calendar::before { content: \"\\f073\"; } .icon-random::before { content: \"\\f074\"; } .icon-comment::before { content: \"\\f075\"; } .icon-magnet::before { content: \"\\f076\"; } .icon-chevron-up::before { content: \"\\f077\"; } .icon-chevron-down::before { content: \"\\f078\"; } .icon-retweet::before { content: \"\\f079\"; } .icon-shopping-cart::before { content: \"\\f07a\"; } .icon-folder-close::before { content: \"\\f07b\"; } .icon-folder-open::before { content: \"\\f07c\"; } .icon-resize-vertical::before { content: \"\\f07d\"; } .icon-resize-horizontal::before { content: \"\\f07e\"; } .icon-bar-chart::before { content: \"\\f080\"; } .icon-twitter-sign::before { content: \"\\f081\"; } .icon-facebook-sign::before { content: \"\\f082\"; } .icon-camera-retro::before { content: \"\\f083\"; } .icon-key::before { content: \"\\f084\"; } .icon-gears:before, .icon-cogs::before { content: \"\\f085\"; } .icon-comments::before { content: \"\\f086\"; } .icon-thumbs-up-alt::before { content: \"\\f087\"; } .icon-thumbs-down-alt::before { content: \"\\f088\"; } .icon-star-half::before { content: \"\\f089\"; } .icon-heart-empty::before { content: \"\\f08a\"; } .icon-signout::before { content: \"\\f08b\"; } .icon-linkedin-sign::before { content: \"\\f08c\"; } .icon-pushpin::before { content: \"\\f08d\"; } .icon-external-link::before { content: \"\\f08e\"; } .icon-signin::before { content: \"\\f090\"; } .icon-trophy::before { content: \"\\f091\"; } .icon-github-sign::before { content: \"\\f092\"; } .icon-upload-alt::before { content: \"\\f093\"; } .icon-lemon::before { content: \"\\f094\"; } .icon-phone::before { content: \"\\f095\"; } .icon-unchecked:before, .icon-check-empty::before { content: \"\\f096\"; } .icon-bookmark-empty::before { content: \"\\f097\"; } .icon-phone-sign::before { content: \"\\f098\"; } .icon-twitter::before { content: \"\\f099\"; } .icon-facebook::before { content: \"\\f09a\"; } .icon-github::before { content: \"\\f09b\"; } .icon-unlock::before { content: \"\\f09c\"; } .icon-credit-card::before { content: \"\\f09d\"; } .icon-rss::before { content: \"\\f09e\"; } .icon-hdd::before { content: \"\\f0a0\"; } .icon-bullhorn::before { content: \"\\f0a1\"; } .icon-bell::before { content: \"\\f0a2\"; } .icon-certificate::before { content: \"\\f0a3\"; } .icon-hand-right::before { content: \"\\f0a4\"; } .icon-hand-left::before { content: \"\\f0a5\"; } .icon-hand-up::before { content: \"\\f0a6\"; } .icon-hand-down::before { content: \"\\f0a7\"; } .icon-circle-arrow-left::before { content: \"\\f0a8\"; } .icon-circle-arrow-right::before { content: \"\\f0a9\"; } .icon-circle-arrow-up::before { content: \"\\f0aa\"; } .icon-circle-arrow-down::before { content: \"\\f0ab\"; } .icon-globe::before { content: \"\\f0ac\"; } .icon-wrench::before { content: \"\\f0ad\"; } .icon-tasks::before { content: \"\\f0ae\"; } .icon-filter::before { content: \"\\f0b0\"; } .icon-briefcase::before { content: \"\\f0b1\"; } .icon-fullscreen::before { content: \"\\f0b2\"; } .icon-group::before { content: \"\\f0c0\"; } .icon-link::before { content: \"\\f0c1\"; } .icon-cloud::before { content: \"\\f0c2\"; } .icon-beaker::before { content: \"\\f0c3\"; } .icon-cut::before { content: \"\\f0c4\"; } .icon-copy::before { content: \"\\f0c5\"; } .icon-paperclip:before, .icon-paper-clip::before { content: \"\\f0c6\"; } .icon-save::before { content: \"\\f0c7\"; } .icon-sign-blank::before { content: \"\\f0c8\"; } .icon-reorder::before { content: \"\\f0c9\"; } .icon-list-ul::before { content: \"\\f0ca\"; } .icon-list-ol::before { content: \"\\f0cb\"; } .icon-strikethrough::before { content: \"\\f0cc\"; } .icon-underline::before { content: \"\\f0cd\"; } .icon-table::before { content: \"\\f0ce\"; } .icon-magic::before { content: \"\\f0d0\"; } .icon-truck::before { content: \"\\f0d1\"; } .icon-pinterest::before { content: \"\\f0d2\"; } .icon-pinterest-sign::before { content: \"\\f0d3\"; } .icon-google-plus-sign::before { content: \"\\f0d4\"; } .icon-google-plus::before { content: \"\\f0d5\"; } .icon-money::before { content: \"\\f0d6\"; } .icon-caret-down::before { content: \"\\f0d7\"; } .icon-caret-up::before { content: \"\\f0d8\"; } .icon-caret-left::before { content: \"\\f0d9\"; } .icon-caret-right::before { content: \"\\f0da\"; } .icon-columns::before { content: \"\\f0db\"; } .icon-sort::before { content: \"\\f0dc\"; } .icon-sort-down::before { content: \"\\f0dd\"; } .icon-sort-up::before { content: \"\\f0de\"; } .icon-envelope::before { content: \"\\f0e0\"; } .icon-linkedin::before { content: \"\\f0e1\"; } .icon-rotate-left:before, .icon-undo::before { content: \"\\f0e2\"; } .icon-legal::before { content: \"\\f0e3\"; } .icon-dashboard::before { content: \"\\f0e4\"; } .icon-comment-alt::before { content: \"\\f0e5\"; } .icon-comments-alt::before { content: \"\\f0e6\"; } .icon-bolt::before { content: \"\\f0e7\"; } .icon-sitemap::before { content: \"\\f0e8\"; } .icon-umbrella::before { content: \"\\f0e9\"; } .icon-paste::before { content: \"\\f0ea\"; } .icon-lightbulb::before { content: \"\\f0eb\"; } .icon-exchange::before { content: \"\\f0ec\"; } .icon-cloud-download::before { content: \"\\f0ed\"; } .icon-cloud-upload::before { content: \"\\f0ee\"; } .icon-user-md::before { content: \"\\f0f0\"; } .icon-stethoscope::before { content: \"\\f0f1\"; } .icon-suitcase::before { content: \"\\f0f2\"; } .icon-bell-alt::before { content: \"\\f0f3\"; } .icon-coffee::before { content: \"\\f0f4\"; } .icon-food::before { content: \"\\f0f5\"; } .icon-file-text-alt::before { content: \"\\f0f6\"; } .icon-building::before { content: \"\\f0f7\"; } .icon-hospital::before { content: \"\\f0f8\"; } .icon-ambulance::before { content: \"\\f0f9\"; } .icon-medkit::before { content: \"\\f0fa\"; } .icon-fighter-jet::before { content: \"\\f0fb\"; } .icon-beer::before { content: \"\\f0fc\"; } .icon-h-sign::before { content: \"\\f0fd\"; } .icon-plus-sign-alt::before { content: \"\\f0fe\"; } .icon-double-angle-left::before { content: \"\\f100\"; } .icon-double-angle-right::before { content: \"\\f101\"; } .icon-double-angle-up::before { content: \"\\f102\"; } .icon-double-angle-down::before { content: \"\\f103\"; } .icon-angle-left::before { content: \"\\f104\"; } .icon-angle-right::before { content: \"\\f105\"; } .icon-angle-up::before { content: \"\\f106\"; } .icon-angle-down::before { content: \"\\f107\"; } .icon-desktop::before { content: \"\\f108\"; } .icon-laptop::before { content: \"\\f109\"; } .icon-tablet::before { content: \"\\f10a\"; } .icon-mobile-phone::before { content: \"\\f10b\"; } .icon-circle-blank::before { content: \"\\f10c\"; } .icon-quote-left::before { content: \"\\f10d\"; } .icon-quote-right::before { content: \"\\f10e\"; } .icon-spinner::before { content: \"\\f110\"; } .icon-circle::before { content: \"\\f111\"; } .icon-mail-reply:before, .icon-reply::before { content: \"\\f112\"; } .icon-github-alt::before { content: \"\\f113\"; } .icon-folder-close-alt::before { content: \"\\f114\"; } .icon-folder-open-alt::before { content: \"\\f115\"; } .icon-expand-alt::before { content: \"\\f116\"; } .icon-collapse-alt::before { content: \"\\f117\"; } .icon-smile::before { content: \"\\f118\"; } .icon-frown::before { content: \"\\f119\"; } .icon-meh::before { content: \"\\f11a\"; } .icon-gamepad::before { content: \"\\f11b\"; } .icon-keyboard::before { content: \"\\f11c\"; } .icon-flag-alt::before { content: \"\\f11d\"; } .icon-flag-checkered::before { content: \"\\f11e\"; } .icon-terminal::before { content: \"\\f120\"; } .icon-code::before { content: \"\\f121\"; } .icon-reply-all::before { content: \"\\f122\"; } .icon-mail-reply-all::before { content: \"\\f122\"; } .icon-star-half-full:before, .icon-star-half-empty::before { content: \"\\f123\"; } .icon-location-arrow::before { content: \"\\f124\"; } .icon-crop::before { content: \"\\f125\"; } .icon-code-fork::before { content: \"\\f126\"; } .icon-unlink::before { content: \"\\f127\"; } .icon-question::before { content: \"\\f128\"; } .icon-info::before { content: \"\\f129\"; } .icon-exclamation::before { content: \"\\f12a\"; } .icon-superscript::before { content: \"\\f12b\"; } .icon-subscript::before { content: \"\\f12c\"; } .icon-eraser::before { content: \"\\f12d\"; } .icon-puzzle-piece::before { content: \"\\f12e\"; } .icon-microphone::before { content: \"\\f130\"; } .icon-microphone-off::before { content: \"\\f131\"; } .icon-shield::before { content: \"\\f132\"; } .icon-calendar-empty::before { content: \"\\f133\"; } .icon-fire-extinguisher::before { content: \"\\f134\"; } .icon-rocket::before { content: \"\\f135\"; } .icon-maxcdn::before { content: \"\\f136\"; } .icon-chevron-sign-left::before { content: \"\\f137\"; } .icon-chevron-sign-right::before { content: \"\\f138\"; } .icon-chevron-sign-up::before { content: \"\\f139\"; } .icon-chevron-sign-down::before { content: \"\\f13a\"; } .icon-html5::before { content: \"\\f13b\"; } .icon-css3::before { content: \"\\f13c\"; } .icon-anchor::before { content: \"\\f13d\"; } .icon-unlock-alt::before { content: \"\\f13e\"; } .icon-bullseye::before { content: \"\\f140\"; } .icon-ellipsis-horizontal::before { content: \"\\f141\"; } .icon-ellipsis-vertical::before { content: \"\\f142\"; } .icon-rss-sign::before { content: \"\\f143\"; } .icon-play-sign::before { content: \"\\f144\"; } .icon-ticket::before { content: \"\\f145\"; } .icon-minus-sign-alt::before { content: \"\\f146\"; } .icon-check-minus::before { content: \"\\f147\"; } .icon-level-up::before { content: \"\\f148\"; } .icon-level-down::before { content: \"\\f149\"; } .icon-check-sign::before { content: \"\\f14a\"; } .icon-edit-sign::before { content: \"\\f14b\"; } .icon-external-link-sign::before { content: \"\\f14c\"; } .icon-share-sign::before { content: \"\\f14d\"; } .icon-compass::before { content: \"\\f14e\"; } .icon-collapse::before { content: \"\\f150\"; } .icon-collapse-top::before { content: \"\\f151\"; } .icon-expand::before { content: \"\\f152\"; } .icon-euro:before, .icon-eur::before { content: \"\\f153\"; } .icon-gbp::before { content: \"\\f154\"; } .icon-dollar:before, .icon-usd::before { content: \"\\f155\"; } .icon-rupee:before, .icon-inr::before { content: \"\\f156\"; } .icon-yen:before, .icon-jpy::before { content: \"\\f157\"; } .icon-renminbi:before, .icon-cny::before { content: \"\\f158\"; } .icon-won:before, .icon-krw::before { content: \"\\f159\"; } .icon-bitcoin:before, .icon-btc::before { content: \"\\f15a\"; } .icon-file::before { content: \"\\f15b\"; } .icon-file-text::before { content: \"\\f15c\"; } .icon-sort-by-alphabet::before { content: \"\\f15d\"; } .icon-sort-by-alphabet-alt::before { content: \"\\f15e\"; } .icon-sort-by-attributes::before { content: \"\\f160\"; } .icon-sort-by-attributes-alt::before { content: \"\\f161\"; } .icon-sort-by-order::before { content: \"\\f162\"; } .icon-sort-by-order-alt::before { content: \"\\f163\"; } .icon-thumbs-up::before { content: \"\\f164\"; } .icon-thumbs-down::before { content: \"\\f165\"; } .icon-youtube-sign::before { content: \"\\f166\"; } .icon-youtube::before { content: \"\\f167\"; } .icon-xing::before { content: \"\\f168\"; } .icon-xing-sign::before { content: \"\\f169\"; } .icon-youtube-play::before { content: \"\\f16a\"; } .icon-dropbox::before { content: \"\\f16b\"; } .icon-stackexchange::before { content: \"\\f16c\"; } .icon-instagram::before { content: \"\\f16d\"; } .icon-flickr::before { content: \"\\f16e\"; } .icon-adn::before { content: \"\\f170\"; } .icon-bitbucket::before { content: \"\\f171\"; } .icon-bitbucket-sign::before { content: \"\\f172\"; } .icon-tumblr::before { content: \"\\f173\"; } .icon-tumblr-sign::before { content: \"\\f174\"; } .icon-long-arrow-down::before { content: \"\\f175\"; } .icon-long-arrow-up::before { content: \"\\f176\"; } .icon-long-arrow-left::before { content: \"\\f177\"; } .icon-long-arrow-right::before { content: \"\\f178\"; } .icon-apple::before { content: \"\\f179\"; } .icon-windows::before { content: \"\\f17a\"; } .icon-android::before { content: \"\\f17b\"; } .icon-linux::before { content: \"\\f17c\"; } .icon-dribbble::before { content: \"\\f17d\"; } .icon-skype::before { content: \"\\f17e\"; } .icon-foursquare::before { content: \"\\f180\"; } .icon-trello::before { content: \"\\f181\"; } .icon-female::before { content: \"\\f182\"; } .icon-male::before { content: \"\\f183\"; } .icon-gittip::before { content: \"\\f184\"; } .icon-sun::before { content: \"\\f185\"; } .icon-moon::before { content: \"\\f186\"; } .icon-archive::before { content: \"\\f187\"; } .icon-bug::before { content: \"\\f188\"; } .icon-vk::before { content: \"\\f189\"; } .icon-weibo::before { content: \"\\f18a\"; } .icon-renren::before { content: \"\\f18b\"; }\n/* General */ .dialog { box-shadow: 0 1px 2px rgba(0, 0, 0, .15); border: 1px solid; display: block; padding: 0; } .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; } .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; } /* 4chan style fixes */ .opContainer, .op { display: block !important; overflow: visible !important; } .reply > .file > .fileText { margin: 0 20px; } [hidden] { display: none !important; } div.center:not(.ad-cnt) { display: none !important; } /* 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 body { padding-top: 2em; } .fixed.bottom body { padding-bottom: 2em; } .fixed #header-bar { right: 0; left: 0; padding: 3px 4px 4px; } .fixed.top #header-bar { top: 0; } .fixed.bottom #header-bar { bottom: 0; } #header-bar { border-width: 0; transition: all .1s .05s ease-in-out; } :root.centered-links #shortcuts { width: 300px; text-align: right; } :root.centered-links #header-bar { text-align: center; } :root.centered-links #custom-board-list { position: relative; left: 150px; } .fixed.top #header-bar { border-bottom-width: 1px; } .fixed.bottom #header-bar { box-shadow: 0 -1px 2px rgba(0, 0, 0, .15); border-top-width: 1px; } .fixed.bottom #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-bar.autohide:not(:hover) { margin-bottom: -1em; -webkit-transform: translateY(-100%); transform: translateY(-100%); } .fixed.bottom #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-bar #scroll-marker { top: 100%; } .fixed.bottom #header-bar #scroll-marker { bottom: 100%; } #header-bar a:not(.entry):not(.close) { text-decoration: none; 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::after { content: \"]\"; } .brackets-wrap::before { 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-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: 6px; top: 0; right: 5px; 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); 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: 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; } /* Announcement Hiding */ :root.hide-announcement #globalMessage { display: none; } a.hide-announcement { float: left; } /* 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; } .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; } .fappeTyme .thread > .noFile, .fappeTyme .threadContainer > .noFile { 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: 2px; } .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, .postingMode ~ #qr select[data-name=thread], #file-n-submit:not(.has-file) #qr-filerm { display: none; } #qr select, #dump-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; } .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; } .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; } /* 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: 2px; 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 1px 1px #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; font-weight: 700; padding: 3px; } 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 { 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; } #menu { position: fixed; outline: none; } .entry { border-bottom: 1px solid rgba(0,0,0,.25); cursor: pointer; display: block; outline: none; padding: 3px 7px; position: relative; text-decoration: none; white-space: nowrap; min-width: 70px; } .left>.entry.has-submenu { padding-right: 17px !important; } .entry:last-child { border-bottom: 0; } .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; } .focused .submenu { display: block; } .imp-exp-result { position: absolute; text-align: center; margin: auto; right: 0px; left: 0px; width: 200px; } .export, .import { cursor: pointer; text-decoration: none !important; } /* Custom Board Titles */ .boardTitle[contenteditable=\"true\"], .boardSubtitle[contenteditable=\"true\"] { cursor: text !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,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAASJJREFUOE9jYAAC7ln7/pODQXrBmq333PvPu/YaSRikB6QXbACpmmHqsRoAMll7+20UQ0H8tmuv/pdffPFfZtNNuByGASBFIPDh5x+4IV6HHoDFYGDJgw+YBoBMBUkgA5BtIKduuvvy//svX+FSB+88wTTAc+/t/83bj/0HScLA5BPXwc7lKJ36f+L6XXDxhUfOYxrAPWUnWKFp9UQUm3iWQxSDXAEDSX3zcIcB96wD/x+8eA1XDNKMHAYg20GW4Y0FkCIYAAUqzEBQOIBciRzlWKMxZelOlMCEcVxq+jHSC1YDJPs3YBgA8jey0/F6ARRwsFAHORukmat9NdbUijMpg/wKcrJodDFOzSBXwA3Alh9AToZFI7a8Asu98BxJbnYGAJb5vYLDANzSAAAAAElFTkSuQmCC') 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/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAydJREFUOE9Nk1tIk2EYx79NyUNqTk0o6KYrnZeChodLDxfeZpCbJk4RXU5Nm7tYRYhiYXbQlaeGutyW2gxtpB1RIyKDEjKwA6Ti2dR5KNDn+fq/S6TBj/f93r3P732e53s/qfnkSdej4GB2SBLbwf+jmB+gUMgOheLg/z7EdCUnO6Ref392SpK8Hyh3I+gBwBo7lUp2xcbyQEoKD6alyQOpqd754/h4FjJXZCRJTl9ftmEzoK5/wdQJxPgkLY2WV1dpc2uLtnZ2eHNnhza3t2nd46GhjAzuValY6jx0iIfS03msoIDuQ9COQCtoUSjohU5HuwgaN5loeXycd3d3aW9vzwvW2K5SkdTi58fvzGb+3tdHFggA3QONEAzn59PvjQ1yqNX0zenkvX0B4ffWaGRraChJd/385JGqKvlzTw/fRqOaIGkEd1DjU52O/3g83BkTw5MOh7yJuUCUM2o0yi2hoSw1IIOhykr+YLNRHYKu4XQvyKA/N5c8yMCCDD7Z7bz26xcJ1rH2rKKCG0UJdRAMlJbyG6uVrkJQjWAB5tSbk0Nr2HwDgvcQiIYur6zQyvo6ucvLueHIEZKuQPBQr+dXra1kRuqXEOwFArtWSytra1QdFUVjNhvPLS3R3OIiLUDUD0F1WBhJJtwDW2Ehu5uaqBICI4IFlRB0QLCEzaboaHrd0cHzCBYsIIuesjK+LAQXkEFrXh676uupGCWcR6AeghLQptGQONUAwfOuLp6Zn6eZuTmaXVig7pISrhI90ENgQbdHhoep32JhFzLpu3WLio8epUYIfs7OUjF6UKJW88XERLqYkEBNej11oG8XhCAvMFAuOn5cNiclsTkhQTbhmpri4lgbEMANWi1DwC/xit3t7bK7rY0Fo4OD3G4wyEURESzloAdnceezlErK8vH5N4KzPj50PTOTfkxP0+THj/RlYoInJyZI8HVqim5qNFwQHk7SucBAPo2PKRMNPLM/4pnFszYkhJsNBu6uqWFHba1sr61lQSveQFZQkFx07BhJmhMnrLn4NLMPH/aSExR0QDbmWhwgyEapwDvXoDxdWBiXnjrV/Bdm2kYUxLwmEgAAAABJRU5ErkJggg==') 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,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAtZJREFUOE+NU91LWmEc7sJtQew/2MUY7INg7CLY3W5GMHazyzEQo9UmfYxZTbAiVlgRqLMSZ+XnDC3z2+Y0+8JGakKZTtR0Tl2wtgtLLQh29cz3ZZ3h3Q68vOc95zzP73l+z+/U1f292O09DRxubxOH23P//1bvtQts3dPnry7LZnXJhcUl5Avf8dHtwY+fv2AyW5DOfIXFakMm+w0G4wISyRRm55TQG0y/Wzv6mikJ52Xf9TmVBoFAAD6fDwqFAqFQCJubmzCbzZiensbp6SmkUikikQi0Wi0kEgm6ewVaStDCfXPDandifn6egoaGhrCzswO1Wg2Hw4HBwUGk02kIBAL4/X4IhUJMTk6ii8dfYggy2RwymQzOz88Rj8dRLpexv7+PSqWCYDCIQqGAra0tJBIJrK2t0XdVAjNDEIl+wfj4OEqlEq2wt7dHrchkMmrBYDCAz+fTIjweD7FYrJbgIJOlgLOzM8jlcip1eXmZ2rFarVAqlRCLxcjlchCJRFRljYJYPAG32418Pg+n04lsNouVlRUcHh7C4/FQIOlHNBqlezgcJgQWxkIgGMbExASVNjY2hvX1dVo9mUzS5wREFLhcLrqTcw2B//M2RkdHodPp4PV6oVKpqH+SCom3v7+fNnF4eJiJusbCJ6+PviSyScakiaR5RIHRaKQpmEwmbAdCeD8zB6vdhebHT8SMhcUlC83bbrdTJRsbG3RwiCVCRNJJpDIoVeNNJJJQzKryV+rrmxiCtyNCCmaz2VhdXQWXy6XDpNfrodFoYLXZUTw+pk222Z3lW3ca26rgSwzBwqIZAwMDlITMAVEwNTVFR5fEJpK8Qyp1AJvDVbrTeLenCmxgfiZ22+urCtWHyu7uLp2wVCpFKx0dHaFYLOLk5KT6Y9kgk89kb95ubK0BX7A8a+1qannRLeW0daj/rU51S3tn9dypfvDw0QiLxbpX/Z7FVK7e/AEj4Wf24/2f5AAAAABJRU5ErkJggg==') center left no-repeat!important; padding-left: 18px; } .linkify.gist { background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAk1JREFUeNqUkzuIE1EUhv955MnsbB6r4kYQLUQQFncV3SnCIqJsoWGDYOGjsIiCtY2Kla1sjLBIsFFcXJC1kaSwENQmXUQSRSUSjCQSTCbkbR4z47lXEgtBNwcu3DNzvvO8R8jlcj7LshKmaWqYQERRTAmCcEru9/sJr9er0QF92BJMAVGr1TQ6CeZAc7lcGAwGkyQAxpTLZU0eDoc8crfbRTgcRjAYRCQSYSmi1WpxY7fbjU6ng1gshmaziXg8zhnGIpVKWbquW9ls1mLZsaMoiqWq6lgnBxY55He/328Vi0XOMFZmqVMD4fF4QBAajcY48khY9JE4HA4enTGMFVkaTHmy+ZzD/5NSqYSNB484w1h55ODO3TVu4FXcWDywl24Cmp0e1WBhyuWELAtIf/qKUrWOONmev3Lpt4NRCXq1gplpBS/v3cDc0nGg9h1o1ZkfwO4Atu1B8cM7HLt8k37V/y5B2b4bJxf2Y+7oEbyJrkMvUjki0YYJ03LidfQxAt4dOHdCw5RdGZcgGobBlQtnV/BDr1GfDai7ZiHZZRi9PoY/e5SCCTUwC9gk1GmMh5YWOcNYkR4Sv1y9uAJbYB82N57h4OnDmN7phjQ0qUkWRJuB+TMaPn/5iFfvv+Ha7eucYey4iWw8q6tRJJNJ3Fp7ClUawEkViBTfkCR0YUNTVHD/4Tpm/P4/U2CeKpUKfD4fJDIMhUKEhP45St50XedZyLQY6Xw+v8AUemVb2oNqtYpCocCWKi2TLLfb7ReZTGZ+kmUi7i2VvfxLgAEAZChMriPcl+IAAAAASUVORK5CYII=') 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,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAklJREFUOE9jYEAAASBTCorZkcSRmTjVCDLziCwG4hfM3EIvGNm44oC6WNEM4WXi5FsEkmfhFX3BxMmfAJSHW9Qr55Px3aZp3X/btq3/hQydPzKysMcCFbBBDeFj4uBdqBJR/gskb1W34j+PmulLoJwbzBJJoMm7dNO7/ntMP/XfpW/v//SKvk+7tl7fvXfTpx5pCdWVSiHFv1wnHQbLi9sE/Wdk5SwBauaCGQB3gUPb5v+7Lr/8/+fvr/9fv/z+f+Pyr/9bV735l9Wy/79Dx/b/Nk0bsLoAHgbeAVHv/v77/f8f0IB7N7+cu3DuecK54z9+7lzz639e9pK/7HwSWMMA5BJwCJeXtOm/fvVj1fcfv369f//92cN7X6ZcPvf9x6Htv//vXP3r/+T245UEYgpskPTNq08LgN749/PH7/93rv/6f/rw7//nj//4f+bU0zQcUQwWBkdVbGz62y+fv3wHeeXrlz//H9798//qpY//M3KqfzGxc8djiWKwZnBUuWQ2/fr46fv/P39+///x/ff/d69//z97+s7fyMb5/+y7d2GLYriDZikFF/1qXXXj/4Pbv/8/f/jn/5MH316/eP6jVlBAaIt6VO1/jxmn/zv27P7Pp2HxEajLD90ra9Sj6/979O37X73w0n+vqOL/0lJyMVBFq0EGgDSD0oKAlu1/oHg4ugGzVCKqfouYuL1Xj676Iajr8AnJFricGqYc3Bw+Zi6BVUxsXLHAdL6QiYMPFNrwpIxHDsUhgtAMAopKDjQn4pPDF7P45QC4hSmc1eX8WgAAAABJRU5ErkJggg==') 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; }\n/* General */ :root.yotsuba .dialog { background-color: #F0E0D6; border-color: #D9BFB7; } :root.yotsuba .field:focus { border-color: #EA8; } /* Header */ :root.yotsuba #header-bar, :root.yotsuba #notifications { font-size: 9pt; color: #B86; } :root.yotsuba #header-bar a, :root.yotsuba #notifications a { color: #800000; } /* 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); } /* Menu */ :root.yotsuba #menu { color: #800000; } :root.yotsuba .entry { border-bottom: 1px solid #D9BFB7; 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,\"); }\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, :root.yotsuba-b #notifications { font-size: 9pt; color: #89A; } :root.yotsuba-b #header-bar a, :root.yotsuba-b #notifications a { color: #34345C; } /* 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); } /* Menu */ :root.yotsuba-b #menu { color: #000; } :root.yotsuba-b .entry { border-bottom: 1px solid #B7C5D9; 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,\"); }\n/* General */ :root.futaba .dialog { background-color: #F0E0D6; border-color: #D9BFB7; } :root.futaba .field:focus { border-color: #EA8; } /* Header */ :root.futaba #header-bar, :root.futaba #notifications { font-size: 11pt; color: #B86; } :root.futaba #header-bar a, :root.futaba #notifications a { color: #800000; } /* 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); } /* Menu */ :root.futaba #menu { color: #800000; } :root.futaba .entry { border-bottom: 1px solid #D9BFB7; 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, :root.burichan #header-bar #notifications { font-size: 11pt; color: #89A; } :root.burichan #header-bar a, :root.burichan #header-bar #notifications a { color: #34345C; } /* 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); } /* Menu */ :root.burichan #menu { color: #000000; } :root.burichan .entry { border-bottom: 1px solid #B7C5D9; 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, :root.tomorrow #notifications { font-size: 9pt; color: #C5C8C6; } :root.tomorrow #header-bar a, :root.tomorrow #notifications a { color: #81A2BE; } /* 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); } /* Menu */ :root.tomorrow #menu { color: #C5C8C6; } :root.tomorrow .entry { border-bottom: 1px solid #111; 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,\"); }\n/* General */ :root.photon .dialog { background-color: #DDD; border-color: #CCC; } :root.photon .field:focus { border-color: #EA8; } /* Header */ :root.photon #header-bar, :root.photon #notifications { font-size: 9pt; color: #333; } :root.photon #header-bar a, :root.photon #notifications a { color: #FF6600; } /* 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); } /* Menu */ :root.photon #menu { color: #333; } :root.photon .entry { border-bottom: 1px solid #CCC; 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,\"); }" + css: "/*! * Font Awesome 3.2.1 * 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{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{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{display:inline}.fa-glass::before{content:\"\\f000\"}.fa-music::before{content:\"\\f001\"}.fa-search::before{content:\"\\f002\"}.fa-envelope-alt::before{content:\"\\f003\"}.fa-heart::before{content:\"\\f004\"}.fa-star::before{content:\"\\f005\"}.fa-star-empty::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-ok::before{content:\"\\f00c\"}.fa-remove::before{content:\"\\f00d\"}.fa-zoom-in::before{content:\"\\f00e\"}.fa-zoom-out::before{content:\"\\f010\"}.fa-off::before,.fa-power-off:before{content:\"\\f011\"}.fa-signal::before{content:\"\\f012\"}.fa-cog::before,.fa-gear:before{content:\"\\f013\"}.fa-trash::before{content:\"\\f014\"}.fa-home::before{content:\"\\f015\"}.fa-file-alt::before{content:\"\\f016\"}.fa-time::before{content:\"\\f017\"}.fa-times::before{content:\"\\f00d\";}.fa-road::before{content:\"\\f018\"}.fa-download-alt::before{content:\"\\f019\"}.fa-download::before{content:\"\\f01a\"}.fa-upload::before{content:\"\\f01b\"}.fa-inbox::before{content:\"\\f01c\"}.fa-play-circle::before{content:\"\\f01d\"}.fa-repeat::before,.fa-rotate-right: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-indent-left::before{content:\"\\f03b\"}.fa-indent-right::before{content:\"\\f03c\"}.fa-facetime-video::before{content:\"\\f03d\"}.fa-picture::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{content:\"\\f044\"}.fa-share::before{content:\"\\f045\"}.fa-check::before{content:\"\\f046\"}.fa-move::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-sign::before{content:\"\\f055\"}.fa-minus-sign::before{content:\"\\f056\"}.fa-remove-sign::before{content:\"\\f057\"}.fa-ok-sign::before{content:\"\\f058\"}.fa-question-sign::before{content:\"\\f059\"}.fa-info-sign::before{content:\"\\f05a\"}.fa-screenshot::before{content:\"\\f05b\"}.fa-remove-circle::before{content:\"\\f05c\"}.fa-ok-circle::before{content:\"\\f05d\"}.fa-ban-circle::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-alt::before{content:\"\\f064\"}.fa-resize-full::before{content:\"\\f065\"}.fa-resize-small::before{content:\"\\f066\"}.fa-plus::before{content:\"\\f067\"}.fa-minus::before{content:\"\\f068\"}.fa-asterisk::before{content:\"\\f069\"}.fa-exclamation-sign::before{content:\"\\f06a\"}.fa-gift::before{content:\"\\f06b\"}.fa-leaf::before{content:\"\\f06c\"}.fa-fire::before{content:\"\\f06d\"}.fa-eye-open::before{content:\"\\f06e\"}.fa-eye-close::before{content:\"\\f070\"}.fa-warning-sign::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-close::before{content:\"\\f07b\"}.fa-folder-open::before{content:\"\\f07c\"}.fa-resize-vertical::before{content:\"\\f07d\"}.fa-resize-horizontal::before{content:\"\\f07e\"}.fa-bar-chart::before{content:\"\\f080\"}.fa-twitter-sign::before{content:\"\\f081\"}.fa-facebook-sign::before{content:\"\\f082\"}.fa-camera-retro::before{content:\"\\f083\"}.fa-key::before{content:\"\\f084\"}.fa-cogs::before,.fa-gears:before{content:\"\\f085\"}.fa-comments::before{content:\"\\f086\"}.fa-thumbs-up-alt::before{content:\"\\f087\"}.fa-thumbs-down-alt::before{content:\"\\f088\"}.fa-star-half::before{content:\"\\f089\"}.fa-heart-empty::before{content:\"\\f08a\"}.fa-signout::before{content:\"\\f08b\"}.fa-linkedin-sign::before{content:\"\\f08c\"}.fa-pushpin::before{content:\"\\f08d\"}.fa-external-link::before{content:\"\\f08e\"}.fa-signin::before{content:\"\\f090\"}.fa-trophy::before{content:\"\\f091\"}.fa-github-sign::before{content:\"\\f092\"}.fa-upload-alt::before{content:\"\\f093\"}.fa-lemon::before{content:\"\\f094\"}.fa-phone::before{content:\"\\f095\"}.fa-check-empty::before,.fa-unchecked:before{content:\"\\f096\"}.fa-bookmark-empty::before{content:\"\\f097\"}.fa-phone-sign::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::before{content:\"\\f0a0\"}.fa-bullhorn::before{content:\"\\f0a1\"}.fa-bell::before{content:\"\\f0a2\"}.fa-certificate::before{content:\"\\f0a3\"}.fa-hand-right::before{content:\"\\f0a4\"}.fa-hand-left::before{content:\"\\f0a5\"}.fa-hand-up::before{content:\"\\f0a6\"}.fa-hand-down::before{content:\"\\f0a7\"}.fa-circle-arrow-left::before{content:\"\\f0a8\"}.fa-circle-arrow-right::before{content:\"\\f0a9\"}.fa-circle-arrow-up::before{content:\"\\f0aa\"}.fa-circle-arrow-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-fullscreen::before{content:\"\\f0b2\"}.fa-group::before{content:\"\\f0c0\"}.fa-link::before{content:\"\\f0c1\"}.fa-cloud::before{content:\"\\f0c2\"}.fa-beaker::before{content:\"\\f0c3\"}.fa-cut::before{content:\"\\f0c4\"}.fa-copy::before{content:\"\\f0c5\"}.fa-paper-clip::before,.fa-paperclip:before{content:\"\\f0c6\"}.fa-save::before{content:\"\\f0c7\"}.fa-sign-blank::before{content:\"\\f0c8\"}.fa-reorder::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-sign::before{content:\"\\f0d3\"}.fa-google-plus-sign::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-sort::before{content:\"\\f0dc\"}.fa-sort-down::before{content:\"\\f0dd\"}.fa-sort-up::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{content:\"\\f0e3\"}.fa-dashboard::before{content:\"\\f0e4\"}.fa-comment-alt::before{content:\"\\f0e5\"}.fa-comments-alt::before{content:\"\\f0e6\"}.fa-bolt::before{content:\"\\f0e7\"}.fa-sitemap::before{content:\"\\f0e8\"}.fa-umbrella::before{content:\"\\f0e9\"}.fa-paste::before{content:\"\\f0ea\"}.fa-lightbulb::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-alt::before{content:\"\\f0f3\"}.fa-coffee::before{content:\"\\f0f4\"}.fa-food::before{content:\"\\f0f5\"}.fa-file-text-alt::before{content:\"\\f0f6\"}.fa-building::before{content:\"\\f0f7\"}.fa-hospital::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-sign::before{content:\"\\f0fd\"}.fa-plus-sign-alt::before{content:\"\\f0fe\"}.fa-double-angle-left::before{content:\"\\f100\"}.fa-double-angle-right::before{content:\"\\f101\"}.fa-double-angle-up::before{content:\"\\f102\"}.fa-double-angle-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{content:\"\\f10b\"}.fa-circle-blank::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-close-alt::before{content:\"\\f114\"}.fa-folder-open-alt::before{content:\"\\f115\"}.fa-expand-alt::before{content:\"\\f116\"}.fa-collapse-alt::before{content:\"\\f117\"}.fa-smile::before{content:\"\\f118\"}.fa-frown::before{content:\"\\f119\"}.fa-meh::before{content:\"\\f11a\"}.fa-gamepad::before{content:\"\\f11b\"}.fa-keyboard::before{content:\"\\f11c\"}.fa-flag-alt::before{content:\"\\f11d\"}.fa-flag-checkered::before{content:\"\\f11e\"}.fa-terminal::before{content:\"\\f120\"}.fa-code::before{content:\"\\f121\"}.fa-mail-reply-all::before,.fa-reply-all::before{content:\"\\f122\"}.fa-star-half-empty::before,.fa-star-half-full:before{content:\"\\f123\"}.fa-location-arrow::before{content:\"\\f124\"}.fa-crop::before{content:\"\\f125\"}.fa-code-fork::before{content:\"\\f126\"}.fa-unlink::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-off::before{content:\"\\f131\"}.fa-shield::before{content:\"\\f132\"}.fa-calendar-empty::before{content:\"\\f133\"}.fa-fire-extinguisher::before{content:\"\\f134\"}.fa-rocket::before{content:\"\\f135\"}.fa-maxcdn::before{content:\"\\f136\"}.fa-chevron-sign-left::before{content:\"\\f137\"}.fa-chevron-sign-right::before{content:\"\\f138\"}.fa-chevron-sign-up::before{content:\"\\f139\"}.fa-chevron-sign-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-horizontal::before{content:\"\\f141\"}.fa-ellipsis-vertical::before{content:\"\\f142\"}.fa-rss-sign::before{content:\"\\f143\"}.fa-play-sign::before{content:\"\\f144\"}.fa-ticket::before{content:\"\\f145\"}.fa-minus-sign-alt::before{content:\"\\f146\"}.fa-check-minus::before{content:\"\\f147\"}.fa-level-up::before{content:\"\\f148\"}.fa-level-down::before{content:\"\\f149\"}.fa-check-sign::before{content:\"\\f14a\"}.fa-edit-sign::before{content:\"\\f14b\"}.fa-external-link-sign::before{content:\"\\f14c\"}.fa-share-sign::before{content:\"\\f14d\"}.fa-compass::before{content:\"\\f14e\"}.fa-collapse::before{content:\"\\f150\"}.fa-collapse-top::before{content:\"\\f151\"}.fa-expand:before{content:\"\\f065\"}.fa-compress:before{content:\"\\f066\"}.fa-eur::before,.fa-euro:before{content:\"\\f153\"}.fa-gbp::before{content:\"\\f154\"}.fa-dollar:before,.fa-usd::before{content:\"\\f155\"}.fa-inr::before,.fa-rupee:before{content:\"\\f156\"}.fa-jpy::before,.fa-yen:before{content:\"\\f157\"}.fa-cny::before,.fa-renminbi:before{content:\"\\f158\"}.fa-krw::before,.fa-won:before{content:\"\\f159\"}.fa-bitcoin:before,.fa-btc::before{content:\"\\f15a\"}.fa-file::before{content:\"\\f15b\"}.fa-file-text::before{content:\"\\f15c\"}.fa-sort-by-alphabet::before{content:\"\\f15d\"}.fa-sort-by-alphabet-alt::before{content:\"\\f15e\"}.fa-sort-by-attributes::before{content:\"\\f160\"}.fa-sort-by-attributes-alt::before{content:\"\\f161\"}.fa-sort-by-order::before{content:\"\\f162\"}.fa-sort-by-order-alt::before{content:\"\\f163\"}.fa-thumbs-up::before{content:\"\\f164\"}.fa-thumbs-down::before{content:\"\\f165\"}.fa-youtube-sign::before{content:\"\\f166\"}.fa-youtube::before{content:\"\\f167\"}.fa-xing::before{content:\"\\f168\"}.fa-xing-sign::before{content:\"\\f169\"}.fa-youtube-play::before{content:\"\\f16a\"}.fa-dropbox::before{content:\"\\f16b\"}.fa-stackexchange::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-sign::before{content:\"\\f172\"}.fa-tumblr::before{content:\"\\f173\"}.fa-tumblr-sign::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::before{content:\"\\f185\"}.fa-moon::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-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 { box-shadow: 0 1px 2px rgba(0, 0, 0, .15); border: 1px solid; display: block; padding: 0; } .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; } /* 4chan style fixes */ .opContainer, .op { display: block !important; overflow: visible !important; } .reply > .file > .fileText { margin: 0 20px; } [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.centered-links #shortcuts { width: 300px; text-align: right; } :root.centered-links #header-bar { text-align: center; } :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; 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::after { content: \"]\"; } .brackets-wrap::before { 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: -1.25em; } #index-search:not([data-searching]) + #index-search-clear { display: none; } .summary { text-decoration: none; } /* Announcement Hiding */ :root.hide-announcement #globalMessage { display: none; } a.hide-announcement { float: left; } /* 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; } .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: 2px; } .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, .postingMode ~ #qr select[data-name=thread], #file-n-submit:not(.has-file) #qr-filerm { display: none; } #qr select, #dump-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; } .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; } .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; } /* 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: 2px; 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; } #menu { position: fixed; outline: none; } .entry { border-bottom: 1px solid rgba(0,0,0,.25); cursor: pointer; display: block; outline: none; padding: 3px 7px; position: relative; text-decoration: none; white-space: nowrap; min-width: 70px; } .left>.entry.has-submenu { padding-right: 17px !important; } .entry:last-child { border-bottom: 0; } .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; } .focused > .submenu { display: block; } .imp-exp-result { position: absolute; text-align: center; margin: auto; right: 0px; left: 0px; width: 200px; } .export, .import { cursor: pointer; text-decoration: none !important; } /* Custom Board Titles */ .boardTitle[contenteditable=\"true\"], .boardSubtitle[contenteditable=\"true\"] { cursor: text !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,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAASJJREFUOE9jYAAC7ln7/pODQXrBmq333PvPu/YaSRikB6QXbACpmmHqsRoAMll7+20UQ0H8tmuv/pdffPFfZtNNuByGASBFIPDh5x+4IV6HHoDFYGDJgw+YBoBMBUkgA5BtIKduuvvy//svX+FSB+88wTTAc+/t/83bj/0HScLA5BPXwc7lKJ36f+L6XXDxhUfOYxrAPWUnWKFp9UQUm3iWQxSDXAEDSX3zcIcB96wD/x+8eA1XDNKMHAYg20GW4Y0FkCIYAAUqzEBQOIBciRzlWKMxZelOlMCEcVxq+jHSC1YDJPs3YBgA8jey0/F6ARRwsFAHORukmat9NdbUijMpg/wKcrJodDFOzSBXwA3Alh9AToZFI7a8Asu98BxJbnYGAJb5vYLDANzSAAAAAElFTkSuQmCC') 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/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAydJREFUOE9Nk1tIk2EYx79NyUNqTk0o6KYrnZeChodLDxfeZpCbJk4RXU5Nm7tYRYhiYXbQlaeGutyW2gxtpB1RIyKDEjKwA6Ti2dR5KNDn+fq/S6TBj/f93r3P732e53s/qfnkSdej4GB2SBLbwf+jmB+gUMgOheLg/z7EdCUnO6Ref392SpK8Hyh3I+gBwBo7lUp2xcbyQEoKD6alyQOpqd754/h4FjJXZCRJTl9ftmEzoK5/wdQJxPgkLY2WV1dpc2uLtnZ2eHNnhza3t2nd46GhjAzuValY6jx0iIfS03msoIDuQ9COQCtoUSjohU5HuwgaN5loeXycd3d3aW9vzwvW2K5SkdTi58fvzGb+3tdHFggA3QONEAzn59PvjQ1yqNX0zenkvX0B4ffWaGRraChJd/385JGqKvlzTw/fRqOaIGkEd1DjU52O/3g83BkTw5MOh7yJuUCUM2o0yi2hoSw1IIOhykr+YLNRHYKu4XQvyKA/N5c8yMCCDD7Z7bz26xcJ1rH2rKKCG0UJdRAMlJbyG6uVrkJQjWAB5tSbk0Nr2HwDgvcQiIYur6zQyvo6ucvLueHIEZKuQPBQr+dXra1kRuqXEOwFArtWSytra1QdFUVjNhvPLS3R3OIiLUDUD0F1WBhJJtwDW2Ehu5uaqBICI4IFlRB0QLCEzaboaHrd0cHzCBYsIIuesjK+LAQXkEFrXh676uupGCWcR6AeghLQptGQONUAwfOuLp6Zn6eZuTmaXVig7pISrhI90ENgQbdHhoep32JhFzLpu3WLio8epUYIfs7OUjF6UKJW88XERLqYkEBNej11oG8XhCAvMFAuOn5cNiclsTkhQTbhmpri4lgbEMANWi1DwC/xit3t7bK7rY0Fo4OD3G4wyEURESzloAdnceezlErK8vH5N4KzPj50PTOTfkxP0+THj/RlYoInJyZI8HVqim5qNFwQHk7SucBAPo2PKRMNPLM/4pnFszYkhJsNBu6uqWFHba1sr61lQSveQFZQkFx07BhJmhMnrLn4NLMPH/aSExR0QDbmWhwgyEapwDvXoDxdWBiXnjrV/Bdm2kYUxLwmEgAAAABJRU5ErkJggg==') 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,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAtZJREFUOE+NU91LWmEc7sJtQew/2MUY7INg7CLY3W5GMHazyzEQo9UmfYxZTbAiVlgRqLMSZ+XnDC3z2+Y0+8JGakKZTtR0Tl2wtgtLLQh29cz3ZZ3h3Q68vOc95zzP73l+z+/U1f292O09DRxubxOH23P//1bvtQts3dPnry7LZnXJhcUl5Avf8dHtwY+fv2AyW5DOfIXFakMm+w0G4wISyRRm55TQG0y/Wzv6mikJ52Xf9TmVBoFAAD6fDwqFAqFQCJubmzCbzZiensbp6SmkUikikQi0Wi0kEgm6ewVaStDCfXPDandifn6egoaGhrCzswO1Wg2Hw4HBwUGk02kIBAL4/X4IhUJMTk6ii8dfYggy2RwymQzOz88Rj8dRLpexv7+PSqWCYDCIQqGAra0tJBIJrK2t0XdVAjNDEIl+wfj4OEqlEq2wt7dHrchkMmrBYDCAz+fTIjweD7FYrJbgIJOlgLOzM8jlcip1eXmZ2rFarVAqlRCLxcjlchCJRFRljYJYPAG32418Pg+n04lsNouVlRUcHh7C4/FQIOlHNBqlezgcJgQWxkIgGMbExASVNjY2hvX1dVo9mUzS5wREFLhcLrqTcw2B//M2RkdHodPp4PV6oVKpqH+SCom3v7+fNnF4eJiJusbCJ6+PviSyScakiaR5RIHRaKQpmEwmbAdCeD8zB6vdhebHT8SMhcUlC83bbrdTJRsbG3RwiCVCRNJJpDIoVeNNJJJQzKryV+rrmxiCtyNCCmaz2VhdXQWXy6XDpNfrodFoYLXZUTw+pk222Z3lW3ca26rgSwzBwqIZAwMDlITMAVEwNTVFR5fEJpK8Qyp1AJvDVbrTeLenCmxgfiZ22+urCtWHyu7uLp2wVCpFKx0dHaFYLOLk5KT6Y9kgk89kb95ubK0BX7A8a+1qannRLeW0daj/rU51S3tn9dypfvDw0QiLxbpX/Z7FVK7e/AEj4Wf24/2f5AAAAABJRU5ErkJggg==') center left no-repeat!important; padding-left: 18px; } .linkify.gist { background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAk1JREFUeNqUkzuIE1EUhv955MnsbB6r4kYQLUQQFncV3SnCIqJsoWGDYOGjsIiCtY2Kla1sjLBIsFFcXJC1kaSwENQmXUQSRSUSjCQSTCbkbR4z47lXEgtBNwcu3DNzvvO8R8jlcj7LshKmaWqYQERRTAmCcEru9/sJr9er0QF92BJMAVGr1TQ6CeZAc7lcGAwGkyQAxpTLZU0eDoc8crfbRTgcRjAYRCQSYSmi1WpxY7fbjU6ng1gshmaziXg8zhnGIpVKWbquW9ls1mLZsaMoiqWq6lgnBxY55He/328Vi0XOMFZmqVMD4fF4QBAajcY48khY9JE4HA4enTGMFVkaTHmy+ZzD/5NSqYSNB484w1h55ODO3TVu4FXcWDywl24Cmp0e1WBhyuWELAtIf/qKUrWOONmev3Lpt4NRCXq1gplpBS/v3cDc0nGg9h1o1ZkfwO4Atu1B8cM7HLt8k37V/y5B2b4bJxf2Y+7oEbyJrkMvUjki0YYJ03LidfQxAt4dOHdCw5RdGZcgGobBlQtnV/BDr1GfDai7ZiHZZRi9PoY/e5SCCTUwC9gk1GmMh5YWOcNYkR4Sv1y9uAJbYB82N57h4OnDmN7phjQ0qUkWRJuB+TMaPn/5iFfvv+Ha7eucYey4iWw8q6tRJJNJ3Fp7ClUawEkViBTfkCR0YUNTVHD/4Tpm/P4/U2CeKpUKfD4fJDIMhUKEhP45St50XedZyLQY6Xw+v8AUemVb2oNqtYpCocCWKi2TLLfb7ReZTGZ+kmUi7i2VvfxLgAEAZChMriPcl+IAAAAASUVORK5CYII=') 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,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAklJREFUOE9jYEAAASBTCorZkcSRmTjVCDLziCwG4hfM3EIvGNm44oC6WNEM4WXi5FsEkmfhFX3BxMmfAJSHW9Qr55Px3aZp3X/btq3/hQydPzKysMcCFbBBDeFj4uBdqBJR/gskb1W34j+PmulLoJwbzBJJoMm7dNO7/ntMP/XfpW/v//SKvk+7tl7fvXfTpx5pCdWVSiHFv1wnHQbLi9sE/Wdk5SwBauaCGQB3gUPb5v+7Lr/8/+fvr/9fv/z+f+Pyr/9bV735l9Wy/79Dx/b/Nk0bsLoAHgbeAVHv/v77/f8f0IB7N7+cu3DuecK54z9+7lzz639e9pK/7HwSWMMA5BJwCJeXtOm/fvVj1fcfv369f//92cN7X6ZcPvf9x6Htv//vXP3r/+T245UEYgpskPTNq08LgN749/PH7/93rv/6f/rw7//nj//4f+bU0zQcUQwWBkdVbGz62y+fv3wHeeXrlz//H9798//qpY//M3KqfzGxc8djiWKwZnBUuWQ2/fr46fv/P39+///x/ff/d69//z97+s7fyMb5/+y7d2GLYriDZikFF/1qXXXj/4Pbv/8/f/jn/5MH316/eP6jVlBAaIt6VO1/jxmn/zv27P7Pp2HxEajLD90ra9Sj6/979O37X73w0n+vqOL/0lJyMVBFq0EGgDSD0oKAlu1/oHg4ugGzVCKqfouYuL1Xj676Iajr8AnJFricGqYc3Bw+Zi6BVUxsXLHAdL6QiYMPFNrwpIxHDsUhgtAMAopKDjQn4pPDF7P45QC4hSmc1eX8WgAAAABJRU5ErkJggg==') 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, :root.yotsuba #notifications { font-size: 9pt; color: #B86; } :root.yotsuba #header-bar a { color: #800000; } /* 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); } /* Menu */ :root.yotsuba #menu { color: #800000; } :root.yotsuba .entry { border-bottom: 1px solid #D9BFB7; 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,\"); }\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, :root.yotsuba-b #notifications { font-size: 9pt; color: #89A; } :root.yotsuba #board-list a, :root.yotsuba #shortcuts a { color: #34345C; } /* 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); } /* Menu */ :root.yotsuba-b #menu { color: #000; } :root.yotsuba-b .entry { border-bottom: 1px solid #B7C5D9; 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,\"); }\n/* General */ :root.futaba .dialog { background-color: #F0E0D6; border-color: #D9BFB7; } :root.futaba .field:focus { border-color: #EA8; } /* Header */ :root.futaba #header-bar, :root.futaba #notifications { font-size: 11pt; color: #B86; } :root.futaba #header-bar a, :root.futaba #notifications a { color: #800000; } /* 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); } /* Menu */ :root.futaba #menu { color: #800000; } :root.futaba .entry { border-bottom: 1px solid #D9BFB7; 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, :root.burichan #header-bar #notifications { font-size: 11pt; color: #89A; } :root.burichan #header-bar a, :root.burichan #header-bar #notifications a { color: #34345C; } /* 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); } /* Menu */ :root.burichan #menu { color: #000000; } :root.burichan .entry { border-bottom: 1px solid #B7C5D9; 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, :root.tomorrow #notifications { font-size: 9pt; color: #C5C8C6; } :root.tomorrow #header-bar a, :root.tomorrow #notifications a { color: #81A2BE; } /* 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); } /* Menu */ :root.tomorrow #menu { color: #C5C8C6; } :root.tomorrow .entry { border-bottom: 1px solid #111; 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,\"); }\n/* General */ :root.photon .dialog { background-color: #DDD; border-color: #CCC; } :root.photon .field:focus { border-color: #EA8; } /* Header */ :root.photon #header-bar, :root.photon #notifications { font-size: 9pt; color: #333; } :root.photon #header-bar a, :root.photon #notifications a { color: #FF6600; } /* 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); } /* Menu */ :root.photon #menu { color: #333; } :root.photon .entry { border-bottom: 1px solid #CCC; 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,\"); }" }; Main.init(); diff --git a/builds/crx/manifest.json b/builds/crx/manifest.json index 1eaedfe12..7c1b9b59d 100755 --- a/builds/crx/manifest.json +++ b/builds/crx/manifest.json @@ -15,7 +15,7 @@ "run_at": "document_start" }], "homepage_url": "http://seaweedchan.github.io/4chan-x/", - "minimum_chrome_version": "27", + "minimum_chrome_version": "31", "permissions": [ "storage" ] diff --git a/builds/crx/script.js b/builds/crx/script.js index 14a00acf0..679b025d5 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript /* -* 4chan X - Version 1.2.45 - 2014-01-07 +* 4chan X - Version 1.2.45 - 2014-01-08 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -82,13 +82,26 @@ 'use strict'; (function() { - var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, CatalogLinks, Clone, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, Header, IDColor, ImageExpand, ImageHover, ImageLoader, InfiniScroll, Keybinds, Linkify, Main, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, g, + 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, 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, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, g, __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; }, __slice = [].slice, __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) { + var i; + i = this.length; + while (i--) { + if (this[i] === val) { + return i; + } + } + return i; + }; + + __indexOf = [].indexOf; + Config = { main: { 'Miscellaneous': { @@ -102,7 +115,6 @@ '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.'], - 'Comment Expansion': [true, 'Add buttons to expand long comments.'], '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.'], @@ -113,8 +125,7 @@ '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.'], - 'Infinite Scrolling': [false, 'Add new posts to the board index upon reaching the bottom of the board.'] + '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.'], @@ -231,12 +242,24 @@ 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, @@ -278,6 +301,7 @@ '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.'], @@ -319,29 +343,6 @@ posts: {} }; - String.prototype.capitalize = function() { - return this.charAt(0).toUpperCase() + this.slice(1); - }; - - String.prototype.contains = function(string) { - return this.indexOf(string) > -1; - }; - - Array.prototype.contains = function(object) { - return this.indexOf(object) > -1; - }; - - Array.prototype.indexOf = function(object) { - var i; - i = this.length; - while (i--) { - if (this[i] === object) { - return i; - } - } - return i; - }; - $ = function(selector, root) { if (root == null) { root = d.body; @@ -349,14 +350,13 @@ return root.querySelector(selector); }; - $.extend = function(object, properties) { + $.extend = function(obj, prop) { var key, val; - for (key in properties) { - val = properties[key]; - if (!properties.hasOwnProperty(key)) { - continue; + for (key in prop) { + val = prop[key]; + if (prop.hasOwnProperty(key)) { + obj[key] = val; } - object[key] = val; } }; @@ -419,7 +419,9 @@ type || (type = form && 'post' || 'get'); r.open(type, url, !sync); if (whenModified) { - r.setRequestHeader('If-Modified-Since', lastModified[url] || '0'); + if (url in lastModified) { + r.setRequestHeader('If-Modified-Since', lastModified[url]); + } $.on(r, 'load', function() { return lastModified[url] = r.getResponseHeader('Last-Modified'); }); @@ -525,7 +527,7 @@ }; $.hasClass = function(el, className) { - return el.classList.contains(className); + return __indexOf.call(el.classList, className) >= 0; }; $.rm = (function() { @@ -542,8 +544,10 @@ })(); $.rmAll = function(root) { - var node; - while (node = root.firstChild) { + var node, _i, _len, _ref; + _ref = __slice.call(root.childNodes); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + node = _ref[_i]; root.removeChild(node); } }; @@ -714,7 +718,7 @@ var cb, key; for (key in changes) { if (cb = $.syncing[key]) { - cb(changes[key].newValue); + cb(changes[key].newValue, key); } } }); @@ -749,10 +753,8 @@ } count = 0; done = function(item) { - var lastError; - lastError = chrome.runtime.lastError; - if (lastError) { - c.error(lastError, lastError.message || 'No message.'); + if (chrome.runtime.lastError) { + c.error(chrome.runtime.lastError.message); } $.extend(items, item); if (!--count) { @@ -810,6 +812,55 @@ return __slice.call(root.querySelectorAll(selector)); }; + Callbacks = (function() { + function Callbacks() {} + + Callbacks.prototype.push = function(_arg) { + var cb, name; + name = _arg.name, cb = _arg.cb; + return this[name] = cb; + }; + + Callbacks.prototype.clean = function() { + var name; + for (name in this) { + if (this.hasOwnProperty(name)) { + this.rm(name); + } + } + }; + + Callbacks.prototype.rm = function(name) { + return delete this[name]; + }; + + Callbacks.prototype.execute = function(node) { + var err, errors, name; + for (name in this) { + if (this.hasOwnProperty(name)) { + try { + this[name].call(node); + } catch (_error) { + err = _error; + if (!errors) { + errors = []; + } + errors.push({ + message: ['"', name, '" crashed on node No.', node, ' (', node.board, ').'].join(''), + error: err + }); + } + } + } + if (errors) { + return Main.handleErrors(errors); + } + }; + + return Callbacks; + + })(); + Board = (function() { Board.prototype.toString = function() { return this.ID; @@ -827,7 +878,7 @@ })(); Thread = (function() { - Thread.callbacks = []; + Thread.callbacks = new Callbacks(); Thread.prototype.toString = function() { return this.ID; @@ -838,20 +889,70 @@ this.board = board; this.fullID = "" + this.board + "." + this.ID; this.posts = {}; + this.isSticky = false; + this.isClosed = false; + this.postLimit = false; + this.fileLimit = false; g.threads[this.fullID] = board.threads[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() { + var post, postID, _i, _len, _ref; + _ref = this.posts; + for (post = _i = 0, _len = _ref.length; _i < _len; post = ++_i) { + postID = _ref[post]; + post.collect(); + } + delete g.threads[this.fullID]; + return delete this.board.threads[this]; + }; + return Thread; })(); Post = (function() { - Post.callbacks = []; + Post.callbacks = new Callbacks(); Post.prototype.toString = function() { return this.ID; @@ -866,6 +967,9 @@ } 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 = { @@ -970,7 +1074,7 @@ return; } fullID = "" + match[1] + "." + match[2]; - if (!this.quotes.contains(fullID)) { + if (__indexOf.call(this.quotes, fullID) < 0) { return this.quotes.push(fullID); } }; @@ -1004,6 +1108,20 @@ } }; + 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()); @@ -1081,6 +1199,13 @@ } }; + Post.prototype.collect = function() { + this.kill(); + delete g.posts[this.fullID]; + delete this.thread.posts[this]; + return delete this.board.posts[this]; + }; + Post.prototype.addClone = function(context) { return new Clone(this, context); }; @@ -1306,6 +1431,9 @@ 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]; @@ -1346,7 +1474,7 @@ this.close = __bind(this.close, this); this.add = __bind(this.add, this); this.el = $.el('div', { - innerHTML: '×
' + innerHTML: '
' }); this.el.style.opacity = 0; this.setType(type); @@ -1368,7 +1496,7 @@ return; } $.off(d, 'visibilitychange', this.add); - $.add($.id('notifications'), this.el); + $.add(Header.noticesRoot, this.el); this.el.clientHeight; this.el.style.opacity = 1; if (this.timeout) { @@ -1385,6 +1513,86 @@ })(); + RandomAccessList = (function() { + function RandomAccessList() { + this.length = 0; + } + + RandomAccessList.prototype.push = function(item) { + var ID, last; + ID = item.ID; + if (this[ID]) { + return; + } + last = this.last; + item.prev = last; + this[ID] = item; + this.last = last ? last.next = item : this.first = item; + return this.length++; + }; + + 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; + 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.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; + + })(); + Polyfill = { init: function() { this.notificationPermission(); @@ -1392,7 +1600,7 @@ return this.visibility(); }, notificationPermission: function() { - if (!window.Notification || 'permission' in Notification) { + if (!window.Notification || 'permission' in Notification || !window.webkitNotifications) { return; } return Object.defineProperty(Notification, 'permission', { @@ -1424,7 +1632,7 @@ }); }, visibility: function() { - if (!('webkitHidden' in document)) { + if ('visibilityState' in d) { return; } Object.defineProperties(HTMLDocument.prototype, { @@ -1447,7 +1655,7 @@ Header = { init: function() { - var barFixedToggler, barPositionToggler, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, menuButton, shortcutToggler, + var barFixedToggler, barPositionToggler, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, menuButton, scrollHeaderToggler, shortcutToggler, _this = this; this.menu = new UI.Menu('header'); menuButton = $.el('span', { @@ -1460,8 +1668,11 @@ headerToggler = $.el('label', { innerHTML: ' Auto-hide header' }); + scrollHeaderToggler = $.el('label', { + innerHTML: ' Auto-hide header on scroll' + }); barPositionToggler = $.el('label', { - innerHTML: ' Bottom header' + innerHTML: ' Bottom header' }); linkJustifyToggler = $.el('label', { innerHTML: " Centered links" @@ -1480,6 +1691,7 @@ href: 'javascript:;' }); this.barFixedToggler = barFixedToggler.firstElementChild; + this.scrollHeaderToggler = scrollHeaderToggler.firstElementChild; this.barPositionToggler = barPositionToggler.firstElementChild; this.linkJustifyToggler = linkJustifyToggler.firstElementChild; this.headerToggler = headerToggler.firstElementChild; @@ -1487,8 +1699,10 @@ 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); @@ -1496,14 +1710,16 @@ $.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', Header.setBarFixed); - $.sync('Bottom Header', Header.setBarPosition); - $.sync('Shortcut Icons', Header.setShortcutIcons); - $.sync('Header auto-hide', Header.setBarVisibility); - $.sync('Centered links', Header.setLinkJustify); + $.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', @@ -1516,6 +1732,8 @@ el: barFixedToggler }, { el: headerToggler + }, { + el: scrollHeaderToggler }, { el: barPositionToggler }, { @@ -1548,10 +1766,11 @@ return _this; }); $.ready(function() { - var a, cs; - _this.footer = $.id('boardNavDesktopFoot'); - if (a = $("a[href*='/" + g.BOARD + "/']", $.id('boardNavDesktopFoot'))) { + var a, cs, footer; + _this.footer = footer = $.id('boardNavDesktopFoot'); + if (a = $("a[href*='/" + g.BOARD + "/']", footer)) { a.className = 'current'; + $.on(a, 'click', Index.cb.link); } cs = $.el('a', { id: 'settingsWindowLink', @@ -1569,7 +1788,7 @@ bar: $.el('div', { id: 'header-bar' }), - notify: $.el('div', { + noticesRoot: $.el('div', { id: 'notifications' }), shortcuts: $.el('span', { @@ -1584,19 +1803,20 @@ setBoardList: function() { var a, boardList, btn, fourchannav, fullBoardList; fourchannav = $.id('boardNavDesktop'); - if (a = $("a[href*='/" + g.BOARD + "/']", fourchannav)) { - a.className = 'current'; - } boardList = $.el('span', { id: 'board-list', innerHTML: "" }); + if (a = $("a[href*='/" + g.BOARD + "/']", boardList)) { + a.className = 'current'; + $.on(a, 'click', Index.cb.link); + } 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, [boardList, Header.shortcuts, Header.notify, Header.toggle]); + $.add(Header.bar, [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); @@ -1611,7 +1831,7 @@ } as = $$('#full-board-list a[title]', Header.bar); nodes = text.match(/[\w@]+((-(all|title|replace|full|index|catalog|url:"[^"]+[^"]"|text:"[^"]+")|\,"[^"]+[^"]"))*|[^\w@]+/g).map(function(t) { - var a, board, m, _i, _len; + var a, board, current, m, _i, _len; if (/^[^\w@]/.test(t)) { return $.tn(t); } @@ -1637,7 +1857,11 @@ a = as[_i]; if (a.textContent === board) { a = a.cloneNode(true); - 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; + current = $.hasClass(a, 'current'); + if (current) { + $.on(a, 'click', Index.cb.link); + } + a.textContent = /-title/.test(t) || /-replace/.test(t) && 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 + "/"; @@ -1669,18 +1893,6 @@ custom.hidden = !showBoardList; return full.hidden = showBoardList; }, - setBarPosition: function(bottom) { - Header.barPositionToggler.checked = bottom; - if (bottom) { - $.rmClass(doc, 'top'); - $.addClass(doc, 'bottom'); - return $.after(Header.bar, Header.notify); - } else { - $.rmClass(doc, 'bottom'); - $.addClass(doc, 'top'); - return $.add(Header.bar, Header.notify); - } - }, setLinkJustify: function(centered) { Header.linkJustifyToggler.checked = centered; if (centered) { @@ -1689,12 +1901,6 @@ return $.rmClass(doc, 'centered-links'); } }, - toggleBarPosition: function() { - $.event('CloseMenu'); - Header.setBarPosition(this.checked); - Conf['Bottom Header'] = this.checked; - return $.set('Bottom Header', this.checked); - }, toggleLinkJustify: function() { var centered; $.event('CloseMenu'); @@ -1747,6 +1953,50 @@ 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; + $.set('Header auto-hide on scroll', hide); + return Header.setHideBarOnScroll(hide); + }, + hideBarOnScroll: function() { + var offsetY; + offsetY = window.pageYOffset; + if (offsetY > (Header.previousOffset || 0)) { + $.addClass(Header.bar, 'autohide'); + $.addClass(Header.bar, 'scroll'); + } else { + $.rmClass(Header.bar, 'autohide'); + $.rmClass(Header.bar, '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.notify); + }, + toggleBarPosition: function() { + $.cb.checked.call(this); + return Header.setBarPosition(this.checked); + }, setFooterVisibility: function(hide) { Header.footerToggler.checked = hide; return Header.footer.hidden = hide; @@ -1780,22 +2030,50 @@ }, hashScroll: function() { var hash, post; - if (!((hash = this.location.hash.slice(1)) && (post = $.id(hash)))) { + hash = this.location.hash.slice(1); + if (!(/^p\d+$/.test(hash) && (post = $.id(hash)))) { return; } if ((Get.postFromRoot(post)).isHidden) { return; } - return Header.scrollToPost(post); + return Header.scrollTo(post); }, - scrollToPost: function(post) { + scrollTo: function(root, down, needed) { + var x; + if (down) { + x = Header.getBottomOf(root); + if (!(needed && x >= 0)) { + return window.scrollBy(0, -x); + } + } else { + x = Header.getTopOf(root); + 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 = post.getBoundingClientRect().top; + top = root.getBoundingClientRect().top; if (Conf['Fixed Header'] && !Conf['Bottom Header']) { - headRect = Header.bar.getBoundingClientRect(); + headRect = Header.toggle.getBoundingClientRect(); top -= headRect.top + headRect.height; } - return window.scrollBy(0, top); + 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; }, addShortcut: function(el) { var shortcut; @@ -1809,11 +2087,11 @@ return Header.menu.toggle(e, this, g); }, createNotification: function(e) { - var cb, content, lifetime, notif, type, _ref; + var cb, content, lifetime, notice, type, _ref; _ref = e.detail, type = _ref.type, content = _ref.content, lifetime = _ref.lifetime, cb = _ref.cb; - notif = new Notice(type, content, lifetime); + notice = new Notice(type, content, lifetime); if (cb) { - return cb(notif); + return cb(notice); } }, areNotificationsEnabled: false, @@ -1850,7 +2128,651 @@ } }; + Index = { + init: function() { + var anchorEntry, input, label, modeEntry, name, refNavEntry, repliesEntry, sortEntry, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2; + if (g.VIEW !== 'index' || g.BOARD.ID === 'f') { + return; + } + this.button = $.el('a', { + className: 'index-refresh-shortcut fa fa-refresh', + title: 'Refresh Index', + 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: ' 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: 90, + subEntries: [modeEntry, sortEntry, repliesEntry, anchorEntry, refNavEntry] + }); + $.addClass(doc, 'index-loading'); + this.update(); + this.root = $.el('div', { + className: 'board' + }); + this.pagelist = $.el('div', { + className: 'pagelist', + hidden: true, + innerHTML: "
" + }); + this.navLinks = $.el('div', { + className: 'navLinks', + innerHTML: "[Catalog] [" + }); + this.searchInput = $('#index-search', this.navLinks); + this.currentPage = this.getCurrentPage(); + $.on(window, 'popstate', this.cb.popstate); + $.on(this.pagelist, 'click', this.cb.pageNav); + $.on(this.searchInput, 'input', this.onSearchInput); + $.on($('#index-search-clear', this.navLinks), 'click', this.clearSearch); + return $.asap((function() { + return $('.board', doc) || d.readyState !== 'loading'; + }), function() { + var board, navLink, _l, _len3, _ref3; + 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); + $.rmClass(doc, 'index-loading'); + return $.asap((function() { + return $('.pagelist') || d.readyState !== 'loading'; + }), function() { + return $.replace($('.pagelist'), Index.pagelist); + }); + }); + }, + cb: { + mode: function() { + Index.togglePagelist(); + return Index.buildIndex(); + }, + sort: function() { + Index.sort(); + return Index.buildIndex(); + }, + replies: function() { + Index.buildThreads(); + Index.sort(); + return Index.buildIndex(); + }, + popstate: function(e) { + var pageNum; + pageNum = Index.getCurrentPage(); + if (Index.currentPage !== pageNum) { + return Index.pageLoad(pageNum); + } + }, + 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]); + }, + link: function(e) { + if (g.VIEW !== 'index' || /catalog/.test(this.href)) { + return; + } + e.preventDefault(); + return Index.update(); + } + }, + 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'] === 'paged') { + 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'] !== 'paged') { + 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() { + var a, href, maxPageNum, next, pageNum, pagesRoot, prev, strong; + 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 ((_ref = Index.req) != null) { + _ref.abort(); + } + if ((_ref1 = Index.notice) != null) { + _ref1.close(); + } + if (d.readyState !== 'loading') { + Index.notice = new Notice('info', 'Refreshing index...'); + } else { + now = Date.now(); + $.ready(function() { + return setTimeout((function() { + if (!(Index.req && !Index.notice)) { + return; + } + return Index.notice = new Notice('info', 'Refreshing index...'); + }), 5 * $.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: true + }); + return $.addClass(Index.button, 'fa-spin'); + }, + load: function(e, pageNum) { + var err, notice, req, timeEl; + $.rmClass(Index.button, 'fa-spin'); + req = Index.req, notice = Index.notice; + delete Index.req; + delete Index.notice; + if (e.type === 'abort') { + req.onloadend = null; + notice.close(); + return; + } + try { + if (req.status === 200) { + Index.parse(JSON.parse(req.response), pageNum); + } else if (req.status === 304 && (pageNum != null)) { + Index.pageNav(pageNum); + } + } catch (_error) { + err = _error; + c.error('Index failure:', err.stack); + if (notice) { + notice.setType('error'); + notice.el.lastElementChild.textContent = 'Index refresh failed.'; + setTimeout(notice.close, 2 * $.SECOND); + } else { + new Notice('error', 'Index refresh failed.', 2); + } + return; + } + if (notice) { + notice.setType('success'); + notice.el.lastElementChild.textContent = 'Index refreshed!'; + setTimeout(notice.close, $.SECOND); + } + timeEl = $('#index-last-refresh', 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) { + var thread, threadID, _ref, _ref1; + 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; + }); + _ref = g.BOARD.threads; + for (threadID in _ref) { + thread = _ref[threadID]; + if (_ref1 = thread.ID, __indexOf.call(Index.liveThreadIDs, _ref1) < 0) { + 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]; + threadRoot = Build.thread(g.BOARD, threadData); + Index.nodes.push(threadRoot, $.el('hr')); + 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); + } + if (thread.ID in thread.posts) { + continue; + } + try { + posts.push(new Post($('.opContainer', threadRoot), thread, g.BOARD)); + } catch (_error) { + err = _error; + if (!errors) { + errors = []; + } + errors.push({ + message: "Parsing of Post No." + thread + " failed. Post 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 += 2) { + 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 i, sortedThreadIDs, threadID, _i, _len; + switch (Conf['Index Sort']) { + case 'bump': + sortedThreadIDs = Index.liveThreadIDs; + break; + case 'lastreply': + sortedThreadIDs = __slice.call(Index.liveThreadData).sort(function(a, b) { + if ('last_replies' in a) { + a = a.last_replies[a.last_replies.length - 1]; + } + if ('last_replies' in b) { + b = b.last_replies[b.last_replies.length - 1]; + } + return b.no - a.no; + }).map(function(data) { + return data.no; + }); + break; + case 'birth': + sortedThreadIDs = __slice.call(Index.liveThreadIDs).sort(function(a, b) { + return b - a; + }); + break; + case 'replycount': + sortedThreadIDs = __slice.call(Index.liveThreadData).sort(function(a, b) { + return b.replies - a.replies; + }).map(function(data) { + return data.no; + }); + break; + case 'filecount': + sortedThreadIDs = __slice.call(Index.liveThreadData).sort(function(a, b) { + return b.images - a.images; + }).map(function(data) { + return data.no; + }); + } + Index.sortedNodes = []; + for (_i = 0, _len = sortedThreadIDs.length; _i < _len; _i++) { + threadID = sortedThreadIDs[_i]; + i = Index.liveThreadIDs.indexOf(threadID) * 2; + Index.sortedNodes.push(Index.nodes[i], Index.nodes[i + 1]); + } + if (Index.isSearching) { + Index.sortedNodes = Index.querySearch(Index.searchInput.value) || Index.sortedNodes; + } + Index.sortOnTop(function(thread) { + return thread.isSticky; + }); + if (Conf['Filter']) { + Index.sortOnTop(function(thread) { + return thread.isOnTop; + }); + } + if (Conf['Anchor Hidden Threads']) { + return Index.sortOnTop(function(thread) { + return !thread.isHidden; + }); + } + }, + sortOnTop: function(match) { + var i, offset, threadRoot, _i, _len, _ref, _ref1; + offset = 0; + _ref = Index.sortedNodes; + for (i = _i = 0, _len = _ref.length; _i < _len; i = _i += 2) { + threadRoot = _ref[i]; + if (match(Get.threadFromRoot(threadRoot))) { + (_ref1 = Index.sortedNodes).splice.apply(_ref1, [offset++ * 2, 0].concat(__slice.call(Index.sortedNodes.splice(i, 2)))); + } + } + }, + buildIndex: function() { + var nodes, nodesPerPage, pageNum; + if (Conf['Index Mode'] === 'paged') { + pageNum = Index.getCurrentPage(); + nodesPerPage = Index.threadsNumPerPage * 2; + nodes = Index.sortedNodes.slice(nodesPerPage * pageNum, nodesPerPage * (pageNum + 1)); + } else { + nodes = Index.sortedNodes; + } + $.rmAll(Index.root); + if (Conf['Show Replies']) { + Index.buildReplies(nodes); + } + $.event('IndexBuild', nodes); + return $.add(Index.root, nodes); + }, + 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 { + pageNum = Index.pageBeforeSearch; + delete Index.pageBeforeSearch; + delete Index.searchInput.dataset.searching; + } + Index.sort(); + if (Conf['Index Mode'] === 'paged') { + 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 found, i, threadRoot, _i, _len, _ref; + found = []; + _ref = Index.sortedNodes; + for (i = _i = 0, _len = _ref.length; _i < _len; i = _i += 2) { + threadRoot = _ref[i]; + if (Index.searchMatch(Get.threadFromRoot(threadRoot), keywords)) { + found.push(Index.sortedNodes[i], Index.sortedNodes[i + 1]); + } + } + 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; @@ -1861,6 +2783,13 @@ return filename; } }, + thumbRotate: (function() { + var n; + n = 0; + return function() { + return n = (n + 1) % 3; + }; + })(), postFromObject: function(data, boardID) { var o; o = { @@ -1890,7 +2819,7 @@ width: data.w, MD5: data.md5, size: data.fsize, - turl: "//t.4cdn.org/" + boardID + "/thumb/" + data.tim + "s.jpg", + turl: "//" + (Build.thumbRotate()) + ".t.4cdn.org/" + boardID + "/thumb/" + data.tim + "s.jpg", theight: data.tn_h, twidth: data.tn_w, isSpoiler: !!data.spoiler, @@ -1905,10 +2834,11 @@ @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE */ - var a, boardID, capcode, capcodeClass, capcodeStart, closed, comment, container, date, dateUTC, email, emailEnd, emailStart, ext, file, fileDims, fileHTML, fileInfo, fileSize, fileThumb, filename, flag, flagCode, flagName, href, imgSrc, isClosed, isOP, isSticky, name, postID, quote, shortFilename, spoilerRange, staticPath, sticky, subject, threadID, tripcode, uniqueID, userID, _i, _len, _ref; + 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 = '//s.4cdn.org/image/'; + staticPath = Build.staticPath, gifIcon = Build.gifIcon; + tripcode = tripcode ? " " + tripcode + "" : ''; if (email) { emailStart = ''; emailEnd = ''; @@ -1916,39 +2846,33 @@ emailStart = ''; emailEnd = ''; } - subject = " " + (subject || '') + " "; - userID = !capcode && uniqueID ? (" (ID: ") + ("" + uniqueID + ") ") : ''; switch (capcode) { case 'admin': case 'admin_highlight': capcodeClass = " capcodeAdmin"; capcodeStart = " ## Admin"; - capcode = (" "; + capcodeIcon = (" "; break; case 'mod': capcodeClass = " capcodeMod"; capcodeStart = " ## Mod"; - capcode = (" "; + capcodeIcon = (" "; break; case 'developer': capcodeClass = " capcodeDeveloper"; capcodeStart = " ## Developer"; - capcode = (" "; + capcodeIcon = (" "; break; default: capcodeClass = ''; capcodeStart = ''; - capcode = ''; + capcodeIcon = ''; } + userID = !capcode && uniqueID ? (" (ID: ") + ("" + uniqueID + ") ") : ''; flag = !flagCode ? '' : boardID === 'pol' ? "  + flagCode + " : " "; if (file != null ? file.isDeleted : void 0) { - fileHTML = isOP ? ("
") + ("File deleted.") + "
" : ("
") + ("File deleted.") + "
"; + fileHTML = isOP ? ("
") + ("File deleted.") + "
" : ("
") + ("File deleted.") + "
"; } else if (file) { - ext = file.name.slice(-3); - if (!file.twidth && !file.theight && ext === 'gif') { - file.twidth = file.width; - file.theight = file.height; - } fileSize = $.bytesToString(file.size); fileThumb = file.turl; if (file.isSpoiler) { @@ -1971,19 +2895,25 @@ shortFilename = a.innerHTML; a.textContent = filename; filename = a.innerHTML.replace(/'/g, '''); - fileDims = ext === 'pdf' ? 'PDF' : "" + file.width + "x" + file.height; + 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 = ''; } - tripcode = tripcode ? " " + tripcode + "" : ''; - sticky = isSticky ? " Sticky" : ''; - closed = isClosed ? " Closed" : ''; + 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 || '') + "
" + " " + "
" + innerHTML: "" + (isOP ? '' : "
>>
") + "
" + (isOP ? fileHTML : '') + "" + (isOP ? '' : fileHTML) + "
" + (comment || '') + "
" + ' ' + "
" }); _ref = $$('.quotelink', container); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -1995,6 +2925,43 @@ 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) { + var OP, files, nodes, posts, root, _ref; + 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 + }); + } + 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)); + } + $.add(root, nodes); + return root; } }; @@ -2051,36 +3018,36 @@ }; }, allQuotelinksLinkingTo: function(post) { - var ID, quote, quotedPost, quotelinks, quoterPost, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3; + var ID, quote, quotedPost, quotelinks, quoterPost, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3, _ref4; quotelinks = []; _ref = g.posts; for (ID in _ref) { quoterPost = _ref[ID]; - if (quoterPost.quotes.contains(post.fullID)) { - _ref1 = [quoterPost].concat(quoterPost.clones); - for (_i = 0, _len = _ref1.length; _i < _len; _i++) { - quoterPost = _ref1[_i]; + if (_ref1 = post.fullID, __indexOf.call(quoterPost.quotes, _ref1) >= 0) { + _ref2 = [quoterPost].concat(quoterPost.clones); + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + quoterPost = _ref2[_i]; quotelinks.push.apply(quotelinks, quoterPost.nodes.quotelinks); } } } if (Conf['Quote Backlinks']) { - _ref2 = post.quotes; - for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) { - quote = _ref2[_j]; + _ref3 = post.quotes; + for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) { + quote = _ref3[_j]; if (!(quotedPost = g.posts[quote])) { continue; } - _ref3 = [quotedPost].concat(quotedPost.clones); - for (_k = 0, _len2 = _ref3.length; _k < _len2; _k++) { - quotedPost = _ref3[_k]; + _ref4 = [quotedPost].concat(quotedPost.clones); + for (_k = 0, _len2 = _ref4.length; _k < _len2; _k++) { + quotedPost = _ref4[_k]; quotelinks.push.apply(quotelinks, __slice.call(quotedPost.nodes.backlinks)); } } } return quotelinks.filter(function(quotelink) { - var boardID, postID, _ref4; - _ref4 = Get.postDataFromLink(quotelink), boardID = _ref4.boardID, postID = _ref4.postID; + var boardID, postID, _ref5; + _ref5 = Get.postDataFromLink(quotelink), boardID = _ref5.boardID, postID = _ref5.postID; return boardID === post.board.ID && postID === post.ID; }); }, @@ -2126,7 +3093,7 @@ return; } status = req.status; - if (![200, 304].contains(status)) { + if (status !== 200 && status !== 304) { if (url = Redirect.to('post', { boardID: boardID, postID: postID @@ -2191,8 +3158,8 @@ 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, + postID: postID, + threadID: threadID, boardID: boardID, name: data.name_processed, capcode: (function() { @@ -2486,7 +3453,7 @@ 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 ? ['100%', 'auto'] : ['auto', '100%'], left = _ref2[0], right = _ref2[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; @@ -2704,7 +3671,7 @@ Filter = { filters: {}, init: function() { - var boards, err, filter, hl, key, op, regexp, stub, top, _i, _len, _ref, _ref1, _ref2, _ref3, _ref4; + 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; } @@ -2724,10 +3691,10 @@ } filter = filter.replace(regexp[0], ''); boards = ((_ref1 = filter.match(/boards:([^;]+)/)) != null ? _ref1[1].toLowerCase() : void 0) || 'global'; - if (boards !== 'global' && !(boards.split(',')).contains(g.BOARD.ID)) { + if (boards !== 'global' && (_ref2 = g.BOARD.ID, __indexOf.call(boards.split(','), _ref2) < 0)) { continue; } - if (['uniqueID', 'MD5'].contains(key)) { + if (key === 'uniqueID' || key === 'MD5') { regexp = regexp[1]; } else { try { @@ -2738,10 +3705,10 @@ continue; } } - op = ((_ref2 = filter.match(/[^t]op:(yes|no|only)/)) != null ? _ref2[1] : void 0) || 'yes'; + op = ((_ref3 = filter.match(/[^t]op:(yes|no|only)/)) != null ? _ref3[1] : void 0) || 'yes'; stub = (function() { - var _ref3; - switch ((_ref3 = filter.match(/stub:(yes|no)/)) != null ? _ref3[1] : void 0) { + var _ref4; + switch ((_ref4 = filter.match(/stub:(yes|no)/)) != null ? _ref4[1] : void 0) { case 'yes': return true; case 'no': @@ -2751,8 +3718,8 @@ } })(); if (hl = /highlight/.test(filter)) { - hl = ((_ref3 = filter.match(/highlight:(\w+)/)) != null ? _ref3[1] : void 0) || 'filter-highlight'; - top = ((_ref4 = filter.match(/top:(yes|no)/)) != null ? _ref4[1] : void 0) || 'yes'; + 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)); @@ -2793,7 +3760,7 @@ }; }, node: function() { - var filter, firstThread, key, result, thisThread, value, _i, _len, _ref; + var filter, key, result, value, _i, _len, _ref; if (this.isClone) { return; } @@ -2819,13 +3786,8 @@ return; } $.addClass(this.nodes.root, result["class"]); - if (!this.isReply && result.top && g.VIEW === 'index') { - thisThread = this.nodes.root.parentNode; - if (firstThread = $('div[class="postContainer opContainer"]')) { - if (firstThread !== this.nodes.root) { - $.before(firstThread.parentNode, [thisThread, thisThread.nextElementSibling]); - } - } + if (!this.isReply && result.top) { + this.thread.isOnTop = true; } } } @@ -2949,7 +3911,7 @@ var re, type, value; type = this.dataset.type; value = Filter[type](Filter.menu.post); - re = ['uniqueID', 'MD5'].contains(type) ? value : value.replace(/\/|\\|\^|\$|\n|\.|\(|\)|\{|\}|\[|\]|\?|\*|\+|\|/g, function(c) { + re = type === 'uniqueID' || type === 'MD5' ? value : value.replace(/\/|\\|\^|\$|\n|\.|\(|\)|\{|\}|\[|\]|\?|\*|\+|\|/g, function(c) { if (c === '\n') { return '\\n'; } else if (c === '\\') { @@ -2958,7 +3920,7 @@ return "\\" + c; } }); - re = ['uniqueID', 'MD5'].contains(type) ? "/" + re + "/" : "/^" + re + "$/"; + re = type === 'uniqueID' || type === 'MD5' ? "/" + re + "/" : "/^" + re + "$/"; return $.get(type, Conf[type], function(item) { var save, section, select, ta, tl; save = item[type]; @@ -3181,12 +4143,16 @@ } }, makeButton: function(post, type) { - var a; + var a, span; + span = $.el('span', { + className: "brackets-wrap", + textContent: "\u00A0" + (type === 'hide' ? '-' : '+') + "\u00A0" + }); a = $.el('a', { className: "" + type + "-reply-button", - innerHTML: " " + (type === 'hide' ? '-' : '+') + " ", href: 'javascript:;' }); + $.add(a, span); $.on(a, 'click', PostHiding.toggle); return a; }, @@ -3215,7 +4181,7 @@ return PostHiding.saveHiddenState(post, post.isHidden); }, hide: function(post, makeStub, hideRecursively) { - var a, button, postInfo, quotelink, _i, _len, _ref; + var a, postInfo, quotelink, _i, _len, _ref; if (makeStub == null) { makeStub = Conf['Stubs']; } @@ -3245,7 +4211,10 @@ post.nodes.stub = $.el('div', { className: 'stub' }); - $.add(post.nodes.stub, Conf['Menu'] ? [a, $.tn(' '), button = Menu.makeButton(post)] : a); + $.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) { @@ -3331,7 +4300,7 @@ _ref = g.posts; for (ID in _ref) { post = _ref[ID]; - if (post.quotes.contains(fullID)) { + if (__indexOf.call(post.quotes, fullID) >= 0) { recursive.apply(null, [post].concat(__slice.call(args))); } } @@ -3345,6 +4314,7 @@ } this.db = new DataBoard('hiddenThreads'); this.syncCatalog(); + $.on(d, 'IndexBuild', this.onIndexBuild); return Thread.callbacks.push({ name: 'Thread Hiding', cb: this.node @@ -3363,6 +4333,22 @@ } 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({ @@ -3524,6 +4510,25 @@ $.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)) || {}; @@ -3557,27 +4562,19 @@ return ThreadHiding.saveHiddenState(thread); }, hide: function(thread, makeStub) { - var OP, a, numReplies, opInfo, span, threadRoot; + var threadRoot; if (makeStub == null) { makeStub = Conf['Stubs']; } - OP = thread.OP; - threadRoot = OP.nodes.root.parentNode; - thread.isHidden = true; - if (!makeStub) { - threadRoot.hidden = threadRoot.nextElementSibling.hidden = true; + if (thread.isHidden) { return; } - numReplies = ((span = $('.summary', threadRoot)) ? +span.textContent.match(/\d+/) : 0) + $$('.opContainer ~ .replyContainer', threadRoot).length; - numReplies = numReplies === 1 ? '1 reply' : "" + (numReplies || 'No') + " replies"; - opInfo = Conf['Anonymize'] ? 'Anonymous' : $('.nameBlock', OP.nodes.info).textContent; - a = ThreadHiding.makeButton(thread, 'show'); - $.add(a, $.tn(" " + opInfo + " (" + numReplies + ")")); - thread.stub = $.el('div', { - className: 'stub' - }); - $.add(thread.stub, Conf['Menu'] ? [a, $.tn(' '), Menu.makeButton()] : a); - return $.prepend(threadRoot, thread.stub); + 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; @@ -3838,7 +4835,7 @@ }); }, node: function() { - var boardID, fullID, i, postID, quotelink, quotelinks, quotes, _ref; + var boardID, fullID, i, postID, quotelink, quotelinks, quotes, _ref, _ref1; if (this.isClone && this.thread === this.context.thread) { return; } @@ -3846,19 +4843,19 @@ return; } quotelinks = this.nodes.quotelinks; - if (this.isClone && quotes.contains(this.thread.fullID)) { + 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 (!quotes.contains(fullID)) { + if (__indexOf.call(quotes, fullID) < 0) { return; } i = 0; while (quotelink = quotelinks[i++]) { - _ref = Get.postDataFromLink(quotelink), boardID = _ref.boardID, postID = _ref.postID; + _ref1 = Get.postDataFromLink(quotelink), boardID = _ref1.boardID, postID = _ref1.postID; if (("" + boardID + "." + postID) === fullID) { $.add(quotelink, $.tn(QuoteOP.text)); } @@ -3991,127 +4988,138 @@ innerHTML: '' }); input = $('input', this.controls); - $.on(input, 'change', QuoteThreading.toggle); + $.on(input, 'change', this.toggle); $.event('AddMenuEntry', { type: 'header', el: this.controls, order: 98 }); - $.on(d, '4chanXInitFinished', this.setup); + if (!Conf['Unread Count']) { + $.on(d, '4chanXInitFinished', this.setup); + } return Post.callbacks.push({ name: 'Quote Threading', cb: this.node }); }, setup: function() { - var ID, post, posts; $.off(d, '4chanXInitFinished', QuoteThreading.setup); - posts = g.posts; - for (ID in posts) { - post = posts[ID]; + return QuoteThreading.force(); + }, + force: function() { + var ID, post, _ref; + _ref = g.posts; + for (ID in _ref) { + post = _ref[ID]; if (post.cb) { - post.cb.call(post); + post.cb(true); } } - return QuoteThreading.hasRun = true; }, node: function() { - var ID, fullID, keys, len, post, posts, qid, quote, quotes, uniq, _i, _len; - if (this.isClone || !QuoteThreading.enabled || this.thread.OP === this) { - return; - } - quotes = this.quotes, ID = this.ID, fullID = this.fullID; + var keys, len, post, posts, quote, _i, _len, _ref; posts = g.posts; - if (!(post = posts[fullID]) || post.isHidden) { + if (this.isClone || !QuoteThreading.enabled) { return; } - uniq = {}; - len = ("" + g.BOARD).length + 1; - for (_i = 0, _len = quotes.length; _i < _len; _i++) { - quote = quotes[_i]; - qid = quote; - if (!(qid.slice(len) < ID)) { - continue; - } - if (qid in posts) { - uniq[qid.slice(len)] = true; + if (Conf['Unread Count']) { + Unread.posts.push(this); + } + if (this.thread.OP === this || !(post = posts[this.fullID]) || post.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); } } - keys = Object.keys(uniq); if (keys.length !== 1) { return; } - this.threaded = "" + g.BOARD + "." + keys[0]; + this.threaded = keys[0]; return this.cb = QuoteThreading.nodeinsert; }, - nodeinsert: function() { - var bottom, height, qpost, qroot, threadContainer, top, _ref; - qpost = g.posts[this.threaded]; - delete this.threaded; - delete this.cb; - if (this.thread.OP === qpost) { + nodeinsert: function(force) { + var bottom, height, post, posts, root, threadContainer, top, _ref; + post = g.posts[this.threaded]; + if (this.thread.OP === post) { return false; } - if (QuoteThreading.hasRun) { + posts = Unread.posts; + root = post.nodes.root; + if (!force) { height = doc.clientHeight; - _ref = qpost.nodes.root.getBoundingClientRect(), bottom = _ref.bottom, top = _ref.top; - if (!(Unread.posts.contains(qpost) || ((bottom < height) && (top > 0)))) { + _ref = root.getBoundingClientRect(), bottom = _ref.bottom, top = _ref.top; + if (!((Conf['Unread Count'] && posts[post.ID]) || ((bottom < height) && (top > 0)))) { return false; } } - qroot = qpost.nodes.root; - if (!$.hasClass(qroot, 'threadOP')) { - $.addClass(qroot, 'threadOP'); + 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' }); - $.after(qroot, threadContainer); - } else { - threadContainer = qroot.nextSibling; + $.add(threadContainer, this.nodes.root); + $.after(root, threadContainer); + $.addClass(root, 'threadOP'); + } + if (!Conf['Unread Count']) { + return true; + } + if (posts[post.ID]) { + posts.after(post, this); + } else { + posts.prepend(this); } - $.add(threadContainer, this.nodes.root); return true; }, toggle: function() { - var container, containers, node, post, replies, reply, thread, _i, _j, _k, _len, _len1, _len2, _ref; - thread = $('.thread'); - replies = $$('.thread > .replyContainer, .threadContainer > .replyContainer', thread); - QuoteThreading.enabled = this.checked; - if (this.checked) { - QuoteThreading.hasRun = false; - for (_i = 0, _len = replies.length; _i < _len; _i++) { - reply = replies[_i]; - QuoteThreading.node.call(node = Get.postFromRoot(reply)); - if (node.cb) { - node.cb(); + var ID, container, containers, nodes, post, posts, thread, _i, _j, _k, _len, _len1, _len2, _ref, _ref1; + if (QuoteThreading.enabled = this.checked) { + QuoteThreading.force(); + } else { + thread = $('.thread'); + posts = []; + nodes = []; + _ref = g.posts; + for (ID in _ref) { + post = _ref[ID]; + if (!(post === post.thread.OP || post.isClone)) { + posts.push(post); } } - QuoteThreading.hasRun = true; - } else { - replies.sort(function(a, b) { - var aID, bID; - aID = Number(a.id.slice(2)); - bID = Number(b.id.slice(2)); - return aID - bID; + posts.sort(function(a, b) { + return a.ID - b.ID; }); - $.add(thread, replies); + 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]; + _ref1 = $$('.threadOP'); + for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { + post = _ref1[_k]; $.rmClass(post, 'threadOP'); } } - return Unread.update(true); }, kb: function() { var control; control = $.id('threadingControl'); - return control.click(); + control.checked = !control.checked; + return QuoteThreading.toggle.call(control); } }; @@ -4285,12 +5293,11 @@ }); } } - if (!this.quotes.contains(quoteID)) { + if (__indexOf.call(this.quotes, quoteID) < 0) { this.quotes.push(quoteID); } if (!a) { - deadlink.textContent = "" + quote + "\u00A0(Dead)"; - return; + return deadlink.textContent = "" + quote + "\u00A0(Dead)"; } $.replace(deadlink, a); if ($.hasClass(a, 'quotelink')) { @@ -4310,6 +5317,2359 @@ } }; + QR = { + 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" + }); + $.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, 'paste', QR.paste); + $.on(d, 'dragover', QR.dragOver); + $.on(d, 'drop', QR.dropFile); + $.on(d, 'dragstart dragend', QR.drag); + switch (g.VIEW) { + case 'index': + return $.on(d, 'IndexRefresh', QR.generatePostableThreadsList); + case 'thread': + return $.on(d, 'ThreadUpdate', 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, 'has-focus'); + }, + focusout: function() { + return $.rmClass(QR.nodes.el, 'has-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 + }); + notif.onclick = function() { + return window.focus(); + }; + notif.onclose = function() { + return notice.close(); + }; + return notif.onshow = function() { + return setTimeout(function() { + notif.onclose = null; + return notif.close(); + }, 7 * $.SECOND); + }; + } + }, + 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'); + }, + 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.handleFile(file, isSingle, max); + } + if (!isSingle) { + return $.addClass(QR.nodes.el, 'dump'); + } + }, + 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; + if (!QR.nodes) { + return; + } + list = QR.nodes.thread; + options = [list.firstChild]; + for (thread in g.BOARD.threads) { + 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 check, dialog, event, flagSelector, i, items, key, mimeTypes, name, node, nodes, save, 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', + 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); + } + check = { + jpg: 'image/jpeg', + pdf: 'application/pdf', + swf: 'application/x-shockwave-flash' + }; + mimeTypes = $('ul.rules > li').textContent.trim().match(/: (.+)/)[1].toLowerCase().replace(/\w+/g, function(type) { + return check[type] || ("image/" + type); + }); + QR.mimeTypes = mimeTypes.split(', '); + QR.mimeTypes.push(''); + 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); + } + if (flagSelector = $('.flagSelector')) { + nodes.flag = flagSelector.cloneNode(true); + nodes.flag.dataset.name = 'flag'; + nodes.flag.dataset["default"] = '0'; + $.add(nodes.form, nodes.flag); + } + $.on(nodes.filename.parentNode, 'click keydown', QR.openFileInput); + $.on(dialog, 'focusin', QR.focusin); + $.on(dialog, 'focusout', QR.focusout); + $.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.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); + } + 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); + }, + 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() { + delete QR.req; + post.unlock(); + QR.cooldown.auto = false; + QR.status(); + 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($.id('postForm').parentNode.action, 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.'; + } + 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+)\ssecond/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() { + if (d.cookie.indexOf('pass_enabled=1') >= 0) { + return; + } + if (!(this.isEnabled = !!$.id('captchaFormPart'))) { + return; + } + return $.asap((function() { + return $.id('recaptcha_challenge_field_holder'); + }), this.ready.bind(this)); + }, + ready: function() { + var imgContainer, input, setLifetime, + _this = this; + setLifetime = function(e) { + return _this.lifetime = e.detail; + }; + $.on(window, 'captcha:timeout', setLifetime); + $.globalEval('window.dispatchEvent(new CustomEvent("captcha:timeout", {detail: RecaptchaState.timeout}))'); + $.off(window, 'captcha:timeout', setLifetime); + imgContainer = $.el('div', { + className: 'captcha-img', + title: 'Reload reCAPTCHA', + innerHTML: '' + }); + input = $.el('input', { + className: 'captcha-input field', + title: 'Verification', + autocomplete: 'off', + spellcheck: false, + tabIndex: 55 + }); + this.nodes = { + challenge: $.id('recaptcha_challenge_field_holder'), + img: imgContainer.firstChild, + input: input + }; + new MutationObserver(this.load.bind(this)).observe(this.nodes.challenge, { + childList: true + }); + $.on(imgContainer, 'click', this.reload.bind(this)); + $.on(input, 'keydown', this.keydown.bind(this)); + $.on(input, 'focus', function() { + return $.addClass(QR.nodes.el, 'focus'); + }); + $.on(input, 'blur', function() { + return $.rmClass(QR.nodes.el, 'focus'); + }); + $.get('captchas', [], function(_arg) { + var captchas; + captchas = _arg.captchas; + return _this.sync(captchas); + }); + $.sync('captchas', this.sync); + this.reload(); + $.addClass(QR.nodes.el, 'has-captcha'); + return $.after(QR.nodes.com.parentNode, [imgContainer, input]); + }, + 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; + 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.length; + 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, event, prev, _i, _len, _ref, + _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 + }; + $.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); + _ref = ['dragStart', 'dragEnter', 'dragLeave', 'dragOver', 'dragEnd', 'drop']; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + event = _ref[_i]; + $.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', + 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; + if (this.status !== 200) { + return; + } + i = 0; + while (postObj = JSON.parse(this.response).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 ID, file, func, post, _i, _len, _ref, _ref1; + $.event('CloseMenu'); + 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; + } + _ref = g.posts; + for (ID in _ref) { + post = _ref[ID]; + _ref1 = [post].concat(post.clones); + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + post = _ref1[_i]; + file = post.file; + if (!(file && file.isImage && doc.contains(post.nodes.root))) { + continue; + } + if (ImageExpand.on && (!Conf['Expand spoilers'] && file.isSpoiler || Conf['Expand from here'] && Header.getTopOf(file.thumb) < 0)) { + continue; + } + $.queueTask(func, 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 = JSON.parse(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 = JSON.parse(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 + }); + 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 + }); + }, + 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, id, post, _ref; + enabled = Conf['prefetch'] = this.checked; + if (enabled) { + _ref = g.threads["" + g.BOARD.ID + "." + g.THREADID].posts; + for (id in _ref) { + post = _ref[id]; + ImageLoader.node.call(post); + } + } + } + }; + + 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)/ig, function(parameter) { + switch (parameter) { + case '%TURL': + return "' + encodeURIComponent(post.file.thumbURL) + '"; + case '%URL': + return "' + encodeURIComponent(post.file.URL) + '"; + case '%MD5': + return "' + encodeURIComponent(post.file.MD5) + '"; + case '%board': + return "' + encodeURIComponent(post.board) + '"; + default: + return 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']) { @@ -4635,9 +7995,15 @@ LiveLeak: { regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/, el: function(a) { - return $.el('object', { - innerHTML: "" + 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: { @@ -4649,7 +8015,7 @@ $.cache("https://mediacru.sh/" + a.dataset.uid + ".json", function() { var embed, file, files, status, type, _i, _j, _len, _len1, _ref; status = this.status; - if (![200, 304].contains(status)) { + if (status !== 200 && status !== 304) { return div.innerHTML = "ERROR " + status; } files = JSON.parse(this.response).files; @@ -4792,9 +8158,12 @@ YouTube: { regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/, el: function(a) { - return $.el('iframe', { + 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) { @@ -4808,2342 +8177,6 @@ } }; - QR = { - init: function() { - var sc; - if (!Conf['Quick Reply']) { - return; - } - this.db = new DataBoard('yourPosts'); - if (Conf['QR Shortcut']) { - sc = $.el('a', { - className: "qr-shortcut fourchanx-icon icon-comment " + (!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" - }); - $.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, 'paste', QR.paste); - $.on(d, 'dragover', QR.dragOver); - $.on(d, 'drop', QR.dropFile); - $.on(d, 'dragstart dragend', QR.drag); - return $.on(d, 'ThreadUpdate', 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, 'has-focus'); - }, - focusout: function() { - return $.rmClass(QR.nodes.el, 'has-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 - }); - notif.onclick = function() { - return window.focus(); - }; - notif.onclose = function() { - return notice.close(); - }; - return notif.onshow = function() { - return setTimeout(function() { - notif.onclose = null; - return notif.close(); - }, 7 * $.SECOND); - }; - } - }, - 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; - }, - 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; - 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' && !((boards.split(',')).contains(g.BOARD.ID))) { - 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 (!types[type].contains(val)) { - 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); - }); - } - }, - cooldown: { - init: function() { - var key, setTimers, type, _base, - _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}))'); - (_base = QR.cooldown).types || (_base.types = {}); - $.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 ('timeout' in cooldown) { - 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(); - } - } - }, - 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'); - }, - 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.handleFile(file, isSingle, max); - } - if (!isSingle) { - return $.addClass(QR.nodes.el, 'dump'); - } - }, - handleFile: function(file, isSingle, max) { - var post; - if (file.size > max) { - QR.error("" + file.name + ": File too large (file: " + ($.bytesToString(file.size)) + ", max: " + ($.bytesToString(max)) + ")."); - return; - } else if (!QR.mimeTypes.contains(file.type)) { - 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) { - 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 && ![32, 13].contains(e.keyCode)) || e.ctrlKey) { - return; - } - e.preventDefault(); - return QR.nodes.fileInput.click(); - }, - posts: [], - post: (function() { - function _Class(select) { - this.select = __bind(this.select, this); - var el, event, prev, _i, _len, _ref, - _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 - }; - $.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); - _ref = ['dragStart', 'dragEnter', 'dragLeave', 'dragOver', 'dragEnd', 'drop']; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - event = _ref[_i]; - $.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; - 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; - - })(), - captcha: { - init: function() { - if (d.cookie.indexOf('pass_enabled=1') >= 0) { - return; - } - if (!(this.isEnabled = !!$.id('captchaFormPart'))) { - return; - } - return $.asap((function() { - return $.id('recaptcha_challenge_field_holder'); - }), this.ready.bind(this)); - }, - ready: function() { - var imgContainer, input, setLifetime, - _this = this; - setLifetime = function(e) { - return _this.lifetime = e.detail; - }; - $.on(window, 'captcha:timeout', setLifetime); - $.globalEval('window.dispatchEvent(new CustomEvent("captcha:timeout", {detail: RecaptchaState.timeout}))'); - $.off(window, 'captcha:timeout', setLifetime); - imgContainer = $.el('div', { - className: 'captcha-img', - title: 'Reload', - innerHTML: '' - }); - input = $.el('input', { - className: 'captcha-input field', - title: 'Verification', - autocomplete: 'off', - spellcheck: false, - tabIndex: 55 - }); - this.nodes = { - challenge: $.id('recaptcha_challenge_field_holder'), - img: imgContainer.firstChild, - input: input - }; - new MutationObserver(this.load.bind(this)).observe(this.nodes.challenge, { - childList: true - }); - $.on(imgContainer, 'click', this.reload.bind(this)); - $.on(input, 'keydown', this.keydown.bind(this)); - $.on(input, 'focus', function() { - return $.addClass(QR.nodes.el, 'focus'); - }); - $.on(input, 'blur', function() { - return $.rmClass(QR.nodes.el, 'focus'); - }); - $.get('captchas', [], function(_arg) { - var captchas; - captchas = _arg.captchas; - return _this.sync(captchas); - }); - $.sync('captchas', this.sync); - this.reload(); - $.addClass(QR.nodes.el, 'has-captcha'); - return $.after(QR.nodes.com.parentNode, [imgContainer, input]); - }, - 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; - 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.length; - 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(); - } - }, - dialog: function() { - var check, dialog, event, flagSelector, i, items, key, mimeTypes, name, node, nodes, save, thread, 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', - 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); - } - check = { - jpg: 'image/jpeg', - pdf: 'application/pdf', - swf: 'application/x-shockwave-flash' - }; - mimeTypes = $('ul.rules > li').textContent.trim().match(/: (.+)/)[1].toLowerCase().replace(/\w+/g, function(type) { - return check[type] || ("image/" + type); - }); - QR.mimeTypes = mimeTypes.split(', '); - QR.mimeTypes.push(''); - 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); - } - if (flagSelector = $('.flagSelector')) { - nodes.flag = flagSelector.cloneNode(true); - nodes.flag.dataset.name = 'flag'; - nodes.flag.dataset["default"] = '0'; - $.add(nodes.form, nodes.flag); - } - for (thread in g.BOARD.threads) { - $.add(nodes.thread, $.el('option', { - value: thread, - textContent: "Thread No." + thread - })); - } - $.on(nodes.filename.parentNode, 'click keydown', QR.openFileInput); - $.on(dialog, 'focusin', QR.focusin); - $.on(dialog, 'focusout', QR.focusout); - $.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.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); - } - QR.persona.init(); - new QR.post(true); - QR.status(); - QR.cooldown.init(); - QR.captcha.init(); - $.add(d.body, dialog); - return $.event('QRDialogCreation', null, dialog); - }, - 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() { - delete QR.req; - post.unlock(); - QR.cooldown.auto = false; - QR.status(); - 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($.id('postForm').parentNode.action, 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.'; - } - 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+)\ssecond/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(); - } - }; - - 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; - if (!(Conf['Fappe Tyme'] || Conf['Werk Tyme']) || g.VIEW === 'catalog' || g.BOARD === 'f') { - return; - } - if (Conf['Fappe Tyme']) { - el = $.el('label', { - innerHTML: " Fappe Tyme", - title: 'Fappe Tyme' - }); - FappeTyme.fappe = input = el.firstElementChild; - $.on(input, 'change', FappeTyme.cb.fappe); - $.event('AddMenuEntry', { - type: 'header', - el: el, - order: 97 - }); - } - if (Conf['Werk Tyme']) { - el = $.el('label', { - innerHTML: " Werk Tyme", - title: 'Werk Tyme' - }); - FappeTyme.werk = input = el.firstElementChild; - $.on(input, 'change', FappeTyme.cb.werk); - $.event('AddMenuEntry', { - type: 'header', - el: el, - order: 98 - }); - } - return Post.callbacks.push({ - name: 'Fappe Tyme', - cb: this.node - }); - }, - node: function() { - if (this.file) { - return; - } - return $.addClass(this.nodes.root, "noFile"); - }, - cb: { - fappe: function() { - $.toggleClass(doc, 'fappeTyme'); - return FappeTyme.fappe.checked = $.hasClass(doc, 'fappeTyme'); - }, - werk: function() { - $.toggleClass(doc, 'werkTyme'); - return FappeTyme.werk.checked = $.hasClass(doc, 'werkTyme'); - } - } - }; - - 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: 'fourchanx-icon icon-picture', - 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; - if (this.status !== 200) { - return; - } - i = 0; - while (postObj = JSON.parse(this.response).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 (['Fit Width', 'Fit Height', 'Hide Thumbnails'].contains(name)) { - $.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 fourchanx-icon icon-resize-full', - textContent: 'EAI', - title: 'Expand All Images', - href: 'javascript:;' - }); - $.on(this.EAI, 'click', ImageExpand.cb.toggleAll); - Header.addShortcut(this.EAI, 2); - 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 ID, file, func, post, _i, _len, _ref, _ref1; - $.event('CloseMenu'); - if (ImageExpand.on = $.hasClass(ImageExpand.EAI, 'expand-all-shortcut')) { - ImageExpand.EAI.className = 'contract-all-shortcut fourchanx-icon icon-resize-small'; - ImageExpand.EAI.title = 'Contract All Images'; - func = ImageExpand.expand; - } else { - ImageExpand.EAI.className = 'expand-all-shortcut fourchanx-icon icon-resize-full'; - ImageExpand.EAI.title = 'Expand All Images'; - func = ImageExpand.contract; - } - _ref = g.posts; - for (ID in _ref) { - post = _ref[ID]; - _ref1 = [post].concat(post.clones); - for (_i = 0, _len = _ref1.length; _i < _len; _i++) { - post = _ref1[_i]; - file = post.file; - if (!(file && file.isImage && doc.contains(post.nodes.root))) { - continue; - } - if (ImageExpand.on && (!Conf['Expand spoilers'] && file.isSpoiler || Conf['Expand from here'] && file.thumb.getBoundingClientRect().top < 0)) { - continue; - } - $.queueTask(func, post); - } - } - }, - setFitness: function() { - return (this.checked ? $.addClass : $.rmClass)(doc, this.name.toLowerCase().replace(/\s+/g, '-')); - } - }, - toggle: function(post) { - var headRect, rect, root, thumb, x, y; - thumb = post.file.thumb; - if (!(post.file.isExpanded || $.hasClass(thumb, 'expanding'))) { - ImageExpand.expand(post); - return; - } - root = post.nodes.root; - rect = (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(); - if (rect.top < 0) { - y = rect.top; - if (Conf['Fixed Header'] && !Conf['Bottom Header']) { - headRect = Header.bar.getBoundingClientRect(); - y -= headRect.top + headRect.height; - } - } - if (rect.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 prev, 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; - } - prev = post.nodes.root.getBoundingClientRect(); - return $.queueTask(function() { - var curr; - $.addClass(post.nodes.root, 'expanded-image'); - $.rmClass(post.file.thumb, 'expanding'); - if (!(prev.top + prev.height <= 0)) { - return; - } - curr = post.nodes.root.getBoundingClientRect(); - return window.scrollBy(0, curr.height - prev.height + curr.top - prev.top); - }); - }, - 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 = JSON.parse(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 = JSON.parse(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 - }); - 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 - }); - }, - 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, id, post, _ref; - enabled = Conf['prefetch'] = this.checked; - if (enabled) { - _ref = g.threads["" + g.BOARD.ID + "." + g.THREADID].posts; - for (id in _ref) { - post = _ref[id]; - ImageLoader.node.call(post); - } - } - } - }; - - 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)/ig, function(parameter) { - switch (parameter) { - case '%TURL': - return "' + encodeURIComponent(post.file.thumbURL) + '"; - case '%URL': - return "' + encodeURIComponent(post.file.URL) + '"; - case '%MD5': - return "' + encodeURIComponent(post.file.MD5) + '"; - case '%board': - return "' + encodeURIComponent(post.board) + '"; - default: - return 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); - } - }; - ArchiveLink = { init: function() { var div, entry, type, _i, _len, _ref; @@ -7390,23 +8423,28 @@ }, node: function() { if (this.isClone) { - return $.on($('.menu-button', this.nodes.info), 'click', Menu.toggle); - } else { - return $.add(this.nodes.info, [$.tn('\u00A0'), Menu.makeButton()]); + $.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 brackets-wrap', - innerHTML: '', - href: 'javascript:;' - }); + var frag; + frag = null; return function() { - var button; - button = a.cloneNode(true); - $.on(button, 'click', Menu.toggle); - return button; + var clone; + if (frag == null) { + frag = $.nodes([ + $.tn(' '), $.el('a', { + className: 'menu-button', + innerHTML: '[]', + href: 'javascript:;' + }) + ]); + } + clone = frag.cloneNode(true); + $.on(clone.lastElementChild, 'click', Menu.toggle); + return clone; }; })(), toggle: function(e) { @@ -7855,7 +8893,7 @@ } }, update: function() { - var url; + var url, _ref; if (!navigator.onLine) { return; } @@ -7865,8 +8903,8 @@ } else { ThreadUpdater.set('timer', 'Update'); } - if (ThreadUpdater.req) { - ThreadUpdater.req.abort(); + if ((_ref = ThreadUpdater.req) != null) { + _ref.abort(); } url = "//a.4cdn.org/" + ThreadUpdater.thread.board + "/res/" + ThreadUpdater.thread + ".json"; return ThreadUpdater.req = $.ajax(url, { @@ -7875,38 +8913,21 @@ whenModified: true }); }, - updateThreadStatus: function(title, OP) { - var icon, message, root, titleLC; - titleLC = title.toLowerCase(); - if (ThreadUpdater.thread["is" + title] === !!OP[titleLC]) { + updateThreadStatus: function(type, status) { + var change, hasChanged; + if (!(hasChanged = ThreadUpdater.thread["is" + type] !== status)) { return; } - if (!(ThreadUpdater.thread["is" + title] = !!OP[titleLC])) { - message = title === 'Sticky' ? 'The thread is not a sticky anymore.' : 'The thread is not closed anymore.'; - new Notice('info', message, 30); - $.rm($("." + titleLC + "Icon", ThreadUpdater.thread.OP.nodes.info)); - return; - } - message = title === 'Sticky' ? 'The thread is now a sticky.' : 'The thread is now closed.'; - new Notice('info', message, 30); - icon = $.el('img', { - src: "//static.4chan.org/image/" + titleLC + ".gif", - alt: title, - title: title, - className: "" + titleLC + "Icon" - }); - root = $('[title="Quote this post"]', ThreadUpdater.thread.OP.nodes.info); - if (title === 'Closed') { - root = $('.stickyIcon', ThreadUpdater.thread.OP.nodes.info) || root; - } - return $.after(root, [$.tn(' '), icon]); + 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 ID, OP, count, deletedFiles, deletedPosts, files, index, key, node, num, post, postObject, posts, root, scroll, _i, _len, _ref; OP = postObjects[0]; Build.spoilerRange[ThreadUpdater.thread.board] = OP.custom_spoiler; - ThreadUpdater.updateThreadStatus('Sticky', OP); - ThreadUpdater.updateThreadStatus('Closed', OP); + ThreadUpdater.updateThreadStatus('Sticky', !!OP.sticky); + ThreadUpdater.updateThreadStatus('Closed', !!OP.closed); ThreadUpdater.thread.postLimit = !!OP.bumplimit; ThreadUpdater.thread.fileLimit = !!OP.imagelimit; posts = []; @@ -7933,12 +8954,12 @@ for (ID in _ref) { post = _ref[ID]; ID = +ID; - if (post.isDead && index.contains(ID)) { - post.resurrect(); - } else if (!index.contains(ID)) { + if (__indexOf.call(index, ID) < 0) { post.kill(); deletedPosts.push(post); - } else if (post.file && !post.file.isDead && !files.contains(ID)) { + } else if (post.isDead) { + post.resurrect(); + } else if (post.file && !(post.file.isDead || __indexOf.call(files, ID) >= 0)) { post.kill(true); deletedFiles.push(post); } @@ -7970,7 +8991,7 @@ } root = post.nodes.root; if (post.cb) { - if (!post.cb.call(post)) { + if (!post.cb()) { $.add(ThreadUpdater.root, root); } } else { @@ -7982,7 +9003,7 @@ window.scrollTo(0, d.body.clientHeight); } else { if (root) { - Header.scrollToPost(root); + Header.scrollTo(root); } } } @@ -8015,7 +9036,7 @@ id: 'watcher-link', textContent: 'Watcher', href: 'javascript:;', - className: 'disabled fourchanx-icon icon-eye-open' + className: 'disabled fa fa-eye-open' }); this.db = new DataBoard('watchedThreads', this.refresh, true); this.dialog = UI.dialog('thread-watcher', 'top: 50px; left: 0px;', "
Thread Watcher ×
"); @@ -8028,6 +9049,13 @@ $.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'); @@ -8136,7 +9164,32 @@ return ThreadWatcher.add(board.threads[threadID]); } }, - threadUpdate: function(e) { + 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({ @@ -8187,7 +9240,10 @@ return; } if (Conf['Auto Prune']) { - ThreadWatcher.rm(boardID, threadID); + ThreadWatcher.db["delete"]({ + boardID: boardID, + threadID: threadID + }); } else { data.isDead = true; ThreadWatcher.db.set({ @@ -8225,7 +9281,7 @@ makeLine: function(boardID, threadID, data) { var div, fullID, href, link, x; x = $.el('a', { - textContent: '×', + className: 'fa fa-times', href: 'javascript:;' }); $.on(x, 'click', ThreadWatcher.cb.rm); @@ -8377,7 +9433,7 @@ }); }, addMenuEntries: function() { - var cb, conf, entries, entry, name, refresh, subEntries, _i, _len, _ref, _ref1, _results; + var cb, conf, entries, entry, name, refresh, subEntries, _i, _len, _ref, _ref1; entries = []; entries.push({ cb: ThreadWatcher.cb.openAll, @@ -8430,7 +9486,6 @@ subEntries: subEntries } }); - _results = []; 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') { @@ -8442,9 +9497,8 @@ if (refresh) { this.refreshers.push(refresh.bind(entry)); } - _results.push($.event('AddMenuEntry', entry)); + $.event('AddMenuEntry', entry); } - return _results; }, createSubEntry: function(name, desc) { var entry, input; @@ -8475,7 +9529,7 @@ this.hr = $.el('hr', { id: 'unread-line' }); - this.posts = []; + this.posts = new RandomAccessList; this.postsQuotingYou = []; return Thread.callbacks.push({ name: 'Unread', @@ -8509,17 +9563,19 @@ } } Unread.addPosts(posts); - return Unread.scroll(); + if (Conf['Quote Threading']) { + QuoteThreading.force(); + } + if (Conf['Scroll to Last Read Post']) { + return Unread.scroll(); + } }, scroll: function() { - var checkPosition, hash, onload, post, posts, root; - if (!Conf['Scroll to Last Read Post']) { - return; - } + var down, hash, post, posts, root; if ((hash = location.hash.match(/\d+/)) && hash[0] in Unread.thread.posts) { return; } - if (post = Unread.posts[0]) { + if (post = Unread.posts.first) { while (root = $.x('preceding-sibling::div[contains(@class,"replyContainer")][1]', post.nodes.root)) { if (!(post = Get.postFromRoot(root)).isHidden) { break; @@ -8528,27 +9584,17 @@ if (!root) { return; } - onload = function() { - if (checkPosition(root)) { - return root.scrollIntoView(false); - } - }; + down = true; } else { posts = Object.keys(Unread.thread.posts); root = Unread.thread.posts[posts[posts.length - 1]].nodes.root; - onload = function() { - if (checkPosition(root)) { - return Header.scrollToPost(root); - } - }; } - checkPosition = function(target) { - return target.getBoundingClientRect().bottom > doc.clientHeight; - }; - return $.on(window, 'load', onload); + if (Header.getBottomOf(root) < 0) { + return Header.scrollTo(root, down); + } }, sync: function() { - var lastReadPost; + var ID, lastReadPost, post; lastReadPost = Unread.db.get({ boardID: Unread.thread.board.ID, threadID: Unread.thread.ID, @@ -8558,7 +9604,14 @@ return; } Unread.lastReadPost = lastReadPost; - Unread.readArray(Unread.posts); + 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(); @@ -8566,28 +9619,24 @@ return Unread.update(); }, addPosts: function(posts) { - var ID, data, post, _i, _len; + var ID, post, _i, _len, _ref; for (_i = 0, _len = posts.length; _i < _len; _i++) { post = posts[_i]; ID = post.ID; - if (ID <= Unread.lastReadPost || post.isHidden) { + if (ID <= Unread.lastReadPost || post.isHidden || QR.db.get({ + boardID: post.board.ID, + threadID: post.thread.ID, + postID: ID + })) { continue; } - if (QR.db) { - data = { - boardID: post.board.ID, - threadID: post.thread.ID, - postID: post.ID - }; - if (QR.db.get(data)) { - continue; - } + if (!(post.prev || post.next)) { + Unread.posts.push(post); } - Unread.posts.push(post); Unread.addPostQuotingYou(post); } if (Conf['Unread Line']) { - Unread.setLine(posts.contains(Unread.posts[0])); + Unread.setLine((_ref = Unread.posts.first, __indexOf.call(posts, _ref) >= 0)); } Unread.read(); return Unread.update(); @@ -8619,7 +9668,7 @@ icon: Favicon.logo }); notif.onclick = function() { - Header.scrollToPost(post.nodes.root); + Header.scrollToIfNeeded(post.nodes.root, true); return window.focus(); }; return notif.onshow = function() { @@ -8636,15 +9685,16 @@ } }, readSinglePost: function(post) { - var i; - if ((i = Unread.posts.indexOf(post)) === -1) { + var ID, i; + ID = post.ID; + if (!Unread.posts[ID]) { return; } - Unread.posts.splice(i, 1); - if (i === 0) { - Unread.lastReadPost = post.ID; + if (post === Unread.posts.first) { + Unread.lastReadPost = ID; Unread.saveLastReadPost(); } + Unread.posts.rm(ID); if ((i = Unread.postsQuotingYou.indexOf(post)) !== -1) { Unread.postsQuotingYou.splice(i, 1); } @@ -8660,35 +9710,22 @@ } return arr.splice(0, i); }, - read: $.debounce(50, function(e) { - var ID, height, i, post, posts; + read: $.debounce(100, function(e) { + var ID, height, post, posts; if (d.hidden || !Unread.posts.length) { return; } height = doc.clientHeight; posts = Unread.posts; - i = 0; - while (post = posts[i]) { - if (post.nodes.root.getBoundingClientRect().bottom < height) { - ID = post.ID; - if (Conf['Mark Quotes of You']) { - if (post.info.yours) { - QuoteYou.lastRead = post.nodes.root; - } - } - if (Conf['Quote Threading']) { - posts.splice(i, 1); - continue; - } - } else { - if (!Conf['Quote Threading']) { - break; - } + while (post = posts.first) { + if (!(Header.getBottomOf(post.nodes.root) > -1)) { + break; + } + ID = post.ID; + posts.rm(ID); + if (Conf['Mark Quotes of You'] && post.info.yours) { + QuoteYou.lastRead = post.nodes.root; } - i++; - } - if (i && !Conf['Quote Threading']) { - posts.splice(0, i); } if (!ID) { return; @@ -8717,7 +9754,7 @@ if (!(d.hidden || force === true)) { return; } - if (!(post = Unread.posts[0])) { + if (!(post = Unread.posts.first)) { return $.rm(Unread.hr); } if ($.x('preceding-sibling::div[contains(@class,"replyContainer")]', post.nodes.root)) { @@ -8745,125 +9782,143 @@ }; Redirect = { - data: { - thread: {}, - post: {}, - file: {} - }, init: function() { - var archive, boardID, boards, data, id, name, type, _i, _len, _ref, _ref1, _ref2; + var archive, archives, boardID, data, id, name, o, type, _i, _len, _ref, _ref1; + o = { + thread: {}, + post: {}, + file: {} + }; + archives = Redirect.archives; _ref = Conf['selectedArchives']; for (boardID in _ref) { data = _ref[boardID]; for (type in data) { id = data[type]; - if (archive = Redirect.archives[id]) { - boards = archive[type] || archive['boards']; - if (!boards.contains(boardID)) { - continue; - } - Redirect.data[type][boardID] = archive; + if ((archive = archives[id]) && __indexOf.call(archive[type] || archive['boards'], boardID) >= 0) { + o[type][boardID] = archive.data; } } } - _ref1 = Redirect.archives; - for (name in _ref1) { - archive = _ref1[name]; - _ref2 = archive.boards; - for (_i = 0, _len = _ref2.length; _i < _len; _i++) { - boardID = _ref2[_i]; - if (!(boardID in Redirect.data.thread)) { - Redirect.data.thread[boardID] = archive; + for (name in archives) { + archive = archives[name]; + _ref1 = archive.boards; + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + boardID = _ref1[_i]; + data = archive.data; + if (!(boardID in o.thread)) { + o.thread[boardID] = data; } - if (!(boardID in Redirect.data.post || archive.software !== 'foolfuuka')) { - Redirect.data.post[boardID] = archive; + if (!(boardID in o.post || archive.software !== 'foolfuuka')) { + o.post[boardID] = data; } - if (!(boardID in Redirect.data.file || !archive.files.contains(boardID))) { - Redirect.data.file[boardID] = archive; + if (!(boardID in o.file || __indexOf.call(archive.files, boardID) < 0)) { + o.file[boardID] = data; } } } + return Redirect.data = o; }, archives: { "Foolz": { - domain: "archive.foolz.us", - http: false, - https: true, - software: "foolfuuka", boards: ["a", "co", "gd", "jp", "m", "sp", "tg", "tv", "v", "vg", "vp", "vr", "wsg"], - files: ["a", "gd", "jp", "m", "tg", "vg", "vp", "vr", "wsg"] + files: ["a", "gd", "jp", "m", "tg", "vg", "vp", "vr", "wsg"], + data: { + domain: "archive.foolz.us", + http: false, + https: true, + software: "foolfuuka" + } }, "NSFW Foolz": { - domain: "nsfw.foolz.us", - http: false, - https: true, - software: "foolfuuka", boards: ["u"], - files: ["u"] + files: ["u"], + data: { + domain: "nsfw.foolz.us", + http: false, + https: true, + software: "foolfuuka" + } }, "The Dark Cave": { - domain: "archive.thedarkcave.org", - http: true, - https: true, - software: "foolfuuka", boards: ["c", "int", "out", "po"], - files: ["c", "po"] + files: ["c", "po"], + data: { + domain: "archive.thedarkcave.org", + http: true, + https: true, + software: "foolfuuka" + } }, "4plebs": { - domain: "archive.4plebs.org", - http: true, - https: true, - software: "foolfuuka", boards: ["hr", "pol", "s4s", "tg", "tv", "x"], - files: ["hr", "pol", "s4s", "tg", "tv", "x"] + files: ["hr", "pol", "s4s", "tg", "tv", "x"], + data: { + domain: "archive.4plebs.org", + http: true, + https: true, + software: "foolfuuka" + } }, "Nyafuu": { - domain: "archive.nyafuu.org", - http: true, - https: true, - software: "foolfuuka", boards: ["c", "w", "wg"], - files: ["c", "w", "wg"] + files: ["c", "w", "wg"], + data: { + domain: "archive.nyafuu.org", + http: true, + https: true, + software: "foolfuuka" + } }, "Install Gentoo": { - domain: "archive.installgentoo.net", - http: false, - https: true, - software: "fuuka", boards: ["diy", "g", "sci"], - files: [] + files: [], + data: { + domain: "archive.installgentoo.net", + http: false, + https: true, + software: "fuuka" + } }, "Rebecca Black Tech": { - domain: "rbt.asia", - http: true, - https: true, - software: "fuuka", boards: ["cgl", "g", "mu", "w"], - files: ["cgl", "g", "mu", "w"] + files: ["cgl", "g", "mu", "w"], + data: { + domain: "rbt.asia", + http: true, + https: true, + software: "fuuka" + } }, "Heinessen": { - domain: "archive.heinessen.com", - http: true, - software: "fuuka", boards: ["an", "fit", "k", "mlp", "r9k", "toy"], - files: ["an", "fit", "k", "r9k", "toy"] + files: ["an", "fit", "k", "r9k", "toy"], + data: { + domain: "archive.heinessen.com", + http: true, + software: "fuuka" + } }, "warosu": { - domain: "fuuka.warosu.org", - http: true, - https: true, - software: "fuuka", boards: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"], - files: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"] + files: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"], + data: { + domain: "fuuka.warosu.org", + http: true, + https: true, + software: "fuuka" + } }, "Foolz Beta": { - domain: "beta.foolz.us", - http: true, - https: true, - withCredentials: true, - software: "foolfuuka", - boards: ["a", "co", "gd", "jp", "m", "s4s", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"], - files: ["a", "gd", "jp", "m", "s4s", "tg", "u", "vg", "vp", "vr", "wsg"] + boards: ["a", "co", "d", "gd", "h", "jp", "m", "mlp", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"], + files: ["a", "d", "gd", "h", "jp", "m", "tg", "u", "vg", "vp", "vr", "wsg"], + data: { + domain: "beta.foolz.us", + http: true, + https: true, + withCredentials: true, + software: "foolfuuka" + } } }, to: function(dest, data) { @@ -9094,11 +10149,10 @@ if (!Conf['Catalog Links']) { return; } - el = $.el('label', { + CatalogLinks.el = el = $.el('label', { id: 'toggleCatalog', href: 'javascript:;', - innerHTML: " Catalog Links", - title: "Turn catalog links " + (Conf['Header catalog links'] ? 'off' : 'on') + "." + innerHTML: " Catalog Links" }); input = $('input', el); $.on(input, 'change', this.toggle); @@ -9113,31 +10167,87 @@ }); }, toggle: function() { - var useCatalog; $.event('CloseMenu'); - $.set('Header catalog links', useCatalog = this.checked); - return CatalogLinks.set(useCatalog); + $.set('Header catalog links', this.checked); + return CatalogLinks.set(this.checked); }, set: function(useCatalog) { - var a, board, path, _i, _len, _ref; + var a, board, generateURL, path, _i, _len, _ref, _ref1; path = useCatalog ? 'catalog' : ''; - _ref = $$("#board-list a:not(.catalog),\n#boardNavDesktopFoot a"); + 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]; - board = a.pathname.split('/')[1]; - if (['f', 'status', '4chan'].contains(board) || !board) { + 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; } - if (Conf['External Catalog']) { - a.href = useCatalog ? CatalogLinks.external(board) : "/" + board + "/"; - } else { - a.pathname = "/" + board + "/" + path; - } + a.href = generateURL(board); } - return this.title = "Turn catalog links " + (useCatalog ? 'off' : 'on') + "."; + return CatalogLinks.el.title = "Turn catalog links " + (useCatalog ? 'off' : 'on') + "."; }, external: function(board) { - return (['a', 'c', 'g', 'co', 'k', 'm', 'o', 'p', 'v', 'vg', 'w', 'cm', '3', 'adv', 'an', 'cgl', 'ck', 'diy', 'fa', 'fit', 'int', 'jp', 'mlp', 'lit', 'mu', 'n', 'po', 'sci', 'toy', 'trv', 'tv', 'vp', 'x', 'q'].contains(board) ? "http://catalog.neet.tv/" + board : ['d', 'e', 'gif', 'h', 'hr', 'hc', 'r9k', 's', 'pol', 'soc', 'u', 'i', 'ic', 'hm', 'r', 'w', 'wg', 'wsg', 't', 'y'].contains(board) ? "http://4index.gropes.us/" + board : "/" + board + "/catalog"); + 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"; + } } }; @@ -9295,7 +10405,7 @@ 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 (![200, 304].contains(status)) { + if (status !== 200 && status !== 304) { a.textContent = "Error " + req.statusText + " (" + status + ")"; return; } @@ -9343,146 +10453,140 @@ if (g.VIEW !== 'index' || !Conf['Thread Expansion']) { return; } - return Thread.callbacks.push({ - name: 'Thread Expansion', - cb: this.node - }); + this.statuses = {}; + return $.on(d, 'IndexRefresh', this.onIndexRefresh); }, - node: function() { - var a, files, posts, span, _ref; - if (!(span = $.x('following-sibling::span[contains(@class,"summary")][1]', this.OP.nodes.root))) { + setButton: function(thread) { + var a; + if (!(a = $.x('following-sibling::a[contains(@class,"summary")][1]', thread.OP.nodes.root))) { return; } - _ref = span.textContent.match(/\d+/g), posts = _ref[0], files = _ref[1]; - a = $.el('a', { - textContent: ExpandThread.text('+', posts, files), - className: 'summary', - href: 'javascript:;' - }); - $.on(a, 'click', ExpandThread.cbToggle); - return $.replace(span, a); + a.textContent = ExpandThread.text.apply(ExpandThread, ['+'].concat(__slice.call(a.textContent.match(/\d+/g)))); + return $.on(a, 'click', ExpandThread.cbToggle); + }, + onIndexRefresh: function() { + var status, thread, threadID, _ref, _ref1, _ref2; + _ref = ExpandThread.statuses; + for (threadID in _ref) { + status = _ref[threadID]; + if ((_ref1 = status.req) != null) { + _ref1.abort(); + } + delete ExpandThread.statuses[threadID]; + } + _ref2 = g.BOARD.threads; + for (threadID in _ref2) { + thread = _ref2[threadID]; + 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() { + 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, files, filesCount, inlined, num, post, posts, postsCount, reply, threadRoot, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3, _ref4; + var a, threadRoot; threadRoot = thread.OP.nodes.root.parentNode; - a = $('.summary', threadRoot); - switch (thread.isExpanded) { - case false: - case void 0: - _ref = $$('.thread > .postContainer', threadRoot); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - post = _ref[_i]; - ExpandComment.expand(Get.postFromRoot(post)); - } - if (!a) { - thread.isExpanded = true; - return; - } - thread.isExpanded = 'loading'; - _ref1 = a.textContent.match(/\d+/g), posts = _ref1[0], files = _ref1[1]; - a.textContent = ExpandThread.text('...', posts, files); - $.cache("//a.4cdn.org/" + thread.board + "/res/" + thread + ".json", function() { - return ExpandThread.parse(this, thread, a); - }); - break; - case 'loading': - thread.isExpanded = false; - if (!a) { - return; - } - _ref2 = a.textContent.match(/\d+/g), posts = _ref2[0], files = _ref2[1]; - a.textContent = ExpandThread.text('+', posts, files); - break; - case true: - thread.isExpanded = false; - 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; - } - } - })(); - posts = $$(".thread > .replyContainer", threadRoot); - _ref3 = [thread.OP.nodes.root].concat(posts.slice(-num)); - for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) { - post = _ref3[_j]; - ExpandComment.contract(Get.postFromRoot(post)); - } - if (!a) { - return; - } - postsCount = 0; - filesCount = 0; - _ref4 = posts.slice(0, -num); - for (_k = 0, _len2 = _ref4.length; _k < _len2; _k++) { - reply = _ref4[_k]; - if (Conf['Quote Inlining']) { - while (inlined = $('.inlined', reply)) { - inlined.click(); - } - } - postsCount++; - if ('file' in Get.postFromRoot(reply)) { - filesCount++; - } - $.rm(reply); - } - a.textContent = ExpandThread.text('+', postsCount, filesCount); + 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, link, post, posts, postsCount, postsObj, postsRoot, reply, root, spoilerRange, _i, _len; - if (a.textContent[0] === '+') { - return; - } - if (![200, 304].contains(req.status)) { + var data, filesCount, post, postData, posts, postsCount, postsRoot, root, _i, _len, _ref; + if ((_ref = req.status) !== 200 && _ref !== 304) { a.textContent = "Error " + req.statusText + " (" + req.status + ")"; - $.off(a, 'click', ExpandThread.cbToggle); return; } - thread.isExpanded = true; - posts = JSON.parse(req.response).posts; - if (spoilerRange = posts.shift().custom_spoiler) { - Build.spoilerRange[thread.board] = spoilerRange; - } - postsObj = []; + data = JSON.parse(req.response).posts; + Build.spoilerRange[thread.board] = data.shift().custom_spoiler; + posts = []; postsRoot = []; filesCount = 0; - for (_i = 0, _len = posts.length; _i < _len; _i++) { - reply = posts[_i]; - if (post = thread.posts[reply.no]) { + for (_i = 0, _len = data.length; _i < _len; _i++) { + postData = data[_i]; + if (post = thread.posts[postData.no]) { if ('file' in post) { filesCount++; } postsRoot.push(post.nodes.root); continue; } - root = Build.postFromObject(reply, thread.board.ID); + root = Build.postFromObject(postData, thread.board.ID); post = new Post(root, thread, thread.board); - link = $('a[title="Highlight this post"]', root); - link.href = "res/" + thread + "#p" + post; - link.nextSibling.href = "res/" + thread + "#q" + post; if ('file' in post) { filesCount++; } - postsObj.push(post); + posts.push(post); postsRoot.push(root); } - Main.callbackNodes(Post, postsObj); + Main.callbackNodes(Post, posts); $.after(a, postsRoot); postsCount = postsRoot.length; a.textContent = ExpandThread.text('-', postsCount, filesCount); @@ -9505,7 +10609,7 @@ if (!this.file || this.isClone) { return; } - return this.file.text.innerHTML = FileInfo.funk(FileInfo, this); + return this.file.text.innerHTML = "" + (FileInfo.funk(FileInfo, this)) + ""; }, createFunc: function(format) { var code; @@ -9598,7 +10702,7 @@ } board = g.BOARD.ID; if (board === 'g') { - $.globalEval("window.addEventListener('prettyprint', function(e) {\n var pre = e.detail;\n pre.innerHTML = prettyPrintOne(pre.innerHTML);\n}, false);"); + $.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 @@ -9613,16 +10717,20 @@ } }, code: function() { - var pre, _i, _len, _ref; + 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, window); - $.addClass(pre, 'prettyprinted'); + $.event('prettyprint', pre.innerHTML, window); } + $.off(window, 'prettyprint:cb', apply); }, math: function() { if (this.isClone || !$('.math', this.nodes.comment)) { @@ -9862,10 +10970,13 @@ Keybinds = { init: function() { - var init; + 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); @@ -9878,14 +10989,17 @@ }; return $.on(d, '4chanXInitFinished', init); }, + sync: function(key, hotkey) { + return Conf[hotkey] = key; + }, keydown: function(e) { - var form, key, notification, notifications, op, target, thread, threadRoot, _i, _len; + var key, notification, notifications, op, target, thread, threadRoot, _i, _len, _ref; if (!(key = Keybinds.keyCode(e))) { return; } target = e.target; - if (['INPUT', 'TEXTAREA'].contains(target.nodeName)) { - if (!/(Esc|Alt|Ctrl|Meta)/.test(key)) { + if ((_ref = target.nodeName) === 'INPUT' || _ref === 'TEXTAREA') { + if (!/(Esc|Alt|Ctrl|Meta|Shift\+\w{2,})/.test(key)) { return; } } @@ -9961,12 +11075,18 @@ 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['Update']: - ThreadUpdater.update(); - break; case Conf['Expand image']: Keybinds.img(threadRoot); break; @@ -9977,32 +11097,39 @@ Gallery.cb.toggle(); break; case Conf['fappeTyme']: - FappeTyme.cb.fappe(); + FappeTyme.cb.toggle.call({ + name: 'fappe' + }); break; case Conf['werkTyme']: - FappeTyme.cb.werk(); + FappeTyme.cb.toggle.call({ + name: 'werk' + }); break; case Conf['Front page']: - window.location = "/" + g.BOARD + "/0#delform"; + if (g.VIEW === 'index') { + Index.userPageNav(0); + } else { + window.location = "/" + g.BOARD + "/"; + } break; case Conf['Open front page']: - $.open("/" + g.BOARD + "/#delform"); + $.open("/" + g.BOARD + "/"); break; case Conf['Next page']: - if (g.VIEW === 'thread') { + if (!(g.VIEW === 'index' && Conf['Index Mode'] === 'paged')) { return; } - if (form = $('.next form')) { - window.location = form.action; - } + $('.next button', Index.pagelist).click(); break; case Conf['Previous page']: - if (g.VIEW === 'thread') { + if (!(g.VIEW === 'index' && Conf['Index Mode'] === 'paged')) { return; } - if (form = $('.prev form')) { - window.location = form.action; - } + $('.prev button', Index.pagelist).click(); + break; + case Conf['Search form']: + Index.searchInput.focus(); break; case Conf['Open catalog']: if (Conf['External Catalog']) { @@ -10042,7 +11169,7 @@ Keybinds.hl(0, threadRoot); break; case Conf['Hide']: - if (g.VIEW === 'index') { + if (ThreadHiding.db) { ThreadHiding.toggle(thread); } break; @@ -10150,44 +11277,28 @@ } }, hl: function(delta, thread) { - var axe, headRect, next, postEl, rect, replies, reply, root, topMargin, _i, _len; + var axis, height, next, postEl, replies, reply, root, _i, _len; + postEl = $('.reply.highlight', thread); if (!delta) { - if (postEl = $('.reply.highlight', thread)) { + if (postEl) { $.rmClass(postEl, 'highlight'); } return; } - if (Conf['Fixed Header'] && Conf['Bottom header']) { - topMargin = 0; - } else { - headRect = Header.bar.getBoundingClientRect(); - topMargin = headRect.top + headRect.height; - } - if (postEl = $('.reply.highlight', thread)) { - $.rmClass(postEl, 'highlight'); - rect = postEl.getBoundingClientRect(); - if (rect.bottom >= topMargin && rect.top <= doc.clientHeight) { + if (postEl) { + height = postEl.getBoundingClientRect().height; + if (Header.getTopOf(postEl) >= -height && Header.getBottomOf(postEl) >= -height) { root = postEl.parentNode; - axe = delta === +1 ? 'following' : 'preceding'; - next = $.x("" + axe + "-sibling::div[contains(@class,'replyContainer')][1]/child::div[contains(@class,'reply')]", root); - if (!next) { - this.focus(postEl); + 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; } - if (!(g.VIEW === 'thread' || $.x('ancestor::div[parent::div[@class="board"]]', next) === thread)) { - return; - } - rect = next.getBoundingClientRect(); - if (rect.top < 0 || rect.bottom > doc.clientHeight) { - if (delta === -1) { - window.scrollBy(0, rect.top - topMargin); - } else { - next.scrollIntoView(false); - } - } + Header.scrollToIfNeeded(next, delta === +1); this.focus(next); + $.rmClass(postEl, 'highlight'); return; } + $.rmClass(postEl, 'highlight'); } replies = $$('.reply', thread); if (delta === -1) { @@ -10195,8 +11306,7 @@ } for (_i = 0, _len = replies.length; _i < _len; _i++) { reply = replies[_i]; - rect = reply.getBoundingClientRect(); - if (delta === +1 && rect.top >= topMargin || delta === -1 && rect.bottom <= doc.clientHeight) { + if (delta === +1 && Header.getTopOf(reply) > 0 || delta === -1 && Header.getBottomOf(reply) > 0) { this.focus(reply); return; } @@ -10258,50 +11368,58 @@ return Nav.scroll(+1); } }, - getThread: function(full) { - var headRect, i, rect, thread, threads, topMargin, _i, _len; - if (Conf['Bottom header'] || !Conf['Fixed Header']) { - topMargin = 0; - } else { - headRect = Header.bar.getBoundingClientRect(); - topMargin = headRect.top + headRect.height; - } - threads = $$('.thread').filter(function(thread) { - thread = Get.threadFromRoot(thread); - return !(thread.isHidden && !thread.stub); - }); - for (i = _i = 0, _len = threads.length; _i < _len; i = ++_i) { - thread = threads[i]; - rect = thread.getBoundingClientRect(); - if (rect.bottom > topMargin) { - if (full) { - return [threads, thread, i, rect, topMargin]; - } else { - return thread; - } + 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 i, rect, thread, threads, top, topMargin, _ref, _ref1; - _ref = Nav.getThread(true), threads = _ref[0], thread = _ref[1], i = _ref[2], rect = _ref[3], topMargin = _ref[4]; - top = rect.top - topMargin; - if ((delta === -1 && top > -5) || (delta === +1 && top < 5)) { - top = ((_ref1 = threads[i + delta]) != null ? _ref1.getBoundingClientRect().top : void 0) - topMargin; + 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 window.scrollBy(0, top); + return Header.scrollTo(thread); } }; RelativeDates = { INTERVAL: $.MINUTE / 2, init: function() { - if (g.VIEW === 'catalog' || !Conf['Relative Post Dates']) { - return; + 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; } - $.on(d, 'visibilitychange ThreadUpdate', this.flush); - this.flush(); return Post.callbacks.push({ name: 'Relative Post Dates', cb: this.node @@ -10314,7 +11432,7 @@ } dateEl = this.nodes.date; dateEl.title = dateEl.textContent; - return RelativeDates.setUpdate(this); + return RelativeDates.update(this); }, relative: function(diff, now, date) { var days, months, number, rounded, unit, years; @@ -10327,43 +11445,51 @@ }, stale: [], flush: function() { - var now, update, _i, _len, _ref; + 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++) { - update = _ref[_i]; - update(now); + data = _ref[_i]; + RelativeDates.update(data, now); } RelativeDates.stale = []; clearTimeout(RelativeDates.timeout); return RelativeDates.timeout = setTimeout(RelativeDates.flush, RelativeDates.INTERVAL); }, - setUpdate: function(post) { - var markStale, setOwnTimeout, update; - setOwnTimeout = function(diff) { - 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(markStale, delay); - }; - update = function(now) { - var date, diff, relative, singlePost, _i, _len, _ref; - date = post.info.date; - diff = now - date; - relative = RelativeDates.relative(diff, now, date); - _ref = [post].concat(post.clones); + 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; } - return setOwnTimeout(diff); - }; - markStale = function() { - return RelativeDates.stale.push(update); - }; - return update(new Date()); + } 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); } }; @@ -10531,7 +11657,7 @@ init: function() { var link, settings; link = $.el('a', { - className: 'settings-link fourchanx-icon icon-wrench', + className: 'settings-link fa fa-wrench', textContent: 'Settings', href: 'javascript:;' }); @@ -10578,7 +11704,7 @@ return; } $.event('CloseMenu'); - html = "
"; + html = "
"; Settings.overlay = overlay = $.el('div', { id: 'overlay' }); @@ -10680,7 +11806,7 @@ } }); div = $.el('div', { - innerHTML: ": Clear manually-hidden threads and posts on all boards. Refresh the page to apply." + innerHTML: ": Clear manually-hidden threads and posts on all boards. Reload the page to apply." }); button = $('button', div); hiddenNum = 0; @@ -10779,7 +11905,7 @@ try { data = JSON.parse(e.target.result); Settings.loadSettings(data); - if (confirm('Import successful. Refresh now?')) { + if (confirm('Import successful. Reload now?')) { return window.location.reload(); } } catch (_error) { @@ -10912,11 +12038,11 @@ $.add(div, ta); return; } - return div.innerHTML = "
Filter is disabled.

\nUse regular expressions, one per line.
\nLines starting with a # will be ignored.
\nFor example, /weeaboo/i will filter posts containing the string `weeaboo`, case-insensitive.
\nMD5 filtering uses exact string matching, not regular expressions.\n

    You can use these settings with each regular expression, separate them with semicolons:\n
  • \n Per boards, separate them with commas. It is global if not specified.
    \n For example: boards:a,jp;.\n
  • \n Filter OPs only along with their threads (`only`), replies only (`no`), or both (`yes`, this is default).
    \n For example: op:only;, op:no; or op:yes;.\n
  • \n Overrule the `Show Stubs` setting if specified: create a stub (`yes`) or not (`no`).
    \n For example: stub:yes; or stub:no;.\n
  • \n Highlight instead of hiding. You can specify a class name to use with a userstyle.
    \n For example: highlight; or highlight:wallpaper;.\n
  • \n Highlighted OPs will have their threads put on top of board pages by default.
    \n For example: top:yes; or top:no;.\n
"; + 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:\n
  • %TURL: Thumbnail URL.
  • %URL: Full image URL.
  • %MD5: MD5 hash.
  • %board: Current board.
"; + 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.
  • %board: Current board.
"; ta = $('textarea', section); $.get('sauces', Conf['sauces'], function(item) { return ta.value = item['sauces']; @@ -10925,7 +12051,7 @@ }, advanced: function(section) { var archive, boardID, boardOptions, boardSelect, boards, data, event, input, inputs, item, items, name, row, rows, ta, table, _i, _j, _k, _l, _len, _len1, _len2, _len3, _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\"]
\n will give you
[ + ] [Technology] [Technology / Anime & Manga / Otaku Culture] [x / wsg / h] [Piracy]
\n if you are on /g/.\n
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 is disabled.

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

    You can use these settings with each item, separate them with semicolons:\n
  • 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.
\n Sage Icon:
\n Position:
Thread Updater is disabled.
\n Interval:
"; + 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 is disabled.

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']; @@ -10934,7 +12060,7 @@ input = $("[name=" + name + "]", section); items[name] = Conf[name]; inputs[name] = input; - event = ['favicon', 'usercss', 'sageEmoji', 'emojiPos'].contains(name) ? 'change' : 'input'; + event = name === 'favicon' || name === 'usercss' || name === 'sageEmoji' || name === 'emojiPos' ? 'change' : 'input'; $.on(input, event, $.cb.value); } ta = $('.personafield', section); @@ -10946,7 +12072,7 @@ var key, val; for (key in items) { val = items[key]; - if (['emojiPos'].contains(key)) { + if (key === 'emojiPos') { continue; } input = inputs[key]; @@ -10977,7 +12103,7 @@ if (archive.software === 'foolfuuka') { data.post.push(name); } - if (archive.files.contains(boardID)) { + if (__indexOf.call(archive.files, boardID) >= 0) { data.file.push(name); } } @@ -11158,38 +12284,7 @@ Main = { init: function() { - var db, flatten, _i, _len, _ref; - 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); - _ref = DataBoard.keys; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - db = _ref[_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 init, pathname, _ref; + var db, flatten, pathname, _i, _len, _ref, _ref1; pathname = location.pathname.split('/'); g.BOARD = new Board(pathname[1]); if ((_ref = g.BOARD.ID) === 'z' || _ref === 'fk') { @@ -11208,6 +12303,37 @@ 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 init; switch (location.hostname) { case 'a.4cdn.org': return; @@ -11216,8 +12342,8 @@ return; case 'i.4cdn.org': $.ready(function() { - var URL; - if (Conf['404 Redirect'] && ['4chan - Temporarily Offline', '4chan - 404 Not Found'].contains(d.title)) { + 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', { @@ -11252,6 +12378,7 @@ 'Header': Header, 'Catalog Links': CatalogLinks, 'Settings': Settings, + 'Index Generator': Index, 'Announcement Hiding': PSAHiding, 'Fourchan thingies': Fourchan, 'Emoji': Emoji, @@ -11293,7 +12420,6 @@ 'Reveal Spoiler Thumbnails': RevealSpoilers, 'Image Loading': ImageLoader, 'Image Hover': ImageHover, - 'Comment Expansion': ExpandComment, 'Thread Expansion': ExpandThread, 'Thread Excerpt': ThreadExcerpt, 'Favicon': Favicon, @@ -11306,8 +12432,7 @@ 'Index Navigation': Nav, 'Keybinds': Keybinds, 'Show Dice Roll': Dice, - 'Banner': Banner, - 'Infinite Scrolling': InfiniScroll + 'Banner': Banner }); $.on(d, 'AddCallback', Main.addCallback); return $.ready(Main.initReady); @@ -11355,8 +12480,8 @@ }); }, initReady: function() { - var board, err, errors, href, passLink, postRoot, posts, styleSelector, thread, threadRoot, threads, _i, _j, _len, _len1, _ref, _ref1; - if (['4chan - Temporarily Offline', '4chan - 404 Not Found'].contains(d.title)) { + var err, errors, href, passLink, post, postRoot, posts, styleSelector, thread, threadRoot, _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, @@ -11368,35 +12493,31 @@ return; } Main.initStyle(); - if (board = $('.board')) { - threads = []; + if (g.VIEW === 'thread' && (threadRoot = $('.thread'))) { + thread = new Thread(+threadRoot.id.slice(1), g.BOARD); 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 - }); + _ref1 = $$('.thread > .postContainer', threadRoot); + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + postRoot = _ref1[_i]; + try { + posts.push(post = new Post(postRoot, thread, g.BOARD, { + isOriginalMarkup: true + })); + } 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.callbackNodes(Thread, [thread]); Main.callbackNodesDB(Post, posts, function() { return $.event('4chanXInitFinished'); }); @@ -11413,91 +12534,40 @@ return; } try { - localStorage.getItem('4chan-settings'); + return localStorage.getItem('4chan-settings'); } catch (_error) { err = _error; - new Notice('warning', 'Cookies need to be enabled on 4chan for 4chan X to properly function.', 30); - Main.disableReports = true; + return new Notice('warning', 'Cookies need to be enabled on 4chan for 4chan X to operate properly.', 30); } - return $.event('4chanXInitFinished'); }, callbackNodes: function(klass, nodes) { - var callback, err, errors, i, len, node, _i, _len, _ref; - len = nodes.length; - _ref = klass.callbacks; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - callback = _ref[_i]; - i = 0; - while (i < len) { - node = nodes[i++]; - try { - callback.cb.call(node); - } catch (_error) { - err = _error; - if (!errors) { - errors = []; - } - errors.push({ - message: "\"" + callback.name + "\" crashed on " + klass.name + " No." + node + " (/" + node.board + "/).", - error: err - }); - } - } - } - if (errors) { - return Main.handleErrors(errors); + var cb, i, node; + i = 0; + cb = klass.callbacks; + while (node = nodes[i++]) { + cb.execute(node); } }, callbackNodesDB: function(klass, nodes, cb) { - var errors, func, i, len, node, queue, softTask; - queue = []; + var callbacks, errors, i, len, softTask; errors = null; - func = function(node) { - var callback, err, _i, _len, _ref; - _ref = klass.callbacks; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - callback = _ref[_i]; - try { - callback.cb.call(node); - } catch (_error) { - err = _error; - if (!errors) { - errors = []; - } - errors.push({ - message: "\"" + callback.name + "\" crashed on " + klass.name + " No." + node + " (/" + node.board + "/).", - error: err - }); - } - } - if (!queue.length) { - if (errors) { - Main.handleErrors(errors); - } - if (cb) { - return cb(); - } - } - }; + len = 0; + i = 0; + callbacks = klass.callbacks; softTask = function() { var node; - node = queue.shift(); - func(node); - if (!queue.length) { - return; + node = nodes[i++]; + callbacks.execute(node); + if (len === i && cb) { + return cb(); } - if (!(queue.length % 7)) { + if (!(i % 7)) { return setTimeout(softTask, 0); } else { return softTask(); } }; len = nodes.length; - i = 0; - while (i < len) { - node = nodes[i++]; - queue.push(node); - } return softTask(); }, addCallback: function(e) { @@ -11548,7 +12618,7 @@ }, parseError: function(data) { var error, message; - Main.logError(data); + c.error(data.message, data.error.stack); message = $.el('div', { textContent: data.message }); @@ -11557,11 +12627,6 @@ }); return [message, error]; }, - errors: [], - logError: function(data) { - c.error(data.message, data.error.stack); - return Main.errors.push(data); - }, isThisPageLegit: function() { var _ref; if (!('thisPageIsLegit' in Main)) { @@ -11569,7 +12634,7 @@ } return Main.thisPageIsLegit; }, - css: "/*! * Font Awesome 3.2.1 * 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: normal; font-style: normal; } .fourchanx-icon::before { font-family: FontAwesome; font-weight: normal; font-style: normal; text-decoration: inherit; -webkit-font-smoothing: antialiased; *margin-right: .3em; text-decoration: inherit; display: none; speak: none; } :root.shortcut-icons .fourchanx-icon::before { display: inline-block; font-size: 13px; visibility: visible; } :root.shortcut-icons #shortcuts .fourchanx-icon::before { font-size: 15px !important; margin-top: -3px !important; position: relative; top: 1px; } :root.shortcut-icons .fourchanx-icon { font-size: 0; visibility: hidden; } :root.shortcut-icons .shortcut.brackets-wrap::before, :root.shortcut-icons .shortcut.brackets-wrap::after { display: none; } /* makes sure icons active on rollover in links */ :root.shortcut-icons a .fourchanx-icon { display: inline; } /* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen readers do not read off random characters that represent icons */ .icon-glass::before { content: \"\\f000\"; } .icon-music::before { content: \"\\f001\"; } .icon-search::before { content: \"\\f002\"; } .icon-envelope-alt::before { content: \"\\f003\"; } .icon-heart::before { content: \"\\f004\"; } .icon-star::before { content: \"\\f005\"; } .icon-star-empty::before { content: \"\\f006\"; } .icon-user::before { content: \"\\f007\"; } .icon-film::before { content: \"\\f008\"; } .icon-th-large::before { content: \"\\f009\"; } .icon-th::before { content: \"\\f00a\"; } .icon-th-list::before { content: \"\\f00b\"; } .icon-ok::before { content: \"\\f00c\"; } .icon-remove::before { content: \"\\f00d\"; } .icon-zoom-in::before { content: \"\\f00e\"; } .icon-zoom-out::before { content: \"\\f010\"; } .icon-power-off:before, .icon-off::before { content: \"\\f011\"; } .icon-signal::before { content: \"\\f012\"; } .icon-gear:before, .icon-cog::before { content: \"\\f013\"; } .icon-trash::before { content: \"\\f014\"; } .icon-home::before { content: \"\\f015\"; } .icon-file-alt::before { content: \"\\f016\"; } .icon-time::before { content: \"\\f017\"; } .icon-road::before { content: \"\\f018\"; } .icon-download-alt::before { content: \"\\f019\"; } .icon-download::before { content: \"\\f01a\"; } .icon-upload::before { content: \"\\f01b\"; } .icon-inbox::before { content: \"\\f01c\"; } .icon-play-circle::before { content: \"\\f01d\"; } .icon-rotate-right:before, .icon-repeat::before { content: \"\\f01e\"; } .icon-refresh::before { content: \"\\f021\"; } .icon-list-alt::before { content: \"\\f022\"; } .icon-lock::before { content: \"\\f023\"; } .icon-flag::before { content: \"\\f024\"; } .icon-headphones::before { content: \"\\f025\"; } .icon-volume-off::before { content: \"\\f026\"; } .icon-volume-down::before { content: \"\\f027\"; } .icon-volume-up::before { content: \"\\f028\"; } .icon-qrcode::before { content: \"\\f029\"; } .icon-barcode::before { content: \"\\f02a\"; } .icon-tag::before { content: \"\\f02b\"; } .icon-tags::before { content: \"\\f02c\"; } .icon-book::before { content: \"\\f02d\"; } .icon-bookmark::before { content: \"\\f02e\"; } .icon-print::before { content: \"\\f02f\"; } .icon-camera::before { content: \"\\f030\"; } .icon-font::before { content: \"\\f031\"; } .icon-bold::before { content: \"\\f032\"; } .icon-italic::before { content: \"\\f033\"; } .icon-text-height::before { content: \"\\f034\"; } .icon-text-width::before { content: \"\\f035\"; } .icon-align-left::before { content: \"\\f036\"; } .icon-align-center::before { content: \"\\f037\"; } .icon-align-right::before { content: \"\\f038\"; } .icon-align-justify::before { content: \"\\f039\"; } .icon-list::before { content: \"\\f03a\"; } .icon-indent-left::before { content: \"\\f03b\"; } .icon-indent-right::before { content: \"\\f03c\"; } .icon-facetime-video::before { content: \"\\f03d\"; } .icon-picture::before { content: \"\\f03e\"; } .icon-pencil::before { content: \"\\f040\"; } .icon-map-marker::before { content: \"\\f041\"; } .icon-adjust::before { content: \"\\f042\"; } .icon-tint::before { content: \"\\f043\"; } .icon-edit::before { content: \"\\f044\"; } .icon-share::before { content: \"\\f045\"; } .icon-check::before { content: \"\\f046\"; } .icon-move::before { content: \"\\f047\"; } .icon-step-backward::before { content: \"\\f048\"; } .icon-fast-backward::before { content: \"\\f049\"; } .icon-backward::before { content: \"\\f04a\"; } .icon-play::before { content: \"\\f04b\"; } .icon-pause::before { content: \"\\f04c\"; } .icon-stop::before { content: \"\\f04d\"; } .icon-forward::before { content: \"\\f04e\"; } .icon-fast-forward::before { content: \"\\f050\"; } .icon-step-forward::before { content: \"\\f051\"; } .icon-eject::before { content: \"\\f052\"; } .icon-chevron-left::before { content: \"\\f053\"; } .icon-chevron-right::before { content: \"\\f054\"; } .icon-plus-sign::before { content: \"\\f055\"; } .icon-minus-sign::before { content: \"\\f056\"; } .icon-remove-sign::before { content: \"\\f057\"; } .icon-ok-sign::before { content: \"\\f058\"; } .icon-question-sign::before { content: \"\\f059\"; } .icon-info-sign::before { content: \"\\f05a\"; } .icon-screenshot::before { content: \"\\f05b\"; } .icon-remove-circle::before { content: \"\\f05c\"; } .icon-ok-circle::before { content: \"\\f05d\"; } .icon-ban-circle::before { content: \"\\f05e\"; } .icon-arrow-left::before { content: \"\\f060\"; } .icon-arrow-right::before { content: \"\\f061\"; } .icon-arrow-up::before { content: \"\\f062\"; } .icon-arrow-down::before { content: \"\\f063\"; } .icon-mail-forward:before, .icon-share-alt::before { content: \"\\f064\"; } .icon-resize-full::before { content: \"\\f065\"; } .icon-resize-small::before { content: \"\\f066\"; } .icon-plus::before { content: \"\\f067\"; } .icon-minus::before { content: \"\\f068\"; } .icon-asterisk::before { content: \"\\f069\"; } .icon-exclamation-sign::before { content: \"\\f06a\"; } .icon-gift::before { content: \"\\f06b\"; } .icon-leaf::before { content: \"\\f06c\"; } .icon-fire::before { content: \"\\f06d\"; } .icon-eye-open::before { content: \"\\f06e\"; } .icon-eye-close::before { content: \"\\f070\"; } .icon-warning-sign::before { content: \"\\f071\"; } .icon-plane::before { content: \"\\f072\"; } .icon-calendar::before { content: \"\\f073\"; } .icon-random::before { content: \"\\f074\"; } .icon-comment::before { content: \"\\f075\"; } .icon-magnet::before { content: \"\\f076\"; } .icon-chevron-up::before { content: \"\\f077\"; } .icon-chevron-down::before { content: \"\\f078\"; } .icon-retweet::before { content: \"\\f079\"; } .icon-shopping-cart::before { content: \"\\f07a\"; } .icon-folder-close::before { content: \"\\f07b\"; } .icon-folder-open::before { content: \"\\f07c\"; } .icon-resize-vertical::before { content: \"\\f07d\"; } .icon-resize-horizontal::before { content: \"\\f07e\"; } .icon-bar-chart::before { content: \"\\f080\"; } .icon-twitter-sign::before { content: \"\\f081\"; } .icon-facebook-sign::before { content: \"\\f082\"; } .icon-camera-retro::before { content: \"\\f083\"; } .icon-key::before { content: \"\\f084\"; } .icon-gears:before, .icon-cogs::before { content: \"\\f085\"; } .icon-comments::before { content: \"\\f086\"; } .icon-thumbs-up-alt::before { content: \"\\f087\"; } .icon-thumbs-down-alt::before { content: \"\\f088\"; } .icon-star-half::before { content: \"\\f089\"; } .icon-heart-empty::before { content: \"\\f08a\"; } .icon-signout::before { content: \"\\f08b\"; } .icon-linkedin-sign::before { content: \"\\f08c\"; } .icon-pushpin::before { content: \"\\f08d\"; } .icon-external-link::before { content: \"\\f08e\"; } .icon-signin::before { content: \"\\f090\"; } .icon-trophy::before { content: \"\\f091\"; } .icon-github-sign::before { content: \"\\f092\"; } .icon-upload-alt::before { content: \"\\f093\"; } .icon-lemon::before { content: \"\\f094\"; } .icon-phone::before { content: \"\\f095\"; } .icon-unchecked:before, .icon-check-empty::before { content: \"\\f096\"; } .icon-bookmark-empty::before { content: \"\\f097\"; } .icon-phone-sign::before { content: \"\\f098\"; } .icon-twitter::before { content: \"\\f099\"; } .icon-facebook::before { content: \"\\f09a\"; } .icon-github::before { content: \"\\f09b\"; } .icon-unlock::before { content: \"\\f09c\"; } .icon-credit-card::before { content: \"\\f09d\"; } .icon-rss::before { content: \"\\f09e\"; } .icon-hdd::before { content: \"\\f0a0\"; } .icon-bullhorn::before { content: \"\\f0a1\"; } .icon-bell::before { content: \"\\f0a2\"; } .icon-certificate::before { content: \"\\f0a3\"; } .icon-hand-right::before { content: \"\\f0a4\"; } .icon-hand-left::before { content: \"\\f0a5\"; } .icon-hand-up::before { content: \"\\f0a6\"; } .icon-hand-down::before { content: \"\\f0a7\"; } .icon-circle-arrow-left::before { content: \"\\f0a8\"; } .icon-circle-arrow-right::before { content: \"\\f0a9\"; } .icon-circle-arrow-up::before { content: \"\\f0aa\"; } .icon-circle-arrow-down::before { content: \"\\f0ab\"; } .icon-globe::before { content: \"\\f0ac\"; } .icon-wrench::before { content: \"\\f0ad\"; } .icon-tasks::before { content: \"\\f0ae\"; } .icon-filter::before { content: \"\\f0b0\"; } .icon-briefcase::before { content: \"\\f0b1\"; } .icon-fullscreen::before { content: \"\\f0b2\"; } .icon-group::before { content: \"\\f0c0\"; } .icon-link::before { content: \"\\f0c1\"; } .icon-cloud::before { content: \"\\f0c2\"; } .icon-beaker::before { content: \"\\f0c3\"; } .icon-cut::before { content: \"\\f0c4\"; } .icon-copy::before { content: \"\\f0c5\"; } .icon-paperclip:before, .icon-paper-clip::before { content: \"\\f0c6\"; } .icon-save::before { content: \"\\f0c7\"; } .icon-sign-blank::before { content: \"\\f0c8\"; } .icon-reorder::before { content: \"\\f0c9\"; } .icon-list-ul::before { content: \"\\f0ca\"; } .icon-list-ol::before { content: \"\\f0cb\"; } .icon-strikethrough::before { content: \"\\f0cc\"; } .icon-underline::before { content: \"\\f0cd\"; } .icon-table::before { content: \"\\f0ce\"; } .icon-magic::before { content: \"\\f0d0\"; } .icon-truck::before { content: \"\\f0d1\"; } .icon-pinterest::before { content: \"\\f0d2\"; } .icon-pinterest-sign::before { content: \"\\f0d3\"; } .icon-google-plus-sign::before { content: \"\\f0d4\"; } .icon-google-plus::before { content: \"\\f0d5\"; } .icon-money::before { content: \"\\f0d6\"; } .icon-caret-down::before { content: \"\\f0d7\"; } .icon-caret-up::before { content: \"\\f0d8\"; } .icon-caret-left::before { content: \"\\f0d9\"; } .icon-caret-right::before { content: \"\\f0da\"; } .icon-columns::before { content: \"\\f0db\"; } .icon-sort::before { content: \"\\f0dc\"; } .icon-sort-down::before { content: \"\\f0dd\"; } .icon-sort-up::before { content: \"\\f0de\"; } .icon-envelope::before { content: \"\\f0e0\"; } .icon-linkedin::before { content: \"\\f0e1\"; } .icon-rotate-left:before, .icon-undo::before { content: \"\\f0e2\"; } .icon-legal::before { content: \"\\f0e3\"; } .icon-dashboard::before { content: \"\\f0e4\"; } .icon-comment-alt::before { content: \"\\f0e5\"; } .icon-comments-alt::before { content: \"\\f0e6\"; } .icon-bolt::before { content: \"\\f0e7\"; } .icon-sitemap::before { content: \"\\f0e8\"; } .icon-umbrella::before { content: \"\\f0e9\"; } .icon-paste::before { content: \"\\f0ea\"; } .icon-lightbulb::before { content: \"\\f0eb\"; } .icon-exchange::before { content: \"\\f0ec\"; } .icon-cloud-download::before { content: \"\\f0ed\"; } .icon-cloud-upload::before { content: \"\\f0ee\"; } .icon-user-md::before { content: \"\\f0f0\"; } .icon-stethoscope::before { content: \"\\f0f1\"; } .icon-suitcase::before { content: \"\\f0f2\"; } .icon-bell-alt::before { content: \"\\f0f3\"; } .icon-coffee::before { content: \"\\f0f4\"; } .icon-food::before { content: \"\\f0f5\"; } .icon-file-text-alt::before { content: \"\\f0f6\"; } .icon-building::before { content: \"\\f0f7\"; } .icon-hospital::before { content: \"\\f0f8\"; } .icon-ambulance::before { content: \"\\f0f9\"; } .icon-medkit::before { content: \"\\f0fa\"; } .icon-fighter-jet::before { content: \"\\f0fb\"; } .icon-beer::before { content: \"\\f0fc\"; } .icon-h-sign::before { content: \"\\f0fd\"; } .icon-plus-sign-alt::before { content: \"\\f0fe\"; } .icon-double-angle-left::before { content: \"\\f100\"; } .icon-double-angle-right::before { content: \"\\f101\"; } .icon-double-angle-up::before { content: \"\\f102\"; } .icon-double-angle-down::before { content: \"\\f103\"; } .icon-angle-left::before { content: \"\\f104\"; } .icon-angle-right::before { content: \"\\f105\"; } .icon-angle-up::before { content: \"\\f106\"; } .icon-angle-down::before { content: \"\\f107\"; } .icon-desktop::before { content: \"\\f108\"; } .icon-laptop::before { content: \"\\f109\"; } .icon-tablet::before { content: \"\\f10a\"; } .icon-mobile-phone::before { content: \"\\f10b\"; } .icon-circle-blank::before { content: \"\\f10c\"; } .icon-quote-left::before { content: \"\\f10d\"; } .icon-quote-right::before { content: \"\\f10e\"; } .icon-spinner::before { content: \"\\f110\"; } .icon-circle::before { content: \"\\f111\"; } .icon-mail-reply:before, .icon-reply::before { content: \"\\f112\"; } .icon-github-alt::before { content: \"\\f113\"; } .icon-folder-close-alt::before { content: \"\\f114\"; } .icon-folder-open-alt::before { content: \"\\f115\"; } .icon-expand-alt::before { content: \"\\f116\"; } .icon-collapse-alt::before { content: \"\\f117\"; } .icon-smile::before { content: \"\\f118\"; } .icon-frown::before { content: \"\\f119\"; } .icon-meh::before { content: \"\\f11a\"; } .icon-gamepad::before { content: \"\\f11b\"; } .icon-keyboard::before { content: \"\\f11c\"; } .icon-flag-alt::before { content: \"\\f11d\"; } .icon-flag-checkered::before { content: \"\\f11e\"; } .icon-terminal::before { content: \"\\f120\"; } .icon-code::before { content: \"\\f121\"; } .icon-reply-all::before { content: \"\\f122\"; } .icon-mail-reply-all::before { content: \"\\f122\"; } .icon-star-half-full:before, .icon-star-half-empty::before { content: \"\\f123\"; } .icon-location-arrow::before { content: \"\\f124\"; } .icon-crop::before { content: \"\\f125\"; } .icon-code-fork::before { content: \"\\f126\"; } .icon-unlink::before { content: \"\\f127\"; } .icon-question::before { content: \"\\f128\"; } .icon-info::before { content: \"\\f129\"; } .icon-exclamation::before { content: \"\\f12a\"; } .icon-superscript::before { content: \"\\f12b\"; } .icon-subscript::before { content: \"\\f12c\"; } .icon-eraser::before { content: \"\\f12d\"; } .icon-puzzle-piece::before { content: \"\\f12e\"; } .icon-microphone::before { content: \"\\f130\"; } .icon-microphone-off::before { content: \"\\f131\"; } .icon-shield::before { content: \"\\f132\"; } .icon-calendar-empty::before { content: \"\\f133\"; } .icon-fire-extinguisher::before { content: \"\\f134\"; } .icon-rocket::before { content: \"\\f135\"; } .icon-maxcdn::before { content: \"\\f136\"; } .icon-chevron-sign-left::before { content: \"\\f137\"; } .icon-chevron-sign-right::before { content: \"\\f138\"; } .icon-chevron-sign-up::before { content: \"\\f139\"; } .icon-chevron-sign-down::before { content: \"\\f13a\"; } .icon-html5::before { content: \"\\f13b\"; } .icon-css3::before { content: \"\\f13c\"; } .icon-anchor::before { content: \"\\f13d\"; } .icon-unlock-alt::before { content: \"\\f13e\"; } .icon-bullseye::before { content: \"\\f140\"; } .icon-ellipsis-horizontal::before { content: \"\\f141\"; } .icon-ellipsis-vertical::before { content: \"\\f142\"; } .icon-rss-sign::before { content: \"\\f143\"; } .icon-play-sign::before { content: \"\\f144\"; } .icon-ticket::before { content: \"\\f145\"; } .icon-minus-sign-alt::before { content: \"\\f146\"; } .icon-check-minus::before { content: \"\\f147\"; } .icon-level-up::before { content: \"\\f148\"; } .icon-level-down::before { content: \"\\f149\"; } .icon-check-sign::before { content: \"\\f14a\"; } .icon-edit-sign::before { content: \"\\f14b\"; } .icon-external-link-sign::before { content: \"\\f14c\"; } .icon-share-sign::before { content: \"\\f14d\"; } .icon-compass::before { content: \"\\f14e\"; } .icon-collapse::before { content: \"\\f150\"; } .icon-collapse-top::before { content: \"\\f151\"; } .icon-expand::before { content: \"\\f152\"; } .icon-euro:before, .icon-eur::before { content: \"\\f153\"; } .icon-gbp::before { content: \"\\f154\"; } .icon-dollar:before, .icon-usd::before { content: \"\\f155\"; } .icon-rupee:before, .icon-inr::before { content: \"\\f156\"; } .icon-yen:before, .icon-jpy::before { content: \"\\f157\"; } .icon-renminbi:before, .icon-cny::before { content: \"\\f158\"; } .icon-won:before, .icon-krw::before { content: \"\\f159\"; } .icon-bitcoin:before, .icon-btc::before { content: \"\\f15a\"; } .icon-file::before { content: \"\\f15b\"; } .icon-file-text::before { content: \"\\f15c\"; } .icon-sort-by-alphabet::before { content: \"\\f15d\"; } .icon-sort-by-alphabet-alt::before { content: \"\\f15e\"; } .icon-sort-by-attributes::before { content: \"\\f160\"; } .icon-sort-by-attributes-alt::before { content: \"\\f161\"; } .icon-sort-by-order::before { content: \"\\f162\"; } .icon-sort-by-order-alt::before { content: \"\\f163\"; } .icon-thumbs-up::before { content: \"\\f164\"; } .icon-thumbs-down::before { content: \"\\f165\"; } .icon-youtube-sign::before { content: \"\\f166\"; } .icon-youtube::before { content: \"\\f167\"; } .icon-xing::before { content: \"\\f168\"; } .icon-xing-sign::before { content: \"\\f169\"; } .icon-youtube-play::before { content: \"\\f16a\"; } .icon-dropbox::before { content: \"\\f16b\"; } .icon-stackexchange::before { content: \"\\f16c\"; } .icon-instagram::before { content: \"\\f16d\"; } .icon-flickr::before { content: \"\\f16e\"; } .icon-adn::before { content: \"\\f170\"; } .icon-bitbucket::before { content: \"\\f171\"; } .icon-bitbucket-sign::before { content: \"\\f172\"; } .icon-tumblr::before { content: \"\\f173\"; } .icon-tumblr-sign::before { content: \"\\f174\"; } .icon-long-arrow-down::before { content: \"\\f175\"; } .icon-long-arrow-up::before { content: \"\\f176\"; } .icon-long-arrow-left::before { content: \"\\f177\"; } .icon-long-arrow-right::before { content: \"\\f178\"; } .icon-apple::before { content: \"\\f179\"; } .icon-windows::before { content: \"\\f17a\"; } .icon-android::before { content: \"\\f17b\"; } .icon-linux::before { content: \"\\f17c\"; } .icon-dribbble::before { content: \"\\f17d\"; } .icon-skype::before { content: \"\\f17e\"; } .icon-foursquare::before { content: \"\\f180\"; } .icon-trello::before { content: \"\\f181\"; } .icon-female::before { content: \"\\f182\"; } .icon-male::before { content: \"\\f183\"; } .icon-gittip::before { content: \"\\f184\"; } .icon-sun::before { content: \"\\f185\"; } .icon-moon::before { content: \"\\f186\"; } .icon-archive::before { content: \"\\f187\"; } .icon-bug::before { content: \"\\f188\"; } .icon-vk::before { content: \"\\f189\"; } .icon-weibo::before { content: \"\\f18a\"; } .icon-renren::before { content: \"\\f18b\"; }\n/* General */ .dialog { box-shadow: 0 1px 2px rgba(0, 0, 0, .15); border: 1px solid; display: block; padding: 0; } .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; } .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; } /* 4chan style fixes */ .opContainer, .op { display: block !important; overflow: visible !important; } .reply > .file > .fileText { margin: 0 20px; } [hidden] { display: none !important; } div.center:not(.ad-cnt) { display: none !important; } /* 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 body { padding-top: 2em; } .fixed.bottom body { padding-bottom: 2em; } .fixed #header-bar { right: 0; left: 0; padding: 3px 4px 4px; } .fixed.top #header-bar { top: 0; } .fixed.bottom #header-bar { bottom: 0; } #header-bar { border-width: 0; transition: all .1s .05s ease-in-out; } :root.centered-links #shortcuts { width: 300px; text-align: right; } :root.centered-links #header-bar { text-align: center; } :root.centered-links #custom-board-list { position: relative; left: 150px; } .fixed.top #header-bar { border-bottom-width: 1px; } .fixed.bottom #header-bar { box-shadow: 0 -1px 2px rgba(0, 0, 0, .15); border-top-width: 1px; } .fixed.bottom #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-bar.autohide:not(:hover) { margin-bottom: -1em; -webkit-transform: translateY(-100%); transform: translateY(-100%); } .fixed.bottom #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-bar #scroll-marker { top: 100%; } .fixed.bottom #header-bar #scroll-marker { bottom: 100%; } #header-bar a:not(.entry):not(.close) { text-decoration: none; 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::after { content: \"]\"; } .brackets-wrap::before { 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-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: 6px; top: 0; right: 5px; 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); 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: 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; } /* Announcement Hiding */ :root.hide-announcement #globalMessage { display: none; } a.hide-announcement { float: left; } /* 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; } .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; } .fappeTyme .thread > .noFile, .fappeTyme .threadContainer > .noFile { 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: 2px; } .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, .postingMode ~ #qr select[data-name=thread], #file-n-submit:not(.has-file) #qr-filerm { display: none; } #qr select, #dump-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; } .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; } .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; } /* 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: 2px; 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 1px 1px #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; font-weight: 700; padding: 3px; } 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 { 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; } #menu { position: fixed; outline: none; } .entry { border-bottom: 1px solid rgba(0,0,0,.25); cursor: pointer; display: block; outline: none; padding: 3px 7px; position: relative; text-decoration: none; white-space: nowrap; min-width: 70px; } .left>.entry.has-submenu { padding-right: 17px !important; } .entry:last-child { border-bottom: 0; } .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; } .focused .submenu { display: block; } .imp-exp-result { position: absolute; text-align: center; margin: auto; right: 0px; left: 0px; width: 200px; } .export, .import { cursor: pointer; text-decoration: none !important; } /* Custom Board Titles */ .boardTitle[contenteditable=\"true\"], .boardSubtitle[contenteditable=\"true\"] { cursor: text !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,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAASJJREFUOE9jYAAC7ln7/pODQXrBmq333PvPu/YaSRikB6QXbACpmmHqsRoAMll7+20UQ0H8tmuv/pdffPFfZtNNuByGASBFIPDh5x+4IV6HHoDFYGDJgw+YBoBMBUkgA5BtIKduuvvy//svX+FSB+88wTTAc+/t/83bj/0HScLA5BPXwc7lKJ36f+L6XXDxhUfOYxrAPWUnWKFp9UQUm3iWQxSDXAEDSX3zcIcB96wD/x+8eA1XDNKMHAYg20GW4Y0FkCIYAAUqzEBQOIBciRzlWKMxZelOlMCEcVxq+jHSC1YDJPs3YBgA8jey0/F6ARRwsFAHORukmat9NdbUijMpg/wKcrJodDFOzSBXwA3Alh9AToZFI7a8Asu98BxJbnYGAJb5vYLDANzSAAAAAElFTkSuQmCC') 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/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAydJREFUOE9Nk1tIk2EYx79NyUNqTk0o6KYrnZeChodLDxfeZpCbJk4RXU5Nm7tYRYhiYXbQlaeGutyW2gxtpB1RIyKDEjKwA6Ti2dR5KNDn+fq/S6TBj/f93r3P732e53s/qfnkSdej4GB2SBLbwf+jmB+gUMgOheLg/z7EdCUnO6Ref392SpK8Hyh3I+gBwBo7lUp2xcbyQEoKD6alyQOpqd754/h4FjJXZCRJTl9ftmEzoK5/wdQJxPgkLY2WV1dpc2uLtnZ2eHNnhza3t2nd46GhjAzuValY6jx0iIfS03msoIDuQ9COQCtoUSjohU5HuwgaN5loeXycd3d3aW9vzwvW2K5SkdTi58fvzGb+3tdHFggA3QONEAzn59PvjQ1yqNX0zenkvX0B4ffWaGRraChJd/385JGqKvlzTw/fRqOaIGkEd1DjU52O/3g83BkTw5MOh7yJuUCUM2o0yi2hoSw1IIOhykr+YLNRHYKu4XQvyKA/N5c8yMCCDD7Z7bz26xcJ1rH2rKKCG0UJdRAMlJbyG6uVrkJQjWAB5tSbk0Nr2HwDgvcQiIYur6zQyvo6ucvLueHIEZKuQPBQr+dXra1kRuqXEOwFArtWSytra1QdFUVjNhvPLS3R3OIiLUDUD0F1WBhJJtwDW2Ehu5uaqBICI4IFlRB0QLCEzaboaHrd0cHzCBYsIIuesjK+LAQXkEFrXh676uupGCWcR6AeghLQptGQONUAwfOuLp6Zn6eZuTmaXVig7pISrhI90ENgQbdHhoep32JhFzLpu3WLio8epUYIfs7OUjF6UKJW88XERLqYkEBNej11oG8XhCAvMFAuOn5cNiclsTkhQTbhmpri4lgbEMANWi1DwC/xit3t7bK7rY0Fo4OD3G4wyEURESzloAdnceezlErK8vH5N4KzPj50PTOTfkxP0+THj/RlYoInJyZI8HVqim5qNFwQHk7SucBAPo2PKRMNPLM/4pnFszYkhJsNBu6uqWFHba1sr61lQSveQFZQkFx07BhJmhMnrLn4NLMPH/aSExR0QDbmWhwgyEapwDvXoDxdWBiXnjrV/Bdm2kYUxLwmEgAAAABJRU5ErkJggg==') 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,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAtZJREFUOE+NU91LWmEc7sJtQew/2MUY7INg7CLY3W5GMHazyzEQo9UmfYxZTbAiVlgRqLMSZ+XnDC3z2+Y0+8JGakKZTtR0Tl2wtgtLLQh29cz3ZZ3h3Q68vOc95zzP73l+z+/U1f292O09DRxubxOH23P//1bvtQts3dPnry7LZnXJhcUl5Avf8dHtwY+fv2AyW5DOfIXFakMm+w0G4wISyRRm55TQG0y/Wzv6mikJ52Xf9TmVBoFAAD6fDwqFAqFQCJubmzCbzZiensbp6SmkUikikQi0Wi0kEgm6ewVaStDCfXPDandifn6egoaGhrCzswO1Wg2Hw4HBwUGk02kIBAL4/X4IhUJMTk6ii8dfYggy2RwymQzOz88Rj8dRLpexv7+PSqWCYDCIQqGAra0tJBIJrK2t0XdVAjNDEIl+wfj4OEqlEq2wt7dHrchkMmrBYDCAz+fTIjweD7FYrJbgIJOlgLOzM8jlcip1eXmZ2rFarVAqlRCLxcjlchCJRFRljYJYPAG32418Pg+n04lsNouVlRUcHh7C4/FQIOlHNBqlezgcJgQWxkIgGMbExASVNjY2hvX1dVo9mUzS5wREFLhcLrqTcw2B//M2RkdHodPp4PV6oVKpqH+SCom3v7+fNnF4eJiJusbCJ6+PviSyScakiaR5RIHRaKQpmEwmbAdCeD8zB6vdhebHT8SMhcUlC83bbrdTJRsbG3RwiCVCRNJJpDIoVeNNJJJQzKryV+rrmxiCtyNCCmaz2VhdXQWXy6XDpNfrodFoYLXZUTw+pk222Z3lW3ca26rgSwzBwqIZAwMDlITMAVEwNTVFR5fEJpK8Qyp1AJvDVbrTeLenCmxgfiZ22+urCtWHyu7uLp2wVCpFKx0dHaFYLOLk5KT6Y9kgk89kb95ubK0BX7A8a+1qannRLeW0daj/rU51S3tn9dypfvDw0QiLxbpX/Z7FVK7e/AEj4Wf24/2f5AAAAABJRU5ErkJggg==') center left no-repeat!important; padding-left: 18px; } .linkify.gist { background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAk1JREFUeNqUkzuIE1EUhv955MnsbB6r4kYQLUQQFncV3SnCIqJsoWGDYOGjsIiCtY2Kla1sjLBIsFFcXJC1kaSwENQmXUQSRSUSjCQSTCbkbR4z47lXEgtBNwcu3DNzvvO8R8jlcj7LshKmaWqYQERRTAmCcEru9/sJr9er0QF92BJMAVGr1TQ6CeZAc7lcGAwGkyQAxpTLZU0eDoc8crfbRTgcRjAYRCQSYSmi1WpxY7fbjU6ng1gshmaziXg8zhnGIpVKWbquW9ls1mLZsaMoiqWq6lgnBxY55He/328Vi0XOMFZmqVMD4fF4QBAajcY48khY9JE4HA4enTGMFVkaTHmy+ZzD/5NSqYSNB484w1h55ODO3TVu4FXcWDywl24Cmp0e1WBhyuWELAtIf/qKUrWOONmev3Lpt4NRCXq1gplpBS/v3cDc0nGg9h1o1ZkfwO4Atu1B8cM7HLt8k37V/y5B2b4bJxf2Y+7oEbyJrkMvUjki0YYJ03LidfQxAt4dOHdCw5RdGZcgGobBlQtnV/BDr1GfDai7ZiHZZRi9PoY/e5SCCTUwC9gk1GmMh5YWOcNYkR4Sv1y9uAJbYB82N57h4OnDmN7phjQ0qUkWRJuB+TMaPn/5iFfvv+Ha7eucYey4iWw8q6tRJJNJ3Fp7ClUawEkViBTfkCR0YUNTVHD/4Tpm/P4/U2CeKpUKfD4fJDIMhUKEhP45St50XedZyLQY6Xw+v8AUemVb2oNqtYpCocCWKi2TLLfb7ReZTGZ+kmUi7i2VvfxLgAEAZChMriPcl+IAAAAASUVORK5CYII=') 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,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAklJREFUOE9jYEAAASBTCorZkcSRmTjVCDLziCwG4hfM3EIvGNm44oC6WNEM4WXi5FsEkmfhFX3BxMmfAJSHW9Qr55Px3aZp3X/btq3/hQydPzKysMcCFbBBDeFj4uBdqBJR/gskb1W34j+PmulLoJwbzBJJoMm7dNO7/ntMP/XfpW/v//SKvk+7tl7fvXfTpx5pCdWVSiHFv1wnHQbLi9sE/Wdk5SwBauaCGQB3gUPb5v+7Lr/8/+fvr/9fv/z+f+Pyr/9bV735l9Wy/79Dx/b/Nk0bsLoAHgbeAVHv/v77/f8f0IB7N7+cu3DuecK54z9+7lzz639e9pK/7HwSWMMA5BJwCJeXtOm/fvVj1fcfv369f//92cN7X6ZcPvf9x6Htv//vXP3r/+T245UEYgpskPTNq08LgN749/PH7/93rv/6f/rw7//nj//4f+bU0zQcUQwWBkdVbGz62y+fv3wHeeXrlz//H9798//qpY//M3KqfzGxc8djiWKwZnBUuWQ2/fr46fv/P39+///x/ff/d69//z97+s7fyMb5/+y7d2GLYriDZikFF/1qXXXj/4Pbv/8/f/jn/5MH316/eP6jVlBAaIt6VO1/jxmn/zv27P7Pp2HxEajLD90ra9Sj6/979O37X73w0n+vqOL/0lJyMVBFq0EGgDSD0oKAlu1/oHg4ugGzVCKqfouYuL1Xj676Iajr8AnJFricGqYc3Bw+Zi6BVUxsXLHAdL6QiYMPFNrwpIxHDsUhgtAMAopKDjQn4pPDF7P45QC4hSmc1eX8WgAAAABJRU5ErkJggg==') 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: -webkit-flex; -webkit-flex-direction: row; background: rgba(0,0,0,0.7); } .gal-viewport { display: -webkit-flex; -webkit-align-items: stretch; -webkit-flex-direction: row; -webkit-flex: 1 1 auto; } .gal-thumbnails { -webkit-flex: 0 0 150px; overflow-y: auto; display: -webkit-flex; -webkit-flex-direction: column; -webkit-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 { -webkit-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 { -webkit-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%; -webkit-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; -webkit-flex: 1 0 auto; display: -webkit-flex; -webkit-align-items: flex-start; -webkit-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; }\n/* General */ :root.yotsuba .dialog { background-color: #F0E0D6; border-color: #D9BFB7; } :root.yotsuba .field:focus { border-color: #EA8; } /* Header */ :root.yotsuba #header-bar, :root.yotsuba #notifications { font-size: 9pt; color: #B86; } :root.yotsuba #header-bar a, :root.yotsuba #notifications a { color: #800000; } /* 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); } /* Menu */ :root.yotsuba #menu { color: #800000; } :root.yotsuba .entry { border-bottom: 1px solid #D9BFB7; 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,\"); }\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, :root.yotsuba-b #notifications { font-size: 9pt; color: #89A; } :root.yotsuba-b #header-bar a, :root.yotsuba-b #notifications a { color: #34345C; } /* 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); } /* Menu */ :root.yotsuba-b #menu { color: #000; } :root.yotsuba-b .entry { border-bottom: 1px solid #B7C5D9; 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,\"); }\n/* General */ :root.futaba .dialog { background-color: #F0E0D6; border-color: #D9BFB7; } :root.futaba .field:focus { border-color: #EA8; } /* Header */ :root.futaba #header-bar, :root.futaba #notifications { font-size: 11pt; color: #B86; } :root.futaba #header-bar a, :root.futaba #notifications a { color: #800000; } /* 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); } /* Menu */ :root.futaba #menu { color: #800000; } :root.futaba .entry { border-bottom: 1px solid #D9BFB7; 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, :root.burichan #header-bar #notifications { font-size: 11pt; color: #89A; } :root.burichan #header-bar a, :root.burichan #header-bar #notifications a { color: #34345C; } /* 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); } /* Menu */ :root.burichan #menu { color: #000000; } :root.burichan .entry { border-bottom: 1px solid #B7C5D9; 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, :root.tomorrow #notifications { font-size: 9pt; color: #C5C8C6; } :root.tomorrow #header-bar a, :root.tomorrow #notifications a { color: #81A2BE; } /* 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); } /* Menu */ :root.tomorrow #menu { color: #C5C8C6; } :root.tomorrow .entry { border-bottom: 1px solid #111; 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,\"); }\n/* General */ :root.photon .dialog { background-color: #DDD; border-color: #CCC; } :root.photon .field:focus { border-color: #EA8; } /* Header */ :root.photon #header-bar, :root.photon #notifications { font-size: 9pt; color: #333; } :root.photon #header-bar a, :root.photon #notifications a { color: #FF6600; } /* 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); } /* Menu */ :root.photon #menu { color: #333; } :root.photon .entry { border-bottom: 1px solid #CCC; 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,\"); }" + css: "/*! * Font Awesome 3.2.1 * 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,d09GRgABAAAAAK2QAA4AAAABOwwAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABRAAAABwAAAAcZi+PV0dERUYAAAFgAAAAHwAAACABwwAET1MvMgAAAYAAAAA+AAAAYIsCehVjbWFwAAABwAAAASQAAAJy0Wu8A2dhc3AAAALkAAAACAAAAAgAAAAQZ2x5ZgAAAuwAAJmaAAEY9H87ZapoZWFkAACciAAAADEAAAA2A9wdq2hoZWEAAJy8AAAAHwAAACQNggfraG10eAAAnNwAAAHJAAAGSBTsDgdsb2NhAACeqAAAAwcAAAMuqThigG1heHAAAKGwAAAAHwAAACAB7AIcbmFtZQAAodAAAAFlAAACuDv6ZZ5wb3N0AACjOAAACk0AABFdUI+v+ndlYmYAAK2IAAAABgAAAAa52FJ3AAAAAQAAAADMPaLPAAAAAMtUgjAAAAAAzp1qV3jaY2BkYGDgA2IJBhBgYmBkYGScCiRZwDwGAAq9AMkAeNpjYGZ9wjiBgZWBhaWHxZiBgaENQjMVM0SB+ThBQWVRMYMDg8JXBjaG/0A+GwOjMpBiRFKiwMAIAANpCRUAAHjazZG7SgNhEIXn31zUIPnHa2KUZbMPoD5BWLAPW9hYGLewlJAnCHmCkMY2pNQmiAiSzspSfIFcQLCUM0W8RM3vxhVBwUYsPDBnOHD4ihkiilE0a6RCJ3UQJvWe48oPt08eJYjJoRYdU5vO6NJJORvOXt51bTcYENKwUUARJZRRRR1NtHGKK/Rwh7GkxZZ1KUhRSlKWqtSlOSRjQvKEePRBpC9EAiMPDz4CVFBDAy2c4ALXGABCwuLIpnjiSyAVqUljQjQ3Zt/smh2zbbYGqf5t/7w37I66HSfHq5zjLGd4mZd4kRd4nueYOcYWKyZt9Fi/6hf9rEf6ST/qB30/exhd42+lkvSJVVZo1vdC9Ir/oKlkZjqxMkPZHxvxX3HfAOwveKYAAQAB//8AD3javL0JfFTl1TB+z3O3mTv73FmSyWQy+2SBJGS2AFmGsJME2QQExIiiCC6gIIrbKIjiLiqltmrUqqWrXezXavGd2mpXfW1rV/33i221/V6ttbY/WyFz+c7z3JnJJCSiff/vB5l7n309z/Occ55zzuUIt53jeLuED07muGzIEeIdIccIFLTcdjK8XQwe2y5xxzj6D7iqfzOo/8wTnPSomOfq0eOSwRHq8LikSCgcT2WSIQfE06keSIY6AiA92lK8GXK+eNw3mqdPyBVvbok2esW8tzEqLohgdJGLp+L4x3PkipaIt85gqGN1Yh0c1tGCHofLSsKtJNVDkh1ehzjem8pkIZPs8EjcvC3nrzt/yzx8Tb9gTXG8Nx7gc2Z7Y6cYPD7csbTF7W5Zei6+EqT2L8XZ1QH89xuSCnBCJ0dYG/LYBpkLYdftXJD+ALsaTgA+onFid2aiQcHjdOMweIS89oF2h/YByHApLw+kMlHtyNdeu1M7fvSSS46CCAEQj15yNayJEUwAsp5Yy6cG4rD66rEUlxzVjt/52te0IzE6O9yJvMyJHOfjurlBjos5JFmQraQFRwAS8Vg84XB5cKwzji7SyuMcSG6X1+MNCLNJRw+fzWR7IOvQJyftoNODA5UPxrR/PJjMXdQO0H5RLvmg9o9YULWIBYsKomQ2HMtZ1Hu+87I0K5xtdQG4WrPhWdLL38mcmd/YdyzXt3Fjn1jo2xjkuWjgpX0t7TNmtLfseykQLXIWVRUaidPoMCiiavnszsOPiTN8Macz5pshPna45e6h4wWaW6Bl6HNM+5bn/Bwn4JC2CmlsYUeAeHt4nFA6pvz9KWfxLiUy0NWmjfRcf8myaHTZJdf3jGhvFO/OO8k6Q/Ssc++Y+9q/WhbnotHc4pZ/vfb/vVH8rF72F3HuRriwDqMqFkfnLSbiEwE0q1IwzcbUTIdXFXFMfNp9q8HtUt1ar9aLE+omq7V7azvh/dfVLvV1eL+Tv9Lj0x7UzLLFXW9++21zvVuywj9gU507ZlwM329q0mYuNtIlQip1Gyn0GiFmwqnlY2K5HVM3Q9gBHdq6o0e1ddCxGPbAlfB91q6mqZtFXNDUA9dqN/Rov9LWf//7vFJuZseHtJK2ESEbx76BSyBUlSAk1SPS8e+gKysgcnYpGLdngmL+4JWjh648KLuDmQWbu419K2/cf+PKPmP35gWZoFvWCq9rz73+OvTsveLWW69Ib9517llzG1vSLfjXOPesc3dt5v+kx7/OcSa6pmRarw1rbuN6udO4s7hLuOu4O7mHuC9znJhOxVsgLNWDyzMbEKxP4QdHKs6gvrQMYGL8x0x/qvomLibIx31sZ5viIXBxX5GjHh6fo9xYjFiVU8tXpzpVmbgMP2ALScKFlKtEwf2TOYs+wgrW6FMYCz8+5uSrk2j3n6LAp4+xukW2iAUK8FL1fNLdetwI1cKEETtFPM8NpDQuNTCQIuw55ubzU8UQjm6jAymgT/KTKs/oT6aK4dhiZfvPybDIgVtvVTforXJM8MP/sH9ifYTrbNQKjZ2djZCjzzE3yVf7ivmp4z56ymo3BJmTPuDXFWdxzMlPGnrKBFWFIQhNOhf/v8/CRx9VEWNGWRiPYce5qeOq3f/mWI0bCjy7ruSs0k3CVzkP+vDMkKVwG0A81Qt4Shjx0QDSTf7itJv9y/w3a4f8fuqAOLmL+vn3lrEo/82wlfr9fu035G70YrkXn3hHdAoHuQjHRV02kMIJI9Cy46mscXz5HpdsBNHJStZ+q/1WLwni6CrVBvFS6b/F0A+N9VdK0fEZfb+I4Nk4T5/hFv1BpyWiz81s3Jbx0eGpBzyIoIR5cVNhXgKnWkZUC6ItI4iCjDnH4WP96anwMTJyUk7qfLMKSXtqa/8USFp1n2ycl2s/CWo/WvuLBVoryX28VrP2fuSWls59iS63Ji5NMSMiCUFsTTrlzGY8Xo8kW7H1DAPAgy/RCog/ej1OumfrOzTFs/e8pP1R+4H2x5f2PHyw5YKGoK1507bltxx9+egty7dtarYFG7Y2H3y4mB/YMoB/JP9pmnLPS+D/9Deg76KgtaX5guCSX165BZNjri1X/nJJ8ILmFmvwIu1ZsqTINmjCNmj8J1ZwxLF9gYtVwEUHkphD99P2TeaHU/k5p7VgdbIH5D+ee6jscF6qMSfk8PlekbkJdcOlo/Rl5WkQvHeMeUTqGZsPRmNcjP5UPCy5PB0UgnB9yjgjLpyRCK5RScb/tNW4XBMyBaR4gqKOiN9jUCvQwcAFnC2HJnEVZxD/Zz3EBe3NImqNlAFFq60gY1AA4e7QK4cOvUIO2c3fUl2RRYqx7i6P2XrLtDa7Ra7/ndUN/hlNtys2q+nahGywLXLWWf+XxW43PW2tbZyrGH13eyyW8YnvMNos5uujLLHPhomJh9ZwCC76ndlD/JlYx1qzT4ndYbzQa7u5w++wfNPu3mY0XZpRLGaTe31tx4w64rawtK2tM5ebzYoleqeyrTqxsjtpsOqJ2/3Ezc6OEi6rw8hsbi53vo6HVM+yeAq/ivSvK0Dp1h6AEI5uSJJFBmkVhCVSXtNZRt/iGLIzxGllc4oPmMKdH81bbTyf423W4hAU2mVF+64i85c4rUMb+0YRnxpioJNeYFtKT5iltgWQtjr5YBUYWadwFwP810cHEOYdMWHJLiMhxvsweHRg5ZW7VvLfZLU/EUulYk849fXvwwE7T+Q5la1/1jXseAn/ShvBk0VAi7CTIUFhjVLXCEql/djt8OKmgviplj+BOBfiqqSX9ML/7jFYeIuhOFAcMJsthh4DUch/BtcG/8GWxqsKIUEtSBFbiujCCAgAv9XiZM4SI5HJnOJ/GIAYlyh1BrLG5/vJd2nftC3foHwARv+KdH0YcHbx3EqW2hqCUDyBK2EiNulFsM4LnDbU2KlxJA9XKGbt++Z6etAfZ4csyTcO+aHT38hjEAxr+XozzDQr2pbRPI0W8Sxv9Gs/8A/pdbO16cK9e9oYZqvvOyWw4eytELSCPQDBDGcnuJvi1o47O4+bhL4jjOjgMHzwmPbrYwcPHoPGY3DpS9pD2kbtoZdegrPhETibH9EqcENhoahhqoOlHOSs6qQvvcTmsQPxpaW4PToQ2jlI861AyRSZd0s6neOKIHWTwGBK7ci8hNROGDcGoJAbppsLS0Z3jggbNo+wpgZclqctLqgBp/kfZid5v7WYszjBhcHauxjuAqelmGv1wcOGqAtWYogNQx7HJDZMAitdUQM87CN+AdjJpBUEs92O9KZqAcoasJzA5+JMj+BS/apF3zct6Dz+dk+ZjpAoEmjjYlw3YiilvbD8VsfNtNfT0UtXH3hkisdAPEtZTzoMuB2hDlHnKkGcvR7vbDzGkDT46U2zPj3rZngZweMZR4OWc2acWq7B4WgGJM2AEl9c8+MpPRf9g+AY3ndzJ/4RR1O9llNVKNQ3JaDA6KZcFazUcFGGA7gq7aqAituRxO2iBCw9YI8LVfAiDCnad0w1Jq1gMxg8BbZw8O8nFZg5ePAkqCFDZrP2HaMRcnbVxeDGqg07SUcVpB09CXQmaau+x+mboc6WoJggTN3WjVUtVKAXmw05288+tKn3urRhtskNOa1mM/QajVrBDh98SFMJgwmKAljoyodQKyT4EI+HV8gbio1BQ1bVd2Ov6uFPQBfw8Eqx6xV8QddZkCNDcd8x3Khq31HSPj7nSyvv1JIcr/DwrmYnZlJ4RAswzuTve7pJY204XFv8dXfVGNm4OsohoXg4O3RphRkcmymWvTAcLI7YHHZ7MBhqIMEPXfTksSVOrWA0qDGSj6lOVSv8+MNWPVTalKzsRYl4L8QjYStBnC3ZQc/7Dnqwy5JQQTKTHQKe/YjacRRSmx2Ohlvu/WEZ+dr9ymLZbjUdMILhIu1HXxhD1e4BddsNCOEip+V88cZE4JYDJRRvy1kKMd5iqFX23U1TQif4X9pzyebrcRFV4zNRbiFbBYQLhaOIsIxt0Uh54DHbUUFOygs7xbpSwXO6wcmFWNvxrP4T5LQt2jsHtb9uu15N0enClaceWPj1s/f/eYGpGcHRotbS/mEodq8U+H2LOgfuB/UguLbdgNlgRCTaP7SvXXze9apeRDylHuhbdMOljnO9Kq/S7BhyywE9wCKDGQaxa2qcgqJhSn4BBxMw0vQp/BPp0vQp/OoErpV6EhdKJwSmeggYP8o8PCMzJndjomPMQznB43i9MFQp7u+TuIp///DoJM/co7Q+YbCaCczWuKDz2BvoGk/T3d5dfapTWhDPX0oJenA5U5SVz5WJ7iAYYDsYgo2dPFfYeujQVm2kqB/zGF34Jhi0f32z0EnhMleiIxxclsFlZcPD7S6jY4MUq24lkbDOm6XrnOLMiDInGUMUa8+xDuX6t/aLhdq6X93ffc2GWxcXtHcddl+8wT3r7W9te/raeEfmujNXWnxxkVsUP26lHRfeiy9K9/fvKoq1ddad01LTDhrjPvJm0Gutv2LWbLU51Rwv37MwOrKfttBGEPF3j8dR6/GHCyKdIrjVELergR7nFdSsDGNIIxDsVYnAlPJdrm95Dqwdw0YXXh6eV/+k9ivtq9qvnqyfF7584Vjc2gOeb7m69o9ACgYgNbKf3PL4vTNCK7cFx5DP4MIu81mb7gXp05/Wjt276Sxz18LgGFIa3LYyNOPexz8BNS/u2fOi9me9X0GeE0YQ52T7Fh6LFdjFg8bD41mtal/TjrF9WIJBXKrC8HG6wmEQQyiaOaivQQovQSHPypo+eWmcPpGUmc2nWnl2peCdtA7In75AsfniNeFwDf3FfTZlwSQVa87De/1irM5d765tnddai++6mFjHQBf3u2/hnC1k7VnMbfs4bcIjtRTK7mmQ3mbE28Q4pPAQEJ12kqAEeSXFR+4RnP652yzmunii07Vk5colrs5E3Gex3Aaf035uQTBNyA1ya/SqW265KtqKThb5848+ClntJa04W4z7Eq56W+bRbz6asdW7Egj9s7+hpbTr1mNM1CtYhFrferBDB9jX+2rR641ikvWcmRH5FObpPaQJd1aV8+KJ24Anfhuec3StuiNpFX8h/AGjVSjyjz90QCkcabd0xBFxhNzJNOhJHJDHf3weSS9KdtAfz9HnCa6YF/J5Gq3l2buI/0X80SCeo9lGAa6DUj4aS/IaC6d8ZgwkLCENpj+O3Q2Wz4aT+5HgWkp9mU3548mII6n+N369+C8Y3NjQ8AD+9fRc29DQy/4e6O3Fv2vZ38be3qMbN9Jkvb1i/tj14nX/1o/Oi36m3ye+xfbo+ioeRQkjQgpijPjyQAE3x/6twuUxzZ1IpWPFdDw9kILhdD5OfhwTTDSyX8ulY5orFiM/ieXTMJwaSMeLmUQZN71P3laqK32q2kQ9FMlA3BNpXCT5EVoBeRYcbQ3AL2M0Lp8e+QjtS7FAfwNmwsrIj+Jpvdk8pyDOcyW2eRV3LrcTIRZpEiulu3A5Z1O4duPZHsKWcZw+JzowSvLKrEulfLLkZcc8ouEJjygxdy9k4mOkXJVfOr/Ro/1FvWzO6ObBO/01HgnwTCRmt+SdZuBFwvt5d7MAsiBEBbVNAAMhVo9kcFhUVyjhh7iFfLBkmUd7J7rwzNFP1ZlMSs0V/KfqMwaYJpP48b8IZisZstQKbnQUh9Gx5aQQITxz0ejluTXbls7tElqthjrJ5KpT4tviSqPBFJai28PGVtESEX274oaI0eDyGcyxUKLWAxJv3L5k9PLd8232ugUNPv5VT8QWqKAtWqHi1O9zPyGW7oqhwxsAd+nApowKfOpggee1cJvXHUokQmpte0RbqC2Mtul+t1fMGy2d4WP/DHdaDEH4rLY2RP2iEf3G8l6el/S9yIw0fxfHNembCeP7hMqgmHWUWNY6hhYpH88lsKT4N6P0AHefUfwN07sWIa8ToqplRMddRizqjtMQjyHDjUP+w/7G3Gk7gKN7TmfjcFGnPXPakEUdptjMMJLQw6ftIEHKnDjsH2o8we0oyQboNHOIa8IeUDEMxKNLyMAYAlVhV5X40HZ+8TtHjrxzhB+hKNOxPH2OJNXNacKlN6vJ4vlj/GR+6AhNShYf2jrK0vH4vHnGwoUzbj6eh4ocwxhvmY6fkVuOs8QjSULZkRDJMlw/q8JsQCLNO+6/2yUjFRMJ2wi9f8hmUm0khhPLpBg6AqAniMkS+cXnFj88aK0PdqWLtW7imXWG2+X/C9T0p00vnu9OzPQlapo8Fk9bW6cEy7afOXRa5w9mCXs7zWapbYPWU9/vdfgGeXfCDaRPe6N9Of9TrQcIkLMO7juunSab7DZ7imwhr7i14HvZ826ctXXuihkGVZDcCSRXDQZiItNDfotiDjgu/SOZ89Ocq8HsNgkS7w84FJfBWqGr2VmmcjFuM8fFPIzKwc0iTnsou2XVxdMwGoJ+7KINXFAanUyapyc8+98G0yHbQZFPxOzYOAUowMmMI4wDIun/2yCOA9MykNv7uVi8adbKZU8sqQOetPR9+aunr/p8aimRAYp/JNO9g60OwUhEARQwOZOBVQIIcPV00SlCk2vJ2vNb0jOnT2vO9fqu/sq6DfXujr4li29YsfP5Vb8M2YIrFi245NK+jcGgctcXtfds5EX5hoe29/dbpoX2PLCpZXTzRiNvVutq+vLwN+DuXeMRDGaemFZKFgCos/jq29ounbNgZ5trZtMFW28YOL0nuygarbEJArHwXEkGRKDregnHufVx6AU12yOke/k0HYusiCNGAViSq//zNiKxgaGesM4PZ8PhEBYPNNab6zrNBqfVJNfbPYam8/2KCt2JcN+9oQVABFnKZnIxs9koTPN0x1vMhM9lowGQZOIUa5xGVb32haYbl1x7OqiqK5a7Fcy5acvTjW54oPemjrhHIuR8K8HRVNV6j2q22xpmTot/f5v2wwffmi67bJIo1tc3KEB4wULALJfXxTHs4wXcjRynenFSe8Eb8noyvaTD64cGkOmUInbHACCMnZGlBnCo9BzQF4E+6xG2UvAdTyfSJahJ8PGMjjlS2tlK6HVBPOFI4yZg0y/36NaEm1SvfrFHeT3LM67/uEG97FcQC1v9stxso5MlJOpr6w02A7FY5YU3t4RDCqE8FPOMRhLstAVcIm91XTy46PG1d7d7CLhn3eoy8gYiYkZBMsQvbrnUbnH6TFKdPM0svRxyuq51zcKfM7RsWbVHvK5OxFIJ2A0GAN8D6XNnBVUb33J6zWCG2ARCxLMTh7Wva5+8pzVRKyt2waBMF3H2FINV8Ag+Y8geN7sjhb/BF3p2bPAIIkh1JmP7eSHVV+eAptXHScz3iA//YgJXdo2W9qb9jC+xhdtXmQPxlHOQ/khz4EjYE5NNAWV1SPTowLXbpl+wIgmHNG7GW5oCeBZ6ppiD87/UtHGqOVjxiem15TmQRELYDHT8xVqZAfH1Uterh+MjjX1qkqG3iGNDfwAap4HRYqJD71eqhv7Y3yedcOrhdP7uON4Ju8X779zly1zc9wGTSpFKEjTHmRSLyJi4cd8x5pO4UhxLKeCzMCbUct4pnP+P2q2LRvL5UruZTyxLBTEf+R9ot+Nj+se3u3q0q8f63x7p/ydtPrX7Y7b5Q3iCE2+pHafwTwY3HxZ/qr4Dp1oYCj3FQ8L4Y8wjIpJ9jJsqZir3SKU0uGwy5+jfK05h0tDJs7E7/5PGVOd30/uUlC7tC/9NyKAcU6tRGzEaIWi0WlQR/R+wHkqsKcfZU8hNdI+l4UeoyAbN7qePU/esupOT9rF0x6Dzjagkzn+3j0O0g4wDacUmikc+bhfJS35agi6Wgi7N+DG6qPMzmexyA5s/dnaW+1Qm4usBGDdL5hIWc51Tu+jI7mJu95Eju0lh9xG4x1lntiQoM6rZIapwz+PlmCO7H4ODquio0G2yzkO2cgGulY4kpYMyHUi+pQEHsop1jhXHvRPY5yS/fXj79mFh+7E85IYJYgsfsH5IdCTuqZa2FOw04fZiQcsVWFII4uCxARMwS/A4Y5kLhZJsJNIDb4nbOAmpyVqkCbhQNiG7k25IIQYBSOMgZoskPrbPAYgqAGU3I4oG29a/tT5PLvcocvH3Mj5JQM7A8GhBGxLfij2uDT0ezaTjb8Uw1bY8P+yhqRQPTfUjbWi0AMNkJB17HIYfi8f/K1HCPwVdxsQ7nqNiBco7iTMGO68Lg8ChkHavvXdRr027JwTT4LMwjS/JcHAXLRg9ForHQ7y04KJXYJr2yjj5FZVKp4fZndi4i3DuXnqnxd874fZrSMjpN13kbyffUer3ApxY4NyMz5tKtPKIlclWnpKGno5MjN7JU+Yoj7RBB9JHvNdDOBfUe/yyEBZkP0KWq39rP+G0V7S12ivLpR1nXOw3dqSSBv/FZ+yQlkM+GoKWUNZrt3uzoRYIRdP9/U+9omG/Xrn7BuOjt/7mzEA4HDjzN7c+arxOX6/Sv7CfEsLYTK6HW4St0meTi+NcerKgjgdtijDaqBBF9cUNLk2KBPJsymWccCZrx1+x8/DOIcIFHdojjqADNi4/snuUQTmf683YeN48w+r0ekYZGPIIYsacrXEIgsUhbUTYsEEb2eBf5j/shyEspnOIFCrlFP/zWb2U3UdqZbsDi5EkXeBkY9+1FizFTl7URopYFPFvgOAGP5ayrDL+7D6+hVs3UaZ3RkeJBqUSB1U9o6iw16Pq96TdEAnKkuphq55K9vfI9CaHSSFhl8V8uYvcCYvqG+xUpO3l3jn9Prvq/ouWZ6t/WDt62e7pvNcg2BXFM7M5Irsjs5decstTW4dxy/CpuJOTiFYs91O11Im+sFDu5S9VxVJjNxjhNS2P+0VzYe8B7UmvCRHs8PlD+zpnrBpatnLOrISHbTCYJFXu+3U4121MQtEx2bQyMuCkiaU7marf+4+XphzrbmVGFQsvjZ9TxSJJrj+N/qymr0ZbUVNzMb5BJjfh6+IaslF7bvxUKqQylRpOpcIb4BXMWwNfYhlqtA8wKy2kpMNzApcozufc0jnDWFqUTVUWVGAsLEqplE9Zujkz3ldZHIDJQlHqBT243E9wagpxVESFmJM+EDFCpJU5VeDYhQSNpk76wGig0cCR+z9eenV8bVV8A6qDlGH8LioONEGmy+3IZPmfqz6fWpxlFKqk8o3iZarZdyznM6vkBaNSXFfGuRHjXmcyVOmmtEwsf4pqWKJMVqrUdnKd5AXVd1LNs6ZoAiY2+4qzWFv2lPhqtZO0JVWpeaVes0GkT1WltYmGSWvD0R0rnvX1avE6cR/VzjAiwUq7xdbolmMve0Mhr9juJWcXAxaXTyz4XBZ0Rblxsou20gk/7lAVJ6odcSUNA6ZtMJqv9om5MaqnmgKKl2G3XM9JtUjjKKZx5YzllfSx81a65i31UGa9leTpdOjIC3TocGDo8OHQ0ZHDDpvpmJrIC8yBI4cPs4+8oBgr5Zfh5KTyvY7xV7O0qslqNIhVaiGGqWtHx+NjA0QeV4zVjRm3Jsa3ZWIjKrVX1zu+xgkVsfnGGiQQJYSTOo5T9U2BzQZUzQitxzoGYxT2xBeqpoVMLw+xr/imLnMV953Apz6e6RPfEEzijxFj4sTSPlQSR2fclDB5s7gzFiO3xbbF+mMxzQdvxtCxLUZu1R/Mo/m0uvhWdOpl7jrxlNCPZXrLclltTOvKSJ+9ejeE/hiWuzU2EIvBm5ovFhuIXhjFWkghlSjuwlLp9Q+8CW/Qd388jmHj1wC9r+SoNlEk5NBVhtyOkK43lAw5dOWhtANPinHSQgXadTb+J9g4gO5hgmi5ieJCuVLMyXmg5WTZqSr5pVK7yq05uQ1VukeT1lqifVsmkUMs19PC7mpTbUAZ3m1UkscGjK9P8dwGkNnTk+zoBS97jm/DNepTT6nqOrXORx2+OnSeHAJ7J7QNHvyw5KUQeGnKsfEyuTLaWkTHEb1kbfXSVlI5yar2iYJzPQK0tuX3+FzvdMJWWgVpcI5OlOMM+51Ys3bB77Fqpx8JmkMqTbdkQhuq5ctmcQsQc56op5ZqBZ0FSVujC6LQGwArlFPgOZztEaITRE4rMurcJY+v+Xve5t0nm+3GdCicau9vbO+9gEW2hILhWQ21kJ/Q+uGKMDv50tpDK35R4zxXMs+rqUmF4q0e/665URqtdqtO94y2Jd0TgWGsT5QGm1Xuk2MM9BgjugKE/IQuj5Mw5JzWobIA7ZAuHY3uqg6Skxo/jIEcjUWHVmAvJ3/HcCnE+Z2J7R2Dgzama1TRPWmFRIX3YgU5SREh6g+At6KW0gM6fwbjK2kxX6WMHshW0mI+LEP44kV0IV0UfPhCtpwufDg4MQAui/vujL31MPM+/FbsTho/IYBwU+WuBMC0qbOXAsbLgUaY1DynKxrJukZQLy6IlH5nUKJSbLhyXZmp5B4XH1R8yoED+Dio0Lcywf/ih0lCwg8nz1Tx1364OPTJstrGKiy8AUqHyJRCm/do6+jy/q2qnofve0DF53nqng8V3vw55lEhzlKyLDTvsY/Yzhs5I+dkusmpBDD5MpHJcSJQYcN0nZyShpAYXFO0Hhi+5IcHV4/Wkr/f9BiS02Jwz4vaH7QfaH+gQla4JXRC/Ytk78P7i7Yz1hz88bfJe+sPjt77CPRqL2i/ZxKdAZgF9dRFz8PciTS2oR9HqqSrpJ+tjL+W1hls7MwFhmDNSRVzEIvH+6nYAz0Lya2YKo6HoPYqHo9zSD6f7td+i+flADuYqXDELfH4kvhWTNCv4yVpsVCqT+dxMZ0zqPCq9IlilKFYiBV3JVLJBJYPsWIuNWdOihS0V7H+eCodxxOe5DIxdiRjBRDrT2PtEMfa2akc0XGIvNQv5qjWPpQ7VsF+Ksd/qUKpH0uiWla/ZUWxjmBR5NZINkMRCpL+kLbQtpbxlqewznxZ37w8mKVuVXCw0thipbSXaXziSDHcIj6QyqcGIE7Hrx/xjnRMR3Qor4diOr/FsR4YoHMRp+jIGO5ZoPBeoumo/LZVxEPMVdJ3byUJgn11hpKOMt2mUUpqZNOnPrljU09EFB02u1k22/jr0o+QH44gtUU4HqkzjZJfwJkbMqfvGt6cnSdFjDaXw+jDk7L+8e/vh3soJoKpuHHnaZveEq9nDEsvLz8mF9cGZYVDur3ozLe/K9rX71J14V2s/i4YwEV/Ke+lbu3r1K0oMHBXSWIX/uJj6StCwDQ9Jl/MZH9pBkzvYxlS8ZLMoOXE7eLfxcv09k3VjqnazeTsJmnIFO0muUkbQu6ZtNkV+xqirhNZWo8VYK2skAoAUcqK6uoOMX1RqudC1ViYB4YbO/ngZKEsfakugnXxOi01gV9Myz3OxGqFQqmkslKqToNSOopqDQXAhgdfYkzPTpR0VaHpeAb24tnnFSvtjWWo9pkki+KPWut8Od/5rdr7DNK191vPR39dKyjo1KNA0ReBUorS3oc3MfhijP6k9iJT3U5+EsMvxvj77y/HQJJpg79Yiak+DyitMp1JjTrLO/5EnX9eTSVoAOKoqh5C2vQtu7zlk686LQWLy4UPJ3EqivU1q6I4XNZvWVVxIh5y/K/PWlWX5VmLS4XzyEVmyWCQzMV7FJutfMeF7cpxFs6DVPMSiiU50iG3w13C+5LsFtnliaYY8pzs0PXUqnXQdEqLWVthp3NSN7/S4eGHtULUX/BHtc7vXutrwZkjv+5sbPFd81wjPIl4lK4DpWNT3zxz794zt3Xn893bqAu+aXV+vRNeKRS0aZ21dXX85ocbOpd14l/Dw8MUDSvDlK7huPfpvYOPPTaIL6fOL2M0sJvdYtCGC0yER5fcyFIdBNwTJU7nBQLVV4hQ8yVUElZXNKWSsQTRWfyjBihERHK+oL32hz24vGrcdRtdB0D+ho/EXa3aW6/+cuTeW2wHvfa2lp76QLPLQQw837Okx0+Maz7x7EXZr3/tq/cllIQrnKhJ9AbtfDwVP+fITe4aXHM1G9Wrt4B01qYR7bmLLmwTl+QGch5fvWCVLHJkMDNLFeYpyfRlP3tod9Rp442JmJJweI0b9u3UbcGIlB9qo9oX4sSbFhfbdBNexugUcQf3JgICvVMauy87wc04bWjotBlzBVh324F1Wd3Xx+u+4Yq0vKAu3XfmykWL1ieH8gBNq3Ze/4VN5ZCNN5RCSrgEHXeByrSHmGGeeAJ3fZ0vLskeBHY2FzrDnEkNc3QWghxuedkMvr1S/vAb3bqgV/cbh2+Eu+EVuLv4lN91zdf8jf49q138ha7btETxPS1xm8t1G/yGWOE3t5Hc27u2XPktqqL8rSu37Hr7xb//ncxs9H/tGpff71q9R/vZvMib2lvgeSMyL/IGeLT/eoPp8Q7LVAbcyNVy3dxc7nSE/GwrsKY6J7YzRttZ4rJiCir1TFsc6mBarJTXryIthFQ7Y0MLeFJHs/FEFhFt0rJ0zSbsyxPkwFgv4Ca4QNuwdYbiNO+xT7vzb2tdrk/CC2A5Y31GcYq+aCDE22MP3gA1Bii4EgsOabt+t+QVuODKy57oPevLM394e29hG+2nppGLx7r5V5l8u2g+eoZ9ARbbP+fXBxoGGt4Cu+Nsu1l1qkTR2m99owPen75vQTi3/AvP7nO+8+2vXbY999Wz9Lmz4/70LoOnEIWo2Cn3JB48ckWqFOilh1B1Z4u7ksX0mslS2pUsPBeJOWaHj3Hh2Y5YhOccXQu6HsaNSbXSB+yDH5tlk0m2alnFYuGfPJbv7a0Ph+upuHBDNFo6ky4UL6R6hrh920Atc70TRmAc8BagagZUAYltQ0bQ3V4Rl7w4NC038PCw6MjLZoG3Sdr/0Ypp0TJktBKb8eioiYCCbok8B7wmWHliylvt5JPDAwVxKFUYeLi4SLUOScBbYFQrPuewDhmJafSobLeYzzZCGnjwGux2U94iPjQ8kKMn2Qn9ruJk2euy1PVp3GUc5y1JjscmvKHaX2HelPbjqnTZCXGxCVoqJXIvVGW7wJOHoDYCQ5DTCtrwRDcZYe48ffIcDdHd2vCY6g6mqYQDKy04Fgn5gdQxpjGf39iX69sI+gtD9HqDOZYtl4PgKJYPBf2NoSQIQSZlS40djH6RJaEZClXBg8eZgRURn0P0mmFIfw6U6Bhcz+IIUjFZbgfVIZRbhSpxhfJddjcgUdMqJTLZgJAM6aoL4KxEhvAowCVsrZZ0wIMgk+2RKqnJ/V2DnkAy2T9thKnTHhMlo1ag99rBrZ3rUgMdfalZdbNLSajWdVm9kCY5wbUv7WquCbbWN83tXnPmFfP0MiYElnMJDRuemp5d1FTPWAyjVj8tBdcXAC9bveHW7sSZX2fxVO9R+w6/u5wg0NXb2nNR37orlq1OhljmcSF68rF7GNwOKWqKCAmuKEnEPSyeSMczcXoGillqjqEHqOKezL2rnfuPBf0vaMdmzHHUCbwICjETud3dVBMwPfDUHe/CwDf+AZ/mW7XPaL/5vOHLc60G4nGCYBdsvJUY0t7O1kWNZ4B06Ia/fGHz58fT/EmmOex2MayofJLh/hPgO3r4ysl2Sq7+89rD2iLt4ed1TZG2rhWtza0rutp0LzV4pOmW30rGkMZ8pJD/ofbsU09B3w91FmNqIO4RBA8lhCif+LyxpNXZynxibpfUL/SzG+0SjWecQNpVKDuf5isTdTp1Cru2UiYuvKHVIS1HKSydlmPlprFcE7trOYmOM1aTb7ToMfLtTXhTp9z4nE7VkVvLlJvOo05U7lXlPJ7ZMarlpdvdauW7oBvGad7qdgdCTBqgfEGX1m/o9C4ywyK8H0l/eocnclSPz2CSBYK0hQ1yapcKOVvcVyA5u3FYJnmbVnDNcmkFGlYs0DCq81fOgWteUCSH5IJhGEaUywF5j0fLO2qoEJqpYIJDNQ4t7/UCC4K8uWA0jWXRhqr4SXlR1+GeTW3M6FIYQulNtRZlMUDcLrliMZBCepaP6KYDOwKCl4ljMO0N/sfs9eNg7fG3QRZr+MPMjiCSnZ4Y+cpPdNa3vdZmEmQQvuKLp5nuhv7HFzSuJsbvketrFHs7Faf3WZPzBD6LTouzwROT41X6dq6T75XqGe8jv2/D8dyGffs2AD7J8IZ9/HCR+fkCfQb3jc3pGib33axDjX5Ol9XtqbQS1dQAOTW+fHlNg/Zky6f6jhfC6QZYhi4hF05rR0YLG1/q1r4sQqniIP4WNUS0ncmFvkBDBG7DN8waPmuRtlMSHEJVYyhvhyMFicnccAyIJl7xjl3okgIuugnXt1XXr8JvU3T9Vt3OClzlMlbfyyAnc3xBr6t8pzzxBnn8ffGkBY7dBk+4/S3d9pZsfMjVemINOi0fcoz/fbieLMHdl+THflQKbEzUZ5xdNarqBXnCUQ2OE0zXC/KjSL8dHxZ06SmGq79YLfAzjhfSzuXYqZhB/FZHbr2IxtJXPIGIrpduLIiv0hfl/yEllMictNlynXPm1c6Z371hzVXi9b8/rX59W/rcxfUei8+9bd7Ou301935p+/du2zwDae7mI7tHmdwUX9h9hH+w1tg4GLf0XbWmXpV3nt3ReWk31JL+XVaD0LsC1vEbF+7+1JFVTuN0IGO5joxrv8q4EdkI23XSjG0fcSfZGE9oZJ33hYbOi798eN/evSDBvdUNIVtfvWhG4tW7bt/7avFGchW8X12bXGXbh+JrVFOulespUZBV1ECmLM0VSoc4ezwo2T1B6uZDCG5ytSkA3YAc0qhUiMTZ2Wh9j8k0jR6itkyFfMlO4ejrVLMPuzn6vVzxainfnz7Gpfv70xI+yVf9zo19FEdo7DQwsafR5/LQAD2v08wCyWuFy2/J54+zDCJ9sjFbJN3D6N+FJfkqOs2MjGfKHh5K/zLl4oTsLTHmdEm/lDNasnSZLauFBgQ+t314u9rUvGx76c1/d5PDmAi38EOv+Zc2N/qLZz959NEXn4WO4Udf3AvnDPGt4eAmh0WRlq06Yyb/5PD27cuam9TtpbfGOTYF8ZDBzI3NS/3kob0vPjoMHc+++OjRJ7UHhvgWPDkdmxRpcMW6vvJas+FaexdnyIHzch13lDteJTem9w975qi4quwVuT/EYNHHN1dUZawImMxRSQY/nsBNhtbDssepuBEVP2JlUVVtL+45WL5eArbK8d/JzOcZFPGHBrYM4NmiP7W81fgpkzvcKcve3apJuSzWaDLL3qdNTvCGmy6XLSblLlnpsXvNhxVrJannCpo03FKd1GCmSc1dNq8Jk5L8fWZnUthDDANWl8tlHTCQPULSab7vPosjKQg9naWIZJMkXCEkHZb7Pm76kkmmEwy5RwAW0iWHdte3FBVqIk3tcxXFLAd2y+tU84VtNTblk4r7DNlwY51RsS71TIvXgMNUSWoymg2By+V1TuuFreOS2gc87WEvcRRHbrPb6mp31Ar8wo1uQtwbF/ICeutsdoyo99IIEg2eiVELm8gCGuett/Hv/ju5Knsww7FjjB9llxiWzcwa4WSnEMPuERjrgd6v4MKUEe0ISBTSmBaHFAnSFRtFqMS1S80dfVt75j9Wr7v6/mgHb1IJEgNE5CUQo/Z6t3L1Hd+G+XAtzCddd1ytuOvtUREkqneJyVzmjuj9V69brf3th7MCD0Pjzmv2e68/xN+q/dfbB+xrG41I0fKyJAkyT8VC3LHGmkU/233r2wcOFA9c8dNFNY0xd1wCjBQkSeatdpCNjWvt+4R1qza8u3+wf+EvK/g80wHs4i4as5oD9CBMZei9f4XCQlQAe0pJV+xXD+CBQ1lvuCJdbGWwn9RC6CCN7ad0UVKKjNrhwwRU9Fo3rSM8vrRDGx7KDflqYk2erBCvnRZtStiDQUusvs3bLv5875UFMRBxpl22YEt+hjGOWO4Xbo+eOfTMVTs92gjdP8EZ3TxrRo033pJIrtq/oP3JLYd12zsknxyc9ePZmzb6Lr+xxTtP7AimI1FnMS/JNoODLH7CF7AvXhLsmF/b7YAN0TOWhKKDc92ezYO3Pjy9pbE/TfLp/pq9/enaK/c1x+bcsuvMcw5zZTt9uqxqN7V/XbWjJdhcU0WqjM6Ika2iPmAiVb4jXrqPx9NUJ5ciVeVdjkmlUlNhldMHgYbuYLK7MqKV4WoJ2lxpZyQgblqT3/tzsd3bVh+zBIP2RFN0Wm1cyHqaYjU+HE8Y6liaP7zlyfZIZP+qZCLcaKpR22dvjmrvsDELenbmn71g+21fhC4+bpwh6LqiGhfZAI7u2vkdwSWL7QHf6SsWE4fBJkvFvDMaSQc7xHnelhsv923cNPvHswY7zj98zpmXz5s/JxbatHK1u2Nwb40+ao3Tpj14QBzc7HHPHYyGluh2lPkco/MR2zrJajGfm2iVWBw59vzJZoer1yXV4Z1Jbz5beUb901EMW3k8MpG8ypZw1Qm2oKV8y9yhDVuu2LyoxtnjrFm0+YotG4bmtjxD5pN5386/UbzbOYWdaP4Ly69e3GpPDs71ezz+uYNJe+viq5d/9pniy6Tt25+lxqKdk5mRHpOBDeI+0khxuZjLYyXVeIa7FFDCNmeRAF+5hask02/dSJ6AaLNoTAKUWscqeSnuCNSiuENSBH5YLY5QIUdmLx0K9CouOCQE3T6LLvSuWphnY1+R4qeCbCIdKZoFEwdLdhqCiDAXR8q6zLo9AmpPK81x2aQjgrseO7H1mwaKLIflZDri4dHNDmH3ROzuL3/60/uwYOfihTNh9iKy+E8Hr7h5MfkTz/9JtnVN2wmvVGN7e8g3fpmaNy+VnD9/9Am44/4Hd23uK94G++LOyIwHyGXVuB/jpzO7LyYqrw86KuFguARtAG+l5swSPKOiMklHiT6kRKMDd6ARxO7wjyCtqq1MEocZ6sQB7UJf/IFzKuYjU+c8QIaBiYsw22ral5CYrTc76uCNuO+q5wmn26fUuOcrNBzdRxOT2TCu120UVysRVCxJTnaXOCbuS1gDirmKbDMz8UaFWp8s7tSvFMltT6q6GCQZ0gplIV+WsCzgy4xK8iuowCTLx24WaT56xTlmJ8tL4XQKGDRW+pSKI5ZT0oSIhJoJRTz1II8wGQjCZUd2U2V8BrPAeqKNlGC2FIaY/v2TgyIki7kqyCUFHXINOlhXeAZUrt7CLaZ3GGmkID2xdMgl48nkdumnF7DLpPI86PcubEumNlFKzKp0FWUNP1pygjsqfPcEt+T2o/mVt7+4ozkdr++e27/LaR3FKdnVP7e7Pp5u3vHi7Ss7GyGILaPs02BjJ7n9kZ8OLf3s+0M/faT+sy/lF9618zQx0xQeTGaWrJ+vW8mZv35JJjkYbsqIp+28a2G+sVPni3bq+mAVfQgr5+ECuPamc0nudtw/pEScyscnPLKEjkTJ661605crIqVTSWqvC4NLUgutlD2X6BHoEZWII6YdD8utOC5eXMsB3kvHJ0xtw7Th6g4ARZbxx/cCFQJgC2nMUNQtBrPFaDCbO4xGg9NoTIsGhecVxS8pRhl/ewQbnhr2LrvD7phFgoLdzr9wZPeI3eFRUjPXnz2n6bTYdP/WRPzMF860py+tnxY7rSl39vqZjUZ3e98crzrb5XLbJTPiuS2KYulZNJca4/B4RsoL/5tGs8mAv7RZlnyi3CaLoizyYpOsmETJaNpllgSPINpNxGIivGKo4Qn/FbptEIPb8dezp0s1mdP2nn7l6et3GBtranw+U3C6ccd6DLhhWaZGiiLW2tIUbBR4o9Uqikqn1xtvs4AgxG/gPV6+QuSW7TwUGJ+KrfcPtzXIjIJmsnT49Lt5PYpaXyux66ayNvh59zndwHWf44bPM4ODzVRwk0ptnuCoITYoODNOKDTEpzA42LloUWcnGWosL8dGxEYLqqrlApXzVDyBsDaDO5eep1R5OZ0qWRegJzUVKKKrh7iZOAhdQvSymN3KOrMuohsl0tOyjPo1rC5tqKfFbAzGEA2+zmoyKwZFEYzqUlfXn2e3nD+388Ccoetm1HpqPDVn1858feZT51//i93520Y/dfWPZv6+E8MWb/bURhfnVy+9/7k9XX+apQ64li9RiCAYid1JXph2a13AP93nXe+JOcHY7q3xZGYs/j9/vb5xuMm7Zlq9pyE6/VfguvUx7Znj2Wn19ZcsrlnrbXy46ZJfvPSNObO7l7Yrm1d513kVh0PxSI0PjJeloDqDzGICpbsZlsbRvUIoGVtitnARu6DcSDo+1AneAK+b+qJOQjU9xLzL5N68cUNdMtewzLhpMK/99bT2CB8wOeVkZ0ftmjqr7IyY4kEbX2+dOXemIrth4HsHSNhaZ3R2dnS5rPXNQu3MBeoCiYfGujW1HZ1J2WkK8JH208CRH9xkXNaQS9Zt2LjZbXLxEqabWSs011tdXR2dTmOdNUwOfG8A3LKCZVvreVswboo45fJ5VbF5y51KwU0YGtMz2fi7MVWU3UdErnzG0LjhsQj9jNZtrki6/UUHZL2gfqjxlfwoB0+ccQY8YZ7SCgt3PA6HTj9d2yqu+3B7LGO8qPn0tpjqgOEORw20UdS7lSSqJAioU0RkhlmvRhqH8wZEZnzjZJYa4Rem06Lfozhnddpl1ezhz7kzSyyS3DSjSXHxfI2vzquY2tOt80TRIjtJF8z8jNTubKqN2mfe40Z0vhrlgTUm0dDir+ddypw+WbKQ7J3n8B6zKluaoi02xeMXpemtM4KCx33PTHu0tsnZLn1G+34XccoWUZzXmuZnjue/AZXlklaJ+od2GMeCWEHQKVJ6D66/usHjZXfnHsFbsgdG+YwZadXcs2DgU7/UfvYF7W+vR1pef/KCxxtC/pbm7ffMW9q3dNqVsP4Fw9H9tw1dNBS74Exhy6b5Vv8NWvGd/3XR3cIt5JqzRZP3K7uEOD/tjpVr++/7mhKP7j96nnvmZb1KWb+A58R3OTeFBj5CLeM4dPNzVOyMuOEEQOAP2uc/97kvPP+HOxJtbol/Rfvj6A/4TvB//hvPaH+0hCNBVs4TbF5X0DXKydREwr97vOGpyVEuWlBwSpz26p/rav/dc8pX92ft1bKwJskf1y4ZFGucP//3T53zeGeNOAh3H/+pLkrEjbPxFxtnoTpO+avJ8XZ7KEbDBTF13If7/6FXDg2NfWwAMVtme4cvlHUAqG2eQmdjlfXDb1HTPBUb6vpeUVuyR8ZNsBGUdNMGUOuLiF9TPQW6mWTT1J5ayC2N0P1BZ41bVCmvWizqB/gcAi4PWO7GvjEuOAaPjFjU45xqIUPFYYtKzabldVkVsfwtpe4qDV2PziSk2zPjClIOIEK1xylWYggXHYszM3v0usIu2U5UZ/1NtVHi0Z55ozbkdvjEYYjuuPQmYiEup/9OXwzMX9X+oF3zq9qIy+njQYL//fQzvwRdi1d73u9yh2rfgPkeEq29qd7psNx06Q7ttUfqXK5I7a9gL9R/1QKx2juR2LD88pmntVBJD5Qr3XE1cE0Ue+Am3HN5J35jJlQ2wwyTWq0V7G19bW190MZeD1UrFB/vED79gFBjHX3PWiMIX9FH2v68Y0OWt2Y3OJ63w9l9ejb69y6MWc6Cv8DvLQ6HpXhticzM1XaQjem+vnTxkQ62t+5ltHgrl2LQQCkr/HExK+4tVsDjzwr0vMxkK1bPgxRoeAcnOgQpT3kRAyntLG3XrD4h7pKcM9ri9Y99oVWertbximMPq3MEvgYvpgby2uXaLXAln2d809QArA+pG7clQnOSs5sCszrqmr3Xd12+akdmYx+1NZofSI1G+ae1nzVp7zVX+DZUvsOEO08WEbgUw1fClCaAUJyk7UGHi4h0aNlnCugAZ5z0RNJte7pdMh5Zdie/zD779OD5i4u7RednHivmHxNj2IMcriwtlxp49rnCZw2dyzoNny0892Tw9Nl2++Lzof0peFEDrF/Tkk+lBugiG0g9DL8B6bHnXE6VrjXV6XruMe2YVpIDJoiTaeI1jJbxIgjb2JOK1ctM7llmZtXps5exG+mT2jyizwZmC4o+vR79aWN2Z2Rx6JaAYm78dtrcUN/0dLvSZJYbXDfd5G9uUtqfbqpvMKe/3WhWArdMSNVUf9NN9U3j05D8hGzEQ7OZmsayNfvHF92kmBtuvTVgUsalqXybjK7pNLdlIu+RCfZRFRa5dBNAuWm4x1XzHsv8NKnEfCxp1ZZP6x6R4mqCfkMSqnAexceXdhQLgWjgjIU1fTWWxkULA/MXBoOLnn1++dESxxH6Eeo+ccERIcS4jjce/czsEssxqHhr3HXWGjInYmkMt/XGr3nUA5dXMx5dM9MrW+Z23zrNnVu+vHZmMZ/LVTMcB9IXHO6ZqXMb58/W2WZG1eG3+fklWfeKnlxk/555XYe5qvHJ4i5xGVKASYeOt+h2vloFdsuFGCj7ahtuFRR78Ur0cpCRuz0wgR5h6Hov6LcWOs6eDOnnP5WJ8wYkhuIMBYOROV2N9YQXyaJGaw2oTo/bsPAMHLFioWPpQAr6dU6kcPaaVS88C1t0qqU/rY3M/syz193xJEA3HxKOXPCJw1vgcs+j18R728KNlsgcUmOtc9d4FQim+/MkX9PRHOJ5iSzPeRDDDsW93XNbVqZnugaTqYEKK7ImePqKXC5eGt0iDtbs+Z+6TRw4z+Oe2XP4gvMPd83bsz+S61nhzi7hcRAdqrG/wqtlfG0GW0J5JKjphFYIsztV2aHfFDqY2V7dZhz7z44yxtiWqk65VrFEAWT07wYyhoLHy7CnMgn3+LipTp0EDQShIU+nvTj5tJ8/Bhzr9M8adlXD5FSAu/ojQGgFnLnq8UlxXZSXTfXF2OU745fQ/1ZBByKSdDDCL+2guKMHVxz1kYoVCNybJHY/wu4lqXpoyVAtk8Kq0uqk1FAuV2TTbhQnm/TWmWNzni9RxKW5zsyhc51ZcuVNE+aarZ/Z80kOIXFRCXANwhRgG9Ghlu9mQ1ucp4NqQP5wUC0B9niaooFhQUwvkhodZCqAsuRqAKBfFqAhE/QkqUyyphxV1fX0mwGH1jud62ErOtFxFN6nmpmTaU4e1RUGaXpMqh3CXOg4+uG6lKxtXIp+9InqJGKjKrqbrImejixkqzX/RJGVrTdGUxhG+H6pqbB1PVgmNm1zhrW+1BjfWEMxtTalvmSpXQldVxL0pvRCRbuVfZQhOl5v8qSeVyoD68RWncda65yiL8VTtauNDVdFSFNX6HR5gTVrnE0Sqs85Sc+dbFRObte5Y7M8CQxwJz5MH80EvyY1E/QPrCB39JTsPnrjlB3RC1I84ZJTcSlRJmwplRtnxuRkpIrkTDyRZFEy0kBuDz0haJSEu52VUDNz9EyR6Y+m7oE0vbaLeJj8PR67nkzCw1JI3rgVaA1hWmSGFsiwPQ81XCd5ZEpjUkIrztiSVGRF1gvxZj3eOL1ER9osEWamKAMk65EzDEOh7fJkcUuRvfiWSswVQI8cliKMn5LN6AasOwJYEYuNUMMtlOtCn3Rnop+gyupxlKD1ZDNpKZFqJZTZy/LSUZLcYXpp2cPHGW+Lyk5SWrgHWCh4mFBBxIPtyqbiWU+WVY67Hm1nDyDylUpjBv1WM9GRDSNunqFZsTb2yqTYhGQiNICOEX3H+QwTQ09k6CTwUkS28l7KcKPUqRzHBFaBurAlAYbx4UC78G+iJgk/j9gkIoog2a3xsIN4eb6GJ2YTSEYrURQJiI0Az4uSQQZewsOVN/E2uyIZeVkEm4s3pPAtg8Uv8D5elGUCkijwJlWQjV5JjNaGJEk284Q3glnmIzbRIhgVVbTyRrNR5M02gwIOuwGMosHA+xW1Tq6TRDApFmKViEXBGkXRwMtBRahxiIIAvGDlW9slSbSTsEG0SjJ2SCaCzWqwSwfPkEWB8IpRghaV8BawAy/L2DrCOyyWELbcaRYEs4F4AXjga3kggkR8NoqVEAPm4hWri0h2g9EjiRIhFrOLF+sMitkh2vxyVCWiSSaiT8SELoO1wSnyhAhGIgEgru8ReQuOEwGjRExmVQZ6RR6WLSq9fDcLhDYehxHkFskmi0Ss4WtFHnsmKsRkkA1A/9lkRQGrQ3BLsgA43EZZFEWjWZbEBl4mvOAhDp53WhQ7bzbyDmLzOI6+dDev8k4JZKOdJ4pgkmQ6VQTcNtFsNEkiwcUk8jajVbAQnDuiEoGX1Toi2O1wkqKQ9jw4QDGDbJAkg0o8gGDhAbsFQYrg0BtreNEkIniLikIAcFwJiJIAgl0SjAYiGgXJqPKSVZQdFoNdMLglItAxEj22WtFgtFiMIlhtvOSlE2szCzaxBsdSoUoOTqzAiCPkRbirBZvBCmYbjplslDFQEQDnVXAJYq1g5EEgsgEHFIfb5sMmGMEqi3ajwEuSWeKtOJLL7pAB7NgFE/gdAs6ZFacRggkBzNN5vtEAxGSUxIgk+Y24mdE8xNVcK4hugcfaZLfdQ6Q6l2KISrJFUggOuoB9DQuqASxOEy85JUE01BC+3hYCI8KN7BQMNbyRIBQjBCCuYLeYsQUqbzPwPBEMzXYl5LATG0/taQoIjbxRMlnAIdY5eYFH8OVFq9KILodJNhiNBt6pGkE0CKrdiDWZeDsxKwaDLEsER1U0gEkgFuwBrjQgiiSO3hD9JNaDyIKZttaA00whjccKcFkRSUQorpVw5ZqIkRfs2Ble6bA0OGptHkGuMzAtBfcJt3Qto5vcVIqxjOUbS5qxVG40gGDORAw4O8e+QeGSRbdX/wyFjlqRzxVXU4njrfE4OZL4BHnN2/bW7bpSzqzrptnt2qvfEe+9ymhzlO4V/ojJYxcyWecjmz4BtyXm7n9CZyKFAqaw6cjINn79QhdX/S1OXdayDk/X2Ui9hNIhKP9O8Q3XiX6Bo6i/lhe4UfpRLmpC/yNZZmTm+fFvNFdmc1EzFG9O5aH0t4j091Uix3iUHrlido4q/rJvRHWIVzkaNJVZmzvBqZpKP/4kcs3Cb5rqNbXoY4bmONUHb8Jf6psSY3Yp2cxROcU29p2SqjEIucs2oCLuEPv+wMTrSEK/HMAJpW+q0Gtr+lH0oRNY9gfcxj4Y0ll2MNS3UeTyRU4L6uyTYdq1YRwCqgCS79uoGwPfWG0TZyHHGQllFbjHvghkZCdQmdGUoco5cvnjRboNKxsGJfTxoBlZrMhrD8A5d2Gnyx8Kukt7QHvgLjpApY8A3QXnYIDqM5sb6X0USwPnYCb2Ba2CL84scvF/mDxfIDEhFzXIRXPRFKxuloLWLaq6HCLH7Js7uBncTG4Ot5Jbz7jilECx69yELCJCMPnXq0vcuPJXrJkJBybfwwRuMS8ppSDLHzl//4rtV0v9V8ye2ycK4z93rfQt23/z/mV9Sulz16O6nTx+dUmalA9tX7H//EeWi31zZ1/RL12tCwsShMLlS+Hs5hZvrP7WonWKT2OLHUwWT2sofSG7+NDS5VeJu26tj3lbmmEriyzri90lbRff5ULcXO6CktUSJIUDAiPbkBQbM7CSgbIBlnJYtixOw3szJW0JfZ9JlHTxS0pclM/iZS7xSf/L/sbmAB80qXJno63WZ27gQ/6X6poa/ff4i3P8L/kbE/X3+P0v1zVNTMVfd/o9K6+4cuVLK9euXb3nilUvr5rgh1wjlh7kG8y+Wltjp6ya0N3c6P/POt9BP/kzOvx1B/0JTFTXMD5R8fV3Vx5cefp/rrziqtVr12LJ470lm5OUz1zH7v8QLri0zgjk6Iew9CtNOQBy/vWHjhdwu7xjJ4FprzwIMHvB0NZDTdd/FvIPvY576L5XM37bKzDtyTt6Dm3t7w38FOmNy3DNWZiee4had2dQl9Ul6kvSKS30GAhBIu2IONziPzvnbz2e3zq/E/6ZK5u6ivty2tvau+QH2ruu/NozrrvuDL4W7iwJce2Yp62ELzbE4E5tR0zfdqAkyyhzS7kN3FbuCm4/d9uYrX8RGI+R7XEMObeWljrD2ZNMkJXJNIbZt2PY1S7DtqlQbWnSKYMxo5uol9jXjvgeZroHy6I+avUEC6El4x/mSoBbZibp0Z2ltfI68wwuhON+XgzkLXaHtbj0YoOAOPGmlfvuvnn1OpO8acW+gyvnGS179liM81Ye3Ldikyw2tZx+4O59KzfJmNJwMfmq1WG35AMi7z++obVj+YZzlyT0V+vyjtbEknM36C+wDoWsp/l4q4h40q+GyAjumMPUvL1V8PFD+eK/vkJMRD8kfdqlrmjEnkOUb2+fADPaB29Pr1q66qqBO9KrGizGxYuNloZV6TsGZl+YOG1V6o7B9hkg9MFeg5yzR6KuW5r3JWdH6aM4O7mvOcoeZHiWKeoytPl4O6JF8H+CJJfTlu8YMhBBsAs+rZCDw7fwgn7vop8bDVyYi3FJ+kWJcfcupROyrCXidmSSMoSMEFLpIVL6NGcqU/FIw+UboOII/RIE0E9BUP3+eV157RfQUmTP70GXxiwEEK6R/7XuFCqq/RAsfXMCM2MZ2jcbf6H9gnxe+4X2Geiiujz0axXANQ6N/kvI6z7G1xZO7BOvFq9mVpldZa0o3YJGSaC9pOUApS8cjPndE9KLVz+466ZzR/+547WHHryMnKl02y1K8ZHTztt6cIA39K7IreotPuML18dr4X6lx25WtPN6L12xtpvMP/cTux48lzdc9umHfrej+Ihitncr5KzBQ1svGBj9Z++q3IpeMr8mXh+s087DuB4F7u9eu+JSLGzTOHk4qis9X/+2B5OBY9+NGdOvdyTLLK+JuqDiJN8aBfohII7P512K9iel3abfwOVxuHkcbi1fpaGLYcfY7RzPhr/G38g+SpS3zlCgVnGVFeyPc7rFBsJV3eaoo0NVXyf9s3/o1Hbxqq+phaHx18z6fRy7xypp0nxcS9vj7e5N5a6ypv3mZE52xhdkag9Bv09LcFmK0ZQ1x5zlW8IJtXNThJ9s5ZndE+p/4rvVN2vH8pMEVrtfZtngLt3g73DFsDH/h4kh8Pcqq8d0WG1Mx/OfXABX/ADu2hdyV+N2wFZBVl8dcqKHZNNhKcI+YIXnkeoOMaarfmeS6GGXwZSZm0yfbBg8lE6mKLYpyYls0nHKQbjmouVb+2bOmFnfcoHPMCOq2ufYt8LgWckuoh2S2vr62uprWyOn15w1a/G581bMh+vE/9LHwWnVB0r7yjYghuaFN28V366OqR6tVcs29K2dXu/PGTqVuU1OIOnDay8zLyG5h6LO5KpUyzRvbd2s2cmZKxd2rGzN1nZp39HHzOpU+cvPOafpkUazIzZwnXahdk0lYsK48lX6Qmn6fRWYIBAY05VJMrqRVqq9YQOqfMIOtsrlAB+q+oIKw36YAgjFm9NZXerHW7K5RiWpJKYw/AGT/IPv+r3t+28EoWNX3yWKySqaV1k70mv3XDpvbl/fL+ZvmRV7Gz4lN3nbY4uWLV521aXLb5tpM1C68TxbwCZGprf0zF6c6x+c3rY8TPJj39zLRaafvf6p/HWqORpfdlWXsw5pyvs618+etXbx3Lk9rlZ/zQkukb5kc3ZGpLXd6fY22s0Gq+XC9kA8No2El8QNM2NRt6fO19U9b9Xi+iq+6Dn01kmNt+mGaVmfOrKy1y3pA+Jxl77Go/dW73GrPmQ2QNDyerJjn5uh6T2qZ2zk9E9e4IaTiE+0L9geM/CWuq7UvvDqFTsDnQEgXbku1QJglaZHuteesWVNZ0u7I+pwyzakudVwy7lWsurFgSuQ1p+eWCzZeINVctt88SX92y46+MSu3V3dHrujVlzttI59Pl0MEbIWBJlHGt+aMxprrZdbEtIb2p+vXjo71OZ3hqL+zlmLP33apntWz57rjgDhVyu8hcQtco0ZTJLNJzeaVO2m71400Dpn1sxgqLWtf2D3sgdg8Nu10WM3lOfGyXFKRWZjoo3/O7mHdMsN1X13TPDD/7B/Yn0n6zyW7egxKeIq9/gYjZs67qOnrHZTcpfJHohUtKxiWxDuqDi1MSdvnSz0lAmqCoOl1V8Wpftw3YlPlexCqEy/sIVa2kDCF6JlK6Al+6IxL90pegGmeAuPQ2yb9ippdh475sw4X3A6RYm+j/1406ZAAH9w9fe+19WFP/53pZDigyUH/wzL+4sMzYtZMzSv84W7WWRgkzbK8nV9r7ixFEICJQfjPeQq+L+dq+EGq27ZqW1mSuJV1FgcLqsQj+giEOyJCJhO+CEi08NTw0zMTIVu8p6Jv2s/gfyDFuN3jaIuDg8DBtUaMid4SpRSSjbHJ8whq2pAwh0Eo2p5Qe2tG477BKRkdPl5gsgi5rcUv8S8QmGUs3mtRh6ACkvQHwBvtHpt1LaoIeNpqwtjIb6Crsg/hsMsqdhDoBsRJe2pSg4zFsRTfVn9EqL09UsdpaTfzkuXdEW9vOSldhPo7Y5Eb+J+esm22apxumtr95XPbtn9+zsu+MZ161uWDQYMxEwkR/KnRz5x5MC27iVWQ8yb6ehZXXuOQ3hJK1vxXM74tMEzF4a/kph54N1Dl/7g2llD1+yfu+XBoDkot0teV/e6T/zmM3s/986a7siuMxr+b3PvAR9HcfeN78y262X3mu6k0/VTPVk63Z26TsWW5Sp3GxtZ2MaWC7hjg9thG4xNMwaMabEgEIoxEMCUJya5JJCQ0HkgpEAinhBeILSQh1CsW/9nZq+p2CbP/33fz/uxddt3Z2ZnZ371+61p3zCnu1rqnbB6Idj+0euyFyhXtyl5cn+mdgKqHMxWTqZyOlvlMrhPDAmmTpXPsSorzCubTvxtwrYnV/Wf2HFe+fSpWjOjYjljzav33XzfvlVNuHKWSHXzHNsSm/EpfBnJS0CS4GXzPY8E60Dwv2bfeWl3fd+2K9qX3+FiVboKo1VsmX/4rbsvv/+TeU2ezfOKq9vWz5pYLS1ZdpssPVL5uSVWpNeEqRVYtuExLg1O1bE6lYDjia8ZlVyUjRPpXB45UxdPMgxhQsWx+8FwDEdGZY1lhMXTjYRw+COVtHfxrQ7beRf3x1xGrtpYKfqMBSql9KePb1nxoCtg/mTeBeFF8XHWFee3r2px0W88sEWqCFZ0TO2oCLW3hSLVnEGpZvftkyYfeGvK3Ttx6U9TOOmbStQH3OsXVrZ5dJDxaDxGT4HPHQST+Gngx5ewzQUd4PiyS2tn9fdX9R2YufNKZ8q15YHu2e3ru8fXNDp98cYJmw7dEOJUrE4Vn7p458MPbOnDEULkzhk+Cnm+KKQq0Ry8Fs0RtSEFFkPw28ZE3S2Qrk2HLOCQ4yjt5r04vY4cZ/GM6yathukN5JjsdIh2LIqkP9xEEdxE9BqlqsDoEyuN1ZzRHe1fNzvatXFnvHF1b2yru3l1+/nLrePii8IXzNsDTuzb1zUt1N4eckfi8Yj7miPSfzdeuq23ye9K3H3TlDjDqWCJ8+a3fnp1L7Mm00pd5qDbV4CaR+NhoM5X19vUubrZWRDppe9s2rdz5oG+qv7+WbWXwtbplXfPn7RxQmOrz14TqmnuWlo/cNkeU9X4izqaJ8XPr7bCWe0aq8GhYJ+76Zp3i02Z++NWy9pXMW6EBrVYBdGSCZ0rb43ipCUfEoM9IYhaoQbpW0Z5dMNpQGZ37ajZDnO8JjbcNLVMi22uZVN3Ht45tUxewLJVh08l8JjEJA5/HLB/S6wxPAY9TvSB5P4ev0ka/OCag9unTdt+UF5IZZDCF0jkl47nuI0CafwDBul+lDaTdUMwFlAxGEqU4hhRNC4SYif6PLIugqRYm8ElRyo1Rcfla2X+czmZAIOkDBEchCTGQUgCEkciyoH/8rVxKgExhog2yzachlvAVo/cg5h4/n3EDMd0AsaZvDLLYIdWAOOkzPKzcDpPpvCB9HOH5+oUUVTYTeJE/Zgjc7TcMQD7UgmRvSiVgH0ynXhWFmASpwa0oovpO5UYAaGAZdckI+PjOUa2qjCinYa3cWBEs/0xryXGaEPyHPS4c767YTdKX0tT6TKe493RI5+by022Y4TknNZusTJWJ2yEJC7AH8XwZhSvY8ohSV7wEQ93mtJKdiJU1INV++q7LgkDEL6kq/5BMLG+fEm3dNUiVVt5c9SKRJdoc3mbaqH0oKfl4llT2WTbYrph6AMS6W+vDvxraVlVdXVV2bY/B8Hc6QfD0qk4X1XkEwRfURUf/9RWdmPrtP5e8s4fRWP9GpJHWJ5G3LDIYcs4tpJ4O0gsFsY7FwxVwG32kpRNcL70JFgMVsyGM5et+NEy5nrpqRlzW+eY1dJTSCUC3dBU1rWi9dhr9PVDbvovoKZ7yZLuSRdcMPRu6gUorNwyPuwMp94G14Mvxo076BpXV/zX4VwAtURewKndvmAAQwmEsUUSD63yyMqPcIFgEELGwlGXvSK9f8dD0m8v4oFiv0pv4Lvf3NL/7IEZMw4827/kiQn787wWu1cD8cY7QOErdKH0gvT+K5fdsFdVoDighKrF/ej019BVXe0H8rwal1+49rJXUBlLTpu5v7O/x7hY7mHAujjZ1cnh5GE2va+FIanVrDXdhUIcHvXZtF1Nx5D0FTaI8XkzeLV/968mXMQ3B8pPUzt1JTpoZgyMgi6kHWq7YNeWFEr9hUqlRe2knQGVwagyciao04FFY50Kjoxx6k5AlWML3mp/xL/G7wfYa1gO0LN00MShkwyqALpAbVEqiRVRi26ldqCbKtDNzRA9Bj1r9KmoVGOcuvM0VY7qEqSyeCBybDVmtMVen8m5PO2soCtGQwBnxJNYGMyv7BtxRiZaEBh42SdmrJXBlAUgYwsBtbz4+kLOURPiL2zqNZh6bjtgMlTAJeRISgYfgunzrr5W9H53pVe8FiNugTVgyhfXARlpCKYpo4+CnfZKncMu7WanNk09UNIztWmDTj7jRbLYLJ+XlE79uajoXcA9iW9y3RfSE5lxQcYFs+D5j0JCLJILMWw+H5UR831RQ4DJAYZhKIPhiGEEyHqK1C/d+fr1u+c5bKEj28rrxze/BJa+/jqYkYcjxupto4DEvgB3gA/BHUzi6k/2r3txUk3fwhmtawKc4upPgPDJr3PgYmbjGNhiD4PgsWM5+wwaE9HYtiy/Ftk61AbwWzgLIgM4OxYDEo3pBdIr0r/uXNV3gddTWBGZNvlWoLrzztQPMAbDyXMgNbAN3wuh4Tom0f/Y8plH6upmm8Rila7/sZce+3D/J+eAbTj1zbkRG7ZtfR2ND+A0RW9HY5hb9lHLzpmYyMqOm3RiABolaD/65OnNgjr1vraIURmNzPPSKkYhaAX2N4zNACaKdvYYuEbBiPRvTbZT2wogW2igSy4Ear2NrtcJBUaFSqpeAvN5SuYMtxUjhdBtDo8kjB5zH3Fqe4cTfZQjcTSNdMhTdSVy6mKfRBFr9ZhbU2qhvE0SHEv6IEYXLKnLT3tMJjNnj7FVOyWZuRbvTU6pTdblZJMk0vSnUwvSclEm/B+j8BijNbIFFquUXMZNhiPtwIhNMpnJOAdRCs8a2F0GzAEPT25HJ468diRQG5i2bJq7hXaLWrWmen5D59Zy3syojYKaMfPlW67aQjYFI9nc2tkwv1qj1oqgkjoN5vz0GqAdvNcNUlRZRRkOi34udbL/yJF+LMLUTJtWAzvVAa2oCoUmNal8nNHI+VRNk/LXQyGVqGXhU8B4Vc+Nfz0A4RtLIFyChVIm63NSUDYkQcWQbuaW/UzuUYYkdzYnvHk4gQvxbNBIssU+CSmBmSRTxNILk6gOFCiX3oJUzhFVV8KgNbMezwUJ7OYAA8CVxbNNXYTOn5Mi73xAdmFgt5NWj+aDvqxcSXhrDFQJtYj4bUmauuxXxcmYaErH+WUm4rm0hqPEFhAz4ei/LM4G1ppJohh5c5k/HGsfIaoDhIOiXVxTi8tVs3TywPi1+w7sWzu+U1WqSmjf1ybQsjOxorKxiakqKKjUtoZMPb09plCrtrKgoIppaqxcseCGp3761A0LaGKVDtWgu7mm1E7cPr2ycvr2iRdOV1eob73hhlvRYvqFt6+vnrKxpjDqdzj8tUVWW6imora2oiZksxbV4n3RwpqNU6rX37702Pq2tvXHyPgv4+PaST4OMeHn/GYy5yUJJTHkYWcGconvMsSa9rsBUavRSD9XKkGc0Fr2YeJGgoT53QBBIu6TkS5BH6oF+q9C52F2yDhGsRShOwNoSczuWdjKDG4h4VGKkFzj8px3LOPnw2SG7Fl87iwl6AbJjQcxcWYfJs5crIIZT/w1l2BP/B2AbuxavOpw6e77YJ9OAH3EBzZA2DoHULUWa94k/vnd78Sc2jdBxcMHWw6vmtJS/ProMgZJULeMd5GNUQ6nESbOWEb8GNQKd6nyCnuWMg7ocE3Q+RqNTpBIG4M+Ufr0DIUcxhPPU/Opvpy3i83GsdAx9JUS0AMZ5ABnerpiqAPgrzeDjBYkw9Gw7UCwFsenOpls0IvsBmcCckiL2t/Q32lpmLBuYF1XfcFeMGFvwarDrrqeOteU/ilkOb4RAEal6Oxv8KulZDrE5Y/Evb/j0gMHLu3ceXjjQn1t54umZc0969b1NC8zvdhS3N9f3BI/vGpBURn+uMuKFmD8jdxW5xaPqq24tkzUL9x4eCf9+3SwSzaHXW6LqTlJL4bUH6OJcfkwqUqaDpW4xMgXgd6lKyrnKRB7mfz2cEqyWT5CJImabEpHV70McX3POwE7pzI2eXFKgLv4JFCcLHbjdW+TUcXZA+/cg3fVd6HWoeWAjHjLErO0+eh77x3da/r9QQLR4fQhKU6QLiaWzUMC2vA5IeYyO/h7016y82rzkhbUNGleUtnnjLVZvxw3xuYg3JHuFM6GiaVx3sOZaDFpgCBNMgNDVEIOD4PU3kVxtJNJYAC6vYtotH4KyVtyVNjgUHLRXpbaK8uS6Xy5yhHZct87R46OnzM17lzpcLI8GE/L8x7ydkkFgVvu6KiTlo/gg+US63ri8Z5vv+Cpw6tOUasO8/H3jsb3LsKom9jwcpQeN7BOSqSS6NGMEvUjF24jOIjZw3IY7ZVUuywB8NkMW7kbkW5ikfFbhq+z2TO9+bywXfWEkKC+Kx+iAWf1UGQ/GhpO7sWhh2wylUCfwtBXuOPTavRxQBm2to8EJw6MXP+WcHlANGLTrr0nA3KOLpvh0iuhqtLYvMMcuZGRjt3szO7GLK5BMEZ8AiZSShG/TAaJJrM+hOZcMr/jiMytKo30a00RntVPyRnECRyjUFdYAvqyrrAvs2upQzSFp3kpUaQB9RqV1D+UyItPeIFM7KY8zPzRfrNHqJ9Rr1J/oT5HEpEeFINK0DyaMzsyYpsdse0fgyP7bMf9/49df67zR9YXv3FjJrJ0FFYTRjPNil05jHAqt346b50+w/7T/xfPh2fYP7zMIHEqgetGgLOofOb5wWxN/zm64nn7Uv8cY+c//w+eKP3zrCX77kYMSDooC2R5oc/YoniWb+Yp6k/UV//3v5L/SS/NDlV5/bUAZDgOvJHhkVXNIGwejakfdmc1kv8jvfv79r7TWLNFI5uMFwwyh/LKk0jfL9M3QRyNjph7J/6/rY+eo0cN3cgkXHgIdp1KkH5FJ+WC9vVlg8jk9crc5wPIFdIgnn/iWQ517GduopYO9zQTeNeMeCaS15dlrPBmaCvM2bdZk4auGuaMDhBPdFT2Q2enWGJGk14EiR/oFL/kIUvJqN9I+iZufRmvP7OK+TGTGd80+W7s4q/EuH1A9sqkTXJIY4X8LzWq1HEZDNw16j54FYawOyfjpcbxqwP2OLobic0PZHAzZKz8IFWNvsVuOWP0nFX/XlIe0YbGqGJKlv4SRLJhkqeSAznpz4V2goGxa/PZWYXCDPYIwZ7HngdOC3ivLFKX05Gw0ct7g5Ew+heMxCJe9C8WtqK9kUYoxzWDsJVlrBY+AaT3pIHBuPSn8bj5+wbi8YFkn8uVSCYTLldfEm8TQWc88Mcx2wVrBzDuQv+QXqVTusDAoCvpUtgSNgVaDoIBlxJrdnFXg5fGMlw8HWvDoV5IvA1YbDW7IzHSnsGYO+a28kYM3j05wqCJIZE4+l7cBQZddNIVx7klp6nIZCmeTCbfOwri8UQi6RoaHMbTitlWchStI2I8ZegTgo84CjmIxCxKVI4zF2bYWvNjLZOyLwrTbmR8UnhAkHDEA/0fI+IwR5Tr+/DHjlUuKSmXLSk/Sy5VfGTJZALZuFy64RfAhpE44JXUVPqfTBhJcaVYQ9WDnJsxzcfLjLUTblTVqOwqKaRSgTfQSo1KJW0B+8GBMXcfJ2tkD/qRT9kibVGNvVvmckPl+s9MuahcHI8yQ7mOyjXGTjgLP1y+7370BHJT8AYq11i74VS5rGRrP9ifLnFINfZuXK6p1LVMmJk1rL2UeUFGmPVkjJ1M+Fy1Hrb701FFxc8Hl4y5m5LLdRyVa2N+e41gpxfG2onKdcbqjrEbHh/9ctEZuGBj7MZjEepfcCN5j7hUGXbnXG9CHSl99rB+Q386dmOR8Q31DTgre8/v3QnO9LbJPacCLROmZ8n3/DdeILjoTO8E37MS3XNjrpzfs/HpyjM0Z9qvLMuNVTKe6mgeBJMzq23XtoBI3hiCTYfDeBBSgy6XTNDucqUGs5wILprIFEMkDnoqDrfzz2jW4jFE19QTyIXe5cV06El2Ph7bhnsOvCAPrw6XFYuAaZkxzNbUohHQFAYD2YC+9lMDopYhjz+VxIbNARmSaoBeZzAMGAyAktFFZXRcui9nsBaHZhHjcx+apbKx74ws61jRzJ6Vc/znYo9IY1r8KN0CWlpurBx+3nJiHBiULcRDuAT0y8OCEhm5AMQmYpUj78/0dEiaoBGMpDcArxGwJ+o0kuooUkf0m8RNMADGTamVKNmyUDtlsYwJRZpAtt/TU10u1xA5gcG/+fOPmjDppNlxW4DsbMwyS9+UJcI9dGgUFS4zkEeU++xYuBbpOd1NrBq5+rTARpAhV85Sn+VTDI19Ak2t65ESPeuwy57MZvFVh+tKBnvW0YkzHIBxvHtdD0xiVz+Z+g6vQsKvfPoY+6kxy62DeWoOkvXIPJ1PjXT2E2hqVMHW9YAELvcZDjDJVHxkiQEp8Rn2U3kYtElKSRmpgqwnvY1YmtNG02x25BmWvhHbmVxJ8MWcxqbZs5saISv7yf/eu7e3dy9zcfui9vZFKbji0IoVh2BMhmc7SBgkj5D+OrR19uyts6W/yRJ6O76oN/UKvqidXoAvWtFH8iqGPiSsk+By0nuHx3yo5RjRTK/kRiKupOM28zrfMI5cYKIxXEQa7BSHIbLlw+OnzMawS0Sj4S5ar+E1Br2RZb0tS9bfevsSTIwrUSLWGdEHDn9zdwQM/Ej6K++xK40mvdLLdcYuHNg8J1qswfnI5DT8g9FepYuuzGLOUuQ7q6bm45FfBzwhUEuY/PLWrTJqlicYSMcHYko0WjTxOsbrCTHBjGdLtntjszhxf8FEQfPs5gL8A2/Nrj5z4JLS27oe6jpSfsmB+JJDV868f+aVh5bEB5sD+278+eFF0xL3HbhqlbvlKkd4zT2rb7z7pr0r71kddlwF+ntmd3bOHv6zfev9ZrXafP/W+XsmV+p0lZP3AMWr26eua/IqObG0ZVnbttc+PTpz/qbl02d7XTOnLd80b8bA8O/Iit9CepzDX8lZR1uZgQmp3ql4zn2MiWlHkTINQnIsnoVHhB+OZGmSuTI3s5grM4izv0CtDMiHWpiA+4Ko3x0ZWTCkqLI5Nqf8chGPt9XCfRU5lSxZaJf+KESYeMmiAhAQTl2NbaMyDiMuNKAqDrL1IekP5Yc6TiWz5UaaXDJ6nkUPz/eWF0s32wzeimKw2vLEQK4qx0BjZPwPWxqkmyPjc5VZNFAdIvMYm8d5Xkj5qFrCOERcoAECpeJEY1CsBTjBSIBCyhCCLh00OCEa6YV8IvSL/b+VfutX2OwFVYqCfffvK1CMq7FJKjkWRubWApOXH/tUGvr02HK0BMynxz4YSeb+8qU33XQpugG6Tc/SpT12m6EKvJpHzpWS8GXLc7dBw/OI73bsulkIJKHsr8cRE/hz+TfqprDVjEvXqqrAblPgukqxf69u4YIqQ6ZaCnQbVFWo/J/WTU3yEsqxlz4TR4i72PevUiJgTxH9EibsUuDfq4ns1AMn/q3Cp+U6tJCzKTq+n0WEGRGf5TNQXk/Qy8nwFu4aOi7okoIuoRPkbI7MKoynK5NeSG++mTz0zqHkm9KboOJNOvEmSI66Bq+uINVJR2i9Kc2X3kwkQAW4H2CmdH3WDoLHYi+SwRqpLmoWtZhaTW2h9hBL6w+px4n/BdUJDQeoHrG89WDeOjoHvTe0jmrhP/M559x/pnU2f92YXY/gbXEMNi6QMPQZ0P+EYdCA/qe3GMowhAREus+Qyh4nCzD2ZmYpUent3BLddh2+4Fs0rU6OfEtwQDEaKFhHzvgi7zf1xahd0hgb6QWQF+n/0gA5z4BzazEF2xB5EI1/ZRE6nrbNWagyai6WzjKxPbyR5BoQ3AMwwgWY9vRlMv9woCiTRcaIkbDVTDYcGtwTD+yf2brs/t7jH3x1MnbB0lissKJ+66mLvEXECVbkRX2LTXpV/B9vmT+hMD5hXcNy6avFesFgcBV75117T/e6X6wLhC87aVEWFxeDv8P+ha7q2K7UA+v1/gKHzkKv9zYYT+lwh2W+NDZgp/TmFBsUWGaTV+d2Fs5vUCpEP/zAazKXNwdaYuI6DWsQTDivKVN3FvXgMqqGmkBtwN8hx5ujIvlF68EIGiqVqDnMpFJWM6oXOojqarb8/2oWOn7ixZcff+jNt+mP/n6zSWTrtDViyF7hrbBY7eLyE6tFU1n11uMP7K9033Tqof9RW0Fb0nDhM33g0ecVlzy7Vqp7elPlIKekCzkbL3JqhqH/3BBRcieNkH92oeK5MvDZ/6whsS0JySXEXuDDeWGj7AUW00gXLOwey4CgYioF3RDxjtJ41Cod22oiVeZlFeI+XHn6Rn4W8yl5fj2SjPhR5jSLSYlmdEyYhkECMPT1mMWEK8eyrqngeOlqxqpp0WoZsFlegdeOWYH9Y1ueGPd3X6GLjYxVy6rllVT/2JXLxbb/hDJjvCBgzkDv4AphTE4CxmfS0WmACgGH2404yYyegERZgs4ji9tFpUFpoyhyWk95pJBTmDi6AJbfHH/rruHngNtPPgB+NQEjx6RlbxzIPV7agCP5pzbcsmNHncYIFHZw8N6u6dpTI86Tviv8+XFZVoWnj3M72UFKRZWgOlSitqeNVpYOKoFI8Gn9hAMJMyBFMQESksBF1gmYuwGQ7mh3Hm0ELU0a8JV081zWYjVapVapFS0s7FzpJpdQCf71nqmo0Pwe+FelADu+q1U1gfah5uL7wdJ2EJF+IGncfs0nn2j8bsyd5IrxmDqpVKrv5mNZbOEEiRGmcsD7bg8GswMyrgd7aSphLGFVFkcqafGqBBNLaQ0OQc8zPzxFeSHrtcC4o6JEBRO8qCvNYiujsQSi0aSOoPwrgVv2+GXdeu50XISs2OaIrmOo92G7HQlaqYSzUgn0d5xJZFwTQwPDPBX0rH+h/qJUfk0cOejUP6K/vjxvBt2X59H4WqlEZ/9r6LiQyTljZKxhzBY5OWc3EUbEfOPYIZkzpggQg5ecOJUZ58x47ENiMRtJ7wAuoqXK/+HTsspaWwEHO/clZlXUIpW0tiK9iC6LtU0pCxrJpo1cwjxNFhPJb1/tggLpvV2B8pKW8faCBbVYUUe76NrcuqS3FxsL/GVN09M7M/o6tt3pKDsVQHr6+dRKajOSRNJvOW1qtJisclArCVgJ5AmMbDbJIIiRw9CogKP3Y5h8BfBZJCAr4AN5XMYAaZ25W4C8W7PYIpl9KHjqNKXSatRKJaDw6xuQuZcG8zJ9WSgD/EiPmc2fA6NjpuPGwkLpM8FrBj2zU7d8Ln2eBgsCAtonPZrGAwLTzPC6vNukvpRvDW45Talt2QcC5WmK9AVAFuG8ZONBcv7AVgwCBKabvYL0mQPI0EFA/NyMHjUX9gpASMMJSZ99YUZFmnsJuUD6sXmlTCNF5d3y3mEPk8eEPvShDBFbZpMclTPM241HNF3+XhKsnoa+JvMpiAluHICatCGJxkZ+QFNxsKsiWBpDW3rz7mmNNec3t5V7J2oFjfYeLasYAON67t49E9gyF9jgxGhvY5PDYp1dYCz2i5WzbvQ6GqrK4kUF5xkUO1ROLVC19N+S0bch/qadmFsrH9lDpv/NTGZm/N3SI2e4hGz3Ddjj8QzlNlpJyLw1MtBaFtYDJNLOsFQywCwnziQ5sRdSS9CPM8tPMuIhIi3r1EE6kM5Rz7+9sxAE8WYQFAI/9sb6gWsQH8Q/DJciJ9IE8A2PWhQrsaspD8YN8Zu9GL3AiyGR3JGwSHsjbgLoEI62QrfZS4vA7CYBwUzmDQVlthuSaxOO0Jd/c9SmoGmlSn+7JCWef2Y/MF0NzWgPrSi4BoAdT78EP05JNFM77bxptY2l4ZDOstLun7Xy4quqJ8+fEqM/vO++oTKlxmyyfXcf8ALD/e8zAaVGqSl7/37pK+n38L5XHIVCfFVHa6jFHagOqh2L/EVtW5bW9TY2lDe5e+T+xmJsZ3o3qtOE71Mn9sx1or9nnT5JSQw9vE49ay6+avzSZZOYc1TpD684KsHoGrWv7GjoDPaQ+gCkb+1iZfw8yo/jzi3Y9EJ6QIDMXdg6mgA9KUp6lPtKry4YSgQaUlSg1YDWabROo3WCG8hEPJMLh6iKUg9aMmgp2/jeIeNnv4z5RdDAMT6u2cProIxmnc2lJvTj8txZBTxBT8SIMT+wcIsTsjMJ2IQyCRPImLHFEGOHyMRASClYML20u7LTf7ELWNSeXf2h5tneUu+aGbMvcfqdIX/P4sNKv1ILIITFfvrw4h5/CO2/ZE7PGnTW7Ob4h1WAZYHNW1Fpqa/uKZ+5EDw5Ax/aHjwSZJGooYrU+zsru0unL1g4s7ynut5SWeG1QQZCABhqxKXpktRHnCOelpbFmAThswuT74/izVnSdRI0HqDw10is65QrvY5nAReZBVwWJiH94Q8EVjFtYwDUH6Q/YJMBAY1EK6epk9I3J3HMLB1PvCs9Y9srB0futYGud+UhQsajJKg/yyVq78mTeyH+xRGxSJbZSGJUO/Bsjm6YLY4SyJHvPGr0vEKOqkCQz8dwsJj8QAZ1B/oLcVoFs1K65eTeWLTvgjXPkPKOqs+OiyU00s9UqZg3yFK6LHXjyb3L74XTL1yxVq5ABDqlWxJ7T4p94XRF7MOqqu2UVOhKO74FXqI74Bpeko1tlvmTPHIsA/ouRaNJxxLSyyy2OhOX4lt6/57sWLp/086IQVOoMUR2btq/tEMOaoFxmDh1feukp+lHU9TcB/Zsn9lt5zmOt3fP3L7ngbnyQJjLO0/jXXjxeGh1G93+EREOo7dHZPmkhbvsGmpRNJl8hz6/HGQNTeXWMWHHd4RxMdHbjln05AXagyS2JHARUBAi0+WtTztF3FAszv4h7Hny7xTSbjSRcweRrDclFxPuT8NU+kM4PUnmoMqUOxbBufXpzxrnkmWDmt3ooJ/A30A5Gpz+kUahYWgprtadptbeIE92O5a6Gtd1NZsYY4lBazVqWLGubWVdQe/eXh0I6dQgSTPoKlZ+531S0qDkQR8U1Mutj24cIlMT7Vr1gHNtVeMkt8LLa2psKtfktvFCWQWulbtYLcA+wCtx3XynXZzse6zMY6A1YYmV5nhWRiRC6kV2DZc/FvVhB9MAEU6B7ZbHZmwwQZ2U4JUadVzLzpH+l/QJzemUcaNmUGUA2/p6ToLZgNWZGFlKBYlvpZsf7+mTrjCoBhklfmkmUDAHKOOiCSR00LRhxk+uE7P8RK/LOgag3WI0iDoLWrrRH+0meN3c6/dIjz2mLXTUPfCS9NhL0n/h31uZoQt/3NhUBk+lWDpe53IPddHP4D/QNaO7+2fDY13wgEP5Y9FapFVlsPc5kkGS796hr10uitLLICyKy7EW1yCK4FdiLbxyhCXzWnwUhNF5tSK+okE+Gb59Rrx4+fno0cE0wL5VmQazz38+fBk9Tr4dui0ISy+TgtATRj4flwoXTS7my+g8fMW5ng9i0UyGigzprxzxfObavNqIuUqCkQ0A5BYYWVjw9mhc+jHagDS/MtMQI99B5ah6yS9hpDn5U9III18Y3DJGG8RJvoeR9LAY6lkYYscrsuGIX3QHgZtm/cwqw9A1VXCZ5fnntI9YwCoGrKhJbddLdWwikfpp6hf0sUdSH78fiVwjfbwMLIWuE+Ct75bcfTfpv5rTce6/05h4biUU3TyL7iu6Y24gsu9J/xp6JzWhC5QWgR+BDzpPTWxgngmcmoiGtxelr4AaLLvxrrvALFD6s3RbGXiZg2RO3rcqj0NVgEOtFByFq+sE1jxVOU/pNIcznmxjC4hlwHfppDwqLTcpGK3q/C3SeqlWWr/lfKWOUZjQiNlnUSj0yzq+ulkWrhsmHH7z8IQGeePmrzqW6RUKC+jTCcwHZGwaGpAGLAqoPP/6++67/nwllA+aRMOyhTtMcB+R1n/o2TwBRzxO2Oz5IdmRutS0Y+Eyg2gS5O+fyA3eURxhOGYTdZq0ohORGXsZV46UzJWWDNJ0ZTk3GME2jhM+smdwyfHTpeRwH5as1xNpJYfcG3BxBouLMlDpvzP5QWRIXmBJ5yuBCyRi9Ccm/4fO4QiBH8sQvBeAlvfw9XB29tKK1M5zenOIDQWJ7Ak6gw82SkNkzxUvnagrIe2UxFGeY6/TrszamD/ZuBaQwykbVQ7jObbzyzHWD8iVAfx+rNV8bCCeclAR7GnNxrdgAk7iGyKcD4DIHgEYAj7MSkH2WxhBPjCatxHKYcHgQa309Ic6k1F72ztqIGgTWhPYxS7/8UfSe7fplCpB+xJY9DpPDqjUoDg/+lHOwvd8CCZqgQkdF4D6ndu0RpP2NlD80Y+Xs0ClInv516V7XtIKKiX98siYyJzfzjGC0YMM5YRsiOgSo1gfHsNhVMVul8tgMOpHMQGkbhEmCSAuCqI/lfCLCiV6l9HTEe5F9rdElkPvUsnmZgs8SMtm4ChqWz6YkYCJ9ctqMSFFoSn1nPQcWAlXoQEZc6mkDqNxe5UQpa8e2uxf7d9Zt26gboffT1+NNnbgjZ1+pkl6LoWxY/FVtfhsfFUtvh5eP7TJjy4aWIfOW+2nD/jRRWhjh3/1sHaRdf2RacZjxKvKQbF0YswIVdmkMDwilR7Gd1o1hkXhHLFb2Cg5RKw8tIxMlwvaSuTzocLBrI1eqiF0qfKZ9O58alQ0TqIS0d+xu6hCHEtdDnLg6zji25ujBqa/E0qSOO/KrFBoBpQGEE+WCEY7iAst6JU76Hv92EoqmPRJNUz4/cUgYbFICReZy5AcjJ5B4d4mZuw16XBBTIVodBMJMerCIV7JEp9DSqKbSkm7ET1SSurUA1qlkqVE3dBdk1wSui9IFAf8MKFO6kzicFnAlycLgGBOFhj1GR6Hy9Oze+V/psUBLBMtz3+Ln8LlaVkAnSOffJtIX5n/PnPjPodGdnP6nVp5HHhOIBFI+ylpI0iTM+lHx8HddKS2rg+8oTNK7xq1OiPwGqVT0CUNpgbpxKLCwiOFPYWL4MAwttaHjtT21YH/0OJLdFp8SSoOXQB9m9Ig7FuErjhSWLio70zffQGOoU3HVvJccYYBKQZkA8KYkdkuAnef+lhuCGg5KDg02uCIbt8HkBIRLC3C55GWQ+eJrBGW5ZckVw4/zm9WZgadIuDRsbKJIhYNQkxuLG+NLA/9KehDL2XAGSzZ8os9F9S5Vfep9DxnoStWhe6/pkSjscPAsOZ6HJ2PRoI+7CIZCLYu7rvswqYTf9HQShtYsqW2aqDMyMLksMbKjf8QvVmBchIfCjACI5q8QTq6cBitFiQwYCAhUbQrL4xwVJAhSCYSYHrqv05TSCN/lwQiymfDxSOm5Bz3EkapqkxjbMgfDWqGkSPFyFZiLhWsUlJsF6WkVTDiRMgj6VhOLU3gD/KbiD7fWyzFHQ6QLPZ6U65hgZ8jxq8RZZKHi/Qgce4yGUtSiRKjYEWzRLsI4tbNZy4T+KHX6y0GSYdDihdLf/z+ZSKxyLLPN2oF5yxTHN/fKz/rT/n+zxGd+668pjTitk19QpORmFxBv5JfJiJ/0v9EZepDI5LVwumBjvd6qGBWpA7EsqtRirB4I6GbuEhZDNwhC+GooJxVXsWGZoILxbRiByP9mpfWqFlGK9oc6AWIH0t3ty7GDdQO6Q5cqCUd4ILB5YvUSo4upy1ahtGbChzFup0v1IA3DUoVbWMdko2mwYt6JCHYoKCWdox7cZfgKy40GxhWq9X87ajGjGlnOJZlGQjYd0Xteq1YP07QbdAJbwDKip6vPYpdsoBmaBom1mk0ug12f6dGo1+n1m/eTzPoQgBZnk/r4/QQao/WXOTscEu+jMyCnX84RQvzh5EQHHcGHNmYseTQQ6jJO3WCqL1gMa7p4q9/9sxhpCKsUGq1Krasr3JOP6gmyWKvgh8IurvRi7xeugGfeRh1sV2ido9O+MuxP+9QFKh2qQFUsoW+3ilvC7o9WlG64oQM0gyo2tMU/QbSH5bI/OtZERNHLrZisCbrOBlyGNtb6WBIgZ1zWVsT5u5OVyPNjonhgOg3fn1U0O3Tiu3bejoLWKN+BW/QK+H63X7/jG1Of09tNFg5raq9NFRgfO5OUbtPJ9Sv7mgSOKNmhkKv09LWWMu8ssVbjWX+yaGqSF1fbLzfDhbf+q79EdwajygrKsM29Kx9KgjVcKldMXd6YY2n1Go2CF5HRWl946TSA685n8Cw149yHneZgRNMh/SAVtGCt8g6t9NeEXR4RcFkrQq0tM1Pv7Pd6J21ZGRwHeAtaabjIBXMBgjHsgJMICOHh90ZLdFixd6Z3YLufuubD94HfDqVwvxLg1J6BeNzrNt7l0WaQ2xqd9b/5w24aDT5/j6qMh5D2mDZcp1w8AnTY9JtBkHQgLUvKbW7tOLcmYIOHVgvaq/A56LV5lkCASJEogbhWafc3jQxQRpaJNvdZJGjBiNGI/VVJOtoXA1nupk51+FMHFz4EOoUJA8RuOTl76SfKRQq4Rei6m3Rryrlf6Yw/8yoUiqkX79N+tyfgUdeoqqASYJuhVacI+j6tSJsNxgMgjQvMM823wjuEQ06Y+pZUduvE+aI2hU6QXpSK8o+L1bWO+qIro47PuZ+yS9ZtjPmPp3smjyqMeLuVThzaxVYm3pBegh8SwyWvKi9L+OWzviqoeMFesUL26U4uEva+d+XjAxeQztuRmXfrBPy+JQUlAZJOwVotL0Y9QzRK1pM1tqoGHNb3eGgF+9ASpC8Q9YRadJjaC8tM2HT2dLmxkM6817c4rClhaezBgce++fhjKOTAQCbvNI7LnDX1d4J4Oi0u2egPWvd0tsEj/yte3jbURv/o9fvQ0u1EQ68huvziPs6vLhoAatSGfbb2fPAigt4204bvwRcdD5r329QqdiFa/EpN3geR2PGHFCO1GcGM5Y9lEgkUkiVlt5CG2jX8UTChXpp6ojNBvvRr04F+4msLVuWwXy9VmOTjoB+m/yr0eql+9MnYP227jTFfIjaMUxNJDhBFkzkomN4szfiCZq9Rg/6jGJICjKGA14jDkq01sQiYXM0jH6cNF0bYjwEOLSmhcMbaGpAGy0cc71w8+ZNWj48bdOumbf1lN0mTBRfKF5bozBwKu2UtW/G3bfNLLlt+mX9za87K7qa5tdMVygaAp3VbaFqp9hV4Guq6S5v49lGT3tFY8An0IknpxQevrprzYQqC3P6FBiiToOnwuAQAMWd9wAw9DX8aogvbrwgdaevzleg4aD0MKBZjcHuCYFv3GG3VcUBIL2MpgeFzlocyvMjGOWcyGEZ0mhy9rsxYhMSN7PgDZjjo0iiZ9aAuhqaGhrMYDNgtKYESOlrpBdqZFktizlRjb+bc2KZn+nZZ4aZAGOXaiCrZgFdbvXiMxQVldVzmuJ2cBiN3Ue1UzNRO4QxhZGXR5MMkDGRMmqRPJkQbYnFhFzRVoCpFnBEC2ZbAEioMOMTIyJmUgh6+TBeimGRue/HEzWYso9JfaGSfo6jHqQktrAlSVwKDmHpTD0N1muUmNxNI3y4Fcak6zm9Wqc0f/OGNDi56p9Vk6X3Jnxw9wdM/x+rDIwJeDSnnBlAJoNoYvtwXb8bEK746DxoFJRKGtAb/7Yg9ZlCUEMIt9CXr1p18OCqVfBwapXs08mvdy2utz9Xb/aM9QYjakaftR2+R73vHFY78YytkK32f41Va2koVz1m16gmUCG5agsaxj1pDDOsb9VT3RjPzX+WVzzcEkD/m9twcOwqM658iwFW4ROkTyfIhiQTQiTJxmmKbKDfvrFqnQdR/+U5VuVpLFN/fa7+I2vpP8urH2EZOcc2M6wCkmvs1oADI+o8rDVy7eTKVmXDWE0BNpy7AUifZ19J9/kOHN3rJ8574pE/c5/3m3Q0YaWIyfJlzIv5E9OY6vgDwOADaO7HgRSYN4Vtn99Y29LdWTMh9YMzVPoze13P5vEtIZsQ1Bv8gdkXGqB5RsWqKw9etO0ep1R+H4C8QmiZmdz2l9ZVkzZMic4dq86xli0Xzaw2KPj1PKPdPM9aeP2FKw89C6s2bACP8jbWoNEKDXOfSW2gRtU9RiKbc3U/+zg3onri2Zrje9T9tfz6/fIsDcGkK3/qwbFqPzSymmx4zPbIYDjG0/bVRZm3LgdijLTnsRjxz8JbCOcZx2OcZEDohYk7mMADYmhUKKPrmk2YvAzy2GxEBewOv99hDwwE7BLx3QKXPcAMxPR0yGjUB5UN8St8U4ztd8ybus1rD/gKbP3VnW7BrlTy6kKTaA91V7n1SiCKAq1TMMA8bQPxxqB7Qkc2GQP9zm2tcE1prmuu968bPwUWO+zlAPjt8PICP4Qb4vPcQpO/LFjRZBLNxTUlTU5bYEqFh7OZdBuoLL97nOSLOdKYiNmXN1Iz91vMRMuFVhzcQqCFMUkxlOmW002C26ORxlxv5I83nakhVsbA+mnS3xmFjhYEE1Dq3VXdIbtoKlTzSqVdcHdW99sKfAG7d9vUeXe0G6f4rog3KIN6ozFE05mWSP1NbgPSHo80z5+2QWeycf6SqQGbs6mkptgsmpoqgmX+JsE9L74BQn8BvNzuB6Dc7iiGU8av89ejhpviwojwGRuFkviHyqlm1BrLqF3UNdQPqMeoXxCZBUe5Y+tXGMOc+ZEgiP5FWPSXds6F02Z5I5uO/UGnYLEQWw/MpgybDRoQSUBrEfCaTejs2mgt5l7CCRc1oJbQ57ldBCk0DUTpIv0Mie180EuAKc1hTMhKYrCQxCQb5DCIhjFdDm+6HKMMc7cUGQ0GY9HT7e2p53smTQM/7gj63UquHQCdyQJaeU2p193R4fKVavhTkNY4IrVFZlPRcof5Co+NA9Ll8Tg0i6r28qukT6RPr6poU5lMqrby/TCwvxytp7TnTQ5HpilcvFc9CbjNRdVhh9nsCFcXmU90dBBo6Q5Oje4Ovs433Hx0Z41h0HDMEw5/OEFaAO6bsFO6oaSy0BAAHulLG9QXA9vaQ7XmslIf+PSukjLzk8oinUUoCTgaL290BAKFDVPawnagMavpujvC4TtqU/SPZ1U0sno921gx7/ijs8ub8HpT+Wy6EZT88pfWRdYVsd9s3d1QFAgUNZCFowlskP5WbIA2YJD+5BcclUAx3DaLvg40Xv4NjZe5/rGQWkrtoPZTt1OPEP0bIwaid80ioae2xh/G2LbGsHuM15J5eRHUOyLk5fkjXtJhmkF41IuNYSYeD9qsIUy9POciXQTDd6Ne4SI9BIRpdHcMZBwWM31P7me47/nH6KH0i0GrxWINgpnnnTfUsFp6YeUy4FqwwOkQaLBAoQmNi4LjSmO0pnzBgspxUaMSzFyIhrXQ445gR2ewsCg4fiJSQGBqYO5c+KpdN7/h6ZT96YYFWjtab3wKfkDWh+zLL12mq/IXruoCTxb6x3cECgsDHeP9hWD6wkhNSKtYCGjB4QS+/+ywgEpLZyjUebi3N/Vr8Jl0ZZmZdoE10qXVNn9z7/Pd9rroH1Irx8VijlnasMo3ft6K6f5w2D/9OFpEHA4l/Ys3xo9/Y0Jq3sebGns4s5nraVz3GV7nTSYerTM6ab30D6CfdGDFbOnbCY/MQFcHeh7pwTeZKWljLX5bGByQbnBDSznYIcdGYn7ff1EiztwHnKwZx8SaYEYRxtZec8bYAqIA74RzVF87Ap+bTaoUAHdp1Err5yV2+rdqdeoL0KNWqSyfl9mk4wIEBcF/WOgLBWlSyIM5BNAr1OsrwTKDeeg8kLrVZNRXwotd9HWVOT4EPDaJhLcOI45iOw72DJhpzoqjq2KA7AEWQLaiQYDEcOsop8pOc/HTgoJX7HhOqVQYnikW6Rhv/IlTlC5EarTJ9bTAK5TSELhV8adhxmcavOtRa4y/B9KPdDqtj56h8aaCUHJ7keIM3gHwPw1XjcaboWSccYITQQ13WwKfSSb5lPsy5twAud6MwVGdElXscbsNepMOUtAJ9XrDqq4/D+38c9dqg04P09v07vT2wklGEDcJQiCVCAgKFYgfTq65t617maKgQLGsu+3eNcM3KRmHikuyB4jfE7MtF6NPmzEDc5CPIH0e/Y+ZlRqkTH8mPShZ2ArJgnRl641gLgBgXmoGmCsJ0sNsCMyUrNIDYB74SHpYEuhm6VXpr6BVen+N9CfC/e5f0wcKMTOb9D7ze+mv0mtAJ30p/UP6OSiid0o/l74E4wgePcXuJPF0+mxpvDgil40AI292BzFZpNuoBbxfZNEf4JWQB36Rp+mBVDP9BDh1kxdcSg8M/R4mtanWmfBYMDXnt/CCqamj4AS4bru0HrZfevOl+24Bt4CFqQ4vKs9g6jBcNb/tcBt446lDT4HPpSO7QT94OfXUHDjxo1SXHT6T52MxpzHiKDSS4KBVTBeOxh9vWi6gspJjLj1TjvmLjZSmZu4zJd7Z/bz0oelar52pLPBJ759I7DpxYlcCvF5S9HBRCfl5eNOMUwdmbNo0g7lkxqaL4VWtnTvf2g70yc7W1Fa71wse/+aRR755BN54b2FpaeG96KJPc6dvyvte9AT7YmR8STibHZrJ6CBUR3IPBrdtf3T79kfho2SR4TGSe/bQ/Xhf+n/+dwnRrIB5wEU3G1aCcMw9LLSJ+o10MYz2ShEp0tsPVeDUSDSBQ9Irg/Dx1NQBUD1WDm8Pezn7QySn4wzEDsyCBCxcEOfiRNHHEsLfEfpk0OcjIknMxyLpEwcaIylMJPkChPsoEGwFSIxwAk7kCDaBH+1m8BHMCxHzsThWgq5SbI4EiwoDvu7YWt2vlrROppkbFy287H3TxIpq6V3p0/JQXHAuijW9/05rZNFchV5b4Zv76vMrQl0z46YCFyf8BcYGzZzhhH0OW1HuHpJu++aQ3qxleaj0mu1KushT53PuOAm2gdLbmwwA3ts6xWWcOdMoaBqNqzdUFF46fmFCoTgCL3N4lYqqal7lsRd6lXxRoULhHRLsF3Z0m8ZV0UaFyRPx9j1nUN50E+epo5++T7I5awuNOwOOdZqiUketsuaFbQ9NtFc6nXp1SPDPC00xtRD+T/ldKcgo2oB0XcJuHSBUxNEYSfkm6ewibh8s1WKhHkm6Ym00EEQDlR4QDkPcsFHMGcByvNzWThrtZ7AOIIwSuHpm+spBeXD2JMX8vatoGKuccP2Tpo5gxe0PVAQ6zNqQx/mrN9y+mjo1q79L6r9bw9r1VXd++7jHqd+nNJav+730j729gfIwo7D4OKDgBO3KxwF9wlZczIwDJcO8X7eVhyymlYI12tx+sWZRR/V8U/FM0GC2c6zJxPEFJtHGI4Gd5QtSNB8sYFat4jS31c1whJaKbavgryOWmLvVofHoTeOcndf81sfWmjzqHlPhQq0pYAZqUDNifAfUtHSeFI7fChMqLVOa4tE8jLXRGyGcY+DKP6Hp/CadNljTsXnj8ilT+1bOmNbUYLY8uDAeDwbZhLT9H9IlV/n81uIpX44zio6CmnA0uho6/uCMxqZOGwPDzB9JPyVCWKq8Ml+kOWw05ag1R/kSl86fG/YXqdRAkD67R11UVD2uc5fRWFbe3DKto6UefJzfpLtO1FmNhc6lIHgCeM+rry8rsf5QWjOtvMznN5l0WoYd1Sb06ThMoa6GEW4sY6vQ8CuDVrpbpdWppDu1CqUpja2HlCSDlFCpQMIgigyx/Z/KxGZQMMUm8T3T8R9Z+OBYOr8KprL3MRu0oBffHSzVMqJ4igRCM4MBA0A3lxKGNK8ToHmKTpF7ZhDgM/jvVhl4gqdwCUYUCg4Of8YSHalBmmeK5pLpe8r+3eHI8ZhRJomLMKJUcA9qih/oFPlVQA2UjWtfj9ozQDIG090KC8deDw0jaekWv2eiYcncoSDDuSoT4FlNFnZ9cPblieqFc9uaZ8wIH7n5xvXrHpq4st9TuWR515be2trp3rYD0ntFztZo1N9BT570KKDRzN22Y8dzLpfbgzbYL98/dNDp9HjafPGOcO/67b9iLmuePLk1Kqi5m9esLqUNNKPJ52Oj0ZyultmS/UbCZJRewgdTc/EflxjajEOkoJDa3Asr4f9KXQQjqS1Dn+2AN9MXD30A7yS8kgSjld1JYhYLkeQ3FekcFFUTJfMWk16y8uwmd3IZ9pEkIzZj9ZIo80Hia8NJijhKHUeEFuNwAJxkzZMvJP2B1FjAuy6r1WUBJ10Wi8s6dKqsqXFuUxMzPV45uWlu04Gm8rImMCkUhw+vTgwtTazp4jVafuLiNxdP5LUaHhzGx5vKypuYIiu+j/z/1aYyaWZ5U1M5eLisSUwtD8X/irf+Kv/GQ/A2cHPs+c2bn4/t0fKcZm9Z2V4Nx2tTN2euKm9sRPOrGrXFt4RvQk95AA9MwAeqQRf4nGCReDGdUY2VC/CoUiCARUuew+N6C90EAkhgbqEDtdhYAYLYLIEOYrmTzICBaNp8gQd/NBvEkDqOdnNWkzeEujEmrOcw/w8exXiSSGStsXAk8ZNMvTSeE2g8VQCZ3wPNHgF5pkDTKk7C0GELCRZ/kTBrxWdY8Cn4PegBJ0u85GInNEfRxIOmcnQxyYHHNyN+0CiemsItSL7H5TFbrDU8h1RNXCNGnsGCtUgU4MjQZ2oFtVhF9OqQeoIeacE3qIkCJ8SFAQTUhCawP7yODsoNge+Pm4AI4hFSQHQ3J82b8D1xAbGVi9i+AvggsXqhWsfkWTNMYGD49LkW/BCa3Ba1EG7U9I3T7exk4S1qJcOK7CJGr7IpaOl2hmFpmuc5xsgACAGk58QYJN4iMVcJVJO8Nvc8tzpYrAdqpVnQaoHOU2BhGJM6qG/kFJylwF+oUgtI1jAWWAyrBaAsLaCBp9BRBIHSyKs4Rs0bATDZjCYALEpFEGhZlc6icliqYrDM4WKVapZWakzdygp7QVQFgKGgzBjwuB0WLYQcp+a1dOH0qMVcZqGBs0grWKcrIOAUZhcDOYZlfCG2hDHdrzTQxU5FmS4UZLQcoE2q0NYrKqxqDUSP5My0FUIjtOh9oGNa6i5azSkhraJpNQ1+CJVGjlWyHKR1ZYJS/YRKQ+t4CHWMoo7V0nqlkqUhUEGGUegUwKCDMZMF8jar3x5QBBYXGpcHBKvK46yYK04xVXT5woVF98TFuK/cxqo8AKDhW6Wba3TazBFX2KPUClDDMsBD0x7T5V7bsjZreTktmFSXjuusVDNo4BOcvMJvCZgu1mkYWNsTbIus8tWPZ5HssDS2QI9EELXK4Yh6BIeg1EFLQDCYRFXdeSWNzd2Rceqgy+2mdUCntxsczIVABByqCtDTai0nzQQKI8sqVBAYVLQCv24o3SbY9AUOQ5HKw5ez4y42mVrv3lQCmcrLQsGmYkEDWmY6fRZzm0dBOwGoqQV0e4Go55k46ywxK2nFTr2SZvj6dgDqi/UVxZBWK0GRaHGCMh+j12msQGdnFVa9GkAj0CiNSh2HSkJzxYzIIKmUYfRWADQGUa9klJBlGY7mga7JrlG3FCtpvqB1XGcRd3+9sFxhMxe3FhaKgG27UONirPuU+lAJrW+sDtk6FQYFZJV8rUE/MaDgQgUd1iIgbnKZVy6wC36Xmi4z2iFUskBv+oWCpxlaxfEAGmIMEAbVRgUAHACMg2Y/gZwC6oFWyzFalqNRswHmuxc0BVaLxWjSCow4yWHgBWWRBXVj9JIKXQUANGlRt9YY1dZ5asM4v0+pYVSCx9PtNrG0Vl/G2TQWtb5TZ1RyBQrOpaO5itq2oPGntZM8SpvBUoQZvpdHO03X16771Xnbys2gyFF2tHPxlvUrG1+bV91VAqHHjxpdIWqKWL9udmzCjrYu1l3tLUDVKlCrJ3VpisNOh1qfw4FLUDrKhWTrEFVDtVBzcWSOP0B7seMcc2vRgSDjxjO0VaYIRiMJGiZcbIDHIxzw8FEWz+1ogxEDQXwVGUtaQI2TsUaHReGXLYHQEL1p51Ve/dMf7202u6TfSIfB/J6aGw9sC/gZYcXW7QeSLhCi33nj1/NK19409A80ocPpz3wzZfqejeMv62rSv08fAkpTx+Qd4wtEqKR9Uyd0NkXKnarLRuhmPnwlZ54677qp6sPwxuqW83nd9vcWLLi9t1OnBezv3rq37ctbPm8q/vyDyX+jLwLghnvEB9+0j482mSXPh48BTUG8vrswUsZZUfeikcbAwhfGwi1Mt18L1Yt1khBdBTBzbbiGkLam2YkhziktBoRvnpDdZnwWLVAmnEJ/PkzQFpPN9xi1jbEIGLONuTnYMH9qdb+zsEzQHyzvLPFV2Kvq1z3U15lY2xGYNLfp0HkWV09beEZ1WU1RTfi/H+i+cm07WP3e0d39U7uvl049u9bQk94ALN4Af6iZFa2wqW08bzDYjVNtbo8tXhlbECpuXdvdvLDJr/NZdKaSYNhVWelqqly0xz9h88Gj7/UY1j4L2Ou7p/bvljekU3gj2wYMke2bZIyUDEKJVYmnsgy0gKUYhEkgrAnNu8RNVY2VgDRGSTFAP+yLqRO1t0RSJyIRODlyOAKU0oaTZfWNpVvLysBhp58rbArC1TCye6tOlwoYTQwUNNJKne5SbYVuCLboyyCVvRb9RKSvpfUny0ovLWlsKMOMh0wpXE1HDsPmrfoyfSqgB1ADjujLdFv1+iHYqh9mfyDxxv5REY7nwP/Efl8iI4PBtEc0uzUQsH9HXB4s+k0yxJ86RBGCckg8pcRfSutyJwWyvH8sxQ4ShEbZyxOkvWbRQuJ/hpGS1MbEiJdOM5ORnGnU7JlcGRz8ES76a+XXyoA92R4aCLUn7QHl15V/LQqX1BkA1b0CJFZ0A8og9e35jz17/gMMltSVgzl7pQv1gj0gfYGpgYEhYBf04Pa90rHyupIiG0isXi0lbHQfvmCPXFYGl9VPIljTwq33DEu5zbJYZlRdT128fVE7+UPr63pgomedNEhKQ8clmROub2gdKclr0ji8pA9KBA8PDPSsWwdezpUjY/9y47j1FmwiyDC4QSRGWay+fEMPC3oNxqKqkrnNNl9To8/WPLc0VGQ0MPNHDCofgz9YJvUV25GEUlJS6AH24r5JluvGGBcqkD7xJnsa9aNubEMl5GRoEKhpAX40lOD8sKCf5CazJJzWH8Chj1iujPlJbC0bI6TzBO+GJQGsVgubXHj7Wx+/dftCeQHWMgbpHa1eJ73zhMqlekJ6R6fXSu8YGFb5xBNKljEAHzoIfE8oPcongA8dBL70QajO3QYtInq2T3rFoFJxvd9otd/0ciqVAdT0sXqj5ptvtAZ0FNTIRzUa+aj0Cjpq0H7zjSat6/2U3UUJqIdSfjyW4aGMgzKRtc9PGFgJ/2TUR0RjDIWBA26J5M18Fq17UvrtE/2/Ob382Ke7D6JJMtArXTF4B6ZT3fg8EG6tMAruuQsPfXfTJReXFuv4j1Btok8m722SHn5796fHlm/75Yv/vOwVUHjHrcD60g4OlpYWT3t1403fHQoLxboSGQeMS6Z9xuXpyD9i9HSPin8flRMSz0OdgMvzv2B05DtyhMOcTz+SofKoIYKIQbyc4Ec5vAqCd+E6PcD1sUmqnZpIMtjKkcKLR0LcDXQMdkl6QrAqQzPYCtKECI3AGMTfRzFB00mD6QA3TsDn+gL2wY7XRFGICr9iTfH2JeMS4Qu7G3X6p0yFNlGkjb9tkGExjouBWvE4PeW4WBsQjw/apQmpxE+A6ifwvNrAsS2vi7WiKD7PGkpddgyk5ggGtbpXzQYhYvrrhgFcsYB8oXwb6Y+QuuInP0Ef+OnTFOB3MF3UlSTWjpN1N2u4GCIJACLljuUCaEakkUxgNRGCCC+H6xiikWJFUGaQpIJnRvzrpGtiLQxBXSAqFu4rSI8xEeQUbK0D2K6HdBEkg0CrH+kt/A7rcVvpNI2x2BjHcsK1NUgRUZQFTlO2uMnk7Klvs9Eqm6gHPMMI3o1dh9efbytQedf0X9vE0Yy+DAgaC8saFKZavaEoWl5SqIWcoFSxUMdzBU1awWiO/MfMiMmBZHokx3NGnULwlLX4m6oYJIlDzqQCrmANR38T/8AVWVZcWmJuRoXYcx6rDzgLGNak0Zjnjq9SANbmHV+uL+BYkWZK2zpsNlXJdQOAu9ZgYTkRyZgMrTbXrC4sappfXcgCha+hv7ukXavxKKFFVNsh0LDGYndD7YKAusVTVayEjL18YUv/pSo9TQP0H7J6pcyN+yD3NTuZUpFRr4qaQ62kdqEvMqsHYx5csoqUTmsGHxM1qz8EfEh/wx9jLOrzI10XjYw4L1VAm1gJdOLAMOz4Rp8uUSihE6QBNqNIp5QVST/ZR3YFsUIrq+Xwh9i9Os1sETpnbFIotboi3ujUOU9U/tfa1TOqql5ftXYx0gwHpNOH/iL9SaccAODQX4AfBCYd/LmUkj6Q/vut3VcnHgALJrVVMpxOz3FX/y5UWQlZnUpTv6hz0+wCUVFuRQUzzW+1lTGs3dYE5swLB5U1Ubui0NfS8tC8wnGa4sJtXw55Juh1drdnvMtxu9bBsmptsY5V9y7v83meWXz+IkfRiaa+myborJ8ekhfXdV6/p7+lY8tTazYCJvHAlZPiN+g0qBvAxubWjVqdGvWohpVwce+2OvR0VIbWPi16uq2U1U7vS2102IUax8wnOsdHBK64roqzT873B26glJSI+dEJnyvSrzFrvQnymITYBww8GiyNFkZgLjr2/HPHDvzS4/2ldHvqpRP3AR8TOfFS6nHgu8/T2zvvm4MHv2GbJceQdMHSt4HtJ2D871Jl0odvLwVHh8DfnL+TfpLGRqbYyziKWo3tLTQWUTmKJwgZaDzWQexKAOjziuJ1Fq+zxSAaCbFI22d0SLVBQxS2h+jwp8zhVfYy1/ze/qW905sMxvXS0ddEu108DsqX+yb2zl8yd5Z7wwv7NrQWROy8patz8cy58Upuwq4lc5vDbgvLaBSOrrpaXSDcfVGTj+VMgoJHepGuKjp/8eWdMNg8bc7sKY1Go7WGs03u2bLpOvDjnk3NLlrnLFCp3pe+BfZAAXjrpE5QaCsm7ZxVZfJOm1KxZwDQkDYW1U3aOKHQKJY2trZW6w2XdXOm8ZPWrb+2s6C757z5syZE9Xp2oZ23tkYaiqF12q6ZzU4BfT/0jVfx1sZQAFYj0cWM5Je/sxSJwjaR3CQiZQE53h2Y3Ub85zdnGIuYv2+cUS8NpT6fsZH53amyzN/GGfS0GRuBo33OFumfQLtlTjuYcJo6DSain2s6OmZv2ZInaxYgaak6nVszJm2n5QyJUUwiTdyZIZWUiTsfOFuSFLxuDP7OY2dLlhrmx06XdTj7aD75qHDGsmKOTlzAHPUoZu4cOGthB9NFBK2YCVRmIJVOn7W0o2R42U6aKyY4V9YSFbCbjHLKltGE80/PktyVxPFLmnTKlSYw9KvvkUvFo2+/OJfDLpwBUT+dsl52Nlz9dGY6cJ0VXj/N93g+ksvNVBRnURKRDEtkMSueXakwFkytZDSiZdCtGCHTxN4F0W1240wpkT69vEF67Sd3SF/f/vqDxssOAf6ZnW9tho6G05TWUGL8XCqx+ek+qNDNjbb39nf6wX3SSgP4dYnxfXD+S4//+XagvOMEKGvZE/3LFc9I3+5+174hwXvBu24brTbYw6297eMv4KW/JBJeqX6Ybt1I0H+iwQC2LETTUobsG8VWTWwXNWPTJTZkQtmDhO3H6N8orx7zgcFfu884rqOl3tDTwbNV5YUV5dYipZq2qjVVjvoJ0XtLDKLG3FFtVKPRwujzmcuay2d79mzp3z/ca0fvmVPVZqLFsticaj40pXbhNLMrZJvRtsJ0udcfVyBJ6qZCXuGDtBUWCaUxbejaQ+Gldo3ZPHXgahAGgWEeKJDlm+jEGh5kZJUuGwzUCiL565lUKCQCFSEZkjVnVoxRKn2QSTwrnfzZgE54m+ZUSq31o8xS0KGdYIvebpW2pBcnAUP2wuTPpJPPCjq4tB1wKkPCqug6P7v2HdZQT2xiTXjr0vMzK1KBFph+iqNyc/nX3jTqszmd6iNXJjuoZT1q3zMnG48acUycSxTpPjkPpO+cGdqjzid3OlvGNvX/ASpLC2oAAHjaY2BkYGBgYWBoiitKj+e3+crAzc4AAufmZoXD6P///zOwN7CBuBwMTCAKADeFC1wAAAB42mNgZGBgY/h3l4GBveE/ELA3MABFkAFjHwCpXQd9AHjahVSxTgMxDPVdLhcJONGFhS4VYmBoF6CI8X4Ato5IiA9ASIiBThFfxkexl+eefXHSVj3pyRfHTuxnOy7SB+GrV0TV74BAu3BAa2QNVBGgAcS+d5CrQfKe9a/+tvKFfVuzx/8Mz7qY7wHf0L+rTas+NNhDd+llDd9btdkH9muGs3u2c7Ie707nsO7Ea5zGpjH3h2OQWBpK0uYbct3a29jNfltThTiXwNeh3Pagl3OWjJD0nY8jd2vLjc95n/iiFtl50eQxcNnbnNuCB3M/uWh4SDUZ87ZSY/Vpf+4sR5oLZWfWWcyHEOlC8vZF7SciP6GvxKYpewk4z+KONA3KexFjI7WsI/W18Ka6pogV/zfZ3MUhhrJngsnfG06D4cynuUr1iSlGnLUAOtTgiYHYFsC41juamOZ+nMVZwXtMtS65D2mvl/nrsF6Ib40Ya+VE5CvzBLzB3zNgX7USC+w7nU/8O8jrffUWXs509lVX5X36oHrc6xjH5svU7t79QDpqvM4R0aMn6dlIVwzot2gV5j0DptyDpq96H3fzkHuf5Q12DOFT51ntTttinrx5h2A/F8l1mIW42dg3FbYXe2ZlnFXttfL7B4LlZboAAAB42mNgYBAjA8oxBDBMYrjC6MRYwLiOiYHJhlmFuYnFg+Ucyy9WG9ZlrH/YQtiOsKex/+EI4ZjE8YDTg3MF5z+uIK4JXLe4dbhn8bjwVPCc4jXjjeFdwufCt4JfjN+Hf5lAhECXwCNBLcFVQi5C24QrRCxEpoh8EPUSXSLmJ3ZA3Eg8TXyT+DcJFYkAiRmSApJ9kj+kEqQmSF2TZpNWkg6TLpFeIv1KRkrGR6ZMZonMB1kV2TrZA3Jack3yTPIZ8nvk/ymYKeQprFB4pKimWKZ4TPGPkoRSgdIeZTXlGcqPVCxUDqgKqKapKajtUfujnqDeo75HQ0ujSWOZxhtNJc0IzTVaPFpOWsu0+XTydJ7oVumJ6FnpTdL7oO+g36H/zMDFYI1hneEjoxyjB8ZCxkHGZ0ykTOxM9ph8M7UwnWPGYBZmtsZcyXyTRYDFA8say1NWYlZJVhts99nx2eXYTbN7YB9kf8Mhx2GWwxXHTU4SThVOj5wZnF2c17lEubxzneZW5HbF3cV9k4ecxzpPN89Fngc873n+8ZLy8vFq8JrntcdbxDvGe52Pl88JXy3ffb4//DL83vi7+Z8IkAjICtgX6BK4KfBdkFFQVtCJYKOQM2EcYZPCfoX7hFeEH4kQiIiJWBHxJdIhMitySuS+yBdRBlF1UXuiWaLNoidFv4sJiymJ2RFrFlsR+wgE4wLi9sXLxNclJCTcS2xKPJSkldSTdCuZJTki+VjKtJR3qQapKann0szS0tI+pDukb0p/leGXsSeTJ9MqsypzV+afLJusnKxJ2TzZLtnLsl/l6OTMyrmQq5Ybkbsg91IeU55D3rS8W/k6+Xn5OwqYCnwKrhTqFK4rYivKKNpWHFV8rYSpJKLkSqlf6bkyv7Iz5V7lFypCKlOqYqoWVf2p9qmeVH2vxqomr2ZBrUvtmzq1urK6BXWn6r7Uy9WX1c9rUGn61DypRaSlquVIq1RrXOu8NpG2lLYV7ULtNzpWdTzprOrc0MXWFdO1outZt0P3th6JnipMCAC81yjfAHjaY2BkYGCcxiTJIMIAAkxAzAiEDAwOYD4DABaYAQwAeNp1kM9OwkAQxr8V/EOMngzx2BjjwQO21RM3RFETBIIEvRak0ih/0lYUH8MH8ODBB/GkN48+gc/h1+lWwGg2u/Obmd1vZgfACp6RgkpnANjcMStk6cU8h1Uca05hA03NaWzhXvM8NvGkeYFvXzUvUv1T8xLW1YPmDNbUo+ZlbKsXzW/Iqg/N7zDVF85wgjIMVDFEB31SCQPakFSGh7ZEA54GLOS4k3wBd4wG9Hq0de4r3OIGDnzeqaKCBhUK2EeeXoOxQ1ygRq6L95eK8UunSc9n1pPbBvbYgcltk22Shd1/lGpU6FAj7tzn6YqWwZsDObuSKdIbYsyMx7pd+Xf0pk1Kqrq0/tQb92dCIeMOLhntSb/XjDmMhqLX4j8mKn3aUE804B9KohqpzHZ+QIWR1Dki9ak+lr5C9pnHDldS35l5l5NK56TWVIeWTOxU91ChHdFGk4xyJhUtcp5ztCYz/QZramjSAAAAeNptVwWU5MYRnV/DtHBmZqa93Vs485mZmWRBz0g3klonWDJTwBzHcZiZmZmZHGZmcJgTp7qlWXjJvt3uqlJDdfWvX70lKumfx5dLV5X+zw8eUU2JSmVQ6f7SPaW7S/eVHkQZFVRRQx0NNNFCGx10MYLR0r2lh0oPYAzj2IDtsD12wI7YCTtjF+yK3bA79sCe2At7Yx/si/2wPw7AgTgIB+MQHIrDcDiOwJE4ChPYiElMYROmMYNZzGEzjsYxOBbH4XicgBOxBSfhZJyCU3EaTscZOBNn4Wycg3NxHs7HBbgQF+FiXIJLcRkuxxW4ElfhalyDa3EdDFwPExZsOBDooQ8XHrZiAB8BQkhE2FYaKT1W6iJGghQZ5rGARSxhGTfgRtyEm3ELbsVtuB134E7chSfgiXgSnoy7cQ/uxX24Hw/gQTwFD+GpeBhPwyN4Op6BZ+JZeDaeg+fieXg+XoAX4kV4MV6Cl+JleDlegVfiVXg1XoPX4nV4Pd6AN+JNeDPegrfibXg73oF34l14N96D9+J9eD8+gA/iQ/gwPoKP4mP4OD6BT+JT+DQ+g8/ic/g8voAv4lF8CV/GV/BVfA1fxzfwTXwL38Z38F18D9/HD/BD/Ag/xk/wU/wMP8cv8Ev8Cr/Gb/BbPIbf4ff4A/6IP+HP+Av+ir/h7/gH/ol/4d/4Dx6nEoGIylShKtWoTg1qUova1KEujdAojdE4baDtaHvagXaknUr70s60C+1Ku9HutAftSXvR3rQP7Uv70f50AB1IB9HBdAgdSofR4XQEHUlH0QRtpEmaok00TTM0S3O0mY6mY+hYOo6OpxPoRNpCJ9HJdAqdSqfR6XQGnUln0dl0Dp1L59H5dAFdSBfRxXQJXUqX0eV0BV1JV9HVdA1dS9eRQdeTSVbpUbLJIUE96pNLHm2lAfkUUEiSItpGMSWUUkbztECLtETLdAPdSDfRzXQL3Uq30e10B91Jd5UermehNzGxZUL1kxMTw35j0U8W/VTRbyr66aKfKfrZop8r+s1FvyXvJ0/L+2ndn8r7VPu+mSTVIEs8u5YIM7bdhgjnhS8jUXVZTytJasYt1RgiiNKlSpaIuNLz/KCRuoZvxn1BqVtXspekJAe1WARyXtSXpQwML2zoXmZpWfZ6tcTrh6ZftmW/msZm4lZcGYgGryYM008rqReISixNp+PIhdBnQZkbQ6WWRaqreqElF9uRby4ZthfbvuA9I2Gm9Vj0YpG4DeWKXtCX9qDS881+iw/jRK4MRdKal34WCIP9aRei2qBZyFlU2xbb0hF1y9R9OTX7Ff5LKpaUg4ZqAjMeVKPYC9OabQYiNis9Gab83XdqXmr6nt1OxWJquMLru2lLywuek7ot/tYPDV/00k4u2iJMRdzOlVgN7+by1ixJvd5SRZ2l7YUOj8vnFbIeO9IzbaGiZsx7jpD1yLPTLBa1SIS257cCMzKUryKumY5akCPMfgrHS6uJa8aiaruCI6QurJukIjIs0x4smLHT7ZkcwqHWGAoVFfRqZDIIGBgyqvdkrOwdPXyo6JUKpSq2Cjvt8D7zscxP3h0q+gjNyM8SQwGjFXhhIbZzEGm5Lge6727LBIeE5ymt6YU9mU9L7FiIMHFl2i2m5aho8sRcallmOBTNOJYL2o92LmovGrmcRcV3jQgdIoUjdifxloXRy3y/U8hJYPr+mFi0fTMwV9yq9L0ew06YPc6RWDTEEgONb6OpBNuXiehwVEIv7OvhVY5nKBq26YvQMeNabIaODOq2DAK+41pg9kORtobxyqKVOCr/GO7pghBpl48eRWpJmxO202MUijjfrF0oyoXRwvF5Eace7zhe6K6MvWWGr+k3GfGG7apF0gUvZVzmgVcgU7DXWidHvMGbx7I8EEsVzuakUbicdFM3C6yEfVWBGy005a7Sm5pIXNPvtTW75JxSV+syRXR9LxwwOPNQ1qMscflYXc4eETNtGOqzphAvrPHmkbvU7nu8g5XjIGcHtU3VZxxwcFW+tzXE841Ghsmbqy09IN+sOHBjeNZavnItCxWHtBlinDQqwE45TpKy63BSMBo4eGHFEr7ftlVYexzYVLRcvsYC3VpUaKtrKYtyiwrIeI5IYxWRG9ZZ9AKj60xZtH6SWoY5XFqithBzzrvV1EwGSY0ZlQ/TtGJP9GwzES2F3DxPqv1YZlFFxbLKGMmcmiVMZoiynaV8lRFHxYw0fryokpjzoqXiY1gM1AEjTsaMJ8p8kj4zRuwNROrygn23mTEvxbysYB8sX1QZvJ7NNJ/ZgyZfI/vD6TuyIumwj/Wl7PNpVjigvcZQ5TsUSy2OuUj1SRu5yEmaCzqJc1HHivOGKTxMKomMGWrc5HmiJU6eYWXTRWWItQr7LRkwfca/wyXJknzH7QLOamRnCG1dUZjjU8ZrKphbG4ztmO/eZEZkzmv5ygmDYWE1mBf4nvtiRIfYGFawTq7mSK2rUmoETpvnpq5MOPiikWReqm6soUCldqzZXKiE4AojmZVVpdTlRB3ByjyfT9Bv8ORI1Z2mGfDuZmiLWiCcgZe2e8ol3mWrYNcF1wE3p6neRE+MOzKzFJRCFXGNv3WWHH/rTIy/dbo6V2t1fnvNxMZwRmt1aN0RyYDLRs03I9VpoKSdQFrqXDobOwW+Nd5a2zKZFkvnYn7PfNow5MPkY6tc/f2lVkEFHJixtRSoaWgNDSq9JRYjlYX57fIFRvm4ahKwI9Uep1ZYDoRb7zPXRabTYJrTuGiot4QaOaIFTS2MZqfBMebqZfoV9WJoaod4mD+6wncFATGZ5MVC52/FZhZrqimqXA4U2TAqK8bk7Ob2msrSTjLOSE5fL2JYZ1Yu8bC5qU6ULS+r2HnCFlxA1YIqjCOroqEfXq4nfGdkWGhyb8ZViTIYTYyhzEtcjmjMZCdU4Vm0HSaootokw0fLhnWWgqDWmhRBrdU1Qblp4E9X7CSZqjE2mTJbOasWIGZm4uq4HePdixIvWVOQxldsw6JVMaYmppr66afWr7GR/R1ZfTnocp1TvjY2fMFJr2CYCxqx+Xf9jNC0rlPCmNo42cpLvq4InPac1qqy5QBZRQpDV42eLYssLvetqJwlTtkL4/LWaKkcZ1Z5EC+UrdRWz2TRXMnZMc1DlgJG5JoWZ6QxNbl5w4o1ZTq1slQkO/6vSR2rOzRrDh5fp2luMqamNqlmurPE1TSzioMUSmWRr7m5OHx6rIxRwaw7DBZ+VDOl80tvSF78xmK9H5tBrcdv2kFcNh2mjo2zG0csL7UyFfriGpgJ/bidd9o06kveaLVKddfoWbT2q8LV2Bo9T/EFfubKhaTOaRpLz6lyYmSL7KZnqdqSDJYiLmoyi5NtGd8YPwcYKrLWY1r2RUU1qoCnXlROMnW1MzN19c+NNy/KVtan+UF1QXiW5H8cQv7lAbOTI/rsxvDwyrZph9ylYc3185qjPs2MODJd80HZ5jrz/BTnV6n2iS1zE928smmDIZVpUjVTqlF3NTetmhnVzKpmTjWb/wsmC9pGAAAAAAFSd7nXAAA=') 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{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{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{display:inline}.fa-glass::before{content:\"\\f000\"}.fa-music::before{content:\"\\f001\"}.fa-search::before{content:\"\\f002\"}.fa-envelope-alt::before{content:\"\\f003\"}.fa-heart::before{content:\"\\f004\"}.fa-star::before{content:\"\\f005\"}.fa-star-empty::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-ok::before{content:\"\\f00c\"}.fa-remove::before{content:\"\\f00d\"}.fa-zoom-in::before{content:\"\\f00e\"}.fa-zoom-out::before{content:\"\\f010\"}.fa-off::before,.fa-power-off:before{content:\"\\f011\"}.fa-signal::before{content:\"\\f012\"}.fa-cog::before,.fa-gear:before{content:\"\\f013\"}.fa-trash::before{content:\"\\f014\"}.fa-home::before{content:\"\\f015\"}.fa-file-alt::before{content:\"\\f016\"}.fa-time::before{content:\"\\f017\"}.fa-times::before{content:\"\\f00d\";}.fa-road::before{content:\"\\f018\"}.fa-download-alt::before{content:\"\\f019\"}.fa-download::before{content:\"\\f01a\"}.fa-upload::before{content:\"\\f01b\"}.fa-inbox::before{content:\"\\f01c\"}.fa-play-circle::before{content:\"\\f01d\"}.fa-repeat::before,.fa-rotate-right: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-indent-left::before{content:\"\\f03b\"}.fa-indent-right::before{content:\"\\f03c\"}.fa-facetime-video::before{content:\"\\f03d\"}.fa-picture::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{content:\"\\f044\"}.fa-share::before{content:\"\\f045\"}.fa-check::before{content:\"\\f046\"}.fa-move::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-sign::before{content:\"\\f055\"}.fa-minus-sign::before{content:\"\\f056\"}.fa-remove-sign::before{content:\"\\f057\"}.fa-ok-sign::before{content:\"\\f058\"}.fa-question-sign::before{content:\"\\f059\"}.fa-info-sign::before{content:\"\\f05a\"}.fa-screenshot::before{content:\"\\f05b\"}.fa-remove-circle::before{content:\"\\f05c\"}.fa-ok-circle::before{content:\"\\f05d\"}.fa-ban-circle::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-alt::before{content:\"\\f064\"}.fa-resize-full::before{content:\"\\f065\"}.fa-resize-small::before{content:\"\\f066\"}.fa-plus::before{content:\"\\f067\"}.fa-minus::before{content:\"\\f068\"}.fa-asterisk::before{content:\"\\f069\"}.fa-exclamation-sign::before{content:\"\\f06a\"}.fa-gift::before{content:\"\\f06b\"}.fa-leaf::before{content:\"\\f06c\"}.fa-fire::before{content:\"\\f06d\"}.fa-eye-open::before{content:\"\\f06e\"}.fa-eye-close::before{content:\"\\f070\"}.fa-warning-sign::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-close::before{content:\"\\f07b\"}.fa-folder-open::before{content:\"\\f07c\"}.fa-resize-vertical::before{content:\"\\f07d\"}.fa-resize-horizontal::before{content:\"\\f07e\"}.fa-bar-chart::before{content:\"\\f080\"}.fa-twitter-sign::before{content:\"\\f081\"}.fa-facebook-sign::before{content:\"\\f082\"}.fa-camera-retro::before{content:\"\\f083\"}.fa-key::before{content:\"\\f084\"}.fa-cogs::before,.fa-gears:before{content:\"\\f085\"}.fa-comments::before{content:\"\\f086\"}.fa-thumbs-up-alt::before{content:\"\\f087\"}.fa-thumbs-down-alt::before{content:\"\\f088\"}.fa-star-half::before{content:\"\\f089\"}.fa-heart-empty::before{content:\"\\f08a\"}.fa-signout::before{content:\"\\f08b\"}.fa-linkedin-sign::before{content:\"\\f08c\"}.fa-pushpin::before{content:\"\\f08d\"}.fa-external-link::before{content:\"\\f08e\"}.fa-signin::before{content:\"\\f090\"}.fa-trophy::before{content:\"\\f091\"}.fa-github-sign::before{content:\"\\f092\"}.fa-upload-alt::before{content:\"\\f093\"}.fa-lemon::before{content:\"\\f094\"}.fa-phone::before{content:\"\\f095\"}.fa-check-empty::before,.fa-unchecked:before{content:\"\\f096\"}.fa-bookmark-empty::before{content:\"\\f097\"}.fa-phone-sign::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::before{content:\"\\f0a0\"}.fa-bullhorn::before{content:\"\\f0a1\"}.fa-bell::before{content:\"\\f0a2\"}.fa-certificate::before{content:\"\\f0a3\"}.fa-hand-right::before{content:\"\\f0a4\"}.fa-hand-left::before{content:\"\\f0a5\"}.fa-hand-up::before{content:\"\\f0a6\"}.fa-hand-down::before{content:\"\\f0a7\"}.fa-circle-arrow-left::before{content:\"\\f0a8\"}.fa-circle-arrow-right::before{content:\"\\f0a9\"}.fa-circle-arrow-up::before{content:\"\\f0aa\"}.fa-circle-arrow-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-fullscreen::before{content:\"\\f0b2\"}.fa-group::before{content:\"\\f0c0\"}.fa-link::before{content:\"\\f0c1\"}.fa-cloud::before{content:\"\\f0c2\"}.fa-beaker::before{content:\"\\f0c3\"}.fa-cut::before{content:\"\\f0c4\"}.fa-copy::before{content:\"\\f0c5\"}.fa-paper-clip::before,.fa-paperclip:before{content:\"\\f0c6\"}.fa-save::before{content:\"\\f0c7\"}.fa-sign-blank::before{content:\"\\f0c8\"}.fa-reorder::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-sign::before{content:\"\\f0d3\"}.fa-google-plus-sign::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-sort::before{content:\"\\f0dc\"}.fa-sort-down::before{content:\"\\f0dd\"}.fa-sort-up::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{content:\"\\f0e3\"}.fa-dashboard::before{content:\"\\f0e4\"}.fa-comment-alt::before{content:\"\\f0e5\"}.fa-comments-alt::before{content:\"\\f0e6\"}.fa-bolt::before{content:\"\\f0e7\"}.fa-sitemap::before{content:\"\\f0e8\"}.fa-umbrella::before{content:\"\\f0e9\"}.fa-paste::before{content:\"\\f0ea\"}.fa-lightbulb::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-alt::before{content:\"\\f0f3\"}.fa-coffee::before{content:\"\\f0f4\"}.fa-food::before{content:\"\\f0f5\"}.fa-file-text-alt::before{content:\"\\f0f6\"}.fa-building::before{content:\"\\f0f7\"}.fa-hospital::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-sign::before{content:\"\\f0fd\"}.fa-plus-sign-alt::before{content:\"\\f0fe\"}.fa-double-angle-left::before{content:\"\\f100\"}.fa-double-angle-right::before{content:\"\\f101\"}.fa-double-angle-up::before{content:\"\\f102\"}.fa-double-angle-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{content:\"\\f10b\"}.fa-circle-blank::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-close-alt::before{content:\"\\f114\"}.fa-folder-open-alt::before{content:\"\\f115\"}.fa-expand-alt::before{content:\"\\f116\"}.fa-collapse-alt::before{content:\"\\f117\"}.fa-smile::before{content:\"\\f118\"}.fa-frown::before{content:\"\\f119\"}.fa-meh::before{content:\"\\f11a\"}.fa-gamepad::before{content:\"\\f11b\"}.fa-keyboard::before{content:\"\\f11c\"}.fa-flag-alt::before{content:\"\\f11d\"}.fa-flag-checkered::before{content:\"\\f11e\"}.fa-terminal::before{content:\"\\f120\"}.fa-code::before{content:\"\\f121\"}.fa-mail-reply-all::before,.fa-reply-all::before{content:\"\\f122\"}.fa-star-half-empty::before,.fa-star-half-full:before{content:\"\\f123\"}.fa-location-arrow::before{content:\"\\f124\"}.fa-crop::before{content:\"\\f125\"}.fa-code-fork::before{content:\"\\f126\"}.fa-unlink::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-off::before{content:\"\\f131\"}.fa-shield::before{content:\"\\f132\"}.fa-calendar-empty::before{content:\"\\f133\"}.fa-fire-extinguisher::before{content:\"\\f134\"}.fa-rocket::before{content:\"\\f135\"}.fa-maxcdn::before{content:\"\\f136\"}.fa-chevron-sign-left::before{content:\"\\f137\"}.fa-chevron-sign-right::before{content:\"\\f138\"}.fa-chevron-sign-up::before{content:\"\\f139\"}.fa-chevron-sign-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-horizontal::before{content:\"\\f141\"}.fa-ellipsis-vertical::before{content:\"\\f142\"}.fa-rss-sign::before{content:\"\\f143\"}.fa-play-sign::before{content:\"\\f144\"}.fa-ticket::before{content:\"\\f145\"}.fa-minus-sign-alt::before{content:\"\\f146\"}.fa-check-minus::before{content:\"\\f147\"}.fa-level-up::before{content:\"\\f148\"}.fa-level-down::before{content:\"\\f149\"}.fa-check-sign::before{content:\"\\f14a\"}.fa-edit-sign::before{content:\"\\f14b\"}.fa-external-link-sign::before{content:\"\\f14c\"}.fa-share-sign::before{content:\"\\f14d\"}.fa-compass::before{content:\"\\f14e\"}.fa-collapse::before{content:\"\\f150\"}.fa-collapse-top::before{content:\"\\f151\"}.fa-expand:before{content:\"\\f065\"}.fa-compress:before{content:\"\\f066\"}.fa-eur::before,.fa-euro:before{content:\"\\f153\"}.fa-gbp::before{content:\"\\f154\"}.fa-dollar:before,.fa-usd::before{content:\"\\f155\"}.fa-inr::before,.fa-rupee:before{content:\"\\f156\"}.fa-jpy::before,.fa-yen:before{content:\"\\f157\"}.fa-cny::before,.fa-renminbi:before{content:\"\\f158\"}.fa-krw::before,.fa-won:before{content:\"\\f159\"}.fa-bitcoin:before,.fa-btc::before{content:\"\\f15a\"}.fa-file::before{content:\"\\f15b\"}.fa-file-text::before{content:\"\\f15c\"}.fa-sort-by-alphabet::before{content:\"\\f15d\"}.fa-sort-by-alphabet-alt::before{content:\"\\f15e\"}.fa-sort-by-attributes::before{content:\"\\f160\"}.fa-sort-by-attributes-alt::before{content:\"\\f161\"}.fa-sort-by-order::before{content:\"\\f162\"}.fa-sort-by-order-alt::before{content:\"\\f163\"}.fa-thumbs-up::before{content:\"\\f164\"}.fa-thumbs-down::before{content:\"\\f165\"}.fa-youtube-sign::before{content:\"\\f166\"}.fa-youtube::before{content:\"\\f167\"}.fa-xing::before{content:\"\\f168\"}.fa-xing-sign::before{content:\"\\f169\"}.fa-youtube-play::before{content:\"\\f16a\"}.fa-dropbox::before{content:\"\\f16b\"}.fa-stackexchange::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-sign::before{content:\"\\f172\"}.fa-tumblr::before{content:\"\\f173\"}.fa-tumblr-sign::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::before{content:\"\\f185\"}.fa-moon::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-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 { box-shadow: 0 1px 2px rgba(0, 0, 0, .15); border: 1px solid; display: block; padding: 0; } .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; } /* 4chan style fixes */ .opContainer, .op { display: block !important; overflow: visible !important; } .reply > .file > .fileText { margin: 0 20px; } [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.centered-links #shortcuts { width: 300px; text-align: right; } :root.centered-links #header-bar { text-align: center; } :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; 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::after { content: \"]\"; } .brackets-wrap::before { 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: -1.25em; } /* ``::-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; } /* Announcement Hiding */ :root.hide-announcement #globalMessage { display: none; } a.hide-announcement { float: left; } /* 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; } .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: 2px; } .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, .postingMode ~ #qr select[data-name=thread], #file-n-submit:not(.has-file) #qr-filerm { display: none; } #qr select, #dump-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; } .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; } .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; } /* 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: 2px; 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; } #menu { position: fixed; outline: none; } .entry { border-bottom: 1px solid rgba(0,0,0,.25); cursor: pointer; display: block; outline: none; padding: 3px 7px; position: relative; text-decoration: none; white-space: nowrap; min-width: 70px; } .left>.entry.has-submenu { padding-right: 17px !important; } .entry:last-child { border-bottom: 0; } .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; } .focused > .submenu { display: block; } .imp-exp-result { position: absolute; text-align: center; margin: auto; right: 0px; left: 0px; width: 200px; } .export, .import { cursor: pointer; text-decoration: none !important; } /* Custom Board Titles */ .boardTitle[contenteditable=\"true\"], .boardSubtitle[contenteditable=\"true\"] { cursor: text !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,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAASJJREFUOE9jYAAC7ln7/pODQXrBmq333PvPu/YaSRikB6QXbACpmmHqsRoAMll7+20UQ0H8tmuv/pdffPFfZtNNuByGASBFIPDh5x+4IV6HHoDFYGDJgw+YBoBMBUkgA5BtIKduuvvy//svX+FSB+88wTTAc+/t/83bj/0HScLA5BPXwc7lKJ36f+L6XXDxhUfOYxrAPWUnWKFp9UQUm3iWQxSDXAEDSX3zcIcB96wD/x+8eA1XDNKMHAYg20GW4Y0FkCIYAAUqzEBQOIBciRzlWKMxZelOlMCEcVxq+jHSC1YDJPs3YBgA8jey0/F6ARRwsFAHORukmat9NdbUijMpg/wKcrJodDFOzSBXwA3Alh9AToZFI7a8Asu98BxJbnYGAJb5vYLDANzSAAAAAElFTkSuQmCC') 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/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAydJREFUOE9Nk1tIk2EYx79NyUNqTk0o6KYrnZeChodLDxfeZpCbJk4RXU5Nm7tYRYhiYXbQlaeGutyW2gxtpB1RIyKDEjKwA6Ti2dR5KNDn+fq/S6TBj/f93r3P732e53s/qfnkSdej4GB2SBLbwf+jmB+gUMgOheLg/z7EdCUnO6Ref392SpK8Hyh3I+gBwBo7lUp2xcbyQEoKD6alyQOpqd754/h4FjJXZCRJTl9ftmEzoK5/wdQJxPgkLY2WV1dpc2uLtnZ2eHNnhza3t2nd46GhjAzuValY6jx0iIfS03msoIDuQ9COQCtoUSjohU5HuwgaN5loeXycd3d3aW9vzwvW2K5SkdTi58fvzGb+3tdHFggA3QONEAzn59PvjQ1yqNX0zenkvX0B4ffWaGRraChJd/385JGqKvlzTw/fRqOaIGkEd1DjU52O/3g83BkTw5MOh7yJuUCUM2o0yi2hoSw1IIOhykr+YLNRHYKu4XQvyKA/N5c8yMCCDD7Z7bz26xcJ1rH2rKKCG0UJdRAMlJbyG6uVrkJQjWAB5tSbk0Nr2HwDgvcQiIYur6zQyvo6ucvLueHIEZKuQPBQr+dXra1kRuqXEOwFArtWSytra1QdFUVjNhvPLS3R3OIiLUDUD0F1WBhJJtwDW2Ehu5uaqBICI4IFlRB0QLCEzaboaHrd0cHzCBYsIIuesjK+LAQXkEFrXh676uupGCWcR6AeghLQptGQONUAwfOuLp6Zn6eZuTmaXVig7pISrhI90ENgQbdHhoep32JhFzLpu3WLio8epUYIfs7OUjF6UKJW88XERLqYkEBNej11oG8XhCAvMFAuOn5cNiclsTkhQTbhmpri4lgbEMANWi1DwC/xit3t7bK7rY0Fo4OD3G4wyEURESzloAdnceezlErK8vH5N4KzPj50PTOTfkxP0+THj/RlYoInJyZI8HVqim5qNFwQHk7SucBAPo2PKRMNPLM/4pnFszYkhJsNBu6uqWFHba1sr61lQSveQFZQkFx07BhJmhMnrLn4NLMPH/aSExR0QDbmWhwgyEapwDvXoDxdWBiXnjrV/Bdm2kYUxLwmEgAAAABJRU5ErkJggg==') 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,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAtZJREFUOE+NU91LWmEc7sJtQew/2MUY7INg7CLY3W5GMHazyzEQo9UmfYxZTbAiVlgRqLMSZ+XnDC3z2+Y0+8JGakKZTtR0Tl2wtgtLLQh29cz3ZZ3h3Q68vOc95zzP73l+z+/U1f292O09DRxubxOH23P//1bvtQts3dPnry7LZnXJhcUl5Avf8dHtwY+fv2AyW5DOfIXFakMm+w0G4wISyRRm55TQG0y/Wzv6mikJ52Xf9TmVBoFAAD6fDwqFAqFQCJubmzCbzZiensbp6SmkUikikQi0Wi0kEgm6ewVaStDCfXPDandifn6egoaGhrCzswO1Wg2Hw4HBwUGk02kIBAL4/X4IhUJMTk6ii8dfYggy2RwymQzOz88Rj8dRLpexv7+PSqWCYDCIQqGAra0tJBIJrK2t0XdVAjNDEIl+wfj4OEqlEq2wt7dHrchkMmrBYDCAz+fTIjweD7FYrJbgIJOlgLOzM8jlcip1eXmZ2rFarVAqlRCLxcjlchCJRFRljYJYPAG32418Pg+n04lsNouVlRUcHh7C4/FQIOlHNBqlezgcJgQWxkIgGMbExASVNjY2hvX1dVo9mUzS5wREFLhcLrqTcw2B//M2RkdHodPp4PV6oVKpqH+SCom3v7+fNnF4eJiJusbCJ6+PviSyScakiaR5RIHRaKQpmEwmbAdCeD8zB6vdhebHT8SMhcUlC83bbrdTJRsbG3RwiCVCRNJJpDIoVeNNJJJQzKryV+rrmxiCtyNCCmaz2VhdXQWXy6XDpNfrodFoYLXZUTw+pk222Z3lW3ca26rgSwzBwqIZAwMDlITMAVEwNTVFR5fEJpK8Qyp1AJvDVbrTeLenCmxgfiZ22+urCtWHyu7uLp2wVCpFKx0dHaFYLOLk5KT6Y9kgk89kb95ubK0BX7A8a+1qannRLeW0daj/rU51S3tn9dypfvDw0QiLxbpX/Z7FVK7e/AEj4Wf24/2f5AAAAABJRU5ErkJggg==') center left no-repeat!important; padding-left: 18px; } .linkify.gist { background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAk1JREFUeNqUkzuIE1EUhv955MnsbB6r4kYQLUQQFncV3SnCIqJsoWGDYOGjsIiCtY2Kla1sjLBIsFFcXJC1kaSwENQmXUQSRSUSjCQSTCbkbR4z47lXEgtBNwcu3DNzvvO8R8jlcj7LshKmaWqYQERRTAmCcEru9/sJr9er0QF92BJMAVGr1TQ6CeZAc7lcGAwGkyQAxpTLZU0eDoc8crfbRTgcRjAYRCQSYSmi1WpxY7fbjU6ng1gshmaziXg8zhnGIpVKWbquW9ls1mLZsaMoiqWq6lgnBxY55He/328Vi0XOMFZmqVMD4fF4QBAajcY48khY9JE4HA4enTGMFVkaTHmy+ZzD/5NSqYSNB484w1h55ODO3TVu4FXcWDywl24Cmp0e1WBhyuWELAtIf/qKUrWOONmev3Lpt4NRCXq1gplpBS/v3cDc0nGg9h1o1ZkfwO4Atu1B8cM7HLt8k37V/y5B2b4bJxf2Y+7oEbyJrkMvUjki0YYJ03LidfQxAt4dOHdCw5RdGZcgGobBlQtnV/BDr1GfDai7ZiHZZRi9PoY/e5SCCTUwC9gk1GmMh5YWOcNYkR4Sv1y9uAJbYB82N57h4OnDmN7phjQ0qUkWRJuB+TMaPn/5iFfvv+Ha7eucYey4iWw8q6tRJJNJ3Fp7ClUawEkViBTfkCR0YUNTVHD/4Tpm/P4/U2CeKpUKfD4fJDIMhUKEhP45St50XedZyLQY6Xw+v8AUemVb2oNqtYpCocCWKi2TLLfb7ReZTGZ+kmUi7i2VvfxLgAEAZChMriPcl+IAAAAASUVORK5CYII=') 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,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAklJREFUOE9jYEAAASBTCorZkcSRmTjVCDLziCwG4hfM3EIvGNm44oC6WNEM4WXi5FsEkmfhFX3BxMmfAJSHW9Qr55Px3aZp3X/btq3/hQydPzKysMcCFbBBDeFj4uBdqBJR/gskb1W34j+PmulLoJwbzBJJoMm7dNO7/ntMP/XfpW/v//SKvk+7tl7fvXfTpx5pCdWVSiHFv1wnHQbLi9sE/Wdk5SwBauaCGQB3gUPb5v+7Lr/8/+fvr/9fv/z+f+Pyr/9bV735l9Wy/79Dx/b/Nk0bsLoAHgbeAVHv/v77/f8f0IB7N7+cu3DuecK54z9+7lzz639e9pK/7HwSWMMA5BJwCJeXtOm/fvVj1fcfv369f//92cN7X6ZcPvf9x6Htv//vXP3r/+T245UEYgpskPTNq08LgN749/PH7/93rv/6f/rw7//nj//4f+bU0zQcUQwWBkdVbGz62y+fv3wHeeXrlz//H9798//qpY//M3KqfzGxc8djiWKwZnBUuWQ2/fr46fv/P39+///x/ff/d69//z97+s7fyMb5/+y7d2GLYriDZikFF/1qXXXj/4Pbv/8/f/jn/5MH316/eP6jVlBAaIt6VO1/jxmn/zv27P7Pp2HxEajLD90ra9Sj6/979O37X73w0n+vqOL/0lJyMVBFq0EGgDSD0oKAlu1/oHg4ugGzVCKqfouYuL1Xj676Iajr8AnJFricGqYc3Bw+Zi6BVUxsXLHAdL6QiYMPFNrwpIxHDsUhgtAMAopKDjQn4pPDF7P45QC4hSmc1eX8WgAAAABJRU5ErkJggg==') 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: -webkit-flex; -webkit-flex-direction: row; background: rgba(0,0,0,0.7); } .gal-viewport { display: -webkit-flex; -webkit-align-items: stretch; -webkit-flex-direction: row; -webkit-flex: 1 1 auto; } .gal-thumbnails { -webkit-flex: 0 0 150px; overflow-y: auto; display: -webkit-flex; -webkit-flex-direction: column; -webkit-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 { -webkit-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 { -webkit-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%; -webkit-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; -webkit-flex: 1 0 auto; display: -webkit-flex; -webkit-align-items: flex-start; -webkit-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, :root.yotsuba #notifications { font-size: 9pt; color: #B86; } :root.yotsuba #header-bar a { color: #800000; } /* 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); } /* Menu */ :root.yotsuba #menu { color: #800000; } :root.yotsuba .entry { border-bottom: 1px solid #D9BFB7; 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,\"); }\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, :root.yotsuba-b #notifications { font-size: 9pt; color: #89A; } :root.yotsuba #board-list a, :root.yotsuba #shortcuts a { color: #34345C; } /* 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); } /* Menu */ :root.yotsuba-b #menu { color: #000; } :root.yotsuba-b .entry { border-bottom: 1px solid #B7C5D9; 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,\"); }\n/* General */ :root.futaba .dialog { background-color: #F0E0D6; border-color: #D9BFB7; } :root.futaba .field:focus { border-color: #EA8; } /* Header */ :root.futaba #header-bar, :root.futaba #notifications { font-size: 11pt; color: #B86; } :root.futaba #header-bar a, :root.futaba #notifications a { color: #800000; } /* 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); } /* Menu */ :root.futaba #menu { color: #800000; } :root.futaba .entry { border-bottom: 1px solid #D9BFB7; 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, :root.burichan #header-bar #notifications { font-size: 11pt; color: #89A; } :root.burichan #header-bar a, :root.burichan #header-bar #notifications a { color: #34345C; } /* 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); } /* Menu */ :root.burichan #menu { color: #000000; } :root.burichan .entry { border-bottom: 1px solid #B7C5D9; 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, :root.tomorrow #notifications { font-size: 9pt; color: #C5C8C6; } :root.tomorrow #header-bar a, :root.tomorrow #notifications a { color: #81A2BE; } /* 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); } /* Menu */ :root.tomorrow #menu { color: #C5C8C6; } :root.tomorrow .entry { border-bottom: 1px solid #111; 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,\"); }\n/* General */ :root.photon .dialog { background-color: #DDD; border-color: #CCC; } :root.photon .field:focus { border-color: #EA8; } /* Header */ :root.photon #header-bar, :root.photon #notifications { font-size: 9pt; color: #333; } :root.photon #header-bar a, :root.photon #notifications a { color: #FF6600; } /* 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); } /* Menu */ :root.photon #menu { color: #333; } :root.photon .entry { border-bottom: 1px solid #CCC; 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,\"); }" }; Main.init(); diff --git a/package.json b/package.json index 06acd9670..71d43c9ff 100755 --- a/package.json +++ b/package.json @@ -13,11 +13,15 @@ "*://sys.4chan.org/*", "*://a.4cdn.org/*", "*://i.4cdn.org/*" - ], "files": { "metajs": "4chan-X.meta.js", "userjs": "4chan-X.user.js" + }, + "min": { + "chrome": "31", + "firefox": "26", + "greasemonkey": "1.13" } }, "devDependencies": { diff --git a/src/Archive/Redirect.coffee b/src/Archive/Redirect.coffee index 8cad73a91..54344f789 100755 --- a/src/Archive/Redirect.coffee +++ b/src/Archive/Redirect.coffee @@ -1,106 +1,119 @@ Redirect = - data: - thread: {} - post: {} - file: {} init: -> + o = + thread: {} + post: {} + file: {} + + {archives} = Redirect + for boardID, data of Conf['selectedArchives'] - for type, id of data - if archive = Redirect.archives[id] - boards = archive[type] or archive['boards'] - continue unless boards.contains boardID - Redirect.data[type][boardID] = archive - for name, archive of Redirect.archives + for type, id of data when (archive = archives[id]) and boardID in (archive[type] or archive['boards']) + o[type][boardID] = archive.data + + for name, archive of archives for boardID in archive.boards - unless boardID of Redirect.data.thread - Redirect.data.thread[boardID] = archive - unless boardID of Redirect.data.post or archive.software isnt 'foolfuuka' - Redirect.data.post[boardID] = archive - unless boardID of Redirect.data.file or !archive.files.contains boardID - Redirect.data.file[boardID] = archive - return + {data} = archive + unless boardID of o.thread + o.thread[boardID] = data + unless boardID of o.post or archive.software isnt 'foolfuuka' + o.post[boardID] = data + unless boardID of o.file or boardID not in archive.files + o.file[boardID] = data + + Redirect.data = o archives: "Foolz": - domain: "archive.foolz.us" - http: false - https: true - software: "foolfuuka" boards: ["a", "co", "gd", "jp", "m", "sp", "tg", "tv", "v", "vg", "vp", "vr", "wsg"] files: ["a", "gd", "jp", "m", "tg", "vg", "vp", "vr", "wsg"] + data: + domain: "archive.foolz.us" + http: false + https: true + software: "foolfuuka" "NSFW Foolz": - domain: "nsfw.foolz.us" - http: false - https: true - software: "foolfuuka" boards: ["u"] files: ["u"] + data: + domain: "nsfw.foolz.us" + http: false + https: true + software: "foolfuuka" "The Dark Cave": - domain: "archive.thedarkcave.org" - http: true - https: true - software: "foolfuuka" boards: ["c", "int", "out", "po"] files: ["c", "po"] + data: + domain: "archive.thedarkcave.org" + http: true + https: true + software: "foolfuuka" "4plebs": - domain: "archive.4plebs.org" - http: true - https: true - software: "foolfuuka" boards: ["hr", "pol", "s4s", "tg", "tv", "x"] files: ["hr", "pol", "s4s", "tg", "tv", "x"] + data: + domain: "archive.4plebs.org" + http: true + https: true + software: "foolfuuka" "Nyafuu": - domain: "archive.nyafuu.org" - http: true - https: true - software: "foolfuuka" boards: ["c", "w", "wg"] files: ["c", "w", "wg"] + data: + domain: "archive.nyafuu.org" + http: true + https: true + software: "foolfuuka" "Install Gentoo": - domain: "archive.installgentoo.net" - http: false - https: true - software: "fuuka" boards: ["diy", "g", "sci"] files: [] + data: + domain: "archive.installgentoo.net" + http: false + https: true + software: "fuuka" "Rebecca Black Tech": - domain: "rbt.asia" - http: true - https: true - software: "fuuka" boards: ["cgl", "g", "mu", "w"] files: ["cgl", "g", "mu", "w"] + data: + domain: "rbt.asia" + http: true + https: true + software: "fuuka" "Heinessen": - domain: "archive.heinessen.com" - http: true - software: "fuuka" boards: ["an", "fit", "k", "mlp", "r9k", "toy"] files: ["an", "fit", "k", "r9k", "toy"] + data: + domain: "archive.heinessen.com" + http: true + software: "fuuka" "warosu": - domain: "fuuka.warosu.org" - http: true - https: true - software: "fuuka" boards: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"] files: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"] + data: + domain: "fuuka.warosu.org" + http: true + https: true + software: "fuuka" "Foolz Beta": - domain: "beta.foolz.us" - http: true - https: true - withCredentials: true - software: "foolfuuka" - boards: ["a", "co", "gd", "jp", "m", "s4s", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"], - files: ["a", "gd", "jp", "m", "s4s", "tg", "u", "vg", "vp", "vr", "wsg"] + boards: ["a", "co", "d", "gd", "h", "jp", "m", "mlp", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"], + files: ["a", "d", "gd", "h", "jp", "m", "tg", "u", "vg", "vp", "vr", "wsg"] + data: + domain: "beta.foolz.us" + http: true + https: true + withCredentials: true + software: "foolfuuka" to: (dest, data) -> archive = (if dest is 'search' then Redirect.data.thread else Redirect.data[dest])[data.boardID] diff --git a/src/Filtering/Filter.coffee b/src/Filtering/Filter.coffee index bab221ec4..934a5fa5f 100755 --- a/src/Filtering/Filter.coffee +++ b/src/Filtering/Filter.coffee @@ -21,10 +21,10 @@ Filter = # and it's not specifically applicable to the current board. # Defaults to global. boards = filter.match(/boards:([^;]+)/)?[1].toLowerCase() or 'global' - if boards isnt 'global' and not (boards.split ',').contains g.BOARD.ID + if boards isnt 'global' and g.BOARD.ID not in boards.split ',' continue - if ['uniqueID', 'MD5'].contains key + if key in ['uniqueID', 'MD5'] # MD5 filter will use strings instead of regular expressions. regexp = regexp[1] else @@ -113,13 +113,8 @@ Filter = # Highlight $.addClass @nodes.root, result.class - if !@isReply and result.top and g.VIEW is 'index' - # Put the highlighted OPs' thread on top of the board page... - thisThread = @nodes.root.parentNode - # ...before the first non highlighted thread. - if firstThread = $ 'div[class="postContainer opContainer"]' - unless firstThread is @nodes.root - $.before firstThread.parentNode, [thisThread, thisThread.nextElementSibling] + if !@isReply and result.top + @thread.isOnTop = true name: (post) -> if 'name' of post.info @@ -223,7 +218,7 @@ Filter = {type} = @dataset # Convert value -> regexp, unless type is MD5 value = Filter[type] Filter.menu.post - re = if ['uniqueID', 'MD5'].contains type then value else value.replace /// + re = if type in ['uniqueID', 'MD5'] then value else value.replace /// / | \\ | \^ @@ -248,7 +243,7 @@ Filter = else "\\#{c}" - re = if ['uniqueID', 'MD5'].contains type + re = if type in ['uniqueID', 'MD5'] "/#{re}/" else "/^#{re}$/" diff --git a/src/Filtering/PostHiding.coffee b/src/Filtering/PostHiding.coffee index 9d3f409c8..d6499e054 100755 --- a/src/Filtering/PostHiding.coffee +++ b/src/Filtering/PostHiding.coffee @@ -51,7 +51,15 @@ PostHiding = return false PostHiding.menu.post = post true - subEntries: [{el: apply}, {el: thisPost}, {el: replies}, {el: makeStub}] + subEntries: [ + el: apply + , + el: thisPost + , + el: replies + , + el: makeStub + ] # Show div = $.el 'div', @@ -85,7 +93,13 @@ PostHiding = thisPost.firstChild.checked = post.isHidden replies.firstChild.checked = if data?.hideRecursively? then data.hideRecursively else Conf['Recursive Hiding'] true - subEntries: [{el: apply}, {el: thisPost}, {el: replies}] + subEntries: [ + el: apply + , + el: thisPost + , + el: replies + ] $.event 'AddMenuEntry', type: 'post' @@ -136,10 +150,13 @@ PostHiding = return makeButton: (post, type) -> + span = $.el 'span', + className: "brackets-wrap" + textContent: "\u00A0#{if type is 'hide' then '-' else '+'}\u00A0" a = $.el 'a', className: "#{type}-reply-button" - innerHTML: " #{if type is 'hide' then '-' else '+'} " href: 'javascript:;' + $.add a, span $.on a, 'click', PostHiding.toggle a @@ -186,10 +203,9 @@ PostHiding = $.add a, $.tn " #{postInfo}" post.nodes.stub = $.el 'div', className: 'stub' - $.add post.nodes.stub, if Conf['Menu'] - [a, $.tn(' '), button = Menu.makeButton post] - else - a + $.add post.nodes.stub, a + if Conf['Menu'] + $.add post.nodes.stub, Menu.makeButton() $.prepend post.nodes.root, post.nodes.stub show: (post, showRecursively=Conf['Recursive Hiding']) -> diff --git a/src/Filtering/Recursive.coffee b/src/Filtering/Recursive.coffee index 71a27713b..da5496ceb 100755 --- a/src/Filtering/Recursive.coffee +++ b/src/Filtering/Recursive.coffee @@ -33,6 +33,6 @@ Recursive = apply: (recursive, post, args...) -> {fullID} = post for ID, post of g.posts - if post.quotes.contains fullID + if fullID in post.quotes recursive post, args... return diff --git a/src/Filtering/ThreadHiding.coffee b/src/Filtering/ThreadHiding.coffee index 56dfb8c76..a38accaec 100755 --- a/src/Filtering/ThreadHiding.coffee +++ b/src/Filtering/ThreadHiding.coffee @@ -4,6 +4,7 @@ ThreadHiding = @db = new DataBoard 'hiddenThreads' @syncCatalog() + $.on d, 'IndexBuild', @onIndexBuild Thread.callbacks.push name: 'Thread Hiding' cb: @node @@ -14,6 +15,17 @@ ThreadHiding = return unless Conf['Thread Hiding Buttons'] $.prepend @OP.nodes.root, ThreadHiding.makeButton @, 'hide' + onIndexBuild: ({detail: nodes}) -> + for root, i in nodes by 2 + thread = Get.threadFromRoot root + continue unless thread.isHidden + unless thread.stub + nodes[i + 1].hidden = true + else unless root.contains thread.stub + # When we come back to a page, the stub is already there. + ThreadHiding.makeStub thread, root + return + syncCatalog: -> # Sync hidden threads from the catalog into the index. hiddenThreads = ThreadHiding.db.get @@ -139,6 +151,23 @@ ThreadHiding = a.dataset.fullID = thread.fullID $.on a, 'click', ThreadHiding.toggle a + makeStub: (thread, root) -> + numReplies = $$('.thread > .replyContainer', root).length + numReplies += +summary.textContent.match /\d+/ if summary = $ '.summary', root + opInfo = if Conf['Anonymize'] + 'Anonymous' + else + $('.nameBlock', thread.OP.nodes.info).textContent + + a = ThreadHiding.makeButton thread, 'show' + $.add a, $.tn " #{opInfo} (#{if numReplies is 1 then '1 reply' else "#{numReplies} replies"})" + thread.stub = $.el 'div', + className: 'stub' + if Conf['Menu'] + $.add thread.stub, [a, Menu.makeButton()] + else + $.add thread.stub, a + $.prepend root, thread.stub saveHiddenState: (thread, makeStub) -> hiddenThreadsOnCatalog = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {} @@ -165,37 +194,13 @@ ThreadHiding = ThreadHiding.saveHiddenState thread hide: (thread, makeStub=Conf['Stubs']) -> - {OP} = thread - threadRoot = OP.nodes.root.parentNode + return if thread.isHidden + threadRoot = thread.OP.nodes.root.parentNode thread.isHidden = true - unless makeStub - threadRoot.hidden = threadRoot.nextElementSibling.hidden = true #
- return + return threadRoot.hidden = threadRoot.nextElementSibling.hidden = true unless makeStub #
- numReplies = ( - if span = $ '.summary', threadRoot - +span.textContent.match /\d+/ - else - 0 - ) + - $$('.opContainer ~ .replyContainer', threadRoot).length - numReplies = if numReplies is 1 then '1 reply' else "#{numReplies or 'No'} replies" - opInfo = - if Conf['Anonymize'] - 'Anonymous' - else - $('.nameBlock', OP.nodes.info).textContent - - a = ThreadHiding.makeButton thread, 'show' - $.add a, $.tn " #{opInfo} (#{numReplies})" - thread.stub = $.el 'div', - className: 'stub' - $.add thread.stub, if Conf['Menu'] - [a, $.tn(' '), Menu.makeButton()] - else - a - $.prepend threadRoot, thread.stub + ThreadHiding.makeStub thread, threadRoot show: (thread) -> if thread.stub diff --git a/src/General/Build.coffee b/src/General/Build.coffee index 0837db529..cd03aecf5 100755 --- a/src/General/Build.coffee +++ b/src/General/Build.coffee @@ -1,4 +1,6 @@ Build = + staticPath: '//s.4cdn.org/image/' + gifIcon: if window.devicePixelRatio >= 2 then '@2x.gif' else '.gif' spoilerRange: {} shortFilename: (filename, isReply) -> # FILENAME SHORTENING SCIENCE: @@ -9,6 +11,9 @@ Build = "#{filename[...threshold - 5]}(...).#{filename[-3..]}" else filename + thumbRotate: do -> + n = 0 + -> n = (n + 1) % 3 postFromObject: (data, boardID) -> o = # id @@ -43,7 +48,7 @@ Build = width: data.w MD5: data.md5 size: data.fsize - turl: "//t.4cdn.org/#{boardID}/thumb/#{data.tim}s.jpg" + turl: "//#{Build.thumbRotate()}.t.4cdn.org/#{boardID}/thumb/#{data.tim}s.jpg" theight: data.tn_h twidth: data.tn_w isSpoiler: !!data.spoiler @@ -62,8 +67,12 @@ Build = file } = o isOP = postID is threadID + {staticPath, gifIcon} = Build - staticPath = '//s.4cdn.org/image/' + tripcode = if tripcode + " #{tripcode}" + else + '' if email emailStart = '' @@ -72,7 +81,29 @@ Build = emailStart = '' emailEnd = '' - subject = " #{subject or ''} " + switch capcode + when 'admin', 'admin_highlight' + capcodeClass = " capcodeAdmin" + capcodeStart = " ## Admin" + capcodeIcon = " " + when 'mod' + capcodeClass = " capcodeMod" + capcodeStart = " ## Mod" + capcodeIcon = " " + when 'developer' + capcodeClass = " capcodeDeveloper" + capcodeStart = " ## Developer" + capcodeIcon = " " + else + capcodeClass = '' + capcodeStart = '' + capcodeIcon = '' userID = if !capcode and uniqueID @@ -81,33 +112,6 @@ Build = else '' - switch capcode - when 'admin', 'admin_highlight' - capcodeClass = " capcodeAdmin" - capcodeStart = " ## Admin" - capcode = " " - when 'mod' - capcodeClass = " capcodeMod" - capcodeStart = " ## Mod" - capcode = " " - when 'developer' - capcodeClass = " capcodeDeveloper" - capcodeStart = " ## Developer" - capcode = " " - else - capcodeClass = '' - capcodeStart = '' - capcode = '' - flag = unless flagCode '' else if boardID is 'pol' @@ -117,21 +121,15 @@ Build = if file?.isDeleted fileHTML = if isOP - "
" + - "File deleted." + + "
" + + "File deleted." + "
" else "
" + - "File deleted." + + "File deleted." + "
" else if file - ext = file.name[-3..] - if !file.twidth and !file.theight and ext is 'gif' # wtf ? - file.twidth = file.width - file.theight = file.height - - fileSize = $.bytesToString file.size - + fileSize = $.bytesToString file.size fileThumb = file.turl if file.isSpoiler fileSize = "Spoiler Image, #{fileSize}" @@ -150,20 +148,17 @@ Build = "#{fileSize}" + "
" - # Ha ha, filenames! # html -> text, translate WebKit's %22s into "s a = $.el 'a', innerHTML: file.name filename = a.textContent.replace /%22/g, '"' - # shorten filename, get html a.textContent = Build.shortFilename filename shortFilename = a.innerHTML - # get html a.textContent = filename filename = a.innerHTML.replace /'/g, ''' - fileDims = if ext is 'pdf' then 'PDF' else "#{file.width}x#{file.height}" + fileDims = if file.name[-3..] is 'pdf' then 'PDF' else "#{file.width}x#{file.height}" fileInfo = "
File: #{file.timestamp}" + "-(#{fileSize}, #{fileDims}#{ if file.isSpoiler @@ -176,20 +171,22 @@ Build = else fileHTML = '' - tripcode = if tripcode - " #{tripcode}" - else - '' - sticky = if isSticky - " Sticky" + " Sticky" else '' closed = if isClosed - " Closed" + " Closed" else '' + if isOP and g.VIEW is '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 #{if isOP then 'op' else 'reply'}Container" @@ -201,3 +198,34 @@ Build = quote.href = "/#{boardID}/res/#{href}" # Fix pathnames container + + summary: (boardID, threadID, posts, files) -> + text = [] + text.push "#{posts} post#{if posts > 1 then 's' else ''}" + text.push "and #{files} image repl#{if files > 1 then 'ies' else 'y'}" if files + text.push 'omitted.' + $.el 'a', + className: 'summary' + textContent: text.join ' ' + href: "/#{boardID}/res/#{threadID}" + thread: (board, data) -> + Build.spoilerRange[board] = data.custom_spoiler + + if (OP = board.posts[data.no]) and root = OP.nodes.root.parentNode + $.rmAll root + else + root = $.el 'div', + className: 'thread' + id: "t#{data.no}" + + nodes = [if OP then OP.nodes.root else Build.postFromObject data, board.ID] + if data.omitted_posts or !Conf['Show Replies'] and data.replies + [posts, files] = if Conf['Show Replies'] + [data.omitted_posts, data.omitted_images] + else + # XXX data.images is not accurate. + [data.replies, data.omitted_images + data.last_replies.filter((data) -> !!data.ext).length] + nodes.push Build.summary board.ID, data.no, posts, files + + $.add root, nodes + root diff --git a/src/General/Cheats.coffee b/src/General/Cheats.coffee new file mode 100644 index 000000000..6b2926359 --- /dev/null +++ b/src/General/Cheats.coffee @@ -0,0 +1,10 @@ +# I am bad at JavaScript and if you reuse this, so are you. +Array::indexOf = (val) -> + i = @length + while i-- + return i if @[i] is val + return i + +# Update CoffeeScript's reference to [].indexOf +# Reserved keywords are ignored in embedded javascript. +`__indexOf = [].indexOf` diff --git a/src/General/Config.coffee b/src/General/Config.coffee index 0a8190afd..17176831a 100755 --- a/src/General/Config.coffee +++ b/src/General/Config.coffee @@ -41,10 +41,6 @@ Config = true 'Reformat the file information.' ] - 'Comment Expansion': [ - true - 'Add buttons to expand long comments.' - ] 'Thread Expansion': [ true 'Add buttons to expand threads.' @@ -89,10 +85,6 @@ Config = false 'Indicate spoilers if Remove Spoilers is enabled, or make the text appear hovered if Remove Spoiler is disabled.' ] - 'Infinite Scrolling': [ - false - 'Add new posts to the board index upon reaching the bottom of the board.' - ] 'Linkification': 'Linkify': [ @@ -497,21 +489,34 @@ http://iqdb.org/?url=%TURL #//archive.foolz.us/%board/search/image/%MD5/;text:View same on foolz /%board/ #//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 - 'Bottom Header': false - 'Centered links': false - 'Header catalog links': false - 'Bottom Board List': true - 'Shortcut Icons': false - 'Custom Board Navigation': true + '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': false + 'Custom Board Navigation': true boardnav: """ [ toggle-all ] @@ -649,6 +654,10 @@ vp-replace 'Shift+c' 'Open the catalog of the current board' ] + 'Search form': [ + 'Ctrl+Alt+s' + 'Focus the search field on the board index.' + ] # Thread Navigation 'Next thread': [ 'Shift+Down' diff --git a/src/General/Get.coffee b/src/General/Get.coffee index caf1005e9..e8144d849 100755 --- a/src/General/Get.coffee +++ b/src/General/Get.coffee @@ -45,7 +45,7 @@ Get = # if it did quote this post, # get all their backlinks. for ID, quoterPost of g.posts - if quoterPost.quotes.contains post.fullID + if post.fullID in quoterPost.quotes for quoterPost in [quoterPost].concat quoterPost.clones quotelinks.push.apply quotelinks, quoterPost.nodes.quotelinks # Second: @@ -98,7 +98,7 @@ Get = return {status} = req - unless [200, 304].contains status + unless status in [200, 304] # The thread can die by the time we check a quote. if url = Redirect.to 'post', {boardID, postID} $.cache url, @@ -170,8 +170,8 @@ Get = threadID = +data.thread_num o = # id - postID: "#{postID}" - threadID: "#{threadID}" + postID: postID + threadID: threadID boardID: boardID # info name: data.name_processed @@ -207,8 +207,7 @@ Get = new Board boardID thread = g.threads["#{boardID}.#{threadID}"] or new Thread threadID, board - post = new Post Build.post(o, true), thread, board, - isArchived: true + post = new Post Build.post(o, true), thread, board, {isArchived: true} Main.callbackNodes Post, [post] Get.insert post, root, context parseMarkup: (text) -> diff --git a/src/General/Header.coffee b/src/General/Header.coffee index f584b409b..a6eec0c65 100755 --- a/src/General/Header.coffee +++ b/src/General/Header.coffee @@ -10,8 +10,10 @@ Header = 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' + innerHTML: ' Bottom header' linkJustifyToggler = $.el 'label', innerHTML: " Centered links" customNavToggler = $.el 'label', @@ -24,34 +26,39 @@ Header = textContent: 'Edit custom board navigation' href: 'javascript:;' - @barFixedToggler = barFixedToggler.firstElementChild - @barPositionToggler = barPositionToggler.firstElementChild - @linkJustifyToggler = linkJustifyToggler.firstElementChild - @headerToggler = headerToggler.firstElementChild - @footerToggler = footerToggler.firstElementChild - @shortcutToggler = shortcutToggler.firstElementChild - @customNavToggler = customNavToggler.firstElementChild + @barFixedToggler = barFixedToggler.firstElementChild + @scrollHeaderToggler = scrollHeaderToggler.firstElementChild + @barPositionToggler = barPositionToggler.firstElementChild + @linkJustifyToggler = linkJustifyToggler.firstElementChild + @headerToggler = headerToggler.firstElementChild + @footerToggler = footerToggler.firstElementChild + @shortcutToggler = shortcutToggler.firstElementChild + @customNavToggler = customNavToggler.firstElementChild - $.on menuButton, 'click', @menuToggle - $.on @barFixedToggler, 'change', @toggleBarFixed - $.on @barPositionToggler, 'change', @toggleBarPosition - $.on @linkJustifyToggler, 'change', @toggleLinkJustify - $.on @headerToggler, 'change', @toggleBarVisibility - $.on @footerToggler, 'change', @toggleFooterVisibility - $.on @shortcutToggler, 'change', @toggleShortcutIcons - $.on @customNavToggler, 'change', @toggleCustomNav - $.on editCustomNav, 'click', @editCustomNav + $.on menuButton, 'click', @menuToggle + $.on @headerToggler, 'change', @toggleBarVisibility + $.on @barFixedToggler, 'change', @toggleBarFixed + $.on @barPositionToggler, 'change', @toggleBarPosition + $.on @scrollHeaderToggler, 'change', @toggleHideBarOnScroll + $.on @linkJustifyToggler, 'change', @toggleLinkJustify + $.on @headerToggler, 'change', @toggleBarVisibility + $.on @footerToggler, 'change', @toggleFooterVisibility + $.on @shortcutToggler, 'change', @toggleShortcutIcons + $.on @customNavToggler, 'change', @toggleCustomNav + $.on editCustomNav, 'click', @editCustomNav - @setBarFixed Conf['Fixed Header'] - @setBarVisibility Conf['Header auto-hide'] - @setLinkJustify Conf['Centered links'] - @setShortcutIcons Conf['Shortcut Icons'] + @setBarFixed Conf['Fixed Header'] + @setHideBarOnScroll Conf['Header auto-hide on scroll'] + @setBarVisibility Conf['Header auto-hide'] + @setLinkJustify Conf['Centered links'] + @setShortcutIcons Conf['Shortcut Icons'] - $.sync 'Fixed Header', Header.setBarFixed - $.sync 'Bottom Header', Header.setBarPosition - $.sync 'Shortcut Icons', Header.setShortcutIcons - $.sync 'Header auto-hide', Header.setBarVisibility - $.sync 'Centered links', Header.setLinkJustify + $.sync 'Fixed Header', @setBarFixed + $.sync 'Header auto-hide on scroll', @setHideBarOnScroll + $.sync 'Bottom Header', @setBarPosition + $.sync 'Shortcut Icons', @setShortcutIcons + $.sync 'Header auto-hide', @setBarVisibility + $.sync 'Centered links', @setLinkJustify @addShortcut menuButton @@ -61,14 +68,23 @@ Header = textContent: 'Header' order: 107 subEntries: [ - {el: barFixedToggler} - {el: headerToggler} - {el: barPositionToggler} - {el: linkJustifyToggler} - {el: footerToggler} - {el: shortcutToggler} - {el: customNavToggler} - {el: editCustomNav} + el: barFixedToggler + , + el: headerToggler + , + el: scrollHeaderToggler + , + el: barPositionToggler + , + el: linkJustifyToggler + , + el: footerToggler + , + el: shortcutToggler + , + el: customNavToggler + , + el: editCustomNav ] $.on window, 'load hashchange', Header.hashScroll @@ -85,9 +101,10 @@ Header = @ $.ready => - @footer = $.id 'boardNavDesktopFoot' - if a = $ "a[href*='/#{g.BOARD}/']", $.id 'boardNavDesktopFoot' + @footer = footer = $.id 'boardNavDesktopFoot' + if a = $ "a[href*='/#{g.BOARD}/']", footer a.className = 'current' + $.on a, 'click', Index.cb.link cs = $.el 'a', id: 'settingsWindowLink' @@ -104,7 +121,7 @@ Header = bar: $.el 'div', id: 'header-bar' - notify: $.el 'div', + noticesRoot: $.el 'div', id: 'notifications' shortcuts: $.el 'span', @@ -118,18 +135,19 @@ Header = setBoardList: -> fourchannav = $.id 'boardNavDesktop' - if a = $ "a[href*='/#{g.BOARD}/']", fourchannav - a.className = 'current' boardList = $.el 'span', id: 'board-list' innerHTML: "" + if a = $ "a[href*='/#{g.BOARD}/']", boardList + a.className = 'current' + $.on a, 'click', Index.cb.link 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, [boardList, Header.shortcuts, Header.notify, Header.toggle] + $.add Header.bar, [boardList, Header.shortcuts, Header.noticesRoot, Header.toggle] Header.setCustomNav Conf['Custom Board Navigation'] Header.generateBoardList Conf['boardnav'].replace /(\r\n|\n|\r)/g, ' ' @@ -166,7 +184,11 @@ Header = if a.textContent is board a = a.cloneNode true - a.textContent = if /-title/.test(t) or /-replace/.test(t) and $.hasClass a, 'current' + current = $.hasClass a, 'current' + if current + $.on a, 'click', Index.cb.link + + a.textContent = if /-title/.test(t) or /-replace/.test(t) and current a.title else if /-full/.test t "/#{board}/ - #{a.title}" @@ -198,17 +220,6 @@ Header = custom.hidden = !showBoardList full.hidden = showBoardList - setBarPosition: (bottom) -> - Header.barPositionToggler.checked = bottom - if bottom - $.rmClass doc, 'top' - $.addClass doc, 'bottom' - $.after Header.bar, Header.notify - else - $.rmClass doc, 'bottom' - $.addClass doc, 'top' - $.add Header.bar, Header.notify - setLinkJustify: (centered) -> Header.linkJustifyToggler.checked = centered if centered @@ -216,14 +227,6 @@ Header = else $.rmClass doc, 'centered-links' - toggleBarPosition: -> - $.event 'CloseMenu' - - Header.setBarPosition @checked - - Conf['Bottom Header'] = @checked - $.set 'Bottom Header', @checked - toggleLinkJustify: -> $.event 'CloseMenu' centered = if @nodeName is 'INPUT' @@ -285,6 +288,54 @@ Header = 'remain visible.'}" new Notice 'info', message, 2 + setHideBarOnScroll: (hide) -> + Header.scrollHeaderToggler.checked = hide + if hide + $.on window, 'scroll', Header.hideBarOnScroll + return + $.off window, 'scroll', Header.hideBarOnScroll + $.rmClass Header.bar, 'scroll' + $.rmClass Header.bar, 'autohide' unless Conf['Header auto-hide'] + + toggleHideBarOnScroll: (e) -> + hide = @checked + $.set 'Header auto-hide on scroll', hide + Header.setHideBarOnScroll hide + + hideBarOnScroll: -> + offsetY = window.pageYOffset + if offsetY > (Header.previousOffset or 0) + $.addClass Header.bar, 'autohide' + $.addClass Header.bar, 'scroll' + else + $.rmClass Header.bar, 'autohide' + $.rmClass Header.bar, 'scroll' + Header.previousOffset = offsetY + + setBarPosition: (bottom) -> + Header.barPositionToggler.checked = bottom + $.event 'CloseMenu' + args = if bottom then [ + 'bottom-header' + 'top-header' + 'bottom' + 'after' + ] else [ + 'top-header' + 'bottom-header' + 'top' + 'add' + ] + + $.addClass doc, args[0] + $.rmClass doc, args[1] + Header.bar.parentNode.className = args[2] + $[args[3]] Header.bar, Header.notify + + toggleBarPosition: -> + $.cb.checked.call @ + Header.setBarPosition @checked + setFooterVisibility: (hide) -> Header.footerToggler.checked = hide Header.footer.hidden = hide @@ -323,16 +374,33 @@ Header = $('input[name=boardnav]', settings).focus() hashScroll: -> - return unless (hash = @location.hash[1..]) and post = $.id hash + hash = @location.hash[1..] + return unless /^p\d+$/.test(hash) and post = $.id hash return if (Get.postFromRoot post).isHidden - Header.scrollToPost post - scrollToPost: (post) -> - {top} = post.getBoundingClientRect() + Header.scrollTo post + scrollTo: (root, down, needed) -> + if down + x = Header.getBottomOf root + window.scrollBy 0, -x unless needed and x >= 0 + else + x = Header.getTopOf root + window.scrollBy 0, x unless needed and x >= 0 + scrollToIfNeeded: (root, down) -> + Header.scrollTo root, down, true + getTopOf: (root) -> + {top} = root.getBoundingClientRect() if Conf['Fixed Header'] and not Conf['Bottom Header'] - headRect = Header.bar.getBoundingClientRect() - top -= headRect.top + headRect.height - window.scrollBy 0, top + headRect = Header.toggle.getBoundingClientRect() + top -= headRect.top + headRect.height + top + getBottomOf: (root) -> + {clientHeight} = doc + bottom = clientHeight - root.getBoundingClientRect().bottom + if Conf['Bottom Header'] + headRect = Header.toggle.getBoundingClientRect() + bottom -= clientHeight - headRect.bottom + headRect.height + bottom addShortcut: (el) -> shortcut = $.el 'span', @@ -340,13 +408,14 @@ Header = $.add shortcut, el $.prepend Header.shortcuts, shortcut + menuToggle: (e) -> Header.menu.toggle e, @, g createNotification: (e) -> {type, content, lifetime, cb} = e.detail - notif = new Notice type, content, lifetime - cb notif if cb + notice = new Notice type, content, lifetime + cb notice if cb areNotificationsEnabled: false enableDesktopNotifications: -> diff --git a/src/General/Index.coffee b/src/General/Index.coffee new file mode 100644 index 000000000..a3f54c8e5 --- /dev/null +++ b/src/General/Index.coffee @@ -0,0 +1,426 @@ +Index = + init: -> + return if g.VIEW isnt 'index' or g.BOARD.ID is 'f' + + @button = $.el 'a', + className: 'index-refresh-shortcut fa fa-refresh' + title: 'Refresh Index' + href: 'javascript:;' + textContent: 'Refresh Index' + $.on @button, 'click', @update + Header.addShortcut @button, 1 + + modeEntry = + el: $.el 'span', textContent: 'Index mode' + subEntries: [ + { el: $.el 'label', innerHTML: ' Paged' } + { el: $.el 'label', innerHTML: ' All threads' } + ] + for label in modeEntry.subEntries + input = label.el.firstChild + input.checked = Conf['Index Mode'] is input.value + $.on input, 'change', $.cb.value + $.on input, 'change', @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' } + ] + for label in sortEntry.subEntries + input = label.el.firstChild + input.checked = Conf['Index Sort'] is input.value + $.on input, 'change', $.cb.value + $.on input, 'change', @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.' + for label in [repliesEntry, anchorEntry, refNavEntry] + input = label.el.firstChild + {name} = input + input.checked = Conf[name] + $.on input, 'change', $.cb.checked + switch name + when 'Show Replies' + $.on input, 'change', @cb.replies + when 'Anchor Hidden Threads' + $.on input, 'change', @cb.sort + + $.event 'AddMenuEntry', + type: 'header' + el: $.el 'span', + textContent: 'Index Navigation' + order: 90 + subEntries: [modeEntry, sortEntry, repliesEntry, anchorEntry, refNavEntry] + + $.addClass doc, 'index-loading' + @update() + @root = $.el 'div', className: 'board' + @pagelist = $.el 'div', + className: 'pagelist' + hidden: true + innerHTML: <%= importHTML('Features/Index-pagelist') %> + @navLinks = $.el 'div', + className: 'navLinks' + innerHTML: <%= importHTML('Features/Index-navlinks') %> + @searchInput = $ '#index-search', @navLinks + @currentPage = @getCurrentPage() + $.on window, 'popstate', @cb.popstate + $.on @pagelist, 'click', @cb.pageNav + $.on @searchInput, 'input', @onSearchInput + $.on $('#index-search-clear', @navLinks), 'click', @clearSearch + $.asap (-> $('.board', doc) or d.readyState isnt 'loading'), -> + board = $ '.board' + $.replace board, Index.root + # Hacks: + # - When removing an element from the document during page load, + # its ancestors will still be correctly created inside of it. + # - Creating loadable elements inside of an origin-less document + # will not download them. + # - Combine the two and you get a download canceller! + # Does not work on Firefox unfortunately. bugzil.la/939713 + d.implementation.createDocument(null, null, null).appendChild board + + for navLink in $$ '.navLinks' + $.rm navLink + $.after $.x('child::form/preceding-sibling::hr[1]'), Index.navLinks + $.rmClass doc, 'index-loading' + $.asap (-> $('.pagelist') or d.readyState isnt 'loading'), -> + $.replace $('.pagelist'), Index.pagelist + + cb: + mode: -> + Index.togglePagelist() + Index.buildIndex() + sort: -> + Index.sort() + Index.buildIndex() + replies: -> + Index.buildThreads() + Index.sort() + Index.buildIndex() + popstate: (e) -> + pageNum = Index.getCurrentPage() + Index.pageLoad pageNum if Index.currentPage isnt pageNum + pageNav: (e) -> + return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0 + switch e.target.nodeName + when 'BUTTON' + a = e.target.parentNode + when 'A' + a = e.target + else + return + return if a.textContent is 'Catalog' + e.preventDefault() + Index.userPageNav +a.pathname.split('/')[2] + link: (e) -> + return if g.VIEW isnt 'index' or /catalog/.test @href + e.preventDefault() + Index.update() + + scrollToIndex: -> + Header.scrollToIfNeeded Index.root + + getCurrentPage: -> + +window.location.pathname.split('/')[2] + userPageNav: (pageNum) -> + if Conf['Refreshed Navigation'] and Conf['Index Mode'] is 'paged' + Index.update pageNum + else + Index.pageNav pageNum + pageNav: (pageNum) -> + return if Index.currentPage is pageNum + history.pushState null, '', if pageNum is 0 then './' else pageNum + Index.pageLoad pageNum + pageLoad: (pageNum) -> + Index.currentPage = pageNum + return if Conf['Index Mode'] isnt 'paged' + Index.buildIndex() + Index.setPage() + Index.scrollToIndex() + + getPagesNum: -> + if Index.isSearching + Math.ceil (Index.sortedNodes.length / 2) / Index.threadsNumPerPage + else + Index.pagesNum + getMaxPageNum: -> + Math.max 0, Index.getPagesNum() - 1 + togglePagelist: -> + Index.pagelist.hidden = Conf['Index Mode'] isnt 'paged' + buildPagelist: -> + pagesRoot = $ '.pages', Index.pagelist + maxPageNum = Index.getMaxPageNum() + if pagesRoot.childElementCount isnt maxPageNum + 1 + nodes = [] + for i in [0..maxPageNum] by 1 + a = $.el 'a', + textContent: i + href: if i then i else './' + nodes.push $.tn('['), a, $.tn '] ' + $.rmAll pagesRoot + $.add pagesRoot, nodes + Index.togglePagelist() + setPage: -> + pageNum = Index.getCurrentPage() + maxPageNum = Index.getMaxPageNum() + pagesRoot = $ '.pages', Index.pagelist + # Previous/Next buttons + prev = pagesRoot.previousSibling.firstChild + next = pagesRoot.nextSibling.firstChild + href = Math.max pageNum - 1, 0 + prev.href = if href is 0 then './' else href + prev.firstChild.disabled = href is pageNum + href = Math.min pageNum + 1, maxPageNum + next.href = if href is 0 then './' else href + next.firstChild.disabled = href is pageNum + # current page + if strong = $ 'strong', pagesRoot + return if +strong.textContent is pageNum + $.replace strong, strong.firstChild + else + strong = $.el 'strong' + a = pagesRoot.children[pageNum] + $.before a, strong + $.add strong, a + + update: (pageNum) -> + return unless navigator.onLine + Index.req?.abort() + Index.notice?.close() + if d.readyState isnt 'loading' + Index.notice = new Notice 'info', 'Refreshing index...' + else + # Delay the notice on initial page load + # and only display it for slow connections. + now = Date.now() + $.ready -> + setTimeout (-> + return unless Index.req and !Index.notice + Index.notice = new Notice 'info', 'Refreshing index...' + ), 5 * $.SECOND - (Date.now() - now) + pageNum = null if typeof pageNum isnt 'number' # event + onload = (e) -> Index.load e, pageNum + Index.req = $.ajax "//a.4cdn.org/#{g.BOARD}/catalog.json", + onabort: onload + onloadend: onload + , + whenModified: true + $.addClass Index.button, 'fa-spin' + load: (e, pageNum) -> + $.rmClass Index.button, 'fa-spin' + {req, notice} = Index + delete Index.req + delete Index.notice + + if e.type is 'abort' + req.onloadend = null + notice.close() + return + + try + if req.status is 200 + Index.parse JSON.parse(req.response), pageNum + else if req.status is 304 and pageNum? + Index.pageNav pageNum + catch err + c.error 'Index failure:', err.stack + # network error or non-JSON content for example. + if notice + notice.setType 'error' + notice.el.lastElementChild.textContent = 'Index refresh failed.' + setTimeout notice.close, 2 * $.SECOND + else + new Notice 'error', 'Index refresh failed.', 2 + return + + if notice + notice.setType 'success' + notice.el.lastElementChild.textContent = 'Index refreshed!' + setTimeout notice.close, $.SECOND + + timeEl = $ '#index-last-refresh', Index.navLinks + timeEl.dataset.utc = Date.parse req.getResponseHeader 'Last-Modified' + RelativeDates.update timeEl + Index.scrollToIndex() + parse: (pages, pageNum) -> + Index.parseThreadList pages + Index.buildThreads() + Index.sort() + Index.buildPagelist() + if pageNum? + Index.pageNav pageNum + return + Index.buildIndex() + Index.setPage() + parseThreadList: (pages) -> + Index.pagesNum = pages.length + Index.threadsNumPerPage = pages[0].threads.length + Index.liveThreadData = pages.reduce ((arr, next) -> arr.concat next.threads), [] + Index.liveThreadIDs = Index.liveThreadData.map (data) -> data.no + for threadID, thread of g.BOARD.threads when thread.ID not in Index.liveThreadIDs + thread.collect() + return + buildThreads: -> + Index.nodes = [] + threads = [] + posts = [] + for threadData, i in Index.liveThreadData + threadRoot = Build.thread g.BOARD, threadData + Index.nodes.push threadRoot, $.el 'hr' + 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 + continue if thread.ID of thread.posts + try + posts.push new Post $('.opContainer', threadRoot), thread, g.BOARD + catch err + # Skip posts that we failed to parse. + errors = [] unless errors + errors.push + message: "Parsing of Post No.#{thread} failed. Post will be skipped." + error: err + Main.handleErrors errors if errors + + # Add the threads and
s in a container to make sure all features work. + $.nodes Index.nodes + Main.callbackNodes Thread, threads + Main.callbackNodes Post, posts + $.event 'IndexRefresh' + buildReplies: (threadRoots) -> + posts = [] + for threadRoot in threadRoots by 2 + thread = Get.threadFromRoot threadRoot + i = Index.liveThreadIDs.indexOf thread.ID + continue unless lastReplies = Index.liveThreadData[i].last_replies + nodes = [] + for data in lastReplies + 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 err + # Skip posts that we failed to parse. + errors = [] unless errors + errors.push + message: "Parsing of Post No.#{data.no} failed. Post will be skipped." + error: err + $.add threadRoot, nodes + + Main.handleErrors errors if errors + Main.callbackNodes Post, posts + sort: -> + switch Conf['Index Sort'] + when 'bump' + sortedThreadIDs = Index.liveThreadIDs + when 'lastreply' + sortedThreadIDs = [Index.liveThreadData...].sort((a, b) -> + a = a.last_replies[a.last_replies.length - 1] if 'last_replies' of a + b = b.last_replies[b.last_replies.length - 1] if 'last_replies' of b + b.no - a.no + ).map (data) -> data.no + when 'birth' + sortedThreadIDs = [Index.liveThreadIDs...].sort (a, b) -> b - a + when 'replycount' + sortedThreadIDs = [Index.liveThreadData...].sort((a, b) -> b.replies - a.replies).map (data) -> data.no + when 'filecount' + sortedThreadIDs = [Index.liveThreadData...].sort((a, b) -> b.images - a.images).map (data) -> data.no + Index.sortedNodes = [] + for threadID in sortedThreadIDs + i = Index.liveThreadIDs.indexOf(threadID) * 2 + Index.sortedNodes.push Index.nodes[i], Index.nodes[i + 1] + if Index.isSearching + Index.sortedNodes = Index.querySearch(Index.searchInput.value) or Index.sortedNodes + # Sticky threads + Index.sortOnTop (thread) -> thread.isSticky + # Highlighted threads + Index.sortOnTop((thread) -> thread.isOnTop) if Conf['Filter'] + # Non-hidden threads + Index.sortOnTop((thread) -> !thread.isHidden) if Conf['Anchor Hidden Threads'] + sortOnTop: (match) -> + offset = 0 + for threadRoot, i in Index.sortedNodes by 2 when match Get.threadFromRoot threadRoot + Index.sortedNodes.splice offset++ * 2, 0, Index.sortedNodes.splice(i, 2)... + return + buildIndex: -> + if Conf['Index Mode'] is 'paged' + pageNum = Index.getCurrentPage() + nodesPerPage = Index.threadsNumPerPage * 2 + nodes = Index.sortedNodes[nodesPerPage * pageNum ... nodesPerPage * (pageNum + 1)] + else + nodes = Index.sortedNodes + $.rmAll Index.root + Index.buildReplies nodes if Conf['Show Replies'] + $.event 'IndexBuild', nodes + $.add Index.root, nodes + + isSearching: false + clearSearch: -> + Index.searchInput.value = null + Index.onSearchInput() + Index.searchInput.focus() + onSearchInput: -> + if Index.isSearching = !!Index.searchInput.value.trim() + unless Index.searchInput.dataset.searching + Index.searchInput.dataset.searching = 1 + Index.pageBeforeSearch = Index.getCurrentPage() + pageNum = 0 + else + pageNum = Index.getCurrentPage() + else + pageNum = Index.pageBeforeSearch + delete Index.pageBeforeSearch + <% if (type === 'userscript') { %> + # XXX https://github.com/greasemonkey/greasemonkey/issues/1571 + Index.searchInput.removeAttribute 'data-searching' + <% } else { %> + delete Index.searchInput.dataset.searching + <% } %> + Index.sort() + # Go to the last available page if we were past the limit. + pageNum = Math.min pageNum, Index.getMaxPageNum() if Conf['Index Mode'] is 'paged' + Index.buildPagelist() + if Index.currentPage is pageNum + Index.buildIndex() + Index.setPage() + else + Index.pageNav pageNum + querySearch: (query) -> + return unless keywords = query.toLowerCase().match /\S+/g + Index.search keywords + search: (keywords) -> + found = [] + for threadRoot, i in Index.sortedNodes by 2 + if Index.searchMatch Get.threadFromRoot(threadRoot), keywords + found.push Index.sortedNodes[i], Index.sortedNodes[i + 1] + found + searchMatch: (thread, keywords) -> + {info, file} = thread.OP + text = [] + for key in ['comment', 'subject', 'name', 'tripcode', 'email'] + text.push info[key] if key of info + text.push file.name if file + text = text.join(' ').toLowerCase() + for keyword in keywords + return false if -1 is text.indexOf keyword + return true diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 7004ba013..06ad673b1 100755 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -1,5 +1,18 @@ Main = init: -> + pathname = location.pathname.split '/' + g.BOARD = new Board pathname[1] + return if g.BOARD.ID in ['z', 'fk'] + g.VIEW = + switch pathname[2] + when 'res' + 'thread' + when 'catalog' + 'catalog' + else + 'index' + if g.VIEW is 'thread' + g.THREADID = +pathname[3] # flatten Config into Conf # and get saved or default values @@ -24,21 +37,6 @@ Main = $.on d, '4chanMainInit', Main.initStyle initFeatures: -> - - pathname = location.pathname.split '/' - g.BOARD = new Board pathname[1] - return if g.BOARD.ID in ['z', 'fk'] - g.VIEW = - switch pathname[2] - when 'res' - 'thread' - when 'catalog' - 'catalog' - else - 'index' - if g.VIEW is 'thread' - g.THREADID = +pathname[3] - switch location.hostname when 'a.4cdn.org' return @@ -47,7 +45,7 @@ Main = return when 'i.4cdn.org' $.ready -> - if Conf['404 Redirect'] and ['4chan - Temporarily Offline', '4chan - 404 Not Found'].contains d.title + if Conf['404 Redirect'] and d.title in ['4chan - Temporarily Offline', '4chan - 404 Not Found'] Redirect.init() pathname = location.pathname.split '/' URL = Redirect.to 'file', @@ -70,13 +68,13 @@ Main = return # c.time 'All initializations' - init 'Polyfill': Polyfill 'Redirect': Redirect 'Header': Header 'Catalog Links': CatalogLinks 'Settings': Settings + 'Index Generator': Index 'Announcement Hiding': PSAHiding 'Fourchan thingies': Fourchan 'Emoji': Emoji @@ -118,7 +116,6 @@ Main = 'Reveal Spoiler Thumbnails': RevealSpoilers 'Image Loading': ImageLoader 'Image Hover': ImageHover - 'Comment Expansion': ExpandComment 'Thread Expansion': ExpandThread 'Thread Excerpt': ThreadExcerpt 'Favicon': Favicon @@ -132,8 +129,6 @@ Main = 'Keybinds': Keybinds 'Show Dice Roll': Dice 'Banner': Banner - 'Infinite Scrolling': InfiniScroll - # c.timeEnd 'All initializations' $.on d, 'AddCallback', Main.addCallback @@ -175,7 +170,7 @@ Main = attributeFilter: ['href'] initReady: -> - if ['4chan - Temporarily Offline', '4chan - 404 Not Found'].contains d.title + if d.title in ['4chan - Temporarily Offline', '4chan - 404 Not Found'] if Conf['404 Redirect'] and g.VIEW is 'thread' href = Redirect.to 'thread', boardID: g.BOARD.ID @@ -187,26 +182,21 @@ Main = # Something might have gone wrong! Main.initStyle() - if board = $ '.board' - threads = [] - posts = [] - - for threadRoot in $$ '.board > .thread', board - thread = new Thread +threadRoot.id[1..], g.BOARD - threads.push thread - for postRoot in $$ '.thread > .postContainer', threadRoot - try - posts.push new Post postRoot, thread, g.BOARD - catch err - # Skip posts that we failed to parse. - unless errors - errors = [] - errors.push - message: "Parsing of Post No.#{postRoot.id.match(/\d+/)} failed. Post will be skipped." - error: err + if g.VIEW is 'thread' and threadRoot = $ '.thread' + thread = new Thread +threadRoot.id[1..], g.BOARD + posts = [] + for postRoot in $$ '.thread > .postContainer', threadRoot + try + posts.push post = new Post postRoot, thread, g.BOARD, {isOriginalMarkup: true} + catch err + # Skip posts that we failed to parse. + errors = [] unless errors + errors.push + message: "Parsing of Post No.#{postRoot.id.match /\d+/} failed. Post will be skipped." + error: err Main.handleErrors errors if errors - Main.callbackNodes Thread, threads + Main.callbackNodes Thread, [thread] Main.callbackNodesDB Post, posts, -> $.event '4chanXInitFinished' @@ -222,67 +212,44 @@ Main = return + <% if (type === 'userscript') { %> + GMver = GM_info.version.split '.' + for v, i in "<%= meta.min.greasemonkey %>".split '.' + break if v < GMver[i] + continue if v is GMver[i] + new Notice 'warning', "Your version of Greasemonkey is outdated (v#{GM_info.version} instead of v<%= meta.min.greasemonkey %> minimum) and <%= meta.name %> may not operate correctly.", 30 + break + <% } %> + try localStorage.getItem '4chan-settings' catch err - new Notice 'warning', 'Cookies need to be enabled on 4chan for <%= meta.name %> to properly function.', 30 - Main.disableReports = true - - $.event '4chanXInitFinished' + new Notice 'warning', 'Cookies need to be enabled on 4chan for <%= meta.name %> to operate properly.', 30 callbackNodes: (klass, nodes) -> - # get the nodes' length only once - len = nodes.length - for callback in klass.callbacks - # c.profile callback.name - i = 0 - while i < len - node = nodes[i++] - try - callback.cb.call node - catch err - errors = [] unless errors - errors.push - message: "\"#{callback.name}\" crashed on #{klass.name} No.#{node} (/#{node.board}/)." - error: err - # c.profileEnd callback.name - Main.handleErrors errors if errors + i = 0 + cb = klass.callbacks + while node = nodes[i++] + cb.execute node + return callbackNodesDB: (klass, nodes, cb) -> - queue = [] errors = null + len = 0 + i = 0 - func = (node) -> - for callback in klass.callbacks - try - callback.cb.call node - catch err - errors = [] unless errors - errors.push - message: "\"#{callback.name}\" crashed on #{klass.name} No.#{node} (/#{node.board}/)." - error: err - # finish - unless queue.length - Main.handleErrors errors if errors - cb() if cb + {callbacks} = klass - softTask = -> - node = queue.shift() - func node - return unless queue.length - unless queue.length % 7 + softTask = -> + node = nodes[i++] + callbacks.execute node + return cb() if len is i and cb + unless i % 7 setTimeout softTask, 0 else softTask() - # get the nodes' length only once - len = nodes.length - i = 0 - - while i < len - node = nodes[i++] - queue.push node - + len = nodes.length softTask() addCallback: (e) -> @@ -324,18 +291,13 @@ Main = new Notice 'error', [div, logs], 30 parseError: (data) -> - Main.logError data + c.error data.message, data.error.stack message = $.el 'div', textContent: data.message error = $.el 'div', textContent: data.error [message, error] - errors: [] - logError: (data) -> - c.error data.message, data.error.stack - Main.errors.push data - isThisPageLegit: -> # 404 error page or similar. unless 'thisPageIsLegit' of Main diff --git a/src/General/Settings.coffee b/src/General/Settings.coffee index d859e8ffe..a7f944a24 100755 --- a/src/General/Settings.coffee +++ b/src/General/Settings.coffee @@ -2,7 +2,7 @@ Settings = init: -> # 4chan X settings link link = $.el 'a', - className: 'settings-link fourchanx-icon icon-wrench' + className: 'settings-link fa fa-wrench' textContent: 'Settings' href: 'javascript:;' $.on link, 'click', Settings.open @@ -41,9 +41,7 @@ Settings = return if Settings.dialog $.event 'CloseMenu' - html = """ - <%= grunt.file.read('src/General/html/Settings/Settings.html').replace(/>\s+<').trim() %> - """ + html = <%= importHTML('Settings/Settings') %> Settings.overlay = overlay = $.el 'div', id: 'overlay' @@ -124,7 +122,7 @@ Settings = return div = $.el 'div', - innerHTML: ": Clear manually-hidden threads and posts on all boards. Refresh the page to apply." + innerHTML: ": Clear manually-hidden threads and posts on all boards. Reload the page to apply." button = $ 'button', div hiddenNum = 0 $.get 'hiddenThreads', boards: {}, (item) -> @@ -187,7 +185,7 @@ Settings = try data = JSON.parse e.target.result Settings.loadSettings data - if confirm 'Import successful. Refresh now?' + if confirm 'Import successful. Reload now?' window.location.reload() catch err output.textContent = 'Import failed due to an error.' @@ -274,12 +272,11 @@ Settings = data filter: (section) -> - section.innerHTML = """ - <%= grunt.file.read('src/General/html/Settings/Filter-select.html').replace(/>\s+<').trim() %> - """ + section.innerHTML = <%= importHTML('Settings/Filter-select') %> select = $ 'select', section $.on select, 'change', Settings.selectFilter Settings.selectFilter.call select + selectFilter: -> div = @nextElementSibling if (name = @value) isnt 'guide' @@ -293,30 +290,24 @@ Settings = $.on ta, 'change', $.cb.value $.add div, ta return - div.innerHTML = """ - <%= grunt.file.read('src/General/html/Settings/Filter-guide.html').replace(/>\s+<').trim() %> - """ + div.innerHTML = <%= importHTML('Settings/Filter-guide') %> sauce: (section) -> - section.innerHTML = """ - <%= grunt.file.read('src/General/html/Settings/Sauce.html').replace(/>\s+<').trim() %> - """ + section.innerHTML = <%= importHTML('Settings/Sauce') %> ta = $ 'textarea', section $.get 'sauces', Conf['sauces'], (item) -> ta.value = item['sauces'] $.on ta, 'change', $.cb.value advanced: (section) -> - section.innerHTML = """ - <%= grunt.file.read('src/General/html/Settings/Advanced.html').replace(/>\s+<').trim() %> - """ - items = {} + section.innerHTML = <%= importHTML('Settings/Advanced') %> + items = {} inputs = {} for name in ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'sageEmoji', 'emojiPos', 'usercss'] input = $ "[name=#{name}]", section items[name] = Conf[name] inputs[name] = input - event = if ['favicon', 'usercss', 'sageEmoji', 'emojiPos'].contains name + event = if name in ['favicon', 'usercss', 'sageEmoji', 'emojiPos'] 'change' else 'input' @@ -330,7 +321,7 @@ Settings = $.get items, (items) -> for key, val of items - continue if ['emojiPos'].contains key + continue if key is 'emojiPos' input = inputs[key] input.value = val continue if key is 'usercss' @@ -351,7 +342,7 @@ Settings = file: [] data.thread.push name data.post.push name if archive.software is 'foolfuuka' - data.file.push name if archive.files.contains boardID + data.file.push name if boardID in archive.files rows = [] boardOptions = [] @@ -460,10 +451,10 @@ Settings = $.cb.checked.call @ usercss: -> CustomCSS.update() + keybinds: (section) -> - section.innerHTML = """ - <%= grunt.file.read('src/General/html/Settings/Keybinds.html').replace(/>\s+<').trim() %> - """ + section.innerHTML = <%= importHTML('Settings/Keybinds') %> + tbody = $ 'tbody', section items = {} inputs = {} diff --git a/src/General/UI.coffee b/src/General/UI.coffee index 8fbe4d97a..2636bf415 100755 --- a/src/General/UI.coffee +++ b/src/General/UI.coffee @@ -173,7 +173,7 @@ UI = do -> ['0px', 'auto'] else ['auto', '0px'] - [left, right] = if eRect.right + sRect.width < cWidth + [left, right] = if eRect.right + sRect.width < cWidth - 150 ['100%', 'auto'] else ['auto', '100%'] diff --git a/src/General/css/font-awesome.css b/src/General/css/font-awesome.css index a23bd2252..e12f4a939 100644 --- a/src/General/css/font-awesome.css +++ b/src/General/css/font-awesome.css @@ -23,1145 +23,4 @@ * 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,<%= grunt.file.read('node_modules/font-awesome/fonts/fontawesome-webfont.woff', {encoding: 'base64'}) %>') format('woff'); - font-weight: normal; - font-style: normal; -} -.fourchanx-icon::before { - font-family: FontAwesome; - font-weight: normal; - font-style: normal; - text-decoration: inherit; - -webkit-font-smoothing: antialiased; - *margin-right: .3em; - text-decoration: inherit; - display: none; - speak: none; -} -:root.shortcut-icons .fourchanx-icon::before { - display: inline-block; - font-size: 13px; - visibility: visible; -} -:root.shortcut-icons #shortcuts .fourchanx-icon::before { - font-size: 15px !important; - margin-top: -3px !important; - position: relative; - top: 1px; -} -:root.shortcut-icons .fourchanx-icon { - font-size: 0; - visibility: hidden; -} -:root.shortcut-icons .shortcut.brackets-wrap::before, -:root.shortcut-icons .shortcut.brackets-wrap::after { - display: none; -} -/* makes sure icons active on rollover in links */ -:root.shortcut-icons a .fourchanx-icon { - display: inline; -} -/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen - readers do not read off random characters that represent icons */ -.icon-glass::before { - content: "\f000"; -} -.icon-music::before { - content: "\f001"; -} -.icon-search::before { - content: "\f002"; -} -.icon-envelope-alt::before { - content: "\f003"; -} -.icon-heart::before { - content: "\f004"; -} -.icon-star::before { - content: "\f005"; -} -.icon-star-empty::before { - content: "\f006"; -} -.icon-user::before { - content: "\f007"; -} -.icon-film::before { - content: "\f008"; -} -.icon-th-large::before { - content: "\f009"; -} -.icon-th::before { - content: "\f00a"; -} -.icon-th-list::before { - content: "\f00b"; -} -.icon-ok::before { - content: "\f00c"; -} -.icon-remove::before { - content: "\f00d"; -} -.icon-zoom-in::before { - content: "\f00e"; -} -.icon-zoom-out::before { - content: "\f010"; -} -.icon-power-off:before, -.icon-off::before { - content: "\f011"; -} -.icon-signal::before { - content: "\f012"; -} -.icon-gear:before, -.icon-cog::before { - content: "\f013"; -} -.icon-trash::before { - content: "\f014"; -} -.icon-home::before { - content: "\f015"; -} -.icon-file-alt::before { - content: "\f016"; -} -.icon-time::before { - content: "\f017"; -} -.icon-road::before { - content: "\f018"; -} -.icon-download-alt::before { - content: "\f019"; -} -.icon-download::before { - content: "\f01a"; -} -.icon-upload::before { - content: "\f01b"; -} -.icon-inbox::before { - content: "\f01c"; -} -.icon-play-circle::before { - content: "\f01d"; -} -.icon-rotate-right:before, -.icon-repeat::before { - content: "\f01e"; -} -.icon-refresh::before { - content: "\f021"; -} -.icon-list-alt::before { - content: "\f022"; -} -.icon-lock::before { - content: "\f023"; -} -.icon-flag::before { - content: "\f024"; -} -.icon-headphones::before { - content: "\f025"; -} -.icon-volume-off::before { - content: "\f026"; -} -.icon-volume-down::before { - content: "\f027"; -} -.icon-volume-up::before { - content: "\f028"; -} -.icon-qrcode::before { - content: "\f029"; -} -.icon-barcode::before { - content: "\f02a"; -} -.icon-tag::before { - content: "\f02b"; -} -.icon-tags::before { - content: "\f02c"; -} -.icon-book::before { - content: "\f02d"; -} -.icon-bookmark::before { - content: "\f02e"; -} -.icon-print::before { - content: "\f02f"; -} -.icon-camera::before { - content: "\f030"; -} -.icon-font::before { - content: "\f031"; -} -.icon-bold::before { - content: "\f032"; -} -.icon-italic::before { - content: "\f033"; -} -.icon-text-height::before { - content: "\f034"; -} -.icon-text-width::before { - content: "\f035"; -} -.icon-align-left::before { - content: "\f036"; -} -.icon-align-center::before { - content: "\f037"; -} -.icon-align-right::before { - content: "\f038"; -} -.icon-align-justify::before { - content: "\f039"; -} -.icon-list::before { - content: "\f03a"; -} -.icon-indent-left::before { - content: "\f03b"; -} -.icon-indent-right::before { - content: "\f03c"; -} -.icon-facetime-video::before { - content: "\f03d"; -} -.icon-picture::before { - content: "\f03e"; -} -.icon-pencil::before { - content: "\f040"; -} -.icon-map-marker::before { - content: "\f041"; -} -.icon-adjust::before { - content: "\f042"; -} -.icon-tint::before { - content: "\f043"; -} -.icon-edit::before { - content: "\f044"; -} -.icon-share::before { - content: "\f045"; -} -.icon-check::before { - content: "\f046"; -} -.icon-move::before { - content: "\f047"; -} -.icon-step-backward::before { - content: "\f048"; -} -.icon-fast-backward::before { - content: "\f049"; -} -.icon-backward::before { - content: "\f04a"; -} -.icon-play::before { - content: "\f04b"; -} -.icon-pause::before { - content: "\f04c"; -} -.icon-stop::before { - content: "\f04d"; -} -.icon-forward::before { - content: "\f04e"; -} -.icon-fast-forward::before { - content: "\f050"; -} -.icon-step-forward::before { - content: "\f051"; -} -.icon-eject::before { - content: "\f052"; -} -.icon-chevron-left::before { - content: "\f053"; -} -.icon-chevron-right::before { - content: "\f054"; -} -.icon-plus-sign::before { - content: "\f055"; -} -.icon-minus-sign::before { - content: "\f056"; -} -.icon-remove-sign::before { - content: "\f057"; -} -.icon-ok-sign::before { - content: "\f058"; -} -.icon-question-sign::before { - content: "\f059"; -} -.icon-info-sign::before { - content: "\f05a"; -} -.icon-screenshot::before { - content: "\f05b"; -} -.icon-remove-circle::before { - content: "\f05c"; -} -.icon-ok-circle::before { - content: "\f05d"; -} -.icon-ban-circle::before { - content: "\f05e"; -} -.icon-arrow-left::before { - content: "\f060"; -} -.icon-arrow-right::before { - content: "\f061"; -} -.icon-arrow-up::before { - content: "\f062"; -} -.icon-arrow-down::before { - content: "\f063"; -} -.icon-mail-forward:before, -.icon-share-alt::before { - content: "\f064"; -} -.icon-resize-full::before { - content: "\f065"; -} -.icon-resize-small::before { - content: "\f066"; -} -.icon-plus::before { - content: "\f067"; -} -.icon-minus::before { - content: "\f068"; -} -.icon-asterisk::before { - content: "\f069"; -} -.icon-exclamation-sign::before { - content: "\f06a"; -} -.icon-gift::before { - content: "\f06b"; -} -.icon-leaf::before { - content: "\f06c"; -} -.icon-fire::before { - content: "\f06d"; -} -.icon-eye-open::before { - content: "\f06e"; -} -.icon-eye-close::before { - content: "\f070"; -} -.icon-warning-sign::before { - content: "\f071"; -} -.icon-plane::before { - content: "\f072"; -} -.icon-calendar::before { - content: "\f073"; -} -.icon-random::before { - content: "\f074"; -} -.icon-comment::before { - content: "\f075"; -} -.icon-magnet::before { - content: "\f076"; -} -.icon-chevron-up::before { - content: "\f077"; -} -.icon-chevron-down::before { - content: "\f078"; -} -.icon-retweet::before { - content: "\f079"; -} -.icon-shopping-cart::before { - content: "\f07a"; -} -.icon-folder-close::before { - content: "\f07b"; -} -.icon-folder-open::before { - content: "\f07c"; -} -.icon-resize-vertical::before { - content: "\f07d"; -} -.icon-resize-horizontal::before { - content: "\f07e"; -} -.icon-bar-chart::before { - content: "\f080"; -} -.icon-twitter-sign::before { - content: "\f081"; -} -.icon-facebook-sign::before { - content: "\f082"; -} -.icon-camera-retro::before { - content: "\f083"; -} -.icon-key::before { - content: "\f084"; -} -.icon-gears:before, -.icon-cogs::before { - content: "\f085"; -} -.icon-comments::before { - content: "\f086"; -} -.icon-thumbs-up-alt::before { - content: "\f087"; -} -.icon-thumbs-down-alt::before { - content: "\f088"; -} -.icon-star-half::before { - content: "\f089"; -} -.icon-heart-empty::before { - content: "\f08a"; -} -.icon-signout::before { - content: "\f08b"; -} -.icon-linkedin-sign::before { - content: "\f08c"; -} -.icon-pushpin::before { - content: "\f08d"; -} -.icon-external-link::before { - content: "\f08e"; -} -.icon-signin::before { - content: "\f090"; -} -.icon-trophy::before { - content: "\f091"; -} -.icon-github-sign::before { - content: "\f092"; -} -.icon-upload-alt::before { - content: "\f093"; -} -.icon-lemon::before { - content: "\f094"; -} -.icon-phone::before { - content: "\f095"; -} -.icon-unchecked:before, -.icon-check-empty::before { - content: "\f096"; -} -.icon-bookmark-empty::before { - content: "\f097"; -} -.icon-phone-sign::before { - content: "\f098"; -} -.icon-twitter::before { - content: "\f099"; -} -.icon-facebook::before { - content: "\f09a"; -} -.icon-github::before { - content: "\f09b"; -} -.icon-unlock::before { - content: "\f09c"; -} -.icon-credit-card::before { - content: "\f09d"; -} -.icon-rss::before { - content: "\f09e"; -} -.icon-hdd::before { - content: "\f0a0"; -} -.icon-bullhorn::before { - content: "\f0a1"; -} -.icon-bell::before { - content: "\f0a2"; -} -.icon-certificate::before { - content: "\f0a3"; -} -.icon-hand-right::before { - content: "\f0a4"; -} -.icon-hand-left::before { - content: "\f0a5"; -} -.icon-hand-up::before { - content: "\f0a6"; -} -.icon-hand-down::before { - content: "\f0a7"; -} -.icon-circle-arrow-left::before { - content: "\f0a8"; -} -.icon-circle-arrow-right::before { - content: "\f0a9"; -} -.icon-circle-arrow-up::before { - content: "\f0aa"; -} -.icon-circle-arrow-down::before { - content: "\f0ab"; -} -.icon-globe::before { - content: "\f0ac"; -} -.icon-wrench::before { - content: "\f0ad"; -} -.icon-tasks::before { - content: "\f0ae"; -} -.icon-filter::before { - content: "\f0b0"; -} -.icon-briefcase::before { - content: "\f0b1"; -} -.icon-fullscreen::before { - content: "\f0b2"; -} -.icon-group::before { - content: "\f0c0"; -} -.icon-link::before { - content: "\f0c1"; -} -.icon-cloud::before { - content: "\f0c2"; -} -.icon-beaker::before { - content: "\f0c3"; -} -.icon-cut::before { - content: "\f0c4"; -} -.icon-copy::before { - content: "\f0c5"; -} -.icon-paperclip:before, -.icon-paper-clip::before { - content: "\f0c6"; -} -.icon-save::before { - content: "\f0c7"; -} -.icon-sign-blank::before { - content: "\f0c8"; -} -.icon-reorder::before { - content: "\f0c9"; -} -.icon-list-ul::before { - content: "\f0ca"; -} -.icon-list-ol::before { - content: "\f0cb"; -} -.icon-strikethrough::before { - content: "\f0cc"; -} -.icon-underline::before { - content: "\f0cd"; -} -.icon-table::before { - content: "\f0ce"; -} -.icon-magic::before { - content: "\f0d0"; -} -.icon-truck::before { - content: "\f0d1"; -} -.icon-pinterest::before { - content: "\f0d2"; -} -.icon-pinterest-sign::before { - content: "\f0d3"; -} -.icon-google-plus-sign::before { - content: "\f0d4"; -} -.icon-google-plus::before { - content: "\f0d5"; -} -.icon-money::before { - content: "\f0d6"; -} -.icon-caret-down::before { - content: "\f0d7"; -} -.icon-caret-up::before { - content: "\f0d8"; -} -.icon-caret-left::before { - content: "\f0d9"; -} -.icon-caret-right::before { - content: "\f0da"; -} -.icon-columns::before { - content: "\f0db"; -} -.icon-sort::before { - content: "\f0dc"; -} -.icon-sort-down::before { - content: "\f0dd"; -} -.icon-sort-up::before { - content: "\f0de"; -} -.icon-envelope::before { - content: "\f0e0"; -} -.icon-linkedin::before { - content: "\f0e1"; -} -.icon-rotate-left:before, -.icon-undo::before { - content: "\f0e2"; -} -.icon-legal::before { - content: "\f0e3"; -} -.icon-dashboard::before { - content: "\f0e4"; -} -.icon-comment-alt::before { - content: "\f0e5"; -} -.icon-comments-alt::before { - content: "\f0e6"; -} -.icon-bolt::before { - content: "\f0e7"; -} -.icon-sitemap::before { - content: "\f0e8"; -} -.icon-umbrella::before { - content: "\f0e9"; -} -.icon-paste::before { - content: "\f0ea"; -} -.icon-lightbulb::before { - content: "\f0eb"; -} -.icon-exchange::before { - content: "\f0ec"; -} -.icon-cloud-download::before { - content: "\f0ed"; -} -.icon-cloud-upload::before { - content: "\f0ee"; -} -.icon-user-md::before { - content: "\f0f0"; -} -.icon-stethoscope::before { - content: "\f0f1"; -} -.icon-suitcase::before { - content: "\f0f2"; -} -.icon-bell-alt::before { - content: "\f0f3"; -} -.icon-coffee::before { - content: "\f0f4"; -} -.icon-food::before { - content: "\f0f5"; -} -.icon-file-text-alt::before { - content: "\f0f6"; -} -.icon-building::before { - content: "\f0f7"; -} -.icon-hospital::before { - content: "\f0f8"; -} -.icon-ambulance::before { - content: "\f0f9"; -} -.icon-medkit::before { - content: "\f0fa"; -} -.icon-fighter-jet::before { - content: "\f0fb"; -} -.icon-beer::before { - content: "\f0fc"; -} -.icon-h-sign::before { - content: "\f0fd"; -} -.icon-plus-sign-alt::before { - content: "\f0fe"; -} -.icon-double-angle-left::before { - content: "\f100"; -} -.icon-double-angle-right::before { - content: "\f101"; -} -.icon-double-angle-up::before { - content: "\f102"; -} -.icon-double-angle-down::before { - content: "\f103"; -} -.icon-angle-left::before { - content: "\f104"; -} -.icon-angle-right::before { - content: "\f105"; -} -.icon-angle-up::before { - content: "\f106"; -} -.icon-angle-down::before { - content: "\f107"; -} -.icon-desktop::before { - content: "\f108"; -} -.icon-laptop::before { - content: "\f109"; -} -.icon-tablet::before { - content: "\f10a"; -} -.icon-mobile-phone::before { - content: "\f10b"; -} -.icon-circle-blank::before { - content: "\f10c"; -} -.icon-quote-left::before { - content: "\f10d"; -} -.icon-quote-right::before { - content: "\f10e"; -} -.icon-spinner::before { - content: "\f110"; -} -.icon-circle::before { - content: "\f111"; -} -.icon-mail-reply:before, -.icon-reply::before { - content: "\f112"; -} -.icon-github-alt::before { - content: "\f113"; -} -.icon-folder-close-alt::before { - content: "\f114"; -} -.icon-folder-open-alt::before { - content: "\f115"; -} -.icon-expand-alt::before { - content: "\f116"; -} -.icon-collapse-alt::before { - content: "\f117"; -} -.icon-smile::before { - content: "\f118"; -} -.icon-frown::before { - content: "\f119"; -} -.icon-meh::before { - content: "\f11a"; -} -.icon-gamepad::before { - content: "\f11b"; -} -.icon-keyboard::before { - content: "\f11c"; -} -.icon-flag-alt::before { - content: "\f11d"; -} -.icon-flag-checkered::before { - content: "\f11e"; -} -.icon-terminal::before { - content: "\f120"; -} -.icon-code::before { - content: "\f121"; -} -.icon-reply-all::before { - content: "\f122"; -} -.icon-mail-reply-all::before { - content: "\f122"; -} -.icon-star-half-full:before, -.icon-star-half-empty::before { - content: "\f123"; -} -.icon-location-arrow::before { - content: "\f124"; -} -.icon-crop::before { - content: "\f125"; -} -.icon-code-fork::before { - content: "\f126"; -} -.icon-unlink::before { - content: "\f127"; -} -.icon-question::before { - content: "\f128"; -} -.icon-info::before { - content: "\f129"; -} -.icon-exclamation::before { - content: "\f12a"; -} -.icon-superscript::before { - content: "\f12b"; -} -.icon-subscript::before { - content: "\f12c"; -} -.icon-eraser::before { - content: "\f12d"; -} -.icon-puzzle-piece::before { - content: "\f12e"; -} -.icon-microphone::before { - content: "\f130"; -} -.icon-microphone-off::before { - content: "\f131"; -} -.icon-shield::before { - content: "\f132"; -} -.icon-calendar-empty::before { - content: "\f133"; -} -.icon-fire-extinguisher::before { - content: "\f134"; -} -.icon-rocket::before { - content: "\f135"; -} -.icon-maxcdn::before { - content: "\f136"; -} -.icon-chevron-sign-left::before { - content: "\f137"; -} -.icon-chevron-sign-right::before { - content: "\f138"; -} -.icon-chevron-sign-up::before { - content: "\f139"; -} -.icon-chevron-sign-down::before { - content: "\f13a"; -} -.icon-html5::before { - content: "\f13b"; -} -.icon-css3::before { - content: "\f13c"; -} -.icon-anchor::before { - content: "\f13d"; -} -.icon-unlock-alt::before { - content: "\f13e"; -} -.icon-bullseye::before { - content: "\f140"; -} -.icon-ellipsis-horizontal::before { - content: "\f141"; -} -.icon-ellipsis-vertical::before { - content: "\f142"; -} -.icon-rss-sign::before { - content: "\f143"; -} -.icon-play-sign::before { - content: "\f144"; -} -.icon-ticket::before { - content: "\f145"; -} -.icon-minus-sign-alt::before { - content: "\f146"; -} -.icon-check-minus::before { - content: "\f147"; -} -.icon-level-up::before { - content: "\f148"; -} -.icon-level-down::before { - content: "\f149"; -} -.icon-check-sign::before { - content: "\f14a"; -} -.icon-edit-sign::before { - content: "\f14b"; -} -.icon-external-link-sign::before { - content: "\f14c"; -} -.icon-share-sign::before { - content: "\f14d"; -} -.icon-compass::before { - content: "\f14e"; -} -.icon-collapse::before { - content: "\f150"; -} -.icon-collapse-top::before { - content: "\f151"; -} -.icon-expand::before { - content: "\f152"; -} -.icon-euro:before, -.icon-eur::before { - content: "\f153"; -} -.icon-gbp::before { - content: "\f154"; -} -.icon-dollar:before, -.icon-usd::before { - content: "\f155"; -} -.icon-rupee:before, -.icon-inr::before { - content: "\f156"; -} -.icon-yen:before, -.icon-jpy::before { - content: "\f157"; -} -.icon-renminbi:before, -.icon-cny::before { - content: "\f158"; -} -.icon-won:before, -.icon-krw::before { - content: "\f159"; -} -.icon-bitcoin:before, -.icon-btc::before { - content: "\f15a"; -} -.icon-file::before { - content: "\f15b"; -} -.icon-file-text::before { - content: "\f15c"; -} -.icon-sort-by-alphabet::before { - content: "\f15d"; -} -.icon-sort-by-alphabet-alt::before { - content: "\f15e"; -} -.icon-sort-by-attributes::before { - content: "\f160"; -} -.icon-sort-by-attributes-alt::before { - content: "\f161"; -} -.icon-sort-by-order::before { - content: "\f162"; -} -.icon-sort-by-order-alt::before { - content: "\f163"; -} -.icon-thumbs-up::before { - content: "\f164"; -} -.icon-thumbs-down::before { - content: "\f165"; -} -.icon-youtube-sign::before { - content: "\f166"; -} -.icon-youtube::before { - content: "\f167"; -} -.icon-xing::before { - content: "\f168"; -} -.icon-xing-sign::before { - content: "\f169"; -} -.icon-youtube-play::before { - content: "\f16a"; -} -.icon-dropbox::before { - content: "\f16b"; -} -.icon-stackexchange::before { - content: "\f16c"; -} -.icon-instagram::before { - content: "\f16d"; -} -.icon-flickr::before { - content: "\f16e"; -} -.icon-adn::before { - content: "\f170"; -} -.icon-bitbucket::before { - content: "\f171"; -} -.icon-bitbucket-sign::before { - content: "\f172"; -} -.icon-tumblr::before { - content: "\f173"; -} -.icon-tumblr-sign::before { - content: "\f174"; -} -.icon-long-arrow-down::before { - content: "\f175"; -} -.icon-long-arrow-up::before { - content: "\f176"; -} -.icon-long-arrow-left::before { - content: "\f177"; -} -.icon-long-arrow-right::before { - content: "\f178"; -} -.icon-apple::before { - content: "\f179"; -} -.icon-windows::before { - content: "\f17a"; -} -.icon-android::before { - content: "\f17b"; -} -.icon-linux::before { - content: "\f17c"; -} -.icon-dribbble::before { - content: "\f17d"; -} -.icon-skype::before { - content: "\f17e"; -} -.icon-foursquare::before { - content: "\f180"; -} -.icon-trello::before { - content: "\f181"; -} -.icon-female::before { - content: "\f182"; -} -.icon-male::before { - content: "\f183"; -} -.icon-gittip::before { - content: "\f184"; -} -.icon-sun::before { - content: "\f185"; -} -.icon-moon::before { - content: "\f186"; -} -.icon-archive::before { - content: "\f187"; -} -.icon-bug::before { - content: "\f188"; -} -.icon-vk::before { - content: "\f189"; -} -.icon-weibo::before { - content: "\f18a"; -} -.icon-renren::before { - content: "\f18b"; -} + @font-face{font-family:FontAwesome;src:url('data:application/font-woff;base64,<%= grunt.file.read('node_modules/font-awesome/fonts/fontawesome-webfont.woff', {encoding: 'base64'}) %>') 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{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{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{display:inline}.fa-glass::before{content:"\f000"}.fa-music::before{content:"\f001"}.fa-search::before{content:"\f002"}.fa-envelope-alt::before{content:"\f003"}.fa-heart::before{content:"\f004"}.fa-star::before{content:"\f005"}.fa-star-empty::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-ok::before{content:"\f00c"}.fa-remove::before{content:"\f00d"}.fa-zoom-in::before{content:"\f00e"}.fa-zoom-out::before{content:"\f010"}.fa-off::before,.fa-power-off:before{content:"\f011"}.fa-signal::before{content:"\f012"}.fa-cog::before,.fa-gear:before{content:"\f013"}.fa-trash::before{content:"\f014"}.fa-home::before{content:"\f015"}.fa-file-alt::before{content:"\f016"}.fa-time::before{content:"\f017"}.fa-times::before{content:"\f00d";}.fa-road::before{content:"\f018"}.fa-download-alt::before{content:"\f019"}.fa-download::before{content:"\f01a"}.fa-upload::before{content:"\f01b"}.fa-inbox::before{content:"\f01c"}.fa-play-circle::before{content:"\f01d"}.fa-repeat::before,.fa-rotate-right: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-indent-left::before{content:"\f03b"}.fa-indent-right::before{content:"\f03c"}.fa-facetime-video::before{content:"\f03d"}.fa-picture::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{content:"\f044"}.fa-share::before{content:"\f045"}.fa-check::before{content:"\f046"}.fa-move::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-sign::before{content:"\f055"}.fa-minus-sign::before{content:"\f056"}.fa-remove-sign::before{content:"\f057"}.fa-ok-sign::before{content:"\f058"}.fa-question-sign::before{content:"\f059"}.fa-info-sign::before{content:"\f05a"}.fa-screenshot::before{content:"\f05b"}.fa-remove-circle::before{content:"\f05c"}.fa-ok-circle::before{content:"\f05d"}.fa-ban-circle::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-alt::before{content:"\f064"}.fa-resize-full::before{content:"\f065"}.fa-resize-small::before{content:"\f066"}.fa-plus::before{content:"\f067"}.fa-minus::before{content:"\f068"}.fa-asterisk::before{content:"\f069"}.fa-exclamation-sign::before{content:"\f06a"}.fa-gift::before{content:"\f06b"}.fa-leaf::before{content:"\f06c"}.fa-fire::before{content:"\f06d"}.fa-eye-open::before{content:"\f06e"}.fa-eye-close::before{content:"\f070"}.fa-warning-sign::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-close::before{content:"\f07b"}.fa-folder-open::before{content:"\f07c"}.fa-resize-vertical::before{content:"\f07d"}.fa-resize-horizontal::before{content:"\f07e"}.fa-bar-chart::before{content:"\f080"}.fa-twitter-sign::before{content:"\f081"}.fa-facebook-sign::before{content:"\f082"}.fa-camera-retro::before{content:"\f083"}.fa-key::before{content:"\f084"}.fa-cogs::before,.fa-gears:before{content:"\f085"}.fa-comments::before{content:"\f086"}.fa-thumbs-up-alt::before{content:"\f087"}.fa-thumbs-down-alt::before{content:"\f088"}.fa-star-half::before{content:"\f089"}.fa-heart-empty::before{content:"\f08a"}.fa-signout::before{content:"\f08b"}.fa-linkedin-sign::before{content:"\f08c"}.fa-pushpin::before{content:"\f08d"}.fa-external-link::before{content:"\f08e"}.fa-signin::before{content:"\f090"}.fa-trophy::before{content:"\f091"}.fa-github-sign::before{content:"\f092"}.fa-upload-alt::before{content:"\f093"}.fa-lemon::before{content:"\f094"}.fa-phone::before{content:"\f095"}.fa-check-empty::before,.fa-unchecked:before{content:"\f096"}.fa-bookmark-empty::before{content:"\f097"}.fa-phone-sign::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::before{content:"\f0a0"}.fa-bullhorn::before{content:"\f0a1"}.fa-bell::before{content:"\f0a2"}.fa-certificate::before{content:"\f0a3"}.fa-hand-right::before{content:"\f0a4"}.fa-hand-left::before{content:"\f0a5"}.fa-hand-up::before{content:"\f0a6"}.fa-hand-down::before{content:"\f0a7"}.fa-circle-arrow-left::before{content:"\f0a8"}.fa-circle-arrow-right::before{content:"\f0a9"}.fa-circle-arrow-up::before{content:"\f0aa"}.fa-circle-arrow-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-fullscreen::before{content:"\f0b2"}.fa-group::before{content:"\f0c0"}.fa-link::before{content:"\f0c1"}.fa-cloud::before{content:"\f0c2"}.fa-beaker::before{content:"\f0c3"}.fa-cut::before{content:"\f0c4"}.fa-copy::before{content:"\f0c5"}.fa-paper-clip::before,.fa-paperclip:before{content:"\f0c6"}.fa-save::before{content:"\f0c7"}.fa-sign-blank::before{content:"\f0c8"}.fa-reorder::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-sign::before{content:"\f0d3"}.fa-google-plus-sign::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-sort::before{content:"\f0dc"}.fa-sort-down::before{content:"\f0dd"}.fa-sort-up::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{content:"\f0e3"}.fa-dashboard::before{content:"\f0e4"}.fa-comment-alt::before{content:"\f0e5"}.fa-comments-alt::before{content:"\f0e6"}.fa-bolt::before{content:"\f0e7"}.fa-sitemap::before{content:"\f0e8"}.fa-umbrella::before{content:"\f0e9"}.fa-paste::before{content:"\f0ea"}.fa-lightbulb::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-alt::before{content:"\f0f3"}.fa-coffee::before{content:"\f0f4"}.fa-food::before{content:"\f0f5"}.fa-file-text-alt::before{content:"\f0f6"}.fa-building::before{content:"\f0f7"}.fa-hospital::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-sign::before{content:"\f0fd"}.fa-plus-sign-alt::before{content:"\f0fe"}.fa-double-angle-left::before{content:"\f100"}.fa-double-angle-right::before{content:"\f101"}.fa-double-angle-up::before{content:"\f102"}.fa-double-angle-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{content:"\f10b"}.fa-circle-blank::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-close-alt::before{content:"\f114"}.fa-folder-open-alt::before{content:"\f115"}.fa-expand-alt::before{content:"\f116"}.fa-collapse-alt::before{content:"\f117"}.fa-smile::before{content:"\f118"}.fa-frown::before{content:"\f119"}.fa-meh::before{content:"\f11a"}.fa-gamepad::before{content:"\f11b"}.fa-keyboard::before{content:"\f11c"}.fa-flag-alt::before{content:"\f11d"}.fa-flag-checkered::before{content:"\f11e"}.fa-terminal::before{content:"\f120"}.fa-code::before{content:"\f121"}.fa-mail-reply-all::before,.fa-reply-all::before{content:"\f122"}.fa-star-half-empty::before,.fa-star-half-full:before{content:"\f123"}.fa-location-arrow::before{content:"\f124"}.fa-crop::before{content:"\f125"}.fa-code-fork::before{content:"\f126"}.fa-unlink::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-off::before{content:"\f131"}.fa-shield::before{content:"\f132"}.fa-calendar-empty::before{content:"\f133"}.fa-fire-extinguisher::before{content:"\f134"}.fa-rocket::before{content:"\f135"}.fa-maxcdn::before{content:"\f136"}.fa-chevron-sign-left::before{content:"\f137"}.fa-chevron-sign-right::before{content:"\f138"}.fa-chevron-sign-up::before{content:"\f139"}.fa-chevron-sign-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-horizontal::before{content:"\f141"}.fa-ellipsis-vertical::before{content:"\f142"}.fa-rss-sign::before{content:"\f143"}.fa-play-sign::before{content:"\f144"}.fa-ticket::before{content:"\f145"}.fa-minus-sign-alt::before{content:"\f146"}.fa-check-minus::before{content:"\f147"}.fa-level-up::before{content:"\f148"}.fa-level-down::before{content:"\f149"}.fa-check-sign::before{content:"\f14a"}.fa-edit-sign::before{content:"\f14b"}.fa-external-link-sign::before{content:"\f14c"}.fa-share-sign::before{content:"\f14d"}.fa-compass::before{content:"\f14e"}.fa-collapse::before{content:"\f150"}.fa-collapse-top::before{content:"\f151"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-eur::before,.fa-euro:before{content:"\f153"}.fa-gbp::before{content:"\f154"}.fa-dollar:before,.fa-usd::before{content:"\f155"}.fa-inr::before,.fa-rupee:before{content:"\f156"}.fa-jpy::before,.fa-yen:before{content:"\f157"}.fa-cny::before,.fa-renminbi:before{content:"\f158"}.fa-krw::before,.fa-won:before{content:"\f159"}.fa-bitcoin:before,.fa-btc::before{content:"\f15a"}.fa-file::before{content:"\f15b"}.fa-file-text::before{content:"\f15c"}.fa-sort-by-alphabet::before{content:"\f15d"}.fa-sort-by-alphabet-alt::before{content:"\f15e"}.fa-sort-by-attributes::before{content:"\f160"}.fa-sort-by-attributes-alt::before{content:"\f161"}.fa-sort-by-order::before{content:"\f162"}.fa-sort-by-order-alt::before{content:"\f163"}.fa-thumbs-up::before{content:"\f164"}.fa-thumbs-down::before{content:"\f165"}.fa-youtube-sign::before{content:"\f166"}.fa-youtube::before{content:"\f167"}.fa-xing::before{content:"\f168"}.fa-xing-sign::before{content:"\f169"}.fa-youtube-play::before{content:"\f16a"}.fa-dropbox::before{content:"\f16b"}.fa-stackexchange::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-sign::before{content:"\f172"}.fa-tumblr::before{content:"\f173"}.fa-tumblr-sign::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::before{content:"\f185"}.fa-moon::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-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)}} diff --git a/src/General/css/style.css b/src/General/css/style.css index 74dff7b37..496fbf924 100755 --- a/src/General/css/style.css +++ b/src/General/css/style.css @@ -34,6 +34,9 @@ background-color: #F2F2F2; color: #888; } +.field::-webkit-search-decoration { + display: none; +} .move { cursor: move; overflow: hidden; @@ -73,6 +76,9 @@ a { div.center:not(.ad-cnt) { display: none !important; } +.page-num { + margin-right: -8px; +} /* fixed, z-index */ #overlay, @@ -118,10 +124,10 @@ div.center:not(.ad-cnt) { z-index: 10; } /* Header */ -.fixed.top body { +.fixed.top-header body { padding-top: 2em; } -.fixed.bottom body { +.fixed.bottom-header body { padding-bottom: 2em; } .fixed #header-bar { @@ -129,10 +135,10 @@ div.center:not(.ad-cnt) { left: 0; padding: 3px 4px 4px; } -.fixed.top #header-bar { +.fixed.top-header #header-bar { top: 0; } -.fixed.bottom #header-bar { +.fixed.bottom-header #header-bar { bottom: 0; } #header-bar { @@ -150,14 +156,14 @@ div.center:not(.ad-cnt) { position: relative; left: 150px; } -.fixed.top #header-bar { +.fixed.top-header #header-bar { border-bottom-width: 1px; } -.fixed.bottom #header-bar { +.fixed.bottom-header #header-bar { box-shadow: 0 -1px 2px rgba(0, 0, 0, .15); border-top-width: 1px; } -.fixed.bottom #header-bar .menu-button i { +.fixed.bottom-header #header-bar .menu-button i { border-top: none; border-bottom: 6px solid; } @@ -168,12 +174,12 @@ div.center:not(.ad-cnt) { box-shadow: none; transition: all .8s .6s cubic-bezier(.55, .055, .675, .19); } -.fixed.top #header-bar.autohide:not(:hover) { +.fixed.top-header #header-bar.autohide:not(:hover) { margin-bottom: -1em; -webkit-transform: translateY(-100%); transform: translateY(-100%); } -.fixed.bottom #header-bar.autohide:not(:hover) { +.fixed.bottom-header #header-bar.autohide:not(:hover) { -webkit-transform: translateY(100%); transform: translateY(100%); } @@ -192,10 +198,10 @@ div.center:not(.ad-cnt) { .fixed #header-bar #scroll-marker { display: block; } -.fixed.top #header-bar #scroll-marker { +.fixed.top-header #header-bar #scroll-marker { top: 100%; } -.fixed.bottom #header-bar #scroll-marker { +.fixed.bottom-header #header-bar #scroll-marker { bottom: 100%; } #header-bar a:not(.entry):not(.close) { @@ -252,7 +258,7 @@ div.center:not(.ad-cnt) { left: 0; transition: all .8s .6s cubic-bezier(.55, .055, .675, .19); } -.fixed.top #header-bar #notifications { +.fixed.top-header #header-bar #notifications { position: absolute; top: 100%; } @@ -284,11 +290,14 @@ div.center:not(.ad-cnt) { color: white; } .notification > .close { - padding: 6px; - top: 0; + 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; @@ -335,7 +344,7 @@ div.center:not(.ad-cnt) { } #fourchanx-settings > nav a.close { text-decoration: none; - padding: 2px; + padding: 0 2px; } .section-container { overflow: auto; @@ -432,6 +441,37 @@ div.center:not(.ad-cnt) { 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: -1.25em; +} +<% 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; +} + + /* Announcement Hiding */ :root.hide-announcement #globalMessage { display: none; @@ -638,10 +678,15 @@ a.hide-announcement { 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 { @@ -897,7 +942,7 @@ input#qr-filename:not(.edit) { opacity: .5; overflow: hidden; position: relative; - text-shadow: 0 1px 1px #000; + text-shadow: 0 0 2px #000; -moz-transition: opacity .25s ease-in-out; vertical-align: top; background-size: cover; @@ -929,8 +974,7 @@ input#qr-filename:not(.edit) { .remove { background: none; color: #e00; - font-weight: 700; - padding: 3px; + padding: 1px; } a:only-of-type > .remove { display: none; @@ -975,7 +1019,7 @@ a:only-of-type > .remove { } /* Menu */ -.menu-button { +.menu-button:not(.fa-bars) { display: inline-block; position: relative; cursor: pointer; @@ -1029,7 +1073,7 @@ a:only-of-type > .remove { left: 100%; top: -1px; } -.focused .submenu { +.focused > .submenu { display: block; } .imp-exp-result { @@ -1274,4 +1318,12 @@ a:only-of-type > .remove { :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; + } } \ No newline at end of file diff --git a/src/General/css/yotsuba-b.css b/src/General/css/yotsuba-b.css index d3bdd0234..5de2a5bd8 100755 --- a/src/General/css/yotsuba-b.css +++ b/src/General/css/yotsuba-b.css @@ -12,7 +12,7 @@ font-size: 9pt; color: #89A; } -:root.yotsuba-b #header-bar a, :root.yotsuba-b #notifications a { +:root.yotsuba #board-list a, :root.yotsuba #shortcuts a { color: #34345C; } diff --git a/src/General/css/yotsuba.css b/src/General/css/yotsuba.css index a06ab02fb..ac1725352 100755 --- a/src/General/css/yotsuba.css +++ b/src/General/css/yotsuba.css @@ -12,7 +12,7 @@ font-size: 9pt; color: #B86; } -:root.yotsuba #header-bar a, :root.yotsuba #notifications a { +:root.yotsuba #header-bar a { color: #800000; } diff --git a/src/General/html/Build/post.html b/src/General/html/Build/post.html index cd01024bb..1773310e9 100755 --- a/src/General/html/Build/post.html +++ b/src/General/html/Build/post.html @@ -1,47 +1,23 @@ """#{if isOP then '' else "
>>
"}
- - #{if isOP then fileHTML else ''} - """ diff --git a/src/General/html/Features/Index-navlinks.html b/src/General/html/Features/Index-navlinks.html new file mode 100644 index 000000000..7caf98685 --- /dev/null +++ b/src/General/html/Features/Index-navlinks.html @@ -0,0 +1,4 @@ +[Catalog]  +[]  + + diff --git a/src/General/html/Features/Index-pagelist.html b/src/General/html/Features/Index-pagelist.html new file mode 100644 index 000000000..fd5f4020b --- /dev/null +++ b/src/General/html/Features/Index-pagelist.html @@ -0,0 +1,14 @@ + +
+ + diff --git a/src/General/html/Settings/Filter-guide.html b/src/General/html/Settings/Filter-guide.html index 3e78003d2..579a12137 100755 --- a/src/General/html/Settings/Filter-guide.html +++ b/src/General/html/Settings/Filter-guide.html @@ -23,7 +23,7 @@ For example: highlight; or highlight:wallpaper;.
  • - Highlighted OPs will have their threads put on top of board pages by default.
    + Highlighted OPs will have their threads put on top of the board index by default.
    For example: top:yes; or top:no;.
  • diff --git a/src/General/lib/$.coffee b/src/General/lib/$.coffee index ddafd149a..d91010ae8 100755 --- a/src/General/lib/$.coffee +++ b/src/General/lib/$.coffee @@ -1,31 +1,17 @@ -String::capitalize = -> - @charAt(0).toUpperCase() + @slice(1); - -String::contains = (string) -> - @indexOf(string) > -1 - -Array::contains = (object) -> - @indexOf(object) > -1 - -Array::indexOf = (object) -> - i = @length - while i-- - return i if @[i] is object - return i - # loosely follows the jquery api: # http://api.jquery.com/ # not chainable $ = (selector, root=d.body) -> root.querySelector selector -$.extend = (object, properties) -> - for key, val of properties - continue unless properties.hasOwnProperty key - object[key] = val +$.extend = (obj, prop) -> + obj[key] = val for key, val of prop when prop.hasOwnProperty key return -$.DAY = 24 * ($.HOUR = 60 * ($.MINUTE = 60 * ($.SECOND = 1000))) +$.DAY = 24 * + $.HOUR = 60 * + $.MINUTE = 60 * + $.SECOND = 1000 $.id = (id) -> d.getElementById id @@ -66,7 +52,7 @@ $.ajax = do -> type or= form and 'post' or 'get' r.open type, url, !sync if whenModified - r.setRequestHeader 'If-Modified-Since', lastModified[url] or '0' + r.setRequestHeader 'If-Modified-Since', lastModified[url] if url of lastModified $.on r, 'load', -> lastModified[url] = r.getResponseHeader 'Last-Modified' $.extend r, options $.extend r.upload, upCallbacks @@ -137,7 +123,7 @@ $.toggleClass = (el, className) -> el.classList.toggle className $.hasClass = (el, className) -> - el.classList.contains className + className in el.classList $.rm = do -> if 'remove' of Element:: @@ -147,7 +133,7 @@ $.rm = do -> $.rmAll = (root) -> # jsperf.com/emptify-element - while node = root.firstChild + for node in [root.childNodes...] # HTMLSelectElement.remove !== Element.remove root.removeChild node return @@ -287,7 +273,7 @@ $.sync = do -> chrome.storage.onChanged.addListener (changes) -> for key of changes if cb = $.syncing[key] - cb changes[key].newValue + cb changes[key].newValue, key return (key, cb) -> $.syncing[key] = cb @@ -330,9 +316,8 @@ $.get = (key, val, cb) -> count = 0 done = (item) -> - {lastError} = chrome.runtime - if lastError - c.error lastError, lastError.message or 'No message.' + if chrome.runtime.lastError + c.error chrome.runtime.lastError.message $.extend items, item cb items unless --count @@ -368,11 +353,12 @@ $.set = do -> set() <% } else { %> + # http://wiki.greasespot.net/Main_Page $.sync = do -> - $.on window, 'storage', (e) -> - if cb = $.syncing[e.key] - cb JSON.parse e.newValue + $.on window, 'storage', ({key, newValue}) -> + if cb = $.syncing[key] + cb JSON.parse(newValue), key (key, cb) -> $.syncing[g.NAMESPACE + key] = cb $.delete = (keys) -> diff --git a/src/General/lib/callbacks.class b/src/General/lib/callbacks.class new file mode 100644 index 000000000..7a832f43c --- /dev/null +++ b/src/General/lib/callbacks.class @@ -0,0 +1,20 @@ +class Callbacks + push: ({name, cb}) -> @[name] = cb + + clean: -> + @rm name for name of @ when @hasOwnProperty name + return + + rm: (name) -> delete @[name] + + execute: (node) -> + for name of @ when @hasOwnProperty name + try + @[name].call node + catch err + errors = [] unless errors + errors.push + message: ['"', name, '" crashed on node No.', node, ' (', node.board, ').'].join('') + error: err + + Main.handleErrors errors if errors diff --git a/src/General/lib/classes.coffee b/src/General/lib/classes.coffee index 74697eec3..5654ff914 100755 --- a/src/General/lib/classes.coffee +++ b/src/General/lib/classes.coffee @@ -1,6 +1,8 @@ +<%= grunt.file.read('src/General/lib/callbacks.class') %> <%= grunt.file.read('src/General/lib/board.class') %> <%= grunt.file.read('src/General/lib/thread.class') %> <%= grunt.file.read('src/General/lib/post.class') %> <%= grunt.file.read('src/General/lib/clone.class') %> <%= grunt.file.read('src/General/lib/databoard.class') %> -<%= grunt.file.read('src/General/lib/notice.class') %> \ No newline at end of file +<%= grunt.file.read('src/General/lib/notice.class') %> +<%= grunt.file.read('src/General/lib/randomaccesslist.class') %> \ No newline at end of file diff --git a/src/General/lib/databoard.class b/src/General/lib/databoard.class index 6a96c6c4f..1dc37e235 100755 --- a/src/General/lib/databoard.class +++ b/src/General/lib/databoard.class @@ -74,7 +74,9 @@ class DataBoard ajaxClean: (boardID) -> $.cache "//a.4cdn.org/#{boardID}/threads.json", (e) => - return if e.target.status isnt 200 + if e.target.status isnt 200 + @delete boardID if e.target.status is 404 + return board = @data.boards[boardID] threads = {} for page in JSON.parse e.target.response diff --git a/src/General/lib/notice.class b/src/General/lib/notice.class index 60b46ff77..ea5f6f360 100644 --- a/src/General/lib/notice.class +++ b/src/General/lib/notice.class @@ -1,7 +1,7 @@ class Notice constructor: (type, content, @timeout) -> @el = $.el 'div', - innerHTML: '×
    ' + innerHTML: '
    ' @el.style.opacity = 0 @setType type $.on @el.firstElementChild, 'click', @close @@ -19,7 +19,7 @@ class Notice $.on d, 'visibilitychange', @add return $.off d, 'visibilitychange', @add - $.add $.id('notifications'), @el + $.add Header.noticesRoot, @el @el.clientHeight # force reflow @el.style.opacity = 1 setTimeout @close, @timeout * $.SECOND if @timeout diff --git a/src/General/lib/polyfill.coffee b/src/General/lib/polyfill.coffee index 0ad7c1fe8..08ce47683 100755 --- a/src/General/lib/polyfill.coffee +++ b/src/General/lib/polyfill.coffee @@ -6,7 +6,7 @@ Polyfill = @visibility() <% } %> notificationPermission: -> - return if !window.Notification or 'permission' of Notification + return if !window.Notification or 'permission' of Notification or !window.webkitNotifications Object.defineProperty Notification, 'permission', get: -> switch webkitNotifications.checkPermission() @@ -27,7 +27,7 @@ Polyfill = cb new Blob [ui8a], type: 'image/png' visibility: -> # page visibility API - return unless 'webkitHidden' of document + return if 'visibilityState' of d Object.defineProperties HTMLDocument.prototype, visibilityState: get: -> @webkitVisibilityState diff --git a/src/General/lib/post.class b/src/General/lib/post.class index a10d570b9..0943e520a 100755 --- a/src/General/lib/post.class +++ b/src/General/lib/post.class @@ -1,11 +1,12 @@ class Post - @callbacks = [] + @callbacks = new Callbacks() toString: -> @ID constructor: (root, @thread, @board, that={}) -> @ID = +root.id[2..] @fullID = "#{@board}.#{@ID}" + @cleanup root if that.isOriginalMarkup post = $ '.post', root info = $ '.postInfo', post @nodes = @@ -55,7 +56,7 @@ class Post @parseComment() @parseQuotes() - @parseFile(that) + @parseFile that @clones = [] g.posts[@fullID] = thread.posts[@] = board.posts[@] = @ @@ -111,7 +112,7 @@ class Post # ES6 Set when? fullID = "#{match[1]}.#{match[2]}" - @quotes.push fullID unless @quotes.contains fullID + @quotes.push fullID unless fullID in @quotes parseFile: (that) -> return unless (fileEl = $ '.file', @nodes.post) and thumb = $ 'img[data-md5]', fileEl @@ -149,6 +150,13 @@ class Post if @file.isImage = /(jpg|png|gif)$/i.test @file.name @file.dimensions = fileText.textContent.match(/\d+x\d+/)[0] + cleanup: (root) -> + for node in $$ '.mobile', root + $.rm node + for node in $$ '.desktop', root + $.rmClass node, 'desktop' + return + kill: (file, now) -> now or= new Date() if file @@ -203,6 +211,12 @@ class Post $.rmClass quotelink, 'deadlink' return + collect: -> + @kill() + delete g.posts[@fullID] + delete @thread.posts[@] + delete @board.posts[@] + addClone: (context) -> new Clone @, context diff --git a/src/General/lib/randomaccesslist.class b/src/General/lib/randomaccesslist.class new file mode 100644 index 000000000..0cc754d60 --- /dev/null +++ b/src/General/lib/randomaccesslist.class @@ -0,0 +1,58 @@ +class RandomAccessList + constructor: -> + @length = 0 + + push: (item) -> + {ID} = item + return if @[ID] + {last} = @ + item.prev = last + @[ID] = item + @last = if last + last.next = item + else + @first = item + @length++ + + after: (root, item) -> + return if item.prev is root + + @rmi item + + {next} = root + root.next = item + item.prev = root + item.next = next + next.prev = item + + prepend: (item) -> + {first} = @ + return if item is first or not @[item.ID] + @rmi item + item.next = first + first.prev = item + @first = item + delete item.prev + + shift: -> + @rm @first.ID + + rm: (ID) -> + item = @[ID] + return unless item + delete @[ID] + @length-- + @rmi item + delete item.next + delete item.prev + + rmi: (item) -> + {prev, next} = item + if prev + prev.next = next + else + @first = next + if next + next.prev = prev + else + @last = prev \ No newline at end of file diff --git a/src/General/lib/thread.class b/src/General/lib/thread.class index 1f15bf714..f05bfeebf 100755 --- a/src/General/lib/thread.class +++ b/src/General/lib/thread.class @@ -1,13 +1,50 @@ class Thread - @callbacks = [] + @callbacks = new Callbacks() toString: -> @ID constructor: (@ID, @board) -> - @fullID = "#{@board}.#{@ID}" - @posts = {} + @fullID = "#{@board}.#{@ID}" + @posts = {} + @isSticky = false + @isClosed = false + @postLimit = false + @fileLimit = false g.threads[@fullID] = board.threads[@] = @ + setPage: (pageNum) -> + icon = $ '.page-num', @OP.nodes.post + for key in ['title', 'textContent'] + icon[key] = icon[key].replace /\d+/, pageNum + return + setStatus: (type, status) -> + name = "is#{type}" + return if @[name] is status + @[name] = status + return unless @OP + typeLC = type.toLowerCase() + unless status + $.rm $ ".#{typeLC}Icon", @OP.nodes.info + return + icon = $.el 'img', + src: "//s.4cdn.org/image/#{typeLC}#{if window.devicePixelRatio >= 2 then '@2x' else ''}.gif" + alt: type + title: type + className: "#{typeLC}Icon" + root = if type is 'Closed' and @isSticky + $ '.stickyIcon', @OP.nodes.info + else if g.VIEW is 'index' + $ '.page-num', @OP.nodes.info + else + $ '[title="Quote this post"]', @OP.nodes.info + $.after root, [$.tn(' '), icon] + kill: -> @isDead = true @timeOfDeath = Date.now() + + collect: -> + for postID, post in @posts + post.collect() + delete g.threads[@fullID] + delete @board.threads[@] diff --git a/src/General/meta/manifest.json b/src/General/meta/manifest.json index 53039fd37..d1c79c7a7 100755 --- a/src/General/meta/manifest.json +++ b/src/General/meta/manifest.json @@ -15,7 +15,7 @@ "run_at": "document_start" }], "homepage_url": "<%= meta.page %>", - "minimum_chrome_version": "27", + "minimum_chrome_version": "<%= meta.min.chrome %>", "permissions": [ "storage" ] diff --git a/src/General/meta/metadata.js b/src/General/meta/metadata.js index 45a414083..2b8c066f2 100755 --- a/src/General/meta/metadata.js +++ b/src/General/meta/metadata.js @@ -1,8 +1,8 @@ // ==UserScript== // @name <%= meta.name %> // @version <%= version %> -// @minGMVer 1.13 -// @minFFVer 22 +// @minGMVer <%= meta.min.greasemonkey %> +// @minFFVer <%= meta.min.firefox %> // @namespace <%= name %> // @description <%= description %> // @license MIT; <%= meta.repo %>blob/<%= meta.mainBranch %>/LICENSE diff --git a/src/Images/FappeTyme.coffee b/src/Images/FappeTyme.coffee index 29ebf2d30..200907e1d 100755 --- a/src/Images/FappeTyme.coffee +++ b/src/Images/FappeTyme.coffee @@ -2,33 +2,21 @@ FappeTyme = init: -> return if !(Conf['Fappe Tyme'] or Conf['Werk Tyme']) or g.VIEW is 'catalog' or g.BOARD is 'f' - if Conf['Fappe Tyme'] + for type in ["Fappe", "Werk"] when Conf["#{type} Tyme"] + lc = type.toLowerCase() el = $.el 'label', - innerHTML: " Fappe Tyme" - title: 'Fappe Tyme' + innerHTML: " #{type} Tyme" + title: "#{type} Tyme" - FappeTyme.fappe = input = el.firstElementChild - - $.on input, 'change', FappeTyme.cb.fappe + FappeTyme[lc] = input = el.firstElementChild + $.on input, 'change', FappeTyme.cb.toggle.bind input $.event 'AddMenuEntry', type: 'header' el: el order: 97 - if Conf['Werk Tyme'] - el = $.el 'label', - innerHTML: " Werk Tyme" - title: 'Werk Tyme' - - FappeTyme.werk = input = el.firstElementChild - - $.on input, 'change', FappeTyme.cb.werk - - $.event 'AddMenuEntry', - type: 'header' - el: el - order: 98 + FappeTyme.cb.set lc if Conf[lc] Post.callbacks.push name: 'Fappe Tyme' @@ -39,9 +27,11 @@ FappeTyme = $.addClass @nodes.root, "noFile" cb: - fappe: -> - $.toggleClass doc, 'fappeTyme' - FappeTyme.fappe.checked = $.hasClass doc, 'fappeTyme' - werk: -> - $.toggleClass doc, 'werkTyme' - FappeTyme.werk.checked = $.hasClass doc, 'werkTyme' \ No newline at end of file + set: (type) -> + FappeTyme[type].checked = Conf[type] + $["#{if Conf[type] then 'add' else 'rm'}Class"] doc, "#{type}Tyme" + + toggle: -> + Conf[@name] = !Conf[@name] + FappeTyme.cb.set @name + $.cb.checked.call FappeTyme[@name] \ No newline at end of file diff --git a/src/Images/Gallery.coffee b/src/Images/Gallery.coffee index 1397ac21e..efa7b20df 100644 --- a/src/Images/Gallery.coffee +++ b/src/Images/Gallery.coffee @@ -6,7 +6,7 @@ Gallery = href: 'javascript:;' id: 'appchan-gal' title: 'Gallery' - className: 'fourchanx-icon icon-picture' + className: 'fa fa-picture' textContent: 'Gallery' $.on el, 'click', @cb.toggle @@ -247,7 +247,7 @@ Gallery = label = $.el 'label', innerHTML: " #{name}" input = label.firstElementChild - if ['Fit Width', 'Fit Height', 'Hide Thumbnails'].contains name + if name in ['Fit Width', 'Fit Height', 'Hide Thumbnails'] $.on input, 'change', Gallery.cb.setFitness input.checked = Conf[name] $.event 'change', null, input diff --git a/src/Images/ImageExpand.coffee b/src/Images/ImageExpand.coffee index 9a882f9fc..135a50481 100755 --- a/src/Images/ImageExpand.coffee +++ b/src/Images/ImageExpand.coffee @@ -3,12 +3,12 @@ ImageExpand = return if g.VIEW is 'catalog' or !Conf['Image Expansion'] @EAI = $.el 'a', - className: 'expand-all-shortcut fourchanx-icon icon-resize-full' + className: 'expand-all-shortcut fa fa-expand' textContent: 'EAI' title: 'Expand All Images' href: 'javascript:;' $.on @EAI, 'click', ImageExpand.cb.toggleAll - Header.addShortcut @EAI, 2 + Header.addShortcut @EAI, 3 Post.callbacks.push name: 'Image Expansion' @@ -33,11 +33,11 @@ ImageExpand = toggleAll: -> $.event 'CloseMenu' if ImageExpand.on = $.hasClass ImageExpand.EAI, 'expand-all-shortcut' - ImageExpand.EAI.className = 'contract-all-shortcut fourchanx-icon icon-resize-small' + 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 fourchanx-icon icon-resize-full' + ImageExpand.EAI.className = 'expand-all-shortcut fa fa-expand' ImageExpand.EAI.title = 'Expand All Images' func = ImageExpand.contract for ID, post of g.posts @@ -46,7 +46,7 @@ ImageExpand = continue unless file and file.isImage and doc.contains post.nodes.root if ImageExpand.on and (!Conf['Expand spoilers'] and file.isSpoiler or - Conf['Expand from here'] and file.thumb.getBoundingClientRect().top < 0) + Conf['Expand from here'] and Header.getTopOf(file.thumb) < 0) continue $.queueTask func, post return @@ -62,7 +62,7 @@ ImageExpand = # Scroll back to the thumbnail when contracting the image # to avoid being left miles away from the relevant post. {root} = post.nodes - rect = (if Conf['Advance on contract'] then do -> + {top, left} = (if Conf['Advance on contract'] then do -> next = root while next = $.x "following::div[contains(@class,'postContainer')][1]", next continue if $('.stub', next) or next.offsetHeight is 0 @@ -72,13 +72,13 @@ ImageExpand = root ).getBoundingClientRect() - if rect.top < 0 - y = rect.top + if top < 0 + y = top if Conf['Fixed Header'] and not Conf['Bottom Header'] headRect = Header.bar.getBoundingClientRect() y -= headRect.top + headRect.height - if rect.left < 0 + if left < 0 x = -window.scrollX window.scrollBy x, y if x or y ImageExpand.contract post @@ -116,13 +116,12 @@ ImageExpand = $.addClass post.nodes.root, 'expanded-image' $.rmClass post.file.thumb, 'expanding' return - prev = post.nodes.root.getBoundingClientRect() + {bottom} = post.nodes.root.getBoundingClientRect() $.queueTask -> $.addClass post.nodes.root, 'expanded-image' $.rmClass post.file.thumb, 'expanding' - return unless prev.top + prev.height <= 0 - curr = post.nodes.root.getBoundingClientRect() - window.scrollBy 0, curr.height - prev.height + curr.top - prev.top + return unless bottom <= 0 + window.scrollBy 0, post.nodes.root.getBoundingClientRect().bottom - bottom error: -> post = Get.postFromNode @ diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index 164756d07..6774be36e 100755 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -288,8 +288,13 @@ Linkify = LiveLeak: regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/ el: (a) -> - $.el 'object', - innerHTML: "" + el = $.el 'iframe', + width: "640", + height: "360", + src: "http://www.liveleak.com/ll_embed?i=#{a.dataset.uid}", + frameborder: "0" + el.setAttribute "allowfullscreen", "true" + el MediaCrush: regExp: /.*(?:mediacru.sh\/)([0-9a-z_]+)/i @@ -298,7 +303,7 @@ Linkify = el = $.el 'div' $.cache "https://mediacru.sh/#{a.dataset.uid}.json", -> {status} = @ - return div.innerHTML = "ERROR #{status}" unless [200, 304].contains status + return div.innerHTML = "ERROR #{status}" unless status in [200, 304] {files} = JSON.parse @response for type in ['video/mp4', 'video/ogv', 'image/svg+xml', 'image/png', 'image/gif', 'image/jpeg', 'image/svg', 'audio/mpeg'] for file in files @@ -404,8 +409,10 @@ Linkify = YouTube: regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/ el: (a) -> - $.el 'iframe', + el = $.el 'iframe', src: "//www.youtube.com/embed/#{a.dataset.uid}#{if a.dataset.option then '#' + a.dataset.option else ''}?wmode=opaque" + el.setAttribute "allowfullscreen", "true" + el title: api: (uid) -> "https://gdata.youtube.com/feeds/api/videos/#{uid}?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode" text: (data) -> data.entry.title.$t diff --git a/src/Menu/Menu.coffee b/src/Menu/Menu.coffee index 3589ac5b2..0cba5448f 100755 --- a/src/Menu/Menu.coffee +++ b/src/Menu/Menu.coffee @@ -10,18 +10,23 @@ Menu = node: -> if @isClone $.on $('.menu-button', @nodes.info), 'click', Menu.toggle - else - $.add @nodes.info, [$.tn('\u00A0'), Menu.makeButton()] + return + $.add @nodes.info, Menu.makeButton() makeButton: do -> - a = $.el 'a', - className: 'menu-button brackets-wrap' - innerHTML: '' - href: 'javascript:;' + frag = null -> - button = a.cloneNode true - $.on button, 'click', Menu.toggle - button + unless frag? + frag = $.nodes [ + $.tn(' ') + $.el 'a', + className: 'menu-button' + innerHTML: '[]' + href: 'javascript:;' + ] + clone = frag.cloneNode true + $.on clone.lastElementChild, 'click', Menu.toggle + clone toggle: (e) -> post = Get.postFromNode @ diff --git a/src/Miscellaneous/CatalogLinks.coffee b/src/Miscellaneous/CatalogLinks.coffee index 48a678874..e123e537c 100755 --- a/src/Miscellaneous/CatalogLinks.coffee +++ b/src/Miscellaneous/CatalogLinks.coffee @@ -1,11 +1,10 @@ CatalogLinks = init: -> return unless Conf['Catalog Links'] - el = $.el 'label', + CatalogLinks.el = el = $.el 'label', id: 'toggleCatalog' href: 'javascript:;' innerHTML: " Catalog Links" - title: "Turn catalog links #{if Conf['Header catalog links'] then 'off' else 'on'}." input = $ 'input', el $.on input, 'change', @toggle @@ -22,32 +21,33 @@ CatalogLinks = toggle: -> $.event 'CloseMenu' - $.set 'Header catalog links', useCatalog = @checked - CatalogLinks.set useCatalog + $.set 'Header catalog links', @checked + CatalogLinks.set @checked set: (useCatalog) -> path = if useCatalog then 'catalog' else '' - for a in $$ """ - #board-list a:not(.catalog), - #boardNavDesktopFoot a - """ - board = a.pathname.split('/')[1] - continue if ['f', 'status', '4chan'].contains(board) or !board - if Conf['External Catalog'] - a.href = if useCatalog - CatalogLinks.external board - else - "/#{board}/" - else - a.pathname = "/#{board}/#{path}" - @title = "Turn catalog links #{if useCatalog then 'off' else 'on'}." + + generateURL = if useCatalog and Conf['External Catalog'] + CatalogLinks.external + else + (board) -> a.href = "/#{board}/#{path}" + + for a in $$ """#board-list a:not(.catalog), #boardNavDesktopFoot a""" + continue if a.hostname not in ['boards.4chan.org', 'catalog.neet.tv', '4index.gropes.us'] or + !(board = a.pathname.split('/')[1]) or + board in ['f', 'status', '4chan'] + + # Href is easier than pathname because then we don't have + # conditions where External Catalog has been disabled between switches. + a.href = generateURL board + + CatalogLinks.el.title = "Turn catalog links #{if useCatalog then 'off' else 'on'}." external: (board) -> - return ( - if ['a', 'c', 'g', 'co', 'k', 'm', 'o', 'p', 'v', 'vg', 'w', 'cm', '3', 'adv', 'an', 'cgl', 'ck', 'diy', 'fa', 'fit', 'int', 'jp', 'mlp', 'lit', 'mu', 'n', 'po', 'sci', 'toy', 'trv', 'tv', 'vp', 'x', 'q'].contains board + switch board + when 'a', 'c', 'g', 'co', 'k', 'm', 'o', 'p', 'v', 'vg', 'w', 'cm', '3', 'adv', 'an', 'cgl', 'ck', 'diy', 'fa', 'fit', 'int', 'jp', 'mlp', 'lit', 'mu', 'n', 'po', 'sci', 'toy', 'trv', 'tv', 'vp', 'x', 'q' "http://catalog.neet.tv/#{board}" - else if ['d', 'e', 'gif', 'h', 'hr', 'hc', 'r9k', 's', 'pol', 'soc', 'u', 'i', 'ic', 'hm', 'r', 'w', 'wg', 'wsg', 't', 'y'].contains board + when 'd', 'e', 'gif', 'h', 'hr', 'hc', 'r9k', 's', 'pol', 'soc', 'u', 'i', 'ic', 'hm', 'r', 'w', 'wg', 'wsg', 't', 'y' "http://4index.gropes.us/#{board}" else - "/#{board}/catalog" - ) \ No newline at end of file + "/#{board}/catalog" \ No newline at end of file diff --git a/src/Miscellaneous/ExpandComment.coffee b/src/Miscellaneous/ExpandComment.coffee index db55f37ba..cb9223ae9 100755 --- a/src/Miscellaneous/ExpandComment.coffee +++ b/src/Miscellaneous/ExpandComment.coffee @@ -32,7 +32,7 @@ ExpandComment = post.nodes.comment = post.nodes.shortComment parse: (req, a, post) -> {status} = req - unless [200, 304].contains status + unless status in [200, 304] a.textContent = "Error #{req.statusText} (#{status})" return diff --git a/src/Miscellaneous/ExpandThread.coffee b/src/Miscellaneous/ExpandThread.coffee index a33a79ef4..3f5a1d3ed 100755 --- a/src/Miscellaneous/ExpandThread.coffee +++ b/src/Miscellaneous/ExpandThread.coffee @@ -1,111 +1,97 @@ ExpandThread = init: -> return if g.VIEW isnt 'index' or !Conf['Thread Expansion'] + @statuses = {} + $.on d, 'IndexRefresh', @onIndexRefresh - Thread.callbacks.push - name: 'Thread Expansion' - cb: @node - - node: -> - return unless span = $.x 'following-sibling::span[contains(@class,"summary")][1]', @OP.nodes.root - [posts, files] = span.textContent.match /\d+/g - a = $.el 'a', - textContent: ExpandThread.text '+', posts, files - className: 'summary' - href: 'javascript:;' + setButton: (thread) -> + return unless a = $.x 'following-sibling::a[contains(@class,"summary")][1]', thread.OP.nodes.root + a.textContent = ExpandThread.text '+', a.textContent.match(/\d+/g)... $.on a, 'click', ExpandThread.cbToggle - $.replace span, a + + onIndexRefresh: -> + for threadID, status of ExpandThread.statuses + status.req?.abort() + delete ExpandThread.statuses[threadID] + for threadID, thread of g.BOARD.threads + ExpandThread.setButton thread + return text: (status, posts, files) -> "#{status} #{posts} post#{if posts > 1 then 's' else ''}" + (if +files then " and #{files} image repl#{if files > 1 then 'ies' else 'y'}" else "") + " #{if status is '-' then 'shown' else 'omitted'}." - cbToggle: -> + cbToggle: (e) -> + return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0 + e.preventDefault() ExpandThread.toggle Get.threadFromNode @ toggle: (thread) -> threadRoot = thread.OP.nodes.root.parentNode - a = $ '.summary', threadRoot - - switch thread.isExpanded - when false, undefined - for post in $$ '.thread > .postContainer', threadRoot - ExpandComment.expand Get.postFromRoot post - unless a - thread.isExpanded = true - return - thread.isExpanded = 'loading' - [posts, files] = a.textContent.match /\d+/g - a.textContent = ExpandThread.text '...', posts, files - $.cache "//a.4cdn.org/#{thread.board}/res/#{thread}.json", -> - ExpandThread.parse @, thread, a - - when 'loading' - thread.isExpanded = false - return unless a - [posts, files] = a.textContent.match /\d+/g - a.textContent = ExpandThread.text '+', posts, files - - when true - thread.isExpanded = false - #goddamit moot - num = if thread.isSticky - 1 - else switch g.BOARD.ID - # XXX boards config - when 'b', 'vg' then 3 - when 't' then 1 - else 5 - posts = $$ ".thread > .replyContainer", threadRoot - for post in [thread.OP.nodes.root].concat posts[-num..] - ExpandComment.contract Get.postFromRoot post - return unless a - postsCount = 0 - filesCount = 0 - for reply in posts[...-num] - if Conf['Quote Inlining'] - # rm clones - inlined.click() while inlined = $ '.inlined', reply - postsCount++ - filesCount++ if 'file' of Get.postFromRoot reply - $.rm reply - a.textContent = ExpandThread.text '+', postsCount, filesCount - return - - parse: (req, thread, a) -> - return if a.textContent[0] is '+' - unless [200, 304].contains req.status - a.textContent = "Error #{req.statusText} (#{req.status})" - $.off a, 'click', ExpandThread.cbToggle + return unless a = $ '.summary', threadRoot + if thread.ID of ExpandThread.statuses + ExpandThread.contract thread, a, threadRoot + else + ExpandThread.expand thread, a, threadRoot + expand: (thread, a, threadRoot) -> + ExpandThread.statuses[thread] = status = {} + a.textContent = ExpandThread.text '...', a.textContent.match(/\d+/g)... + status.req = $.cache "//a.4cdn.org/#{thread.board}/res/#{thread}.json", -> + delete status.req + ExpandThread.parse @, thread, a + contract: (thread, a, threadRoot) -> + status = ExpandThread.statuses[thread] + delete ExpandThread.statuses[thread] + if status.req + status.req.abort() + a.textContent = ExpandThread.text '+', a.textContent.match(/\d+/g)... if a return - thread.isExpanded = true + replies = $$ '.thread > .replyContainer', threadRoot + if Conf['Show Replies'] + num = if thread.isSticky + 1 + else switch g.BOARD.ID + # XXX boards config + when 'b', 'vg' then 3 + when 't' then 1 + else 5 + replies = replies[...-num] + postsCount = 0 + filesCount = 0 + for reply in replies + # rm clones + inlined.click() while inlined = $ '.inlined', reply if Conf['Quote Inlining'] + postsCount++ + filesCount++ if 'file' of Get.postFromRoot reply + $.rm reply + a.textContent = ExpandThread.text '+', postsCount, filesCount + parse: (req, thread, a) -> + if req.status not in [200, 304] + a.textContent = "Error #{req.statusText} (#{req.status})" + return - {posts} = JSON.parse req.response - if spoilerRange = posts.shift().custom_spoiler - Build.spoilerRange[thread.board] = spoilerRange + data = JSON.parse(req.response).posts + Build.spoilerRange[thread.board] = data.shift().custom_spoiler - postsObj = [] + posts = [] postsRoot = [] filesCount = 0 - for reply in posts - if post = thread.posts[reply.no] + for postData in data + if post = thread.posts[postData.no] filesCount++ if 'file' of post postsRoot.push post.nodes.root continue - root = Build.postFromObject reply, thread.board.ID + root = Build.postFromObject postData, thread.board.ID post = new Post root, thread, thread.board - link = $ 'a[title="Highlight this post"]', root - link.href = "res/#{thread}#p#{post}" - link.nextSibling.href = "res/#{thread}#q#{post}" filesCount++ if 'file' of post - postsObj.push post + posts.push post postsRoot.push root - Main.callbackNodes Post, postsObj + Main.callbackNodes Post, posts $.after a, postsRoot - postsCount = postsRoot.length + postsCount = postsRoot.length a.textContent = ExpandThread.text '-', postsCount, filesCount Fourchan.parseThread thread.ID, 1, postsCount diff --git a/src/Miscellaneous/FileInfo.coffee b/src/Miscellaneous/FileInfo.coffee index 89d694313..e58f9faef 100755 --- a/src/Miscellaneous/FileInfo.coffee +++ b/src/Miscellaneous/FileInfo.coffee @@ -8,7 +8,7 @@ FileInfo = cb: @node node: -> return if !@file or @isClone - @file.text.innerHTML = FileInfo.funk FileInfo, @ + @file.text.innerHTML = "#{FileInfo.funk FileInfo, @}" createFunc: (format) -> code = format.replace /%(.)/g, (s, c) -> if c of FileInfo.formatters @@ -21,11 +21,10 @@ FileInfo = return "#{size.toFixed()} Bytes" i = 1 + ['KB', 'MB'].indexOf unit size /= 1024 while i-- - size = - if unit is 'MB' - Math.round(size * 100) / 100 - else - size.toFixed() + size = if unit is 'MB' + Math.round(size * 100) / 100 + else + size.toFixed() "#{size} #{unit}" escape: (name) -> name.replace /<|>/g, (c) -> diff --git a/src/Miscellaneous/Fourchan.coffee b/src/Miscellaneous/Fourchan.coffee index 64b1f4562..6f290d662 100755 --- a/src/Miscellaneous/Fourchan.coffee +++ b/src/Miscellaneous/Fourchan.coffee @@ -6,8 +6,9 @@ Fourchan = if board is 'g' $.globalEval """ window.addEventListener('prettyprint', function(e) { - var pre = e.detail; - pre.innerHTML = prettyPrintOne(pre.innerHTML); + window.dispatchEvent(new CustomEvent('prettyprint:cb', { + detail: prettyPrintOne(e.detail) + })); }, false); """ Post.callbacks.push @@ -32,9 +33,11 @@ Fourchan = cb: @math code: -> return if @isClone + apply = (e) -> pre.innerHTML = e.detail + $.on window, 'prettyprint:cb', apply for pre in $$ '.prettyprint:not(.prettyprinted)', @nodes.comment - $.event 'prettyprint', pre, window - $.addClass pre, 'prettyprinted' + $.event 'prettyprint', pre.innerHTML, window + $.off window, 'prettyprint:cb', apply return math: -> return if @isClone or !$ '.math', @nodes.comment diff --git a/src/Miscellaneous/Keybinds.coffee b/src/Miscellaneous/Keybinds.coffee index 932f019f1..d81814396 100755 --- a/src/Miscellaneous/Keybinds.coffee +++ b/src/Miscellaneous/Keybinds.coffee @@ -2,20 +2,25 @@ Keybinds = init: -> return if g.VIEW is 'catalog' or !Conf['Keybinds'] + for hotkey of Conf.hotkeys + $.sync hotkey, Keybinds.sync + init = -> $.off d, '4chanXInitFinished', init - $.on d, 'keydown', Keybinds.keydown + $.on d, 'keydown', Keybinds.keydown for node in $$ '[accesskey]' node.removeAttribute 'accesskey' return $.on d, '4chanXInitFinished', init + sync: (key, hotkey) -> + Conf[hotkey] = key + keydown: (e) -> return unless key = Keybinds.keyCode e {target} = e - if ['INPUT', 'TEXTAREA'].contains target.nodeName - return unless /(Esc|Alt|Ctrl|Meta)/.test key - + if target.nodeName in ['INPUT', 'TEXTAREA'] + return unless /(Esc|Alt|Ctrl|Meta|Shift\+\w{2,})/.test key threadRoot = Nav.getThread() if op = $ '.op', threadRoot thread = Get.postFromNode(op).thread @@ -59,11 +64,15 @@ Keybinds = Keybinds.sage() if QR.nodes when Conf['Submit QR'] QR.submit() if QR.nodes and !QR.status() - # Thread related + # Index/Thread related + when Conf['Update'] + switch g.VIEW + when 'thread' + ThreadUpdater.update() + when 'index' + Index.update() when Conf['Watch'] ThreadWatcher.toggle thread - when Conf['Update'] - ThreadUpdater.update() # Images when Conf['Expand image'] Keybinds.img threadRoot @@ -72,22 +81,25 @@ Keybinds = when Conf['Open Gallery'] Gallery.cb.toggle() when Conf['fappeTyme'] - FappeTyme.cb.fappe() + FappeTyme.cb.toggle.call {name: 'fappe'} when Conf['werkTyme'] - FappeTyme.cb.werk() + FappeTyme.cb.toggle.call {name: 'werk'} # Board Navigation when Conf['Front page'] - window.location = "/#{g.BOARD}/0#delform" + if g.VIEW is 'index' + Index.userPageNav 0 + else + window.location = "/#{g.BOARD}/" when Conf['Open front page'] - $.open "/#{g.BOARD}/#delform" + $.open "/#{g.BOARD}/" when Conf['Next page'] - return if g.VIEW is 'thread' - if form = $ '.next form' - window.location = form.action + return unless g.VIEW is 'index' and Conf['Index Mode'] is 'paged' + $('.next button', Index.pagelist).click() when Conf['Previous page'] - return if g.VIEW is 'thread' - if form = $ '.prev form' - window.location = form.action + return unless g.VIEW is 'index' and Conf['Index Mode'] is 'paged' + $('.prev button', Index.pagelist).click() + when Conf['Search form'] + Index.searchInput.focus() when Conf['Open catalog'] if Conf['External Catalog'] window.location = CatalogLinks.external(g.BOARD.ID) @@ -114,7 +126,7 @@ Keybinds = when Conf['Deselect reply'] Keybinds.hl 0, threadRoot when Conf['Hide'] - ThreadHiding.toggle thread if g.VIEW is 'index' + ThreadHiding.toggle thread if ThreadHiding.db when Conf['Previous Post Quoting You'] QuoteYou.cb.seek 'preceding' when Conf['Next Post Quoting You'] @@ -200,43 +212,31 @@ Keybinds = location.href = url hl: (delta, thread) -> + postEl = $ '.reply.highlight', thread + unless delta - if postEl = $ '.reply.highlight', thread - $.rmClass postEl, 'highlight' + $.rmClass postEl, 'highlight' if postEl return - if Conf['Fixed Header'] and Conf['Bottom header'] - topMargin = 0 - else - headRect = Header.bar.getBoundingClientRect() - topMargin = headRect.top + headRect.height - if postEl = $ '.reply.highlight', thread - $.rmClass postEl, 'highlight' - rect = postEl.getBoundingClientRect() - if rect.bottom >= topMargin and rect.top <= doc.clientHeight # We're at least partially visible + + if postEl + {height} = postEl.getBoundingClientRect() + if Header.getTopOf(postEl) >= -height and Header.getBottomOf(postEl) >= -height # We're at least partially visible root = postEl.parentNode - axe = if delta is +1 + axis = if delta is +1 'following' else 'preceding' - next = $.x "#{axe}-sibling::div[contains(@class,'replyContainer')][1]/child::div[contains(@class,'reply')]", root - unless next - @focus postEl - return - return unless g.VIEW is 'thread' or $.x('ancestor::div[parent::div[@class="board"]]', next) is thread - rect = next.getBoundingClientRect() - if rect.top < 0 or rect.bottom > doc.clientHeight - if delta is -1 - window.scrollBy 0, rect.top - topMargin - else - next.scrollIntoView false + return unless next = $.x "#{axis}-sibling::div[contains(@class,'replyContainer') and not(@hidden) and not(child::div[@class='stub'])][1]/child::div[contains(@class,'reply')]", root + Header.scrollToIfNeeded next, delta is +1 @focus next + $.rmClass postEl, 'highlight' return + $.rmClass postEl, 'highlight' replies = $$ '.reply', thread replies.reverse() if delta is -1 for reply in replies - rect = reply.getBoundingClientRect() - if delta is +1 and rect.top >= topMargin or delta is -1 and rect.bottom <= doc.clientHeight + if delta is +1 and Header.getTopOf(reply) > 0 or delta is -1 and Header.getBottomOf(reply) > 0 @focus reply return diff --git a/src/Miscellaneous/Nav.coffee b/src/Miscellaneous/Nav.coffee index cca467683..659cce4e1 100755 --- a/src/Miscellaneous/Nav.coffee +++ b/src/Miscellaneous/Nav.coffee @@ -38,29 +38,24 @@ Nav = else Nav.scroll +1 - getThread: (full) -> - if Conf['Bottom header'] or !Conf['Fixed Header'] - topMargin = 0 - else - headRect = Header.bar.getBoundingClientRect() - topMargin = headRect.top + headRect.height - threads = $$('.thread').filter (thread) -> - thread = Get.threadFromRoot thread - !(thread.isHidden and !thread.stub) - for thread, i in threads - rect = thread.getBoundingClientRect() - if rect.bottom > topMargin # not scrolled past - return if full then [threads, thread, i, rect, topMargin] else thread + getThread: -> + for threadRoot in $$ '.thread' + thread = Get.threadFromRoot threadRoot + continue if thread.isHidden and !thread.stub + if Header.getTopOf(threadRoot) >= -threadRoot.getBoundingClientRect().height # not scrolled past + return threadRoot return $ '.board' scroll: (delta) -> - [threads, thread, i, rect, topMargin] = Nav.getThread true - top = rect.top - topMargin - - # unless we're not at the beginning of the current thread - # (and thus wanting to move to beginning) - # or we're above the first thread and don't want to skip it - if (delta is -1 and top > -5) or (delta is +1 and top < 5) - top = threads[i + delta]?.getBoundingClientRect().top - topMargin - - window.scrollBy 0, top + thread = Nav.getThread() + axis = if delta is +1 + 'following' + else + 'preceding' + if next = $.x "#{axis}-sibling::div[contains(@class,'thread') and not(@hidden)][1]", thread + # Unless we're not at the beginning of the current thread, + # and thus wanting to move to beginning, + # or we're above the first thread and don't want to skip it. + top = Header.getTopOf thread + thread = next if delta is +1 and top < 5 or delta is -1 and top > -5 + Header.scrollTo thread diff --git a/src/Miscellaneous/RelativeDates.coffee b/src/Miscellaneous/RelativeDates.coffee index d678ecb23..810c7bceb 100755 --- a/src/Miscellaneous/RelativeDates.coffee +++ b/src/Miscellaneous/RelativeDates.coffee @@ -1,13 +1,17 @@ RelativeDates = INTERVAL: $.MINUTE / 2 init: -> - return if g.VIEW is 'catalog' or !Conf['Relative Post Dates'] - - # Flush when page becomes visible again or when the thread updates. - $.on d, 'visibilitychange ThreadUpdate', @flush - - # Start the timeout. - @flush() + switch g.VIEW + when 'index' + @flush() + $.on d, 'visibilitychange', @flush + return unless Conf['Relative Post Dates'] + when 'thread' + return unless Conf['Relative Post Dates'] + @flush() + $.on d, 'visibilitychange ThreadUpdate', @flush if g.VIEW is 'thread' + else + return Post.callbacks.push name: 'Relative Post Dates' @@ -21,7 +25,7 @@ RelativeDates = dateEl = @nodes.date dateEl.title = dateEl.textContent - RelativeDates.setUpdate @ + RelativeDates.update @ # diff is milliseconds from now. relative: (diff, now, date) -> @@ -71,37 +75,41 @@ RelativeDates = return if d.hidden now = new Date() - update now for update in RelativeDates.stale + RelativeDates.update data, now for data in RelativeDates.stale RelativeDates.stale = [] # Reset automatic flush. clearTimeout RelativeDates.timeout RelativeDates.timeout = setTimeout RelativeDates.flush, RelativeDates.INTERVAL - # Create function `update()`, closed over post, that, when called - # from `flush()`, updates the elements, and re-calls `setOwnTimeout()` to - # re-add `update()` to the stale list later. - setUpdate: (post) -> - setOwnTimeout = (diff) -> - delay = if diff < $.MINUTE - $.SECOND - (diff + $.SECOND / 2) % $.SECOND - else if diff < $.HOUR - $.MINUTE - (diff + $.MINUTE / 2) % $.MINUTE - else if diff < $.DAY - $.HOUR - (diff + $.HOUR / 2) % $.HOUR - else - $.DAY - (diff + $.DAY / 2) % $.DAY - setTimeout markStale, delay - - update = (now) -> - {date} = post.info - diff = now - date - relative = RelativeDates.relative diff, now, date - for singlePost in [post].concat post.clones + # `update()`, when called from `flush()`, updates the elements, + # and re-calls `setOwnTimeout()` to re-add `data` to the stale list later. + update: (data, now) -> + isPost = data instanceof Post + date = if isPost + data.info.date + else + new Date +data.dataset.utc + now or= new Date() + diff = now - date + relative = RelativeDates.relative diff, now, date + if isPost + for singlePost in [data].concat data.clones singlePost.nodes.date.firstChild.textContent = relative - setOwnTimeout diff - - markStale = -> RelativeDates.stale.push update - - # Kick off initial timeout. - update new Date() + else + data.firstChild.textContent = relative + RelativeDates.setOwnTimeout diff, data + setOwnTimeout: (diff, data) -> + delay = if diff < $.MINUTE + $.SECOND - (diff + $.SECOND / 2) % $.SECOND + else if diff < $.HOUR + $.MINUTE - (diff + $.MINUTE / 2) % $.MINUTE + else if diff < $.DAY + $.HOUR - (diff + $.HOUR / 2) % $.HOUR + else + $.DAY - (diff + $.DAY / 2) % $.DAY + setTimeout RelativeDates.markStale, delay, data + markStale: (data) -> + return if data in RelativeDates.stale # We can call RelativeDates.update() multiple times. + return if data instanceof Post and !g.posts[data.fullID] # collected post. + RelativeDates.stale.push data diff --git a/src/Monitoring/Favicon.coffee b/src/Monitoring/Favicon.coffee index ea02b3ca2..f87879fcc 100755 --- a/src/Monitoring/Favicon.coffee +++ b/src/Monitoring/Favicon.coffee @@ -45,5 +45,5 @@ Favicon = Favicon.unread = Favicon.unreadNSFW Favicon.unreadY = Favicon.unreadNSFWY - dead: 'data:image/gif;base64,<%= grunt.file.read("src/General/img/favicons/dead.gif", {encoding: "base64"}) %>' - logo: 'data:image/png;base64,<%= grunt.file.read("src/General/img/icon128.png", {encoding: "base64"}) %>' + dead: 'data:image/gif;base64,<%= grunt.file.read("src/General/img/favicons/dead.gif", {encoding: "base64"}) %>' + logo: 'data:image/png;base64,<%= grunt.file.read("src/General/img/icon128.png", {encoding: "base64"}) %>' diff --git a/src/Monitoring/ThreadUpdater.coffee b/src/Monitoring/ThreadUpdater.coffee index 1700ac8fb..1a7be4a60 100755 --- a/src/Monitoring/ThreadUpdater.coffee +++ b/src/Monitoring/ThreadUpdater.coffee @@ -208,43 +208,32 @@ ThreadUpdater = ThreadUpdater.set 'timer', '...' else ThreadUpdater.set 'timer', 'Update' - ThreadUpdater.req.abort() if ThreadUpdater.req + ThreadUpdater.req?.abort() url = "//a.4cdn.org/#{ThreadUpdater.thread.board}/res/#{ThreadUpdater.thread}.json" ThreadUpdater.req = $.ajax url, onloadend: ThreadUpdater.cb.load, whenModified: true - updateThreadStatus: (title, OP) -> - titleLC = title.toLowerCase() - return if ThreadUpdater.thread["is#{title}"] is !!OP[titleLC] - unless ThreadUpdater.thread["is#{title}"] = !!OP[titleLC] - message = if title is 'Sticky' - 'The thread is not a sticky anymore.' + updateThreadStatus: (type, status) -> + return unless hasChanged = ThreadUpdater.thread["is#{type}"] isnt status + ThreadUpdater.thread.setStatus type, status + change = if type is 'Sticky' + if status + 'now a sticky' else - 'The thread is not closed anymore.' - new Notice 'info', message, 30 - $.rm $ ".#{titleLC}Icon", ThreadUpdater.thread.OP.nodes.info - return - message = if title is 'Sticky' - 'The thread is now a sticky.' + 'not a sticky anymore' else - 'The thread is now closed.' - new Notice 'info', message, 30 - icon = $.el 'img', - src: "//static.4chan.org/image/#{titleLC}.gif" - alt: title - title: title - className: "#{titleLC}Icon" - root = $ '[title="Quote this post"]', ThreadUpdater.thread.OP.nodes.info - if title is 'Closed' - root = $('.stickyIcon', ThreadUpdater.thread.OP.nodes.info) or root - $.after root, [$.tn(' '), icon] + if status + 'now closed' + else + 'not closed anymore' + new Notice 'info', "The thread is #{change}.", 30 parse: (postObjects) -> OP = postObjects[0] Build.spoilerRange[ThreadUpdater.thread.board] = OP.custom_spoiler - ThreadUpdater.updateThreadStatus 'Sticky', OP - ThreadUpdater.updateThreadStatus 'Closed', OP + ThreadUpdater.updateThreadStatus 'Sticky', !!OP.sticky + ThreadUpdater.updateThreadStatus 'Closed', !!OP.closed ThreadUpdater.thread.postLimit = !!OP.bumplimit ThreadUpdater.thread.fileLimit = !!OP.imagelimit @@ -265,18 +254,20 @@ ThreadUpdater = deletedPosts = [] deletedFiles = [] + # Check for deleted posts/files. for ID, post of ThreadUpdater.thread.posts # XXX tmp fix for 4chan's racing condition # giving us false-positive dead posts. # continue if post.isDead ID = +ID - if post.isDead and index.contains ID - post.resurrect() - else unless index.contains ID + + unless ID in index post.kill() deletedPosts.push post - else if post.file and !post.file.isDead and not files.contains ID + else if post.isDead + post.resurrect() + else if post.file and not (post.file.isDead or ID in files) post.kill true deletedFiles.push post @@ -304,7 +295,7 @@ ThreadUpdater = continue unless posts.hasOwnProperty key root = post.nodes.root if post.cb - unless post.cb.call post + unless post.cb() $.add ThreadUpdater.root, root else $.add ThreadUpdater.root, root @@ -313,7 +304,7 @@ ThreadUpdater = if Conf['Bottom Scroll'] window.scrollTo 0, d.body.clientHeight else - Header.scrollToPost root if root + Header.scrollTo root if root $.queueTask -> # Enable 4chan features. diff --git a/src/Monitoring/ThreadWatcher.coffee b/src/Monitoring/ThreadWatcher.coffee index bc71c11b9..e5b63500f 100755 --- a/src/Monitoring/ThreadWatcher.coffee +++ b/src/Monitoring/ThreadWatcher.coffee @@ -6,12 +6,10 @@ ThreadWatcher = id: 'watcher-link' textContent: 'Watcher' href: 'javascript:;' - className: 'disabled fourchanx-icon icon-eye-open' + className: 'disabled fa fa-eye-open' @db = new DataBoard 'watchedThreads', @refresh, true - @dialog = UI.dialog 'thread-watcher', 'top: 50px; left: 0px;', """ - <%= grunt.file.read('src/General/html/Monitoring/ThreadWatcher.html').replace(/>\s+<').trim() %> - """ + @dialog = UI.dialog 'thread-watcher', 'top: 50px; left: 0px;', <%= importHTML('Monitoring/ThreadWatcher') %> @status = $ '#watcher-status', @dialog @list = @dialog.lastElementChild @@ -19,7 +17,13 @@ ThreadWatcher = $.on d, 'ThreadUpdate', @cb.threadUpdate if g.VIEW is 'thread' $.on sc, 'click', @toggleWatcher $.on $('.move>.close', ThreadWatcher.dialog), 'click', @toggleWatcher + $.on d, '4chanXInitFinished', @ready + switch g.VIEW + when 'index' + $.on d, 'IndexRefresh', @cb.onIndexRefresh + when 'thread' + $.on d, 'ThreadUpdate', @cb.onThreadRefresh if Conf['Toggleable Thread Watcher'] Header.addShortcut sc @@ -89,7 +93,17 @@ ThreadWatcher = $.set 'AutoWatch', threadID else if Conf['Auto Watch Reply'] ThreadWatcher.add board.threads[threadID] - threadUpdate: (e) -> + onIndexRefresh: -> + {db} = ThreadWatcher + boardID = g.BOARD.ID + for threadID, data of db.data.boards[boardID] when not data.isDead and threadID not of g.BOARD.threads + if Conf['Auto Prune'] + ThreadWatcher.db.delete {boardID, threadID} + else + data.isDead = true + ThreadWatcher.db.set {boardID, threadID, val: data} + ThreadWatcher.refresh() + onThreadRefresh: (e) -> {thread} = e.detail return unless e.detail[404] and ThreadWatcher.db.get {boardID: thread.board.ID, threadID: thread.ID} # Update 404 status. @@ -120,7 +134,7 @@ ThreadWatcher = ThreadWatcher.status.textContent = status return if @status isnt 404 if Conf['Auto Prune'] - ThreadWatcher.rm boardID, threadID + ThreadWatcher.db.delete {boardID, threadID} else data.isDead = true ThreadWatcher.db.set {boardID, threadID, val: data} @@ -139,7 +153,7 @@ ThreadWatcher = makeLine: (boardID, threadID, data) -> x = $.el 'a', - textContent: '×' + className: 'fa fa-times' href: 'javascript:;' $.on x, 'click', ThreadWatcher.cb.rm @@ -281,6 +295,7 @@ ThreadWatcher = $.on entry.el, 'click', cb if cb @refreshers.push refresh.bind entry if refresh $.event 'AddMenuEntry', entry + return createSubEntry: (name, desc) -> entry = type: 'thread watcher' diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee index 76e3e30d0..059fa2dc6 100755 --- a/src/Monitoring/Unread.coffee +++ b/src/Monitoring/Unread.coffee @@ -5,7 +5,7 @@ Unread = @db = new DataBoard 'lastReadPosts', @sync @hr = $.el 'hr', id: 'unread-line' - @posts = [] + @posts = new RandomAccessList @postsQuotingYou = [] Thread.callbacks.push @@ -27,32 +27,27 @@ Unread = ready: -> $.off d, '4chanXInitFinished', Unread.ready posts = [] - for ID, post of Unread.thread.posts - posts.push post if post.isReply + posts.push post for ID, post of Unread.thread.posts when post.isReply Unread.addPosts posts - Unread.scroll() + QuoteThreading.force() if Conf['Quote Threading'] + Unread.scroll() if Conf['Scroll to Last Read Post'] scroll: -> - return unless Conf['Scroll to Last Read Post'] # Let the header's onload callback handle it. return if (hash = location.hash.match /\d+/) and hash[0] of Unread.thread.posts - if post = Unread.posts[0] + if post = Unread.posts.first # Scroll to a non-hidden, non-OP post that's before the first unread post. while root = $.x 'preceding-sibling::div[contains(@class,"replyContainer")][1]', post.nodes.root break unless (post = Get.postFromRoot root).isHidden return unless root - onload = -> root.scrollIntoView false if checkPosition root + down = true else # Scroll to the last read post. posts = Object.keys Unread.thread.posts {root} = Unread.thread.posts[posts[posts.length - 1]].nodes - onload = -> Header.scrollToPost root if checkPosition root - checkPosition = (target) -> - # Scroll to the target unless we scrolled past it. - target.getBoundingClientRect().bottom > doc.clientHeight - # Prevent the browser to scroll back to - # the previous scroll location on page load. - $.on window, 'load', onload + + # Scroll to the target unless we scrolled past it. + Header.scrollTo root, down if Header.getBottomOf(root) < 0 sync: -> lastReadPost = Unread.db.get @@ -61,7 +56,13 @@ Unread = defaultValue: 0 return unless Unread.lastReadPost < lastReadPost Unread.lastReadPost = lastReadPost - Unread.readArray Unread.posts + + post = Unread.posts.first + while post + break if ({ID} = post) > Unread.lastReadPost + post = post.next + Unread.posts.rm ID + Unread.readArray Unread.postsQuotingYou Unread.setLine() if Conf['Unread Line'] Unread.update() @@ -69,19 +70,16 @@ Unread = addPosts: (posts) -> for post in posts {ID} = post - if ID <= Unread.lastReadPost or post.isHidden - continue - if QR.db - data = - boardID: post.board.ID - threadID: post.thread.ID - postID: post.ID - continue if QR.db.get data - Unread.posts.push post + continue if ID <= Unread.lastReadPost or post.isHidden or QR.db.get { + boardID: post.board.ID + threadID: post.thread.ID + postID: ID + } + Unread.posts.push post unless post.prev or post.next Unread.addPostQuotingYou post if Conf['Unread Line'] # Force line on visible threads if there were no unread posts previously. - Unread.setLine posts.contains Unread.posts[0] + Unread.setLine Unread.posts.first in posts Unread.read() Unread.update() @@ -102,7 +100,7 @@ Unread = body: post.info.comment icon: Favicon.logo notif.onclick = -> - Header.scrollToPost post.nodes.root + Header.scrollToIfNeeded post.nodes.root, true window.focus() notif.onshow = -> setTimeout -> @@ -116,11 +114,12 @@ Unread = Unread.addPosts e.detail.newPosts readSinglePost: (post) -> - return if (i = Unread.posts.indexOf post) is -1 - Unread.posts.splice i, 1 - if i is 0 - Unread.lastReadPost = post.ID + {ID} = post + return unless Unread.posts[ID] + if post is Unread.posts.first + Unread.lastReadPost = ID Unread.saveLastReadPost() + Unread.posts.rm ID if (i = Unread.postsQuotingYou.indexOf post) isnt -1 Unread.postsQuotingYou.splice i, 1 Unread.update() @@ -130,28 +129,18 @@ Unread = break if post.ID > Unread.lastReadPost arr.splice 0, i - read: $.debounce 50, (e) -> + read: $.debounce 100, (e) -> return if d.hidden or !Unread.posts.length height = doc.clientHeight - {posts} = Unread - i = 0 - while post = posts[i] - if post.nodes.root.getBoundingClientRect().bottom < height # post is not completely read - {ID} = post - if Conf['Mark Quotes of You'] - if post.info.yours - QuoteYou.lastRead = post.nodes.root - if Conf['Quote Threading'] - posts.splice i, 1 - continue - else - unless Conf['Quote Threading'] - break - i++ - - if i and !Conf['Quote Threading'] - posts.splice 0, i + {posts} = Unread + while post = posts.first + break unless Header.getBottomOf(post.nodes.root) > -1 # post is not completely read + {ID} = post + posts.rm ID + + if Conf['Mark Quotes of You'] and post.info.yours + QuoteYou.lastRead = post.nodes.root return unless ID @@ -163,13 +152,13 @@ Unread = saveLastReadPost: $.debounce 2 * $.SECOND, -> return if Unread.thread.isDead Unread.db.set - boardID: Unread.thread.board.ID + boardID: Unread.thread.board.ID threadID: Unread.thread.ID val: Unread.lastReadPost setLine: (force) -> return unless d.hidden or force is true - return $.rm Unread.hr unless post = Unread.posts[0] + return $.rm Unread.hr unless post = Unread.posts.first if $.x 'preceding-sibling::div[contains(@class,"replyContainer")]', post.nodes.root # not the first reply $.before post.nodes.root, Unread.hr @@ -177,7 +166,7 @@ Unread = count = Unread.posts.length if Conf['Unread Count'] - d.title = "#{if Conf['Quoted Title'] and Unread.postsQuotingYou.length then '(!) ' else ''}#{if count or !Conf['Hide Unread Count at (0)'] then "(#{count}) " else ''}#{if g.DEAD then "/#{g.BOARD}/ - 404" else "#{Unread.title}"}" + d.title = "#{if Conf['Quoted Title'] and Unread.postsQuotingYou.length then '(!) ' else ''}#{if count or !Conf['Hide Unread Count at (0)'] then "(#{count}) " else ''}#{if g.DEAD then "/#{g.BOARD}/ - 404" else "#{Unread.title}"}" <% if (type === 'crx') { %> # XXX Chrome bug where it doesn't always update the tab title. # crbug.com/124381 diff --git a/src/Posting/QR.captcha.coffee b/src/Posting/QR.captcha.coffee new file mode 100644 index 000000000..671ae233f --- /dev/null +++ b/src/Posting/QR.captcha.coffee @@ -0,0 +1,124 @@ +QR.captcha = + init: -> + return if d.cookie.indexOf('pass_enabled=1') >= 0 + return unless @isEnabled = !!$.id 'captchaFormPart' + $.asap (-> $.id 'recaptcha_challenge_field_holder'), @ready.bind @ + + ready: -> + setLifetime = (e) => @lifetime = e.detail + $.on window, 'captcha:timeout', setLifetime + $.globalEval 'window.dispatchEvent(new CustomEvent("captcha:timeout", {detail: RecaptchaState.timeout}))' + $.off window, 'captcha:timeout', setLifetime + + imgContainer = $.el 'div', + className: 'captcha-img' + title: 'Reload reCAPTCHA' + innerHTML: '' + input = $.el 'input', + className: 'captcha-input field' + title: 'Verification' + autocomplete: 'off' + spellcheck: false + tabIndex: 55 + @nodes = + challenge: $.id 'recaptcha_challenge_field_holder' + img: imgContainer.firstChild + input: input + + new MutationObserver(@load.bind @).observe @nodes.challenge, + childList: true + + $.on imgContainer, 'click', @reload.bind @ + $.on input, 'keydown', @keydown.bind @ + $.on input, 'focus', -> $.addClass QR.nodes.el, 'focus' + $.on input, 'blur', -> $.rmClass QR.nodes.el, 'focus' + + $.get 'captchas', [], ({captchas}) => + @sync captchas + $.sync 'captchas', @sync + # start with an uncached captcha + @reload() + + <% if (type === 'userscript') { %> + # XXX Firefox lacks focusin/focusout support. + $.on input, 'blur', QR.focusout + $.on input, 'focus', QR.focusin + <% } %> + + $.addClass QR.nodes.el, 'has-captcha' + $.after QR.nodes.com.parentNode, [imgContainer, input] + + sync: (captchas) -> + QR.captcha.captchas = captchas + QR.captcha.count() + + getOne: -> + @clear() + if captcha = @captchas.shift() + {challenge, response} = captcha + @count() + $.set 'captchas', @captchas + else + challenge = @nodes.img.alt + if response = @nodes.input.value then @reload() + if response + response = response.trim() + # one-word-captcha: + # If there's only one word, duplicate it. + response = "#{response} #{response}" unless /\s/.test response + {challenge, response} + + save: -> + return unless response = @nodes.input.value.trim() + @captchas.push + challenge: @nodes.img.alt + response: response + timeout: @timeout + @count() + @reload() + $.set 'captchas', @captchas + + clear: -> + now = Date.now() + for captcha, i in @captchas + break if captcha.timeout > now + return unless i + @captchas = @captchas[i..] + @count() + $.set 'captchas', @captchas + + load: -> + return unless @nodes.challenge.firstChild + # -1 minute to give upload some time. + @timeout = Date.now() + @lifetime * $.SECOND - $.MINUTE + challenge = @nodes.challenge.firstChild.value + @nodes.img.alt = challenge + @nodes.img.src = "//www.google.com/recaptcha/api/image?c=#{challenge}" + @nodes.input.value = null + @clear() + + count: -> + count = @captchas.length + @nodes.input.placeholder = switch count + when 0 + 'Verification (Shift + Enter to cache)' + when 1 + 'Verification (1 cached captcha)' + else + "Verification (#{count} cached captchas)" + @nodes.input.alt = count # For XTRM RICE. + + reload: (focus) -> + # the 't' argument prevents the input from being focused + $.globalEval 'Recaptcha.reload("t")' + # Focus if we meant to. + @nodes.input.focus() if focus + + keydown: (e) -> + if e.keyCode is 8 and not @nodes.input.value + @reload() + else if e.keyCode is 13 and e.shiftKey + @save() + else + return + e.preventDefault() \ No newline at end of file diff --git a/src/Posting/QuickReply.coffee b/src/Posting/QR.coffee old mode 100755 new mode 100644 similarity index 51% rename from src/Posting/QuickReply.coffee rename to src/Posting/QR.coffee index 50b8e33b9..b2d7fd437 --- a/src/Posting/QuickReply.coffee +++ b/src/Posting/QR.coffee @@ -3,10 +3,11 @@ QR = return if !Conf['Quick Reply'] @db = new DataBoard 'yourPosts' + @posts = [] if Conf['QR Shortcut'] sc = $.el 'a', - className: "qr-shortcut fourchanx-icon icon-comment #{unless Conf['Persistent QR'] then 'disabled' else ''}" + className: "qr-shortcut fa fa-comment-o #{unless Conf['Persistent QR'] then 'disabled' else ''}" textContent: 'QR' title: 'Quick Reply' href: 'javascript:;' @@ -65,11 +66,15 @@ QR = $.on d, 'dragover', QR.dragOver $.on d, 'drop', QR.dropFile $.on d, 'dragstart dragend', QR.drag - $.on d, 'ThreadUpdate', -> - if g.DEAD - QR.abort() - else - QR.status() + switch g.VIEW + when 'index' + $.on d, 'IndexRefresh', QR.generatePostableThreadsList + when 'thread' + $.on d, 'ThreadUpdate', -> + if g.DEAD + QR.abort() + else + QR.status() node: -> $.on $('a[title="Quote this post"]', @nodes.info), 'click', QR.quote @@ -199,192 +204,6 @@ QR = value status.disabled = disabled or false - persona: - pwd: '' - always: {} - init: -> - QR.persona.getPassword() - $.get 'QR.personas', Conf['QR.personas'], ({'QR.personas': personas}) -> - types = - name: [] - email: [] - sub: [] - for item in personas.split '\n' - QR.persona.parseItem item.trim(), types - for type, arr of types - QR.persona.loadPersonas type, arr - return - - parseItem: (item, types) -> - return if item[0] is '#' - return unless match = item.match /(name|email|subject|password):"(.*)"/i - [match, type, val] = match - - # Don't mix up item settings with val. - item = item.replace match, '' - - boards = item.match(/boards:([^;]+)/i)?[1].toLowerCase() or 'global' - if boards isnt 'global' and not ((boards.split ',').contains g.BOARD.ID) - return - - if type is 'password' - QR.persona.pwd = val - return - - type = 'sub' if type is 'subject' - - if /always/i.test item - QR.persona.always[type] = val - - unless types[type].contains val - types[type].push val - - loadPersonas: (type, arr) -> - list = $ "#list-#{type}", QR.nodes.el - for val in arr when val - $.add list, $.el 'option', - textContent: val - return - - getPassword: -> - unless QR.persona.pwd - QR.persona.pwd = if m = d.cookie.match /4chan_pass=([^;]+)/ - decodeURIComponent m[1] - else if input = $.id 'postPassword' - input.value - else - # If we're in a closed thread, #postPassword isn't available. - # And since #delPassword.value is only filled on window.onload - # we'd rather use #postPassword when we can. - $.id('delPassword').value - return QR.persona.pwd - - get: (cb) -> - $.get 'QR.persona', {}, ({'QR.persona': persona}) -> - cb persona - - set: (post) -> - $.get 'QR.persona', {}, ({'QR.persona': persona}) -> - persona = - name: post.name - email: if /^sage$/.test post.email then persona.email else post.email - sub: if Conf['Remember Subject'] then post.sub else undefined - flag: post.flag - $.set 'QR.persona', persona - - cooldown: - init: -> - return unless Conf['Cooldown'] - setTimers = (e) => QR.cooldown.types = e.detail - $.on window, 'cooldown:timers', setTimers - $.globalEval 'window.dispatchEvent(new CustomEvent("cooldown:timers", {detail: cooldowns}))' - QR.cooldown.types or= {} # XXX tmp workaround until all pages and the catalogs get the cooldowns var. - $.off window, 'cooldown:timers', setTimers - for type of 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, {}, (item) -> - QR.cooldown.cooldowns = item[key] - QR.cooldown.start() - $.sync key, QR.cooldown.sync - start: -> - return unless Conf['Cooldown'] - return if QR.cooldown.isCounting - QR.cooldown.isCounting = true - QR.cooldown.count() - - sync: (cooldowns) -> - # Add each cooldowns, don't overwrite everything in case we - # still need to prune one in the current tab to auto-post. - for id of cooldowns - QR.cooldown.cooldowns[id] = cooldowns[id] - QR.cooldown.start() - - set: (data) -> - return unless Conf['Cooldown'] - {req, post, isReply, threadID, delay} = data - start = if req then req.uploadEndTime else Date.now() - if delay - cooldown = {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, threadID} - QR.cooldown.cooldowns[start] = cooldown - $.set "cooldown.#{g.BOARD}", QR.cooldown.cooldowns - QR.cooldown.start() - - unset: (id) -> - delete QR.cooldown.cooldowns[id] - if Object.keys(QR.cooldown.cooldowns).length - $.set "cooldown.#{g.BOARD}", QR.cooldown.cooldowns - else - $.delete "cooldown.#{g.BOARD}" - - count: -> - unless 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 isnt 'new' - hasFile = !!post.file - seconds = null - {types, cooldowns, upSpd, upSpdAccuracy} = QR.cooldown - - for start, cooldown of cooldowns - if 'delay' of cooldown - if cooldown.delay - seconds = Math.max seconds, cooldown.delay-- - else - seconds = Math.max seconds, 0 - QR.cooldown.unset start - continue - - if 'timeout' of cooldown - # XXX tmp conversion from previous cooldowns - QR.cooldown.unset start - continue - - if isReply is cooldown.isReply - # Only cooldowns relevant to this post can set the seconds variable: - # reply cooldown with a reply, thread cooldown with a thread - elapsed = Math.floor (now - start) / $.SECOND - continue if elapsed < 0 # clock changed since then? - type = unless isReply - 'thread' - else if hasFile - 'image' - else - 'reply' - maxTimer = Math.max types[type] or 0, types[type + '_intra'] or 0 - unless start <= now <= start + maxTimer * $.SECOND - QR.cooldown.unset start - type += '_intra' if isReply and +post.thread is cooldown.threadID - seconds = Math.max seconds, types[type] - elapsed - - if seconds and Conf['Cooldown Prediction'] and hasFile and upSpd - seconds -= Math.floor post.file.size / upSpd * upSpdAccuracy - seconds = if seconds > 0 then seconds else 0 - # Update the status when we change posting type. - # Don't get stuck at some random number. - # Don't interfere with progress status updates. - update = seconds isnt null or !!QR.cooldown.seconds - QR.cooldown.seconds = seconds - QR.status() if update - QR.submit() if seconds is 0 and QR.cooldown.auto and !QR.req - quote: (e) -> e?.preventDefault() return unless QR.postingIsEnabled @@ -471,7 +290,7 @@ QR = if file.size > max QR.error "#{file.name}: File too large (file: #{$.bytesToString file.size}, max: #{$.bytesToString max})." return - else unless QR.mimeTypes.contains file.type + else unless file.type in QR.mimeTypes unless /^text/.test file.type QR.error "#{file.name}: Unsupported file type." return @@ -495,408 +314,32 @@ QR = $.addClass QR.nodes.filename, 'edit' QR.nodes.filename.focus() return $.on QR.nodes.filename, 'blur', -> $.rmClass QR.nodes.filename, 'edit' - return if e.target.nodeName is 'INPUT' or (e.keyCode and not [32, 13].contains e.keyCode) or e.ctrlKey + return if e.target.nodeName is 'INPUT' or (e.keyCode and e.keyCode not in [32, 13]) or e.ctrlKey e.preventDefault() QR.nodes.fileInput.click() - posts: [] - - post: class - constructor: (select) -> - el = $.el 'a', - className: 'qr-preview' - draggable: true - href: 'javascript:;' - innerHTML: '×' - - @nodes = - el: el - rm: el.firstChild - label: $ 'label', el - spoiler: $ 'input', el - span: el.lastChild - - <% if (type === 'userscript') { %> - # XXX Firefox lacks focusin/focusout support. - for elm in $$ '*', el - $.on elm, 'blur', QR.focusout - $.on elm, 'focus', QR.focusin - <% } %> - $.on el, 'click', @select - $.on @nodes.rm, 'click', (e) => e.stopPropagation(); @rm() - $.on @nodes.label, 'click', (e) => e.stopPropagation() - $.on @nodes.spoiler, 'change', (e) => - @spoiler = e.target.checked - QR.nodes.spoiler.checked = @spoiler if @ is QR.selected - $.add QR.nodes.dumpList, el - - for event in ['dragStart', 'dragEnter', 'dragLeave', 'dragOver', 'dragEnd', 'drop'] - $.on el, event.toLowerCase(), @[event] - - @thread = if g.VIEW is 'thread' - g.THREADID - else - 'new' - - prev = QR.posts[QR.posts.length - 1] - QR.posts.push @ - @nodes.spoiler.checked = @spoiler = if prev and Conf['Remember Spoiler'] - prev.spoiler - else - false - QR.persona.get (persona) => - @name = if 'name' of QR.persona.always - QR.persona.always.name - else if prev - prev.name - else - persona.name - - @email = if 'email' of QR.persona.always - QR.persona.always.email - else if prev and !/^sage$/.test prev.email - prev.email - else - persona.email - - @sub = if 'sub' of QR.persona.always - QR.persona.always.sub - else if Conf['Remember Subject'] - if prev then prev.sub else persona.sub - else - '' - - if QR.nodes.flag - @flag = if prev - prev.flag - else - persona.flag - @load() if QR.selected is @ # load persona - @select() if select - @unlock() - - rm: -> - @delete() - index = QR.posts.indexOf @ - if QR.posts.length is 1 - new QR.post true - $.rmClass QR.nodes.el, 'dump' - else if @ is QR.selected - (QR.posts[index-1] or QR.posts[index+1]).select() - QR.posts.splice index, 1 - QR.status() - delete: -> - $.rm @nodes.el - URL.revokeObjectURL @URL - - lock: (lock=true) -> - @isLocked = lock - return unless @ is QR.selected - for name in ['thread', 'name', 'email', 'sub', 'com', 'fileButton', 'filename', 'spoiler', 'flag'] when node = QR.nodes[name] - node.disabled = lock - @nodes.rm.style.visibility = if lock then 'hidden' else '' - (if lock then $.off else $.on) QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput - @nodes.spoiler.disabled = lock - @nodes.el.draggable = !lock - - unlock: -> - @lock false - - select: => - if QR.selected - QR.selected.nodes.el.id = null - QR.selected.forceSave() - QR.selected = @ - @lock @isLocked - @nodes.el.id = 'selected' - # Scroll the list to center the focused post. - rectEl = @nodes.el.getBoundingClientRect() - rectList = @nodes.el.parentNode.getBoundingClientRect() - @nodes.el.parentNode.scrollLeft += rectEl.left + rectEl.width/2 - rectList.left - rectList.width/2 - @load() - $.event 'QRPostSelection', @ - - load: -> - # Load this post's values. - for name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'flag'] - continue unless node = QR.nodes[name] - node.value = @[name] or node.dataset.default or null - @showFileData() - QR.characterCount() - - save: (input) -> - if input.type is 'checkbox' - @spoiler = input.checked - return - {name} = input.dataset - @[name] = input.value or input.dataset.default or null - switch name - when 'thread' - QR.status() - when 'com' - @nodes.span.textContent = @com - QR.characterCount() - # Disable auto-posting if you're typing in the first post - # during the last 5 seconds of the cooldown. - if QR.cooldown.auto and @ is QR.posts[0] and 0 < QR.cooldown.seconds <= 5 - QR.cooldown.auto = false - when 'filename' - return unless @file - @file.newName = @filename.replace /[/\\]/g, '-' - unless /\.(jpe?g|png|gif|pdf|swf)$/i.test @filename - # 4chan will truncate the filename if it has no extension, - # but it will always replace the extension by the correct one, - # so we suffix it with '.jpg' when needed. - @file.newName += '.jpg' - @updateFilename() - - forceSave: -> - return unless @ is QR.selected - # Do this in case people use extensions - # that do not trigger the `input` event. - for name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler', 'flag'] - continue unless node = QR.nodes[name] - @save node - return - - setFile: (@file) -> - @filename = file.name - @filesize = $.bytesToString file.size - @nodes.label.hidden = false if QR.spoiler - URL.revokeObjectURL @URL - @showFileData() if @ is QR.selected - unless /^image/.test file.type - @nodes.el.style.backgroundImage = null - return - @setThumbnail() - - setThumbnail: -> - # Create a redimensioned thumbnail. - img = $.el 'img' - - img.onload = => - # Generate thumbnails only if they're really big. - # Resized pictures through canvases look like ass, - # so we generate thumbnails `s` times bigger then expected - # to avoid crappy resized quality. - s = 90*2 - s *= 3 if @file.type is 'image/gif' # let them animate - {height, width} = img - if height < s or width < s - @URL = fileURL - @nodes.el.style.backgroundImage = "url(#{@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 - cv.toBlob (blob) => - @URL = URL.createObjectURL blob - @nodes.el.style.backgroundImage = "url(#{@URL})" - - fileURL = URL.createObjectURL @file - img.src = fileURL - - rmFile: -> - return if @isLocked - delete @file - delete @filename - delete @filesize - @nodes.el.title = null - QR.nodes.fileContainer.title = '' - @nodes.el.style.backgroundImage = null - @nodes.label.hidden = true if QR.spoiler - @showFileData() - URL.revokeObjectURL @URL - - updateFilename: -> - long = "#{@filename} (#{@filesize})\nCtrl+click to edit filename. Shift+click to clear." - @nodes.el.title = long - return unless @ is QR.selected - QR.nodes.fileContainer.title = long - - showFileData: -> - if @file - @updateFilename() - QR.nodes.filename.value = @filename - QR.nodes.spoiler.checked = @spoiler - $.addClass QR.nodes.fileSubmit, 'has-file' - else - $.rmClass QR.nodes.fileSubmit, 'has-file' - - pasteText: (file) -> - reader = new FileReader() - reader.onload = (e) => - text = e.target.result - if @com - @com += "\n#{text}" - else - @com = text - if QR.selected is @ - QR.nodes.com.value = @com - @nodes.span.textContent = @com - reader.readAsText file - - dragStart: (e) -> - e.dataTransfer.setDragImage @, e.layerX, e.layerY - $.addClass @, 'drag' - dragEnd: -> $.rmClass @, 'drag' - dragEnter: -> $.addClass @, 'over' - dragLeave: -> $.rmClass @, 'over' - - dragOver: (e) -> - e.preventDefault() - e.dataTransfer.dropEffect = 'move' - - drop: -> - $.rmClass @, 'over' - return unless @draggable - el = $ '.drag', @parentNode - index = (el) -> [el.parentNode.children...].indexOf el - oldIndex = index el - newIndex = index @ - (if oldIndex < newIndex then $.after else $.before) @, el - post = QR.posts.splice(oldIndex, 1)[0] - QR.posts.splice newIndex, 0, post - QR.status() - - captcha: - init: -> - return if d.cookie.indexOf('pass_enabled=1') >= 0 - return unless @isEnabled = !!$.id 'captchaFormPart' - $.asap (-> $.id 'recaptcha_challenge_field_holder'), @ready.bind @ - - ready: -> - setLifetime = (e) => @lifetime = e.detail - $.on window, 'captcha:timeout', setLifetime - $.globalEval 'window.dispatchEvent(new CustomEvent("captcha:timeout", {detail: RecaptchaState.timeout}))' - $.off window, 'captcha:timeout', setLifetime - - imgContainer = $.el 'div', - className: 'captcha-img' - title: 'Reload' - innerHTML: '' - input = $.el 'input', - className: 'captcha-input field' - title: 'Verification' - autocomplete: 'off' - spellcheck: false - tabIndex: 55 - @nodes = - challenge: $.id 'recaptcha_challenge_field_holder' - img: imgContainer.firstChild - input: input - - new MutationObserver(@load.bind @).observe @nodes.challenge, - childList: true - - $.on imgContainer, 'click', @reload.bind @ - $.on input, 'keydown', @keydown.bind @ - $.on input, 'focus', -> $.addClass QR.nodes.el, 'focus' - $.on input, 'blur', -> $.rmClass QR.nodes.el, 'focus' - - $.get 'captchas', [], ({captchas}) => - @sync captchas - $.sync 'captchas', @sync - # start with an uncached captcha - @reload() - - <% if (type === 'userscript') { %> - # XXX Firefox lacks focusin/focusout support. - $.on input, 'blur', QR.focusout - $.on input, 'focus', QR.focusin - <% } %> - - $.addClass QR.nodes.el, 'has-captcha' - $.after QR.nodes.com.parentNode, [imgContainer, input] - - sync: (captchas) -> - QR.captcha.captchas = captchas - QR.captcha.count() - - getOne: -> - @clear() - if captcha = @captchas.shift() - {challenge, response} = captcha - @count() - $.set 'captchas', @captchas - else - challenge = @nodes.img.alt - if response = @nodes.input.value then @reload() - if response - response = response.trim() - # one-word-captcha: - # If there's only one word, duplicate it. - response = "#{response} #{response}" unless /\s/.test response - {challenge, response} - - save: -> - return unless response = @nodes.input.value.trim() - @captchas.push - challenge: @nodes.img.alt - response: response - timeout: @timeout - @count() - @reload() - $.set 'captchas', @captchas - - clear: -> - now = Date.now() - for captcha, i in @captchas - break if captcha.timeout > now - return unless i - @captchas = @captchas[i..] - @count() - $.set 'captchas', @captchas - - load: -> - return unless @nodes.challenge.firstChild - # -1 minute to give upload some time. - @timeout = Date.now() + @lifetime * $.SECOND - $.MINUTE - challenge = @nodes.challenge.firstChild.value - @nodes.img.alt = challenge - @nodes.img.src = "//www.google.com/recaptcha/api/image?c=#{challenge}" - @nodes.input.value = null - @clear() - - count: -> - count = @captchas.length - @nodes.input.placeholder = switch count - when 0 - 'Verification (Shift + Enter to cache)' - when 1 - 'Verification (1 cached captcha)' - else - "Verification (#{count} cached captchas)" - @nodes.input.alt = count - - reload: (focus) -> - # the 't' argument prevents the input from being focused - $.globalEval 'Recaptcha.reload("t")' - # Focus if we meant to. - @nodes.input.focus() if focus - - keydown: (e) -> - if e.keyCode is 8 and not @nodes.input.value - @reload() - else if e.keyCode is 13 and e.shiftKey - @save() - else - return - e.preventDefault() + generatePostableThreadsList: -> + return unless QR.nodes + list = QR.nodes.thread + options = [list.firstChild] + for thread of g.BOARD.threads + options.push $.el 'option', + value: thread + textContent: "Thread No.#{thread}" + val = list.value + $.rmAll list + $.add list, options + list.value = val + return unless list.value + # Fix the value if the option disappeared. + list.value = if g.VIEW is 'thread' + g.THREADID + else + 'new' dialog: -> QR.nodes = nodes = - el: dialog = UI.dialog 'qr', 'top:0;right:0;', """ - <%= grunt.file.read('src/General/html/Features/QuickReply.html').replace(/>\s+<').trim() %> - """ + el: dialog = UI.dialog 'qr', 'top:0;right:0;', <%= importHTML('Features/QuickReply') %> nodes[key] = $ value, dialog for key, value of { move: '.move' @@ -964,12 +407,6 @@ QR = nodes.flag.dataset.default = '0' $.add nodes.form, nodes.flag - # Make a list of threads. - for thread of g.BOARD.threads - $.add nodes.thread, $.el 'option', - value: thread - textContent: "Thread No.#{thread}" - $.on nodes.filename.parentNode, 'click keydown', QR.openFileInput <% if (type === 'userscript') { %> @@ -1011,6 +448,7 @@ QR = $.set 'QR Size', @style.cssText <% } %> + QR.generatePostableThreadsList() QR.persona.init() new QR.post true QR.status() diff --git a/src/Posting/QR.cooldown.coffee b/src/Posting/QR.cooldown.coffee new file mode 100644 index 000000000..31201fec2 --- /dev/null +++ b/src/Posting/QR.cooldown.coffee @@ -0,0 +1,106 @@ +QR.cooldown = + init: -> + return unless Conf['Cooldown'] + setTimers = (e) => 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 of 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, {}, (item) -> + QR.cooldown.cooldowns = item[key] + QR.cooldown.start() + $.sync key, QR.cooldown.sync + start: -> + return unless Conf['Cooldown'] + return if QR.cooldown.isCounting + QR.cooldown.isCounting = true + QR.cooldown.count() + + sync: (cooldowns) -> + # Add each cooldowns, don't overwrite everything in case we + # still need to prune one in the current tab to auto-post. + for id of cooldowns + QR.cooldown.cooldowns[id] = cooldowns[id] + QR.cooldown.start() + + set: (data) -> + return unless Conf['Cooldown'] + {req, post, isReply, threadID, delay} = data + start = if req then req.uploadEndTime else Date.now() + if delay + cooldown = {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, threadID} + QR.cooldown.cooldowns[start] = cooldown + $.set "cooldown.#{g.BOARD}", QR.cooldown.cooldowns + QR.cooldown.start() + + unset: (id) -> + delete QR.cooldown.cooldowns[id] + if Object.keys(QR.cooldown.cooldowns).length + $.set "cooldown.#{g.BOARD}", QR.cooldown.cooldowns + else + $.delete "cooldown.#{g.BOARD}" + + count: -> + unless 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 isnt 'new' + hasFile = !!post.file + seconds = null + {types, cooldowns, upSpd, upSpdAccuracy} = QR.cooldown + + for start, cooldown of cooldowns + if 'delay' of cooldown + if cooldown.delay + seconds = Math.max seconds, cooldown.delay-- + else + seconds = Math.max seconds, 0 + QR.cooldown.unset start + continue + + if isReply is cooldown.isReply + # Only cooldowns relevant to this post can set the seconds variable: + # reply cooldown with a reply, thread cooldown with a thread + elapsed = Math.floor (now - start) / $.SECOND + continue if elapsed < 0 # clock changed since then? + type = unless isReply + 'thread' + else if hasFile + 'image' + else + 'reply' + maxTimer = Math.max types[type] or 0, types[type + '_intra'] or 0 + unless start <= now <= start + maxTimer * $.SECOND + QR.cooldown.unset start + type += '_intra' if isReply and +post.thread is cooldown.threadID + seconds = Math.max seconds, types[type] - elapsed + + if seconds and Conf['Cooldown Prediction'] and hasFile and upSpd + seconds -= Math.floor post.file.size / upSpd * upSpdAccuracy + seconds = if seconds > 0 then seconds else 0 + # Update the status when we change posting type. + # Don't get stuck at some random number. + # Don't interfere with progress status updates. + update = seconds isnt null or !!QR.cooldown.seconds + QR.cooldown.seconds = seconds + QR.status() if update + QR.submit() if seconds is 0 and QR.cooldown.auto and !QR.req \ No newline at end of file diff --git a/src/Posting/QR.persona.coffee b/src/Posting/QR.persona.coffee new file mode 100644 index 000000000..35238d27d --- /dev/null +++ b/src/Posting/QR.persona.coffee @@ -0,0 +1,72 @@ +QR.persona = + pwd: '' + always: {} + init: -> + QR.persona.getPassword() + $.get 'QR.personas', Conf['QR.personas'], ({'QR.personas': personas}) -> + types = + name: [] + email: [] + sub: [] + for item in personas.split '\n' + QR.persona.parseItem item.trim(), types + for type, arr of types + QR.persona.loadPersonas type, arr + return + + parseItem: (item, types) -> + return if item[0] is '#' + return unless match = item.match /(name|email|subject|password):"(.*)"/i + [match, type, val] = match + + # Don't mix up item settings with val. + item = item.replace match, '' + + boards = item.match(/boards:([^;]+)/i)?[1].toLowerCase() or 'global' + return if boards isnt 'global' and g.BOARD.ID not in boards.split ',' + + + if type is 'password' + QR.persona.pwd = val + return + + type = 'sub' if type is 'subject' + + if /always/i.test item + QR.persona.always[type] = val + + unless val in types[type] + types[type].push val + + loadPersonas: (type, arr) -> + list = $ "#list-#{type}", QR.nodes.el + for val in arr when val + $.add list, $.el 'option', + textContent: val + return + + getPassword: -> + unless QR.persona.pwd + QR.persona.pwd = if m = d.cookie.match /4chan_pass=([^;]+)/ + decodeURIComponent m[1] + else if input = $.id 'postPassword' + input.value + else + # If we're in a closed thread, #postPassword isn't available. + # And since #delPassword.value is only filled on window.onload + # we'd rather use #postPassword when we can. + $.id('delPassword').value + return QR.persona.pwd + + get: (cb) -> + $.get 'QR.persona', {}, ({'QR.persona': persona}) -> + cb persona + + set: (post) -> + $.get 'QR.persona', {}, ({'QR.persona': persona}) -> + persona = + name: post.name + email: if /^sage$/.test post.email then persona.email else post.email + sub: if Conf['Remember Subject'] then post.sub else undefined + flag: post.flag + $.set 'QR.persona', persona diff --git a/src/Posting/QR.post.coffee b/src/Posting/QR.post.coffee new file mode 100644 index 000000000..3345a864f --- /dev/null +++ b/src/Posting/QR.post.coffee @@ -0,0 +1,265 @@ +QR.post = class + constructor: (select) -> + el = $.el 'a', + className: 'qr-preview' + draggable: true + href: 'javascript:;' + innerHTML: '' + + @nodes = + el: el + rm: el.firstChild + label: $ 'label', el + spoiler: $ 'input', el + span: el.lastChild + + <% if (type === 'userscript') { %> + # XXX Firefox lacks focusin/focusout support. + for elm in $$ '*', el + $.on elm, 'blur', QR.focusout + $.on elm, 'focus', QR.focusin + <% } %> + $.on el, 'click', @select + $.on @nodes.rm, 'click', (e) => e.stopPropagation(); @rm() + $.on @nodes.label, 'click', (e) => e.stopPropagation() + $.on @nodes.spoiler, 'change', (e) => + @spoiler = e.target.checked + QR.nodes.spoiler.checked = @spoiler if @ is QR.selected + $.add QR.nodes.dumpList, el + + for event in ['dragStart', 'dragEnter', 'dragLeave', 'dragOver', 'dragEnd', 'drop'] + $.on el, event.toLowerCase(), @[event] + + @thread = if g.VIEW is 'thread' + g.THREADID + else + 'new' + + prev = QR.posts[QR.posts.length - 1] + QR.posts.push @ + @nodes.spoiler.checked = @spoiler = if prev and Conf['Remember Spoiler'] + prev.spoiler + else + false + QR.persona.get (persona) => + @name = if 'name' of QR.persona.always + QR.persona.always.name + else if prev + prev.name + else + persona.name + + @email = if 'email' of QR.persona.always + QR.persona.always.email + else if prev and !/^sage$/.test prev.email + prev.email + else + persona.email + + @sub = if 'sub' of QR.persona.always + QR.persona.always.sub + else if Conf['Remember Subject'] + if prev then prev.sub else persona.sub + else + '' + + if QR.nodes.flag + @flag = if prev + prev.flag + else + persona.flag + @load() if QR.selected is @ # load persona + @select() if select + @unlock() + + rm: -> + @delete() + index = QR.posts.indexOf @ + if QR.posts.length is 1 + new QR.post true + $.rmClass QR.nodes.el, 'dump' + else if @ is QR.selected + (QR.posts[index-1] or QR.posts[index+1]).select() + QR.posts.splice index, 1 + QR.status() + delete: -> + $.rm @nodes.el + URL.revokeObjectURL @URL + + lock: (lock=true) -> + @isLocked = lock + return unless @ is QR.selected + for name in ['thread', 'name', 'email', 'sub', 'com', 'fileButton', 'filename', 'spoiler', 'flag'] when node = QR.nodes[name] + node.disabled = lock + @nodes.rm.style.visibility = if lock then 'hidden' else '' + (if lock then $.off else $.on) QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput + @nodes.spoiler.disabled = lock + @nodes.el.draggable = !lock + + unlock: -> + @lock false + + select: => + if QR.selected + QR.selected.nodes.el.id = null + QR.selected.forceSave() + QR.selected = @ + @lock @isLocked + @nodes.el.id = 'selected' + # Scroll the list to center the focused post. + rectEl = @nodes.el.getBoundingClientRect() + rectList = @nodes.el.parentNode.getBoundingClientRect() + @nodes.el.parentNode.scrollLeft += rectEl.left + rectEl.width/2 - rectList.left - rectList.width/2 + @load() + $.event 'QRPostSelection', @ + + load: -> + # Load this post's values. + for name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'flag'] + continue unless node = QR.nodes[name] + node.value = @[name] or node.dataset.default or null + @showFileData() + QR.characterCount() + + save: (input) -> + if input.type is 'checkbox' + @spoiler = input.checked + return + {name} = input.dataset + @[name] = input.value or input.dataset.default or null + switch name + when 'thread' + QR.status() + when 'com' + @nodes.span.textContent = @com + QR.characterCount() + # Disable auto-posting if you're typing in the first post + # during the last 5 seconds of the cooldown. + if QR.cooldown.auto and @ is QR.posts[0] and 0 < QR.cooldown.seconds <= 5 + QR.cooldown.auto = false + when 'filename' + return unless @file + @file.newName = @filename.replace /[/\\]/g, '-' + unless /\.(jpe?g|png|gif|pdf|swf)$/i.test @filename + # 4chan will truncate the filename if it has no extension, + # but it will always replace the extension by the correct one, + # so we suffix it with '.jpg' when needed. + @file.newName += '.jpg' + @updateFilename() + + forceSave: -> + return unless @ is QR.selected + # Do this in case people use extensions + # that do not trigger the `input` event. + for name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler', 'flag'] + continue unless node = QR.nodes[name] + @save node + return + + setFile: (@file) -> + @filename = file.name + @filesize = $.bytesToString file.size + @nodes.label.hidden = false if QR.spoiler + URL.revokeObjectURL @URL + @showFileData() if @ is QR.selected + unless /^image/.test file.type + @nodes.el.style.backgroundImage = null + return + @setThumbnail() + + setThumbnail: -> + # Create a redimensioned thumbnail. + img = $.el 'img' + + img.onload = => + # Generate thumbnails only if they're really big. + # Resized pictures through canvases look like ass, + # so we generate thumbnails `s` times bigger then expected + # to avoid crappy resized quality. + s = 90 * 2 * window.devicePixelRatio + s *= 3 if @file.type is 'image/gif' # let them animate + {height, width} = img + if height < s or width < s + @URL = fileURL + @nodes.el.style.backgroundImage = "url(#{@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 + cv.toBlob (blob) => + @URL = URL.createObjectURL blob + @nodes.el.style.backgroundImage = "url(#{@URL})" + + fileURL = URL.createObjectURL @file + img.src = fileURL + + rmFile: -> + return if @isLocked + delete @file + delete @filename + delete @filesize + @nodes.el.title = null + QR.nodes.fileContainer.title = '' + @nodes.el.style.backgroundImage = null + @nodes.label.hidden = true if QR.spoiler + @showFileData() + URL.revokeObjectURL @URL + + updateFilename: -> + long = "#{@filename} (#{@filesize})\nCtrl+click to edit filename. Shift+click to clear." + @nodes.el.title = long + return unless @ is QR.selected + QR.nodes.fileContainer.title = long + + showFileData: -> + if @file + @updateFilename() + QR.nodes.filename.value = @filename + QR.nodes.spoiler.checked = @spoiler + $.addClass QR.nodes.fileSubmit, 'has-file' + else + $.rmClass QR.nodes.fileSubmit, 'has-file' + + pasteText: (file) -> + reader = new FileReader() + reader.onload = (e) => + text = e.target.result + if @com + @com += "\n#{text}" + else + @com = text + if QR.selected is @ + QR.nodes.com.value = @com + @nodes.span.textContent = @com + reader.readAsText file + + dragStart: (e) -> + e.dataTransfer.setDragImage @, e.layerX, e.layerY + $.addClass @, 'drag' + dragEnd: -> $.rmClass @, 'drag' + dragEnter: -> $.addClass @, 'over' + dragLeave: -> $.rmClass @, 'over' + + dragOver: (e) -> + e.preventDefault() + e.dataTransfer.dropEffect = 'move' + + drop: -> + $.rmClass @, 'over' + return unless @draggable + el = $ '.drag', @parentNode + index = (el) -> [el.parentNode.children...].indexOf el + oldIndex = index el + newIndex = index @ + (if oldIndex < newIndex then $.after else $.before) @, el + post = QR.posts.splice(oldIndex, 1)[0] + QR.posts.splice newIndex, 0, post + QR.status() \ No newline at end of file diff --git a/src/Quotelinks/QuoteOP.coffee b/src/Quotelinks/QuoteOP.coffee index 1f5554dfe..33b680112 100755 --- a/src/Quotelinks/QuoteOP.coffee +++ b/src/Quotelinks/QuoteOP.coffee @@ -19,7 +19,7 @@ QuoteOP = {quotelinks} = @nodes # rm (OP) from cross-thread quotes. - if @isClone and quotes.contains @thread.fullID + if @isClone and @thread.fullID in quotes i = 0 while quotelink = quotelinks[i++] quotelink.textContent = quotelink.textContent.replace QuoteOP.text, '' @@ -27,7 +27,7 @@ QuoteOP = {fullID} = (if @isClone then @context else @).thread # add (OP) to quotes quoting this context's OP. - return unless quotes.contains fullID + return unless fullID in quotes i = 0 while quotelink = quotelinks[i++] {boardID, postID} = Get.postDataFromLink quotelink diff --git a/src/Quotelinks/QuoteThreading.coffee b/src/Quotelinks/QuoteThreading.coffee index ec28ddfcb..f4d99c024 100755 --- a/src/Quotelinks/QuoteThreading.coffee +++ b/src/Quotelinks/QuoteThreading.coffee @@ -11,14 +11,14 @@ QuoteThreading = innerHTML: '' input = $ 'input', @controls - $.on input, 'change', QuoteThreading.toggle + $.on input, 'change', @toggle $.event 'AddMenuEntry', type: 'header' el: @controls order: 98 - $.on d, '4chanXInitFinished', @setup + $.on d, '4chanXInitFinished', @setup unless Conf['Unread Count'] Post.callbacks.push name: 'Quote Threading' @@ -26,83 +26,87 @@ QuoteThreading = setup: -> $.off d, '4chanXInitFinished', QuoteThreading.setup - {posts} = g + QuoteThreading.force() - for ID, post of posts - if post.cb - post.cb.call post - - QuoteThreading.hasRun = true + force: -> + post.cb true for ID, post of g.posts when post.cb + return node: -> - return if @isClone or not QuoteThreading.enabled or @thread.OP is @ - - {quotes, ID, fullID} = @ {posts} = g - return if !(post = posts[fullID]) or post.isHidden # Filtered + return if @isClone or not QuoteThreading.enabled + Unread.posts.push @ if Conf['Unread Count'] - uniq = {} - len = "#{g.BOARD}".length + 1 - for quote in quotes - qid = quote - continue unless qid[len..] < ID - if qid of posts - uniq[qid[len..]] = true + return if @thread.OP is @ or !(post = posts[@fullID]) or post.isHidden # Filtered + + keys = [] + len = g.BOARD.ID.length + 1 + keys.push quote for quote in @quotes when (quote[len..] < @ID) and quote of posts - keys = Object.keys uniq return unless keys.length is 1 - @threaded = "#{g.BOARD}.#{keys[0]}" + @threaded = keys[0] @cb = QuoteThreading.nodeinsert - nodeinsert: -> - qpost = g.posts[@threaded] + nodeinsert: (force) -> + post = g.posts[@threaded] - delete @threaded - delete @cb + return false if @thread.OP is post - return false if @thread.OP is qpost + {posts} = Unread + {root} = post.nodes - if QuoteThreading.hasRun + unless force height = doc.clientHeight - {bottom, top} = qpost.nodes.root.getBoundingClientRect() + {bottom, top} = root.getBoundingClientRect() # Post is unread or is fully visible. - return false unless Unread.posts.contains(qpost) or ((bottom < height) and (top > 0)) + return false unless (Conf['Unread Count'] and posts[post.ID]) or ((bottom < height) and (top > 0)) - qroot = qpost.nodes.root - unless $.hasClass qroot, 'threadOP' - $.addClass qroot, 'threadOP' + if $.hasClass root, 'threadOP' + threadContainer = root.nextElementSibling + post = Get.postFromRoot $.x 'descendant::div[contains(@class,"postContainer")][last()]', threadContainer + $.add threadContainer, @nodes.root + + else threadContainer = $.el 'div', className: 'threadContainer' - $.after qroot, threadContainer - else - threadContainer = qroot.nextSibling + $.add threadContainer, @nodes.root + $.after root, threadContainer + $.addClass root, 'threadOP' + + return true unless Conf['Unread Count'] + + if posts[post.ID] + posts.after post, @ + + else + posts.prepend @ - $.add threadContainer, @nodes.root return true toggle: -> - thread = $ '.thread' - replies = $$ '.thread > .replyContainer, .threadContainer > .replyContainer', thread - QuoteThreading.enabled = @checked - if @checked - QuoteThreading.hasRun = false - for reply in replies - QuoteThreading.node.call node = Get.postFromRoot reply - node.cb() if node.cb - QuoteThreading.hasRun = true + if QuoteThreading.enabled = @checked + QuoteThreading.force() + else - replies.sort (a, b) -> - aID = Number a.id[2..] - bID = Number b.id[2..] - aID - bID - $.add thread, replies + thread = $('.thread') + posts = [] + nodes = [] + + posts.push post for ID, post of g.posts when not (post is post.thread.OP or post.isClone) + posts.sort (a, b) -> a.ID - b.ID + + nodes.push post.nodes.root for post in posts + $.add thread, nodes + containers = $$ '.threadContainer', thread $.rm container for container in containers $.rmClass post, 'threadOP' for post in $$ '.threadOP' - Unread.update true + + return kb: -> control = $.id 'threadingControl' - control.click() \ No newline at end of file + control.checked = not control.checked + QuoteThreading.toggle.call control \ No newline at end of file diff --git a/src/Quotelinks/Quotify.coffee b/src/Quotelinks/Quotify.coffee index 3fd3fc2bb..6d00037a7 100755 --- a/src/Quotelinks/Quotify.coffee +++ b/src/Quotelinks/Quotify.coffee @@ -69,19 +69,16 @@ Quotify = $.addClass a, 'quotelink' $.extend a.dataset, {boardID, postID} - unless @quotes.contains quoteID - @quotes.push quoteID + @quotes.push quoteID unless quoteID in @quotes - unless a - deadlink.textContent = "#{quote}\u00A0(Dead)" - return + return deadlink.textContent = "#{quote}\u00A0(Dead)" unless a $.replace deadlink, a if $.hasClass a, 'quotelink' @nodes.quotelinks.push a fixDeadlink: (deadlink) -> - if !(el = deadlink.previousSibling) or el.nodeName is 'BR' + if not (el = deadlink.previousSibling) or el.nodeName is 'BR' green = $.el 'span', className: 'quote' $.before deadlink, green