diff --git a/CHANGELOG.md b/CHANGELOG.md index 8eab756be..bac0d5303 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,3 @@ - **MayhemYDG**: - Tiny posting cooldown adjustment: * You can post an image reply immediately after a non-image reply. @@ -9,10 +8,14 @@ * Timers vary across boards. - Updated post and deletion cooldown timers to match 4chan changes: they are now twice longer. - Added support for the flag selector on /pol/. +- Minor Chrome 30 fix. **seaweedchan**: - Fix thread updater bug introduced in last version - Just some small fixes. +- Delete cooldown update +- Small bug fixes +- Don't show warnings AND desktop notifications at the same time, and prefer QR warnings unless the document is hidden **zixaphir**: - Some changes to mascots @@ -36,6 +39,7 @@ * The QR will now warn you when you are running low on cached captchas while auto-posting. **seaweedchan**: +<<<<<<< HEAD - Gallery Bugfix: hide thumbnails **Zixaphir**: @@ -66,6 +70,12 @@ *2013-08-21* **Zixaphir**: +======= + - Visual overhaul for gallery mode + +**Zixaphir**: + - Fix an issue with the file dialog randomly opening multiple times (with seaweedchan) +>>>>>>> v3 ![Gallery](src/General/img/changelog/2.3.6.png) - **New Feature**: `Gallery`. * Disabled by default. diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 313210131..86739fb0e 100755 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -90,11 +90,11 @@ module.exports = (grunt) -> stderr: true failOnError: true commit: - command: [ - 'git commit -am "Release <%= pkg.meta.name %> v<%= pkg.version %>."' - 'git tag -a <%= pkg.version %> -m "<%= pkg.meta.name %> v<%= pkg.version %>."' - 'git tag -af stable -m "<%= pkg.meta.name %> v<%= pkg.version %>."' - ].join ' && ' + command: """ + git commit -am "Release <%= pkg.meta.name %> v<%= pkg.version %>." + git tag -a <%= pkg.version %> -m "<%= pkg.meta.name %> v<%= pkg.version %>." + git tag -af stable -m "<%= pkg.meta.name %> v<%= pkg.version %>." + """ push: command: 'git push origin --tags -f && git push origin --all' diff --git a/LICENSE b/LICENSE index 28109fe2c..c04c1330c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* -* appchan x - Version 2.3.10 - 2013-09-24 +* appchan x - Version 2.3.10 - 2013-10-13 * * Licensed under the MIT license. * https://github.com/zixaphir/appchan-x/blob/master/LICENSE diff --git a/builds/4chan-X.meta.js b/builds/4chan-X.meta.js new file mode 100755 index 000000000..3c8c07303 --- /dev/null +++ b/builds/4chan-X.meta.js @@ -0,0 +1,21 @@ +// ==UserScript== +// @name 4chan X +// @version 1.2.41 +// @minGMVer 1.13 +// @minFFVer 22 +// @namespace 4chan-X +// @description Cross-browser userscript for maximum lurking on 4chan. +// @license MIT; https://github.com/seaweedchan/4chan-x/blob/master/LICENSE +// @match *://api.4chan.org/* +// @match *://boards.4chan.org/* +// @match *://images.4chan.org/* +// @match *://sys.4chan.org/* +// @grant GM_getValue +// @grant GM_setValue +// @grant GM_deleteValue +// @grant GM_openInTab +// @run-at document-start +// @updateURL https://github.com/seaweedchan/4chan-x/raw/stable/builds/4chan-X.meta.js +// @downloadURL https://github.com/seaweedchan/4chan-x/raw/stable/builds/4chan-X.user.js +// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC +// ==/UserScript== diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js new file mode 100644 index 000000000..a08d0fe53 --- /dev/null +++ b/builds/4chan-X.user.js @@ -0,0 +1,11855 @@ +// Generated by CoffeeScript +// ==UserScript== +// @name 4chan X +// @version 1.2.41 +// @minGMVer 1.13 +// @minFFVer 22 +// @namespace 4chan-X +// @description Cross-browser userscript for maximum lurking on 4chan. +// @license MIT; https://github.com/seaweedchan/4chan-x/blob/master/LICENSE +// @match *://api.4chan.org/* +// @match *://boards.4chan.org/* +// @match *://images.4chan.org/* +// @match *://sys.4chan.org/* +// @grant GM_getValue +// @grant GM_setValue +// @grant GM_deleteValue +// @grant GM_openInTab +// @run-at document-start +// @updateURL https://github.com/seaweedchan/4chan-x/raw/stable/builds/4chan-X.meta.js +// @downloadURL https://github.com/seaweedchan/4chan-x/raw/stable/builds/4chan-X.user.js +// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC +// ==/UserScript== + +/* +* 4chan X - Version 1.2.41 - 2013-10-13 +* +* Licensed under the MIT license. +* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE +* +* Appchan X Copyright © 2013-2013 Zixaphir +* http://zixaphir.github.io/appchan-x/ +* 4chan x Copyright © 2009-2011 James Campos +* https://github.com/aeosynth/4chan-x +* 4chan x Copyright © 2012-2013 Nicolas Stepien +* https://4chan-x.just-believe.in/ +* 4chan x Copyright © 2013-2013 Jordan Bates +* http://seaweedchan.github.io/4chan-x/ +* 4chan x Copyright © 2012-2013 ihavenoface +* http://ihavenoface.github.io/4chan-x/ +* 4chan SS Copyright © 2011-2013 Ahodesuka +* https://github.com/ahodesuka/4chan-Style-Script/ +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, +* copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following +* conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +* OTHER DEALINGS IN THE SOFTWARE. +* +* Contributors: +* aeosynth +* mayhemydg +* noface +* !K.WeEabo0o +* blaise +* that4chanwolf +* desuwa +* seaweed +* e000 +* ahodesuka +* Shou +* ferongr +* xat +* Ongpot +* thisisanon +* Anonymous +* Seiba +* herpaderpderp +* WakiMiko +* btmcsweeney +* AppleBloom +* detharonil +* +* All the people who've taken the time to write bug reports. +* +* Thank you. +*/ + +/* +* Contains data from external sources: +* +* audio/beep.wav from http://freesound.org/people/pierrecartoons1979/sounds/90112/ +* cc-by-nc-3.0 +* +* 4chan/4chan-JS (https://github.com/4chan/4chan-JS) +* Copyright (c) 2012-2013, 4chan LLC +* All rights reserved. +* +* license: https://github.com/4chan/4chan-JS/blob/master/LICENSE +*/ +'use strict'; + +(function() { + var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, CatalogLinks, Clone, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, Header, IDColor, ImageExpand, ImageHover, ImageLoader, 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, + __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); }; }; + + Config = { + main: { + 'Miscellaneous': { + 'Catalog Links': [true, 'Add toggle link in header menu to turn Navigation links into links to each board\'s catalog.'], + 'External Catalog': [false, 'Link to external catalog instead of the internal one.'], + 'QR Shortcut': [false, 'Adds a small [QR] link in the header.'], + 'Announcement Hiding': [true, 'Add button to hide 4chan announcements.'], + 'Desktop Notifications': [false, 'Enables desktop notifications across various 4chan X features.'], + '404 Redirect': [true, 'Redirect dead threads and images.'], + 'Keybinds': [true, 'Bind actions to keyboard shortcuts.'], + 'Time Formatting': [true, 'Localize and format timestamps.'], + 'Relative Post Dates': [true, 'Display dates like "3 minutes ago". Tooltip shows the timestamp.'], + 'File Info Formatting': [true, 'Reformat the file information.'], + '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.'], + 'Show Dice Roll': [true, 'Show dice that were entered into the email field.'], + 'Custom Board Titles': [true, 'Allow editing of the board title and subtitle by ctrl+clicking them'], + 'Persistent Custom Board Titles': [false, 'Force custom board titles to be persistent, even if moot updates the board titles.'], + 'Show Updated Notifications': [true, 'Show notifications when 4chan X is successfully updated.'], + 'Emoji': [false, 'Adds icons next to names for different emails'], + 'Color User IDs': [false, 'Assign unique colors to user IDs on boards that use them'], + 'Remove Spoilers': [false, 'Remove all spoilers in text.'], + 'Reveal Spoilers': [false, 'Indicate spoilers if Remove Spoilers is enabled, or make the text appear hovered if Remove Spoiler is disabled.'] + }, + 'Linkification': { + 'Linkify': [true, 'Convert text into links where applicable.'], + 'Embedding': [true, 'Embed supported services.'], + 'Auto-embed': [false, 'Auto-embed Linkify Embeds.'], + 'Link Title': [true, 'Replace the link of a supported site with its actual title. Currently Supported: YouTube, Vimeo, SoundCloud, and Github gists'] + }, + 'Filtering': { + 'Anonymize': [false, 'Make everyone Anonymous.'], + 'Filter': [true, 'Self-moderation placebo.'], + 'Recursive Hiding': [true, 'Hide replies of hidden posts, recursively.'], + 'Thread Hiding Buttons': [false, 'Add buttons to hide entire threads.'], + 'Reply Hiding Buttons': [false, 'Add buttons to hide single replies.'], + 'Filtered Backlinks': [true, 'When enabled, shows backlinks to filtered posts with a line-through decoration. Otherwise, hides the backlinks.'], + 'Stubs': [true, 'Show stubs of hidden threads / replies.'] + }, + 'Images': { + 'Image Expansion': [true, 'Expand images.'], + 'Image Hover': [true, 'Show full image on mouseover.'], + 'Gallery': [true, 'Adds a simple and cute image gallery.'], + 'Sauce': [true, 'Add sauce links to images.'], + 'Reveal Spoiler Thumbnails': [false, 'Replace spoiler thumbnails with the original image.'], + 'Replace GIF': [false, 'Replace thumbnail of gifs with its actual image.'], + 'Replace PNG': [false, 'Replace pngs.'], + 'Replace JPG': [false, 'Replace jpgs.'], + 'Image Prefetching': [false, 'Preload images'], + 'Fappe Tyme': [false, 'Hide posts without images. *hint* *hint*'], + 'Werk Tyme': [false, 'Hide all post images.'] + }, + 'Menu': { + 'Menu': [true, 'Add a drop-down menu to posts.'], + 'Report Link': [true, 'Add a report link to the menu.'], + 'Thread Hiding Link': [true, 'Add a link to hide entire threads.'], + 'Reply Hiding Link': [true, 'Add a link to hide single replies.'], + 'Delete Link': [true, 'Add post and image deletion links to the menu.'], + 'Archive Link': [true, 'Add an archive link to the menu.'] + }, + 'Monitoring': { + 'Thread Updater': [true, 'Fetch and insert new replies. Has more options in its own dialog.'], + 'Unread Count': [true, 'Show the unread posts count in the tab title.'], + 'Hide Unread Count at (0)': [false, 'Hide the unread posts count in the tab title when it reaches 0.'], + 'Unread Favicon': [true, 'Show a different favicon when there are unread posts.'], + 'Unread Line': [true, 'Show a line to distinguish read posts from unread ones.'], + 'Scroll to Last Read Post': [true, 'Scroll back to the last read post when reopening a thread.'], + 'Thread Excerpt': [true, 'Show an excerpt of the thread in the tab title.'], + 'Thread Stats': [true, 'Display reply and image count.'], + 'Page Count in Stats': [false, 'Display the page count in the thread stats as well.'], + 'Updater and Stats in Header': [true, 'Places the thread updater and thread stats in the header instead of floating them.'], + 'Thread Watcher': [true, 'Bookmark threads.'], + 'Toggleable Thread Watcher': [true, 'Adds a shortcut for the thread watcher, hides the watcher by default, and makes it scroll with the page.'] + }, + 'Posting': { + 'Quick Reply': [true, 'All-in-one form to reply, create threads, automate dumping and more.'], + 'Persistent QR': [true, 'The Quick reply won\'t disappear after posting.'], + 'Auto Hide QR': [true, 'Automatically hide the quick reply when posting.'], + 'Open Post in New Tab': [true, 'Open new threads or replies to a thread from the index in a new tab.'], + 'Remember Subject': [false, 'Remember the subject field, instead of resetting after posting.'], + 'Remember QR Size': [false, 'Remember the size of the Quick reply.'], + 'Remember Spoiler': [false, 'Remember the spoiler state, instead of resetting after posting.'], + 'Hide Original Post Form': [true, 'Hide the normal post form.'], + 'Cooldown': [true, 'Indicate the remaining time before posting again.'], + 'Cooldown Prediction': [true, 'Decrease the cooldown time by taking into account upload speed. Disable it if it\'s inaccurate for you.'], + 'Posting Success Notifications': [true, 'Show notifications on successful post creation or file uploading.'], + 'Captcha Warning Notifications': [true, 'When disabled, shows a red border on the CAPTCHA input until a key is pressed instead of a notification.'] + }, + 'Quote Links': { + 'Quote Backlinks': [true, 'Add quote backlinks.'], + 'OP Backlinks': [true, 'Add backlinks to the OP.'], + 'Quote Inlining': [true, 'Inline quoted post on click.'], + 'Quote Hash Navigation': [false, 'Include an extra link after quotes for autoscrolling to quoted posts.'], + 'Forward Hiding': [true, 'Hide original posts of inlined backlinks.'], + 'Quote Previewing': [true, 'Show quoted post on hover.'], + 'Quote Highlighting': [true, 'Highlight the previewed post.'], + 'Resurrect Quotes': [true, 'Link dead quotes to the archives.'], + 'Mark Quotes of You': [true, 'Add \'(You)\' to quotes linking to your posts.'], + 'Quoted Title': [false, 'Change the page title to reflect you\'ve been quoted.'], + 'Highlight Posts Quoting You': [false, 'Highlights any posts that contain a quote to your post.'], + 'Highlight Own Posts': [false, 'Highlights own posts if Mark Quotes of You is enabled.'], + 'Mark OP Quotes': [true, 'Add \'(OP)\' to OP quotes.'], + 'Mark Cross-thread Quotes': [true, 'Add \'(Cross-thread)\' to cross-threads quotes.'], + 'Quote Threading': [true, 'Thread conversations'] + } + }, + imageExpansion: { + 'Fit width': [false, ''], + 'Fit height': [false, ''], + 'Expand spoilers': [true, 'Expand all images along with spoilers.'], + 'Expand from here': [false, 'Expand all images only from current position to thread end.'], + 'Advance on contract': [false, 'Advance to next post when contracting an expanded image.'] + }, + gallery: { + 'Hide Thumbnails': [false], + 'Fit Width': [true], + 'Fit Height': [true] + }, + threadWatcher: { + 'Current Board': [false, 'Only show watched threads from the current board.'], + 'Auto Watch': [true, 'Automatically watch threads you start.'], + 'Auto Watch Reply': [false, 'Automatically watch threads you reply to.'], + 'Auto Prune': [false, 'Automatically prune 404\'d threads.'] + }, + filter: { + name: "# Filter any namefags:\n#/^(?!Anonymous$)/", + uniqueID: "# Filter a specific ID:\n#/Txhvk1Tl/", + tripcode: "# Filter any tripfag\n#/^!/", + capcode: "# Set a custom class for mods:\n#/Mod$/;highlight:mod;op:yes\n# Set a custom class for moot:\n#/Admin$/;highlight:moot;op:yes", + email: "", + subject: "# Filter Generals on /v/:\n#/general/i;boards:v;op:only", + comment: "# Filter Stallman copypasta on /g/:\n#/what you\'re refer+ing to as linux/i;boards:g", + flag: '', + filename: '', + dimensions: "# Highlight potential wallpapers:\n#/1920x1080/;op:yes;highlight;top:no;boards:w,wg", + filesize: '', + MD5: '' + }, + sauces: "https://www.google.com/searchbyimage?image_url=%TURL\nhttp://iqdb.org/?url=%TURL\n#//tineye.com/search?url=%TURL\n#http://saucenao.com/search.php?url=%TURL\n#http://3d.iqdb.org/?url=%TURL\n#http://regex.info/exif.cgi?imgurl=%URL\n# uploaders:\n#http://imgur.com/upload?url=%URL;text:Upload to imgur\n#http://ompldr.org/upload?url1=%URL;text:Upload to ompldr\n# \"View Same\" in archives:\n#//archive.foolz.us/_/search/image/%MD5/;text:View same on foolz\n#//archive.foolz.us/%board/search/image/%MD5/;text:View same on foolz /%board/\n#//archive.installgentoo.net/%board/image/%MD5;text:View same on installgentoo /%board/", + 'sageEmoji': '4chan SS', + 'emojiPos': 'before', + 'Custom CSS': 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 + }, + boardnav: "[ toggle-all ]\na-replace\nc-replace\ng-replace\nk-replace\nv-replace\nvg-replace\nvr-replace\nck-replace\nco-replace\nfit-replace\njp-replace\nmu-replace\nsp-replace\ntv-replace\nvp-replace\n[external-text:\"FAQ\",\"https://github.com/seaweedchan/4chan-x/wiki/Frequently-Asked-Questions\"]", + QR: { + 'QR.personas': "#email:\"sage\";boards:jp;always" + }, + time: '%m/%d/%y(%a)%H:%M:%S', + backlink: '>>%id', + fileInfo: '%L (%p%s, %r)', + favicon: 'ferongr', + usercss: '', + hotkeys: { + 'Toggle board list': ['Ctrl+b', 'Toggle the full board list.'], + 'Toggle header': ['Shift+h', 'Toggle the auto-hide option of the header.'], + 'Open empty QR': ['i', 'Open QR without post number inserted.'], + 'Open QR': ['Shift+i', 'Open QR with post number inserted.'], + 'Open settings': ['Alt+o', 'Open Settings.'], + 'Close': ['Esc', 'Close Settings, Notifications or QR.'], + 'Spoiler tags': ['Ctrl+s', 'Insert spoiler tags.'], + 'Code tags': ['Alt+c', 'Insert code tags.'], + 'Eqn tags': ['Alt+e', 'Insert eqn tags.'], + 'Math tags': ['Alt+m', 'Insert math tags.'], + 'Toggle sage': ['Alt+s', 'Toggle sage in email field'], + 'Submit QR': ['Ctrl+Enter', 'Submit post.'], + 'Watch': ['w', 'Watch thread.'], + 'Update': ['r', 'Update the thread now.'], + 'Expand image': ['Shift+e', 'Expand selected image.'], + 'Expand images': ['e', 'Expand all images.'], + 'Open Gallery': ['g', 'Opens the gallery.'], + 'fappeTyme': ['f', 'Fappe Tyme.'], + 'werkTyme': ['Shift+w', 'Werk Tyme'], + 'Front page': ['0', 'Jump to page 0.'], + 'Open front page': ['Shift+0', 'Open page 0 in a new tab.'], + 'Next page': ['Shift+Right', 'Jump to the next page.'], + 'Previous page': ['Shift+Left', 'Jump to the previous page.'], + 'Open catalog': ['Shift+c', 'Open the catalog of the current board'], + 'Next thread': ['Shift+Down', 'See next thread.'], + 'Previous thread': ['Shift+Up', 'See previous thread.'], + 'Expand thread': ['Ctrl+e', 'Expand thread.'], + 'Open thread': ['o', 'Open thread in current tab.'], + 'Open thread tab': ['Shift+o', 'Open thread in new tab.'], + 'Next reply': ['j', 'Select next reply.'], + 'Previous reply': ['k', 'Select previous reply.'], + 'Deselect reply': ['Shift+d', 'Deselect reply.'], + 'Hide': ['x', 'Hide thread.'], + 'Previous Post Quoting You': ['Alt+Up', 'Scroll to the previous post that quotes you.'], + 'Next Post Quoting You': ['Alt+Down', 'Scroll to the next post that quotes you.'] + }, + updater: { + checkbox: { + 'Beep': [false, 'Beep on new post to completely read thread.'], + 'Auto Scroll': [false, 'Scroll updated posts into view. Only enabled at bottom of page.'], + 'Bottom Scroll': [false, 'Always scroll to the bottom, not the first new post. Useful for event threads.'], + 'Scroll BG': [false, 'Auto-scroll background tabs.'], + 'Auto Update': [true, 'Automatically fetch new posts.'], + 'Optional Increase': [false, 'Increase the intervals between updates on threads without new posts.'] + }, + 'Interval': 30 + } + }; + + Conf = {}; + + c = console; + + d = document; + + doc = d.documentElement; + + g = { + VERSION: '1.2.41', + NAMESPACE: '4chan X.', + boards: {}, + threads: {}, + 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; + } + return root.querySelector(selector); + }; + + $.extend = function(object, properties) { + var key, val; + + for (key in properties) { + val = properties[key]; + if (!properties.hasOwnProperty(key)) { + continue; + } + object[key] = val; + } + }; + + $.DAY = 24 * ($.HOUR = 60 * ($.MINUTE = 60 * ($.SECOND = 1000))); + + $.id = function(id) { + return d.getElementById(id); + }; + + $.ready = function(fc) { + var cb; + + if (d.readyState !== 'loading') { + $.queueTask(fc); + return; + } + cb = function() { + $.off(d, 'DOMContentLoaded', cb); + return fc(); + }; + return $.on(d, 'DOMContentLoaded', cb); + }; + + $.formData = function(form) { + var fd, key, val; + + if (form instanceof HTMLFormElement) { + return new FormData(form); + } + fd = new FormData(); + for (key in form) { + val = form[key]; + if (val) { + if (typeof val === 'object' && 'newName' in val) { + fd.append(key, val, val.newName); + } else { + fd.append(key, val); + } + } + } + return fd; + }; + + $.extend = function(object, properties) { + var key, val; + + for (key in properties) { + val = properties[key]; + object[key] = val; + } + }; + + $.ajax = (function() { + var lastModified; + + lastModified = {}; + return function(url, options, extra) { + var form, r, sync, type, upCallbacks, whenModified; + + if (extra == null) { + extra = {}; + } + type = extra.type, whenModified = extra.whenModified, upCallbacks = extra.upCallbacks, form = extra.form, sync = extra.sync; + r = new XMLHttpRequest(); + type || (type = form && 'post' || 'get'); + r.open(type, url, !sync); + if (whenModified) { + r.setRequestHeader('If-Modified-Since', lastModified[url] || '0'); + $.on(r, 'load', function() { + return lastModified[url] = r.getResponseHeader('Last-Modified'); + }); + } + $.extend(r, options); + $.extend(r.upload, upCallbacks); + r.send(form); + return r; + }; + })(); + + $.cache = (function() { + var reqs; + + reqs = {}; + return function(url, cb, options) { + var err, req, rm; + + if (req = reqs[url]) { + if (req.readyState === 4) { + cb.call(req, req.evt); + } else { + req.callbacks.push(cb); + } + return; + } + rm = function() { + return delete reqs[url]; + }; + try { + req = $.ajax(url, options); + } catch (_error) { + err = _error; + return; + } + $.on(req, 'load', function(e) { + var _i, _len, _ref; + + _ref = this.callbacks; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + cb = _ref[_i]; + cb.call(this, e); + } + this.evt = e; + return delete this.callbacks; + }); + $.on(req, 'abort error', rm); + req.callbacks = [cb]; + return reqs[url] = req; + }; + })(); + + $.cb = { + checked: function() { + $.set(this.name, this.checked); + return Conf[this.name] = this.checked; + }, + value: function() { + $.set(this.name, this.value.trim()); + return Conf[this.name] = this.value; + } + }; + + $.asap = function(test, cb) { + if (test()) { + return cb(); + } else { + return setTimeout($.asap, 25, test, cb); + } + }; + + $.addStyle = function(css, id) { + var style; + + style = $.el('style', { + id: id, + textContent: css + }); + $.asap((function() { + return d.head; + }), function() { + return $.add(d.head, style); + }); + return style; + }; + + $.x = function(path, root) { + root || (root = d.body); + return d.evaluate(path, root, null, 8, null).singleNodeValue; + }; + + $.X = function(path, root) { + root || (root = d.body); + return d.evaluate(path, root, null, 7, null); + }; + + $.addClass = function(el, className) { + return el.classList.add(className); + }; + + $.rmClass = function(el, className) { + return el.classList.remove(className); + }; + + $.toggleClass = function(el, className) { + return el.classList.toggle(className); + }; + + $.hasClass = function(el, className) { + return el.classList.contains(className); + }; + + $.rm = (function() { + if ('remove' in Element.prototype) { + return function(el) { + return el.remove(); + }; + } else { + return function(el) { + var _ref; + + return (_ref = el.parentNode) != null ? _ref.removeChild(el) : void 0; + }; + } + })(); + + $.rmAll = function(root) { + var node; + + while (node = root.firstChild) { + root.removeChild(node); + } + }; + + $.tn = function(s) { + return d.createTextNode(s); + }; + + $.frag = function() { + return d.createDocumentFragment(); + }; + + $.nodes = function(nodes) { + var frag, node, _i, _len; + + if (!(nodes instanceof Array)) { + return nodes; + } + frag = $.frag(); + for (_i = 0, _len = nodes.length; _i < _len; _i++) { + node = nodes[_i]; + frag.appendChild(node); + } + return frag; + }; + + $.add = function(parent, el) { + return parent.appendChild($.nodes(el)); + }; + + $.prepend = function(parent, el) { + return parent.insertBefore($.nodes(el), parent.firstChild); + }; + + $.after = function(root, el) { + return root.parentNode.insertBefore($.nodes(el), root.nextSibling); + }; + + $.before = function(root, el) { + return root.parentNode.insertBefore($.nodes(el), root); + }; + + $.replace = function(root, el) { + return root.parentNode.replaceChild($.nodes(el), root); + }; + + $.el = function(tag, properties) { + var el; + + el = d.createElement(tag); + if (properties) { + $.extend(el, properties); + } + return el; + }; + + $.on = function(el, events, handler) { + var event, _i, _len, _ref; + + _ref = events.split(' '); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + event = _ref[_i]; + el.addEventListener(event, handler, false); + } + }; + + $.off = function(el, events, handler) { + var event, _i, _len, _ref; + + _ref = events.split(' '); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + event = _ref[_i]; + el.removeEventListener(event, handler, false); + } + }; + + $.event = function(event, detail, root) { + if (root == null) { + root = d; + } + return root.dispatchEvent(new CustomEvent(event, { + bubbles: true, + detail: detail + })); + }; + + $.open = GM_openInTab; + + $.debounce = function(wait, fn) { + var args, exec, lastCall, that, timeout; + + lastCall = 0; + timeout = null; + that = null; + args = null; + exec = function() { + lastCall = Date.now(); + return fn.apply(that, args); + }; + return function() { + args = arguments; + that = this; + if (lastCall < Date.now() - wait) { + return exec(); + } + clearTimeout(timeout); + return timeout = setTimeout(exec, wait); + }; + }; + + $.queueTask = (function() { + var execTask, taskChannel, taskQueue; + + taskQueue = []; + execTask = function() { + var args, func, task; + + task = taskQueue.shift(); + func = task[0]; + args = Array.prototype.slice.call(task, 1); + return func.apply(func, args); + }; + if (window.MessageChannel) { + taskChannel = new MessageChannel(); + taskChannel.port1.onmessage = execTask; + return function() { + taskQueue.push(arguments); + return taskChannel.port2.postMessage(null); + }; + } else { + return function() { + taskQueue.push(arguments); + return setTimeout(execTask, 0); + }; + } + })(); + + $.globalEval = function(code) { + var script; + + script = $.el('script', { + textContent: code + }); + $.add(d.head || doc, script); + return $.rm(script); + }; + + $.bytesToString = function(size) { + var unit; + + unit = 0; + while (size >= 1024) { + size /= 1024; + unit++; + } + size = unit > 1 ? Math.round(size * 100) / 100 : Math.round(size); + return "" + size + " " + ['B', 'KB', 'MB', 'GB'][unit]; + }; + + $.minmax = function(value, min, max) { + return (value < min ? min : value > max ? max : value); + }; + + $.item = function(key, val) { + var item; + + item = {}; + item[key] = val; + return item; + }; + + $.syncing = {}; + + $.sync = (function() { + $.on(window, 'storage', function(e) { + var cb; + + if (cb = $.syncing[e.key]) { + return cb(JSON.parse(e.newValue)); + } + }); + return function(key, cb) { + return $.syncing[g.NAMESPACE + key] = cb; + }; + })(); + + $["delete"] = function(keys) { + var key, _i, _len; + + if (!(keys instanceof Array)) { + keys = [keys]; + } + for (_i = 0, _len = keys.length; _i < _len; _i++) { + key = keys[_i]; + key = g.NAMESPACE + key; + localStorage.removeItem(key); + GM_deleteValue(key); + } + }; + + $.get = function(key, val, cb) { + var items; + + if (typeof cb === 'function') { + items = $.item(key, val); + } else { + items = key; + cb = val; + } + return $.queueTask(function() { + for (key in items) { + if (val = GM_getValue(g.NAMESPACE + key)) { + items[key] = JSON.parse(val); + } + } + return cb(items); + }); + }; + + $.set = (function() { + var set; + + set = function(key, val) { + key = g.NAMESPACE + key; + val = JSON.stringify(val); + if (key in $.syncing) { + localStorage.setItem(key, val); + } + return GM_setValue(key, val); + }; + return function(keys, val) { + var key; + + if (typeof keys === 'string') { + set(keys, val); + return; + } + for (key in keys) { + val = keys[key]; + set(key, val); + } + }; + })(); + + $$ = function(selector, root) { + if (root == null) { + root = d.body; + } + return __slice.call(root.querySelectorAll(selector)); + }; + + Board = (function() { + Board.prototype.toString = function() { + return this.ID; + }; + + function Board(ID) { + this.ID = ID; + this.threads = {}; + this.posts = {}; + g.boards[this] = this; + } + + return Board; + + })(); + + Thread = (function() { + Thread.callbacks = []; + + Thread.prototype.toString = function() { + return this.ID; + }; + + function Thread(ID, board) { + this.ID = ID; + this.board = board; + this.fullID = "" + this.board + "." + this.ID; + this.posts = {}; + g.threads[this.fullID] = board.threads[this] = this; + } + + Thread.prototype.kill = function() { + this.isDead = true; + return this.timeOfDeath = Date.now(); + }; + + return Thread; + + })(); + + Post = (function() { + Post.callbacks = []; + + Post.prototype.toString = function() { + return this.ID; + }; + + function Post(root, thread, board, that) { + var capcode, date, email, flag, info, name, post, subject, tripcode, uniqueID; + + this.thread = thread; + this.board = board; + if (that == null) { + that = {}; + } + this.ID = +root.id.slice(2); + this.fullID = "" + this.board + "." + this.ID; + post = $('.post', root); + info = $('.postInfo', post); + this.nodes = { + root: root, + post: post, + info: info, + comment: $('.postMessage', post), + links: [], + quotelinks: [], + backlinks: info.getElementsByClassName('backlink') + }; + if (!(this.isReply = $.hasClass(post, 'reply'))) { + this.thread.OP = this; + this.thread.isSticky = !!$('.stickyIcon', info); + this.thread.isClosed = !!$('.closedIcon', info); + } + this.info = {}; + if (subject = $('.subject', info)) { + this.nodes.subject = subject; + this.info.subject = subject.textContent; + } + if (name = $('.name', info)) { + this.nodes.name = name; + this.info.name = name.textContent; + } + if (email = $('.useremail', info)) { + this.nodes.email = email; + this.info.email = decodeURIComponent(email.href.slice(7)); + } + if (tripcode = $('.postertrip', info)) { + this.nodes.tripcode = tripcode; + this.info.tripcode = tripcode.textContent; + } + if (uniqueID = $('.posteruid', info)) { + this.nodes.uniqueID = uniqueID; + this.info.uniqueID = uniqueID.firstElementChild.textContent; + } + if (capcode = $('.capcode.hand', info)) { + this.nodes.capcode = capcode; + this.info.capcode = capcode.textContent.replace('## ', ''); + } + if (flag = $('.flag, .countryFlag', info)) { + this.nodes.flag = flag; + this.info.flag = flag.title; + } + if (date = $('.dateTime', info)) { + this.nodes.date = date; + this.info.date = new Date(date.dataset.utc * 1000); + } + if (Conf['Quick Reply']) { + this.info.yours = QR.db.get({ + boardID: this.board, + threadID: this.thread, + postID: this.ID + }); + } + this.parseComment(); + this.parseQuotes(); + this.parseFile(that); + this.clones = []; + g.posts[this.fullID] = thread.posts[this] = board.posts[this] = this; + if (that.isArchived) { + this.kill(); + } + } + + Post.prototype.parseComment = function() { + var bq, i, node, nodes, text; + + this.nodes.comment.normalize(); + bq = this.nodes.comment.cloneNode(true); + nodes = $$('.abbr, .exif, b', bq); + i = 0; + while (node = nodes[i++]) { + $.rm(node); + } + text = ""; + nodes = $.X('.//br|.//text()', bq); + i = 0; + while (node = nodes.snapshotItem(i++)) { + text += node.data || '\n'; + } + return this.info.comment = text.trim().replace(/\s+$/gm, ''); + }; + + Post.prototype.parseQuotes = function() { + var quotelink, _i, _len, _ref; + + this.quotes = []; + _ref = $$('.quotelink', this.nodes.comment); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + quotelink = _ref[_i]; + this.parseQuote(quotelink); + } + }; + + Post.prototype.parseQuote = function(quotelink) { + var fullID, match; + + if (!(match = quotelink.href.match(/boards\.4chan\.org\/([^\/]+)\/res\/\d+#p(\d+)$/))) { + return; + } + this.nodes.quotelinks.push(quotelink); + if (this.isClone) { + return; + } + fullID = "" + match[1] + "." + match[2]; + if (!this.quotes.contains(fullID)) { + return this.quotes.push(fullID); + } + }; + + Post.prototype.parseFile = function(that) { + var alt, anchor, fileEl, fileInfo, size, thumb, unit; + + if (!((fileEl = $('.file', this.nodes.post)) && (thumb = $('img[data-md5]', fileEl)))) { + return; + } + alt = thumb.alt; + anchor = thumb.parentNode; + fileInfo = fileEl.firstElementChild; + this.file = { + info: fileInfo, + text: fileInfo.firstElementChild, + thumb: thumb, + URL: anchor.href, + size: alt.match(/[\d.]+\s\w+/)[0], + MD5: thumb.dataset.md5, + isSpoiler: $.hasClass(anchor, 'imgspoiler') + }; + size = +this.file.size.match(/[\d.]+/)[0]; + unit = ['B', 'KB', 'MB', 'GB'].indexOf(this.file.size.match(/\w+$/)[0]); + while (unit-- > 0) { + size *= 1024; + } + this.file.sizeInBytes = size; + this.file.thumbURL = that.isArchived ? thumb.src : "" + location.protocol + "//thumbs.4chan.org/" + this.board + "/thumb/" + (this.file.URL.match(/(\d+)\./)[1]) + "s.jpg"; + this.file.name = $('span[title]', fileInfo).title; + if (this.file.isImage = /(jpg|png|gif)$/i.test(this.file.name)) { + return this.file.dimensions = this.file.text.textContent.match(/\d+x\d+/)[0]; + } + }; + + Post.prototype.kill = function(file, now) { + var clone, quotelink, strong, _i, _j, _len, _len1, _ref, _ref1; + + now || (now = new Date()); + if (file) { + if (this.file.isDead) { + return; + } + this.file.isDead = true; + this.file.timeOfDeath = now; + $.addClass(this.nodes.root, 'deleted-file'); + } else { + if (this.isDead) { + return; + } + this.isDead = true; + this.timeOfDeath = now; + $.addClass(this.nodes.root, 'deleted-post'); + } + if (!(strong = $('strong.warning', this.nodes.info))) { + strong = $.el('strong', { + className: 'warning', + textContent: this.isReply ? '[Deleted]' : '[Dead]' + }); + $.after($('input', this.nodes.info), strong); + } + strong.textContent = file ? '[File deleted]' : '[Deleted]'; + if (this.isClone) { + return; + } + _ref = this.clones; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + clone = _ref[_i]; + clone.kill(file, now); + } + if (file) { + return; + } + _ref1 = Get.allQuotelinksLinkingTo(this); + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + quotelink = _ref1[_j]; + if (!(!$.hasClass(quotelink, 'deadlink'))) { + continue; + } + $.add(quotelink, $.tn('\u00A0(Dead)')); + $.addClass(quotelink, 'deadlink'); + } + }; + + Post.prototype.resurrect = function() { + var clone, quotelink, strong, _i, _j, _len, _len1, _ref, _ref1; + + delete this.isDead; + delete this.timeOfDeath; + $.rmClass(this.nodes.root, 'deleted-post'); + strong = $('strong.warning', this.nodes.info); + if (this.file && this.file.isDead) { + strong.textContent = '[File deleted]'; + } else { + $.rm(strong); + } + if (this.isClone) { + return; + } + _ref = this.clones; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + clone = _ref[_i]; + clone.resurrect(); + } + _ref1 = Get.allQuotelinksLinkingTo(this); + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + quotelink = _ref1[_j]; + if ($.hasClass(quotelink, 'deadlink')) { + quotelink.textContent = quotelink.textContent.replace('\u00A0(Dead)', ''); + $.rmClass(quotelink, 'deadlink'); + } + } + }; + + Post.prototype.addClone = function(context) { + return new Clone(this, context); + }; + + Post.prototype.rmClone = function(index) { + var clone, _i, _len, _ref; + + this.clones.splice(index, 1); + _ref = this.clones.slice(index); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + clone = _ref[_i]; + clone.nodes.root.dataset.clone = index++; + } + }; + + return Post; + + })(); + + Clone = (function(_super) { + __extends(Clone, _super); + + function Clone(origin, context) { + var file, info, inline, inlined, key, nodes, post, root, val, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3; + + this.origin = origin; + this.context = context; + _ref = ['ID', 'fullID', 'board', 'thread', 'info', 'quotes', 'isReply']; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + this[key] = origin[key]; + } + nodes = origin.nodes; + root = nodes.root.cloneNode(true); + post = $('.post', root); + info = $('.postInfo', post); + this.nodes = { + root: root, + post: post, + info: info, + comment: $('.postMessage', post), + quotelinks: [], + backlinks: info.getElementsByClassName('backlink') + }; + _ref1 = $$('.inline', post); + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + inline = _ref1[_j]; + $.rm(inline); + } + _ref2 = $$('.inlined', post); + for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { + inlined = _ref2[_k]; + $.rmClass(inlined, 'inlined'); + } + root.hidden = false; + $.rmClass(root, 'forwarded'); + $.rmClass(post, 'highlight'); + if (nodes.subject) { + this.nodes.subject = $('.subject', info); + } + if (nodes.name) { + this.nodes.name = $('.name', info); + } + if (nodes.email) { + this.nodes.email = $('.useremail', info); + } + if (nodes.tripcode) { + this.nodes.tripcode = $('.postertrip', info); + } + if (nodes.uniqueID) { + this.nodes.uniqueID = $('.posteruid', info); + } + if (nodes.capcode) { + this.nodes.capcode = $('.capcode', info); + } + if (nodes.flag) { + this.nodes.flag = $('.countryFlag', info); + } + if (nodes.date) { + this.nodes.date = $('.dateTime', info); + } + this.parseQuotes(); + if (origin.file) { + this.file = {}; + _ref3 = origin.file; + for (key in _ref3) { + val = _ref3[key]; + this.file[key] = val; + } + file = $('.file', post); + this.file.info = file.firstElementChild; + this.file.text = this.file.info.firstElementChild; + this.file.thumb = $('img[data-md5]', file); + this.file.fullImage = $('.full-image', file); + } + if (origin.isDead) { + this.isDead = true; + } + this.isClone = true; + root.dataset.clone = origin.clones.push(this) - 1; + } + + return Clone; + + })(Post); + + DataBoard = (function() { + DataBoard.keys = ['hiddenThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts', 'watchedThreads']; + + function DataBoard(key, sync, dontClean) { + var init, + _this = this; + + this.key = key; + this.onSync = __bind(this.onSync, this); + this.data = Conf[key]; + $.sync(key, this.onSync); + if (!dontClean) { + this.clean(); + } + if (!sync) { + return; + } + init = function() { + $.off(d, '4chanXInitFinished', init); + return _this.sync = sync; + }; + $.on(d, '4chanXInitFinished', init); + } + + DataBoard.prototype.save = function() { + return $.set(this.key, this.data); + }; + + DataBoard.prototype["delete"] = function(_arg) { + var boardID, postID, threadID; + + boardID = _arg.boardID, threadID = _arg.threadID, postID = _arg.postID; + if (postID) { + delete this.data.boards[boardID][threadID][postID]; + this.deleteIfEmpty({ + boardID: boardID, + threadID: threadID + }); + } else if (threadID) { + delete this.data.boards[boardID][threadID]; + this.deleteIfEmpty({ + boardID: boardID + }); + } else { + delete this.data.boards[boardID]; + } + return this.save(); + }; + + DataBoard.prototype.deleteIfEmpty = function(_arg) { + var boardID, threadID; + + boardID = _arg.boardID, threadID = _arg.threadID; + if (threadID) { + if (!Object.keys(this.data.boards[boardID][threadID]).length) { + delete this.data.boards[boardID][threadID]; + return this.deleteIfEmpty({ + boardID: boardID + }); + } + } else if (!Object.keys(this.data.boards[boardID]).length) { + return delete this.data.boards[boardID]; + } + }; + + DataBoard.prototype.set = function(_arg) { + var boardID, postID, threadID, val, _base, _base1, _base2; + + boardID = _arg.boardID, threadID = _arg.threadID, postID = _arg.postID, val = _arg.val; + if (postID !== void 0) { + ((_base = ((_base1 = this.data.boards)[boardID] || (_base1[boardID] = {})))[threadID] || (_base[threadID] = {}))[postID] = val; + } else if (threadID !== void 0) { + ((_base2 = this.data.boards)[boardID] || (_base2[boardID] = {}))[threadID] = val; + } else { + this.data.boards[boardID] = val; + } + return this.save(); + }; + + DataBoard.prototype.get = function(_arg) { + var ID, board, boardID, defaultValue, postID, thread, threadID, val, _i, _len; + + boardID = _arg.boardID, threadID = _arg.threadID, postID = _arg.postID, defaultValue = _arg.defaultValue; + if (board = this.data.boards[boardID]) { + if (!threadID) { + if (postID) { + for (thread = _i = 0, _len = board.length; _i < _len; thread = ++_i) { + ID = board[thread]; + if (postID in thread) { + val = thread[postID]; + break; + } + } + } else { + val = board; + } + } else if (thread = board[threadID]) { + val = postID ? thread[postID] : thread; + } + } + return val || defaultValue; + }; + + DataBoard.prototype.clean = function() { + var boardID, now, val, _ref; + + _ref = this.data.boards; + for (boardID in _ref) { + val = _ref[boardID]; + this.deleteIfEmpty({ + boardID: boardID + }); + } + now = Date.now(); + if ((this.data.lastChecked || 0) < now - 2 * $.HOUR) { + this.data.lastChecked = now; + for (boardID in this.data.boards) { + this.ajaxClean(boardID); + } + } + return this.save(); + }; + + DataBoard.prototype.ajaxClean = function(boardID) { + var _this = this; + + return $.cache("//api.4chan.org/" + boardID + "/threads.json", function(e) { + var board, page, thread, threads, _i, _j, _len, _len1, _ref, _ref1; + + if (e.target.status !== 200) { + return; + } + board = _this.data.boards[boardID]; + threads = {}; + _ref = JSON.parse(e.target.response); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + page = _ref[_i]; + _ref1 = page.threads; + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + thread = _ref1[_j]; + if (thread.no in board) { + threads[thread.no] = board[thread.no]; + } + } + } + _this.data.boards[boardID] = threads; + _this.deleteIfEmpty({ + boardID: boardID + }); + return _this.save(); + }); + }; + + DataBoard.prototype.onSync = function(data) { + this.data = data || { + boards: {} + }; + return typeof this.sync === "function" ? this.sync() : void 0; + }; + + return DataBoard; + + })(); + + Notice = (function() { + function Notice(type, content, timeout) { + this.timeout = timeout; + this.close = __bind(this.close, this); + this.add = __bind(this.add, this); + this.el = $.el('div', { + innerHTML: '×
' + }); + this.el.style.opacity = 0; + this.setType(type); + $.on(this.el.firstElementChild, 'click', this.close); + if (typeof content === 'string') { + content = $.tn(content); + } + $.add(this.el.lastElementChild, content); + $.ready(this.add); + } + + Notice.prototype.setType = function(type) { + return this.el.className = "notification " + type; + }; + + Notice.prototype.add = function() { + if (d.hidden) { + $.on(d, 'visibilitychange', this.add); + return; + } + $.off(d, 'visibilitychange', this.add); + $.add($.id('notifications'), this.el); + this.el.clientHeight; + this.el.style.opacity = 1; + if (this.timeout) { + return setTimeout(this.close, this.timeout * $.SECOND); + } + }; + + Notice.prototype.close = function() { + $.off(d, 'visibilitychange', this.add); + return $.rm(this.el); + }; + + return Notice; + + })(); + + Polyfill = { + init: function() {}, + notificationPermission: function() { + if (!window.Notification || 'permission' in Notification) { + return; + } + return Object.defineProperty(Notification, 'permission', { + get: function() { + switch (webkitNotifications.checkPermission()) { + case 0: + return 'granted'; + case 1: + return 'default'; + case 2: + return 'denied'; + } + } + }); + }, + toBlob: function() { + var _base; + + return (_base = HTMLCanvasElement.prototype).toBlob || (_base.toBlob = function(cb) { + var data, i, l, ui8a, _i; + + data = atob(this.toDataURL().slice(22)); + l = data.length; + ui8a = new Uint8Array(l); + for (i = _i = 0; _i < l; i = _i += 1) { + ui8a[i] = data.charCodeAt(i); + } + return cb(new Blob([ui8a], { + type: 'image/png' + })); + }); + }, + visibility: function() { + if (!('webkitHidden' in document)) { + return; + } + Object.defineProperties(HTMLDocument.prototype, { + visibilityState: { + get: function() { + return this.webkitVisibilityState; + } + }, + hidden: { + get: function() { + return this.webkitHidden; + } + } + }); + return $.on(d, 'webkitvisibilitychange', function() { + return $.event('visibilitychange'); + }); + } + }; + + Header = { + init: function() { + var barFixedToggler, barPositionToggler, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, menuButton, shortcutToggler, + _this = this; + + this.menu = new UI.Menu('header'); + menuButton = $.el('span', { + className: 'menu-button', + innerHTML: '' + }); + barFixedToggler = $.el('label', { + innerHTML: ' Fixed Header' + }); + headerToggler = $.el('label', { + innerHTML: ' Auto-hide header' + }); + barPositionToggler = $.el('label', { + innerHTML: ' Bottom header' + }); + linkJustifyToggler = $.el('label', { + innerHTML: " Centered links" + }); + customNavToggler = $.el('label', { + innerHTML: ' Custom board navigation' + }); + footerToggler = $.el('label', { + innerHTML: " Hide bottom board list" + }); + shortcutToggler = $.el('label', { + innerHTML: " Shortcut Icons" + }); + editCustomNav = $.el('a', { + textContent: 'Edit custom board navigation', + href: 'javascript:;' + }); + this.barFixedToggler = barFixedToggler.firstElementChild; + this.barPositionToggler = barPositionToggler.firstElementChild; + this.linkJustifyToggler = linkJustifyToggler.firstElementChild; + this.headerToggler = headerToggler.firstElementChild; + this.footerToggler = footerToggler.firstElementChild; + this.shortcutToggler = shortcutToggler.firstElementChild; + this.customNavToggler = customNavToggler.firstElementChild; + $.on(menuButton, 'click', this.menuToggle); + $.on(this.barFixedToggler, 'change', this.toggleBarFixed); + $.on(this.barPositionToggler, 'change', this.toggleBarPosition); + $.on(this.linkJustifyToggler, 'change', this.toggleLinkJustify); + $.on(this.headerToggler, 'change', this.toggleBarVisibility); + $.on(this.footerToggler, 'change', this.toggleFooterVisibility); + $.on(this.shortcutToggler, 'change', this.toggleShortcutIcons); + $.on(this.customNavToggler, 'change', this.toggleCustomNav); + $.on(editCustomNav, 'click', this.editCustomNav); + this.setBarFixed(Conf['Fixed Header']); + this.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); + this.addShortcut(menuButton); + $.event('AddMenuEntry', { + type: 'header', + el: $.el('span', { + textContent: 'Header' + }), + order: 107, + subEntries: [ + { + el: barFixedToggler + }, { + el: headerToggler + }, { + el: barPositionToggler + }, { + el: linkJustifyToggler + }, { + el: footerToggler + }, { + el: shortcutToggler + }, { + el: customNavToggler + }, { + el: editCustomNav + } + ] + }); + $.on(window, 'load hashchange', Header.hashScroll); + $.on(d, 'CreateNotification', this.createNotification); + $.asap((function() { + return d.body; + }), function() { + if (!Main.isThisPageLegit()) { + return; + } + $.asap((function() { + return $.id('boardNavMobile') || d.readyState !== 'loading'; + }), Header.setBoardList); + $.prepend(d.body, _this.bar); + $.add(d.body, Header.hover); + _this.setBarPosition(Conf['Bottom Header']); + return _this; + }); + $.ready(function() { + var a, cs; + + _this.footer = $.id('boardNavDesktopFoot'); + if (a = $("a[href*='/" + g.BOARD + "/']", $.id('boardNavDesktopFoot'))) { + a.className = 'current'; + } + cs = $.el('a', { + id: 'settingsWindowLink', + href: 'javascript:;', + textContent: 'Catalog Settings' + }); + if (g.VIEW === 'catalog') { + _this.addShortcut(cs); + } + Header.setFooterVisibility(Conf['Bottom Board List']); + return $.sync('Bottom Board List', Header.setFooterVisibility); + }); + return this.enableDesktopNotifications(); + }, + bar: $.el('div', { + id: 'header-bar' + }), + notify: $.el('div', { + id: 'notifications' + }), + shortcuts: $.el('span', { + id: 'shortcuts' + }), + hover: $.el('div', { + id: 'hoverUI' + }), + toggle: $.el('div', { + id: 'scroll-marker' + }), + 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: "" + }); + 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]); + Header.setCustomNav(Conf['Custom Board Navigation']); + Header.generateBoardList(Conf['boardnav'].replace(/(\r\n|\n|\r)/g, ' ')); + $.sync('Custom Board Navigation', Header.setCustomNav); + return $.sync('boardnav', Header.generateBoardList); + }, + generateBoardList: function(text) { + var as, list, nodes; + + list = $('#custom-board-list', Header.bar); + $.rmAll(list); + if (!text) { + return; + } + 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; + + if (/^[^\w@]/.test(t)) { + return $.tn(t); + } + if (/^toggle-all/.test(t)) { + a = $.el('a', { + className: 'show-board-list-button', + textContent: (t.match(/-text:"(.+)"/) || [null, '+'])[1], + href: 'javascript:;' + }); + $.on(a, 'click', Header.toggleBoardList); + return a; + } + if (/^external/.test(t)) { + a = $.el('a', { + href: (t.match(/\,"(.+)"/) || [null, '+'])[1], + textContent: (t.match(/-text:"(.+)"\,/) || [null, '+'])[1], + className: 'external' + }); + return a; + } + board = /^current/.test(t) ? g.BOARD.ID : t.match(/^[^-]+/)[0]; + for (_i = 0, _len = as.length; _i < _len; _i++) { + a = as[_i]; + if (a.textContent === board) { + a = a.cloneNode(true); + a.textContent = /-title/.test(t) || /-replace/.test(t) && $.hasClass(a, 'current') ? a.title : /-full/.test(t) ? "/" + board + "/ - " + a.title : (m = t.match(/-text:"(.+)"/)) ? m[1] : a.textContent; + if (m = t.match(/-(index|catalog)/)) { + a.dataset.only = m[1]; + a.href = "//boards.4chan.org/" + board + "/"; + if (m[1] === 'catalog') { + if (Conf['External Catalog']) { + a.href = CatalogLinks.external(board); + } else { + a.href += 'catalog'; + } + $.addClass(a, 'catalog'); + } + } + if (board === '@') { + $.addClass(a, 'navSmall'); + } + return a; + } + } + return $.tn(t); + }); + return $.add(list, nodes); + }, + toggleBoardList: function() { + var bar, custom, full, showBoardList; + + bar = Header.bar; + custom = $('#custom-board-list', bar); + full = $('#full-board-list', bar); + showBoardList = !full.hidden; + custom.hidden = !showBoardList; + return full.hidden = showBoardList; + }, + 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) { + return $.addClass(doc, 'centered-links'); + } else { + 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'); + centered = this.nodeName === 'INPUT' ? this.checked : void 0; + Header.setLinkJustify(centered); + return $.set('Centered links', centered); + }, + setBarFixed: function(fixed) { + Header.barFixedToggler.checked = fixed; + if (fixed) { + $.addClass(doc, 'fixed'); + return $.addClass(Header.bar, 'dialog'); + } else { + $.rmClass(doc, 'fixed'); + return $.rmClass(Header.bar, 'dialog'); + } + }, + toggleBarFixed: function() { + $.event('CloseMenu'); + Header.setBarFixed(this.checked); + Conf['Fixed Header'] = this.checked; + return $.set('Fixed Header', this.checked); + }, + setShortcutIcons: function(show) { + Header.shortcutToggler.checked = show; + if (show) { + return $.addClass(doc, 'shortcut-icons'); + } else { + return $.rmClass(doc, 'shortcut-icons'); + } + }, + toggleShortcutIcons: function() { + $.event('CloseMenu'); + Header.setShortcutIcons(this.checked); + Conf['Shortcut Icons'] = this.checked; + return $.set('Shortcut Icons', this.checked); + }, + setBarVisibility: function(hide) { + Header.headerToggler.checked = hide; + $.event('CloseMenu'); + (hide ? $.addClass : $.rmClass)(Header.bar, 'autohide'); + return (hide ? $.addClass : $.rmClass)(doc, 'autohide'); + }, + toggleBarVisibility: function() { + var hide, message; + + hide = this.nodeName === 'INPUT' ? this.checked : !$.hasClass(Header.bar, 'autohide'); + this.checked = hide; + $.set('Header auto-hide', Conf['Header auto-hide'] = hide); + Header.setBarVisibility(hide); + message = "The header bar will " + (hide ? 'automatically hide itself.' : 'remain visible.'); + return new Notice('info', message, 2); + }, + setFooterVisibility: function(hide) { + Header.footerToggler.checked = hide; + return Header.footer.hidden = hide; + }, + toggleFooterVisibility: function() { + var hide, message; + + $.event('CloseMenu'); + hide = this.nodeName === 'INPUT' ? this.checked : !!Header.footer.hidden; + Header.setFooterVisibility(hide); + $.set('Bottom Board List', hide); + message = hide ? 'The bottom navigation will now be hidden.' : 'The bottom navigation will remain visible.'; + return new Notice('info', message, 2); + }, + setCustomNav: function(show) { + var btn, cust, full, _ref; + + Header.customNavToggler.checked = show; + cust = $('#custom-board-list', Header.bar); + full = $('#full-board-list', Header.bar); + btn = $('.hide-board-list-button', full); + return _ref = show ? [false, true] : [true, false], cust.hidden = _ref[0], full.hidden = _ref[1], _ref; + }, + toggleCustomNav: function() { + $.cb.checked.call(this); + return Header.setCustomNav(this.checked); + }, + editCustomNav: function() { + var settings; + + Settings.open('Advanced'); + settings = $.id('fourchanx-settings'); + return $('input[name=boardnav]', settings).focus(); + }, + hashScroll: function() { + var hash, post; + + if (!((hash = this.location.hash.slice(1)) && (post = $.id(hash)))) { + return; + } + if ((Get.postFromRoot(post)).isHidden) { + return; + } + return Header.scrollToPost(post); + }, + scrollToPost: function(post) { + var headRect, top; + + top = post.getBoundingClientRect().top; + if (Conf['Fixed Header'] && !Conf['Bottom Header']) { + headRect = Header.bar.getBoundingClientRect(); + top -= headRect.top + headRect.height; + } + return window.scrollBy(0, top); + }, + addShortcut: function(el) { + var shortcut; + + shortcut = $.el('span', { + className: 'shortcut brackets-wrap' + }); + $.add(shortcut, el); + return $.prepend(Header.shortcuts, shortcut); + }, + menuToggle: function(e) { + return Header.menu.toggle(e, this, g); + }, + createNotification: function(e) { + var cb, content, lifetime, notif, type, _ref; + + _ref = e.detail, type = _ref.type, content = _ref.content, lifetime = _ref.lifetime, cb = _ref.cb; + notif = new Notice(type, content, lifetime); + if (cb) { + return cb(notif); + } + }, + areNotificationsEnabled: false, + enableDesktopNotifications: function() { + var authorize, disable, el, notice, _ref; + + if (!(window.Notification && Conf['Desktop Notifications'])) { + return; + } + switch (Notification.permission) { + case 'granted': + Header.areNotificationsEnabled = true; + return; + case 'denied': + return; + } + el = $.el('span', { + innerHTML: "Desktop notification permissions are not granted.\n[FAQ]
\n or " + }); + _ref = $$('button', el), authorize = _ref[0], disable = _ref[1]; + $.on(authorize, 'click', function() { + return Notification.requestPermission(function(status) { + Header.areNotificationsEnabled = status === 'granted'; + if (status === 'default') { + return; + } + return notice.close(); + }); + }); + $.on(disable, 'click', function() { + $.set('Desktop Notifications', false); + return notice.close(); + }); + return notice = new Notice('info', el); + } + }; + + Build = { + spoilerRange: {}, + shortFilename: function(filename, isReply) { + var threshold; + + threshold = isReply ? 30 : 40; + if (filename.length - 4 > threshold) { + return "" + filename.slice(0, threshold - 5) + "(...)." + filename.slice(-3); + } else { + return filename; + } + }, + postFromObject: function(data, boardID) { + var o; + + o = { + postID: data.no, + threadID: data.resto || data.no, + boardID: boardID, + name: data.name, + capcode: data.capcode, + tripcode: data.trip, + uniqueID: data.id, + email: data.email ? encodeURI(data.email.replace(/"/g, '"')) : '', + subject: data.sub, + flagCode: data.country, + flagName: data.country_name, + date: data.now, + dateUTC: data.time, + comment: data.com, + isSticky: !!data.sticky, + isClosed: !!data.closed + }; + if (data.ext || data.filedeleted) { + o.file = { + name: data.filename + data.ext, + timestamp: "" + data.tim + data.ext, + url: boardID === 'f' ? "//images.4channel.org/" + boardID + "/src/" + data.filename + data.ext : "//images.4chan.org/" + boardID + "/src/" + data.tim + data.ext, + height: data.h, + width: data.w, + MD5: data.md5, + size: data.fsize, + turl: "//thumbs.4chan.org/" + boardID + "/thumb/" + data.tim + "s.jpg", + theight: data.tn_h, + twidth: data.tn_w, + isSpoiler: !!data.spoiler, + isDeleted: !!data.filedeleted + }; + } + return Build.post(o); + }, + post: function(o, isArchived) { + /* + This function contains code from 4chan-JS (https://github.com/4chan/4chan-JS). + @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE + */ + + var a, boardID, capcode, capcodeClass, 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; + + 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 = '//static.4chan.org/image/'; + if (email) { + emailStart = ''; + emailEnd = ''; + } else { + emailStart = ''; + emailEnd = ''; + } + subject = " " + (subject || '') + " "; + userID = !capcode && uniqueID ? (" (ID: ") + ("" + uniqueID + ") ") : ''; + switch (capcode) { + case 'admin': + case 'admin_highlight': + capcodeClass = " capcodeAdmin"; + capcodeStart = " ## Admin"; + capcode = (" "; + break; + case 'mod': + capcodeClass = " capcodeMod"; + capcodeStart = " ## Mod"; + capcode = (" "; + break; + case 'developer': + capcodeClass = " capcodeDeveloper"; + capcodeStart = " ## Developer"; + capcode = (" "; + break; + default: + capcodeClass = ''; + capcodeStart = ''; + capcode = ''; + } + flag = !flagCode ? '' : boardID === 'pol' ? "  + flagCode + " : " "; + if (file != null ? file.isDeleted : void 0) { + 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) { + fileSize = "Spoiler Image, " + fileSize; + if (!isArchived) { + fileThumb = "" + staticPath + "spoiler"; + if (spoilerRange = Build.spoilerRange[boardID]) { + fileThumb += ("-" + boardID) + Math.floor(1 + spoilerRange * Math.random()); + } + fileThumb += '.png'; + file.twidth = file.theight = 100; + } + } + imgSrc = boardID === 'f' ? '' : ("") + ("" + fileSize + "") + ""; + a = $.el('a', { + innerHTML: file.name + }); + filename = a.textContent.replace(/%22/g, '"'); + a.textContent = Build.shortFilename(filename); + shortFilename = a.innerHTML; + a.textContent = filename; + filename = a.innerHTML.replace(/'/g, '''); + fileDims = ext === '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" : ''; + container = $.el('div', { + id: "pc" + postID, + className: "postContainer " + (isOP ? 'op' : 'reply') + "Container", + innerHTML: "" + (isOP ? '' : "
>>
") + "
" + (isOP ? fileHTML : '') + "" + (isOP ? '' : fileHTML) + "
" + (comment || '') + "
" + " " + "
" + }); + _ref = $$('.quotelink', container); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + quote = _ref[_i]; + href = quote.getAttribute('href'); + if (href[0] === '/') { + continue; + } + quote.href = "/" + boardID + "/res/" + href; + } + return container; + } + }; + + Get = { + threadExcerpt: function(thread) { + var OP, excerpt, _ref; + + OP = thread.OP; + excerpt = ((_ref = OP.info.subject) != null ? _ref.trim() : void 0) || OP.info.comment.replace(/\n+/g, ' // ') || Conf['Anonymize'] && 'Anonymous' || $('.nameBlock', OP.nodes.info).textContent.trim(); + if (excerpt.length > 70) { + excerpt = "" + excerpt.slice(0, 67) + "..."; + } + return "/" + thread.board + "/ - " + excerpt; + }, + threadFromRoot: function(root) { + return g.threads["" + g.BOARD + "." + root.id.slice(1)]; + }, + threadFromNode: function(node) { + return Get.threadFromRoot($.x('ancestor::div[@class="thread"]', node)); + }, + postFromRoot: function(root) { + var boardID, index, link, post, postID; + + link = $('a[title="Highlight this post"]', root); + boardID = link.pathname.split('/')[1]; + postID = link.hash.slice(2); + index = root.dataset.clone; + post = g.posts["" + boardID + "." + postID]; + if (index) { + return post.clones[index]; + } else { + return post; + } + }, + postFromNode: function(root) { + return Get.postFromRoot($.x('(ancestor::div[contains(@class,"postContainer")][1]|following::div[contains(@class,"postContainer")][1])', root)); + }, + contextFromNode: function(node) { + return Get.postFromRoot($.x('ancestor::div[parent::div[@class="thread"]][1]', node)); + }, + postDataFromLink: function(link) { + var boardID, path, postID, threadID, _ref; + + if (link.hostname === 'boards.4chan.org') { + path = link.pathname.split('/'); + boardID = path[1]; + threadID = path[3]; + postID = link.hash.slice(2); + } else { + _ref = link.dataset, boardID = _ref.boardID, threadID = _ref.threadID, postID = _ref.postID; + threadID || (threadID = 0); + } + return { + boardID: boardID, + threadID: +threadID, + postID: +postID + }; + }, + allQuotelinksLinkingTo: function(post) { + var ID, quote, quotedPost, quotelinks, quoterPost, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3; + + 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]; + 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]; + if (!(quotedPost = g.posts[quote])) { + continue; + } + _ref3 = [quotedPost].concat(quotedPost.clones); + for (_k = 0, _len2 = _ref3.length; _k < _len2; _k++) { + quotedPost = _ref3[_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; + return boardID === post.board.ID && postID === post.ID; + }); + }, + postClone: function(boardID, threadID, postID, root, context) { + var post, url; + + if (post = g.posts["" + boardID + "." + postID]) { + Get.insert(post, root, context); + return; + } + root.textContent = "Loading post No." + postID + "..."; + if (threadID) { + return $.cache("//api.4chan.org/" + boardID + "/res/" + threadID + ".json", function() { + return Get.fetchedPost(this, boardID, threadID, postID, root, context); + }); + } else if (url = Redirect.to('post', { + boardID: boardID, + postID: postID + })) { + return $.cache(url, function() { + return Get.archivedPost(this, boardID, postID, root, context); + }, { + withCredentials: url.archive.withCredentials + }); + } + }, + insert: function(post, root, context) { + var clone, nodes; + + if (!root.parentNode) { + return; + } + clone = post.addClone(context); + Main.callbackNodes(Clone, [clone]); + nodes = clone.nodes; + $.rmAll(nodes.root); + $.add(nodes.root, nodes.post); + $.rmAll(root); + return $.add(root, nodes.root); + }, + fetchedPost: function(req, boardID, threadID, postID, root, context) { + var board, post, posts, status, thread, url, _i, _len; + + if (post = g.posts["" + boardID + "." + postID]) { + Get.insert(post, root, context); + return; + } + status = req.status; + if (![200, 304].contains(status)) { + if (url = Redirect.to('post', { + boardID: boardID, + postID: postID + })) { + $.cache(url, function() { + return Get.archivedPost(this, boardID, postID, root, context); + }, { + withCredentials: url.archive.withCredentials + }); + } else { + $.addClass(root, 'warning'); + root.textContent = status === 404 ? "Thread No." + threadID + " 404'd." : "Error " + req.statusText + " (" + req.status + ")."; + } + return; + } + posts = JSON.parse(req.response).posts; + Build.spoilerRange[boardID] = posts[0].custom_spoiler; + for (_i = 0, _len = posts.length; _i < _len; _i++) { + post = posts[_i]; + if (post.no === postID) { + break; + } + } + if (post.no !== postID) { + if (url = Redirect.to('post', { + boardID: boardID, + postID: postID + })) { + $.cache(url, function() { + return Get.archivedPost(this, boardID, postID, root, context); + }, { + withCredentials: url.archive.withCredentials + }); + } else { + $.addClass(root, 'warning'); + root.textContent = "Post No." + postID + " was not found."; + } + return; + } + board = g.boards[boardID] || new Board(boardID); + thread = g.threads["" + boardID + "." + threadID] || new Thread(threadID, board); + post = new Post(Build.postFromObject(post, boardID), thread, board); + Main.callbackNodes(Post, [post]); + return Get.insert(post, root, context); + }, + archivedPost: function(req, boardID, postID, root, context) { + var board, bq, comment, data, o, post, thread, threadID, _ref; + + if (post = g.posts["" + boardID + "." + postID]) { + Get.insert(post, root, context); + return; + } + data = JSON.parse(req.response); + if (data.error) { + $.addClass(root, 'warning'); + root.textContent = data.error; + return; + } + bq = $.el('blockquote', { + textContent: data.comment + }); + bq.innerHTML = bq.innerHTML.replace(/\n|\[\/?[a-z]+(:lit)?\]/g, Get.parseMarkup); + comment = bq.innerHTML.replace(/(^|>)(>[^<$]*)(<|$)/g, '$1$2$3').replace(/((>){2}(>\/[a-z\d]+\/)?\d+)/g, '$1'); + threadID = +data.thread_num; + o = { + postID: "" + postID, + threadID: "" + threadID, + boardID: boardID, + name: data.name_processed, + capcode: (function() { + switch (data.capcode) { + case 'M': + return 'mod'; + case 'A': + return 'admin'; + case 'D': + return 'developer'; + } + })(), + tripcode: data.trip, + uniqueID: data.poster_hash, + email: data.email ? encodeURI(data.email) : '', + subject: data.title_processed, + flagCode: data.poster_country, + flagName: data.poster_country_name_processed, + date: data.fourchan_date, + dateUTC: data.timestamp, + comment: comment + }; + if ((_ref = data.media) != null ? _ref.media_filename : void 0) { + o.file = { + name: data.media.media_filename_processed, + timestamp: data.media.media_orig, + url: data.media.media_link || data.media.remote_media_link, + height: data.media.media_h, + width: data.media.media_w, + MD5: data.media.media_hash, + size: data.media.media_size, + turl: data.media.thumb_link || ("//thumbs.4chan.org/" + boardID + "/thumb/" + data.media.preview_orig), + theight: data.media.preview_h, + twidth: data.media.preview_w, + isSpoiler: data.media.spoiler === '1' + }; + } + board = g.boards[boardID] || new Board(boardID); + thread = g.threads["" + boardID + "." + threadID] || new Thread(threadID, board); + post = new Post(Build.post(o, true), thread, board, { + isArchived: true + }); + Main.callbackNodes(Post, [post]); + return Get.insert(post, root, context); + }, + parseMarkup: function(text) { + switch (text) { + case '\n': + return '
'; + case '[b]': + return ''; + case '[/b]': + return ''; + case '[spoiler]': + return ''; + case '[/spoiler]': + return ''; + case '[code]': + return '
';
+        case '[/code]':
+          return '
'; + case '[moot]': + return '
'; + case '[/moot]': + return '
'; + case '[banned]': + return ''; + case '[/banned]': + return ''; + default: + return text.replace(':lit', ''); + } + } + }; + + UI = (function() { + var Menu, dialog, drag, dragend, dragstart, hover, hoverend, hoverstart, touchend, touchmove; + + dialog = function(id, position, html) { + var child, el, move, _i, _len, _ref; + + el = $.el('div', { + className: 'dialog', + innerHTML: html, + id: id + }); + el.style.cssText = position; + $.get("" + id + ".position", position, function(item) { + return el.style.cssText = item["" + id + ".position"]; + }); + move = $('.move', el); + $.on(move, 'touchstart mousedown', dragstart); + _ref = move.children; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + child = _ref[_i]; + if (!child.tagName) { + continue; + } + $.on(child, 'touchstart mousedown', function(e) { + return e.stopPropagation(); + }); + } + return el; + }; + Menu = (function() { + var currentMenu, lastToggledButton; + + currentMenu = null; + + lastToggledButton = null; + + function Menu(type) { + this.type = type; + this.addEntry = __bind(this.addEntry, this); + this.keybinds = __bind(this.keybinds, this); + this.close = __bind(this.close, this); + $.on(d, 'AddMenuEntry', this.addEntry); + this.entries = []; + } + + Menu.prototype.makeMenu = function() { + var menu; + + menu = $.el('div', { + className: 'dialog', + id: 'menu', + tabIndex: 0 + }); + $.on(menu, 'click', function(e) { + return e.stopPropagation(); + }); + $.on(menu, 'keydown', this.keybinds); + return menu; + }; + + Menu.prototype.toggle = function(e, button, data) { + var previousButton; + + e.preventDefault(); + e.stopPropagation(); + if (currentMenu) { + previousButton = lastToggledButton; + this.close(); + if (previousButton === button) { + return; + } + } + if (!this.entries.length) { + return; + } + return this.open(button, data); + }; + + Menu.prototype.open = function(button, data) { + var bLeft, bRect, bTop, bottom, cHeight, cWidth, entry, left, mRect, menu, right, style, top, _i, _len, _ref, _ref1, _ref2; + + menu = this.makeMenu(); + currentMenu = menu; + lastToggledButton = button; + this.entries.sort(function(first, second) { + return first.order - second.order; + }); + _ref = this.entries; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + entry = _ref[_i]; + this.insertEntry(entry, menu, data); + } + $.addClass(lastToggledButton, 'active'); + $.on(d, 'click', this.close); + $.on(d, 'CloseMenu', this.close); + $.add(Header.hover, menu); + mRect = menu.getBoundingClientRect(); + bRect = button.getBoundingClientRect(); + bTop = window.scrollY + bRect.top; + bLeft = window.scrollX + bRect.left; + cHeight = doc.clientHeight; + cWidth = doc.clientWidth; + _ref1 = bRect.top + bRect.height + mRect.height < cHeight ? [bRect.bottom, null] : [null, cHeight - bRect.top], top = _ref1[0], bottom = _ref1[1]; + _ref2 = bRect.left + mRect.width < cWidth ? [bRect.left, null] : [null, cWidth - bRect.right], left = _ref2[0], right = _ref2[1]; + style = menu.style; + style.top = "" + top + "px"; + style.right = "" + right + "px"; + style.bottom = "" + bottom + "px"; + style.left = "" + left + "px"; + if (right) { + $.addClass(menu, 'left'); + } + entry = $('.entry', menu); + this.focus(entry); + return menu.focus(); + }; + + Menu.prototype.insertEntry = function(entry, parent, data) { + var subEntry, submenu, _i, _len, _ref; + + if (typeof entry.open === 'function') { + if (!entry.open(data)) { + return; + } + } + $.add(parent, entry.el); + if (!entry.subEntries) { + return; + } + if (submenu = $('.submenu', entry.el)) { + $.rm(submenu); + } + submenu = $.el('div', { + className: 'dialog submenu' + }); + _ref = entry.subEntries; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + subEntry = _ref[_i]; + this.insertEntry(subEntry, submenu, data); + } + $.add(entry.el, submenu); + }; + + Menu.prototype.close = function() { + $.rm(currentMenu); + $.rmClass(lastToggledButton, 'active'); + currentMenu = null; + lastToggledButton = null; + return $.off(d, 'click CloseMenu', this.close); + }; + + Menu.prototype.findNextEntry = function(entry, direction) { + var entries; + + entries = __slice.call(entry.parentNode.children); + entries.sort(function(first, second) { + return first.style.order - second.style.order; + }); + return entries[entries.indexOf(entry) + direction]; + }; + + Menu.prototype.keybinds = function(e) { + var entry, next, nextPrev, subEntry, submenu; + + entry = $('.focused', currentMenu); + while (subEntry = $('.focused', entry)) { + entry = subEntry; + } + switch (e.keyCode) { + case 27: + lastToggledButton.focus(); + this.close(); + break; + case 13: + case 32: + entry.click(); + break; + case 38: + if (next = this.findNextEntry(entry, -1)) { + this.focus(next); + } + break; + case 40: + if (next = this.findNextEntry(entry, +1)) { + this.focus(next); + } + break; + case 39: + if ((submenu = $('.submenu', entry)) && (next = submenu.firstElementChild)) { + while (nextPrev = this.findNextEntry(next, -1)) { + next = nextPrev; + } + this.focus(next); + } + break; + case 37: + if (next = $.x('parent::*[contains(@class,"submenu")]/parent::*', entry)) { + this.focus(next); + } + break; + default: + return; + } + e.preventDefault(); + return e.stopPropagation(); + }; + + Menu.prototype.focus = function(entry) { + var bottom, cHeight, cWidth, eRect, focused, left, right, sRect, style, submenu, top, _i, _len, _ref, _ref1, _ref2; + + while (focused = $.x('parent::*/child::*[contains(@class,"focused")]', entry)) { + $.rmClass(focused, 'focused'); + } + _ref = $$('.focused', entry); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + focused = _ref[_i]; + $.rmClass(focused, 'focused'); + } + $.addClass(entry, 'focused'); + if (!(submenu = $('.submenu', entry))) { + return; + } + sRect = submenu.getBoundingClientRect(); + eRect = entry.getBoundingClientRect(); + cHeight = doc.clientHeight; + cWidth = doc.clientWidth; + _ref1 = eRect.top + sRect.height < cHeight ? ['0px', 'auto'] : ['auto', '0px'], top = _ref1[0], bottom = _ref1[1]; + _ref2 = eRect.right + sRect.width < cWidth ? ['100%', 'auto'] : ['auto', '100%'], left = _ref2[0], right = _ref2[1]; + style = submenu.style; + style.top = top; + style.bottom = bottom; + style.left = left; + return style.right = right; + }; + + Menu.prototype.addEntry = function(e) { + var entry; + + entry = e.detail; + if (entry.type !== this.type) { + return; + } + this.parseEntry(entry); + return this.entries.push(entry); + }; + + Menu.prototype.parseEntry = function(entry) { + var el, subEntries, subEntry, _i, _len; + + el = entry.el, subEntries = entry.subEntries; + $.addClass(el, 'entry'); + $.on(el, 'focus mouseover', (function(e) { + e.stopPropagation(); + return this.focus(el); + }).bind(this)); + el.style.order = entry.order || 100; + if (!subEntries) { + return; + } + $.addClass(el, 'has-submenu'); + for (_i = 0, _len = subEntries.length; _i < _len; _i++) { + subEntry = subEntries[_i]; + this.parseEntry(subEntry); + } + }; + + return Menu; + + })(); + dragstart = function(e) { + var el, isTouching, o, rect, screenHeight, screenWidth, _ref; + + if (e.type === 'mousedown' && e.button !== 0) { + return; + } + e.preventDefault(); + if (isTouching = e.type === 'touchstart') { + e = e.changedTouches[e.changedTouches.length - 1]; + } + el = $.x('ancestor::div[contains(@class,"dialog")][1]', this); + rect = el.getBoundingClientRect(); + screenHeight = doc.clientHeight; + screenWidth = doc.clientWidth; + o = { + id: el.id, + style: el.style, + dx: e.clientX - rect.left, + dy: e.clientY - rect.top, + height: screenHeight - rect.height, + width: screenWidth - rect.width, + screenHeight: screenHeight, + screenWidth: screenWidth, + isTouching: isTouching + }; + _ref = Conf['Header auto-hide'] || !Conf['Fixed Header'] ? [0, 0] : Conf['Bottom Header'] ? [0, Header.bar.getBoundingClientRect().height] : [Header.bar.getBoundingClientRect().height, 0], o.topBorder = _ref[0], o.bottomBorder = _ref[1]; + if (isTouching) { + o.identifier = e.identifier; + o.move = touchmove.bind(o); + o.up = touchend.bind(o); + $.on(d, 'touchmove', o.move); + return $.on(d, 'touchend touchcancel', o.up); + } else { + o.move = drag.bind(o); + o.up = dragend.bind(o); + $.on(d, 'mousemove', o.move); + return $.on(d, 'mouseup', o.up); + } + }; + touchmove = function(e) { + var touch, _i, _len, _ref; + + _ref = e.changedTouches; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + touch = _ref[_i]; + if (touch.identifier === this.identifier) { + drag.call(this, touch); + return; + } + } + }; + drag = function(e) { + var bottom, clientX, clientY, left, right, style, top; + + clientX = e.clientX, clientY = e.clientY; + left = clientX - this.dx; + left = left < 10 ? 0 : this.width - left < 10 ? null : left / this.screenWidth * 100 + '%'; + top = clientY - this.dy; + top = top < (10 + this.topBorder) ? this.topBorder + 'px' : this.height - top < (10 + this.bottomBorder) ? null : top / this.screenHeight * 100 + '%'; + right = left === null ? 0 : null; + bottom = top === null ? this.bottomBorder + 'px' : null; + style = this.style; + style.left = left; + style.right = right; + style.top = top; + return style.bottom = bottom; + }; + touchend = function(e) { + var touch, _i, _len, _ref; + + _ref = e.changedTouches; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + touch = _ref[_i]; + if (touch.identifier === this.identifier) { + dragend.call(this); + return; + } + } + }; + dragend = function() { + if (this.isTouching) { + $.off(d, 'touchmove', this.move); + $.off(d, 'touchend touchcancel', this.up); + } else { + $.off(d, 'mousemove', this.move); + $.off(d, 'mouseup', this.up); + } + return $.set("" + this.id + ".position", this.style.cssText); + }; + hoverstart = function(_arg) { + var asapTest, cb, el, endEvents, latestEvent, o, root; + + root = _arg.root, el = _arg.el, latestEvent = _arg.latestEvent, endEvents = _arg.endEvents, asapTest = _arg.asapTest, cb = _arg.cb; + o = { + root: root, + el: el, + style: el.style, + cb: cb, + endEvents: endEvents, + latestEvent: latestEvent, + clientHeight: doc.clientHeight, + clientWidth: doc.clientWidth + }; + o.hover = hover.bind(o); + o.hoverend = hoverend.bind(o); + $.asap(function() { + return !el.parentNode || asapTest(); + }, function() { + if (el.parentNode) { + return o.hover(o.latestEvent); + } + }); + $.on(root, endEvents, o.hoverend); + if ($.x('ancestor::div[contains(@class,"inline")][1]', root)) { + $.on(d, 'keydown', o.hoverend); + } + $.on(root, 'mousemove', o.hover); + o.workaround = function(e) { + if (!root.contains(e.target)) { + return o.hoverend(); + } + }; + return $.on(doc, 'mousemove', o.workaround); + }; + hover = function(e) { + var clientX, clientY, height, left, right, style, top, _ref; + + this.latestEvent = e; + height = this.el.offsetHeight; + clientX = e.clientX, clientY = e.clientY; + top = clientY - 120; + top = this.clientHeight <= height || top <= 0 ? 0 : top + height >= this.clientHeight ? this.clientHeight - height : top; + _ref = clientX <= this.clientWidth - 400 ? [clientX + 45 + 'px', null] : [null, this.clientWidth - clientX + 45 + 'px'], left = _ref[0], right = _ref[1]; + style = this.style; + style.top = top + 'px'; + style.left = left; + return style.right = right; + }; + hoverend = function(e) { + if (e.type === 'keydown' && e.keyCode !== 13 || e.target.nodeName === "TEXTAREA") { + return; + } + $.rm(this.el); + $.off(this.root, this.endEvents, this.hoverend); + $.off(d, 'keydown', this.hoverend); + $.off(this.root, 'mousemove', this.hover); + $.off(doc, 'mousemove', this.workaround); + if (this.cb) { + return this.cb.call(this); + } + }; + return { + dialog: dialog, + Menu: Menu, + hover: hoverstart + }; + })(); + + Anonymize = { + init: function() { + if (g.VIEW === 'catalog' || !Conf['Anonymize']) { + return; + } + return Post.callbacks.push({ + name: 'Anonymize', + cb: this.node + }); + }, + node: function() { + var email, name, tripcode, _ref; + + if (this.info.capcode || this.isClone) { + return; + } + _ref = this.nodes, name = _ref.name, tripcode = _ref.tripcode, email = _ref.email; + if (this.info.name !== 'Anonymous') { + name.textContent = 'Anonymous'; + } + if (tripcode) { + $.rm(tripcode); + delete this.nodes.tripcode; + } + if (this.info.email) { + $.replace(email, name); + return delete this.nodes.email; + } + } + }; + + Filter = { + filters: {}, + init: function() { + var boards, err, filter, hl, key, op, regexp, stub, top, _i, _len, _ref, _ref1, _ref2, _ref3, _ref4; + + if (g.VIEW === 'catalog' || !Conf['Filter']) { + return; + } + if (!Conf['Filtered Backlinks']) { + $.addClass(doc, 'hide-backlinks'); + } + for (key in Config.filter) { + this.filters[key] = []; + _ref = Conf[key].split('\n'); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + filter = _ref[_i]; + if (filter[0] === '#') { + continue; + } + if (!(regexp = filter.match(/\/(.+)\/(\w*)/))) { + continue; + } + filter = filter.replace(regexp[0], ''); + boards = ((_ref1 = filter.match(/boards:([^;]+)/)) != null ? _ref1[1].toLowerCase() : void 0) || 'global'; + if (boards !== 'global' && !(boards.split(',')).contains(g.BOARD.ID)) { + continue; + } + if (['uniqueID', 'MD5'].contains(key)) { + regexp = regexp[1]; + } else { + try { + regexp = RegExp(regexp[1], regexp[2]); + } catch (_error) { + err = _error; + new Notice('warning', err.message, 60); + continue; + } + } + op = ((_ref2 = filter.match(/[^t]op:(yes|no|only)/)) != null ? _ref2[1] : void 0) || 'yes'; + stub = (function() { + var _ref3; + + switch ((_ref3 = filter.match(/stub:(yes|no)/)) != null ? _ref3[1] : void 0) { + case 'yes': + return true; + case 'no': + return false; + default: + return Conf['Stubs']; + } + })(); + 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'; + top = top === 'yes'; + } + this.filters[key].push(this.createFilter(regexp, op, stub, hl, top)); + } + if (!this.filters[key].length) { + delete this.filters[key]; + } + } + if (!Object.keys(this.filters).length) { + return; + } + return Post.callbacks.push({ + name: 'Filter', + cb: this.node + }); + }, + createFilter: function(regexp, op, stub, hl, top) { + var settings, test; + + test = typeof regexp === 'string' ? function(value) { + return regexp === value; + } : function(value) { + return regexp.test(value); + }; + settings = { + hide: !hl, + stub: stub, + "class": hl, + top: top + }; + return function(value, isReply) { + if (isReply && op === 'only' || !isReply && op === 'no') { + return false; + } + if (!test(value)) { + return false; + } + return settings; + }; + }, + node: function() { + var filter, firstThread, key, result, thisThread, value, _i, _len, _ref; + + if (this.isClone) { + return; + } + for (key in Filter.filters) { + value = Filter[key](this); + if (value === false) { + continue; + } + _ref = Filter.filters[key]; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + filter = _ref[_i]; + if (!(result = filter(value, this.isReply))) { + continue; + } + if (result.hide) { + if (this.isReply) { + PostHiding.hide(this, result.stub); + } else if (g.VIEW === 'index') { + ThreadHiding.hide(this.thread, result.stub); + } else { + continue; + } + return; + } + $.addClass(this.nodes.root, result["class"]); + if (!this.isReply && result.top && 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]); + } + } + } + } + } + }, + name: function(post) { + if ('name' in post.info) { + return post.info.name; + } + return false; + }, + uniqueID: function(post) { + if ('uniqueID' in post.info) { + return post.info.uniqueID; + } + return false; + }, + tripcode: function(post) { + if ('tripcode' in post.info) { + return post.info.tripcode; + } + return false; + }, + capcode: function(post) { + if ('capcode' in post.info) { + return post.info.capcode; + } + return false; + }, + email: function(post) { + if ('email' in post.info) { + return post.info.email; + } + return false; + }, + subject: function(post) { + if ('subject' in post.info) { + return post.info.subject || false; + } + return false; + }, + comment: function(post) { + if ('comment' in post.info) { + return post.info.comment; + } + return false; + }, + flag: function(post) { + if ('flag' in post.info) { + return post.info.flag; + } + return false; + }, + filename: function(post) { + if (post.file) { + return post.file.name; + } + return false; + }, + dimensions: function(post) { + if (post.file && post.file.isImage) { + return post.file.dimensions; + } + return false; + }, + filesize: function(post) { + if (post.file) { + return post.file.size; + } + return false; + }, + MD5: function(post) { + if (post.file) { + return post.file.MD5; + } + return false; + }, + menu: { + init: function() { + var div, entry, type, _i, _len, _ref; + + if (g.VIEW === 'catalog' || !Conf['Menu'] || !Conf['Filter']) { + return; + } + div = $.el('div', { + textContent: 'Filter' + }); + entry = { + type: 'post', + el: div, + order: 50, + open: function(post) { + Filter.menu.post = post; + return true; + }, + subEntries: [] + }; + _ref = [['Name', 'name'], ['Unique ID', 'uniqueID'], ['Tripcode', 'tripcode'], ['Capcode', 'capcode'], ['E-mail', 'email'], ['Subject', 'subject'], ['Comment', 'comment'], ['Flag', 'flag'], ['Filename', 'filename'], ['Image dimensions', 'dimensions'], ['Filesize', 'filesize'], ['Image MD5', 'MD5']]; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + type = _ref[_i]; + entry.subEntries.push(Filter.menu.createSubEntry(type[0], type[1])); + } + return $.event('AddMenuEntry', entry); + }, + createSubEntry: function(text, type) { + var el; + + el = $.el('a', { + href: 'javascript:;', + textContent: text + }); + el.dataset.type = type; + $.on(el, 'click', Filter.menu.makeFilter); + return { + el: el, + open: function(post) { + var value; + + value = Filter[type](post); + return value !== false; + } + }; + }, + makeFilter: function() { + var re, type, value; + + type = this.dataset.type; + value = Filter[type](Filter.menu.post); + re = ['uniqueID', 'MD5'].contains(type) ? value : value.replace(/\/|\\|\^|\$|\n|\.|\(|\)|\{|\}|\[|\]|\?|\*|\+|\|/g, function(c) { + if (c === '\n') { + return '\\n'; + } else if (c === '\\') { + return '\\\\'; + } else { + return "\\" + c; + } + }); + re = ['uniqueID', 'MD5'].contains(type) ? "/" + re + "/" : "/^" + re + "$/"; + return $.get(type, Conf[type], function(item) { + var save, section, select, ta, tl; + + save = item[type]; + save = save ? "" + save + "\n" + re : re; + $.set(type, save); + Settings.open('Filter'); + section = $('.section-container'); + select = $('select[name=filter]', section); + select.value = type; + Settings.selectFilter.call(select); + ta = $('textarea', section); + tl = ta.textLength; + ta.setSelectionRange(tl, tl); + return ta.focus(); + }); + } + } + }; + + PostHiding = { + init: function() { + if (g.VIEW === 'catalog' || !Conf['Reply Hiding Buttons'] && !Conf['Reply Hiding Link']) { + return; + } + if (Conf['Reply Hiding Buttons']) { + $.addClass(doc, "reply-hide"); + } + this.db = new DataBoard('hiddenPosts'); + return Post.callbacks.push({ + name: 'Reply Hiding', + cb: this.node + }); + }, + node: function() { + var data; + + if (!this.isReply || this.isClone) { + return; + } + if (data = PostHiding.db.get({ + boardID: this.board.ID, + threadID: this.thread.ID, + postID: this.ID + })) { + if (data.thisPost) { + PostHiding.hide(this, data.makeStub, data.hideRecursively); + } else { + Recursive.apply(PostHiding.hide, this, data.makeStub, true); + Recursive.add(PostHiding.hide, this, data.makeStub, true); + } + } + if (!Conf['Reply Hiding Buttons']) { + return; + } + return $.replace($('.sideArrows', this.nodes.root), PostHiding.makeButton(this, 'hide')); + }, + menu: { + init: function() { + var apply, div, hideStubLink, makeStub, replies, thisPost; + + if (g.VIEW === 'catalog' || !Conf['Menu'] || !Conf['Reply Hiding Link']) { + return; + } + div = $.el('div', { + className: 'hide-reply-link', + textContent: 'Hide reply' + }); + apply = $.el('a', { + textContent: 'Apply', + href: 'javascript:;' + }); + $.on(apply, 'click', PostHiding.menu.hide); + thisPost = $.el('label', { + innerHTML: ' This post' + }); + replies = $.el('label', { + innerHTML: " Hide replies" + }); + makeStub = $.el('label', { + innerHTML: " Make stub" + }); + $.event('AddMenuEntry', { + type: 'post', + el: div, + order: 20, + open: function(post) { + if (!post.isReply || post.isClone || post.isHidden) { + return false; + } + PostHiding.menu.post = post; + return true; + }, + subEntries: [ + { + el: apply + }, { + el: thisPost + }, { + el: replies + }, { + el: makeStub + } + ] + }); + div = $.el('div', { + className: 'show-reply-link', + textContent: 'Show reply' + }); + apply = $.el('a', { + textContent: 'Apply', + href: 'javascript:;' + }); + $.on(apply, 'click', PostHiding.menu.show); + thisPost = $.el('label', { + innerHTML: ' This post' + }); + replies = $.el('label', { + innerHTML: " Show replies" + }); + hideStubLink = $.el('a', { + textContent: 'Hide stub', + href: 'javascript:;' + }); + $.on(hideStubLink, 'click', PostHiding.menu.hideStub); + $.event('AddMenuEntry', { + type: 'post', + el: div, + order: 20, + open: function(post) { + var data; + + if (!post.isReply || post.isClone || !post.isHidden) { + return false; + } + if (!(data = PostHiding.db.get({ + boardID: post.board.ID, + threadID: post.thread.ID, + postID: post.ID + }))) { + return false; + } + PostHiding.menu.post = post; + thisPost.firstChild.checked = post.isHidden; + replies.firstChild.checked = (data != null ? data.hideRecursively : void 0) != null ? data.hideRecursively : Conf['Recursive Hiding']; + return true; + }, + subEntries: [ + { + el: apply + }, { + el: thisPost + }, { + el: replies + } + ] + }); + return $.event('AddMenuEntry', { + type: 'post', + el: hideStubLink, + order: 15, + open: function(post) { + var data; + + if (!post.isReply || post.isClone || !post.isHidden) { + return false; + } + if (!(data = PostHiding.db.get({ + boardID: post.board.ID, + threadID: post.thread.ID, + postID: post.ID + }))) { + return false; + } + return PostHiding.menu.post = post; + } + }); + }, + hide: function() { + var makeStub, parent, post, replies, thisPost; + + parent = this.parentNode; + thisPost = $('input[name=thisPost]', parent).checked; + replies = $('input[name=replies]', parent).checked; + makeStub = $('input[name=makeStub]', parent).checked; + post = PostHiding.menu.post; + if (thisPost) { + PostHiding.hide(post, makeStub, replies); + } else if (replies) { + Recursive.apply(PostHiding.hide, post, makeStub, true); + Recursive.add(PostHiding.hide, post, makeStub, true); + } else { + return; + } + PostHiding.saveHiddenState(post, true, thisPost, makeStub, replies); + return $.event('CloseMenu'); + }, + show: function() { + var data, parent, post, replies, thisPost; + + parent = this.parentNode; + thisPost = $('input[name=thisPost]', parent).checked; + replies = $('input[name=replies]', parent).checked; + post = PostHiding.menu.post; + if (thisPost) { + PostHiding.show(post, replies); + } else if (replies) { + Recursive.apply(PostHiding.show, post, true); + Recursive.rm(PostHiding.hide, post, true); + } else { + return; + } + if (data = PostHiding.db.get({ + boardID: post.board.ID, + threadID: post.thread.ID, + postID: post.ID + })) { + PostHiding.saveHiddenState(post, !(thisPost && replies), !thisPost, data.makeStub, !replies); + } + return $.event('CloseMenu'); + }, + hideStub: function() { + var post; + + post = PostHiding.menu.post; + post.nodes.root.hidden = true; + $.event('CloseMenu'); + } + }, + makeButton: function(post, type) { + var a; + + a = $.el('a', { + className: "" + type + "-reply-button", + innerHTML: " " + (type === 'hide' ? '-' : '+') + " ", + href: 'javascript:;' + }); + $.on(a, 'click', PostHiding.toggle); + return a; + }, + saveHiddenState: function(post, isHiding, thisPost, makeStub, hideRecursively) { + var data; + + data = { + boardID: post.board.ID, + threadID: post.thread.ID, + postID: post.ID + }; + if (isHiding) { + data.val = { + thisPost: thisPost !== false, + makeStub: makeStub, + hideRecursively: hideRecursively + }; + return PostHiding.db.set(data); + } else { + return PostHiding.db["delete"](data); + } + }, + toggle: function() { + var post; + + post = Get.postFromNode(this); + PostHiding[(post.isHidden ? 'show' : 'hide')](post); + return PostHiding.saveHiddenState(post, post.isHidden); + }, + hide: function(post, makeStub, hideRecursively) { + var a, button, postInfo, quotelink, _i, _len, _ref; + + if (makeStub == null) { + makeStub = Conf['Stubs']; + } + if (hideRecursively == null) { + hideRecursively = Conf['Recursive Hiding']; + } + if (post.isHidden) { + return; + } + post.isHidden = true; + if (hideRecursively) { + Recursive.apply(PostHiding.hide, post, makeStub, true); + Recursive.add(PostHiding.hide, post, makeStub, true); + } + _ref = Get.allQuotelinksLinkingTo(post); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + quotelink = _ref[_i]; + $.addClass(quotelink, 'filtered'); + } + if (!makeStub) { + post.nodes.root.hidden = true; + return; + } + a = PostHiding.makeButton(post, 'show'); + postInfo = Conf['Anonymize'] ? 'Anonymous' : $('.nameBlock', post.nodes.info).textContent; + $.add(a, $.tn(" " + postInfo)); + post.nodes.stub = $.el('div', { + className: 'stub' + }); + $.add(post.nodes.stub, Conf['Menu'] ? [a, $.tn(' '), button = Menu.makeButton(post)] : a); + return $.prepend(post.nodes.root, post.nodes.stub); + }, + show: function(post, showRecursively) { + var quotelink, _i, _len, _ref; + + if (showRecursively == null) { + showRecursively = Conf['Recursive Hiding']; + } + if (post.nodes.stub) { + $.rm(post.nodes.stub); + delete post.nodes.stub; + } else { + post.nodes.root.hidden = false; + } + post.isHidden = false; + if (showRecursively) { + Recursive.apply(PostHiding.show, post, true); + Recursive.rm(PostHiding.hide, post); + } + _ref = Get.allQuotelinksLinkingTo(post); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + quotelink = _ref[_i]; + $.rmClass(quotelink, 'filtered'); + } + } + }; + + Recursive = { + recursives: {}, + init: function() { + if (g.VIEW === 'catalog') { + return; + } + return Post.callbacks.push({ + name: 'Recursive', + cb: this.node + }); + }, + node: function() { + var i, obj, quote, recursive, _i, _j, _len, _len1, _ref, _ref1; + + if (this.isClone) { + return; + } + _ref = this.quotes; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + quote = _ref[_i]; + if (obj = Recursive.recursives[quote]) { + _ref1 = obj.recursives; + for (i = _j = 0, _len1 = _ref1.length; _j < _len1; i = ++_j) { + recursive = _ref1[i]; + recursive.apply(null, [this].concat(__slice.call(obj.args[i]))); + } + } + } + }, + add: function() { + var args, obj, post, recursive, _base, _name; + + recursive = arguments[0], post = arguments[1], args = 3 <= arguments.length ? __slice.call(arguments, 2) : []; + obj = (_base = Recursive.recursives)[_name = post.fullID] || (_base[_name] = { + recursives: [], + args: [] + }); + obj.recursives.push(recursive); + return obj.args.push(args); + }, + rm: function(recursive, post) { + var i, obj, rec, _i, _len, _ref; + + if (!(obj = Recursive.recursives[post.fullID])) { + return; + } + _ref = obj.recursives; + for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { + rec = _ref[i]; + if (rec === recursive) { + obj.recursives.splice(i, 1); + obj.args.splice(i, 1); + } + } + }, + apply: function() { + var ID, args, fullID, post, recursive, _ref; + + recursive = arguments[0], post = arguments[1], args = 3 <= arguments.length ? __slice.call(arguments, 2) : []; + fullID = post.fullID; + _ref = g.posts; + for (ID in _ref) { + post = _ref[ID]; + if (post.quotes.contains(fullID)) { + recursive.apply(null, [post].concat(__slice.call(args))); + } + } + } + }; + + ThreadHiding = { + init: function() { + if (g.VIEW !== 'index' || !Conf['Thread Hiding Buttons'] && !Conf['Thread Hiding Link']) { + return; + } + this.db = new DataBoard('hiddenThreads'); + this.syncCatalog(); + return Thread.callbacks.push({ + name: 'Thread Hiding', + cb: this.node + }); + }, + node: function() { + var data; + + if (data = ThreadHiding.db.get({ + boardID: this.board.ID, + threadID: this.ID + })) { + ThreadHiding.hide(this, data.makeStub); + } + if (!Conf['Thread Hiding Buttons']) { + return; + } + return $.prepend(this.OP.nodes.root, ThreadHiding.makeButton(this, 'hide')); + }, + syncCatalog: function() { + var hiddenThreads, hiddenThreadsOnCatalog, threadID; + + hiddenThreads = ThreadHiding.db.get({ + boardID: g.BOARD.ID, + defaultValue: {} + }); + hiddenThreadsOnCatalog = JSON.parse(localStorage.getItem("4chan-hide-t-" + g.BOARD)) || {}; + for (threadID in hiddenThreadsOnCatalog) { + if (!(threadID in hiddenThreads)) { + hiddenThreads[threadID] = {}; + } + } + for (threadID in hiddenThreads) { + if (!(threadID in hiddenThreadsOnCatalog)) { + delete hiddenThreads[threadID]; + } + } + if ((ThreadHiding.db.data.lastChecked || 0) > Date.now() - $.MINUTE) { + ThreadHiding.cleanCatalog(hiddenThreadsOnCatalog); + } + return ThreadHiding.db.set({ + boardID: g.BOARD.ID, + val: hiddenThreads + }); + }, + cleanCatalog: function(hiddenThreadsOnCatalog) { + return $.cache("//api.4chan.org/" + g.BOARD + "/threads.json", function() { + var page, thread, threads, _i, _j, _len, _len1, _ref, _ref1; + + if (this.status !== 200) { + return; + } + threads = {}; + _ref = JSON.parse(this.response); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + page = _ref[_i]; + _ref1 = page.threads; + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + thread = _ref1[_j]; + if (thread.no in hiddenThreadsOnCatalog) { + threads[thread.no] = hiddenThreadsOnCatalog[thread.no]; + } + } + } + if (Object.keys(threads).length) { + return localStorage.setItem("4chan-hide-t-" + g.BOARD, JSON.stringify(threads)); + } else { + return localStorage.removeItem("4chan-hide-t-" + g.BOARD); + } + }); + }, + menu: { + init: function() { + var apply, div, hideStubLink, makeStub; + + if (g.VIEW !== 'index' || !Conf['Menu'] || !Conf['Thread Hiding Link']) { + return; + } + div = $.el('div', { + className: 'hide-thread-link', + textContent: 'Hide thread' + }); + apply = $.el('a', { + textContent: 'Apply', + href: 'javascript:;' + }); + $.on(apply, 'click', ThreadHiding.menu.hide); + makeStub = $.el('label', { + innerHTML: " Make stub" + }); + $.event('AddMenuEntry', { + type: 'post', + el: div, + order: 20, + open: function(_arg) { + var isReply, thread; + + thread = _arg.thread, isReply = _arg.isReply; + if (isReply || thread.isHidden) { + return false; + } + ThreadHiding.menu.thread = thread; + return true; + }, + subEntries: [ + { + el: apply + }, { + el: makeStub + } + ] + }); + div = $.el('a', { + className: 'show-thread-link', + textContent: 'Show thread', + href: 'javascript:;' + }); + $.on(div, 'click', ThreadHiding.menu.show); + $.event('AddMenuEntry', { + type: 'post', + el: div, + order: 20, + open: function(_arg) { + var isReply, thread; + + thread = _arg.thread, isReply = _arg.isReply; + if (isReply || !thread.isHidden) { + return false; + } + ThreadHiding.menu.thread = thread; + return true; + } + }); + hideStubLink = $.el('a', { + textContent: 'Hide stub', + href: 'javascript:;' + }); + $.on(hideStubLink, 'click', ThreadHiding.menu.hideStub); + return $.event('AddMenuEntry', { + type: 'post', + el: hideStubLink, + order: 15, + open: function(_arg) { + var isReply, thread; + + thread = _arg.thread, isReply = _arg.isReply; + if (isReply || !thread.isHidden) { + return false; + } + return ThreadHiding.menu.thread = thread; + } + }); + }, + hide: function() { + var makeStub, thread; + + makeStub = $('input', this.parentNode).checked; + thread = ThreadHiding.menu.thread; + ThreadHiding.hide(thread, makeStub); + ThreadHiding.saveHiddenState(thread, makeStub); + return $.event('CloseMenu'); + }, + show: function() { + var thread; + + thread = ThreadHiding.menu.thread; + ThreadHiding.show(thread); + ThreadHiding.saveHiddenState(thread); + return $.event('CloseMenu'); + }, + hideStub: function() { + var thread; + + thread = ThreadHiding.menu.thread; + ThreadHiding.hide(thread, false); + $.event('CloseMenu'); + } + }, + makeButton: function(thread, type) { + var a; + + a = $.el('a', { + className: "" + type + "-thread-button", + innerHTML: " " + (type === 'hide' ? '-' : '+') + " ", + href: 'javascript:;' + }); + a.dataset.fullID = thread.fullID; + $.on(a, 'click', ThreadHiding.toggle); + return a; + }, + saveHiddenState: function(thread, makeStub) { + var hiddenThreadsOnCatalog; + + hiddenThreadsOnCatalog = JSON.parse(localStorage.getItem("4chan-hide-t-" + g.BOARD)) || {}; + if (thread.isHidden) { + ThreadHiding.db.set({ + boardID: thread.board.ID, + threadID: thread.ID, + val: { + makeStub: makeStub + } + }); + hiddenThreadsOnCatalog[thread] = true; + } else { + ThreadHiding.db["delete"]({ + boardID: thread.board.ID, + threadID: thread.ID + }); + delete hiddenThreadsOnCatalog[thread]; + } + return localStorage.setItem("4chan-hide-t-" + g.BOARD, JSON.stringify(hiddenThreadsOnCatalog)); + }, + toggle: function(thread) { + if (!(thread instanceof Thread)) { + thread = g.threads[this.dataset.fullID]; + } + if (thread.isHidden) { + ThreadHiding.show(thread); + } else { + ThreadHiding.hide(thread); + } + return ThreadHiding.saveHiddenState(thread); + }, + hide: function(thread, makeStub) { + var OP, a, numReplies, opInfo, span, 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; + 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); + }, + show: function(thread) { + var threadRoot; + + if (thread.stub) { + $.rm(thread.stub); + delete thread.stub; + } + threadRoot = thread.OP.nodes.root.parentNode; + return threadRoot.nextElementSibling.hidden = threadRoot.hidden = thread.isHidden = false; + } + }; + + QuoteBacklink = { + init: function() { + var format; + + if (g.VIEW === 'catalog' || !Conf['Quote Backlinks']) { + return; + } + format = Conf['backlink'].replace(/%id/g, "' + id + '"); + this.funk = Function('id', "return '" + format + "'"); + this.containers = {}; + Post.callbacks.push({ + name: 'Quote Backlinking Part 1', + cb: this.firstNode + }); + return Post.callbacks.push({ + name: 'Quote Backlinking Part 2', + cb: this.secondNode + }); + }, + firstNode: function() { + var a, clone, container, containers, frag, link, post, quote, _i, _j, _k, _len, _len1, _len2, _ref, _ref1; + + if (this.isClone || !this.quotes.length) { + return; + } + a = $.el('a', { + href: "/" + this.board + "/res/" + this.thread + "#p" + this, + className: this.isHidden ? 'filtered backlink' : 'backlink', + textContent: (QuoteBacklink.funk(this.ID)) + (Conf['Mark Quotes of You'] && this.info.yours ? '\u00A0(You)' : '') + }); + _ref = this.quotes; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + quote = _ref[_i]; + containers = [QuoteBacklink.getContainer(quote)]; + if ((post = g.posts[quote]) && post.nodes.backlinkContainer) { + _ref1 = post.clones; + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + clone = _ref1[_j]; + containers.push(clone.nodes.backlinkContainer); + } + } + for (_k = 0, _len2 = containers.length; _k < _len2; _k++) { + container = containers[_k]; + frag = [$.tn(' '), link = a.cloneNode(true)]; + if (Conf['Quote Previewing']) { + $.on(link, 'mouseover', QuotePreview.mouseover); + } + if (Conf['Quote Inlining']) { + $.on(link, 'click', QuoteInline.toggle); + if (Conf['Quote Hash Navigation']) { + frag.push.apply(frag, QuoteInline.qiQuote(link, $.hasClass(link, 'filtered'))); + } + } + $.add(container, frag); + } + } + }, + secondNode: function() { + var container; + + if (this.isClone && (this.origin.isReply || Conf['OP Backlinks'])) { + this.nodes.backlinkContainer = $('.container', this.nodes.info); + return; + } + if (!(this.isReply || Conf['OP Backlinks'])) { + return; + } + container = QuoteBacklink.getContainer(this.fullID); + this.nodes.backlinkContainer = container; + return $.add(this.nodes.info, container); + }, + getContainer: function(id) { + var _base; + + return (_base = this.containers)[id] || (_base[id] = $.el('span', { + className: 'container' + })); + } + }; + + QuoteCT = { + init: function() { + if (g.VIEW === 'catalog' || !Conf['Mark Cross-thread Quotes']) { + return; + } + if (Conf['Comment Expansion']) { + ExpandComment.callbacks.push(this.node); + } + this.text = '\u00A0(Cross-thread)'; + return Post.callbacks.push({ + name: 'Mark Cross-thread Quotes', + cb: this.node + }); + }, + node: function() { + var board, boardID, quotelink, thread, threadID, _i, _len, _ref, _ref1, _ref2; + + if (this.isClone && this.thread === this.context.thread) { + return; + } + _ref = this.isClone ? this.context : this, board = _ref.board, thread = _ref.thread; + _ref1 = this.nodes.quotelinks; + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + quotelink = _ref1[_i]; + _ref2 = Get.postDataFromLink(quotelink), boardID = _ref2.boardID, threadID = _ref2.threadID; + if (!threadID) { + continue; + } + if (this.isClone) { + quotelink.textContent = quotelink.textContent.replace(QuoteCT.text, ''); + } + if (boardID === board.ID && threadID !== thread.ID) { + $.add(quotelink, $.tn(QuoteCT.text)); + } + } + } + }; + + QuoteInline = { + init: function() { + if (g.VIEW === 'catalog' || !Conf['Quote Inlining']) { + return; + } + if (Conf['Quote Hash Navigation']) { + this.node = function() { + var link, _i, _len, _ref; + + _ref = this.nodes.quotelinks.concat(__slice.call(this.nodes.backlinks)); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + link = _ref[_i]; + if (!this.isClone) { + $.after(link, QuoteInline.qiQuote(link, $.hasClass(link, 'filtered'))); + } + $.on(link, 'click', QuoteInline.toggle); + } + }; + } else { + this.node = function() { + var link, _i, _len, _ref; + + _ref = this.nodes.quotelinks.concat(__slice.call(this.nodes.backlinks)); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + link = _ref[_i]; + $.on(link, 'click', QuoteInline.toggle); + } + }; + } + if (Conf['Comment Expansion']) { + ExpandComment.callbacks.push(this.node); + } + return Post.callbacks.push({ + name: 'Quote Inlining', + cb: this.node + }); + }, + qiQuote: function(link, hidden) { + return [ + $.tn(' '), $.el('a', { + className: hidden ? 'hashlink filtered' : 'hashlink', + textContent: '#', + href: link.href + }) + ]; + }, + toggle: function(e) { + var boardID, context, postID, threadID, _ref; + + if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) { + return; + } + e.preventDefault(); + _ref = Get.postDataFromLink(this), boardID = _ref.boardID, threadID = _ref.threadID, postID = _ref.postID; + context = Get.contextFromNode(this); + if ($.hasClass(this, 'inlined')) { + QuoteInline.rm(this, boardID, threadID, postID, context); + } else { + if ($.x("ancestor::div[@id='p" + postID + "']", this)) { + return; + } + QuoteInline.add(this, boardID, threadID, postID, context); + } + return this.classList.toggle('inlined'); + }, + findRoot: function(quotelink, isBacklink) { + if (isBacklink) { + return quotelink.parentNode.parentNode; + } else { + return $.x('ancestor-or-self::*[parent::blockquote][1]', quotelink); + } + }, + add: function(quotelink, boardID, threadID, postID, context) { + var inline, isBacklink, post, qroot, root; + + isBacklink = $.hasClass(quotelink, 'backlink'); + inline = $.el('div', { + id: "i" + postID, + className: 'inline' + }); + root = QuoteInline.findRoot(quotelink, isBacklink); + $.after(root, inline); + qroot = $.x('ancestor::*[contains(@class,"postContainer")][1]', root); + $.addClass(qroot, 'hasInline'); + Get.postClone(boardID, threadID, postID, inline, context); + if (!((post = g.posts["" + boardID + "." + postID]) && context.thread === post.thread)) { + return; + } + if (isBacklink && Conf['Forward Hiding']) { + $.addClass(post.nodes.root, 'forwarded'); + post.forwarded++ || (post.forwarded = 1); + } + if (!Unread.posts) { + return; + } + return Unread.readSinglePost(post); + }, + rm: function(quotelink, boardID, threadID, postID, context) { + var el, inlined, isBacklink, post, qroot, root, _ref; + + isBacklink = $.hasClass(quotelink, 'backlink'); + root = QuoteInline.findRoot(quotelink, isBacklink); + root = $.x("following-sibling::div[@id='i" + postID + "'][1]", root); + qroot = $.x('ancestor::*[contains(@class,"postContainer")][1]', root); + $.rm(root); + if (!$('.inline', qroot)) { + $.rmClass(qroot, 'hasInline'); + } + if (!(el = root.firstElementChild)) { + return; + } + post = g.posts["" + boardID + "." + postID]; + post.rmClone(el.dataset.clone); + if (Conf['Forward Hiding'] && isBacklink && context.thread === g.threads["" + boardID + "." + threadID] && !--post.forwarded) { + delete post.forwarded; + $.rmClass(post.nodes.root, 'forwarded'); + } + while (inlined = $('.inlined', el)) { + _ref = Get.postDataFromLink(inlined), boardID = _ref.boardID, threadID = _ref.threadID, postID = _ref.postID; + QuoteInline.rm(inlined, boardID, threadID, postID, context); + $.rmClass(inlined, 'inlined'); + } + } + }; + + QuoteOP = { + init: function() { + if (g.VIEW === 'catalog' || !Conf['Mark OP Quotes']) { + return; + } + if (Conf['Comment Expansion']) { + ExpandComment.callbacks.push(this.node); + } + this.text = '\u00A0(OP)'; + return Post.callbacks.push({ + name: 'Mark OP Quotes', + cb: this.node + }); + }, + node: function() { + var boardID, fullID, i, postID, quotelink, quotelinks, quotes, _ref; + + if (this.isClone && this.thread === this.context.thread) { + return; + } + if (!(quotes = this.quotes).length) { + return; + } + quotelinks = this.nodes.quotelinks; + if (this.isClone && quotes.contains(this.thread.fullID)) { + 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)) { + return; + } + i = 0; + while (quotelink = quotelinks[i++]) { + _ref = Get.postDataFromLink(quotelink), boardID = _ref.boardID, postID = _ref.postID; + if (("" + boardID + "." + postID) === fullID) { + $.add(quotelink, $.tn(QuoteOP.text)); + } + } + } + }; + + QuotePreview = { + init: function() { + if (g.VIEW === 'catalog' || !Conf['Quote Previewing']) { + return; + } + if (Conf['Comment Expansion']) { + ExpandComment.callbacks.push(this.node); + } + return Post.callbacks.push({ + name: 'Quote Previewing', + cb: this.node + }); + }, + node: function() { + var link, _i, _len, _ref; + + _ref = this.nodes.quotelinks.concat(__slice.call(this.nodes.backlinks)); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + link = _ref[_i]; + $.on(link, 'mouseover', QuotePreview.mouseover); + } + }, + mouseover: function(e) { + var boardID, clone, origin, post, postID, posts, qp, quote, quoterID, threadID, _i, _j, _len, _len1, _ref, _ref1; + + if ($.hasClass(this, 'inlined')) { + return; + } + _ref = Get.postDataFromLink(this), boardID = _ref.boardID, threadID = _ref.threadID, postID = _ref.postID; + qp = $.el('div', { + id: 'qp', + className: 'dialog' + }); + $.add(Header.hover, qp); + Get.postClone(boardID, threadID, postID, qp, Get.contextFromNode(this)); + UI.hover({ + root: this, + el: qp, + latestEvent: e, + endEvents: 'mouseout click', + cb: QuotePreview.mouseout, + asapTest: function() { + return qp.firstElementChild; + } + }); + if (!(origin = g.posts["" + boardID + "." + postID])) { + return; + } + if (Conf['Quote Highlighting']) { + posts = [origin].concat(origin.clones); + posts.pop(); + for (_i = 0, _len = posts.length; _i < _len; _i++) { + post = posts[_i]; + $.addClass(post.nodes.post, 'qphl'); + } + } + quoterID = $.x('ancestor::*[@id][1]', this).id.match(/\d+$/)[0]; + clone = Get.postFromRoot(qp.firstChild); + _ref1 = clone.nodes.quotelinks.concat(__slice.call(clone.nodes.backlinks)); + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + quote = _ref1[_j]; + if (quote.hash.slice(2) === quoterID) { + $.addClass(quote, 'forwardlink'); + } + } + }, + mouseout: function() { + var clone, post, root, _i, _len, _ref; + + if (!(root = this.el.firstElementChild)) { + return; + } + clone = Get.postFromRoot(root); + post = clone.origin; + post.rmClone(root.dataset.clone); + if (!Conf['Quote Highlighting']) { + return; + } + _ref = [post].concat(post.clones); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + post = _ref[_i]; + $.rmClass(post.nodes.post, 'qphl'); + } + } + }; + + QuoteStrikeThrough = { + init: function() { + if (g.VIEW === 'catalog' || !Conf['Reply Hiding Buttons'] && !Conf['Reply Hiding Link'] && !Conf['Filter']) { + return; + } + return Post.callbacks.push({ + name: 'Strike-through Quotes', + cb: this.node + }); + }, + node: function() { + var boardID, postID, quotelink, _i, _len, _ref, _ref1, _ref2; + + if (this.isClone) { + return; + } + _ref = this.nodes.quotelinks; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + quotelink = _ref[_i]; + _ref1 = Get.postDataFromLink(quotelink), boardID = _ref1.boardID, postID = _ref1.postID; + if ((_ref2 = g.posts["" + boardID + "." + postID]) != null ? _ref2.isHidden : void 0) { + $.addClass(quotelink, 'filtered'); + } + } + } + }; + + /* + <3 aeosynth + */ + + + QuoteThreading = { + init: function() { + var input; + + if (!(Conf['Quote Threading'] && g.VIEW === 'thread')) { + return; + } + this.enabled = true; + this.controls = $.el('span', { + innerHTML: '' + }); + input = $('input', this.controls); + $.on(input, 'change', QuoteThreading.toggle); + $.event('AddMenuEntry', { + type: 'header', + el: this.controls, + order: 98 + }); + $.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]; + if (post.cb) { + post.cb.call(post); + } + } + 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; + posts = g.posts; + if (!(post = posts[fullID]) || post.isHidden) { + 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; + } + } + keys = Object.keys(uniq); + if (keys.length !== 1) { + return; + } + this.threaded = "" + g.BOARD + "." + 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) { + return false; + } + if (QuoteThreading.hasRun) { + height = doc.clientHeight; + _ref = qpost.nodes.root.getBoundingClientRect(), bottom = _ref.bottom, top = _ref.top; + if (!(Unread.posts.contains(qpost) || ((bottom < height) && (top > 0)))) { + return false; + } + } + qroot = qpost.nodes.root; + if (!$.hasClass(qroot, 'threadOP')) { + $.addClass(qroot, 'threadOP'); + threadContainer = $.el('div', { + className: 'threadContainer' + }); + $.after(qroot, threadContainer); + } else { + threadContainer = qroot.nextSibling; + } + $.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(); + } + } + 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; + }); + $.add(thread, replies); + containers = $$('.threadContainer', thread); + for (_j = 0, _len1 = containers.length; _j < _len1; _j++) { + container = containers[_j]; + $.rm(container); + } + _ref = $$('.threadOP'); + for (_k = 0, _len2 = _ref.length; _k < _len2; _k++) { + post = _ref[_k]; + $.rmClass(post, 'threadOP'); + } + } + return Unread.update(true); + }, + kb: function() { + var control; + + control = $.id('threadingControl'); + return control.click(); + } + }; + + QuoteYou = { + init: function() { + if (!(g.VIEW !== 'catalog' && Conf['Mark Quotes of You'] && Conf['Quick Reply'])) { + return; + } + if (Conf['Highlight Own Posts']) { + $.addClass(doc, 'highlight-own'); + } + if (Conf['Highlight Posts Quoting You']) { + $.addClass(doc, 'highlight-you'); + } + if (Conf['Comment Expansion']) { + ExpandComment.callbacks.push(this.node); + } + this.text = '\u00A0(You)'; + return Post.callbacks.push({ + name: 'Mark Quotes of You', + cb: this.node + }); + }, + node: function() { + var quotelink, _i, _len, _ref; + + if (this.isClone) { + return; + } + if (this.info.yours) { + $.addClass(this.nodes.root, 'yourPost'); + } + if (!this.quotes.length) { + return; + } + _ref = this.nodes.quotelinks; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + quotelink = _ref[_i]; + if (!(QR.db.get(Get.postDataFromLink(quotelink)))) { + continue; + } + $.add(quotelink, $.tn(QuoteYou.text)); + $.addClass(quotelink, 'you'); + $.addClass(this.nodes.root, 'quotesYou'); + } + }, + cb: { + seek: function(type) { + var highlight, post, posts, result, str; + + if (!(Conf['Mark Quotes of You'] && Conf['Quick Reply'])) { + return; + } + if (highlight = $('.highlight')) { + $.rmClass(highlight, 'highlight'); + } + if (!QuoteYou.lastRead) { + if (!(post = QuoteYou.lastRead = $('.quotesYou'))) { + new Notice('warning', 'No posts are currently quoting you, loser.', 20); + return; + } + if (QuoteYou.cb.scroll(post)) { + return; + } + } else { + post = QuoteYou.lastRead; + } + str = "" + type + "::div[contains(@class,'quotesYou')]"; + while (post = (result = $.X(str, post)).snapshotItem(type === 'preceding' ? result.snapshotLength - 1 : 0)) { + if (QuoteYou.cb.scroll(post)) { + return; + } + } + posts = $$('.quotesYou'); + return QuoteYou.cb.scroll(posts[type === 'following' ? 0 : posts.length - 1]); + }, + scroll: function(post) { + if (Get.postFromRoot(post).isHidden) { + return false; + } else { + QuoteYou.lastRead = post; + window.location = "#" + post.id; + Header.scrollToPost(post); + $.addClass($('.post', post), 'highlight'); + return true; + } + } + } + }; + + Quotify = { + init: function() { + if (g.VIEW === 'catalog' || !Conf['Resurrect Quotes']) { + return; + } + if (Conf['Comment Expansion']) { + ExpandComment.callbacks.push(this.node); + } + return Post.callbacks.push({ + name: 'Resurrect Quotes', + cb: this.node + }); + }, + node: function() { + var deadlink, _i, _len, _ref; + + _ref = $$('.deadlink', this.nodes.comment); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + deadlink = _ref[_i]; + if (this.isClone) { + if ($.hasClass(deadlink, 'quotelink')) { + this.nodes.quotelinks.push(deadlink); + } + } else { + Quotify.parseDeadlink.call(this, deadlink); + } + } + }, + parseDeadlink: function(deadlink) { + var a, boardID, m, post, postID, quote, quoteID, redirect, _ref; + + if ($.hasClass(deadlink.parentNode, 'prettyprint')) { + Quotify.fixDeadlink(deadlink); + return; + } + quote = deadlink.textContent; + if (!(postID = (_ref = quote.match(/\d+$/)) != null ? _ref[0] : void 0)) { + return; + } + if (postID[0] === '0') { + Quotify.fixDeadlink(deadlink); + return; + } + boardID = (m = quote.match(/^>>>\/([a-z\d]+)/)) ? m[1] : this.board.ID; + quoteID = "" + boardID + "." + postID; + if (post = g.posts[quoteID]) { + if (!post.isDead) { + a = $.el('a', { + href: "/" + boardID + "/res/" + post.thread + "#p" + postID, + className: 'quotelink', + textContent: quote + }); + } else { + a = $.el('a', { + href: "/" + boardID + "/res/" + post.thread + "#p" + postID, + className: 'quotelink deadlink', + target: '_blank', + textContent: "" + quote + "\u00A0(Dead)" + }); + $.extend(a.dataset, { + boardID: boardID, + threadID: post.thread.ID, + postID: postID + }); + } + } else if (redirect = Redirect.to('thread', { + boardID: boardID, + threadID: 0, + postID: postID + })) { + a = $.el('a', { + href: redirect, + className: 'deadlink', + target: '_blank', + textContent: "" + quote + "\u00A0(Dead)" + }); + if (Redirect.to('post', { + boardID: boardID, + postID: postID + })) { + $.addClass(a, 'quotelink'); + $.extend(a.dataset, { + boardID: boardID, + postID: postID + }); + } + } + if (!this.quotes.contains(quoteID)) { + this.quotes.push(quoteID); + } + if (!a) { + deadlink.textContent = "" + quote + "\u00A0(Dead)"; + return; + } + $.replace(deadlink, a); + if ($.hasClass(a, 'quotelink')) { + return this.nodes.quotelinks.push(a); + } + }, + fixDeadlink: function(deadlink) { + var el, green; + + if (!(el = deadlink.previousSibling) || el.nodeName === 'BR') { + green = $.el('span', { + className: 'quote' + }); + $.before(deadlink, green); + $.add(green, deadlink); + } + return $.replace(deadlink, __slice.call(deadlink.childNodes)); + } + }; + + Linkify = { + init: function() { + if (g.VIEW === 'catalog' || !Conf['Linkify']) { + return; + } + this.regString = /((https?|mailto|git|magnet|ftp|irc):([a-z\d%\/])|[-a-z\d]+[.](aero|asia|biz|cat|com|coop|info|int|jobs|mobi|museum|name|net|org|post|pro|tel|travel|xxx|edu|gov|mil|[a-z]{2})(\/|(?!.))|[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}|[-\w\d.@]+@[a-z\d.-]+\.[a-z\d])/i; + if (Conf['Comment Expansion']) { + ExpandComment.callbacks.push(this.node); + } + if (Conf['Title Link']) { + $.sync('CachedTitles', Linkify.titleSync); + } + return Post.callbacks.push({ + name: 'Linkify', + cb: this.node + }); + }, + node: function() { + var data, el, end, endNode, i, index, items, length, link, links, node, result, saved, snapshot, space, test, word, _i, _len, _ref; + + if (this.isClone) { + if (Conf['Embedding']) { + i = 0; + items = $$('.embed', this.nodes.comment); + while (el = items[i++]) { + $.on(el, 'click', Linkify.cb.toggle); + if ($.hasClass(el, 'embedded')) { + Linkify.cb.toggle.call(el); + } + } + } + return; + } + test = /[^\s'"]+/g; + space = /[\s'"]/; + snapshot = $.X('.//br|.//text()', this.nodes.comment); + i = 0; + links = []; + while (node = snapshot.snapshotItem(i++)) { + data = node.data; + if (node.parentElement.nodeName === "A" || !data) { + continue; + } + while (result = test.exec(data)) { + index = result.index; + endNode = node; + word = result[0]; + if ((length = index + word.length) === data.length) { + test.lastIndex = 0; + while ((saved = snapshot.snapshotItem(i++))) { + if (saved.nodeName === 'BR') { + break; + } + endNode = saved; + data = saved.data; + word += data; + length = data.length; + if (end = space.exec(data)) { + test.lastIndex = length = end.index; + i--; + break; + } + } + } + if (Linkify.regString.exec(word)) { + links.push(Linkify.makeRange(node, endNode, index, length)); + } + if (!(test.lastIndex && node === endNode)) { + break; + } + } + } + _ref = links.reverse(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + link = _ref[_i]; + this.nodes.links.push(Linkify.makeLink(link, this)); + link.detach(); + } + if (!(Conf['Embedding'] || Conf['Link Title'])) { + return; + } + links = this.nodes.links; + i = 0; + while (link = links[i++]) { + if (data = Linkify.services(link)) { + if (Conf['Embedding']) { + Linkify.embed(data); + } + if (Conf['Link Title']) { + Linkify.title(data); + } + } + } + }, + makeRange: function(startNode, endNode, startOffset, endOffset) { + var range; + + range = document.createRange(); + range.setStart(startNode, startOffset); + range.setEnd(endNode, endOffset); + return range; + }, + makeLink: function(range) { + var a, char, i, text; + + text = range.toString(); + i = 0; + while (/[(\[{<>]/.test(text.charAt(i))) { + i++; + } + if (i) { + text = text.slice(i); + while (range.startOffset + i >= range.startContainer.data.length) { + i--; + } + if (i) { + range.setStart(range.startContainer, range.startOffset + i); + } + } + i = 0; + while (/[)\]}>.,]/.test(char = text.charAt(text.length - (1 + i)))) { + if (!(/[.,]/.test(char) || (text.match(/[()\[\]{}<>]/g)).length % 2)) { + break; + } + i++; + } + if (i) { + text = text.slice(0, -i); + while (range.endOffset - i < 0) { + i--; + } + if (i) { + range.setEnd(range.endContainer, range.endOffset - i); + } + } + if (!/(https?|mailto|git|magnet|ftp|irc):/.test(text)) { + text = (/@/.test(text) ? 'mailto:' : 'http://') + text; + } + a = $.el('a', { + className: 'linkify', + rel: 'nofollow noreferrer', + target: '_blank', + href: text + }); + $.add(a, range.extractContents()); + range.insertNode(a); + return a; + }, + services: function(link) { + var href, key, match, type, _ref; + + href = link.href; + _ref = Linkify.types; + for (key in _ref) { + type = _ref[key]; + if (!(match = type.regExp.exec(href))) { + continue; + } + return [key, match[1], match[2], link]; + } + }, + embed: function(data) { + var embed, href, key, link, name, options, uid, value, _ref; + + key = data[0], uid = data[1], options = data[2], link = data[3]; + href = link.href; + embed = $.el('a', { + className: 'embedder', + href: 'javascript:;', + textContent: '(embed)' + }); + _ref = { + key: key, + href: href, + uid: uid, + options: options + }; + for (name in _ref) { + value = _ref[name]; + embed.dataset[name] = value; + } + embed.dataset.nodedata = link.innerHTML; + $.addClass(link, "" + embed.dataset.key); + $.on(embed, 'click', Linkify.cb.toggle); + $.after(link, [$.tn(' '), embed]); + if (Conf['Auto-embed']) { + Linkify.cb.toggle.call(embed); + } + data.push(embed); + }, + title: function(data) { + var embed, err, key, link, options, service, title, titles, uid; + + key = data[0], uid = data[1], options = data[2], link = data[3], embed = data[4]; + if (!(service = Linkify.types[key].title)) { + return; + } + titles = Conf['CachedTitles']; + if (title = titles[uid]) { + if (link) { + link.textContent = title[0]; + } + if (Conf['Embedding']) { + return embed.dataset.title = title[0]; + } + } else { + try { + $.cache(service.api(uid), function() { + return title = Linkify.cb.title(this, data); + }); + } catch (_error) { + err = _error; + if (link) { + link.innerHTML = "[" + key + "] Title Link Blocked (are you using NoScript?)"; + } + return; + } + if (title) { + titles[uid] = [title, Date.now()]; + return $.set('CachedTitles', titles); + } + } + }, + titleSync: function(value) { + return Conf['CachedTitles'] = value; + }, + cb: { + toggle: function() { + var string, _ref; + + _ref = $.hasClass(this, "embedded") ? ['unembed', '(embed)'] : ['embed', '(unembed)'], string = _ref[0], this.textContent = _ref[1]; + $.replace(this.previousElementSibling, Linkify.cb[string](this)); + return $.toggleClass(this, 'embedded'); + }, + embed: function(a) { + var el, style, type; + + el = (type = Linkify.types[a.dataset.key]).el(a); + el.style.cssText = (style = type.style) ? style : "border: 0; width: 640px; height: 390px"; + return el; + }, + unembed: function(a) { + var el; + + el = $.el('a', { + rel: 'nofollow noreferrer', + target: 'blank', + className: 'linkify', + href: a.dataset.href, + innerHTML: a.dataset.title || a.dataset.nodedata + }); + $.addClass(el, a.dataset.key); + return el; + }, + title: function(response, data) { + var embed, key, link, options, service, text, uid; + + key = data[0], uid = data[1], options = data[2], link = data[3], embed = data[4]; + service = Linkify.types[key].title; + switch (response.status) { + case 200: + case 304: + text = "" + (service.text(JSON.parse(response.responseText))); + if (Conf['Embedding']) { + embed.dataset.title = text; + } + break; + case 404: + text = "[" + key + "] Not Found"; + break; + case 403: + text = "[" + key + "] Forbidden or Private"; + break; + default: + text = "[" + key + "] " + this.status + "'d"; + } + if (link) { + return link.textContent = text; + } + } + }, + types: { + audio: { + regExp: /(.*\.(mp3|ogg|wav))$/, + el: function(a) { + return $.el('audio', { + controls: 'controls', + preload: 'auto', + src: a.dataset.uid + }); + } + }, + gist: { + regExp: /.*(?:gist.github.com.*\/)([^\/][^\/]*)$/, + el: function(a) { + var div; + + return div = $.el('iframe', { + src: "http://www.purplegene.com/script?url=https://gist.github.com/" + a.dataset.uid + ".js" + }); + }, + title: { + api: function(uid) { + return "https://api.github.com/gists/" + uid; + }, + text: function(_arg) { + var file, files; + + files = _arg.files; + for (file in files) { + if (files.hasOwnProperty(file)) { + return file; + } + } + } + } + }, + image: { + regExp: /(http|www).*\.(gif|png|jpg|jpeg|bmp)$/, + style: 'border: 0; width: auto; height: auto;', + el: function(a) { + return $.el('div', { + innerHTML: "" + }); + } + }, + InstallGentoo: { + regExp: /.*(?:paste.installgentoo.com\/view\/)([0-9a-z_]+)/, + el: function(a) { + return $.el('iframe', { + src: "http://paste.installgentoo.com/view/embed/" + a.dataset.uid + }); + } + }, + LiveLeak: { + regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/, + el: function(a) { + return $.el('object', { + innerHTML: "" + }); + } + }, + MediaCrush: { + regExp: /.*(?:mediacru.sh\/)([0-9a-z_]+)/i, + style: 'border: 0; width: 640px; height: 480px; resize: both;', + el: function(a) { + return $.el('iframe', { + src: "https://mediacru.sh/" + a.dataset.uid + }); + } + }, + pastebin: { + regExp: /.*(?:pastebin.com\/(?!u\/))([^#\&\?]*).*/, + el: function(a) { + var div; + + return div = $.el('iframe', { + src: "http://pastebin.com/embed_iframe.php?i=" + a.dataset.uid + }); + } + }, + SoundCloud: { + regExp: /.*(?:soundcloud.com\/|snd.sc\/)([^#\&\?]*).*/, + style: 'height: auto; width: 500px; display: inline-block;', + el: function(a) { + var div; + + div = $.el('div', { + className: "soundcloud", + name: "soundcloud" + }); + $.ajax("//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + a.dataset.uid, { + onloadend: function() { + return div.innerHTML = JSON.parse(this.responseText).html; + } + }, false); + return div; + }, + title: { + api: function(uid) { + return "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + uid; + }, + text: function(_) { + return _.title; + } + } + }, + TwitchTV: { + regExp: /.*(?:twitch.tv\/)([^#\&\?]*).*/, + style: "border: none; width: 640px; height: 360px;", + el: function(a) { + var channel, chapter, result, _; + + if (result = /(\w+)\/(?:[a-z]\/)?(\d+)/i.exec(a.dataset.uid)) { + _ = result[0], channel = result[1], chapter = result[2]; + return $.el('object', { + data: 'http://www.twitch.tv/widgets/archive_embed_player.swf', + innerHTML: "\n" + }); + } else { + channel = (/(\w+)/.exec(a.dataset.uid))[0]; + return $.el('object', { + data: "http://www.twitch.tv/widgets/live_embed_player.swf?channel=" + channel, + innerHTML: "\n\n" + }); + } + } + }, + Vocaroo: { + regExp: /.*(?:vocaroo.com\/)([^#\&\?]*).*/, + style: 'border: 0; width: 150px; height: 45px;', + el: function(a) { + return $.el('object', { + innerHTML: "" + }); + } + }, + Vimeo: { + regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/, + el: function(a) { + return $.el('iframe', { + src: "//player.vimeo.com/video/" + a.dataset.uid + "?wmode=opaque" + }); + }, + title: { + api: function(uid) { + return "https://vimeo.com/api/oembed.json?url=http://vimeo.com/" + uid; + }, + text: function(_) { + return _.title; + } + } + }, + Vine: { + regExp: /.*(?:vine.co\/)([^#\&\?]*).*/, + style: 'border: none; width: 500px; height: 500px;', + el: function(a) { + return $.el('iframe', { + src: "https://vine.co/" + a.dataset.uid + "/card" + }); + } + }, + YouTube: { + regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/, + el: function(a) { + return $.el('iframe', { + src: "//www.youtube.com/embed/" + a.dataset.uid + (a.dataset.option ? '#' + a.dataset.option : '') + "?wmode=opaque" + }); + }, + title: { + api: function(uid) { + return "https://gdata.youtube.com/feeds/api/videos/" + uid + "?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode"; + }, + text: function(data) { + return data.entry.title.$t; + } + } + } + } + }; + + 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, hasFile, 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 (hasFile = !!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, + hasFile: hasFile, + 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; + } + if (!isReply) { + type = 'thread'; + } else if (hasFile) { + if (!cooldown.hasFile) { + seconds = Math.max(seconds, 0); + continue; + } + type = 'image'; + } else { + type = '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] === 'images.4chan.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("//api.4chan.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] === 'images.4chan.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("//api.4chan.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.info, nodes); + } + }; + + ArchiveLink = { + init: function() { + var div, entry, type, _i, _len, _ref; + + if (g.VIEW === 'catalog' || !Conf['Menu'] || !Conf['Archive Link']) { + return; + } + div = $.el('div', { + textContent: 'Archive' + }); + entry = { + type: 'post', + el: div, + order: 90, + open: function(_arg) { + var ID, board, thread; + + ID = _arg.ID, thread = _arg.thread, board = _arg.board; + return !!Redirect.to('thread', { + postID: ID, + threadID: thread.ID, + boardID: board.ID + }); + }, + subEntries: [] + }; + _ref = [['Post', 'post'], ['Name', 'name'], ['Tripcode', 'tripcode'], ['E-mail', 'email'], ['Subject', 'subject'], ['Filename', 'filename'], ['Image MD5', 'MD5']]; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + type = _ref[_i]; + entry.subEntries.push(this.createSubEntry(type[0], type[1])); + } + return $.event('AddMenuEntry', entry); + }, + createSubEntry: function(text, type) { + var el, open; + + el = $.el('a', { + textContent: text, + target: '_blank' + }); + open = type === 'post' ? function(_arg) { + var ID, board, thread; + + ID = _arg.ID, thread = _arg.thread, board = _arg.board; + el.href = Redirect.to('thread', { + postID: ID, + threadID: thread.ID, + boardID: board.ID + }); + return true; + } : function(post) { + var value; + + value = Filter[type](post); + if (!value) { + return false; + } + el.href = Redirect.to('search', { + boardID: post.board.ID, + type: type, + value: value, + isSearch: true + }); + return true; + }; + return { + el: el, + open: open + }; + } + }; + + DeleteLink = { + init: function() { + var div, fileEl, fileEntry, postEl, postEntry; + + if (g.VIEW === 'catalog' || !Conf['Menu'] || !Conf['Delete Link']) { + return; + } + div = $.el('div', { + className: 'delete-link', + textContent: 'Delete' + }); + postEl = $.el('a', { + className: 'delete-post', + href: 'javascript:;' + }); + fileEl = $.el('a', { + className: 'delete-file', + href: 'javascript:;' + }); + postEntry = { + el: postEl, + open: function() { + postEl.textContent = 'Post'; + $.on(postEl, 'click', DeleteLink["delete"]); + return true; + } + }; + fileEntry = { + el: fileEl, + open: function(_arg) { + var file; + + file = _arg.file; + if (!file || file.isDead) { + return false; + } + fileEl.textContent = 'File'; + $.on(fileEl, 'click', DeleteLink["delete"]); + return true; + } + }; + return $.event('AddMenuEntry', { + type: 'post', + el: div, + order: 40, + open: function(post) { + var node; + + if (post.isDead) { + return false; + } + DeleteLink.post = post; + node = div.firstChild; + node.textContent = 'Delete'; + DeleteLink.cooldown.start(post, node); + return true; + }, + subEntries: [postEntry, fileEntry] + }); + }, + "delete": function() { + var fileOnly, form, link, post; + + post = DeleteLink.post; + if (DeleteLink.cooldown.counting === post) { + return; + } + $.off(this, 'click', DeleteLink["delete"]); + fileOnly = $.hasClass(this, 'delete-file'); + this.textContent = "Deleting " + (fileOnly ? 'file' : 'post') + "..."; + form = { + mode: 'usrdel', + onlyimgdel: fileOnly, + pwd: QR.persona.getPassword() + }; + form[post.ID] = 'delete'; + link = this; + return $.ajax($.id('delform').action.replace("/" + g.BOARD + "/", "/" + post.board + "/"), { + responseType: 'document', + withCredentials: true, + onload: function() { + return DeleteLink.load(link, post, fileOnly, this.response); + }, + onerror: function() { + return DeleteLink.error(link); + } + }, { + form: $.formData(form) + }); + }, + load: function(link, post, fileOnly, resDoc) { + var msg, s; + + if (resDoc.title === '4chan - Banned') { + s = 'Banned!'; + } else if (msg = resDoc.getElementById('errmsg')) { + s = msg.textContent; + $.on(link, 'click', DeleteLink["delete"]); + } else { + if (resDoc.title === 'Updating index...') { + (post.origin || post).kill(fileOnly); + } + s = 'Deleted'; + } + return link.textContent = s; + }, + error: function(link) { + link.textContent = 'Connection error, please retry.'; + return $.on(link, 'click', DeleteLink["delete"]); + }, + cooldown: { + start: function(post, node) { + var length, seconds, _ref; + + if (!((_ref = QR.db) != null ? _ref.get({ + boardID: post.board.ID, + threadID: post.thread.ID, + postID: post.ID + }) : void 0)) { + delete DeleteLink.cooldown.counting; + return; + } + DeleteLink.cooldown.counting = post; + length = 60; + seconds = Math.ceil((length * $.SECOND - (Date.now() - post.info.date)) / $.SECOND); + return DeleteLink.cooldown.count(post, seconds, length, node); + }, + count: function(post, seconds, length, node) { + if (DeleteLink.cooldown.counting !== post) { + return; + } + if (!((0 <= seconds && seconds <= length))) { + if (DeleteLink.cooldown.counting === post) { + node.textContent = 'Delete'; + delete DeleteLink.cooldown.counting; + } + return; + } + setTimeout(DeleteLink.cooldown.count, 1000, post, seconds - 1, length, node); + return node.textContent = "Delete (" + seconds + ")"; + } + } + }; + + DownloadLink = { + init: function() { + var a; + + if (g.VIEW === 'catalog' || !Conf['Menu'] || !Conf['Download Link']) { + return; + } + a = $.el('a', { + className: 'download-link', + textContent: 'Download file' + }); + return $.event('AddMenuEntry', { + type: 'post', + el: a, + order: 100, + open: function(_arg) { + var file; + + file = _arg.file; + if (!file) { + return false; + } + a.href = file.URL; + a.download = file.name; + return true; + } + }); + } + }; + + Menu = { + init: function() { + if (g.VIEW === 'catalog' || !Conf['Menu']) { + return; + } + this.menu = new UI.Menu('post'); + return Post.callbacks.push({ + name: 'Menu', + cb: this.node + }); + }, + node: function() { + if (this.isClone) { + return $.on($('.menu-button', this.nodes.info), 'click', Menu.toggle); + } else { + return $.add(this.nodes.info, [$.tn('\u00A0'), Menu.makeButton()]); + } + }, + makeButton: (function() { + var a; + + a = $.el('a', { + className: 'menu-button brackets-wrap', + innerHTML: '', + href: 'javascript:;' + }); + return function() { + var button; + + button = a.cloneNode(true); + $.on(button, 'click', Menu.toggle); + return button; + }; + })(), + toggle: function(e) { + var post; + + post = Get.postFromNode(this); + return Menu.menu.toggle(e, this, post); + } + }; + + ReportLink = { + init: function() { + var a; + + if (g.VIEW === 'catalog' || !Conf['Menu'] || !Conf['Report Link']) { + return; + } + a = $.el('a', { + className: 'report-link', + href: 'javascript:;', + textContent: 'Report this post' + }); + $.on(a, 'click', ReportLink.report); + return $.event('AddMenuEntry', { + type: 'post', + el: a, + order: 10, + open: function(post) { + ReportLink.post = post; + return !post.isDead; + } + }); + }, + report: function() { + var id, post, set, url; + + post = ReportLink.post; + url = "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post; + id = Date.now(); + set = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=1,width=685,height=200"; + return window.open(url, id, set); + } + }; + + Favicon = { + init: function() { + return $.ready(function() { + var href; + + Favicon.el = $('link[rel="shortcut icon"]', d.head); + Favicon.el.type = 'image/x-icon'; + href = Favicon.el.href; + Favicon.SFW = /ws\.ico$/.test(href); + Favicon["default"] = href; + return Favicon["switch"](); + }); + }, + "switch": function() { + switch (Conf['favicon']) { + case 'ferongr': + Favicon.unreadDead = 'data:image/gif;base64,R0lGODlhEAAQAPIGAOgLAnMFAL8AAOkMA/+AgP+rqwAAAAAAACH5BAUKAAYALAAAAAAQABAAQANKaLrcDYDBF8YgAQZiswJVp1mDZ4CB+aUmmkYnq4IFphGFGoMwr0MwySSGs62KGZBAIAJZli2gcLhA9V6STTNkjAkCX803LDmVgwkAOw=='; + Favicon.unreadDeadY = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAxUlEQVR42q1TOwrCQBB9s0FRtJI0WoqFtSLYegoP4gVSeJsUHsHSI3iFeIqRXXgwrhlXwYHHhLwPTB7B36abBCV+0pA4DUBQUNZYQptGtW3jtoKyxgoe0yrBCoyZfL/5ioQ3URZOXW9I341l3oo+NXEZiW4CEuIzvPECopED4OaZ3RNmeAm4u+a8Jr5f17VyVoL8fr8qcltzwlyyj2iqcgPOQ9ExkHAITgD75bYBe0A5S4H/P9htuWMF3QXoQpwaKeT+lnsC6JE5I6aq6fEAAAAASUVORK5CYII='; + Favicon.unreadSFW = 'data:image/gif;base64,R0lGODlhEAAQAPIGAADX8QBwfgC2zADY8nnl8qLp8gAAAAAAACH5BAUKAAYALAAAAAAQABAAQANKaLrcDYDBF8YgAQZiswJVp1mDZ4CB+aUmmkYnq4IFphGFGoMwr0MwySSGs62KGZBAIAJZli2gcLhA9V6STTNkjAkCX803LDmVgwkAOw=='; + Favicon.unreadSFWY = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAxElEQVQ4y2NgoBq4/vE/HJOsBiRQUIfA2AzBqQYqUfn00/9FLz+BaQxDCKqBmX7jExijKEDSDJPHrnnbGQhGV4RmOFwdVkNwhQMheYwQxhaIi7b9Z9A3gWAQm2BUoQOgRhgA8o7j1ozLC4LCyAZcx6kZI5qg4kLKqggDFFWxJySsUQVzlb4pwgAJaTRvokcVNgOqOv8zcHBCsL07DgNg8YsczzA5MxtUL+DMD8g0slxI/H8GQ/P/DJKyeKIRpglXZsIiBwBhP5O+VbI/JgAAAABJRU5ErkJggg=='; + Favicon.unreadNSFW = 'data:image/gif;base64,R0lGODlhEAAQAPIGAFT+ACh5AEncAFX/Acz/su7/5gAAAAAAACH5BAUKAAYALAAAAAAQABAAQANKaLrcDYDBF8YgAQZiswJVp1mDZ4CB+aUmmkYnq4IFphGFGoMwr0MwySSGs62KGZBAIAJZli2gcLhA9V6STTNkjAkCX803LDmVgwkAOw=='; + Favicon.unreadNSFWY = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAx0lEQVQ4y2NgoBYI+cfwH4ZJVgMS0KhEYGyG4FQDkzjzf9P/d/+fgWl0QwiqgSkI/c8IxsgKkDXD5LFq9rwDweiK0A2HqcNqCK5wICSPEcLYAtH+AMN/IXMIBrEJRie6OEgjDAC5x3FqxuUFNiEUA67j1IweTTBxBQ1puAG86jgSEraogskJWSBcwCGF5k30qMJmgMFEhv/MXBAs5oLDAFj8IsczTE7UEeECbhU8+QGZRpaTi2b4L2zF8J9TGk80wjThykzY5AAW/2O1C2mIbgAAAABJRU5ErkJggg=='; + break; + case 'xat-': + Favicon.unreadDead = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAxUlEQVR42s1SQQrCMBDsQ8WDIEV6LTT2A4og2Hi0veo7fIAH04e06L1jphIJRtoVLw4MmWZnh2aT6K8Ax82uyFOV6SSJK5Kae74naIZHfhfx5HxaTC8kdeCRhNzms8ayFTaHJuTLzvKpJSE+sVoDhxIoK2qv5vgGDutoz8vfhlJd33w1gDGg5h5r9NArCzA1UNevgPtQQJplmtMeOwI99AYBW73PI8EQqXsvIbjGduAaxwPcQ/oqwF/dUw5r5GfArcLa73gAGxUeHPIycrIAAAAASUVORK5CYII='; + Favicon.unreadDeadY = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA80lEQVQ4y2NgGEzgPwx3TZhYVVJRPik7O30uCIPYIDFkNRia/yNhEH9ieszB5ZlxZ0EYxMamhqAhn1KT3gPxB5I1wxT9r6r8B8T/ccnjDAOwhvaO//9nz/n/f85cMBtdHiMEQYG1DOhfkLP/V1T8A2u+eOn//0uXwAYdiIr6ZyQi8ltOWPCLm5vzVuIMuHT5///Ll8EGWEhJ/YcBfn7+lxgGlJSXT4KFNi4vwAA3N/c7DAN6J02uYsATiBqoBrzCmpCwROMHWDRaIBmANRCxGQBLSOCEBcRcUIzXAGQalpRBbB8iXMCAJPEfh9wRKG1HtRwIAJCmKOzjRex1AAAAAElFTkSuQmCC'; + Favicon.unreadSFW = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAzUlEQVQ4y2NgGEzgPwxP7OuqqigtmZSZnj0XhEFskBiyGgzNetMPwzGIH5M/8WBczfKzIAxiY1ND0JCkDZ/eA/EHkjXDFFUe+f8PiP/jkscZBiDcfvr//9lX/v+fA8QgNro8RgiCAiuuetlZkLMrgDaDNF988///pdcQg0BiIDmQGpBaogy4BDTg8htkAz7jNqAcGFWw0MbnBZAakFoMAyZP6K1CDixcgQhiQ9UyEBONH4iNRqwGwBISSQYg07CkjE0OX35gwKEAnxz5AADUHTv3RAHZ7QAAAABJRU5ErkJggg=='; + Favicon.unreadSFWY = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA/klEQVQ4y2NgGEzgPwxP7OuqqigtmZSZnj0XhEFskBiyGgzNetMPwzGIH5M/8WBczfKzIAxiY1ND0JCkDZ/eA/EHkjXDFFUe+f8PiP/jkscZBiDcfvr//9lX/v+fA8QgNro8RgiCAiuuetlZkLMrgDaDNF988///pdcQg6KmHPgnomL0W1BM7ours9tWogy4BDTg8huIAVLaFv9hgJ+f/yWGAeXAqIKFNi4vwAA3N/c7DAMmT+itQg4s9EBkF5dDNuAV1oSEJRo/wKKRS1ELbgDWQMRmACwhgdhSoXn/GVnZwRivAcg0LCmD2Ly6VgRdwIAk8R+H3BEobUe1HAgA7g5BI+Q2tn8AAAAASUVORK5CYII='; + Favicon.unreadNSFW = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAzklEQVQ4y2NgGEzgPwxP7JpYVVZSMSk9O2suCIPYIDFkNRia084YwzGIHzMx/mDc8vizIAxiY1ND0JCST0nvgfgDyZphinr+V/4D4v+45HGGAQjP+t/+f/X/2UA8B8xGl8cIQVBgxS2LPwtyds//in8gzTf/XwTiS2CDQGIgOZAakFqiDLgF1Hzr/2WEAZ/xGFBWUj4JFtr4vABSA1KLYcDk3slVyIGFKxBBbKhaBmKi8QOx0YjVAFhCIskAZBqWlLHJ4csPDDgU4JMjHwAAAfUvfvahyqUAAAAASUVORK5CYII='; + Favicon.unreadNSFWY = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA/klEQVQ4y2NgGEzgPwxP7JpYVVZSMSk9O2suCIPYIDFkNRia084YwzGIHzMx/mDc8vizIAxiY1ND0JCST0nvgfgDyZphinr+V/4D4v+45HGGAQjP+t/+f/X/2UA8B8xGl8cIQVBgxS2LPwtyds//in8gzTf/XwTiS2CDMg9E/ZMwEvktKCf4xdnNdStRBtwCar71/zLYADkLqf8wwM/P/xLDgLKS8kmw0MblBRjg5uZ+h2HA5N7JVciBhR6IAgocyAa8wpqQsETjB1g0iulwww3AGojYDIAlJBDbqkT2PwsHExjjNQCZhiVlEFvOhp+gCxiQJP7jkDsCpe2olgMB9UE2wvseYUsAAAAASUVORK5CYII='; + break; + case 'Mayhem': + Favicon.unreadDead = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABFklEQVR4AZ2R4WqEMBCEFy1yiJQQ14gcIhIuFBFR+qPQ93+v66QMksrlTwMfkZ2ZZbMKTgVqYIDl3YAbeCM31lJP/Zul4MAEPJjBQGNDLGsz8PQ6aqLAP5PTdd1WlmU09mSKtdTDRgrkzspJPKq6RxMahfj9yhOzQEZwZAwfzrk1ox3MXibIN8hO4MAjeV72CemJGWblnRsOYOdoGw0jebB20BPAwKzUQPlrFhrXFw1Wagu9yuzZwINzVAZCURRL+gRr7Wd8Vtqg4Th/lsUmewyk9WQ/A7NiwJz5VV/GmO+MNjMrFvh/NPDMigHTaeJN09a27ZHRJmalBg54CgfvAGYSLpoHjlmpuAwFdzDy7oGS/qIpM9UPFGg1b1kUlssAAAAASUVORK5CYII='; + Favicon.unreadDeadY = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABR0lEQVR4AYWSQWq0QBCFCw0SRIK0PQ4hiIhEZBhEySLyewUPEMgqR/JIXiDhzz7kKKYePIZajEzDRxfV9dWU3SO6IiVWUsVxT5R75Y4gTmwNnUh4kCulUiuV8sjChDjmKtaUcHgmHsnNrMPh0IVhiMIjKZGzNXDoyhMzF7C89z2KtFGD+FoNXEUKZdgpaPM8P++cDXTtBDca7EyQK8+bXTufYBccuvLAG26UnqN1LCgI4g/lm7zTgSux4vk0J8rnKw3+m1//pBPbBrVyGZVNmiAITviEtm3t+D+2QcJx7GUxlN4594K4ZY75Xzh0JVWqnad6TdP0H+LRNBjHcYNDV5xS32qwaC4my7Lwn6guu5QoomgbdFmWDYhnM8E8zxscuhLzPWtKA/dGqUizrityX9M0YX+DQ1ciXobnP6vgfmTOM7Znnk70B58pPaEvx+epAAAAAElFTkSuQmCC'; + Favicon.unreadSFW = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA/ElEQVR4AZ3RUWqEMBSF4ftQZAhSREQJIiIXpQwi+tSldkFdWPsLhyEE0ocKH2Fyzg1mNJ4KAQ1arTUeeJMH6qwTUJmCHjMcC6KKtbSIylzdXpl18J/k4fdTpUFmPLOOa9bGe+P4+n5RYYfLXuiMsAlXofBxK2QXpvwN/jqg+AY91vR+pStk+apZe0fEhhMXDhUmWXEoO9WNmrWAzvRPq7jnB2jvUGfWTEgPcJzZFTbZk/0Tnh5QI+af6lVGvq/Do2atwVL4VJ+3QrZo1lr4Pw5wzVqDWaV7SUvHrZDNmrWAHq7g0rphkS3LXDMBVqFGhxGT1gGdDFnWaab6BRmXRvbxDmYiAAAAAElFTkSuQmCC'; + Favicon.unreadSFWY = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABQElEQVR4AY2SQUrEQBBFS9CMNFEkhAQdYmiCIUgcZlYGc4VsBcGVF/AuWXme4F7RtXiVWF9+Y9MYtOHRTdX/NZWaEj2RYpQTJeEdK4fKPuA7DjSGXiQkU0qlUqxySmFMEsYsNSU8zEmK4OwdEbmkKCclYoGmolfWCGyenh1O0EJE2gXNWpFC2S0IGrCQ29EbdPCPAmEHmXIxByf8hDAPD71yzAnXypatbSgoAN8Pyju5h4deMUrqJk1z+0uBN+/XX+gxfoFK2QafUJO2aRq//Q+/QIx2wr+Kwq0rusrP/QKf9MTCtbQLf9U1wNvYnz3qug45S68kSvVXgbPbx3nvYPXNOI7cRPWySukK+DcGCvA+urqZ3RmGAbmSXjFK5rpwW8nhWVJP04TYa9/3uO/goVciDiPlZhW8c8ZAHuRSeqIv32FK/GYGL8YAAAAASUVORK5CYII='; + Favicon.unreadNSFW = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA/ElEQVR4AZ3RUWqEMBSF4ftQZAihDCKKiAQJShERQx+6o662e2p/4TCEQF468BEm95yLovFr4PBEq9PjgTd5wBcZp6559AiIWDAq6KXV3aJMUMfDOsTf7Mf/XaFBAvYiE9W16b74/vl8UeBAlKOSmWAzUiXwcavMkrrFE9QXVJ+gx5q9XvUVivmqrr1jxIYLCacCs6y6S8psGNU1hw4Bu4JHuUB3pzJBHZcviLiKV9jkyO4vxHyBx1h+qlcY5b2Wj+raE0vlU33dKrNFXWsR/7EgqmtPBIXuIw+dt8osqGsOPaIGSeeGRbZiFtVxsAYeHSbMOgd0MhSzTp3mD4RaQX4aW3NMAAAAAElFTkSuQmCC'; + Favicon.unreadNSFWY = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABP0lEQVR4AYWS0UqFQBCGhziImNRBRImDmUgiIaF0kWSP4AMEXXXTE/QiPpL3UdR19Crb/PAvLEtyFj5mmfn/cdxd0RUokbJXEsZYCZUd4D72NBG8wkKmlEqtVMoFhTFJmKuoKelBTVIkjbNE5IainJTIeZqaXjkg8fp+Z7GCjiLQbWgOihTKsCFowUZtoNef4HgDf4JMuTbe8n/Br8NDr5zxhBul52i3FBQE+xflmzzTA69ESmpPmubunwZfztc/6IncBrXSe7/QkK5tW3f8H7dBjHH8q6Kwt033V6Hb4JeeWPgsq42rugfYZ92psWscRwMPvZIo9bEGD2+F2YUnBizLwpeoXnYpbQM34kAB9peP58aueZ4NPPRKxPusaRoYG6UizbquyH1O04T4RA+8EvAwUr6sgjFnDuReLaUn+ANygUa7+9SCWgAAAABJRU5ErkJggg=='; + break; + case 'Original': + Favicon.unreadDead = 'data:image/gif;base64,R0lGODlhEAAQAKECAAAAAP8AAP///////yH5BAEKAAMALAAAAAAQABAAAAI/nI95wsqygIRxDgGCBhTrwF3Zxowg5H1cSopS6FrGQ82PU1951ckRmYKJVCXizLRC9kAnT0aIiR6lCFT1cigAADs='; + Favicon.unreadDeadY = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAhElEQVR42q1RwQnAMAjMu5M4guAKXa4j5dUROo5tipSDcrFChUONd0di2m/hEGVOHDyIPufgwAFASDkpoSzmBrkJ2UMyR9LsJ3rvrqo3Rt1YMIMhhNnOxLMnoMFBxHyJAr2IOBFzA8U+6pLBdmEJTA0aMVjpDd6Loks0s5HZNwYx8tfZCZ0kll7ORffZAAAAAElFTkSuQmCC'; + Favicon.unreadSFW = 'data:image/gif;base64,R0lGODlhEAAQAKECAAAAAC6Xw////////yH5BAEKAAMALAAAAAAQABAAAAI/nI95wsqygIRxDgGCBhTrwF3Zxowg5H1cSopS6FrGQ82PU1951ckRmYKJVCXizLRC9kAnT0aIiR6lCFT1cigAADs='; + Favicon.unreadSFWY = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAALVBMVEUAAAAAAAAAAAAAAAABBQcHFx4KISoNLToaVW4oKCgul8M4ODg7OzvBwcH///8uS/CdAAAAA3RSTlMAx9dmesIgAAAAV0lEQVR42m2NWw6AIBAD1eILZO5/XI0UAgm7H9tOsu0yGWAQSOoFijHOxOANGqm/LczpOaXs4gISrPZ+gc2+hO5w2xdwgOjBFUIF+sEJrhUl9JFr+badFwR+BfqlmGUJAAAAAElFTkSuQmCC'; + Favicon.unreadNSFW = 'data:image/gif;base64,R0lGODlhEAAQAKECAAAAAGbMM////////yH5BAEKAAMALAAAAAAQABAAAAI/nI95wsqygIRxDgGCBhTrwF3Zxowg5H1cSopS6FrGQ82PU1951ckRmYKJVCXizLRC9kAnT0aIiR6lCFT1cigAADs='; + Favicon.unreadNSFWY = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAALVBMVEUAAAAAAAAAAAAAAAAECAIQIAgWLAsePA8oKCg4ODg6dB07OztmzDPBwcH///+rsf3XAAAAA3RSTlMAx9dmesIgAAAAV0lEQVR42m2NWw6AIBAD1eIDhbn/cTVSCCTsfmw7ybbLZIBBIKkXKKU0E4M3aKT+tjCn5xiziwuIsNr7BTb7ErrDZV/AAaIHdwgV6AcnuFaU0Eeu5dt2XiUyBjCQ2bIrAAAAAElFTkSuQmCC'; + } + if (Favicon.SFW) { + Favicon.unread = Favicon.unreadSFW; + return Favicon.unreadY = Favicon.unreadSFWY; + } else { + Favicon.unread = Favicon.unreadNSFW; + return Favicon.unreadY = Favicon.unreadNSFWY; + } + }, + dead: 'data:image/gif;base64,R0lGODlhEAAQAKECAAAAAP8AAP///////yH5BAEKAAIALAAAAAAQABAAAAIvlI+pq+D9DAgUoFkPDlbs7lFZKIJOJJ3MyraoB14jFpOcVMpzrnF3OKlZYsMWowAAOw==', + logo: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAgMAAAC+UIlYAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAGlJREFUWMPtlkEKADEIA/tJP9lXLttQto2yHxgDHozTi0ToGK2WKZZ+HAQQMZc+xBwI4EZ+wAC2IfPuSIDOZJrSZQEAX9eVJhhwIuUYAnQe8rhAEMAZlTI2MID9f5Clyh0JeE1V1ZEAvB4qDfwuJTSGRAAAAABJRU5ErkJggg==' + }; + + ThreadExcerpt = { + init: function() { + if (g.VIEW !== 'thread' || !Conf['Thread Excerpt']) { + return; + } + return Thread.callbacks.push({ + name: 'Thread Excerpt', + cb: this.node + }); + }, + node: function() { + return d.title = Get.threadExcerpt(this); + } + }; + + ThreadStats = { + init: function() { + var sc, + _this = this; + + if (g.VIEW !== 'thread' || !Conf['Thread Stats']) { + return; + } + if (Conf['Updater and Stats in Header']) { + this.dialog = sc = $.el('span', { + innerHTML: "0 / 0" + (Conf["Page Count in Stats"] ? " / 0" : ""), + id: 'thread-stats', + title: 'Post Count / File Count' + (Conf["Page Count in Stats"] ? " / Page Count" : "") + }); + $.ready(function() { + return Header.addShortcut(sc); + }); + } else { + this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', "
0 / 0" + (Conf["Page Count in Stats"] ? " / 0" : "") + "
"); + $.ready(function() { + return $.add(d.body, sc); + }); + } + this.postCountEl = $('#post-count', sc); + this.fileCountEl = $('#file-count', sc); + this.pageCountEl = $('#page-count', sc); + return Thread.callbacks.push({ + name: 'Thread Stats', + cb: this.node + }); + }, + node: function() { + var ID, fileCount, post, postCount, _ref; + + postCount = 0; + fileCount = 0; + _ref = this.posts; + for (ID in _ref) { + post = _ref[ID]; + postCount++; + if (post.file) { + fileCount++; + } + } + ThreadStats.thread = this; + ThreadStats.fetchPage(); + ThreadStats.update(postCount, fileCount); + return $.on(d, 'ThreadUpdate', ThreadStats.onUpdate); + }, + onUpdate: function(e) { + var fileCount, postCount, _ref; + + if (e.detail[404]) { + return; + } + _ref = e.detail, postCount = _ref.postCount, fileCount = _ref.fileCount; + return ThreadStats.update(postCount, fileCount); + }, + update: function(postCount, fileCount) { + var fileCountEl, postCountEl, thread; + + thread = ThreadStats.thread, postCountEl = ThreadStats.postCountEl, fileCountEl = ThreadStats.fileCountEl; + postCountEl.textContent = postCount; + fileCountEl.textContent = fileCount; + (thread.postLimit && !thread.isSticky ? $.addClass : $.rmClass)(postCountEl, 'warning'); + return (thread.fileLimit && !thread.isSticky ? $.addClass : $.rmClass)(fileCountEl, 'warning'); + }, + fetchPage: function() { + if (!Conf["Page Count in Stats"]) { + return; + } + if (ThreadStats.thread.isDead) { + ThreadStats.pageCountEl.textContent = 'Dead'; + $.addClass(ThreadStats.pageCountEl, 'warning'); + return; + } + setTimeout(ThreadStats.fetchPage, 2 * $.MINUTE); + return $.ajax("//api.4chan.org/" + ThreadStats.thread.board + "/threads.json", { + onload: ThreadStats.onThreadsLoad + }, { + whenModified: true + }); + }, + onThreadsLoad: function() { + var page, pages, thread, _i, _j, _len, _len1, _ref; + + if (!(Conf["Page Count in Stats"] && this.status === 200)) { + return; + } + pages = JSON.parse(this.response); + for (_i = 0, _len = pages.length; _i < _len; _i++) { + page = pages[_i]; + _ref = page.threads; + for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { + thread = _ref[_j]; + if (thread.no === ThreadStats.thread.ID) { + ThreadStats.pageCountEl.textContent = page.page; + (page.page === pages.length - 1 ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning'); + return; + } + } + } + } + }; + + ThreadUpdater = { + init: function() { + var checked, conf, el, input, name, sc, settings, subEntries, _ref, + _this = this; + + if (g.VIEW !== 'thread' || !Conf['Thread Updater']) { + return; + } + if (Conf['Updater and Stats in Header']) { + this.dialog = sc = $.el('span', { + innerHTML: "", + id: 'updater' + }); + $.ready(function() { + return Header.addShortcut(sc); + }); + } else { + this.dialog = sc = UI.dialog('updater', 'bottom: 0px; left: 0px;', "
"); + $.addClass(doc, 'float'); + $.ready(function() { + $.addClass(doc, 'float'); + return $.add(d.body, sc); + }); + } + this.checkPostCount = 0; + this.timer = $('#update-timer', sc); + this.status = $('#update-status', sc); + this.isUpdating = Conf['Auto Update']; + $.on(this.timer, 'click', ThreadUpdater.update); + $.on(this.status, 'click', ThreadUpdater.update); + subEntries = []; + _ref = Config.updater.checkbox; + for (name in _ref) { + conf = _ref[name]; + checked = Conf[name] ? 'checked' : ''; + el = $.el('label', { + title: "" + conf[1], + innerHTML: " " + name + }); + input = el.firstElementChild; + $.on(input, 'change', $.cb.checked); + if (input.name === 'Scroll BG') { + $.on(input, 'change', ThreadUpdater.cb.scrollBG); + ThreadUpdater.cb.scrollBG(); + } else if (input.name === 'Auto Update') { + $.on(input, 'change', ThreadUpdater.cb.update); + } + subEntries.push({ + el: el + }); + } + settings = $.el('span', { + innerHTML: 'Interval' + }); + $.on(settings, 'click', this.intervalShortcut); + subEntries.push({ + el: settings + }); + $.event('AddMenuEntry', { + type: 'header', + el: $.el('span', { + textContent: 'Updater' + }), + order: 110, + subEntries: subEntries + }); + return Thread.callbacks.push({ + name: 'Thread Updater', + cb: this.node + }); + }, + node: function() { + ThreadUpdater.thread = this; + ThreadUpdater.root = this.OP.nodes.root.parentNode; + ThreadUpdater.lastPost = +ThreadUpdater.root.lastElementChild.id.match(/\d+/)[0]; + ThreadUpdater.outdateCount = 0; + ThreadUpdater.cb.interval.call($.el('input', { + value: Conf['Interval'] + })); + $.on(window, 'online offline', ThreadUpdater.cb.online); + $.on(d, 'QRPostSuccessful', ThreadUpdater.cb.checkpost); + $.on(d, 'visibilitychange', ThreadUpdater.cb.visibility); + return ThreadUpdater.cb.online(); + }, + /* + http://freesound.org/people/pierrecartoons1979/sounds/90112/ + cc-by-nc-3.0 + */ + + beep: 'data:audio/wav;base64,UklGRjQDAABXQVZFZm10IBAAAAABAAEAgD4AAIA+AAABAAgAc21wbDwAAABBAAADAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkYXRhzAIAAGMms8em0tleMV4zIpLVo8nhfSlcPR102Ki+5JspVEkdVtKzs+K1NEhUIT7DwKrcy0g6WygsrM2k1NpiLl0zIY/WpMrjgCdbPhxw2Kq+5Z4qUkkdU9K1s+K5NkVTITzBwqnczko3WikrqM+l1NxlLF0zIIvXpsnjgydZPhxs2ay95aIrUEkdUdC3suK8N0NUIjq+xKrcz002WioppdGm091pK1w0IIjYp8jkhydXPxxq2K295aUrTkoeTs65suK+OUFUIzi7xqrb0VA0WSoootKm0t5tKlo1H4TYqMfkiydWQBxm16+85actTEseS8y7seHAPD9TIza5yKra01QyWSson9On0d5wKVk2H4DYqcfkjidUQB1j1rG75KsvSkseScu8seDCPz1TJDW2yara1FYxWSwnm9Sn0N9zKVg2H33ZqsXkkihSQR1g1bK65K0wSEsfR8i+seDEQTxUJTOzy6rY1VowWC0mmNWoz993KVc3H3rYq8TklSlRQh1d1LS647AyR0wgRMbAsN/GRDpTJTKwzKrX1l4vVy4lldWpzt97KVY4IXbUr8LZljVPRCxhw7W3z6ZISkw1VK+4sMWvXEhSPk6buay9sm5JVkZNiLWqtrJ+TldNTnquqbCwilZXU1BwpKirrpNgWFhTaZmnpquZbFlbVmWOpaOonHZcXlljhaGhpZ1+YWBdYn2cn6GdhmdhYGN3lp2enIttY2Jjco+bnJuOdGZlZXCImJqakHpoZ2Zug5WYmZJ/bGlobX6RlpeSg3BqaW16jZSVkoZ0bGtteImSk5KIeG5tbnaFkJKRinxxbm91gY2QkIt/c3BwdH6Kj4+LgnZxcXR8iI2OjIR5c3J0e4WLjYuFe3VzdHmCioyLhn52dHR5gIiKioeAeHV1eH+GiYqHgXp2dnh9hIiJh4J8eHd4fIKHiIeDfXl4eHyBhoeHhH96eHmA', + cb: { + online: function() { + if (ThreadUpdater.online = navigator.onLine) { + ThreadUpdater.outdateCount = 0; + ThreadUpdater.setInterval(); + ThreadUpdater.set('status', null, null); + } else { + ThreadUpdater.set('timer', null); + ThreadUpdater.set('status', 'Offline', 'warning'); + } + return ThreadUpdater.cb.autoUpdate(); + }, + post: function(e) { + if (!(ThreadUpdater.isUpdating && e.detail.threadID === ThreadUpdater.thread.ID)) { + return; + } + ThreadUpdater.outdateCount = 0; + if (ThreadUpdater.seconds > 2) { + return setTimeout(ThreadUpdater.update, 1000); + } + }, + checkpost: function(e) { + if (!ThreadUpdater.checkPostCount) { + if (e.detail.threadID !== ThreadUpdater.thread.ID) { + return; + } + ThreadUpdater.seconds = 0; + ThreadUpdater.outdateCount = 0; + ThreadUpdater.set('timer', '...'); + } + if (!(g.DEAD || ThreadUpdater.foundPost || ThreadUpdater.checkPostCount >= 5)) { + return setTimeout(ThreadUpdater.update, ++ThreadUpdater.checkPostCount * $.SECOND); + } + ThreadUpdater.setInterval(); + ThreadUpdater.checkPostCount = 0; + delete ThreadUpdater.foundPost; + return delete ThreadUpdater.postID; + }, + visibility: function() { + if (d.hidden) { + return; + } + ThreadUpdater.outdateCount = 0; + if (ThreadUpdater.seconds > ThreadUpdater.interval) { + return ThreadUpdater.set('timer', ThreadUpdater.getInterval()); + } + }, + scrollBG: function() { + return ThreadUpdater.scrollBG = Conf['Scroll BG'] ? function() { + return true; + } : function() { + return !d.hidden; + }; + }, + interval: function() { + var val; + + val = parseInt(this.value, 10); + if (val < 1) { + val = 1; + } + ThreadUpdater.interval = this.value = val; + return $.cb.value.call(this); + }, + load: function(e) { + var klass, req, text, _ref; + + req = ThreadUpdater.req; + switch (req.status) { + case 200: + g.DEAD = false; + ThreadUpdater.parse(JSON.parse(req.response).posts); + ThreadUpdater.set('timer', ThreadUpdater.getInterval()); + break; + case 404: + g.DEAD = true; + ThreadUpdater.set('timer', null); + ThreadUpdater.set('status', '404', 'warning'); + clearTimeout(ThreadUpdater.timeoutID); + ThreadUpdater.thread.kill(); + $.event('ThreadUpdate', { + 404: true, + thread: ThreadUpdater.thread + }); + break; + default: + ThreadUpdater.outdateCount++; + ThreadUpdater.set('timer', ThreadUpdater.getInterval()); + _ref = req.status === 304 ? [null, null] : ["" + req.statusText + " (" + req.status + ")", 'warning'], text = _ref[0], klass = _ref[1]; + ThreadUpdater.set('status', text, klass); + } + if (ThreadUpdater.postID) { + return ThreadUpdater.cb.checkpost(); + } + } + }, + setInterval: function() { + var cur, i, j; + + i = ThreadUpdater.interval; + j = (cur = ThreadUpdater.outdateCount < 10) ? cur : 10; + if (!d.hidden) { + j = j < 7 ? j : 7; + } + ThreadUpdater.seconds = Conf['Optional Increase'] ? (cur = [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j] > i) ? cur : i : i; + ThreadUpdater.set('timer', ThreadUpdater.seconds++); + return ThreadUpdater.count(true); + }, + intervalShortcut: function() { + var settings; + + Settings.open('Advanced'); + settings = $.id('fourchanx-settings'); + return $('input[name=Interval]', settings).focus(); + }, + set: function(name, text, klass) { + var el, node; + + el = ThreadUpdater[name]; + if (node = el.firstChild) { + node.data = text; + } else { + el.textContent = text; + } + if (klass !== void 0) { + return el.className = klass; + } + }, + timeout: function() { + var n; + + ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.timeout, 1000); + if (!(n = --ThreadUpdater.seconds)) { + return ThreadUpdater.update(); + } else if (n <= -60) { + ThreadUpdater.set('status', 'Retrying', null); + return ThreadUpdater.update(); + } else if (n > 0) { + return ThreadUpdater.set('timer', n); + } + }, + update: function() { + var url; + + if (!navigator.onLine) { + return; + } + ThreadUpdater.count(); + if (Conf['Auto Update']) { + ThreadUpdater.set('timer', '...'); + } else { + ThreadUpdater.set('timer', 'Update'); + } + if (ThreadUpdater.req) { + ThreadUpdater.req.abort(); + } + url = "//api.4chan.org/" + ThreadUpdater.thread.board + "/res/" + ThreadUpdater.thread + ".json"; + return ThreadUpdater.req = $.ajax(url, { + onloadend: ThreadUpdater.cb.load + }, { + whenModified: true + }); + }, + updateThreadStatus: function(title, OP) { + var icon, message, root, titleLC; + + titleLC = title.toLowerCase(); + if (ThreadUpdater.thread["is" + title] === !!OP[titleLC]) { + 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]); + }, + 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.thread.postLimit = !!OP.bumplimit; + ThreadUpdater.thread.fileLimit = !!OP.imagelimit; + posts = []; + index = []; + files = []; + count = 0; + for (_i = 0, _len = postObjects.length; _i < _len; _i++) { + postObject = postObjects[_i]; + num = postObject.no; + index.push(num); + if (postObject.fsize) { + files.push(num); + } + if (num <= ThreadUpdater.lastPost) { + continue; + } + count++; + node = Build.postFromObject(postObject, ThreadUpdater.thread.board.ID); + posts.push(new Post(node, ThreadUpdater.thread, ThreadUpdater.thread.board)); + } + deletedPosts = []; + deletedFiles = []; + _ref = ThreadUpdater.thread.posts; + for (ID in _ref) { + post = _ref[ID]; + ID = +ID; + if (post.isDead && index.contains(ID)) { + post.resurrect(); + } else if (!index.contains(ID)) { + post.kill(); + deletedPosts.push(post); + } else if (post.file && !post.file.isDead && !files.contains(ID)) { + post.kill(true); + deletedFiles.push(post); + } + if (ThreadUpdater.postID && ThreadUpdater.postID === ID) { + ThreadUpdater.foundPost = true; + } + } + if (!count) { + ThreadUpdater.set('status', null, null); + ThreadUpdater.outdateCount++; + } else { + ThreadUpdater.set('status', "+" + count, 'new'); + ThreadUpdater.outdateCount = 0; + if (Conf['Beep'] && d.hidden && Unread.posts && !Unread.posts.length) { + if (!ThreadUpdater.audio) { + ThreadUpdater.audio = $.el('audio', { + src: ThreadUpdater.beep + }); + } + ThreadUpdater.audio.play(); + } + ThreadUpdater.lastPost = posts[count - 1].ID; + Main.callbackNodes(Post, posts); + scroll = Conf['Auto Scroll'] && ThreadUpdater.scrollBG() && ThreadUpdater.root.getBoundingClientRect().bottom - doc.clientHeight < 25; + for (key in posts) { + post = posts[key]; + if (!posts.hasOwnProperty(key)) { + continue; + } + root = post.nodes.root; + if (post.cb) { + if (!post.cb.call(post)) { + $.add(ThreadUpdater.root, root); + } + } else { + $.add(ThreadUpdater.root, root); + } + } + if (scroll) { + if (Conf['Bottom Scroll']) { + window.scrollTo(0, d.body.clientHeight); + } else { + if (root) { + Header.scrollToPost(root); + } + } + } + $.queueTask(function() { + var length, threadID; + + threadID = ThreadUpdater.thread.ID; + length = $$('.thread > .postContainer', ThreadUpdater.root).length; + return Fourchan.parseThread(threadID, length - count, length); + }); + } + return $.event('ThreadUpdate', { + 404: false, + thread: ThreadUpdater.thread, + newPosts: posts, + deletedPosts: deletedPosts, + deletedFiles: deletedFiles, + postCount: OP.replies + 1, + fileCount: OP.images + (!!ThreadUpdater.thread.OP.file && !ThreadUpdater.thread.OP.file.isDead) + }); + } + }; + + ThreadWatcher = { + init: function() { + var now, sc; + + if (!Conf['Thread Watcher']) { + return; + } + this.shortcut = sc = $.el('a', { + id: 'watcher-link', + textContent: 'Watcher', + href: 'javascript:;', + className: 'disabled fourchanx-icon icon-eye-open' + }); + this.db = new DataBoard('watchedThreads', this.refresh, true); + this.dialog = UI.dialog('thread-watcher', 'top: 50px; left: 0px;', "
Thread Watcher ×
"); + this.status = $('#watcher-status', this.dialog); + this.list = this.dialog.lastElementChild; + $.on(d, 'QRPostSuccessful', this.cb.post); + if (g.VIEW === 'thread') { + $.on(d, 'ThreadUpdate', this.cb.threadUpdate); + } + $.on(sc, 'click', this.toggleWatcher); + $.on($('.move>.close', ThreadWatcher.dialog), 'click', this.toggleWatcher); + $.on(d, '4chanXInitFinished', this.ready); + if (Conf['Toggleable Thread Watcher']) { + Header.addShortcut(sc); + $.addClass(doc, 'fixed-watcher'); + } + now = Date.now(); + if ((this.db.data.lastChecked || 0) < now - 2 * $.HOUR) { + this.db.data.lastChecked = now; + ThreadWatcher.fetchAllStatus(); + this.db.save(); + } + return Thread.callbacks.push({ + name: 'Thread Watcher', + cb: this.node + }); + }, + node: function() { + var toggler; + + toggler = $.el('img', { + className: 'watch-thread-link' + }); + $.on(toggler, 'click', ThreadWatcher.cb.toggle); + return $.before($('input', this.OP.nodes.post), toggler); + }, + ready: function() { + $.off(d, '4chanXInitFinished', ThreadWatcher.ready); + if (!Main.isThisPageLegit()) { + return; + } + ThreadWatcher.refresh(); + $.add(d.body, ThreadWatcher.dialog); + if (Conf['Toggleable Thread Watcher']) { + ThreadWatcher.dialog.hidden = true; + } + if (!Conf['Auto Watch']) { + return; + } + return $.get('AutoWatch', 0, function(_arg) { + var AutoWatch, thread; + + AutoWatch = _arg.AutoWatch; + if (!(thread = g.BOARD.threads[AutoWatch])) { + return; + } + ThreadWatcher.add(thread); + return $["delete"]('AutoWatch'); + }); + }, + toggleWatcher: function() { + $.toggleClass(ThreadWatcher.shortcut, 'disabled'); + return ThreadWatcher.dialog.hidden = !ThreadWatcher.dialog.hidden; + }, + cb: { + openAll: function() { + var a, _i, _len, _ref; + + if ($.hasClass(this, 'disabled')) { + return; + } + _ref = $$('a[title]', ThreadWatcher.list); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + a = _ref[_i]; + $.open(a.href); + } + return $.event('CloseMenu'); + }, + checkThreads: function() { + if ($.hasClass(this, 'disabled')) { + return; + } + return ThreadWatcher.fetchAllStatus(); + }, + pruneDeads: function() { + var boardID, data, threadID, _i, _len, _ref, _ref1; + + if ($.hasClass(this, 'disabled')) { + return; + } + _ref = ThreadWatcher.getAll(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + _ref1 = _ref[_i], boardID = _ref1.boardID, threadID = _ref1.threadID, data = _ref1.data; + if (!data.isDead) { + continue; + } + delete ThreadWatcher.db.data.boards[boardID][threadID]; + ThreadWatcher.db.deleteIfEmpty({ + boardID: boardID + }); + } + ThreadWatcher.db.save(); + ThreadWatcher.refresh(); + return $.event('CloseMenu'); + }, + toggle: function() { + return ThreadWatcher.toggle(Get.postFromNode(this).thread); + }, + rm: function() { + var boardID, threadID, _ref; + + _ref = this.parentNode.dataset.fullID.split('.'), boardID = _ref[0], threadID = _ref[1]; + return ThreadWatcher.rm(boardID, +threadID); + }, + post: function(e) { + var board, postID, threadID, _ref; + + _ref = e.detail, board = _ref.board, postID = _ref.postID, threadID = _ref.threadID; + if (postID === threadID) { + if (Conf['Auto Watch']) { + return $.set('AutoWatch', threadID); + } + } else if (Conf['Auto Watch Reply']) { + return ThreadWatcher.add(board.threads[threadID]); + } + }, + threadUpdate: function(e) { + var thread; + + thread = e.detail.thread; + if (!(e.detail[404] && ThreadWatcher.db.get({ + boardID: thread.board.ID, + threadID: thread.ID + }))) { + return; + } + return ThreadWatcher.add(thread); + } + }, + fetchCount: { + fetched: 0, + fetching: 0 + }, + fetchAllStatus: function() { + var thread, threads, _i, _len; + + if (!(threads = ThreadWatcher.getAll()).length) { + return; + } + ThreadWatcher.status.textContent = '...'; + for (_i = 0, _len = threads.length; _i < _len; _i++) { + thread = threads[_i]; + ThreadWatcher.fetchStatus(thread); + } + }, + fetchStatus: function(_arg) { + var boardID, data, fetchCount, threadID; + + boardID = _arg.boardID, threadID = _arg.threadID, data = _arg.data; + if (data.isDead) { + return; + } + fetchCount = ThreadWatcher.fetchCount; + fetchCount.fetching++; + return $.ajax("//api.4chan.org/" + boardID + "/res/" + threadID + ".json", { + onloadend: function() { + var status; + + fetchCount.fetched++; + if (fetchCount.fetched === fetchCount.fetching) { + fetchCount.fetched = 0; + fetchCount.fetching = 0; + status = ''; + } else { + status = "" + (Math.round(fetchCount.fetched / fetchCount.fetching * 100)) + "%"; + } + ThreadWatcher.status.textContent = status; + if (this.status !== 404) { + return; + } + if (Conf['Auto Prune']) { + ThreadWatcher.rm(boardID, threadID); + } else { + data.isDead = true; + ThreadWatcher.db.set({ + boardID: boardID, + threadID: threadID, + val: data + }); + } + return ThreadWatcher.refresh(); + } + }, { + type: 'head' + }); + }, + getAll: function() { + var all, boardID, data, threadID, threads, _ref; + + all = []; + _ref = ThreadWatcher.db.data.boards; + for (boardID in _ref) { + threads = _ref[boardID]; + if (Conf['Current Board'] && boardID !== g.BOARD.ID) { + continue; + } + for (threadID in threads) { + data = threads[threadID]; + all.push({ + boardID: boardID, + threadID: threadID, + data: data + }); + } + } + return all; + }, + makeLine: function(boardID, threadID, data) { + var div, fullID, href, link, x; + + x = $.el('a', { + textContent: '×', + href: 'javascript:;' + }); + $.on(x, 'click', ThreadWatcher.cb.rm); + if (data.isDead) { + href = Redirect.to('thread', { + boardID: boardID, + threadID: threadID + }); + } + link = $.el('a', { + href: href || ("/" + boardID + "/res/" + threadID), + textContent: data.excerpt, + title: data.excerpt + }); + div = $.el('div'); + fullID = "" + boardID + "." + threadID; + div.dataset.fullID = fullID; + if (g.VIEW === 'thread' && fullID === ("" + g.BOARD + "." + g.THREADID)) { + $.addClass(div, 'current'); + } + if (data.isDead) { + $.addClass(div, 'dead-thread'); + } + $.add(div, [x, $.tn(' '), link]); + return div; + }, + refresh: function() { + var boardID, data, helper, list, nodes, refresher, thread, threadID, toggler, watched, _i, _j, _len, _len1, _ref, _ref1, _ref2, _ref3; + + nodes = []; + _ref = ThreadWatcher.getAll(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + _ref1 = _ref[_i], boardID = _ref1.boardID, threadID = _ref1.threadID, data = _ref1.data; + nodes.push(ThreadWatcher.makeLine(boardID, threadID, data)); + } + list = ThreadWatcher.list; + $.rmAll(list); + $.add(list, nodes); + _ref2 = g.BOARD.threads; + for (threadID in _ref2) { + thread = _ref2[threadID]; + toggler = $('.watch-thread-link', thread.OP.nodes.post); + watched = ThreadWatcher.db.get({ + boardID: thread.board.ID, + threadID: threadID + }); + helper = watched ? ['addClass', 'Unwatch'] : ['rmClass', 'Watch']; + $[helper[0]](toggler, 'watched'); + toggler.title = "" + helper[1] + " Thread"; + } + _ref3 = ThreadWatcher.menu.refreshers; + for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) { + refresher = _ref3[_j]; + refresher(); + } + }, + toggle: function(thread) { + var boardID, threadID; + + boardID = thread.board.ID; + threadID = thread.ID; + if (ThreadWatcher.db.get({ + boardID: boardID, + threadID: threadID + })) { + return ThreadWatcher.rm(boardID, threadID); + } else { + return ThreadWatcher.add(thread); + } + }, + add: function(thread) { + var boardID, data, threadID; + + data = {}; + boardID = thread.board.ID; + threadID = thread.ID; + if (thread.isDead) { + if (Conf['Auto Prune'] && ThreadWatcher.db.get({ + boardID: boardID, + threadID: threadID + })) { + ThreadWatcher.rm(boardID, threadID); + return; + } + data.isDead = true; + } + data.excerpt = Get.threadExcerpt(thread); + ThreadWatcher.db.set({ + boardID: boardID, + threadID: threadID, + val: data + }); + return ThreadWatcher.refresh(); + }, + rm: function(boardID, threadID) { + ThreadWatcher.db["delete"]({ + boardID: boardID, + threadID: threadID + }); + return ThreadWatcher.refresh(); + }, + convert: function(oldFormat) { + var boardID, data, newFormat, threadID, threads; + + newFormat = {}; + for (boardID in oldFormat) { + threads = oldFormat[boardID]; + for (threadID in threads) { + data = threads[threadID]; + (newFormat[boardID] || (newFormat[boardID] = {}))[threadID] = { + excerpt: data.textContent + }; + } + } + return newFormat; + }, + menu: { + refreshers: [], + init: function() { + var menu; + + if (!Conf['Thread Watcher']) { + return; + } + menu = new UI.Menu('thread watcher'); + $.on($('.menu-button', ThreadWatcher.dialog), 'click', function(e) { + return menu.toggle(e, this, ThreadWatcher); + }); + this.addHeaderMenuEntry(); + return this.addMenuEntries(); + }, + addHeaderMenuEntry: function() { + var entryEl; + + if (g.VIEW !== 'thread') { + return; + } + entryEl = $.el('a', { + href: 'javascript:;' + }); + $.event('AddMenuEntry', { + type: 'header', + el: entryEl, + order: 60 + }); + $.on(entryEl, 'click', function() { + return ThreadWatcher.toggle(g.threads["" + g.BOARD + "." + g.THREADID]); + }); + return this.refreshers.push(function() { + var addClass, rmClass, text, _ref; + + _ref = $('.current', ThreadWatcher.list) ? ['unwatch-thread', 'watch-thread', 'Unwatch thread'] : ['watch-thread', 'unwatch-thread', 'Watch thread'], addClass = _ref[0], rmClass = _ref[1], text = _ref[2]; + $.addClass(entryEl, addClass); + $.rmClass(entryEl, rmClass); + return entryEl.textContent = text; + }); + }, + addMenuEntries: function() { + var cb, conf, entries, entry, name, refresh, subEntries, _i, _len, _ref, _ref1, _results; + + entries = []; + entries.push({ + cb: ThreadWatcher.cb.openAll, + entry: { + type: 'thread watcher', + el: $.el('a', { + textContent: 'Open all threads' + }) + }, + refresh: function() { + return (ThreadWatcher.list.firstElementChild ? $.rmClass : $.addClass)(this.el, 'disabled'); + } + }); + entries.push({ + cb: ThreadWatcher.cb.checkThreads, + entry: { + type: 'thread watcher', + el: $.el('a', { + textContent: 'Check 404\'d threads' + }) + }, + refresh: function() { + return ($('div:not(.dead-thread)', ThreadWatcher.list) ? $.rmClass : $.addClass)(this.el, 'disabled'); + } + }); + entries.push({ + cb: ThreadWatcher.cb.pruneDeads, + entry: { + type: 'thread watcher', + el: $.el('a', { + textContent: 'Prune 404\'d threads' + }) + }, + refresh: function() { + return ($('.dead-thread', ThreadWatcher.list) ? $.rmClass : $.addClass)(this.el, 'disabled'); + } + }); + subEntries = []; + _ref = Config.threadWatcher; + for (name in _ref) { + conf = _ref[name]; + subEntries.push(this.createSubEntry(name, conf[1])); + } + entries.push({ + entry: { + type: 'thread watcher', + el: $.el('span', { + textContent: 'Settings' + }), + subEntries: subEntries + } + }); + _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') { + entry.el.href = 'javascript:;'; + } + if (cb) { + $.on(entry.el, 'click', cb); + } + if (refresh) { + this.refreshers.push(refresh.bind(entry)); + } + _results.push($.event('AddMenuEntry', entry)); + } + return _results; + }, + createSubEntry: function(name, desc) { + var entry, input; + + entry = { + type: 'thread watcher', + el: $.el('label', { + innerHTML: " " + name, + title: desc + }) + }; + input = entry.el.firstElementChild; + input.checked = Conf[name]; + $.on(input, 'change', $.cb.checked); + if (name === 'Current Board') { + $.on(input, 'change', ThreadWatcher.refresh); + } + return entry; + } + } + }; + + Unread = { + init: function() { + if (g.VIEW !== 'thread' || !Conf['Unread Count'] && !Conf['Unread Favicon'] && !Conf['Desktop Notifications']) { + return; + } + this.db = new DataBoard('lastReadPosts', this.sync); + this.hr = $.el('hr', { + id: 'unread-line' + }); + this.posts = []; + this.postsQuotingYou = []; + return Thread.callbacks.push({ + name: 'Unread', + cb: this.node + }); + }, + node: function() { + Unread.thread = this; + Unread.title = d.title; + Unread.lastReadPost = Unread.db.get({ + boardID: this.board.ID, + threadID: this.ID, + defaultValue: 0 + }); + $.on(d, '4chanXInitFinished', Unread.ready); + $.on(d, 'ThreadUpdate', Unread.onUpdate); + $.on(d, 'scroll visibilitychange', Unread.read); + if (Conf['Unread Line']) { + return $.on(d, 'visibilitychange', Unread.setLine); + } + }, + ready: function() { + var ID, post, posts, _ref; + + $.off(d, '4chanXInitFinished', Unread.ready); + posts = []; + _ref = Unread.thread.posts; + for (ID in _ref) { + post = _ref[ID]; + if (post.isReply) { + posts.push(post); + } + } + Unread.addPosts(posts); + return Unread.scroll(); + }, + scroll: function() { + var checkPosition, hash, onload, post, posts, root; + + if (!Conf['Scroll to Last Read Post']) { + return; + } + if ((hash = location.hash.match(/\d+/)) && hash[0] in Unread.thread.posts) { + return; + } + if (post = Unread.posts[0]) { + while (root = $.x('preceding-sibling::div[contains(@class,"replyContainer")][1]', post.nodes.root)) { + if (!(post = Get.postFromRoot(root)).isHidden) { + break; + } + } + if (!root) { + return; + } + onload = function() { + if (checkPosition(root)) { + return root.scrollIntoView(false); + } + }; + } 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); + }, + sync: function() { + var lastReadPost; + + lastReadPost = Unread.db.get({ + boardID: Unread.thread.board.ID, + threadID: Unread.thread.ID, + defaultValue: 0 + }); + if (!(Unread.lastReadPost < lastReadPost)) { + return; + } + Unread.lastReadPost = lastReadPost; + Unread.readArray(Unread.posts); + Unread.readArray(Unread.postsQuotingYou); + if (Conf['Unread Line']) { + Unread.setLine(); + } + return Unread.update(); + }, + addPosts: function(posts) { + var ID, data, post, _i, _len; + + for (_i = 0, _len = posts.length; _i < _len; _i++) { + post = posts[_i]; + ID = post.ID; + if (ID <= Unread.lastReadPost || post.isHidden) { + continue; + } + if (QR.db) { + data = { + boardID: post.board.ID, + threadID: post.thread.ID, + postID: post.ID + }; + if (QR.db.get(data)) { + continue; + } + } + Unread.posts.push(post); + Unread.addPostQuotingYou(post); + } + if (Conf['Unread Line']) { + Unread.setLine(posts.contains(Unread.posts[0])); + } + Unread.read(); + return Unread.update(); + }, + addPostQuotingYou: function(post) { + var quotelink, _i, _len, _ref; + + if (!QR.db) { + return; + } + _ref = post.nodes.quotelinks; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + quotelink = _ref[_i]; + if (!(QR.db.get(Get.postDataFromLink(quotelink)))) { + continue; + } + Unread.postsQuotingYou.push(post); + Unread.openNotification(post); + return; + } + }, + openNotification: function(post) { + var name, notif; + + if (!Header.areNotificationsEnabled) { + return; + } + name = Conf['Anonymize'] ? 'Anonymous' : $('.nameBlock', post.nodes.info).textContent.trim(); + notif = new Notification("" + name + " replied to you", { + body: post.info.comment, + icon: Favicon.logo + }); + notif.onclick = function() { + Header.scrollToPost(post.nodes.root); + return window.focus(); + }; + return notif.onshow = function() { + return setTimeout(function() { + return notif.close(); + }, 7 * $.SECOND); + }; + }, + onUpdate: function(e) { + if (e.detail[404]) { + return Unread.update(); + } else { + return Unread.addPosts(e.detail.newPosts); + } + }, + readSinglePost: function(post) { + var i; + + if ((i = Unread.posts.indexOf(post)) === -1) { + return; + } + Unread.posts.splice(i, 1); + if (i === 0) { + Unread.lastReadPost = post.ID; + Unread.saveLastReadPost(); + } + if ((i = Unread.postsQuotingYou.indexOf(post)) !== -1) { + Unread.postsQuotingYou.splice(i, 1); + } + return Unread.update(); + }, + readArray: function(arr) { + var i, post, _i, _len; + + for (i = _i = 0, _len = arr.length; _i < _len; i = ++_i) { + post = arr[i]; + if (post.ID > Unread.lastReadPost) { + break; + } + } + return arr.splice(0, i); + }, + read: $.debounce(50, function(e) { + var ID, height, i, 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; + } + } + i++; + } + if (i && !Conf['Quote Threading']) { + posts.splice(0, i); + } + if (!ID) { + return; + } + if (Unread.lastReadPost < ID || !Unread.lastReadPost) { + Unread.lastReadPost = ID; + } + Unread.saveLastReadPost(); + Unread.readArray(Unread.postsQuotingYou); + if (e) { + return Unread.update(); + } + }), + saveLastReadPost: $.debounce(2 * $.SECOND, function() { + if (Unread.thread.isDead) { + return; + } + return Unread.db.set({ + boardID: Unread.thread.board.ID, + threadID: Unread.thread.ID, + val: Unread.lastReadPost + }); + }), + setLine: function(force) { + var post; + + if (!(d.hidden || force === true)) { + return; + } + if (!(post = Unread.posts[0])) { + return $.rm(Unread.hr); + } + if ($.x('preceding-sibling::div[contains(@class,"replyContainer")]', post.nodes.root)) { + return $.before(post.nodes.root, Unread.hr); + } + }, + update: function() { + var count; + + count = Unread.posts.length; + if (Conf['Unread Count']) { + d.title = "" + (Conf['Quoted Title'] && Unread.postsQuotingYou.length ? '(!) ' : '') + (count || !Conf['Hide Unread Count at (0)'] ? "(" + count + ") " : '') + (g.DEAD ? "/" + g.BOARD + "/ - 404" : "" + Unread.title); + } + if (!Conf['Unread Favicon']) { + return; + } + Favicon.el.href = g.DEAD ? Unread.postsQuotingYou[0] ? Favicon.unreadDeadY : count ? Favicon.unreadDead : Favicon.dead : count ? Unread.postsQuotingYou[0] ? Favicon.unreadY : Favicon.unread : Favicon["default"]; + return $.add(d.head, Favicon.el); + } + }; + + Redirect = { + data: { + thread: {}, + post: {}, + file: {} + }, + init: function() { + var archive, boardID, boards, data, id, name, type, _i, _len, _ref, _ref1, _ref2; + + _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; + } + } + } + _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; + } + if (!(boardID in Redirect.data.post || archive.software !== 'foolfuuka')) { + Redirect.data.post[boardID] = archive; + } + if (!(boardID in Redirect.data.file || !archive.files.contains(boardID))) { + Redirect.data.file[boardID] = archive; + } + } + } + }, + archives: { + '4plebs': { + domain: 'archive.4plebs.org', + http: true, + https: true, + software: 'foolfuuka', + boards: ['hr', 'tg', 'tv', 'x'], + files: ['hr', 'tg', 'tv', 'x'] + }, + 'fap archive': { + domain: 'fuuka.worldathleticproject.org', + http: true, + https: true, + software: 'foolfuuka', + boards: ['b', 'e', 'h', 'hc', 'p', 's', 'soc', 'sp', 'u'], + files: ['b', 'e', 'h', 'hc', 'p', 's', 'soc', 'sp', 'u'] + }, + 'Foolz': { + domain: 'archive.foolz.us', + http: false, + https: true, + software: 'foolfuuka', + boards: ['a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'vp', 'vr', 'wsg'], + files: ['a', 'gd', 'jp', 'm', 'q', 'tg', 'vg', 'vp', 'vr', 'wsg'] + }, + 'Foolz Beta': { + domain: 'beta.foolz.us', + http: true, + https: true, + withCredentials: true, + software: 'foolfuuka', + boards: ['a', 'co', 'd', 'gd', 'h', 'jp', 'm', 'mlp', 'q', 'sp', 'tg', 'tv', 'u', 'v', 'vg', 'vp', 'vr', 'wsg'], + files: ['a', 'd', 'gd', 'h', 'jp', 'm', 'q', 'tg', 'u', 'vg', 'vp', 'vr', 'wsg'] + }, + 'Heinessen': { + domain: 'archive.heinessen.com', + http: true, + software: 'fuuka', + boards: ['an', 'fit', 'k', 'mlp', 'r9k', 'toy'], + files: ['an', 'k', 'toy'] + }, + 'Install Gentoo': { + domain: 'archive.installgentoo.net', + http: false, + https: true, + software: 'fuuka', + boards: ['diy', 'g', 'sci'], + files: [] + }, + 'NSFW Foolz': { + domain: 'nsfw.foolz.us', + http: false, + https: true, + software: 'foolfuuka', + boards: ['u'], + files: ['u'] + }, + 'Nyafuu': { + domain: 'archive.nyafuu.org', + http: true, + https: true, + software: 'foolfuuka', + boards: ['c', 'w', 'wg'], + files: ['c', 'w', 'wg'] + }, + 'Rebecca Black Tech': { + domain: 'rbt.asia', + http: true, + https: true, + software: 'fuuka', + boards: ['cgl', 'g', 'mu', 'w'], + files: ['cgl', 'g', 'mu', 'w'] + }, + 'The Dark Cave': { + domain: 'archive.thedarkcave.org', + http: true, + https: true, + software: 'foolfuuka', + boards: ['c', 'int', 'out', 'po'], + files: ['c', 'po'] + }, + 'warosu': { + domain: 'fuuka.warosu.org', + http: true, + https: true, + software: 'fuuka', + boards: ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 'tg', 'vr'], + files: ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 'tg', 'vr'] + } + }, + to: function(dest, data) { + var archive; + + archive = (dest === 'search' ? Redirect.data.thread : Redirect.data[dest])[data.boardID]; + if (!archive) { + return ''; + } + return Redirect[dest](archive, data); + }, + protocol: function(archive) { + var protocol; + + protocol = location.protocol; + if (!archive[protocol.slice(0, -1)]) { + protocol = protocol === 'https:' ? 'http:' : 'https:'; + } + return "" + protocol + "//"; + }, + thread: function(archive, _arg) { + var boardID, path, postID, threadID; + + boardID = _arg.boardID, threadID = _arg.threadID, postID = _arg.postID; + path = threadID ? "" + boardID + "/thread/" + threadID : "" + boardID + "/post/" + postID; + if (archive.software === 'foolfuuka') { + path += '/'; + } + if (threadID && postID) { + path += archive.software === 'foolfuuka' ? "#" + postID : "#p" + postID; + } + return "" + (Redirect.protocol(archive)) + archive.domain + "/" + path; + }, + post: function(archive, _arg) { + var URL, boardID, postID; + + boardID = _arg.boardID, postID = _arg.postID; + URL = new String("" + (Redirect.protocol(archive)) + archive.domain + "/_/api/chan/post/?board=" + boardID + "&num=" + postID); + URL.archive = archive; + return URL; + }, + file: function(archive, _arg) { + var boardID, filename; + + boardID = _arg.boardID, filename = _arg.filename; + return "" + (Redirect.protocol(archive)) + archive.domain + "/" + boardID + "/full_image/" + filename; + }, + search: function(archive, _arg) { + var boardID, path, type, value; + + boardID = _arg.boardID, type = _arg.type, value = _arg.value; + type = type === 'name' ? 'username' : type === 'MD5' ? 'image' : type; + value = encodeURIComponent(value); + path = archive.software === 'foolfuuka' ? "" + boardID + "/search/" + type + "/" + value : "" + boardID + "/?task=search2&search_" + (type === 'image' ? 'media_hash' : type) + "=" + value; + return "" + (Redirect.protocol(archive)) + archive.domain + "/" + path; + } + }; + + PSAHiding = { + init: function() { + if (!Conf['Announcement Hiding']) { + return; + } + $.addClass(doc, 'hide-announcement'); + return $.on(d, '4chanXInitFinished', this.setup); + }, + setup: function() { + var btn, entry, psa; + + $.off(d, '4chanXInitFinished', PSAHiding.setup); + if (!(psa = $.id('globalMessage'))) { + $.rmClass(doc, 'hide-announcement'); + return; + } + entry = { + type: 'header', + el: $.el('a', { + textContent: 'Show announcement', + className: 'show-announcement', + href: 'javascript:;' + }), + order: 50, + open: function() { + return psa.hidden; + } + }; + $.event('AddMenuEntry', entry); + $.on(entry.el, 'click', PSAHiding.toggle); + PSAHiding.btn = btn = $.el('a', { + innerHTML: ' - ', + title: 'Hide announcement.', + className: 'hide-announcement', + href: 'javascript:;' + }); + $.on(btn, 'click', PSAHiding.toggle); + $.get('hiddenPSA', 0, function(_arg) { + var hiddenPSA; + + hiddenPSA = _arg.hiddenPSA; + PSAHiding.sync(hiddenPSA); + $.before(psa, btn); + return $.rmClass(doc, 'hide-announcement'); + }); + return $.sync('hiddenPSA', PSAHiding.sync); + }, + toggle: function(e) { + var UTC; + + if ($.hasClass(this, 'hide-announcement')) { + UTC = +$.id('globalMessage').dataset.utc; + $.set('hiddenPSA', UTC); + } else { + $.event('CloseMenu'); + $["delete"]('hiddenPSA'); + } + return PSAHiding.sync(UTC); + }, + sync: function(UTC) { + var hr, psa; + + psa = $.id('globalMessage'); + psa.hidden = PSAHiding.btn.hidden = UTC && UTC >= +psa.dataset.utc ? true : false; + if ((hr = psa.nextElementSibling) && hr.nodeName === 'HR') { + return hr.hidden = psa.hidden; + } + } + }; + + Banner = { + init: function() { + return $.asap((function() { + return d.body; + }), function() { + return $.asap((function() { + return $('.abovePostForm'); + }), Banner.ready); + }); + }, + ready: function() { + var banner, child, children, i; + + banner = $(".boardBanner"); + children = banner.children; + i = 0; + while (child = children[i++]) { + if (i === 1) { + child.id = "Banner"; + child.title = "Click to change"; + $.on(child, 'click', Banner.cb.toggle); + continue; + } + if (Conf['Custom Board Titles']) { + Banner.custom(child).title = "Ctrl+click to edit board " + (i === 3 ? 'sub' : '') + "title"; + child.spellcheck = false; + } + } + }, + cb: { + toggle: (function() { + var types; + + types = { + jpg: 227, + png: 270, + gif: 253 + }; + return function() { + var num, type; + + type = Object.keys(types)[Math.floor(3 * Math.random())]; + num = Math.floor(types[type] * Math.random()); + return this.src = "//static.4chan.org/image/title/" + num + "." + type; + }; + })(), + click: function(e) { + if (e.ctrlKey) { + this.contentEditable = true; + return this.focus(); + } + }, + keydown: function(e) { + e.stopPropagation(); + if (!e.shiftKey && e.keyCode === 13) { + return this.blur(); + } + }, + focus: function() { + var items, string, string2; + + this.textContent = this.innerHTML; + string = "" + g.BOARD + "." + this.className; + string2 = "" + string + ".orig"; + items = { + title: this.innerHTML + }; + items[string] = ''; + items[string2] = false; + $.get(items, function(items) { + if (!(items[string2] && items.title === items[string])) { + return $.set(string2, items.title); + } + }); + }, + blur: function() { + this.innerHTML = this.textContent; + this.contentEditable = false; + return $.set("" + g.BOARD + "." + this.className, this.textContent); + } + }, + custom: function(child) { + var cachedTest, string; + + cachedTest = child.innerHTML; + string = "" + g.BOARD + "." + child.className; + $.on(child, 'click keydown focus blur', function(e) { + return Banner.cb[e.type].apply(this, [e]); + }); + $.get(string, cachedTest, function(item) { + var string2, title; + + if (!(title = item[string])) { + return; + } + if (Conf['Persistent Custom Board Titles']) { + return child.innerHTML = title; + } + string2 = "" + string + ".orig"; + return $.get(string2, cachedTest, function(itemb) { + if (cachedTest === itemb[string2]) { + return child.innerHTML = title; + } else { + $.set(string, cachedTest); + return $.set(string2, cachedTest); + } + }); + }); + return child; + } + }; + + CatalogLinks = { + init: function() { + var el, input; + + if (!Conf['Catalog Links']) { + return; + } + el = $.el('label', { + id: 'toggleCatalog', + href: 'javascript:;', + innerHTML: " Catalog Links", + title: "Turn catalog links " + (Conf['Header catalog links'] ? 'off' : 'on') + "." + }); + input = $('input', el); + $.on(input, 'change', this.toggle); + $.sync('Header catalog links', CatalogLinks.set); + $.event('AddMenuEntry', { + type: 'header', + el: el, + order: 95 + }); + return $.on(d, '4chanXInitFinished', function() { + return CatalogLinks.set(Conf['Header catalog links']); + }); + }, + toggle: function() { + var useCatalog; + + $.event('CloseMenu'); + $.set('Header catalog links', useCatalog = this.checked); + return CatalogLinks.set(useCatalog); + }, + set: function(useCatalog) { + var a, board, path, _i, _len, _ref; + + path = useCatalog ? 'catalog' : ''; + _ref = $$("#board-list a[href*=\"boards.4chan.org\"]:not(.catalog),\n#boardNavDesktopFoot a[href*=\"boards.4chan.org\"]"); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + a = _ref[_i]; + board = a.pathname.split('/')[1]; + if (['f', 'status', '4chan'].contains(board) || !board) { + continue; + } + if (Conf['External Catalog']) { + a.href = useCatalog ? CatalogLinks.external(board) : "//boards.4chan.org/" + board + "/"; + } else { + a.pathname = "/" + board + "/" + path; + } + } + return this.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 : "//boards.4chan.org/" + board + "/catalog"); + } + }; + + IDColor = { + init: function() { + if (g.VIEW === 'catalog' || !Conf['Color User IDs']) { + return; + } + this.ids = {}; + return Post.callbacks.push({ + name: 'Color User IDs', + cb: this.node + }); + }, + node: function() { + var rgb, span, style, uid; + + if (this.isClone || !(uid = this.info.uniqueID)) { + return; + } + span = $('.hand', this.nodes.uniqueID); + if (!(span && span.nodeName === 'SPAN')) { + return; + } + rgb = IDColor.compute(uid); + style = span.style; + style.color = rgb[3]; + style.backgroundColor = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")"; + $.addClass(span, 'painted'); + return span.title = 'Highlight posts by this ID'; + }, + compute: function(uid) { + var hash, rgb; + + if (IDColor.ids[uid]) { + return IDColor.ids[uid]; + } + hash = IDColor.hash(uid); + rgb = [(hash >> 24) & 0xFF, (hash >> 16) & 0xFF, (hash >> 8) & 0xFF]; + rgb[3] = (rgb[0] * 0.299 + rgb[1] * 0.587 + rgb[2] * 0.114) > 125 ? '#000' : '#fff'; + return this.ids[uid] = rgb; + }, + hash: function(uid) { + var i, msg; + + msg = 0; + i = 0; + while (i < 8) { + msg = (msg << 5) - msg + uid.charCodeAt(i++); + } + return msg; + } + }; + + CustomCSS = { + init: function() { + if (!Conf['Custom CSS']) { + return; + } + return this.addStyle(); + }, + addStyle: function() { + return this.style = $.addStyle(Conf['usercss']); + }, + rmStyle: function() { + if (this.style) { + $.rm(this.style); + return delete this.style; + } + }, + update: function() { + if (!this.style) { + this.addStyle(); + } + return this.style.textContent = Conf['usercss']; + } + }; + + Dice = { + init: function() { + if (g.BOARD.ID !== 'tg' || g.VIEW === 'catalog' || !Conf['Show Dice Roll']) { + return; + } + return Post.callbacks.push({ + name: 'Show Dice Roll', + cb: this.node + }); + }, + node: function() { + var dicestats, roll, _ref; + + if (this.isClone || !(dicestats = (_ref = this.info.email) != null ? _ref.match(/dice[+\s](\d+)d(\d+)/) : void 0)) { + return; + } + roll = $('b', this.nodes.comment).firstChild; + return roll.data = "Rolled " + dicestats[1] + "d" + dicestats[2] + ": " + (roll.data.slice(7)); + } + }; + + Emoji = { + init: function() { + var css, icon, name, pos, _ref; + + if (!Conf['Emoji']) { + return; + } + pos = Conf['emojiPos']; + css = ["a.useremail[href]:last-of-type::" + pos + " {\n vertical-align: top;\n margin-" + (pos === "before" ? "right" : "left") + ": 5px;\n}\n"]; + this.icons["PlanNine"] = Emoji.icons["Plan9"]; + this.icons['Sage'] = Emoji.sage[Conf['sageEmoji']]; + _ref = this.icons; + for (name in _ref) { + icon = _ref[name]; + if (!this.icons.hasOwnProperty(name)) { + continue; + } + css.push("a.useremail[href*='" + name + "']:last-of-type::" + pos + ",\na.useremail[href*='" + (name.toLowerCase()) + "']:last-of-type::" + pos + ",\na.useremail[href*='" + (name.toUpperCase()) + "']:last-of-type::" + pos + " {\n content: url('data:image/png;base64," + icon + "');\n}\n"); + } + return $.addStyle(css.join(""), 'emoji'); + }, + sage: { + '4chan SS': 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAANCAYAAACZ3F9/AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAa9JREFUKFOdkt0rg2EUwM95b2zlL0CRRLngksznXrJsNtYW1tjYhM3mY6+IXZAbikhTKJp8XZAp81UmWYhIRHHhUi60e7s6ntdCa2449es8PfU7z+k5B6AbyuE/wQlc4BcO2d06unAUBCgFE0hianOd3NHIcy8NPwrUf9NBPZcOEi7ayXZiea/1V7+ljaXeYAfOgg2So2TOwQWGnwQafOgi962TnMFmatozUeNu4yetASspVvgXiUvii5K5Nm6z56ol3Hdtpy+cwSYy+HRUt1nLsoEato0kXyh6wTac+24brThWv6MNOYNW9prlG/uxmbRrFaT0VrCspZoNPSUNJNyCBcoiLZuhLH0o9U6UrAfGKCz7RlLM81Q8XUwqr4oKPLIQmnA8IupBigacVy7yrya/2JouhryJHJJNykg+UxLGOtz6+SQNpEiMcduls4Wvoli9WklVKz+ol5SU4U6ngql8Qj2eRI+GyajBhSRH4r3cUxhSeRVhsYBmWUWiyM+UMDmDUI2nsfuSC1I27nLgYZJlP8jhjJ3PY8iE+L8tWx4kQC6MQA5b1D9HNiRCFhx8AF/e2qh92VnKAAAAAElFTkSuQmCC', + 'appchan': 'iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAABa1BMVEUAAACqrKiCgYIAAAAAAAAAAACHmX5pgl5NUEx/hnx4hXRSUVMiIyKwrbFzn19SbkZ1d3OvtqtpaWhcX1ooMyRsd2aWkZddkEV8vWGcpZl+kHd7jHNdYFuRmI4bHRthaV5WhUFsfGZReUBFZjdJazpGVUBnamYfHB9TeUMzSSpHgS1cY1k1NDUyOC8yWiFywVBoh1lDSEAZHBpucW0ICQgUHhBjfFhCRUA+QTtEQUUBAQFyo1praWspKigWFRZHU0F6j3E9Oz5VWFN0j2hncWONk4sAAABASDxJWkJKTUgAAAAvNC0fJR0DAwMAAAA9QzoWGhQAAAA8YytvrFOJsnlqyT9oqExqtkdrsExpsUsqQx9rpVJDbzBBbi5utk9jiFRuk11iqUR64k5Wf0JIZTpadk5om1BkyjmF1GRNY0FheFdXpjVXhz86XSp2yFJwslR3w1NbxitbtDWW5nNnilhFXTtYqDRwp1dSijiJ7H99AAAAUnRSTlMAJTgNGQml71ypu3cPEN/RDh8HBbOwQN7wVg4CAQZ28vs9EDluXjo58Ge8xwMy0P3+rV8cT73sawEdTv63NAa3rQwo4cUdAl3hWQSWvS8qqYsjEDiCzAAAAIVJREFUeNpFx7GKAQAYAOD/A7GbZVAWZTBZFGQw6LyCF/MIkiTdcOmWSzYbJVE2u1KX0J1v+8QDv/EkyS0yXF/NgeEILiHfyc74mICTQltqYXBeAWU9HGxU09YqqEvAElGjyZYjPyLqitjzHSEiGkrsfMWr0VLe+oy/djGP//YwfbeP8bN3Or0bkqEVblAAAAAASUVORK5CYII=' + }, + icons: { + 'Plan9': 'iVBORw0KGgoAAAANSUhEUgAAAAwAAAAPCAYAAAGn5h7fAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AoYAzE15J1s7QAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAACAElEQVQoz3WSz4sSARTHvzMjygpqYg3+KIhkE83DKtKlf0C9SrTRuZNEx0VowU6CuSeJNlwwpEO2kJ6SQBiIUAzFjRDF4wrjKosnGx3HmdehFDfpe/2+z/s++D5gU7IsEwRByICIiAEAIiIAYAFAXsjYVr/fLxMRNVvN+prJ5/OA3+/XERFNf02JyeVyDx0OxyvLNQsnimLKfcf9KRQKXQAAnE6nlf5qMpnQycnbP/kAoKoqsSwLAJhOp+AAwOv1otvtpqxWq73dbt/r9XqvEQ6HUalUEvF4XLd5IpvNZqlerzd5nlf6/f6tTCZjBACk0+nb+XxeW4UrikLJZPImAGA0Gq0NIqJyuSyyANDr9Q5Wu1utFvR6/SULAI1G4+vK8Pv90DTtGwsAJpPpaGUYDAZ0Op3PHAAEg8H3tVqtbrtu21sqyxuRSOQJk0ql9IvF4r7b7f7pcrlejkaj57IsH58Pzp8dvjhc/lsBk0gkbLFYrFqtVvd27+4qOk733ePxPDCbzVBVFfP5fCiK4rvhxfDN/qP9wSasGwwGMv1HiqJQsVg8ZlfTHMepkiR1t05gGJBGmM/nMBqNj9nN9kql0lNN064ARISzH2cQBAGz2ewLu2na7XYLwzBbvxYIBBCNRrFj3BmsAZ/PZ+J5/kOhUIAkSVeA8XiMZqt5efrx9OA3GfcgvyVno9cAAAAASUVORK5CYII=', + 'Neko': 'iVBORw0KGgoAAAANSUhEUgAAABMAAAARCAMAAAAIRmf1AAACoFBMVEUAAABnUFZoUVddU1T6+PvFwLzn4eFXVlT/+vZpZGCgm5dKU1Cfnpz//flbWljr5uLp5OCalpNZWFb//f3r6+n28ff9+PRaVVH59Pr//vr38vj57/Dp7eyjn5zq8O5aVVJbYV9nVFhjUFRiWFlZVlFgZGOboJzm5uZhamfz9/bt8fDw6+drb26bl5j/8/lkX1z06uldWFS5r61UT0tfWlbDwr3Ew76moqNRTU7Mx8P75OpeY19pWl1XW1qzr6x5eHaLiojv7+1UT0xIU0uzqadVS0nV0MxkZGT5+PPk497///ra29Xq5eFtY2H28e2hnJignJlUUE1dXV2vrqxkY2FkYF/m3d5vZmfDuruhl5aZlJHx8O75+PZWVVP29vT/9fTj3trv6ubh5eRdXFqTkpBOTUtqZmX88/RMQ0T78vPEvr7HwcHDwsDq6ef///3Gx8H++fXEv7tZWVedmZZXXVudnJp0c3FZU1f79fnb1dlXUVVjXWFrZmy8t7359/qLj455e3q4s69vamZjX1zy4+avpaReWFz/+f1NR0vu6Ozp4+f48/lnYmi8ur3Iw7/69fHz7+xbV1SZmJZVUk1ZV1zq5ez++f/c196uqbDn4uj9+P7z7vRVVVXt6ORiXl/OycXHw8CPi4ihoJ5aWF3/+v/k3+axrLOsp67LzMZYU1m2sq9dWF5WUU1WUk/Au7eYlJGqpqObmphYVV749f7p5Or38fPu6OpiXFz38fH79vLz7urv6+hhYF5cWWKal6D//f/Z09Xg29exraqbl5RqaW6kpKTq5uPv7Of/+PDj29D//vP18Ozs5+OloJymoZ1ZVVJZWVlkYF2hnpmblIyspJmVjYKQi4enop5STUlRTUpcWUhqY1BgWT9ZUjhcV1NiXVkkhke3AAAABHRSTlMA5vjapJ+a9wAAAP9JREFUGBk9wA1EAwEAhuHv3dTQAkLiUlJFJWF0QDLFYDRXIMkomBgxNIYxhOk4wwCqQhQjxgxSGIsALFA5BiYbMZHajz1oJlx51sBJpf6Gd3zONcrqm/r1W8ByK0r+XV1LXyOLLnjW6hMGpu0u1IzPSdO17DgrGC6AadrVodGcDQYbhguP6wAvAaC0BRZQalkUQ8UQDz5tAof0XbejOFcV5xiUoCfjj3O/nf0ZbqAMPYmzU18KSDaRQ08qnfw+B2JNdAEQt2O5vctUGjhoIBU4ygPsj2Vh5zYopDK73hsirdkPTwGCbSHpiYFwYVVC/17pCFSBeUmoqwYQuZtWxx+BVEz0LeVKIQAAAABJRU5ErkJggg==', + 'Madotsuki': 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAMAAADTRh9nAAAALVBMVEUAAAC3iopWLTtWPkHnvqUcBxx5GCZyAAARERGbdXJrRUyGRUyYbY23coZFGDRFGEYfAAAAAXRSTlMAQObYZgAAAGhJREFUeF5Vy1kOQyEMQ1Fshzd12P9y61AixLX4yJFo1cvVUfT23GaflF0HPLln6bhnZVKCcrIWGqpCUcKYSP3JSIRySKTtULPNwMaD8/NC8tsyqsd1hR+6qeqIDHc3LD0B3KdtV1f2A+LJBBIHSgcEAAAAAElFTkSuQmCC', + 'Sega': 'iVBORw0KGgoAAAANSUhEUgAAACwAAAALBAMAAAD2A3K8AAAAMFBMVEUAAACMjpOChImytLmdnqMrKzDIyM55dnkODQ94foQ7PkXm5Olsb3VUUVVhZmw8Sl6klHLxAAAAAXRSTlMAQObYZgAAANFJREFUGJVjYIACRiUlJUUGDHBk4syTkxQwhO3/rQ/4ZYsuymi3YEFUqAhC4LCJZJGIi1uimKKjk3KysbOxsaMnAwNLyqoopaXhttf2it1anrJqke1pr1DlBAZhicLnM5YXZ4RWlIYoezx0zrjYqG6czCDsYRzxIko6Q/qFaKy0690Ij0MxN8K2MIhJXF+hsfxJxuwdpYGVaUU3Mm5bqgKFOZOFit3Vp23J3pgsqLxFUXpLtlD5bgcGBs45794dn6mkOVFQUOjNmXPPz8ysOcAAANw6SHLtrqolAAAAAElFTkSuQmCC', + 'Sakamoto': 'iVBORw0KGgoAAAANSUhEUgAAABEAAAAQCAYAAADwMZRfAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAxVJREFUOE+Nk19IU1EYwK+GQQTVQ39egh6ibKlzw91z7rn3bvfOmddNszl1bjKXc5rJJGmBUr7Yg9qTD0IalFgRBEYg6EDQQB+GovQyQgiaUZsoLcgHMcr069w7MgcGXfi453zn+37fv3MYZt/n99e76tzVj4JN/hP79fvXnV3hnNabwUBjoOHcgTYOu/JQspgTzsqKgn9BfD4vkWTzur287PqLVy+zM+yePB7KsRXLywTjnSpnZctBkPCdW8ccDuU55vBO8RXbkC/oP5ph19V5+7LIky0OY1BKbZEbLcFSt7u6pN7jLmltCVrr3DV5jY3+KovFEsccB1KJNVpefe10BqS2tqqO4/AuphBB4L/LkrRqNgtJs1lMypLls1kU38mytMLz/E8VIlutqVqX6/weZG52OttRXjbE0cP/FYLRlpVjDXuQ/r77x2XZPKkCHA4HBAIBkCQpAygIAvh8Pu2MZgO0Lz+QSa/sQfwN9RfpVN66XC6Ynp6GhYUFGBwczAC1t7fD0tISxONx6O7upgHILmsqvLcHodOggfiV/v5+SCaT4HQ6IRaLgdfr1bIRRREmJyfBZrNBNBqF+fl5sNsdgE2GiAbp6bmbdbXC7qWQbxMTE7C2tgY6nQ5SqRSEw2ENopaoZpCXlwdTU1NaoECgCbgiU6y8QH+ECYWaTymK7TWdys7MzIwGaWtrg42NDejo6AB1WjU1NZo+FArB2NgYrK6uQrAlCASxn2z6wkuMp87VIAhkE2MEAwMDkEgkYHx8HBYXF0HtkQpRy1BLiEQisLy8rPVNKSsFjEzrXH4+z1hlS4xDhKadNu7t7YPR0VHweDzAEVWfHru6HxkZgeHhYVAURYNjkylVWKArZjjMzqmdVi+QCsLUkQiEjvDvncEkvU7/qQ0Vgukeo48Go87IiCJnZNmipxiz7wXEbVDnbUxQOgM12h9n6qTq6NvapRdtkwaP0XK8RmPuYSbxYfaQ/sJJhjfknuFRURUi7AMOozcCwl94hLZp5F+EioDQVwqYI6jomZU1NFtM+rOSxZjVazcyvwHr/p/Kws1jegAAAABJRU5ErkJggg==', + 'Baka': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAA0pJREFUOE91k3tI01EUx39JOpA0H4jNx0pbD3XTalISWf8YFlEgldqDsBLLyqjEKBCiLLWiggh6/KEV1WZ7OaelLZvDdDafNW1JFraWe/32+01FrUZ9uy4ylLpw4Z5z7/nc77n3HIqaMRIjZJyEcNX+uFCFeGmI/GZciEIsCFJUTvoAzDz+1y7K76MSwhX5hXl6z+WSbrzU2KB8YEGDwgrTaxZ3b7xHcaHhR3xw7Z5/UviB1ReP5XSg3+TAqYJOxMzWISFIC0GQDomhTVA9skCnsaAwp/vnMq66dBokNuBR9uFd7T9Z1zCunjci0qcRJUVdoJ3DYOhRnC/qBZ+jQbfeCc+37yjY2UEg0iwvJE0k9l8Z+8xqHmTgot0QLdQgTaQFQ2AsOzlHvOu1S5pwOLsHHo8HjHMCq2MazNvTlByKHyrJLDvdR25jMWRxYx5HjeMH2r1BDOOeguRua4OI14jx8a8YH5tA+al3EHKlW6mYOapb2oZBOOwMbEMseAE12L+jjUh3w+VipyAZ65oxn1NP/GMYGR6Ftn4Qsf7qa9S82Y/l/X122G0uL2TbxmZEz1WhXW8mUol8moXu+SCi/OoQ6VsDh3UUwyQ1k9GOaI5MTkX4yWTGHutvgI1F28sviAlRgxeoRm62HvsyW8En9pZ1TYgi6TntoyQtFm86rVgUoJZRvDnKMmXVAGxWmkAYOBwudBqGcHCvHulrGpGT2Uy+z4yT+QYsCXtCUpp8GxbKhx8gDK0ro+KjJGvzdjfDZnN6VdisLD5/JjArQ2zW66PJOj2lEZtStaBphkwah7K6kMJ/GEulp1bMWhAmMbTozOQRaWRtfoZVgjo4iRra4SYgGi26TwjxVeDKhR7Y7U606ixICq9tr7hd7+OthRWL7yUnJ1WPmXotqLhpRICPHCePtuFV6xdUPTAhcWEtRHEqfHpPyto4hPXLXnzflSEJnFaN3OCKDcsFsrEntR9RUmxARLAUgT5iBPuJsXWDBj0dZjRU9yNV+PTbpjTp9OA/pOSk24nRkXf1J462oPxcJ65f6ULlHSMulepRerYDgvj7A0cKpNz/tyTZqbzXO4t0ZZGQJ34RH11lFHIlA8LIqreCCMUZRY3cd2bwL/5/RmjNSXqtAAAAAElFTkSuQmCC', + 'Ponyo': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAuNJREFUOE+Nk3tI01EUx39BTytConQTt1am07m5abi5KT5S8z2dj1yOEMUC7aUgIoimlmUEWX9kBZGWaamEmE6U1BI1XNPNGTrnHs33IwuSXrL4NgcJ0mNdOHDh3PPhnPP9XoKwcroJYvMQiRSicHCQKCgUyZC9/T5rNet5KUFs0zCZbZMsFmZ9fTEjEEBDp4/KSSSb/4JoGIyWaTYbiykpWEhOxhSHAzWD0aqkUGhWAcVkW58xlvuPhfh4zItEmOHxYDR3MhcdDaNAsKJydAz5IySKRNjEUmy88vjOVaU8F0iPCqCNjEBHkC/UYaGYFwqxmJoKLYOhkxPElg0QsbNtTlmox9yjRD9UCbnoOR+J/lwRWtOCcdXfDc2BPpg0d7CQlIQZPh9KKlVkAQjJ2x2zmOSsQu7hpzUJfBhLjsNQmADjxcT10Bcl4rE4EHc5LjBEhEPn7f1WTqXSLQB/s1Tp7vslsoIkyPPiMJAbi86McBguiaHKjoEqR4jJy2K0nAxApzMN5iUGrclrKVaz2fUvuF4tRbxDKA90w5VjTFyLZKHpTBSq4/1QnxGB2qxoVIZx0JopRCPHFSNOThfWZzfrXDcZEowH4iA05ATg68hDtBaL0HAuCm3lJ9Bfcx2fFNUoi/DCjRgfNHHd1wCZA2TyXjNkE6F0cBDpPFiojeNi8EkJdFoN3vXch0nbBJOhDd907dANv8JITxNqziag3ZsJbUDAwLin50Q9QWwl1qSYoNOVvUcOoqOqAAa9Fu9H2/F9+B5WZLcwOyxFX18flLI+VASyMGVeoJHD+Tzq5BS1PoaKRrNT8127P74swsq4FCa9FKvqBqwaOiz3hdEuLKueYSyECT2LNW0eIfo3E/WmEbvnG1MUJnWdpWhDGDvxQXZHo+RR0uW2tnv+auPX+TvtJm7zKpaen/4y2yjBUlcxlvtvmvT16ZWDpQeoVv3/60F/NrHjTf4ugazIXtJ8ivjnz/sJ+yGQRjcqUdIAAAAASUVORK5CYII=', + 'Rabite': 'iVBORw0KGgoAAAANSUhEUgAAABIAAAAQCAYAAAAbBi9cAAAD/0lEQVR4Xl2MXUxbdQDFz/9+9Lb3tkBLCxTKhzgoOOZAsokbJmZxDFHnd+LL4hKVzBgfNCY++ODbjDEaZowvErOM6HRu6hKZY2rIAOkCY4OSDTpFaAsrlJa2t5+39+NvjT7tnJzknIfzI98Nf/C6TuXdguWBd1q9rcb8/CwsZiu2Ywm4nDVo3VWLZCKDaDwJq9mCg31PgjAMKKUwmcyYvTbek9iJRDm6M/XswEDjwNz6plWW6wdZhjUAintFCEEhn0N04zYskljaDLaj8ar49oUrsYR6mrFJNj322w46H8y+mitM/ZJKZmyE4XAvjJSsazpyuSzslVZIkgWKOvvRgQ6Xrdlhqmds7o7bFZoLkctreKxf7GtuCE7IyUQjBQcQ8j/lvxCGQJZz0IoCVpamTtzfIh9nwiaIrCQyjNg8mq11oDLUhNXRJfT1Ozr3tS/PqpnQ80qRgjAmKIqBfK4ItbSLKoOZqR/6neLkENlSUAIhlktvEf+sD2rkm8nWTHtvZCGMVON1ePuaoBER31/MXGly1wSqq9Uug6FluYyWXJiPqFXmjd4Dh9oF9ZKKimYXRtYCx8lmMIDIxlIPGz591av0mtanF7FcCEN6iMXeox2wOJ0QJAmUAoRQaIqCnWAQY1/ewKNGNeQuYXkm0d2NC2e+wvmRr/Hx+6+8PHayrbDyyQBNDb9As3PHKDWG6MTM23RoeJAWsqeoWvyUUv0UHf7pBB0fe4OeeXe3/vmHbx3+8dwIGJ4IsFpMMFe0fbtAn+nwZePr1u4MBK8XIALG/Rt479wYrs2vgeNNAMNgMbiNzybuoKVvn+Gs9kbr6qpBfJfGYHFIkJUCoGwfqcoMX/b27EGhwgOjoCADDlP+CA51ugFFRzoB8FYNaQ1oqKD44+eNL+wNj7zJGQSIhe8+jgQ9thk+27v/KRY6L4FSCkVOwtlQj6P73Qgt/o1ERoKt4iUkE7+jrZMHyzIoK9cOBFfT4LbWAk+0a7ZLnvqHcTNdACgFScfAcjxEdy00VQclHGo7dqGeYxHbvIo6hwhSghCehb3G5p6eW7VxXC5/xGWToMgrKKoaCnIalI9CIARasQAqloMI/x4BWrLLYwE1AEPTwCGHaGjz7pw/leZUNV8wNm9BLy6CxsvxZ1kMbaY4TKIIXlNBsynoVjvAC4CuAoYOVi+CMfLYCUfg95tPHuzZB0YtKzsb58RMucWE/fZmhCbdOP9rNnLnxko6GVoB8lFwyVVw8b/AyeulHoJyN4Rb19dTFyeqBlu6njvfsWcvOJvLs7DMmw/7bvpeE4pU2OIcgcqmp4fGAgt2Txwvqr7lTp5V7LquZxXC6+BqEvGcY5pyjaM1tffJbk89NE3FP5VQ6y7a+paZAAAAAElFTkSuQmCC', + 'Arch': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABCFBMVEUAAAAA//8rqtVAqtUQj88tpdIYks46otwVldUbktEaldMjldM2qNcXk9IWktQZkdIYlc8mnNUXlNEZktEZlNIYktIWlNMXktE7o9klmdMXktFHqdkXk9EWk9EYk9IlmtQXlNEXktAWk9AWlNEYlNFDptkZldMYk9E4otg/p9kXktEXk9AXlNA4otclmdQXk9IYktEXlNEwn9YXk9IXk9FFp9o3otgXk9FPrdwXk9E2otdCptkXk9E/ptkcldIXk9Edl9IXk9EjmdUXk9EXk9EXk9EbldIcldIjmdMmmtQsndUvntYyn9YyoNYzoNc0odc1odc2odc6pNg7pNg9pdlDp9pJqttOrdzlYlFbAAAARXRSTlMAAQYMEBEVFhgcHR0mLS8zNTY3PT4/RU1kdXp6e3+Cg4WIiYqMjZGXl5mbnqSnrbS3zMzV3OPk7Ozv8fT29vf4+fz8/f7SyXIjAAAAmUlEQVR4XlXI1WLCUBQF0YM3SHB3a1B3l7Bx1///E6ANkDtva0jKbCW2XIH1z2hiZEZ4uUgxo7JedTQye/KN/Sb5tbJ+7V9OXd1n+O+38257TL+tah3mADAwSMM7wzQWF4Hff6ubQIZIAIb6vxEF4CZyATXhZa4HwEnEA+2QgoiyQDnIEWkjVSBBZBqXbCRlKYo8+Rwkyx54AOYfFe7HhFa7AAAAAElFTkSuQmCC', + 'CentOS': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAB5lBMVEUAAADy8tng4Ovs9tnk5O3c7bX44LLduNO1tdDh7r/eutj43q2kocX23az07N+qqsvUqcmXl7331ZXJj7r40o/Pn8T42qP63KjNw9n21p3Y387Ml7732JzR55z05MSxtMLGn8TC4Hx8eqt8e62Af6/B4HnG4oPC4HzH44fBf7LCgbOkoMTcsrmtn8PWqcfFtKrj4Jvs2ZOz2FnMqLXT3KfY5p60Z6NUU5XRuqHzwWSywqDn3JaiiLWahrWhkry5zJjRmqm1Z6P1wmb1y319fK632mK5cKi5nH+73Gu73Gy73W283W+9eK17e6y1yZS3aqRZWJdcW5ldXJplXZppaKBwb6VwcKV5eKswL306OYNPTpGkfK+m0kGpUJWq1EnEqIuXK3+Xh7ahP4qhkryMfK6BgK+CdpGMaKKMa6O9ea2+eq6+oYW/eq+NbqWVlL2Wlr7AjanA4HnA4HrBkqbBlafB33rCgbLCmKjCxIzC1mSs1UytV5mtxIWt1lCuz2evWpuvXJywxYzHjrvH4oXIjrrN2HXO5pTO5pXUlYnUlYvVl5Hb0G7e0XTg03rhr5fpzHPpzXTp0Hvtz3/wrDHytknyt0zyuE3yuVHzvVr0wGP1x3T1yHf1yXe0ZaL2zYP30o730pD31ZeRIcF5AAAAQ3RSTlMAFBkbHEhJS0xMTk5UWWBsd4SEiIiPkJCVlZaam6CjpK29wMPDxMTFxcnK193e3+Dg4uTn5+fo6e/v8/P4+fn7/P7+J4XBAAAAAOBJREFUeF5Vj1OvAwEYBb/yGlu717atLW0b17Zt2/6nze42TTpvMw8nOZCAmwUpiIY6c5IiLi9tPX64GairqszHQ4X2VB64v1Cs6PxMPJSdHM777s6/jyaMRGiRLyyrb88OpjZ3CzAXrm1sqzSNNeN7kVBPNgB7cG51abE5l9cXDces7emQ1uadHhutFUg6gpPKkSIqQGavwz7r7O/+/3t/rSdjI9XDM3qz4fr3B/3iA0aJTG9x71+9oR/PLDwUe2wm19bly+fTIxHyEETatbPewGEw6Mk/tKZCEqSQQUlIHB/QNBEjjVN1AAAAAElFTkSuQmCC', + 'Debian': 'iVBORw0KGgoAAAANSUhEUgAAAA0AAAAQCAYAAADNo/U5AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAZ5JREFUOE+Nkk0oBHEYxv8fu5GQj3JwcaDkIAc5IpR87M7MKnIVJVKclaIQ5Sy5OLkgR7n5OigcSNpmd2c2Vyfl4KT8/muWiVU79TTv+7zv837NCBF6PG1X+NpZyEYSD9mIc+tHnBPe23B9xKrCuTmbQA/JKfABrhBswa1hH4A38IwfOxPdX1qcjiCQxO5NyrjKV70TnSbeRPwJvGN3i4yyqnEucPY8ZZX9GSEgGK+RvFfyjk2VKZxzBNG8wJWWgh/xtDOeUXZ7Slr6TrSLYL9N4SMgYTTcwdc2ArvJcElhSVcM6mCNSV8n9hA59yTU5UWMG6HIbLhIWlglgWiC2L4Z79qTdo40D6ISuOWwKCWHyk9Fv8ldpUHOuGTuynwSBUynddPdlbEosVpP9Eu4FnOsRzUYNTsdmZN/d5LDiqM0w+2CMdAFFsFGWgfXxZnheqe/z+0puwEM0HHYV3Z9Sgz8TEz7GkQvpuJ/36ggj2AaHLrSlkULWV5x+h2E8xkZL16YVjGNaAUscfZ/f6c/k9ywLKI2MMcRWl0RLy007idmRbQJ7RIfDAAAAABJRU5ErkJggg==', + 'Fedora': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABPlBMVEUAAAApQXIpQXIpQXIqQ3UpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIpQXIqQ3QpQXIpQXIqRHYpQXIpQXIqQ3QqRHYpQXI8brT///8uTYMpQnM5Zqg5ZqnS1+I4ZaY4ZactSn8uRnYrQ3MrRXgsRHUsR3s8bbM8brMtSX4wUosxVI01XZw2X50vUIguToQvR3c6X5o6aKs6aq08Un8qQnM9VIFDWINJXohKcKlXapEqQ3UvUIc2X55bhcBdcJVgcpdhfapmd5tuk8dxgqJ1hKR5jbB6iah/m8Shudq3v9C4wNG/x9bFy9nFzNnFzNrIz9zK0NzK0t/O2+3P1eA2YaDU2eTb3+jb4Oje4urj6fHm6e/s7/Tz9fj3+fz7/P38/f3+/v83YaEa/NNxAAAAHnRSTlMABAoVGyY1SVlpeIuQsLfDzdHW4+3y8/b39/n6+vr4+ns8AAAAyklEQVR4XiWN5XrDMAxF75KOknYdZJS0klNmHjMzMzO9/wvMcH7I37mSJShsJ+5NjMT6umDoHyXDcI/2qJadh++P3cle1de+9yPe3/bTY92wzfzr7wGtP3JrAI72BZGVtcAdQlwHy+JS1pDbBE9qamZF3BYrjQxPEXwKc6dC8bXFm0QIpmt8kn0Rn093q82UCtK8oXZckwFJzuulV8bHkajPyXdbnJnARfDHs0trz+JQ+5AFvzp/L0+cL2qPAINUPrq5OC6p/64F/AMnrST+Dq/r7QAAAABJRU5ErkJggg==', + 'FreeBSD': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAABIAAAASABGyWs+AAAABmJLR0QA/wD/AP+gvaeTAAADXklEQVQYGQXBS2wUZQDA8f83j33M9rF9d7u4loaWklaDpkSo9KDGaIKUaGxshD2YSPRiuDVeTDyhBxosJCoa40ktpAkPDcUqAYVIpUSUPrAulEdD2bbb7e7ObGcfM/P5+4kwKDvq6yJ1FYYcvb+YAkqAHo/HQ7FYrFIoCiurq9ZXJ06YSOkA+kBzfX06bys3zHxS9EL0tXDVyZfefacqV+X/ZSJx5+qLbx98LhaL9RiGEZWlEsWC/Thd9q6Pf3vs2u6Orc83rFsvTwwfLf5obgywT1Vjh2Hh+rbNsnTssJdNLedK5aIrpSuldKVXKsnH4+Pyn6FDXn5tMef9O+3NvdkvP1V4+EYw2AoQ+KSx8dRYS6NXXnwovaItXduSrrkinWxGOmZWJi9OyOK9m1LmsjIz9IH8QUMOd3WfAQwNKCy2tJwbHB5+XasPaxIHmc4g7WWEZ1MquBiRFlJTf1E7+Tl/H/8asavPzTY1nWd2ZkMDRPeBeHPz5ojwsilEQCBvTSKunCF3M8FSNkBGVTHDYYrLj8jVNhDZ2SMa2zo3MTamaIC/u6Ojr3DtrOrvP0BpdATnyBeIhTxpR5ABUlKSUlXS1dWstbVxdz6hPL0l1quGqkLaKwNvVcjEXNRd/4mit4Z19DjefBEPyCKxgQJQcF28dBrHNDGTSZSezsjeff0hraa2Vs2vrvit81O4vj9xLJcC4ADrQA7YAGqBGsAql/EtLdFQE/L7dF1XZmdnSrbPMJfXoLDmolQK8gJyQBowgQhQDRQBD+hsraVhd4e5MH+/oExfvWLJ9q3/3S7qMpNH2hsS40kFS4EUUAMA2IANRIBXv4uzuO67c2PykqkA5YmZ6bN18YPi0Yoknxc4AsJPCMLVAk2BLKDosCWqs/PZaulkuxk9fekcUBAAQGDks5FT0W++3NuYuC0DVUL4DIEdlIQDAj0IRkigaMjArkFx0tf523sffrQHyKsAgHPhwoXLL+yP9/kePNhk5ExUTyKFkJVAUAiCFZrQup4Rv9ftuLV/6ONBYBVABQAArMvJ5MXW7duD6P62sD8UrPAFRU1TpeCpCnGvPZr7WW///v0jpw+VC9ZdAAABAAAAAMLo7drWrmQyPWG/r8tnaGIjaM05ujr16x/ZBFh5AACA/wGZnIuw4Z4A3AAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMi0wNy0wNFQxMDowOTo0OS0wNDowMOPVpFwAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTItMDctMDRUMTA6MDk6NDktMDQ6MDCSiBzgAAAAAElFTkSuQmCC', + 'Gentoo': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAB9VBMVEUAAAD///+AgICqqv+AgIC/v9+Ojqqii9GAgKptYZKQkOmPj/ddUYBgW4eVjeCTgfiWjO5wbJaZkvPBvepkXomYkNldV4Bzbpl6dJ+Uj7ynoO6Vi+1qZI63se2mnudjXYjOy+GCfaqZjvWlm/Pc2e+Oh7NeWIOWjfeXjeW1sd+gl+diXIfp5/KHgKnn5/F2cZx6c6ZgWoXc2e6dltrAvNu0scrX1eTOyujCvup4c5qpovVpY43///+6uPPJyPXq6fvm5vrz8/z8/P7+/v/d3PixqvmxrPSyrfe0sPO0sfS3tMve2/3r6vy6ufPz8/3d3fi3tM63tPO4tsu5tsu5tvO6tfe6t/Vva5KRjKy7tvW7t/W9vPO/vM+/vvPCwfPEw/TFwvTFxOfGxfTGxvTHxvTIx/TJx/aTiOrNzPXNzfXQzfnRzuHS0fbS0vbT0uHU0e/U0uTU0/bW0+zW1ffX1vfY1/jZ2Pjb2/jc2uSTiemVkLSlnvbe3PTe3vng3fzg3f3g4Pnh4Pnh4fri4enj4/nk5Prl5Prm4/ymn/bn5vro5/rp6O/p6funoPWsqs3t7Pvt7fXv7vzv7v3w7/nx7/3y8f3y8v3z8vytqPWuqPX09P319P319P719f339v739/34+P35+f37+/+uqev9/f6vqvSwrPQAR0dcAAAAPHRSTlMAAQIDBAgJCwwVFyAsNUFHSVBneH+Bh4mVmZmanKCxsrK2tr3ExtDW19rb4ODl5u3t7u/w8/T6+/z9/f4MkNJ1AAAA8ElEQVR4XjXNw5aDURSE0YrRtm3b54+dtm3btm3bz9k3Wek9+2pSYFwT8ibzE93hwAtdJqK3nZo4J9hFXbP+vFHOthV6gnGzstZq94wdCs4UCCDymQ2v7X0LdYoSQ0MIENRYzJbRlPTTHu73ZNAL8vivmVui98PpzuqffX0mIPHJGtOQenukteJ+aS3b9htNpDnT9TeZH1bHAwBRMhGpd6e6uNrLoRgxBKmsX47nBlp678ojpEA40fejcmW4e/No0V8IIPfj6eKgbEJ3ZUnzgE1OqWp9Q3VeWRAsg51f1dZ8c31RmAsc+N5JGbG+zvj3BzDCPrzMDC9SAAAAAElFTkSuQmCC', + 'Mint': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAACVVBMVEUAAADh4eEAAAAAAAAAAAAAAAAAAAAsLCyXl5dgYGCnp6eTk5N3d3fBwcGqqqq8vLzNzc3Ozs7Ozs7Pz8/Pz9DQ0NHR0dLS0tLS0tPT09Pf3t/Pz8/i4eLb29vZ2drZ2tna2dra2trf3t/u7O/u7e/u7O/r6+vt7O/w7/Lw8PDy8fTz8fXz8fbx8fHz8/P19fb49/j49/n6+vuPxlmWyGOx437h9NDr9eD6/fj////+/v75/vTA5Jv6/fb7/fnL5bDL5q+AxjeDxUCEzTyGxUaGzjyHxkiHzz6J0D+Kxk6K0kCLyE2M00WNy06P00mSz1OUyF+W2FGX1FiY0F6Z02CZ21ac0Wiez2yfz2+f2mOh4GCi4GOi4WKi4mOk12+k3Wul32um1Hin0nun4G6n5Gin5Wmo23Op2Huq1n+q43Cr526s4Hit23+v6XSw34Cw34Gw6nWx4IKy4IOy44Cy63ez146z34az4IWz4YW03Y217nu38H2625e645G74pK83pu98Iq984W+4ZjA4px0tzDA5ZrB8ZDC5p7D55/E947F6KHF+JHH4qvH6qTI46/K5LLL5LN1tzLL5bN1uTDL57DM5bPM6qzM66/N5rTP6LbP6bTR6rfS573T67vT7LrV7r3X68XX7MHX773Y77/Y9rvZ8cHa7cjd88bi88/j8tTk8djk9tHm8trn89vo89zo9N3p9N3p9d7p9tvq9d/s+93s/dzy+erz+O73+vT4/PX5/fT5/fX5/vN1uzB3vTD6/ff6/fh5uTj8/fv9/vr9/vx8wjV/xDmrMRH0AAAAOXRSTlMAAAECAwQJDzk/RUlNU3F0kpSVlpeYmpucnaKjpKWqqqqtu8LExMTEzdTU1NXY4evy8vP+/v7+/v6LaR1mAAABD0lEQVR4XiXI03bEABAA0KltW9kaW3eSZW3btm3btm3b/q4mp/fxgqKOtpamhrqaqoqykrQYABh+PVMU9fjE5Xp8o54kgPHN0EBHU2N5YXZykiua0HHd2759VF2Sk5IYE5GGsmCEWLV1kVWwt5O+3x/qpgsy8k4ja+cJl2/v5C22tlgCAHtw9TQSa4s+AzfPSm0BRNl9SydhWJzLC567KrNhgrNwHIJ5qTz/2f9w7Jw/DNqIjVr04exW0AEOXcN3Ab7enr9eDW2VTJgehONyc2Z8XP5YdD0Tcuhcc4/r45OjGX51TEjYPbh8THRPvbz+CHusgSZlT7rP8PkCwfQKaQUi9Igr6JsRBMFiWZgb/AHKElRzKopZJQAAAABJRU5ErkJggg==', + 'Osx': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABrVBMVEUAAAD///////+qqqr///+ZmZn///+qqqqAgID///////+tra339/eAgICoqKjx8fGMjIzm5ubh4eGPj4/g4ODIyMiAgICSkpKLi4vS1tbPz8+Xl5eMjIypqanIyMjW1tZ2dnbR0dGamprFxcV3d3d+fn60tbV3d3dcXFx3d3epqal7fHxxcXF+foCnp6hYWFhyc3Ojo6SMjI5fX196enp+fn6Li4xERERqamqgoKFpaWmFhoeen6A/Pz9QUFCWlpeSk5SUlZWUlZaOjo+Tk5RHR0cuLi5YWFgwMDAeHh40NDQ3Nzc6OjpcXF1rbG0XFxdSU1NVVVVXV1dZWVlbW1tnZ2lwcHABAQEEBAQXFxchISI+P0BISUpaW1xHR0kNDg4qKyszNDU1NTY9Pj8NDQ1cXF4XFxhSU1QSEhIDAwMrKywtLS4uLi4wMDFHSElISEggISE0NDVJSktNTU1FRUVWVlhGRkYEBAVBQUE0NTZQUVJQUVMFBQUqKitWV1lXV1daWlpaWlw+Pj8bGxtcXV9dXV1fX19fYGFgYGBkZGRlZmhpaWlsbGxwcHB2dna844Y9AAAAV3RSTlMAAQIDAwUFBggMDhkeICMkKCgqMDIzPj9ERFBib4CCg4iMjZCcnp+jqamrw83W1tvb3ePl6Ojp6+vs7u7v8PHy9PT09PT3+vr7/f39/f39/v7+/v7+/v50ou7NAAAA30lEQVR4XkXIY3vDYABG4SepMdq2bRSz/capzdm2fvOuDO397Rw0Ly4tz2QAQPbcxuZ2E/STJwfxPhWgG355fRrVAIVb1zeP9UDLfiSwkAcADe8fn7tFxWuEXFRDoer/OgoMTRBCumj8yJwPBo8Zhpk14U856/HI8n0ZUtpZ1udrSzfVneA4roNKjdrwpcMRilb8d8G60+lKnrpWcn9bO+B23w2O8Tzfq4aiNSZJqzn5O4Kw16h06fPZ+VUlUHfo97+VAEb7rSh2UgDd4/U+TBlQY7FMj5gBIGvcarVVfQPVPTG94D0j9QAAAABJRU5ErkJggg==', + 'Rhel': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABj1BMVEUAAAD///////8AAAD///////8AAAD///8AAAD///////8AAAD///8AAAD+/v4AAAAAAAAAAAArKysAAAD///////8AAAAAAAAAAAAAAAD///8AAAAAAAAAAAD///8AAAD///8AAAAAAAAAAAAAAAB5eXn+/v5JSUnKysrS0tJ5eXmqqqqxsrL+/v4ZCgknJyeHh4eIiIjo6OgZCAdOTk7t7e3///8GCwwPAAArKyv19fX29vb9/f0EAAD////+/v4AAAAGBgYHAAAJAAAMAAANAQAPAQAVAQFyCQV9fX2pIRzmEQjn5+cBAAAFAAAAAADnEQjvEgn////uEQjyEgnsEQjzEgnxEgljBwPaEAj9EwnwEglHBQJHBQNNBQIBAAB3CQR5CQSHCgWLCgWRCgWTCwadDAWmDAapDAa/DgfKDwjWEAgGAADh4eHiEQjmEQjmEQkKAADoEQgLAQDtEQgMAQDuEQnvEQjvEQkPAQAfAgEuAwEvAwE8BAL1Egn3Egn4Egn6Egk+BAL+/v5CBQJrB0muAAAAT3RSTlMAAAMEBAkYGhsbMTRLUmpvcHeIjLe6vcHCxM3P0NbW3Ojp6u/w9ff5+fn6+vr6+/v7+/v8/Pz9/f39/f39/f7+/v7+/v7+/v7+/v7+/v7+Q8UoNAAAAO5JREFUeF4tiwVPA0EYRL9SXIsWl+LuxfcOd2Z3764quLu788NZNrxkksmbDP2R7vH6GioLs+iffEzNXd4+TqPErUUpVqMOvwgdzMPn1rv5vPsVeufBTaBK/bH2FPvkEUuIG5jIIc+sHYn/HJ3dC/Hxuo4y8s44dzwBbFkisHN8bVIdXs6jb+H97aCwbHEIqgcml64CD7YllNkAVQC940MLYe5YzvIeQAXNrd19Roc5MdzfdQLUUKaUYyuG9I8y1g4gj6hIak4X5cBIT2MquZJrJdOqpY11ZpAiqVwbY/C7KY1cRCrZxX4pWXVuiuq/hs49kg4OyP4AAAAASUVORK5CYII=', + 'Sabayon': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABvFBMVEUAAAAcUaYdVKwAAAAAAAUABAwWRY4YSZYhZtIhaNYHDx0KCgoFDBcKCgoRMmYSNm0fXL0fXb8AAAAYS5gaTp8fXLwgXsEGBgYFBQUZSpgZTZ4JFSgODg4IEiIOJkwOKVIkW7EnXbQLGzUTExMKGC8LHjwMIkITExMiIiIPEBEPJ00QEhMXOXAaPncOJEgoXbApXbEcHBwwMDAEAgAfHRgQDgo3NC8AAAAHBwcKCgoLCwsJCQkaGhofHx8lJSUwMDA0NDQ4ODiRkZEICQocHBweHh4GBgYHCg8mJiYnJycpKSkrKystLS0uLi4ICAgODg43NzcRERF1dXUUFBSjo6O1tbUbGxsEBAMLGS8MDA0iIiIjIyMkJCQNDQ0NHTYKCQkoKCgPDw8QEBArMDkKCgkRERIREhMxMTEyMjISIz00Njk1NTU2NjYCAgIVFRU5OTo5P0c8PD0+Pj4/QURAQEBHR0dKSkpMTExSUlJiYmJlZWVnZ2cWFhZ2dnZ4eHh8fHx9fX2FhYUXFxeVlZWXl5eYmJiZmZmcnJwZGRmlpaWrq6usrKyvr68KFiq/v7/FxcXY2Nji4uLn5+ft7e0yif9uAAAAN3RSTlMAAAApKSkqKioqg4OEhISEhoa1tra3t7y9vr7S09PT09TU+Pj5+fn5+/v7+/v7+/v7/v7+/v7+70RY/wAAAPpJREFUeF4dyWNjw2AUBeC7dfYyorM6rx1exKltzLZt2/rDa/J8OgBVVlFDX39jcTZoUqCse251a2dvu6ccUtWlanLQ4Vpel+ThlWq1l3wEz58tx4dOt1dMlAJk9A5gMjG75LHwo46hzkwosGOMbejumoRvubC9EOrMviT0E0Us9fvN9dA6zxJCNv6+ECGsb6oNWsgmpZT9/UTUZo3Em6AW34guTL4jiAudiCM1kLcw8/SmHERfT1/eueBiDqR1GK1n9w+K8nglxYxd6QAML4ztXoQuj8YFgWcgqdJp8qzty26vaboCNIxBCshyQDKov0aXr29v1ufq1PwPx5Q7bCoh6eoAAAAASUVORK5CYII=', + 'Slackware': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AcEDi0qZWWDgAAAAx1JREFUOMt9kktoXHUchb/ffc1M7rySSdJMOknFPMRitLgoNKKI8ZHGKkgrjU8SitidimSh2UkXoQmoO1dGQSxJjdvOtqSaqlR0USEGSjVJGxuSmWR6M3fu4/93YX0g4rc9HA6cc4Q7DI+fpzz7PA8++2mxvZAeBZ4xhHtFcJRmXWsWvb36/OLcyxf5B/KHeYHy7DmGx1+YSDjmWTdlobTGMAStQGkNoLXS4tXDq7u7tUcWz49tA8jR8QUuzB5n5NTCV13F9JEo1JJwTLKuzU61QiOMcd0UDb+BncwQK3Rl15eNja3ui/Njq8aF2eMcO/XlBz0H8oO2ZUkum6A13WB99TtyzXlaCi24SaFa+ZFCzsG2DNnfkdbFjsI1APPhk+d6ujqznycdCxFozadYWvyMpx47wa+bPkGksKwUNnsk3TaCGASRXDZh5LpHXPPg4Rcni+3uYBxrtBbQghlscOVKmYHeEm0ZIZ9xyLffw41ND6VAa43SmjiMByzHYtjzwr9arfshxf5jOKlvKZfn8es77N2uks24PPfSFD/9Uvt7AtPKWmEU9d645eHYJo5tcKi/FX/zG+zmQxQH+rANk862DOW5N/hhaY64cJSa5xNFCgDDILZACMKYWAmh73HmzFsMlBQJ06LeiMinE1S3KzRCm5rXIIoUIoKIYCVM36urZFbEoiBLNMIhAE6/NsSB7h6SKZdL8xsUOnpx9j1KbTdARACIowArYe1ergfNT2i0mIbJys0GI6PT3N1/hJvrPxOFdRJNBQIy/FapI4Bpgohgcjuw+jq8jy8tV55MNBWI4ohS802CpizKv8q+FgALZAfYgSyAZtNro1oLaU1VvxCA029Oraxs7u/tKnXiNjn8HyKwur6lI++6vPK4V7IA7u+1Dyu1tr183ddNbkHuXP8/zEIYeFqiLRl6YO/p0bHJdflT/PD9qZa1W+ry99fcvlAlcZwUpuUAglIRYVgnDEIOlna4q0M/NPnuO1/PzMwg/045O/XeibUt5/Xangx6viSVFpK2jtMpvdyWCz+5ryf10clX3/amp6eZmJjgd441URWWJY8BAAAAAElFTkSuQmCC', + 'Trisquel': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABjFBMVEX///8AAAAAAAAAAAAAADMAAGYAAAAAHFUAGWYAF10AImYAIGAAHloAHGMAKGsAGmYAJmYAJGEAKnUAJ1gAMXYAJnEAJGQAI2EAK28AK3cAGTEAMHgALXEALXgALG0AFUAAI2oAK3EAMngANoYALXMANIAAM4IANIIAL3gANIcANokANoQANYQAOY0ANIYANooAN4kAN40AOY0APZMANIUAOY0AO5AAPZUAPJAAP5MAPpQAQJUAOYsAPpYANoUAPpoAPpUAM4AAQJkAPZIAPJEAQpgAN4cAPpQAPZUAPJEAO4oAOosAOo8AQJoAOYsAO44AQpsAO48AQp0AP5UAQpoARJwAQ58ARaAAQZgAQ54AQ50AQpgARaIARqMARaMARaIAR6QARaIARaEASakARKEAR6MASqsARKEASKcAR6MARqYAR6UATbEATa8ARqUARKAAR6oARqMASKgATK8AR6QATbIATbAASq0AR6cASKgASqwAR6UASKcATa8ASqoASqwAS6wASKoAS60ATbHn4CTpAAAAhHRSTlMAAQIFBQUGCQoLDxAREhMUFBUYGhobHB0eHh8gIiIjJCQkJCYoLC0xMTE0NDo6Oz1BQUNHSUxOVFVVVldaWl5iY2RkZWZoamtsb3FycnR1ent9f4KDhIiJioyNkJGYm5+foqOkpqamqKmqrKytsLKzs7e4uLy8v8TFxcXGx8rO0NXY2eZc4XYcAAAA00lEQVR4XkWN1VoCUQAG/3NWtwh7CTsQJOyk7BaDxuxA6bbrxf32gt25m7kZqDRYxziooDV7+1AalMUavQh2AsEZoWvzigLun+T17/c8QiJZ7qu2QKiNmyZthdcR1/as353jIeU1GxMHo5XHdqPFeX8IaDMdHPYN6dRN7LR4qQewdTa35HWkyh+fbxERAMjwlAWJv3CPSKDQ+H7XvHdkV4Pua3Gtm4sPKIF/WV8dop4VKBw/NU33B3x1JbTt+XwhkJQoqRfWvHOy28uqH8JIdomR/R+s9yR3Cso77AAAAABJRU5ErkJggg==', + 'Ubuntu': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABKVBMVEX////ojFzplGf1zbnqnHLvs5P10b3yuZv1xKrytZXvtJXys5LysI32waT0n3HxiVHwg0jxhk31kFn0h0zxf0P0hUrveTv2iU3yfkD1hEfyejv5eDLybSX0aR7zZxvyayH6ZxnxZBj4YhH7XAb5WALlUQLeTwHgUAHeTgHfTwD65NzdTQDdTQHdTgD31MfcTgLcTADcTQD////xt5/31Mf54dfmfE/dUAbeVQ/jcUDcTgHeWBnnflHohFvpjGbqkGztnX342Mz53dLgXiP65d399PHdUgrtoYLyu6Xzvaf76eLfXB/rkm/fWhvupojwrpTeVhTgYSfgYynzwa30xbL1ybnngFT31snngljhZS3539XhZzDiajbibDn77OX88Ovrl3X99vTjbz1fisGCAAAAMHRSTlMABgYGBwcHJiorMDA1NXGHjY2Nl5mZmZyfn6O5u8XHzc3X193j9fj4+vr6/f39/f08OUojAAAAx0lEQVR4Xi3HZVbDYBhGwQctWqzFPXiQ+36pu+LubvtfBKcN82/UEhld2vWXxyL6F92gbTPabse8hU/uHMx1SZoyyJWPTwq1Rs7GpYE9+Cg+OJcs1MHvU9y4fnrN31yUm18vMCIPjtw3QMndw4rs8ieVzAAcBlewpe1KM3uaBuD3Dda1BhWXAsi6AFY1a2SqifxZ+rnxWYcJDRkUS3fO1R5vwe+XZgw4D4L3RAJiknoXCVX3WeiUpJ5pIxTvVmg45pl5k4Ot/AGV2iqZBWgJJAAAAABJRU5ErkJggg==', + 'Windows': 'iVBORw0KGgoAAAANSUhEUgAAABIAAAAQCAYAAAAbBi9cAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAA+pJREFUOE+F0n84FHYcB3CWSsL9ojo/6ik64c6PnTjmSS0limmrpBm2G002y++xzXRz6zE0R4nbw+RnTj/WD4sbanLkkAe55ccYlyNme4SrO9u9d13PI3/saZ+/vs/3831ez+f9eb5aWsuqy2mjRYeNUa7YmtjfTico7jNJ8z0eG24NB9vvnDrvufzpq89Npnr8VjMddNmuRh9rDfp36mFg91oM7qPIc5JdbDJq3An/JfCu7Hl53W2lpS220pP2OuniN299jAYbYizSENIoAgbCTdrTKtxOJVdvGo8psUwKy7Vxe4ez1YEVudGP8YEZzyveInFJ6mZRHHqYazDspw/pJwTIuERM5JIwmUdGdyo9K7/BszGzzg6fXzZHGJ8KvzQqXKOpoIeZLjofWR++BPWyCEnPY4xFGEKWQcLjMjKmr1MwfcMYwmz/Y4KOgNki0V5k1dkjUWCK93Kp2PMFFawos8cm1gZ2GqjLXktL4mbQPHLQ4B9ZDFE5+S356fQlyuJMqzH++HnTo6ui2OO1ko9Ul+4fxfd3d4F7k4YTReqpuFS88bGZUE2QNNDobuIq8Q5CduHb7lFJaTnvnym9ergjMWD/FG8zf+aKS3G9JO5C01Asah6wUXrvALKEDoitMMHhDKrKJdg8RU2s0EB2EWWur8dd7PDPFv6dUC0Gv3kAN36VPRGP/5k5NS6lljWxG0TDiSr1VKhoPwhevRMSqkwRxDObc/DavGtpP6zoi8XOyZfhnyNEvKANBU0P8VPfI/wyNCGXSn7wlEmyA9KrgmOKGth3eDVvPfyywq2dnUEv2R9qG2rLsH7xJXziKnWcI8tlTvEC7Mu8hROlImTU9aKqcwQ1vWOihWFu+sJknmph5CvxQh87c7bNh/NXo03hrMCosyvLmMNgMF7TQL6J1dsZIUVwjKqEO+cajp5vxPN439U/gKBt8PTcYHzL/BgHCyOf4unAISj6mFC2bYC82kB5Ls460NHRUVsDeYSXpGw7UgC7sAtwShDgzdM38W7BbURXtqpqhfmB8sEQuXwoCM/6faGQuGCxyxyKWhIm+PrSD495WL3cT0hhi8Whc3NbAs9KaOyCTvrJ8qkdX19XBeTUDU00+55USFzVU2yHstcaix0mUAjJkJeuRU868Ucmk0lcguiBnMAVxjbbdHV1yeq8+u4Hgo22huSG+iQXp83ftaxW3lsPZcs6KG5T8OwaAfJiPcxlrVRVRhvF02i0F/t5VbHZ7JWDfErKTLnhE3mFPuRFepg/uxqz6TqLv6euGj3ut87t/4ylvre3t3ZehOWWO1zjSFEqMVP4GfGb/DBykJcjmaZOoLsc+hcVY/LaAgcTQAAAAABJRU5ErkJggg==', + 'OpenBSD': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AoYAykIPu64pQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAADTklEQVQ4y32RXUxTdxjGn3N6eujoKT3SUkuk3VoBGfVjgFXAsZ7WkipyYXQbuu3CzUXZmGRbssnFEseFkWzgcGzGXky9MWL8TsC4IeFgtK4oAqOnG5vMVl1pCMVWQD7b/y5M6jLdflfvxfPked/nBQA0NDSChqnGVrLuGkES742NhJAdhAKAuk9yyUs5Gry7RQMZAARCWgivpQiPe71P5DUfH0xaqTL7m/iiLkJmphawa+e4SM2PvUyC4yUIBu8CnAQKAK53rCA5OUtQtStVpJ4Gw/FOBddZVKhCfq4MP4n6+at+DUsJm/e0G9JZzYEvI2tHwlEYjDxomkZ+3nG8WroRtHihZVOhVlorDQzh0okhcByDP4ZGcf+X9XAsvY5/RsBa7Kq5H/CqLctKyl/g08S2i6fq8W/MS3P34T9wNDVYSeDX1eTD9xhiLXbtB/Akwmmv6Kr+ICFkLpGhtNSM3qsSstS3oX8lSsmsxS6ZVn3j6PvVVqhUcvC8AtPxVPxwygVKvngN89WOjgVprggGA4eenjB4nsXsTASpC63I0wVTZYPR11FoKRB8Ax54PCFk6BhMTk5CPR3GSbHouGzknr/bYFq9EAvfc9Tu1sLjHcXNKxLuTOTgzOlOe7IHBc/beAXWpWmXlz8a84nhcLQ+ecVzsAEQrMWuMX+f9HZF2YPZ28FVSNfoPWqOzMUmqYMAJm7+/OOzXQFwHGpyEV+vi+yvtxBC9pDmpgJC4tvI3mo9GTitIxvW24nT7ug67HY/3eDs2bbyrVsrY2day70rV6kRfDAHk5lDLJqAmmeRiD9GJDKHvwb74R8G0mkTPjrQTTG122xkTTbwaV2b1H4u16JQKXGr7yG2b8/H1MQ09IsTSEmRwzf4CCwzD+dmE1re8CI7wwi5XNlFf9vaTXX4dWJg4LLl7h05fpNGwNAMWpp9CIVYNO/tRCzGwpDFQaVMQTS2CKY0BWr3GVGWNSXKACDDaA4Mh976pq9f5Sy09GgKlmeAMIBKzUKpU+BFoxJecRhUfAbMxDi4eADfHVmE79v7q575gvvYeVvjZ58LD5mwsKUyX0hnf0feslnQCWD4zxnc6reKisxsfH2oscqcmTmK/+Ow252cna7K52r+Bky6PqmoT5HBAAAAAElFTkSuQmCC', + 'Gnu': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AoYAywUV5gQrwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAADcElEQVQ4y43Tb0jjBRzH8c9v+7nNMebcUW21Cc78g/wcuhByIScoMRwoTBmFlZCmIJ14axqkgoYIkXIqKIVBEuJNUBEUPRlpqDC3Q2Ex0nTezun2YOaPLXNIv7Vvj7zgiOj1+PPk/eADjuNEuHN6ekqMw+H4IzMz8xChUCjV1NT0JbO7uxtfXFy8NZvNr21tbd0AAEQikY6I0m1tbQbx2NjYZiqV+vn29jY+PDw8xhYWFj45PDzcb25uhlQqfSTief6X0dFRpqKigvF4PPPipaWlY7lcXhCLxXJnZmY+ZTY2NnzX19ePGxsbHw0MDLivrq5mc3Jy2pPJZLVWq/2cdbvdDSzLholoNJ1OMy6Xq0Ymk5HNZktOTU29qMgA8HYqlaKDgwNKp9M0PT09BgAM/iGuqqoimUx2yPP8U5/P9wEAMB0dHRUKheJHiUTyeGhoqAUAnE7nR0qlsjcQCLwjlsvlz+bm5mQWi0VSWlr6bXV1tU6hUMj6+/vfN5lMN0xxcfG1zWZ7SETTSqWSGhoamPHxcajV6s+8Xu9Xou7u7t9VKtW00+mkSCTC6PV6aDQa8Dw/Wl9fP8UAQCgUosvLSyovL2eWl5dRUFBw7Ha7v9vc3By5K3g1EAg8FQSBiIguLi4IgBwA2LtEjuPuJxKJ62AwKFpdXf0eQBIvYVmW/cLlchEAWK1WAADT09NzX6PR/OTz+eKVlZUzKpVqTyqVvsnzfLCkpGSrtrb2t97eXnFeXl5ZKpWyZ2RkPPP7/UUnJyefGI3GU+zt7aU4jotOTk7mAUBfX1+b1Wq9kcvlBIAcDgctLCyQxWKhoqIi6uzs/BoAVlZW3qqpqbllZmdnf1hfX//Q4/HEzWbzX+3t7fcMBgMFg0EYjUYmEolAEAREo1Hk5+fT+fk5Mzg4GD86OpJ0dXXJGQBoaWl5Ra/XP6yrq3tQVlam2N7ehslkAsuySCaTUKvVSCQS2NnZSXAcJxYEQTEyMvKeIAhLDADY7fZ7BoPhm6ysLFpbWzuan5//WKvVvsHzPEWjUSYSiSA3N5d0Oh0TjUaf+/1+S2Nj46/4FwYAr7e2tnbF4/E/iYjC4TCFw+F0LBaj/f19mpiYeID/IAagAyABYLXb7cLZ2Rml02nyer3POY6rwv8hEr34u0IkEk1mZ2cTgGMA7768/RtL5JKsGzrLIgAAAABJRU5ErkJggg==', + 'CrunchBang': 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAQCAQAAAC45EetAAAA8ElEQVR4XnWOsUpCYQBGz1TIHYu2Qix6g0DEtSeQu/UIISJtUS8gJq61F1wcdMohcBDxKUR8hsz1xA/y44/cs3znbB+RJ0Skl3pSkeFQbUs79VAPzrwPFRmN1Ja0Ug/16I93+1oi4lKte+zMXv32WuoAm43lXMrqzbFncgWw21lORf4+/PREKpAhYqZuPXZ+T/3yXbZEajV1JavUQ104sRcq0myqc5mnHurWqc/7yhExVwuPncl+C4Bu13L60ueAwcByOtLhgAIRCzU38fRGTmSxUBvSSD3Ui1NvQkXWa7Uq1dRD9R17HiqyRUSy1NP6B7e1Yu2GtlUKAAAAAElFTkSuQmCC', + 'Yuno': 'iVBORw0KGgoAAAANSUhEUgAAABgAAAAPCAYAAAD+pA/bAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAABDtJREFUOE+FlHtMm1UYxrtsi8aEgCb+oTFmZur+WNS5RaPERU10C2qGaBgb6hgwLwMmHTIKlIKlQIHSQrmU24BSSmnpBVooUmihtEC5yKWDjVu5uOkcEca4lG5E93j6EQmELX7Jky/fOed9fu973vMdGu0xT3Cgz57yXMZLDdXcy821PFWLKmuA6HqLMqtLX5POl4iYb2ukWW8IOOFe/qfe3/M4n0eOjwyZD8//bldODOk37N1yDJgl+LVdjEGLFKO9KkzZm8hbje7mIrTXZ7sMtTydrJh15H8hHW11XvN/jGS7VudcD5w34ZZzeQYb67fwYO03LN4exo1+LWzNxbA05O5QuzbHqRYn+++CHDx4YK9WLfaedfQzV5em54g5Zbi8OIml+VFMDLWQ7GXoaSmFWZsDZVGCO2u0EbkhHTrhFqi9PmelSsQ8tAtSVch60dpUeGe4kxgZxegzVkBzlQ2NKBG2+iJIMqMok9r8OLRIMqApToSqmAWTmk9B2+o2YW79oshU7ABcuvAFrVGWXkVKpBYoSaBSxIS2mINpiwbjZiUMZRloVfJQyaXDKObBpimBScpHFe8KmmXpaKhK3arGrBVuVBclHN2CiPNin1OVs1tVJYlQlyZBxA6DviQVo6ZaOKd7sTplw53BVugruBBzfsRslw7rZPxaczWutSpQV/gzJPxo1JexyfaxKBBpuiEx+tw+CpKdEvGWTprGlhcwqbIzL5/DYKMYndpK3L1hxf3ZfkrzwybUZjPhnOqmvlcmutFF1jis9QSShOrcWNSXJ1MA0ou/NZWc8Ddfe4VGO3bk0JON1dyMMlK+gmxNrZCFhZF2Kng7YNO0awt4b7wLNp2EqtAsF6ImP56SG0B6siovTYpIjg15gapCVhAfJRUyIBFEo6k8AyuTtkcC/qvG/XbDexulWJvqgYH0o0nKhVHFJ40XwFQnWM5OCX+XMg86c3KvVMSMapCmPpSTIygTxGKZZOcOXhrr3Mp4uzkFuG6B3ajE3TELDDU8qEmsmvRATxquKkxAnSTFjwKEfv3JU9JC5unG6rQ1bTkbQ4Yq/DVgxOqwBWt2K9Yne3ZCZvrgHO2k5paHzOhSiVCZSkdNTgzy40JRlPgDhDHBCxUZdCs91G8fLeK87zOl6XSOICZYXMGNhDqX9fDP/mbK2DXVi/szm03eLpejl5pzOfqwOt4JBT8OeYwQt/4R/BR0OzXiLCM5LOCji/4nXt46rpywgG+zor5RxgSdupBzJdglSY+5ZZbl3XNY6mbn7W0Lcx06zBg1WBjtcC6OmG+OmRTrFrnIUZESZeVeCpwh8TpiPsQ47/tloM97T+/6m8mg55mT3tStyL54mhlwwtszNvjzD8/6HH8i7PvvPPRioZdRWuDBZUR6pEWG7I8P9Xs1Jsj36MfvvO5J/+rTw58dP7afJPfBgeef3XGz/gskFVpJc4HwGwAAAABJRU5ErkJggg==' + } + }; + + ExpandComment = { + init: function() { + if (g.VIEW !== 'index' || !Conf['Comment Expansion']) { + return; + } + if (g.BOARD.ID === 'g') { + this.callbacks.push(Fourchan.code); + } + if (g.BOARD.ID === 'sci') { + this.callbacks.push(Fourchan.math); + } + return Post.callbacks.push({ + name: 'Comment Expansion', + cb: this.node + }); + }, + node: function() { + var a; + + if (a = $('.abbr > a:not([onclick])', this.nodes.comment)) { + return $.on(a, 'click', ExpandComment.cb); + } + }, + callbacks: [], + cb: function(e) { + e.preventDefault(); + return ExpandComment.expand(Get.postFromNode(this)); + }, + expand: function(post) { + var a; + + if (post.nodes.longComment && !post.nodes.longComment.parentNode) { + $.replace(post.nodes.shortComment, post.nodes.longComment); + post.nodes.comment = post.nodes.longComment; + return; + } + if (!(a = $('.abbr > a', post.nodes.comment))) { + return; + } + a.textContent = "Post No." + post + " Loading..."; + return $.cache("//api.4chan.org" + a.pathname + ".json", function() { + return ExpandComment.parse(this, a, post); + }); + }, + contract: function(post) { + var a; + + if (!post.nodes.shortComment) { + return; + } + a = $('.abbr > a', post.nodes.shortComment); + a.textContent = 'here'; + $.replace(post.nodes.longComment, post.nodes.shortComment); + return post.nodes.comment = post.nodes.shortComment; + }, + parse: function(req, a, post) { + var callback, clone, comment, href, postObj, posts, quote, spoilerRange, status, _i, _j, _k, _len, _len1, _len2, _ref, _ref1; + + status = req.status; + if (![200, 304].contains(status)) { + a.textContent = "Error " + req.statusText + " (" + status + ")"; + return; + } + posts = JSON.parse(req.response).posts; + if (spoilerRange = posts[0].custom_spoiler) { + Build.spoilerRange[g.BOARD] = spoilerRange; + } + for (_i = 0, _len = posts.length; _i < _len; _i++) { + postObj = posts[_i]; + if (postObj.no === post.ID) { + break; + } + } + if (postObj.no !== post.ID) { + a.textContent = "Post No." + post + " not found."; + return; + } + comment = post.nodes.comment; + clone = comment.cloneNode(false); + clone.innerHTML = postObj.com; + _ref = $$('.quotelink', clone); + for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { + quote = _ref[_j]; + href = quote.getAttribute('href'); + if (href[0] === '/') { + continue; + } + quote.href = "/" + post.board + "/res/" + href; + } + post.nodes.shortComment = comment; + $.replace(comment, clone); + post.nodes.comment = post.nodes.longComment = clone; + post.parseComment(); + post.parseQuotes(); + _ref1 = ExpandComment.callbacks; + for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { + callback = _ref1[_k]; + callback.call(post); + } + } + }; + + ExpandThread = { + init: function() { + if (g.VIEW !== 'index' || !Conf['Thread Expansion']) { + return; + } + return Thread.callbacks.push({ + name: 'Thread Expansion', + cb: this.node + }); + }, + node: function() { + var a, files, posts, span, _ref; + + if (!(span = $.x('following-sibling::span[contains(@class,"summary")][1]', this.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); + }, + 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() { + 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; + + 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("//api.4chan.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); + } + }, + 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)) { + 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 = []; + postsRoot = []; + filesCount = 0; + for (_i = 0, _len = posts.length; _i < _len; _i++) { + reply = posts[_i]; + if (post = thread.posts[reply.no]) { + if ('file' in post) { + filesCount++; + } + postsRoot.push(post.nodes.root); + continue; + } + root = Build.postFromObject(reply, 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); + postsRoot.push(root); + } + Main.callbackNodes(Post, postsObj); + $.after(a, postsRoot); + postsCount = postsRoot.length; + a.textContent = ExpandThread.text('-', postsCount, filesCount); + return Fourchan.parseThread(thread.ID, 1, postsCount); + } + }; + + FileInfo = { + init: function() { + if (g.VIEW === 'catalog' || !Conf['File Info Formatting']) { + return; + } + this.funk = this.createFunc(Conf['fileInfo']); + return Post.callbacks.push({ + name: 'File Info Formatting', + cb: this.node + }); + }, + node: function() { + if (!this.file || this.isClone) { + return; + } + return this.file.text.innerHTML = FileInfo.funk(FileInfo, this); + }, + createFunc: function(format) { + var code; + + code = format.replace(/%(.)/g, function(s, c) { + if (c in FileInfo.formatters) { + return "' + FileInfo.formatters." + c + ".call(post) + '"; + } else { + return s; + } + }); + return Function('FileInfo', 'post', "return '" + code + "'"); + }, + convertUnit: function(size, unit) { + var i; + + if (unit === 'B') { + return "" + (size.toFixed()) + " Bytes"; + } + i = 1 + ['KB', 'MB'].indexOf(unit); + while (i--) { + size /= 1024; + } + size = unit === 'MB' ? Math.round(size * 100) / 100 : size.toFixed(); + return "" + size + " " + unit; + }, + escape: function(name) { + return name.replace(/<|>/g, function(c) { + return c === '<' && '<' || '>'; + }); + }, + formatters: { + t: function() { + return this.file.URL.match(/\d+\..+$/)[0]; + }, + T: function() { + return "" + (FileInfo.formatters.t.call(this)) + ""; + }, + l: function() { + return "" + (FileInfo.formatters.n.call(this)) + ""; + }, + L: function() { + return "" + (FileInfo.formatters.N.call(this)) + ""; + }, + n: function() { + var fullname, shortname; + + fullname = this.file.name; + shortname = Build.shortFilename(this.file.name, this.isReply); + if (fullname === shortname) { + return FileInfo.escape(fullname); + } else { + return "" + (FileInfo.escape(shortname)) + "" + (FileInfo.escape(fullname)) + ""; + } + }, + N: function() { + return FileInfo.escape(this.file.name); + }, + p: function() { + if (this.file.isSpoiler) { + return 'Spoiler, '; + } else { + return ''; + } + }, + s: function() { + return this.file.size; + }, + B: function() { + return FileInfo.convertUnit(this.file.sizeInBytes, 'B'); + }, + K: function() { + return FileInfo.convertUnit(this.file.sizeInBytes, 'KB'); + }, + M: function() { + return FileInfo.convertUnit(this.file.sizeInBytes, 'MB'); + }, + r: function() { + if (this.file.isImage) { + return this.file.dimensions; + } else { + return 'PDF'; + } + } + } + }; + + Fourchan = { + init: function() { + var board; + + if (g.VIEW === 'catalog') { + return; + } + board = g.BOARD.ID; + if (board === 'g') { + $.globalEval("window.addEventListener('prettyprint', function(e) {\n var pre = e.detail;\n pre.innerHTML = prettyPrintOne(pre.innerHTML);\n}, false);"); + Post.callbacks.push({ + name: 'Parse /g/ code', + cb: this.code + }); + } + if (board === 'sci') { + $.globalEval("window.addEventListener('jsmath', function(e) {\n if (jsMath.loaded) {\n // process one post\n jsMath.ProcessBeforeShowing(e.detail);\n } else {\n // load jsMath and process whole document\n jsMath.Autoload.Script.Push('ProcessBeforeShowing', [null]);\n jsMath.Autoload.LoadJsMath();\n }\n}, false);"); + return Post.callbacks.push({ + name: 'Parse /sci/ math', + cb: this.math + }); + } + }, + code: function() { + var pre, _i, _len, _ref; + + if (this.isClone) { + return; + } + _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'); + } + }, + math: function() { + if (this.isClone || !$('.math', this.nodes.comment)) { + return; + } + return $.event('jsmath', this.nodes.post, window); + }, + parseThread: function(threadID, offset, limit) { + return $.event('4chanParsingDone', { + threadId: threadID, + offset: offset, + limit: limit + }); + } + }; + + IDColor = { + init: function() { + if (g.VIEW === 'catalog' || !Conf['Color User IDs']) { + return; + } + this.ids = {}; + return Post.callbacks.push({ + name: 'Color User IDs', + cb: this.node + }); + }, + node: function() { + var rgb, span, style, uid; + + if (this.isClone || !(uid = this.info.uniqueID)) { + return; + } + rgb = IDColor.compute(uid); + span = this.nodes.uniqueID; + style = span.style; + style.color = rgb[3]; + style.backgroundColor = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")"; + $.addClass(span, 'painted'); + span.textContent = uid; + return span.title = 'Highlight posts by this ID'; + }, + compute: function(uniqueID) { + var hash, rgb; + + if (uniqueID in IDColor.ids) { + return IDColor.ids[uniqueID]; + } + hash = this.hash(uniqueID); + rgb = [(hash >> 24) & 0xFF, (hash >> 16) & 0xFF, (hash >> 8) & 0xFF]; + rgb.push((rgb[0] * 0.299 + rgb[1] * 0.587 + rgb[2] * 0.114) > 170 ? 'black' : 'white'); + return this.ids[uniqueID] = rgb; + }, + hash: function(uniqueID) { + var i, msg, _i, _ref; + + msg = 0; + for (i = _i = 0, _ref = uniqueID.length; _i < _ref; i = _i += 1) { + msg = (msg << 5) - msg + uniqueID.charCodeAt(i); + } + return msg; + } + }; + + Keybinds = { + init: function() { + var init; + + if (g.VIEW === 'catalog' || !Conf['Keybinds']) { + return; + } + init = function() { + var node, _i, _len, _ref; + + $.off(d, '4chanXInitFinished', init); + $.on(d, 'keydown', Keybinds.keydown); + _ref = $$('[accesskey]'); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + node = _ref[_i]; + node.removeAttribute('accesskey'); + } + }; + return $.on(d, '4chanXInitFinished', init); + }, + keydown: function(e) { + var form, key, notification, notifications, op, target, thread, threadRoot, _i, _len; + + if (!(key = Keybinds.keyCode(e))) { + return; + } + target = e.target; + if (['INPUT', 'TEXTAREA'].contains(target.nodeName)) { + if (!/(Esc|Alt|Ctrl|Meta)/.test(key)) { + return; + } + } + threadRoot = Nav.getThread(); + if (op = $('.op', threadRoot)) { + thread = Get.postFromNode(op).thread; + } + switch (key) { + case Conf['Toggle board list']: + if (Conf['Custom Board Navigation']) { + Header.toggleBoardList(); + } + break; + case Conf['Toggle header']: + Header.toggleBarVisibility(); + break; + case Conf['Open empty QR']: + Keybinds.qr(threadRoot); + break; + case Conf['Open QR']: + Keybinds.qr(threadRoot, true); + break; + case Conf['Open settings']: + Settings.open(); + break; + case Conf['Close']: + if (Settings.dialog) { + Settings.close(); + } else if ((notifications = $$('.notification')).length) { + for (_i = 0, _len = notifications.length; _i < _len; _i++) { + notification = notifications[_i]; + $('.close', notification).click(); + } + } else if (QR.nodes) { + if (Conf['Persistent QR']) { + QR.hide(); + } else { + QR.close(); + } + } + break; + case Conf['Spoiler tags']: + if (target.nodeName !== 'TEXTAREA') { + return; + } + Keybinds.tags('spoiler', target); + break; + case Conf['Code tags']: + if (target.nodeName !== 'TEXTAREA') { + return; + } + Keybinds.tags('code', target); + break; + case Conf['Eqn tags']: + if (target.nodeName !== 'TEXTAREA') { + return; + } + Keybinds.tags('eqn', target); + break; + case Conf['Math tags']: + if (target.nodeName !== 'TEXTAREA') { + return; + } + Keybinds.tags('math', target); + break; + case Conf['Toggle sage']: + if (QR.nodes) { + Keybinds.sage(); + } + break; + case Conf['Submit QR']: + if (QR.nodes && !QR.status()) { + QR.submit(); + } + break; + case Conf['Watch']: + ThreadWatcher.toggle(thread); + break; + case Conf['Update']: + ThreadUpdater.update(); + break; + case Conf['Expand image']: + Keybinds.img(threadRoot); + break; + case Conf['Expand images']: + Keybinds.img(threadRoot, true); + break; + case Conf['Open Gallery']: + Gallery.cb.toggle(); + break; + case Conf['fappeTyme']: + FappeTyme.cb.fappe(); + break; + case Conf['werkTyme']: + FappeTyme.cb.werk(); + break; + case Conf['Front page']: + window.location = "/" + g.BOARD + "/0#delform"; + break; + case Conf['Open front page']: + $.open("/" + g.BOARD + "/#delform"); + break; + case Conf['Next page']: + if (g.VIEW === 'thread') { + return; + } + if (form = $('.next form')) { + window.location = form.action; + } + break; + case Conf['Previous page']: + if (g.VIEW === 'thread') { + return; + } + if (form = $('.prev form')) { + window.location = form.action; + } + break; + case Conf['Open catalog']: + if (Conf['External Catalog']) { + window.location = CatalogLinks.external(g.BOARD.ID); + } else { + window.location = "/" + g.BOARD + "/catalog"; + } + break; + case Conf['Next thread']: + if (g.VIEW !== 'index') { + return; + } + Nav.scroll(+1); + break; + case Conf['Previous thread']: + if (g.VIEW !== 'index') { + return; + } + Nav.scroll(-1); + break; + case Conf['Expand thread']: + ExpandThread.toggle(thread); + break; + case Conf['Open thread']: + Keybinds.open(thread); + break; + case Conf['Open thread tab']: + Keybinds.open(thread, true); + break; + case Conf['Next reply']: + Keybinds.hl(+1, threadRoot); + break; + case Conf['Previous reply']: + Keybinds.hl(-1, threadRoot); + break; + case Conf['Deselect reply']: + Keybinds.hl(0, threadRoot); + break; + case Conf['Hide']: + if (g.VIEW === 'index') { + ThreadHiding.toggle(thread); + } + break; + case Conf['Previous Post Quoting You']: + QuoteYou.cb.seek('preceding'); + break; + case Conf['Next Post Quoting You']: + QuoteYou.cb.seek('following'); + break; + default: + return; + } + e.preventDefault(); + return e.stopPropagation(); + }, + keyCode: function(e) { + var kc, key; + + key = (function() { + switch (kc = e.keyCode) { + case 8: + return ''; + case 13: + return 'Enter'; + case 27: + return 'Esc'; + case 37: + return 'Left'; + case 38: + return 'Up'; + case 39: + return 'Right'; + case 40: + return 'Down'; + default: + if ((48 <= kc && kc <= 57) || (65 <= kc && kc <= 90)) { + return String.fromCharCode(kc).toLowerCase(); + } else { + return null; + } + } + })(); + if (key) { + if (e.altKey) { + key = 'Alt+' + key; + } + if (e.ctrlKey) { + key = 'Ctrl+' + key; + } + if (e.metaKey) { + key = 'Meta+' + key; + } + if (e.shiftKey) { + key = 'Shift+' + key; + } + } + return key; + }, + qr: function(thread, quote) { + if (!(Conf['Quick Reply'] && QR.postingIsEnabled)) { + return; + } + QR.open(); + if (quote) { + QR.quote.call($('input', $('.post.highlight', thread) || thread)); + } + QR.nodes.com.focus(); + if (Conf['QR Shortcut']) { + return $.rmClass($('.qr-shortcut'), 'disabled'); + } + }, + tags: function(tag, ta) { + var range, selEnd, selStart, value; + + value = ta.value; + selStart = ta.selectionStart; + selEnd = ta.selectionEnd; + ta.value = value.slice(0, selStart) + ("[" + tag + "]") + value.slice(selStart, selEnd) + ("[/" + tag + "]") + value.slice(selEnd); + range = ("[" + tag + "]").length + selEnd; + ta.setSelectionRange(range, range); + return $.event('input', null, ta); + }, + sage: function() { + var isSage; + + isSage = /sage/i.test(QR.nodes.email.value); + return QR.nodes.email.value = isSage ? "" : "sage"; + }, + img: function(thread, all) { + var post; + + if (all) { + return ImageExpand.cb.toggleAll(); + } else { + post = Get.postFromNode($('.post.highlight', thread) || $('.op', thread)); + return ImageExpand.toggle(post); + } + }, + open: function(thread, tab) { + var url; + + if (g.VIEW !== 'index') { + return; + } + url = "/" + thread.board + "/res/" + thread; + if (tab) { + return $.open(url); + } else { + return location.href = url; + } + }, + hl: function(delta, thread) { + var axe, headRect, next, postEl, rect, replies, reply, root, topMargin, _i, _len; + + if (!delta) { + if (postEl = $('.reply.highlight', thread)) { + $.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) { + 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); + 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); + } + } + this.focus(next); + return; + } + } + replies = $$('.reply', thread); + if (delta === -1) { + replies.reverse(); + } + 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) { + this.focus(reply); + return; + } + } + }, + focus: function(post) { + return $.addClass(post, 'highlight'); + } + }; + + Nav = { + init: function() { + var append, next, prev, span; + + switch (g.VIEW) { + case 'index': + if (!Conf['Index Navigation']) { + return; + } + break; + case 'thread': + if (!Conf['Reply Navigation']) { + return; + } + break; + default: + return; + } + span = $.el('span', { + id: 'navlinks' + }); + prev = $.el('a', { + textContent: '▲', + href: 'javascript:;' + }); + next = $.el('a', { + textContent: '▼', + href: 'javascript:;' + }); + $.on(prev, 'click', this.prev); + $.on(next, 'click', this.next); + $.add(span, [prev, $.tn(' '), next]); + append = function() { + $.off(d, '4chanXInitFinished', append); + return $.add(d.body, span); + }; + return $.on(d, '4chanXInitFinished', append); + }, + prev: function() { + if (g.VIEW === 'thread') { + return window.scrollTo(0, 0); + } else { + return Nav.scroll(-1); + } + }, + next: function() { + if (g.VIEW === 'thread') { + return window.scrollTo(0, d.body.scrollHeight); + } else { + return Nav.scroll(+1); + } + }, + getThread: function(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; + } + } + } + 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; + } + return window.scrollBy(0, top); + } + }; + + RelativeDates = { + INTERVAL: $.MINUTE / 2, + init: function() { + if (g.VIEW === 'catalog' || !Conf['Relative Post Dates']) { + return; + } + $.on(d, 'visibilitychange ThreadUpdate', this.flush); + this.flush(); + return Post.callbacks.push({ + name: 'Relative Post Dates', + cb: this.node + }); + }, + node: function() { + var dateEl; + + if (this.isClone) { + return; + } + dateEl = this.nodes.date; + dateEl.title = dateEl.textContent; + return RelativeDates.setUpdate(this); + }, + relative: function(diff, now, date) { + var days, months, number, rounded, unit, years; + + unit = (number = diff / $.DAY) >= 1 ? (years = now.getYear() - date.getYear(), months = now.getMonth() - date.getMonth(), days = now.getDate() - date.getDate(), years > 1 ? (number = years - (months < 0 || months === 0 && days < 0), 'year') : years === 1 && (months > 0 || months === 0 && days >= 0) ? (number = years, 'year') : (months = (months + 12) % 12) > 1 ? (number = months - (days < 0), 'month') : months === 1 && days >= 0 ? (number = months, 'month') : 'day') : (number = diff / $.HOUR) >= 1 ? 'hour' : (number = diff / $.MINUTE) >= 1 ? 'minute' : (number = Math.max(0, diff) / $.SECOND, 'second'); + rounded = Math.round(number); + if (rounded !== 1) { + unit += 's'; + } + return "" + rounded + " " + unit + " ago"; + }, + stale: [], + flush: function() { + var now, update, _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); + } + 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); + 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()); + } + }; + + RemoveSpoilers = { + init: function() { + if (Conf['Reveal Spoilers'] && !Conf['Remove Spoilers']) { + $.addClass(doc, 'reveal-spoilers'); + } + if (!Conf['Remove Spoilers']) { + return; + } + if (Conf['Reveal Spoilers']) { + this.wrapper = function(text) { + return "[spoiler]" + text + "[/spoiler]"; + }; + } + return Post.callbacks.push({ + name: 'Reveal Spoilers', + cb: this.node + }); + }, + wrapper: function(text) { + return text; + }, + node: function(post) { + var spoiler, spoilers, _i, _len; + + spoilers = $$('s', this.nodes.comment); + for (_i = 0, _len = spoilers.length; _i < _len; _i++) { + spoiler = spoilers[_i]; + $.replace(spoiler, $.tn(RemoveSpoilers.wrapper(spoiler.textContent))); + } + } + }; + + Report = { + init: function() { + if (!/report/.test(location.search)) { + return; + } + return $.asap((function() { + return $.id('recaptcha_response_field'); + }), Report.ready); + }, + ready: function() { + var field; + + field = $.id('recaptcha_response_field'); + $.on(field, 'keydown', function(e) { + if (e.keyCode === 8 && !field.value) { + return $.globalEval('Recaptcha.reload("t")'); + } + }); + return $.on($('form'), 'submit', function(e) { + var response; + + e.preventDefault(); + response = field.value.trim(); + if (!/\s/.test(response)) { + field.value = "" + response + " " + response; + } + return this.submit(); + }); + } + }; + + Time = { + init: function() { + if (g.VIEW === 'catalog' || !Conf['Time Formatting']) { + return; + } + this.funk = this.createFunc(Conf['time']); + return Post.callbacks.push({ + name: 'Time Formatting', + cb: this.node + }); + }, + node: function() { + if (this.isClone) { + return; + } + return this.nodes.date.textContent = Time.funk(Time, this.info.date); + }, + createFunc: function(format) { + var code; + + code = format.replace(/%([A-Za-z])/g, function(s, c) { + if (c in Time.formatters) { + return "' + Time.formatters." + c + ".call(date) + '"; + } else { + return s; + } + }); + return Function('Time', 'date', "return '" + code + "'"); + }, + day: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + month: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + zeroPad: function(n) { + if (n < 10) { + return "0" + n; + } else { + return n; + } + }, + formatters: { + a: function() { + return Time.day[this.getDay()].slice(0, 3); + }, + A: function() { + return Time.day[this.getDay()]; + }, + b: function() { + return Time.month[this.getMonth()].slice(0, 3); + }, + B: function() { + return Time.month[this.getMonth()]; + }, + d: function() { + return Time.zeroPad(this.getDate()); + }, + e: function() { + return this.getDate(); + }, + H: function() { + return Time.zeroPad(this.getHours()); + }, + I: function() { + return Time.zeroPad(this.getHours() % 12 || 12); + }, + k: function() { + return this.getHours(); + }, + l: function() { + return this.getHours() % 12 || 12; + }, + m: function() { + return Time.zeroPad(this.getMonth() + 1); + }, + M: function() { + return Time.zeroPad(this.getMinutes()); + }, + p: function() { + if (this.getHours() < 12) { + return 'AM'; + } else { + return 'PM'; + } + }, + P: function() { + if (this.getHours() < 12) { + return 'am'; + } else { + return 'pm'; + } + }, + S: function() { + return Time.zeroPad(this.getSeconds()); + }, + y: function() { + return this.getFullYear().toString().slice(2); + }, + Y: function() { + return this.getFullYear(); + } + } + }; + + Settings = { + init: function() { + var link, settings; + + link = $.el('a', { + className: 'settings-link fourchanx-icon icon-wrench', + textContent: 'Settings', + href: 'javascript:;' + }); + $.on(link, 'click', Settings.open); + Header.addShortcut(link); + $.get('previousversion', null, function(item) { + var changelog, el, previous; + + if (previous = item['previousversion']) { + if (previous === g.VERSION) { + return; + } + changelog = 'https://github.com/seaweedchan/4chan-x/blob/master/CHANGELOG.md'; + el = $.el('span', { + innerHTML: "4chan X has been updated to version " + g.VERSION + "." + }); + if (Conf['Show Updated Notifications']) { + new Notice('info', el, 30); + } + } else { + $.on(d, '4chanXInitFinished', Settings.open); + } + return $.set('previousversion', g.VERSION); + }); + Settings.addSection('Main', Settings.main); + Settings.addSection('Filter', Settings.filter); + Settings.addSection('Sauce', Settings.sauce); + Settings.addSection('Advanced', Settings.advanced); + Settings.addSection('Keybinds', Settings.keybinds); + $.on(d, 'AddSettingsSection', Settings.addSection); + $.on(d, 'OpenSettings', function(e) { + return Settings.open(e.detail); + }); + settings = JSON.parse(localStorage.getItem('4chan-settings')) || {}; + if (settings.disableAll) { + return; + } + settings.disableAll = true; + return localStorage.setItem('4chan-settings', JSON.stringify(settings)); + }, + open: function(openSection) { + var dialog, html, link, links, overlay, section, sectionToOpen, _i, _len, _ref; + + $.off(d, '4chanXInitFinished', Settings.open); + if (Settings.dialog) { + return; + } + $.event('CloseMenu'); + html = "
"; + Settings.overlay = overlay = $.el('div', { + id: 'overlay' + }); + Settings.dialog = dialog = $.el('div', { + id: 'fourchanx-settings', + className: 'dialog', + innerHTML: html + }); + $.on($('.export', Settings.dialog), 'click', Settings["export"]); + $.on($('.import', Settings.dialog), 'click', Settings["import"]); + $.on($('input', Settings.dialog), 'change', Settings.onImport); + links = []; + _ref = Settings.sections; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + section = _ref[_i]; + link = $.el('a', { + className: "tab-" + section.hyphenatedTitle, + textContent: section.title, + href: 'javascript:;' + }); + $.on(link, 'click', Settings.openSection.bind(section)); + links.push(link, $.tn(' | ')); + if (section.title === openSection) { + sectionToOpen = link; + } + } + links.pop(); + $.add($('.sections-list', dialog), links); + (sectionToOpen ? sectionToOpen : links[0]).click(); + $.on($('.close', dialog), 'click', Settings.close); + $.on(overlay, 'click', Settings.close); + $.add(d.body, [overlay, dialog]); + return $.event('OpenSettings', null, dialog); + }, + close: function() { + if (!Settings.dialog) { + return; + } + $.rm(Settings.overlay); + $.rm(Settings.dialog); + delete Settings.overlay; + return delete Settings.dialog; + }, + sections: [], + addSection: function(title, open) { + var hyphenatedTitle, _ref; + + if (typeof title !== 'string') { + _ref = title.detail, title = _ref.title, open = _ref.open; + } + hyphenatedTitle = title.toLowerCase().replace(/\s+/g, '-'); + return Settings.sections.push({ + title: title, + hyphenatedTitle: hyphenatedTitle, + open: open + }); + }, + openSection: function() { + var section, selected; + + if (selected = $('.tab-selected', Settings.dialog)) { + $.rmClass(selected, 'tab-selected'); + } + $.addClass($(".tab-" + this.hyphenatedTitle, Settings.dialog), 'tab-selected'); + section = $('section', Settings.dialog); + $.rmAll(section); + section.className = "section-" + this.hyphenatedTitle; + this.open(section, g); + section.scrollTop = 0; + return $.event('OpenSettings', null, section); + }, + main: function(section) { + var arr, button, description, div, fs, hiddenNum, input, inputs, items, key, obj, _ref; + + items = {}; + inputs = {}; + _ref = Config.main; + for (key in _ref) { + obj = _ref[key]; + fs = $.el('fieldset', { + innerHTML: "" + key + "" + }); + for (key in obj) { + arr = obj[key]; + description = arr[1]; + div = $.el('div', { + innerHTML: ": " + description + "" + }); + input = $('input', div); + $.on(input, 'change', $.cb.checked); + items[key] = Conf[key]; + inputs[key] = input; + $.add(fs, div); + } + $.add(section, fs); + } + $.get(items, function(items) { + var val; + + for (key in items) { + val = items[key]; + inputs[key].checked = val; + } + }); + div = $.el('div', { + innerHTML: ": Clear manually-hidden threads and posts on all boards. Refresh the page to apply." + }); + button = $('button', div); + hiddenNum = 0; + $.get('hiddenThreads', { + boards: {} + }, function(item) { + var ID, board, thread, _ref1; + + _ref1 = item.hiddenThreads.boards; + for (ID in _ref1) { + board = _ref1[ID]; + for (ID in board) { + thread = board[ID]; + hiddenNum++; + } + } + return button.textContent = "Hidden: " + hiddenNum; + }); + $.get('hiddenPosts', { + boards: {} + }, function(item) { + var ID, board, post, thread, _ref1; + + _ref1 = item.hiddenPosts.boards; + for (ID in _ref1) { + board = _ref1[ID]; + for (ID in board) { + thread = board[ID]; + for (ID in thread) { + post = thread[ID]; + hiddenNum++; + } + } + } + return button.textContent = "Hidden: " + hiddenNum; + }); + $.on(button, 'click', function() { + this.textContent = 'Hidden: 0'; + return $.get('hiddenThreads', { + boards: {} + }, function(item) { + var boardID; + + for (boardID in item.hiddenThreads.boards) { + localStorage.removeItem("4chan-hide-t-" + boardID); + } + return $["delete"](['hiddenThreads', 'hiddenPosts']); + }); + }); + return $.after($('input[name="Stubs"]', section).parentNode.parentNode, div); + }, + "export": function(now, data) { + var a, db, p, _i, _len, _ref; + + if (typeof now !== 'number') { + now = Date.now(); + data = { + version: g.VERSION, + date: now + }; + _ref = DataBoard.keys; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + db = _ref[_i]; + Conf[db] = { + boards: {} + }; + } + $.get(Conf, function(Conf) { + delete Conf['archives']; + data.Conf = Conf; + return Settings["export"](now, data); + }); + return; + } + a = $.el('a', { + className: 'warning', + textContent: 'Save me!', + download: "4chan X v" + g.VERSION + "-" + now + ".json", + href: "data:application/json;base64," + (btoa(unescape(encodeURIComponent(JSON.stringify(data, null, 2))))), + target: '_blank' + }); + p = $('.imp-exp-result', Settings.dialog); + $.rmAll(p); + return $.add(p, a); + }, + "import": function() { + return this.nextElementSibling.click(); + }, + onImport: function() { + var file, output, reader; + + if (!(file = this.files[0])) { + return; + } + output = $('.imp-exp-result'); + if (!confirm('Your current settings will be entirely overwritten, are you sure?')) { + output.textContent = 'Import aborted.'; + return; + } + reader = new FileReader(); + reader.onload = function(e) { + var data, err; + + try { + data = JSON.parse(e.target.result); + Settings.loadSettings(data); + if (confirm('Import successful. Refresh now?')) { + return window.location.reload(); + } + } catch (_error) { + err = _error; + output.textContent = 'Import failed due to an error.'; + return c.error(err.stack); + } + }; + return reader.readAsText(file); + }, + loadSettings: function(data) { + var key, val, version, _ref; + + version = data.version.split('.'); + if (version[0] === '2') { + data = Settings.convertSettings(data, { + 'Disable 4chan\'s extension': '', + 'Catalog Links': '', + 'Reply Navigation': '', + 'Show Stubs': 'Stubs', + 'Image Auto-Gif': 'Auto-GIF', + 'Expand From Current': '', + 'Unread Tab Icon': 'Unread Favicon', + 'Post in Title': 'Thread Excerpt', + 'Auto Hide QR': '', + 'Open Reply in New Tab': '', + 'Remember QR size': '', + 'Quote Inline': 'Quote Inlining', + 'Quote Preview': 'Quote Previewing', + 'Indicate OP quote': 'Mark OP Quotes', + 'Indicate Cross-thread Quotes': 'Mark Cross-thread Quotes', + 'Reply Hiding': 'Reply Hiding Buttons', + 'Thread Hiding': 'Thread Hiding Buttons', + 'uniqueid': 'uniqueID', + 'mod': 'capcode', + 'country': 'flag', + 'md5': 'MD5', + 'openEmptyQR': 'Open empty QR', + 'openQR': 'Open QR', + 'openOptions': 'Open settings', + 'close': 'Close', + 'spoiler': 'Spoiler tags', + 'code': 'Code tags', + 'submit': 'Submit QR', + 'watch': 'Watch', + 'update': 'Update', + 'unreadCountTo0': '', + 'expandAllImages': 'Expand images', + 'expandImage': 'Expand image', + 'zero': 'Front page', + 'nextPage': 'Next page', + 'previousPage': 'Previous page', + 'nextThread': 'Next thread', + 'previousThread': 'Previous thread', + 'expandThread': 'Expand thread', + 'openThreadTab': 'Open thread', + 'openThread': 'Open thread tab', + 'nextReply': 'Next reply', + 'previousReply': 'Previous reply', + 'hide': 'Hide', + 'Scrolling': 'Auto Scroll', + 'Verbose': '' + }); + data.Conf.sauces = data.Conf.sauces.replace(/\$\d/g, function(c) { + switch (c) { + case '$1': + return '%TURL'; + case '$2': + return '%URL'; + case '$3': + return '%MD5'; + case '$4': + return '%board'; + default: + return c; + } + }); + _ref = Config.hotkeys; + for (key in _ref) { + val = _ref[key]; + if (key in data.Conf) { + data.Conf[key] = data.Conf[key].replace(/ctrl|alt|meta/g, function(s) { + return "" + (s[0].toUpperCase()) + s.slice(1); + }).replace(/(^|.+\+)[A-Z]$/g, function(s) { + return "Shift+" + s.slice(0, -1) + (s.slice(-1).toLowerCase()); + }); + } + } + data.Conf['WatchedThreads'] = data.WatchedThreads; + } + if (data.Conf['WatchedThreads']) { + data.Conf['watchedThreads'] = { + boards: ThreadWatcher.convert(data.Conf['WatchedThreads']) + }; + delete data.Conf['WatchedThreads']; + } + return $.set(data.Conf); + }, + convertSettings: function(data, map) { + var newKey, prevKey; + + for (prevKey in map) { + newKey = map[prevKey]; + if (newKey) { + data.Conf[newKey] = data.Conf[prevKey]; + } + delete data.Conf[prevKey]; + } + return data; + }, + filter: function(section) { + var select; + + section.innerHTML = "
"; + select = $('select', section); + $.on(select, 'change', Settings.selectFilter); + return Settings.selectFilter.call(select); + }, + selectFilter: function() { + var div, name, ta; + + div = this.nextElementSibling; + if ((name = this.value) !== 'guide') { + $.rmAll(div); + ta = $.el('textarea', { + name: name, + className: 'field', + spellcheck: false + }); + $.get(name, Conf[name], function(item) { + return ta.value = item[name]; + }); + $.on(ta, 'change', $.cb.value); + $.add(div, ta); + return; + } + return div.innerHTML = "
Filter is disabled.

\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
"; + }, + 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.
"; + ta = $('textarea', section); + $.get('sauces', Conf['sauces'], function(item) { + return ta.value = item['sauces']; + }); + return $.on(ta, 'change', $.cb.value); + }, + 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:
"; + items = {}; + inputs = {}; + _ref = ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'sageEmoji', 'emojiPos', 'usercss']; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + name = _ref[_i]; + input = $("[name=" + name + "]", section); + items[name] = Conf[name]; + inputs[name] = input; + event = ['favicon', 'usercss', 'sageEmoji', 'emojiPos'].contains(name) ? 'change' : 'input'; + $.on(input, event, $.cb.value); + } + ta = $('.personafield', section); + $.get('QR.personas', Conf['QR.personas'], function(item) { + return ta.value = item['QR.personas']; + }); + $.on(ta, 'change', $.cb.value); + $.get(items, function(items) { + var key, val; + + for (key in items) { + val = items[key]; + if (['emojiPos'].contains(key)) { + continue; + } + input = inputs[key]; + input.value = val; + if (key === 'usercss') { + continue; + } + $.on(input, event, Settings[key]); + Settings[key].call(input); + } + }); + $.on($('input[name=Interval]', section), 'change', ThreadUpdater.cb.interval); + $.on($('input[name="Custom CSS"]', section), 'change', Settings.togglecss); + $.on($.id('apply-css'), 'click', Settings.usercss); + boards = {}; + _ref1 = Redirect.archives; + for (name in _ref1) { + archive = _ref1[name]; + _ref2 = archive.boards; + for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) { + boardID = _ref2[_j]; + data = boards[boardID] || (boards[boardID] = { + thread: [], + post: [], + file: [] + }); + data.thread.push(name); + if (archive.software === 'foolfuuka') { + data.post.push(name); + } + if (archive.files.contains(boardID)) { + data.file.push(name); + } + } + } + rows = []; + boardOptions = []; + _ref3 = Object.keys(boards).sort(); + for (_k = 0, _len2 = _ref3.length; _k < _len2; _k++) { + boardID = _ref3[_k]; + row = $.el('tr', { + className: "board-" + boardID + }); + row.hidden = boardID !== g.BOARD.ID; + boardOptions.push($.el('option', { + textContent: "/" + boardID + "/", + value: "board-" + boardID, + selected: boardID === g.BOARD.ID + })); + data = boards[boardID]; + _ref4 = ['thread', 'post', 'file']; + for (_l = 0, _len3 = _ref4.length; _l < _len3; _l++) { + item = _ref4[_l]; + $.add(row, Settings.addArchiveCell(boardID, data, item)); + } + rows.push(row); + } + $.add($('tbody', section), rows); + boardSelect = $('#archive-board-select', section); + $.add(boardSelect, boardOptions); + table = $.id('archive-table'); + $.on(boardSelect, 'change', function() { + $('tbody > :not([hidden])', table).hidden = true; + return $("tbody > ." + this.value, table).hidden = false; + }); + $.get('selectedArchives', Conf['selectedArchives'], function(_arg) { + var option, selectedArchives, type; + + selectedArchives = _arg.selectedArchives; + for (boardID in selectedArchives) { + data = selectedArchives[boardID]; + for (type in data) { + name = data[type]; + if (option = $("select[data-boardid='" + boardID + "'][data-type='" + type + "'] > option[value='" + name + "']", section)) { + option.selected = true; + } + } + } + }); + }, + addArchiveCell: function(boardID, data, type) { + var archive, i, length, options, select, td; + + length = data[type].length; + td = $.el('td', { + className: 'archive-cell' + }); + if (!length) { + td.textContent = '--'; + return td; + } + options = []; + i = 0; + while (i < length) { + archive = data[type][i++]; + options.push($.el('option', { + textContent: archive, + value: archive + })); + } + td.innerHTML = ''; + select = td.firstElementChild; + if (!(select.disabled = length === 1)) { + select.setAttribute('data-boardid', boardID); + select.setAttribute('data-type', type); + $.on(select, 'change', Settings.saveSelectedArchive); + } + $.add(select, options); + return td; + }, + saveSelectedArchive: function() { + var _this = this; + + return $.get('selectedArchives', Conf['selectedArchives'], function(_arg) { + var selectedArchives, _name; + + selectedArchives = _arg.selectedArchives; + (selectedArchives[_name = _this.dataset.boardid] || (selectedArchives[_name] = {}))[_this.dataset.type] = _this.value; + return $.set('selectedArchives', selectedArchives); + }); + }, + boardnav: function() { + return Header.generateBoardList(this.value); + }, + time: function() { + var funk; + + funk = Time.createFunc(this.value); + return this.nextElementSibling.textContent = funk(Time, new Date()); + }, + backlink: function() { + return this.nextElementSibling.textContent = this.value.replace(/%id/, '123456789'); + }, + fileInfo: function() { + var data, funk; + + data = { + isReply: true, + file: { + URL: '//images.4chan.org/g/src/1334437723720.jpg', + name: 'd9bb2efc98dd0df141a94399ff5880b7.jpg', + size: '276 KB', + sizeInBytes: 276 * 1024, + dimensions: '1280x720', + isImage: true, + isSpoiler: true + } + }; + funk = FileInfo.createFunc(this.value); + return this.nextElementSibling.innerHTML = funk(FileInfo, data); + }, + favicon: function() { + Favicon["switch"](); + if (g.VIEW === 'thread' && Conf['Unread Favicon']) { + Unread.update(); + } + return this.nextElementSibling.innerHTML = "\n\n\n"; + }, + sageEmoji: function() { + return this.nextElementSibling.innerHTML = ""; + }, + togglecss: function() { + if ($('textarea[name=usercss]', $.x('ancestor::fieldset[1]', this)).disabled = !this.checked) { + CustomCSS.rmStyle(); + } else { + CustomCSS.addStyle(); + } + return $.cb.checked.call(this); + }, + usercss: function() { + return CustomCSS.update(); + }, + keybinds: function(section) { + var arr, input, inputs, items, key, tbody, tr, _ref; + + section.innerHTML = "
Keybinds are disabled.
Allowed keys: a-z, 0-9, Ctrl, Shift, Alt, Meta, Enter, Esc, Up, Down, Right, Left.
Press Backspace to disable a keybind.
ActionsKeybinds
"; + tbody = $('tbody', section); + items = {}; + inputs = {}; + _ref = Config.hotkeys; + for (key in _ref) { + arr = _ref[key]; + tr = $.el('tr', { + innerHTML: "" + arr[1] + "" + }); + input = $('input', tr); + input.name = key; + input.spellcheck = false; + items[key] = Conf[key]; + inputs[key] = input; + $.on(input, 'keydown', Settings.keybind); + $.add(tbody, tr); + } + return $.get(items, function(items) { + var val; + + for (key in items) { + val = items[key]; + inputs[key].value = val; + } + }); + }, + keybind: function(e) { + var key; + + if (e.keyCode === 9) { + return; + } + e.preventDefault(); + e.stopPropagation(); + if ((key = Keybinds.keyCode(e)) == null) { + return; + } + this.value = key; + return $.cb.value.call(this); + } + }; + + Main = { + init: function() { + var db, flatten, _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(); + }); + $.on(d, '4chanMainInit', Main.initStyle); + return $.asap((function() { + return d.head && $('link[rel="shortcut icon"]', d.head) || d.readyState !== 'loading'; + }), Main.initStyle); + }, + initFeatures: function() { + var init, pathname, _ref; + + pathname = location.pathname.split('/'); + g.BOARD = new Board(pathname[1]); + if ((_ref = g.BOARD.ID) === 'z' || _ref === 'fk') { + return; + } + g.VIEW = (function() { + switch (pathname[2]) { + case 'res': + return 'thread'; + case 'catalog': + return 'catalog'; + default: + return 'index'; + } + })(); + if (g.VIEW === 'thread') { + g.THREADID = +pathname[3]; + } + switch (location.hostname) { + case 'api.4chan.org': + return; + case 'sys.4chan.org': + Report.init(); + return; + case 'images.4chan.org': + $.ready(function() { + var URL; + + if (Conf['404 Redirect'] && ['4chan - Temporarily Offline', '4chan - 404 Not Found'].contains(d.title)) { + Redirect.init(); + pathname = location.pathname.split('/'); + URL = Redirect.to('file', { + boardID: g.BOARD.ID, + filename: pathname[pathname.length - 1] + }); + if (URL) { + return location.replace(URL); + } + } + }); + return; + } + init = function(features) { + var err, module, name; + + for (name in features) { + module = features[name]; + try { + module.init(); + } catch (_error) { + err = _error; + Main.handleErrors({ + message: "\"" + name + "\" initialization crashed.", + error: err + }); + } + } + }; + init({ + 'Polyfill': Polyfill, + 'Redirect': Redirect, + 'Header': Header, + 'Catalog Links': CatalogLinks, + 'Settings': Settings, + 'Announcement Hiding': PSAHiding, + 'Fourchan thingies': Fourchan, + 'Emoji': Emoji, + 'Color User IDs': IDColor, + 'Custom CSS': CustomCSS, + 'Linkify': Linkify, + 'Reveal Spoilers': RemoveSpoilers, + 'Resurrect Quotes': Quotify, + 'Filter': Filter, + 'Thread Hiding Buttons': ThreadHiding, + 'Reply Hiding Buttons': PostHiding, + 'Recursive': Recursive, + 'Strike-through Quotes': QuoteStrikeThrough, + 'Quick Reply': QR, + 'Menu': Menu, + 'Report Link': ReportLink, + 'Thread Hiding (Menu)': ThreadHiding.menu, + 'Reply Hiding (Menu)': PostHiding.menu, + 'Delete Link': DeleteLink, + 'Filter (Menu)': Filter.menu, + 'Download Link': DownloadLink, + 'Archive Link': ArchiveLink, + 'Quote Inlining': QuoteInline, + 'Quote Previewing': QuotePreview, + 'Quote Backlinks': QuoteBacklink, + 'Mark Quotes of You': QuoteYou, + 'Mark OP Quotes': QuoteOP, + 'Mark Cross-thread Quotes': QuoteCT, + 'Anonymize': Anonymize, + 'Time Formatting': Time, + 'Relative Post Dates': RelativeDates, + 'File Info Formatting': FileInfo, + 'Fappe Tyme': FappeTyme, + 'Gallery': Gallery, + 'Gallery (menu)': Gallery.menu, + 'Sauce': Sauce, + 'Image Expansion': ImageExpand, + 'Image Expansion (Menu)': ImageExpand.menu, + 'Reveal Spoiler Thumbnails': RevealSpoilers, + 'Image Loading': ImageLoader, + 'Image Hover': ImageHover, + 'Comment Expansion': ExpandComment, + 'Thread Expansion': ExpandThread, + 'Thread Excerpt': ThreadExcerpt, + 'Favicon': Favicon, + 'Unread': Unread, + 'Quote Threading': QuoteThreading, + 'Thread Stats': ThreadStats, + 'Thread Updater': ThreadUpdater, + 'Thread Watcher': ThreadWatcher, + 'Thread Watcher (Menu)': ThreadWatcher.menu, + 'Index Navigation': Nav, + 'Keybinds': Keybinds, + 'Show Dice Roll': Dice, + 'Banner': Banner + }); + $.on(d, 'AddCallback', Main.addCallback); + return $.ready(Main.initReady); + }, + initStyle: function() { + var mainStyleSheet, setStyle, style, styleSheets, _ref; + + $.off(d, '4chanMainInit', Main.initStyle); + if (!Main.isThisPageLegit() || $.hasClass(doc, 'fourchan-x')) { + return; + } + if ((_ref = $('link[href*=mobile]', d.head)) != null) { + _ref.disabled = true; + } + $.addClass(doc, 'gecko'); + $.addClass(doc, 'fourchan-x'); + $.addClass(doc, 'seaweedchan'); + $.addClass(doc, g.VIEW); + $.addStyle(Main.css); + if (g.VIEW === 'catalog') { + $.addClass(doc, $.id('base-css').href.match(/catalog_(\w+)/)[1].replace('_new', '').replace(/_+/g, '-')); + return; + } + style = 'yotsuba-b'; + mainStyleSheet = $('link[title=switch]', d.head); + styleSheets = $$('link[rel="alternate stylesheet"]', d.head); + setStyle = function() { + var styleSheet, _i, _len; + + $.rmClass(doc, style); + for (_i = 0, _len = styleSheets.length; _i < _len; _i++) { + styleSheet = styleSheets[_i]; + if (styleSheet.href === mainStyleSheet.href) { + style = styleSheet.title.toLowerCase().replace('new', '').trim().replace(/\s+/g, '-'); + break; + } + } + return $.addClass(doc, style); + }; + setStyle(); + if (!mainStyleSheet) { + return; + } + return new MutationObserver(setStyle).observe(mainStyleSheet, { + attributes: true, + attributeFilter: ['href'] + }); + }, + initReady: function() { + var 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)) { + if (Conf['404 Redirect'] && g.VIEW === 'thread') { + href = Redirect.to('thread', { + boardID: g.BOARD.ID, + threadID: g.THREADID, + postID: +location.hash.match(/\d+/) + }); + location.replace(href || ("/" + g.BOARD + "/")); + } + return; + } + Main.initStyle(); + if (board = $('.board')) { + threads = []; + posts = []; + _ref = $$('.board > .thread', board); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + threadRoot = _ref[_i]; + thread = new Thread(+threadRoot.id.slice(1), g.BOARD); + threads.push(thread); + _ref1 = $$('.thread > .postContainer', threadRoot); + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + postRoot = _ref1[_j]; + try { + posts.push(new Post(postRoot, thread, g.BOARD)); + } catch (_error) { + err = _error; + if (!errors) { + errors = []; + } + errors.push({ + message: "Parsing of Post No." + (postRoot.id.match(/\d+/)) + " failed. Post will be skipped.", + error: err + }); + } + } + } + if (errors) { + Main.handleErrors(errors); + } + Main.callbackNodes(Thread, threads); + Main.callbackNodesDB(Post, posts, function() { + return $.event('4chanXInitFinished'); + }); + if (styleSelector = $.id('styleSelector')) { + passLink = $.el('a', { + textContent: '4chan Pass', + href: 'javascript:;' + }); + $.on(passLink, 'click', function() { + return window.open('//sys.4chan.org/auth', 'This will steal your data.', 'left=0,top=0,width=500,height=255,toolbar=0,resizable=0'); + }); + $.before(styleSelector.previousSibling, [$.tn('['), passLink, $.tn(']\u00A0\u00A0')]); + } + return; + } + try { + 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 $.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); + } + }, + callbackNodesDB: function(klass, nodes, cb) { + var errors, func, i, len, node, queue, softTask; + + queue = []; + softTask = function() { + var args, func, task; + + task = queue.shift(); + func = task[0]; + args = Array.prototype.slice.call(task, 1); + func.apply(func, args); + if (!queue.length) { + return; + } + if ((queue.length % 7) === 0) { + return setTimeout(softTask, 0); + } else { + return softTask(); + } + }; + len = nodes.length; + i = 0; + errors = null; + func = function(node, i) { + 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 (i === len) { + if (errors) { + Main.handleErrors(errors); + } + if (cb) { + return cb(); + } + } + }; + while (i < len) { + node = nodes[i]; + queue.push([func, node, ++i]); + } + return softTask(); + }, + addCallback: function(e) { + var Klass, obj; + + obj = e.detail; + if (typeof obj.callback.name !== 'string') { + throw new Error("Invalid callback name: " + obj.callback.name); + } + switch (obj.type) { + case 'Post': + Klass = Post; + break; + case 'Thread': + Klass = Thread; + break; + default: + return; + } + obj.callback.isAddon = true; + return Klass.callbacks.push(obj.callback); + }, + handleErrors: function(errors) { + var div, error, logs, _i, _len; + + if (!(errors instanceof Array)) { + error = errors; + } else if (errors.length === 1) { + error = errors[0]; + } + if (error) { + new Notice('error', Main.parseError(error), 15); + return; + } + div = $.el('div', { + innerHTML: "" + errors.length + " errors occurred. [show]" + }); + $.on(div.lastElementChild, 'click', function() { + var _ref; + + return _ref = this.textContent === 'show' ? ['hide', false] : ['show', true], this.textContent = _ref[0], logs.hidden = _ref[1], _ref; + }); + logs = $.el('div', { + hidden: true + }); + for (_i = 0, _len = errors.length; _i < _len; _i++) { + error = errors[_i]; + $.add(logs, Main.parseError(error)); + } + return new Notice('error', [div, logs], 30); + }, + parseError: function(data) { + var error, message; + + Main.logError(data); + message = $.el('div', { + textContent: data.message + }); + error = $.el('div', { + textContent: data.error + }); + 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)) { + Main.thisPageIsLegit = location.hostname === 'boards.4chan.org' && !$('link[href*="favicon-status.ico"]', d.head) && ((_ref = d.title) !== '4chan - Temporarily Offline' && _ref !== '4chan - Error' && _ref !== '504 Gateway Time-out'); + } + return Main.thisPageIsLegit; + }, + css: "/*! * Font Awesome 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; } [hidden] { 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,\"); }" + }; + + Main.init(); + +}).call(this); diff --git a/builds/appchan-x.user.js b/builds/appchan-x.user.js index 7841cc544..ddcf2c3ee 100644 --- a/builds/appchan-x.user.js +++ b/builds/appchan-x.user.js @@ -20,7 +20,7 @@ // ==/UserScript== /* -* appchan x - Version 2.3.10 - 2013-09-24 +* appchan x - Version 2.3.10 - 2013-10-13 * * Licensed under the MIT license. * https://github.com/zixaphir/appchan-x/blob/master/LICENSE @@ -108,7 +108,7 @@ 'use strict'; (function() { - var $, $$, Anonymize, ArchiveLink, Banner, Board, Build, CatalogLinks, Clone, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, GlobalMessage, Header, IDColor, ImageExpand, ImageHover, ImageLoader, JSColor, Keybinds, Linkify, Main, MascotTools, Mascots, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Rice, Sauce, Settings, Style, ThemeTools, Themes, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, editMascot, editTheme, g, userNavigation, + 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, GlobalMessage, Header, IDColor, ImageExpand, ImageHover, ImageLoader, JSColor, Keybinds, Linkify, Main, MascotTools, Mascots, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Rice, Sauce, Settings, Style, ThemeTools, Themes, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, editMascot, editTheme, g, userNavigation, __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; }, @@ -3209,7 +3209,7 @@ })(); Thread = (function() { - Thread.prototype.callbacks = []; + Thread.callbacks = []; Thread.prototype.toString = function() { return this.ID; @@ -3233,7 +3233,7 @@ })(); Post = (function() { - Post.prototype.callbacks = []; + Post.callbacks = []; Post.prototype.toString = function() { return this.ID; @@ -4263,7 +4263,7 @@ emailStart = ''; emailEnd = ''; } - subject = "" + (subject || '') + ""; + subject = " " + (subject || '') + " "; userID = !capcode && uniqueID ? (" (ID: ") + ("" + uniqueID + ") ") : ''; switch (capcode) { case 'admin': @@ -4330,7 +4330,7 @@ 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++) { @@ -5048,7 +5048,7 @@ if (g.VIEW === 'catalog' || !Conf['Anonymize']) { return; } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Anonymize', cb: this.node }); @@ -5139,7 +5139,7 @@ if (!Object.keys(this.filters).length) { return; } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Filter', cb: this.node }); @@ -5369,7 +5369,7 @@ $.addClass(doc, "reply-hide"); } this.db = new DataBoard('hiddenPosts'); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Reply Hiding', cb: this.node }); @@ -5672,7 +5672,7 @@ if (g.VIEW === 'catalog') { return; } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Recursive', cb: this.node }); @@ -5743,7 +5743,7 @@ } this.db = new DataBoard('hiddenThreads'); this.syncCatalog(); - return Thread.prototype.callbacks.push({ + return Thread.callbacks.push({ name: 'Thread Hiding', cb: this.node }); @@ -6012,11 +6012,11 @@ format = Conf['backlink'].replace(/%id/g, "' + id + '"); this.funk = Function('id', "return '" + format + "'"); this.containers = {}; - Post.prototype.callbacks.push({ + Post.callbacks.push({ name: 'Quote Backlinking Part 1', cb: this.firstNode }); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Quote Backlinking Part 2', cb: this.secondNode }); @@ -6095,7 +6095,7 @@ ExpandComment.callbacks.push(this.node); } this.text = '\u00A0(Cross-thread)'; - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Mark Cross-thread Quotes', cb: this.node }); @@ -6156,7 +6156,7 @@ if (Conf['Comment Expansion']) { ExpandComment.callbacks.push(this.node); } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Quote Inlining', cb: this.node }); @@ -6258,7 +6258,7 @@ ExpandComment.callbacks.push(this.node); } this.text = '\u00A0(OP)'; - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Mark OP Quotes', cb: this.node }); @@ -6301,7 +6301,7 @@ if (Conf['Comment Expansion']) { ExpandComment.callbacks.push(this.node); } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Quote Previewing', cb: this.node }); @@ -6384,7 +6384,7 @@ if (g.VIEW === 'catalog' || !Conf['Reply Hiding Buttons'] && !Conf['Reply Hiding Link'] && !Conf['Filter']) { return; } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Strike-through Quotes', cb: this.node }); @@ -6430,7 +6430,7 @@ order: 98 }); $.on(d, '4chanXInitFinished', this.setup); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Quote Threading', cb: this.node }); @@ -6567,7 +6567,8 @@ if (Conf['Comment Expansion']) { ExpandComment.callbacks.push(this.node); } - return Post.prototype.callbacks.push({ + this.text = '\u00A0(You)'; + return Post.callbacks.push({ name: 'Mark Quotes of You', cb: this.node }); @@ -6590,7 +6591,7 @@ if (!(QR.db.get(Get.postDataFromLink(quotelink)))) { continue; } - $.add(quotelink, $.tn('\u00A0(You)')); + $.add(quotelink, $.tn(QuoteYou.text)); $.addClass(quotelink, 'you'); $.addClass(this.nodes.root, 'quotesYou'); } @@ -6647,7 +6648,7 @@ if (Conf['Comment Expansion']) { ExpandComment.callbacks.push(this.node); } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Resurrect Quotes', cb: this.node }); @@ -6739,6 +6740,15 @@ } }, fixDeadlink: function(deadlink) { + var el, green; + + if (!(el = deadlink.previousSibling) || el.nodeName === 'BR') { + green = $.el('span', { + className: 'quote' + }); + $.before(deadlink, green); + $.add(green, deadlink); + } return $.replace(deadlink, __slice.call(deadlink.childNodes)); } }; @@ -6755,7 +6765,7 @@ if (Conf['Title Link']) { $.sync('CachedTitles', Linkify.titleSync); } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Linkify', cb: this.node }); @@ -7217,7 +7227,7 @@ $.ready(this.persist); } } - Post.prototype.callbacks.push({ + Post.callbacks.push({ name: 'Quick Reply', cb: this.node }); @@ -7386,7 +7396,7 @@ } if (QR.captcha.isEnabled && /captcha|verification/i.test(el.textContent)) { QR.captcha.nodes.input.focus(); - if (Conf['Captcha Warning Notifications']) { + if (Conf['Captcha Warning Notifications'] && !d.hidden) { QR.notify(el); } else { $.addClass(QR.captcha.nodes.input, 'error'); @@ -7405,17 +7415,17 @@ var notice, notif; notice = new Notice('warning', el); - QR.notifications.push(notice); - if (!Header.areNotificationsEnabled) { - return; + 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(); + }; } - notif = new Notification(el.textContent, { - body: el.textContent, - icon: Favicon.logo - }); - return notif.onclick = function() { - return window.focus(); - }; }, notifications: [], cleanNotifications: function() { @@ -7760,8 +7770,7 @@ } e.preventDefault(); QR.open(); - QR.handleFiles(e.dataTransfer.files); - return $.addClass(QR.nodes.el, 'dump'); + return QR.handleFiles(e.dataTransfer.files); }, paste: function(e) { var blob, files, item, _i, _len, _ref; @@ -8719,6 +8728,10 @@ 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) { @@ -8790,6 +8803,40 @@ } }; + 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; @@ -8818,7 +8865,7 @@ $.on(el, 'click', FappeTyme.cb.werk); Header.addShortcut(el, true); } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Fappe Tyme', cb: this.node }); @@ -8855,7 +8902,7 @@ }); $.on(el, 'click', this.cb.toggle); Header.addShortcut(el, true); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Gallery', cb: this.node }); @@ -9155,7 +9202,7 @@ }); $.on(this.EAI, 'click', ImageExpand.cb.toggleAll); Header.addShortcut(this.EAI, true); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Image Expansion', cb: this.node }); @@ -9414,7 +9461,7 @@ if (g.VIEW === 'catalog' || !Conf['Image Hover']) { return; } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Image Hover', cb: this.node }); @@ -9509,7 +9556,7 @@ if (!(Conf["Image Prefetching"] || Conf["Replace JPG"] || Conf["Replace PNG"] || Conf["Replace GIF"])) { return; } - Post.prototype.callbacks.push({ + Post.callbacks.push({ name: 'Image Replace', cb: this.node }); @@ -9568,8 +9615,7 @@ if (g.VIEW === 'catalog' || !Conf['Reveal Spoiler Thumbnails']) { return; } - return Post.prototype.callbacks.push({ - name: 'Reveal Spoiler Thumbnails', + return Post.callbacks.push({ cb: this.node }); }, @@ -9611,7 +9657,7 @@ this.link = $.el('a', { target: '_blank' }); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Sauce', cb: this.node }); @@ -9909,7 +9955,7 @@ return; } this.menu = new UI.Menu('post'); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Menu', cb: this.node }); @@ -10025,7 +10071,7 @@ if (g.VIEW !== 'thread' || !Conf['Thread Excerpt']) { return; } - return Thread.prototype.callbacks.push({ + return Thread.callbacks.push({ name: 'Thread Excerpt', cb: this.node }); @@ -10061,7 +10107,7 @@ this.postCountEl = $('#post-count', sc); this.fileCountEl = $('#file-count', sc); this.pageCountEl = $('#page-count', sc); - return Thread.prototype.callbacks.push({ + return Thread.callbacks.push({ name: 'Thread Stats', cb: this.node }); @@ -10206,7 +10252,7 @@ order: 110, subEntries: subEntries }); - return Thread.prototype.callbacks.push({ + return Thread.callbacks.push({ name: 'Thread Updater', cb: this.node }); @@ -10215,6 +10261,7 @@ ThreadUpdater.thread = this; ThreadUpdater.root = this.OP.nodes.root.parentNode; ThreadUpdater.lastPost = +ThreadUpdater.root.lastElementChild.id.match(/\d+/)[0]; + ThreadUpdater.outdateCount = 0; ThreadUpdater.cb.interval.call($.el('input', { value: Conf['Interval'] })); @@ -10232,7 +10279,7 @@ beep: 'data:audio/wav;base64,UklGRjQDAABXQVZFZm10IBAAAAABAAEAgD4AAIA+AAABAAgAc21wbDwAAABBAAADAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkYXRhzAIAAGMms8em0tleMV4zIpLVo8nhfSlcPR102Ki+5JspVEkdVtKzs+K1NEhUIT7DwKrcy0g6WygsrM2k1NpiLl0zIY/WpMrjgCdbPhxw2Kq+5Z4qUkkdU9K1s+K5NkVTITzBwqnczko3WikrqM+l1NxlLF0zIIvXpsnjgydZPhxs2ay95aIrUEkdUdC3suK8N0NUIjq+xKrcz002WioppdGm091pK1w0IIjYp8jkhydXPxxq2K295aUrTkoeTs65suK+OUFUIzi7xqrb0VA0WSoootKm0t5tKlo1H4TYqMfkiydWQBxm16+85actTEseS8y7seHAPD9TIza5yKra01QyWSson9On0d5wKVk2H4DYqcfkjidUQB1j1rG75KsvSkseScu8seDCPz1TJDW2yara1FYxWSwnm9Sn0N9zKVg2H33ZqsXkkihSQR1g1bK65K0wSEsfR8i+seDEQTxUJTOzy6rY1VowWC0mmNWoz993KVc3H3rYq8TklSlRQh1d1LS647AyR0wgRMbAsN/GRDpTJTKwzKrX1l4vVy4lldWpzt97KVY4IXbUr8LZljVPRCxhw7W3z6ZISkw1VK+4sMWvXEhSPk6buay9sm5JVkZNiLWqtrJ+TldNTnquqbCwilZXU1BwpKirrpNgWFhTaZmnpquZbFlbVmWOpaOonHZcXlljhaGhpZ1+YWBdYn2cn6GdhmdhYGN3lp2enIttY2Jjco+bnJuOdGZlZXCImJqakHpoZ2Zug5WYmZJ/bGlobX6RlpeSg3BqaW16jZSVkoZ0bGtteImSk5KIeG5tbnaFkJKRinxxbm91gY2QkIt/c3BwdH6Kj4+LgnZxcXR8iI2OjIR5c3J0e4WLjYuFe3VzdHmCioyLhn52dHR5gIiKioeAeHV1eH+GiYqHgXp2dnh9hIiJh4J8eHd4fIKHiIeDfXl4eHyBhoeHhH96eHmA', cb: { online: function() { - if (navigator.onLine) { + if (ThreadUpdater.online = navigator.onLine) { ThreadUpdater.outdateCount = 0; ThreadUpdater.setInterval(); ThreadUpdater.set('status', null, null); @@ -10240,7 +10287,7 @@ ThreadUpdater.set('timer', null); ThreadUpdater.set('status', 'Offline', 'warning'); } - return ThreadUpdater.count(true); + return ThreadUpdater.cb.autoUpdate(); }, post: function(e) { if (!(ThreadUpdater.isUpdating && e.detail.threadID === ThreadUpdater.thread.ID)) { @@ -10273,7 +10320,9 @@ return; } ThreadUpdater.outdateCount = 0; - return ThreadUpdater.seconds = Math.min(ThreadUpdater.seconds, ThreadUpdater.interval); + if (ThreadUpdater.seconds > ThreadUpdater.interval) { + return ThreadUpdater.set('timer', ThreadUpdater.getInterval()); + } }, scrollBG: function() { return ThreadUpdater.scrollBG = Conf['Scroll BG'] ? function() { @@ -10296,25 +10345,17 @@ var klass, req, text, _ref; req = ThreadUpdater.req; - delete ThreadUpdater.req; - if (e.type !== 'loadend') { - req.onloadend = null; - if (e.type === 'timeout') { - ThreadUpdater.set('status', 'Retrying', null); - ThreadUpdater.update(); - } - return; - } switch (req.status) { case 200: g.DEAD = false; ThreadUpdater.parse(JSON.parse(req.response).posts); - ThreadUpdater.setInterval(); + ThreadUpdater.set('timer', ThreadUpdater.getInterval()); break; case 404: g.DEAD = true; ThreadUpdater.set('timer', null); ThreadUpdater.set('status', '404', 'warning'); + clearTimeout(ThreadUpdater.timeoutID); ThreadUpdater.thread.kill(); $.event('ThreadUpdate', { 404: true, @@ -10323,7 +10364,7 @@ break; default: ThreadUpdater.outdateCount++; - ThreadUpdater.setInterval(); + ThreadUpdater.set('timer', ThreadUpdater.getInterval()); _ref = req.status === 304 ? [null, null] : ["" + req.statusText + " (" + req.status + ")", 'warning'], text = _ref[0], klass = _ref[1]; ThreadUpdater.set('status', text, klass); } @@ -10364,20 +10405,17 @@ return el.className = klass; } }, - count: function(start) { - clearTimeout(ThreadUpdater.timeoutID); - if (start && ThreadUpdater.isUpdating && navigator.onLine) { - return ThreadUpdater.timeout(); - } - }, timeout: function() { - var sec; + var n; ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.timeout, 1000); - sec = ThreadUpdater.seconds--; - ThreadUpdater.set('timer', sec); - if (sec <= 0) { + if (!(n = --ThreadUpdater.seconds)) { return ThreadUpdater.update(); + } else if (n <= -60) { + ThreadUpdater.set('status', 'Retrying', null); + return ThreadUpdater.update(); + } else if (n > 0) { + return ThreadUpdater.set('timer', n); } }, update: function() { @@ -10397,10 +10435,7 @@ } url = "//api.4chan.org/" + ThreadUpdater.thread.board + "/res/" + ThreadUpdater.thread + ".json"; return ThreadUpdater.req = $.ajax(url, { - onabort: ThreadUpdater.cb.load, - onloadend: ThreadUpdater.cb.load, - ontimeout: ThreadUpdater.cb.load, - timeout: $.MINUTE + onloadend: ThreadUpdater.cb.load }, { whenModified: true }); @@ -10560,7 +10595,7 @@ ThreadWatcher.fetchAllStatus(); this.db.save(); } - return Thread.prototype.callbacks.push({ + return Thread.callbacks.push({ name: 'Thread Watcher', cb: this.node }); @@ -11024,7 +11059,7 @@ }); this.posts = []; this.postsQuotingYou = []; - return Thread.prototype.callbacks.push({ + return Thread.callbacks.push({ name: 'Unread', cb: this.node }); @@ -11157,6 +11192,7 @@ } Unread.postsQuotingYou.push(post); Unread.openNotification(post); + return; } }, openNotification: function(post) { @@ -11350,7 +11386,7 @@ http: true, https: true, software: 'foolfuuka', - boards: ['b', 'e', 'h', 'hc', 'p', 's', 'u'], + boards: ['b', 'e', 'h', 'hc', 'p', 's', 'soc', 'sp', 'u'], files: ['b', 'e', 'h', 'hc', 'p', 's', 'soc', 'sp', 'u'] }, 'Foolz': { @@ -11361,14 +11397,6 @@ boards: ['a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'vp', 'vr', 'wsg'], files: ['a', 'gd', 'jp', 'm', 'q', 'tg', 'vg', 'vp', 'vr', 'wsg'] }, - 'Foolz a Shit': { - domain: 'archive.foolzashit.com', - http: true, - https: true, - software: 'foolfuuka', - boards: ['adv', 'asp', 'cm', 'd', 'e', 'i', 'lgbt', 'n', 'o', 'p', 'pol', 's', 's4s', 't', 'trv', 'y'], - files: ['cm', 'd', 'e', 'i', 'n', 'o', 'p', 's', 'trv', 'y'] - }, 'Foolz Beta': { domain: 'beta.foolz.us', http: true, @@ -12385,7 +12413,7 @@ $.ready(function() { return Rice.nodes(d.body); }); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Rice Checkboxes', cb: this.node }); @@ -13201,7 +13229,7 @@ return; } this.ids = {}; - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Color User IDs', cb: this.node }); @@ -13275,7 +13303,7 @@ if (g.BOARD.ID !== 'tg' || g.VIEW === 'catalog' || !Conf['Show Dice Roll']) { return; } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Show Dice Roll', cb: this.node }); @@ -13302,7 +13330,7 @@ if (g.BOARD.ID === 'sci') { this.callbacks.push(Fourchan.math); } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Comment Expansion', cb: this.node }); @@ -13398,7 +13426,7 @@ if (g.VIEW !== 'index' || !Conf['Thread Expansion']) { return; } - return Thread.prototype.callbacks.push({ + return Thread.callbacks.push({ name: 'Thread Expansion', cb: this.node }); @@ -13554,7 +13582,7 @@ return; } this.funk = this.createFunc(Conf['fileInfo']); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'File Info Formatting', cb: this.node }); @@ -13661,14 +13689,14 @@ 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);"); - Post.prototype.callbacks.push({ + Post.callbacks.push({ name: 'Parse /g/ code', cb: this.code }); } if (board === 'sci') { $.globalEval("window.addEventListener('jsmath', function(e) {\n if (jsMath.loaded) {\n // process one post\n jsMath.ProcessBeforeShowing(e.detail);\n } else {\n // load jsMath and process whole document\n jsMath.Autoload.Script.Push('ProcessBeforeShowing', [null]);\n jsMath.Autoload.LoadJsMath();\n }\n}, false);"); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Parse /sci/ math', cb: this.math }); @@ -13702,6 +13730,54 @@ } }; + IDColor = { + init: function() { + if (g.VIEW === 'catalog' || !Conf['Color User IDs']) { + return; + } + this.ids = {}; + return Post.callbacks.push({ + name: 'Color User IDs', + cb: this.node + }); + }, + node: function() { + var rgb, span, style, uid; + + if (this.isClone || !(uid = this.info.uniqueID)) { + return; + } + rgb = IDColor.compute(uid); + span = this.nodes.uniqueID; + style = span.style; + style.color = rgb[3]; + style.backgroundColor = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")"; + $.addClass(span, 'painted'); + span.textContent = uid; + return span.title = 'Highlight posts by this ID'; + }, + compute: function(uniqueID) { + var hash, rgb; + + if (uniqueID in IDColor.ids) { + return IDColor.ids[uniqueID]; + } + hash = this.hash(uniqueID); + rgb = [(hash >> 24) & 0xFF, (hash >> 16) & 0xFF, (hash >> 8) & 0xFF]; + rgb.push((rgb[0] * 0.299 + rgb[1] * 0.587 + rgb[2] * 0.114) > 170 ? 'black' : 'white'); + return this.ids[uniqueID] = rgb; + }, + hash: function(uniqueID) { + var i, msg, _i, _ref; + + msg = 0; + for (i = _i = 0, _ref = uniqueID.length; _i < _ref; i = _i += 1) { + msg = (msg << 5) - msg + uniqueID.charCodeAt(i); + } + return msg; + } + }; + Keybinds = { init: function() { var init; @@ -14149,7 +14225,7 @@ } $.on(d, 'visibilitychange ThreadUpdate', this.flush); this.flush(); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Relative Post Dates', cb: this.node }); @@ -14233,7 +14309,7 @@ return "[spoiler]" + text + "[/spoiler]"; }; } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Reveal Spoilers', cb: this.node }); @@ -14289,7 +14365,7 @@ return; } this.funk = this.createFunc(Conf['time']); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Time Formatting', cb: this.node }); @@ -14773,7 +14849,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 Status/Twitter link (status, @).
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.
Thread Updater is disabled.
\n Interval:
Custom CSS
"; + 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.
Thread Updater is disabled.
\n Interval:
Custom CSS
"; items = {}; inputs = {}; _ref = ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss']; @@ -15838,7 +15914,7 @@ var callback, err, errors, i, len, node, _i, _len, _ref; len = nodes.length; - _ref = klass.prototype.callbacks; + _ref = klass.callbacks; for (_i = 0, _len = _ref.length; _i < _len; _i++) { callback = _ref[_i]; i = 0; @@ -15888,7 +15964,7 @@ func = function(node, i) { var callback, err, _i, _len, _ref; - _ref = klass.prototype.callbacks; + _ref = klass.callbacks; for (_i = 0, _len = _ref.length; _i < _len; _i++) { callback = _ref[_i]; try { @@ -15937,7 +16013,7 @@ return; } obj.callback.isAddon = true; - return Klass.prototype.callbacks.push(obj.callback); + return Klass.callbacks.push(obj.callback); }, handleErrors: function(errors) { var div, error, logs, _i, _len; diff --git a/builds/crx/manifest.json b/builds/crx/manifest.json index 6b214de34..b289d67d6 100644 --- a/builds/crx/manifest.json +++ b/builds/crx/manifest.json @@ -15,7 +15,7 @@ "run_at": "document_start" }], "homepage_url": "http://zixaphir.github.com/appchan-x/", - "minimum_chrome_version": "29", + "minimum_chrome_version": "27", "permissions": [ "storage" ] diff --git a/builds/crx/script.js b/builds/crx/script.js index 84c52ace7..edc21d8d9 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript /* -* appchan x - Version 2.3.10 - 2013-09-24 +* appchan x - Version 2.3.10 - 2013-10-13 * * Licensed under the MIT license. * https://github.com/zixaphir/appchan-x/blob/master/LICENSE @@ -88,7 +88,7 @@ 'use strict'; (function() { - var $, $$, Anonymize, ArchiveLink, Banner, Board, Build, CatalogLinks, Clone, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, GlobalMessage, Header, IDColor, ImageExpand, ImageHover, ImageLoader, JSColor, Keybinds, Linkify, Main, MascotTools, Mascots, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Rice, Sauce, Settings, Style, ThemeTools, Themes, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, editMascot, editTheme, g, userNavigation, + 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, GlobalMessage, Header, IDColor, ImageExpand, ImageHover, ImageLoader, JSColor, Keybinds, Linkify, Main, MascotTools, Mascots, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Rice, Sauce, Settings, Style, ThemeTools, Themes, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, editMascot, editTheme, g, userNavigation, __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, @@ -3221,7 +3221,7 @@ })(); Thread = (function() { - Thread.prototype.callbacks = []; + Thread.callbacks = []; Thread.prototype.toString = function() { return this.ID; @@ -3245,7 +3245,7 @@ })(); Post = (function() { - Post.prototype.callbacks = []; + Post.callbacks = []; Post.prototype.toString = function() { return this.ID; @@ -4280,7 +4280,7 @@ emailStart = ''; emailEnd = ''; } - subject = "" + (subject || '') + ""; + subject = " " + (subject || '') + " "; userID = !capcode && uniqueID ? (" (ID: ") + ("" + uniqueID + ") ") : ''; switch (capcode) { case 'admin': @@ -4347,7 +4347,7 @@ 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++) { @@ -5058,7 +5058,7 @@ if (g.VIEW === 'catalog' || !Conf['Anonymize']) { return; } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Anonymize', cb: this.node }); @@ -5149,7 +5149,7 @@ if (!Object.keys(this.filters).length) { return; } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Filter', cb: this.node }); @@ -5379,7 +5379,7 @@ $.addClass(doc, "reply-hide"); } this.db = new DataBoard('hiddenPosts'); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Reply Hiding', cb: this.node }); @@ -5682,7 +5682,7 @@ if (g.VIEW === 'catalog') { return; } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Recursive', cb: this.node }); @@ -5753,7 +5753,7 @@ } this.db = new DataBoard('hiddenThreads'); this.syncCatalog(); - return Thread.prototype.callbacks.push({ + return Thread.callbacks.push({ name: 'Thread Hiding', cb: this.node }); @@ -6022,11 +6022,11 @@ format = Conf['backlink'].replace(/%id/g, "' + id + '"); this.funk = Function('id', "return '" + format + "'"); this.containers = {}; - Post.prototype.callbacks.push({ + Post.callbacks.push({ name: 'Quote Backlinking Part 1', cb: this.firstNode }); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Quote Backlinking Part 2', cb: this.secondNode }); @@ -6105,7 +6105,7 @@ ExpandComment.callbacks.push(this.node); } this.text = '\u00A0(Cross-thread)'; - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Mark Cross-thread Quotes', cb: this.node }); @@ -6166,7 +6166,7 @@ if (Conf['Comment Expansion']) { ExpandComment.callbacks.push(this.node); } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Quote Inlining', cb: this.node }); @@ -6268,7 +6268,7 @@ ExpandComment.callbacks.push(this.node); } this.text = '\u00A0(OP)'; - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Mark OP Quotes', cb: this.node }); @@ -6311,7 +6311,7 @@ if (Conf['Comment Expansion']) { ExpandComment.callbacks.push(this.node); } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Quote Previewing', cb: this.node }); @@ -6394,7 +6394,7 @@ if (g.VIEW === 'catalog' || !Conf['Reply Hiding Buttons'] && !Conf['Reply Hiding Link'] && !Conf['Filter']) { return; } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Strike-through Quotes', cb: this.node }); @@ -6440,7 +6440,7 @@ order: 98 }); $.on(d, '4chanXInitFinished', this.setup); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Quote Threading', cb: this.node }); @@ -6577,7 +6577,8 @@ if (Conf['Comment Expansion']) { ExpandComment.callbacks.push(this.node); } - return Post.prototype.callbacks.push({ + this.text = '\u00A0(You)'; + return Post.callbacks.push({ name: 'Mark Quotes of You', cb: this.node }); @@ -6600,7 +6601,7 @@ if (!(QR.db.get(Get.postDataFromLink(quotelink)))) { continue; } - $.add(quotelink, $.tn('\u00A0(You)')); + $.add(quotelink, $.tn(QuoteYou.text)); $.addClass(quotelink, 'you'); $.addClass(this.nodes.root, 'quotesYou'); } @@ -6657,7 +6658,7 @@ if (Conf['Comment Expansion']) { ExpandComment.callbacks.push(this.node); } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Resurrect Quotes', cb: this.node }); @@ -6749,6 +6750,15 @@ } }, fixDeadlink: function(deadlink) { + var el, green; + + if (!(el = deadlink.previousSibling) || el.nodeName === 'BR') { + green = $.el('span', { + className: 'quote' + }); + $.before(deadlink, green); + $.add(green, deadlink); + } return $.replace(deadlink, __slice.call(deadlink.childNodes)); } }; @@ -6765,7 +6775,7 @@ if (Conf['Title Link']) { $.sync('CachedTitles', Linkify.titleSync); } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Linkify', cb: this.node }); @@ -7227,7 +7237,7 @@ $.ready(this.persist); } } - Post.prototype.callbacks.push({ + Post.callbacks.push({ name: 'Quick Reply', cb: this.node }); @@ -7392,7 +7402,7 @@ } if (QR.captcha.isEnabled && /captcha|verification/i.test(el.textContent)) { QR.captcha.nodes.input.focus(); - if (Conf['Captcha Warning Notifications']) { + if (Conf['Captcha Warning Notifications'] && !d.hidden) { QR.notify(el); } else { $.addClass(QR.captcha.nodes.input, 'error'); @@ -7411,26 +7421,26 @@ var notice, notif; notice = new Notice('warning', el); - QR.notifications.push(notice); - if (!Header.areNotificationsEnabled) { - return; + 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); + }; } - 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() { @@ -7775,8 +7785,7 @@ } e.preventDefault(); QR.open(); - QR.handleFiles(e.dataTransfer.files); - return $.addClass(QR.nodes.el, 'dump'); + return QR.handleFiles(e.dataTransfer.files); }, paste: function(e) { var blob, files, item, _i, _len, _ref; @@ -8709,6 +8718,10 @@ 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) { @@ -8780,6 +8793,40 @@ } }; + 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; @@ -8808,7 +8855,7 @@ $.on(el, 'click', FappeTyme.cb.werk); Header.addShortcut(el, true); } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Fappe Tyme', cb: this.node }); @@ -8845,7 +8892,7 @@ }); $.on(el, 'click', this.cb.toggle); Header.addShortcut(el, true); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Gallery', cb: this.node }); @@ -9145,7 +9192,7 @@ }); $.on(this.EAI, 'click', ImageExpand.cb.toggleAll); Header.addShortcut(this.EAI, true); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Image Expansion', cb: this.node }); @@ -9404,7 +9451,7 @@ if (g.VIEW === 'catalog' || !Conf['Image Hover']) { return; } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Image Hover', cb: this.node }); @@ -9499,7 +9546,7 @@ if (!(Conf["Image Prefetching"] || Conf["Replace JPG"] || Conf["Replace PNG"] || Conf["Replace GIF"])) { return; } - Post.prototype.callbacks.push({ + Post.callbacks.push({ name: 'Image Replace', cb: this.node }); @@ -9558,8 +9605,7 @@ if (g.VIEW === 'catalog' || !Conf['Reveal Spoiler Thumbnails']) { return; } - return Post.prototype.callbacks.push({ - name: 'Reveal Spoiler Thumbnails', + return Post.callbacks.push({ cb: this.node }); }, @@ -9601,7 +9647,7 @@ this.link = $.el('a', { target: '_blank' }); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Sauce', cb: this.node }); @@ -9899,7 +9945,7 @@ return; } this.menu = new UI.Menu('post'); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Menu', cb: this.node }); @@ -10015,7 +10061,7 @@ if (g.VIEW !== 'thread' || !Conf['Thread Excerpt']) { return; } - return Thread.prototype.callbacks.push({ + return Thread.callbacks.push({ name: 'Thread Excerpt', cb: this.node }); @@ -10051,7 +10097,7 @@ this.postCountEl = $('#post-count', sc); this.fileCountEl = $('#file-count', sc); this.pageCountEl = $('#page-count', sc); - return Thread.prototype.callbacks.push({ + return Thread.callbacks.push({ name: 'Thread Stats', cb: this.node }); @@ -10196,7 +10242,7 @@ order: 110, subEntries: subEntries }); - return Thread.prototype.callbacks.push({ + return Thread.callbacks.push({ name: 'Thread Updater', cb: this.node }); @@ -10205,6 +10251,7 @@ ThreadUpdater.thread = this; ThreadUpdater.root = this.OP.nodes.root.parentNode; ThreadUpdater.lastPost = +ThreadUpdater.root.lastElementChild.id.match(/\d+/)[0]; + ThreadUpdater.outdateCount = 0; ThreadUpdater.cb.interval.call($.el('input', { value: Conf['Interval'] })); @@ -10222,7 +10269,7 @@ beep: 'data:audio/wav;base64,UklGRjQDAABXQVZFZm10IBAAAAABAAEAgD4AAIA+AAABAAgAc21wbDwAAABBAAADAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkYXRhzAIAAGMms8em0tleMV4zIpLVo8nhfSlcPR102Ki+5JspVEkdVtKzs+K1NEhUIT7DwKrcy0g6WygsrM2k1NpiLl0zIY/WpMrjgCdbPhxw2Kq+5Z4qUkkdU9K1s+K5NkVTITzBwqnczko3WikrqM+l1NxlLF0zIIvXpsnjgydZPhxs2ay95aIrUEkdUdC3suK8N0NUIjq+xKrcz002WioppdGm091pK1w0IIjYp8jkhydXPxxq2K295aUrTkoeTs65suK+OUFUIzi7xqrb0VA0WSoootKm0t5tKlo1H4TYqMfkiydWQBxm16+85actTEseS8y7seHAPD9TIza5yKra01QyWSson9On0d5wKVk2H4DYqcfkjidUQB1j1rG75KsvSkseScu8seDCPz1TJDW2yara1FYxWSwnm9Sn0N9zKVg2H33ZqsXkkihSQR1g1bK65K0wSEsfR8i+seDEQTxUJTOzy6rY1VowWC0mmNWoz993KVc3H3rYq8TklSlRQh1d1LS647AyR0wgRMbAsN/GRDpTJTKwzKrX1l4vVy4lldWpzt97KVY4IXbUr8LZljVPRCxhw7W3z6ZISkw1VK+4sMWvXEhSPk6buay9sm5JVkZNiLWqtrJ+TldNTnquqbCwilZXU1BwpKirrpNgWFhTaZmnpquZbFlbVmWOpaOonHZcXlljhaGhpZ1+YWBdYn2cn6GdhmdhYGN3lp2enIttY2Jjco+bnJuOdGZlZXCImJqakHpoZ2Zug5WYmZJ/bGlobX6RlpeSg3BqaW16jZSVkoZ0bGtteImSk5KIeG5tbnaFkJKRinxxbm91gY2QkIt/c3BwdH6Kj4+LgnZxcXR8iI2OjIR5c3J0e4WLjYuFe3VzdHmCioyLhn52dHR5gIiKioeAeHV1eH+GiYqHgXp2dnh9hIiJh4J8eHd4fIKHiIeDfXl4eHyBhoeHhH96eHmA', cb: { online: function() { - if (navigator.onLine) { + if (ThreadUpdater.online = navigator.onLine) { ThreadUpdater.outdateCount = 0; ThreadUpdater.setInterval(); ThreadUpdater.set('status', null, null); @@ -10230,7 +10277,7 @@ ThreadUpdater.set('timer', null); ThreadUpdater.set('status', 'Offline', 'warning'); } - return ThreadUpdater.count(true); + return ThreadUpdater.cb.autoUpdate(); }, post: function(e) { if (!(ThreadUpdater.isUpdating && e.detail.threadID === ThreadUpdater.thread.ID)) { @@ -10263,7 +10310,9 @@ return; } ThreadUpdater.outdateCount = 0; - return ThreadUpdater.seconds = Math.min(ThreadUpdater.seconds, ThreadUpdater.interval); + if (ThreadUpdater.seconds > ThreadUpdater.interval) { + return ThreadUpdater.set('timer', ThreadUpdater.getInterval()); + } }, scrollBG: function() { return ThreadUpdater.scrollBG = Conf['Scroll BG'] ? function() { @@ -10286,25 +10335,17 @@ var klass, req, text, _ref; req = ThreadUpdater.req; - delete ThreadUpdater.req; - if (e.type !== 'loadend') { - req.onloadend = null; - if (e.type === 'timeout') { - ThreadUpdater.set('status', 'Retrying', null); - ThreadUpdater.update(); - } - return; - } switch (req.status) { case 200: g.DEAD = false; ThreadUpdater.parse(JSON.parse(req.response).posts); - ThreadUpdater.setInterval(); + ThreadUpdater.set('timer', ThreadUpdater.getInterval()); break; case 404: g.DEAD = true; ThreadUpdater.set('timer', null); ThreadUpdater.set('status', '404', 'warning'); + clearTimeout(ThreadUpdater.timeoutID); ThreadUpdater.thread.kill(); $.event('ThreadUpdate', { 404: true, @@ -10313,7 +10354,7 @@ break; default: ThreadUpdater.outdateCount++; - ThreadUpdater.setInterval(); + ThreadUpdater.set('timer', ThreadUpdater.getInterval()); _ref = req.status === 304 ? [null, null] : ["" + req.statusText + " (" + req.status + ")", 'warning'], text = _ref[0], klass = _ref[1]; ThreadUpdater.set('status', text, klass); } @@ -10354,20 +10395,17 @@ return el.className = klass; } }, - count: function(start) { - clearTimeout(ThreadUpdater.timeoutID); - if (start && ThreadUpdater.isUpdating && navigator.onLine) { - return ThreadUpdater.timeout(); - } - }, timeout: function() { - var sec; + var n; ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.timeout, 1000); - sec = ThreadUpdater.seconds--; - ThreadUpdater.set('timer', sec); - if (sec <= 0) { + if (!(n = --ThreadUpdater.seconds)) { return ThreadUpdater.update(); + } else if (n <= -60) { + ThreadUpdater.set('status', 'Retrying', null); + return ThreadUpdater.update(); + } else if (n > 0) { + return ThreadUpdater.set('timer', n); } }, update: function() { @@ -10387,10 +10425,7 @@ } url = "//api.4chan.org/" + ThreadUpdater.thread.board + "/res/" + ThreadUpdater.thread + ".json"; return ThreadUpdater.req = $.ajax(url, { - onabort: ThreadUpdater.cb.load, - onloadend: ThreadUpdater.cb.load, - ontimeout: ThreadUpdater.cb.load, - timeout: $.MINUTE + onloadend: ThreadUpdater.cb.load }, { whenModified: true }); @@ -10550,7 +10585,7 @@ ThreadWatcher.fetchAllStatus(); this.db.save(); } - return Thread.prototype.callbacks.push({ + return Thread.callbacks.push({ name: 'Thread Watcher', cb: this.node }); @@ -11014,7 +11049,7 @@ }); this.posts = []; this.postsQuotingYou = []; - return Thread.prototype.callbacks.push({ + return Thread.callbacks.push({ name: 'Unread', cb: this.node }); @@ -11147,6 +11182,7 @@ } Unread.postsQuotingYou.push(post); Unread.openNotification(post); + return; } }, openNotification: function(post) { @@ -11346,7 +11382,7 @@ http: true, https: true, software: 'foolfuuka', - boards: ['b', 'e', 'h', 'hc', 'p', 's', 'u'], + boards: ['b', 'e', 'h', 'hc', 'p', 's', 'soc', 'sp', 'u'], files: ['b', 'e', 'h', 'hc', 'p', 's', 'soc', 'sp', 'u'] }, 'Foolz': { @@ -11357,14 +11393,6 @@ boards: ['a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'vp', 'vr', 'wsg'], files: ['a', 'gd', 'jp', 'm', 'q', 'tg', 'vg', 'vp', 'vr', 'wsg'] }, - 'Foolz a Shit': { - domain: 'archive.foolzashit.com', - http: true, - https: true, - software: 'foolfuuka', - boards: ['adv', 'asp', 'cm', 'd', 'e', 'i', 'lgbt', 'n', 'o', 'p', 'pol', 's', 's4s', 't', 'trv', 'y'], - files: ['cm', 'd', 'e', 'i', 'n', 'o', 'p', 's', 'trv', 'y'] - }, 'Foolz Beta': { domain: 'beta.foolz.us', http: true, @@ -12381,7 +12409,7 @@ $.ready(function() { return Rice.nodes(d.body); }); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Rice Checkboxes', cb: this.node }); @@ -13197,7 +13225,7 @@ return; } this.ids = {}; - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Color User IDs', cb: this.node }); @@ -13271,7 +13299,7 @@ if (g.BOARD.ID !== 'tg' || g.VIEW === 'catalog' || !Conf['Show Dice Roll']) { return; } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Show Dice Roll', cb: this.node }); @@ -13298,7 +13326,7 @@ if (g.BOARD.ID === 'sci') { this.callbacks.push(Fourchan.math); } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Comment Expansion', cb: this.node }); @@ -13394,7 +13422,7 @@ if (g.VIEW !== 'index' || !Conf['Thread Expansion']) { return; } - return Thread.prototype.callbacks.push({ + return Thread.callbacks.push({ name: 'Thread Expansion', cb: this.node }); @@ -13550,7 +13578,7 @@ return; } this.funk = this.createFunc(Conf['fileInfo']); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'File Info Formatting', cb: this.node }); @@ -13657,14 +13685,14 @@ 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);"); - Post.prototype.callbacks.push({ + Post.callbacks.push({ name: 'Parse /g/ code', cb: this.code }); } if (board === 'sci') { $.globalEval("window.addEventListener('jsmath', function(e) {\n if (jsMath.loaded) {\n // process one post\n jsMath.ProcessBeforeShowing(e.detail);\n } else {\n // load jsMath and process whole document\n jsMath.Autoload.Script.Push('ProcessBeforeShowing', [null]);\n jsMath.Autoload.LoadJsMath();\n }\n}, false);"); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Parse /sci/ math', cb: this.math }); @@ -13698,6 +13726,54 @@ } }; + IDColor = { + init: function() { + if (g.VIEW === 'catalog' || !Conf['Color User IDs']) { + return; + } + this.ids = {}; + return Post.callbacks.push({ + name: 'Color User IDs', + cb: this.node + }); + }, + node: function() { + var rgb, span, style, uid; + + if (this.isClone || !(uid = this.info.uniqueID)) { + return; + } + rgb = IDColor.compute(uid); + span = this.nodes.uniqueID; + style = span.style; + style.color = rgb[3]; + style.backgroundColor = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")"; + $.addClass(span, 'painted'); + span.textContent = uid; + return span.title = 'Highlight posts by this ID'; + }, + compute: function(uniqueID) { + var hash, rgb; + + if (uniqueID in IDColor.ids) { + return IDColor.ids[uniqueID]; + } + hash = this.hash(uniqueID); + rgb = [(hash >> 24) & 0xFF, (hash >> 16) & 0xFF, (hash >> 8) & 0xFF]; + rgb.push((rgb[0] * 0.299 + rgb[1] * 0.587 + rgb[2] * 0.114) > 170 ? 'black' : 'white'); + return this.ids[uniqueID] = rgb; + }, + hash: function(uniqueID) { + var i, msg, _i, _ref; + + msg = 0; + for (i = _i = 0, _ref = uniqueID.length; _i < _ref; i = _i += 1) { + msg = (msg << 5) - msg + uniqueID.charCodeAt(i); + } + return msg; + } + }; + Keybinds = { init: function() { var init; @@ -14145,7 +14221,7 @@ } $.on(d, 'visibilitychange ThreadUpdate', this.flush); this.flush(); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Relative Post Dates', cb: this.node }); @@ -14229,7 +14305,7 @@ return "[spoiler]" + text + "[/spoiler]"; }; } - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Reveal Spoilers', cb: this.node }); @@ -14285,7 +14361,7 @@ return; } this.funk = this.createFunc(Conf['time']); - return Post.prototype.callbacks.push({ + return Post.callbacks.push({ name: 'Time Formatting', cb: this.node }); @@ -14767,7 +14843,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 Status/Twitter link (status, @).
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.
Thread Updater is disabled.
\n Interval:
Custom CSS
"; + 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.
Thread Updater is disabled.
\n Interval:
Custom CSS
"; items = {}; inputs = {}; _ref = ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss']; @@ -15826,7 +15902,7 @@ var callback, err, errors, i, len, node, _i, _len, _ref; len = nodes.length; - _ref = klass.prototype.callbacks; + _ref = klass.callbacks; for (_i = 0, _len = _ref.length; _i < _len; _i++) { callback = _ref[_i]; i = 0; @@ -15876,7 +15952,7 @@ func = function(node, i) { var callback, err, _i, _len, _ref; - _ref = klass.prototype.callbacks; + _ref = klass.callbacks; for (_i = 0, _len = _ref.length; _i < _len; _i++) { callback = _ref[_i]; try { @@ -15925,7 +16001,7 @@ return; } obj.callback.isAddon = true; - return Klass.prototype.callbacks.push(obj.callback); + return Klass.callbacks.push(obj.callback); }, handleErrors: function(errors) { var div, error, logs, _i, _len; diff --git a/css/style.css b/css/style.css deleted file mode 100644 index dbba61fa3..000000000 --- a/css/style.css +++ /dev/null @@ -1,938 +0,0 @@ -/* General */ -.dialog { - box-shadow: 0 1px 2px rgba(0, 0, 0, .15); - border: 1px solid; - display: block; - padding: 0; -} -.field { - background-color: #FFF; - border: 1px solid #CCC; - -moz-box-sizing: border-box; - box-sizing: border-box; - color: #333; - font-family: inherit; - font-size: 13px; - margin: 0; - padding: 2px 4px 3px; - outline: none; - transition: color .25s, border-color .25s, flex .25s; -} -.field::-moz-placeholder, -.field:hover::-moz-placeholder { - color: #AAA !important; -} -.field:hover { - border-color: #999; -} -.field:hover, .field:focus { - color: #000; -} -.field[disabled] { - background-color: #F2F2F2; - color: #888; -} -.move { - cursor: move; -} -label, .watcher-toggler { - cursor: pointer; -} -a[href="javascript:;"] { - text-decoration: none; -} -.warning { - color: red; -} - -/* 4chan style fixes */ -.opContainer, .op { - display: block !important; -} -.post { - overflow: visible !important; -} -[hidden] { - display: none !important; -} - -/* fixed, z-index */ -#overlay, -#qp, #ihover, -#updater, #thread-stats, -#navlinks, #header, -#qr { - position: fixed; -} -#overlay { - z-index: 999; -} -#notifications { - z-index: 70; -} -#qp, #ihover { - z-index: 60; -} -#menu { - z-index: 50; -} -#navlinks, #updater, #thread-stats { - z-index: 40; -} -#qr { - z-index: 30; -} -#thread-watcher:hover { - z-index: 20; -} -#header { - z-index: 10; -} -#thread-watcher { - z-index: 5; -} - -/* Header */ -:root.top-header body { - margin-top: 2em; -} -:root.bottom-header body { - margin-bottom: 2em; -} -:root.fourchan-x #navtopright, -:root.fourchan-x #navbotright, -:root.fourchan-x:not(.show-original-top-board-list) #boardNavDesktop, -:root.fourchan-x:not(.show-original-bot-board-list) #boardNavDesktopFoot { - display: none !important; -} -#header { - right: 0; - left: 0; -} -#header.top { - top: 0; -} -#header.bottom { - bottom: 0; -} -#header-bar { - border-width: 0; - display: flex; - padding: 3px; - position: relative; - transition: all .1s .05s ease-in-out; -} -#header.top #header-bar { - border-bottom-width: 1px; -} -#header.bottom #header-bar { - box-shadow: 0 -1px 2px rgba(0, 0, 0, .15); - border-top-width: 1px; -} -#board-list { - flex: 1; - align-self: center; - text-align: center; -} -#header-bar.autohide:not(:hover) { - box-shadow: none; - transition: all .8s .6s cubic-bezier(.55, .055, .675, .19); -} -#header.top #header-bar.autohide:not(:hover) { - margin-bottom: -1em; - -webkit-transform: translateY(-100%); - transform: translateY(-100%); -} -#header.bottom #header-bar.autohide:not(:hover) { - -webkit-transform: translateY(100%); - transform: translateY(100%); -} -#toggle-header-bar { - left: 0; - right: 0; - height: 10px; - position: absolute; -} -#header.top #toggle-header-bar { - cursor: n-resize; - bottom: -10px; -} -#header.bottom #toggle-header-bar { - cursor: s-resize; - top: -10px; -} -#header.top #header-bar.autohide #toggle-header-bar { - cursor: s-resize; -} -#header.bottom #header-bar.autohide #toggle-header-bar { - cursor: n-resize; -} -#header-bar a:not(.entry) { - text-decoration: none; - padding: 1px; -} -.shortcut:not(:last-child)::after { - content: " / "; -} -.brackets-wrap::before { - content: " [ "; -} -.brackets-wrap::after { - content: " ] "; -} - -/* Notifications */ -#notifications { - height: 0; - text-align: center; -} -#header.bottom #notifications { - position: fixed; - top: 0; - left: 0; - width: 100%; -} -.notification { - color: #FFF; - font-weight: 700; - text-shadow: 0 1px 2px rgba(0, 0, 0, .5); - box-shadow: 0 1px 2px rgba(0, 0, 0, .15); - border-radius: 2px; - margin: 1px auto; - width: 500px; - max-width: 100%; - position: relative; - transition: all .25s ease-in-out; -} -.notification.error { - background-color: hsla(0, 100%, 38%, .9); -} -.notification.warning { - background-color: hsla(36, 100%, 38%, .9); -} -.notification.info { - background-color: hsla(200, 100%, 38%, .9); -} -.notification.success { - background-color: hsla(104, 100%, 38%, .9); -} -.notification a { - color: white; -} -.notification > .close { - padding: 6px; - top: 0; - right: 0; - position: absolute; -} -.message { - -moz-box-sizing: border-box; - box-sizing: border-box; - padding: 6px 20px; - max-height: 200px; - width: 100%; - overflow: auto; -} - -/* Settings */ -:root.fourchan-x body { - -moz-box-sizing: border-box; - box-sizing: border-box; -} -#overlay { - background-color: rgba(0, 0, 0, .5); - display: flex; - position: fixed; - top: 0; - left: 0; - height: 100%; - width: 100%; -} -#fourchanx-settings { - -moz-box-sizing: border-box; - box-sizing: border-box; - box-shadow: 0 0 15px rgba(0, 0, 0, .15); - height: 600px; - max-height: 100%; - width: 900px; - max-width: 100%; - margin: auto; - padding: 3px; - display: flex; - flex-direction: column; -} -#fourchanx-settings > nav { - display: flex; - padding: 2px 2px 0; -} -#fourchanx-settings > nav a { - text-decoration: underline; -} -#fourchanx-settings > nav a.close { - text-decoration: none; - padding: 2px; -} -.sections-list { - flex: 1; -} -.tab-selected { - font-weight: 700; -} -.section-container { - flex: 1; - position: relative; -} -.section-container > section { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - overflow: auto; -} -.section-sauce ul, -.section-rice ul { - list-style: none; - margin: 0; - padding: 8px; -} -.section-sauce li, -.section-rice li { - padding-left: 4px; -} -.section-main label { - text-decoration: underline; -} -.section-filter ul, -.section-qr ul { - padding: 0; -} -.section-filter li, -.section-qr li { - margin: 10px 40px; -} -.section-filter textarea { - height: 500px; -} -.section-qr textarea { - height: 200px; -} -.section-sauce textarea { - height: 350px; -} -.section-rice .field[name="boardnav"] { - width: 100%; -} -.section-rice textarea { - height: 150px; -} -.section-archives table { - width: 100%; -} -.section-archives th:not(:first-child) { - width: 30%; -} -.section-archives td { - text-align: center; -} -.section-archives select { - width: 90%; -} -.section-keybinds .field { - font-family: monospace; -} -#fourchanx-settings fieldset { - border: 1px solid; - border-radius: 3px; -} -#fourchanx-settings legend { - font-weight: 700; -} -#fourchanx-settings textarea { - font-family: monospace; - min-width: 100%; - max-width: 100%; -} -#fourchanx-settings code { - color: #000; - background-color: #FFF; - padding: 0 2px; -} -.unscroll { - overflow: hidden; -} - -/* Announcement Hiding */ -:root.hide-announcement #globalMessage, -:root.hide-announcement-enabled #toggleMsgBtn { - display: none; -} -a.hide-announcement { - float: left; -} - -/* Unread */ -#unread-line { - margin: 0; -} - -/* Thread Updater */ -#updater:not(:hover) { - background: none; - border: none; - box-shadow: none; -} -#updater > .move { - padding: 0 3px; -} -#updater > div:last-child { - text-align: center; -} -#updater input[type=number] { - width: 4em; -} -#updater:not(:hover) > div:not(.move) { - display: none; -} -#updater input[type="button"] { - width: 100%; -} -.new { - color: limegreen; -} - -/* Thread Watcher */ -#thread-watcher { - max-width: 200px; - min-width: 150px; - padding: 3px; - position: absolute; -} -#thread-watcher > div:first-child { - display: flex; - align-items: center; -} -#thread-watcher .move { - flex: 1; -} -#watcher-status:not(:empty)::before { - content: "("; -} -#watcher-status:not(:empty)::after { - content: ")"; -} -#watched-threads:not(:hover) { - max-height: 150px; - overflow: hidden; -} -#watched-threads div { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -#watched-threads .current { - font-weight: 700; -} -#watched-threads a { - text-decoration: none; -} -#watched-threads .dead-thread a[title] { - text-decoration: line-through; -} - -/* Thread Stats */ -#thread-stats { - background: none; - border: none; - box-shadow: none; -} - -/* Quote */ -.deadlink { - text-decoration: none !important; -} -.backlink.deadlink:not(.forwardlink), -.quotelink.deadlink:not(.forwardlink) { - text-decoration: underline !important; -} -.inlined { - opacity: .5; -} -#qp input, .forwarded { - display: none; -} -.quotelink.forwardlink, -.backlink.forwardlink { - text-decoration: none; - border-bottom: 1px dashed; -} -.filtered { - text-decoration: underline line-through; -} -.inline { - border: 1px solid; - display: table; - margin: 2px 0; -} -.inline .post { - border: 0 !important; - background-color: transparent !important; - display: table !important; - margin: 0 !important; - padding: 1px 2px !important; -} -#qp > .opContainer::after { - content: ''; - clear: both; - display: table; -} -#qp .post { - border: none; - margin: 0; - padding: 2px 2px 5px; -} -#qp img { - max-height: 80vh; - max-width: 50vw; -} -.qphl { - outline: 2px solid rgba(216, 94, 49, .7); -} - -/* File */ -.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; -} -.expanded-image { - clear: both; -} -.expanded-image > .op > .file::after { - content: ''; - clear: both; - display: table; -} -:root.fit-height .full-image { - max-height: 100vh; -} -:root.fit-width .full-image { - max-width: 100%; -} -:root.gecko.fit-width .full-image { - width: 100%; -} -#ihover { - -moz-box-sizing: border-box; - box-sizing: border-box; - max-height: 100%; - max-width: 75%; - padding-bottom: 16px; -} - -/* Index/Reply Navigation */ -#navlinks { - font-size: 16px; - top: 25px; - right: 10px; -} - -/* Filter */ -.opContainer.filter-highlight { - box-shadow: inset 5px 0 rgba(255, 0, 0, .5); -} -.filter-highlight > .reply { - box-shadow: -5px 0 rgba(255, 0, 0, .5); -} - -/* 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 { - display: none; -} -#qr select, #dump-button, .remove, .captcha-img { - cursor: pointer; -} -#qr > div { - min-width: 300px; - display: flex; - align-items: center; -} -#qr .move { - align-self: stretch; - flex: 1; -} -#qr select[data-name=thread] { - margin: 0; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - border: none; - background: none; - font: inherit; -} -#qr option { - color: #000; - background-color: #F7F7F7; -} -#qr .close { - padding: 0 3px; -} -#qr > form { - display: flex; - flex-direction: column; -} -.persona { - display: flex; -} -.persona .field { - flex: 1; -} -.persona .field:hover, -.persona .field:focus { - flex: 3; -} -#dump-button { - background: linear-gradient(#EEE, #CCC); - border: 1px solid #CCC; - margin: 0; - padding: 2px 4px 3px; - outline: none; - width: 30px; -} -#dump-button:hover, -#dump-button:focus { - background: linear-gradient(#FFF, #DDD); -} -#dump-button:active, -.dump #dump-button:not(:hover):not(:focus) { - background: linear-gradient(#CCC, #DDD); -} -:root.gecko #dump-button { - padding: 0; -} -#qr:not(.dump) #dump-list-container { - display: none; -} -#dump-list-container { - height: 100px; - position: relative; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; -} -#dump-list { - counter-reset: qrpreviews; - top: 0; - right: 0; - bottom: 0; - left: 0; - overflow: hidden; - position: absolute; - white-space: nowrap; -} -#dump-list:hover { - bottom: -12px; - overflow-x: auto; - z-index: 1; -} -#dump-list::-webkit-scrollbar { - height: 12px; -} -#dump-list::-webkit-scrollbar-thumb { - border: 1px solid; -} -.qr-preview { - background-position: 50% 20%; - background-size: cover; - border: 1px solid #808080; - color: #FFF !important; - font-size: 12px; - -moz-box-sizing: border-box; - box-sizing: border-box; - cursor: move; - display: inline-block; - height: 92px; - width: 92px; - margin: 4px; - padding: 2px; - opacity: .6; - outline: none; - overflow: hidden; - position: relative; - text-shadow: 0 1px 1px #000; - transition: opacity .25s ease-in-out; - vertical-align: top; - white-space: pre; -} -.qr-preview:hover, -.qr-preview:focus { - opacity: .9; - color: #FFF !important; -} -.qr-preview#selected { - opacity: 1; -} -.qr-preview::before { - counter-increment: qrpreviews; - content: counter(qrpreviews); - font-weight: 700; - text-shadow: 0 0 3px #000, 0 0 5px #000; - position: absolute; - top: 3px; - right: 3px; -} -.qr-preview.drag { - border-color: red; - border-style: dashed; - opacity: 1; -} -.qr-preview.over { - border-color: #FFF; - border-style: dashed; - opacity: 1; -} -.remove { - color: #E00 !important; - font-weight: 700; - padding: 3px; -} -.remove:hover::after { - content: ' Remove'; -} -.qr-preview > label { - background: rgba(0, 0, 0, .5); - right: 0; - bottom: 0; - left: 0; - position: absolute; - text-align: center; -} -.qr-preview > label > input { - margin: 1px 0; - vertical-align: bottom; -} -#add-post { - display: inline-block; - font-size: 30px; - height: 30px; - width: 30px; - line-height: 1; - text-align: center; - position: absolute; - right: 0; - bottom: 0; - z-index: 1; -} -#qr textarea { - min-height: 160px; - min-width: 100%; - display: block; -} -#qr.has-captcha textarea { - min-height: 120px; -} -.textarea { - position: relative; -} -#char-count { - color: #000; - background: hsla(0, 0%, 100%, .5); - font-size: 8pt; - position: absolute; - bottom: 1px; - right: 1px; - pointer-events: none; -} -#char-count.warning { - color: red; -} -.captcha-img { - background: #FFF; - outline: 1px solid #CCC; - outline-offset: -1px; -} -.captcha-img > img { - display: block; - height: 57px; - width: 300px; -} -#file-n-submit-container { - position: relative; -} -#file-n-submit { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - display: flex; - align-items: center; -} -#file-n-submit-container input[type='file'] { - /* Keep it to set an appropriate height to the container. */ - visibility: hidden; -} -#file-n-submit-container input { - margin: 0; -} -#file-n-submit input[type='submit'] { - order: 1; -} -#file-n-submit.has-file #qr-no-file, -#file-n-submit:not(.has-file) #qr-filename, -#file-n-submit:not(.has-file) #qr-filesize, -#file-n-submit:not(.has-file) #qr-file-spoiler, -#file-n-submit:not(.has-file) #qr-filerm, -#qr-filename:focus ~ #qr-filesize { - display: none; -} -#qr-no-file, -#qr-filename, -#qr-filesize, -#qr-filerm, -#qr-file-spoiler { - margin: 0 2px !important; -} -#qr-no-file { - cursor: default; - flex: 1; -} -#qr-filename { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background: none; - border: none !important; - color: inherit; - font: inherit; - flex: 1; - text-overflow: ellipsis; -} -#qr-filesize { - font-size: .8em; -} -#qr-filesize::before { - content: " ("; -} -#qr-filesize::after { - content: ")"; -} - -/* Menu */ -.menu-button { - position: relative; -} -.menu-button i:not(.icon-reorder) { - border-top: 6px solid; - border-right: 4px solid transparent; - border-left: 4px solid transparent; - display: inline-block; - margin: 2px; - vertical-align: middle; -} -@media screen and (resolution: 1dppx) { - .icon-reorder { - font-size: 14px; - } - #shortcuts .icon-reorder { - vertical-align: -1px; - } -} -#menu { - border-bottom: 0; - display: flex; - margin: 2px 0; - flex-direction: column; - position: absolute; - outline: none; -} -#menu.top { - top: 100%; -} -#menu.bottom { - bottom: 100%; -} -#menu.left { - left: 0; -} -#menu.right { - right: 0; -} -.entry { - cursor: pointer; - outline: none; - padding: 3px 7px; - position: relative; - text-decoration: none; - white-space: nowrap; -} -.entry.disabled { - color: graytext !important; -} -.entry.has-submenu { - padding-right: 20px; -} -.has-submenu::after { - content: ''; - border-left: 6px solid; - border-top: 4px solid transparent; - border-bottom: 4px solid transparent; - display: inline-block; - margin: 4px; - position: absolute; - right: 3px; -} -.has-submenu:not(.focused) > .submenu { - display: none; -} -.submenu { - border-bottom: 0; - display: flex; - flex-direction: column; - position: absolute; - margin: -1px 0; -} -.submenu.top { - top: 0; -} -.submenu.bottom { - bottom: 0; -} -.submenu.left { - left: 100%; -} -.submenu.right { - right: 100%; -} -.entry input { - margin: 0; -} - -/* colored uid */ - -.posteruid.painted { - padding: 0 5px; - border-radius: 1em; - font-size: 0.8em; - cursor: pointer; -} diff --git a/img/changelog/3.9.0/0.png b/img/changelog/3.9.0/0.png new file mode 100644 index 000000000..36509d96d Binary files /dev/null and b/img/changelog/3.9.0/0.png differ diff --git a/json/archives.json b/json/archives.json index e2654302b..6a3291d10 100644 --- a/json/archives.json +++ b/json/archives.json @@ -43,15 +43,6 @@ "software": "foolfuuka", "boards": ["c", "w", "wg"], "files": ["c", "w", "wg"] -}, { - "uid": 11, - "name": "Foolz a Shit", - "domain": "archive.foolzashit.com", - "http": true, - "https": true, - "software": "foolfuuka", - "boards": ["adv", "asp", "cm", "d", "e", "i", "lgbt", "n", "o", "p", "pol", "s", "s4s", "t", "trv", "y"], - "files": ["cm", "d", "e", "i", "n", "o", "p", "s", "trv", "y"] }, { "uid": 12, "name": "FapArchive", @@ -59,8 +50,8 @@ "http": true, "https": true, "software": "foolfuuka", - "boards": ["b", "e", "h", "hc", "p", "s", "soc", "sp", "u"], - "files": ["b", "e", "h", "hc", "p", "s", "soc", "sp", "u"] + "boards": ["b", "con", "e", "h", "hc", "p", "s", "soc", "sp", "u"], + "files": ["b", "con", "e", "h", "hc", "p", "s", "soc", "sp", "u"] }, { "uid": 7, "name": "Install Gentoo", @@ -77,8 +68,8 @@ "http": true, "https": true, "software": "fuuka", - "boards": ["cgl", "g", "mu", "w"], - "files": ["cgl", "g", "mu", "w"] + "boards": ["cgl", "con", "g", "mu", "w"], + "files": ["cgl", "con", "g", "mu", "w"] }, { "uid": 9, "name": "Heinessen", diff --git a/package.json b/package.json index 7edd56218..ec7957962 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "font-awesome": "git://github.com/MayhemYDG/Font-Awesome.git#df4285951124f9ca1f3907438462e5ba9e464bcb", "grunt": "~0.4.1", "grunt-bump": "~0.0.11", - "grunt-concurrent": "~0.3.1", + "grunt-concurrent": "~0.4.0", "grunt-contrib-clean": "~0.5.0", "grunt-contrib-coffee": "~0.7.0", "grunt-contrib-compress": "~0.5.2", diff --git a/src/Archive/Redirect.coffee b/src/Archive/Redirect.coffee index c34f48910..401b31bb7 100755 --- a/src/Archive/Redirect.coffee +++ b/src/Archive/Redirect.coffee @@ -35,7 +35,7 @@ Redirect = http: true https: true software: 'foolfuuka' - boards: ['b', 'e', 'h', 'hc', 'p', 's', 'u'] + boards: ['b', 'e', 'h', 'hc', 'p', 's', 'soc', 'sp', 'u'] files: ['b', 'e', 'h', 'hc', 'p', 's', 'soc', 'sp', 'u'] 'Foolz': @@ -46,14 +46,6 @@ Redirect = boards: ['a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'vp', 'vr', 'wsg'] files: ['a', 'gd', 'jp', 'm', 'q', 'tg', 'vg', 'vp', 'vr', 'wsg'] - 'Foolz a Shit': - domain: 'archive.foolzashit.com' - http: true - https: true - software: 'foolfuuka' - boards: ['adv', 'asp', 'cm', 'd', 'e', 'i', 'lgbt', 'n', 'o', 'p', 'pol', 's', 's4s', 't', 'trv', 'y'] - files: ['cm', 'd', 'e', 'i', 'n', 'o', 'p', 's', 'trv', 'y'] - 'Foolz Beta': domain: 'beta.foolz.us' http: true diff --git a/src/Filtering/Anonymize.coffee b/src/Filtering/Anonymize.coffee index d5b80f99c..414c428e5 100755 --- a/src/Filtering/Anonymize.coffee +++ b/src/Filtering/Anonymize.coffee @@ -2,7 +2,7 @@ Anonymize = init: -> return if g.VIEW is 'catalog' or !Conf['Anonymize'] - Post::callbacks.push + Post.callbacks.push name: 'Anonymize' cb: @node node: -> diff --git a/src/Filtering/Filter.coffee b/src/Filtering/Filter.coffee index e3f190edc..bab221ec4 100755 --- a/src/Filtering/Filter.coffee +++ b/src/Filtering/Filter.coffee @@ -67,7 +67,7 @@ Filter = delete @filters[key] return unless Object.keys(@filters).length - Post::callbacks.push + Post.callbacks.push name: 'Filter' cb: @node diff --git a/src/Filtering/PostHiding.coffee b/src/Filtering/PostHiding.coffee index afc998665..8a192a590 100644 --- a/src/Filtering/PostHiding.coffee +++ b/src/Filtering/PostHiding.coffee @@ -6,7 +6,7 @@ PostHiding = $.addClass doc, "reply-hide" @db = new DataBoard 'hiddenPosts' - Post::callbacks.push + Post.callbacks.push name: 'Reply Hiding' cb: @node diff --git a/src/Filtering/Recursive.coffee b/src/Filtering/Recursive.coffee index 6af82976c..71a27713b 100755 --- a/src/Filtering/Recursive.coffee +++ b/src/Filtering/Recursive.coffee @@ -3,7 +3,7 @@ Recursive = init: -> return if g.VIEW is 'catalog' - Post::callbacks.push + Post.callbacks.push name: 'Recursive' cb: @node @@ -35,4 +35,4 @@ Recursive = for ID, post of g.posts if post.quotes.contains fullID recursive post, args... - return \ No newline at end of file + return diff --git a/src/Filtering/ThreadHiding.coffee b/src/Filtering/ThreadHiding.coffee index 1ce165cfd..da7911fcb 100644 --- a/src/Filtering/ThreadHiding.coffee +++ b/src/Filtering/ThreadHiding.coffee @@ -4,7 +4,7 @@ ThreadHiding = @db = new DataBoard 'hiddenThreads' @syncCatalog() - Thread::callbacks.push + Thread.callbacks.push name: 'Thread Hiding' cb: @node diff --git a/src/General/Build.coffee b/src/General/Build.coffee index b56123ae2..5a43737ea 100755 --- a/src/General/Build.coffee +++ b/src/General/Build.coffee @@ -72,7 +72,7 @@ Build = emailStart = '' emailEnd = '' - subject = "#{subject or ''}" + subject = " #{subject or ''} " userID = if !capcode and uniqueID diff --git a/src/General/Config.coffee b/src/General/Config.coffee index 5fed5c08d..d1763b1bd 100755 --- a/src/General/Config.coffee +++ b/src/General/Config.coffee @@ -799,8 +799,10 @@ Config = #/Mod$/;highlight:mod;op:yes # Set a custom class for moot: #/Admin$/;highlight:moot;op:yes - """ +""" + email: "" + subject: """ # Filter Generals on /v/: #/general/i;boards:v;op:only @@ -853,7 +855,7 @@ http://iqdb.org/?url=%TURL [ toggle-all ] [current-title] [external-text:"FAQ","https://github.com/seaweedchan/4chan-x/wiki/Frequently-Asked-Questions"] - """ +""" QR: 'QR.personas': """#email:"sage";boards:jp;always""" diff --git a/src/General/Main.coffee b/src/General/Main.coffee index ca5ae74a8..050445ce9 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -234,7 +234,7 @@ Main = callbackNodes: (klass, nodes) -> # get the nodes' length only once len = nodes.length - for callback in klass::callbacks + for callback in klass.callbacks # c.profile callback.name i = 0 while i < len @@ -269,7 +269,7 @@ Main = errors = null func = (node, i) -> - for callback in klass::callbacks + for callback in klass.callbacks try callback.cb.call node catch err @@ -301,7 +301,7 @@ Main = else return obj.callback.isAddon = true - Klass::callbacks.push obj.callback + Klass.callbacks.push obj.callback handleErrors: (errors) -> unless errors instanceof Array diff --git a/src/General/css/style.css b/src/General/css/style.css index 876fc366c..b30299d28 100755 --- a/src/General/css/style.css +++ b/src/General/css/style.css @@ -675,7 +675,7 @@ a.hide-announcement { :root.hide-original-post-form .postingMode, :root.hide-original-post-form #togglePostForm, #qr.autohide:not(.has-focus):not(:hover) > form, -.postingMode ~ #qr select, +.postingMode ~ #qr select[data-name=thread], #file-n-submit:not(.has-file) #qr-filerm { display: none; } @@ -837,7 +837,7 @@ input#qr-filename:not(.edit) { position: absolute; } /* Thread Select / Spoiler Label */ -#qr select { +#qr select[data-name=thread] { float: right; } #qr.has-spoiler .has-file #qr-spoiler-label { diff --git a/src/General/html/Build/post.html b/src/General/html/Build/post.html index 395255057..cd01024bb 100755 --- a/src/General/html/Build/post.html +++ b/src/General/html/Build/post.html @@ -34,7 +34,7 @@ """ \ No newline at end of file +""" diff --git a/src/General/html/Settings/Advanced.html b/src/General/html/Settings/Advanced.html index f0e593a16..07b49fc96 100755 --- a/src/General/html/Settings/Advanced.html +++ b/src/General/html/Settings/Advanced.html @@ -17,7 +17,7 @@ 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 Status/Twitter link (status, @).
+
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
diff --git a/src/General/lib/post.class b/src/General/lib/post.class index 65ace17aa..8e4fc4a37 100755 --- a/src/General/lib/post.class +++ b/src/General/lib/post.class @@ -1,5 +1,5 @@ class Post - callbacks: [] + @callbacks = [] toString: -> @ID constructor: (root, @thread, @board, that={}) -> diff --git a/src/General/lib/thread.class b/src/General/lib/thread.class index 57d82a316..1f15bf714 100755 --- a/src/General/lib/thread.class +++ b/src/General/lib/thread.class @@ -1,5 +1,5 @@ class Thread - callbacks: [] + @callbacks = [] toString: -> @ID constructor: (@ID, @board) -> diff --git a/src/General/meta/manifest.json b/src/General/meta/manifest.json index b7c607807..53039fd37 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": "29", + "minimum_chrome_version": "27", "permissions": [ "storage" ] diff --git a/src/Images/AutoGIF.coffee b/src/Images/AutoGIF.coffee new file mode 100644 index 000000000..71ccc7d8e --- /dev/null +++ b/src/Images/AutoGIF.coffee @@ -0,0 +1,20 @@ +AutoGIF = + init: -> + return if g.VIEW is 'catalog' or !Conf['Auto-GIF'] or g.BOARD.ID in ['gif', 'wsg'] + + Post.callbacks.push + name: 'Auto-GIF' + cb: @node + node: -> + return if @isClone or @isHidden or @thread.isHidden or !@file?.isImage + {thumb, URL} = @file + return unless /gif$/.test(URL) and !/spoiler/.test thumb.src + if @file.isSpoiler + # Revealed spoilers do not have height/width set, this fixes auto-gifs dimensions. + {style} = thumb + style.maxHeight = style.maxWidth = if @isReply then '125px' else '250px' + gif = $.el 'img' + $.on gif, 'load', -> + # Replace the thumbnail once the GIF has finished loading. + thumb.src = URL + gif.src = URL diff --git a/src/Images/FappeTyme.coffee b/src/Images/FappeTyme.coffee index 673d8fd42..26f7d4046 100755 --- a/src/Images/FappeTyme.coffee +++ b/src/Images/FappeTyme.coffee @@ -25,7 +25,7 @@ FappeTyme = Header.addShortcut el, true - Post::callbacks.push + Post.callbacks.push name: 'Fappe Tyme' cb: @node diff --git a/src/Images/Gallery.coffee b/src/Images/Gallery.coffee index c3f8565d0..c10acef26 100644 --- a/src/Images/Gallery.coffee +++ b/src/Images/Gallery.coffee @@ -13,7 +13,7 @@ Gallery = Header.addShortcut el, true - Post::callbacks.push + Post.callbacks.push name: 'Gallery' cb: @node diff --git a/src/Images/ImageExpand.coffee b/src/Images/ImageExpand.coffee index 857ee33f4..87213bc62 100644 --- a/src/Images/ImageExpand.coffee +++ b/src/Images/ImageExpand.coffee @@ -12,7 +12,7 @@ ImageExpand = Header.addShortcut @EAI, true - Post::callbacks.push + Post.callbacks.push name: 'Image Expansion' cb: @node diff --git a/src/Images/ImageHover.coffee b/src/Images/ImageHover.coffee index 5356f50c3..9ac26e045 100755 --- a/src/Images/ImageHover.coffee +++ b/src/Images/ImageHover.coffee @@ -2,7 +2,7 @@ ImageHover = init: -> return if g.VIEW is 'catalog' or !Conf['Image Hover'] - Post::callbacks.push + Post.callbacks.push name: 'Image Hover' cb: @node node: -> diff --git a/src/Images/ImageLoader.coffee b/src/Images/ImageLoader.coffee index b4df767d2..66220873e 100755 --- a/src/Images/ImageLoader.coffee +++ b/src/Images/ImageLoader.coffee @@ -3,7 +3,7 @@ ImageLoader = return if g.VIEW is 'catalog' return unless Conf["Image Prefetching"] or Conf["Replace JPG"] or Conf["Replace PNG"] or Conf["Replace GIF"] - Post::callbacks.push + Post.callbacks.push name: 'Image Replace' cb: @node diff --git a/src/Images/RevealSpoilers.coffee b/src/Images/RevealSpoilers.coffee index 8a884dd8b..b528036da 100755 --- a/src/Images/RevealSpoilers.coffee +++ b/src/Images/RevealSpoilers.coffee @@ -2,11 +2,11 @@ RevealSpoilers = init: -> return if g.VIEW is 'catalog' or !Conf['Reveal Spoiler Thumbnails'] - Post::callbacks.push - name: 'Reveal Spoiler Thumbnails' + Post.callbacks.push cb: @node + node: -> return if @isClone or !@file?.isSpoiler {thumb} = @file thumb.removeAttribute 'style' - thumb.src = @file.thumbURL \ No newline at end of file + thumb.src = @file.thumbURL diff --git a/src/Images/Sauce.coffee b/src/Images/Sauce.coffee index bc585c101..4e7922b06 100755 --- a/src/Images/Sauce.coffee +++ b/src/Images/Sauce.coffee @@ -11,7 +11,7 @@ Sauce = return unless links.length @links = links @link = $.el 'a', target: '_blank' - Post::callbacks.push + Post.callbacks.push name: 'Sauce' cb: @node createSauceLink: (link) -> diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index b4d23ed97..9a7673e62 100755 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -26,7 +26,7 @@ Linkify = if Conf['Title Link'] $.sync 'CachedTitles', Linkify.titleSync - Post::callbacks.push + Post.callbacks.push name: 'Linkify' cb: @node diff --git a/src/Menu/Menu.coffee b/src/Menu/Menu.coffee index e6cd83afb..dac660ec2 100644 --- a/src/Menu/Menu.coffee +++ b/src/Menu/Menu.coffee @@ -3,7 +3,7 @@ Menu = return if g.VIEW is 'catalog' or !Conf['Menu'] @menu = new UI.Menu 'post' - Post::callbacks.push + Post.callbacks.push name: 'Menu' cb: @node diff --git a/src/Miscellaneous/ColorUserIDs.coffee b/src/Miscellaneous/ColorUserIDs.coffee index c47d0cbcd..acb1b9b6c 100755 --- a/src/Miscellaneous/ColorUserIDs.coffee +++ b/src/Miscellaneous/ColorUserIDs.coffee @@ -3,7 +3,7 @@ IDColor = return if g.VIEW is 'catalog' or not Conf['Color User IDs'] @ids = {} - Post::callbacks.push + Post.callbacks.push name: 'Color User IDs' cb: @node diff --git a/src/Miscellaneous/Dice.coffee b/src/Miscellaneous/Dice.coffee index 7b7a3c386..d02698344 100755 --- a/src/Miscellaneous/Dice.coffee +++ b/src/Miscellaneous/Dice.coffee @@ -1,7 +1,7 @@ Dice = init: -> return if g.BOARD.ID isnt 'tg' or g.VIEW is 'catalog' or !Conf['Show Dice Roll'] - Post::callbacks.push + Post.callbacks.push name: 'Show Dice Roll' cb: @node node: -> diff --git a/src/Miscellaneous/ExpandComment.coffee b/src/Miscellaneous/ExpandComment.coffee index c256df59b..38f58baf7 100755 --- a/src/Miscellaneous/ExpandComment.coffee +++ b/src/Miscellaneous/ExpandComment.coffee @@ -2,12 +2,10 @@ ExpandComment = init: -> return if g.VIEW isnt 'index' or !Conf['Comment Expansion'] - if g.BOARD.ID is 'g' - @callbacks.push Fourchan.code - if g.BOARD.ID is 'sci' - @callbacks.push Fourchan.math + @callbacks.push Fourchan.code if g.BOARD.ID is 'g' + @callbacks.push Fourchan.math if g.BOARD.ID is 'sci' - Post::callbacks.push + Post.callbacks.push name: 'Comment Expansion' cb: @node diff --git a/src/Miscellaneous/ExpandThread.coffee b/src/Miscellaneous/ExpandThread.coffee index 5ea898fc7..0f0853f5f 100755 --- a/src/Miscellaneous/ExpandThread.coffee +++ b/src/Miscellaneous/ExpandThread.coffee @@ -2,7 +2,7 @@ ExpandThread = init: -> return if g.VIEW isnt 'index' or !Conf['Thread Expansion'] - Thread::callbacks.push + Thread.callbacks.push name: 'Thread Expansion' cb: @node diff --git a/src/Miscellaneous/FileInfo.coffee b/src/Miscellaneous/FileInfo.coffee index 38cff4ff4..89d694313 100755 --- a/src/Miscellaneous/FileInfo.coffee +++ b/src/Miscellaneous/FileInfo.coffee @@ -3,7 +3,7 @@ FileInfo = return if g.VIEW is 'catalog' or !Conf['File Info Formatting'] @funk = @createFunc Conf['fileInfo'] - Post::callbacks.push + Post.callbacks.push name: 'File Info Formatting' cb: @node node: -> @@ -48,4 +48,4 @@ FileInfo = B: -> FileInfo.convertUnit @file.sizeInBytes, 'B' K: -> FileInfo.convertUnit @file.sizeInBytes, 'KB' M: -> FileInfo.convertUnit @file.sizeInBytes, 'MB' - r: -> if @file.isImage then @file.dimensions else 'PDF' \ No newline at end of file + r: -> if @file.isImage then @file.dimensions else 'PDF' diff --git a/src/Miscellaneous/Fourchan.coffee b/src/Miscellaneous/Fourchan.coffee index 95f418ab4..64b1f4562 100755 --- a/src/Miscellaneous/Fourchan.coffee +++ b/src/Miscellaneous/Fourchan.coffee @@ -10,7 +10,7 @@ Fourchan = pre.innerHTML = prettyPrintOne(pre.innerHTML); }, false); """ - Post::callbacks.push + Post.callbacks.push name: 'Parse /g/ code' cb: @code if board is 'sci' @@ -27,7 +27,7 @@ Fourchan = } }, false); """ - Post::callbacks.push + Post.callbacks.push name: 'Parse /sci/ math' cb: @math code: -> diff --git a/src/Miscellaneous/IDColor.coffee b/src/Miscellaneous/IDColor.coffee new file mode 100644 index 000000000..1fbdd5718 --- /dev/null +++ b/src/Miscellaneous/IDColor.coffee @@ -0,0 +1,40 @@ +IDColor = + init: -> + return if g.VIEW is 'catalog' or !Conf['Color User IDs'] + @ids = {} + + Post.callbacks.push + name: 'Color User IDs' + cb: @node + + node: -> + return if @isClone or !(uid = @info.uniqueID) + rgb = IDColor.compute uid + span = @nodes.uniqueID + {style} = span + style.color = rgb[3] + style.backgroundColor = "rgb(#{rgb[0]},#{rgb[1]},#{rgb[2]})" + $.addClass span, 'painted' + span.textContent = uid + span.title = 'Highlight posts by this ID' + + compute: (uniqueID) -> + if uniqueID of IDColor.ids + return IDColor.ids[uniqueID] + hash = @hash uniqueID + rgb = [ + (hash >> 24) & 0xFF + (hash >> 16) & 0xFF + (hash >> 8) & 0xFF + ] + rgb.push if (rgb[0] * 0.299 + rgb[1] * 0.587 + rgb[2] * 0.114) > 170 + 'black' + else + 'white' + @ids[uniqueID] = rgb + + hash: (uniqueID) -> + msg = 0 + for i in [0...uniqueID.length] by 1 + msg = (msg << 5) - msg + uniqueID.charCodeAt i + msg diff --git a/src/Miscellaneous/RelativeDates.coffee b/src/Miscellaneous/RelativeDates.coffee index 81f85285c..d678ecb23 100755 --- a/src/Miscellaneous/RelativeDates.coffee +++ b/src/Miscellaneous/RelativeDates.coffee @@ -9,7 +9,7 @@ RelativeDates = # Start the timeout. @flush() - Post::callbacks.push + Post.callbacks.push name: 'Relative Post Dates' cb: @node node: -> @@ -104,4 +104,4 @@ RelativeDates = markStale = -> RelativeDates.stale.push update # Kick off initial timeout. - update new Date() \ No newline at end of file + update new Date() diff --git a/src/Miscellaneous/RemoveSpoilers.coffee b/src/Miscellaneous/RemoveSpoilers.coffee index b9e5d0e58..5518f88bf 100755 --- a/src/Miscellaneous/RemoveSpoilers.coffee +++ b/src/Miscellaneous/RemoveSpoilers.coffee @@ -9,7 +9,7 @@ RemoveSpoilers = @wrapper = (text) -> "[spoiler]#{text}[/spoiler]" - Post::callbacks.push + Post.callbacks.push name: 'Reveal Spoilers' cb: @node diff --git a/src/Miscellaneous/Time.coffee b/src/Miscellaneous/Time.coffee index 540cfb637..40d581179 100755 --- a/src/Miscellaneous/Time.coffee +++ b/src/Miscellaneous/Time.coffee @@ -3,7 +3,7 @@ Time = return if g.VIEW is 'catalog' or !Conf['Time Formatting'] @funk = @createFunc Conf['time'] - Post::callbacks.push + Post.callbacks.push name: 'Time Formatting' cb: @node node: -> diff --git a/src/Monitoring/ThreadExcerpt.coffee b/src/Monitoring/ThreadExcerpt.coffee index ed67edfa4..dc55c1b04 100755 --- a/src/Monitoring/ThreadExcerpt.coffee +++ b/src/Monitoring/ThreadExcerpt.coffee @@ -2,8 +2,8 @@ ThreadExcerpt = init: -> return if g.VIEW isnt 'thread' or !Conf['Thread Excerpt'] - Thread::callbacks.push + Thread.callbacks.push name: 'Thread Excerpt' cb: @node node: -> - d.title = Get.threadExcerpt @ \ No newline at end of file + d.title = Get.threadExcerpt @ diff --git a/src/Monitoring/ThreadStats.coffee b/src/Monitoring/ThreadStats.coffee index d9b1ac263..d34007515 100755 --- a/src/Monitoring/ThreadStats.coffee +++ b/src/Monitoring/ThreadStats.coffee @@ -19,7 +19,7 @@ ThreadStats = @fileCountEl = $ '#file-count', sc @pageCountEl = $ '#page-count', sc - Thread::callbacks.push + Thread.callbacks.push name: 'Thread Stats' cb: @node diff --git a/src/Monitoring/ThreadUpdater.coffee b/src/Monitoring/ThreadUpdater.coffee index 0607d258a..9bea86938 100755 --- a/src/Monitoring/ThreadUpdater.coffee +++ b/src/Monitoring/ThreadUpdater.coffee @@ -54,7 +54,7 @@ ThreadUpdater = order: 110 subEntries: subEntries - Thread::callbacks.push + Thread.callbacks.push name: 'Thread Updater' cb: @node @@ -62,6 +62,7 @@ ThreadUpdater = ThreadUpdater.thread = @ ThreadUpdater.root = @OP.nodes.root.parentNode ThreadUpdater.lastPost = +ThreadUpdater.root.lastElementChild.id.match(/\d+/)[0] + ThreadUpdater.outdateCount = 0 ThreadUpdater.cb.interval.call $.el 'input', value: Conf['Interval'] @@ -80,14 +81,14 @@ ThreadUpdater = cb: online: -> - if navigator.onLine + if ThreadUpdater.online = navigator.onLine ThreadUpdater.outdateCount = 0 ThreadUpdater.setInterval() ThreadUpdater.set 'status', null, null else ThreadUpdater.set 'timer', null ThreadUpdater.set 'status', 'Offline', 'warning' - ThreadUpdater.count true + ThreadUpdater.cb.autoUpdate() post: (e) -> return unless ThreadUpdater.isUpdating and e.detail.threadID is ThreadUpdater.thread.ID ThreadUpdater.outdateCount = 0 @@ -108,7 +109,8 @@ ThreadUpdater = return if d.hidden # Reset the counter when we focus this tab. ThreadUpdater.outdateCount = 0 - ThreadUpdater.seconds = Math.min ThreadUpdater.seconds, ThreadUpdater.interval + if ThreadUpdater.seconds > ThreadUpdater.interval + ThreadUpdater.set 'timer', ThreadUpdater.getInterval() scrollBG: -> ThreadUpdater.scrollBG = if Conf['Scroll BG'] -> true @@ -121,29 +123,23 @@ ThreadUpdater = $.cb.value.call @ load: (e) -> {req} = ThreadUpdater - delete ThreadUpdater.req - if e.type isnt 'loadend' # timeout or abort - req.onloadend = null - if e.type is 'timeout' - ThreadUpdater.set 'status', 'Retrying', null - ThreadUpdater.update() - return switch req.status when 200 g.DEAD = false ThreadUpdater.parse JSON.parse(req.response).posts - ThreadUpdater.setInterval() + ThreadUpdater.set 'timer', ThreadUpdater.getInterval() when 404 g.DEAD = true ThreadUpdater.set 'timer', null ThreadUpdater.set 'status', '404', 'warning' + clearTimeout ThreadUpdater.timeoutID ThreadUpdater.thread.kill() $.event 'ThreadUpdate', 404: true thread: ThreadUpdater.thread else ThreadUpdater.outdateCount++ - ThreadUpdater.setInterval() + ThreadUpdater.set 'timer', ThreadUpdater.getInterval() [text, klass] = if req.status is 304 [null, null] else @@ -183,15 +179,15 @@ ThreadUpdater = el.textContent = text el.className = klass if klass isnt undefined - count: (start) -> - clearTimeout ThreadUpdater.timeoutID - ThreadUpdater.timeout() if start and ThreadUpdater.isUpdating and navigator.onLine - timeout: -> ThreadUpdater.timeoutID = setTimeout ThreadUpdater.timeout, 1000 - sec = ThreadUpdater.seconds-- - ThreadUpdater.set 'timer', sec - ThreadUpdater.update() if sec <= 0 + unless n = --ThreadUpdater.seconds + ThreadUpdater.update() + else if n <= -60 + ThreadUpdater.set 'status', 'Retrying', null + ThreadUpdater.update() + else if n > 0 + ThreadUpdater.set 'timer', n update: -> return unless navigator.onLine @@ -202,12 +198,7 @@ ThreadUpdater = ThreadUpdater.set 'timer', 'Update' ThreadUpdater.req.abort() if ThreadUpdater.req url = "//api.4chan.org/#{ThreadUpdater.thread.board}/res/#{ThreadUpdater.thread}.json" - ThreadUpdater.req = $.ajax url, - onabort: ThreadUpdater.cb.load - onloadend: ThreadUpdater.cb.load - ontimeout: ThreadUpdater.cb.load - timeout: $.MINUTE - , + ThreadUpdater.req = $.ajax url, onloadend: ThreadUpdater.cb.load, whenModified: true updateThreadStatus: (title, OP) -> diff --git a/src/Monitoring/ThreadWatcher.coffee b/src/Monitoring/ThreadWatcher.coffee index 42352094b..e95444cc2 100755 --- a/src/Monitoring/ThreadWatcher.coffee +++ b/src/Monitoring/ThreadWatcher.coffee @@ -19,7 +19,7 @@ ThreadWatcher = ThreadWatcher.fetchAllStatus() @db.save() - Thread::callbacks.push + Thread.callbacks.push name: 'Thread Watcher' cb: @node diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee index 90a279ee8..76e3e30d0 100755 --- a/src/Monitoring/Unread.coffee +++ b/src/Monitoring/Unread.coffee @@ -8,7 +8,7 @@ Unread = @posts = [] @postsQuotingYou = [] - Thread::callbacks.push + Thread.callbacks.push name: 'Unread' cb: @node @@ -90,7 +90,7 @@ Unread = for quotelink in post.nodes.quotelinks when QR.db.get Get.postDataFromLink quotelink Unread.postsQuotingYou.push post Unread.openNotification post - return + return openNotification: (post) -> return unless Header.areNotificationsEnabled diff --git a/src/Posting/QuickReply.coffee b/src/Posting/QuickReply.coffee index 79aefcfce..dcbbcdfdf 100644 --- a/src/Posting/QuickReply.coffee +++ b/src/Posting/QuickReply.coffee @@ -10,7 +10,7 @@ QR = else $.ready @persist - Post::callbacks.push + Post.callbacks.push name: 'Quick Reply' cb: @node @@ -140,7 +140,7 @@ QR = if QR.captcha.isEnabled and /captcha|verification/i.test el.textContent # Focus the captcha input on captcha error. QR.captcha.nodes.input.focus() - if Conf['Captcha Warning Notifications'] + if Conf['Captcha Warning Notifications'] and !d.hidden QR.notify el else $.addClass QR.captcha.nodes.input, 'error' @@ -152,22 +152,23 @@ QR = notify: (el) -> notice = new Notice 'warning', el - QR.notifications.push notice - return unless Header.areNotificationsEnabled - notif = new Notification el.textContent, - body: el.textContent - icon: Favicon.logo - notif.onclick = -> window.focus() - <% if (type === 'crx') { %> - # Firefox automatically closes notifications - # so we can't control the onclose properly. - notif.onclose = -> notice.close() - notif.onshow = -> - setTimeout -> - notif.onclose = null - notif.close() - , 7 * $.SECOND - <% } %> + unless Header.areNotificationsEnabled and d.hidden + QR.notifications.push notice + else + notif = new Notification el.textContent, + body: el.textContent + icon: Favicon.logo + notif.onclick = -> window.focus() + <% if (type === 'crx') { %> + # Firefox automatically closes notifications + # so we can't control the onclose properly. + notif.onclose = -> notice.close() + notif.onshow = -> + setTimeout -> + notif.onclose = null + notif.close() + , 7 * $.SECOND + <% } %> notifications: [] @@ -445,7 +446,6 @@ QR = e.preventDefault() QR.open() QR.handleFiles e.dataTransfer.files - $.addClass QR.nodes.el, 'dump' paste: (e) -> files = [] @@ -1247,12 +1247,15 @@ QR = ThreadUpdater.postID = postID + + # Post/upload confirmed as successful. $.event 'QRPostSuccessful', { board: g.BOARD threadID postID } + $.event 'QRPostSuccessful_', {threadID, postID} # Enable auto-posting if we have stuff left to post, disable it otherwise. postsCount = QR.posts.length - 1 diff --git a/src/Quotelinks/QuoteBacklink.coffee b/src/Quotelinks/QuoteBacklink.coffee index 37ec5642a..7b4cd5112 100755 --- a/src/Quotelinks/QuoteBacklink.coffee +++ b/src/Quotelinks/QuoteBacklink.coffee @@ -16,10 +16,10 @@ QuoteBacklink = format = Conf['backlink'].replace /%id/g, "' + id + '" @funk = Function 'id', "return '#{format}'" @containers = {} - Post::callbacks.push + Post.callbacks.push name: 'Quote Backlinking Part 1' cb: @firstNode - Post::callbacks.push + Post.callbacks.push name: 'Quote Backlinking Part 2' cb: @secondNode firstNode: -> @@ -58,4 +58,4 @@ QuoteBacklink = $.addClass @nodes.post, 'quoted' getContainer: (id) -> @containers[id] or= - $.el 'span', className: 'container' \ No newline at end of file + $.el 'span', className: 'container' diff --git a/src/Quotelinks/QuoteCT.coffee b/src/Quotelinks/QuoteCT.coffee index b0618d487..953c7e20f 100755 --- a/src/Quotelinks/QuoteCT.coffee +++ b/src/Quotelinks/QuoteCT.coffee @@ -7,7 +7,7 @@ QuoteCT = # \u00A0 is nbsp @text = '\u00A0(Cross-thread)' - Post::callbacks.push + Post.callbacks.push name: 'Mark Cross-thread Quotes' cb: @node node: -> diff --git a/src/Quotelinks/QuoteInline.coffee b/src/Quotelinks/QuoteInline.coffee index 95a225e38..3aa3fd455 100755 --- a/src/Quotelinks/QuoteInline.coffee +++ b/src/Quotelinks/QuoteInline.coffee @@ -18,7 +18,7 @@ QuoteInline = if Conf['Comment Expansion'] ExpandComment.callbacks.push @node - Post::callbacks.push + Post.callbacks.push name: 'Quote Inlining' cb: @node diff --git a/src/Quotelinks/QuoteOP.coffee b/src/Quotelinks/QuoteOP.coffee index 721688a6b..1f5554dfe 100755 --- a/src/Quotelinks/QuoteOP.coffee +++ b/src/Quotelinks/QuoteOP.coffee @@ -7,7 +7,7 @@ QuoteOP = # \u00A0 is nbsp @text = '\u00A0(OP)' - Post::callbacks.push + Post.callbacks.push name: 'Mark OP Quotes' cb: @node diff --git a/src/Quotelinks/QuotePreview.coffee b/src/Quotelinks/QuotePreview.coffee index 15fd65f64..17c9e1b0c 100755 --- a/src/Quotelinks/QuotePreview.coffee +++ b/src/Quotelinks/QuotePreview.coffee @@ -5,7 +5,7 @@ QuotePreview = if Conf['Comment Expansion'] ExpandComment.callbacks.push @node - Post::callbacks.push + Post.callbacks.push name: 'Quote Previewing' cb: @node diff --git a/src/Quotelinks/QuoteStrikeThrough.coffee b/src/Quotelinks/QuoteStrikeThrough.coffee index 53d4e0fc1..0a7098f39 100755 --- a/src/Quotelinks/QuoteStrikeThrough.coffee +++ b/src/Quotelinks/QuoteStrikeThrough.coffee @@ -2,7 +2,7 @@ QuoteStrikeThrough = init: -> return if g.VIEW is 'catalog' or !Conf['Reply Hiding Buttons'] and !Conf['Reply Hiding Link'] and !Conf['Filter'] - Post::callbacks.push + Post.callbacks.push name: 'Strike-through Quotes' cb: @node @@ -12,4 +12,4 @@ QuoteStrikeThrough = {boardID, postID} = Get.postDataFromLink quotelink if g.posts["#{boardID}.#{postID}"]?.isHidden $.addClass quotelink, 'filtered' - return \ No newline at end of file + return diff --git a/src/Quotelinks/QuoteThreading.coffee b/src/Quotelinks/QuoteThreading.coffee index f977a82a3..ec28ddfcb 100755 --- a/src/Quotelinks/QuoteThreading.coffee +++ b/src/Quotelinks/QuoteThreading.coffee @@ -20,7 +20,7 @@ QuoteThreading = $.on d, '4chanXInitFinished', @setup - Post::callbacks.push + Post.callbacks.push name: 'Quote Threading' cb: @node diff --git a/src/Quotelinks/QuoteYou.coffee b/src/Quotelinks/QuoteYou.coffee index cd8b641c4..52423e364 100755 --- a/src/Quotelinks/QuoteYou.coffee +++ b/src/Quotelinks/QuoteYou.coffee @@ -11,7 +11,9 @@ QuoteYou = if Conf['Comment Expansion'] ExpandComment.callbacks.push @node - Post::callbacks.push + # \u00A0 is nbsp + @text = '\u00A0(You)' + Post.callbacks.push name: 'Mark Quotes of You' cb: @node @@ -25,7 +27,7 @@ QuoteYou = return unless @quotes.length for quotelink in @nodes.quotelinks when QR.db.get Get.postDataFromLink quotelink - $.add quotelink, $.tn '\u00A0(You)' + $.add quotelink, $.tn QuoteYou.text $.addClass quotelink, 'you' $.addClass @nodes.root, 'quotesYou' return diff --git a/src/Quotelinks/Quotify.coffee b/src/Quotelinks/Quotify.coffee index c8679c17e..3fd3fc2bb 100755 --- a/src/Quotelinks/Quotify.coffee +++ b/src/Quotelinks/Quotify.coffee @@ -5,7 +5,7 @@ Quotify = if Conf['Comment Expansion'] ExpandComment.callbacks.push @node - Post::callbacks.push + Post.callbacks.push name: 'Resurrect Quotes' cb: @node node: -> @@ -81,4 +81,9 @@ Quotify = @nodes.quotelinks.push a fixDeadlink: (deadlink) -> + if !(el = deadlink.previousSibling) or el.nodeName is 'BR' + green = $.el 'span', + className: 'quote' + $.before deadlink, green + $.add green, deadlink $.replace deadlink, [deadlink.childNodes...] diff --git a/src/Theming/Rice.coffee b/src/Theming/Rice.coffee index ac940db59..e9a63ad43 100644 --- a/src/Theming/Rice.coffee +++ b/src/Theming/Rice.coffee @@ -3,7 +3,7 @@ Rice = $.ready -> Rice.nodes d.body - Post::callbacks.push + Post.callbacks.push name: 'Rice Checkboxes' cb: @node