diff --git a/CHANGELOG.md b/CHANGELOG.md index 086f80c71..4379584f7 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,179 @@ Sometimes the changelog has notes (not comprehensive) acknowledging people's wor The links to individual versions below are to copies of the script with the update URL removed. If you want automatic updates, install the script from the links on the [main page](https://github.com/ccd0/4chan-x). +## v1.11.0 + +**v1.11.0.6** *(2015-06-20)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.11.0.6/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.11.0.6/builds/4chan-X-noupdate.crx "Chromium version")] +- Support toggling images in the captcha with the number keys (as arranged in the numpad) and the UIOJKLM,. keys. +- Arrow key navigation now works in noscript captcha. +- Various captcha-related improvements/bugfixes. +- Support space bar, numbers in numpad, comma, and space in keybinds. + +**v1.11.0.5** *(2015-06-20)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.11.0.5/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.11.0.5/builds/4chan-X-noupdate.crx "Chromium version")] +- Add `Captcha Language` setting in the `Advanced` panel. +- Minor bugfixes. + +**v1.11.0.4** *(2015-06-20)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.11.0.4/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.11.0.4/builds/4chan-X-noupdate.crx "Chromium version")] +- Noscript captcha improvements, including the ability to click the image rather than the little checkbox. + +**v1.11.0.3** *(2015-06-19)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.11.0.3/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.11.0.3/builds/4chan-X-noupdate.crx "Chromium version")] +- Merge v1.10.14.4: Update script for new non-Javascript captcha using image selection. + +**v1.11.0.2** *(2015-06-19)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.11.0.2/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.11.0.2/builds/4chan-X-noupdate.crx "Chromium version")] +- Remove code that focused on first image of the captcha as Google now focuses on the refresh button. + +**v1.11.0.1** *(2015-06-16)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.11.0.1/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.11.0.1/builds/4chan-X-noupdate.crx "Chromium version")] +- Merge v1.10.14.3: Fix words being cut off in non-Javascript captcha. + +**v1.11.0.0** *(2015-06-14)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.11.0.0/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.11.0.0/builds/4chan-X-noupdate.crx "Chromium version")] +- Based on v1.10.14.2. +- Gallery now preloads the next image in sequence. +- `Stretch to Fit` option added to gallery. +- Various bug fixes. +- Drop workarounds for old versions of Chromium (< v34). + +### v1.10.14 + +**v1.10.14.4** *(2015-06-19)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.14.4/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.14.4/builds/4chan-X-noupdate.crx "Chromium version")] +- Update script for new non-Javascript captcha using image selection. + +**v1.10.14.3** *(2015-06-16)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.14.3/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.14.3/builds/4chan-X-noupdate.crx "Chromium version")] +- Fix words being cut off in non-Javascript captcha. + +**v1.10.14.2** *(2015-06-11)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.14.2/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.14.2/builds/4chan-X-noupdate.crx "Chromium version")] +- Bring back workaround for scrolling to top caused by captcha as some users are still reporting it happening. + +**v1.10.14.1** *(2015-06-11)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.14.1/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.14.1/builds/4chan-X-noupdate.crx "Chromium version")] +- Add ImgOps to default sauce examples: `//imgops.com/%URL;types:gif,jpg,png` + +**v1.10.14.0** *(2015-06-07)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.14.0/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.14.0/builds/4chan-X-noupdate.crx "Chromium version")] +- Based on v1.10.13.4. +- Fix board-specific filters in thread watcher (filters apply to unread posts quoting you). + +### v1.10.13 + +**v1.10.13.4** *(2015-06-05)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.13.4/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.13.4/builds/4chan-X-noupdate.crx "Chromium version")] +- Support selecting images in the image captcha with the space bar in addition to the enter key. + +**v1.10.13.3** *(2015-06-03)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.13.3/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.13.3/builds/4chan-X-noupdate.crx "Chromium version")] +- Merge v1.10.12.10: Revert workaround for scrolling to top as it seems to have been fixed on Google's end. + +**v1.10.13.2** *(2015-06-03)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.13.2/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.13.2/builds/4chan-X-noupdate.crx "Chromium version")] +- Merge v1.10.12.9: Update for captcha changes. +- Merge v1.10.12.9: Work around issue where the captcha causes scrolling to the top of the page. + +**v1.10.13.1** *(2015-05-27)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.13.1/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.13.1/builds/4chan-X-noupdate.crx "Chromium version")] +- Fix manual page number updating not working when `Updater and Stats in Header` is off. + +**v1.10.13.0** *(2015-05-26)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.13.0/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.13.0/builds/4chan-X-noupdate.crx "Chromium version")] +- Based on v1.10.12.8. +- You can now manually update the page number in thread stats by clicking on it. +- Page number in thread stats does not show on /f/. +- Change default gallery slideshow toggle keybind from `s` to `Ctrl+Right`. +- Add IJKL as an alternative to the arrow keys for the image captcha. +- Clicking on the thread watcher refresh button while it's loading threads aborts it. +- Make thread watcher a bit more efficient by fixing some cases where it wasn't sending `If-Modified-Since`. +- Restore clearing the unread count in the thread watcher when a thread 404's. +- Various minor bugfixes. + +### v1.10.12 + +**v1.10.12.10** *(2015-06-03)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.10/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.10/builds/4chan-X-noupdate.crx "Chromium version")] +- Revert workaround for scrolling to top as it seems to have been fixed on Google's end. + +**v1.10.12.9** *(2015-06-03)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.9/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.9/builds/4chan-X-noupdate.crx "Chromium version")] +- Update for captcha changes. +- Work around issue where the captcha causes scrolling to the top of the page. + +**v1.10.12.8** *(2015-05-22)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.8/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.8/builds/4chan-X-noupdate.crx "Chromium version")] +- Remove guard against dropping files into captcha due to continued reports of disabled captchas. + +**v1.10.12.7** *(2015-05-15)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.7/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.7/builds/4chan-X-noupdate.crx "Chromium version")] +- Fix bug where dragging and dropping disabled the captcha even after the drop was complete. + +**v1.10.12.6** *(2015-05-15)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.6/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.6/builds/4chan-X-noupdate.crx "Chromium version")] +- (Hasumi) Update archive.moe: Add /an/. + +**v1.10.12.5** *(2015-05-14)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.5/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.5/builds/4chan-X-noupdate.crx "Chromium version")] +- Fix bug where submit button was still getting unwanted focus under certain conditions. + +**v1.10.12.4** *(2015-05-10)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.4/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.4/builds/4chan-X-noupdate.crx "Chromium version")] +- Improve reporting to archive functionality. + +**v1.10.12.3** *(2015-05-09)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.3/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.3/builds/4chan-X-noupdate.crx "Chromium version")] +- Fix some captcha-related bugs. + +**v1.10.12.2** *(2015-05-08)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.2/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.2/builds/4chan-X-noupdate.crx "Chromium version")] +- Fix sandboxed HTTP sauce links not loading from HTTPS pages. + +**v1.10.12.1** *(2015-05-04)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.1/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.1/builds/4chan-X-noupdate.crx "Chromium version")] +- Give paste area cursor pointer like the other QR buttons. + +**v1.10.12.0** *(2015-05-04)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.0/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.12.0/builds/4chan-X-noupdate.crx "Chromium version")] +- Based on v1.10.11.12. +- Fix files dropped on captcha causing navigation to the file. +- Fix size of report window when Javascript is disabled. +- QR character count now handles surrogate pairs correctly and turns red when the limit of 2000 is reached. +- Add `;sandbox` option to sauce links to open links without scripts or popups. Re-add swfchan. + +### v1.10.11 + +**v1.10.11.12** *(2015-05-03)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.12/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.12/builds/4chan-X-noupdate.crx "Chromium version")] +- Add scrollbars to captcha popup when larger than window. + +**v1.10.11.11** *(2015-05-03)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.11/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.11/builds/4chan-X-noupdate.crx "Chromium version")] +- Remove swfchan from default sauce list. + +**v1.10.11.10** *(2015-05-03)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.10/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.10/builds/4chan-X-noupdate.crx "Chromium version")] +- Make link to MDN in filter guide stand out better. + +**v1.10.11.9** *(2015-05-02)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.9/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.9/builds/4chan-X-noupdate.crx "Chromium version")] +- Fix loading archived /f/ posts from before tag was recorded. + +**v1.10.11.8** *(2015-05-02)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.8/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.8/builds/4chan-X-noupdate.crx "Chromium version")] +- Fix some /f/-specific quotelink bugs. + +**v1.10.11.7** *(2015-05-02)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.7/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.7/builds/4chan-X-noupdate.crx "Chromium version")] +- Fix "PDF" being parsed as a Flash tag. + +**v1.10.11.6** *(2015-04-26)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.6/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.6/builds/4chan-X-noupdate.crx "Chromium version")] +- Fix size of window for reporting to fgts archive. + +**v1.10.11.5** *(2015-04-26)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.5/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.5/builds/4chan-X-noupdate.crx "Chromium version")] +- Reduce unwanted scrolling from captcha. + +**v1.10.11.4** *(2015-04-26)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.4/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.4/builds/4chan-X-noupdate.crx "Chromium version")] +- Don't apply filters to the unread post count in the thread watcher, but do apply them to unread posts quoting you. + +**v1.10.11.3** *(2015-04-26)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.3/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.3/builds/4chan-X-noupdate.crx "Chromium version")] +- Merge v1.10.10.4: Possible fix for bug causing scrolling to the top of the page upon loading image captcha. + +**v1.10.11.2** *(2015-04-25)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.2/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.2/builds/4chan-X-noupdate.crx "Chromium version")] +- Posts hidden by filtering are no longer counted as unread posts in the thread watcher. +- Add Flash tag (`%g`) to File Info Formatting. +- Fix subject not being displayed in old non-OP posts loaded from the archives. + +**v1.10.11.1** *(2015-04-24)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.1/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.1/builds/4chan-X-noupdate.crx "Chromium version")] +- Merge v1.10.10.3: Fix original post form not showing when JS is disabled. + +**v1.10.11.0** *(2015-04-24)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.0/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.0/builds/4chan-X-noupdate.crx "Chromium version")] +- Based on v1.10.10.2. +- Fix whitespace being stripped from the comment before filtering. This makes it possible to filter whitespace spam. + ### v1.10.10 +**v1.10.10.4** *(2015-04-26)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.10.4/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.10.4/builds/4chan-X-noupdate.crx "Chromium version")] +- Possible fix for bug causing scrolling to the top of the page upon loading image captcha. + +**v1.10.10.3** *(2015-04-24)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.10.3/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.10.3/builds/4chan-X-noupdate.crx "Chromium version")] +- Fix original post form not showing when JS is disabled. + +**v1.10.10.2** *(2015-04-21)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.10.2/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.10.2/builds/4chan-X-noupdate.crx "Chromium version")] +- Add focus indication to verify button in captcha popup. + +**v1.10.10.1** *(2015-04-19)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.10.1/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.10.1/builds/4chan-X-noupdate.crx "Chromium version")] +- Merge v1.10.9.5: (thebladeee) Archive list: Transferred /w/ and /wg/ back to Nyafuu. + **v1.10.10.0** *(2015-04-18)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.10.0/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.10.0/builds/4chan-X-noupdate.crx "Chromium version")] - Based on v1.10.9.4. - Make images in image captcha selectable with arrow keys. @@ -11,6 +182,9 @@ The links to individual versions below are to copies of the script with the upda ### v1.10.9 +**v1.10.9.5** *(2015-04-19)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.9.5/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.9.5/builds/4chan-X-noupdate.crx "Chromium version")] +- (thebladeee) Archive list: Transferred /w/ and /wg/ back to Nyafuu. + **v1.10.9.4** *(2015-04-17)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.9.4/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.9.4/builds/4chan-X-noupdate.crx "Chromium version")] - (Hasumi) Update archive.moe: Add /gif/. diff --git a/Gruntfile.coffee b/Gruntfile.coffee index ba1d5a8c3..f95978860 100755 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -8,6 +8,12 @@ module.exports = (grunt) -> json = (data) -> "`#{JSON.stringify(data).replace(/`/g, '\\`')}`" + importCSS = (filenames...) -> + grunt.template.process( + filenames.map((name) -> grunt.file.read "src/General/css/#{name}.css").join(''), + {data: grunt.config 'pkg'} + ).trim().replace(/\n+/g, '\n').split(/^/m).map(JSON.stringify).join(' +\n').replace(/`/g, '\\`') + importHTML = (filename) -> html grunt.template.process(grunt.file.read("src/General/html/#{filename}.html").replace(/^ +/gm, '').replace(/\r?\n/g, ''), data: grunt.config('pkg')) @@ -16,13 +22,14 @@ module.exports = (grunt) -> parts = [] text = template while text - if part = text.match /^[^{}]+(?!{)/ + if part = text.match /^(?:[^{}\\]|\\.)+(?!{)/ text = text[part[0].length..] - context = (context + part[0]) + unescaped = part[0].replace /\\(.)/g, '$1' + context = (context + unescaped) .replace(/(=['"])[^'"<>]*/g, '$1') .replace(/(<\w+)( [\w-]+((?=[ >])|=''|=""))*/g, '$1') .replace(/^([^'"<>]+|<\/?\w+>)*/, '') - parts.push json part[0] + parts.push json unescaped else if part = text.match /^([^}]){([^}`]*)}/ text = text[part[0].length..] unless context is '' or (part[1] is '$' and /\=['"]$/.test context) or part[1] is '?' @@ -68,6 +75,7 @@ module.exports = (grunt) -> options: process: Object.create(null, data: get: -> pkg = grunt.config 'pkg' + pkg.importCSS = importCSS pkg.importHTML = importHTML pkg.html = html pkg.assert = assert @@ -184,6 +192,7 @@ module.exports = (grunt) -> """.split('\n').join('&&') stable: command: """ + git push . HEAD:bstable git tag -af stable -m "<%= pkg.meta.name %> v<%= pkg.meta.version %>." git checkout gh-pages git pull diff --git a/LICENSE b/LICENSE index 6d39a98a9..7812a0c8d 100755 --- a/LICENSE +++ b/LICENSE @@ -72,9 +72,6 @@ * 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 +* Font Awesome by Dave Gandy (http://fontawesome.io) +* license: http://fontawesome.io/license/ */ diff --git a/README.md b/README.md index 6063f9b7e..2e750592f 100755 --- a/README.md +++ b/README.md @@ -28,7 +28,8 @@ Only the latest stable version of 4chan X is available. ## Other browsers This fork of 4chan X is not guaranteed to work correctly in other browsers, but you are welcome to try your luck. Pull requests to fix the bugs you will likely find are always welcome. You may fare better with [loadletter's fork](https://github.com/loadletter/4chan-x), which has fewer features but less dependence on browser-specific APIs. -- [Installing 4chan X in dwb](https://github.com/ccd0/4chan-x/wiki/Installing-4chan-X-in-dwb) +- Some people have reported success in Safari using [JS Blocker](http://jsblocker.toggleable.com/) to install the Firefox/Greasemonkey version. +- Instructions are available for [installing 4chan X in dwb](https://github.com/ccd0/4chan-x/wiki/Installing-4chan-X-in-dwb). ## Beta version New features and non-urgent bugfixes are released on the beta channel for further testing before they are moved the stable version. Please [report](https://github.com/ccd0/4chan-x/issues) any issues you find, and be sure to mention which version you're using. You should back up your settings regularly to prevent them from being lost due to bugs. diff --git a/builds/4chan-X-beta.crx b/builds/4chan-X-beta.crx index f1f10e149..9bfbaa0d9 100644 Binary files a/builds/4chan-X-beta.crx and b/builds/4chan-X-beta.crx differ diff --git a/builds/4chan-X-beta.meta.js b/builds/4chan-X-beta.meta.js index c0ec86ac3..6b927ebda 100644 --- a/builds/4chan-X-beta.meta.js +++ b/builds/4chan-X-beta.meta.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan X beta -// @version 1.10.10.0 +// @version 1.11.0.6 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X @@ -10,9 +10,11 @@ // @match *://sys.4chan.org/* // @match *://a.4cdn.org/* // @match *://i.4cdn.org/* +// @match *://www.4chan.org/banned +// @match *://www.4chan.org/feedback // @match https://www.google.com/recaptcha/api2/anchor?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @match https://www.google.com/recaptcha/api2/frame?*&k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* -// @match *://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc +// @match *://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue diff --git a/builds/4chan-X-beta.user.js b/builds/4chan-X-beta.user.js index 894a13bfb..bd2c64c83 100644 --- a/builds/4chan-X-beta.user.js +++ b/builds/4chan-X-beta.user.js @@ -1,7 +1,7 @@ // Generated by CoffeeScript // ==UserScript== // @name 4chan X beta -// @version 1.10.10.0 +// @version 1.11.0.6 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X @@ -11,9 +11,11 @@ // @match *://sys.4chan.org/* // @match *://a.4cdn.org/* // @match *://i.4cdn.org/* +// @match *://www.4chan.org/banned +// @match *://www.4chan.org/feedback // @match https://www.google.com/recaptcha/api2/anchor?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @match https://www.google.com/recaptcha/api2/frame?*&k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* -// @match *://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc +// @match *://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue @@ -100,11 +102,8 @@ * 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 +* Font Awesome by Dave Gandy (http://fontawesome.io) +* license: http://fontawesome.io/license/ */ 'use strict'; @@ -238,7 +237,7 @@ 'Auto-load captcha': [false, 'Automatically load the captcha in the QR even if your post is empty.', 1], 'Post on Captcha Completion': [false, 'Submit the post immediately when the captcha is completed.', 1], 'Bottom QR Link': [true, 'Places a link on the bottom of threads to open the QR.', 1], - 'Captcha Fixes': [true, 'Make captcha more keyboard-navigable.'] + 'Captcha Fixes': [true, 'Make captcha easier to use, especially with the keyboard.'] }, 'Quote Links': { 'Quote Backlinks': [true, 'Add quote backlinks.'], @@ -270,6 +269,7 @@ 'Hide Thumbnails': [false], 'Fit Width': [true], 'Fit Height': [true], + 'Stretch to Fit': [false], 'Scroll to Post': [true], 'Slide Delay': [6.0] }, @@ -295,7 +295,7 @@ filesize: '', MD5: '' }, - sauces: "https://www.google.com/searchbyimage?image_url=%IMG\nhttp://iqdb.org/?url=%IMG\nhttp://eye.swfchan.com/search/?q=%name;types:swf\n#//tineye.com/search?url=%IMG\n#https://www.yandex.com/images/search?rpt=imageview&img_url=%IMG\n#//saucenao.com/search.php?url=%IMG\n#http://3d.iqdb.org/?url=%IMG\n#http://regex.info/exif.cgi?imgurl=%URL\n# uploaders:\n#//imgur.com/upload?url=%URL;types:gif,jpg,png,pdf;text:Upload to imgur\n# \"View Same\" in archives:\n#https://archive.moe/_/search/image/%MD5/;text:View same on archive.moe\n#https://archive.moe/%board/search/image/%MD5/;text:View same on archive.moe/%board/;boards:a,biz,c,co,diy,fit,gd,h,i,jp,k,m,mlp,po,qa,r9k,s4s,sci,tg,u,v,vg,vp,vr,wsg\n#https://rbt.asia/%board/image/%MD5;text:View same on RBT /%board/;boards:cgl,g,mu,qa,w", + sauces: "https://www.google.com/searchbyimage?image_url=%IMG\nhttp://iqdb.org/?url=%IMG\nhttp://eye.swfchan.com/search/?q=%name;types:swf;sandbox\n#//tineye.com/search?url=%IMG\n#https://www.yandex.com/images/search?rpt=imageview&img_url=%IMG\n#//saucenao.com/search.php?url=%IMG\n#http://3d.iqdb.org/?url=%IMG\n# tools:\n#http://regex.info/exif.cgi?imgurl=%URL\n#//imgops.com/%URL;types:gif,jpg,png\n# uploaders:\n#//imgur.com/upload?url=%URL;types:gif,jpg,png,pdf;text:Upload to imgur\n# \"View Same\" in archives:\n#https://archive.moe/_/search/image/%MD5/;text:View same on archive.moe\n#https://archive.moe/%board/search/image/%MD5/;text:View same on archive.moe/%board/;boards:a,biz,c,co,diy,fit,gd,h,i,jp,k,m,mlp,po,qa,r9k,s4s,sci,tg,u,v,vg,vp,vr,wsg\n#https://rbt.asia/%board/image/%MD5;text:View same on RBT /%board/;boards:cgl,g,mu,qa,w", FappeT: { werk: false }, @@ -325,9 +325,10 @@ QR: { 'QR.personas': "#options:\"sage\";boards:jp;always" }, + captchaLanguage: '', time: '%m/%d/%y(%a)%H:%M:%S', backlink: '>>%id', - fileInfo: '%l (%p%s, %r)', + fileInfo: '%l (%p%s, %r%g)', favicon: 'ferongr', usercss: '', hotkeys: { @@ -349,7 +350,7 @@ 'Expand images': ['e', 'Expand all images.'], 'Open Gallery': ['g', 'Opens the gallery.'], 'Pause': ['p', 'Pause/play videos in the gallery.'], - 'Slideshow': ['s', 'Toggle the gallery slideshow mode.'], + 'Slideshow': ['Ctrl+Right', 'Toggle the gallery slideshow mode.'], 'fappeTyme': ['f', 'Toggle Fappe Tyme.'], 'werkTyme': ['Shift+w', 'Toggle Werk Tyme.'], 'Front page': ['1', 'Jump to front page.'], @@ -399,7 +400,7 @@ doc = d.documentElement; g = { - VERSION: '1.10.10.0', + VERSION: '1.11.0.6', NAMESPACE: '4chan X.', boards: {} }; @@ -433,6 +434,10 @@ return html; }; + E.url = function(content) { + return "data:text/html;charset=utf-8," + (encodeURIComponent(content.innerHTML)); + }; + $ = function(selector, root) { if (root == null) { root = d.body; @@ -505,7 +510,7 @@ }); }; return function(url, options, extra) { - var err, form, r, type, upCallbacks, whenModified; + var err, form, r, ref, type, upCallbacks, whenModified; if (extra == null) { extra = {}; } @@ -523,11 +528,11 @@ return; } if (whenModified) { - if (url in lastModified) { - r.setRequestHeader('If-Modified-Since', lastModified[url]); + if (((ref = lastModified[whenModified]) != null ? ref[url] : void 0) != null) { + r.setRequestHeader('If-Modified-Since', lastModified[whenModified][url]); } $.on(r, 'load', function() { - return lastModified[url] = r.getResponseHeader('Last-Modified'); + return (lastModified[whenModified] || (lastModified[whenModified] = {}))[url] = r.getResponseHeader('Last-Modified'); }); } if (/\.json$/.test(url)) { @@ -1245,7 +1250,7 @@ this.info.nameBlock = Conf['Anonymize'] ? 'Anonymous' : this.nodes.nameBlock.textContent.trim(); if (subject = $('.subject', info)) { this.nodes.subject = subject; - this.info.subject = subject.textContent; + this.info.subject = subject.textContent || void 0; } if (name = $('.name', info)) { this.nodes.name = name; @@ -1296,28 +1301,33 @@ } Post.prototype.parseComment = function() { - var bq, k, len1, node, ref, spoilers; + var abbr, bq, commentDisplay, k, len1, len2, node, q, ref, spoilers; this.nodes.comment.normalize(); bq = this.nodes.comment.cloneNode(true); - ref = $$('.abbr, .exif, b, marquee', bq); + ref = $$('.abbr + br, .exif, b, .fortune', bq); for (k = 0, len1 = ref.length; k < len1; k++) { node = ref[k]; $.rm(node); } + if (abbr = $('.abbr', bq)) { + $.rm(abbr); + } this.info.comment = this.nodesToText(bq); - spoilers = $$('s', bq); - return this.info.commentSpoilered = (function() { - var len2, q; + if (abbr) { + this.info.comment = this.info.comment.replace(/\n\n$/, ''); + } + commentDisplay = this.info.comment; + if (!(Conf['Remove Spoilers'] || Conf['Reveal Spoilers'])) { + spoilers = $$('s', bq); if (spoilers.length) { for (q = 0, len2 = spoilers.length; q < len2; q++) { node = spoilers[q]; $.replace(node, $.tn('[spoiler]')); } - return this.nodesToText(bq); - } else { - return this.info.comment; + commentDisplay = this.nodesToText(bq); } - }).call(this); + } + return this.info.commentDisplay = commentDisplay.trim().replace(/\s+$/gm, ''); }; Post.prototype.nodesToText = function(bq) { @@ -1328,7 +1338,7 @@ while (node = nodes.snapshotItem(i++)) { text += node.data || '\n'; } - return text.trim().replace(/\s+$/gm, ''); + return text; }; Post.prototype.parseQuotes = function() { @@ -1357,7 +1367,7 @@ }; Post.prototype.parseFile = function() { - var fileEl, fileText, info, link, ref, ref1, size, thumb, unit; + var fileEl, fileText, info, link, m, ref, ref1, ref2, size, thumb, unit; if (!(fileEl = $('.file', this.nodes.post))) { return; } @@ -1371,12 +1381,13 @@ this.file = { text: fileText, link: link, - URL: link.href, + url: link.href, name: fileText.title || link.title || link.textContent, size: info[1], isImage: /(jpg|png|gif)$/i.test(link.href), isVideo: /webm$/i.test(link.href), - dimensions: (ref1 = info[0].match(/\d+x\d+/)) != null ? ref1[0] : void 0 + dimensions: (ref1 = info[0].match(/\d+x\d+/)) != null ? ref1[0] : void 0, + tag: (ref2 = info[0].match(/,[^,]*, ([a-z]+)\)/i)) != null ? ref2[1] : void 0 }; size = +this.file.size.match(/[\d.]+/)[0]; unit = ['B', 'KB', 'MB', 'GB'].indexOf(this.file.size.match(/\w+$/)[0]); @@ -1387,7 +1398,7 @@ if ((thumb = $('.fileThumb > [data-md5]', fileEl))) { return $.extend(this.file, { thumb: thumb, - thumbURL: location.protocol + "//i.4cdn.org/" + this.board + "/" + (link.href.match(/(\d+)\./)[1]) + "s.jpg", + thumbURL: (m = link.href.match(/\d+(?=\.\w+$)/)) ? location.protocol + "//i.4cdn.org/" + this.board + "/" + m[0] + "s.jpg" : void 0, MD5: thumb.dataset.md5, isSpoiler: $.hasClass(thumb.parentNode, 'imgspoiler') }); @@ -2046,7 +2057,7 @@ function Connection(target1, origin1, cb1) { this.target = target1; this.origin = origin1; - this.cb = cb1; + this.cb = cb1 != null ? cb1 : {}; this.onMessage = bind(this.onMessage, this); this.send = bind(this.send, this); $.on(window, 'message', this.onMessage); @@ -2257,42 +2268,50 @@ postID: this.postID, threadID: this.threadID, boardID: this.boardID, - name: data.name, + isReply: this.postID !== this.threadID + }; + o.info = { + subject: data.title, + email: data.email, + name: data.name || '', + tripcode: data.trip, capcode: (function() { switch (data.capcode) { case 'M': - return 'mod'; + return 'Mod'; case 'A': - return 'admin'; + return 'Admin'; case 'D': - return 'developer'; + return 'Developer'; } })(), - tripcode: data.trip, uniqueID: data.poster_hash, - email: data.email || '', - subject: data.title, flagCode: data.poster_country, - flagName: data.poster_country_name, - date: data.fourchan_date, + flag: data.poster_country_name, dateUTC: data.timestamp, - comment: comment + dateText: data.fourchan_date, + commentHTML: comment }; + if (o.info.capcode) { + delete o.info.uniqueID; + } if ((ref = data.media) != null ? ref.media_filename : void 0) { o.file = { name: data.media.media_filename, - timestamp: data.media.media_orig, - url: data.media.media_link || data.media.remote_media_link || ("//i.4cdn.org/" + this.boardID + "/" + (encodeURIComponent(data.media[this.boardID === 'f' ? 'media_filename' : 'media_orig']))), + url: data.media.media_link || data.media.remote_media_link || (location.protocol + "//i.4cdn.org/" + this.boardID + "/" + (encodeURIComponent(data.media[this.boardID === 'f' ? 'media_filename' : 'media_orig']))), 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 || ("//i.4cdn.org/" + this.boardID + "/" + data.media.preview_orig), + size: $.bytesToString(data.media.media_size), + thumbURL: data.media.thumb_link || (location.protocol + "//i.4cdn.org/" + this.boardID + "/" + data.media.preview_orig), theight: data.media.preview_h, twidth: data.media.preview_w, isSpoiler: data.media.spoiler === '1' }; - if (this.boardID === 'f') { + if (!/\.pdf$/.test(o.file.url)) { + o.file.dimensions = o.file.width + "x" + o.file.height; + } + if (this.boardID === 'f' && data.media.exif) { o.file.tag = JSON.parse(data.media.exif).Tag; } } @@ -2301,7 +2320,7 @@ post = new Post(Build.post(o), thread, board); post.kill(); if (post.file) { - post.file.thumbURL = o.file.turl; + post.file.thumbURL = o.file.thumbURL; } post.isFetchedQuote = true; Main.callbackNodes(Post, [post]); @@ -2350,26 +2369,7 @@ Polyfill = { init: function() { - this.notificationPermission(); - this.toBlob(); - return this.visibility(); - }, - notificationPermission: function() { - if (!window.Notification || 'permission' in Notification || !window.webkitNotifications) { - return; - } - return Object.defineProperty(Notification, 'permission', { - get: function() { - switch (webkitNotifications.checkPermission()) { - case 0: - return 'granted'; - case 1: - return 'default'; - case 2: - return 'denied'; - } - } - }); + return this.toBlob(); }, toBlob: function() { var base1; @@ -2385,26 +2385,6 @@ type: 'image/png' })); }); - }, - visibility: function() { - if ('visibilityState' in d) { - return; - } - Object.defineProperties(HTMLDocument.prototype, { - visibilityState: { - get: function() { - return this.webkitVisibilityState; - } - }, - hidden: { - get: function() { - return this.webkitHidden; - } - } - }); - return $.on(d, 'webkitvisibilitychange', function() { - return $.event('visibilitychange'); - }); } }; @@ -3610,7 +3590,7 @@ return Index.load(e, state); } }, { - whenModified: true + whenModified: 'Index' }); return $.addClass(Index.button, 'fa-spin'); }, @@ -4008,13 +3988,14 @@ if (text == null) { return text; } - return text.replace(/<[^>]*>/g, '').replace(/&(amp|#039|quot|lt|gt);/g, function(c) { + return text.replace(/<[^>]*>/g, '').replace(/&(amp|#039|quot|lt|gt|#44);/g, function(c) { return { '&': '&', ''': "'", '"': '"', '<': '<', - '>': '>' + '>': '>', + ',': ',' }[c]; }); }, @@ -4046,117 +4027,128 @@ return "/" + boardID + "/thread/" + threadID + "#p" + postID; } }, - postFromObject: function(data, boardID, suppressThumb) { + parseJSON: function(data, boardID) { var o; o = { postID: data.no, threadID: data.resto || data.no, boardID: boardID, - name: Build.unescape(data.name), - capcode: data.capcode, - tripcode: data.trip, - uniqueID: data.id, - email: Build.unescape(data.email), - subject: Build.unescape(data.sub), - flagCode: data.country, - flagName: Build.unescape(data.country_name), - date: data.now, - dateUTC: data.time, - comment: { - innerHTML: data.com || '' - }, + isReply: !!data.resto, isSticky: !!data.sticky, isClosed: !!data.closed, - isArchived: !!data.archived + isArchived: !!data.archived, + fileDeleted: !!data.filedeleted }; - if (data.filedeleted) { - o.file = { - isDeleted: true - }; - } else if (data.ext) { + o.info = { + subject: Build.unescape(data.sub), + email: Build.unescape(data.email), + name: Build.unescape(data.name) || '', + tripcode: data.trip, + uniqueID: data.id, + flagCode: data.country, + flag: Build.unescape(data.country_name), + dateUTC: data.time, + dateText: data.now, + commentHTML: { + innerHTML: data.com || '' + } + }; + if (data.capcode) { + o.info.capcode = data.capcode.replace(/_highlight$/, '').replace(/_/g, ' ').replace(/\b\w/g, function(c) { + return c.toUpperCase(); + }); + o.capcodeHighlight = /_highlight$/.test(data.capcode); + delete o.info.uniqueID; + } + if (data.ext) { o.file = { name: (Build.unescape(data.filename)) + data.ext, - timestamp: "" + data.tim + data.ext, - url: boardID === 'f' ? "//i.4cdn.org/" + boardID + "/" + (encodeURIComponent(data.filename)) + data.ext : "//i.4cdn.org/" + boardID + "/" + data.tim + data.ext, + url: boardID === 'f' ? location.protocol + "//i.4cdn.org/" + boardID + "/" + (encodeURIComponent(data.filename)) + data.ext : location.protocol + "//i.4cdn.org/" + boardID + "/" + data.tim + data.ext, height: data.h, width: data.w, MD5: data.md5, - size: data.fsize, - turl: "//i.4cdn.org/" + boardID + "/" + data.tim + "s.jpg", + size: $.bytesToString(data.fsize), + thumbURL: location.protocol + "//i.4cdn.org/" + boardID + "/" + data.tim + "s.jpg", theight: data.tn_h, twidth: data.tn_w, isSpoiler: !!data.spoiler, - isDeleted: false, tag: data.tag }; + if (!/\.pdf$/.test(o.file.url)) { + o.file.dimensions = o.file.width + "x" + o.file.height; + } } + return o; + }, + parseComment: function(o) { + var html; + html = o.info.commentHTML.innerHTML.replace(//gi, '\n').replace(/\n\nRolled [^<]*<\/b>/i, '').replace(/]*>/g, ''); + return o.info.comment = Build.unescape(html); + }, + postFromObject: function(data, boardID, suppressThumb) { + var o; + o = Build.parseJSON(data, boardID); return Build.post(o, suppressThumb); }, post: function(o, suppressThumb) { - - /* - This function contains code from 4chan-JS (https://github.com/4chan/4chan-JS). - @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE - */ - var boardID, capcode, capcodeDescription, capcodeLC, capcodeLong, capcodePlural, capcodeText, capcodeUC, comment, container, date, dateUTC, email, file, fileBlock, fileDims, fileSize, fileThumb, flagCode, flagName, gifIcon, href, isOP, k, len1, match, name, postClass, postID, postInfo, postLink, quote, quoteLink, ref, shortFilename, staticPath, subject, threadID, tripcode, uniqueID, wholePost; - 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, comment = o.comment, file = o.file; - name || (name = ''); - subject || (subject = ''); - isOP = postID === threadID; + var boardID, capcode, capcodeDescription, capcodeLC, capcodeLong, capcodePlural, capcodeUC, commentHTML, container, dateText, dateUTC, email, file, fileBlock, fileThumb, fileURL, flag, flagCode, gifIcon, href, k, len1, match, name, postClass, postID, postInfo, postLink, protocol, quote, quoteLink, ref, ref1, shortFilename, staticPath, subject, threadID, tripcode, uniqueID, wholePost; + postID = o.postID, threadID = o.threadID, boardID = o.boardID, file = o.file; + ref = o.info, subject = ref.subject, email = ref.email, name = ref.name, tripcode = ref.tripcode, capcode = ref.capcode, uniqueID = ref.uniqueID, flagCode = ref.flagCode, flag = ref.flag, dateUTC = ref.dateUTC, dateText = ref.dateText, commentHTML = ref.commentHTML; staticPath = Build.staticPath, gifIcon = Build.gifIcon; /* Post Info */ if (capcode) { - capcodeLC = capcode.split('_')[0]; - capcodeUC = capcodeLC[0].toUpperCase() + capcodeLC.slice(1); - capcodeText = capcodeUC; - capcodeLong = { - 'Admin': 'Administrator', - 'Mod': 'Moderator' - }[capcodeUC] || capcodeUC; - capcodePlural = capcodeLong + "s"; - capcodeDescription = "a 4chan " + capcodeLong; - if (capcode === 'admin_emeritus') { - capcodeText = 'Admin Emeritus'; + capcodeUC = capcode.split(' ')[0]; + capcodeLC = capcodeUC.toLowerCase(); + if (capcode === 'Admin Emeritus') { capcodePlural = 'the Administrator Emeritus'; capcodeDescription = "4chan's founding Administrator"; + } else { + capcodeLong = { + 'Admin': 'Administrator', + 'Mod': 'Moderator' + }[capcode] || capcode; + capcodePlural = capcodeLong + "s"; + capcodeDescription = "a 4chan " + capcodeLong; } } postLink = Build.postURL(boardID, threadID, postID); quoteLink = Build.sameThread(boardID, threadID) ? "javascript:quote('" + (+postID) + "');" : "/" + boardID + "/thread/" + threadID + "#q" + postID; postInfo = { - innerHTML: "
" + (isOP || boardID === "f" ? "" + E(subject) + " " : "") + "" + (email ? "" : "") + "" + E(name) + "" + (tripcode ? " " + E(tripcode) + "" : "") + (capcode ? " ## " + E(capcodeText) + "" : "") + (email ? "" : "") + (boardID === "f" && isOP || capcode ? "" : " ") + (capcode ? " \""" : "") + (uniqueID && !capcode ? " (ID: " + E(uniqueID) + ")" : "") + (flagCode ? " " : "") + " " + E(date) + " No." + E(postID) + "" + (o.isSticky ? " \"Sticky\"" : "") + (o.isClosed && !o.isArchived ? " \"Closed\"" : "") + (o.isArchived ? " \"Archived\"" : "") + (isOP && g.VIEW === "index" ? "   [Reply]" : "") + "
" + innerHTML: "
" + (!o.isReply || boardID === "f" || subject ? "" + E(subject || "") + " " : "") + "" + (email ? "" : "") + "" + E(name) + "" + (tripcode ? " " + E(tripcode) + "" : "") + (capcode ? " ## " + E(capcode) + "" : "") + (email ? "" : "") + (boardID === "f" && !o.isReply || capcode ? "" : " ") + (capcode ? " \""" : "") + (uniqueID && !capcode ? " (ID: " + E(uniqueID) + ")" : "") + (flagCode ? " " : "") + " " + E(dateText) + " No." + E(postID) + "" + (o.isSticky ? " \"Sticky\"" : "") + (o.isClosed && !o.isArchived ? " \"Closed\"" : "") + (o.isArchived ? " \"Archived\"" : "") + (!o.isReply && g.VIEW === "index" ? "   [Reply]" : "") + "
" }; /* File Info */ - if (file && !file.isDeleted) { + if (file) { + protocol = /^https?:(?=\/\/i\.4cdn\.org\/)/; + fileURL = file.url.replace(protocol, ''); shortFilename = Build.shortFilename(file.name); - fileSize = $.bytesToString(file.size); - fileDims = file.url.slice(-4) === '.pdf' ? 'PDF' : file.width + "x" + file.height; - fileThumb = file.isSpoiler ? Build.spoilerThumb(boardID) : file.turl; + fileThumb = file.isSpoiler ? Build.spoilerThumb(boardID) : file.thumbURL.replace(protocol, ''); } fileBlock = { - innerHTML: (file ? "
" + (file.isDeleted ? "\"File" : (boardID === "f" ? "
File: " + E(file.name) + "-(" + E(fileSize) + ", " + E(fileDims) + ", " + E(file.tag) + ")
" : "
File: " + (file.isSpoiler ? "Spoiler Image" : E(shortFilename)) + " (" + E(fileSize) + ", " + E(fileDims) + ")
")) + "
" : "") + innerHTML: (file ? "
" + (boardID === "f" ? "
File: " + E(file.name) + "-(" + E(file.size) + ", " + E(file.dimensions) + (file.tag ? ", " + E(file.tag) : "") + ")
" : "
File: " + (file.isSpoiler ? "Spoiler Image" : E(shortFilename)) + " (" + E(file.size) + ", " + E(file.dimensions || "PDF") + ")
") + "
" : (o.fileDeleted ? "
\"File
" : "")) }; /* Whole Post */ - postClass = isOP ? 'op' : 'reply'; + postClass = o.isReply ? 'reply' : 'op'; wholePost = { - innerHTML: (!isOP ? "
>>
" : "") + "
" + (isOP ? fileBlock.innerHTML + postInfo.innerHTML : postInfo.innerHTML + fileBlock.innerHTML) + "
" + comment.innerHTML + "
" + innerHTML: (o.isReply ? "
>>
" : "") + "
" + (o.isReply ? postInfo.innerHTML + fileBlock.innerHTML : fileBlock.innerHTML + postInfo.innerHTML) + "
" + commentHTML.innerHTML + "
" }; container = $.el('div', { className: "postContainer " + postClass + "Container", id: "pc" + postID }); $.extend(container, wholePost); - ref = $$('.quotelink', container); - for (k = 0, len1 = ref.length; k < len1; k++) { - quote = ref[k]; + ref1 = $$('.quotelink', container); + for (k = 0, len1 = ref1.length; k < len1; k++) { + quote = ref1[k]; href = quote.getAttribute('href'); if ((href[0] === '#') && !(Build.sameThread(boardID, threadID))) { quote.href = ("/" + boardID + "/thread/" + threadID) + href; } else if ((match = href.match(/^\/([^\/]+)\/thread\/(\d+)/)) && (Build.sameThread(match[1], match[2]))) { quote.href = href.match(/(#[^#]*)?$/)[0] || '#'; + } else if (/^\d+(#|$)/.test(href) && !(g.VIEW === 'thread' && g.BOARD.ID === boardID)) { + quote.href = "/" + boardID + "/thread/" + href; } } return container; @@ -4303,7 +4295,7 @@ threadExcerpt: function(thread) { var OP, excerpt, ref; OP = thread.OP; - excerpt = ("/" + thread.board + "/ - ") + (((ref = OP.info.subject) != null ? ref.trim() : void 0) || OP.info.comment.replace(/\n+/g, ' // ') || OP.info.nameBlock); + excerpt = ("/" + thread.board + "/ - ") + (((ref = OP.info.subject) != null ? ref.trim() : void 0) || OP.info.commentDisplay.replace(/\n+/g, ' // ') || OP.info.nameBlock); if (excerpt.length > 73) { return excerpt.slice(0, 70) + "..."; } @@ -4985,7 +4977,7 @@ Filter = { filters: {}, init: function() { - var boards, err, filter, hl, k, key, len1, line, op, ref, ref1, ref2, ref3, ref4, ref5, ref6, regexp, stub, top; + var boards, err, filter, hl, k, key, len1, line, op, ref, ref1, ref2, ref3, ref4, ref5, regexp, stub, top; if (!(((ref = g.VIEW) === 'index' || ref === 'thread') && Conf['Filter'])) { return; } @@ -5005,9 +4997,7 @@ } filter = line.replace(regexp[0], ''); boards = ((ref2 = filter.match(/boards:([^;]+)/)) != null ? ref2[1].toLowerCase() : void 0) || 'global'; - if (boards !== 'global' && (ref3 = g.BOARD.ID, indexOf.call(boards.split(','), ref3) < 0)) { - continue; - } + boards = boards === 'global' ? null : boards.split(','); if (key === 'uniqueID' || key === 'MD5') { regexp = regexp[1]; } else { @@ -5019,10 +5009,10 @@ continue; } } - op = ((ref4 = filter.match(/[^t]op:(yes|no|only)/)) != null ? ref4[1] : void 0) || 'yes'; + op = ((ref3 = filter.match(/[^t]op:(yes|no|only)/)) != null ? ref3[1] : void 0) || 'yes'; stub = (function() { - var ref5; - switch ((ref5 = filter.match(/stub:(yes|no)/)) != null ? ref5[1] : void 0) { + var ref4; + switch ((ref4 = filter.match(/stub:(yes|no)/)) != null ? ref4[1] : void 0) { case 'yes': return true; case 'no': @@ -5032,11 +5022,11 @@ } })(); if (hl = /highlight/.test(filter)) { - hl = ((ref5 = filter.match(/highlight:([\w-]+)/)) != null ? ref5[1] : void 0) || 'filter-highlight'; - top = ((ref6 = filter.match(/top:(yes|no)/)) != null ? ref6[1] : void 0) || 'yes'; + hl = ((ref4 = filter.match(/highlight:([\w-]+)/)) != null ? ref4[1] : void 0) || 'filter-highlight'; + top = ((ref5 = filter.match(/top:(yes|no)/)) != null ? ref5[1] : void 0) || 'yes'; top = top === 'yes'; } - this.filters[key].push(this.createFilter(regexp, op, stub, hl, top)); + this.filters[key].push(this.createFilter(regexp, boards, op, stub, hl, top)); } if (!this.filters[key].length) { delete this.filters[key]; @@ -5050,7 +5040,7 @@ cb: this.node }); }, - createFilter: function(regexp, op, stub, hl, top) { + createFilter: function(regexp, boards, op, stub, hl, top) { var settings, test; test = typeof regexp === 'string' ? function(value) { return regexp === value; @@ -5063,7 +5053,10 @@ "class": hl, top: top }; - return function(value, isReply) { + return function(value, boardID, isReply) { + if (boards && indexOf.call(boards, boardID) < 0) { + return false; + } if (isReply && op === 'only' || !isReply && op === 'no') { return false; } @@ -5083,7 +5076,7 @@ ref = Filter.filters[key]; for (k = 0, len1 = ref.length; k < len1; k++) { filter = ref[k]; - if (!(result = filter(value, this.isReply))) { + if (!(result = filter(value, this.board.ID, this.isReply))) { continue; } if (result.hide && !this.isFetchedQuote) { @@ -5107,6 +5100,23 @@ } } }, + isHidden: function(post) { + var filter, k, key, len1, ref, result, value; + for (key in Filter.filters) { + if ((value = Filter[key](post)) != null) { + ref = Filter.filters[key]; + for (k = 0, len1 = ref.length; k < len1; k++) { + filter = ref[k]; + if (result = filter(value, post.boardID, post.isReply)) { + if (result.hide) { + return true; + } + } + } + } + } + return false; + }, name: function(post) { return post.info.name; }, @@ -5120,10 +5130,11 @@ return post.info.capcode; }, subject: function(post) { - return post.info.subject || void 0; + return post.info.subject; }, comment: function(post) { - return post.info.comment; + var ref; + return (ref = post.info.comment) != null ? ref : Build.parseComment(post); }, flag: function(post) { return post.info.flag; @@ -6176,7 +6187,7 @@ }, mouseover: function(e) { var boardID, clone, k, len1, len2, origin, post, postID, posts, q, qp, quote, quoterID, ref, ref1, threadID; - if ($.hasClass(this, 'inlined')) { + if ($.hasClass(this, 'inlined') || !d.contains(this)) { return; } ref = Get.postDataFromLink(this), boardID = ref.boardID, threadID = ref.threadID, postID = ref.postID; @@ -6660,7 +6671,7 @@ QR = { mimeTypes: ['image/jpeg', 'image/png', 'image/gif', 'application/pdf', 'application/vnd.adobe.flash.movie', 'application/x-shockwave-flash', 'video/webm'], init: function() { - var noscript, sc; + var sc; if (!Conf['Quick Reply']) { return; } @@ -6669,9 +6680,7 @@ if (g.VIEW === 'archive') { return; } - $.globalEval('document.documentElement.dataset.jsEnabled = true;'); - noscript = Conf['Force Noscript Captcha'] || !doc.dataset.jsEnabled; - this.captcha = Captcha[noscript ? 'noscript' : 'v2']; + this.captcha = Captcha.v2; $.on(d, '4chanXInitFinished', this.initReady); Post.callbacks.push({ name: 'Quick Reply', @@ -6699,7 +6708,7 @@ } if (Conf['Hide Original Post Form']) { $.addClass(doc, 'hide-original-post-form'); - if (!doc.dataset.jsEnabled) { + if (!$.hasClass(doc, 'js-enabled')) { return $.onExists(doc, '#postForm noscript', true, $.rm); } } @@ -6819,26 +6828,31 @@ return QR.captcha.destroy(); }, focus: function() { - $.queueTask(function() { - var focus; - if (!$$('.goog-bubble-content > iframe').some(function(el) { - return el.getBoundingClientRect().top >= 0; - })) { - focus = d.activeElement && QR.nodes.el.contains(d.activeElement); - return $[focus ? 'addClass' : 'rmClass'](QR.nodes.el, 'focus'); + return $.queueTask(function() { + if (!QR.inBubble()) { + QR.hasFocus = d.activeElement && QR.nodes.el.contains(d.activeElement); + QR.nodes.el.classList.toggle('focus', QR.hasFocus); + } + if (QR.captcha.isEnabled && !QR.captcha.noscript) { + if (QR.inCaptcha()) { + QR.scrollY = window.scrollY; + return $.on(d, 'scroll', QR.scrollLock); + } else { + return $.off(d, 'scroll', QR.scrollLock); + } } }); - if (typeof chrome !== "undefined" && chrome !== null) { - if (d.activeElement && QR.nodes.el.contains(d.activeElement) && d.activeElement.nodeName === 'IFRAME') { - QR.scrollY = window.scrollY; - return $.on(d, 'scroll', QR.scrollLock); - } else { - return $.off(d, 'scroll', QR.scrollLock); - } - } + }, + inBubble: function() { + var ref; + return ref = d.activeElement, indexOf.call($$('.goog-bubble-content > iframe'), ref) >= 0; + }, + inCaptcha: function() { + var ref; + return (((ref = d.activeElement) != null ? ref.nodeName : void 0) === 'IFRAME' && QR.nodes.el.contains(d.activeElement)) || (QR.hasFocus && QR.inBubble()); }, scrollLock: function() { - if (d.activeElement && QR.nodes.el.contains(d.activeElement) && d.activeElement.nodeName === 'IFRAME') { + if (QR.inCaptcha()) { return window.scroll(window.scrollX, QR.scrollY); } else { return $.off(d, 'scroll', QR.scrollLock); @@ -7018,10 +7032,10 @@ characterCount: function() { var count, counter; counter = QR.nodes.charCount; - count = QR.nodes.com.textLength; + count = QR.nodes.com.value.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, '_').length; counter.textContent = count; counter.hidden = count < 1000; - return (count > 1500 ? $.addClass : $.rmClass)(counter, 'warning'); + return (count > 2000 ? $.addClass : $.rmClass)(counter, 'warning'); }, getFile: function() { var ref; @@ -7321,7 +7335,7 @@ event = node.nodeName === 'SELECT' ? 'change' : 'input'; $.on(nodes[name], event, save); } - if (Conf['Remember QR Size']) { + if ((typeof chrome === "undefined" || chrome === null) && Conf['Remember QR Size']) { $.get('QR Size', '', function(item) { return nodes.com.style.cssText = item['QR Size']; }); @@ -7604,15 +7618,17 @@ Captcha = {}; Captcha.fixes = { - selectors: { - image: '.rc-imageselect-target > .rc-imageselect-tile > img' - }, + imageKeys: '789456123uiojklm'.split('').concat(['Comma', 'Period']), + css: '.rc-imageselect-target > div:focus {\n outline: 2px solid #4a90e2;\n}\n.rc-button-default:focus {\n box-shadow: inset 0 0 0 2px #0063d6;\n}', + cssNoscript: '.fbc-payload-imageselect {\n position: relative;\n}\n.fbc-payload-imageselect > label {\n position: absolute;\n display: block;\n height: 93.3px;\n width: 93.3px;\n}\nlabel[data-row="0"] {top: 0px;}\nlabel[data-row="1"] {top: 93.3px;}\nlabel[data-row="2"] {top: 186.6px;}\nlabel[data-col="0"] {left: 0px;}\nlabel[data-col="1"] {left: 93.3px;}\nlabel[data-col="2"] {left: 186.6px;}', init: function() { switch (location.pathname.split('/')[3]) { case 'anchor': return this.initMain(); case 'frame': return this.initPopup(); + case 'fallback': + return this.initNoscript(); } }, initMain: function() { @@ -7630,7 +7646,7 @@ }); }, initPopup: function() { - $.addStyle(this.selectors.image + ":focus {outline: 2px solid #4a90e2;}"); + $.addStyle(this.css); this.fixImages(); new MutationObserver((function(_this) { return function() { @@ -7642,383 +7658,135 @@ }); return $.on(d, 'keydown', this.keybinds.bind(this)); }, - fixImages: function() { - var focus, img, k, len1, ref; - if (!(this.images = $$(this.selectors.image)).length) { + initNoscript: function() { + this.noscript = true; + this.images = $$('.fbc-payload-imageselect > input'); + if (!this.images.length) { return; } - focus = this.images[0].tabIndex !== 0; + $.addStyle(this.cssNoscript); + this.addLabels(); + $.on(d, 'keydown', this.keybinds.bind(this)); + return $.on($('.fbc-imageselect-challenge > form'), 'submit', this.checkForm.bind(this)); + }, + fixImages: function() { + var img, k, len1, ref; + this.images = $$('.rc-imageselect-target > div'); ref = this.images; for (k = 0, len1 = ref.length; k < len1; k++) { img = ref[k]; img.tabIndex = 0; } - if (focus) { - return this.focusImage(); + if (this.images.length) { + return this.addTooltips(this.images); } }, - focusImage: function() { - var img; - img = this.images[0]; - return $.asap(function() { - if (!doc.contains(img)) { - return true; + addLabels: function() { + var checkbox, i, imageSelect, label, labels; + imageSelect = $('.fbc-payload-imageselect'); + labels = (function() { + var k, len1, ref, results; + ref = this.images; + results = []; + for (i = k = 0, len1 = ref.length; k < len1; i = ++k) { + checkbox = ref[i]; + checkbox.id = "checkbox-" + i; + label = $.el('label', { + htmlFor: checkbox.id + }); + label.dataset.row = Math.floor(i / 3); + label.dataset.col = i % 3; + results.push(label); } - img.focus(); - return d.activeElement === img; - }, function() {}); + return results; + }).call(this); + $.add(imageSelect, labels); + return this.addTooltips(labels); + }, + addTooltips: function(nodes) { + var i, k, len1, node; + for (i = k = 0, len1 = nodes.length; k < len1; i = ++k) { + node = nodes[i]; + node.title = this.imageKeys[i] + " or " + (this.imageKeys[i + 9][0].toUpperCase()) + this.imageKeys[i + 9].slice(1); + } + }, + checkForm: function(e) { + var checkbox, k, len1, n, ref; + n = 0; + ref = this.images; + for (k = 0, len1 = ref.length; k < len1; k++) { + checkbox = ref[k]; + if (checkbox.checked) { + n++; + } + } + if (n === 0) { + return e.preventDefault(); + } }, keybinds: function(e) { - var dx, reload, verify, x; - if (!(this.images && doc.contains(this.images[0]) && d.activeElement)) { + var dx, i, key, reload, verify, x; + if (!(this.images && doc.contains(this.images[0]))) { return; } - reload = $.id('recaptcha-reload-button'); - verify = $.id('recaptcha-verify-button'); + reload = $('#recaptcha-reload-button, .fbc-button-reload'); + verify = $('#recaptcha-verify-button, .fbc-button-verify > input'); x = this.images.indexOf(d.activeElement); if (x < 0) { - if (!$('.rc-controls').contains(d.activeElement)) { - return; - } x = d.activeElement === verify ? 11 : 9; } - if (!(dx = { - 38: 9, - 40: 3, - 37: 11, - 39: 1 - }[e.keyCode])) { + key = Keybinds.keyCode(e); + if (!this.noscript && key === 'Space' && x < 9) { + this.images[x].click(); + } else if ((i = this.imageKeys.indexOf(key)) >= 0) { + this.images[i % 9].click(); + verify.focus(); + } else if (dx = { + 'Up': 9, + 'Down': 3, + 'Left': 11, + 'Right': 1 + }[key]) { + x = (x + dx) % 12; + if (x === 10) { + x = dx === 11 ? 9 : 11; + } + (this.images[x] || { + 9: reload, + 11: verify + }[x]).focus(); + } else { return; } - x = (x + dx) % 12; - if (x === 10) { - x = dx === 11 ? 9 : 11; - } - (this.images[x] || { - 9: reload, - 11: verify - }[x]).focus(); e.preventDefault(); return e.stopPropagation(); } }; - Captcha.noscript = { - lifetime: 2 * $.MINUTE, - iframeURL: '//www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc', + Captcha.language = { init: function() { - var container, input; - if (d.cookie.indexOf('pass_enabled=1') >= 0) { + if (!(Conf['captchaLanguage'].trim() && d.cookie.indexOf('pass_enabled=1') < 0 && !Conf['Hide Original Post Form'])) { return; } - if (!(this.isEnabled = !!$.id('g-recaptcha'))) { - return; - } - container = $.el('div', { - className: 'captcha-img', - title: 'Reload reCAPTCHA' + return $.onExists(doc, '#captchaFormPart', true, function(node) { + return $.onExists(node, 'iframe', true, Captcha.language.fixIframe); }); - input = $.el('input', { - className: 'captcha-input field', - title: 'Verification', - autocomplete: 'off', - spellcheck: false - }); - this.nodes = { - container: container, - input: input - }; - $.on(input, 'keydown', this.keydown.bind(this)); - $.on(this.nodes.container, 'click', (function(_this) { - return function() { - _this.reload(); - return _this.nodes.input.focus(); - }; - })(this)); - this.conn = new Connection(null, location.protocol + "//www.google.com", { - challenge: this.load.bind(this), - token: this.save.bind(this), - error: this.error.bind(this) - }); - $.addClass(QR.nodes.el, 'has-captcha'); - $.after(QR.nodes.com.parentNode, [container, input]); - this.captchas = []; - $.get('captchas', [], function(arg) { - var captchas; - captchas = arg.captchas; - QR.captcha.sync(captchas); - return QR.captcha.clear(); - }); - $.sync('captchas', this.sync); - this.beforeSetup(); - return this.setup(); }, - initFrame: function() { - var cb, conn, img, ref, ref1; - conn = new Connection(window.parent, location.protocol + "//boards.4chan.org", { - response: function(response) { - $.id('response').value = response; - return $('.fbc-challenge > form').submit(); - } - }); - conn.send({ - token: (ref = $('.fbc-verification-token > textarea')) != null ? ref.value : void 0, - error: (ref1 = $('.fbc-error')) != null ? ref1.textContent : void 0 - }); - if (!(img = $('.fbc-payload > img'))) { + fixPage: function() { + if (!(Conf['captchaLanguage'].trim() && d.cookie.indexOf('pass_enabled=1') < 0)) { return; } - cb = function() { - var canvas; - canvas = $.el('canvas'); - canvas.width = img.width; - canvas.height = img.height; - canvas.getContext('2d').drawImage(img, 0, 0); - return conn.send({ - challenge: canvas.toDataURL() - }); - }; - if (img.complete) { - return cb(); - } else { - return $.on(img, 'load', cb); - } + return $.onExists(doc, 'iframe', true, Captcha.language.fixIframe); }, - timers: {}, - cb: { - focus: function() { - return QR.captcha.setup(false, true); - } - }, - beforeSetup: function() { - var container, input, ref; - ref = this.nodes, container = ref.container, input = ref.input; - container.hidden = true; - input.value = ''; - input.placeholder = 'Focus to load reCAPTCHA'; - this.count(); - return $.on(input, 'focus click', this.cb.focus); - }, - needed: function() { - var captchaCount, postsCount; - captchaCount = this.captchas.length; - if (QR.req) { - captchaCount++; - } - postsCount = QR.posts.length; - if (postsCount === 1 && !Conf['Auto-load captcha'] && !QR.posts[0].com && !QR.posts[0].file) { - postsCount = 0; - } - return captchaCount < postsCount; - }, - onNewPost: function() {}, - onPostChange: function() {}, - setup: function(focus, force) { - if (!(this.isEnabled && (this.needed() || force))) { + fixIframe: function(el) { + var lang, src; + if (!(lang = Conf['captchaLanguage'].trim())) { return; } - if (!this.nodes.iframe) { - this.nodes.iframe = $.el('iframe', { - id: 'qr-captcha-iframe', - src: this.iframeURL - }); - $.add(d.body, this.nodes.iframe); - this.conn.target = this.nodes.iframe.contentWindow; - } else if (!this.occupied || force) { - this.nodes.iframe.src = this.iframeURL; + src = /[?&]hl=/.test(el.src) ? el.src.replace(/([?&]hl=)[^&]*/, '$1' + encodeURIComponent(lang)) : el.src + ("&hl=" + (encodeURIComponent(lang))); + if (el.src !== src) { + return el.src = src; } - this.occupied = true; - if (focus) { - return this.nodes.input.focus(); - } - }, - afterSetup: function() { - var container, input, ref; - ref = this.nodes, container = ref.container, input = ref.input; - container.hidden = false; - input.placeholder = 'Verification'; - this.count(); - $.off(input, 'focus click', this.cb.focus); - if (QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight) { - QR.nodes.el.style.top = ''; - return QR.nodes.el.style.bottom = '0px'; - } - }, - destroy: function() { - if (!this.isEnabled) { - return; - } - if (this.nodes.img) { - $.rm(this.nodes.img); - } - delete this.nodes.img; - if (this.nodes.iframe) { - $.rm(this.nodes.iframe); - } - delete this.nodes.iframe; - delete this.occupied; - return this.beforeSetup(); - }, - sync: function(captchas) { - if (captchas == null) { - captchas = []; - } - QR.captcha.captchas = captchas; - return QR.captcha.count(); - }, - getOne: function() { - var captcha; - this.clear(); - if (captcha = this.captchas.shift()) { - this.count(); - $.set('captchas', this.captchas); - return captcha.response; - } else if (/\S/.test(this.nodes.input.value)) { - return (function(_this) { - return function(cb) { - _this.submitCB = cb; - return _this.sendResponse(); - }; - })(this); - } else { - return null; - } - }, - sendResponse: function() { - var response; - response = this.nodes.input.value; - if (/\S/.test(response)) { - return this.conn.send({ - response: response - }); - } - }, - save: function(token) { - delete this.occupied; - this.nodes.input.value = ''; - if (this.submitCB) { - this.submitCB(token); - delete this.submitCB; - if (this.needed()) { - return this.reload(); - } else { - return this.destroy(); - } - } else { - $.forceSync('captchas'); - this.captchas.push({ - response: token, - timeout: this.timeout - }); - this.count(); - $.set('captchas', this.captchas); - return this.reload(); - } - }, - error: function(message) { - this.occupied = true; - this.nodes.input.value = ''; - if (this.submitCB) { - this.submitCB(); - delete this.submitCB; - } - return QR.error("Captcha Error: " + message); - }, - clear: function() { - var captcha, i, k, len1, now, ref; - if (!this.captchas.length) { - return; - } - $.forceSync('captchas'); - now = Date.now(); - ref = this.captchas; - for (i = k = 0, len1 = ref.length; k < len1; i = ++k) { - 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(src) { - var container, img, input, ref; - ref = this.nodes, container = ref.container, input = ref.input, img = ref.img; - this.occupied = true; - this.timeout = Date.now() + this.lifetime; - if (!img) { - img = this.nodes.img = new Image(); - $.one(img, 'load', this.afterSetup.bind(this)); - $.on(img, 'load', function() { - return this.hidden = false; - }); - $.add(container, img); - } - img.src = src; - input.value = ''; - this.clear(); - clearTimeout(this.timers.expire); - return this.timers.expire = setTimeout(this.expire.bind(this), this.lifetime); - }, - count: function() { - var count, placeholder; - count = this.captchas ? this.captchas.length : 0; - placeholder = this.nodes.input.placeholder.replace(/\ \(.*\)$/, ''); - placeholder += (function() { - switch (count) { - case 0: - if (placeholder === 'Verification') { - return ' (Shift + Enter to cache)'; - } else { - return ''; - } - break; - case 1: - return ' (1 cached captcha)'; - default: - return " (" + count + " cached captchas)"; - } - })(); - this.nodes.input.placeholder = placeholder; - this.nodes.input.alt = count; - clearTimeout(this.timers.clear); - if (this.captchas.length) { - return this.timers.clear = setTimeout(this.clear.bind(this), this.captchas[0].timeout - Date.now()); - } - }, - expire: function() { - if (!this.nodes.iframe) { - return; - } - if (!d.hidden && (this.needed() || d.activeElement === this.nodes.input)) { - return this.reload(); - } else { - return this.destroy(); - } - }, - reload: function() { - var ref; - this.nodes.iframe.src = this.iframeURL; - this.occupied = true; - return (ref = this.nodes.img) != null ? ref.hidden = true : void 0; - }, - keydown: function(e) { - if (e.keyCode === 8 && !this.nodes.input.value) { - if (this.nodes.iframe) { - this.reload(); - } else { - this.setup(); - } - } else if (e.keyCode === 13 && e.shiftKey) { - this.sendResponse(); - } else { - return; - } - return e.preventDefault(); } }; @@ -8032,6 +7800,16 @@ if (!(this.isEnabled = !!$.id('g-recaptcha'))) { return; } + if (this.noscript = Conf['Force Noscript Captcha'] || !$.hasClass(doc, 'js-enabled')) { + this.conn = new Connection(null, location.protocol + "//www.google.com", { + token: (function(_this) { + return function(token) { + return _this.save(true, token); + }; + })(this) + }); + $.addClass(QR.nodes.el, 'noscript-captcha'); + } this.captchas = []; $.get('captchas', [], function(arg) { var captchas; @@ -8062,9 +7840,26 @@ }; })(this)); }, + initFrame: function() { + var conn, ref, token; + if (token = (ref = $('.fbc-verification-token > textarea')) != null ? ref.value : void 0) { + conn = new Connection(window.parent, location.protocol + "//boards.4chan.org"); + return conn.send({ + token: token + }); + } + }, shouldFocus: false, timeouts: {}, postsCount: 0, + noscriptURL: function() { + var lang, url; + url = '//www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc'; + if (lang = Conf['captchaLanguage'].trim()) { + url += "&hl=" + (encodeURIComponent(lang)); + } + return url; + }, needed: function() { var captchaCount; captchaCount = this.captchas.length; @@ -8100,7 +7895,7 @@ if (!(this.isEnabled && (this.needed() || force))) { return; } - if (focus) { + if (focus && !QR.inBubble()) { this.shouldFocus = true; } if (this.timeouts.destroy) { @@ -8111,6 +7906,7 @@ if (this.nodes.container) { if (this.shouldFocus && (iframe = $('iframe', this.nodes.container))) { iframe.focus(); + QR.focus(); delete this.shouldFocus; } return; @@ -8123,6 +7919,22 @@ childList: true, subtree: true }); + if (this.noscript) { + return this.setupNoscript(); + } else { + return this.setupJS(); + } + }, + setupNoscript: function() { + var iframe; + iframe = $.el('iframe', { + id: 'qr-captcha-iframe', + src: this.noscriptURL() + }); + $.add(this.nodes.container, iframe); + return this.conn.target = iframe.contentWindow; + }, + setupJS: function() { return $.globalEval('(function() {\n function render() {\n var container = document.querySelector("#qr .captcha-container");\n container.dataset.widgetID = window.grecaptcha.render(container, {\n sitekey: \'6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc\',\n theme: document.documentElement.classList.contains(\'tomorrow\') ? \'dark\' : \'light\',\n callback: function(response) {\n window.dispatchEvent(new CustomEvent("captcha:success", {detail: response}));\n }\n });\n }\n if (window.grecaptcha) {\n render();\n } else {\n var cbNative = window.onRecaptchaLoaded;\n window.onRecaptchaLoaded = function() {\n render();\n cbNative();\n }\n }\n})();'); }, afterSetup: function(mutations) { @@ -8142,7 +7954,7 @@ } }, setupIFrame: function(iframe) { - this.setupTime = Date.now(); + Captcha.language.fixIframe(iframe); $.addClass(QR.nodes.el, 'captcha-open'); if (QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight) { QR.nodes.el.style.top = null; @@ -8199,24 +8011,26 @@ return null; } }, - save: function(pasted) { - var base1, focus, ref, ref1; + save: function(pasted, token) { + var base1, focus, ref; $.forceSync('captchas'); this.captchas.push({ - response: $('textarea', this.nodes.container).value, - timeout: (pasted ? this.setupTime : Date.now()) + this.lifetime + response: token || $('textarea', this.nodes.container).value, + timeout: Date.now() + this.lifetime }); $.set('captchas', this.captchas); this.count(); + focus = ((ref = d.activeElement) != null ? ref.nodeName : void 0) === 'IFRAME' && /https?:\/\/www\.google\.com\/recaptcha\//.test(d.activeElement.src); if (this.needed()) { - if (QR.cooldown.auto || Conf['Post on Captcha Completion']) { - this.shouldFocus = true; - } else { - QR.nodes.status.focus(); + if (focus) { + if (QR.cooldown.auto || Conf['Post on Captcha Completion']) { + this.shouldFocus = true; + } else { + QR.nodes.status.focus(); + } } this.reload(); } else { - focus = ((ref = d.activeElement) != null ? ref.nodeName : void 0) === 'IFRAME' && ((ref1 = d.activeElement.src) != null ? ref1.slice(0, 38) : void 0) === 'https://www.google.com/recaptcha/api2/'; if (pasted) { this.destroy(); } else { @@ -8252,7 +8066,7 @@ this.captchas = this.captchas.slice(i); this.count(); $.set('captchas', this.captchas); - return this.setup(true); + return this.setup(d.activeElement === QR.nodes.status); }, count: function() { this.nodes.counter.textContent = "Captchas: " + this.captchas.length; @@ -8262,7 +8076,11 @@ } }, reload: function() { - return $.globalEval('(function() {\n var container = document.querySelector("#qr .captcha-container");\n window.grecaptcha.reset(container.dataset.widgetID);\n})();'); + if (this.noscript) { + return $('iframe', this.nodes.container).src = this.noscriptURL(); + } else { + return $.globalEval('(function() {\n var container = document.querySelector("#qr .captcha-container");\n window.grecaptcha.reset(container.dataset.widgetID);\n})();'); + } } }; @@ -9286,7 +9104,7 @@ Gallery.fullIDs[post.fullID] = true; thumb = $.el('a', { className: 'gal-thumb', - href: post.file.URL, + href: post.file.url, target: '_blank', title: post.file.name }); @@ -9299,29 +9117,43 @@ Gallery.images.push(thumb); return $.add(Gallery.nodes.thumbs, thumb); }, + load: function(thumb, errorCB) { + var elType, ext, file; + ext = thumb.href.match(/\w*$/); + elType = { + 'webm': 'video', + 'pdf': 'iframe' + }[ext] || 'img'; + file = $.el(elType, { + title: thumb.title + }); + $.extend(file.dataset, thumb.dataset); + $.on(file, 'error', errorCB); + file.src = thumb.href; + return file; + }, open: function(thumb) { - var el, elType, file, name, newID, nodes, oldID, post, ref, slideshow; + var el, file, newID, nodes, oldID, post, ref; nodes = Gallery.nodes; - name = nodes.name; oldID = +nodes.current.dataset.id; newID = +thumb.dataset.id; - slideshow = Gallery.slideshow && (newID > oldID || (oldID === Gallery.images.length - 1 && newID === 0)); - if (el = $('.gal-highlight', nodes.thumbs)) { + if (el = Gallery.images[oldID]) { $.rmClass(el, 'gal-highlight'); } $.addClass(thumb, 'gal-highlight'); - elType = /\.webm$/.test(thumb.href) ? 'video' : /\.pdf$/.test(thumb.href) ? 'iframe' : 'img'; - $[elType === 'iframe' ? 'addClass' : 'rmClass'](doc, 'gal-pdf'); - file = $.el(elType, { - title: name.download = name.textContent = thumb.title - }); - $.extend(file.dataset, thumb.dataset); - $.on(file, 'error', Gallery.error); - file.src = name.href = thumb.href; + nodes.thumbs.scrollTop = thumb.offsetTop + thumb.offsetHeight / 2 - nodes.thumbs.clientHeight / 2; + if (((ref = Gallery.cache) != null ? ref.dataset.id : void 0) === '' + newID) { + file = Gallery.cache; + $.off(file, 'error', Gallery.cacheError); + $.on(file, 'error', Gallery.error); + } else { + file = Gallery.load(thumb, Gallery.error); + } $.off(nodes.current, 'error', Gallery.error); ImageCommon.pause(nodes.current); $.replace(nodes.current, file); - if (elType === 'video') { + nodes.current = file; + if (file.nodeName === 'VIDEO') { file.loop = true; Volume.setup(file); if (Conf['Autoplay']) { @@ -9331,19 +9163,22 @@ ImageCommon.addControls(file); } } + doc.classList.toggle('gal-pdf', file.nodeName === 'IFRAME'); + Gallery.cb.setHeight(); nodes.count.textContent = +thumb.dataset.id + 1; - nodes.current = file; + nodes.name.download = nodes.name.textContent = thumb.title; + nodes.name.href = thumb.href; nodes.frame.scrollTop = 0; nodes.next.focus(); - if (slideshow) { + if (Gallery.slideshow && (newID > oldID || (oldID === Gallery.images.length - 1 && newID === 0))) { Gallery.setupTimer(); } else { Gallery.cb.stop(); } - if (Conf['Scroll to Post'] && (post = (ref = (post = g.posts[file.dataset.post])) != null ? ref.nodes.root : void 0)) { - Header.scrollTo(post); + if (Conf['Scroll to Post'] && (post = g.posts[file.dataset.post])) { + Header.scrollTo(post.nodes.root); } - return nodes.thumbs.scrollTop = thumb.offsetTop + thumb.offsetHeight / 2 - nodes.thumbs.clientHeight / 2; + return Gallery.cache = Gallery.load(Gallery.images[(newID + 1) % Gallery.images.length], Gallery.cacheError); }, error: function() { var ref; @@ -9365,6 +9200,9 @@ }; })(this)); }, + cacheError: function() { + return delete Gallery.cache; + }, cleanupTimer: function() { var current; clearTimeout(Gallery.timeoutID); @@ -9526,6 +9364,11 @@ setFitness: function() { return (this.checked ? $.addClass : $.rmClass)(doc, "gal-" + (this.name.toLowerCase().replace(/\s+/g, '-'))); }, + setHeight: function() { + var current, dim, frame, height, ref, ref1, ref2, width; + ref = Gallery.nodes, current = ref.current, frame = ref.frame; + return current.style.minHeight = Conf['Stretch to Fit'] && (dim = (ref1 = g.posts[current.dataset.post]) != null ? ref1.file.dimensions : void 0) ? ((ref2 = dim.split('x'), width = ref2[0], height = ref2[1], ref2), Math.min(doc.clientHeight - 25, height / width * frame.clientWidth) + 'px') : null; + }, setDelay: function() { return Gallery.delay = +this.value; } @@ -9550,11 +9393,14 @@ var input, label; label = UI.checkbox(name, name); input = label.firstElementChild; - if (name === 'Fit Width' || name === 'Fit Height' || name === 'Hide Thumbnails') { + if (name === 'Hide Thumbnails' || name === 'Fit Width' || name === 'Fit Height') { $.on(input, 'change', Gallery.cb.setFitness); } $.event('change', null, input); $.on(input, 'change', $.cb.checked); + if (name === 'Hide Thumbnails' || name === 'Fit Width' || name === 'Fit Height' || name === 'Stretch to Fit') { + $.on(input, 'change', Gallery.cb.setHeight); + } return { el: label }; @@ -9563,7 +9409,7 @@ var delayInput, delayLabel, item, subEntries; subEntries = (function() { var k, len1, ref, results; - ref = ['Hide Thumbnails', 'Fit Width', 'Fit Height', 'Scroll to Post']; + ref = ['Hide Thumbnails', 'Fit Width', 'Fit Height', 'Stretch to Fit', 'Scroll to Post']; results = []; for (k = 0, len1 = ref.length; k < len1; k++) { item = ref[k]; @@ -9638,7 +9484,7 @@ }, error: function(file, post, delay, cb) { var URL, redirect, src, timeoutID; - src = post.file.URL.split('/'); + src = post.file.url.split('/'); URL = Redirect.to('file', { boardID: post.board.ID, filename: src[src.length - 1] @@ -9688,7 +9534,7 @@ post.kill(true); return redirect(); } else { - return URL = post.file.URL; + return URL = post.file.url; } } }); @@ -9883,7 +9729,7 @@ if (file.videoControls) { $.rm(file.videoControls); } - file.thumb.parentNode.href = file.URL; + file.thumb.parentNode.href = file.url; file.thumb.parentNode.target = '_blank'; ref = ['isExpanding', 'isExpanded', 'videoControls', 'wasPlaying', 'scrollIntoView']; for (k = 0, len1 = ref.length; k < len1; k++) { @@ -9950,7 +9796,7 @@ el = file.fullImage = $.el((isVideo ? 'video' : 'img')); el.dataset.fullID = post.fullID; $.on(el, 'error', ImageExpand.error); - el.src = src || file.URL; + el.src = src || file.url; } el.className = 'full-image'; $.after(thumb, el); @@ -10179,7 +10025,7 @@ el = $.el((isVideo ? 'video' : 'img')); el.dataset.fullID = post.fullID; $.on(el, 'error', error); - el.src = file.URL; + el.src = file.url; } if (Conf['Restart when Opened']) { ImageCommon.rewind(el); @@ -10310,22 +10156,22 @@ attr = ref[k]; video.style[attr] = thumb.style[attr]; } - video.src = file.URL; + video.src = file.url; $.replace(thumb, video); file.thumb = video; return file.videoThumb = true; }, prefetch: function(post) { - var URL, clone, el, file, isImage, isVideo, k, len1, match, ref, replace, thumb, type; + var clone, el, file, isImage, isVideo, k, len1, match, ref, replace, thumb, type, url; file = post.file; if (!file) { return; } - isImage = file.isImage, isVideo = file.isVideo, thumb = file.thumb, URL = file.URL; + isImage = file.isImage, isVideo = file.isVideo, thumb = file.thumb, url = file.url; if (file.isPrefetched || !(isImage || isVideo) || post.isHidden || post.thread.isHidden) { return; } - type = (match = URL.match(/\.([^.]+)$/)[1].toUpperCase()) === 'JPEG' ? 'JPG' : match; + type = (match = url.match(/\.([^.]+)$/)[1].toUpperCase()) === 'JPEG' ? 'JPG' : match; replace = Conf["Replace " + type] && !/spoiler/.test(thumb.src || thumb.dataset.src); if (!(replace || Conf['prefetch'])) { return; @@ -10357,13 +10203,13 @@ ref1 = post.clones; for (q = 0, len2 = ref1.length; q < len2; q++) { clone = ref1[q]; - clone.file.thumb.src = URL; + clone.file.thumb.src = url; } - thumb.src = URL; + thumb.src = url; return thumb.removeAttribute('data-src'); }); } - return el.src = URL; + return el.src = url; }, toggle: function() { if (Conf['prefetch'] = this.checked) { @@ -10405,7 +10251,7 @@ }, node: function() { var el; - if (!(this.file && /webm$/i.test(this.file.URL))) { + if (!(this.file && /webm$/i.test(this.file.url))) { return; } if (this.isClone) { @@ -10426,7 +10272,7 @@ load: function() { $.rmClass(this.parentNode, 'error'); $.addClass(this.parentNode, 'loading'); - return CrossOrigin.binary(Get.postFromNode(this).file.URL, (function(_this) { + return CrossOrigin.binary(Get.postFromNode(this).file.url, (function(_this) { return function(data) { var output, title; $.rmClass(_this.parentNode, 'loading'); @@ -10543,32 +10389,44 @@ cb: this.node }); }, + sandbox: function(url) { + return E.url({ + innerHTML: "[sb] " + E(url) + "" + }); + }, + rmOrigin: function(e) { + if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) { + return; + } + $.open(this.href); + return e.preventDefault(); + }, createSauceLink: function(link, post) { - var a, ext, i, k, key, len1, m, part, parts, ref, ref1, ref2, skip; + var a, ext, i, k, key, len1, m, part, parts, ref, ref1, ref2, skip, url; if (!(link = link.trim())) { return null; } parts = {}; - ref = link.split(/;(?=(?:text|boards|types):)/); + ref = link.split(/;(?=(?:text|boards|types|sandbox):?)/); for (i = k = 0, len1 = ref.length; k < len1; i = ++k) { part = ref[i]; if (i === 0) { parts['url'] = part; } else { - m = part.match(/^(\w*):(.*)$/); + m = part.match(/^(\w*):?(.*)$/); parts[m[1]] = m[2]; } } parts['text'] || (parts['text'] = ((ref1 = parts['url'].match(/(\w+)\.\w+\//)) != null ? ref1[1] : void 0) || '?'); - ext = post.file.URL.match(/[^.]*$/)[0]; + ext = post.file.url.match(/[^.]*$/)[0]; skip = false; for (key in parts) { parts[key] = parts[key].replace(/%(T?URL|IMG|MD5|board|name|%|semi)/g, function(parameter) { var type; type = { '%TURL': post.file.thumbURL, - '%URL': post.file.URL, - '%IMG': ext === 'gif' || ext === 'jpg' || ext === 'png' ? post.file.URL : post.file.thumbURL, + '%URL': post.file.url, + '%IMG': ext === 'gif' || ext === 'jpg' || ext === 'png' ? post.file.url : post.file.thumbURL, '%MD5': post.file.MD5, '%board': post.board.ID, '%name': post.file.name, @@ -10597,12 +10455,19 @@ if (!(!parts['types'] || indexOf.call(parts['types'].split(','), ext) >= 0)) { return null; } + url = parts['url']; + if (parts['sandbox'] != null) { + url = Sauce.sandbox(url); + } a = Sauce.link.cloneNode(true); - a.href = parts['url']; + a.href = url; a.textContent = parts['text']; if (/^javascript:/i.test(parts['url'])) { a.removeAttribute('target'); } + if (parts['sandbox'] != null) { + $.on(a, 'click', Sauce.rmOrigin); + } return a; }, node: function() { @@ -10719,6 +10584,9 @@ }, wheel: function(e) { var el, volume; + if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey) { + return; + } if (!(el = $('video:not([data-md5])', this))) { return; } @@ -11035,7 +10903,7 @@ content = { innerHTML: "" + E(a.dataset.uid) + "" }; - el.src = "data:text/html;charset=utf-8," + (encodeURIComponent(content.innerHTML)); + el.src = E.url(content); return el; }, title: { @@ -11713,7 +11581,7 @@ if (!file) { return false; } - a.href = file.URL; + a.href = file.url; a.download = file.name; return true; } @@ -11771,27 +11639,36 @@ } a = $.el('a', { className: 'report-link', - href: 'javascript:;', - textContent: 'Report this post' + href: 'javascript:;' }); $.on(a, 'click', ReportLink.report); return Menu.menu.addEntry({ el: a, order: 10, open: function(post) { - ReportLink.url = !post.isDead ? "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post : Conf['Archive Report'] ? Redirect.to('report', { - boardID: post.board.ID, - postID: post.ID - }) : void 0; + if (!(post.isDead || (post.thread.isDead && !post.thread.isArchived))) { + a.textContent = 'Report this post'; + ReportLink.url = "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post; + ReportLink.height = 200; + } else if (Conf['Archive Report']) { + a.textContent = 'Report to archive'; + ReportLink.url = Redirect.to('report', { + boardID: post.board.ID, + postID: post.ID + }); + ReportLink.height = 350; + } else { + ReportLink.url = ''; + } return !!ReportLink.url; } }); }, report: function() { - var id, set, url; - url = ReportLink.url; + var height, id, set, url; + url = ReportLink.url, height = ReportLink.height; id = Date.now(); - set = "toolbar=0,scrollbars=1,location=0,status=1,menubar=0,resizable=1,width=685,height=200"; + set = "toolbar=0,scrollbars=1,location=0,status=1,menubar=0,resizable=1,width=700,height=" + height; return window.open(url, id, set); } }; @@ -11919,13 +11796,13 @@ return; } statsHTML = { - innerHTML: "? / ?" + (Conf["IP Count in Stats"] ? " / ?" : "") + (Conf["Page Count in Stats"] ? " / ?" : "") + innerHTML: "? / ?" + (Conf["IP Count in Stats"] ? " / ?" : "") + (Conf["Page Count in Stats"] && g.BOARD.ID !== "f" ? " / ?" : "") }; statsTitle = 'Posts / Files'; if (Conf['IP Count in Stats']) { statsTitle += ' / IPs'; } - if (Conf['Page Count in Stats']) { + if (Conf['Page Count in Stats'] && g.BOARD.ID !== 'f') { statsTitle += ' / Page'; } if (Conf['Updater and Stats in Header']) { @@ -11950,6 +11827,9 @@ this.fileCountEl = $('#file-count', sc); this.ipCountEl = $('#ip-count', sc); this.pageCountEl = $('#page-count', sc); + if (this.pageCountEl) { + $.on(this.pageCountEl, 'click', ThreadStats.fetchPage); + } return Thread.callbacks.push({ name: 'Thread Stats', cb: this.node @@ -11964,7 +11844,7 @@ if (post.file) { fileCount++; } - if (Conf["Page Count in Stats"]) { + if (ThreadStats.pageCountEl) { return ThreadStats.lastPost = post.info.date; } }); @@ -11980,13 +11860,13 @@ } ref = e.detail, postCount = ref.postCount, fileCount = ref.fileCount, ipCount = ref.ipCount, newPosts = ref.newPosts; ThreadStats.update(postCount, fileCount, ipCount); - if (!Conf["Page Count in Stats"]) { + if (!ThreadStats.pageCountEl) { return; } if (newPosts.length) { ThreadStats.lastPost = g.posts[newPosts[newPosts.length - 1]].info.date; } - if (ThreadStats.lastPost > ThreadStats.lastPageUpdate && ((ref1 = ThreadStats.pageCountEl) != null ? ref1.textContent : void 0) !== '1') { + if (((ref1 = ThreadStats.pageCountEl) != null ? ref1.textContent : void 0) !== '1') { return ThreadStats.fetchPage(); } }, @@ -11995,14 +11875,14 @@ thread = ThreadStats.thread, postCountEl = ThreadStats.postCountEl, fileCountEl = ThreadStats.fileCountEl, ipCountEl = ThreadStats.ipCountEl; postCountEl.textContent = postCount; fileCountEl.textContent = fileCount; - if ((ipCount != null) && Conf["IP Count in Stats"]) { + if ((ipCount != null) && ipCountEl) { ipCountEl.textContent = ipCount; } (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"]) { + if (!ThreadStats.pageCountEl) { return; } clearTimeout(ThreadStats.timeout); @@ -12015,28 +11895,37 @@ return $.ajax("//a.4cdn.org/" + ThreadStats.thread.board + "/threads.json", { onload: ThreadStats.onThreadsLoad }, { - whenModified: true + whenModified: 'ThreadStats' }); }, onThreadsLoad: function() { var k, len1, len2, page, q, ref, ref1, thread; - if (!(Conf["Page Count in Stats"] && this.status === 200)) { - return; - } - ref = this.response; - for (k = 0, len1 = ref.length; k < len1; k++) { - page = ref[k]; - ref1 = page.threads; - for (q = 0, len2 = ref1.length; q < len2; q++) { - thread = ref1[q]; - if (!(thread.no === ThreadStats.thread.ID)) { - continue; + if (this.status === 200) { + ref = this.response; + for (k = 0, len1 = ref.length; k < len1; k++) { + page = ref[k]; + ref1 = page.threads; + for (q = 0, len2 = ref1.length; q < len2; q++) { + thread = ref1[q]; + if (!(thread.no === ThreadStats.thread.ID)) { + continue; + } + ThreadStats.pageCountEl.textContent = page.page; + (page.page === this.response.length ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning'); + ThreadStats.lastPageUpdate = new Date(thread.last_modified * $.SECOND); + ThreadStats.retry(); + return; } - ThreadStats.pageCountEl.textContent = page.page; - (page.page === this.response.length ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning'); - ThreadStats.lastPageUpdate = new Date(thread.last_modified * $.SECOND); - return; } + } else if (this.status === 304) { + return ThreadStats.retry(); + } + }, + retry: function() { + var ref; + if (ThreadStats.lastPost > ThreadStats.lastPageUpdate && ((ref = ThreadStats.pageCountEl) != null ? ref.textContent : void 0) !== '1') { + clearTimeout(ThreadStats.timeout); + return ThreadStats.timeout = setTimeout(ThreadStats.fetchPage, 5 * $.SECOND); } } }; @@ -12331,7 +12220,7 @@ onloadend: ThreadUpdater.cb.load, timeout: $.MINUTE }, { - whenModified: true + whenModified: 'ThreadUpdater' }); }, updateThreadStatus: function(type, status) { @@ -12489,7 +12378,7 @@ this.unreaddb = Unread.db || new DataBoard('lastReadPosts'); $.on(d, 'QRPostSuccessful', this.cb.post); $.on(sc, 'click', this.toggleWatcher); - $.on(this.refreshButton, 'click', this.fetchAllStatus); + $.on(this.refreshButton, 'click', this.buttonFetchAll); $.on(this.closeButton, 'click', this.toggleWatcher); $.on(d, '4chanXInitFinished', this.ready); switch (g.VIEW) { @@ -12569,7 +12458,7 @@ if (ThreadWatcher.isWatched(this.thread)) { $.addClass(this.nodes.root, 'watched'); } - return $.on(this.nodes.thumb.parentNode, 'click', (function(_this) { + $.on(this.nodes.thumb.parentNode, 'click', (function(_this) { return function(e) { if (!(e.button === 0 && e.altKey)) { return; @@ -12578,6 +12467,11 @@ return e.preventDefault(); }; })(this)); + return $.on(this.nodes.thumb.parentNode, 'mousedown', function(e) { + if (e.button === 0 && e.altKey) { + return e.preventDefault(); + } + }); }, ready: function() { $.off(d, '4chanXInitFinished', ThreadWatcher.ready); @@ -12670,13 +12564,20 @@ data = ref[threadID]; if (!(data != null ? data.isDead : void 0) && !(threadID in g.BOARD.threads)) { if (Conf['Auto Prune'] || !(data && typeof data === 'object')) { - ThreadWatcher.db["delete"]({ + db["delete"]({ boardID: boardID, threadID: threadID }); } else { + if (Conf['Show Unread Count']) { + ThreadWatcher.fetchStatus({ + boardID: boardID, + threadID: threadID, + data: data + }); + } data.isDead = true; - ThreadWatcher.db.set({ + db.set({ boardID: boardID, threadID: threadID, val: data @@ -12698,9 +12599,24 @@ return ThreadWatcher.add(thread); } }, - fetchCount: { - fetched: 0, - fetching: 0 + requests: [], + fetched: 0, + clearRequests: function() { + ThreadWatcher.requests = []; + ThreadWatcher.fetched = 0; + ThreadWatcher.status.textContent = ''; + return $.rmClass(ThreadWatcher.refreshButton, 'fa-spin'); + }, + abort: function() { + var k, len1, ref, req; + ref = ThreadWatcher.requests; + for (k = 0, len1 = ref.length; k < len1; k++) { + req = ref[k]; + if (req.readyState !== 4) { + req.abort(); + } + } + return ThreadWatcher.clearRequests(); }, fetchAuto: function() { var db, interval, now; @@ -12718,6 +12634,13 @@ } return ThreadWatcher.timeout = setTimeout(ThreadWatcher.fetchAuto, interval); }, + buttonFetchAll: function() { + if (ThreadWatcher.requests.length) { + return ThreadWatcher.abort(); + } else { + return ThreadWatcher.fetchAllStatus(); + } + }, fetchAllStatus: function() { var k, len1, ref, thread, threads; ThreadWatcher.db.forceSync(); @@ -12734,37 +12657,34 @@ } }, fetchStatus: function(thread, force) { - var boardID, data, fetchCount, threadID; + var boardID, data, req, threadID; boardID = thread.boardID, threadID = thread.threadID, data = thread.data; if (data.isDead && !force) { return; } - fetchCount = ThreadWatcher.fetchCount; - if (fetchCount.fetching === 0) { + if (ThreadWatcher.requests.length === 0) { ThreadWatcher.status.textContent = '...'; $.addClass(ThreadWatcher.refreshButton, 'fa-spin'); } - fetchCount.fetching++; - return $.ajax("//a.4cdn.org/" + boardID + "/thread/" + threadID + ".json", { + req = $.ajax("//a.4cdn.org/" + boardID + "/thread/" + threadID + ".json", { onloadend: function() { return ThreadWatcher.parseStatus.call(this, thread); - } + }, + timeout: $.MINUTE + }, { + whenModified: force ? false : 'ThreadWatcher' }); + return ThreadWatcher.requests.push(req); }, parseStatus: function(arg) { - var boardID, data, fetchCount, isDead, k, lastReadPost, len1, match, postObj, quotingYou, ref, ref1, regexp, status, threadID, unread; + var boardID, data, isDead, k, lastReadPost, len1, match, postObj, quotesYou, quotingYou, ref, ref1, regexp, threadID, unread; boardID = arg.boardID, threadID = arg.threadID, data = arg.data; - fetchCount = ThreadWatcher.fetchCount; - fetchCount.fetched++; - if (fetchCount.fetched === fetchCount.fetching) { - fetchCount.fetched = 0; - fetchCount.fetching = 0; - status = ''; - $.rmClass(ThreadWatcher.refreshButton, 'fa-spin'); + ThreadWatcher.fetched++; + if (ThreadWatcher.fetched === ThreadWatcher.requests.length) { + ThreadWatcher.clearRequests(); } else { - status = (Math.round(fetchCount.fetched / fetchCount.fetching * 100)) + "%"; + ThreadWatcher.status.textContent = (Math.round(ThreadWatcher.fetched / ThreadWatcher.requests.length * 100)) + "%"; } - ThreadWatcher.status.textContent = status; if (this.status === 200 && this.response) { isDead = !!this.response.posts[0].archived; if (isDead && Conf['Auto Prune']) { @@ -12798,17 +12718,21 @@ if (!(QR.db && postObj.com)) { continue; } - regexp = /]*\bhref="(?:\/([^\/]+)\/thread\/(\d+))?(?:#p(\d+))?"/g; + quotesYou = false; + regexp = /]*\bhref="(?:\/([^\/]+)\/thread\/)?(\d+)?(?:#p(\d+))?"/g; while (match = regexp.exec(postObj.com)) { if (QR.db.get({ boardID: match[1] || boardID, threadID: match[2] || threadID, postID: match[3] || match[2] || threadID })) { - quotingYou++; - continue; + quotesYou = true; + break; } } + if (quotesYou && !Filter.isHidden(Build.parseJSON(postObj, boardID))) { + quotingYou++; + } } if (isDead !== data.isDead || unread !== data.unread || quotingYou !== data.quotingYou) { data.isDead = isDead; @@ -12829,6 +12753,8 @@ }); } else { data.isDead = true; + delete data.unread; + delete data.quotingYou; ThreadWatcher.db.set({ boardID: boardID, threadID: threadID, @@ -13344,7 +13270,7 @@ return; } notif = new Notification(post.info.nameBlock + " replied to you", { - body: post.info[Conf['Remove Spoilers'] || Conf['Reveal Spoilers'] ? 'comment' : 'commentSpoilered'], + body: post.info.commentDisplay, icon: Favicon.logo }); notif.onclick = function() { @@ -13378,6 +13304,9 @@ }, read: $.debounce(100, function(e) { var ID, count, data, height, ref, ref1, root; + if (!Unread.posts.size && Unread.readCount !== Unread.thread.posts.keys.length) { + Unread.saveLastReadPost(); + } if (d.hidden || !Unread.posts.size) { return; } @@ -13528,7 +13457,7 @@ } return Redirect.data = o; }, - archives: [{"uid":0,"name":"Moe","domain":"archive.moe","http":false,"https":true,"software":"foolfuuka","boards":["a","biz","c","co","diy","fit","gd","gif","h","i","int","jp","k","m","mlp","out","po","qa","r9k","s4s","sci","tg","tv","u","v","vg","vp","vr","wsg"],"files":["a","biz","c","co","diy","fit","gd","h","i","jp","k","m","mlp","po","qa","r9k","s4s","sci","tg","u","v","vg","vp","vr","wsg"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":4,"name":"Nyafuu Archive","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"archive.loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["c","d","e","i","lgbt","t","u","w","wg"],"files":["c","d","e","i","lgbt","t","u","w","wg"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":false,"https":true,"software":"fuuka","boards":["cgl","g","mu","qa","w"],"files":["cgl","g","mu","qa","w"]},{"uid":10,"name":"warosu","domain":"warosu.org","http":false,"https":true,"software":"fuuka","boards":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"],"files":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"]},{"uid":15,"name":"fgts","domain":"fgts.jp","http":true,"https":true,"software":"foolfuuka","boards":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"],"files":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"]},{"uid":22,"name":"not4plebs","domain":"totally.not4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["sp"],"files":["sp"]},{"uid":23,"name":"DesuStorage","domain":"archive.desustorage.org","http":false,"https":true,"software":"foolfuuka","boards":["mlp","qa"],"files":["mlp","qa"]}], + archives: [{"uid":0,"name":"Moe","domain":"archive.moe","http":false,"https":true,"software":"foolfuuka","boards":["a","an","biz","c","co","diy","fit","gd","gif","h","i","int","jp","k","m","mlp","out","po","qa","r9k","s4s","sci","tg","tv","u","v","vg","vp","vr","wsg"],"files":["a","biz","c","co","diy","fit","gd","h","i","jp","k","m","mlp","po","qa","r9k","s4s","sci","tg","u","v","vg","vp","vr","wsg"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":4,"name":"Nyafuu Archive","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"archive.loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["c","d","e","i","lgbt","t","u"],"files":["c","d","e","i","lgbt","t","u"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":false,"https":true,"software":"fuuka","boards":["cgl","g","mu","qa","w"],"files":["cgl","g","mu","qa","w"]},{"uid":10,"name":"warosu","domain":"warosu.org","http":false,"https":true,"software":"fuuka","boards":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"],"files":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"]},{"uid":15,"name":"fgts","domain":"fgts.jp","http":true,"https":true,"software":"foolfuuka","boards":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"],"files":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"]},{"uid":22,"name":"not4plebs","domain":"totally.not4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["sp"],"files":["sp"]},{"uid":23,"name":"DesuStorage","domain":"archive.desustorage.org","http":false,"https":true,"software":"foolfuuka","boards":["mlp","qa"],"files":["mlp","qa"]}], to: function(dest, data) { var archive; archive = (dest === 'search' || dest === 'board' ? Redirect.data.thread : Redirect.data[dest])[data.boardID]; @@ -14365,22 +14294,22 @@ formatters: { t: function() { return { - innerHTML: E(this.file.URL.match(/[^/]*$/)[0]) + innerHTML: E(this.file.url.match(/[^/]*$/)[0]) }; }, T: function() { return { - innerHTML: "" + FileInfo.formatters.t.call(this).innerHTML + "" + innerHTML: "" + FileInfo.formatters.t.call(this).innerHTML + "" }; }, l: function() { return { - innerHTML: "" + FileInfo.formatters.n.call(this).innerHTML + "" + innerHTML: "" + FileInfo.formatters.n.call(this).innerHTML + "" }; }, L: function() { return { - innerHTML: "" + FileInfo.formatters.N.call(this).innerHTML + "" + innerHTML: "" + FileInfo.formatters.N.call(this).innerHTML + "" }; }, n: function() { @@ -14432,6 +14361,11 @@ innerHTML: E(this.file.dimensions || "PDF") }; }, + g: function() { + return { + innerHTML: (this.file.tag ? ", " + E(this.file.tag) : "") + }; + }, '%': function() { return { innerHTML: "%" @@ -14493,21 +14427,25 @@ }); }, code: function() { - var i, k, len1, pre, ref; if (this.isClone) { return; } - ref = $$('.prettyprint', this.nodes.comment); - for (i = k = 0, len1 = ref.length; k < len1; i = ++k) { - pre = ref[i]; - if (!$.hasClass(pre, 'prettyprinted')) { - $.event('prettyprint', { - ID: this.fullID, - i: i, - html: pre.innerHTML - }, window); - } - } + return $.ready((function(_this) { + return function() { + var i, k, len1, pre, ref; + ref = $$('.prettyprint', _this.nodes.comment); + for (i = k = 0, len1 = ref.length; k < len1; i = ++k) { + pre = ref[i]; + if (!$.hasClass(pre, 'prettyprinted')) { + $.event('prettyprint', { + ID: _this.fullID, + i: i, + html: pre.innerHTML + }, window); + } + } + }; + })(this)); }, math: function() { if ((this.isClone && doc.contains(this.origin.nodes.root)) || !$('.math', this.nodes.comment)) { @@ -14946,6 +14884,8 @@ return 'Enter'; case 27: return 'Esc'; + case 32: + return 'Space'; case 37: return 'Left'; case 38: @@ -14954,9 +14894,15 @@ return 'Right'; case 40: return 'Down'; + case 188: + return 'Comma'; + case 190: + return 'Period'; default: if ((48 <= kc && kc <= 57) || (65 <= kc && kc <= 90)) { return String.fromCharCode(kc).toLowerCase(); + } else if ((96 <= kc && kc <= 105)) { + return String.fromCharCode(kc - 48).toLowerCase(); } else { return null; } @@ -15316,36 +15262,53 @@ }; Report = { + css: "noscript > div, noscript > div > div {\n" + +" height: 545px !important;\n" + +"}\n" + +"noscript > div > div > div:first-child, noscript iframe {\n" + +" height: 423px !important;\n" + +"}\n" + +":root:not(.js-enabled) #g-recaptcha {\n" + +" height: auto;\n" + +"}", init: function() { var match; if (!(/\bmode=report\b/.test(location.search) && (match = location.search.match(/\bno=(\d+)/)))) { return; } + Captcha.language.fixPage(); this.postID = +match[1]; return $.ready(this.ready); }, ready: function() { - new MutationObserver(Report.resize).observe(d.body, { - childList: true, - attributes: true, - subtree: true - }); + $.addStyle(Report.css); if (Conf['Archive Report']) { - return Report.archive(); + Report.archive(); + } + if ($.hasClass(doc, 'js-enabled')) { + return new MutationObserver(function() { + return Report.fit('.gc-bubbleDefault'); + }).observe(d.body, { + childList: true, + attributes: true, + subtree: true + }); + } else { + return Report.fit('body'); } }, - resize: function() { - var bubble, dy; - if (!(bubble = $('.gc-bubbleDefault'))) { + fit: function(selector) { + var dy, el; + if (!(el = $(selector, doc))) { return; } - dy = bubble.getBoundingClientRect().bottom - doc.clientHeight; + dy = el.getBoundingClientRect().bottom - doc.clientHeight + 8; if (dy > 0) { return window.resizeBy(0, dy); } }, archive: function() { - var link, message, url; + var link, message, types, url; Redirect.init(); if (!(url = Redirect.to('report', { boardID: g.BOARD.ID, @@ -15354,21 +15317,29 @@ return; } if ((message = $('h3')) && /Report submitted!/.test(message.textContent)) { - $.globalEval('self.close = function(){};'); - window.resizeTo(685, 320); - location.replace(url); + if (location.hash === '#redirect') { + $.globalEval('self.close = function(){};'); + window.resizeBy(0, 350 - doc.clientHeight); + location.replace(url); + } return; } link = $.el('a', { href: url, - textContent: 'Report to fgts' + textContent: 'Report to archive' }); $.on(link, 'click', function(e) { if (!(e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0)) { - return window.resizeTo(685, 320); + return window.resizeBy(0, 350 - doc.clientHeight); } }); - return $.add(d.body, [$.tn(' ['), link, $.tn(']')]); + $.add(d.body, [$.tn(' ['), link, $.tn(']')]); + if (types = $.id('reportTypes')) { + return $.on(types, 'change', function(e) { + var ref; + return $('form').action = (ref = e.target.value) === 'illegal' || ref === 'spam' ? '#redirect' : ''; + }); + } } }; @@ -15604,6 +15575,9 @@ div = $.el('div', { innerHTML: ": " + E(description) + "" }); + if ((typeof chrome !== "undefined" && chrome !== null) && key === 'Remember QR Size') { + div.hidden = true; + } input = $('input', div); $.on(input, 'change', function() { this.parentNode.parentNode.dataset.checked = this.checked; @@ -15870,14 +15844,14 @@ return; } $.extend(div, { - innerHTML: "
Filter is disabled.

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

    You can use these settings with each regular expression, separate them with semicolons:
  • Per boards, separate them with commas. It is global if not specified.
    For example: boards:a,jp;.
  • Filter OPs only along with their threads (\`only\`), replies only (\`no\`), or both (\`yes\`, this is default).
    For example: op:only;, op:no; or op:yes;.
  • Overrule the \`Show Stubs\` setting if specified: create a stub (\`yes\`) or not (\`no\`).
    For example: stub:yes; or stub:no;.
  • Highlight instead of hiding. You can specify a class name to use with a userstyle.
    For example: highlight; or highlight:wallpaper;.
  • Highlighted OPs will have their threads put on top of the board index by default.
    For example: top:yes; or top:no;.

Note: If you're using the native catalog rather than 4chan X's catalog, 4chan X's filters do not apply there.
The native catalog has its own separate filter list.

" + innerHTML: "
Filter is disabled.

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

    You can use these settings with each regular expression, separate them with semicolons:
  • Per boards, separate them with commas. It is global if not specified.
    For example: boards:a,jp;.
  • Filter OPs only along with their threads (\`only\`), replies only (\`no\`), or both (\`yes\`, this is default).
    For example: op:only;, op:no; or op:yes;.
  • Overrule the \`Show Stubs\` setting if specified: create a stub (\`yes\`) or not (\`no\`).
    For example: stub:yes; or stub:no;.
  • Highlight instead of hiding. You can specify a class name to use with a userstyle.
    For example: highlight; or highlight:wallpaper;.
  • Highlighted OPs will have their threads put on top of the board index by default.
    For example: top:yes; or top:no;.

Note: If you're using the native catalog rather than 4chan X's catalog, 4chan X's filters do not apply there.
The native catalog has its own separate filter list.

" }); return $('.warning', div).hidden = Conf['Filter']; }, sauce: function(section) { var ta; $.extend(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.
You can specify the applicable boards by appending ;boards:[board1],[board2].
You can specify the applicable file types by appending ;types:[extension1],[extension2].
    These parameters will be replaced by their corresponding values:
  • %TURL: Thumbnail URL.
  • %URL: Full image URL.
  • %IMG: Full image URL for GIF, JPG, and PNG; thumbnail URL for other types.
  • %MD5: MD5 hash.
  • %name: Original file name.
  • %board: Current board.
  • %%, %semi: Literal % and ;.
" + innerHTML: "
Sauce is disabled.
Lines starting with a # will be ignored.
You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards by appending ;boards:[board1],[board2].
You can specify the applicable file types by appending ;types:[extension1],[extension2].
You can open links with scripts and popups disabled by appending ;sandbox.
    These parameters will be replaced by their corresponding values:
  • %TURL: Thumbnail URL.
  • %URL: Full image URL.
  • %IMG: Full image URL for GIF, JPG, and PNG; thumbnail URL for other types.
  • %MD5: MD5 hash.
  • %name: Original file name.
  • %board: Current board.
  • %%, %semi: Literal % and ;.
" }); $('.warning', section).hidden = Conf['Sauce']; ta = $('textarea', section); @@ -15889,7 +15863,7 @@ advanced: function(section) { var aa, applyCSS, archBoards, boardID, boardOptions, boardSelect, boards, customCSS, files, i, input, inputs, interval, item, items, k, len1, len2, len3, len4, len5, len6, len7, name, o, q, ref, ref1, ref2, ref3, ref4, ref5, ref6, row, rows, software, ta, table, u, w, warning, withCredentials, y, z; $.extend(section, { - innerHTML: "
Archiver
404 Redirect is disabled.
Thread redirectionPost fetchingFile redirection
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
Archive link: g-archive
Internal archive link: g-expired
Title link: g-title
Board link (Replace with title when on that board): g-replace
Full text link: g-full
Custom text link: g-text:"Install Gentoo"
Index-only link: g-index
Catalog-only link: g-catalog
External link: external-text:"Google","http://www.google.com"
Combinations are possible: g-index-text:"Technology Index"
Full board list toggle: toggle-all

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

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

    You can use these settings with each item, separate them with semicolons:
  • Possible items are: name, options (or equivalently email), subject and password.
  • Wrap values of items with quotes, like this: options:"sage".
  • Force values as defaults with the always keyword, for example: options:"sage";always.
  • Select specific boards for an item, separated with commas, for example: options:"sage";boards:jp;always.
Unread Favicon is disabled.
Thread Updater is disabled.
Interval: seconds
Custom Cooldown Time
Seconds:
" + innerHTML: "
Archiver
404 Redirect is disabled.
Thread redirectionPost fetchingFile redirection
Captcha Language
Choose from list of language codes. Leave blank to autoselect.
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
Archive link: g-archive
Internal archive link: g-expired
Title link: g-title
Board link (Replace with title when on that board): g-replace
Full text link: g-full
Custom text link: g-text:"Install Gentoo"
Index-only link: g-index
Catalog-only link: g-catalog
External link: external-text:"Google","http://www.google.com"
Combinations are possible: g-index-text:"Technology Index"
Full board list toggle: toggle-all

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

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

    You can use these settings with each item, separate them with semicolons:
  • Possible items are: name, options (or equivalently email), subject and password.
  • Wrap values of items with quotes, like this: options:"sage".
  • Force values as defaults with the always keyword, for example: options:"sage";always.
  • Select specific boards for an item, separated with commas, for example: options:"sage";boards:jp;always.
Unread Favicon is disabled.
Thread Updater is disabled.
Interval: seconds
Custom Cooldown Time
Seconds:
" }); ref = $$('.warning', section); for (k = 0, len1 = ref.length; k < len1; k++) { @@ -15898,7 +15872,7 @@ } items = {}; inputs = {}; - ref1 = ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss', 'customCooldown']; + ref1 = ['captchaLanguage', 'boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss', 'customCooldown']; for (q = 0, len2 = ref1.length; q < len2; q++) { name = ref1[q]; input = $("[name='" + name + "']", section); @@ -15911,7 +15885,9 @@ $.on(input, 'change', Settings[name]); } else { $.on(input, 'input', $.cb.value); - $.on(input, 'input', Settings[name]); + if (name in Settings) { + $.on(input, 'input', Settings[name]); + } } } ta = $('.personafield', section); @@ -15925,10 +15901,9 @@ val = items[key]; input = inputs[key]; input.value = val; - if (key === 'usercss' || key === 'customCooldown') { - continue; + if (key in Settings && key !== 'usercss') { + Settings[key].call(input); } - Settings[key].call(input); } }); interval = $('input[name="Interval"]', section); @@ -16079,13 +16054,15 @@ data = { isReply: true, file: { - URL: '//i.4cdn.org/g/1334437723720.jpg', + url: '//i.4cdn.org/g/1334437723720.jpg', name: 'd9bb2efc98dd0df141a94399ff5880b7.jpg', size: '276 KB', sizeInBytes: 276 * 1024, dimensions: '1280x720', isImage: true, - isSpoiler: true + isVideo: false, + isSpoiler: true, + tag: 'Loop' } }; return FileInfo.format(this.value, data, this.nextElementSibling); @@ -16165,19 +16142,31 @@ if (location.hostname === 'www.google.com') { if (location.pathname === '/recaptcha/api/fallback') { $.ready(function() { - return Captcha.noscript.initFrame(); - }); - } else { - $.get('Captcha Fixes', true, function(arg) { - var enabled; - enabled = arg['Captcha Fixes']; - if (enabled) { - return $.ready(function() { - return Captcha.fixes.init(); - }); - } + return Captcha.v2.initFrame(); }); } + $.get('Captcha Fixes', true, function(arg) { + var enabled; + enabled = arg['Captcha Fixes']; + if (enabled) { + return $.ready(function() { + return Captcha.fixes.init(); + }); + } + }); + return; + } + if (location.hostname === 'www.4chan.org') { + $.onExists(d.documentElement, 'body', false, function() { + return $.addStyle(Main.cssWWW); + }); + Conf = { + 'captchaLanguage': Config.captchaLanguage + }; + $.get(Conf, function(items) { + $.extend(Conf, items); + return Captcha.language.fixPage(); + }); return; } g.threads = new SimpleDict(); @@ -16247,7 +16236,10 @@ }); }, initFeatures: function() { - var err, feature, k, len1, name, pathname, ref, ref1; + var err, feature, k, len1, name, pathname, ref, ref1, ref2; + if ((ref = location.hostname) === 'boards.4chan.org' || ref === 'sys.4chan.org') { + $.globalEval('document.documentElement.classList.add("js-enabled");'); + } switch (location.hostname) { case 'a.4cdn.org': return; @@ -16261,8 +16253,8 @@ $.asap((function() { return d.readyState !== 'loading'; }), function() { - var URL, pathname, ref, video; - if (Conf['404 Redirect'] && ((ref = d.title) === '4chan - Temporarily Offline' || ref === '4chan - 404 Not Found')) { + var URL, pathname, ref1, video; + if (Conf['404 Redirect'] && ((ref1 = d.title) === '4chan - Temporarily Offline' || ref1 === '4chan - 404 Not Found')) { Redirect.init(); pathname = location.pathname.split('/'); URL = Redirect.to('file', { @@ -16291,9 +16283,9 @@ history.replaceState(null, '', pathname.slice(0, 4).join('/') + location.hash); } } - ref = Main.features; - for (k = 0, len1 = ref.length; k < len1; k++) { - ref1 = ref[k], name = ref1[0], feature = ref1[1]; + ref1 = Main.features; + for (k = 0, len1 = ref1.length; k < len1; k++) { + ref2 = ref1[k], name = ref2[0], feature = ref2[1]; try { feature.init(); } catch (_error) { @@ -17146,6 +17138,15 @@ " 0% {transform:rotate(0deg);}\n" + " 100% {transform:rotate(359deg);}\n" + "}\n" + +"noscript > div, noscript > div > div {\n" + +" height: 545px !important;\n" + +"}\n" + +"noscript > div > div > div:first-child, noscript iframe {\n" + +" height: 423px !important;\n" + +"}\n" + +":root:not(.js-enabled) #g-recaptcha {\n" + +" height: auto;\n" + +"}\n" + "/* General */\n" + ".dialog {\n" + " border: 1px solid;\n" + @@ -17252,10 +17253,8 @@ " /* party hats */\n" + " pointer-events: none;\n" + "}\n" + -"marquee,\n" + -".postMessage marquee + br,\n" + -".postMessage marquee + br + br {\n" + -" display: none;\n" + +":root:not(.js-enabled) #postForm {\n" + +" display: table;\n" + "}\n" + "/* Anti-autoplay */\n" + "audio.controls-added {\n" + @@ -17311,6 +17310,9 @@ "#thread-watcher {\n" + " z-index: 10;\n" + "}\n" + +":root.fixed:not(.gallery-open) #header-bar:not(.autohide) {\n" + +" z-index: 5;\n" + +"}\n" + "/* Header */\n" + ".fixed.top-header body {\n" + " padding-top: 2em;\n" + @@ -17675,6 +17677,9 @@ ".section-filter textarea {\n" + " height: 500px;\n" + "}\n" + +".section-filter a, .section-advanced a {\n" + +" text-decoration: underline;\n" + +"}\n" + ".section-sauce textarea {\n" + " height: 350px;\n" + "}\n" + @@ -17998,12 +18003,15 @@ " border: none;\n" + " box-shadow: none;\n" + "}\n" + -":root.float #thread-stats > .move > span {\n" + +":root.float #thread-stats > .move > :not(#page-count) {\n" + " pointer-events: none;\n" + "}\n" + ":root.float #thread-stats {\n" + " padding: 0px 3px;\n" + "}\n" + +"#page-count {\n" + +" cursor: pointer;\n" + +"}\n" + "/* Quote */\n" + ".catalog-thread > .comment > span.quote, #arc-list span.quote {\n" + " color: #789922;\n" + @@ -18257,9 +18265,7 @@ " display: none !important;\n" + "}\n" + "#qr select,\n" + -"#url-button,\n" + -"#custom-cooldown-button,\n" + -"#dump-button,\n" + +"#qr-filename-container > a,\n" + ".remove,\n" + ".captcha-img {\n" + " cursor: pointer;\n" + @@ -18271,6 +18277,10 @@ " min-width: 300px;\n" + " border-radius: 3px 3px 0 0;\n" + "}\n" + +"#qr > form {\n" + +" max-height: calc(100vh - 75px);\n" + +" overflow-y: auto;\n" + +"}\n" + "#qrtab {\n" + " border-radius: 3px 3px 0 0;\n" + "}\n" + @@ -18281,13 +18291,6 @@ " float: right;\n" + " padding: 0 3px;\n" + "}\n" + -"#qr .warning {\n" + -" min-height: 1.6em;\n" + -" vertical-align: middle;\n" + -" padding: 0 1px;\n" + -" border-width: 1px;\n" + -" border-style: solid;\n" + -"}\n" + ".qr-link-container {\n" + " text-align: center;\n" + "}\n" + @@ -18349,23 +18352,7 @@ " position: relative;\n" + " top: 2px;\n" + "}\n" + -"/* Noscript Recaptcha */\n" + -".captcha-img {\n" + -" margin: 0px;\n" + -" text-align: center;\n" + -" background-image: #fff;\n" + -" font-size: 0px;\n" + -" min-height: 59px;\n" + -" min-width: 302px;\n" + -"}\n" + -".captcha-input{\n" + -" width: 100%;\n" + -" margin: 1px 0 0;\n" + -"}\n" + -"#qr-captcha-iframe {\n" + -" display: none;\n" + -"}\n" + -"/* Recaptcha v2 */\n" + +"/* Captcha */\n" + "#qr .captcha-root {\n" + " position: relative;\n" + "}\n" + @@ -18389,6 +18376,21 @@ " display: block;\n" + " width: 100%;\n" + "}\n" + +"#qr-captcha-iframe {\n" + +" width: 302px;\n" + +" height: 423px;\n" + +" border: 0;\n" + +" display: block;\n" + +" margin: auto;\n" + +"}\n" + +".goog-bubble-content {\n" + +" max-width: 100vw;\n" + +" max-height: 100vh;\n" + +" overflow: auto;\n" + +"}\n" + +".goog-bubble-content iframe {\n" + +" position: static !important;\n" + +"}\n" + "/* File Input, Submit Button */\n" + "#file-n-submit {\n" + " display: -webkit-flex;\n" + @@ -18402,6 +18404,7 @@ " background: linear-gradient(to bottom, #F8F8F8, #DCDCDC) no-repeat;\n" + " border: 1px solid #BBB;\n" + " border-radius: 2px;\n" + +" height: 100%;\n" + "}\n" + "#qr-file-button {\n" + " width: 15%;\n" + @@ -18579,6 +18582,8 @@ "}\n" + ".textarea {\n" + " position: relative;\n" + +" display: -webkit-flex;\n" + +" display: flex;\n" + "}\n" + ":root.webkit .textarea {\n" + " margin-bottom: -2px;\n" + @@ -18592,6 +18597,9 @@ " right: 1px;\n" + " pointer-events: none;\n" + "}\n" + +"#char-count.warning {\n" + +" color: red;\n" + +"}\n" + "/* Menu */\n" + ".menu-button:not(.fa-bars) {\n" + " display: inline-block;\n" + @@ -18614,7 +18622,7 @@ " height: 15px;\n" + " text-align: center;\n" + "}\n" + -".menu-button + .container:not(:empty) {\n" + +".menu-button + .container :first-child {\n" + " margin-left: -5px;\n" + "}\n" + "#menu {\n" + @@ -18897,13 +18905,6 @@ "}\n" + ".gal-fit-height .gal-image img,\n" + ".gal-fit-height .gal-image video {\n" + -" /*\n" + -" Chrome doesn't support viewpoint units in calc()\n" + -" http://bugs.chromium.org/168840\n" + -" \"It looks like the original author of viewport units in WebKit is not coming back to fix this stuff.\"\n" + -" Well, fuck.\n" + -" */\n" + -" max-height: 95vh;\n" + " max-height: calc(100vh - 25px);\n" + "}\n" + ".gal-image iframe {\n" + @@ -19535,7 +19536,19 @@ " font-family: sans-serif !important;\n" + " text-shadow: 1px 1px 1px rgba(0,74,153,0.6);\n" + "}", - features: [['Polyfill', Polyfill], ['Redirect', Redirect], ['Header', Header], ['Catalog Links', CatalogLinks], ['Settings', Settings], ['Index Generator', Index], ['Disable Autoplay', AntiAutoplay], ['Announcement Hiding', PSAHiding], ['Fourchan thingies', Fourchan], ['Color User IDs', IDColor], ['Highlight by User ID', IDHighlight], ['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], ['Index Generator (Menu)', Index.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], ['Volume Control', Volume], ['WEBM Metadata', Metadata], ['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], ['Mark New IPs', MarkNewIPs], ['Index Navigation', Nav], ['Keybinds', Keybinds], ['Banner', Banner], ['Flash Features', Flash]] + cssWWW: "noscript > div, noscript > div > div {\n" + +" height: 545px !important;\n" + +"}\n" + +"noscript > div > div > div:first-child, noscript iframe {\n" + +" height: 423px !important;\n" + +"}\n" + +":root:not(.js-enabled) #g-recaptcha {\n" + +" height: auto;\n" + +"}\n" + +"#captcha-cnt {\n" + +" height: auto;\n" + +"}", + features: [['Polyfill', Polyfill], ['Captcha Language', Captcha.language], ['Redirect', Redirect], ['Header', Header], ['Catalog Links', CatalogLinks], ['Settings', Settings], ['Index Generator', Index], ['Disable Autoplay', AntiAutoplay], ['Announcement Hiding', PSAHiding], ['Fourchan thingies', Fourchan], ['Color User IDs', IDColor], ['Highlight by User ID', IDHighlight], ['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], ['Index Generator (Menu)', Index.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], ['Volume Control', Volume], ['WEBM Metadata', Metadata], ['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], ['Mark New IPs', MarkNewIPs], ['Index Navigation', Nav], ['Keybinds', Keybinds], ['Banner', Banner], ['Flash Features', Flash]] }; Main.init(); diff --git a/builds/4chan-X-noupdate.crx b/builds/4chan-X-noupdate.crx index 60a36fc07..c0da5c73a 100644 Binary files a/builds/4chan-X-noupdate.crx and b/builds/4chan-X-noupdate.crx differ diff --git a/builds/4chan-X-noupdate.user.js b/builds/4chan-X-noupdate.user.js index 46d25dc46..af7e124d4 100644 --- a/builds/4chan-X-noupdate.user.js +++ b/builds/4chan-X-noupdate.user.js @@ -1,7 +1,7 @@ // Generated by CoffeeScript // ==UserScript== // @name 4chan X -// @version 1.10.10.0 +// @version 1.11.0.6 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X @@ -11,9 +11,11 @@ // @match *://sys.4chan.org/* // @match *://a.4cdn.org/* // @match *://i.4cdn.org/* +// @match *://www.4chan.org/banned +// @match *://www.4chan.org/feedback // @match https://www.google.com/recaptcha/api2/anchor?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @match https://www.google.com/recaptcha/api2/frame?*&k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* -// @match *://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc +// @match *://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue @@ -99,11 +101,8 @@ * 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 +* Font Awesome by Dave Gandy (http://fontawesome.io) +* license: http://fontawesome.io/license/ */ 'use strict'; @@ -237,7 +236,7 @@ 'Auto-load captcha': [false, 'Automatically load the captcha in the QR even if your post is empty.', 1], 'Post on Captcha Completion': [false, 'Submit the post immediately when the captcha is completed.', 1], 'Bottom QR Link': [true, 'Places a link on the bottom of threads to open the QR.', 1], - 'Captcha Fixes': [true, 'Make captcha more keyboard-navigable.'] + 'Captcha Fixes': [true, 'Make captcha easier to use, especially with the keyboard.'] }, 'Quote Links': { 'Quote Backlinks': [true, 'Add quote backlinks.'], @@ -269,6 +268,7 @@ 'Hide Thumbnails': [false], 'Fit Width': [true], 'Fit Height': [true], + 'Stretch to Fit': [false], 'Scroll to Post': [true], 'Slide Delay': [6.0] }, @@ -294,7 +294,7 @@ filesize: '', MD5: '' }, - sauces: "https://www.google.com/searchbyimage?image_url=%IMG\nhttp://iqdb.org/?url=%IMG\nhttp://eye.swfchan.com/search/?q=%name;types:swf\n#//tineye.com/search?url=%IMG\n#https://www.yandex.com/images/search?rpt=imageview&img_url=%IMG\n#//saucenao.com/search.php?url=%IMG\n#http://3d.iqdb.org/?url=%IMG\n#http://regex.info/exif.cgi?imgurl=%URL\n# uploaders:\n#//imgur.com/upload?url=%URL;types:gif,jpg,png,pdf;text:Upload to imgur\n# \"View Same\" in archives:\n#https://archive.moe/_/search/image/%MD5/;text:View same on archive.moe\n#https://archive.moe/%board/search/image/%MD5/;text:View same on archive.moe/%board/;boards:a,biz,c,co,diy,fit,gd,h,i,jp,k,m,mlp,po,qa,r9k,s4s,sci,tg,u,v,vg,vp,vr,wsg\n#https://rbt.asia/%board/image/%MD5;text:View same on RBT /%board/;boards:cgl,g,mu,qa,w", + sauces: "https://www.google.com/searchbyimage?image_url=%IMG\nhttp://iqdb.org/?url=%IMG\nhttp://eye.swfchan.com/search/?q=%name;types:swf;sandbox\n#//tineye.com/search?url=%IMG\n#https://www.yandex.com/images/search?rpt=imageview&img_url=%IMG\n#//saucenao.com/search.php?url=%IMG\n#http://3d.iqdb.org/?url=%IMG\n# tools:\n#http://regex.info/exif.cgi?imgurl=%URL\n#//imgops.com/%URL;types:gif,jpg,png\n# uploaders:\n#//imgur.com/upload?url=%URL;types:gif,jpg,png,pdf;text:Upload to imgur\n# \"View Same\" in archives:\n#https://archive.moe/_/search/image/%MD5/;text:View same on archive.moe\n#https://archive.moe/%board/search/image/%MD5/;text:View same on archive.moe/%board/;boards:a,biz,c,co,diy,fit,gd,h,i,jp,k,m,mlp,po,qa,r9k,s4s,sci,tg,u,v,vg,vp,vr,wsg\n#https://rbt.asia/%board/image/%MD5;text:View same on RBT /%board/;boards:cgl,g,mu,qa,w", FappeT: { werk: false }, @@ -324,9 +324,10 @@ QR: { 'QR.personas': "#options:\"sage\";boards:jp;always" }, + captchaLanguage: '', time: '%m/%d/%y(%a)%H:%M:%S', backlink: '>>%id', - fileInfo: '%l (%p%s, %r)', + fileInfo: '%l (%p%s, %r%g)', favicon: 'ferongr', usercss: '', hotkeys: { @@ -348,7 +349,7 @@ 'Expand images': ['e', 'Expand all images.'], 'Open Gallery': ['g', 'Opens the gallery.'], 'Pause': ['p', 'Pause/play videos in the gallery.'], - 'Slideshow': ['s', 'Toggle the gallery slideshow mode.'], + 'Slideshow': ['Ctrl+Right', 'Toggle the gallery slideshow mode.'], 'fappeTyme': ['f', 'Toggle Fappe Tyme.'], 'werkTyme': ['Shift+w', 'Toggle Werk Tyme.'], 'Front page': ['1', 'Jump to front page.'], @@ -398,7 +399,7 @@ doc = d.documentElement; g = { - VERSION: '1.10.10.0', + VERSION: '1.11.0.6', NAMESPACE: '4chan X.', boards: {} }; @@ -432,6 +433,10 @@ return html; }; + E.url = function(content) { + return "data:text/html;charset=utf-8," + (encodeURIComponent(content.innerHTML)); + }; + $ = function(selector, root) { if (root == null) { root = d.body; @@ -504,7 +509,7 @@ }); }; return function(url, options, extra) { - var err, form, r, type, upCallbacks, whenModified; + var err, form, r, ref, type, upCallbacks, whenModified; if (extra == null) { extra = {}; } @@ -522,11 +527,11 @@ return; } if (whenModified) { - if (url in lastModified) { - r.setRequestHeader('If-Modified-Since', lastModified[url]); + if (((ref = lastModified[whenModified]) != null ? ref[url] : void 0) != null) { + r.setRequestHeader('If-Modified-Since', lastModified[whenModified][url]); } $.on(r, 'load', function() { - return lastModified[url] = r.getResponseHeader('Last-Modified'); + return (lastModified[whenModified] || (lastModified[whenModified] = {}))[url] = r.getResponseHeader('Last-Modified'); }); } if (/\.json$/.test(url)) { @@ -1244,7 +1249,7 @@ this.info.nameBlock = Conf['Anonymize'] ? 'Anonymous' : this.nodes.nameBlock.textContent.trim(); if (subject = $('.subject', info)) { this.nodes.subject = subject; - this.info.subject = subject.textContent; + this.info.subject = subject.textContent || void 0; } if (name = $('.name', info)) { this.nodes.name = name; @@ -1295,28 +1300,33 @@ } Post.prototype.parseComment = function() { - var bq, k, len1, node, ref, spoilers; + var abbr, bq, commentDisplay, k, len1, len2, node, q, ref, spoilers; this.nodes.comment.normalize(); bq = this.nodes.comment.cloneNode(true); - ref = $$('.abbr, .exif, b, marquee', bq); + ref = $$('.abbr + br, .exif, b, .fortune', bq); for (k = 0, len1 = ref.length; k < len1; k++) { node = ref[k]; $.rm(node); } + if (abbr = $('.abbr', bq)) { + $.rm(abbr); + } this.info.comment = this.nodesToText(bq); - spoilers = $$('s', bq); - return this.info.commentSpoilered = (function() { - var len2, q; + if (abbr) { + this.info.comment = this.info.comment.replace(/\n\n$/, ''); + } + commentDisplay = this.info.comment; + if (!(Conf['Remove Spoilers'] || Conf['Reveal Spoilers'])) { + spoilers = $$('s', bq); if (spoilers.length) { for (q = 0, len2 = spoilers.length; q < len2; q++) { node = spoilers[q]; $.replace(node, $.tn('[spoiler]')); } - return this.nodesToText(bq); - } else { - return this.info.comment; + commentDisplay = this.nodesToText(bq); } - }).call(this); + } + return this.info.commentDisplay = commentDisplay.trim().replace(/\s+$/gm, ''); }; Post.prototype.nodesToText = function(bq) { @@ -1327,7 +1337,7 @@ while (node = nodes.snapshotItem(i++)) { text += node.data || '\n'; } - return text.trim().replace(/\s+$/gm, ''); + return text; }; Post.prototype.parseQuotes = function() { @@ -1356,7 +1366,7 @@ }; Post.prototype.parseFile = function() { - var fileEl, fileText, info, link, ref, ref1, size, thumb, unit; + var fileEl, fileText, info, link, m, ref, ref1, ref2, size, thumb, unit; if (!(fileEl = $('.file', this.nodes.post))) { return; } @@ -1370,12 +1380,13 @@ this.file = { text: fileText, link: link, - URL: link.href, + url: link.href, name: fileText.title || link.title || link.textContent, size: info[1], isImage: /(jpg|png|gif)$/i.test(link.href), isVideo: /webm$/i.test(link.href), - dimensions: (ref1 = info[0].match(/\d+x\d+/)) != null ? ref1[0] : void 0 + dimensions: (ref1 = info[0].match(/\d+x\d+/)) != null ? ref1[0] : void 0, + tag: (ref2 = info[0].match(/,[^,]*, ([a-z]+)\)/i)) != null ? ref2[1] : void 0 }; size = +this.file.size.match(/[\d.]+/)[0]; unit = ['B', 'KB', 'MB', 'GB'].indexOf(this.file.size.match(/\w+$/)[0]); @@ -1386,7 +1397,7 @@ if ((thumb = $('.fileThumb > [data-md5]', fileEl))) { return $.extend(this.file, { thumb: thumb, - thumbURL: location.protocol + "//i.4cdn.org/" + this.board + "/" + (link.href.match(/(\d+)\./)[1]) + "s.jpg", + thumbURL: (m = link.href.match(/\d+(?=\.\w+$)/)) ? location.protocol + "//i.4cdn.org/" + this.board + "/" + m[0] + "s.jpg" : void 0, MD5: thumb.dataset.md5, isSpoiler: $.hasClass(thumb.parentNode, 'imgspoiler') }); @@ -2045,7 +2056,7 @@ function Connection(target1, origin1, cb1) { this.target = target1; this.origin = origin1; - this.cb = cb1; + this.cb = cb1 != null ? cb1 : {}; this.onMessage = bind(this.onMessage, this); this.send = bind(this.send, this); $.on(window, 'message', this.onMessage); @@ -2256,42 +2267,50 @@ postID: this.postID, threadID: this.threadID, boardID: this.boardID, - name: data.name, + isReply: this.postID !== this.threadID + }; + o.info = { + subject: data.title, + email: data.email, + name: data.name || '', + tripcode: data.trip, capcode: (function() { switch (data.capcode) { case 'M': - return 'mod'; + return 'Mod'; case 'A': - return 'admin'; + return 'Admin'; case 'D': - return 'developer'; + return 'Developer'; } })(), - tripcode: data.trip, uniqueID: data.poster_hash, - email: data.email || '', - subject: data.title, flagCode: data.poster_country, - flagName: data.poster_country_name, - date: data.fourchan_date, + flag: data.poster_country_name, dateUTC: data.timestamp, - comment: comment + dateText: data.fourchan_date, + commentHTML: comment }; + if (o.info.capcode) { + delete o.info.uniqueID; + } if ((ref = data.media) != null ? ref.media_filename : void 0) { o.file = { name: data.media.media_filename, - timestamp: data.media.media_orig, - url: data.media.media_link || data.media.remote_media_link || ("//i.4cdn.org/" + this.boardID + "/" + (encodeURIComponent(data.media[this.boardID === 'f' ? 'media_filename' : 'media_orig']))), + url: data.media.media_link || data.media.remote_media_link || (location.protocol + "//i.4cdn.org/" + this.boardID + "/" + (encodeURIComponent(data.media[this.boardID === 'f' ? 'media_filename' : 'media_orig']))), 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 || ("//i.4cdn.org/" + this.boardID + "/" + data.media.preview_orig), + size: $.bytesToString(data.media.media_size), + thumbURL: data.media.thumb_link || (location.protocol + "//i.4cdn.org/" + this.boardID + "/" + data.media.preview_orig), theight: data.media.preview_h, twidth: data.media.preview_w, isSpoiler: data.media.spoiler === '1' }; - if (this.boardID === 'f') { + if (!/\.pdf$/.test(o.file.url)) { + o.file.dimensions = o.file.width + "x" + o.file.height; + } + if (this.boardID === 'f' && data.media.exif) { o.file.tag = JSON.parse(data.media.exif).Tag; } } @@ -2300,7 +2319,7 @@ post = new Post(Build.post(o), thread, board); post.kill(); if (post.file) { - post.file.thumbURL = o.file.turl; + post.file.thumbURL = o.file.thumbURL; } post.isFetchedQuote = true; Main.callbackNodes(Post, [post]); @@ -2349,26 +2368,7 @@ Polyfill = { init: function() { - this.notificationPermission(); - this.toBlob(); - return this.visibility(); - }, - notificationPermission: function() { - if (!window.Notification || 'permission' in Notification || !window.webkitNotifications) { - return; - } - return Object.defineProperty(Notification, 'permission', { - get: function() { - switch (webkitNotifications.checkPermission()) { - case 0: - return 'granted'; - case 1: - return 'default'; - case 2: - return 'denied'; - } - } - }); + return this.toBlob(); }, toBlob: function() { var base1; @@ -2384,26 +2384,6 @@ type: 'image/png' })); }); - }, - visibility: function() { - if ('visibilityState' in d) { - return; - } - Object.defineProperties(HTMLDocument.prototype, { - visibilityState: { - get: function() { - return this.webkitVisibilityState; - } - }, - hidden: { - get: function() { - return this.webkitHidden; - } - } - }); - return $.on(d, 'webkitvisibilitychange', function() { - return $.event('visibilitychange'); - }); } }; @@ -3609,7 +3589,7 @@ return Index.load(e, state); } }, { - whenModified: true + whenModified: 'Index' }); return $.addClass(Index.button, 'fa-spin'); }, @@ -4007,13 +3987,14 @@ if (text == null) { return text; } - return text.replace(/<[^>]*>/g, '').replace(/&(amp|#039|quot|lt|gt);/g, function(c) { + return text.replace(/<[^>]*>/g, '').replace(/&(amp|#039|quot|lt|gt|#44);/g, function(c) { return { '&': '&', ''': "'", '"': '"', '<': '<', - '>': '>' + '>': '>', + ',': ',' }[c]; }); }, @@ -4045,117 +4026,128 @@ return "/" + boardID + "/thread/" + threadID + "#p" + postID; } }, - postFromObject: function(data, boardID, suppressThumb) { + parseJSON: function(data, boardID) { var o; o = { postID: data.no, threadID: data.resto || data.no, boardID: boardID, - name: Build.unescape(data.name), - capcode: data.capcode, - tripcode: data.trip, - uniqueID: data.id, - email: Build.unescape(data.email), - subject: Build.unescape(data.sub), - flagCode: data.country, - flagName: Build.unescape(data.country_name), - date: data.now, - dateUTC: data.time, - comment: { - innerHTML: data.com || '' - }, + isReply: !!data.resto, isSticky: !!data.sticky, isClosed: !!data.closed, - isArchived: !!data.archived + isArchived: !!data.archived, + fileDeleted: !!data.filedeleted }; - if (data.filedeleted) { - o.file = { - isDeleted: true - }; - } else if (data.ext) { + o.info = { + subject: Build.unescape(data.sub), + email: Build.unescape(data.email), + name: Build.unescape(data.name) || '', + tripcode: data.trip, + uniqueID: data.id, + flagCode: data.country, + flag: Build.unescape(data.country_name), + dateUTC: data.time, + dateText: data.now, + commentHTML: { + innerHTML: data.com || '' + } + }; + if (data.capcode) { + o.info.capcode = data.capcode.replace(/_highlight$/, '').replace(/_/g, ' ').replace(/\b\w/g, function(c) { + return c.toUpperCase(); + }); + o.capcodeHighlight = /_highlight$/.test(data.capcode); + delete o.info.uniqueID; + } + if (data.ext) { o.file = { name: (Build.unescape(data.filename)) + data.ext, - timestamp: "" + data.tim + data.ext, - url: boardID === 'f' ? "//i.4cdn.org/" + boardID + "/" + (encodeURIComponent(data.filename)) + data.ext : "//i.4cdn.org/" + boardID + "/" + data.tim + data.ext, + url: boardID === 'f' ? location.protocol + "//i.4cdn.org/" + boardID + "/" + (encodeURIComponent(data.filename)) + data.ext : location.protocol + "//i.4cdn.org/" + boardID + "/" + data.tim + data.ext, height: data.h, width: data.w, MD5: data.md5, - size: data.fsize, - turl: "//i.4cdn.org/" + boardID + "/" + data.tim + "s.jpg", + size: $.bytesToString(data.fsize), + thumbURL: location.protocol + "//i.4cdn.org/" + boardID + "/" + data.tim + "s.jpg", theight: data.tn_h, twidth: data.tn_w, isSpoiler: !!data.spoiler, - isDeleted: false, tag: data.tag }; + if (!/\.pdf$/.test(o.file.url)) { + o.file.dimensions = o.file.width + "x" + o.file.height; + } } + return o; + }, + parseComment: function(o) { + var html; + html = o.info.commentHTML.innerHTML.replace(//gi, '\n').replace(/\n\nRolled [^<]*<\/b>/i, '').replace(/]*>/g, ''); + return o.info.comment = Build.unescape(html); + }, + postFromObject: function(data, boardID, suppressThumb) { + var o; + o = Build.parseJSON(data, boardID); return Build.post(o, suppressThumb); }, post: function(o, suppressThumb) { - - /* - This function contains code from 4chan-JS (https://github.com/4chan/4chan-JS). - @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE - */ - var boardID, capcode, capcodeDescription, capcodeLC, capcodeLong, capcodePlural, capcodeText, capcodeUC, comment, container, date, dateUTC, email, file, fileBlock, fileDims, fileSize, fileThumb, flagCode, flagName, gifIcon, href, isOP, k, len1, match, name, postClass, postID, postInfo, postLink, quote, quoteLink, ref, shortFilename, staticPath, subject, threadID, tripcode, uniqueID, wholePost; - 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, comment = o.comment, file = o.file; - name || (name = ''); - subject || (subject = ''); - isOP = postID === threadID; + var boardID, capcode, capcodeDescription, capcodeLC, capcodeLong, capcodePlural, capcodeUC, commentHTML, container, dateText, dateUTC, email, file, fileBlock, fileThumb, fileURL, flag, flagCode, gifIcon, href, k, len1, match, name, postClass, postID, postInfo, postLink, protocol, quote, quoteLink, ref, ref1, shortFilename, staticPath, subject, threadID, tripcode, uniqueID, wholePost; + postID = o.postID, threadID = o.threadID, boardID = o.boardID, file = o.file; + ref = o.info, subject = ref.subject, email = ref.email, name = ref.name, tripcode = ref.tripcode, capcode = ref.capcode, uniqueID = ref.uniqueID, flagCode = ref.flagCode, flag = ref.flag, dateUTC = ref.dateUTC, dateText = ref.dateText, commentHTML = ref.commentHTML; staticPath = Build.staticPath, gifIcon = Build.gifIcon; /* Post Info */ if (capcode) { - capcodeLC = capcode.split('_')[0]; - capcodeUC = capcodeLC[0].toUpperCase() + capcodeLC.slice(1); - capcodeText = capcodeUC; - capcodeLong = { - 'Admin': 'Administrator', - 'Mod': 'Moderator' - }[capcodeUC] || capcodeUC; - capcodePlural = capcodeLong + "s"; - capcodeDescription = "a 4chan " + capcodeLong; - if (capcode === 'admin_emeritus') { - capcodeText = 'Admin Emeritus'; + capcodeUC = capcode.split(' ')[0]; + capcodeLC = capcodeUC.toLowerCase(); + if (capcode === 'Admin Emeritus') { capcodePlural = 'the Administrator Emeritus'; capcodeDescription = "4chan's founding Administrator"; + } else { + capcodeLong = { + 'Admin': 'Administrator', + 'Mod': 'Moderator' + }[capcode] || capcode; + capcodePlural = capcodeLong + "s"; + capcodeDescription = "a 4chan " + capcodeLong; } } postLink = Build.postURL(boardID, threadID, postID); quoteLink = Build.sameThread(boardID, threadID) ? "javascript:quote('" + (+postID) + "');" : "/" + boardID + "/thread/" + threadID + "#q" + postID; postInfo = { - innerHTML: "
" + (isOP || boardID === "f" ? "" + E(subject) + " " : "") + "" + (email ? "" : "") + "" + E(name) + "" + (tripcode ? " " + E(tripcode) + "" : "") + (capcode ? " ## " + E(capcodeText) + "" : "") + (email ? "" : "") + (boardID === "f" && isOP || capcode ? "" : " ") + (capcode ? " \""" : "") + (uniqueID && !capcode ? " (ID: " + E(uniqueID) + ")" : "") + (flagCode ? " " : "") + " " + E(date) + " No." + E(postID) + "" + (o.isSticky ? " \"Sticky\"" : "") + (o.isClosed && !o.isArchived ? " \"Closed\"" : "") + (o.isArchived ? " \"Archived\"" : "") + (isOP && g.VIEW === "index" ? "   [Reply]" : "") + "
" + innerHTML: "
" + (!o.isReply || boardID === "f" || subject ? "" + E(subject || "") + " " : "") + "" + (email ? "" : "") + "" + E(name) + "" + (tripcode ? " " + E(tripcode) + "" : "") + (capcode ? " ## " + E(capcode) + "" : "") + (email ? "" : "") + (boardID === "f" && !o.isReply || capcode ? "" : " ") + (capcode ? " \""" : "") + (uniqueID && !capcode ? " (ID: " + E(uniqueID) + ")" : "") + (flagCode ? " " : "") + " " + E(dateText) + " No." + E(postID) + "" + (o.isSticky ? " \"Sticky\"" : "") + (o.isClosed && !o.isArchived ? " \"Closed\"" : "") + (o.isArchived ? " \"Archived\"" : "") + (!o.isReply && g.VIEW === "index" ? "   [Reply]" : "") + "
" }; /* File Info */ - if (file && !file.isDeleted) { + if (file) { + protocol = /^https?:(?=\/\/i\.4cdn\.org\/)/; + fileURL = file.url.replace(protocol, ''); shortFilename = Build.shortFilename(file.name); - fileSize = $.bytesToString(file.size); - fileDims = file.url.slice(-4) === '.pdf' ? 'PDF' : file.width + "x" + file.height; - fileThumb = file.isSpoiler ? Build.spoilerThumb(boardID) : file.turl; + fileThumb = file.isSpoiler ? Build.spoilerThumb(boardID) : file.thumbURL.replace(protocol, ''); } fileBlock = { - innerHTML: (file ? "
" + (file.isDeleted ? "\"File" : (boardID === "f" ? "
File: " + E(file.name) + "-(" + E(fileSize) + ", " + E(fileDims) + ", " + E(file.tag) + ")
" : "
File: " + (file.isSpoiler ? "Spoiler Image" : E(shortFilename)) + " (" + E(fileSize) + ", " + E(fileDims) + ")
")) + "
" : "") + innerHTML: (file ? "
" + (boardID === "f" ? "
File: " + E(file.name) + "-(" + E(file.size) + ", " + E(file.dimensions) + (file.tag ? ", " + E(file.tag) : "") + ")
" : "
File: " + (file.isSpoiler ? "Spoiler Image" : E(shortFilename)) + " (" + E(file.size) + ", " + E(file.dimensions || "PDF") + ")
") + "
" : (o.fileDeleted ? "
\"File
" : "")) }; /* Whole Post */ - postClass = isOP ? 'op' : 'reply'; + postClass = o.isReply ? 'reply' : 'op'; wholePost = { - innerHTML: (!isOP ? "
>>
" : "") + "
" + (isOP ? fileBlock.innerHTML + postInfo.innerHTML : postInfo.innerHTML + fileBlock.innerHTML) + "
" + comment.innerHTML + "
" + innerHTML: (o.isReply ? "
>>
" : "") + "
" + (o.isReply ? postInfo.innerHTML + fileBlock.innerHTML : fileBlock.innerHTML + postInfo.innerHTML) + "
" + commentHTML.innerHTML + "
" }; container = $.el('div', { className: "postContainer " + postClass + "Container", id: "pc" + postID }); $.extend(container, wholePost); - ref = $$('.quotelink', container); - for (k = 0, len1 = ref.length; k < len1; k++) { - quote = ref[k]; + ref1 = $$('.quotelink', container); + for (k = 0, len1 = ref1.length; k < len1; k++) { + quote = ref1[k]; href = quote.getAttribute('href'); if ((href[0] === '#') && !(Build.sameThread(boardID, threadID))) { quote.href = ("/" + boardID + "/thread/" + threadID) + href; } else if ((match = href.match(/^\/([^\/]+)\/thread\/(\d+)/)) && (Build.sameThread(match[1], match[2]))) { quote.href = href.match(/(#[^#]*)?$/)[0] || '#'; + } else if (/^\d+(#|$)/.test(href) && !(g.VIEW === 'thread' && g.BOARD.ID === boardID)) { + quote.href = "/" + boardID + "/thread/" + href; } } return container; @@ -4302,7 +4294,7 @@ threadExcerpt: function(thread) { var OP, excerpt, ref; OP = thread.OP; - excerpt = ("/" + thread.board + "/ - ") + (((ref = OP.info.subject) != null ? ref.trim() : void 0) || OP.info.comment.replace(/\n+/g, ' // ') || OP.info.nameBlock); + excerpt = ("/" + thread.board + "/ - ") + (((ref = OP.info.subject) != null ? ref.trim() : void 0) || OP.info.commentDisplay.replace(/\n+/g, ' // ') || OP.info.nameBlock); if (excerpt.length > 73) { return excerpt.slice(0, 70) + "..."; } @@ -4984,7 +4976,7 @@ Filter = { filters: {}, init: function() { - var boards, err, filter, hl, k, key, len1, line, op, ref, ref1, ref2, ref3, ref4, ref5, ref6, regexp, stub, top; + var boards, err, filter, hl, k, key, len1, line, op, ref, ref1, ref2, ref3, ref4, ref5, regexp, stub, top; if (!(((ref = g.VIEW) === 'index' || ref === 'thread') && Conf['Filter'])) { return; } @@ -5004,9 +4996,7 @@ } filter = line.replace(regexp[0], ''); boards = ((ref2 = filter.match(/boards:([^;]+)/)) != null ? ref2[1].toLowerCase() : void 0) || 'global'; - if (boards !== 'global' && (ref3 = g.BOARD.ID, indexOf.call(boards.split(','), ref3) < 0)) { - continue; - } + boards = boards === 'global' ? null : boards.split(','); if (key === 'uniqueID' || key === 'MD5') { regexp = regexp[1]; } else { @@ -5018,10 +5008,10 @@ continue; } } - op = ((ref4 = filter.match(/[^t]op:(yes|no|only)/)) != null ? ref4[1] : void 0) || 'yes'; + op = ((ref3 = filter.match(/[^t]op:(yes|no|only)/)) != null ? ref3[1] : void 0) || 'yes'; stub = (function() { - var ref5; - switch ((ref5 = filter.match(/stub:(yes|no)/)) != null ? ref5[1] : void 0) { + var ref4; + switch ((ref4 = filter.match(/stub:(yes|no)/)) != null ? ref4[1] : void 0) { case 'yes': return true; case 'no': @@ -5031,11 +5021,11 @@ } })(); if (hl = /highlight/.test(filter)) { - hl = ((ref5 = filter.match(/highlight:([\w-]+)/)) != null ? ref5[1] : void 0) || 'filter-highlight'; - top = ((ref6 = filter.match(/top:(yes|no)/)) != null ? ref6[1] : void 0) || 'yes'; + hl = ((ref4 = filter.match(/highlight:([\w-]+)/)) != null ? ref4[1] : void 0) || 'filter-highlight'; + top = ((ref5 = filter.match(/top:(yes|no)/)) != null ? ref5[1] : void 0) || 'yes'; top = top === 'yes'; } - this.filters[key].push(this.createFilter(regexp, op, stub, hl, top)); + this.filters[key].push(this.createFilter(regexp, boards, op, stub, hl, top)); } if (!this.filters[key].length) { delete this.filters[key]; @@ -5049,7 +5039,7 @@ cb: this.node }); }, - createFilter: function(regexp, op, stub, hl, top) { + createFilter: function(regexp, boards, op, stub, hl, top) { var settings, test; test = typeof regexp === 'string' ? function(value) { return regexp === value; @@ -5062,7 +5052,10 @@ "class": hl, top: top }; - return function(value, isReply) { + return function(value, boardID, isReply) { + if (boards && indexOf.call(boards, boardID) < 0) { + return false; + } if (isReply && op === 'only' || !isReply && op === 'no') { return false; } @@ -5082,7 +5075,7 @@ ref = Filter.filters[key]; for (k = 0, len1 = ref.length; k < len1; k++) { filter = ref[k]; - if (!(result = filter(value, this.isReply))) { + if (!(result = filter(value, this.board.ID, this.isReply))) { continue; } if (result.hide && !this.isFetchedQuote) { @@ -5106,6 +5099,23 @@ } } }, + isHidden: function(post) { + var filter, k, key, len1, ref, result, value; + for (key in Filter.filters) { + if ((value = Filter[key](post)) != null) { + ref = Filter.filters[key]; + for (k = 0, len1 = ref.length; k < len1; k++) { + filter = ref[k]; + if (result = filter(value, post.boardID, post.isReply)) { + if (result.hide) { + return true; + } + } + } + } + } + return false; + }, name: function(post) { return post.info.name; }, @@ -5119,10 +5129,11 @@ return post.info.capcode; }, subject: function(post) { - return post.info.subject || void 0; + return post.info.subject; }, comment: function(post) { - return post.info.comment; + var ref; + return (ref = post.info.comment) != null ? ref : Build.parseComment(post); }, flag: function(post) { return post.info.flag; @@ -6175,7 +6186,7 @@ }, mouseover: function(e) { var boardID, clone, k, len1, len2, origin, post, postID, posts, q, qp, quote, quoterID, ref, ref1, threadID; - if ($.hasClass(this, 'inlined')) { + if ($.hasClass(this, 'inlined') || !d.contains(this)) { return; } ref = Get.postDataFromLink(this), boardID = ref.boardID, threadID = ref.threadID, postID = ref.postID; @@ -6659,7 +6670,7 @@ QR = { mimeTypes: ['image/jpeg', 'image/png', 'image/gif', 'application/pdf', 'application/vnd.adobe.flash.movie', 'application/x-shockwave-flash', 'video/webm'], init: function() { - var noscript, sc; + var sc; if (!Conf['Quick Reply']) { return; } @@ -6668,9 +6679,7 @@ if (g.VIEW === 'archive') { return; } - $.globalEval('document.documentElement.dataset.jsEnabled = true;'); - noscript = Conf['Force Noscript Captcha'] || !doc.dataset.jsEnabled; - this.captcha = Captcha[noscript ? 'noscript' : 'v2']; + this.captcha = Captcha.v2; $.on(d, '4chanXInitFinished', this.initReady); Post.callbacks.push({ name: 'Quick Reply', @@ -6698,7 +6707,7 @@ } if (Conf['Hide Original Post Form']) { $.addClass(doc, 'hide-original-post-form'); - if (!doc.dataset.jsEnabled) { + if (!$.hasClass(doc, 'js-enabled')) { return $.onExists(doc, '#postForm noscript', true, $.rm); } } @@ -6818,26 +6827,31 @@ return QR.captcha.destroy(); }, focus: function() { - $.queueTask(function() { - var focus; - if (!$$('.goog-bubble-content > iframe').some(function(el) { - return el.getBoundingClientRect().top >= 0; - })) { - focus = d.activeElement && QR.nodes.el.contains(d.activeElement); - return $[focus ? 'addClass' : 'rmClass'](QR.nodes.el, 'focus'); + return $.queueTask(function() { + if (!QR.inBubble()) { + QR.hasFocus = d.activeElement && QR.nodes.el.contains(d.activeElement); + QR.nodes.el.classList.toggle('focus', QR.hasFocus); + } + if (QR.captcha.isEnabled && !QR.captcha.noscript) { + if (QR.inCaptcha()) { + QR.scrollY = window.scrollY; + return $.on(d, 'scroll', QR.scrollLock); + } else { + return $.off(d, 'scroll', QR.scrollLock); + } } }); - if (typeof chrome !== "undefined" && chrome !== null) { - if (d.activeElement && QR.nodes.el.contains(d.activeElement) && d.activeElement.nodeName === 'IFRAME') { - QR.scrollY = window.scrollY; - return $.on(d, 'scroll', QR.scrollLock); - } else { - return $.off(d, 'scroll', QR.scrollLock); - } - } + }, + inBubble: function() { + var ref; + return ref = d.activeElement, indexOf.call($$('.goog-bubble-content > iframe'), ref) >= 0; + }, + inCaptcha: function() { + var ref; + return (((ref = d.activeElement) != null ? ref.nodeName : void 0) === 'IFRAME' && QR.nodes.el.contains(d.activeElement)) || (QR.hasFocus && QR.inBubble()); }, scrollLock: function() { - if (d.activeElement && QR.nodes.el.contains(d.activeElement) && d.activeElement.nodeName === 'IFRAME') { + if (QR.inCaptcha()) { return window.scroll(window.scrollX, QR.scrollY); } else { return $.off(d, 'scroll', QR.scrollLock); @@ -7017,10 +7031,10 @@ characterCount: function() { var count, counter; counter = QR.nodes.charCount; - count = QR.nodes.com.textLength; + count = QR.nodes.com.value.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, '_').length; counter.textContent = count; counter.hidden = count < 1000; - return (count > 1500 ? $.addClass : $.rmClass)(counter, 'warning'); + return (count > 2000 ? $.addClass : $.rmClass)(counter, 'warning'); }, getFile: function() { var ref; @@ -7320,7 +7334,7 @@ event = node.nodeName === 'SELECT' ? 'change' : 'input'; $.on(nodes[name], event, save); } - if (Conf['Remember QR Size']) { + if ((typeof chrome === "undefined" || chrome === null) && Conf['Remember QR Size']) { $.get('QR Size', '', function(item) { return nodes.com.style.cssText = item['QR Size']; }); @@ -7603,15 +7617,17 @@ Captcha = {}; Captcha.fixes = { - selectors: { - image: '.rc-imageselect-target > .rc-imageselect-tile > img' - }, + imageKeys: '789456123uiojklm'.split('').concat(['Comma', 'Period']), + css: '.rc-imageselect-target > div:focus {\n outline: 2px solid #4a90e2;\n}\n.rc-button-default:focus {\n box-shadow: inset 0 0 0 2px #0063d6;\n}', + cssNoscript: '.fbc-payload-imageselect {\n position: relative;\n}\n.fbc-payload-imageselect > label {\n position: absolute;\n display: block;\n height: 93.3px;\n width: 93.3px;\n}\nlabel[data-row="0"] {top: 0px;}\nlabel[data-row="1"] {top: 93.3px;}\nlabel[data-row="2"] {top: 186.6px;}\nlabel[data-col="0"] {left: 0px;}\nlabel[data-col="1"] {left: 93.3px;}\nlabel[data-col="2"] {left: 186.6px;}', init: function() { switch (location.pathname.split('/')[3]) { case 'anchor': return this.initMain(); case 'frame': return this.initPopup(); + case 'fallback': + return this.initNoscript(); } }, initMain: function() { @@ -7629,7 +7645,7 @@ }); }, initPopup: function() { - $.addStyle(this.selectors.image + ":focus {outline: 2px solid #4a90e2;}"); + $.addStyle(this.css); this.fixImages(); new MutationObserver((function(_this) { return function() { @@ -7641,383 +7657,135 @@ }); return $.on(d, 'keydown', this.keybinds.bind(this)); }, - fixImages: function() { - var focus, img, k, len1, ref; - if (!(this.images = $$(this.selectors.image)).length) { + initNoscript: function() { + this.noscript = true; + this.images = $$('.fbc-payload-imageselect > input'); + if (!this.images.length) { return; } - focus = this.images[0].tabIndex !== 0; + $.addStyle(this.cssNoscript); + this.addLabels(); + $.on(d, 'keydown', this.keybinds.bind(this)); + return $.on($('.fbc-imageselect-challenge > form'), 'submit', this.checkForm.bind(this)); + }, + fixImages: function() { + var img, k, len1, ref; + this.images = $$('.rc-imageselect-target > div'); ref = this.images; for (k = 0, len1 = ref.length; k < len1; k++) { img = ref[k]; img.tabIndex = 0; } - if (focus) { - return this.focusImage(); + if (this.images.length) { + return this.addTooltips(this.images); } }, - focusImage: function() { - var img; - img = this.images[0]; - return $.asap(function() { - if (!doc.contains(img)) { - return true; + addLabels: function() { + var checkbox, i, imageSelect, label, labels; + imageSelect = $('.fbc-payload-imageselect'); + labels = (function() { + var k, len1, ref, results; + ref = this.images; + results = []; + for (i = k = 0, len1 = ref.length; k < len1; i = ++k) { + checkbox = ref[i]; + checkbox.id = "checkbox-" + i; + label = $.el('label', { + htmlFor: checkbox.id + }); + label.dataset.row = Math.floor(i / 3); + label.dataset.col = i % 3; + results.push(label); } - img.focus(); - return d.activeElement === img; - }, function() {}); + return results; + }).call(this); + $.add(imageSelect, labels); + return this.addTooltips(labels); + }, + addTooltips: function(nodes) { + var i, k, len1, node; + for (i = k = 0, len1 = nodes.length; k < len1; i = ++k) { + node = nodes[i]; + node.title = this.imageKeys[i] + " or " + (this.imageKeys[i + 9][0].toUpperCase()) + this.imageKeys[i + 9].slice(1); + } + }, + checkForm: function(e) { + var checkbox, k, len1, n, ref; + n = 0; + ref = this.images; + for (k = 0, len1 = ref.length; k < len1; k++) { + checkbox = ref[k]; + if (checkbox.checked) { + n++; + } + } + if (n === 0) { + return e.preventDefault(); + } }, keybinds: function(e) { - var dx, reload, verify, x; - if (!(this.images && doc.contains(this.images[0]) && d.activeElement)) { + var dx, i, key, reload, verify, x; + if (!(this.images && doc.contains(this.images[0]))) { return; } - reload = $.id('recaptcha-reload-button'); - verify = $.id('recaptcha-verify-button'); + reload = $('#recaptcha-reload-button, .fbc-button-reload'); + verify = $('#recaptcha-verify-button, .fbc-button-verify > input'); x = this.images.indexOf(d.activeElement); if (x < 0) { - if (!$('.rc-controls').contains(d.activeElement)) { - return; - } x = d.activeElement === verify ? 11 : 9; } - if (!(dx = { - 38: 9, - 40: 3, - 37: 11, - 39: 1 - }[e.keyCode])) { + key = Keybinds.keyCode(e); + if (!this.noscript && key === 'Space' && x < 9) { + this.images[x].click(); + } else if ((i = this.imageKeys.indexOf(key)) >= 0) { + this.images[i % 9].click(); + verify.focus(); + } else if (dx = { + 'Up': 9, + 'Down': 3, + 'Left': 11, + 'Right': 1 + }[key]) { + x = (x + dx) % 12; + if (x === 10) { + x = dx === 11 ? 9 : 11; + } + (this.images[x] || { + 9: reload, + 11: verify + }[x]).focus(); + } else { return; } - x = (x + dx) % 12; - if (x === 10) { - x = dx === 11 ? 9 : 11; - } - (this.images[x] || { - 9: reload, - 11: verify - }[x]).focus(); e.preventDefault(); return e.stopPropagation(); } }; - Captcha.noscript = { - lifetime: 2 * $.MINUTE, - iframeURL: '//www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc', + Captcha.language = { init: function() { - var container, input; - if (d.cookie.indexOf('pass_enabled=1') >= 0) { + if (!(Conf['captchaLanguage'].trim() && d.cookie.indexOf('pass_enabled=1') < 0 && !Conf['Hide Original Post Form'])) { return; } - if (!(this.isEnabled = !!$.id('g-recaptcha'))) { - return; - } - container = $.el('div', { - className: 'captcha-img', - title: 'Reload reCAPTCHA' + return $.onExists(doc, '#captchaFormPart', true, function(node) { + return $.onExists(node, 'iframe', true, Captcha.language.fixIframe); }); - input = $.el('input', { - className: 'captcha-input field', - title: 'Verification', - autocomplete: 'off', - spellcheck: false - }); - this.nodes = { - container: container, - input: input - }; - $.on(input, 'keydown', this.keydown.bind(this)); - $.on(this.nodes.container, 'click', (function(_this) { - return function() { - _this.reload(); - return _this.nodes.input.focus(); - }; - })(this)); - this.conn = new Connection(null, location.protocol + "//www.google.com", { - challenge: this.load.bind(this), - token: this.save.bind(this), - error: this.error.bind(this) - }); - $.addClass(QR.nodes.el, 'has-captcha'); - $.after(QR.nodes.com.parentNode, [container, input]); - this.captchas = []; - $.get('captchas', [], function(arg) { - var captchas; - captchas = arg.captchas; - QR.captcha.sync(captchas); - return QR.captcha.clear(); - }); - $.sync('captchas', this.sync); - this.beforeSetup(); - return this.setup(); }, - initFrame: function() { - var cb, conn, img, ref, ref1; - conn = new Connection(window.parent, location.protocol + "//boards.4chan.org", { - response: function(response) { - $.id('response').value = response; - return $('.fbc-challenge > form').submit(); - } - }); - conn.send({ - token: (ref = $('.fbc-verification-token > textarea')) != null ? ref.value : void 0, - error: (ref1 = $('.fbc-error')) != null ? ref1.textContent : void 0 - }); - if (!(img = $('.fbc-payload > img'))) { + fixPage: function() { + if (!(Conf['captchaLanguage'].trim() && d.cookie.indexOf('pass_enabled=1') < 0)) { return; } - cb = function() { - var canvas; - canvas = $.el('canvas'); - canvas.width = img.width; - canvas.height = img.height; - canvas.getContext('2d').drawImage(img, 0, 0); - return conn.send({ - challenge: canvas.toDataURL() - }); - }; - if (img.complete) { - return cb(); - } else { - return $.on(img, 'load', cb); - } + return $.onExists(doc, 'iframe', true, Captcha.language.fixIframe); }, - timers: {}, - cb: { - focus: function() { - return QR.captcha.setup(false, true); - } - }, - beforeSetup: function() { - var container, input, ref; - ref = this.nodes, container = ref.container, input = ref.input; - container.hidden = true; - input.value = ''; - input.placeholder = 'Focus to load reCAPTCHA'; - this.count(); - return $.on(input, 'focus click', this.cb.focus); - }, - needed: function() { - var captchaCount, postsCount; - captchaCount = this.captchas.length; - if (QR.req) { - captchaCount++; - } - postsCount = QR.posts.length; - if (postsCount === 1 && !Conf['Auto-load captcha'] && !QR.posts[0].com && !QR.posts[0].file) { - postsCount = 0; - } - return captchaCount < postsCount; - }, - onNewPost: function() {}, - onPostChange: function() {}, - setup: function(focus, force) { - if (!(this.isEnabled && (this.needed() || force))) { + fixIframe: function(el) { + var lang, src; + if (!(lang = Conf['captchaLanguage'].trim())) { return; } - if (!this.nodes.iframe) { - this.nodes.iframe = $.el('iframe', { - id: 'qr-captcha-iframe', - src: this.iframeURL - }); - $.add(d.body, this.nodes.iframe); - this.conn.target = this.nodes.iframe.contentWindow; - } else if (!this.occupied || force) { - this.nodes.iframe.src = this.iframeURL; + src = /[?&]hl=/.test(el.src) ? el.src.replace(/([?&]hl=)[^&]*/, '$1' + encodeURIComponent(lang)) : el.src + ("&hl=" + (encodeURIComponent(lang))); + if (el.src !== src) { + return el.src = src; } - this.occupied = true; - if (focus) { - return this.nodes.input.focus(); - } - }, - afterSetup: function() { - var container, input, ref; - ref = this.nodes, container = ref.container, input = ref.input; - container.hidden = false; - input.placeholder = 'Verification'; - this.count(); - $.off(input, 'focus click', this.cb.focus); - if (QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight) { - QR.nodes.el.style.top = ''; - return QR.nodes.el.style.bottom = '0px'; - } - }, - destroy: function() { - if (!this.isEnabled) { - return; - } - if (this.nodes.img) { - $.rm(this.nodes.img); - } - delete this.nodes.img; - if (this.nodes.iframe) { - $.rm(this.nodes.iframe); - } - delete this.nodes.iframe; - delete this.occupied; - return this.beforeSetup(); - }, - sync: function(captchas) { - if (captchas == null) { - captchas = []; - } - QR.captcha.captchas = captchas; - return QR.captcha.count(); - }, - getOne: function() { - var captcha; - this.clear(); - if (captcha = this.captchas.shift()) { - this.count(); - $.set('captchas', this.captchas); - return captcha.response; - } else if (/\S/.test(this.nodes.input.value)) { - return (function(_this) { - return function(cb) { - _this.submitCB = cb; - return _this.sendResponse(); - }; - })(this); - } else { - return null; - } - }, - sendResponse: function() { - var response; - response = this.nodes.input.value; - if (/\S/.test(response)) { - return this.conn.send({ - response: response - }); - } - }, - save: function(token) { - delete this.occupied; - this.nodes.input.value = ''; - if (this.submitCB) { - this.submitCB(token); - delete this.submitCB; - if (this.needed()) { - return this.reload(); - } else { - return this.destroy(); - } - } else { - $.forceSync('captchas'); - this.captchas.push({ - response: token, - timeout: this.timeout - }); - this.count(); - $.set('captchas', this.captchas); - return this.reload(); - } - }, - error: function(message) { - this.occupied = true; - this.nodes.input.value = ''; - if (this.submitCB) { - this.submitCB(); - delete this.submitCB; - } - return QR.error("Captcha Error: " + message); - }, - clear: function() { - var captcha, i, k, len1, now, ref; - if (!this.captchas.length) { - return; - } - $.forceSync('captchas'); - now = Date.now(); - ref = this.captchas; - for (i = k = 0, len1 = ref.length; k < len1; i = ++k) { - 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(src) { - var container, img, input, ref; - ref = this.nodes, container = ref.container, input = ref.input, img = ref.img; - this.occupied = true; - this.timeout = Date.now() + this.lifetime; - if (!img) { - img = this.nodes.img = new Image(); - $.one(img, 'load', this.afterSetup.bind(this)); - $.on(img, 'load', function() { - return this.hidden = false; - }); - $.add(container, img); - } - img.src = src; - input.value = ''; - this.clear(); - clearTimeout(this.timers.expire); - return this.timers.expire = setTimeout(this.expire.bind(this), this.lifetime); - }, - count: function() { - var count, placeholder; - count = this.captchas ? this.captchas.length : 0; - placeholder = this.nodes.input.placeholder.replace(/\ \(.*\)$/, ''); - placeholder += (function() { - switch (count) { - case 0: - if (placeholder === 'Verification') { - return ' (Shift + Enter to cache)'; - } else { - return ''; - } - break; - case 1: - return ' (1 cached captcha)'; - default: - return " (" + count + " cached captchas)"; - } - })(); - this.nodes.input.placeholder = placeholder; - this.nodes.input.alt = count; - clearTimeout(this.timers.clear); - if (this.captchas.length) { - return this.timers.clear = setTimeout(this.clear.bind(this), this.captchas[0].timeout - Date.now()); - } - }, - expire: function() { - if (!this.nodes.iframe) { - return; - } - if (!d.hidden && (this.needed() || d.activeElement === this.nodes.input)) { - return this.reload(); - } else { - return this.destroy(); - } - }, - reload: function() { - var ref; - this.nodes.iframe.src = this.iframeURL; - this.occupied = true; - return (ref = this.nodes.img) != null ? ref.hidden = true : void 0; - }, - keydown: function(e) { - if (e.keyCode === 8 && !this.nodes.input.value) { - if (this.nodes.iframe) { - this.reload(); - } else { - this.setup(); - } - } else if (e.keyCode === 13 && e.shiftKey) { - this.sendResponse(); - } else { - return; - } - return e.preventDefault(); } }; @@ -8031,6 +7799,16 @@ if (!(this.isEnabled = !!$.id('g-recaptcha'))) { return; } + if (this.noscript = Conf['Force Noscript Captcha'] || !$.hasClass(doc, 'js-enabled')) { + this.conn = new Connection(null, location.protocol + "//www.google.com", { + token: (function(_this) { + return function(token) { + return _this.save(true, token); + }; + })(this) + }); + $.addClass(QR.nodes.el, 'noscript-captcha'); + } this.captchas = []; $.get('captchas', [], function(arg) { var captchas; @@ -8061,9 +7839,26 @@ }; })(this)); }, + initFrame: function() { + var conn, ref, token; + if (token = (ref = $('.fbc-verification-token > textarea')) != null ? ref.value : void 0) { + conn = new Connection(window.parent, location.protocol + "//boards.4chan.org"); + return conn.send({ + token: token + }); + } + }, shouldFocus: false, timeouts: {}, postsCount: 0, + noscriptURL: function() { + var lang, url; + url = '//www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc'; + if (lang = Conf['captchaLanguage'].trim()) { + url += "&hl=" + (encodeURIComponent(lang)); + } + return url; + }, needed: function() { var captchaCount; captchaCount = this.captchas.length; @@ -8099,7 +7894,7 @@ if (!(this.isEnabled && (this.needed() || force))) { return; } - if (focus) { + if (focus && !QR.inBubble()) { this.shouldFocus = true; } if (this.timeouts.destroy) { @@ -8110,6 +7905,7 @@ if (this.nodes.container) { if (this.shouldFocus && (iframe = $('iframe', this.nodes.container))) { iframe.focus(); + QR.focus(); delete this.shouldFocus; } return; @@ -8122,6 +7918,22 @@ childList: true, subtree: true }); + if (this.noscript) { + return this.setupNoscript(); + } else { + return this.setupJS(); + } + }, + setupNoscript: function() { + var iframe; + iframe = $.el('iframe', { + id: 'qr-captcha-iframe', + src: this.noscriptURL() + }); + $.add(this.nodes.container, iframe); + return this.conn.target = iframe.contentWindow; + }, + setupJS: function() { return $.globalEval('(function() {\n function render() {\n var container = document.querySelector("#qr .captcha-container");\n container.dataset.widgetID = window.grecaptcha.render(container, {\n sitekey: \'6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc\',\n theme: document.documentElement.classList.contains(\'tomorrow\') ? \'dark\' : \'light\',\n callback: function(response) {\n window.dispatchEvent(new CustomEvent("captcha:success", {detail: response}));\n }\n });\n }\n if (window.grecaptcha) {\n render();\n } else {\n var cbNative = window.onRecaptchaLoaded;\n window.onRecaptchaLoaded = function() {\n render();\n cbNative();\n }\n }\n})();'); }, afterSetup: function(mutations) { @@ -8141,7 +7953,7 @@ } }, setupIFrame: function(iframe) { - this.setupTime = Date.now(); + Captcha.language.fixIframe(iframe); $.addClass(QR.nodes.el, 'captcha-open'); if (QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight) { QR.nodes.el.style.top = null; @@ -8198,24 +8010,26 @@ return null; } }, - save: function(pasted) { - var base1, focus, ref, ref1; + save: function(pasted, token) { + var base1, focus, ref; $.forceSync('captchas'); this.captchas.push({ - response: $('textarea', this.nodes.container).value, - timeout: (pasted ? this.setupTime : Date.now()) + this.lifetime + response: token || $('textarea', this.nodes.container).value, + timeout: Date.now() + this.lifetime }); $.set('captchas', this.captchas); this.count(); + focus = ((ref = d.activeElement) != null ? ref.nodeName : void 0) === 'IFRAME' && /https?:\/\/www\.google\.com\/recaptcha\//.test(d.activeElement.src); if (this.needed()) { - if (QR.cooldown.auto || Conf['Post on Captcha Completion']) { - this.shouldFocus = true; - } else { - QR.nodes.status.focus(); + if (focus) { + if (QR.cooldown.auto || Conf['Post on Captcha Completion']) { + this.shouldFocus = true; + } else { + QR.nodes.status.focus(); + } } this.reload(); } else { - focus = ((ref = d.activeElement) != null ? ref.nodeName : void 0) === 'IFRAME' && ((ref1 = d.activeElement.src) != null ? ref1.slice(0, 38) : void 0) === 'https://www.google.com/recaptcha/api2/'; if (pasted) { this.destroy(); } else { @@ -8251,7 +8065,7 @@ this.captchas = this.captchas.slice(i); this.count(); $.set('captchas', this.captchas); - return this.setup(true); + return this.setup(d.activeElement === QR.nodes.status); }, count: function() { this.nodes.counter.textContent = "Captchas: " + this.captchas.length; @@ -8261,7 +8075,11 @@ } }, reload: function() { - return $.globalEval('(function() {\n var container = document.querySelector("#qr .captcha-container");\n window.grecaptcha.reset(container.dataset.widgetID);\n})();'); + if (this.noscript) { + return $('iframe', this.nodes.container).src = this.noscriptURL(); + } else { + return $.globalEval('(function() {\n var container = document.querySelector("#qr .captcha-container");\n window.grecaptcha.reset(container.dataset.widgetID);\n})();'); + } } }; @@ -9285,7 +9103,7 @@ Gallery.fullIDs[post.fullID] = true; thumb = $.el('a', { className: 'gal-thumb', - href: post.file.URL, + href: post.file.url, target: '_blank', title: post.file.name }); @@ -9298,29 +9116,43 @@ Gallery.images.push(thumb); return $.add(Gallery.nodes.thumbs, thumb); }, + load: function(thumb, errorCB) { + var elType, ext, file; + ext = thumb.href.match(/\w*$/); + elType = { + 'webm': 'video', + 'pdf': 'iframe' + }[ext] || 'img'; + file = $.el(elType, { + title: thumb.title + }); + $.extend(file.dataset, thumb.dataset); + $.on(file, 'error', errorCB); + file.src = thumb.href; + return file; + }, open: function(thumb) { - var el, elType, file, name, newID, nodes, oldID, post, ref, slideshow; + var el, file, newID, nodes, oldID, post, ref; nodes = Gallery.nodes; - name = nodes.name; oldID = +nodes.current.dataset.id; newID = +thumb.dataset.id; - slideshow = Gallery.slideshow && (newID > oldID || (oldID === Gallery.images.length - 1 && newID === 0)); - if (el = $('.gal-highlight', nodes.thumbs)) { + if (el = Gallery.images[oldID]) { $.rmClass(el, 'gal-highlight'); } $.addClass(thumb, 'gal-highlight'); - elType = /\.webm$/.test(thumb.href) ? 'video' : /\.pdf$/.test(thumb.href) ? 'iframe' : 'img'; - $[elType === 'iframe' ? 'addClass' : 'rmClass'](doc, 'gal-pdf'); - file = $.el(elType, { - title: name.download = name.textContent = thumb.title - }); - $.extend(file.dataset, thumb.dataset); - $.on(file, 'error', Gallery.error); - file.src = name.href = thumb.href; + nodes.thumbs.scrollTop = thumb.offsetTop + thumb.offsetHeight / 2 - nodes.thumbs.clientHeight / 2; + if (((ref = Gallery.cache) != null ? ref.dataset.id : void 0) === '' + newID) { + file = Gallery.cache; + $.off(file, 'error', Gallery.cacheError); + $.on(file, 'error', Gallery.error); + } else { + file = Gallery.load(thumb, Gallery.error); + } $.off(nodes.current, 'error', Gallery.error); ImageCommon.pause(nodes.current); $.replace(nodes.current, file); - if (elType === 'video') { + nodes.current = file; + if (file.nodeName === 'VIDEO') { file.loop = true; Volume.setup(file); if (Conf['Autoplay']) { @@ -9330,19 +9162,22 @@ ImageCommon.addControls(file); } } + doc.classList.toggle('gal-pdf', file.nodeName === 'IFRAME'); + Gallery.cb.setHeight(); nodes.count.textContent = +thumb.dataset.id + 1; - nodes.current = file; + nodes.name.download = nodes.name.textContent = thumb.title; + nodes.name.href = thumb.href; nodes.frame.scrollTop = 0; nodes.next.focus(); - if (slideshow) { + if (Gallery.slideshow && (newID > oldID || (oldID === Gallery.images.length - 1 && newID === 0))) { Gallery.setupTimer(); } else { Gallery.cb.stop(); } - if (Conf['Scroll to Post'] && (post = (ref = (post = g.posts[file.dataset.post])) != null ? ref.nodes.root : void 0)) { - Header.scrollTo(post); + if (Conf['Scroll to Post'] && (post = g.posts[file.dataset.post])) { + Header.scrollTo(post.nodes.root); } - return nodes.thumbs.scrollTop = thumb.offsetTop + thumb.offsetHeight / 2 - nodes.thumbs.clientHeight / 2; + return Gallery.cache = Gallery.load(Gallery.images[(newID + 1) % Gallery.images.length], Gallery.cacheError); }, error: function() { var ref; @@ -9364,6 +9199,9 @@ }; })(this)); }, + cacheError: function() { + return delete Gallery.cache; + }, cleanupTimer: function() { var current; clearTimeout(Gallery.timeoutID); @@ -9525,6 +9363,11 @@ setFitness: function() { return (this.checked ? $.addClass : $.rmClass)(doc, "gal-" + (this.name.toLowerCase().replace(/\s+/g, '-'))); }, + setHeight: function() { + var current, dim, frame, height, ref, ref1, ref2, width; + ref = Gallery.nodes, current = ref.current, frame = ref.frame; + return current.style.minHeight = Conf['Stretch to Fit'] && (dim = (ref1 = g.posts[current.dataset.post]) != null ? ref1.file.dimensions : void 0) ? ((ref2 = dim.split('x'), width = ref2[0], height = ref2[1], ref2), Math.min(doc.clientHeight - 25, height / width * frame.clientWidth) + 'px') : null; + }, setDelay: function() { return Gallery.delay = +this.value; } @@ -9549,11 +9392,14 @@ var input, label; label = UI.checkbox(name, name); input = label.firstElementChild; - if (name === 'Fit Width' || name === 'Fit Height' || name === 'Hide Thumbnails') { + if (name === 'Hide Thumbnails' || name === 'Fit Width' || name === 'Fit Height') { $.on(input, 'change', Gallery.cb.setFitness); } $.event('change', null, input); $.on(input, 'change', $.cb.checked); + if (name === 'Hide Thumbnails' || name === 'Fit Width' || name === 'Fit Height' || name === 'Stretch to Fit') { + $.on(input, 'change', Gallery.cb.setHeight); + } return { el: label }; @@ -9562,7 +9408,7 @@ var delayInput, delayLabel, item, subEntries; subEntries = (function() { var k, len1, ref, results; - ref = ['Hide Thumbnails', 'Fit Width', 'Fit Height', 'Scroll to Post']; + ref = ['Hide Thumbnails', 'Fit Width', 'Fit Height', 'Stretch to Fit', 'Scroll to Post']; results = []; for (k = 0, len1 = ref.length; k < len1; k++) { item = ref[k]; @@ -9637,7 +9483,7 @@ }, error: function(file, post, delay, cb) { var URL, redirect, src, timeoutID; - src = post.file.URL.split('/'); + src = post.file.url.split('/'); URL = Redirect.to('file', { boardID: post.board.ID, filename: src[src.length - 1] @@ -9687,7 +9533,7 @@ post.kill(true); return redirect(); } else { - return URL = post.file.URL; + return URL = post.file.url; } } }); @@ -9882,7 +9728,7 @@ if (file.videoControls) { $.rm(file.videoControls); } - file.thumb.parentNode.href = file.URL; + file.thumb.parentNode.href = file.url; file.thumb.parentNode.target = '_blank'; ref = ['isExpanding', 'isExpanded', 'videoControls', 'wasPlaying', 'scrollIntoView']; for (k = 0, len1 = ref.length; k < len1; k++) { @@ -9949,7 +9795,7 @@ el = file.fullImage = $.el((isVideo ? 'video' : 'img')); el.dataset.fullID = post.fullID; $.on(el, 'error', ImageExpand.error); - el.src = src || file.URL; + el.src = src || file.url; } el.className = 'full-image'; $.after(thumb, el); @@ -10178,7 +10024,7 @@ el = $.el((isVideo ? 'video' : 'img')); el.dataset.fullID = post.fullID; $.on(el, 'error', error); - el.src = file.URL; + el.src = file.url; } if (Conf['Restart when Opened']) { ImageCommon.rewind(el); @@ -10309,22 +10155,22 @@ attr = ref[k]; video.style[attr] = thumb.style[attr]; } - video.src = file.URL; + video.src = file.url; $.replace(thumb, video); file.thumb = video; return file.videoThumb = true; }, prefetch: function(post) { - var URL, clone, el, file, isImage, isVideo, k, len1, match, ref, replace, thumb, type; + var clone, el, file, isImage, isVideo, k, len1, match, ref, replace, thumb, type, url; file = post.file; if (!file) { return; } - isImage = file.isImage, isVideo = file.isVideo, thumb = file.thumb, URL = file.URL; + isImage = file.isImage, isVideo = file.isVideo, thumb = file.thumb, url = file.url; if (file.isPrefetched || !(isImage || isVideo) || post.isHidden || post.thread.isHidden) { return; } - type = (match = URL.match(/\.([^.]+)$/)[1].toUpperCase()) === 'JPEG' ? 'JPG' : match; + type = (match = url.match(/\.([^.]+)$/)[1].toUpperCase()) === 'JPEG' ? 'JPG' : match; replace = Conf["Replace " + type] && !/spoiler/.test(thumb.src || thumb.dataset.src); if (!(replace || Conf['prefetch'])) { return; @@ -10356,13 +10202,13 @@ ref1 = post.clones; for (q = 0, len2 = ref1.length; q < len2; q++) { clone = ref1[q]; - clone.file.thumb.src = URL; + clone.file.thumb.src = url; } - thumb.src = URL; + thumb.src = url; return thumb.removeAttribute('data-src'); }); } - return el.src = URL; + return el.src = url; }, toggle: function() { if (Conf['prefetch'] = this.checked) { @@ -10404,7 +10250,7 @@ }, node: function() { var el; - if (!(this.file && /webm$/i.test(this.file.URL))) { + if (!(this.file && /webm$/i.test(this.file.url))) { return; } if (this.isClone) { @@ -10425,7 +10271,7 @@ load: function() { $.rmClass(this.parentNode, 'error'); $.addClass(this.parentNode, 'loading'); - return CrossOrigin.binary(Get.postFromNode(this).file.URL, (function(_this) { + return CrossOrigin.binary(Get.postFromNode(this).file.url, (function(_this) { return function(data) { var output, title; $.rmClass(_this.parentNode, 'loading'); @@ -10542,32 +10388,44 @@ cb: this.node }); }, + sandbox: function(url) { + return E.url({ + innerHTML: "[sb] " + E(url) + "" + }); + }, + rmOrigin: function(e) { + if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) { + return; + } + $.open(this.href); + return e.preventDefault(); + }, createSauceLink: function(link, post) { - var a, ext, i, k, key, len1, m, part, parts, ref, ref1, ref2, skip; + var a, ext, i, k, key, len1, m, part, parts, ref, ref1, ref2, skip, url; if (!(link = link.trim())) { return null; } parts = {}; - ref = link.split(/;(?=(?:text|boards|types):)/); + ref = link.split(/;(?=(?:text|boards|types|sandbox):?)/); for (i = k = 0, len1 = ref.length; k < len1; i = ++k) { part = ref[i]; if (i === 0) { parts['url'] = part; } else { - m = part.match(/^(\w*):(.*)$/); + m = part.match(/^(\w*):?(.*)$/); parts[m[1]] = m[2]; } } parts['text'] || (parts['text'] = ((ref1 = parts['url'].match(/(\w+)\.\w+\//)) != null ? ref1[1] : void 0) || '?'); - ext = post.file.URL.match(/[^.]*$/)[0]; + ext = post.file.url.match(/[^.]*$/)[0]; skip = false; for (key in parts) { parts[key] = parts[key].replace(/%(T?URL|IMG|MD5|board|name|%|semi)/g, function(parameter) { var type; type = { '%TURL': post.file.thumbURL, - '%URL': post.file.URL, - '%IMG': ext === 'gif' || ext === 'jpg' || ext === 'png' ? post.file.URL : post.file.thumbURL, + '%URL': post.file.url, + '%IMG': ext === 'gif' || ext === 'jpg' || ext === 'png' ? post.file.url : post.file.thumbURL, '%MD5': post.file.MD5, '%board': post.board.ID, '%name': post.file.name, @@ -10596,12 +10454,19 @@ if (!(!parts['types'] || indexOf.call(parts['types'].split(','), ext) >= 0)) { return null; } + url = parts['url']; + if (parts['sandbox'] != null) { + url = Sauce.sandbox(url); + } a = Sauce.link.cloneNode(true); - a.href = parts['url']; + a.href = url; a.textContent = parts['text']; if (/^javascript:/i.test(parts['url'])) { a.removeAttribute('target'); } + if (parts['sandbox'] != null) { + $.on(a, 'click', Sauce.rmOrigin); + } return a; }, node: function() { @@ -10718,6 +10583,9 @@ }, wheel: function(e) { var el, volume; + if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey) { + return; + } if (!(el = $('video:not([data-md5])', this))) { return; } @@ -11034,7 +10902,7 @@ content = { innerHTML: "" + E(a.dataset.uid) + "" }; - el.src = "data:text/html;charset=utf-8," + (encodeURIComponent(content.innerHTML)); + el.src = E.url(content); return el; }, title: { @@ -11712,7 +11580,7 @@ if (!file) { return false; } - a.href = file.URL; + a.href = file.url; a.download = file.name; return true; } @@ -11770,27 +11638,36 @@ } a = $.el('a', { className: 'report-link', - href: 'javascript:;', - textContent: 'Report this post' + href: 'javascript:;' }); $.on(a, 'click', ReportLink.report); return Menu.menu.addEntry({ el: a, order: 10, open: function(post) { - ReportLink.url = !post.isDead ? "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post : Conf['Archive Report'] ? Redirect.to('report', { - boardID: post.board.ID, - postID: post.ID - }) : void 0; + if (!(post.isDead || (post.thread.isDead && !post.thread.isArchived))) { + a.textContent = 'Report this post'; + ReportLink.url = "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post; + ReportLink.height = 200; + } else if (Conf['Archive Report']) { + a.textContent = 'Report to archive'; + ReportLink.url = Redirect.to('report', { + boardID: post.board.ID, + postID: post.ID + }); + ReportLink.height = 350; + } else { + ReportLink.url = ''; + } return !!ReportLink.url; } }); }, report: function() { - var id, set, url; - url = ReportLink.url; + var height, id, set, url; + url = ReportLink.url, height = ReportLink.height; id = Date.now(); - set = "toolbar=0,scrollbars=1,location=0,status=1,menubar=0,resizable=1,width=685,height=200"; + set = "toolbar=0,scrollbars=1,location=0,status=1,menubar=0,resizable=1,width=700,height=" + height; return window.open(url, id, set); } }; @@ -11918,13 +11795,13 @@ return; } statsHTML = { - innerHTML: "? / ?" + (Conf["IP Count in Stats"] ? " / ?" : "") + (Conf["Page Count in Stats"] ? " / ?" : "") + innerHTML: "? / ?" + (Conf["IP Count in Stats"] ? " / ?" : "") + (Conf["Page Count in Stats"] && g.BOARD.ID !== "f" ? " / ?" : "") }; statsTitle = 'Posts / Files'; if (Conf['IP Count in Stats']) { statsTitle += ' / IPs'; } - if (Conf['Page Count in Stats']) { + if (Conf['Page Count in Stats'] && g.BOARD.ID !== 'f') { statsTitle += ' / Page'; } if (Conf['Updater and Stats in Header']) { @@ -11949,6 +11826,9 @@ this.fileCountEl = $('#file-count', sc); this.ipCountEl = $('#ip-count', sc); this.pageCountEl = $('#page-count', sc); + if (this.pageCountEl) { + $.on(this.pageCountEl, 'click', ThreadStats.fetchPage); + } return Thread.callbacks.push({ name: 'Thread Stats', cb: this.node @@ -11963,7 +11843,7 @@ if (post.file) { fileCount++; } - if (Conf["Page Count in Stats"]) { + if (ThreadStats.pageCountEl) { return ThreadStats.lastPost = post.info.date; } }); @@ -11979,13 +11859,13 @@ } ref = e.detail, postCount = ref.postCount, fileCount = ref.fileCount, ipCount = ref.ipCount, newPosts = ref.newPosts; ThreadStats.update(postCount, fileCount, ipCount); - if (!Conf["Page Count in Stats"]) { + if (!ThreadStats.pageCountEl) { return; } if (newPosts.length) { ThreadStats.lastPost = g.posts[newPosts[newPosts.length - 1]].info.date; } - if (ThreadStats.lastPost > ThreadStats.lastPageUpdate && ((ref1 = ThreadStats.pageCountEl) != null ? ref1.textContent : void 0) !== '1') { + if (((ref1 = ThreadStats.pageCountEl) != null ? ref1.textContent : void 0) !== '1') { return ThreadStats.fetchPage(); } }, @@ -11994,14 +11874,14 @@ thread = ThreadStats.thread, postCountEl = ThreadStats.postCountEl, fileCountEl = ThreadStats.fileCountEl, ipCountEl = ThreadStats.ipCountEl; postCountEl.textContent = postCount; fileCountEl.textContent = fileCount; - if ((ipCount != null) && Conf["IP Count in Stats"]) { + if ((ipCount != null) && ipCountEl) { ipCountEl.textContent = ipCount; } (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"]) { + if (!ThreadStats.pageCountEl) { return; } clearTimeout(ThreadStats.timeout); @@ -12014,28 +11894,37 @@ return $.ajax("//a.4cdn.org/" + ThreadStats.thread.board + "/threads.json", { onload: ThreadStats.onThreadsLoad }, { - whenModified: true + whenModified: 'ThreadStats' }); }, onThreadsLoad: function() { var k, len1, len2, page, q, ref, ref1, thread; - if (!(Conf["Page Count in Stats"] && this.status === 200)) { - return; - } - ref = this.response; - for (k = 0, len1 = ref.length; k < len1; k++) { - page = ref[k]; - ref1 = page.threads; - for (q = 0, len2 = ref1.length; q < len2; q++) { - thread = ref1[q]; - if (!(thread.no === ThreadStats.thread.ID)) { - continue; + if (this.status === 200) { + ref = this.response; + for (k = 0, len1 = ref.length; k < len1; k++) { + page = ref[k]; + ref1 = page.threads; + for (q = 0, len2 = ref1.length; q < len2; q++) { + thread = ref1[q]; + if (!(thread.no === ThreadStats.thread.ID)) { + continue; + } + ThreadStats.pageCountEl.textContent = page.page; + (page.page === this.response.length ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning'); + ThreadStats.lastPageUpdate = new Date(thread.last_modified * $.SECOND); + ThreadStats.retry(); + return; } - ThreadStats.pageCountEl.textContent = page.page; - (page.page === this.response.length ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning'); - ThreadStats.lastPageUpdate = new Date(thread.last_modified * $.SECOND); - return; } + } else if (this.status === 304) { + return ThreadStats.retry(); + } + }, + retry: function() { + var ref; + if (ThreadStats.lastPost > ThreadStats.lastPageUpdate && ((ref = ThreadStats.pageCountEl) != null ? ref.textContent : void 0) !== '1') { + clearTimeout(ThreadStats.timeout); + return ThreadStats.timeout = setTimeout(ThreadStats.fetchPage, 5 * $.SECOND); } } }; @@ -12330,7 +12219,7 @@ onloadend: ThreadUpdater.cb.load, timeout: $.MINUTE }, { - whenModified: true + whenModified: 'ThreadUpdater' }); }, updateThreadStatus: function(type, status) { @@ -12488,7 +12377,7 @@ this.unreaddb = Unread.db || new DataBoard('lastReadPosts'); $.on(d, 'QRPostSuccessful', this.cb.post); $.on(sc, 'click', this.toggleWatcher); - $.on(this.refreshButton, 'click', this.fetchAllStatus); + $.on(this.refreshButton, 'click', this.buttonFetchAll); $.on(this.closeButton, 'click', this.toggleWatcher); $.on(d, '4chanXInitFinished', this.ready); switch (g.VIEW) { @@ -12568,7 +12457,7 @@ if (ThreadWatcher.isWatched(this.thread)) { $.addClass(this.nodes.root, 'watched'); } - return $.on(this.nodes.thumb.parentNode, 'click', (function(_this) { + $.on(this.nodes.thumb.parentNode, 'click', (function(_this) { return function(e) { if (!(e.button === 0 && e.altKey)) { return; @@ -12577,6 +12466,11 @@ return e.preventDefault(); }; })(this)); + return $.on(this.nodes.thumb.parentNode, 'mousedown', function(e) { + if (e.button === 0 && e.altKey) { + return e.preventDefault(); + } + }); }, ready: function() { $.off(d, '4chanXInitFinished', ThreadWatcher.ready); @@ -12669,13 +12563,20 @@ data = ref[threadID]; if (!(data != null ? data.isDead : void 0) && !(threadID in g.BOARD.threads)) { if (Conf['Auto Prune'] || !(data && typeof data === 'object')) { - ThreadWatcher.db["delete"]({ + db["delete"]({ boardID: boardID, threadID: threadID }); } else { + if (Conf['Show Unread Count']) { + ThreadWatcher.fetchStatus({ + boardID: boardID, + threadID: threadID, + data: data + }); + } data.isDead = true; - ThreadWatcher.db.set({ + db.set({ boardID: boardID, threadID: threadID, val: data @@ -12697,9 +12598,24 @@ return ThreadWatcher.add(thread); } }, - fetchCount: { - fetched: 0, - fetching: 0 + requests: [], + fetched: 0, + clearRequests: function() { + ThreadWatcher.requests = []; + ThreadWatcher.fetched = 0; + ThreadWatcher.status.textContent = ''; + return $.rmClass(ThreadWatcher.refreshButton, 'fa-spin'); + }, + abort: function() { + var k, len1, ref, req; + ref = ThreadWatcher.requests; + for (k = 0, len1 = ref.length; k < len1; k++) { + req = ref[k]; + if (req.readyState !== 4) { + req.abort(); + } + } + return ThreadWatcher.clearRequests(); }, fetchAuto: function() { var db, interval, now; @@ -12717,6 +12633,13 @@ } return ThreadWatcher.timeout = setTimeout(ThreadWatcher.fetchAuto, interval); }, + buttonFetchAll: function() { + if (ThreadWatcher.requests.length) { + return ThreadWatcher.abort(); + } else { + return ThreadWatcher.fetchAllStatus(); + } + }, fetchAllStatus: function() { var k, len1, ref, thread, threads; ThreadWatcher.db.forceSync(); @@ -12733,37 +12656,34 @@ } }, fetchStatus: function(thread, force) { - var boardID, data, fetchCount, threadID; + var boardID, data, req, threadID; boardID = thread.boardID, threadID = thread.threadID, data = thread.data; if (data.isDead && !force) { return; } - fetchCount = ThreadWatcher.fetchCount; - if (fetchCount.fetching === 0) { + if (ThreadWatcher.requests.length === 0) { ThreadWatcher.status.textContent = '...'; $.addClass(ThreadWatcher.refreshButton, 'fa-spin'); } - fetchCount.fetching++; - return $.ajax("//a.4cdn.org/" + boardID + "/thread/" + threadID + ".json", { + req = $.ajax("//a.4cdn.org/" + boardID + "/thread/" + threadID + ".json", { onloadend: function() { return ThreadWatcher.parseStatus.call(this, thread); - } + }, + timeout: $.MINUTE + }, { + whenModified: force ? false : 'ThreadWatcher' }); + return ThreadWatcher.requests.push(req); }, parseStatus: function(arg) { - var boardID, data, fetchCount, isDead, k, lastReadPost, len1, match, postObj, quotingYou, ref, ref1, regexp, status, threadID, unread; + var boardID, data, isDead, k, lastReadPost, len1, match, postObj, quotesYou, quotingYou, ref, ref1, regexp, threadID, unread; boardID = arg.boardID, threadID = arg.threadID, data = arg.data; - fetchCount = ThreadWatcher.fetchCount; - fetchCount.fetched++; - if (fetchCount.fetched === fetchCount.fetching) { - fetchCount.fetched = 0; - fetchCount.fetching = 0; - status = ''; - $.rmClass(ThreadWatcher.refreshButton, 'fa-spin'); + ThreadWatcher.fetched++; + if (ThreadWatcher.fetched === ThreadWatcher.requests.length) { + ThreadWatcher.clearRequests(); } else { - status = (Math.round(fetchCount.fetched / fetchCount.fetching * 100)) + "%"; + ThreadWatcher.status.textContent = (Math.round(ThreadWatcher.fetched / ThreadWatcher.requests.length * 100)) + "%"; } - ThreadWatcher.status.textContent = status; if (this.status === 200 && this.response) { isDead = !!this.response.posts[0].archived; if (isDead && Conf['Auto Prune']) { @@ -12797,17 +12717,21 @@ if (!(QR.db && postObj.com)) { continue; } - regexp = /]*\bhref="(?:\/([^\/]+)\/thread\/(\d+))?(?:#p(\d+))?"/g; + quotesYou = false; + regexp = /]*\bhref="(?:\/([^\/]+)\/thread\/)?(\d+)?(?:#p(\d+))?"/g; while (match = regexp.exec(postObj.com)) { if (QR.db.get({ boardID: match[1] || boardID, threadID: match[2] || threadID, postID: match[3] || match[2] || threadID })) { - quotingYou++; - continue; + quotesYou = true; + break; } } + if (quotesYou && !Filter.isHidden(Build.parseJSON(postObj, boardID))) { + quotingYou++; + } } if (isDead !== data.isDead || unread !== data.unread || quotingYou !== data.quotingYou) { data.isDead = isDead; @@ -12828,6 +12752,8 @@ }); } else { data.isDead = true; + delete data.unread; + delete data.quotingYou; ThreadWatcher.db.set({ boardID: boardID, threadID: threadID, @@ -13343,7 +13269,7 @@ return; } notif = new Notification(post.info.nameBlock + " replied to you", { - body: post.info[Conf['Remove Spoilers'] || Conf['Reveal Spoilers'] ? 'comment' : 'commentSpoilered'], + body: post.info.commentDisplay, icon: Favicon.logo }); notif.onclick = function() { @@ -13377,6 +13303,9 @@ }, read: $.debounce(100, function(e) { var ID, count, data, height, ref, ref1, root; + if (!Unread.posts.size && Unread.readCount !== Unread.thread.posts.keys.length) { + Unread.saveLastReadPost(); + } if (d.hidden || !Unread.posts.size) { return; } @@ -13527,7 +13456,7 @@ } return Redirect.data = o; }, - archives: [{"uid":0,"name":"Moe","domain":"archive.moe","http":false,"https":true,"software":"foolfuuka","boards":["a","biz","c","co","diy","fit","gd","gif","h","i","int","jp","k","m","mlp","out","po","qa","r9k","s4s","sci","tg","tv","u","v","vg","vp","vr","wsg"],"files":["a","biz","c","co","diy","fit","gd","h","i","jp","k","m","mlp","po","qa","r9k","s4s","sci","tg","u","v","vg","vp","vr","wsg"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":4,"name":"Nyafuu Archive","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"archive.loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["c","d","e","i","lgbt","t","u","w","wg"],"files":["c","d","e","i","lgbt","t","u","w","wg"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":false,"https":true,"software":"fuuka","boards":["cgl","g","mu","qa","w"],"files":["cgl","g","mu","qa","w"]},{"uid":10,"name":"warosu","domain":"warosu.org","http":false,"https":true,"software":"fuuka","boards":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"],"files":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"]},{"uid":15,"name":"fgts","domain":"fgts.jp","http":true,"https":true,"software":"foolfuuka","boards":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"],"files":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"]},{"uid":22,"name":"not4plebs","domain":"totally.not4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["sp"],"files":["sp"]},{"uid":23,"name":"DesuStorage","domain":"archive.desustorage.org","http":false,"https":true,"software":"foolfuuka","boards":["mlp","qa"],"files":["mlp","qa"]}], + archives: [{"uid":0,"name":"Moe","domain":"archive.moe","http":false,"https":true,"software":"foolfuuka","boards":["a","an","biz","c","co","diy","fit","gd","gif","h","i","int","jp","k","m","mlp","out","po","qa","r9k","s4s","sci","tg","tv","u","v","vg","vp","vr","wsg"],"files":["a","biz","c","co","diy","fit","gd","h","i","jp","k","m","mlp","po","qa","r9k","s4s","sci","tg","u","v","vg","vp","vr","wsg"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":4,"name":"Nyafuu Archive","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"archive.loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["c","d","e","i","lgbt","t","u"],"files":["c","d","e","i","lgbt","t","u"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":false,"https":true,"software":"fuuka","boards":["cgl","g","mu","qa","w"],"files":["cgl","g","mu","qa","w"]},{"uid":10,"name":"warosu","domain":"warosu.org","http":false,"https":true,"software":"fuuka","boards":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"],"files":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"]},{"uid":15,"name":"fgts","domain":"fgts.jp","http":true,"https":true,"software":"foolfuuka","boards":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"],"files":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"]},{"uid":22,"name":"not4plebs","domain":"totally.not4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["sp"],"files":["sp"]},{"uid":23,"name":"DesuStorage","domain":"archive.desustorage.org","http":false,"https":true,"software":"foolfuuka","boards":["mlp","qa"],"files":["mlp","qa"]}], to: function(dest, data) { var archive; archive = (dest === 'search' || dest === 'board' ? Redirect.data.thread : Redirect.data[dest])[data.boardID]; @@ -14364,22 +14293,22 @@ formatters: { t: function() { return { - innerHTML: E(this.file.URL.match(/[^/]*$/)[0]) + innerHTML: E(this.file.url.match(/[^/]*$/)[0]) }; }, T: function() { return { - innerHTML: "" + FileInfo.formatters.t.call(this).innerHTML + "" + innerHTML: "" + FileInfo.formatters.t.call(this).innerHTML + "" }; }, l: function() { return { - innerHTML: "" + FileInfo.formatters.n.call(this).innerHTML + "" + innerHTML: "" + FileInfo.formatters.n.call(this).innerHTML + "" }; }, L: function() { return { - innerHTML: "" + FileInfo.formatters.N.call(this).innerHTML + "" + innerHTML: "" + FileInfo.formatters.N.call(this).innerHTML + "" }; }, n: function() { @@ -14431,6 +14360,11 @@ innerHTML: E(this.file.dimensions || "PDF") }; }, + g: function() { + return { + innerHTML: (this.file.tag ? ", " + E(this.file.tag) : "") + }; + }, '%': function() { return { innerHTML: "%" @@ -14492,21 +14426,25 @@ }); }, code: function() { - var i, k, len1, pre, ref; if (this.isClone) { return; } - ref = $$('.prettyprint', this.nodes.comment); - for (i = k = 0, len1 = ref.length; k < len1; i = ++k) { - pre = ref[i]; - if (!$.hasClass(pre, 'prettyprinted')) { - $.event('prettyprint', { - ID: this.fullID, - i: i, - html: pre.innerHTML - }, window); - } - } + return $.ready((function(_this) { + return function() { + var i, k, len1, pre, ref; + ref = $$('.prettyprint', _this.nodes.comment); + for (i = k = 0, len1 = ref.length; k < len1; i = ++k) { + pre = ref[i]; + if (!$.hasClass(pre, 'prettyprinted')) { + $.event('prettyprint', { + ID: _this.fullID, + i: i, + html: pre.innerHTML + }, window); + } + } + }; + })(this)); }, math: function() { if ((this.isClone && doc.contains(this.origin.nodes.root)) || !$('.math', this.nodes.comment)) { @@ -14945,6 +14883,8 @@ return 'Enter'; case 27: return 'Esc'; + case 32: + return 'Space'; case 37: return 'Left'; case 38: @@ -14953,9 +14893,15 @@ return 'Right'; case 40: return 'Down'; + case 188: + return 'Comma'; + case 190: + return 'Period'; default: if ((48 <= kc && kc <= 57) || (65 <= kc && kc <= 90)) { return String.fromCharCode(kc).toLowerCase(); + } else if ((96 <= kc && kc <= 105)) { + return String.fromCharCode(kc - 48).toLowerCase(); } else { return null; } @@ -15315,36 +15261,53 @@ }; Report = { + css: "noscript > div, noscript > div > div {\n" + +" height: 545px !important;\n" + +"}\n" + +"noscript > div > div > div:first-child, noscript iframe {\n" + +" height: 423px !important;\n" + +"}\n" + +":root:not(.js-enabled) #g-recaptcha {\n" + +" height: auto;\n" + +"}", init: function() { var match; if (!(/\bmode=report\b/.test(location.search) && (match = location.search.match(/\bno=(\d+)/)))) { return; } + Captcha.language.fixPage(); this.postID = +match[1]; return $.ready(this.ready); }, ready: function() { - new MutationObserver(Report.resize).observe(d.body, { - childList: true, - attributes: true, - subtree: true - }); + $.addStyle(Report.css); if (Conf['Archive Report']) { - return Report.archive(); + Report.archive(); + } + if ($.hasClass(doc, 'js-enabled')) { + return new MutationObserver(function() { + return Report.fit('.gc-bubbleDefault'); + }).observe(d.body, { + childList: true, + attributes: true, + subtree: true + }); + } else { + return Report.fit('body'); } }, - resize: function() { - var bubble, dy; - if (!(bubble = $('.gc-bubbleDefault'))) { + fit: function(selector) { + var dy, el; + if (!(el = $(selector, doc))) { return; } - dy = bubble.getBoundingClientRect().bottom - doc.clientHeight; + dy = el.getBoundingClientRect().bottom - doc.clientHeight + 8; if (dy > 0) { return window.resizeBy(0, dy); } }, archive: function() { - var link, message, url; + var link, message, types, url; Redirect.init(); if (!(url = Redirect.to('report', { boardID: g.BOARD.ID, @@ -15353,21 +15316,29 @@ return; } if ((message = $('h3')) && /Report submitted!/.test(message.textContent)) { - $.globalEval('self.close = function(){};'); - window.resizeTo(685, 320); - location.replace(url); + if (location.hash === '#redirect') { + $.globalEval('self.close = function(){};'); + window.resizeBy(0, 350 - doc.clientHeight); + location.replace(url); + } return; } link = $.el('a', { href: url, - textContent: 'Report to fgts' + textContent: 'Report to archive' }); $.on(link, 'click', function(e) { if (!(e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0)) { - return window.resizeTo(685, 320); + return window.resizeBy(0, 350 - doc.clientHeight); } }); - return $.add(d.body, [$.tn(' ['), link, $.tn(']')]); + $.add(d.body, [$.tn(' ['), link, $.tn(']')]); + if (types = $.id('reportTypes')) { + return $.on(types, 'change', function(e) { + var ref; + return $('form').action = (ref = e.target.value) === 'illegal' || ref === 'spam' ? '#redirect' : ''; + }); + } } }; @@ -15603,6 +15574,9 @@ div = $.el('div', { innerHTML: ": " + E(description) + "" }); + if ((typeof chrome !== "undefined" && chrome !== null) && key === 'Remember QR Size') { + div.hidden = true; + } input = $('input', div); $.on(input, 'change', function() { this.parentNode.parentNode.dataset.checked = this.checked; @@ -15869,14 +15843,14 @@ return; } $.extend(div, { - innerHTML: "
Filter is disabled.

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

    You can use these settings with each regular expression, separate them with semicolons:
  • Per boards, separate them with commas. It is global if not specified.
    For example: boards:a,jp;.
  • Filter OPs only along with their threads (\`only\`), replies only (\`no\`), or both (\`yes\`, this is default).
    For example: op:only;, op:no; or op:yes;.
  • Overrule the \`Show Stubs\` setting if specified: create a stub (\`yes\`) or not (\`no\`).
    For example: stub:yes; or stub:no;.
  • Highlight instead of hiding. You can specify a class name to use with a userstyle.
    For example: highlight; or highlight:wallpaper;.
  • Highlighted OPs will have their threads put on top of the board index by default.
    For example: top:yes; or top:no;.

Note: If you're using the native catalog rather than 4chan X's catalog, 4chan X's filters do not apply there.
The native catalog has its own separate filter list.

" + innerHTML: "
Filter is disabled.

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

    You can use these settings with each regular expression, separate them with semicolons:
  • Per boards, separate them with commas. It is global if not specified.
    For example: boards:a,jp;.
  • Filter OPs only along with their threads (\`only\`), replies only (\`no\`), or both (\`yes\`, this is default).
    For example: op:only;, op:no; or op:yes;.
  • Overrule the \`Show Stubs\` setting if specified: create a stub (\`yes\`) or not (\`no\`).
    For example: stub:yes; or stub:no;.
  • Highlight instead of hiding. You can specify a class name to use with a userstyle.
    For example: highlight; or highlight:wallpaper;.
  • Highlighted OPs will have their threads put on top of the board index by default.
    For example: top:yes; or top:no;.

Note: If you're using the native catalog rather than 4chan X's catalog, 4chan X's filters do not apply there.
The native catalog has its own separate filter list.

" }); return $('.warning', div).hidden = Conf['Filter']; }, sauce: function(section) { var ta; $.extend(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.
You can specify the applicable boards by appending ;boards:[board1],[board2].
You can specify the applicable file types by appending ;types:[extension1],[extension2].
    These parameters will be replaced by their corresponding values:
  • %TURL: Thumbnail URL.
  • %URL: Full image URL.
  • %IMG: Full image URL for GIF, JPG, and PNG; thumbnail URL for other types.
  • %MD5: MD5 hash.
  • %name: Original file name.
  • %board: Current board.
  • %%, %semi: Literal % and ;.
" + innerHTML: "
Sauce is disabled.
Lines starting with a # will be ignored.
You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards by appending ;boards:[board1],[board2].
You can specify the applicable file types by appending ;types:[extension1],[extension2].
You can open links with scripts and popups disabled by appending ;sandbox.
    These parameters will be replaced by their corresponding values:
  • %TURL: Thumbnail URL.
  • %URL: Full image URL.
  • %IMG: Full image URL for GIF, JPG, and PNG; thumbnail URL for other types.
  • %MD5: MD5 hash.
  • %name: Original file name.
  • %board: Current board.
  • %%, %semi: Literal % and ;.
" }); $('.warning', section).hidden = Conf['Sauce']; ta = $('textarea', section); @@ -15888,7 +15862,7 @@ advanced: function(section) { var aa, applyCSS, archBoards, boardID, boardOptions, boardSelect, boards, customCSS, files, i, input, inputs, interval, item, items, k, len1, len2, len3, len4, len5, len6, len7, name, o, q, ref, ref1, ref2, ref3, ref4, ref5, ref6, row, rows, software, ta, table, u, w, warning, withCredentials, y, z; $.extend(section, { - innerHTML: "
Archiver
404 Redirect is disabled.
Thread redirectionPost fetchingFile redirection
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
Archive link: g-archive
Internal archive link: g-expired
Title link: g-title
Board link (Replace with title when on that board): g-replace
Full text link: g-full
Custom text link: g-text:"Install Gentoo"
Index-only link: g-index
Catalog-only link: g-catalog
External link: external-text:"Google","http://www.google.com"
Combinations are possible: g-index-text:"Technology Index"
Full board list toggle: toggle-all

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

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

    You can use these settings with each item, separate them with semicolons:
  • Possible items are: name, options (or equivalently email), subject and password.
  • Wrap values of items with quotes, like this: options:"sage".
  • Force values as defaults with the always keyword, for example: options:"sage";always.
  • Select specific boards for an item, separated with commas, for example: options:"sage";boards:jp;always.
Unread Favicon is disabled.
Thread Updater is disabled.
Interval: seconds
Custom Cooldown Time
Seconds:
" + innerHTML: "
Archiver
404 Redirect is disabled.
Thread redirectionPost fetchingFile redirection
Captcha Language
Choose from list of language codes. Leave blank to autoselect.
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
Archive link: g-archive
Internal archive link: g-expired
Title link: g-title
Board link (Replace with title when on that board): g-replace
Full text link: g-full
Custom text link: g-text:"Install Gentoo"
Index-only link: g-index
Catalog-only link: g-catalog
External link: external-text:"Google","http://www.google.com"
Combinations are possible: g-index-text:"Technology Index"
Full board list toggle: toggle-all

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

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

    You can use these settings with each item, separate them with semicolons:
  • Possible items are: name, options (or equivalently email), subject and password.
  • Wrap values of items with quotes, like this: options:"sage".
  • Force values as defaults with the always keyword, for example: options:"sage";always.
  • Select specific boards for an item, separated with commas, for example: options:"sage";boards:jp;always.
Unread Favicon is disabled.
Thread Updater is disabled.
Interval: seconds
Custom Cooldown Time
Seconds:
" }); ref = $$('.warning', section); for (k = 0, len1 = ref.length; k < len1; k++) { @@ -15897,7 +15871,7 @@ } items = {}; inputs = {}; - ref1 = ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss', 'customCooldown']; + ref1 = ['captchaLanguage', 'boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss', 'customCooldown']; for (q = 0, len2 = ref1.length; q < len2; q++) { name = ref1[q]; input = $("[name='" + name + "']", section); @@ -15910,7 +15884,9 @@ $.on(input, 'change', Settings[name]); } else { $.on(input, 'input', $.cb.value); - $.on(input, 'input', Settings[name]); + if (name in Settings) { + $.on(input, 'input', Settings[name]); + } } } ta = $('.personafield', section); @@ -15924,10 +15900,9 @@ val = items[key]; input = inputs[key]; input.value = val; - if (key === 'usercss' || key === 'customCooldown') { - continue; + if (key in Settings && key !== 'usercss') { + Settings[key].call(input); } - Settings[key].call(input); } }); interval = $('input[name="Interval"]', section); @@ -16078,13 +16053,15 @@ data = { isReply: true, file: { - URL: '//i.4cdn.org/g/1334437723720.jpg', + url: '//i.4cdn.org/g/1334437723720.jpg', name: 'd9bb2efc98dd0df141a94399ff5880b7.jpg', size: '276 KB', sizeInBytes: 276 * 1024, dimensions: '1280x720', isImage: true, - isSpoiler: true + isVideo: false, + isSpoiler: true, + tag: 'Loop' } }; return FileInfo.format(this.value, data, this.nextElementSibling); @@ -16164,19 +16141,31 @@ if (location.hostname === 'www.google.com') { if (location.pathname === '/recaptcha/api/fallback') { $.ready(function() { - return Captcha.noscript.initFrame(); - }); - } else { - $.get('Captcha Fixes', true, function(arg) { - var enabled; - enabled = arg['Captcha Fixes']; - if (enabled) { - return $.ready(function() { - return Captcha.fixes.init(); - }); - } + return Captcha.v2.initFrame(); }); } + $.get('Captcha Fixes', true, function(arg) { + var enabled; + enabled = arg['Captcha Fixes']; + if (enabled) { + return $.ready(function() { + return Captcha.fixes.init(); + }); + } + }); + return; + } + if (location.hostname === 'www.4chan.org') { + $.onExists(d.documentElement, 'body', false, function() { + return $.addStyle(Main.cssWWW); + }); + Conf = { + 'captchaLanguage': Config.captchaLanguage + }; + $.get(Conf, function(items) { + $.extend(Conf, items); + return Captcha.language.fixPage(); + }); return; } g.threads = new SimpleDict(); @@ -16246,7 +16235,10 @@ }); }, initFeatures: function() { - var err, feature, k, len1, name, pathname, ref, ref1; + var err, feature, k, len1, name, pathname, ref, ref1, ref2; + if ((ref = location.hostname) === 'boards.4chan.org' || ref === 'sys.4chan.org') { + $.globalEval('document.documentElement.classList.add("js-enabled");'); + } switch (location.hostname) { case 'a.4cdn.org': return; @@ -16260,8 +16252,8 @@ $.asap((function() { return d.readyState !== 'loading'; }), function() { - var URL, pathname, ref, video; - if (Conf['404 Redirect'] && ((ref = d.title) === '4chan - Temporarily Offline' || ref === '4chan - 404 Not Found')) { + var URL, pathname, ref1, video; + if (Conf['404 Redirect'] && ((ref1 = d.title) === '4chan - Temporarily Offline' || ref1 === '4chan - 404 Not Found')) { Redirect.init(); pathname = location.pathname.split('/'); URL = Redirect.to('file', { @@ -16290,9 +16282,9 @@ history.replaceState(null, '', pathname.slice(0, 4).join('/') + location.hash); } } - ref = Main.features; - for (k = 0, len1 = ref.length; k < len1; k++) { - ref1 = ref[k], name = ref1[0], feature = ref1[1]; + ref1 = Main.features; + for (k = 0, len1 = ref1.length; k < len1; k++) { + ref2 = ref1[k], name = ref2[0], feature = ref2[1]; try { feature.init(); } catch (_error) { @@ -17145,6 +17137,15 @@ " 0% {transform:rotate(0deg);}\n" + " 100% {transform:rotate(359deg);}\n" + "}\n" + +"noscript > div, noscript > div > div {\n" + +" height: 545px !important;\n" + +"}\n" + +"noscript > div > div > div:first-child, noscript iframe {\n" + +" height: 423px !important;\n" + +"}\n" + +":root:not(.js-enabled) #g-recaptcha {\n" + +" height: auto;\n" + +"}\n" + "/* General */\n" + ".dialog {\n" + " border: 1px solid;\n" + @@ -17251,10 +17252,8 @@ " /* party hats */\n" + " pointer-events: none;\n" + "}\n" + -"marquee,\n" + -".postMessage marquee + br,\n" + -".postMessage marquee + br + br {\n" + -" display: none;\n" + +":root:not(.js-enabled) #postForm {\n" + +" display: table;\n" + "}\n" + "/* Anti-autoplay */\n" + "audio.controls-added {\n" + @@ -17310,6 +17309,9 @@ "#thread-watcher {\n" + " z-index: 10;\n" + "}\n" + +":root.fixed:not(.gallery-open) #header-bar:not(.autohide) {\n" + +" z-index: 5;\n" + +"}\n" + "/* Header */\n" + ".fixed.top-header body {\n" + " padding-top: 2em;\n" + @@ -17674,6 +17676,9 @@ ".section-filter textarea {\n" + " height: 500px;\n" + "}\n" + +".section-filter a, .section-advanced a {\n" + +" text-decoration: underline;\n" + +"}\n" + ".section-sauce textarea {\n" + " height: 350px;\n" + "}\n" + @@ -17997,12 +18002,15 @@ " border: none;\n" + " box-shadow: none;\n" + "}\n" + -":root.float #thread-stats > .move > span {\n" + +":root.float #thread-stats > .move > :not(#page-count) {\n" + " pointer-events: none;\n" + "}\n" + ":root.float #thread-stats {\n" + " padding: 0px 3px;\n" + "}\n" + +"#page-count {\n" + +" cursor: pointer;\n" + +"}\n" + "/* Quote */\n" + ".catalog-thread > .comment > span.quote, #arc-list span.quote {\n" + " color: #789922;\n" + @@ -18256,9 +18264,7 @@ " display: none !important;\n" + "}\n" + "#qr select,\n" + -"#url-button,\n" + -"#custom-cooldown-button,\n" + -"#dump-button,\n" + +"#qr-filename-container > a,\n" + ".remove,\n" + ".captcha-img {\n" + " cursor: pointer;\n" + @@ -18270,6 +18276,10 @@ " min-width: 300px;\n" + " border-radius: 3px 3px 0 0;\n" + "}\n" + +"#qr > form {\n" + +" max-height: calc(100vh - 75px);\n" + +" overflow-y: auto;\n" + +"}\n" + "#qrtab {\n" + " border-radius: 3px 3px 0 0;\n" + "}\n" + @@ -18280,13 +18290,6 @@ " float: right;\n" + " padding: 0 3px;\n" + "}\n" + -"#qr .warning {\n" + -" min-height: 1.6em;\n" + -" vertical-align: middle;\n" + -" padding: 0 1px;\n" + -" border-width: 1px;\n" + -" border-style: solid;\n" + -"}\n" + ".qr-link-container {\n" + " text-align: center;\n" + "}\n" + @@ -18348,23 +18351,7 @@ " position: relative;\n" + " top: 2px;\n" + "}\n" + -"/* Noscript Recaptcha */\n" + -".captcha-img {\n" + -" margin: 0px;\n" + -" text-align: center;\n" + -" background-image: #fff;\n" + -" font-size: 0px;\n" + -" min-height: 59px;\n" + -" min-width: 302px;\n" + -"}\n" + -".captcha-input{\n" + -" width: 100%;\n" + -" margin: 1px 0 0;\n" + -"}\n" + -"#qr-captcha-iframe {\n" + -" display: none;\n" + -"}\n" + -"/* Recaptcha v2 */\n" + +"/* Captcha */\n" + "#qr .captcha-root {\n" + " position: relative;\n" + "}\n" + @@ -18388,6 +18375,21 @@ " display: block;\n" + " width: 100%;\n" + "}\n" + +"#qr-captcha-iframe {\n" + +" width: 302px;\n" + +" height: 423px;\n" + +" border: 0;\n" + +" display: block;\n" + +" margin: auto;\n" + +"}\n" + +".goog-bubble-content {\n" + +" max-width: 100vw;\n" + +" max-height: 100vh;\n" + +" overflow: auto;\n" + +"}\n" + +".goog-bubble-content iframe {\n" + +" position: static !important;\n" + +"}\n" + "/* File Input, Submit Button */\n" + "#file-n-submit {\n" + " display: -webkit-flex;\n" + @@ -18401,6 +18403,7 @@ " background: linear-gradient(to bottom, #F8F8F8, #DCDCDC) no-repeat;\n" + " border: 1px solid #BBB;\n" + " border-radius: 2px;\n" + +" height: 100%;\n" + "}\n" + "#qr-file-button {\n" + " width: 15%;\n" + @@ -18578,6 +18581,8 @@ "}\n" + ".textarea {\n" + " position: relative;\n" + +" display: -webkit-flex;\n" + +" display: flex;\n" + "}\n" + ":root.webkit .textarea {\n" + " margin-bottom: -2px;\n" + @@ -18591,6 +18596,9 @@ " right: 1px;\n" + " pointer-events: none;\n" + "}\n" + +"#char-count.warning {\n" + +" color: red;\n" + +"}\n" + "/* Menu */\n" + ".menu-button:not(.fa-bars) {\n" + " display: inline-block;\n" + @@ -18613,7 +18621,7 @@ " height: 15px;\n" + " text-align: center;\n" + "}\n" + -".menu-button + .container:not(:empty) {\n" + +".menu-button + .container :first-child {\n" + " margin-left: -5px;\n" + "}\n" + "#menu {\n" + @@ -18896,13 +18904,6 @@ "}\n" + ".gal-fit-height .gal-image img,\n" + ".gal-fit-height .gal-image video {\n" + -" /*\n" + -" Chrome doesn't support viewpoint units in calc()\n" + -" http://bugs.chromium.org/168840\n" + -" \"It looks like the original author of viewport units in WebKit is not coming back to fix this stuff.\"\n" + -" Well, fuck.\n" + -" */\n" + -" max-height: 95vh;\n" + " max-height: calc(100vh - 25px);\n" + "}\n" + ".gal-image iframe {\n" + @@ -19534,7 +19535,19 @@ " font-family: sans-serif !important;\n" + " text-shadow: 1px 1px 1px rgba(0,74,153,0.6);\n" + "}", - features: [['Polyfill', Polyfill], ['Redirect', Redirect], ['Header', Header], ['Catalog Links', CatalogLinks], ['Settings', Settings], ['Index Generator', Index], ['Disable Autoplay', AntiAutoplay], ['Announcement Hiding', PSAHiding], ['Fourchan thingies', Fourchan], ['Color User IDs', IDColor], ['Highlight by User ID', IDHighlight], ['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], ['Index Generator (Menu)', Index.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], ['Volume Control', Volume], ['WEBM Metadata', Metadata], ['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], ['Mark New IPs', MarkNewIPs], ['Index Navigation', Nav], ['Keybinds', Keybinds], ['Banner', Banner], ['Flash Features', Flash]] + cssWWW: "noscript > div, noscript > div > div {\n" + +" height: 545px !important;\n" + +"}\n" + +"noscript > div > div > div:first-child, noscript iframe {\n" + +" height: 423px !important;\n" + +"}\n" + +":root:not(.js-enabled) #g-recaptcha {\n" + +" height: auto;\n" + +"}\n" + +"#captcha-cnt {\n" + +" height: auto;\n" + +"}", + features: [['Polyfill', Polyfill], ['Captcha Language', Captcha.language], ['Redirect', Redirect], ['Header', Header], ['Catalog Links', CatalogLinks], ['Settings', Settings], ['Index Generator', Index], ['Disable Autoplay', AntiAutoplay], ['Announcement Hiding', PSAHiding], ['Fourchan thingies', Fourchan], ['Color User IDs', IDColor], ['Highlight by User ID', IDHighlight], ['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], ['Index Generator (Menu)', Index.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], ['Volume Control', Volume], ['WEBM Metadata', Metadata], ['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], ['Mark New IPs', MarkNewIPs], ['Index Navigation', Nav], ['Keybinds', Keybinds], ['Banner', Banner], ['Flash Features', Flash]] }; Main.init(); diff --git a/builds/4chan-X.crx b/builds/4chan-X.crx index 91bddfec4..55623a0c3 100644 Binary files a/builds/4chan-X.crx and b/builds/4chan-X.crx differ diff --git a/builds/4chan-X.meta.js b/builds/4chan-X.meta.js index b22044a36..f5f8087d6 100644 --- a/builds/4chan-X.meta.js +++ b/builds/4chan-X.meta.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan X -// @version 1.10.10.0 +// @version 1.11.0.6 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X @@ -10,9 +10,11 @@ // @match *://sys.4chan.org/* // @match *://a.4cdn.org/* // @match *://i.4cdn.org/* +// @match *://www.4chan.org/banned +// @match *://www.4chan.org/feedback // @match https://www.google.com/recaptcha/api2/anchor?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @match https://www.google.com/recaptcha/api2/frame?*&k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* -// @match *://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc +// @match *://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 2ff9c4946..b9841fb38 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -1,7 +1,7 @@ // Generated by CoffeeScript // ==UserScript== // @name 4chan X -// @version 1.10.10.0 +// @version 1.11.0.6 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X @@ -11,9 +11,11 @@ // @match *://sys.4chan.org/* // @match *://a.4cdn.org/* // @match *://i.4cdn.org/* +// @match *://www.4chan.org/banned +// @match *://www.4chan.org/feedback // @match https://www.google.com/recaptcha/api2/anchor?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @match https://www.google.com/recaptcha/api2/frame?*&k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* -// @match *://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc +// @match *://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue @@ -100,11 +102,8 @@ * 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 +* Font Awesome by Dave Gandy (http://fontawesome.io) +* license: http://fontawesome.io/license/ */ 'use strict'; @@ -238,7 +237,7 @@ 'Auto-load captcha': [false, 'Automatically load the captcha in the QR even if your post is empty.', 1], 'Post on Captcha Completion': [false, 'Submit the post immediately when the captcha is completed.', 1], 'Bottom QR Link': [true, 'Places a link on the bottom of threads to open the QR.', 1], - 'Captcha Fixes': [true, 'Make captcha more keyboard-navigable.'] + 'Captcha Fixes': [true, 'Make captcha easier to use, especially with the keyboard.'] }, 'Quote Links': { 'Quote Backlinks': [true, 'Add quote backlinks.'], @@ -270,6 +269,7 @@ 'Hide Thumbnails': [false], 'Fit Width': [true], 'Fit Height': [true], + 'Stretch to Fit': [false], 'Scroll to Post': [true], 'Slide Delay': [6.0] }, @@ -295,7 +295,7 @@ filesize: '', MD5: '' }, - sauces: "https://www.google.com/searchbyimage?image_url=%IMG\nhttp://iqdb.org/?url=%IMG\nhttp://eye.swfchan.com/search/?q=%name;types:swf\n#//tineye.com/search?url=%IMG\n#https://www.yandex.com/images/search?rpt=imageview&img_url=%IMG\n#//saucenao.com/search.php?url=%IMG\n#http://3d.iqdb.org/?url=%IMG\n#http://regex.info/exif.cgi?imgurl=%URL\n# uploaders:\n#//imgur.com/upload?url=%URL;types:gif,jpg,png,pdf;text:Upload to imgur\n# \"View Same\" in archives:\n#https://archive.moe/_/search/image/%MD5/;text:View same on archive.moe\n#https://archive.moe/%board/search/image/%MD5/;text:View same on archive.moe/%board/;boards:a,biz,c,co,diy,fit,gd,h,i,jp,k,m,mlp,po,qa,r9k,s4s,sci,tg,u,v,vg,vp,vr,wsg\n#https://rbt.asia/%board/image/%MD5;text:View same on RBT /%board/;boards:cgl,g,mu,qa,w", + sauces: "https://www.google.com/searchbyimage?image_url=%IMG\nhttp://iqdb.org/?url=%IMG\nhttp://eye.swfchan.com/search/?q=%name;types:swf;sandbox\n#//tineye.com/search?url=%IMG\n#https://www.yandex.com/images/search?rpt=imageview&img_url=%IMG\n#//saucenao.com/search.php?url=%IMG\n#http://3d.iqdb.org/?url=%IMG\n# tools:\n#http://regex.info/exif.cgi?imgurl=%URL\n#//imgops.com/%URL;types:gif,jpg,png\n# uploaders:\n#//imgur.com/upload?url=%URL;types:gif,jpg,png,pdf;text:Upload to imgur\n# \"View Same\" in archives:\n#https://archive.moe/_/search/image/%MD5/;text:View same on archive.moe\n#https://archive.moe/%board/search/image/%MD5/;text:View same on archive.moe/%board/;boards:a,biz,c,co,diy,fit,gd,h,i,jp,k,m,mlp,po,qa,r9k,s4s,sci,tg,u,v,vg,vp,vr,wsg\n#https://rbt.asia/%board/image/%MD5;text:View same on RBT /%board/;boards:cgl,g,mu,qa,w", FappeT: { werk: false }, @@ -325,9 +325,10 @@ QR: { 'QR.personas': "#options:\"sage\";boards:jp;always" }, + captchaLanguage: '', time: '%m/%d/%y(%a)%H:%M:%S', backlink: '>>%id', - fileInfo: '%l (%p%s, %r)', + fileInfo: '%l (%p%s, %r%g)', favicon: 'ferongr', usercss: '', hotkeys: { @@ -349,7 +350,7 @@ 'Expand images': ['e', 'Expand all images.'], 'Open Gallery': ['g', 'Opens the gallery.'], 'Pause': ['p', 'Pause/play videos in the gallery.'], - 'Slideshow': ['s', 'Toggle the gallery slideshow mode.'], + 'Slideshow': ['Ctrl+Right', 'Toggle the gallery slideshow mode.'], 'fappeTyme': ['f', 'Toggle Fappe Tyme.'], 'werkTyme': ['Shift+w', 'Toggle Werk Tyme.'], 'Front page': ['1', 'Jump to front page.'], @@ -399,7 +400,7 @@ doc = d.documentElement; g = { - VERSION: '1.10.10.0', + VERSION: '1.11.0.6', NAMESPACE: '4chan X.', boards: {} }; @@ -433,6 +434,10 @@ return html; }; + E.url = function(content) { + return "data:text/html;charset=utf-8," + (encodeURIComponent(content.innerHTML)); + }; + $ = function(selector, root) { if (root == null) { root = d.body; @@ -505,7 +510,7 @@ }); }; return function(url, options, extra) { - var err, form, r, type, upCallbacks, whenModified; + var err, form, r, ref, type, upCallbacks, whenModified; if (extra == null) { extra = {}; } @@ -523,11 +528,11 @@ return; } if (whenModified) { - if (url in lastModified) { - r.setRequestHeader('If-Modified-Since', lastModified[url]); + if (((ref = lastModified[whenModified]) != null ? ref[url] : void 0) != null) { + r.setRequestHeader('If-Modified-Since', lastModified[whenModified][url]); } $.on(r, 'load', function() { - return lastModified[url] = r.getResponseHeader('Last-Modified'); + return (lastModified[whenModified] || (lastModified[whenModified] = {}))[url] = r.getResponseHeader('Last-Modified'); }); } if (/\.json$/.test(url)) { @@ -1245,7 +1250,7 @@ this.info.nameBlock = Conf['Anonymize'] ? 'Anonymous' : this.nodes.nameBlock.textContent.trim(); if (subject = $('.subject', info)) { this.nodes.subject = subject; - this.info.subject = subject.textContent; + this.info.subject = subject.textContent || void 0; } if (name = $('.name', info)) { this.nodes.name = name; @@ -1296,28 +1301,33 @@ } Post.prototype.parseComment = function() { - var bq, k, len1, node, ref, spoilers; + var abbr, bq, commentDisplay, k, len1, len2, node, q, ref, spoilers; this.nodes.comment.normalize(); bq = this.nodes.comment.cloneNode(true); - ref = $$('.abbr, .exif, b, marquee', bq); + ref = $$('.abbr + br, .exif, b, .fortune', bq); for (k = 0, len1 = ref.length; k < len1; k++) { node = ref[k]; $.rm(node); } + if (abbr = $('.abbr', bq)) { + $.rm(abbr); + } this.info.comment = this.nodesToText(bq); - spoilers = $$('s', bq); - return this.info.commentSpoilered = (function() { - var len2, q; + if (abbr) { + this.info.comment = this.info.comment.replace(/\n\n$/, ''); + } + commentDisplay = this.info.comment; + if (!(Conf['Remove Spoilers'] || Conf['Reveal Spoilers'])) { + spoilers = $$('s', bq); if (spoilers.length) { for (q = 0, len2 = spoilers.length; q < len2; q++) { node = spoilers[q]; $.replace(node, $.tn('[spoiler]')); } - return this.nodesToText(bq); - } else { - return this.info.comment; + commentDisplay = this.nodesToText(bq); } - }).call(this); + } + return this.info.commentDisplay = commentDisplay.trim().replace(/\s+$/gm, ''); }; Post.prototype.nodesToText = function(bq) { @@ -1328,7 +1338,7 @@ while (node = nodes.snapshotItem(i++)) { text += node.data || '\n'; } - return text.trim().replace(/\s+$/gm, ''); + return text; }; Post.prototype.parseQuotes = function() { @@ -1357,7 +1367,7 @@ }; Post.prototype.parseFile = function() { - var fileEl, fileText, info, link, ref, ref1, size, thumb, unit; + var fileEl, fileText, info, link, m, ref, ref1, ref2, size, thumb, unit; if (!(fileEl = $('.file', this.nodes.post))) { return; } @@ -1371,12 +1381,13 @@ this.file = { text: fileText, link: link, - URL: link.href, + url: link.href, name: fileText.title || link.title || link.textContent, size: info[1], isImage: /(jpg|png|gif)$/i.test(link.href), isVideo: /webm$/i.test(link.href), - dimensions: (ref1 = info[0].match(/\d+x\d+/)) != null ? ref1[0] : void 0 + dimensions: (ref1 = info[0].match(/\d+x\d+/)) != null ? ref1[0] : void 0, + tag: (ref2 = info[0].match(/,[^,]*, ([a-z]+)\)/i)) != null ? ref2[1] : void 0 }; size = +this.file.size.match(/[\d.]+/)[0]; unit = ['B', 'KB', 'MB', 'GB'].indexOf(this.file.size.match(/\w+$/)[0]); @@ -1387,7 +1398,7 @@ if ((thumb = $('.fileThumb > [data-md5]', fileEl))) { return $.extend(this.file, { thumb: thumb, - thumbURL: location.protocol + "//i.4cdn.org/" + this.board + "/" + (link.href.match(/(\d+)\./)[1]) + "s.jpg", + thumbURL: (m = link.href.match(/\d+(?=\.\w+$)/)) ? location.protocol + "//i.4cdn.org/" + this.board + "/" + m[0] + "s.jpg" : void 0, MD5: thumb.dataset.md5, isSpoiler: $.hasClass(thumb.parentNode, 'imgspoiler') }); @@ -2046,7 +2057,7 @@ function Connection(target1, origin1, cb1) { this.target = target1; this.origin = origin1; - this.cb = cb1; + this.cb = cb1 != null ? cb1 : {}; this.onMessage = bind(this.onMessage, this); this.send = bind(this.send, this); $.on(window, 'message', this.onMessage); @@ -2257,42 +2268,50 @@ postID: this.postID, threadID: this.threadID, boardID: this.boardID, - name: data.name, + isReply: this.postID !== this.threadID + }; + o.info = { + subject: data.title, + email: data.email, + name: data.name || '', + tripcode: data.trip, capcode: (function() { switch (data.capcode) { case 'M': - return 'mod'; + return 'Mod'; case 'A': - return 'admin'; + return 'Admin'; case 'D': - return 'developer'; + return 'Developer'; } })(), - tripcode: data.trip, uniqueID: data.poster_hash, - email: data.email || '', - subject: data.title, flagCode: data.poster_country, - flagName: data.poster_country_name, - date: data.fourchan_date, + flag: data.poster_country_name, dateUTC: data.timestamp, - comment: comment + dateText: data.fourchan_date, + commentHTML: comment }; + if (o.info.capcode) { + delete o.info.uniqueID; + } if ((ref = data.media) != null ? ref.media_filename : void 0) { o.file = { name: data.media.media_filename, - timestamp: data.media.media_orig, - url: data.media.media_link || data.media.remote_media_link || ("//i.4cdn.org/" + this.boardID + "/" + (encodeURIComponent(data.media[this.boardID === 'f' ? 'media_filename' : 'media_orig']))), + url: data.media.media_link || data.media.remote_media_link || (location.protocol + "//i.4cdn.org/" + this.boardID + "/" + (encodeURIComponent(data.media[this.boardID === 'f' ? 'media_filename' : 'media_orig']))), 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 || ("//i.4cdn.org/" + this.boardID + "/" + data.media.preview_orig), + size: $.bytesToString(data.media.media_size), + thumbURL: data.media.thumb_link || (location.protocol + "//i.4cdn.org/" + this.boardID + "/" + data.media.preview_orig), theight: data.media.preview_h, twidth: data.media.preview_w, isSpoiler: data.media.spoiler === '1' }; - if (this.boardID === 'f') { + if (!/\.pdf$/.test(o.file.url)) { + o.file.dimensions = o.file.width + "x" + o.file.height; + } + if (this.boardID === 'f' && data.media.exif) { o.file.tag = JSON.parse(data.media.exif).Tag; } } @@ -2301,7 +2320,7 @@ post = new Post(Build.post(o), thread, board); post.kill(); if (post.file) { - post.file.thumbURL = o.file.turl; + post.file.thumbURL = o.file.thumbURL; } post.isFetchedQuote = true; Main.callbackNodes(Post, [post]); @@ -2350,26 +2369,7 @@ Polyfill = { init: function() { - this.notificationPermission(); - this.toBlob(); - return this.visibility(); - }, - notificationPermission: function() { - if (!window.Notification || 'permission' in Notification || !window.webkitNotifications) { - return; - } - return Object.defineProperty(Notification, 'permission', { - get: function() { - switch (webkitNotifications.checkPermission()) { - case 0: - return 'granted'; - case 1: - return 'default'; - case 2: - return 'denied'; - } - } - }); + return this.toBlob(); }, toBlob: function() { var base1; @@ -2385,26 +2385,6 @@ type: 'image/png' })); }); - }, - visibility: function() { - if ('visibilityState' in d) { - return; - } - Object.defineProperties(HTMLDocument.prototype, { - visibilityState: { - get: function() { - return this.webkitVisibilityState; - } - }, - hidden: { - get: function() { - return this.webkitHidden; - } - } - }); - return $.on(d, 'webkitvisibilitychange', function() { - return $.event('visibilitychange'); - }); } }; @@ -3610,7 +3590,7 @@ return Index.load(e, state); } }, { - whenModified: true + whenModified: 'Index' }); return $.addClass(Index.button, 'fa-spin'); }, @@ -4008,13 +3988,14 @@ if (text == null) { return text; } - return text.replace(/<[^>]*>/g, '').replace(/&(amp|#039|quot|lt|gt);/g, function(c) { + return text.replace(/<[^>]*>/g, '').replace(/&(amp|#039|quot|lt|gt|#44);/g, function(c) { return { '&': '&', ''': "'", '"': '"', '<': '<', - '>': '>' + '>': '>', + ',': ',' }[c]; }); }, @@ -4046,117 +4027,128 @@ return "/" + boardID + "/thread/" + threadID + "#p" + postID; } }, - postFromObject: function(data, boardID, suppressThumb) { + parseJSON: function(data, boardID) { var o; o = { postID: data.no, threadID: data.resto || data.no, boardID: boardID, - name: Build.unescape(data.name), - capcode: data.capcode, - tripcode: data.trip, - uniqueID: data.id, - email: Build.unescape(data.email), - subject: Build.unescape(data.sub), - flagCode: data.country, - flagName: Build.unescape(data.country_name), - date: data.now, - dateUTC: data.time, - comment: { - innerHTML: data.com || '' - }, + isReply: !!data.resto, isSticky: !!data.sticky, isClosed: !!data.closed, - isArchived: !!data.archived + isArchived: !!data.archived, + fileDeleted: !!data.filedeleted }; - if (data.filedeleted) { - o.file = { - isDeleted: true - }; - } else if (data.ext) { + o.info = { + subject: Build.unescape(data.sub), + email: Build.unescape(data.email), + name: Build.unescape(data.name) || '', + tripcode: data.trip, + uniqueID: data.id, + flagCode: data.country, + flag: Build.unescape(data.country_name), + dateUTC: data.time, + dateText: data.now, + commentHTML: { + innerHTML: data.com || '' + } + }; + if (data.capcode) { + o.info.capcode = data.capcode.replace(/_highlight$/, '').replace(/_/g, ' ').replace(/\b\w/g, function(c) { + return c.toUpperCase(); + }); + o.capcodeHighlight = /_highlight$/.test(data.capcode); + delete o.info.uniqueID; + } + if (data.ext) { o.file = { name: (Build.unescape(data.filename)) + data.ext, - timestamp: "" + data.tim + data.ext, - url: boardID === 'f' ? "//i.4cdn.org/" + boardID + "/" + (encodeURIComponent(data.filename)) + data.ext : "//i.4cdn.org/" + boardID + "/" + data.tim + data.ext, + url: boardID === 'f' ? location.protocol + "//i.4cdn.org/" + boardID + "/" + (encodeURIComponent(data.filename)) + data.ext : location.protocol + "//i.4cdn.org/" + boardID + "/" + data.tim + data.ext, height: data.h, width: data.w, MD5: data.md5, - size: data.fsize, - turl: "//i.4cdn.org/" + boardID + "/" + data.tim + "s.jpg", + size: $.bytesToString(data.fsize), + thumbURL: location.protocol + "//i.4cdn.org/" + boardID + "/" + data.tim + "s.jpg", theight: data.tn_h, twidth: data.tn_w, isSpoiler: !!data.spoiler, - isDeleted: false, tag: data.tag }; + if (!/\.pdf$/.test(o.file.url)) { + o.file.dimensions = o.file.width + "x" + o.file.height; + } } + return o; + }, + parseComment: function(o) { + var html; + html = o.info.commentHTML.innerHTML.replace(//gi, '\n').replace(/\n\nRolled [^<]*<\/b>/i, '').replace(/]*>/g, ''); + return o.info.comment = Build.unescape(html); + }, + postFromObject: function(data, boardID, suppressThumb) { + var o; + o = Build.parseJSON(data, boardID); return Build.post(o, suppressThumb); }, post: function(o, suppressThumb) { - - /* - This function contains code from 4chan-JS (https://github.com/4chan/4chan-JS). - @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE - */ - var boardID, capcode, capcodeDescription, capcodeLC, capcodeLong, capcodePlural, capcodeText, capcodeUC, comment, container, date, dateUTC, email, file, fileBlock, fileDims, fileSize, fileThumb, flagCode, flagName, gifIcon, href, isOP, k, len1, match, name, postClass, postID, postInfo, postLink, quote, quoteLink, ref, shortFilename, staticPath, subject, threadID, tripcode, uniqueID, wholePost; - 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, comment = o.comment, file = o.file; - name || (name = ''); - subject || (subject = ''); - isOP = postID === threadID; + var boardID, capcode, capcodeDescription, capcodeLC, capcodeLong, capcodePlural, capcodeUC, commentHTML, container, dateText, dateUTC, email, file, fileBlock, fileThumb, fileURL, flag, flagCode, gifIcon, href, k, len1, match, name, postClass, postID, postInfo, postLink, protocol, quote, quoteLink, ref, ref1, shortFilename, staticPath, subject, threadID, tripcode, uniqueID, wholePost; + postID = o.postID, threadID = o.threadID, boardID = o.boardID, file = o.file; + ref = o.info, subject = ref.subject, email = ref.email, name = ref.name, tripcode = ref.tripcode, capcode = ref.capcode, uniqueID = ref.uniqueID, flagCode = ref.flagCode, flag = ref.flag, dateUTC = ref.dateUTC, dateText = ref.dateText, commentHTML = ref.commentHTML; staticPath = Build.staticPath, gifIcon = Build.gifIcon; /* Post Info */ if (capcode) { - capcodeLC = capcode.split('_')[0]; - capcodeUC = capcodeLC[0].toUpperCase() + capcodeLC.slice(1); - capcodeText = capcodeUC; - capcodeLong = { - 'Admin': 'Administrator', - 'Mod': 'Moderator' - }[capcodeUC] || capcodeUC; - capcodePlural = capcodeLong + "s"; - capcodeDescription = "a 4chan " + capcodeLong; - if (capcode === 'admin_emeritus') { - capcodeText = 'Admin Emeritus'; + capcodeUC = capcode.split(' ')[0]; + capcodeLC = capcodeUC.toLowerCase(); + if (capcode === 'Admin Emeritus') { capcodePlural = 'the Administrator Emeritus'; capcodeDescription = "4chan's founding Administrator"; + } else { + capcodeLong = { + 'Admin': 'Administrator', + 'Mod': 'Moderator' + }[capcode] || capcode; + capcodePlural = capcodeLong + "s"; + capcodeDescription = "a 4chan " + capcodeLong; } } postLink = Build.postURL(boardID, threadID, postID); quoteLink = Build.sameThread(boardID, threadID) ? "javascript:quote('" + (+postID) + "');" : "/" + boardID + "/thread/" + threadID + "#q" + postID; postInfo = { - innerHTML: "
" + (isOP || boardID === "f" ? "" + E(subject) + " " : "") + "" + (email ? "" : "") + "" + E(name) + "" + (tripcode ? " " + E(tripcode) + "" : "") + (capcode ? " ## " + E(capcodeText) + "" : "") + (email ? "" : "") + (boardID === "f" && isOP || capcode ? "" : " ") + (capcode ? " \""" : "") + (uniqueID && !capcode ? " (ID: " + E(uniqueID) + ")" : "") + (flagCode ? " " : "") + " " + E(date) + " No." + E(postID) + "" + (o.isSticky ? " \"Sticky\"" : "") + (o.isClosed && !o.isArchived ? " \"Closed\"" : "") + (o.isArchived ? " \"Archived\"" : "") + (isOP && g.VIEW === "index" ? "   [Reply]" : "") + "
" + innerHTML: "
" + (!o.isReply || boardID === "f" || subject ? "" + E(subject || "") + " " : "") + "" + (email ? "" : "") + "" + E(name) + "" + (tripcode ? " " + E(tripcode) + "" : "") + (capcode ? " ## " + E(capcode) + "" : "") + (email ? "" : "") + (boardID === "f" && !o.isReply || capcode ? "" : " ") + (capcode ? " \""" : "") + (uniqueID && !capcode ? " (ID: " + E(uniqueID) + ")" : "") + (flagCode ? " " : "") + " " + E(dateText) + " No." + E(postID) + "" + (o.isSticky ? " \"Sticky\"" : "") + (o.isClosed && !o.isArchived ? " \"Closed\"" : "") + (o.isArchived ? " \"Archived\"" : "") + (!o.isReply && g.VIEW === "index" ? "   [Reply]" : "") + "
" }; /* File Info */ - if (file && !file.isDeleted) { + if (file) { + protocol = /^https?:(?=\/\/i\.4cdn\.org\/)/; + fileURL = file.url.replace(protocol, ''); shortFilename = Build.shortFilename(file.name); - fileSize = $.bytesToString(file.size); - fileDims = file.url.slice(-4) === '.pdf' ? 'PDF' : file.width + "x" + file.height; - fileThumb = file.isSpoiler ? Build.spoilerThumb(boardID) : file.turl; + fileThumb = file.isSpoiler ? Build.spoilerThumb(boardID) : file.thumbURL.replace(protocol, ''); } fileBlock = { - innerHTML: (file ? "
" + (file.isDeleted ? "\"File" : (boardID === "f" ? "
File: " + E(file.name) + "-(" + E(fileSize) + ", " + E(fileDims) + ", " + E(file.tag) + ")
" : "
File: " + (file.isSpoiler ? "Spoiler Image" : E(shortFilename)) + " (" + E(fileSize) + ", " + E(fileDims) + ")
")) + "
" : "") + innerHTML: (file ? "
" + (boardID === "f" ? "
File: " + E(file.name) + "-(" + E(file.size) + ", " + E(file.dimensions) + (file.tag ? ", " + E(file.tag) : "") + ")
" : "
File: " + (file.isSpoiler ? "Spoiler Image" : E(shortFilename)) + " (" + E(file.size) + ", " + E(file.dimensions || "PDF") + ")
") + "
" : (o.fileDeleted ? "
\"File
" : "")) }; /* Whole Post */ - postClass = isOP ? 'op' : 'reply'; + postClass = o.isReply ? 'reply' : 'op'; wholePost = { - innerHTML: (!isOP ? "
>>
" : "") + "
" + (isOP ? fileBlock.innerHTML + postInfo.innerHTML : postInfo.innerHTML + fileBlock.innerHTML) + "
" + comment.innerHTML + "
" + innerHTML: (o.isReply ? "
>>
" : "") + "
" + (o.isReply ? postInfo.innerHTML + fileBlock.innerHTML : fileBlock.innerHTML + postInfo.innerHTML) + "
" + commentHTML.innerHTML + "
" }; container = $.el('div', { className: "postContainer " + postClass + "Container", id: "pc" + postID }); $.extend(container, wholePost); - ref = $$('.quotelink', container); - for (k = 0, len1 = ref.length; k < len1; k++) { - quote = ref[k]; + ref1 = $$('.quotelink', container); + for (k = 0, len1 = ref1.length; k < len1; k++) { + quote = ref1[k]; href = quote.getAttribute('href'); if ((href[0] === '#') && !(Build.sameThread(boardID, threadID))) { quote.href = ("/" + boardID + "/thread/" + threadID) + href; } else if ((match = href.match(/^\/([^\/]+)\/thread\/(\d+)/)) && (Build.sameThread(match[1], match[2]))) { quote.href = href.match(/(#[^#]*)?$/)[0] || '#'; + } else if (/^\d+(#|$)/.test(href) && !(g.VIEW === 'thread' && g.BOARD.ID === boardID)) { + quote.href = "/" + boardID + "/thread/" + href; } } return container; @@ -4303,7 +4295,7 @@ threadExcerpt: function(thread) { var OP, excerpt, ref; OP = thread.OP; - excerpt = ("/" + thread.board + "/ - ") + (((ref = OP.info.subject) != null ? ref.trim() : void 0) || OP.info.comment.replace(/\n+/g, ' // ') || OP.info.nameBlock); + excerpt = ("/" + thread.board + "/ - ") + (((ref = OP.info.subject) != null ? ref.trim() : void 0) || OP.info.commentDisplay.replace(/\n+/g, ' // ') || OP.info.nameBlock); if (excerpt.length > 73) { return excerpt.slice(0, 70) + "..."; } @@ -4985,7 +4977,7 @@ Filter = { filters: {}, init: function() { - var boards, err, filter, hl, k, key, len1, line, op, ref, ref1, ref2, ref3, ref4, ref5, ref6, regexp, stub, top; + var boards, err, filter, hl, k, key, len1, line, op, ref, ref1, ref2, ref3, ref4, ref5, regexp, stub, top; if (!(((ref = g.VIEW) === 'index' || ref === 'thread') && Conf['Filter'])) { return; } @@ -5005,9 +4997,7 @@ } filter = line.replace(regexp[0], ''); boards = ((ref2 = filter.match(/boards:([^;]+)/)) != null ? ref2[1].toLowerCase() : void 0) || 'global'; - if (boards !== 'global' && (ref3 = g.BOARD.ID, indexOf.call(boards.split(','), ref3) < 0)) { - continue; - } + boards = boards === 'global' ? null : boards.split(','); if (key === 'uniqueID' || key === 'MD5') { regexp = regexp[1]; } else { @@ -5019,10 +5009,10 @@ continue; } } - op = ((ref4 = filter.match(/[^t]op:(yes|no|only)/)) != null ? ref4[1] : void 0) || 'yes'; + op = ((ref3 = filter.match(/[^t]op:(yes|no|only)/)) != null ? ref3[1] : void 0) || 'yes'; stub = (function() { - var ref5; - switch ((ref5 = filter.match(/stub:(yes|no)/)) != null ? ref5[1] : void 0) { + var ref4; + switch ((ref4 = filter.match(/stub:(yes|no)/)) != null ? ref4[1] : void 0) { case 'yes': return true; case 'no': @@ -5032,11 +5022,11 @@ } })(); if (hl = /highlight/.test(filter)) { - hl = ((ref5 = filter.match(/highlight:([\w-]+)/)) != null ? ref5[1] : void 0) || 'filter-highlight'; - top = ((ref6 = filter.match(/top:(yes|no)/)) != null ? ref6[1] : void 0) || 'yes'; + hl = ((ref4 = filter.match(/highlight:([\w-]+)/)) != null ? ref4[1] : void 0) || 'filter-highlight'; + top = ((ref5 = filter.match(/top:(yes|no)/)) != null ? ref5[1] : void 0) || 'yes'; top = top === 'yes'; } - this.filters[key].push(this.createFilter(regexp, op, stub, hl, top)); + this.filters[key].push(this.createFilter(regexp, boards, op, stub, hl, top)); } if (!this.filters[key].length) { delete this.filters[key]; @@ -5050,7 +5040,7 @@ cb: this.node }); }, - createFilter: function(regexp, op, stub, hl, top) { + createFilter: function(regexp, boards, op, stub, hl, top) { var settings, test; test = typeof regexp === 'string' ? function(value) { return regexp === value; @@ -5063,7 +5053,10 @@ "class": hl, top: top }; - return function(value, isReply) { + return function(value, boardID, isReply) { + if (boards && indexOf.call(boards, boardID) < 0) { + return false; + } if (isReply && op === 'only' || !isReply && op === 'no') { return false; } @@ -5083,7 +5076,7 @@ ref = Filter.filters[key]; for (k = 0, len1 = ref.length; k < len1; k++) { filter = ref[k]; - if (!(result = filter(value, this.isReply))) { + if (!(result = filter(value, this.board.ID, this.isReply))) { continue; } if (result.hide && !this.isFetchedQuote) { @@ -5107,6 +5100,23 @@ } } }, + isHidden: function(post) { + var filter, k, key, len1, ref, result, value; + for (key in Filter.filters) { + if ((value = Filter[key](post)) != null) { + ref = Filter.filters[key]; + for (k = 0, len1 = ref.length; k < len1; k++) { + filter = ref[k]; + if (result = filter(value, post.boardID, post.isReply)) { + if (result.hide) { + return true; + } + } + } + } + } + return false; + }, name: function(post) { return post.info.name; }, @@ -5120,10 +5130,11 @@ return post.info.capcode; }, subject: function(post) { - return post.info.subject || void 0; + return post.info.subject; }, comment: function(post) { - return post.info.comment; + var ref; + return (ref = post.info.comment) != null ? ref : Build.parseComment(post); }, flag: function(post) { return post.info.flag; @@ -6176,7 +6187,7 @@ }, mouseover: function(e) { var boardID, clone, k, len1, len2, origin, post, postID, posts, q, qp, quote, quoterID, ref, ref1, threadID; - if ($.hasClass(this, 'inlined')) { + if ($.hasClass(this, 'inlined') || !d.contains(this)) { return; } ref = Get.postDataFromLink(this), boardID = ref.boardID, threadID = ref.threadID, postID = ref.postID; @@ -6660,7 +6671,7 @@ QR = { mimeTypes: ['image/jpeg', 'image/png', 'image/gif', 'application/pdf', 'application/vnd.adobe.flash.movie', 'application/x-shockwave-flash', 'video/webm'], init: function() { - var noscript, sc; + var sc; if (!Conf['Quick Reply']) { return; } @@ -6669,9 +6680,7 @@ if (g.VIEW === 'archive') { return; } - $.globalEval('document.documentElement.dataset.jsEnabled = true;'); - noscript = Conf['Force Noscript Captcha'] || !doc.dataset.jsEnabled; - this.captcha = Captcha[noscript ? 'noscript' : 'v2']; + this.captcha = Captcha.v2; $.on(d, '4chanXInitFinished', this.initReady); Post.callbacks.push({ name: 'Quick Reply', @@ -6699,7 +6708,7 @@ } if (Conf['Hide Original Post Form']) { $.addClass(doc, 'hide-original-post-form'); - if (!doc.dataset.jsEnabled) { + if (!$.hasClass(doc, 'js-enabled')) { return $.onExists(doc, '#postForm noscript', true, $.rm); } } @@ -6819,26 +6828,31 @@ return QR.captcha.destroy(); }, focus: function() { - $.queueTask(function() { - var focus; - if (!$$('.goog-bubble-content > iframe').some(function(el) { - return el.getBoundingClientRect().top >= 0; - })) { - focus = d.activeElement && QR.nodes.el.contains(d.activeElement); - return $[focus ? 'addClass' : 'rmClass'](QR.nodes.el, 'focus'); + return $.queueTask(function() { + if (!QR.inBubble()) { + QR.hasFocus = d.activeElement && QR.nodes.el.contains(d.activeElement); + QR.nodes.el.classList.toggle('focus', QR.hasFocus); + } + if (QR.captcha.isEnabled && !QR.captcha.noscript) { + if (QR.inCaptcha()) { + QR.scrollY = window.scrollY; + return $.on(d, 'scroll', QR.scrollLock); + } else { + return $.off(d, 'scroll', QR.scrollLock); + } } }); - if (typeof chrome !== "undefined" && chrome !== null) { - if (d.activeElement && QR.nodes.el.contains(d.activeElement) && d.activeElement.nodeName === 'IFRAME') { - QR.scrollY = window.scrollY; - return $.on(d, 'scroll', QR.scrollLock); - } else { - return $.off(d, 'scroll', QR.scrollLock); - } - } + }, + inBubble: function() { + var ref; + return ref = d.activeElement, indexOf.call($$('.goog-bubble-content > iframe'), ref) >= 0; + }, + inCaptcha: function() { + var ref; + return (((ref = d.activeElement) != null ? ref.nodeName : void 0) === 'IFRAME' && QR.nodes.el.contains(d.activeElement)) || (QR.hasFocus && QR.inBubble()); }, scrollLock: function() { - if (d.activeElement && QR.nodes.el.contains(d.activeElement) && d.activeElement.nodeName === 'IFRAME') { + if (QR.inCaptcha()) { return window.scroll(window.scrollX, QR.scrollY); } else { return $.off(d, 'scroll', QR.scrollLock); @@ -7018,10 +7032,10 @@ characterCount: function() { var count, counter; counter = QR.nodes.charCount; - count = QR.nodes.com.textLength; + count = QR.nodes.com.value.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, '_').length; counter.textContent = count; counter.hidden = count < 1000; - return (count > 1500 ? $.addClass : $.rmClass)(counter, 'warning'); + return (count > 2000 ? $.addClass : $.rmClass)(counter, 'warning'); }, getFile: function() { var ref; @@ -7321,7 +7335,7 @@ event = node.nodeName === 'SELECT' ? 'change' : 'input'; $.on(nodes[name], event, save); } - if (Conf['Remember QR Size']) { + if ((typeof chrome === "undefined" || chrome === null) && Conf['Remember QR Size']) { $.get('QR Size', '', function(item) { return nodes.com.style.cssText = item['QR Size']; }); @@ -7604,15 +7618,17 @@ Captcha = {}; Captcha.fixes = { - selectors: { - image: '.rc-imageselect-target > .rc-imageselect-tile > img' - }, + imageKeys: '789456123uiojklm'.split('').concat(['Comma', 'Period']), + css: '.rc-imageselect-target > div:focus {\n outline: 2px solid #4a90e2;\n}\n.rc-button-default:focus {\n box-shadow: inset 0 0 0 2px #0063d6;\n}', + cssNoscript: '.fbc-payload-imageselect {\n position: relative;\n}\n.fbc-payload-imageselect > label {\n position: absolute;\n display: block;\n height: 93.3px;\n width: 93.3px;\n}\nlabel[data-row="0"] {top: 0px;}\nlabel[data-row="1"] {top: 93.3px;}\nlabel[data-row="2"] {top: 186.6px;}\nlabel[data-col="0"] {left: 0px;}\nlabel[data-col="1"] {left: 93.3px;}\nlabel[data-col="2"] {left: 186.6px;}', init: function() { switch (location.pathname.split('/')[3]) { case 'anchor': return this.initMain(); case 'frame': return this.initPopup(); + case 'fallback': + return this.initNoscript(); } }, initMain: function() { @@ -7630,7 +7646,7 @@ }); }, initPopup: function() { - $.addStyle(this.selectors.image + ":focus {outline: 2px solid #4a90e2;}"); + $.addStyle(this.css); this.fixImages(); new MutationObserver((function(_this) { return function() { @@ -7642,383 +7658,135 @@ }); return $.on(d, 'keydown', this.keybinds.bind(this)); }, - fixImages: function() { - var focus, img, k, len1, ref; - if (!(this.images = $$(this.selectors.image)).length) { + initNoscript: function() { + this.noscript = true; + this.images = $$('.fbc-payload-imageselect > input'); + if (!this.images.length) { return; } - focus = this.images[0].tabIndex !== 0; + $.addStyle(this.cssNoscript); + this.addLabels(); + $.on(d, 'keydown', this.keybinds.bind(this)); + return $.on($('.fbc-imageselect-challenge > form'), 'submit', this.checkForm.bind(this)); + }, + fixImages: function() { + var img, k, len1, ref; + this.images = $$('.rc-imageselect-target > div'); ref = this.images; for (k = 0, len1 = ref.length; k < len1; k++) { img = ref[k]; img.tabIndex = 0; } - if (focus) { - return this.focusImage(); + if (this.images.length) { + return this.addTooltips(this.images); } }, - focusImage: function() { - var img; - img = this.images[0]; - return $.asap(function() { - if (!doc.contains(img)) { - return true; + addLabels: function() { + var checkbox, i, imageSelect, label, labels; + imageSelect = $('.fbc-payload-imageselect'); + labels = (function() { + var k, len1, ref, results; + ref = this.images; + results = []; + for (i = k = 0, len1 = ref.length; k < len1; i = ++k) { + checkbox = ref[i]; + checkbox.id = "checkbox-" + i; + label = $.el('label', { + htmlFor: checkbox.id + }); + label.dataset.row = Math.floor(i / 3); + label.dataset.col = i % 3; + results.push(label); } - img.focus(); - return d.activeElement === img; - }, function() {}); + return results; + }).call(this); + $.add(imageSelect, labels); + return this.addTooltips(labels); + }, + addTooltips: function(nodes) { + var i, k, len1, node; + for (i = k = 0, len1 = nodes.length; k < len1; i = ++k) { + node = nodes[i]; + node.title = this.imageKeys[i] + " or " + (this.imageKeys[i + 9][0].toUpperCase()) + this.imageKeys[i + 9].slice(1); + } + }, + checkForm: function(e) { + var checkbox, k, len1, n, ref; + n = 0; + ref = this.images; + for (k = 0, len1 = ref.length; k < len1; k++) { + checkbox = ref[k]; + if (checkbox.checked) { + n++; + } + } + if (n === 0) { + return e.preventDefault(); + } }, keybinds: function(e) { - var dx, reload, verify, x; - if (!(this.images && doc.contains(this.images[0]) && d.activeElement)) { + var dx, i, key, reload, verify, x; + if (!(this.images && doc.contains(this.images[0]))) { return; } - reload = $.id('recaptcha-reload-button'); - verify = $.id('recaptcha-verify-button'); + reload = $('#recaptcha-reload-button, .fbc-button-reload'); + verify = $('#recaptcha-verify-button, .fbc-button-verify > input'); x = this.images.indexOf(d.activeElement); if (x < 0) { - if (!$('.rc-controls').contains(d.activeElement)) { - return; - } x = d.activeElement === verify ? 11 : 9; } - if (!(dx = { - 38: 9, - 40: 3, - 37: 11, - 39: 1 - }[e.keyCode])) { + key = Keybinds.keyCode(e); + if (!this.noscript && key === 'Space' && x < 9) { + this.images[x].click(); + } else if ((i = this.imageKeys.indexOf(key)) >= 0) { + this.images[i % 9].click(); + verify.focus(); + } else if (dx = { + 'Up': 9, + 'Down': 3, + 'Left': 11, + 'Right': 1 + }[key]) { + x = (x + dx) % 12; + if (x === 10) { + x = dx === 11 ? 9 : 11; + } + (this.images[x] || { + 9: reload, + 11: verify + }[x]).focus(); + } else { return; } - x = (x + dx) % 12; - if (x === 10) { - x = dx === 11 ? 9 : 11; - } - (this.images[x] || { - 9: reload, - 11: verify - }[x]).focus(); e.preventDefault(); return e.stopPropagation(); } }; - Captcha.noscript = { - lifetime: 2 * $.MINUTE, - iframeURL: '//www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc', + Captcha.language = { init: function() { - var container, input; - if (d.cookie.indexOf('pass_enabled=1') >= 0) { + if (!(Conf['captchaLanguage'].trim() && d.cookie.indexOf('pass_enabled=1') < 0 && !Conf['Hide Original Post Form'])) { return; } - if (!(this.isEnabled = !!$.id('g-recaptcha'))) { - return; - } - container = $.el('div', { - className: 'captcha-img', - title: 'Reload reCAPTCHA' + return $.onExists(doc, '#captchaFormPart', true, function(node) { + return $.onExists(node, 'iframe', true, Captcha.language.fixIframe); }); - input = $.el('input', { - className: 'captcha-input field', - title: 'Verification', - autocomplete: 'off', - spellcheck: false - }); - this.nodes = { - container: container, - input: input - }; - $.on(input, 'keydown', this.keydown.bind(this)); - $.on(this.nodes.container, 'click', (function(_this) { - return function() { - _this.reload(); - return _this.nodes.input.focus(); - }; - })(this)); - this.conn = new Connection(null, location.protocol + "//www.google.com", { - challenge: this.load.bind(this), - token: this.save.bind(this), - error: this.error.bind(this) - }); - $.addClass(QR.nodes.el, 'has-captcha'); - $.after(QR.nodes.com.parentNode, [container, input]); - this.captchas = []; - $.get('captchas', [], function(arg) { - var captchas; - captchas = arg.captchas; - QR.captcha.sync(captchas); - return QR.captcha.clear(); - }); - $.sync('captchas', this.sync); - this.beforeSetup(); - return this.setup(); }, - initFrame: function() { - var cb, conn, img, ref, ref1; - conn = new Connection(window.parent, location.protocol + "//boards.4chan.org", { - response: function(response) { - $.id('response').value = response; - return $('.fbc-challenge > form').submit(); - } - }); - conn.send({ - token: (ref = $('.fbc-verification-token > textarea')) != null ? ref.value : void 0, - error: (ref1 = $('.fbc-error')) != null ? ref1.textContent : void 0 - }); - if (!(img = $('.fbc-payload > img'))) { + fixPage: function() { + if (!(Conf['captchaLanguage'].trim() && d.cookie.indexOf('pass_enabled=1') < 0)) { return; } - cb = function() { - var canvas; - canvas = $.el('canvas'); - canvas.width = img.width; - canvas.height = img.height; - canvas.getContext('2d').drawImage(img, 0, 0); - return conn.send({ - challenge: canvas.toDataURL() - }); - }; - if (img.complete) { - return cb(); - } else { - return $.on(img, 'load', cb); - } + return $.onExists(doc, 'iframe', true, Captcha.language.fixIframe); }, - timers: {}, - cb: { - focus: function() { - return QR.captcha.setup(false, true); - } - }, - beforeSetup: function() { - var container, input, ref; - ref = this.nodes, container = ref.container, input = ref.input; - container.hidden = true; - input.value = ''; - input.placeholder = 'Focus to load reCAPTCHA'; - this.count(); - return $.on(input, 'focus click', this.cb.focus); - }, - needed: function() { - var captchaCount, postsCount; - captchaCount = this.captchas.length; - if (QR.req) { - captchaCount++; - } - postsCount = QR.posts.length; - if (postsCount === 1 && !Conf['Auto-load captcha'] && !QR.posts[0].com && !QR.posts[0].file) { - postsCount = 0; - } - return captchaCount < postsCount; - }, - onNewPost: function() {}, - onPostChange: function() {}, - setup: function(focus, force) { - if (!(this.isEnabled && (this.needed() || force))) { + fixIframe: function(el) { + var lang, src; + if (!(lang = Conf['captchaLanguage'].trim())) { return; } - if (!this.nodes.iframe) { - this.nodes.iframe = $.el('iframe', { - id: 'qr-captcha-iframe', - src: this.iframeURL - }); - $.add(d.body, this.nodes.iframe); - this.conn.target = this.nodes.iframe.contentWindow; - } else if (!this.occupied || force) { - this.nodes.iframe.src = this.iframeURL; + src = /[?&]hl=/.test(el.src) ? el.src.replace(/([?&]hl=)[^&]*/, '$1' + encodeURIComponent(lang)) : el.src + ("&hl=" + (encodeURIComponent(lang))); + if (el.src !== src) { + return el.src = src; } - this.occupied = true; - if (focus) { - return this.nodes.input.focus(); - } - }, - afterSetup: function() { - var container, input, ref; - ref = this.nodes, container = ref.container, input = ref.input; - container.hidden = false; - input.placeholder = 'Verification'; - this.count(); - $.off(input, 'focus click', this.cb.focus); - if (QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight) { - QR.nodes.el.style.top = ''; - return QR.nodes.el.style.bottom = '0px'; - } - }, - destroy: function() { - if (!this.isEnabled) { - return; - } - if (this.nodes.img) { - $.rm(this.nodes.img); - } - delete this.nodes.img; - if (this.nodes.iframe) { - $.rm(this.nodes.iframe); - } - delete this.nodes.iframe; - delete this.occupied; - return this.beforeSetup(); - }, - sync: function(captchas) { - if (captchas == null) { - captchas = []; - } - QR.captcha.captchas = captchas; - return QR.captcha.count(); - }, - getOne: function() { - var captcha; - this.clear(); - if (captcha = this.captchas.shift()) { - this.count(); - $.set('captchas', this.captchas); - return captcha.response; - } else if (/\S/.test(this.nodes.input.value)) { - return (function(_this) { - return function(cb) { - _this.submitCB = cb; - return _this.sendResponse(); - }; - })(this); - } else { - return null; - } - }, - sendResponse: function() { - var response; - response = this.nodes.input.value; - if (/\S/.test(response)) { - return this.conn.send({ - response: response - }); - } - }, - save: function(token) { - delete this.occupied; - this.nodes.input.value = ''; - if (this.submitCB) { - this.submitCB(token); - delete this.submitCB; - if (this.needed()) { - return this.reload(); - } else { - return this.destroy(); - } - } else { - $.forceSync('captchas'); - this.captchas.push({ - response: token, - timeout: this.timeout - }); - this.count(); - $.set('captchas', this.captchas); - return this.reload(); - } - }, - error: function(message) { - this.occupied = true; - this.nodes.input.value = ''; - if (this.submitCB) { - this.submitCB(); - delete this.submitCB; - } - return QR.error("Captcha Error: " + message); - }, - clear: function() { - var captcha, i, k, len1, now, ref; - if (!this.captchas.length) { - return; - } - $.forceSync('captchas'); - now = Date.now(); - ref = this.captchas; - for (i = k = 0, len1 = ref.length; k < len1; i = ++k) { - 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(src) { - var container, img, input, ref; - ref = this.nodes, container = ref.container, input = ref.input, img = ref.img; - this.occupied = true; - this.timeout = Date.now() + this.lifetime; - if (!img) { - img = this.nodes.img = new Image(); - $.one(img, 'load', this.afterSetup.bind(this)); - $.on(img, 'load', function() { - return this.hidden = false; - }); - $.add(container, img); - } - img.src = src; - input.value = ''; - this.clear(); - clearTimeout(this.timers.expire); - return this.timers.expire = setTimeout(this.expire.bind(this), this.lifetime); - }, - count: function() { - var count, placeholder; - count = this.captchas ? this.captchas.length : 0; - placeholder = this.nodes.input.placeholder.replace(/\ \(.*\)$/, ''); - placeholder += (function() { - switch (count) { - case 0: - if (placeholder === 'Verification') { - return ' (Shift + Enter to cache)'; - } else { - return ''; - } - break; - case 1: - return ' (1 cached captcha)'; - default: - return " (" + count + " cached captchas)"; - } - })(); - this.nodes.input.placeholder = placeholder; - this.nodes.input.alt = count; - clearTimeout(this.timers.clear); - if (this.captchas.length) { - return this.timers.clear = setTimeout(this.clear.bind(this), this.captchas[0].timeout - Date.now()); - } - }, - expire: function() { - if (!this.nodes.iframe) { - return; - } - if (!d.hidden && (this.needed() || d.activeElement === this.nodes.input)) { - return this.reload(); - } else { - return this.destroy(); - } - }, - reload: function() { - var ref; - this.nodes.iframe.src = this.iframeURL; - this.occupied = true; - return (ref = this.nodes.img) != null ? ref.hidden = true : void 0; - }, - keydown: function(e) { - if (e.keyCode === 8 && !this.nodes.input.value) { - if (this.nodes.iframe) { - this.reload(); - } else { - this.setup(); - } - } else if (e.keyCode === 13 && e.shiftKey) { - this.sendResponse(); - } else { - return; - } - return e.preventDefault(); } }; @@ -8032,6 +7800,16 @@ if (!(this.isEnabled = !!$.id('g-recaptcha'))) { return; } + if (this.noscript = Conf['Force Noscript Captcha'] || !$.hasClass(doc, 'js-enabled')) { + this.conn = new Connection(null, location.protocol + "//www.google.com", { + token: (function(_this) { + return function(token) { + return _this.save(true, token); + }; + })(this) + }); + $.addClass(QR.nodes.el, 'noscript-captcha'); + } this.captchas = []; $.get('captchas', [], function(arg) { var captchas; @@ -8062,9 +7840,26 @@ }; })(this)); }, + initFrame: function() { + var conn, ref, token; + if (token = (ref = $('.fbc-verification-token > textarea')) != null ? ref.value : void 0) { + conn = new Connection(window.parent, location.protocol + "//boards.4chan.org"); + return conn.send({ + token: token + }); + } + }, shouldFocus: false, timeouts: {}, postsCount: 0, + noscriptURL: function() { + var lang, url; + url = '//www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc'; + if (lang = Conf['captchaLanguage'].trim()) { + url += "&hl=" + (encodeURIComponent(lang)); + } + return url; + }, needed: function() { var captchaCount; captchaCount = this.captchas.length; @@ -8100,7 +7895,7 @@ if (!(this.isEnabled && (this.needed() || force))) { return; } - if (focus) { + if (focus && !QR.inBubble()) { this.shouldFocus = true; } if (this.timeouts.destroy) { @@ -8111,6 +7906,7 @@ if (this.nodes.container) { if (this.shouldFocus && (iframe = $('iframe', this.nodes.container))) { iframe.focus(); + QR.focus(); delete this.shouldFocus; } return; @@ -8123,6 +7919,22 @@ childList: true, subtree: true }); + if (this.noscript) { + return this.setupNoscript(); + } else { + return this.setupJS(); + } + }, + setupNoscript: function() { + var iframe; + iframe = $.el('iframe', { + id: 'qr-captcha-iframe', + src: this.noscriptURL() + }); + $.add(this.nodes.container, iframe); + return this.conn.target = iframe.contentWindow; + }, + setupJS: function() { return $.globalEval('(function() {\n function render() {\n var container = document.querySelector("#qr .captcha-container");\n container.dataset.widgetID = window.grecaptcha.render(container, {\n sitekey: \'6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc\',\n theme: document.documentElement.classList.contains(\'tomorrow\') ? \'dark\' : \'light\',\n callback: function(response) {\n window.dispatchEvent(new CustomEvent("captcha:success", {detail: response}));\n }\n });\n }\n if (window.grecaptcha) {\n render();\n } else {\n var cbNative = window.onRecaptchaLoaded;\n window.onRecaptchaLoaded = function() {\n render();\n cbNative();\n }\n }\n})();'); }, afterSetup: function(mutations) { @@ -8142,7 +7954,7 @@ } }, setupIFrame: function(iframe) { - this.setupTime = Date.now(); + Captcha.language.fixIframe(iframe); $.addClass(QR.nodes.el, 'captcha-open'); if (QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight) { QR.nodes.el.style.top = null; @@ -8199,24 +8011,26 @@ return null; } }, - save: function(pasted) { - var base1, focus, ref, ref1; + save: function(pasted, token) { + var base1, focus, ref; $.forceSync('captchas'); this.captchas.push({ - response: $('textarea', this.nodes.container).value, - timeout: (pasted ? this.setupTime : Date.now()) + this.lifetime + response: token || $('textarea', this.nodes.container).value, + timeout: Date.now() + this.lifetime }); $.set('captchas', this.captchas); this.count(); + focus = ((ref = d.activeElement) != null ? ref.nodeName : void 0) === 'IFRAME' && /https?:\/\/www\.google\.com\/recaptcha\//.test(d.activeElement.src); if (this.needed()) { - if (QR.cooldown.auto || Conf['Post on Captcha Completion']) { - this.shouldFocus = true; - } else { - QR.nodes.status.focus(); + if (focus) { + if (QR.cooldown.auto || Conf['Post on Captcha Completion']) { + this.shouldFocus = true; + } else { + QR.nodes.status.focus(); + } } this.reload(); } else { - focus = ((ref = d.activeElement) != null ? ref.nodeName : void 0) === 'IFRAME' && ((ref1 = d.activeElement.src) != null ? ref1.slice(0, 38) : void 0) === 'https://www.google.com/recaptcha/api2/'; if (pasted) { this.destroy(); } else { @@ -8252,7 +8066,7 @@ this.captchas = this.captchas.slice(i); this.count(); $.set('captchas', this.captchas); - return this.setup(true); + return this.setup(d.activeElement === QR.nodes.status); }, count: function() { this.nodes.counter.textContent = "Captchas: " + this.captchas.length; @@ -8262,7 +8076,11 @@ } }, reload: function() { - return $.globalEval('(function() {\n var container = document.querySelector("#qr .captcha-container");\n window.grecaptcha.reset(container.dataset.widgetID);\n})();'); + if (this.noscript) { + return $('iframe', this.nodes.container).src = this.noscriptURL(); + } else { + return $.globalEval('(function() {\n var container = document.querySelector("#qr .captcha-container");\n window.grecaptcha.reset(container.dataset.widgetID);\n})();'); + } } }; @@ -9286,7 +9104,7 @@ Gallery.fullIDs[post.fullID] = true; thumb = $.el('a', { className: 'gal-thumb', - href: post.file.URL, + href: post.file.url, target: '_blank', title: post.file.name }); @@ -9299,29 +9117,43 @@ Gallery.images.push(thumb); return $.add(Gallery.nodes.thumbs, thumb); }, + load: function(thumb, errorCB) { + var elType, ext, file; + ext = thumb.href.match(/\w*$/); + elType = { + 'webm': 'video', + 'pdf': 'iframe' + }[ext] || 'img'; + file = $.el(elType, { + title: thumb.title + }); + $.extend(file.dataset, thumb.dataset); + $.on(file, 'error', errorCB); + file.src = thumb.href; + return file; + }, open: function(thumb) { - var el, elType, file, name, newID, nodes, oldID, post, ref, slideshow; + var el, file, newID, nodes, oldID, post, ref; nodes = Gallery.nodes; - name = nodes.name; oldID = +nodes.current.dataset.id; newID = +thumb.dataset.id; - slideshow = Gallery.slideshow && (newID > oldID || (oldID === Gallery.images.length - 1 && newID === 0)); - if (el = $('.gal-highlight', nodes.thumbs)) { + if (el = Gallery.images[oldID]) { $.rmClass(el, 'gal-highlight'); } $.addClass(thumb, 'gal-highlight'); - elType = /\.webm$/.test(thumb.href) ? 'video' : /\.pdf$/.test(thumb.href) ? 'iframe' : 'img'; - $[elType === 'iframe' ? 'addClass' : 'rmClass'](doc, 'gal-pdf'); - file = $.el(elType, { - title: name.download = name.textContent = thumb.title - }); - $.extend(file.dataset, thumb.dataset); - $.on(file, 'error', Gallery.error); - file.src = name.href = thumb.href; + nodes.thumbs.scrollTop = thumb.offsetTop + thumb.offsetHeight / 2 - nodes.thumbs.clientHeight / 2; + if (((ref = Gallery.cache) != null ? ref.dataset.id : void 0) === '' + newID) { + file = Gallery.cache; + $.off(file, 'error', Gallery.cacheError); + $.on(file, 'error', Gallery.error); + } else { + file = Gallery.load(thumb, Gallery.error); + } $.off(nodes.current, 'error', Gallery.error); ImageCommon.pause(nodes.current); $.replace(nodes.current, file); - if (elType === 'video') { + nodes.current = file; + if (file.nodeName === 'VIDEO') { file.loop = true; Volume.setup(file); if (Conf['Autoplay']) { @@ -9331,19 +9163,22 @@ ImageCommon.addControls(file); } } + doc.classList.toggle('gal-pdf', file.nodeName === 'IFRAME'); + Gallery.cb.setHeight(); nodes.count.textContent = +thumb.dataset.id + 1; - nodes.current = file; + nodes.name.download = nodes.name.textContent = thumb.title; + nodes.name.href = thumb.href; nodes.frame.scrollTop = 0; nodes.next.focus(); - if (slideshow) { + if (Gallery.slideshow && (newID > oldID || (oldID === Gallery.images.length - 1 && newID === 0))) { Gallery.setupTimer(); } else { Gallery.cb.stop(); } - if (Conf['Scroll to Post'] && (post = (ref = (post = g.posts[file.dataset.post])) != null ? ref.nodes.root : void 0)) { - Header.scrollTo(post); + if (Conf['Scroll to Post'] && (post = g.posts[file.dataset.post])) { + Header.scrollTo(post.nodes.root); } - return nodes.thumbs.scrollTop = thumb.offsetTop + thumb.offsetHeight / 2 - nodes.thumbs.clientHeight / 2; + return Gallery.cache = Gallery.load(Gallery.images[(newID + 1) % Gallery.images.length], Gallery.cacheError); }, error: function() { var ref; @@ -9365,6 +9200,9 @@ }; })(this)); }, + cacheError: function() { + return delete Gallery.cache; + }, cleanupTimer: function() { var current; clearTimeout(Gallery.timeoutID); @@ -9526,6 +9364,11 @@ setFitness: function() { return (this.checked ? $.addClass : $.rmClass)(doc, "gal-" + (this.name.toLowerCase().replace(/\s+/g, '-'))); }, + setHeight: function() { + var current, dim, frame, height, ref, ref1, ref2, width; + ref = Gallery.nodes, current = ref.current, frame = ref.frame; + return current.style.minHeight = Conf['Stretch to Fit'] && (dim = (ref1 = g.posts[current.dataset.post]) != null ? ref1.file.dimensions : void 0) ? ((ref2 = dim.split('x'), width = ref2[0], height = ref2[1], ref2), Math.min(doc.clientHeight - 25, height / width * frame.clientWidth) + 'px') : null; + }, setDelay: function() { return Gallery.delay = +this.value; } @@ -9550,11 +9393,14 @@ var input, label; label = UI.checkbox(name, name); input = label.firstElementChild; - if (name === 'Fit Width' || name === 'Fit Height' || name === 'Hide Thumbnails') { + if (name === 'Hide Thumbnails' || name === 'Fit Width' || name === 'Fit Height') { $.on(input, 'change', Gallery.cb.setFitness); } $.event('change', null, input); $.on(input, 'change', $.cb.checked); + if (name === 'Hide Thumbnails' || name === 'Fit Width' || name === 'Fit Height' || name === 'Stretch to Fit') { + $.on(input, 'change', Gallery.cb.setHeight); + } return { el: label }; @@ -9563,7 +9409,7 @@ var delayInput, delayLabel, item, subEntries; subEntries = (function() { var k, len1, ref, results; - ref = ['Hide Thumbnails', 'Fit Width', 'Fit Height', 'Scroll to Post']; + ref = ['Hide Thumbnails', 'Fit Width', 'Fit Height', 'Stretch to Fit', 'Scroll to Post']; results = []; for (k = 0, len1 = ref.length; k < len1; k++) { item = ref[k]; @@ -9638,7 +9484,7 @@ }, error: function(file, post, delay, cb) { var URL, redirect, src, timeoutID; - src = post.file.URL.split('/'); + src = post.file.url.split('/'); URL = Redirect.to('file', { boardID: post.board.ID, filename: src[src.length - 1] @@ -9688,7 +9534,7 @@ post.kill(true); return redirect(); } else { - return URL = post.file.URL; + return URL = post.file.url; } } }); @@ -9883,7 +9729,7 @@ if (file.videoControls) { $.rm(file.videoControls); } - file.thumb.parentNode.href = file.URL; + file.thumb.parentNode.href = file.url; file.thumb.parentNode.target = '_blank'; ref = ['isExpanding', 'isExpanded', 'videoControls', 'wasPlaying', 'scrollIntoView']; for (k = 0, len1 = ref.length; k < len1; k++) { @@ -9950,7 +9796,7 @@ el = file.fullImage = $.el((isVideo ? 'video' : 'img')); el.dataset.fullID = post.fullID; $.on(el, 'error', ImageExpand.error); - el.src = src || file.URL; + el.src = src || file.url; } el.className = 'full-image'; $.after(thumb, el); @@ -10179,7 +10025,7 @@ el = $.el((isVideo ? 'video' : 'img')); el.dataset.fullID = post.fullID; $.on(el, 'error', error); - el.src = file.URL; + el.src = file.url; } if (Conf['Restart when Opened']) { ImageCommon.rewind(el); @@ -10310,22 +10156,22 @@ attr = ref[k]; video.style[attr] = thumb.style[attr]; } - video.src = file.URL; + video.src = file.url; $.replace(thumb, video); file.thumb = video; return file.videoThumb = true; }, prefetch: function(post) { - var URL, clone, el, file, isImage, isVideo, k, len1, match, ref, replace, thumb, type; + var clone, el, file, isImage, isVideo, k, len1, match, ref, replace, thumb, type, url; file = post.file; if (!file) { return; } - isImage = file.isImage, isVideo = file.isVideo, thumb = file.thumb, URL = file.URL; + isImage = file.isImage, isVideo = file.isVideo, thumb = file.thumb, url = file.url; if (file.isPrefetched || !(isImage || isVideo) || post.isHidden || post.thread.isHidden) { return; } - type = (match = URL.match(/\.([^.]+)$/)[1].toUpperCase()) === 'JPEG' ? 'JPG' : match; + type = (match = url.match(/\.([^.]+)$/)[1].toUpperCase()) === 'JPEG' ? 'JPG' : match; replace = Conf["Replace " + type] && !/spoiler/.test(thumb.src || thumb.dataset.src); if (!(replace || Conf['prefetch'])) { return; @@ -10357,13 +10203,13 @@ ref1 = post.clones; for (q = 0, len2 = ref1.length; q < len2; q++) { clone = ref1[q]; - clone.file.thumb.src = URL; + clone.file.thumb.src = url; } - thumb.src = URL; + thumb.src = url; return thumb.removeAttribute('data-src'); }); } - return el.src = URL; + return el.src = url; }, toggle: function() { if (Conf['prefetch'] = this.checked) { @@ -10405,7 +10251,7 @@ }, node: function() { var el; - if (!(this.file && /webm$/i.test(this.file.URL))) { + if (!(this.file && /webm$/i.test(this.file.url))) { return; } if (this.isClone) { @@ -10426,7 +10272,7 @@ load: function() { $.rmClass(this.parentNode, 'error'); $.addClass(this.parentNode, 'loading'); - return CrossOrigin.binary(Get.postFromNode(this).file.URL, (function(_this) { + return CrossOrigin.binary(Get.postFromNode(this).file.url, (function(_this) { return function(data) { var output, title; $.rmClass(_this.parentNode, 'loading'); @@ -10543,32 +10389,44 @@ cb: this.node }); }, + sandbox: function(url) { + return E.url({ + innerHTML: "[sb] " + E(url) + "" + }); + }, + rmOrigin: function(e) { + if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) { + return; + } + $.open(this.href); + return e.preventDefault(); + }, createSauceLink: function(link, post) { - var a, ext, i, k, key, len1, m, part, parts, ref, ref1, ref2, skip; + var a, ext, i, k, key, len1, m, part, parts, ref, ref1, ref2, skip, url; if (!(link = link.trim())) { return null; } parts = {}; - ref = link.split(/;(?=(?:text|boards|types):)/); + ref = link.split(/;(?=(?:text|boards|types|sandbox):?)/); for (i = k = 0, len1 = ref.length; k < len1; i = ++k) { part = ref[i]; if (i === 0) { parts['url'] = part; } else { - m = part.match(/^(\w*):(.*)$/); + m = part.match(/^(\w*):?(.*)$/); parts[m[1]] = m[2]; } } parts['text'] || (parts['text'] = ((ref1 = parts['url'].match(/(\w+)\.\w+\//)) != null ? ref1[1] : void 0) || '?'); - ext = post.file.URL.match(/[^.]*$/)[0]; + ext = post.file.url.match(/[^.]*$/)[0]; skip = false; for (key in parts) { parts[key] = parts[key].replace(/%(T?URL|IMG|MD5|board|name|%|semi)/g, function(parameter) { var type; type = { '%TURL': post.file.thumbURL, - '%URL': post.file.URL, - '%IMG': ext === 'gif' || ext === 'jpg' || ext === 'png' ? post.file.URL : post.file.thumbURL, + '%URL': post.file.url, + '%IMG': ext === 'gif' || ext === 'jpg' || ext === 'png' ? post.file.url : post.file.thumbURL, '%MD5': post.file.MD5, '%board': post.board.ID, '%name': post.file.name, @@ -10597,12 +10455,19 @@ if (!(!parts['types'] || indexOf.call(parts['types'].split(','), ext) >= 0)) { return null; } + url = parts['url']; + if (parts['sandbox'] != null) { + url = Sauce.sandbox(url); + } a = Sauce.link.cloneNode(true); - a.href = parts['url']; + a.href = url; a.textContent = parts['text']; if (/^javascript:/i.test(parts['url'])) { a.removeAttribute('target'); } + if (parts['sandbox'] != null) { + $.on(a, 'click', Sauce.rmOrigin); + } return a; }, node: function() { @@ -10719,6 +10584,9 @@ }, wheel: function(e) { var el, volume; + if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey) { + return; + } if (!(el = $('video:not([data-md5])', this))) { return; } @@ -11035,7 +10903,7 @@ content = { innerHTML: "" + E(a.dataset.uid) + "" }; - el.src = "data:text/html;charset=utf-8," + (encodeURIComponent(content.innerHTML)); + el.src = E.url(content); return el; }, title: { @@ -11713,7 +11581,7 @@ if (!file) { return false; } - a.href = file.URL; + a.href = file.url; a.download = file.name; return true; } @@ -11771,27 +11639,36 @@ } a = $.el('a', { className: 'report-link', - href: 'javascript:;', - textContent: 'Report this post' + href: 'javascript:;' }); $.on(a, 'click', ReportLink.report); return Menu.menu.addEntry({ el: a, order: 10, open: function(post) { - ReportLink.url = !post.isDead ? "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post : Conf['Archive Report'] ? Redirect.to('report', { - boardID: post.board.ID, - postID: post.ID - }) : void 0; + if (!(post.isDead || (post.thread.isDead && !post.thread.isArchived))) { + a.textContent = 'Report this post'; + ReportLink.url = "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post; + ReportLink.height = 200; + } else if (Conf['Archive Report']) { + a.textContent = 'Report to archive'; + ReportLink.url = Redirect.to('report', { + boardID: post.board.ID, + postID: post.ID + }); + ReportLink.height = 350; + } else { + ReportLink.url = ''; + } return !!ReportLink.url; } }); }, report: function() { - var id, set, url; - url = ReportLink.url; + var height, id, set, url; + url = ReportLink.url, height = ReportLink.height; id = Date.now(); - set = "toolbar=0,scrollbars=1,location=0,status=1,menubar=0,resizable=1,width=685,height=200"; + set = "toolbar=0,scrollbars=1,location=0,status=1,menubar=0,resizable=1,width=700,height=" + height; return window.open(url, id, set); } }; @@ -11919,13 +11796,13 @@ return; } statsHTML = { - innerHTML: "? / ?" + (Conf["IP Count in Stats"] ? " / ?" : "") + (Conf["Page Count in Stats"] ? " / ?" : "") + innerHTML: "? / ?" + (Conf["IP Count in Stats"] ? " / ?" : "") + (Conf["Page Count in Stats"] && g.BOARD.ID !== "f" ? " / ?" : "") }; statsTitle = 'Posts / Files'; if (Conf['IP Count in Stats']) { statsTitle += ' / IPs'; } - if (Conf['Page Count in Stats']) { + if (Conf['Page Count in Stats'] && g.BOARD.ID !== 'f') { statsTitle += ' / Page'; } if (Conf['Updater and Stats in Header']) { @@ -11950,6 +11827,9 @@ this.fileCountEl = $('#file-count', sc); this.ipCountEl = $('#ip-count', sc); this.pageCountEl = $('#page-count', sc); + if (this.pageCountEl) { + $.on(this.pageCountEl, 'click', ThreadStats.fetchPage); + } return Thread.callbacks.push({ name: 'Thread Stats', cb: this.node @@ -11964,7 +11844,7 @@ if (post.file) { fileCount++; } - if (Conf["Page Count in Stats"]) { + if (ThreadStats.pageCountEl) { return ThreadStats.lastPost = post.info.date; } }); @@ -11980,13 +11860,13 @@ } ref = e.detail, postCount = ref.postCount, fileCount = ref.fileCount, ipCount = ref.ipCount, newPosts = ref.newPosts; ThreadStats.update(postCount, fileCount, ipCount); - if (!Conf["Page Count in Stats"]) { + if (!ThreadStats.pageCountEl) { return; } if (newPosts.length) { ThreadStats.lastPost = g.posts[newPosts[newPosts.length - 1]].info.date; } - if (ThreadStats.lastPost > ThreadStats.lastPageUpdate && ((ref1 = ThreadStats.pageCountEl) != null ? ref1.textContent : void 0) !== '1') { + if (((ref1 = ThreadStats.pageCountEl) != null ? ref1.textContent : void 0) !== '1') { return ThreadStats.fetchPage(); } }, @@ -11995,14 +11875,14 @@ thread = ThreadStats.thread, postCountEl = ThreadStats.postCountEl, fileCountEl = ThreadStats.fileCountEl, ipCountEl = ThreadStats.ipCountEl; postCountEl.textContent = postCount; fileCountEl.textContent = fileCount; - if ((ipCount != null) && Conf["IP Count in Stats"]) { + if ((ipCount != null) && ipCountEl) { ipCountEl.textContent = ipCount; } (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"]) { + if (!ThreadStats.pageCountEl) { return; } clearTimeout(ThreadStats.timeout); @@ -12015,28 +11895,37 @@ return $.ajax("//a.4cdn.org/" + ThreadStats.thread.board + "/threads.json", { onload: ThreadStats.onThreadsLoad }, { - whenModified: true + whenModified: 'ThreadStats' }); }, onThreadsLoad: function() { var k, len1, len2, page, q, ref, ref1, thread; - if (!(Conf["Page Count in Stats"] && this.status === 200)) { - return; - } - ref = this.response; - for (k = 0, len1 = ref.length; k < len1; k++) { - page = ref[k]; - ref1 = page.threads; - for (q = 0, len2 = ref1.length; q < len2; q++) { - thread = ref1[q]; - if (!(thread.no === ThreadStats.thread.ID)) { - continue; + if (this.status === 200) { + ref = this.response; + for (k = 0, len1 = ref.length; k < len1; k++) { + page = ref[k]; + ref1 = page.threads; + for (q = 0, len2 = ref1.length; q < len2; q++) { + thread = ref1[q]; + if (!(thread.no === ThreadStats.thread.ID)) { + continue; + } + ThreadStats.pageCountEl.textContent = page.page; + (page.page === this.response.length ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning'); + ThreadStats.lastPageUpdate = new Date(thread.last_modified * $.SECOND); + ThreadStats.retry(); + return; } - ThreadStats.pageCountEl.textContent = page.page; - (page.page === this.response.length ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning'); - ThreadStats.lastPageUpdate = new Date(thread.last_modified * $.SECOND); - return; } + } else if (this.status === 304) { + return ThreadStats.retry(); + } + }, + retry: function() { + var ref; + if (ThreadStats.lastPost > ThreadStats.lastPageUpdate && ((ref = ThreadStats.pageCountEl) != null ? ref.textContent : void 0) !== '1') { + clearTimeout(ThreadStats.timeout); + return ThreadStats.timeout = setTimeout(ThreadStats.fetchPage, 5 * $.SECOND); } } }; @@ -12331,7 +12220,7 @@ onloadend: ThreadUpdater.cb.load, timeout: $.MINUTE }, { - whenModified: true + whenModified: 'ThreadUpdater' }); }, updateThreadStatus: function(type, status) { @@ -12489,7 +12378,7 @@ this.unreaddb = Unread.db || new DataBoard('lastReadPosts'); $.on(d, 'QRPostSuccessful', this.cb.post); $.on(sc, 'click', this.toggleWatcher); - $.on(this.refreshButton, 'click', this.fetchAllStatus); + $.on(this.refreshButton, 'click', this.buttonFetchAll); $.on(this.closeButton, 'click', this.toggleWatcher); $.on(d, '4chanXInitFinished', this.ready); switch (g.VIEW) { @@ -12569,7 +12458,7 @@ if (ThreadWatcher.isWatched(this.thread)) { $.addClass(this.nodes.root, 'watched'); } - return $.on(this.nodes.thumb.parentNode, 'click', (function(_this) { + $.on(this.nodes.thumb.parentNode, 'click', (function(_this) { return function(e) { if (!(e.button === 0 && e.altKey)) { return; @@ -12578,6 +12467,11 @@ return e.preventDefault(); }; })(this)); + return $.on(this.nodes.thumb.parentNode, 'mousedown', function(e) { + if (e.button === 0 && e.altKey) { + return e.preventDefault(); + } + }); }, ready: function() { $.off(d, '4chanXInitFinished', ThreadWatcher.ready); @@ -12670,13 +12564,20 @@ data = ref[threadID]; if (!(data != null ? data.isDead : void 0) && !(threadID in g.BOARD.threads)) { if (Conf['Auto Prune'] || !(data && typeof data === 'object')) { - ThreadWatcher.db["delete"]({ + db["delete"]({ boardID: boardID, threadID: threadID }); } else { + if (Conf['Show Unread Count']) { + ThreadWatcher.fetchStatus({ + boardID: boardID, + threadID: threadID, + data: data + }); + } data.isDead = true; - ThreadWatcher.db.set({ + db.set({ boardID: boardID, threadID: threadID, val: data @@ -12698,9 +12599,24 @@ return ThreadWatcher.add(thread); } }, - fetchCount: { - fetched: 0, - fetching: 0 + requests: [], + fetched: 0, + clearRequests: function() { + ThreadWatcher.requests = []; + ThreadWatcher.fetched = 0; + ThreadWatcher.status.textContent = ''; + return $.rmClass(ThreadWatcher.refreshButton, 'fa-spin'); + }, + abort: function() { + var k, len1, ref, req; + ref = ThreadWatcher.requests; + for (k = 0, len1 = ref.length; k < len1; k++) { + req = ref[k]; + if (req.readyState !== 4) { + req.abort(); + } + } + return ThreadWatcher.clearRequests(); }, fetchAuto: function() { var db, interval, now; @@ -12718,6 +12634,13 @@ } return ThreadWatcher.timeout = setTimeout(ThreadWatcher.fetchAuto, interval); }, + buttonFetchAll: function() { + if (ThreadWatcher.requests.length) { + return ThreadWatcher.abort(); + } else { + return ThreadWatcher.fetchAllStatus(); + } + }, fetchAllStatus: function() { var k, len1, ref, thread, threads; ThreadWatcher.db.forceSync(); @@ -12734,37 +12657,34 @@ } }, fetchStatus: function(thread, force) { - var boardID, data, fetchCount, threadID; + var boardID, data, req, threadID; boardID = thread.boardID, threadID = thread.threadID, data = thread.data; if (data.isDead && !force) { return; } - fetchCount = ThreadWatcher.fetchCount; - if (fetchCount.fetching === 0) { + if (ThreadWatcher.requests.length === 0) { ThreadWatcher.status.textContent = '...'; $.addClass(ThreadWatcher.refreshButton, 'fa-spin'); } - fetchCount.fetching++; - return $.ajax("//a.4cdn.org/" + boardID + "/thread/" + threadID + ".json", { + req = $.ajax("//a.4cdn.org/" + boardID + "/thread/" + threadID + ".json", { onloadend: function() { return ThreadWatcher.parseStatus.call(this, thread); - } + }, + timeout: $.MINUTE + }, { + whenModified: force ? false : 'ThreadWatcher' }); + return ThreadWatcher.requests.push(req); }, parseStatus: function(arg) { - var boardID, data, fetchCount, isDead, k, lastReadPost, len1, match, postObj, quotingYou, ref, ref1, regexp, status, threadID, unread; + var boardID, data, isDead, k, lastReadPost, len1, match, postObj, quotesYou, quotingYou, ref, ref1, regexp, threadID, unread; boardID = arg.boardID, threadID = arg.threadID, data = arg.data; - fetchCount = ThreadWatcher.fetchCount; - fetchCount.fetched++; - if (fetchCount.fetched === fetchCount.fetching) { - fetchCount.fetched = 0; - fetchCount.fetching = 0; - status = ''; - $.rmClass(ThreadWatcher.refreshButton, 'fa-spin'); + ThreadWatcher.fetched++; + if (ThreadWatcher.fetched === ThreadWatcher.requests.length) { + ThreadWatcher.clearRequests(); } else { - status = (Math.round(fetchCount.fetched / fetchCount.fetching * 100)) + "%"; + ThreadWatcher.status.textContent = (Math.round(ThreadWatcher.fetched / ThreadWatcher.requests.length * 100)) + "%"; } - ThreadWatcher.status.textContent = status; if (this.status === 200 && this.response) { isDead = !!this.response.posts[0].archived; if (isDead && Conf['Auto Prune']) { @@ -12798,17 +12718,21 @@ if (!(QR.db && postObj.com)) { continue; } - regexp = /]*\bhref="(?:\/([^\/]+)\/thread\/(\d+))?(?:#p(\d+))?"/g; + quotesYou = false; + regexp = /]*\bhref="(?:\/([^\/]+)\/thread\/)?(\d+)?(?:#p(\d+))?"/g; while (match = regexp.exec(postObj.com)) { if (QR.db.get({ boardID: match[1] || boardID, threadID: match[2] || threadID, postID: match[3] || match[2] || threadID })) { - quotingYou++; - continue; + quotesYou = true; + break; } } + if (quotesYou && !Filter.isHidden(Build.parseJSON(postObj, boardID))) { + quotingYou++; + } } if (isDead !== data.isDead || unread !== data.unread || quotingYou !== data.quotingYou) { data.isDead = isDead; @@ -12829,6 +12753,8 @@ }); } else { data.isDead = true; + delete data.unread; + delete data.quotingYou; ThreadWatcher.db.set({ boardID: boardID, threadID: threadID, @@ -13344,7 +13270,7 @@ return; } notif = new Notification(post.info.nameBlock + " replied to you", { - body: post.info[Conf['Remove Spoilers'] || Conf['Reveal Spoilers'] ? 'comment' : 'commentSpoilered'], + body: post.info.commentDisplay, icon: Favicon.logo }); notif.onclick = function() { @@ -13378,6 +13304,9 @@ }, read: $.debounce(100, function(e) { var ID, count, data, height, ref, ref1, root; + if (!Unread.posts.size && Unread.readCount !== Unread.thread.posts.keys.length) { + Unread.saveLastReadPost(); + } if (d.hidden || !Unread.posts.size) { return; } @@ -13528,7 +13457,7 @@ } return Redirect.data = o; }, - archives: [{"uid":0,"name":"Moe","domain":"archive.moe","http":false,"https":true,"software":"foolfuuka","boards":["a","biz","c","co","diy","fit","gd","gif","h","i","int","jp","k","m","mlp","out","po","qa","r9k","s4s","sci","tg","tv","u","v","vg","vp","vr","wsg"],"files":["a","biz","c","co","diy","fit","gd","h","i","jp","k","m","mlp","po","qa","r9k","s4s","sci","tg","u","v","vg","vp","vr","wsg"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":4,"name":"Nyafuu Archive","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"archive.loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["c","d","e","i","lgbt","t","u","w","wg"],"files":["c","d","e","i","lgbt","t","u","w","wg"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":false,"https":true,"software":"fuuka","boards":["cgl","g","mu","qa","w"],"files":["cgl","g","mu","qa","w"]},{"uid":10,"name":"warosu","domain":"warosu.org","http":false,"https":true,"software":"fuuka","boards":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"],"files":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"]},{"uid":15,"name":"fgts","domain":"fgts.jp","http":true,"https":true,"software":"foolfuuka","boards":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"],"files":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"]},{"uid":22,"name":"not4plebs","domain":"totally.not4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["sp"],"files":["sp"]},{"uid":23,"name":"DesuStorage","domain":"archive.desustorage.org","http":false,"https":true,"software":"foolfuuka","boards":["mlp","qa"],"files":["mlp","qa"]}], + archives: [{"uid":0,"name":"Moe","domain":"archive.moe","http":false,"https":true,"software":"foolfuuka","boards":["a","an","biz","c","co","diy","fit","gd","gif","h","i","int","jp","k","m","mlp","out","po","qa","r9k","s4s","sci","tg","tv","u","v","vg","vp","vr","wsg"],"files":["a","biz","c","co","diy","fit","gd","h","i","jp","k","m","mlp","po","qa","r9k","s4s","sci","tg","u","v","vg","vp","vr","wsg"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":4,"name":"Nyafuu Archive","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"archive.loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["c","d","e","i","lgbt","t","u"],"files":["c","d","e","i","lgbt","t","u"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":false,"https":true,"software":"fuuka","boards":["cgl","g","mu","qa","w"],"files":["cgl","g","mu","qa","w"]},{"uid":10,"name":"warosu","domain":"warosu.org","http":false,"https":true,"software":"fuuka","boards":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"],"files":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"]},{"uid":15,"name":"fgts","domain":"fgts.jp","http":true,"https":true,"software":"foolfuuka","boards":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"],"files":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"]},{"uid":22,"name":"not4plebs","domain":"totally.not4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["sp"],"files":["sp"]},{"uid":23,"name":"DesuStorage","domain":"archive.desustorage.org","http":false,"https":true,"software":"foolfuuka","boards":["mlp","qa"],"files":["mlp","qa"]}], to: function(dest, data) { var archive; archive = (dest === 'search' || dest === 'board' ? Redirect.data.thread : Redirect.data[dest])[data.boardID]; @@ -14365,22 +14294,22 @@ formatters: { t: function() { return { - innerHTML: E(this.file.URL.match(/[^/]*$/)[0]) + innerHTML: E(this.file.url.match(/[^/]*$/)[0]) }; }, T: function() { return { - innerHTML: "" + FileInfo.formatters.t.call(this).innerHTML + "" + innerHTML: "" + FileInfo.formatters.t.call(this).innerHTML + "" }; }, l: function() { return { - innerHTML: "" + FileInfo.formatters.n.call(this).innerHTML + "" + innerHTML: "" + FileInfo.formatters.n.call(this).innerHTML + "" }; }, L: function() { return { - innerHTML: "" + FileInfo.formatters.N.call(this).innerHTML + "" + innerHTML: "" + FileInfo.formatters.N.call(this).innerHTML + "" }; }, n: function() { @@ -14432,6 +14361,11 @@ innerHTML: E(this.file.dimensions || "PDF") }; }, + g: function() { + return { + innerHTML: (this.file.tag ? ", " + E(this.file.tag) : "") + }; + }, '%': function() { return { innerHTML: "%" @@ -14493,21 +14427,25 @@ }); }, code: function() { - var i, k, len1, pre, ref; if (this.isClone) { return; } - ref = $$('.prettyprint', this.nodes.comment); - for (i = k = 0, len1 = ref.length; k < len1; i = ++k) { - pre = ref[i]; - if (!$.hasClass(pre, 'prettyprinted')) { - $.event('prettyprint', { - ID: this.fullID, - i: i, - html: pre.innerHTML - }, window); - } - } + return $.ready((function(_this) { + return function() { + var i, k, len1, pre, ref; + ref = $$('.prettyprint', _this.nodes.comment); + for (i = k = 0, len1 = ref.length; k < len1; i = ++k) { + pre = ref[i]; + if (!$.hasClass(pre, 'prettyprinted')) { + $.event('prettyprint', { + ID: _this.fullID, + i: i, + html: pre.innerHTML + }, window); + } + } + }; + })(this)); }, math: function() { if ((this.isClone && doc.contains(this.origin.nodes.root)) || !$('.math', this.nodes.comment)) { @@ -14946,6 +14884,8 @@ return 'Enter'; case 27: return 'Esc'; + case 32: + return 'Space'; case 37: return 'Left'; case 38: @@ -14954,9 +14894,15 @@ return 'Right'; case 40: return 'Down'; + case 188: + return 'Comma'; + case 190: + return 'Period'; default: if ((48 <= kc && kc <= 57) || (65 <= kc && kc <= 90)) { return String.fromCharCode(kc).toLowerCase(); + } else if ((96 <= kc && kc <= 105)) { + return String.fromCharCode(kc - 48).toLowerCase(); } else { return null; } @@ -15316,36 +15262,53 @@ }; Report = { + css: "noscript > div, noscript > div > div {\n" + +" height: 545px !important;\n" + +"}\n" + +"noscript > div > div > div:first-child, noscript iframe {\n" + +" height: 423px !important;\n" + +"}\n" + +":root:not(.js-enabled) #g-recaptcha {\n" + +" height: auto;\n" + +"}", init: function() { var match; if (!(/\bmode=report\b/.test(location.search) && (match = location.search.match(/\bno=(\d+)/)))) { return; } + Captcha.language.fixPage(); this.postID = +match[1]; return $.ready(this.ready); }, ready: function() { - new MutationObserver(Report.resize).observe(d.body, { - childList: true, - attributes: true, - subtree: true - }); + $.addStyle(Report.css); if (Conf['Archive Report']) { - return Report.archive(); + Report.archive(); + } + if ($.hasClass(doc, 'js-enabled')) { + return new MutationObserver(function() { + return Report.fit('.gc-bubbleDefault'); + }).observe(d.body, { + childList: true, + attributes: true, + subtree: true + }); + } else { + return Report.fit('body'); } }, - resize: function() { - var bubble, dy; - if (!(bubble = $('.gc-bubbleDefault'))) { + fit: function(selector) { + var dy, el; + if (!(el = $(selector, doc))) { return; } - dy = bubble.getBoundingClientRect().bottom - doc.clientHeight; + dy = el.getBoundingClientRect().bottom - doc.clientHeight + 8; if (dy > 0) { return window.resizeBy(0, dy); } }, archive: function() { - var link, message, url; + var link, message, types, url; Redirect.init(); if (!(url = Redirect.to('report', { boardID: g.BOARD.ID, @@ -15354,21 +15317,29 @@ return; } if ((message = $('h3')) && /Report submitted!/.test(message.textContent)) { - $.globalEval('self.close = function(){};'); - window.resizeTo(685, 320); - location.replace(url); + if (location.hash === '#redirect') { + $.globalEval('self.close = function(){};'); + window.resizeBy(0, 350 - doc.clientHeight); + location.replace(url); + } return; } link = $.el('a', { href: url, - textContent: 'Report to fgts' + textContent: 'Report to archive' }); $.on(link, 'click', function(e) { if (!(e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0)) { - return window.resizeTo(685, 320); + return window.resizeBy(0, 350 - doc.clientHeight); } }); - return $.add(d.body, [$.tn(' ['), link, $.tn(']')]); + $.add(d.body, [$.tn(' ['), link, $.tn(']')]); + if (types = $.id('reportTypes')) { + return $.on(types, 'change', function(e) { + var ref; + return $('form').action = (ref = e.target.value) === 'illegal' || ref === 'spam' ? '#redirect' : ''; + }); + } } }; @@ -15604,6 +15575,9 @@ div = $.el('div', { innerHTML: ": " + E(description) + "" }); + if ((typeof chrome !== "undefined" && chrome !== null) && key === 'Remember QR Size') { + div.hidden = true; + } input = $('input', div); $.on(input, 'change', function() { this.parentNode.parentNode.dataset.checked = this.checked; @@ -15870,14 +15844,14 @@ return; } $.extend(div, { - innerHTML: "
Filter is disabled.

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

    You can use these settings with each regular expression, separate them with semicolons:
  • Per boards, separate them with commas. It is global if not specified.
    For example: boards:a,jp;.
  • Filter OPs only along with their threads (\`only\`), replies only (\`no\`), or both (\`yes\`, this is default).
    For example: op:only;, op:no; or op:yes;.
  • Overrule the \`Show Stubs\` setting if specified: create a stub (\`yes\`) or not (\`no\`).
    For example: stub:yes; or stub:no;.
  • Highlight instead of hiding. You can specify a class name to use with a userstyle.
    For example: highlight; or highlight:wallpaper;.
  • Highlighted OPs will have their threads put on top of the board index by default.
    For example: top:yes; or top:no;.

Note: If you're using the native catalog rather than 4chan X's catalog, 4chan X's filters do not apply there.
The native catalog has its own separate filter list.

" + innerHTML: "
Filter is disabled.

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

    You can use these settings with each regular expression, separate them with semicolons:
  • Per boards, separate them with commas. It is global if not specified.
    For example: boards:a,jp;.
  • Filter OPs only along with their threads (\`only\`), replies only (\`no\`), or both (\`yes\`, this is default).
    For example: op:only;, op:no; or op:yes;.
  • Overrule the \`Show Stubs\` setting if specified: create a stub (\`yes\`) or not (\`no\`).
    For example: stub:yes; or stub:no;.
  • Highlight instead of hiding. You can specify a class name to use with a userstyle.
    For example: highlight; or highlight:wallpaper;.
  • Highlighted OPs will have their threads put on top of the board index by default.
    For example: top:yes; or top:no;.

Note: If you're using the native catalog rather than 4chan X's catalog, 4chan X's filters do not apply there.
The native catalog has its own separate filter list.

" }); return $('.warning', div).hidden = Conf['Filter']; }, sauce: function(section) { var ta; $.extend(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.
You can specify the applicable boards by appending ;boards:[board1],[board2].
You can specify the applicable file types by appending ;types:[extension1],[extension2].
    These parameters will be replaced by their corresponding values:
  • %TURL: Thumbnail URL.
  • %URL: Full image URL.
  • %IMG: Full image URL for GIF, JPG, and PNG; thumbnail URL for other types.
  • %MD5: MD5 hash.
  • %name: Original file name.
  • %board: Current board.
  • %%, %semi: Literal % and ;.
" + innerHTML: "
Sauce is disabled.
Lines starting with a # will be ignored.
You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards by appending ;boards:[board1],[board2].
You can specify the applicable file types by appending ;types:[extension1],[extension2].
You can open links with scripts and popups disabled by appending ;sandbox.
    These parameters will be replaced by their corresponding values:
  • %TURL: Thumbnail URL.
  • %URL: Full image URL.
  • %IMG: Full image URL for GIF, JPG, and PNG; thumbnail URL for other types.
  • %MD5: MD5 hash.
  • %name: Original file name.
  • %board: Current board.
  • %%, %semi: Literal % and ;.
" }); $('.warning', section).hidden = Conf['Sauce']; ta = $('textarea', section); @@ -15889,7 +15863,7 @@ advanced: function(section) { var aa, applyCSS, archBoards, boardID, boardOptions, boardSelect, boards, customCSS, files, i, input, inputs, interval, item, items, k, len1, len2, len3, len4, len5, len6, len7, name, o, q, ref, ref1, ref2, ref3, ref4, ref5, ref6, row, rows, software, ta, table, u, w, warning, withCredentials, y, z; $.extend(section, { - innerHTML: "
Archiver
404 Redirect is disabled.
Thread redirectionPost fetchingFile redirection
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
Archive link: g-archive
Internal archive link: g-expired
Title link: g-title
Board link (Replace with title when on that board): g-replace
Full text link: g-full
Custom text link: g-text:"Install Gentoo"
Index-only link: g-index
Catalog-only link: g-catalog
External link: external-text:"Google","http://www.google.com"
Combinations are possible: g-index-text:"Technology Index"
Full board list toggle: toggle-all

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

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

    You can use these settings with each item, separate them with semicolons:
  • Possible items are: name, options (or equivalently email), subject and password.
  • Wrap values of items with quotes, like this: options:"sage".
  • Force values as defaults with the always keyword, for example: options:"sage";always.
  • Select specific boards for an item, separated with commas, for example: options:"sage";boards:jp;always.
Unread Favicon is disabled.
Thread Updater is disabled.
Interval: seconds
Custom Cooldown Time
Seconds:
" + innerHTML: "
Archiver
404 Redirect is disabled.
Thread redirectionPost fetchingFile redirection
Captcha Language
Choose from list of language codes. Leave blank to autoselect.
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
Archive link: g-archive
Internal archive link: g-expired
Title link: g-title
Board link (Replace with title when on that board): g-replace
Full text link: g-full
Custom text link: g-text:"Install Gentoo"
Index-only link: g-index
Catalog-only link: g-catalog
External link: external-text:"Google","http://www.google.com"
Combinations are possible: g-index-text:"Technology Index"
Full board list toggle: toggle-all

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

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

    You can use these settings with each item, separate them with semicolons:
  • Possible items are: name, options (or equivalently email), subject and password.
  • Wrap values of items with quotes, like this: options:"sage".
  • Force values as defaults with the always keyword, for example: options:"sage";always.
  • Select specific boards for an item, separated with commas, for example: options:"sage";boards:jp;always.
Unread Favicon is disabled.
Thread Updater is disabled.
Interval: seconds
Custom Cooldown Time
Seconds:
" }); ref = $$('.warning', section); for (k = 0, len1 = ref.length; k < len1; k++) { @@ -15898,7 +15872,7 @@ } items = {}; inputs = {}; - ref1 = ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss', 'customCooldown']; + ref1 = ['captchaLanguage', 'boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss', 'customCooldown']; for (q = 0, len2 = ref1.length; q < len2; q++) { name = ref1[q]; input = $("[name='" + name + "']", section); @@ -15911,7 +15885,9 @@ $.on(input, 'change', Settings[name]); } else { $.on(input, 'input', $.cb.value); - $.on(input, 'input', Settings[name]); + if (name in Settings) { + $.on(input, 'input', Settings[name]); + } } } ta = $('.personafield', section); @@ -15925,10 +15901,9 @@ val = items[key]; input = inputs[key]; input.value = val; - if (key === 'usercss' || key === 'customCooldown') { - continue; + if (key in Settings && key !== 'usercss') { + Settings[key].call(input); } - Settings[key].call(input); } }); interval = $('input[name="Interval"]', section); @@ -16079,13 +16054,15 @@ data = { isReply: true, file: { - URL: '//i.4cdn.org/g/1334437723720.jpg', + url: '//i.4cdn.org/g/1334437723720.jpg', name: 'd9bb2efc98dd0df141a94399ff5880b7.jpg', size: '276 KB', sizeInBytes: 276 * 1024, dimensions: '1280x720', isImage: true, - isSpoiler: true + isVideo: false, + isSpoiler: true, + tag: 'Loop' } }; return FileInfo.format(this.value, data, this.nextElementSibling); @@ -16165,19 +16142,31 @@ if (location.hostname === 'www.google.com') { if (location.pathname === '/recaptcha/api/fallback') { $.ready(function() { - return Captcha.noscript.initFrame(); - }); - } else { - $.get('Captcha Fixes', true, function(arg) { - var enabled; - enabled = arg['Captcha Fixes']; - if (enabled) { - return $.ready(function() { - return Captcha.fixes.init(); - }); - } + return Captcha.v2.initFrame(); }); } + $.get('Captcha Fixes', true, function(arg) { + var enabled; + enabled = arg['Captcha Fixes']; + if (enabled) { + return $.ready(function() { + return Captcha.fixes.init(); + }); + } + }); + return; + } + if (location.hostname === 'www.4chan.org') { + $.onExists(d.documentElement, 'body', false, function() { + return $.addStyle(Main.cssWWW); + }); + Conf = { + 'captchaLanguage': Config.captchaLanguage + }; + $.get(Conf, function(items) { + $.extend(Conf, items); + return Captcha.language.fixPage(); + }); return; } g.threads = new SimpleDict(); @@ -16247,7 +16236,10 @@ }); }, initFeatures: function() { - var err, feature, k, len1, name, pathname, ref, ref1; + var err, feature, k, len1, name, pathname, ref, ref1, ref2; + if ((ref = location.hostname) === 'boards.4chan.org' || ref === 'sys.4chan.org') { + $.globalEval('document.documentElement.classList.add("js-enabled");'); + } switch (location.hostname) { case 'a.4cdn.org': return; @@ -16261,8 +16253,8 @@ $.asap((function() { return d.readyState !== 'loading'; }), function() { - var URL, pathname, ref, video; - if (Conf['404 Redirect'] && ((ref = d.title) === '4chan - Temporarily Offline' || ref === '4chan - 404 Not Found')) { + var URL, pathname, ref1, video; + if (Conf['404 Redirect'] && ((ref1 = d.title) === '4chan - Temporarily Offline' || ref1 === '4chan - 404 Not Found')) { Redirect.init(); pathname = location.pathname.split('/'); URL = Redirect.to('file', { @@ -16291,9 +16283,9 @@ history.replaceState(null, '', pathname.slice(0, 4).join('/') + location.hash); } } - ref = Main.features; - for (k = 0, len1 = ref.length; k < len1; k++) { - ref1 = ref[k], name = ref1[0], feature = ref1[1]; + ref1 = Main.features; + for (k = 0, len1 = ref1.length; k < len1; k++) { + ref2 = ref1[k], name = ref2[0], feature = ref2[1]; try { feature.init(); } catch (_error) { @@ -17146,6 +17138,15 @@ " 0% {transform:rotate(0deg);}\n" + " 100% {transform:rotate(359deg);}\n" + "}\n" + +"noscript > div, noscript > div > div {\n" + +" height: 545px !important;\n" + +"}\n" + +"noscript > div > div > div:first-child, noscript iframe {\n" + +" height: 423px !important;\n" + +"}\n" + +":root:not(.js-enabled) #g-recaptcha {\n" + +" height: auto;\n" + +"}\n" + "/* General */\n" + ".dialog {\n" + " border: 1px solid;\n" + @@ -17252,10 +17253,8 @@ " /* party hats */\n" + " pointer-events: none;\n" + "}\n" + -"marquee,\n" + -".postMessage marquee + br,\n" + -".postMessage marquee + br + br {\n" + -" display: none;\n" + +":root:not(.js-enabled) #postForm {\n" + +" display: table;\n" + "}\n" + "/* Anti-autoplay */\n" + "audio.controls-added {\n" + @@ -17311,6 +17310,9 @@ "#thread-watcher {\n" + " z-index: 10;\n" + "}\n" + +":root.fixed:not(.gallery-open) #header-bar:not(.autohide) {\n" + +" z-index: 5;\n" + +"}\n" + "/* Header */\n" + ".fixed.top-header body {\n" + " padding-top: 2em;\n" + @@ -17675,6 +17677,9 @@ ".section-filter textarea {\n" + " height: 500px;\n" + "}\n" + +".section-filter a, .section-advanced a {\n" + +" text-decoration: underline;\n" + +"}\n" + ".section-sauce textarea {\n" + " height: 350px;\n" + "}\n" + @@ -17998,12 +18003,15 @@ " border: none;\n" + " box-shadow: none;\n" + "}\n" + -":root.float #thread-stats > .move > span {\n" + +":root.float #thread-stats > .move > :not(#page-count) {\n" + " pointer-events: none;\n" + "}\n" + ":root.float #thread-stats {\n" + " padding: 0px 3px;\n" + "}\n" + +"#page-count {\n" + +" cursor: pointer;\n" + +"}\n" + "/* Quote */\n" + ".catalog-thread > .comment > span.quote, #arc-list span.quote {\n" + " color: #789922;\n" + @@ -18257,9 +18265,7 @@ " display: none !important;\n" + "}\n" + "#qr select,\n" + -"#url-button,\n" + -"#custom-cooldown-button,\n" + -"#dump-button,\n" + +"#qr-filename-container > a,\n" + ".remove,\n" + ".captcha-img {\n" + " cursor: pointer;\n" + @@ -18271,6 +18277,10 @@ " min-width: 300px;\n" + " border-radius: 3px 3px 0 0;\n" + "}\n" + +"#qr > form {\n" + +" max-height: calc(100vh - 75px);\n" + +" overflow-y: auto;\n" + +"}\n" + "#qrtab {\n" + " border-radius: 3px 3px 0 0;\n" + "}\n" + @@ -18281,13 +18291,6 @@ " float: right;\n" + " padding: 0 3px;\n" + "}\n" + -"#qr .warning {\n" + -" min-height: 1.6em;\n" + -" vertical-align: middle;\n" + -" padding: 0 1px;\n" + -" border-width: 1px;\n" + -" border-style: solid;\n" + -"}\n" + ".qr-link-container {\n" + " text-align: center;\n" + "}\n" + @@ -18349,23 +18352,7 @@ " position: relative;\n" + " top: 2px;\n" + "}\n" + -"/* Noscript Recaptcha */\n" + -".captcha-img {\n" + -" margin: 0px;\n" + -" text-align: center;\n" + -" background-image: #fff;\n" + -" font-size: 0px;\n" + -" min-height: 59px;\n" + -" min-width: 302px;\n" + -"}\n" + -".captcha-input{\n" + -" width: 100%;\n" + -" margin: 1px 0 0;\n" + -"}\n" + -"#qr-captcha-iframe {\n" + -" display: none;\n" + -"}\n" + -"/* Recaptcha v2 */\n" + +"/* Captcha */\n" + "#qr .captcha-root {\n" + " position: relative;\n" + "}\n" + @@ -18389,6 +18376,21 @@ " display: block;\n" + " width: 100%;\n" + "}\n" + +"#qr-captcha-iframe {\n" + +" width: 302px;\n" + +" height: 423px;\n" + +" border: 0;\n" + +" display: block;\n" + +" margin: auto;\n" + +"}\n" + +".goog-bubble-content {\n" + +" max-width: 100vw;\n" + +" max-height: 100vh;\n" + +" overflow: auto;\n" + +"}\n" + +".goog-bubble-content iframe {\n" + +" position: static !important;\n" + +"}\n" + "/* File Input, Submit Button */\n" + "#file-n-submit {\n" + " display: -webkit-flex;\n" + @@ -18402,6 +18404,7 @@ " background: linear-gradient(to bottom, #F8F8F8, #DCDCDC) no-repeat;\n" + " border: 1px solid #BBB;\n" + " border-radius: 2px;\n" + +" height: 100%;\n" + "}\n" + "#qr-file-button {\n" + " width: 15%;\n" + @@ -18579,6 +18582,8 @@ "}\n" + ".textarea {\n" + " position: relative;\n" + +" display: -webkit-flex;\n" + +" display: flex;\n" + "}\n" + ":root.webkit .textarea {\n" + " margin-bottom: -2px;\n" + @@ -18592,6 +18597,9 @@ " right: 1px;\n" + " pointer-events: none;\n" + "}\n" + +"#char-count.warning {\n" + +" color: red;\n" + +"}\n" + "/* Menu */\n" + ".menu-button:not(.fa-bars) {\n" + " display: inline-block;\n" + @@ -18614,7 +18622,7 @@ " height: 15px;\n" + " text-align: center;\n" + "}\n" + -".menu-button + .container:not(:empty) {\n" + +".menu-button + .container :first-child {\n" + " margin-left: -5px;\n" + "}\n" + "#menu {\n" + @@ -18897,13 +18905,6 @@ "}\n" + ".gal-fit-height .gal-image img,\n" + ".gal-fit-height .gal-image video {\n" + -" /*\n" + -" Chrome doesn't support viewpoint units in calc()\n" + -" http://bugs.chromium.org/168840\n" + -" \"It looks like the original author of viewport units in WebKit is not coming back to fix this stuff.\"\n" + -" Well, fuck.\n" + -" */\n" + -" max-height: 95vh;\n" + " max-height: calc(100vh - 25px);\n" + "}\n" + ".gal-image iframe {\n" + @@ -19535,7 +19536,19 @@ " font-family: sans-serif !important;\n" + " text-shadow: 1px 1px 1px rgba(0,74,153,0.6);\n" + "}", - features: [['Polyfill', Polyfill], ['Redirect', Redirect], ['Header', Header], ['Catalog Links', CatalogLinks], ['Settings', Settings], ['Index Generator', Index], ['Disable Autoplay', AntiAutoplay], ['Announcement Hiding', PSAHiding], ['Fourchan thingies', Fourchan], ['Color User IDs', IDColor], ['Highlight by User ID', IDHighlight], ['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], ['Index Generator (Menu)', Index.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], ['Volume Control', Volume], ['WEBM Metadata', Metadata], ['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], ['Mark New IPs', MarkNewIPs], ['Index Navigation', Nav], ['Keybinds', Keybinds], ['Banner', Banner], ['Flash Features', Flash]] + cssWWW: "noscript > div, noscript > div > div {\n" + +" height: 545px !important;\n" + +"}\n" + +"noscript > div > div > div:first-child, noscript iframe {\n" + +" height: 423px !important;\n" + +"}\n" + +":root:not(.js-enabled) #g-recaptcha {\n" + +" height: auto;\n" + +"}\n" + +"#captcha-cnt {\n" + +" height: auto;\n" + +"}", + features: [['Polyfill', Polyfill], ['Captcha Language', Captcha.language], ['Redirect', Redirect], ['Header', Header], ['Catalog Links', CatalogLinks], ['Settings', Settings], ['Index Generator', Index], ['Disable Autoplay', AntiAutoplay], ['Announcement Hiding', PSAHiding], ['Fourchan thingies', Fourchan], ['Color User IDs', IDColor], ['Highlight by User ID', IDHighlight], ['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], ['Index Generator (Menu)', Index.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], ['Volume Control', Volume], ['WEBM Metadata', Metadata], ['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], ['Mark New IPs', MarkNewIPs], ['Index Navigation', Nav], ['Keybinds', Keybinds], ['Banner', Banner], ['Flash Features', Flash]] }; Main.init(); diff --git a/builds/4chan-X.zip b/builds/4chan-X.zip index 38be0486d..32aaa06aa 100644 Binary files a/builds/4chan-X.zip and b/builds/4chan-X.zip differ diff --git a/builds/updates-beta.xml b/builds/updates-beta.xml index d95aabceb..a4e215c31 100644 --- a/builds/updates-beta.xml +++ b/builds/updates-beta.xml @@ -1,7 +1,7 @@ - + diff --git a/builds/updates.xml b/builds/updates.xml index 8fee44d97..c9952f5c6 100644 --- a/builds/updates.xml +++ b/builds/updates.xml @@ -1,7 +1,7 @@ - + diff --git a/index.html b/index.html index a362e7f25..7527918b3 100644 --- a/index.html +++ b/index.html @@ -37,7 +37,8 @@ Only the latest stable version of 4chan X is available.

Other browsers

This fork of 4chan X is not guaranteed to work correctly in other browsers, but you are welcome to try your luck. Pull requests to fix the bugs you will likely find are always welcome. You may fare better with loadletter's fork, which has fewer features but less dependence on browser-specific APIs.

Beta version

New features and non-urgent bugfixes are released on the beta channel for further testing before they are moved the stable version. Please report any issues you find, and be sure to mention which version you're using. You should back up your settings regularly to prevent them from being lost due to bugs.

diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 686d074f5..14a25f054 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,7 +1,7 @@ { "name": "4chan-X", - "npm-shrinkwrap-version": "5.3.0", - "node-version": "v0.10.29", + "npm-shrinkwrap-version": "5.4.0", + "node-version": "v0.10.38", "dependencies": { "crx": { "version": "3.0.2", @@ -269,6 +269,90 @@ } } }, + "grunt-contrib-jshint": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.6.5.tgz", + "dependencies": { + "jshint": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.1.11.tgz", + "dependencies": { + "cli": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz" + }, + "console-browserify": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz" + }, + "shelljs": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz" + }, + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz" + } + } + } + } + }, + "grunt-contrib-watch": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.5.3.tgz", + "dependencies": { + "gaze": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.4.3.tgz", + "dependencies": { + "globule": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", + "dependencies": { + "lodash": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz" + } + } + } + } + }, + "tiny-lr": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.0.4.tgz", + "dependencies": { + "debug": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz" + }, + "faye-websocket": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz" + }, + "noptify": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz", + "dependencies": { + "nopt": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.6.tgz" + } + } + } + } + }, + "qs": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz" + } + } + } + } + }, "grunt-legacy-log": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.1.tgz", @@ -403,6 +487,404 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.6.0.tgz", "dependencies": { + "grunt": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz", + "dependencies": { + "async": { + "version": "0.1.22", + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz" + }, + "coffee-script": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz" + }, + "colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz" + }, + "dateformat": { + "version": "1.0.2-1.2.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz" + }, + "eventemitter2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz" + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + }, + "findup-sync": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz" + }, + "sigmund": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + } + } + }, + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + } + } + }, + "getobject": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz" + }, + "glob": { + "version": "3.1.21", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "dependencies": { + "graceful-fs": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" + }, + "inherits": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz" + } + } + }, + "grunt-contrib-jshint": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.6.5.tgz", + "dependencies": { + "jshint": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.1.11.tgz", + "dependencies": { + "cli": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz" + }, + "console-browserify": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz" + }, + "shelljs": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz" + }, + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz" + } + } + } + } + }, + "grunt-contrib-watch": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.5.3.tgz", + "dependencies": { + "gaze": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.4.3.tgz", + "dependencies": { + "globule": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", + "dependencies": { + "lodash": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz" + } + } + } + } + }, + "tiny-lr": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.0.4.tgz", + "dependencies": { + "debug": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz" + }, + "faye-websocket": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz" + }, + "noptify": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz", + "dependencies": { + "nopt": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.6.tgz" + } + } + } + } + }, + "qs": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz" + } + } + } + } + }, + "grunt-legacy-log": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.1.tgz", + "dependencies": { + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + }, + "underscore.string": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz" + } + } + }, + "grunt-legacy-util": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz" + }, + "hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + }, + "iconv-lite": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz" + }, + "js-yaml": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz", + "dependencies": { + "argparse": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", + "dependencies": { + "underscore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + }, + "underscore.string": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz" + } + } + }, + "esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz" + } + } + }, + "lodash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz" + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz" + }, + "sigmund": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz" + } + } + }, + "underscore.string": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz" + }, + "which": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz" + } + } + }, + "grunt-contrib-jshint": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.10.0.tgz", + "dependencies": { + "hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + }, + "jshint": { + "version": "2.5.11", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.5.11.tgz", + "dependencies": { + "cli": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + } + } + } + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "dependencies": { + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz" + } + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + }, + "htmlparser2": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.2.tgz", + "dependencies": { + "domelementtype": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz" + }, + "domhandler": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz" + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "dependencies": { + "dom-serializer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz" + }, + "entities": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz" + } + } + } + } + }, + "entities": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz" + }, + "readable-stream": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + } + } + } + } + }, + "minimatch": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + }, + "shelljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz" + }, + "strip-json-comments": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.2.tgz" + }, + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" + } + } + } + } + }, "rimraf": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" @@ -455,6 +937,414 @@ "version": "1.9.1", "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.9.1.tgz" }, + "grunt": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz", + "dependencies": { + "async": { + "version": "0.1.22", + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz" + }, + "coffee-script": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz" + }, + "colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz" + }, + "dateformat": { + "version": "1.0.2-1.2.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz" + }, + "eventemitter2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz" + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + }, + "findup-sync": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz" + }, + "sigmund": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + } + } + }, + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + } + } + }, + "getobject": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz" + }, + "glob": { + "version": "3.1.21", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "dependencies": { + "graceful-fs": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" + }, + "inherits": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz" + } + } + }, + "grunt-contrib-jshint": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.6.5.tgz", + "dependencies": { + "jshint": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.1.11.tgz", + "dependencies": { + "cli": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz" + }, + "console-browserify": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz" + }, + "shelljs": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz" + }, + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz" + } + } + } + } + }, + "grunt-contrib-watch": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.5.3.tgz", + "dependencies": { + "gaze": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.4.3.tgz", + "dependencies": { + "globule": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", + "dependencies": { + "lodash": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz" + } + } + } + } + }, + "tiny-lr": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.0.4.tgz", + "dependencies": { + "debug": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz" + }, + "faye-websocket": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz" + }, + "noptify": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz", + "dependencies": { + "nopt": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.6.tgz" + } + } + } + } + }, + "qs": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz" + } + } + } + } + }, + "grunt-legacy-log": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.1.tgz", + "dependencies": { + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + }, + "underscore.string": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz" + } + } + }, + "grunt-legacy-util": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz" + }, + "hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + }, + "iconv-lite": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz" + }, + "js-yaml": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz", + "dependencies": { + "argparse": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", + "dependencies": { + "underscore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + }, + "underscore.string": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz" + } + } + }, + "esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz" + } + } + }, + "lodash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz" + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz" + }, + "sigmund": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz" + } + } + }, + "rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" + }, + "underscore.string": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz" + }, + "which": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz" + } + } + }, + "grunt-contrib-jshint": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.11.2.tgz", + "dependencies": { + "hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + }, + "jshint": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.7.0.tgz", + "dependencies": { + "cli": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz" + }, + "sigmund": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + } + } + } + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "dependencies": { + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz" + } + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + }, + "htmlparser2": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.2.tgz", + "dependencies": { + "domelementtype": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz" + }, + "domhandler": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz" + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "dependencies": { + "dom-serializer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz" + }, + "entities": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz" + } + } + } + } + }, + "entities": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz" + }, + "readable-stream": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + } + } + } + } + }, + "lodash": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.6.0.tgz" + }, + "minimatch": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.7.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "shelljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz" + }, + "strip-json-comments": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.2.tgz" + } + } + } + } + }, "lodash": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.3.0.tgz" @@ -507,6 +1397,414 @@ } } }, + "grunt": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz", + "dependencies": { + "async": { + "version": "0.1.22", + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz" + }, + "coffee-script": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz" + }, + "colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz" + }, + "dateformat": { + "version": "1.0.2-1.2.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz" + }, + "eventemitter2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz" + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + }, + "findup-sync": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz" + }, + "sigmund": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + } + } + }, + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + } + } + }, + "getobject": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz" + }, + "glob": { + "version": "3.1.21", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "dependencies": { + "graceful-fs": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" + }, + "inherits": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz" + } + } + }, + "grunt-contrib-jshint": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.6.5.tgz", + "dependencies": { + "jshint": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.1.11.tgz", + "dependencies": { + "cli": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz" + }, + "console-browserify": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz" + }, + "shelljs": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz" + }, + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz" + } + } + } + } + }, + "grunt-contrib-watch": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.5.3.tgz", + "dependencies": { + "gaze": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.4.3.tgz", + "dependencies": { + "globule": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", + "dependencies": { + "lodash": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz" + } + } + } + } + }, + "tiny-lr": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.0.4.tgz", + "dependencies": { + "debug": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz" + }, + "faye-websocket": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz" + }, + "noptify": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz", + "dependencies": { + "nopt": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.6.tgz" + } + } + } + } + }, + "qs": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz" + } + } + } + } + }, + "grunt-legacy-log": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.1.tgz", + "dependencies": { + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + }, + "underscore.string": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz" + } + } + }, + "grunt-legacy-util": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz" + }, + "hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + }, + "iconv-lite": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz" + }, + "js-yaml": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz", + "dependencies": { + "argparse": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", + "dependencies": { + "underscore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + }, + "underscore.string": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz" + } + } + }, + "esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz" + } + } + }, + "lodash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz" + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz" + }, + "sigmund": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz" + } + } + }, + "rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" + }, + "underscore.string": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz" + }, + "which": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz" + } + } + }, + "grunt-contrib-jshint": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.11.2.tgz", + "dependencies": { + "hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + }, + "jshint": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.7.0.tgz", + "dependencies": { + "cli": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz" + }, + "sigmund": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + } + } + } + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "dependencies": { + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz" + } + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + }, + "htmlparser2": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.2.tgz", + "dependencies": { + "domelementtype": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz" + }, + "domhandler": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz" + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "dependencies": { + "dom-serializer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz" + }, + "entities": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz" + } + } + } + } + }, + "entities": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz" + }, + "readable-stream": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + } + } + } + } + }, + "lodash": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.6.0.tgz" + }, + "minimatch": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.7.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "shelljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz" + }, + "strip-json-comments": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.2.tgz" + } + } + } + } + }, "source-map": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.3.0.tgz", @@ -564,24 +1862,38 @@ "file-sync-cmp": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz" - } - } - }, - "grunt-contrib-jshint": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.11.1.tgz", - "dependencies": { - "hooker": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" }, - "jshint": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.6.3.tgz", + "grunt": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz", "dependencies": { - "cli": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.5.tgz", + "async": { + "version": "0.1.22", + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz" + }, + "coffee-script": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz" + }, + "colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz" + }, + "dateformat": { + "version": "1.0.2-1.2.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz" + }, + "eventemitter2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz" + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + }, + "findup-sync": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", "dependencies": { "glob": { "version": "3.2.11", @@ -596,8 +1908,402 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", "dependencies": { "lru-cache": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz" + }, + "sigmund": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + } + } + }, + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + } + } + }, + "getobject": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz" + }, + "glob": { + "version": "3.1.21", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "dependencies": { + "graceful-fs": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" + }, + "inherits": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz" + } + } + }, + "grunt-contrib-jshint": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.6.5.tgz", + "dependencies": { + "jshint": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.1.11.tgz", + "dependencies": { + "cli": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz" + }, + "console-browserify": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz" + }, + "shelljs": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz" + }, + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz" + } + } + } + } + }, + "grunt-contrib-watch": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.5.3.tgz", + "dependencies": { + "gaze": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.4.3.tgz", + "dependencies": { + "globule": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", + "dependencies": { + "lodash": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz" + } + } + } + } + }, + "tiny-lr": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.0.4.tgz", + "dependencies": { + "debug": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz" + }, + "faye-websocket": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz" + }, + "noptify": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz", + "dependencies": { + "nopt": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.6.tgz" + } + } + } + } + }, + "qs": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz" + } + } + } + } + }, + "grunt-legacy-log": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.1.tgz", + "dependencies": { + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + }, + "underscore.string": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz" + } + } + }, + "grunt-legacy-util": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz" + }, + "hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + }, + "iconv-lite": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz" + }, + "js-yaml": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz", + "dependencies": { + "argparse": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", + "dependencies": { + "underscore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + }, + "underscore.string": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz" + } + } + }, + "esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz" + } + } + }, + "lodash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz" + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz" + }, + "sigmund": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz" + } + } + }, + "rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" + }, + "underscore.string": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz" + }, + "which": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz" + } + } + }, + "grunt-contrib-jshint": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.11.2.tgz", + "dependencies": { + "hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + }, + "jshint": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.7.0.tgz", + "dependencies": { + "cli": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz" + }, + "sigmund": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + } + } + } + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "dependencies": { + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz" + } + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + }, + "htmlparser2": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.2.tgz", + "dependencies": { + "domelementtype": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz" + }, + "domhandler": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz" + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "dependencies": { + "dom-serializer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz" + }, + "entities": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz" + } + } + } + } + }, + "entities": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz" + }, + "readable-stream": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + } + } + } + } + }, + "lodash": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.6.0.tgz" + }, + "minimatch": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.7.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "shelljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz" + }, + "strip-json-comments": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.2.tgz" + } + } + } + } + } + } + }, + "grunt-contrib-jshint": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.11.2.tgz", + "dependencies": { + "hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + }, + "jshint": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.7.0.tgz", + "dependencies": { + "cli": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz" }, "sigmund": { "version": "1.0.0", @@ -683,17 +2389,27 @@ } } }, + "lodash": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.6.0.tgz" + }, "minimatch": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.7.tgz", "dependencies": { - "lru-cache": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" - }, - "sigmund": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + "brace-expansion": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } } } }, @@ -704,10 +2420,6 @@ "strip-json-comments": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.2.tgz" - }, - "underscore": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" } } } @@ -765,6 +2477,404 @@ } } }, + "grunt": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz", + "dependencies": { + "async": { + "version": "0.1.22", + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz" + }, + "coffee-script": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz" + }, + "colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz" + }, + "dateformat": { + "version": "1.0.2-1.2.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz" + }, + "eventemitter2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz" + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + }, + "findup-sync": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz" + }, + "sigmund": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + } + } + }, + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + } + } + }, + "getobject": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz" + }, + "glob": { + "version": "3.1.21", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "dependencies": { + "graceful-fs": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" + }, + "inherits": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz" + } + } + }, + "grunt-contrib-jshint": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.6.5.tgz", + "dependencies": { + "jshint": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.1.11.tgz", + "dependencies": { + "cli": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz" + }, + "console-browserify": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz" + }, + "shelljs": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz" + }, + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz" + } + } + } + } + }, + "grunt-contrib-watch": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.5.3.tgz", + "dependencies": { + "gaze": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.4.3.tgz", + "dependencies": { + "globule": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", + "dependencies": { + "lodash": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz" + } + } + } + } + }, + "tiny-lr": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.0.4.tgz", + "dependencies": { + "debug": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz" + }, + "faye-websocket": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz" + }, + "noptify": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz", + "dependencies": { + "nopt": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.6.tgz" + } + } + } + } + }, + "qs": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz" + } + } + } + } + }, + "grunt-legacy-log": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.1.tgz", + "dependencies": { + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + }, + "underscore.string": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz" + } + } + }, + "grunt-legacy-util": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz" + }, + "hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + }, + "iconv-lite": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz" + }, + "js-yaml": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz", + "dependencies": { + "argparse": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", + "dependencies": { + "underscore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + }, + "underscore.string": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz" + } + } + }, + "esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz" + } + } + }, + "lodash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz" + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz" + }, + "sigmund": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz" + } + } + }, + "rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" + }, + "underscore.string": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz" + }, + "which": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz" + } + } + }, + "grunt-contrib-jshint": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.8.0.tgz", + "dependencies": { + "jshint": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.4.4.tgz", + "dependencies": { + "cli": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz", + "dependencies": { + "glob": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.9.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.8.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "once": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "path-is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + } + } + } + } + }, + "console-browserify": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz" + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + }, + "htmlparser2": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", + "dependencies": { + "domelementtype": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz" + }, + "domhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz" + }, + "domutils": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz" + }, + "readable-stream": { + "version": "1.0.33", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + } + } + } + } + }, + "minimatch": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.4.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + }, + "shelljs": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz" + }, + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz" + } + } + } + } + }, "lodash": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz" @@ -809,6 +2919,421 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/grunt-markdown/-/grunt-markdown-0.7.0.tgz", "dependencies": { + "grunt": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz", + "dependencies": { + "async": { + "version": "0.1.22", + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz" + }, + "coffee-script": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz" + }, + "colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz" + }, + "dateformat": { + "version": "1.0.2-1.2.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz" + }, + "eventemitter2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz" + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + }, + "findup-sync": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz" + }, + "sigmund": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + } + } + }, + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + } + } + }, + "getobject": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz" + }, + "glob": { + "version": "3.1.21", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "dependencies": { + "graceful-fs": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" + }, + "inherits": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz" + } + } + }, + "grunt-contrib-jshint": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.6.5.tgz", + "dependencies": { + "jshint": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.1.11.tgz", + "dependencies": { + "cli": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz" + }, + "console-browserify": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz" + }, + "shelljs": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz" + }, + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz" + } + } + } + } + }, + "grunt-contrib-watch": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.5.3.tgz", + "dependencies": { + "gaze": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.4.3.tgz", + "dependencies": { + "globule": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", + "dependencies": { + "lodash": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz" + } + } + } + } + }, + "tiny-lr": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.0.4.tgz", + "dependencies": { + "debug": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz" + }, + "faye-websocket": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz" + }, + "noptify": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz", + "dependencies": { + "nopt": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.6.tgz" + } + } + } + } + }, + "qs": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz" + } + } + } + } + }, + "grunt-legacy-log": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.1.tgz", + "dependencies": { + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + }, + "underscore.string": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz" + } + } + }, + "grunt-legacy-util": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz" + }, + "hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + }, + "iconv-lite": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz" + }, + "js-yaml": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz", + "dependencies": { + "argparse": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", + "dependencies": { + "underscore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + }, + "underscore.string": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz" + } + } + }, + "esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz" + } + } + }, + "lodash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz" + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz" + }, + "sigmund": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz" + } + } + }, + "rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" + }, + "underscore.string": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz" + }, + "which": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz" + } + } + }, + "grunt-contrib-jshint": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.3.0.tgz", + "dependencies": { + "jshint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-1.1.0.tgz", + "dependencies": { + "cli": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz", + "dependencies": { + "glob": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.9.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.8.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "once": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "path-is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + } + } + } + } + }, + "esprima": { + "version": "2.2.0", + "from": "esprima@https://github.com/ariya/esprima/tarball/master", + "resolved": "https://github.com/ariya/esprima/tarball/master" + }, + "minimatch": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.4.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + }, + "peakle": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/peakle/-/peakle-0.0.1.tgz" + }, + "shelljs": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz" + }, + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz" + } + } + } + } + }, + "grunt-contrib-watch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.3.1.tgz", + "dependencies": { + "gaze": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.3.4.tgz", + "dependencies": { + "fileset": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/fileset/-/fileset-0.1.5.tgz", + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + } + } + } + } + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + } + } + } + } + }, "highlight.js": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-8.4.0.tgz" @@ -875,6 +3400,494 @@ "version": "0.8.2", "resolved": "https://registry.npmjs.org/grunt-webstore-upload/-/grunt-webstore-upload-0.8.2.tgz", "dependencies": { + "grunt": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz", + "dependencies": { + "async": { + "version": "0.1.22", + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz" + }, + "coffee-script": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz" + }, + "colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz" + }, + "dateformat": { + "version": "1.0.2-1.2.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz" + }, + "eventemitter2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz" + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + }, + "findup-sync": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz" + }, + "sigmund": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + } + } + }, + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + } + } + }, + "getobject": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz" + }, + "glob": { + "version": "3.1.21", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "dependencies": { + "graceful-fs": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" + }, + "inherits": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz" + } + } + }, + "grunt-contrib-jshint": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.6.5.tgz", + "dependencies": { + "jshint": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.1.11.tgz", + "dependencies": { + "cli": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz" + }, + "console-browserify": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz" + }, + "shelljs": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz" + }, + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz" + } + } + } + } + }, + "grunt-contrib-watch": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.5.3.tgz", + "dependencies": { + "gaze": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.4.3.tgz", + "dependencies": { + "globule": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", + "dependencies": { + "lodash": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz" + } + } + } + } + }, + "tiny-lr": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.0.4.tgz", + "dependencies": { + "debug": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz" + }, + "faye-websocket": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz" + }, + "noptify": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz", + "dependencies": { + "nopt": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.6.tgz" + } + } + } + } + }, + "qs": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz" + } + } + } + } + }, + "grunt-legacy-log": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.1.tgz", + "dependencies": { + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + }, + "underscore.string": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz" + } + } + }, + "grunt-legacy-util": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz" + }, + "hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + }, + "iconv-lite": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz" + }, + "js-yaml": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz", + "dependencies": { + "argparse": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", + "dependencies": { + "underscore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + }, + "underscore.string": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz" + } + } + }, + "esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz" + } + } + }, + "lodash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz" + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz" + }, + "sigmund": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz" + } + } + }, + "rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" + }, + "underscore.string": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz" + }, + "which": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz" + } + } + }, + "grunt-contrib-clean": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.5.0.tgz", + "dependencies": { + "rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" + } + } + }, + "grunt-contrib-jshint": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.8.0.tgz", + "dependencies": { + "jshint": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.4.4.tgz", + "dependencies": { + "cli": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz", + "dependencies": { + "glob": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.9.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.8.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "once": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "path-is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + } + } + } + } + }, + "console-browserify": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz" + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + }, + "htmlparser2": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", + "dependencies": { + "domelementtype": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz" + }, + "domhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz" + }, + "domutils": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz" + }, + "readable-stream": { + "version": "1.0.33", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + } + } + } + } + }, + "minimatch": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.4.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + }, + "shelljs": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz" + }, + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz" + } + } + } + } + }, + "load-grunt-tasks": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/load-grunt-tasks/-/load-grunt-tasks-0.3.0.tgz", + "dependencies": { + "findup-sync": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + } + } + } + } + }, + "globule": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.2.0.tgz", + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + } + } + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + } + } + } + } + }, "lodash": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz" @@ -893,6 +3906,408 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/jszip/-/jszip-2.5.0.tgz", "dependencies": { + "grunt": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz", + "dependencies": { + "async": { + "version": "0.1.22", + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz" + }, + "coffee-script": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz" + }, + "colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz" + }, + "dateformat": { + "version": "1.0.2-1.2.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz" + }, + "eventemitter2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz" + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + }, + "findup-sync": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz" + }, + "sigmund": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + } + } + }, + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + } + } + }, + "getobject": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz" + }, + "glob": { + "version": "3.1.21", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "dependencies": { + "graceful-fs": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" + }, + "inherits": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz" + } + } + }, + "grunt-contrib-jshint": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.6.5.tgz", + "dependencies": { + "jshint": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.1.11.tgz", + "dependencies": { + "cli": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz" + }, + "console-browserify": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz" + }, + "shelljs": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz" + }, + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz" + } + } + } + } + }, + "grunt-contrib-watch": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.5.3.tgz", + "dependencies": { + "gaze": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.4.3.tgz", + "dependencies": { + "globule": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", + "dependencies": { + "lodash": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz" + } + } + } + } + }, + "tiny-lr": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.0.4.tgz", + "dependencies": { + "debug": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz" + }, + "faye-websocket": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz" + }, + "noptify": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz", + "dependencies": { + "nopt": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.6.tgz" + } + } + } + } + }, + "qs": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz" + } + } + } + } + }, + "grunt-legacy-log": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.1.tgz", + "dependencies": { + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + }, + "underscore.string": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz" + } + } + }, + "grunt-legacy-util": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz" + }, + "hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + }, + "iconv-lite": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz" + }, + "js-yaml": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz", + "dependencies": { + "argparse": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", + "dependencies": { + "underscore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + }, + "underscore.string": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz" + } + } + }, + "esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz" + } + } + }, + "lodash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz" + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz" + }, + "sigmund": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz" + } + } + }, + "rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" + }, + "underscore.string": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz" + }, + "which": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz" + } + } + }, + "grunt-contrib-jshint": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.10.0.tgz", + "dependencies": { + "hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + }, + "jshint": { + "version": "2.5.11", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.5.11.tgz", + "dependencies": { + "cli": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + } + } + } + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "dependencies": { + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz" + } + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + }, + "htmlparser2": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.2.tgz", + "dependencies": { + "domelementtype": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz" + }, + "domhandler": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz" + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "dependencies": { + "dom-serializer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz" + }, + "entities": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz" + } + } + } + } + }, + "entities": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz" + }, + "readable-stream": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + } + } + } + } + }, + "minimatch": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + }, + "shelljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz" + }, + "strip-json-comments": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.2.tgz" + }, + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" + } + } + } + } + }, "pako": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.6.tgz" @@ -900,8 +4315,8 @@ } }, "load-grunt-tasks": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/load-grunt-tasks/-/load-grunt-tasks-3.1.0.tgz", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/load-grunt-tasks/-/load-grunt-tasks-3.2.0.tgz", "dependencies": { "findup-sync": { "version": "0.2.1", @@ -926,8 +4341,8 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "minimatch": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.1.tgz", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.8.tgz", "dependencies": { "brace-expansion": { "version": "1.1.0", @@ -946,8 +4361,8 @@ } }, "once": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.1.tgz", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", "dependencies": { "wrappy": { "version": "1.0.1", @@ -978,8 +4393,8 @@ } }, "minimatch": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.1.tgz", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.8.tgz", "dependencies": { "brace-expansion": { "version": "1.1.0", @@ -1002,8 +4417,8 @@ } }, "npm-shrinkwrap": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-shrinkwrap/-/npm-shrinkwrap-5.3.0.tgz", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/npm-shrinkwrap/-/npm-shrinkwrap-5.4.0.tgz", "dependencies": { "array-find": { "version": "0.1.1", @@ -1548,8 +4963,8 @@ "resolved": "https://registry.npmjs.org/read-json/-/read-json-0.1.0.tgz" }, "rimraf": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.3.2.tgz", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.3.3.tgz", "dependencies": { "glob": { "version": "4.5.3", @@ -1570,8 +4985,8 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "minimatch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.4.tgz", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.7.tgz", "dependencies": { "brace-expansion": { "version": "1.1.0", @@ -1648,8 +5063,8 @@ "resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-2.0.0.tgz" }, "semver": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.1.tgz" + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.3.tgz" }, "sorted-object": { "version": "1.0.0", diff --git a/package.json b/package.json index e3f2a0116..150be93eb 100755 --- a/package.json +++ b/package.json @@ -3,8 +3,8 @@ "description": "Cross-browser userscript for maximum lurking on 4chan.", "meta": { "name": "4chan X", - "version": "1.10.10.0", - "date": "2015-04-18T07:45:01.778Z", + "version": "1.11.0.6", + "date": "2015-06-21T06:05:51.745Z", "repo": "https://github.com/ccd0/4chan-x/", "page": "https://github.com/ccd0/4chan-x", "downloads": "https://ccd0.github.io/4chan-x/builds/", @@ -21,9 +21,11 @@ "*://sys.4chan.org/*", "*://a.4cdn.org/*", "*://i.4cdn.org/*", + "*://www.4chan.org/banned", + "*://www.4chan.org/feedback", "https://www.google.com/recaptcha/api2/anchor?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc*", "https://www.google.com/recaptcha/api2/frame?*&k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc*", - "*://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc" + "*://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc*" ], "suffix": { "stable": "", @@ -38,7 +40,7 @@ "dev": " dev" }, "min": { - "chrome": "32", + "chrome": "34", "firefox": "26", "greasemonkey": "1.14" } @@ -52,14 +54,14 @@ "grunt-contrib-coffee": "^0.13.0", "grunt-contrib-concat": "^0.5.1", "grunt-contrib-copy": "^0.8.0", - "grunt-contrib-jshint": "^0.11.1", + "grunt-contrib-jshint": "^0.11.2", "grunt-contrib-watch": "^0.6.1", "grunt-markdown": "^0.7.0", "grunt-shell": "^1.1.2", "grunt-webstore-upload": "^0.8.2", "jszip": "^2.5.0", - "load-grunt-tasks": "^3.1.0", - "npm-shrinkwrap": "^5.3.0" + "load-grunt-tasks": "^3.2.0", + "npm-shrinkwrap": "^5.4.0" }, "repository": { "type": "git", diff --git a/src/Archive/archives.json b/src/Archive/archives.json index 6a0640044..f1a0e0bd5 100644 --- a/src/Archive/archives.json +++ b/src/Archive/archives.json @@ -5,7 +5,7 @@ "http": false, "https": true, "software": "foolfuuka", - "boards": ["a", "biz", "c", "co", "diy", "fit", "gd", "gif", "h", "i", "int", "jp", "k", "m", "mlp", "out", "po", "qa", "r9k", "s4s", "sci", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"], + "boards": ["a", "an", "biz", "c", "co", "diy", "fit", "gd", "gif", "h", "i", "int", "jp", "k", "m", "mlp", "out", "po", "qa", "r9k", "s4s", "sci", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"], "files": ["a", "biz", "c", "co", "diy", "fit", "gd", "h", "i", "jp", "k", "m", "mlp", "po", "qa", "r9k", "s4s", "sci", "tg", "u", "v", "vg", "vp", "vr", "wsg"] }, { "uid": 3, @@ -32,8 +32,8 @@ "http": true, "https": true, "software": "foolfuuka", - "boards": ["c", "d", "e", "i", "lgbt", "t", "u", "w", "wg"], - "files": ["c", "d", "e", "i", "lgbt", "t", "u", "w", "wg"] + "boards": ["c", "d", "e", "i", "lgbt", "t", "u"], + "files": ["c", "d", "e", "i", "lgbt", "t", "u"] }, { "uid": 8, "name": "Rebecca Black Tech", diff --git a/src/Filtering/Filter.coffee b/src/Filtering/Filter.coffee index a81f419c8..d1da7e139 100755 --- a/src/Filtering/Filter.coffee +++ b/src/Filtering/Filter.coffee @@ -17,12 +17,10 @@ Filter = # Don't mix up filter flags with the regular expression. filter = line.replace regexp[0], '' - # Do not add this filter to the list if it's not a global one - # and it's not specifically applicable to the current board. + # Comma-separated list of the boards this filter applies to. # Defaults to global. boards = filter.match(/boards:([^;]+)/)?[1].toLowerCase() or 'global' - if boards isnt 'global' and g.BOARD.ID not in boards.split ',' - continue + boards = if boards is 'global' then null else boards.split(',') if key in ['uniqueID', 'MD5'] # MD5 filter will use strings instead of regular expressions. @@ -66,7 +64,7 @@ Filter = top = filter.match(/top:(yes|no)/)?[1] or 'yes' top = top is 'yes' # Turn it into a boolean - @filters[key].push @createFilter regexp, op, stub, hl, top + @filters[key].push @createFilter regexp, boards, op, stub, hl, top # Only execute filter types that contain valid filters. unless @filters[key].length @@ -77,7 +75,7 @@ Filter = name: 'Filter' cb: @node - createFilter: (regexp, op, stub, hl, top) -> + createFilter: (regexp, boards, op, stub, hl, top) -> test = if typeof regexp is 'string' # MD5 checking @@ -91,7 +89,9 @@ Filter = class: hl top: top - (value, isReply) -> + (value, boardID, isReply) -> + if boards and boardID not in boards + return false if isReply and op is 'only' or !isReply and op is 'no' return false unless test value @@ -103,7 +103,7 @@ Filter = for key of Filter.filters when (value = Filter[key] @)? # Continue if there's nothing to filter (no tripcode for example). - for filter in Filter.filters[key] when result = filter value, @isReply + for filter in Filter.filters[key] when result = filter value, @board.ID, @isReply # Hide if result.hide and not @isFetchedQuote if @isReply @@ -121,12 +121,18 @@ Filter = if !@isReply and result.top @thread.isOnTop = true + isHidden: (post) -> + for key of Filter.filters when (value = Filter[key] post)? + for filter in Filter.filters[key] when result = filter value, post.boardID, post.isReply + return true if result.hide + false + name: (post) -> post.info.name uniqueID: (post) -> post.info.uniqueID tripcode: (post) -> post.info.tripcode capcode: (post) -> post.info.capcode - subject: (post) -> post.info.subject or undefined - comment: (post) -> post.info.comment + subject: (post) -> post.info.subject + comment: (post) -> post.info.comment ? Build.parseComment(post) flag: (post) -> post.info.flag filename: (post) -> post.file?.name dimensions: (post) -> post.file?.dimensions diff --git a/src/General/Build.coffee b/src/General/Build.coffee index 7bbe30d0e..0b76f84e1 100755 --- a/src/General/Build.coffee +++ b/src/General/Build.coffee @@ -2,10 +2,12 @@ Build = staticPath: '//s.4cdn.org/image/' gifIcon: if window.devicePixelRatio >= 2 then '@2x.gif' else '.gif' spoilerRange: {} + unescape: (text) -> return text unless text? - text.replace(/<[^>]*>/g, '').replace /&(amp|#039|quot|lt|gt);/g, (c) -> - {'&': '&', ''': "'", '"': '"', '<': '<', '>': '>'}[c] + text.replace(/<[^>]*>/g, '').replace /&(amp|#039|quot|lt|gt|#44);/g, (c) -> + {'&': '&', ''': "'", '"': '"', '<': '<', '>': '>', ',': ','}[c] + shortFilename: (filename) -> threshold = 30 ext = filename.match(/\.?[^\.]*$/)[0] @@ -13,93 +15,100 @@ Build = "#{filename[...threshold - 5]}(...)#{ext}" else filename + spoilerThumb: (boardID) -> if spoilerRange = Build.spoilerRange[boardID] # Randomize the spoiler image. "#{Build.staticPath}spoiler-#{boardID}#{Math.floor 1 + spoilerRange * Math.random()}.png" else "#{Build.staticPath}spoiler.png" + sameThread: (boardID, threadID) -> g.VIEW is 'thread' and g.BOARD.ID is boardID and g.THREADID is +threadID + postURL: (boardID, threadID, postID) -> if Build.sameThread boardID, threadID "#p#{postID}" else "/#{boardID}/thread/#{threadID}#p#{postID}" - postFromObject: (data, boardID, suppressThumb) -> + + parseJSON: (data, boardID) -> o = # id postID: data.no threadID: data.resto or data.no boardID: boardID - # info - name: Build.unescape data.name - capcode: data.capcode - tripcode: data.trip - uniqueID: data.id - email: Build.unescape data.email - subject: Build.unescape data.sub - flagCode: data.country - flagName: Build.unescape data.country_name - date: data.now - dateUTC: data.time - comment: {innerHTML: data.com or ''} + isReply: !!data.resto # thread status isSticky: !!data.sticky isClosed: !!data.closed isArchived: !!data.archived - # file - if data.filedeleted - o.file = - isDeleted: true - else if data.ext + # file status + fileDeleted: !!data.filedeleted + o.info = + subject: Build.unescape data.sub + email: Build.unescape data.email + name: Build.unescape(data.name) or '' + tripcode: data.trip + uniqueID: data.id + flagCode: data.country + flag: Build.unescape data.country_name + dateUTC: data.time + dateText: data.now + commentHTML: {innerHTML: data.com or ''} + if data.capcode + o.info.capcode = data.capcode.replace(/_highlight$/, '').replace(/_/g, ' ').replace(/\b\w/g, (c) -> c.toUpperCase()) + o.capcodeHighlight = /_highlight$/.test data.capcode + delete o.info.uniqueID + if data.ext o.file = name: (Build.unescape data.filename) + data.ext - timestamp: "#{data.tim}#{data.ext}" url: if boardID is 'f' - "//i.4cdn.org/#{boardID}/#{encodeURIComponent data.filename}#{data.ext}" + "#{location.protocol}//i.4cdn.org/#{boardID}/#{encodeURIComponent data.filename}#{data.ext}" else - "//i.4cdn.org/#{boardID}/#{data.tim}#{data.ext}" + "#{location.protocol}//i.4cdn.org/#{boardID}/#{data.tim}#{data.ext}" height: data.h width: data.w MD5: data.md5 - size: data.fsize - turl: "//i.4cdn.org/#{boardID}/#{data.tim}s.jpg" + size: $.bytesToString data.fsize + thumbURL: "#{location.protocol}//i.4cdn.org/#{boardID}/#{data.tim}s.jpg" theight: data.tn_h twidth: data.tn_w isSpoiler: !!data.spoiler - isDeleted: false tag: data.tag + o.file.dimensions = "#{o.file.width}x#{o.file.height}" unless /\.pdf$/.test o.file.url + o + + parseComment: (o) -> + html = o.info.commentHTML.innerHTML + .replace(//gi, '\n') + .replace(/\n\nRolled [^<]*<\/b>/i, '') # Rolls (/tg/) + .replace(/]*>/g, '') + o.info.comment = Build.unescape html + + postFromObject: (data, boardID, suppressThumb) -> + o = Build.parseJSON data, boardID Build.post o, suppressThumb + post: (o, suppressThumb) -> - ### - This function contains code from 4chan-JS (https://github.com/4chan/4chan-JS). - @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE - ### - { - postID, threadID, boardID - name, capcode, tripcode, uniqueID, email, subject, flagCode, flagName, date, dateUTC - comment - file - } = o - name or= '' - subject or= '' - isOP = postID is threadID + {postID, threadID, boardID, file} = o + {subject, email, name, tripcode, capcode, uniqueID, flagCode, flag, dateUTC, dateText, commentHTML} = o.info {staticPath, gifIcon} = Build ### Post Info ### if capcode - capcodeLC = capcode.split('_')[0] - capcodeUC = capcodeLC[0].toUpperCase() + capcodeLC[1..] - capcodeText = capcodeUC - capcodeLong = {'Admin': 'Administrator', 'Mod': 'Moderator'}[capcodeUC] or capcodeUC - capcodePlural = "#{capcodeLong}s" - capcodeDescription = "a 4chan #{capcodeLong}" - if capcode is 'admin_emeritus' - capcodeText = 'Admin Emeritus' + capcodeUC = capcode.split(' ')[0] + capcodeLC = capcodeUC.toLowerCase() + if capcode is 'Admin Emeritus' capcodePlural = 'the Administrator Emeritus' capcodeDescription = "4chan's founding Administrator" + else + capcodeLong = {'Admin': 'Administrator', 'Mod': 'Moderator'}[capcode] or capcode + capcodePlural = "#{capcodeLong}s" + capcodeDescription = "a 4chan #{capcodeLong}" postLink = Build.postURL boardID, threadID, postID quoteLink = if Build.sameThread boardID, threadID @@ -111,17 +120,17 @@ Build = ### File Info ### - if file and not file.isDeleted + if file + protocol = /^https?:(?=\/\/i\.4cdn\.org\/)/ + fileURL = file.url.replace protocol, '' shortFilename = Build.shortFilename file.name - fileSize = $.bytesToString file.size - fileDims = if file.url[-4..] is '.pdf' then 'PDF' else "#{file.width}x#{file.height}" - fileThumb = if file.isSpoiler then Build.spoilerThumb boardID else file.turl + fileThumb = if file.isSpoiler then Build.spoilerThumb(boardID) else file.thumbURL.replace(protocol, '') fileBlock = <%= importHTML('Build/File') %> ### Whole Post ### - postClass = if isOP then 'op' else 'reply' + postClass = if o.isReply then 'reply' else 'op' wholePost = <%= importHTML('Build/Post') %> @@ -130,13 +139,15 @@ Build = id: "pc#{postID}" $.extend container, wholePost - # Fix pathnames + # Fix quotelinks for quote in $$ '.quotelink', container href = quote.getAttribute 'href' if (href[0] is '#') and !(Build.sameThread boardID, threadID) quote.href = "/#{boardID}/thread/#{threadID}" + href else if (match = href.match /^\/([^\/]+)\/thread\/(\d+)/) and (Build.sameThread match[1], match[2]) quote.href = href.match(/(#[^#]*)?$/)[0] or '#' + else if /^\d+(#|$)/.test(href) and not (g.VIEW is 'thread' and g.BOARD.ID is boardID) # used on /f/ + quote.href = "/#{boardID}/thread/#{href}" container diff --git a/src/General/BuildTest.coffee b/src/General/BuildTest.coffee index 3638fdaa2..8bf1c9219 100644 --- a/src/General/BuildTest.coffee +++ b/src/General/BuildTest.coffee @@ -38,22 +38,37 @@ BuildTest = for postData in posts if postData.no is post.ID t1 = new Date().getTime() - root = Build.postFromObject postData, post.board.ID + obj = Build.parseJSON postData, post.board.ID + root = Build.post obj t2 = new Date().getTime() BuildTest.time += t2 - t1 post2 = new Post root, post.thread, post.board + fail = false + x = post.normalizedOriginal y = post2.normalizedOriginal - if x.isEqualNode y - c.log "#{post.fullID} correct" - else + unless x.isEqualNode y + fail = true c.log "#{post.fullID} differs" - BuildTest.postsFailed++ [x2, y2] = BuildTest.firstDiff x, y c.log x2 c.log y2 c.log x.outerHTML c.log y.outerHTML + + for key of Config.filter when not (key is 'MD5' and post.board.ID is 'f') + val1 = Filter[key] obj + val2 = Filter[key] post2 + if val1 isnt val2 + fail = true + c.log "#{post.fullID} has filter bug in #{key}" + c.log val1 + c.log val2 + + if fail + BuildTest.postsFailed++ + else + c.log "#{post.fullID} correct" BuildTest.postsRemaining-- BuildTest.report() if BuildTest.postsRemaining is 0 post2.isFetchedQuote = true @@ -61,8 +76,9 @@ BuildTest = testAll: -> g.posts.forEach (post) -> - unless post.isClone or post.isFetchedQuote or $ '.abbr', post.nodes.comment - BuildTest.testOne post + unless post.isClone or post.isFetchedQuote + unless (abbr = $ '.abbr', post.nodes.comment) and /Comment too long\./.test(abbr.textContent) + BuildTest.testOne post return postsRemaining: 0 diff --git a/src/General/Config.coffee b/src/General/Config.coffee index b8eb7d363..02b25e2ab 100755 --- a/src/General/Config.coffee +++ b/src/General/Config.coffee @@ -421,13 +421,11 @@ Config = 'Open new threads or replies to a thread from the index in a new tab.' 1 ] - <% if (type === 'userscript') { %> 'Remember QR Size': [ false 'Remember the size of the Quick reply.' 1 ] - <% } %> 'Remember Spoiler': [ false 'Remember the spoiler state, instead of resetting after posting.' @@ -485,7 +483,7 @@ Config = ] 'Captcha Fixes': [ true - 'Make captcha more keyboard-navigable.' + 'Make captcha easier to use, especially with the keyboard.' ] 'Quote Links': @@ -592,6 +590,9 @@ Config = 'Fit Height': [ true ] + 'Stretch to Fit': [ + false + ] 'Scroll to Post': [ true ] @@ -674,12 +675,14 @@ Config = sauces: """ https://www.google.com/searchbyimage?image_url=%IMG http://iqdb.org/?url=%IMG - http://eye.swfchan.com/search/?q=%name;types:swf + http://eye.swfchan.com/search/?q=%name;types:swf;sandbox #//tineye.com/search?url=%IMG #https://www.yandex.com/images/search?rpt=imageview&img_url=%IMG #//saucenao.com/search.php?url=%IMG #http://3d.iqdb.org/?url=%IMG + # tools: #http://regex.info/exif.cgi?imgurl=%URL + #//imgops.com/%URL;types:gif,jpg,png # uploaders: #//imgur.com/upload?url=%URL;types:gif,jpg,png,pdf;text:Upload to imgur # "View Same" in archives: @@ -743,11 +746,13 @@ Config = #options:"sage";boards:jp;always """ + captchaLanguage: '' + time: '%m/%d/%y(%a)%H:%M:%S' backlink: '>>%id' - fileInfo: '%l (%p%s, %r)' + fileInfo: '%l (%p%s, %r%g)' favicon: 'ferongr' @@ -830,7 +835,7 @@ Config = 'Pause/play videos in the gallery.' ] 'Slideshow': [ - 's' + 'Ctrl+Right' 'Toggle the gallery slideshow mode.' ] 'fappeTyme': [ diff --git a/src/General/Get.coffee b/src/General/Get.coffee index bfd705f9e..9dffdf722 100755 --- a/src/General/Get.coffee +++ b/src/General/Get.coffee @@ -3,7 +3,7 @@ Get = {OP} = thread excerpt = "/#{thread.board}/ - " + ( OP.info.subject?.trim() or - OP.info.comment.replace(/\n+/g, ' // ') or + OP.info.commentDisplay.replace(/\n+/g, ' // ') or OP.info.nameBlock) return "#{excerpt[...70]}..." if excerpt.length > 73 excerpt diff --git a/src/General/Globals.coffee b/src/General/Globals.coffee index 82bcf0278..43cd70ca4 100755 --- a/src/General/Globals.coffee +++ b/src/General/Globals.coffee @@ -19,3 +19,6 @@ E.cat = (templates) -> html = '' html += x.innerHTML for x in templates html + +E.url = (content) -> + "data:text/html;charset=utf-8,#{encodeURIComponent content.innerHTML}" diff --git a/src/General/Index.coffee b/src/General/Index.coffee index c61f40e5b..74a4dfaab 100644 --- a/src/General/Index.coffee +++ b/src/General/Index.coffee @@ -431,7 +431,7 @@ Index = Index.req = $.ajax "//a.4cdn.org/#{g.BOARD}/catalog.json", onloadend: (e) -> Index.load e, state , - whenModified: true + whenModified: 'Index' $.addClass Index.button, 'fa-spin' load: (e, state) -> diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 6de834f61..f52330270 100755 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -2,11 +2,18 @@ Main = init: -> if location.hostname is 'www.google.com' if location.pathname is '/recaptcha/api/fallback' - $.ready -> Captcha.noscript.initFrame() - else - $.get 'Captcha Fixes', true, ({'Captcha Fixes': enabled}) -> - if enabled - $.ready -> Captcha.fixes.init() + $.ready -> Captcha.v2.initFrame() + $.get 'Captcha Fixes', true, ({'Captcha Fixes': enabled}) -> + if enabled + $.ready -> Captcha.fixes.init() + return + + if location.hostname is 'www.4chan.org' + $.onExists d.documentElement, 'body', false, -> $.addStyle Main.cssWWW + Conf = {'captchaLanguage': Config.captchaLanguage} + $.get Conf, (items) -> + $.extend Conf, items + Captcha.language.fixPage() return g.threads = new SimpleDict() @@ -57,6 +64,9 @@ Main = $.onExists doc, 'body', false, Main.initStyle initFeatures: -> + if location.hostname in ['boards.4chan.org', 'sys.4chan.org'] + $.globalEval 'document.documentElement.classList.add("js-enabled");' + switch location.hostname when 'a.4cdn.org' return @@ -298,17 +308,13 @@ Main = $.ready -> cb() if Main.isThisPageLegit() - css: `<%= - grunt.template.process( - ['font-awesome', 'style', 'yotsuba', 'yotsuba-b', 'futaba', 'burichan', 'tomorrow', 'photon'].map(function(name) { - return grunt.file.read('src/General/css/'+name+'.css'); - }).join(''), - {data: {type: type}} - ).trim().replace(/\n+/g, '\n').split(/^/m).map(JSON.stringify).join(' +\n').replace(/`/g, '\\`') - %>` + css: `<%= importCSS('font-awesome', 'noscript', 'style', 'yotsuba', 'yotsuba-b', 'futaba', 'burichan', 'tomorrow', 'photon') %>` + + cssWWW: `<%= importCSS('noscript', 'www') %>` features: [ ['Polyfill', Polyfill] + ['Captcha Language', Captcha.language] ['Redirect', Redirect] ['Header', Header] ['Catalog Links', CatalogLinks] diff --git a/src/General/Settings.coffee b/src/General/Settings.coffee index f842925d4..09d8a652c 100755 --- a/src/General/Settings.coffee +++ b/src/General/Settings.coffee @@ -103,6 +103,7 @@ Settings = description = arr[1] div = $.el 'div', <%= html(': ${description}') %> + div.hidden = true if chrome? and key is 'Remember QR Size' # XXX not supported input = $ 'input', div $.on input, 'change', -> @parentNode.parentNode.dataset.checked = @checked @@ -314,7 +315,7 @@ Settings = items = {} inputs = {} - for name in ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss', 'customCooldown'] + for name in ['captchaLanguage', 'boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss', 'customCooldown'] input = $ "[name='#{name}']", section items[name] = Conf[name] inputs[name] = input @@ -323,9 +324,9 @@ Settings = else if name is 'favicon' $.on input, 'change', $.cb.value $.on input, 'change', Settings[name] - else + else $.on input, 'input', $.cb.value - $.on input, 'input', Settings[name] + $.on input, 'input', Settings[name] if name of Settings # Quick Reply Personas ta = $ '.personafield', section @@ -338,8 +339,8 @@ Settings = for key, val of items input = inputs[key] input.value = val - continue if key in ['usercss', 'customCooldown'] - Settings[key].call input + if key of Settings and key isnt 'usercss' + Settings[key].call input return interval = $ 'input[name="Interval"]', section @@ -454,13 +455,15 @@ Settings = data = isReply: true file: - URL: '//i.4cdn.org/g/1334437723720.jpg' + url: '//i.4cdn.org/g/1334437723720.jpg' name: 'd9bb2efc98dd0df141a94399ff5880b7.jpg' size: '276 KB' sizeInBytes: 276 * 1024 dimensions: '1280x720' isImage: true + isVideo: false isSpoiler: true + tag: 'Loop' FileInfo.format @value, data, @nextElementSibling favicon: -> diff --git a/src/General/css/noscript.css b/src/General/css/noscript.css new file mode 100644 index 000000000..01baf5523 --- /dev/null +++ b/src/General/css/noscript.css @@ -0,0 +1,9 @@ +noscript > div, noscript > div > div { + height: 545px !important; +} +noscript > div > div > div:first-child, noscript iframe { + height: 423px !important; +} +:root:not(.js-enabled) #g-recaptcha { + height: auto; +} diff --git a/src/General/css/style.css b/src/General/css/style.css index f3d1bbc5b..e9ae46c30 100755 --- a/src/General/css/style.css +++ b/src/General/css/style.css @@ -105,10 +105,8 @@ hr + div.center:not(.ad-cnt):not(.topad):not(.middlead):not(.bottomad) { /* party hats */ pointer-events: none; } -marquee, -.postMessage marquee + br, -.postMessage marquee + br + br { - display: none; +:root:not(.js-enabled) #postForm { + display: table; } /* Anti-autoplay */ @@ -166,6 +164,9 @@ audio.controls-added { #thread-watcher { z-index: 10; } +:root.fixed:not(.gallery-open) #header-bar:not(.autohide) { + z-index: 5; +} /* Header */ .fixed.top-header body { @@ -532,6 +533,9 @@ div[data-checked="false"] > .suboption-list { .section-filter textarea { height: 500px; } +.section-filter a, .section-advanced a { + text-decoration: underline; +} .section-sauce textarea { height: 350px; } @@ -867,12 +871,15 @@ span.hide-announcement { border: none; box-shadow: none; } -:root.float #thread-stats > .move > span { +:root.float #thread-stats > .move > :not(#page-count) { pointer-events: none; } :root.float #thread-stats { padding: 0px 3px; } +#page-count { + cursor: pointer; +} /* Quote */ .catalog-thread > .comment > span.quote, #arc-list span.quote { @@ -1134,9 +1141,7 @@ input[name="Default Volume"] { display: none !important; } #qr select, -#url-button, -#custom-cooldown-button, -#dump-button, +#qr-filename-container > a, .remove, .captcha-img { cursor: pointer; @@ -1148,6 +1153,10 @@ input[name="Default Volume"] { min-width: 300px; border-radius: 3px 3px 0 0; } +#qr > form { + max-height: calc(100vh - 75px); + overflow-y: auto; +} #qrtab { border-radius: 3px 3px 0 0; } @@ -1158,13 +1167,6 @@ input[name="Default Volume"] { 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; } @@ -1227,7 +1229,7 @@ input.field.tripped:not(:hover):not(:focus) { top: 2px; } -/* Noscript Recaptcha */ +/* Recaptcha v1 */ .captcha-img { margin: 0px; text-align: center; @@ -1240,9 +1242,6 @@ input.field.tripped:not(:hover):not(:focus) { width: 100%; margin: 1px 0 0; } -#qr-captcha-iframe { - display: none; -} /* Recaptcha v2 */ #qr .captcha-root { @@ -1268,6 +1267,21 @@ input.field.tripped:not(:hover):not(:focus) { display: block; width: 100%; } +#qr-captcha-iframe { + width: 302px; + height: 423px; + border: 0; + display: block; + margin: auto; +} +.goog-bubble-content { + max-width: 100vw; + max-height: 100vh; + overflow: auto; +} +.goog-bubble-content iframe { + position: static !important; +} /* File Input, Submit Button */ #file-n-submit { @@ -1282,6 +1296,7 @@ input.field.tripped:not(:hover):not(:focus) { background: linear-gradient(to bottom, #F8F8F8, #DCDCDC) no-repeat; border: 1px solid #BBB; border-radius: 2px; + height: 100%; } #qr-file-button { width: 15%; @@ -1462,6 +1477,8 @@ a:only-of-type > .remove { } .textarea { position: relative; + display: -webkit-flex; + display: flex; } :root.webkit .textarea { margin-bottom: -2px; @@ -1475,6 +1492,9 @@ a:only-of-type > .remove { right: 1px; pointer-events: none; } +#char-count.warning { + color: red; +} /* Menu */ .menu-button:not(.fa-bars) { @@ -1498,7 +1518,7 @@ a:only-of-type > .remove { height: 15px; text-align: center; } -.menu-button + .container:not(:empty) { +.menu-button + .container :first-child { margin-left: -5px; } #menu { @@ -1745,13 +1765,6 @@ grunt.file.expand('src/General/img/links/*.png').map(function(file) { } .gal-fit-height .gal-image img, .gal-fit-height .gal-image video { - /* - Chrome doesn't support viewpoint units in calc() - http://bugs.chromium.org/168840 - "It looks like the original author of viewport units in WebKit is not coming back to fix this stuff." - Well, fuck. - */ - max-height: 95vh; max-height: calc(100vh - 25px); } .gal-image iframe { diff --git a/src/General/css/www.css b/src/General/css/www.css new file mode 100644 index 000000000..01b12c399 --- /dev/null +++ b/src/General/css/www.css @@ -0,0 +1,3 @@ +#captcha-cnt { + height: auto; +} diff --git a/src/General/html/Build/File.html b/src/General/html/Build/File.html index 2b1e0fed4..d48f11db2 100644 --- a/src/General/html/Build/File.html +++ b/src/General/html/Build/File.html @@ -1,29 +1,35 @@ -?{file}{
- ?{file.isDeleted}{ - - File deleted. - - }{?{boardID === "f"}{ +?{file}{ +
+ ?{boardID === "f"}{
File: - ${file.name} - -(${fileSize}, ${fileDims}, ${file.tag}) + ${file.name} + -(${file.size}, ${file.dimensions}?{file.tag}{, ${file.tag}})
}{
File: - + ?{file.isSpoiler}{Spoiler Image}{${shortFilename}} - (${fileSize}, ${fileDims}) + (${file.size}, ${file.dimensions || "PDF"})
- + ${fileSize} - }} -
} + } +
+}{ + ?{o.fileDeleted}{ +
+ + File deleted. + +
+ } +} diff --git a/src/General/html/Build/Post.html b/src/General/html/Build/Post.html index fc2b36cca..bc9cf1852 100644 --- a/src/General/html/Build/Post.html +++ b/src/General/html/Build/Post.html @@ -1,5 +1,5 @@ -?{!isOP}{
>>
} -
- ?{isOP}{&{fileBlock}&{postInfo}}{&{postInfo}&{fileBlock}} -
&{comment}
+?{o.isReply}{
>>
} +
+ ?{o.isReply}{&{postInfo}&{fileBlock}}{&{fileBlock}&{postInfo}} +
&{commentHTML}
diff --git a/src/General/html/Build/PostInfo.html b/src/General/html/Build/PostInfo.html index fbd848f5c..75f7c383d 100644 --- a/src/General/html/Build/PostInfo.html +++ b/src/General/html/Build/PostInfo.html @@ -1,24 +1,24 @@ diff --git a/src/General/html/Features/Sandbox.html b/src/General/html/Features/Sandbox.html new file mode 100644 index 000000000..bc8ddbd52 --- /dev/null +++ b/src/General/html/Features/Sandbox.html @@ -0,0 +1,16 @@ + +[sb] ${url} + + + + diff --git a/src/General/html/Settings/Advanced.html b/src/General/html/Settings/Advanced.html index a9ddd65a0..e6b5f38ef 100755 --- a/src/General/html/Settings/Advanced.html +++ b/src/General/html/Settings/Advanced.html @@ -12,6 +12,12 @@ +
+ Captcha Language +
Choose from list of language codes. Leave blank to autoselect.
+
+
+
Custom Board Navigation
@@ -41,7 +47,7 @@
Time Formatting is disabled.
:
- +
Day: %a, %A, %d, %e
Month: %m, %b, %B
Year: %y, %Y
@@ -64,6 +70,7 @@
Spoiler indicator: %p
Size: %B (Bytes), %K (KB), %M (MB), %s (4chan default)
Resolution: %r (Displays 'PDF' for PDF files)
+
Tag: %g
Literal %: %%
diff --git a/src/General/html/Settings/Filter-guide.html b/src/General/html/Settings/Filter-guide.html index 5b5f981cc..9d453ced6 100755 --- a/src/General/html/Settings/Filter-guide.html +++ b/src/General/html/Settings/Filter-guide.html @@ -1,6 +1,6 @@
Filter is disabled.

- Use regular expressions, one per line.
+ Use regular expressions, one per line.
Lines starting with a # will be ignored.
For example, /weeaboo/i will filter posts containing the string `weeaboo`, case-insensitive.
MD5 filtering uses exact string matching, not regular expressions. diff --git a/src/General/html/Settings/Sauce.html b/src/General/html/Settings/Sauce.html index ff1c76644..bf13382a3 100755 --- a/src/General/html/Settings/Sauce.html +++ b/src/General/html/Settings/Sauce.html @@ -3,6 +3,7 @@

You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards by appending ;boards:[board1],[board2].
You can specify the applicable file types by appending ;types:[extension1],[extension2].
+
You can open links with scripts and popups disabled by appending ;sandbox.