├── .htaccess
├── COPYING
├── README.md
├── adminAPI.php
├── backend
├── .gitignore
├── .htaccess
├── archiver-check.php
├── archiver.php
└── setUp.php
├── cfg
├── .gitignore
├── .htaccess
├── Apache License for DroidSansMono.txt
├── DroidSansMono.ttf
├── navlinks.json
├── pages.json
├── permissions.json
├── site.json
└── styles.json
├── css
├── .htaccess
├── bstats-catalog.css
├── bstats-common.css
├── bstats-material.css
├── bstats-overrides.css
├── bstats-tomorrow.css
├── bstats-yotsuba-blue.css
├── bstats-yotsuba-pink.css
├── bstats-yotsuba.css
├── material.css
├── tomorrow.css
├── yotsuba-blue.css
├── yotsuba-pink.css
└── yotsuba.css
├── favicon.ico
├── htmls
├── accessDenied.html
├── article.html
├── article_admin.html
├── banned.html
├── copyright.html
├── dashboard.html
├── faq.html
├── ga.html
├── indexArchiveBoard.html
├── indexBoard.html
├── loginform.html
├── loginticker.html
├── navbar.html
├── notimplemented.html
├── pagebody.html
├── pagefoot.html
├── pagehead.html
├── pagelist
│ ├── pagelist_first.html
│ ├── pagelist_last.html
│ └── pagelist_middle.html
├── post
│ ├── banned_image.html
│ └── catalog.html
├── postForm.html
├── reqForm.html
├── scp.html
└── threadStats.html
├── image
├── 404.png
├── adminicon.gif
├── closed.gif
├── developericon.gif
├── fade-blue.png
├── fade.png
├── fadepink.png
├── managericon.gif
├── modicon.gif
└── sticky.gif
├── inc
├── .htaccess
├── autoload.php
├── classes
│ ├── Api
│ │ ├── AdminApi.php
│ │ ├── FuukaApiAdaptor.php
│ │ └── PublicApi.php
│ ├── Controller
│ │ ├── Action.php
│ │ └── Router.php
│ ├── ImageBoard
│ │ ├── Board.php
│ │ ├── Post.php
│ │ ├── Thread.php
│ │ └── Yotsuba.php
│ ├── Model
│ │ ├── FileInfo.php
│ │ ├── IModel.php
│ │ ├── Model.php
│ │ ├── OldModel.php
│ │ └── PostSearchResult.php
│ ├── NotFoundException.php
│ ├── Site
│ │ ├── Archivers.php
│ │ ├── Config.php
│ │ ├── PermissionException.php
│ │ ├── Site.php
│ │ └── User.php
│ └── View
│ │ ├── BoardIndexPage.php
│ │ ├── BoardPage.php
│ │ ├── Catalog.php
│ │ ├── FancyPage.php
│ │ ├── HtmlElement.php
│ │ ├── IPage.php
│ │ ├── JsonPage.php
│ │ ├── OrphanPost.php
│ │ ├── Page.php
│ │ ├── Pages
│ │ ├── Apply.php
│ │ ├── Banned.php
│ │ ├── Captcha.php
│ │ ├── Dashboard.php
│ │ ├── Faq.php
│ │ ├── FourOhFour.php
│ │ ├── Index.php
│ │ ├── News.php
│ │ ├── Reports.php
│ │ └── ServerControlPanel.php
│ │ ├── PostRenderer.php
│ │ ├── Search.php
│ │ └── ThreadView.php
├── config.php
└── globals.php
├── index.php
├── script
├── .htaccess
├── AdvancedSearch.js
├── boardUpdate.js
├── bstats-admin.js
├── bstatsnative.js
└── scp.js
└── sql
├── init.sql
└── newboard.sql
/.htaccess:
--------------------------------------------------------------------------------
1 | AddOutputFilterByType DEFLATE text/plain
2 | AddOutputFilterByType DEFLATE text/html
3 | AddOutputFilterByType DEFLATE text/css
4 | AddOutputFilterByType DEFLATE application/javascript
5 | AddOutputFilterByType DEFLATE application/x-javascript
6 | AddOutputFilterByType DEFLATE application/json
7 |
8 |
9 | RewriteEngine on
10 | RewriteCond %{REQUEST_FILENAME} !-f
11 | RewriteCond %{REQUEST_FILENAME} !-d
12 |
13 | RewriteRule ^(.*)$ /index.php/$1 [NC,L,QSA]
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # b-stats
2 |
3 | Pretty good imageboard archival website software.
4 |
5 | ## What is it?
6 | Initially conceived as a website to track board statistics, b-stats has
7 | since eschewed such a goal and is now an archive for some 4chan boards.
8 |
9 | The "reference implementation" (if you can call it that) is currently
10 | located at https://archive.b-stats.org/. Most of the time, it's running
11 | exactly what you see in the repo, although I usually deploy code there
12 | prior to committing, to make sure it works in a production environment.
13 |
14 | ## Installing
15 |
16 | b-stats is written to work on Linux and Windows servers, with minimal
17 | setup required. It has been tested on both Windows and Linux using
18 | Apache 2.2 and 2.4, MySQL 5.5 and MariaDB 10, and currently uses the
19 | features of PHP 7.
20 |
21 | ### Basic Requirements
22 | - Web Server supporting URL rewriting (Apache 2.4 is known to work well)
23 | - PHP 7 (with PDO, cURL and GD enabled)
24 | - MariaDB Server (slight modification would be required to use MySQL instead)
25 |
26 | For Windows, `psexec` from Sysinternals is required to use the archive
27 | part, and should be in a directory in the system `PATH`. I'm looking for
28 | a better way, but PHP on Windows is somewhat limited.
29 |
30 | Surprisingly little RAM (under 30MB) is needed to run the archive
31 | script, although that does not take into account the amount of memory
32 | used by MariaDB, which can be in the hundreds of megabytes.
33 |
34 | ### Setup
35 |
36 | - Make a directory, set up your web server to point there.
37 | - `git clone https://github.com/bstats/b-stats.git .`
38 | - `cd backend`
39 | - `php ./setUp.php`
40 | - Go to your website
41 | - TODO: easy way to add boards
42 | - (there's an API for it at /admin/addBoard)
43 |
44 | ## Project Goals
45 |
46 | TODO
--------------------------------------------------------------------------------
/adminAPI.php:
--------------------------------------------------------------------------------
1 | getPrivilege() >= Site::LEVEL_ADMIN) {
13 | try {
14 | switch ($_POST['a']) {
15 | case "deletePost":
16 | OldModel::deletePost($_POST['no'], $_POST['b']);
17 | $err = false;
18 | break;
19 | case "banImage":
20 | OldModel::banHash($_POST['hash']);
21 | $err = false;
22 | break;
23 | case "deleteReport":
24 | OldModel::deleteReport($_POST['no'], $_POST['b']);
25 | $err = false;
26 | break;
27 | case "banReporter":
28 | OldModel::banReporter($_POST['no'], $_POST['b']);
29 | $err = false;
30 | break;
31 | case "restorePost":
32 | if (Site::getUser()->getPrivilege() >= Site::LEVEL_TERRANCE) {
33 | OldModel::restorePost($_POST['no'], $_POST['b']);
34 | } else {
35 | list($err, $errmsg) = [true, "Check your privilege"];
36 | }
37 | break;
38 | default:
39 | $err = true;
40 | $errmsg = "No action.";
41 | break;
42 | }
43 | } catch (Exception $e) {
44 | $err = true;
45 | $errmsg = $e->getMessage();
46 | }
47 | } else {
48 | $err = true;
49 | $errmsg = "Check your privilege.";
50 | }
51 |
52 | echo json_encode(["err"=>$err,"errmsg"=>$errmsg]);
--------------------------------------------------------------------------------
/backend/.gitignore:
--------------------------------------------------------------------------------
1 | *.error
2 | *.log
3 | *.pid
4 |
--------------------------------------------------------------------------------
/backend/.htaccess:
--------------------------------------------------------------------------------
1 | Options -Indexes
2 | ErrorDocument 403 /index.php
3 | ErrorDocument 404 /index.php
4 | Order Deny,Allow
5 | Deny from all
--------------------------------------------------------------------------------
/backend/archiver-check.php:
--------------------------------------------------------------------------------
1 | getBoards(true);
9 |
10 | $log = function($msg) {
11 | echo $msg.PHP_EOL;
12 | file_put_contents("check.log", $msg.PHP_EOL, FILE_APPEND);
13 | };
14 |
15 | $log("Checking at ".date('c'));
16 |
17 | foreach($boards as $board)
18 | {
19 | if( !$board->isArchive() )
20 | continue;
21 | $status = Archivers::getStatus($board->getName());
22 | switch($status) {
23 | case Archivers::STOPPED_UNCLEAN:
24 | Archivers::run($board->getName());
25 | $log("Restarted uncleanly stopped archiver for " . $board->getName() . ".");
26 | break;
27 | case Archivers::RUNNING:
28 | $log("Archiver for {$board->getName()} is running normally.");
29 | break;
30 | case Archivers::STOPPING:
31 | $log("Archiver for {$board->getName()} is stopping normally.");
32 | break;
33 | case Archivers::STOPPED:
34 | $log("Archiver for {$board->getName()} is stopped normally.");
35 | break;
36 | }
37 | }
--------------------------------------------------------------------------------
/backend/setUp.php:
--------------------------------------------------------------------------------
1 | report_mode = MYSQLI_REPORT_ALL;
53 |
54 | user_entry_1:
55 | $root_username = readline("Enter your mysql root username: ");
56 | $root_password = readline("Enter your mysql root password: ");
57 | echo "Attempting login... ";
58 | try {
59 | $db = new mysqli('localhost', $root_username, $root_password) ;
60 | } catch (Exception $e ) {
61 | echo "Unable to login!".PHP_EOL;
62 | echo "Specific error: ".$e->getMessage().PHP_EOL;
63 | if (strtolower(readline("Try retyping user info? (y/n): ")) == "y") {
64 | goto user_entry_1;
65 | } else {
66 | echo "Make sure MySQL is configured properly and run this script again.".PHP_EOL;
67 | exit;
68 | }
69 | }
70 | echo "Login success!",PHP_EOL;
71 |
72 | /*
73 | * Create or select database.
74 | */
75 | if(strtolower(readline("Create a new database? (y/n)")) == "y"){
76 | database_new_entry:
77 | $database = $db->real_escape_string(readline("Enter your database name: "));
78 | echo "Attempting to create database `$database`... ";
79 | try {
80 | $db->query("CREATE DATABASE `$database` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;");
81 | } catch (Exception $ex) {
82 | echo "Could not create database `$database`!".PHP_EOL;
83 | echo "Specific error: ".$e->getMessage().PHP_EOL;
84 | if (strtolower(readline("Try re-entering user info? (y/n): ")) == "y") {
85 | goto user_entry_1;
86 | } else {
87 | echo "Make sure MySQL is configured properly and run this script again.".PHP_EOL;
88 | exit;
89 | }
90 | }
91 | echo "Success!".PHP_EOL;
92 | }
93 | else{
94 | database_existing_entry:
95 | $database = $db->real_escape_string(readline("Enter your existing database name: "));
96 | echo "Connecting to `$database`... ";
97 | try {
98 | $db->select_db($database);
99 | } catch (Exception $ex) {
100 | echo "Error! Could not select database `$database`.".PHP_EOL;
101 | if (strtolower(readline("Try re-entering db name? (y/n): ")) == "y") {
102 | goto database_existing_entry;
103 | } elseif (strtolower(readline("Try making a new db instead? (y/n): ")) == "y") {
104 | goto database_new_entry;
105 | } else{
106 | echo "Make sure MySQL is configured properly and run this script again.".PHP_EOL;
107 | exit;
108 | }
109 | }
110 | echo "Success!".PHP_EOL;
111 | }
112 |
113 | /*
114 | * Create or enter user information.
115 | */
116 | if(strtolower(readline("Create new readonly and read+write users? (y/n)")) == "y"){
117 | $username_ro = readline("Enter your new read-only user's username: ");
118 | $password_ro = readline("Enter your new read-only user's password: ");
119 | $username_rw = readline("Enter your new read+write user's username: ");
120 | $password_rw = readline("Enter your new read+write user's password: ");
121 |
122 | echo "Creating users... ";
123 | try {
124 | $db->query("CREATE USER '".$db->real_escape_string($username_ro).
125 | "'@'localhost' IDENTIFIED BY '".$db->real_escape_string($password_ro)."';");
126 | $db->query("CREATE USER '".$db->real_escape_string($username_rw).
127 | "'@'localhost' IDENTIFIED BY '".$db->real_escape_string($password_rw)."';");
128 | $db->query("GRANT SELECT ON `$database`.* TO '".$db->real_escape_string($username_ro)."'@'localhost'; ");
129 | $db->query("GRANT ALL ON `$database`.* TO '".$db->real_escape_string($username_rw)."'@'localhost'; ");
130 | } catch (Exception $ex) {
131 | echo "Could not create users!".PHP_EOL;
132 | echo "Specific error: ".$e->getMessage();
133 | if (strtolower(readline("Try re-entering root user info? (y/n): ")) == "y") {
134 | goto user_entry_1;
135 | } else {
136 | echo "Make sure MySQL is configured properly and run this script again.".PHP_EOL;
137 | exit;
138 | }
139 | }
140 | echo "Success!".PHP_EOL;
141 | }
142 | else{
143 | $username_ro = readline("Enter your new read-only user's username: ");
144 | $password_ro = readline("Enter your new read-only user's password: ");
145 | $username_rw = readline("Enter your new read+write user's username: ");
146 | $password_rw = readline("Enter your new read+write user's password: ");
147 | }
148 |
149 | echo PHP_EOL.PHP_EOL.PHP_EOL."[ SERVER CONFIG ]".PHP_EOL.PHP_EOL;
150 | echo "The site must know its servers for the front-end, thumbs, images, and swfs.".PHP_EOL;
151 | $servers = [];
152 | foreach(["site","images","thumbs","swf"] as $server)
153 | {
154 | echo "Server: $server";
155 | $servers[$server]["http"] = strtolower(readline("Use http (y/n): ")) == "y";
156 | $servers[$server]["hostname"] = readline("Enter HTTP hostname: ");
157 | $servers[$server]["port"] = readline("Enter HTTP port: ");
158 | $servers[$server]["https"] = strtolower(readline("Use https (y/n): ")) == "y";
159 | $servers[$server]["httpshostname"] = readline("Enter HTTPS hostname: ");
160 | $servers[$server]["httpsport"] = readline("Enter HTTPS port: ");
161 | }
162 |
163 | echo PHP_EOL."For the image, thumbs, and swf servers we need the URL format.".PHP_EOL;
164 | echo "The format should look something like this:".PHP_EOL;
165 | echo "/dir/%hex%%ext%".PHP_EOL;
166 | echo "Available paramaters:".PHP_EOL;
167 | echo "%hex% : the media's MD5 hash".PHP_EOL;
168 | echo " %1% : the first hex digit of the MD5".PHP_EOL;
169 | echo " %2% : the second hex digit of the MD5".PHP_EOL;
170 | echo "%ext% : the media's file extension".PHP_EOL;
171 | foreach(["images","thumbs","swf"] as $server)
172 | {
173 | $servers[$server]["format"] = readline("Enter $server URL format: ");
174 | }
175 |
176 | echo "Writing mysql configuration to ../cfg/mysql.json ... ";
177 | $mysql = [];
178 | $mysql["read-only"] = [];
179 | $mysql["read-write"] = [];
180 |
181 | $mysql["read-only"]["username"] = $username_ro;
182 | $mysql["read-only"]["password"] = $password_ro;
183 | $mysql["read-only"]["db"] = $database;
184 | $mysql["read-only"]["server"] = 'localhost';
185 | $mysql["read-write"]["username"] = $username_rw;
186 | $mysql["read-write"]["password"] = $password_rw;
187 | $mysql["read-write"]["db"] = $database;
188 | $mysql["read-write"]["server"] = 'localhost';
189 | file_put_contents("../cfg/mysql.json", json_encode($mysql, JSON_PRETTY_PRINT));
190 | echo "Done.".PHP_EOL;
191 |
192 | echo "Writing server configuration to ../cfg/servers.json ... ";
193 | file_put_contents("../cfg/mysql.json", json_encode($servers, JSON_PRETTY_PRINT));
194 |
195 | echo "Done.".PHP_EOL;
196 |
197 |
198 | echo "Setting up required tables...".PHP_EOL;
199 | $driver->report_mode = MYSQLI_REPORT_ERROR;
200 | if(!$db->query(file_get_contents("../sql/init.sql"))){
201 | echo "Could not set up all the tables for some reason! Start over!".PHP_EOL;
202 | echo "Error(s): ".PHP_EOL;
203 | foreach($db->error_list as $error) {
204 | print_r($error);
205 | echo PHP_EOL;
206 | }
207 | exit;
208 | }
209 |
210 | echo PHP_EOL.PHP_EOL.PHP_EOL."[ SITE CUSTOMIZATION ]".PHP_EOL.PHP_EOL;
211 | $site = [];
212 | $site['name'] = readline("Enter a site name to show up on all pages (e.g., 'b-stats archive'): ");
213 | $date = date('j F Y');
214 | $site['subtitle'] = readline("Enter a subtitle to show up below the name (e.g. 'since $date'): ");
215 | $site['pagetitle'] = readline("Enter a default HTML page title (e.g., 'archive'): ");
216 | $site['ga_id'] = readline("If you have a Google Analytics ID, enter it, otherwise press enter: ");
217 | if($site['ga_id'] == '') {
218 | unset($site['ga_id']);
219 | }
220 |
221 | echo "Writing site configuration to ../cfg/site.json ... ";
222 | $json = json_encode($site,JSON_PRETTY_PRINT);
223 | file_put_contents("../cfg/site.json", json_encode($site, JSON_PRETTY_PRINT));
224 | echo "Done!".PHP_EOL.PHP_EOL;
225 |
226 | echo "One last thing, before your site is ready. You must create".PHP_EOL;
227 | echo "an admin user account.".PHP_EOL;
228 |
229 | $username = readline("Enter admin username: ");
230 | $password = readline("Enter admin password: ");
231 |
232 | Model::get()->addUser($username, $password, Site::LEVEL_TERRANCE, "yotsuba");
233 |
234 | echo "That's it! Your site is ready to go (hopefully)!".PHP_EOL;
235 | echo "Thank you for choosing my shitty PHP scripts!".PHP_EOL;
236 | echo PHP_EOL."~terrance".PHP_EOL;
--------------------------------------------------------------------------------
/cfg/.gitignore:
--------------------------------------------------------------------------------
1 | mysql.json
2 | servers.json
3 |
--------------------------------------------------------------------------------
/cfg/.htaccess:
--------------------------------------------------------------------------------
1 | Options -Indexes
2 | Order deny,allow
3 | Deny from all
--------------------------------------------------------------------------------
/cfg/DroidSansMono.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bstats/b-stats/8d97d6ed837d772f57c318908b20eec347315058/cfg/DroidSansMono.ttf
--------------------------------------------------------------------------------
/cfg/navlinks.json:
--------------------------------------------------------------------------------
1 | {
2 | "Home":"/",
3 | "FAQs":"/faq",
4 | "News":"/news"
5 | }
6 |
--------------------------------------------------------------------------------
/cfg/pages.json:
--------------------------------------------------------------------------------
1 | {
2 | "": "Index",
3 | "index": "Index",
4 | "apply": "Apply",
5 | "dash": "Dashboard",
6 | "faq": "Faq",
7 | "news": "News",
8 | "reports": "Reports",
9 | "scp": "ServerControlPanel",
10 | "captcha": "Captcha"
11 | }
--------------------------------------------------------------------------------
/cfg/permissions.json:
--------------------------------------------------------------------------------
1 | {
2 | "search" : 2,
3 | "delete" : 3,
4 | "admin" : 3,
5 | "news" : 9,
6 | "owner" : 9
7 | }
8 |
--------------------------------------------------------------------------------
/cfg/site.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "/b/stats 2.0: beta edition",
3 | "subtitle": "since 27 Jan 2013
Please send questions/comments/spam/death threats/love letters to webmaster (at) b-stats.org
All times EST/EDT",
4 | "pagetitle": "archive"
5 | }
--------------------------------------------------------------------------------
/cfg/styles.json:
--------------------------------------------------------------------------------
1 | {
2 | "yotsuba":["/css/yotsuba.css","/css/bstats-yotsuba.css"],
3 | "yotsuba-blue":["/css/yotsuba-blue.css","/css/bstats-yotsuba-blue.css"],
4 | "tomorrow":["/css/tomorrow.css","/css/bstats-tomorrow.css"],
5 | "yotsuba-pink":["/css/yotsuba-pink.css","/css/bstats-yotsuba-pink.css"],
6 | "material-design":["/css/yotsuba-blue.css","https://fonts.googleapis.com/css?family=Roboto","/css/material.css","/css/bstats-material.css"],
7 | "none":[""]
8 | }
--------------------------------------------------------------------------------
/css/.htaccess:
--------------------------------------------------------------------------------
1 | ExpiresActive On
2 | Header unset ETag
3 | ExpiresDefault "access plus 1 day"
4 | Header append Cache-Control "max-age=86400"
5 | Header append Cache-Control "public"
--------------------------------------------------------------------------------
/css/bstats-catalog.css:
--------------------------------------------------------------------------------
1 | #threads {
2 | padding: 20px 0px;
3 | text-align: center;
4 | }
5 |
6 | .thread {
7 | vertical-align: top;
8 | display: inline-block;
9 | word-wrap: break-word;
10 | overflow: hidden;
11 | margin-top: 5px;
12 | padding: 5px 0 3px 0;
13 | position: relative;
14 | }
15 |
16 | .thread a {
17 | border: none;
18 | }
19 |
20 | .thread img {
21 | display: inline;
22 | }
23 |
24 | .small .thread {
25 | width: 165px;
26 | }
27 |
28 | .large .thread {
29 | width: 270px;
30 | }
31 |
32 | .extended-small .thread {
33 | width: 165px;
34 | max-height: 320px;
35 | }
36 |
37 | .extended-large .thread {
38 | width: 270px;
39 | max-height: 410px;
40 | }
41 |
42 | .hl {
43 | border-style: solid;
44 | border-width: 3px;
45 | }
46 |
47 | .pinned {
48 | border: 3px dashed #34345C;
49 | }
50 |
51 | .pinned:hover {
52 | border-color: red;
53 | }
54 |
55 | .thumb {
56 | display: block;
57 | margin: auto;
58 | z-index: 2;
59 | box-shadow: 0 0 5px rgba(0,0,0,0.25);
60 | min-height: 50px;
61 | min-width: 50px;
62 | }
63 |
64 | .meta {
65 | cursor: help;
66 | font-size: 10px;
67 | line-height: 8px;
68 | margin-top: 1px;
69 | }
70 |
71 | .teaser {
72 | display: none;
73 | }
74 |
75 | .extended-small .teaser,.extended-large .teaser {
76 | display: block;
77 | }
78 |
79 | .teaser s {
80 | background-color: #000;
81 | color: #000;
82 | text-decoration: none;
83 | }
84 |
85 | .teaser s:focus,.teaser s:hover {
86 | color: #fff;
87 | }
88 |
89 | .left {
90 | float: left;
91 | }
92 |
93 | .right {
94 | float: right;
95 | }
96 |
97 | .clear {
98 | clear: both;
99 | }
100 |
--------------------------------------------------------------------------------
/css/bstats-common.css:
--------------------------------------------------------------------------------
1 | h1,h2,h3,h4,h5,h6{
2 | text-align:center;
3 | }
4 |
5 | .centertext {
6 | text-align:center;
7 | }
8 |
9 | .pageTime {
10 | text-align:center;
11 | font-size:75%;
12 | }
13 | .navbar{
14 | position:fixed;
15 | z-index:100;
16 | top:0px;
17 | left:0px;
18 | right:0px;
19 | vertical-align:middle;
20 | cursor:default;
21 | display:inline-block;
22 | line-height: 18px;
23 | }
24 |
25 | .threadbox{
26 | height:75px;
27 | /*width:200px;*/
28 | vertical-align:middle;
29 | margin:20px 15px;
30 | margin-bottom: 50px;
31 | /*float:left;*/
32 | display:inline-block;
33 | font-size: 0.75em;
34 | }
35 |
36 | .navelement{
37 | height:18px;
38 | display:inline-block;
39 | width:auto;
40 | padding: 2px 3px;
41 | vertical-align:middle;
42 | /*cursor:pointer;*/
43 | text-decoration:none;
44 | }
45 | .navelement * {
46 | cursor:pointer;
47 | text-decoration:none;
48 | }
49 | .copyright,.push{
50 | height:35px;
51 | font-size: .8em;
52 | text-align:center;
53 | }
54 | .copyright {
55 | margin: 0 auto;
56 | display: block;
57 | }
58 | .wrapper {
59 | min-height: 100%;
60 | height: auto !important;
61 | height: 100%;
62 | /*margin: -20px auto -15px; /* the bottom margin is the negative value of the footer's height */
63 | /*padding: 20px 0 0 0;*/
64 | margin:0 auto -35px;
65 | padding:0;
66 | padding-top: 5px;
67 | }
68 |
69 | .shadow {
70 | text-shadow:2px 2px 5px rgba(0,0,0,0.5);
71 | }
72 |
73 | a.button, input.button {
74 | display:inline-block;
75 | border: 1px solid rgba(0,0,0,0.5);
76 | text-decoration: none;
77 | padding: 2px;
78 | background-color: rgba(128,128,128,0.5);
79 | color: black;
80 | border-radius: 4px;
81 | min-width:1em;
82 | }
83 |
84 | a.button:hover, input.button:hover{
85 | background-color: rgba(0,0,0,0.5);
86 | border: 1px solid rgba(0,0,0,1);
87 | color: rgba(255,255,255,1) !important;
88 | }
89 |
90 | .faqItem {
91 | width: 40em;
92 | margin: 0 auto;
93 | padding: 2em;
94 | border-style: solid;
95 | border-width: 2px;
96 | border-color: black;
97 | border-radius: 8px;
98 | margin-bottom: 1.5em;
99 | box-shadow: 0px 0px 15px rgba(0,0,0,0.5);
100 | }
101 |
102 | .faqQuestion, .faqAnswer {
103 | padding-left: 25px ;
104 | text-indent: -25px ;
105 | margin-bottom: 8px;
106 | margin-left: 8px
107 | }
108 |
109 | .faqQuestion {
110 | font-size: 20px;
111 | text-shadow: 1px 1px 3px rgba(0,0,0,0.15);
112 | }
113 |
114 | .faqQuestion:first-letter, .faqAnswer:first-letter {
115 | font-size: 18px;
116 | font-weight: bold;
117 | width: 2em;
118 | font-family: monospace;
119 | text-shadow: none;
120 | }
121 |
122 |
123 | #hover {
124 | position: fixed;
125 | }
126 |
127 | #hover .postContainer .post {
128 | border: 1px solid rgba(128, 128, 128, 0.25);
129 | padding: 1px;
130 | box-shadow: rgba(0, 0, 0, 0.15) 0px 1px 2px;
131 | margin-right: 5px;
132 | }
133 |
134 | .inline {
135 | border: 1px solid rgba(128, 128, 128, 0.25);
136 | display: table;
137 | background-color: rgba(255, 255, 255, .14);
138 | margin: 2px 0;
139 | }
140 |
141 | .inline * {
142 | background-color: rgba(0,0,0,0) !important;
143 | }
144 |
145 | .inlined {
146 | opacity: .5;
147 | }
148 |
149 | .expand-loading {
150 | opacity: 0.5;
151 | }
152 |
153 | .expand-loaded {
154 | display:none !important;
155 | }
156 |
157 | .expanded {
158 |
159 | }
160 |
161 | table.flashListing tr:not(:first-of-type) * {
162 | border-color: rgba(0,0,0,0);
163 | }
164 |
165 | a.miniButton{
166 | font-size:0.7em;
167 | display:inline-block;
168 | border: 1px solid rgba(128,128,128,0.3);
169 | position: relative;
170 | padding: 0 1px;
171 | top: -2px;
172 | }
173 | a.miniButton:hover{
174 | border: 1px solid rgba(0,0,0,0.3);
175 | }
176 | a.clicked, a.clicked:hover {
177 | border: 1px solid rgba(128,128,128,0.3);
178 | }
179 | .dashTable {
180 | border: none;
181 | width: 25em;
182 |
183 | }
184 | .dashTable th {
185 | background-color: rgba(0,0,0,0.5);
186 | }
187 | .dashTable tr, .dashTable td {
188 |
189 | }
190 | .dashTable td{
191 | /*width:50%;*/
192 | }
193 | .dashTable tr td:first-of-type {
194 | font-weight: bold;
195 | }
196 | .dashTable tr td:nth-of-type(2) {
197 |
198 | }
199 |
200 | .reportTable {
201 | border: none;
202 | width: 35em;
203 | text-align:center;
204 | }
205 | .reportTable th {
206 | background-color: rgba(0,0,0,0.5);
207 | }
208 | .right{
209 | text-align:right;
210 | }
211 | .errorMsg{
212 | margin-top: 1em;
213 | font-size: 2em;
214 | text-align:center;
215 | }
216 |
217 | .newsBox {
218 | width: 40em;
219 | margin: 0 auto 2em auto;
220 | }
221 |
222 | .newsArticleHead {
223 | border-radius: 8px 8px 0 0;
224 | border: 1px solid;
225 | padding-left: 5px;
226 | padding-top: 3px;
227 | line-height: 1.5em;
228 | }
229 |
230 | .newsArticleTitle {
231 | font-weight: bold;
232 | font-size: 1.5em;
233 | }
234 |
235 | .newsArticleInfo {
236 | font-size: 0.8em;
237 | }
238 |
239 | .newsArticleBody {
240 | padding: 1em 0.5em 0.5em 0.5em;
241 | border-bottom: 1px solid;
242 | border-left:1px solid;
243 | border-right:1px solid;
244 | }
245 |
246 | .borderless, .borderless *{
247 | border: none;
248 | }
249 |
250 | .boardlist_big {
251 | margin: 0 auto;
252 | text-align:center;
253 | padding: 2em;
254 | }
255 | .boardlist_big a {
256 | font-weight: bold;
257 | line-height: 2em;
258 | font-size: 1.5em;
259 | color: inherit !important;
260 | text-decoration: none;
261 | }
262 | .boardlist_big a:hover {
263 | text-decoration: underline;
264 | }
265 |
266 | .boardLinkBig .miniBoardInfo
267 | {
268 | font-size: 0.8em;
269 | position: relative;
270 | top: -1em;
271 | opacity: 0.5;
272 | }
273 |
274 | .name-col
275 | {
276 | max-width: 250px;
277 | overflow: hidden;
278 | text-overflow: ellipsis;
279 | white-space: nowrap;
280 | word-wrap: break-word;
281 | }
282 |
283 | ul.stylemenu {
284 | list-style:none;
285 | display: inline;
286 | width:100%;
287 | margin-left: 8px;
288 | display:inline;
289 | margin-right:2px;
290 | }
291 | ul.stylemenu li {
292 | float:right;
293 | position:relative;
294 | }
295 | ul.stylemenu a {
296 | display:block;
297 | }
298 | ul.stylemenu ul{
299 | background:rgba(255,255,255,0); /* But! Let's make the background fully transparent where we can, we don't actually want to see it if we can help it... */
300 | list-style:none;
301 | position:absolute;
302 | z-index: 99;
303 | width: 8em;
304 | box-shadow: 0px 3px 3px rgba(0,0,0,0.3);
305 | padding: 0;
306 | right:9999em; /* Hide off-screen when not needed (this is more accessible than display:none;) */
307 | }
308 | ul.stylemenu ul li{
309 | float:none;
310 | text-align:right;
311 | }
312 | ul.stylemenu li:hover ul{ /* Display the dropdown on hover */
313 | right:0px; /* Bring back on-screen when needed */
314 | }
315 | strike strike {
316 | text-decoration: none;
317 | position: relative;
318 | }
319 | strike strike:after{
320 | content: ' ';
321 | font-size: inherit;
322 | display: block;
323 | position: absolute;
324 | right: 0;
325 | left: 0;
326 | top: 40%;
327 | bottom: 40%;
328 | border-top: 1px solid #000;
329 | border-bottom: 1px solid #000;
330 | }
331 |
332 | a.backlink {
333 | text-decoration: none;
334 | }
335 |
336 | a.backlink span.linkpart {
337 | padding: 0;
338 | }
339 |
--------------------------------------------------------------------------------
/css/bstats-material.css:
--------------------------------------------------------------------------------
1 | .navbar {
2 | background-color:#B3B9DB;
3 | box-shadow: 0 0 3px 3px rgba(0,0,0,0.35);
4 | }
5 | .navelement {
6 | background-color:#B3B9DB;
7 | }
8 | .navelement:hover{
9 | background-color:#9AC;
10 | color:#00C;
11 | }
12 | .threadbox a img{
13 | box-shadow: 0 0 2px 2px #BBC;
14 | }
15 | .threadbox a img:hover{
16 | box-shadow: 0 0 3px 3px #99A;
17 | }
18 | .dashTable th, .reportTable th {
19 | background-color: #8CE;
20 | }
21 |
22 | .newsArticleHead{
23 | background-color: #ADF;
24 | }
25 |
26 | .newsArticleBody {
27 | background-color: #fff;
28 | color: black;
29 | }
30 |
31 | table.flashListing {
32 | border-collapse: separate;
33 | border-spacing: 1px;
34 | }
35 |
36 | .topLinks {
37 | color: transparent;
38 | }
--------------------------------------------------------------------------------
/css/bstats-overrides.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 22px;
3 | margin: 0;
4 | }
5 |
6 | div.pagelist {
7 | display: inline-block;
8 | float: none;
9 | }
10 | /*a.anchor{
11 | padding-top:25px;
12 | margin-top:-25px;
13 | -webkit-background-clip:content-box;
14 | background-clip:content-box;
15 | display: block;
16 | position: static;
17 | }*/
--------------------------------------------------------------------------------
/css/bstats-tomorrow.css:
--------------------------------------------------------------------------------
1 | .navbar {
2 | background-color:#282a2e;
3 | box-shadow: 0 0 4px 4px #111;
4 | }
5 | .navelement {
6 | background-color:#282a2e;
7 | }
8 | .navelement:hover{
9 | background-color:#222;
10 | color:#c00;
11 | }
12 | .threadbox a{
13 | box-shadow: 0 0 2px 2px #1a1a1a;
14 | }
15 | .threadbox a:hover{
16 | box-shadow: 0 0 3px 3px #111;
17 | }
18 | table {
19 | border-collapse: collapse;
20 | }
21 | td, tr {
22 | border: 1px solid black;
23 |
24 | }
25 | .newsBox{
26 | border-radius: 8px 8px 0px 0px;
27 | box-shadow: 0px 0px 6px rgba(0,0,0,0.75);
28 | }
29 | .newsArticleHead{
30 | background-color: #111;
31 | }
32 | .newsArticleBody {
33 | background-color: #282a2e;
34 | }
35 | table.flashListing {
36 | border-collapse: separate;
37 | border-spacing:1px;
38 | }
39 | table.flashListing * {
40 | border-color: rgba(0,0,0,0);
41 | }
--------------------------------------------------------------------------------
/css/bstats-yotsuba-blue.css:
--------------------------------------------------------------------------------
1 | .navbar {
2 | background-color:#B3B9DB;
3 | box-shadow: 0 0 3px 3px rgba(0,0,0,0.35);
4 | }
5 | .navelement {
6 | background-color:#B3B9DB;
7 | }
8 | .navelement:hover{
9 | background-color:#9AC;
10 | color:#00C;
11 | }
12 | .threadbox a img{
13 | box-shadow: 0 0 2px 2px #BBC;
14 | }
15 | .threadbox a img:hover{
16 | box-shadow: 0 0 3px 3px #99A;
17 | }
18 | .dashTable th, .reportTable th {
19 | background-color: #8CE;
20 | }
21 |
22 | .newsArticleHead{
23 | background-color: #ADF;
24 | }
25 |
26 | .newsArticleBody {
27 | background-color: #fff;
28 | color: black;
29 | }
30 |
31 | table.flashListing {
32 | border-collapse: separate;
33 | border-spacing: 1px;
34 | }
--------------------------------------------------------------------------------
/css/bstats-yotsuba-pink.css:
--------------------------------------------------------------------------------
1 | .navbar {
2 | background-color:#cf7dca;
3 | box-shadow: 0 0 3px 3px rgba(0,0,0,0.3);
4 | }
5 | .navelement {
6 | background-color:#cf7dca;
7 | }
8 | .navelement:hover{
9 | background-color: #c876bf;
10 | color:#c00;
11 | }
12 | .threadbox a img{
13 | box-shadow: 0 0 2px 2px #CCB;
14 | }
15 | .threadbox a img:hover{
16 | box-shadow: 0 0 3px 3px #AA9;
17 | }
18 | .dashTable th, .reportTable th {
19 | background-color: #d66384;
20 | }
--------------------------------------------------------------------------------
/css/bstats-yotsuba.css:
--------------------------------------------------------------------------------
1 | .navbar {
2 | background-color:#f0e0d6;
3 | box-shadow: 0 0 3px 3px rgba(0,0,0,0.2);
4 | }
5 | .navelement {
6 | background-color:#f0e0d6;
7 | }
8 | .navelement:hover{
9 | background-color:rgb(200, 150, 118);
10 | color:#c00;
11 | }
12 | .threadbox a img{
13 | box-shadow: 0 0 2px 2px #CCB;
14 | }
15 | .threadbox a img:hover{
16 | box-shadow: 0 0 3px 3px #AA9;
17 | }
18 | .dashTable th, .reportTable th {
19 | background-color: #EA8;
20 | }
21 |
22 | .newsArticleHead{
23 | background-color: #fca;
24 | }
25 |
26 | .newsArticleBody {
27 | background-color: #fff;
28 | color: black;
29 | }
30 |
31 | table.flashListing {
32 | border-collapse: separate;
33 | border-spacing: 1px;
34 | }
--------------------------------------------------------------------------------
/css/material.css:
--------------------------------------------------------------------------------
1 |
2 | body{background:#f5f5f5;color:#000;font-family:'Roboto','Helvetica','Arial',sans-serif;font-size:14px;font-weight:400;line-height:20px;padding:0;margin:0;}.board{background:#e5e5e5;border-top:1px solid #ccc;margin-bottom:50px;padding-bottom:25px;}.is_index .board{padding-bottom:50px;}a,a:visited,.quoteLink,.quotelink,.deadlink,div#boardNavMobile .pageJump a,.persistentNav .pageJump a,.summary a.replylink,div.post div.postInfo span.postNum a:visited,div.post div.postInfo span.postNum a.replylink{color:rgb(83,109,254)!important;text-decoration:none!important;}a:hover,.quoteLink:hover,.quotelink:hover,.deadlink:hover,.summary a.replylink:hover,.persistentNav .pageJump a:hover,div#boardNavMobile .pageJump a:hover,div.post div.postInfo span.postNum a:hover,.posteruid .hand:hover{color:rgb(83,109,254)!important;text-decoration:underline!important;}.postInfo a.postMenuBtn,.postInfo a.postMenuBtn:hover{color:rgb(83,109,254)!important;text-decoration:none!important;}div.postContainer{display:block;width:80%;margin:15px auto 0 auto;}.thread{margin-top:30px!important;padding-top:15px;border-top:1px solid #ccc;}.thread:first-child{padding-top:0;border-top:none;}.md-plus-btn{border-radius:50%;font-size:24px;height:56px;margin:auto;min-width:56px;width:56px;padding:0;overflow:hidden;box-shadow:0 1px 1.5px 0 rgba(0,0,0,.12),0 1px 1px 0 rgba(0,0,0,.24);position:relative;line-height:normal;position:fixed;right:25px;bottom:25px;}div.boardBanner>div.boardTitle{margin-top:40px;font-size:36px;color:#676767;margin-bottom:40px;font-family:'Roboto','Helvetica','Arial',sans-serif;font-weight:normal;letter-spacing:0;}:-ms-input-placeholder{color:rgba(0,0,0,.35)}::-webkit-input-placeholder{color:rgba(0,0,0,.35)}:-moz-placeholder{color:rgba(0,0,0,.35)}::-moz-placeholder{color:rgba(0,0,0,.35)}#togglePostFormLink,.ad-plea,#blotter tfoot td,#ctrl-top,div.post div.postInfo span.postNum,div.pagelist div.pages{color:transparent;}table.postForm>tbody>tr>td:first-child,.thread-stats{color:#000;}.postInfo .backlink a.quotelink,.postInfo .backlink a.quotelink:hover{color:rgb(83,109,254)!important;}.ws input[type="text"],.nws input[type="text"]{border:none!important;border-bottom:1px solid rgba(0,0,0,.12)!important;font-size:14px!important;font-family:"Helvetica","Arial",sans-serif;padding:4px!important;-webkit-transition:border-bottom-color 0.2s;transition:border-bottom-color 0.2s;}.ws input[type="text"]:focus,.ws #quickReply input[type="submit"]:focus{border-bottom:1px solid rgb(83,109,254)!important;}.nws input[type="text"]:focus,.nws #quickReply input[type="submit"]:focus{border-bottom:1px solid rgb(244,67,54)!important;}.tomorrow .extPanel,.recaptcha_input_area #recaptcha_response_field{border:0!important;}.ws input[type="submit"],.nws input[type="submit"],button{border:none;border-radius:2px;position:relative;height:28px;min-width:64px;display:inline-block;text-transform:uppercase;outline:none;cursor:pointer;background:rgb(83,109,254);color:#fff;font-size:14px;font-family:'Roboto','Helvetica','Arial',sans-serif;line-height:28px;margin-left:8px;}.nws input[type="submit"],.nws button{background:rgb(244,67,54)}#qrCaptchaContainerAlt{margin-bottom:8px;}#postForm input[type="text"]{margin:4px 0;}#postForm td:first-child{text-align:right;padding-right:10px;}.ws textarea,.nws textarea{border:0!important}.ws textarea:focus,.nws textarea:focus{border:0!important}table#postForm td{background:transparent;border:0;font-weight:normal;}#toggleMsgBtn{margin-left:5px!important;}.rules{text-align:center;}#qrHeader{background:#676767!important;color:#fff!important;}#qrHeader a{color:#fff!important;}#search-box{height:inherit;line-height:inherit;margin:0;padding:inherit;}#ctrl-top{padding-bottom:10px;border-top:1px solid #ddd;padding-top:10px;text-align:center;}.reply:target,.reply.highlight{background:#eee!important;padding:10px!important;border:0!important;}.navLinks+hr,.open-qr-wrap,.board hr,#bannerCnt,#ctrl-top+hr,#ctrl-top>hr{display:none;}.navLinks{padding-bottom:8px;color:transparent;width:80%;margin:auto;}.navLinks label{color:rgb(83,109,254);}.navLinks label+span{color:#000;}.navLinksBot{margin-top:30px;text-align:left;}div#boardNavDesktop,div#boardNavDesktopFoot,div#boardNavMobile{background:#fff;padding:5px 10px;box-shadow:0 2px 2px 0 rgba(0,0,0,.08),0 3px 1px -2px rgba(0,0,0,.06),0 1px 5px 0 rgba(0,0,0,.04);margin-bottom:25px;color:transparent;font-size:14px;}div#boardNavMobile{color:#000;border:0;}div#boardNavDesktopFoot{margin-bottom:0;margin-top:25px;padding-top:25px;padding-bottom:25px;}div#boardNavDesktop a,div#boardNavDesktopFoot a{margin-left:-3px;margin-right:-3px;padding:0;}.persistentNav .pageJump a{margin:0;padding-right:5px;}div.pagelist{background:#fff;margin-top:25px;margin-bottom:25px;border:0;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);margin-left:25px;padding:8px;border-radius:2px;}div#absbot{background:#fff;color:#999;}div#absbot #footer-links a{text-decoration:none!important;color:rgb(83,109,254)!important;}div#absbot #footer-links a:hover{text-decoration:underline!important;}.bottomCtrl{display:none}.ad-plea-bottom+hr{display:none;}div#boardNavDesktopFoot{margin-bottom:0;}div.post,.extPanel,div.reply,.dd-menu ul{background:#fff;border-radius:2px;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);border:none;}div.pagelist div.cataloglink{border:0;}div.post{max-width:75%;margin:0;padding:10px;}.dd-menu li{padding:6px 10px!important;border:0!important;}.dd-menu li:hover{background-color:#eee!important;}#quote-preview{border:0!important;padding:10px!important;}div.op{display:block;max-width:none;}.fileText{color:#999;font-size:12px;margin-top:2px;}.op .fileText{margin-top:-5px;margin-bottom:5px;}.fileText a{color:#999!important;text-decoration:none;}.posteruid,.dateTime{color:#999;}.postInfo input[type="checkbox"]{display:none;}div.sideArrows{display:none;}hr{border:0;border-bottom:1px solid #ddd;}div.post:after{display:block;content:' ';clear:both;}div.post div.file .fileThumb img{object-fit:cover;}div.reply div.file .fileThumb img:not(.expanded-thumb){border-radius:75px;width:75px!important;height:75px!important;}div.op div.file .fileThumb img:not(.expanded-thumb){border-radius:150px;width:150px!important;height:150px!important;}.postMessage{margin-left:20px;margin-top:5px;}#boardNavDesktop::after{content:' ';display:block;clear:both;}span.summary{border-radius:0 0 2px 2px;background-color:#f5f5f5;width:80%;display:block;margin:-1px auto 0 auto;box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);border-top:1px solid rgba(0,0,0,0.1);text-indent:10px;padding:10px 0;}
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bstats/b-stats/8d97d6ed837d772f57c318908b20eec347315058/favicon.ico
--------------------------------------------------------------------------------
/htmls/accessDenied.html:
--------------------------------------------------------------------------------
1 |
General Info | 5 ||
---|---|
Your username | 8 | |
Your privilege | 11 | |
Your preferred style | 14 | |