It can build it kind off

This commit is contained in:
Lalle 2023-04-22 10:27:00 +02:00
parent b159a1ac87
commit 0d4893dfa6
No known key found for this signature in database
GPG Key ID: A6583D207A8F6B0D
53 changed files with 405 additions and 17482 deletions

24
.gitignore vendored
View File

@ -119,3 +119,27 @@ node_modules/
/dist/
/builds/*.gz
/test/
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

File diff suppressed because it is too large Load Diff

View File

@ -1,49 +0,0 @@
## Reporting bugs
Bug reports and feature requests for 4chan X are tracked at **https://github.com/ccd0/4chan-x/issues?q=is%3Aopen+sort%3Aupdated-desc**.
You can submit a bug report / feature request via your Github account.
If you're reporting a bug, the more detail you can give, the better. If I can't reproduce your bug, I probably won't be able to fix it. You can help by doing the following:
1. Include precise steps to reproduce the problem, with the expected and actual results.
2. Make sure your browser, 4chan X, and userscript manager (e.g. Greasemonkey, ViolentMoney, or Tampermonkey) are up to date. **Include the versions you're using in bug reports.**
3. Open your console with Shift+Control+J (⇧⌘J on OS X Firefox, ⌘⌥J on OS X Chromium), and **look for error messages**, especially ones that occur at the same time as the bug. Include these in your bug report. If you're using Firefox, be sure to check the browser console (Shift+Control+J), not just the web console (Shift+Control+K) as errors may not show up in the latter. Messages about "Content Security Policy" are expected and can be ignored.
4. If other people (including me) aren't having your problem, **test whether it happens in a fresh profile**. Here are instructions for [Firefox](https://support.mozilla.org/en-US/kb/profile-manager-create-and-remove-firefox-profiles) and [Chromium](https://developer.chrome.com/devtools/docs/clean-testing-environment).
5. **Please mention any other extensions / scripts you are using.** To check if a bug is due to a conflict with another extension, temporarily disable any other extensions and userscripts. If the bug goes away, turn them back on one by one until you find the one causing the problem.
6. To test if the bug occurs under the default settings or only with specific settings, back up your settings and reset them using the **Export** and **Reset Settings** links in the settings panel. If the bug only occurs under specific settings, upload your exported settings to a site like https://paste.installgentoo.com/, and link to it in your bug report. If your settings contains sensitive information (e.g. personas), edit the text file manually.
7. Test if the bug occurs using the **native extension** with 4chan X disabled. If it does, it's likely a problem with 4chan or your browser rather than with 4chan X.
## Development & Contribution
### Get started
- Install [git](https://git-scm.com/), [node.js](https://nodejs.org/), [npm](https://www.npmjs.com/) (usually distributed with node), and GNU Make (on Windows, the [MinGW](http://www.mingw.org/) port will work, and the [GnuWin](http://gnuwin32.sourceforge.net/) port has been reported to work as well).
- Clone 4chan X: `git clone https://github.com/ccd0/4chan-x.git`<br>(If this is taking too long, you can add `--depth 100` to fetch only recent history.)
- Open the directory: `cd 4chan-x`
- Fetch needed dependencies with: `npm install`
### Build
- Build with `make`.
### Contribute
- 4chan X is mostly written in [CoffeeScript](http://coffeescript.org/). If you're already familiar with Javascript, it doesn't take long to pick up.
- Edit the sources in the src/ directory (not the compiled scripts in builds/).
- Fetch needed dependencies with: `npm install`
- Compile the script with: `make`
- Install the compiled script (found in the testbuilds/ directory), and test your changes.
- Make sure you have set your name and email as you want them, as they will be published in your commit message:<br>`git config user.name yourname`<br>`git config user.email youremail`
- Commit your changes: `git commit -a`
- Open a pull request by doing any of the following:
- Fork this repository on Github, push your changes to your fork, and make a pull request through the Github website.
- Push your changes to any online Git repository, and send an email with an explanation of your changes and the URL, branch, and commit you want me to pull from.
- Export your changes via `git bundle` (e.g. `git bundle create file.bundle master..your-branch`), and upload them to a file host. Then send an email with an explanation of your changes and the URL of the file.
Pull requests to archive.json should be sent upstream: https://github.com/4chenz/archives.json
4chan X updates from there automatically.
### More info
Further documentation is available at https://github.com/ccd0/4chan-x/wiki/Developer-Documentation.

79
LICENSE
View File

@ -1,79 +0,0 @@
/*
* 4chan X
*
* Licensed under the MIT license.
* https://github.com/ccd0/4chan-x/blob/master/LICENSE
*
* Appchan X Copyright © 2013-2016 Zixaphir <zixaphirmoxphar@gmail.com>
* http://zixaphir.github.io/appchan-x/
* 4chan x Copyright © 2009-2011 James Campos <james.r.campos@gmail.com>
* https://github.com/aeosynth/4chan-x
* 4chan x Copyright © 2012-2014 Nicolas Stepien <stepien.nicolas@gmail.com>
* https://4chan-x.just-believe.in/
* 4chan x Copyright © 2013-2014 Jordan Bates <saudrapsmann@gmail.com>
* http://seaweedchan.github.io/4chan-x/
* 4chan x Copyright © 2012-2013 ihavenoface
* http://ihavenoface.github.io/4chan-x/
* 4chan SS Copyright © 2011-2013 Ahodesuka
* https://github.com/ahodesuka/4chan-Style-Script/
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Contributors:
* aeosynth
* mayhemydg
* noface
* !K.WeEabo0o
* blaise
* that4chanwolf
* desuwa
* seaweed
* e000
* ahodesuka
* Shou
* ferongr
* xat
* Ongpot
* thisisanon
* Anonymous
* Seiba
* herpaderpderp
* WakiMiko
* btmcsweeney
* AppleBloom
* detharonil
*
* All the people who've taken the time to write bug reports.
*
* Thank you.
*/
/*
* Contains data from external sources:
*
* src/Monitoring/ThreadUpdater/beep.wav from http://freesound.org/people/pierrecartoons1979/sounds/90112/
* cc-by-nc-3.0
*
* Font Awesome by Dave Gandy (http://fontawesome.io)
* license: http://fontawesome.io/license/
*
* Icons used to identify various websites are property of the respective websites.
*/

View File

@ -1,8 +0,0 @@
clean:
rm -rf dist/*
install:
npm run build
sneed: clean install
cat dist/4chan-XT.user.js | xclip -selection clipboard

176
README.md
View File

@ -1,176 +0,0 @@
# 4chan XT
**This repo is work in progress!** Use the build from the [repo this is forked from](https://github.com/ccd0/4chan-x) in the meantime.
PR to upstream: https://github.com/ccd0/4chan-x/pull/3341.
The 4chan XT project is a migration of 4chan X from coffeescript to TypeScript/JavaScript. It is named XT both as a continuation of eXTended, and a T for TypeScript. The goals of this project is to first get a working bundle from js/ts files, and then gradually convert js files to ts and add types as needed.
## TODO
- find alternative for `<% if (`
- [x] made html templates jsx/txt functions
- this uses the typescript compiler to compile the jsx
- render code is in [src/globals/jsx.ts](./src/globals/jsx.ts)
- [x] binary files are included as base64 in the bundle step, they do need explicit imports
- [ ] \<% if (readJSON('/.tests_enabled')) { %\>, are these still used?
- build script
- [x] userscript
- [ ] .crx extension
- [x] crx directory that can be loaded as an unpacked extension is created
- [x] beta
- [x] noupdate
- [ ] run and debug
- [ ] port updates made to 4chan-X made since this was forked
## Other notes
- A lot of files have circular dependencies, but rollup can handle that
- but for some scripts that add to the same object I had to merge them, like Posting/QR and site/SW.yotsuba.js
- sometimes something might not be initialized before use, for example, `$.dict()` and `$.SECONDS`
- I moved these to a new file called helpers.ts, which shouldn't have dependencies itself, so it's also available
- tsconfig.json has `"checkJs": true,`, and a lot of js files report type errors when opened because of unknown properties on objects and reassigning variables with different types. These errors don't block the bundle at this moment.
- old files in the builds directory stay as reference until the new builds are functional, new files go in the builds/test directory
- old build scripts are also kept around for reference until the new build output is fully functional
- the es 2020 target was choses for optional chaining
- @violentmonkey/types was chosen over @types/greasemonkey because @types/greasemonkey only declares the GM object, and not GM\_ functions
## commits since this was forked
<details>
<summary>Click to expand</summary>
- [x] 944b04210c119aedf8da1a8bcabaca9b80312118 Update archive list.
- [x] 59ee8c57792d0f82491756a077e25f506fd62994 Desuarchive removes /gif/
- [x] 402679e33a06dfbe0dc39ceba5c24fed761b6a19 desuarchive removes /wsg/ files
- [x] 86071184aa39b3585f06c1a4e2921c411ad8cf10 archived.moe adds /pw/ search, tokyochronos has hosting issues
- [x] 8a6392b1cf721ddfae6d8f4e3ec2566f15755370 add Eientei
- [x] 451a06f54b878ce433b0775858affefc71927fc7 alice.al domain change
- [x] 2a8bf2adb0737ce7bb1e21f6b959e4c6e1de1bc7 Disable Javascript Whitelist on captcha iframe. #3292
- [x] e9c1529da7844a42a1b40458c2c77b77e23ca537 Make QR post more like original form post. #3330
- [x] d16062a8fac5c092c34310c7704ac3980494b6ef Merge remote-tracking branch '4chenz/master'
- [x] 8795b1c56dbdfb52a32ddb3ea80b549f0048dc7b Add Google Lens image search url
- [x] f3f03f5e79fb5f26c0fd4406b2ab6796851ea471 Replace Google image search link with Google Lens.
- [x] c68a8afbdf30e3cbb35f0834b364f20600151adf Switch Google image search back to old version, thanks to https://boards.4channel.org/g/thread/91737566#p91789527
- [x] aef984da1a6af4d0003b51e7f03bce252ac71dff Remove empty space from ads if they don't load. https://kissu.moe/b/res/7155#11052
- [x] 19268975ea2d49a753624315b0928f27496aac02 Update Randomize Filename to match current 4chan format. https://boards.4channel.org/g/thread/91737566#p91784238
- [x] 2a47dfd8ba724b17f5bc5f9214bea8ce8b469398 Catch errors due to "Restricted" selection. #2905
- [x] 27957c25af5d182adc38f1e67a098ab338631ccd Release 4chan X v1.14.22.2.
- [x] eb25d6e797a1673fd7cddb257fce04055383ec9b Update chrome-webstore-upload.
- [x] 14e67e9a958633e37b4e4a6293cfa3a921c1eab0 Release 4chan X v1.14.22.3.
- [x] 7295b21b73eb13ec53fdc61767ada341c2e13144 Avoid breaking sauce settings of people with links to original Google Images and Google Lens, provided they didn't already update to v1.14.22.3.
- [x] 71873cd7b22a565c2a41fa24f63f7504152683eb Recognize JPEG files with .jfif extensions as images for purposes of Image Hover etc.; also recognize .avif and .jxl files as images.
- [x] ea2462ecc47327c6f0c31348d95fd2b1b6447cb3 Release 4chan X v1.14.22.4.
</details>
---
Original readme:
![screenshot](https://ccd0.github.io/4chan-x/img/screenshot.png)
# 4chan X
4chan X is a script that adds various features to anonymous imageboards. It was originally developed for 4chan but has no affiliation with it.
It was previously developed by [aeosynth](https://github.com/aeosynth/4chan-x), [Mayhem](https://github.com/MayhemYDG/4chan-x), [ihavenoface](https://github.com/ihavenoface/4chan-x), [Zixaphir](https://github.com/zixaphir/appchan-x), [Seaweed](https://github.com/seaweedchan/4chan-x), and [Spittie](https://github.com/Spittie/4chan-x), with contributions from many others.
If you're looking for a maintained fork of OneeChan (a style script used in addition to 4chan X), try
https://github.com/KevinParnell/OneeChan.
## Please note
**Uninstalling**: 4chan X disables the native extension, so if you uninstall 4chan X, you'll need to re-enable it. To do this, click the `[Settings]` link in the top right corner, uncheck "`Disable the native extension`" in the panel that appears, and click the "`Save Settings`" button. If you don't see a "`Save Settings`" button, it may be being hidden by your ad blocker.
**Private browsing**: By default, 4chan X remembers your last read post in a thread and which posts were made by you, even if you are in private browsing / incognito mode. If you want to turn this off, uncheck the `Remember Last Read Post` and `Remember Your Posts` options in the settings panel. You can clear all 4chan browsing history saved by 4chan X by resetting your settings.
Use of the "Link Title" feature to fetch titles of Youtube links is subject to Youtube's [Terms of Service](https://www.youtube.com/t/terms) and [Privacy Policy](http://www.google.com/policies/privacy). For more details on what information is sent to Youtube and other sites, and how to turn it off if you don't want the feature, see 4chan X's [privacy documentation](https://github.com/ccd0/4chan-x/wiki/Privacy).
## Install
### Firefox
Install [Violentmonkey](https://addons.mozilla.org/en-US/firefox/addon/violentmonkey/), [Tampermonkey](https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/), or [Greasemonkey](https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/) (issues since v4: [#2526](https://github.com/greasemonkey/greasemonkey/issues/2526), [#2576](https://github.com/greasemonkey/greasemonkey/issues/2574)), then **[click here to install 4chan X](https://www.4chan-x.net/builds/4chan-X.user.js)**.
Ports of Greasemonkey are available for [SeaMonkey](https://sourceforge.net/projects/gmport/) and [Pale Moon](https://github.com/janekptacijarabaci/greasemonkey/releases/latest).
### Chromium
**Userscript**: Install [Violentmonkey](https://chrome.google.com/webstore/detail/violent-monkey/jinjaccalgkegednnccohejagnlnfdag) or [Tampermonkey](https://tampermonkey.net/), then **[click here to install 4chan X](https://www.4chan-x.net/builds/4chan-X.user.js)**.
**Chrome extension**: 4chan X is also available as a standalone Chrome extension. The Chrome extension has the additional feature of being able to sync your settings and data with other devices via Chrome Sync. But there is an issue when the script updates: Whenever the Chrome extension is updated, until you hard refresh (F5) the tab, 4chan X is unable to save any data (such as posts marked as yours and settings changes). The userscript version above does not have this problem when 4chan X updates, only when Violentmonkey / Tampermonkey is updated. To install as a Chrome extension:
- **Chromium**, **Vivaldi**: **[Download 4chan X](https://www.4chan-x.net/builds/4chan-X.crx)**, then open `chrome://extensions` and drag the downloaded file onto the page. Alternatively, you can install 4chan X from the **[Chrome store](https://chrome.google.com/webstore/detail/ohnjgmpcibpbafdlkimncjhflgedgpam)**.
- **Opera**: **[Click to install 4chan X](https://www.4chan-x.net/builds/4chan-X.crx)**, then follow the prompts to activate it in your extension manager.
- **Chrome**: Install 4chan X from the **[Chrome store](https://chrome.google.com/webstore/detail/ohnjgmpcibpbafdlkimncjhflgedgpam)**.
Note: This version of 4chan X does not work with Opera 12. If you need Opera 12 support, try [loadletter's fork](https://github.com/loadletter/4chan-x) instead.
### Safari
Install the [Userscripts](https://itunes.apple.com/us/app/userscripts/id1463298887) extension. Enable it by pressing `⌘,`, navigating to the extensions pane and checking `Userscripts` checkbox. Now open the Userscripts editor by clicking on the `</>` button in the taskbar. Then click on the `+` button and select the `New Javascript` option. Replace the default text with the contents of the 4chan X **[script](https://www.4chan-x.net/builds/4chan-X.user.js)**. Finally save it by pressing `⌘s`.
### WebKitGTK+ / QtWebKit / QtWebEngine
Several minimal browsers have support for userscripts and can run 4chan X. Due to the lack of the cross-site GM\_\* API, and lack of support for userscripts in iframes, not all features will work. You may experience crashes when repeatedly solving the default image-based captchas. You can avoid this problem by enabling `Use Recaptcha v1` in your settings.
- **dwb**: Install the userscripts extension, then save the [script](https://www.4chan-x.net/builds/4chan-X.user.js) to the `$XDG_CONFIG_HOME/dwb/greasemonkey` or `$HOME/.config/dwb/greasemonkey` directory (creating it if necessary):
```
dwbem -N -i userscripts
wget -P ${XDG_CONFIG_HOME:-$HOME/.config}/dwb/greasemonkey https://www.4chan-x.net/builds/4chan-X.user.js
```
- **Midori**: Enable `User addons` in your preferences, under the Extensions tab. In the Privacy tab, check `Enable HTML5 local storage support`. Optionally, if you want 4chan X to be able to open new tabs when you start or reply to a thread, you will need to check `Allow scripts to open popups` under the Behavior tab. Then click the link to the [script](https://www.4chan-x.net/builds/4chan-X.user.js) to install it.
- **Luakit**: Navigate to the [script](https://www.4chan-x.net/builds/4chan-X.user.js), then type the command `:usi` to install it.
- **uzbl**: Install the script from https://github.com/singpolyma/singpolyma/blob/master/uzbl/data/scripts/userscript.sh, enable it in your config file, and then save [4chan X](https://www.4chan-x.net/builds/4chan-X.user.js) to `$XDG_DATA_HOME/uzbl/userscripts` (or `$HOME/.local/share/uzbl/userscripts`). The commands below assume you have run uzbl at least once to create its config file.
```
wget -P "${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/scripts" https://raw.githubusercontent.com/singpolyma/singpolyma/master/uzbl/data/scripts/userscript.sh
chmod +x "${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/scripts/userscript.sh"
echo '@on_event LOAD_COMMIT spawn @scripts_dir/userscript.sh document-start' >> "${XDG_CONFIG_HOME:-$HOME/.config}/uzbl/config"
echo '@on_event LOAD_FINISH spawn @scripts_dir/userscript.sh document-end' >> "${XDG_CONFIG_HOME:-$HOME/.config}/uzbl/config"
wget -P "${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/userscripts" https://www.4chan-x.net/builds/4chan-X.user.js
```
- **qutebrowser**: Save the [script](https://www.4chan-x.net/builds/4chan-X.user.js) to the `$XDG_DATA_HOME/qutebrowser/greasemonkey` or `$HOME/.local/share/qutebrowser/greasemonkey` directory:
```
wget -P ${XDG_DATA_HOME:-$HOME/.local/share}/qutebrowser/greasemonkey https://www.4chan-x.net/builds/4chan-X.user.js
```
### MS Edge
Install [Tampermonkey](https://www.microsoft.com/en-us/store/p/tampermonkey/9nblggh5162s), then **[click here to install 4chan X](https://www.4chan-x.net/builds/4chan-X.user.js)**.
### Other browsers
4chan X can be used in some browsers that do not support userscripts using [a local proxy](https://github.com/ccd0/4chan-x-proxy). Not all features will work.
## 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?q=is%3Aopen+sort%3Aupdated-desc) 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.
To install the **beta** version and get updates whenever there's a new **beta** version:
- [Install userscript](https://www.4chan-x.net/builds/4chan-X-beta.user.js) (use with Greasemonkey / Violentmonkey / Tampermonkey / JS Blocker / etc.)
- [Download Chrome extension](https://www.4chan-x.net/builds/4chan-X-beta.crx) (download and drag to `chrome://extensions`)
To install the current **beta** version but get updates from the **stable** channel (for example, if just you want a particular recent feature):
- [Install userscript](https://github.com/ccd0/4chan-x/raw/beta/builds/4chan-X.user.js)
- [Download Chrome extension](https://github.com/ccd0/4chan-x/raw/beta/builds/4chan-X.crx)
## Troubleshooting
If you encounter a bug, try the steps [here](https://github.com/ccd0/4chan-x/blob/master/CONTRIBUTING.md#reporting-bugs), then report it to the [issue tracker](https://github.com/ccd0/4chan-x/issues?q=is%3Aopen+sort%3Aupdated-desc). If the bug seems to be caused by a script update, you can install a old version from the [changelog](https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md).
## More information
- [Changelog](https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md)
- [Frequently Asked Questions](https://github.com/ccd0/4chan-x/wiki/Frequently-Asked-Questions)
- [Report Bugs](https://github.com/ccd0/4chan-x/issues)
- [Contributing](https://github.com/ccd0/4chan-x/blob/master/CONTRIBUTING.md)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 KiB

5673
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,165 +1,28 @@
{
"name": "4chan-XT",
"description": "4chan XT is a script that adds various features to anonymous imageboards.",
"meta": {
"name": "4chan XT",
"path": "4chan-XT",
"fork": "TuxedoTako",
"page": "https://github.com/TuxedoTako/4chan-xt",
"downloads": "https://github.com/TuxedoTako/4chan-xt/releases",
"oldVersions": "https://raw.githubusercontent.com/ccd0/4chan-x/",
"faq": "https://github.com/TuxedoTako/4chan-xt/wiki/Frequently-Asked-Questions",
"captchaFAQ": "https://github.com/TuxedoTako/4chan-xt/wiki/Captcha-FAQ",
"cssGuide": "https://github.com/TuxedoTako/4chan-xt/wiki/Styling-Guide",
"license": "https://github.com/TuxedoTako/4chan-xt/blob/master/LICENSE",
"changelog": "https://github.com/TuxedoTako/4chan-xt/blob/master/CHANGELOG.md",
"issues": "https://github.com/TuxedoTako/4chan-xt/issues",
"newIssue": "https://github.com/TuxedoTako/4chan-xt/issues",
"newIssueMaxLength": 8181,
"alternatives": "https://www.4chan-x.net/4chan_alternatives.html",
"appid": "lacclbnghgdicfifcamcmcnilckjamag",
"appidGecko": "4chan-x@4chan-x.net",
"chromeStoreID": "ohnjgmpcibpbafdlkimncjhflgedgpam",
"recaptchaKey": "6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc",
"distBranch": "gh-pages",
"includes_only": [
"*://boards.4chan.org/*",
"*://sys.4chan.org/*",
"*://www.4chan.org/*",
"*://boards.4channel.org/*",
"*://sys.4channel.org/*",
"*://www.4channel.org/*",
"*://i.4cdn.org/*",
"*://is.4chan.org/*",
"*://is2.4chan.org/*",
"*://is.4channel.org/*",
"*://is2.4channel.org/*"
],
"matches_only": [
"*://*.4chan.org/*",
"*://*.4channel.org/*",
"*://*.4cdn.org/*"
],
"matches": [
"https://erischan.org/*",
"https://www.erischan.org/*",
"https://fufufu.moe/*",
"https://gnfos.com/*",
"https://himasugi.blog/*",
"https://www.himasugi.blog/*",
"https://kakashinenpo.com/*",
"https://www.kakashinenpo.com/*",
"https://kissu.moe/*",
"https://www.kissu.moe/*",
"https://lainchan.org/*",
"https://www.lainchan.org/*",
"https://merorin.com/*",
"https://ota-ch.com/*",
"https://www.ota-ch.com/*",
"https://ponyville.us/*",
"https://www.ponyville.us/*",
"https://smuglo.li/*",
"https://notso.smuglo.li/*",
"https://smugloli.net/*",
"https://smug.nepu.moe/*",
"https://sportschan.org/*",
"https://www.sportschan.org/*",
"https://sushigirl.us/*",
"https://www.sushigirl.us/*",
"https://tvch.moe/*"
],
"matches_extra": [],
"exclude_matches": [
"*://www.4chan.org/advertise",
"*://www.4chan.org/advertise?*",
"*://www.4chan.org/donate",
"*://www.4chan.org/donate?*",
"*://www.4channel.org/advertise",
"*://www.4channel.org/advertise?*",
"*://www.4channel.org/donate",
"*://www.4channel.org/donate?*"
],
"grants": [
"GM_getValue",
"GM_setValue",
"GM_deleteValue",
"GM_listValues",
"GM_addValueChangeListener",
"GM_openInTab",
"GM_xmlhttpRequest",
"GM.getValue",
"GM.setValue",
"GM.deleteValue",
"GM.listValues",
"GM.openInTab",
"GM.xmlHttpRequest"
],
"min": {
"chrome": "80",
"firefox": "74",
"greasemonkey": "1.14"
}
},
"devDependencies": {
"@rollup/plugin-typescript": "^11.0.0",
"@rollup/pluginutils": "^5.0.2",
"@types/chrome": "^0.0.217",
"@types/node": "^18.14.5",
"@typescript-eslint/eslint-plugin": "^5.43.0",
"@violentmonkey/types": "^0.1.5",
"chrome-webstore-upload": "^1.0.0",
"eslint": "^8.39.0",
"eslint-config-prettier": "^8.8.0",
"eslint-config-standard-with-typescript": "^34.0.1",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-n": "^15.0.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-promise": "^6.0.0",
"esprima": "^4.0.1",
"font-awesome": "=4.7.0",
"jshint": "^2.13.4",
"jszip": "^3.10.0",
"lodash.template": "^4.5.0",
"markdown-it": "^12.3.2",
"markdown-it-anchor": "^7.1.0",
"prettier": "^2.8.7",
"request": "^2.88.2",
"rollup": "^3.17.2",
"rollup-plugin-typescript2": "^0.34.1",
"tslib": "^2.5.0",
"typescript": "^4.9.5",
"vite": "^4.2.2",
"vite-plugin-monkey": "^3.1.3"
},
"repository": {
"type": "git",
"url": "https://github.com/ccd0/4chan-x.git"
},
"contributors": [
"James Campos <james.r.campos@gmail.com>",
"Nicolas Stepien <stepien.nicolas@gmail.com>",
"ihavenoface <noface@outlook.com>",
"Zixaphir <zixaphirmoxphar@gmail.com>",
"seaweedchan <jtbates@asu.edu>",
"Kabir Sala <spittiepie@gmail.com>",
"ccd0 <admin@containerchan.org>"
],
"license": "MIT",
"readmeFilename": "README.md",
"engines": {
"node": ">=16.0.0"
},
"name": "4chenz",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"build": "node ./tools/rollup",
"build:beta": "node ./tools/rollup -beta",
"build:noupdate": "node ./tools/rollup -noupdate",
"format": "prettier --write \"./**/*.{js,jsx,ts,tsx,json,css,scss,md}\"",
"lint": "eslint \"./**/*.{js,jsx}\" --fix"
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"devDependencies": {
"@rollup/plugin-html": "^1.0.2",
"@types/node": "^18.15.13",
"typescript": "^5.0.4",
"vite": "^4.2.2",
"vite-plugin-html": "^3.2.0",
"vite-plugin-monkey": "^3.1.4"
},
"dependencies": {
"@types/jquery": "^3.5.16",
"@vitejs/plugin-react": "^3.1.0",
"jquery": "^3.6.4"
"@rollup/pluginutils": "^5.0.2",
"@types/chrome": "^0.0.233",
"@violentmonkey/types": "^0.1.5",
"font-awesome": "^4.7.0",
"htmlparser2": "^8.0.2",
"node-html-parser": "^6.1.5",
"vite-plugin-html-inject": "^1.0.1"
}
}
}

2861
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -26,9 +26,6 @@ import { c, Conf, d, doc, g } from '../globals/globals'
import Header from './Header'
import UI from './UI'
import Menu from '../Menu/Menu'
import NavLinksPage from './Index/NavLinks.html'
import PageList from './Index/PageList.html'
import BoardConfig from './BoardConfig'
import Get from './Get'
import { dict, SECOND } from '../platform/helpers'
@ -152,7 +149,7 @@ var Index = {
// Navigation links at top of index
this.navLinks = $.el('div', { className: 'navLinks json-index' })
$.extend(this.navLinks, { innerHTML: NavLinksPage })
$.extend(this.navLinks, $.loadHTML("./Index/NavLinks.html"))
$('.cataloglink a', this.navLinks).href = CatalogLinks.catalog()
if (!BoardConfig.isArchived(g.BOARD.ID)) {
$('.archlistlink', this.navLinks).hidden = true
@ -214,7 +211,7 @@ var Index = {
// Page list
this.pagelist = $.el('div', { className: 'pagelist json-index' })
$.extend(this.pagelist, { innerHTML: PageList })
$.extend(this.pagelist, $.loadHTML("./Index/PageList.html"))
$('.cataloglink a', this.pagelist).href = CatalogLinks.catalog()
$.on(this.pagelist, 'click', this.cb.pageNav)

View File

@ -1,9 +1,4 @@
import SettingsPage from './Settings/SettingsHtml'
import FilterGuidePage from './Settings/Filter-guide.html'
import SaucePage from './Settings/Sauce.html'
import AdvancedPage from './Settings/Advanced.html'
import KeybindsPage from './Settings/Keybinds.html'
import FilterSelectPage from './Settings/Filter-select.html'
import Redirect from '../Archive/Redirect'
import DataBoard from '../classes/DataBoard'
import Notice from '../classes/Notice'
@ -1111,7 +1106,7 @@ vp-replace
},
filter(section) {
$.extend(section, { innerHTML: FilterSelectPage })
$.extend(section, $.loadHTML('./Settings/Filter-select.html'))
const select = $('select', section)
$.on(select, 'change', Settings.selectFilter)
return Settings.selectFilter.call(select)
@ -1142,12 +1137,12 @@ vp-replace
.map((x, i) => ({
innerHTML: (i ? ',' : '') + `<wbr>${E(x)}`,
}))
$.extend(div, { innerHTML: FilterGuidePage })
$.extend(div, $.loadHTML('./Settings/Filter-guide.html'))
return ($('.warning', div).hidden = Conf['Filter'])
},
sauce(section) {
$.extend(section, { innerHTML: SaucePage })
$.extend(section, $.loadHTML('./Settings/Sauce.html'))
$('.warning', section).hidden = Conf['Sauce']
const ta = $('textarea', section)
$.get('sauces', Conf['sauces'], function (item) {
@ -1159,7 +1154,7 @@ vp-replace
advanced(section) {
let input, name
$.extend(section, { innerHTML: AdvancedPage })
$.extend(section, $.loadHTML('./Settings/Advanced.html'))
for (var warning of $$('.warning', section)) {
warning.hidden = Conf[warning.dataset.feature]
}
@ -1479,7 +1474,7 @@ vp-replace
keybinds(section) {
let key
$.extend(section, { innerHTML: KeybindsPage })
$.extend(section, $.loadHTML('./Settings/Keybinds.html'))
$('.warning', section).hidden = Conf['Keybinds']
const tbody = $('tbody', section)

View File

@ -5,7 +5,6 @@
* DS205: Consider reworking code to avoid use of IIFEs
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
import galleryPage from './Gallery/Gallery.html'
import $ from '../platform/$'
import Callbacks from '../classes/Callbacks'
import Notice from '../classes/Notice'
@ -97,7 +96,7 @@ var Gallery = {
Gallery.slideshow = false
nodes.el = dialog = $.el('div', { id: 'a-gallery' })
$.extend(dialog, { innerHTML: galleryPage })
$.extend(dialog, $.loadHTML('./Gallery/Gallery.html'))
const object = {
buttons: '.gal-buttons',

View File

@ -8,7 +8,6 @@ import $ from '../platform/$'
import $$ from '../platform/$$'
import CrossOrigin from '../platform/CrossOrigin'
import { dict } from '../platform/helpers'
import EmbeddingPage from './Embedding/Embed.html'
/*
* decaffeinate suggestions:
* DS101: Remove unnecessary use of Array.from
@ -33,7 +32,7 @@ var Embedding = {
}
if (Conf['Embedding'] && g.VIEW !== 'archive') {
this.dialog = UI.dialog('embedding', { innerHTML: EmbeddingPage })
this.dialog = UI.dialog('embedding', $.loadHTML("./Embedding/Embed.html'"))
this.media = $('#media-embed', this.dialog)
$.one(d, '4chanXInitFinished', this.ready)
$.on(d, 'IndexRefreshInternal', () =>

View File

@ -1,6 +1,5 @@
import Redirect from '../Archive/Redirect'
import $ from '../platform/$'
import ReportPage from './Report/ArchiveReport.html'
import CSS from '../css/CSS'
import Captcha from '../Posting/Captcha'
import { Conf, d, g } from '../globals/globals'
@ -69,7 +68,7 @@ var Report = {
id: 'archive-report',
hidden: true,
},
{ innerHTML: ReportPage }
$.loadHTML('./Report/ArchiveReport.html')
)
const enabled = $('#archive-report-enabled', fieldset)
const reason = $('#archive-report-reason', fieldset)

View File

@ -1,4 +1,3 @@
import ThreadWatcherPage from './ThreadWatcher/ThreadWatcher.html'
import $ from '../platform/$'
import Board from '../classes/Board'
import Callbacks from '../classes/Callbacks'
@ -49,7 +48,7 @@ var ThreadWatcher = {
this.db = new DataBoard('watchedThreads', this.refresh, true)
this.dbLM = new DataBoard('watcherLastModified', null, true)
this.dialog = UI.dialog('thread-watcher', { innerHTML: ThreadWatcherPage })
this.dialog = UI.dialog('thread-watcher', $.loadHTML("./ThreadWatcher/ThreadWatcher.html"))
this.status = $('#watcher-status', this.dialog)
this.list = this.dialog.lastElementChild
this.refreshButton = $('.refresh', this.dialog)

View File

@ -1,4 +1,3 @@
import QuickReplyPage from './QR/QuickReply.html'
import $ from '../platform/$'
import Callbacks from '../classes/Callbacks'
import Notice from '../classes/Notice'
@ -861,7 +860,7 @@ const QR: QRNodes = {
let dialog, event, nodes
let name
QR.nodes = nodes = {
el: (dialog = UI.dialog('qr', { innerHTML: QuickReplyPage })),
el: (dialog = UI.dialog('qr', $.loadHTML("./QR/QuickReply.html"))),
}
const setNode = (name, query) => (nodes[name] = $(query, dialog))

View File

@ -1,11 +1,11 @@
import version from '../../version.json'
import meta from '../../package.json'
import type SimpleDict from '../classes/SimpleDict'
import type Post from '../classes/Post'
import type Thread from '../classes/Thread'
import type SWTinyboard from '../site/SW.tinyboard'
// interfaces might be incomplete
const version = "0.0.0"
const meta = "sw"
export interface BoardConfig {
forced_anon: any
sjis_tags: any
@ -67,8 +67,8 @@ export const g: {
VIEW?: string
xpath?: HTMLElement
} = {
VERSION: version.version,
NAMESPACE: meta.name,
VERSION: version,
NAMESPACE: meta,
sites: Object.create(null),
boards: Object.create(null),
}

View File

@ -14,6 +14,11 @@ import SimpleDict from '../classes/SimpleDict'
const $ = (selector, root = document.body) => root.querySelector(selector)
$.id = (id: string) => d.getElementById(id)
$.cache = dict()
$.loadHTML = async function (path) {
const response = await fetch(path);
const HTMLString = await response.text();
return HTMLString;
}
$.ajaxPage = function (url: string, options: AjaxPageOptions = {}) {
if (options == null) {
options = {}

1
src/typescript.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="32" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path fill="#007ACC" d="M0 128v128h256V0H0z"></path><path fill="#FFF" d="m56.612 128.85l-.081 10.483h33.32v94.68h23.568v-94.68h33.321v-10.28c0-5.69-.122-10.444-.284-10.566c-.122-.162-20.4-.244-44.983-.203l-44.74.122l-.121 10.443Zm149.955-10.742c6.501 1.625 11.459 4.51 16.01 9.224c2.357 2.52 5.851 7.111 6.136 8.208c.08.325-11.053 7.802-17.798 11.988c-.244.162-1.22-.894-2.317-2.52c-3.291-4.795-6.745-6.867-12.028-7.233c-7.76-.528-12.759 3.535-12.718 10.321c0 1.992.284 3.17 1.097 4.795c1.707 3.536 4.876 5.649 14.832 9.956c18.326 7.883 26.168 13.084 31.045 20.48c5.445 8.249 6.664 21.415 2.966 31.208c-4.063 10.646-14.14 17.879-28.323 20.276c-4.388.772-14.79.65-19.504-.203c-10.28-1.828-20.033-6.908-26.047-13.572c-2.357-2.6-6.949-9.387-6.664-9.874c.122-.163 1.178-.813 2.356-1.504c1.138-.65 5.446-3.129 9.509-5.485l7.355-4.267l1.544 2.276c2.154 3.29 6.867 7.801 9.712 9.305c8.167 4.307 19.383 3.698 24.909-1.26c2.357-2.153 3.332-4.388 3.332-7.68c0-2.966-.366-4.266-1.91-6.501c-1.99-2.845-6.054-5.242-17.595-10.24c-13.206-5.69-18.895-9.224-24.096-14.832c-3.007-3.25-5.852-8.452-7.03-12.8c-.975-3.617-1.22-12.678-.447-16.335c2.723-12.76 12.353-21.659 26.25-24.3c4.51-.853 14.994-.528 19.424.569Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

3
src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
/// <reference types="vite/client" />
/// <reference types="vite-plugin-monkey/client" />
//// <reference types="vite-plugin-monkey/global" />

1
src/vite.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,74 +0,0 @@
<!doctype html>
<html><head>
<meta charset="utf-8">
<title>4chan X</title>
<link rel="stylesheet" href="web.css">
<link rel="icon" href="img/icon.gif">
<link rel="chrome-webstore-item" href="https://chrome.google.com/webstore/detail/ohnjgmpcibpbafdlkimncjhflgedgpam">
</head><body>
<div id="header">
<h1 id="4chan-x">4chan X</h1>
<div id="links">
<a href="https://github.com/ccd0/4chan-x">Source Code</a>
<a href="https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md">Changelog</a>
<a href="https://github.com/ccd0/4chan-x/wiki/Frequently-Asked-Questions">FAQ</a>
<a href="https://github.com/ccd0/4chan-x/wiki/Privacy">Privacy</a>
<a href="https://github.com/ccd0/4chan-x/issues">Report Bugs</a>
</div>
</div>
<a class="screenshot" href="img/screenshot.png"><img src="img/screenshot.png" alt="Screenshot"></a>
<%=
content
.match(/<\/h1>([^]*)<h2 id="more-information"/)[1]
.replace(
/(<h3 id="(.*?)">)(.*?)(<\/h3>[^]*?)(?=<h)/g,
'<input hidden type="checkbox" id="$2-hide"><div>$1<label for="$2-hide">$3</label>$4</div>'
)
%>
<script>
function imagePreview() {
this.removeEventListener('mouseover', imagePreview, false);
var img = new Image();
img.src = this.href;
img.alt = 'preview';
var span = document.createElement('span');
span.className = 'hover';
span.appendChild(img);
this.parentNode.insertBefore(span, this.nextSibling);
}
function storeInstall(e) {
if (!e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey && e.button === 0) {
var url = this.href;
chrome.webstore.install(url, function(){}, function(){
location.href = url;
});
e.preventDefault();
}
}
for (var i = 0; i < document.links.length; i++) {
var link = document.links[i];
if (/\.png$/.test(link.pathname) && !link.querySelector('.hover')) {
link.addEventListener('mouseover', imagePreview, false);
} else if (window.chrome && link.host === 'chrome.google.com') {
link.addEventListener('click', storeInstall, false);
}
}
var engine = (function() {
if (/Edge\//.test(navigator.userAgent)) return 'edge';
if (/Chrome\//.test(navigator.userAgent)) return 'blink';
if (/WebKit\//.test(navigator.userAgent)) return 'webkit';
if (/Gecko\/|Goanna/.test(navigator.userAgent)) return 'gecko';
if (/Presto\//.test(navigator.userAgent)) return 'presto';
})();
var engines = {'firefox': 'gecko', 'chromium': 'blink presto', 'safari': 'webkit', 'webkitgtk-qtwebkit-qtwebengine': 'webkit', 'ms-edge': 'edge', 'other-browsers': ''};
if (location.hash.slice(1) in engines) {
for (browser in engines) {
document.getElementById(browser + '-hide').checked = (browser !== location.hash.slice(1));
}
} else if (engine) {
for (browser in engines) {
document.getElementById(browser + '-hide').checked = (engines[browser].indexOf(engine) < 0);
}
}
</script>
</body></html>

View File

@ -1,6 +0,0 @@
{
"esnext": true,
"undef": true,
"unused": true,
"node": true
}

View File

@ -1,19 +0,0 @@
#!/usr/bin/env python3
import urllib.request, urllib.error, json
banners = []
for ext in ['jpg', 'png', 'gif']:
for i in range(300):
banner = str(i) + '.' + ext
req = urllib.request.Request('http://s.4cdn.org/image/title/' + banner, method='HEAD')
try:
try:
status = urllib.request.urlopen(req).status
except urllib.error.URLError:
status = urllib.request.urlopen(req).status
except urllib.error.HTTPError as e:
status = e.status
print(banner, status)
if status == 200:
banners.append(banner)
with open('src/config/banners.json', 'w') as f:
f.write(json.dumps(banners))

View File

@ -1,26 +0,0 @@
var fs = require('fs')
function bump(version, level) {
var parts = version.split('.')
var i
for (i = 0; i < level; i++) {
parts[i] = parts[i] || '0'
}
parts[level - 1] = +parts[level - 1] + 1
for (i = level; i < parts.length; i++) {
parts[i] = '0'
}
return parts.join('.')
}
function setversion(version) {
var data = { version: version, date: new Date() }
fs.writeFileSync('version.json', JSON.stringify(data, null, 2))
}
var level = +process.argv[2]
var v = JSON.parse(fs.readFileSync('version.json', 'utf8'))
var oldversion = v.version
var version = bump(oldversion, level)
setversion(version)
console.log(`Version updated from v${oldversion} to v${version}.`)

View File

@ -1,17 +0,0 @@
var fs = require('fs')
var names = []
for (var d of fs.readdirSync('src')) {
for (var f of fs.readdirSync(`src/${d}`)) {
var m = f.match(/^([$A-Z][$\w]*)\.(?:coffee|js)$/)
if (m) names.push(m[1])
}
}
var decl = `var ${names.sort().join(', ')};\n`
var oldDecl
try {
oldDecl = fs.readFileSync('tmp/declaration.js', 'utf8')
} catch (err) {}
if (decl !== oldDecl) {
fs.writeFileSync('tmp/declaration.js', decl, 'utf8')
}

View File

@ -1,8 +0,0 @@
var fs = require('fs')
var installMap = JSON.parse(fs.readFileSync('install.json', 'utf8'))
for (var src in installMap) {
for (var dest of installMap[src]) {
fs.writeFileSync(dest, fs.readFileSync(src))
}
}

View File

@ -1,13 +0,0 @@
var fs = require('fs')
var md = require('markdown-it')({ linkify: true }).use(
require('markdown-it-anchor'),
{ slugify: s => String(s).trim().toLowerCase().replace(/\W+/g, '-') }
)
var template = require('lodash.template')
var readme = fs.readFileSync('README.md', 'utf8')
var content = md.render(readme)
var webtemplate = fs.readFileSync('template.jst', 'utf8')
var output = template(webtemplate)({ content: content })
output = output.replace(/\r\n/g, '\n')
fs.writeFileSync('test.html', output)

View File

@ -1,5 +0,0 @@
var fs = require('fs')
var text = fs.readFileSync(process.argv[2], 'utf8')
text = text.replace(/\r\n/g, '\n')
fs.writeFileSync(process.argv[3], text)

View File

@ -1,18 +0,0 @@
var fs = require('fs')
var pkg = JSON.parse(fs.readFileSync('package.json'))
var vars = {}
var k
vars.name = pkg.name
for (k in pkg.meta) {
vars[`meta_${k}`] = pkg.meta[k]
}
for (k in pkg.devDependencies) {
vars[`version_${k}`] = pkg.devDependencies[k]
}
for (k in vars) {
console.log(`\$(eval ${k} := ${vars[k]})`)
}

View File

@ -1,27 +0,0 @@
import { readFile } from 'fs/promises'
import { createFilter } from '@rollup/pluginutils'
/**
* @param {{
* include: import("@rollup/pluginutils").FilterPattern,
* exclude?: import("@rollup/pluginutils").FilterPattern,
* }} opts
* @returns {import("rollup").Plugin}
*/
export default function importBase64(opts) {
if (!opts.include) {
throw Error('include option should be specified')
}
const filter = createFilter(opts.include, opts.exclude)
return {
name: 'base64',
async load(id) {
if (!filter(id)) return
const file = await readFile(id)
return `export default '${file.toString('base64')}';`
},
}
}

View File

@ -1,53 +0,0 @@
import { createFilter } from '@rollup/pluginutils'
import { dirname } from 'path'
import { fileURLToPath } from 'url'
const __dirname = dirname(fileURLToPath(import.meta.url))
export default async function setupFileInliner(packageJson) {
/** @param {string} string */
const escape = string =>
string.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${')
/**
* @param {{
* include: import("@rollup/pluginutils").FilterPattern,
* exclude?: import("@rollup/pluginutils").FilterPattern,
* transformer?: (input: string) => string
* wrap?: boolean
* }} opts
* @returns {import("rollup").Plugin}
*/
return function inlineFile(opts) {
if (!opts.include) {
throw Error('include option should be specified')
}
if (opts.transformer && typeof opts.transformer !== 'function') {
throw new Error('If transformer is given, it must be a function')
}
const wrap = 'wrap' in opts ? opts.wrap : true
const filter = createFilter(opts.include, opts.exclude)
return {
name: 'inlineFile',
async transform(code, id) {
if (filter(id)) {
if (opts.transformer) {
code = opts.transformer(code)
}
if (!wrap) return code
code = escape(code)
code = code.replace(/<%= meta\.(\w+) %>/g, (match, $1) => {
return escape(packageJson.meta[$1])
})
return `export default \`${code}\`;`
}
},
}
}
}

View File

@ -1,116 +0,0 @@
import { rollup } from 'rollup'
import typescript from '@rollup/plugin-typescript'
import setupFileInliner from './rollup-plugin-inline-file.js'
import { dirname, resolve } from 'path'
import { fileURLToPath } from 'url'
import generateMetadata from '../src/meta/metadata.js'
import { copyFile, readFile, writeFile } from 'fs/promises'
import importBase64 from './rollup-plugin-base64.js'
import generateManifestJson from '../src/meta/manifestJson.js'
const __dirname = dirname(fileURLToPath(import.meta.url))
const buildDir = resolve(__dirname, '../dist/')
let channel = ''
if (process.argv.includes('-beta')) {
channel = '-beta'
} else if (process.argv.includes('-noupdate')) {
channel = '-noupdate'
}
;(async () => {
const packageJson = JSON.parse(
await readFile(resolve(__dirname, '../package.json'), 'utf-8')
)
const metadata = await generateMetadata(packageJson, channel)
const license = await readFile(resolve(__dirname, '../LICENSE'), 'utf8')
const version = JSON.parse(
await readFile(resolve(__dirname, '../version.json'), 'utf-8')
)
const inlineFile = await setupFileInliner(packageJson)
const bundle = await rollup({
input: resolve(__dirname, '../src/main/Main.js'),
plugins: [
typescript(),
inlineFile({
include: ['**/*.html', '**/*.css'],
}),
importBase64({
include: [
'**/*.png',
'**/*.gif',
'**/*.wav',
'**/*.woff',
'**/*.woff2',
],
}),
inlineFile({
include: '**/package.json',
wrap: false,
transformer(input) {
const data = JSON.parse(input)
return `export default ${JSON.stringify(data.meta, undefined, 1)};`
},
}),
inlineFile({
include: '**/*.json',
exclude: '**/package.json',
wrap: false,
transformer(input) {
return `export default ${input};`
},
}),
],
})
/** @type {import('rollup').OutputOptions} */
const sharedBundleOpts = {
format: 'iife',
generatedCode: {
// needed for possible circular dependencies
constBindings: false,
},
// Can't be none as long as the root file defined exports
// exports: 'none',
}
// user script
await bundle.write({
...sharedBundleOpts,
banner: metadata + license,
// file: '../builds/test/rollupOutput.js',
file: resolve(buildDir, `${packageJson.meta.path}${channel}.user.js`),
})
// chrome extension
const crxDir = resolve(buildDir, 'crx')
await bundle.write({
...sharedBundleOpts,
banner: license,
file: resolve(crxDir, 'script.js'),
})
await copyFile(
resolve(__dirname, '../src/meta/eventPage.js'),
resolve(crxDir, 'eventPage.js')
)
writeFile(
resolve(crxDir, 'manifest.json'),
generateManifestJson(packageJson, version, channel)
)
for (const file of ['icon16.png', 'icon48.png', 'icon128.png']) {
await copyFile(
resolve(__dirname, '../src/meta/', file),
resolve(crxDir, file)
)
}
})()

View File

@ -1,271 +0,0 @@
/* jshint evil: true */
var fs = require('fs')
var path = require('path')
var _template = require('lodash.template')
var esprima = require('esprima')
// disable ES6 delimiters
var _templateSettings = { interpolate: /<%=([\s\S]+?)%>/g }
// Functions used in templates.
var tools = {}
var read = (tools.read = filename =>
fs.readFileSync(filename, 'utf8').replace(/\r\n/g, '\n'))
var readJSON = (tools.readJSON = filename => JSON.parse(read(filename)))
tools.readBase64 = filename => fs.readFileSync(filename).toString('base64')
tools.readHTML = function (filename) {
var text = read(filename).replace(/^ +/gm, '').replace(/\r?\n/g, '')
text = _template(text, _templateSettings)(pkg) // package.json data only; no recursive imports
return tools.html(text)
}
tools.multiline = function (text) {
return text
.replace(/\n+/g, '\n')
.split(/^/m)
.map(JSON.stringify)
.join('+')
.replace(/"\+"/g, '\\\n')
}
// Convert JSONify-able object to Javascript expression.
var constExpression = data => JSON.stringify(data).replace(/`/g, '\\`')
function TextStream(text) {
this.text = text
}
TextStream.prototype.eat = function (regexp) {
var match = regexp.exec(this.text)
if (match && match.index === 0) {
this.text = this.text.slice(match[0].length)
}
return match
}
function parseHTMLTemplate(stream, context) {
var template = stream.text // text from beginning, for error messages
var expression = new HTMLExpression(context)
var match
try {
while (stream.text) {
// Literal HTML
if (
(match = stream.eat(
// characters not indicating start or end of placeholder, using backslash as escape
/^(?:[^\\{}]|\\.)+(?!{)/
))
) {
var unescaped = match[0].replace(/\\(.)/g, '$1')
expression.addLiteral(unescaped)
// Placeholder
} else if (
(match = stream.eat(
// symbol identifying placeholder type and first argument (enclosed by {})
// backtick not allowed in arguments as it can end embedded JS in Coffeescript
/^([^}]){([^}`]*)}/
))
) {
var type = match[1]
var args = [match[2]]
if (type === '?') {
// conditional expression can take up to two subtemplate arguments
for (var i = 0; i < 2 && stream.eat(/^{/); i++) {
var subtemplate = parseHTMLTemplate(stream, context)
args.push(subtemplate)
if (!stream.eat(/^}/)) {
throw new Error(
`Unexpected characters in subtemplate (${stream.text})`
)
}
}
}
expression.addPlaceholder(new Placeholder(type, args))
// No match: end of subtemplate (} next) or error
} else {
break
}
}
return expression.build()
} catch (err) {
throw new Error(`${err.message}: ${template}`)
}
}
function HTMLExpression(context) {
this.parts = []
this.startContext = this.endContext = context || ''
}
HTMLExpression.prototype.addLiteral = function (text) {
this.parts.push(constExpression(text))
this.endContext = this.endContext
.replace(/(=['"])[^'"<>]*/g, '$1') // remove values from quoted attributes (no '"<> allowed)
.replace(/(<\w+)( [\w-]+((?=[ >])|=''|=""))*/g, '$1') // remove attributes from tags
.replace(/^([^'"<>]+|<\/?\w+>)*/, '') // remove text (no '"<> allowed) and tags
}
HTMLExpression.prototype.addPlaceholder = function (placeholder) {
if (!placeholder.allowed(this.endContext)) {
throw new Error(
`Illegal insertion of placeholder (type ${placeholder.type}) into HTML template (at ${this.endContext})`
)
}
this.parts.push(placeholder.build())
}
HTMLExpression.prototype.build = function () {
if (this.startContext !== this.endContext) {
throw new Error(`HTML template is ill-formed (at ${this.endContext})`)
}
return this.parts.length === 0 ? '""' : this.parts.join(' + ')
}
function Placeholder(type, args) {
this.type = type
this.args = args
}
Placeholder.prototype.allowed = function (context) {
switch (this.type) {
case '$':
// escaped text allowed outside tags or in quoted attributes
return context === '' || /\=['"]$/.test(context)
case '&':
case '@':
// contents of one/many HTML element or template allowed outside tags only
return context === ''
case '?':
// conditionals allowed anywhere so long as their contents don't change context (checked by HTMLExpression.prototype.build)
return true
}
throw new Error(`Unrecognized placeholder type (${this.type})`)
}
Placeholder.prototype.build = function () {
// first argument is always JS expression; validate it so we don't accidentally break out of placeholders
var expr = this.args[0]
var ast
try {
ast = esprima.parse(expr)
} catch (err) {
throw new Error(`Invalid JavaScript in template (${expr})`)
}
if (
!(
ast.type === 'Program' &&
ast.body.length == 1 &&
ast.body[0].type === 'ExpressionStatement'
)
) {
throw new Error(`JavaScript in template is not an expression (${expr})`)
}
switch (this.type) {
case '$':
return `E(${expr})` // $ : escaped text
case '&':
return `(${expr}).innerHTML` // & : contents of HTML element or template (of form {innerHTML: "safeHTML"})
case '@':
return `E.cat(${expr})` // @ : contents of array of HTML elements or templates (see src/General/Globals.coffee for E.cat)
case '?':
return `((${expr}) ? ${this.args[1] || '""'} : ${this.args[2] || '""'})` // ? : conditional expression
}
throw new Error(`Unrecognized placeholder type (${this.type})`)
}
// HTML template generator with placeholders of forms ${}, &{}, @{}, and ?{}{}{} (see Placeholder.prototype.build)
// that checks safety of generated expressions at compile time.
tools.html = function (template) {
var stream = new TextStream(template)
var output = parseHTMLTemplate(stream)
if (stream.text) {
throw new Error(
`Unexpected characters in template (${stream.text}): ${template}`
)
}
return `{innerHTML: ${output}}`
}
function includesDir(templateName) {
var dir = path.dirname(templateName)
var subdir = path.basename(templateName).replace(/\.[^.]+$/, '')
if (fs.readdirSync(dir).indexOf(subdir) >= 0) {
return path.join(dir, subdir)
} else {
return dir
}
}
function resolvePath(includeName, templateName) {
var dir
if (includeName[0] === '/') {
dir = process.cwd()
} else {
dir = includesDir(templateName)
}
return path.join(dir, includeName)
}
function wrapTool(tool, templateName) {
return function (includeName) {
return tool(resolvePath(includeName, templateName))
}
}
function loadModules(templateName) {
var dir = includesDir(templateName)
var moduleNames = fs.readdirSync(dir).filter(f => /\.inc$/.test(f))
var modules = {}
for (var name of moduleNames) {
var code = read(path.join(dir, name))
modules[name.replace(/\.inc$/, '')] = new Function(code)()
}
return modules
}
// Import variables from package.json.
var pkg = readJSON('package.json')
function interpolate(text, data, filename) {
var context = {},
key
for (key in tools) {
context[key] = /^read/.test(key)
? wrapTool(tools[key], filename)
: tools[key]
}
for (key in pkg) {
context[key] = pkg[key]
}
if (data) {
for (key in data) {
context[key] = data[key]
}
}
context.files = fs.readdirSync(includesDir(filename))
context.require = loadModules(filename)
return _template(text, _templateSettings)(context)
}
module.exports = interpolate
if (require.main === module) {
;(function () {
// Take variables from command line.
var data = {}
for (var i = 4; i < process.argv.length; i++) {
var m = process.argv[i].match(/(.*?)=(.*)/)
data[m[1]] = m[2]
}
var text = read(process.argv[2])
text = interpolate(text, data, process.argv[2])
fs.writeFileSync(process.argv[3], text)
})()
}

View File

@ -1,7 +0,0 @@
{
"compilerOptions": {
"moduleResolution": "node16",
"types": ["@violentmonkey/types", "@types/chrome", "node"]
},
"extends": "../tsconfig.json"
}

View File

@ -1,10 +0,0 @@
var fs = require('fs')
var pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'))
console.log(
Object.keys(pkg.devDependencies)
.filter(name => !/^=/.test(pkg.devDependencies[name]))
.map(name => `${name}@${process.argv[2] || pkg.devDependencies[name]}`)
.join(' ')
)

View File

@ -1,49 +0,0 @@
var fs = require('fs')
var child_process = require('child_process')
var pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'))
var v = JSON.parse(fs.readFileSync('version.json', 'utf8'))
var name = pkg.name
var oldVersions = pkg.meta.oldVersions
var version = v.version
var date = v.date
var branch = version.replace(/\.\d+$/, '')
var headerLevel = branch.replace(/(\.0)*$/, '').split('.').length
var headerPrefix = new Array(headerLevel + 1).join('#')
var separator = `${headerPrefix} v${branch}`
var today = date.split('T')[0]
var filename = `/builds/${name}-noupdate`
var ffLink = `${oldVersions}${version}${filename}.user.js`
var crLink = `${oldVersions}${version}${filename}.crx`
var line = `**v${version}** *(${today})* - [[Userscript](${ffLink})] [[Chrome extension](${crLink})]`
var changelog = fs.readFileSync('CHANGELOG.md', 'utf8')
var breakPos = changelog.indexOf(separator)
if (breakPos >= 0) {
breakPos += separator.length
} else {
breakPos = Math.max(changelog.indexOf('\n\n#'), 0)
line = `${separator}\n\n${line}`
}
var prevVersion = changelog.substr(breakPos).match(/\*\*v([\d\.]+)\*\*/)[1]
if (prevVersion.replace(/\.\d+$/, '') !== branch) {
line += `\n- Based on v${prevVersion}.`
}
line +=
'\n- ' +
child_process
.execSync(`git log --pretty=format:%s ${prevVersion}..HEAD`)
.toString()
.replace(/\n/g, '\n- ')
fs.writeFileSync(
'CHANGELOG.md',
`${changelog.substr(0, breakPos)}\n\n${line}${changelog.substr(breakPos)}`,
'utf8'
)
console.log(`Changelog updated for v${version}.`)

View File

@ -1,22 +1,38 @@
{
"compilerOptions": {
"module": "ES2022",
"noImplicitAny": false,
"removeComments": false,
"sourceMap": true,
"allowSyntheticDefaultImports": true,
"target": "ES2020",
"allowJs": true,
"checkJs": true,
"useDefineForClassFields": true,
"module": "ES2022",
"lib": [
"ES2020",
"DOM"
],
"moduleResolution": "Node",
"sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"noEmit": true,
"allowJs": true,
"jsx": "react",
"jsxFactory": "h",
"jsxFragmentFactory": "hFragment",
"types": ["@violentmonkey/types", "@types/chrome", "@types/jquery"],
"lib": ["DOM", "ES2020"],
// needs to be in the deepest dir used as target in the rollup build
// https://stackoverflow.com/q/40460790, https://github.com/rollup/plugins/issues/243
"outDir": "builds/test/crx/tsOutput"
"checkJs": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noImplicitAny": false,
"jsxFragmentFactory": "h.fragment",
"skipLibCheck": true,
"types": [
"@violentmonkey/types",
"@types/chrome"
]
},
"exclude": ["builds/test/tsOutput", "src/meta/*", "./tools/*", "dist/*"]
"include": [
"src"
],
"exclude": [
"node_modules",
"src/meta*"
]
}

View File

@ -1,4 +0,0 @@
{
"version": "1.14.22.4",
"date": "2023-02-28T22:19:57.872Z"
}

17
vite.config.ts Normal file
View File

@ -0,0 +1,17 @@
import { transform } from 'typescript';
import { defineConfig } from 'vite';
import monkey from 'vite-plugin-monkey';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
monkey({
entry: 'src/main/Main.js',
userscript: {
icon: 'https://vitejs.dev/logo.svg',
namespace: 'npm/vite-plugin-monkey',
match: ['https://www.google.com/'],
},
}),
],
});

109
web.css
View File

@ -1,109 +0,0 @@
body {
font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans,
sans-serif;
margin: 1em;
}
#header {
background-color: #eee;
margin-bottom: 1em;
text-align: center;
}
h1 {
margin: 0;
line-height: 1.5;
}
#links {
background-color: #e0e0e0;
display: table;
width: 100%;
height: 1.5em;
}
#links > a {
display: table-cell;
vertical-align: middle;
width: 20%;
color: #000;
text-decoration: none;
}
#links > a:hover,
#links > a:focus {
background-color: #ccc;
font-weight: bold;
}
a.screenshot {
display: block;
width: 640px;
max-width: 100%;
margin: auto;
}
a.screenshot > img {
width: 100%;
}
@media (min-width: 1120px) {
a.screenshot {
float: right;
margin: 0 0 1em 1em;
}
}
span.hover {
display: none;
}
a:hover + span.hover {
display: block;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
pointer-events: none;
background: rgba(0, 0, 0, 0.4);
}
span.hover > img {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
box-shadow: 5px 5px 20px rgba(0, 0, 0, 0.4);
}
@media (max-width: 960px) {
a.screenshot:hover + span.hover {
display: none;
}
}
@supports not (pointer-events: auto) {
a[href$='.png'] {
position: relative;
}
a[href$='.png']::after {
content: ' ';
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1;
}
}
h2 ~ p,
h2 ~ p + ul,
input + div,
div > h3 ~ * {
margin-left: 1em;
}
input + div {
margin-top: 1em;
margin-bottom: 1em;
margin-left: 1em;
}
h3 {
display: inline;
}
input:checked + div > h3 ~ * {
display: none;
}