├── .eslintrc.js
├── .gitignore
├── .htaccess
├── .vscode
├── launch.json
└── settings.json
├── 404.html
├── CNAME
├── LICENSE
├── README.md
├── _redirects
├── css
└── style.css
├── dmca.html
├── docker-compose.yml
├── httpd.conf
├── images
├── 1437338478_screen-full.svg
├── favicon.png
├── fullscreen.png
└── play.svg
├── index.html
├── js
├── EmbedIt.js
├── fix-tagsyo-link.js
├── ie_hacks.js
├── jquery.touchwipe.js
├── js.cookie.js
└── script.js
├── nodejs
├── .eslintrc.js
├── app.js
└── test-embedit.js
├── package-lock.json
├── package.json
├── redditp-redirect
└── docker-compose.yml
├── server.js
├── start.cmd
├── test-data
├── reddit-image-v2.json
└── reddit.com.json
└── vercel.json
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "browser": true
4 | },
5 | "globals": {
6 | "$": false,
7 | "toastr": false,
8 | "Cookies": false,
9 | "embedit": false,
10 | },
11 | "extends": "eslint:recommended",
12 | "rules": {
13 | "accessor-pairs": "error",
14 | "array-bracket-newline": "error",
15 | "array-bracket-spacing": [
16 | "error",
17 | "never"
18 | ],
19 | "array-callback-return": "error",
20 | "array-element-newline": "off",
21 | "arrow-body-style": "error",
22 | "arrow-parens": "error",
23 | "arrow-spacing": "error",
24 | "block-scoped-var": "off",
25 | "block-spacing": [
26 | "error",
27 | "never"
28 | ],
29 | "brace-style": "off",
30 | "callback-return": "error",
31 | "camelcase": "off",
32 | "capitalized-comments": "off",
33 | "class-methods-use-this": "error",
34 | "comma-dangle": "off",
35 | "comma-spacing": "off",
36 | "comma-style": [
37 | "error",
38 | "last"
39 | ],
40 | "complexity": "error",
41 | "computed-property-spacing": [
42 | "error",
43 | "never"
44 | ],
45 | "consistent-return": "off",
46 | "consistent-this": "error",
47 | "curly": "off",
48 | "default-case": "off",
49 | "dot-location": [
50 | "error",
51 | "property"
52 | ],
53 | "dot-notation": "off",
54 | "eol-last": "error",
55 | "eqeqeq": "off",
56 | "for-direction": "error",
57 | "func-call-spacing": "error",
58 | "func-name-matching": "error",
59 | "func-names": [
60 | "error",
61 | "never"
62 | ],
63 | "func-style": "off",
64 | "function-paren-newline": "off",
65 | "generator-star-spacing": "error",
66 | "getter-return": "error",
67 | "global-require": "error",
68 | "guard-for-in": "error",
69 | "handle-callback-err": "error",
70 | "id-blacklist": "error",
71 | "id-length": "off",
72 | "id-match": "error",
73 | "implicit-arrow-linebreak": "error",
74 | "indent": "off",
75 | "indent-legacy": "off",
76 | "init-declarations": "off",
77 | "jsx-quotes": "error",
78 | "key-spacing": "off",
79 | "keyword-spacing": "off",
80 | "line-comment-position": "off",
81 | "linebreak-style": [
82 | "error",
83 | "unix"
84 | ],
85 | "lines-around-comment": "off",
86 | "lines-around-directive": "error",
87 | "lines-between-class-members": "error",
88 | "max-depth": "error",
89 | "max-len": "off",
90 | "max-lines": "off",
91 | "max-nested-callbacks": "error",
92 | "max-params": "error",
93 | "max-statements": "off",
94 | "max-statements-per-line": "error",
95 | "multiline-comment-style": "off",
96 | "new-cap": "error",
97 | "new-parens": "error",
98 | "newline-after-var": "off",
99 | "newline-before-return": "off",
100 | "newline-per-chained-call": "off",
101 | "no-alert": "error",
102 | "no-array-constructor": "error",
103 | "no-await-in-loop": "error",
104 | "no-bitwise": "error",
105 | "no-buffer-constructor": "error",
106 | "no-caller": "error",
107 | "no-catch-shadow": "error",
108 | "no-confusing-arrow": "error",
109 | "no-console": "off",
110 | "no-continue": "off",
111 | "no-div-regex": "error",
112 | "no-duplicate-imports": "error",
113 | "no-else-return": "off",
114 | "no-empty-function": "error",
115 | "no-eq-null": "off",
116 | "no-eval": "error",
117 | "no-extend-native": "error",
118 | "no-extra-bind": "error",
119 | "no-extra-label": "error",
120 | "no-extra-parens": "off",
121 | "no-floating-decimal": "error",
122 | "no-implicit-globals": "off",
123 | "no-implied-eval": "error",
124 | "no-inline-comments": "off",
125 | "no-inner-declarations": [
126 | "error",
127 | "functions"
128 | ],
129 | "no-invalid-this": "error",
130 | "no-iterator": "error",
131 | "no-label-var": "error",
132 | "no-labels": "error",
133 | "no-lone-blocks": "error",
134 | "no-lonely-if": "off",
135 | "no-loop-func": "error",
136 | "no-magic-numbers": "off",
137 | "no-mixed-operators": [
138 | "error",
139 | {
140 | "allowSamePrecedence": true
141 | }
142 | ],
143 | "no-mixed-requires": "error",
144 | "no-multi-assign": "error",
145 | "no-multi-spaces": "off",
146 | "no-multi-str": "error",
147 | "no-multiple-empty-lines": "off",
148 | "no-native-reassign": "error",
149 | "no-negated-condition": "off",
150 | "no-negated-in-lhs": "error",
151 | "no-nested-ternary": "error",
152 | "no-new": "error",
153 | "no-new-func": "error",
154 | "no-new-object": "error",
155 | "no-new-require": "error",
156 | "no-new-wrappers": "error",
157 | "no-octal-escape": "error",
158 | "no-param-reassign": "off",
159 | "no-path-concat": "error",
160 | "no-plusplus": [
161 | "error",
162 | {
163 | "allowForLoopAfterthoughts": true
164 | }
165 | ],
166 | "no-process-env": "error",
167 | "no-process-exit": "error",
168 | "no-proto": "error",
169 | "no-prototype-builtins": "error",
170 | "no-restricted-globals": "error",
171 | "no-restricted-imports": "error",
172 | "no-restricted-modules": "error",
173 | "no-restricted-properties": "error",
174 | "no-restricted-syntax": "error",
175 | "no-return-assign": "error",
176 | "no-return-await": "error",
177 | "no-script-url": "error",
178 | "no-self-compare": "error",
179 | "no-sequences": "error",
180 | "no-shadow": "error",
181 | "no-shadow-restricted-names": "error",
182 | "no-spaced-func": "error",
183 | "no-sync": "error",
184 | "no-tabs": "error",
185 | "no-template-curly-in-string": "error",
186 | "no-ternary": "off",
187 | "no-throw-literal": "error",
188 | "no-trailing-spaces": "off",
189 | "no-undef-init": "error",
190 | "no-undefined": "off",
191 | "no-underscore-dangle": "error",
192 | "no-unmodified-loop-condition": "error",
193 | "no-unneeded-ternary": "error",
194 | "no-unused-expressions": "error",
195 | "no-use-before-define": "off",
196 | "no-useless-call": "error",
197 | "no-useless-computed-key": "error",
198 | "no-useless-concat": "off",
199 | "no-useless-constructor": "error",
200 | "no-useless-rename": "error",
201 | "no-useless-return": "error",
202 | "no-var": "off",
203 | "no-void": "error",
204 | "no-warning-comments": "off",
205 | "no-whitespace-before-property": "error",
206 | "no-with": "error",
207 | "object-curly-newline": "off",
208 | "object-curly-spacing": "off",
209 | "object-property-newline": "error",
210 | "object-shorthand": "off",
211 | "one-var": "off",
212 | "one-var-declaration-per-line": "error",
213 | "operator-assignment": [
214 | "error",
215 | "always"
216 | ],
217 | "operator-linebreak": "error",
218 | "padded-blocks": "off",
219 | "padding-line-between-statements": "error",
220 | "prefer-arrow-callback": "off",
221 | "prefer-const": "error",
222 | "prefer-destructuring": "off",
223 | "prefer-numeric-literals": "error",
224 | "prefer-promise-reject-errors": "error",
225 | "prefer-reflect": "off",
226 | "prefer-rest-params": "off",
227 | "prefer-spread": "error",
228 | "prefer-template": "off",
229 | "quote-props": "off",
230 | "quotes": "off",
231 | "radix": "error",
232 | "require-await": "error",
233 | "require-jsdoc": "off",
234 | "rest-spread-spacing": "error",
235 | "semi": "off",
236 | "semi-spacing": [
237 | "error",
238 | {
239 | "after": true,
240 | "before": false
241 | }
242 | ],
243 | "semi-style": [
244 | "error",
245 | "last"
246 | ],
247 | "sort-imports": "error",
248 | "sort-keys": "off",
249 | "sort-vars": "error",
250 | "space-before-blocks": "off",
251 | "space-before-function-paren": "off",
252 | "space-in-parens": "off",
253 | "space-infix-ops": "error",
254 | "space-unary-ops": "error",
255 | "spaced-comment": "off",
256 | "strict": [
257 | "error",
258 | "never"
259 | ],
260 | "switch-colon-spacing": "error",
261 | "symbol-description": "error",
262 | "template-curly-spacing": "error",
263 | "template-tag-spacing": "error",
264 | "unicode-bom": [
265 | "error",
266 | "never"
267 | ],
268 | "valid-jsdoc": "error",
269 | "vars-on-top": "off",
270 | "wrap-iife": "error",
271 | "wrap-regex": "error",
272 | "yield-star-spacing": "error"
273 | }
274 | };
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by http://www.gitignore.io
2 |
3 | ### Windows ###
4 | # Windows image file caches
5 | Thumbs.db
6 | ehthumbs.db
7 |
8 | # Folder config file
9 | Desktop.ini
10 |
11 | # Recycle Bin used on file shares
12 | $RECYCLE.BIN/
13 |
14 | # Windows Installer files
15 | *.cab
16 | *.msi
17 | *.msm
18 | *.msp
19 |
20 |
21 | ### Node ###
22 | # Logs
23 | logs
24 | *.log
25 |
26 | # Runtime data
27 | pids
28 | *.pid
29 | *.seed
30 |
31 | # Directory for instrumented libs generated by jscoverage/JSCover
32 | lib-cov
33 |
34 | # Coverage directory used by tools like istanbul
35 | coverage
36 |
37 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
38 | .grunt
39 |
40 | # Compiled binary addons (http://nodejs.org/api/addons.html)
41 | build/Release
42 |
43 | # Dependency directory
44 | # Deployed apps should consider commenting this line out:
45 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
46 | node_modules
47 | /.DS_Store
48 | /.idea
49 |
--------------------------------------------------------------------------------
/.htaccess:
--------------------------------------------------------------------------------
1 | #Action php54-cgi /php54.cgi
2 | #AddHandler php54-cgi .php
3 |
4 | # Updates here should coincide with rp.getRestOfUrl()
5 | RewriteEngine On
6 |
7 | RewriteBase /
8 | RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
9 | RewriteRule ^(.*)$ http://%1/$1 [R=301,L]
10 |
11 | RewriteRule ^r/(.*) /index.html
12 | RewriteRule ^u(ser)?/(.*) /index.html
13 | RewriteRule ^domain/(.*) /index.html
14 | RewriteRule ^search(.*) /index.html
15 |
16 | # to hide .git, allow redditp.com/.compact, and letsencrypt looks at `.well-known`
17 | RewriteRule ^\.([^w].*) /index.html
18 |
19 | # new multireddits with /me/
20 | RewriteRule ^me/(.*) /index.html
21 |
22 | ErrorDocument 404 /404.html
23 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible Node.js debug attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch via NPM",
11 | "runtimeExecutable": "npm",
12 | "windows": {
13 | "runtimeExecutable": "npm.cmd"
14 | },
15 | "runtimeArgs": [
16 | "run",
17 | "debug"
18 | ],
19 | "port": 9229
20 | }
21 | ]
22 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.enable": true
3 | }
--------------------------------------------------------------------------------
/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | RedditP 404
6 |
10 |
11 |
12 |
29 |
30 |
31 | Sorry, couldn't find that page (404)...
32 | Maybe try these presentations instead:
33 |
59 |
60 |
61 | You could also
62 | find the bug or email
63 | the author.
64 |
65 |
66 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/CNAME:
--------------------------------------------------------------------------------
1 | redditp.com
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # redditp
2 |
3 | A full screen reddit presentation or slide show.
4 |
5 | http://redditp.com
6 |
7 | ## Hotkeys
8 |
9 | - a - toggles auto-next (play/pause)
10 | - t - collapse/uncollapse title
11 | - c - collapse/uncollapse controls
12 | - i - open image in a new tab
13 | - r - open comments in a new tab
14 | - u - open user slideshow in new tab
15 | - f - toggle full screen mode
16 | - m - toggle sound
17 | - g - skip gallery
18 | - Arrow keys, pgup/pgdown, spacebar change slides
19 | - Swipe gestures on phones
20 |
21 | ## Features
22 |
23 | - All /r/ subreddits, including different ?sort stuff.
24 | - /user/ , /domain/ , /me/ url's work.
25 | - Url's ending with ['.jpg', '.jpeg', '.gif', '.bmp', '.png']
26 | - You can save the html file locally and use it, just make sure you add a
27 | separator e.g. the question mark in file:///c/myredditp.html?/r/gifs so the
28 | browser knows to pick up the right file and go to the right subreddit.
29 | - Support for /r/random and /r/randnsfw virtual subreddits. These'll be tricky
30 | unless I cheat as they contain redirects.
31 |
32 | Possible future features, depending on feedback:
33 |
34 | - Zoom/Pan for comics
35 | - Imgur albums support
36 | - Offline access support, though I don't know if this is even possible actually
37 | (caching external image resources).
38 | - Login and upvoting support
39 |
40 | ## Host your own redditp
41 |
42 | Redditp relies on the `/r/subreddit` in the URL to fetch the JSON from the
43 | corresponding reddit endpoint. There are a few ways you can set up support for
44 | these URLs yourself:
45 |
46 | - You can use an Apache server with the `.htaccess` file.
47 | - Netlify removed redditp without warning. So now we're moving the hosting to
48 | Vercel, Cloudflare, or GitHub pages. Not sure. See vc.redditp.com for Vercel
49 | - Use NodeJS (see `package.json`).
50 | - Use a simple HTTP server and put the subreddit URL in the get parameters like
51 | `http://localhost?/r/subreddit`.
52 | - Use GitHub pages by copying `index.html` into `404.html` which will make all
53 | unknown URLs reach the same `index.html`. This currently only works with a
54 | custom domain because of where the `.js` and `.css` files are located.
55 |
56 | ## Credits
57 |
58 | - Ubershmekel http://yuvalg.com/
59 | - [js-cookie](https://github.com/js-cookie/js-cookie) for managing cookies
60 | - Favicon by Double-J designs
61 | http://www.iconfinder.com/icondetails/68600/64/_icon
62 | - Slideshow based on http://demo.marcofolio.net/fullscreen_image_slider/
63 | - Author of slideshow base: Marco Kuiper (http://www.marcofolio.net/)
64 | - And many more that have contributed to this project through feedback and pull
65 | requests https://github.com/ubershmekel/redditp/graphs/contributors
66 |
--------------------------------------------------------------------------------
/_redirects:
--------------------------------------------------------------------------------
1 | # DOES THIS FILE EVEN WORK AT ALL?
2 | # vercel.json seems to be the only one that counts.
3 | # source dest httpcode
4 | /r/* / 200
5 | /u/* / 200
6 | /user/* / 200
7 | /domain/* / 200
8 | /search/* / 200
9 | /search / 200
10 |
11 |
--------------------------------------------------------------------------------
/css/style.css:
--------------------------------------------------------------------------------
1 | /* BASIC RESET */
2 | ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,body,html,p,blockquote,fieldset,input{margin:0; padding:0;}
3 |
4 | /* HTML ELEMENTS */
5 | body { overflow:hidden; background-color: #000; color:#fff;}
6 |
7 | /* PICTURES */
8 | #pictureSlider {
9 | position: absolute;
10 | left: 0;
11 | top: 0;
12 | width: 100%;
13 | height: 100%;
14 | z-index:-999;
15 | }
16 |
17 | #pictureSlider div { height:100%; width:100%; position:absolute; z-index:-999; }
18 |
19 | /* NAVIGATION BOX */
20 | #navigationBoxes {float: left;}
21 | a { text-decoration:none; color:#eee; font-weight: bold; }
22 | a:hover {
23 | opacity: 1.0;
24 | border-bottom:1px dotted;
25 | }
26 | .navbox { width:450px; max-height: 40%; overflow: auto; position:relative; left: 0px; opacity: 0.9; background-color: rgba(0,0,0,0.7);z-index:2;}
27 | /* 108px so the next row is half-visible */
28 | .numberButtonList { overflow: auto; max-height: 108px; }
29 | .navbox ul { list-style-type:none; display:block; margin:0px; right:10px; top:10px; text-align: left;}
30 | .navbox ul li { display: inline-block; list-style-type: none; }
31 | .numberButton {
32 | float: left;
33 |
34 | min-width: 15px;
35 | margin-left: 5px;
36 | text-decoration: none;
37 | color: #eee;
38 |
39 | font: bold 12px Helvetica, Arial, Sans-serif;
40 | text-align: center;
41 | line-height: 18px;
42 | padding: 3px 5px;
43 | }
44 | .over18 {
45 | color: #f99;
46 | }
47 | .galleryCount {
48 | color: rgb(158, 217, 8);
49 | }
50 | .navbox ul li a {
51 | cursor:pointer;
52 |
53 | }
54 | .numberButtonList ul li a:hover { background:#888; border: 0; }
55 | .numberButtonList ul li a.active {
56 | color: #00AAAA;
57 | -moz-box-shadow: 2px 2px 3px #eee;
58 | /* For IE 8 */
59 | -ms-filter: "progid:DXImageTransform.Microsoft.Shadow(Strength=3, Direction=135, Color='#EEEEEE')";
60 | /* For IE 5.5 - 7 */
61 | filter: progid:DXImageTransform.Microsoft.Shadow(Strength=3, Direction=135, Color='#EEEEEE');
62 | }
63 |
64 | #controlsDiv {position: absolute; bottom: 64px; left: 0;right: 0;}
65 | #titleDiv {position: absolute; top: 0;left: 0;right: 0;}
66 | .navbox h2 { padding: 10px 20px 10px 10px; font: bold 20px Helvetica, Arial, Sans-serif; }
67 | .navbox h3 { padding: 0px 20px 10px 10px; font: bold 12px Helvetica, Arial, Sans-serif; }
68 | .navbox p { padding:20px 20px 20px 20px; font-family: "Segoe UI","HelveticaNeue-Light","Helvetica Neue Light","Helvetica Neue",Arial,Tahoma,Verdana,sans-serif; font-size:13px; color:#111; }
69 | .navbox p.bottom { position:absolute; bottom:5px; right:5px; }
70 |
71 | .nbmenu {
72 | margin: 0 0 0 5px;
73 | width: 100%;
74 | font-family: "Segoe UI","HelveticaNeue-Light","Helvetica Neue Light","Helvetica Neue",Arial,Tahoma,Verdana,sans-serif;
75 | font-size:14px;
76 |
77 | }
78 |
79 | .nbmenu li {
80 | text-align: left;
81 | padding: 6px;
82 | max-width: 300px;
83 | overflow: hidden;
84 | }
85 |
86 | #navboxContents {
87 | padding: 10px 25px 10px 0;
88 | margin:0;
89 | }
90 |
91 | label.checkbox { font-weight: bold; }
92 |
93 | .cam { background-image:url("../images/bg_cam_txt.png"); }
94 | .clouds { }
95 | .key { background-image:url("../images/bg_key_txt.png"); }
96 | .flowers { background-image:url("../images/bg_flowers_txt.png"); }
97 |
98 | .collapser { border: solid 1px #555; font-size: 20px; color: #bbb; padding: 0 10px; float:right; cursor: hand; cursor: pointer;}
99 | .checkbox { cursor: hand; cursor: pointer; }
100 |
101 | .prevArrow {
102 | position: absolute;
103 | top: 50%;
104 | left: 10px;
105 | width: 0;
106 | height: 0;
107 |
108 | border-top: 25px solid transparent;
109 | border-bottom: 25px solid transparent;
110 | border-right: 50px solid white;
111 | z-index:2;
112 | opacity: 0.8;
113 |
114 | cursor: hand; cursor: pointer;
115 | }
116 |
117 | .nextArrow {
118 | position: absolute;
119 | top: 50%;
120 | right: 10px;
121 | width: 0;
122 | height: 0;
123 |
124 | border-top: 25px solid transparent;
125 | border-bottom: 25px solid transparent;
126 | border-left: 50px solid white;
127 | z-index:2;
128 | opacity: 0.8;
129 | cursor: hand; cursor: pointer;
130 | }
131 |
132 | #fullScreenButton {
133 | width: 20px;
134 | padding-left: 10px;
135 | cursor:pointer;
136 | }
137 |
138 | #playButton {
139 | position: absolute;
140 | left: 0;
141 | top: 0;
142 | width: 100%;
143 | height: 100%;
144 | cursor: pointer;
145 | z-index: 2
146 | }
147 |
--------------------------------------------------------------------------------
/dmca.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | RedditP DMCA
6 |
10 |
11 |
12 |
29 |
30 |
31 | Sorry, couldn't find that page probably because of a DMCA takedown.
32 |
33 | Here's a link to the reddit page if that
34 | works.
35 |
36 | Try these presentations instead:
37 |
63 |
64 |
65 | You could also
66 | find the bug or email
67 | the author.
68 |
69 |
70 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 | services:
3 | redditp:
4 | image: httpd:2.2.32
5 | labels:
6 | - "traefik.enable=true"
7 | - "traefik.docker.network=reverseproxy_default"
8 | - "traefik.backend=redditp"
9 | - "traefik.frontend.rule=Host:redditp.com"
10 | networks:
11 | - "reverseproxy_default"
12 | restart: always
13 | volumes:
14 | - .:/usr/local/apache2/htdocs/:ro
15 | - ./httpd.conf:/usr/local/apache2/conf/httpd.conf:ro
16 | logging:
17 | options:
18 | max-size: "50m"
19 |
20 | networks:
21 | reverseproxy_default:
22 | external:
23 | name: reverseproxy_default
24 |
--------------------------------------------------------------------------------
/httpd.conf:
--------------------------------------------------------------------------------
1 | #
2 | # This is the main Apache HTTP server configuration file. It contains the
3 | # configuration directives that give the server its instructions.
4 | # See for detailed information.
5 | # In particular, see
6 | #
7 | # for a discussion of each configuration directive.
8 | #
9 | # Do NOT simply read the instructions in here without understanding
10 | # what they do. They're here only as hints or reminders. If you are unsure
11 | # consult the online docs. You have been warned.
12 | #
13 | # Configuration and logfile names: If the filenames you specify for many
14 | # of the server's control files begin with "/" (or "drive:/" for Win32), the
15 | # server will use that explicit path. If the filenames do *not* begin
16 | # with "/", the value of ServerRoot is prepended -- so 'log/access_log'
17 | # with ServerRoot set to '/www' will be interpreted by the
18 | # server as '/www/log/access_log', where as '/log/access_log' will be
19 | # interpreted as '/log/access_log'.
20 |
21 | #
22 | # ServerRoot: The top of the directory tree under which the server's
23 | # configuration, error, and log files are kept.
24 | #
25 | # Do not add a slash at the end of the directory path. If you point
26 | # ServerRoot at a non-local disk, be sure to point the LockFile directive
27 | # at a local disk. If you wish to share the same ServerRoot for multiple
28 | # httpd daemons, you will need to change at least LockFile and PidFile.
29 | #
30 | ServerRoot "/usr/local/apache2"
31 |
32 | #
33 | # Listen: Allows you to bind Apache to specific IP addresses and/or
34 | # ports, instead of the default. See also the
35 | # directive.
36 | #
37 | # Change this to Listen on specific IP addresses as shown below to
38 | # prevent Apache from glomming onto all bound IP addresses.
39 | #
40 | #Listen 12.34.56.78:80
41 | Listen 80
42 |
43 | #
44 | # Dynamic Shared Object (DSO) Support
45 | #
46 | # To be able to use the functionality of a module which was built as a DSO you
47 | # have to place corresponding `LoadModule' lines at this location so the
48 | # directives contained in it are actually available _before_ they are used.
49 | # Statically compiled modules (those listed by `httpd -l') do not need
50 | # to be loaded here.
51 | #
52 | # Example:
53 | # LoadModule foo_module modules/mod_foo.so
54 | #
55 | LoadModule authn_file_module modules/mod_authn_file.so
56 | LoadModule authn_dbm_module modules/mod_authn_dbm.so
57 | LoadModule authn_anon_module modules/mod_authn_anon.so
58 | LoadModule authn_dbd_module modules/mod_authn_dbd.so
59 | LoadModule authn_default_module modules/mod_authn_default.so
60 | LoadModule authn_alias_module modules/mod_authn_alias.so
61 | LoadModule authz_host_module modules/mod_authz_host.so
62 | LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
63 | LoadModule authz_user_module modules/mod_authz_user.so
64 | LoadModule authz_dbm_module modules/mod_authz_dbm.so
65 | LoadModule authz_owner_module modules/mod_authz_owner.so
66 | LoadModule authnz_ldap_module modules/mod_authnz_ldap.so
67 | LoadModule authz_default_module modules/mod_authz_default.so
68 | LoadModule auth_basic_module modules/mod_auth_basic.so
69 | LoadModule auth_digest_module modules/mod_auth_digest.so
70 | LoadModule file_cache_module modules/mod_file_cache.so
71 | LoadModule cache_module modules/mod_cache.so
72 | LoadModule disk_cache_module modules/mod_disk_cache.so
73 | LoadModule mem_cache_module modules/mod_mem_cache.so
74 | LoadModule dbd_module modules/mod_dbd.so
75 | LoadModule dumpio_module modules/mod_dumpio.so
76 | LoadModule reqtimeout_module modules/mod_reqtimeout.so
77 | LoadModule ext_filter_module modules/mod_ext_filter.so
78 | LoadModule include_module modules/mod_include.so
79 | LoadModule filter_module modules/mod_filter.so
80 | LoadModule substitute_module modules/mod_substitute.so
81 | LoadModule charset_lite_module modules/mod_charset_lite.so
82 | LoadModule deflate_module modules/mod_deflate.so
83 | LoadModule ldap_module modules/mod_ldap.so
84 | LoadModule log_config_module modules/mod_log_config.so
85 | LoadModule log_forensic_module modules/mod_log_forensic.so
86 | LoadModule logio_module modules/mod_logio.so
87 | LoadModule env_module modules/mod_env.so
88 | LoadModule mime_magic_module modules/mod_mime_magic.so
89 | LoadModule cern_meta_module modules/mod_cern_meta.so
90 | LoadModule expires_module modules/mod_expires.so
91 | LoadModule headers_module modules/mod_headers.so
92 | LoadModule ident_module modules/mod_ident.so
93 | LoadModule usertrack_module modules/mod_usertrack.so
94 | LoadModule unique_id_module modules/mod_unique_id.so
95 | LoadModule setenvif_module modules/mod_setenvif.so
96 | LoadModule version_module modules/mod_version.so
97 | LoadModule proxy_module modules/mod_proxy.so
98 | LoadModule proxy_connect_module modules/mod_proxy_connect.so
99 | LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
100 | LoadModule proxy_http_module modules/mod_proxy_http.so
101 | LoadModule proxy_scgi_module modules/mod_proxy_scgi.so
102 | LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
103 | LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
104 | LoadModule ssl_module modules/mod_ssl.so
105 | LoadModule mime_module modules/mod_mime.so
106 | LoadModule dav_module modules/mod_dav.so
107 | LoadModule status_module modules/mod_status.so
108 | LoadModule autoindex_module modules/mod_autoindex.so
109 | LoadModule asis_module modules/mod_asis.so
110 | LoadModule info_module modules/mod_info.so
111 | LoadModule cgi_module modules/mod_cgi.so
112 | LoadModule dav_fs_module modules/mod_dav_fs.so
113 | LoadModule dav_lock_module modules/mod_dav_lock.so
114 | LoadModule vhost_alias_module modules/mod_vhost_alias.so
115 | LoadModule negotiation_module modules/mod_negotiation.so
116 | LoadModule dir_module modules/mod_dir.so
117 | LoadModule imagemap_module modules/mod_imagemap.so
118 | LoadModule actions_module modules/mod_actions.so
119 | LoadModule speling_module modules/mod_speling.so
120 | LoadModule userdir_module modules/mod_userdir.so
121 | LoadModule alias_module modules/mod_alias.so
122 | LoadModule rewrite_module modules/mod_rewrite.so
123 |
124 |
125 |
126 | #
127 | # If you wish httpd to run as a different user or group, you must run
128 | # httpd as root initially and it will switch.
129 | #
130 | # User/Group: The name (or #number) of the user/group to run httpd as.
131 | # It is usually good practice to create a dedicated user and group for
132 | # running httpd, as with most system services.
133 | #
134 | User daemon
135 | Group daemon
136 |
137 |
138 |
139 |
140 | # 'Main' server configuration
141 | #
142 | # The directives in this section set up the values used by the 'main'
143 | # server, which responds to any requests that aren't handled by a
144 | # definition. These values also provide defaults for
145 | # any containers you may define later in the file.
146 | #
147 | # All of these directives may appear inside containers,
148 | # in which case these default settings will be overridden for the
149 | # virtual host being defined.
150 | #
151 |
152 | #
153 | # ServerAdmin: Your address, where problems with the server should be
154 | # e-mailed. This address appears on some server-generated pages, such
155 | # as error documents. e.g. admin@your-domain.com
156 | #
157 | ServerAdmin you@example.com
158 |
159 | #
160 | # ServerName gives the name and port that the server uses to identify itself.
161 | # This can often be determined automatically, but we recommend you specify
162 | # it explicitly to prevent problems during startup.
163 | #
164 | # If your host doesn't have a registered DNS name, enter its IP address here.
165 | #
166 | #ServerName www.example.com:80
167 |
168 | #
169 | # DocumentRoot: The directory out of which you will serve your
170 | # documents. By default, all requests are taken from this directory, but
171 | # symbolic links and aliases may be used to point to other locations.
172 | #
173 | DocumentRoot "/usr/local/apache2/htdocs"
174 |
175 | #
176 | # Each directory to which Apache has access can be configured with respect
177 | # to which services and features are allowed and/or disabled in that
178 | # directory (and its subdirectories).
179 | #
180 | # First, we configure the "default" to be a very restrictive set of
181 | # features.
182 | #
183 |
184 | Options FollowSymLinks
185 | AllowOverride None
186 | Order deny,allow
187 | Deny from all
188 |
189 |
190 | #
191 | # Note that from this point forward you must specifically allow
192 | # particular features to be enabled - so if something's not working as
193 | # you might expect, make sure that you have specifically enabled it
194 | # below.
195 | #
196 |
197 | #
198 | # This should be changed to whatever you set DocumentRoot to.
199 | #
200 |
201 | #
202 | # Possible values for the Options directive are "None", "All",
203 | # or any combination of:
204 | # Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
205 | #
206 | # Note that "MultiViews" must be named *explicitly* --- "Options All"
207 | # doesn't give it to you.
208 | #
209 | # The Options directive is both complicated and important. Please see
210 | # http://httpd.apache.org/docs/2.2/mod/core.html#options
211 | # for more information.
212 | #
213 | Options Indexes FollowSymLinks
214 |
215 | #
216 | # AllowOverride controls what directives may be placed in .htaccess files.
217 | # It can be "All", "None", or any combination of the keywords:
218 | # Options FileInfo AuthConfig Limit
219 | #
220 | AllowOverride All
221 |
222 | #
223 | # Controls who can get stuff from this server.
224 | #
225 | Order allow,deny
226 | Allow from all
227 |
228 |
229 |
230 | #
231 | # DirectoryIndex: sets the file that Apache will serve if a directory
232 | # is requested.
233 | #
234 |
235 | DirectoryIndex index.html
236 |
237 |
238 | #
239 | # The following lines prevent .htaccess and .htpasswd files from being
240 | # viewed by Web clients.
241 | #
242 |
243 | Order allow,deny
244 | Deny from all
245 | Satisfy All
246 |
247 |
248 | #
249 | # ErrorLog: The location of the error log file.
250 | # If you do not specify an ErrorLog directive within a
251 | # container, error messages relating to that virtual host will be
252 | # logged here. If you *do* define an error logfile for a
253 | # container, that host's errors will be logged there and not here.
254 | #
255 | ErrorLog /proc/self/fd/2
256 |
257 | #
258 | # LogLevel: Control the number of messages logged to the error_log.
259 | # Possible values include: debug, info, notice, warn, error, crit,
260 | # alert, emerg.
261 | #
262 | LogLevel warn
263 |
264 |
265 | #
266 | # The following directives define some format nicknames for use with
267 | # a CustomLog directive (see below).
268 | #
269 | LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
270 | LogFormat "%h %l %u %t \"%r\" %>s %b" common
271 |
272 |
273 | # You need to enable mod_logio.c to use %I and %O
274 | LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
275 |
276 |
277 | #
278 | # The location and format of the access logfile (Common Logfile Format).
279 | # If you do not define any access logfiles within a
280 | # container, they will be logged here. Contrariwise, if you *do*
281 | # define per- access logfiles, transactions will be
282 | # logged therein and *not* in this file.
283 | #
284 | CustomLog /proc/self/fd/1 common
285 |
286 | #
287 | # If you prefer a logfile with access, agent, and referer information
288 | # (Combined Logfile Format) you can use the following directive.
289 | #
290 | #CustomLog "logs/access_log" combined
291 |
292 |
293 |
294 | #
295 | # Redirect: Allows you to tell clients about documents that used to
296 | # exist in your server's namespace, but do not anymore. The client
297 | # will make a new request for the document at its new location.
298 | # Example:
299 | # Redirect permanent /foo http://www.example.com/bar
300 |
301 | #
302 | # Alias: Maps web paths into filesystem paths and is used to
303 | # access content that does not live under the DocumentRoot.
304 | # Example:
305 | # Alias /webpath /full/filesystem/path
306 | #
307 | # If you include a trailing / on /webpath then the server will
308 | # require it to be present in the URL. You will also likely
309 | # need to provide a section to allow access to
310 | # the filesystem path.
311 |
312 | #
313 | # ScriptAlias: This controls which directories contain server scripts.
314 | # ScriptAliases are essentially the same as Aliases, except that
315 | # documents in the target directory are treated as applications and
316 | # run by the server when requested rather than as documents sent to the
317 | # client. The same rules about trailing "/" apply to ScriptAlias
318 | # directives as to Alias.
319 | #
320 | ScriptAlias /cgi-bin/ "/usr/local/apache2/cgi-bin/"
321 |
322 |
323 |
324 |
325 | #
326 | # ScriptSock: On threaded servers, designate the path to the UNIX
327 | # socket used to communicate with the CGI daemon of mod_cgid.
328 | #
329 | #Scriptsock logs/cgisock
330 |
331 |
332 | #
333 | # "/usr/local/apache2/cgi-bin" should be changed to whatever your ScriptAliased
334 | # CGI directory exists, if you have that configured.
335 | #
336 |
337 | AllowOverride None
338 | Options None
339 | Order allow,deny
340 | Allow from all
341 |
342 |
343 | #
344 | # DefaultType: the default MIME type the server will use for a document
345 | # if it cannot otherwise determine one, such as from filename extensions.
346 | # If your server contains mostly text or HTML documents, "text/plain" is
347 | # a good value. If most of your content is binary, such as applications
348 | # or images, you may want to use "application/octet-stream" instead to
349 | # keep browsers from trying to display binary files as though they are
350 | # text.
351 | #
352 | DefaultType text/plain
353 |
354 |
355 | #
356 | # Avoid passing HTTP_PROXY environment to CGI's on this or any proxied
357 | # backend servers which have lingering "httpoxy" defects.
358 | # 'Proxy' request header is undefined by the IETF, not listed by IANA
359 | #
360 | RequestHeader unset Proxy early
361 |
362 |
363 |
364 | #
365 | # TypesConfig points to the file containing the list of mappings from
366 | # filename extension to MIME-type.
367 | #
368 | TypesConfig conf/mime.types
369 |
370 | #
371 | # AddType allows you to add to or override the MIME configuration
372 | # file specified in TypesConfig for specific file types.
373 | #
374 | #AddType application/x-gzip .tgz
375 | #
376 | # AddEncoding allows you to have certain browsers uncompress
377 | # information on the fly. Note: Not all browsers support this.
378 | #
379 | #AddEncoding x-compress .Z
380 | #AddEncoding x-gzip .gz .tgz
381 | #
382 | # If the AddEncoding directives above are commented-out, then you
383 | # probably should define those extensions to indicate media types:
384 | #
385 | AddType application/x-compress .Z
386 | AddType application/x-gzip .gz .tgz
387 |
388 | #
389 | # AddHandler allows you to map certain file extensions to "handlers":
390 | # actions unrelated to filetype. These can be either built into the server
391 | # or added with the Action directive (see below)
392 | #
393 | # To use CGI scripts outside of ScriptAliased directories:
394 | # (You will also need to add "ExecCGI" to the "Options" directive.)
395 | #
396 | #AddHandler cgi-script .cgi
397 |
398 | # For type maps (negotiated resources):
399 | #AddHandler type-map var
400 |
401 | #
402 | # Filters allow you to process content before it is sent to the client.
403 | #
404 | # To parse .shtml files for server-side includes (SSI):
405 | # (You will also need to add "Includes" to the "Options" directive.)
406 | #
407 | #AddType text/html .shtml
408 | #AddOutputFilter INCLUDES .shtml
409 |
410 |
411 | #
412 | # The mod_mime_magic module allows the server to use various hints from the
413 | # contents of the file itself to determine its type. The MIMEMagicFile
414 | # directive tells the module where the hint definitions are located.
415 | #
416 | #MIMEMagicFile conf/magic
417 |
418 | #
419 | # Customizable error responses come in three flavors:
420 | # 1) plain text 2) local redirects 3) external redirects
421 | #
422 | # Some examples:
423 | #ErrorDocument 500 "The server made a boo boo."
424 | #ErrorDocument 404 /missing.html
425 | #ErrorDocument 404 "/cgi-bin/missing_handler.pl"
426 | #ErrorDocument 402 http://www.example.com/subscription_info.html
427 | #
428 |
429 | #
430 | # MaxRanges: Maximum number of Ranges in a request before
431 | # returning the entire resource, or one of the special
432 | # values 'default', 'none' or 'unlimited'.
433 | # Default setting is to accept 200 Ranges.
434 | #MaxRanges unlimited
435 |
436 | #
437 | # EnableMMAP and EnableSendfile: On systems that support it,
438 | # memory-mapping or the sendfile syscall is used to deliver
439 | # files. This usually improves server performance, but must
440 | # be turned off when serving from networked-mounted
441 | # filesystems or if support for these functions is otherwise
442 | # broken on your system.
443 | #
444 | #EnableMMAP off
445 | #EnableSendfile off
446 |
447 | # Supplemental configuration
448 | #
449 | # The configuration files in the conf/extra/ directory can be
450 | # included to add extra features or to modify the default configuration of
451 | # the server, or you may simply copy their contents here and change as
452 | # necessary.
453 |
454 | # Server-pool management (MPM specific)
455 | #Include conf/extra/httpd-mpm.conf
456 |
457 | # Multi-language error messages
458 | #Include conf/extra/httpd-multilang-errordoc.conf
459 |
460 | # Fancy directory listings
461 | #Include conf/extra/httpd-autoindex.conf
462 |
463 | # Language settings
464 | #Include conf/extra/httpd-languages.conf
465 |
466 | # User home directories
467 | #Include conf/extra/httpd-userdir.conf
468 |
469 | # Real-time info on requests and configuration
470 | #Include conf/extra/httpd-info.conf
471 |
472 | # Virtual hosts
473 | #Include conf/extra/httpd-vhosts.conf
474 |
475 | # Local access to the Apache HTTP Server Manual
476 | #Include conf/extra/httpd-manual.conf
477 |
478 | # Distributed authoring and versioning (WebDAV)
479 | #Include conf/extra/httpd-dav.conf
480 |
481 | # Various default settings
482 | #Include conf/extra/httpd-default.conf
483 |
484 | # Secure (SSL/TLS) connections
485 | #Include conf/extra/httpd-ssl.conf
486 | #
487 | # Note: The following must must be present to support
488 | # starting without SSL on platforms with no /dev/random equivalent
489 | # but a statically compiled-in mod_ssl.
490 | #
491 |
492 | SSLRandomSeed startup builtin
493 | SSLRandomSeed connect builtin
494 |
495 |
--------------------------------------------------------------------------------
/images/1437338478_screen-full.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubershmekel/redditp/43f0c8b9e4e5f8e59ce5690fc8d70b012af22937/images/favicon.png
--------------------------------------------------------------------------------
/images/fullscreen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubershmekel/redditp/43f0c8b9e4e5f8e59ce5690fc8d70b012af22937/images/fullscreen.png
--------------------------------------------------------------------------------
/images/play.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
21 |
23 |
27 |
31 |
32 |
43 |
54 |
55 |
77 |
79 |
80 |
82 | image/svg+xml
83 |
85 |
86 |
87 |
88 |
89 |
94 |
97 |
102 | Mobile browsers won't autoplay without a tap
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 | redditP
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
31 |
32 |
33 |
36 |
69 |
70 |
73 |
76 |
92 |
113 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
197 |
198 |
-
199 |
200 | Javascript is required for redditp to work :(
201 |
202 |
203 | Javascript is required for redditp to work :(
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
Maybe try these presentations instead:
212 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
--------------------------------------------------------------------------------
/js/EmbedIt.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line no-global-assign,no-native-reassign
2 | embedit = {};
3 |
4 | embedit.imageTypes = {
5 | image: "image",
6 | gfycat: "gfycat",
7 | gifv: "gifv",
8 | redgif: "redgif",
9 | };
10 |
11 | embedit.redditBaseUrl = "http://old.reddit.com";
12 |
13 | if (typeof window === "undefined") {
14 | // eslint-disable-next-line no-redeclare
15 | var window = {};
16 | }
17 |
18 | if (window.location && window.location.protocol === "https:") {
19 | // page is secure
20 | embedit.redditBaseUrl = "https://old.reddit.com";
21 | }
22 |
23 | embedit.video = function (webmUrl, mp4Url) {
24 | // imgur is annoying and when you use the tags
25 | // it tries to redirect you to the gifv page instead of serving
26 | // video. We can only circumvent that by putting the src
27 | // on the tag :/
28 |
29 | var video = $(' ');
30 | if (webmUrl) {
31 | video.append($(" ").attr("src", webmUrl));
32 | }
33 | if (mp4Url) {
34 | video.append($(" ").attr("src", mp4Url));
35 | }
36 | return video;
37 | //video.attr("src", urls[0]);
38 | /*
39 | var url;
40 | if(!webmUrl && !mp4Url) {
41 | console.error("Empty video urls given");
42 | return;
43 | }
44 |
45 | if(mp4Url)
46 | url = mp4Url
47 | else
48 | url = webmUrl
49 |
50 | if (Modernizr.video.webm && webmUrl) {
51 | // Thank you http://diveintohtml5.info/detect.html
52 | // try WebM
53 | url = webmUrl;
54 | }
55 |
56 | url = url.replace("http://", "https://");
57 | video.attr("src", url);
58 | return video;*/
59 | };
60 |
61 | embedit.unsupported = function (url) {
62 | console.log("Omitting unsupported url: '" + url + "'");
63 | };
64 |
65 | embedit.redGifConvert = function (url, embedFunc) {
66 | var name = embedit.redGifUrlToId(url);
67 |
68 | if (!name) {
69 | console.log("Failed to identify redgif name");
70 | return false;
71 | }
72 |
73 | // https://github.com/ubershmekel/redditp/issues/138
74 | // Redgifs isn't allowing CORS requests to others.
75 | // access-control-allow-origin: https://www.redgifs.com
76 | const iframeUrl = "https://www.redgifs.com/ifr/" + name;
77 | embedFunc(
78 | $(
79 | ''
82 | )
83 | );
84 | return true;
85 | };
86 |
87 | embedit.convertors = [
88 | {
89 | name: "imgurAlbums",
90 | detect: /imgur\.com\/a\/.*/,
91 | convert: function (url, embedFunc) {
92 | embedit.unsupported(url);
93 | embedFunc(null);
94 | return true;
95 | },
96 | },
97 | {
98 | name: "imgurGifv",
99 | detect: /imgur\.com.*(gif|gifv|mp4|webm)/,
100 | convert: function (url, embedFunc) {
101 | var no_extension = url.replace(/\.\w+$/, "");
102 | var webmUrl = no_extension + ".webm";
103 | var mp4Url = no_extension + ".mp4";
104 | embedFunc(embedit.video(webmUrl, mp4Url));
105 | return true;
106 | },
107 | },
108 | {
109 | name: "imgurNoExtension",
110 | detect: /imgur\.com[^.]+/,
111 | convert: function (url, embedFunc) {
112 | var newUrl = url + ".jpg";
113 | var image = $(" ");
114 | image.attr("src", newUrl);
115 | embedFunc(image);
116 | return true;
117 | },
118 | },
119 | {
120 | name: "redditPost",
121 | detect: /reddit\.com\/r\/.*/,
122 | convert: function (/*url*/) {
123 | //embedit.unsupported(url);
124 | return false;
125 | },
126 | },
127 | {
128 | name: "gfycat",
129 | detect: /gfycat\.com.*/,
130 | convert: function (url, embedFunc) {
131 | var name = embedit.gfyUrlToId(url);
132 | if (!name) return false;
133 |
134 | $.ajax({
135 | //url: 'https://gfycat.com/cajax/get/' + name,
136 | url: "https://api.gfycat.com/v1/gfycats/" + name,
137 | dataType: "json",
138 | success: function (data) {
139 | if (!data || !data.gfyItem || !data.gfyItem.webmUrl) {
140 | embedFunc(null);
141 | return;
142 | }
143 | embedFunc(embedit.video(data.gfyItem.webmUrl, data.gfyItem.mp4Url));
144 | },
145 | error: function () {
146 | var newUrl = url.replace("gfycat.com/", "redgifs.com/watch/");
147 | console.log("gfycat failed load, trying redgif", newUrl);
148 | embedit.redGifConvert(newUrl, embedFunc);
149 | },
150 | });
151 | return true;
152 | },
153 | },
154 | {
155 | name: "redgifs",
156 | detect: /redgifs\.com.*/,
157 | convert: embedit.redGifConvert,
158 | },
159 | {
160 | name: "v.reddit",
161 | detect: /v\.redd\.it.*/,
162 | convert: function (url, embedFunc) {
163 | //var url = url + '/HLSPlaylist.m3u8';
164 | //var url = url + '/DASH_4_8_M?source=fallback';
165 | // NOTE - this step relies on the fallback_rul we get from reddit in script.js.
166 | // For embedit to support v.redd.it URLS on its own there will need to be something more clever here.
167 | embedFunc(embedit.video(url));
168 | return true;
169 |
170 | //} else if (pic.url.indexOf('//v.redd.it/') >= 0) {
171 | //pic.type = imageTypes.gifv;
172 | //pic.url = pic.url + '/HLSPlaylist.m3u8';
173 | },
174 | },
175 | {
176 | name: "imageExtension",
177 | detect: /\.(png|jpg|jpeg|gif|bmp)$/,
178 | convert: function (url, embedFunc) {
179 | var newElem = $(" ", {
180 | id: "",
181 | src: url,
182 | alt: "",
183 | });
184 | embedFunc(newElem);
185 | return true;
186 | },
187 | },
188 | {
189 | name: "preview.redd.it",
190 | detect: /preview\.redd\.it.*/,
191 | convert: function (url, embedFunc) {
192 | embedFunc(embedit.video(url));
193 | return true;
194 | },
195 | },
196 | ];
197 |
198 | embedit.embed = function (url, embedFunc) {
199 | //var embedFunc = function (elem) {
200 | // elem.appendTo($("#container"));
201 | //}
202 | var keys = Object.keys(embedit.convertors);
203 | for (var i = 0; i < keys.length; i++) {
204 | var key = keys[i];
205 | var convertor = embedit.convertors[key];
206 | if (url.match(convertor.detect)) {
207 | //console.log("Matched: " + url + "\n to - " + convertor.name);
208 | var handled = convertor.convert(url, embedFunc);
209 | //console.log(newElem);
210 | if (handled) return true;
211 | }
212 | }
213 | embedit.unsupported(url);
214 | embedFunc(null);
215 | };
216 |
217 | embedit.gfyUrlToId = function (url) {
218 | //https://gfycat.com/cajax/get/ScaryGrizzledComet
219 | var match = url.match(/gfycat.com\/(gifs\/detail\/)?(\w+)/i);
220 | if (match && match.length > 2) {
221 | return match[2];
222 | } else {
223 | return false;
224 | }
225 | };
226 |
227 | embedit.redGifUrlToId = function (url) {
228 | //https://redgifs.com/ifr/unhappyfluidgrassspider'
229 | var matches = url.match(/redgifs.com\/watch\/([\w-]+)\/?/i);
230 | if (matches && matches.length > 1) {
231 | return matches[1];
232 | }
233 |
234 | matches = url.match(/redgifs.com\/ifr\/([\w-]+)\/?/i);
235 | if (matches && matches.length > 1) {
236 | return matches[1];
237 | }
238 |
239 | return false;
240 | };
241 |
242 | function isImageExtension(url) {
243 | var goodExtensions = [".jpg", ".jpeg", ".gif", ".bmp", ".png"];
244 | var dotLocation = url.lastIndexOf(".");
245 | if (dotLocation < 0) {
246 | console.log("skipped no dot: " + url);
247 | return false;
248 | }
249 | var extension = url.substring(dotLocation);
250 |
251 | return goodExtensions.indexOf(extension) >= 0;
252 | }
253 |
254 | embedit.processRedditJson = function (data) {
255 | var result = {
256 | children: [],
257 | after: null,
258 | };
259 |
260 | // handle single page json
261 | if (data && data.length === 2 && data[0].data.children.length === 1) {
262 | // this means we're in single post link
263 | // response consists of two json objects, one for post, one for comments
264 | data = data[0];
265 | }
266 |
267 | //redditData = data //global for debugging data
268 | // NOTE: if data.data.after is null then this causes us to start
269 | // from the top on the next getRedditImages which is fine.
270 | if (data && data.data && data.data.after) {
271 | result.after = data.data.after;
272 | }
273 |
274 | if (data && data.data && data.data.children) {
275 | result.children = data.data.children;
276 | } else {
277 | // comments of e.g. a photoshopbattles post
278 | //children = rp.flattenRedditData(data);
279 | //throw new Error("Comments pages not yet supported");
280 | }
281 |
282 | if (result.children.length === 0) {
283 | console.log(
284 | "What case is this? Does the data have any length? Is this the standard nothing found case? TODO: debug this"
285 | );
286 | result.children = data;
287 | }
288 |
289 | return result;
290 | };
291 |
292 | embedit.redditItemToPic = function (item) {
293 | var pic = {
294 | url: item.data.url || item.data.link_url,
295 | title: item.data.title || item.data.link_title,
296 | over18: item.data.over_18,
297 | subreddit: item.data.subreddit,
298 | commentsLink: embedit.redditBaseUrl + item.data.permalink,
299 | userLink: item.data.author,
300 | data: item.data,
301 | };
302 |
303 | if (!embedit.transformRedditData(pic)) {
304 | return null;
305 | }
306 |
307 | return pic;
308 | };
309 |
310 | function decodeEntities(encodedString) {
311 | // https://stackoverflow.com/questions/44195322/a-plain-javascript-way-to-decode-html-entities-works-on-both-browsers-and-node
312 | var translate_re = /&(nbsp|amp|quot|lt|gt);/g;
313 | var translate = {
314 | nbsp: " ",
315 | amp: "&",
316 | quot: '"',
317 | lt: "<",
318 | gt: ">",
319 | };
320 | return encodedString
321 | .replace(translate_re, function (match, entity) {
322 | return translate[entity];
323 | })
324 | .replace(/(\d+);/gi, function (match, numStr) {
325 | var num = parseInt(numStr, 10);
326 | return String.fromCharCode(num);
327 | });
328 | }
329 |
330 | embedit.transformRedditData = function (pic) {
331 | // TODO: convert this to a more functional style
332 |
333 | pic.type = embedit.imageTypes.image;
334 | // Replace HTTP with HTTPS on gfycat and imgur to avoid this:
335 | // Mixed Content: The page at 'https://redditp.com/r/gifs' was loaded over HTTPS, but requested an insecure video 'http://i.imgur.com/LzsnbNU.webm'. This content should also be served over HTTPS.
336 | var http_prefix = "http://";
337 | var https_prefix = "https://";
338 | if (pic.url.indexOf("gfycat.com") >= 0) {
339 | pic.type = embedit.imageTypes.gfycat;
340 | pic.url = pic.url.replace(http_prefix, https_prefix);
341 | } else if (pic.url.indexOf("redgifs.com") >= 0) {
342 | pic.type = embedit.imageTypes.redgif;
343 | pic.url = pic.url.replace(http_prefix, https_prefix);
344 | } else if (pic.url.indexOf("//v.redd.it/") >= 0) {
345 | // NOTE DO NOT ADD DOMAINS HERE - MODIFY EMBEDIT.JS instead
346 | // NOTE DO NOT ADD DOMAINS HERE - MODIFY EMBEDIT.JS instead
347 | // NOTE DO NOT ADD DOMAINS HERE - MODIFY EMBEDIT.JS instead
348 | // Sadly, we have to add domains here or they get dropped in the "cannot display url" error below.
349 | // Need to redesign this redditp thing.
350 | if (pic.data.media) {
351 | pic.type = embedit.imageTypes.gifv;
352 | pic.url = pic.data.media.reddit_video.fallback_url;
353 | } else if (
354 | pic.data.crosspost_parent_list &&
355 | pic.data.crosspost_parent_list[0].media
356 | ) {
357 | pic.type = embedit.imageTypes.gifv;
358 | pic.url =
359 | pic.data.crosspost_parent_list[0].media.reddit_video.fallback_url;
360 | } else {
361 | // some crossposts don't have a pic.data.media obj?
362 | return false;
363 | }
364 | // Note that this `DASH_audio.mp4` is now `DASH_AUDIO_128.mp4`.
365 | // You can find it in the `.mpd` file, but I don't want to parse that.
366 | // Instead - we'll use `dash.min.js` to create the video element.
367 | pic.sound =
368 | pic.url.substring(0, pic.url.lastIndexOf("/")) + "/DASH_audio.mp4";
369 | } else if (pic.url.search(/^http.*imgur.*gifv?$/) > -1) {
370 | pic.type = embedit.imageTypes.gifv;
371 | pic.url = pic.url.replace(http_prefix, https_prefix);
372 | } else if (pic.url.indexOf("reddit.com/gallery") >= 0) {
373 | console.log("GOTCHA!");
374 |
375 | var dataSource;
376 | if (pic.data.gallery_data && pic.data.gallery_data.items) {
377 | dataSource = pic.data;
378 | } else if (
379 | pic.data.crosspost_parent_list &&
380 | pic.data.crosspost_parent_list[0] &&
381 | pic.data.crosspost_parent_list[0].gallery_data &&
382 | pic.data.crosspost_parent_list[0].gallery_data.items
383 | ) {
384 | // Grab the first image from the crosspost parent
385 | dataSource = pic.data.crosspost_parent_list[0];
386 | }
387 |
388 | var firstItemId = dataSource.gallery_data.items[0].media_id;
389 | var encodedUrl = dataSource.media_metadata[firstItemId]["s"]["u"];
390 |
391 | pic.type = embedit.imageTypes.image;
392 | if (encodedUrl === undefined) {
393 | // some posts don't have the u key, but have gif and mp4 keys
394 | encodedUrl = dataSource.media_metadata[firstItemId]["s"]["mp4"];
395 | pic.type = embedit.imageTypes.gifv;
396 | }
397 | pic.url = decodeEntities(encodedUrl);
398 | console.log(pic.url);
399 | } else if (isImageExtension(pic.url)) {
400 | // simple image
401 | } else {
402 | var betterUrl = tryConvertUrl(pic.url);
403 | if (betterUrl !== "") {
404 | pic.url = betterUrl;
405 | } else {
406 | if (window.debug) {
407 | console.log("cannot display url as image: " + pic.url);
408 | }
409 | return false;
410 | }
411 | }
412 |
413 | return true;
414 | };
415 |
416 | var tryConvertUrl = function (url) {
417 | if (url.indexOf("imgur.com") > 0 || url.indexOf("/gallery/") > 0) {
418 | // special cases with imgur
419 |
420 | if (url.indexOf("gifv") >= 0) {
421 | if (url.indexOf("i.") === 0) {
422 | url = url.replace("imgur.com", "i.imgur.com");
423 | }
424 | return url.replace(".gifv", ".gif");
425 | }
426 |
427 | if (url.indexOf("/a/") > 0 || url.indexOf("/gallery/") > 0) {
428 | // albums aren't supported yet
429 | //log('Unsupported gallery: ' + url);
430 | return "";
431 | }
432 |
433 | // imgur is really nice and serves the image with whatever extension
434 | // you give it. '.jpg' is arbitrary
435 | // regexp removes /r// prefix if it exists
436 | // E.g. http://imgur.com/r/aww/x9q6yW9
437 | return url.replace(/r\/[^ /]+\/(\w+)/, "$1") + ".jpg";
438 | }
439 |
440 | return "";
441 | };
442 |
443 | //////////////////////////////////////////////////////////
444 | // Exports
445 | //////////////////////////////////////////////////////////
446 |
447 | function browserNodeExport(exported, name) {
448 | // based off of http://www.matteoagosti.com/blog/2013/02/24/writing-javascript-modules-for-both-browser-and-node/
449 | if (typeof module !== "undefined" && typeof module.exports !== "undefined") {
450 | /* global module */
451 | module.exports = exported;
452 | } else {
453 | if (typeof define === "function" && define.amd) {
454 | /* global define */
455 | define([], function () {
456 | return exported;
457 | });
458 | } else {
459 | window[name] = exported;
460 | }
461 | }
462 | }
463 |
464 | browserNodeExport(embedit, "embedit");
465 |
--------------------------------------------------------------------------------
/js/fix-tagsyo-link.js:
--------------------------------------------------------------------------------
1 | function fixTagsyoLink() {
2 | var subMatch = /\/r\/([a-zA-Z_0-9\-]+)/.exec(window.location.href);
3 | if (!subMatch) {
4 | return;
5 | }
6 | var subName = subMatch[1];
7 | var query = "SELECT * FROM url WHERE LOWER(channel) = '" + subName.toLowerCase() + "' ORDER BY timestamp DESC !slideshow";
8 | var tagsyoLink = "http://www.tagsyo.com/q/?q=" + encodeURIComponent(query);
9 | document.getElementById('tagsyo-link').href = tagsyoLink;
10 | console.log("fixed tagsyo link");
11 | }
12 |
13 | fixTagsyoLink();
14 |
--------------------------------------------------------------------------------
/js/ie_hacks.js:
--------------------------------------------------------------------------------
1 | // https://stackoverflow.com/questions/7742781/why-does-javascript-only-work-after-opening-developer-tools-in-ie-once
2 | // IE doesn't have console.log and fails, wtf...
3 | // I used to use the log history thing, but all browsers have consoles nowadays...
4 | // http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
5 | // Avoid `console` errors in browsers that lack a console.
6 | (function() {
7 | var method;
8 | // eslint-disable-next-line no-empty-function
9 | var noop = function () {};
10 | var methods = [
11 | 'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
12 | 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
13 | 'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
14 | 'timeStamp', 'trace', 'warn'
15 | ];
16 | var length = methods.length;
17 | window.console = window.console || {};
18 | var console = window.console;
19 |
20 | while (length > 0) {
21 | length -= 1;
22 | method = methods[length];
23 |
24 | // Only stub undefined methods.
25 | if (!console[method]) {
26 | console[method] = noop;
27 | }
28 | }
29 | }());
30 |
31 |
32 | // IE doesn't have indexOf...
33 | if (!Array.indexOf) {
34 | // eslint-disable-next-line no-extend-native
35 | Array.prototype.indexOf = function (obj) {
36 | for (var i = 0; i < this.length; i++) {
37 | if (this[i] == obj) {
38 | return i;
39 | }
40 | }
41 | return -1;
42 | };
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/js/jquery.touchwipe.js:
--------------------------------------------------------------------------------
1 | !function(e){e.fn.touchwipe=function(t){var n={min_move_x:20,min_move_y:20,wipeLeft:function(){},wipeRight:function(){},wipeUp:function(){},wipeDown:function(){},preventDefaultEvents:!0};return t&&e.extend(n,t),this.each(function(){function e(){this.removeEventListener("touchmove",t),o=null,c=!1}function t(t){if(n.preventDefaultEvents&&t.preventDefault(),t.touches.length>=2)return void e();if(c){var i=t.touches[0].pageX,h=t.touches[0].pageY,s=o-i,a=u-h;Math.abs(s)>=n.min_move_x?(e(),s>0?n.wipeLeft():n.wipeRight()):Math.abs(a)>=n.min_move_y&&(e(),a>0?n.wipeDown():n.wipeUp())}}function i(e){1==e.touches.length&&(o=e.touches[0].pageX,u=e.touches[0].pageY,c=!0,this.addEventListener("touchmove",t,!1))}var o,u,c=!1;"ontouchstart"in document.documentElement&&this.addEventListener("touchstart",i,!1)}),this}}(jQuery);
--------------------------------------------------------------------------------
/js/js.cookie.js:
--------------------------------------------------------------------------------
1 | !function(e){if("function"==typeof define&&define.amd)define(e);else if("object"==typeof exports)module.exports=e();else{var n=window.Cookies,t=window.Cookies=e();t.noConflict=function(){return window.Cookies=n,t}}}(function(){function e(){for(var e=0,n={};e1){if(i=e({path:"/"},o.defaults,i),"number"==typeof i.expires){var s=new Date;s.setMilliseconds(s.getMilliseconds()+864e5*i.expires),i.expires=s}try{c=JSON.stringify(r),/^[\{\[]/.test(c)&&(r=c)}catch(a){}return r=encodeURIComponent(String(r)),r=r.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),n=encodeURIComponent(String(n)),n=n.replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent),n=n.replace(/[\(\)]/g,escape),document.cookie=[n,"=",r,i.expires&&"; expires="+i.expires.toUTCString(),i.path&&"; path="+i.path,i.domain&&"; domain="+i.domain,i.secure?"; secure":""].join("")}n||(c={});for(var p=document.cookie?document.cookie.split("; "):[],u=/(%[0-9A-Z]{2})+/g,d=0;dgithub');
56 | }
57 |
58 |
59 | $(function () {
60 |
61 | var pictureSliderId = "#pictureSlider";
62 |
63 | $("#subredditUrl").text("Loading Reddit Slideshow");
64 | $("#navboxTitle").text("Loading Reddit Slideshow");
65 |
66 | /*var fadeoutWhenIdle = true;
67 | var setupFadeoutOnIdle = function () {
68 | $('.fadeOnIdle').fadeTo('fast', 0);
69 | var navboxVisible = false;
70 | var fadeoutTimer = null;
71 | var fadeoutFunction = function () {
72 | navboxVisible = false;
73 | if (fadeoutWhenIdle) {
74 | $('.fadeOnIdle').fadeTo('slow', 0);
75 | }
76 | };
77 | $("body").mousemove(function () {
78 | if (navboxVisible) {
79 | clearTimeout(fadeoutTimer);
80 | fadeoutTimer = setTimeout(fadeoutFunction, 2000);
81 | return;
82 | }
83 | navboxVisible = true;
84 | $('.fadeOnIdle').fadeTo('fast', 1);
85 | fadeoutTimer = setTimeout(fadeoutFunction, 2000);
86 | });
87 | };*/
88 | // this fadeout was really inconvenient on mobile phones
89 | // and instead the minimize buttons should be used.
90 | //setupFadeoutOnIdle();
91 |
92 | var getNextSlideIndex = function (currentIndex, skipCount) {
93 | if(typeof skipCount !== "number"){
94 | var skipCount = 1
95 | }
96 | if (!rp.settings.nsfw) {
97 | // Skip any nsfw if you should
98 | for (var i = currentIndex + skipCount; i < rp.photos.length; i++) {
99 | if (!rp.photos[i].over18) {
100 | return i;
101 | }
102 | }
103 | return 0;
104 | }
105 | if (isLastImage(currentIndex) && !rp.session.loadingNextImages) {
106 | // The only reason we got here and there aren't more pictures yet
107 | // is because there are no more images to load, start over
108 | return 0;
109 | }
110 | // Just go to the next slide, this should be the common case
111 | return currentIndex + skipCount;
112 | };
113 | function nextSlide(skipCount) {
114 | var next = getNextSlideIndex(rp.session.activeIndex,skipCount);
115 | saveHistory(next);
116 | startAnimation(next);
117 | }
118 |
119 | function prevSlide() {
120 | var index = rp.session.activeIndex - 1;
121 | if (!rp.settings.nsfw) {
122 | for (; index > 0; index--) {
123 | if (!rp.photos[index].over18) {
124 | break;
125 | }
126 | }
127 | // index will be zero here if no sfw items found
128 | }
129 |
130 | saveHistory(index);
131 | startAnimation(index);
132 | }
133 |
134 |
135 | var autoNextSlide = function () {
136 | if (rp.settings.shouldAutoNextSlide) {
137 | // startAnimation takes care of the setTimeout
138 | nextSlide();
139 | }
140 | };
141 |
142 | function open_in_background(selector) {
143 | // as per https://developer.mozilla.org/en-US/docs/Web/API/event.initMouseEvent
144 | // works on latest chrome, safari and opera
145 | var link = $(selector)[0];
146 |
147 | // Simulating a ctrl key won't trigger a background tab on IE and Firefox ( https://bugzilla.mozilla.org/show_bug.cgi?id=812202 )
148 | // so we need to open a new window
149 | if (navigator.userAgent.match(/msie/i) || navigator.userAgent.match(/trident/i) || navigator.userAgent.match(/firefox/i)) {
150 | window.open(link.href, '_blank');
151 | } else {
152 | var mev = document.createEvent("MouseEvents");
153 | mev.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, true, false, false, true, 0, null);
154 | link.dispatchEvent(mev);
155 | }
156 | }
157 |
158 | $("#pictureSlider").touchwipe({
159 | // wipeLeft means the user moved his finger from right to left.
160 | wipeLeft: nextSlide,
161 | wipeRight: prevSlide,
162 | wipeUp: nextSlide,
163 | wipeDown: prevSlide,
164 | min_move_x: 20,
165 | min_move_y: 20,
166 | preventDefaultEvents: false
167 | });
168 |
169 | var OPENSTATE_ATTR = "data-openstate";
170 | $('.collapser').click(function () {
171 | var state = $(this).attr(OPENSTATE_ATTR);
172 | if (state === "open") {
173 | // close it
174 | $(this).text("+");
175 | // move to the left just enough so the collapser arrow is visible
176 | var arrowLeftPoint = $(this).position().left;
177 | $(this).parent().animate({
178 | left: "-" + arrowLeftPoint + "px"
179 | });
180 | $(this).attr(OPENSTATE_ATTR, "closed");
181 | } else {
182 | // open it
183 | $(this).text("-");
184 | $(this).parent().animate({
185 | left: "0px"
186 | });
187 | $(this).attr(OPENSTATE_ATTR, "open");
188 | }
189 | });
190 |
191 | // Arguments are image paths relative to the current page.
192 | var preLoadImages = function () {
193 | var args_len = arguments.length;
194 | for (var i = args_len; i--;) {
195 | var cacheImage = document.createElement('img');
196 | cacheImage.src = arguments[i];
197 | // Chrome makes the web request without keeping a copy of the image.
198 | //rp.cache.push(cacheImage);
199 | }
200 | };
201 |
202 | var cookieNames = {
203 | nsfwCookie: "nsfwCookie",
204 | shouldAutoNextSlideCookie: "shouldAutoNextSlideCookie",
205 | timeToNextSlideCookie: "timeToNextSlideCookie",
206 | soundCookie: "soundCookie"
207 | };
208 |
209 | var setCookie = function (c_name, value) {
210 | Cookies.set(c_name, value, {
211 | expires: rp.settings.cookieDays,
212 | // All the cookie issues are from requests to reddit.com
213 | // So no need for this "Lax" here.
214 | // sameSite: "Lax",
215 | });
216 | };
217 |
218 |
219 | var getCookie = function (c_name) {
220 | // undefined in case nothing found
221 | return Cookies.get(c_name);
222 | };
223 |
224 | var updateSound = function () {
225 | rp.settings.sound = $('#sound').is(':checked');
226 | setCookie(cookieNames.soundCookie, rp.settings.sound);
227 | var videoTags = document.getElementsByTagName('video');
228 | if (videoTags.length === 1) {
229 | videoTags[0].muted = !rp.settings.sound;
230 | }
231 | var audioTags = document.getElementsByTagName('audio');
232 | if (audioTags.length === 1) {
233 | audioTags[0].muted = !rp.settings.sound;
234 | } else {
235 | console.log(audioTags);
236 | }
237 | };
238 |
239 | var resetNextSlideTimer = function () {
240 | clearTimeout(rp.session.nextSlideTimeoutId);
241 | rp.session.nextSlideTimeoutId = setTimeout(autoNextSlide, rp.settings.timeToNextSlide);
242 | };
243 |
244 | var updateAutoNext = function () {
245 | rp.settings.shouldAutoNextSlide = $("#autoNextSlide").is(':checked');
246 | setCookie(cookieNames.shouldAutoNextSlideCookie, rp.settings.shouldAutoNextSlide);
247 | resetNextSlideTimer();
248 | };
249 |
250 | var toggleSound = function () {
251 | $("#sound").each(function () {
252 | this.checked = !this.checked;
253 | console.log(this.checked);
254 | $(this).trigger('change');
255 | });
256 | };
257 |
258 | var toggleFullScreen = function () {
259 | var elem = document.getElementById('page');
260 | if (document.fullscreenElement || // alternative standard method
261 | document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement) { // current working methods
262 | if (document.exitFullscreen) {
263 | document.exitFullscreen();
264 | } else if (document.msExitFullscreen) {
265 | document.msExitFullscreen();
266 | } else if (document.mozCancelFullScreen) {
267 | document.mozCancelFullScreen();
268 | } else if (document.webkitExitFullscreen) {
269 | document.webkitExitFullscreen();
270 | }
271 | } else {
272 | if (elem.requestFullscreen) {
273 | elem.requestFullscreen();
274 | } else if (elem.msRequestFullscreen) {
275 | elem.msRequestFullscreen();
276 | } else if (elem.mozRequestFullScreen) {
277 | elem.mozRequestFullScreen();
278 | } else if (elem.webkitRequestFullscreen) {
279 | elem.webkitRequestFullscreen();
280 | }
281 | }
282 | };
283 |
284 | var updateNsfw = function () {
285 | rp.settings.nsfw = $("#nsfw").is(':checked');
286 | setCookie(cookieNames.nsfwCookie, rp.settings.nsfw);
287 | };
288 |
289 | var initState = function () {
290 | var nsfwByCookie = getCookie(cookieNames.nsfwCookie);
291 | if (nsfwByCookie === undefined) {
292 | rp.settings.nsfw = true;
293 | } else {
294 | rp.settings.nsfw = (nsfwByCookie === "true");
295 | $("#nsfw").prop("checked", rp.settings.nsfw);
296 | }
297 | $('#nsfw').change(updateNsfw);
298 |
299 | // Fix sound cookie
300 | var soundByCookie = getCookie(cookieNames.soundCookie);
301 | if (soundByCookie === undefined) {
302 | rp.settings.sound = false;
303 | } else {
304 | rp.settings.sound = (soundByCookie === "true");
305 | $("#sound").prop("checked", rp.settings.sound);
306 | }
307 | $('#sound').change(updateSound);
308 |
309 | var autoByCookie = getCookie(cookieNames.shouldAutoNextSlideCookie);
310 | if (autoByCookie === undefined) {
311 | updateAutoNext();
312 | } else {
313 | rp.settings.shouldAutoNextSlide = (autoByCookie === "true");
314 | $("#autoNextSlide").prop("checked", rp.settings.shouldAutoNextSlide);
315 | }
316 | $('#autoNextSlide').change(updateAutoNext);
317 |
318 | var updateTimeToNextSlide = function () {
319 | var val = $('#timeToNextSlide').val();
320 | rp.settings.timeToNextSlide = parseFloat(val) * 1000;
321 | setCookie(cookieNames.timeToNextSlideCookie, val);
322 | };
323 |
324 | var timeByCookie = getCookie(cookieNames.timeToNextSlideCookie);
325 | if (timeByCookie === undefined) {
326 | updateTimeToNextSlide();
327 | } else {
328 | rp.settings.timeToNextSlide = parseFloat(timeByCookie) * 1000;
329 | $('#timeToNextSlide').val(timeByCookie);
330 | }
331 |
332 | $('#fullScreenButton').click(toggleFullScreen);
333 |
334 | $('#timeToNextSlide').keyup(updateTimeToNextSlide);
335 |
336 | $('#prevButton').click(prevSlide);
337 | $('#nextButton').click(nextSlide);
338 | };
339 |
340 | var addNumberButton = function (numberButton) {
341 | var navboxUls = $(".navbox ul");
342 | var thisNavboxUl = navboxUls[navboxUls.length - 1];
343 |
344 | var newListItem = $(" ").appendTo(thisNavboxUl);
345 | numberButton.appendTo(newListItem);
346 |
347 | // so li's have a space between them and can word-wrap in the box
348 | navboxUls.append(document.createTextNode(' '));
349 | };
350 |
351 |
352 |
353 |
354 | var addImageSlide = function (item) {
355 | /*
356 | var pic = {
357 | "title": title,
358 | "url": url,
359 | "commentsLink": commentsLink,
360 | "over18": over18,
361 | "isVideo": video
362 | }
363 | */
364 | if(!item.data.is_gallery){
365 | var pic = embedit.redditItemToPic(item);
366 | if (!pic) {
367 | return;
368 | }
369 | for (i = 0; i < rp.photos.length; i += 1) {
370 | if (pic.url === rp.photos[i].url) {
371 | return;
372 | }
373 | }
374 | rp.photos.push(pic);
375 | rp.session.foundOneImage = true;
376 | var i = rp.photos.length - 1;
377 | var numberButton = $(" ").html((i+1)-galleryOffset)
378 | .data("index", i)
379 | .attr("title", rp.photos[i].title)
380 | .attr("id", "numberButton" + (i + 1))
381 | if (pic.over18) {
382 | numberButton.addClass("over18");
383 | }
384 | numberButton.click(function () {
385 | showImage($(this));
386 | });
387 | numberButton.addClass("numberButton");
388 | addNumberButton(numberButton);
389 | } else {
390 | const x = (rp.photos.length+1)-galleryOffset
391 | galleryOffset+=(item.data.gallery_data.items.length)-1
392 | $.each(item.data.gallery_data.items, function (j, image) {
393 | pic = {
394 | "title": item.data.title,
395 | "url": "https://i.redd.it/"+image.media_id+"."+(item.data.media_metadata[image.media_id].m).split('/')[1],
396 | "data": item.data,
397 | "commentsLink": item.data.url,
398 | "over18": item.data.over_18,
399 | "isVideo": item.data.is_video,
400 | "subreddit": item.data.subreddit,
401 | "galleryItem": j+1,
402 | "galleryTotal": item.data.gallery_data.items.length,
403 | "userLink": item.data.author,
404 | "type": (item.data.media_metadata[image.media_id].m).split('/')[0]
405 | };
406 | for (i = 0; i < rp.photos.length; i += 1) {
407 | if (pic.url === rp.photos[i].url) {
408 | return;
409 | }
410 | }
411 | rp.photos.push(pic);
412 | rp.session.foundOneImage = true;
413 |
414 |
415 | });
416 | var i = rp.photos.length - 1;
417 | var numberButton = $(" ").html(x)
418 | .data("index", i-(rp.photos[i].galleryItem-1))
419 | .attr("title", rp.photos[i].title)
420 | .attr("id", "numberButton" + ((i + 1)-(rp.photos[i].galleryTotal-1)))
421 | .addClass("numberButton")
422 | .addClass("gallery");
423 | numberButton.append($(" ").html("/"+rp.photos[i].galleryTotal).css({fontSize: 10}).addClass("galleryCount"))
424 | if (pic.over18) {
425 | numberButton.addClass("over18");
426 | }
427 | numberButton.click(function () {
428 | showImage($(this))
429 | });
430 | addNumberButton(numberButton);
431 | }
432 |
433 | // Do not preload all images, this is just not performant.
434 | // Especially in gif or high-res subreddits where each image can be 50 MB.
435 | // My high-end desktop browser was unresponsive at times.
436 | //preLoadImages(pic.url);
437 |
438 | };
439 |
440 | var arrow = {
441 | left: 37,
442 | up: 38,
443 | right: 39,
444 | down: 40
445 | };
446 | //var ONE_KEY = 49;
447 | //var NINE_KEY = 57;
448 | var SPACE = 32;
449 | var PAGEUP = 33;
450 | var PAGEDOWN = 34;
451 | //var ENTER = 13;
452 | var A_KEY = 65;
453 | var C_KEY = 67;
454 | var M_KEY = 77;
455 | var F_KEY = 70;
456 | var I_KEY = 73;
457 | var R_KEY = 82;
458 | var T_KEY = 84;
459 | var W_KEY = 87;
460 | var S_KEY = 83;
461 | var U_KEY = 85;
462 | var G_KEY = 71;
463 |
464 |
465 | // Register keyboard events on the whole document
466 | $(document).keyup(async function (e) {
467 | if (e.ctrlKey) {
468 | // ctrl key is pressed so we're most likely switching tabs or doing something
469 | // unrelated to redditp UI
470 | return;
471 | }
472 |
473 | //log(e.keyCode, e.which, e.charCode);
474 |
475 | // 37 - left
476 | // 38 - up
477 | // 39 - right
478 | // 40 - down
479 | // More info: http://stackoverflow.com/questions/302122/jquery-event-keypress-which-key-was-pressed
480 | // http://stackoverflow.com/questions/1402698/binding-arrow-keys-in-js-jquery
481 | var code = (e.keyCode ? e.keyCode : e.which);
482 |
483 | switch (code) {
484 | case C_KEY:
485 | $('#controlsDiv .collapser').click();
486 | break;
487 | case T_KEY:
488 | $('#titleDiv .collapser').click();
489 | break;
490 | case A_KEY:
491 | var $ans = $("#autoNextSlide");
492 | $ans.prop("checked", !$ans.is(':checked'));
493 | updateAutoNext();
494 | break;
495 | case I_KEY:
496 | open_in_background("#navboxLink");
497 | break;
498 | case U_KEY:
499 | open_in_background("#navboxUser");
500 | break;
501 | case R_KEY:
502 | open_in_background("#navboxCommentsLink");
503 | break;
504 | case F_KEY:
505 | toggleFullScreen();
506 | break;
507 | case M_KEY:
508 | toggleSound();
509 | break;
510 | case PAGEUP:
511 | case arrow.left:
512 | case arrow.up:
513 | case W_KEY:
514 | return prevSlide();
515 | case PAGEDOWN:
516 | case arrow.right:
517 | case arrow.down:
518 | case SPACE:
519 | case S_KEY:
520 | return nextSlide();
521 | case G_KEY:
522 | skipGallery()
523 | break;
524 | }
525 | });
526 |
527 |
528 | //
529 | // Shows an image and plays the animation
530 | //
531 | var showImage = function (docElem) {
532 | // Retrieve the index we need to use
533 | var imageIndex = docElem.data("index");
534 |
535 | saveHistory(imageIndex);
536 | startAnimation(imageIndex);
537 | };
538 |
539 | var isLastImage = function (imageIndex) {
540 | if (rp.settings.nsfw) {
541 | return imageIndex === rp.photos.length - 1;
542 | } else {
543 | // look for remaining sfw images
544 | for (var i = imageIndex + 1; i < rp.photos.length; i++) {
545 | if (!rp.photos[i].over18) {
546 | return false;
547 | }
548 | }
549 | return true;
550 | }
551 | };
552 |
553 | var preloadNextImage = function (imageIndex) {
554 | var next = getNextSlideIndex(imageIndex);
555 | // Always clear cache - no need for memory bloat.
556 | // We only keep the next image preloaded.
557 | rp.cache = {};
558 | if (next < rp.photos.length)
559 | rp.cache[next] = createDiv(next);
560 | };
561 |
562 | // History / back button stuff
563 | var lastSavedHistoryState = {
564 | index: -1,
565 | url: "",
566 | };
567 | var scheduledAnimation = null;
568 |
569 | var loadHistory = function (state) {
570 | //console.log("Loading history state " + event.state);
571 |
572 | var index;
573 | if (state == null || rp.photos[state.index] == null || rp.photos[state.index].url != state.url) {
574 | index = 0;
575 | } else {
576 | index = state.index;
577 | lastSavedHistoryState = state;
578 | }
579 |
580 | startAnimation(index);
581 | };
582 |
583 | window.onpopstate = function (event) {
584 | // This is called when back/forward button is pressed and there is custom history states saved.
585 | loadHistory(event.state);
586 | };
587 |
588 | var saveHistory = function (index) {
589 | if (window.history == null) {
590 | return; // History api is not supported, do nothing
591 | }
592 |
593 | var photo = rp.photos[index];
594 | if (index != lastSavedHistoryState.index && photo != null) {
595 | //console.log("Recorded history state " + index);
596 | lastSavedHistoryState = {
597 | index: index,
598 | url: photo.url,
599 | };
600 | history.pushState(lastSavedHistoryState, photo.title);
601 | }
602 | };
603 |
604 | var animationFinished = function () {
605 | if (scheduledAnimation != null) {
606 | var next = scheduledAnimation;
607 | scheduledAnimation = null;
608 | startAnimation(next);
609 | }
610 | };
611 |
612 | var showDefault = function () {
613 | // What to show initially
614 | if (window.history != null) {
615 | loadHistory(history.state);
616 | } else {
617 | startAnimation(0);
618 | }
619 | };
620 |
621 | //
622 | // Starts the animation, based on the image index
623 | //
624 | // Variable to store if the animation is playing or not
625 | var startAnimation = async function (imageIndex) {
626 | resetNextSlideTimer();
627 |
628 | if (rp.session.isAnimating) {
629 | // If animating, queue given image to be animated after this
630 | scheduledAnimation = imageIndex;
631 | return;
632 | }
633 |
634 | // If the same number has been chosen, or the index is outside the
635 | // rp.photos range, or we're already animating, do nothing
636 | if (rp.session.activeIndex === imageIndex || imageIndex > rp.photos.length - 1 || imageIndex < 0 || rp.session.isAnimating || rp.photos.length === 0) {
637 | return;
638 | }
639 |
640 | rp.session.isAnimating = true;
641 | await animateNavigationBox(imageIndex);
642 | slideBackgroundPhoto(imageIndex);
643 | preloadNextImage(imageIndex);
644 |
645 | // Set the active index to the used image index
646 | rp.session.activeIndex = imageIndex;
647 |
648 | if (isLastImage(rp.session.activeIndex) && rp.subredditUrl.indexOf('/imgur') !== 0) {
649 | getRedditImages();
650 | }
651 | };
652 |
653 | var toggleNumberButton = async function (imageIndex,turnOn) {
654 | if (imageIndex<0){return}
655 | var photo = rp.photos[imageIndex]
656 | if (!photo.galleryItem){
657 | var numberButton = $("#numberButton"+(imageIndex+1));
658 | } else {
659 | var numberButton = $("#numberButton"+((imageIndex+1)-(rp.photos[imageIndex].galleryItem-1)));
660 | }
661 | if (turnOn) {
662 | numberButton.addClass('active');
663 | } else {
664 | numberButton.removeClass('active');
665 | }
666 | };
667 |
668 | //
669 | // Animate the navigation box
670 | //
671 | var animateNavigationBox = async function (imageIndex) {
672 | console.log(imageIndex)
673 | var photo = rp.photos[imageIndex];
674 | var subreddit = '/r/' + photo.subreddit;
675 | var user = '/u/' + photo.userLink + '/submitted';
676 |
677 | $('#navboxTitle').html(photo.title);
678 | $('#navboxSubreddit').attr('href', embedit.redditBaseUrl + subreddit).html(subreddit);
679 | $('#navboxLink').attr('href', photo.url).attr('title', photo.title);
680 | $('#navboxCommentsLink').attr('href', photo.commentsLink).attr('title', "Comments on reddit");
681 | $('#navboxUser').attr('href', window.location.origin + user).attr('user', "User on reddit");
682 | if (photo.galleryItem){
683 | $("#navboxGallery").text("Gallery: "+photo.galleryItem+"/"+photo.galleryTotal);
684 | } else {
685 | $("#navboxGallery").text("")
686 | }
687 | document.title = photo.title + " - " + subreddit + " - redditP";
688 |
689 | await toggleNumberButton(rp.session.activeIndex, false);
690 | await toggleNumberButton(imageIndex, true);
691 | };
692 |
693 | var playButton = $(' ');
694 | playButton.click(function () {
695 | if ($('video')[0]) {
696 | $('video')[0].play();
697 | } else {
698 | // serious bug, why did we show the play button but have no video there?
699 | reportError('Play button pressed but no video there');
700 | }
701 | playButton.hide();
702 | });
703 | $("#page").append(playButton);
704 | playButton.hide();
705 |
706 | var startPlayingVideo = function (vid_jq) {
707 | // Loop or auto next slide
708 | // TODO: make this update every time you check/uncheck auto-play
709 | if (rp.settings.shouldAutoNextSlide) {
710 | clearTimeout(rp.session.nextSlideTimeoutId);
711 | vid_jq.removeAttr('loop');
712 | }
713 |
714 | // sound cookie setting
715 | vid_jq[0].muted = !rp.settings.sound;
716 |
717 | // Disable subtitles by default
718 | var subTracks = vid_jq[0].textTracks || [];
719 | for (var i = 0; i < subTracks.length; i++) {
720 | subTracks[i].mode = 'hidden';
721 | }
722 |
723 | var onEndFunc = function (/*e*/) {
724 | if (rp.settings.shouldAutoNextSlide)
725 | nextSlide();
726 | };
727 | vid_jq.one('ended', onEndFunc);
728 | // Tested on Firefox 43, gfycats that were preloaded do not autoplay when shown so
729 | // this is the workaround. We also prefer the play to start after the fadein finishes.
730 | var playPromise = vid_jq[0].play();
731 | if (playPromise && playPromise.catch) {
732 | // playPromise is `undefined` in firefox 46-48 it seems
733 | playPromise.catch(function (e) {
734 | // a trick to get around: DOMException: play() can only be initiated by a user gesture.
735 | // We show a play button that the user can press
736 | if (e.name === "NotAllowedError") {
737 | //code: 0
738 | //message: "play() can only be initiated by a user gesture."
739 | //name: "NotAllowedError"
740 | playButton.show();
741 | //setTimeout(function() {vid_jq[0].play();}, 100);
742 | } else {
743 | // AbortError can happen I think with e.g. a 404, user clicking next before loading finishes,
744 | // In that case, we don't want the play button to show. Here is a recorded example from
745 | // https://redditp.com/r/eyebleach/top?t=month
746 | //code: 20,
747 | //message: "The play() request was interrupted by a call to pause().",
748 | //name: "AbortError",
749 | }
750 | console.log(e);
751 | });
752 | }
753 | };
754 |
755 | //
756 | // Slides the background photos
757 | //
758 | var slideBackgroundPhoto = function (imageIndex) {
759 | var divNode;
760 | if (rp.cache[imageIndex] === undefined) {
761 | divNode = createDiv(imageIndex);
762 | } else {
763 | divNode = rp.cache[imageIndex];
764 | }
765 |
766 | divNode.prependTo(pictureSliderId);
767 | fixRedditVideo(imageIndex);
768 |
769 | $(pictureSliderId + " div").fadeIn(rp.settings.animationSpeed);
770 | var oldDiv = $(pictureSliderId + " div:not(:first)");
771 | oldDiv.fadeOut(rp.settings.animationSpeed, function () {
772 | oldDiv.remove();
773 | rp.session.isAnimating = false;
774 | animationFinished();
775 |
776 | var maybeVid = $('video');
777 | if (maybeVid.length > 0) {
778 | startPlayingVideo(maybeVid);
779 | }
780 | });
781 | };
782 |
783 | var fixRedditVideo = function (imageIndex) {
784 | var photo = rp.photos[imageIndex];
785 | if (photo.url.indexOf("//v.redd.it/") < 0) {
786 | // only fix reddit videos
787 | return;
788 | }
789 | if (!photo.data.secure_media || !photo.data.secure_media.reddit_video) {
790 | console.log("Some new reddit videos seem to have a null secure_media. Hmmm.");
791 | return;
792 | }
793 | var url = photo.data.secure_media.reddit_video.dash_url;
794 | var player = dashjs.MediaPlayer().create();
795 | player.initialize(document.querySelector("video"), url, true);
796 | }
797 |
798 | var createDiv = function (imageIndex) {
799 | // Retrieve the accompanying photo based on the index
800 | var photo = rp.photos[imageIndex];
801 | //log("Creating div for " + imageIndex + " - " + photo.url);
802 |
803 | // Create a new div and apply the CSS
804 | var divNode = $("
");
805 | if (photo.type === embedit.imageTypes.image) {
806 |
807 | // TODO: REFACTOR BOTH IMAGES AND VIDEOS TO WORK WITH ONE FRAMEWORK - EMBEDIT
808 |
809 | // An actual image. Not a video/gif.
810 | // `preLoadImages` because making a div with a background css does not cause chrome
811 | // to preload it :/
812 | preLoadImages(photo.url);
813 | var cssMap = Object();
814 | cssMap['display'] = "none";
815 | cssMap['background-image'] = "url(" + photo.url + ")";
816 | cssMap['background-repeat'] = "no-repeat";
817 | cssMap['background-size'] = "contain";
818 | cssMap['background-position'] = "center";
819 |
820 | divNode.css(cssMap).addClass("clouds");
821 | } else { //if(photo.type === imageTypes.gfycat || photo.type === imageTypes.gifv) {
822 | embedit.embed(photo.url, function (elem) {
823 | if (!elem) {
824 | reportError('Failed to handle url');
825 | return divNode;
826 | }
827 | if (photo.url.indexOf("//v.redd.it/") >= 0) {
828 | // Embedit is wrong here, ignore it.
829 | // I'm ashamed of the spaghetti I'm in, but I'm also tired
830 | // and want to go to sleep with this working.
831 | // elem = document.createElement("video");
832 | elem = $(' ');
833 | }
834 | divNode.append(elem);
835 |
836 | $(elem).attr({
837 | playsinline: '',
838 | });
839 | if (photo.sound) {
840 | // this case is for videos from v.redd.it domain only
841 | // $(" ").appendTo($(elem));
842 | // console.log("we are here!", photo)
843 | // console.log("we are here!", photo.data.secure_media.reddit_video.dash_url)
844 |
845 | // var $audioTag = $("audio", elem).get(0);
846 | // var $videoTag = $("video", divNode).get(0);
847 |
848 | // // sync reddit audio and video tracks
849 | // $audioTag.currentTime = $videoTag.currentTime;
850 | // $videoTag.onplay = function () {
851 | // $audioTag.play();
852 | // };
853 | // $videoTag.onpause = function () {
854 | // $audioTag.pause();
855 | // };
856 | // $videoTag.onseeking = function () {
857 | // $audioTag.currentTime = $videoTag.currentTime;
858 | // };
859 | }
860 | elem.width('100%').height('100%');
861 | // We start paused and play after the fade in.
862 | // This is to avoid cached or preloaded videos from playing.
863 | if (elem[0].pause) {
864 | // Note this doesn't work on iframe embeds.
865 | elem[0].pause();
866 | }
867 | });
868 | }// else {
869 | // reportError('Unhandled image type');
870 | //}
871 |
872 | return divNode;
873 | };
874 | var skipGallery = async function () {
875 | photo = rp.photos[rp.session.activeIndex];
876 | if (!photo.data.is_gallery){
877 | return
878 | }
879 | var skipCount = (photo.galleryTotal - photo.galleryItem)+1
880 | nextSlide(skipCount)
881 | };
882 |
883 | var verifyNsfwMakesSense = function () {
884 | // Cases when you forgot NSFW off but went to /r/nsfw
885 | // can cause strange bugs, let's help the user when over 80% of the
886 | // content is NSFW.
887 | var nsfwImages = 0;
888 | for (var i = 0; i < rp.photos.length; i++) {
889 | if (rp.photos[i].over18) {
890 | nsfwImages += 1;
891 | }
892 | }
893 |
894 | if (0.8 < nsfwImages * 1.0 / rp.photos.length) {
895 | rp.settings.nsfw = true;
896 | $("#nsfw").prop("checked", rp.settings.nsfw);
897 | }
898 | };
899 |
900 | var decodeUrl = function (url) {
901 | return decodeURIComponent(url.replace(/\+/g, " "));
902 | };
903 | rp.getRestOfUrl = function () {
904 | // Separate to before the question mark and after
905 | // Detect predefined reddit url paths. If you modify this be sure to fix
906 | // .htaccess
907 | // This is a good idea so we can give a quick 404 page when appropriate.
908 |
909 | var regexS = "(/(?:(?:r/)|(?:imgur/a/)|(?:u(?:ser)?/)|(?:domain/)|(?:search))[^?]*)[?]?(.*)";
910 | var regex = new RegExp(regexS);
911 | var results = regex.exec(window.location.href);
912 | //log(results);
913 | if (results === null) {
914 | return ["", ""];
915 | } else {
916 | return [results[1], decodeUrl(results[2])];
917 | }
918 | };
919 |
920 | var failCleanup = function () {
921 | if (rp.photos.length > 0) {
922 | // already loaded images, don't ruin the existing experience
923 | return;
924 | }
925 |
926 | // remove "loading" title
927 | $('#navboxTitle').text('');
928 |
929 | // display alternate recommendations
930 | $('#recommend').css({ 'display': 'block' });
931 | };
932 |
933 | var parseQuery = function (queryString) {
934 | var query = {};
935 | var pairs = (queryString[0] === '?' ? queryString.substr(1) : queryString).split('&');
936 | for (var i = 0; i < pairs.length; i++) {
937 | var pair = pairs[i].split('=');
938 | query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || '');
939 | }
940 | return query;
941 | };
942 |
943 | var shuffle = function (arr) {
944 | var i, j, x;
945 | for (i = arr.length - 1; i > 0; i--) {
946 | j = Math.floor(Math.random() * (i + 1));
947 | x = arr[i];
948 | arr[i] = arr[j];
949 | arr[j] = x;
950 | }
951 | return arr;
952 | };
953 |
954 | var isShuffleOn = function () {
955 | var query = parseQuery(window.location.search);
956 | return !!query.shuffle;
957 | };
958 |
959 | var getRedditImages = function () {
960 | //if (noMoreToLoad){
961 | // log("No more images to load, will rotate to start.");
962 | // return;
963 | //}
964 |
965 | rp.session.loadingNextImages = true;
966 |
967 | var subredditUrl = rp.subredditUrl;
968 |
969 | // If requesting more images from an already-loaded random page, grab
970 | // the actual current subreddit from the photos; after= doesn't work on
971 | // random pages.
972 | if (rp.photos.length > 0 && (subredditUrl === "/r/randnsfw" || subredditUrl === "/r/random")) {
973 | subredditUrl = "/r/" + rp.photos[0].subreddit;
974 | }
975 |
976 | // Seems since sometime in 2023:
977 | // Works - https://www.reddit.com/r/aww/.json?
978 | // Fails - https://www.reddit.com/r/aww.json?
979 | if (subredditUrl.length > 0 && subredditUrl[subredditUrl.length - 1] !== "/") {
980 | subredditUrl += "/";
981 | }
982 | var jsonUrl = embedit.redditBaseUrl + subredditUrl + ".json?" + (rp.session.after ? rp.session.after + "&" : "") + getVars;
983 |
984 | var failedAjax = function (/*data*/) {
985 | var message = "Failed ajax, maybe a bad url? Sorry about that :(";
986 | var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
987 | if (isFirefox) {
988 | message = "Failed ajax, Firefox try to disable tracking protection from the shield in the URL bar";
989 | }
990 | reportError(message);
991 | failCleanup();
992 | };
993 |
994 | var handleData = function (data) {
995 | var childrenAndAfter = embedit.processRedditJson(data);
996 | var children = childrenAndAfter.children;
997 | var after = childrenAndAfter.after;
998 |
999 | if (children.length === 0) {
1000 | reportError("No data from this url :(");
1001 | return;
1002 | }
1003 |
1004 | if (isShuffleOn()) {
1005 | shuffle(children);
1006 | }
1007 |
1008 | $.each(children, function (i, item) {
1009 | // `item.data.link_url` seems to be an item for reddit images
1010 | // or maybe the api change for user pages?
1011 | // First saw it at `https://redditp.com/u/doherty99` in the permalink:
1012 | // "https://www.reddit.com/r/gonewild/comments/7h7srj/pull_my_hair_and_fuck_me_from_behind/"
1013 | if (!item || !item.data) {
1014 | reportError('invald data item');
1015 | return;
1016 | }
1017 | addImageSlide(item);
1018 | });
1019 |
1020 | verifyNsfwMakesSense();
1021 |
1022 | if (!rp.session.foundOneImage) {
1023 | // Note: the jsonp url may seem malformed but jquery fixes it.
1024 | //log(jsonUrl);
1025 | reportError("Sorry, no displayable images found in that url :(");
1026 | }
1027 |
1028 | // show the first image
1029 | if (rp.session.activeIndex == -1) {
1030 | // was startShow()
1031 | showDefault();
1032 | }
1033 |
1034 | if (after) {
1035 | rp.session.after = "&after=" + after;
1036 | } else {
1037 | console.log("No more pages to load from this subreddit, reloading the start");
1038 |
1039 | // Show the user we're starting from the top
1040 | var numberButton = $(" ").addClass("numberButton").text("-");
1041 | addNumberButton(numberButton);
1042 | }
1043 | rp.session.loadingNextImages = false;
1044 |
1045 | };
1046 |
1047 | if (rp.settings.debug)
1048 | console.log('Ajax requesting: ' + jsonUrl);
1049 |
1050 | // Note we're still using `jsonp` despite potential issues because
1051 | // `http://www.redditp.com/r/randnsfw` wasn't working with CORS for some reason.
1052 | // https://github.com/ubershmekel/redditp/issues/104
1053 | // Another issue caused with these is
1054 | // multireddits of a user. E.g.
1055 | // http://localhost:8080/?/u/eightbitbailey/submitted
1056 | // http://www.redditp.com/u/eightbitbailey/submitted
1057 | // var useJsonP = jsonUrl.indexOf('\/comments\/') !== -1
1058 | // || jsonUrl.indexOf('\/r\/randnsfw') !== -1
1059 | // || jsonUrl.indexOf('\/r\/random') !== -1;
1060 | var useJsonP = true;
1061 | if (useJsonP) {
1062 | jsonUrl += '&jsonp=?';
1063 | }
1064 |
1065 | // I still haven't been able to catch jsonp 404 events so the timeout
1066 | // is the current solution sadly.
1067 | $.ajax({
1068 | url: jsonUrl,
1069 | dataType: useJsonP ? 'jsonp' : 'json',
1070 | jsonp: useJsonP,
1071 | success: handleData,
1072 | error: failedAjax,
1073 | 404: failedAjax,
1074 | timeout: 5000
1075 | });
1076 | };
1077 |
1078 | var getImgurAlbum = function (url) {
1079 | var albumID = url.match(/.*\/(.+?$)/)[1];
1080 | var jsonUrl = 'https://api.imgur.com/3/album/' + albumID;
1081 | //log(jsonUrl);
1082 | var failedAjax = function (/*data*/) {
1083 | reportError("Failed ajax, maybe a bad url? Sorry about that :(");
1084 | failCleanup();
1085 | };
1086 | var handleData = function (data) {
1087 |
1088 | //log(data);
1089 |
1090 | var children = data.data.images;
1091 |
1092 | if (children.length === 0) {
1093 | reportError("No data from this url :(");
1094 | return;
1095 | }
1096 |
1097 | if (isShuffleOn()) {
1098 | shuffle(children);
1099 | }
1100 | $.each(children, function (i, item) {
1101 | addImageSlide({
1102 | url: item.link,
1103 | title: item.title,
1104 | over18: item.nsfw,
1105 | commentsLink: ""
1106 | });
1107 | });
1108 |
1109 | verifyNsfwMakesSense();
1110 |
1111 | if (!rp.session.foundOneImage) {
1112 | console.log(jsonUrl);
1113 | reportError("Sorry, no displayable images found in that url :(");
1114 | }
1115 |
1116 | // show the first image
1117 | if (rp.session.activeIndex === -1) {
1118 | // was startShow();
1119 | showDefault();
1120 | }
1121 |
1122 | //log("No more pages to load from this subreddit, reloading the start");
1123 |
1124 | // Show the user we're starting from the top
1125 | //var numberButton = $(" ").addClass("numberButton").text("-");
1126 | //addNumberButton(numberButton);
1127 |
1128 | rp.session.loadingNextImages = false;
1129 | };
1130 |
1131 | $.ajax({
1132 | url: jsonUrl,
1133 | dataType: 'json',
1134 | success: handleData,
1135 | error: failedAjax,
1136 | 404: failedAjax,
1137 | timeout: 5000,
1138 | beforeSend: function (xhr) {
1139 | xhr.setRequestHeader('Authorization',
1140 | 'Client-ID ' + 'f2edd1ef8e66eaf');
1141 | }
1142 | });
1143 | };
1144 |
1145 | var setupUrls = function () {
1146 | rp.urlData = rp.getRestOfUrl();
1147 | //log(rp.urlData)
1148 | rp.subredditUrl = rp.urlData[0];
1149 | getVars = rp.urlData[1];
1150 |
1151 | if (getVars.length > 0) {
1152 | getVarsQuestionMark = "?" + getVars;
1153 | } else {
1154 | getVarsQuestionMark = "";
1155 | }
1156 |
1157 | // Remove .compact as it interferes with .json (we got "/r/all/.compact.json" which doesn't work).
1158 | rp.subredditUrl = rp.subredditUrl.replace(/.compact/, "");
1159 | // Consolidate double slashes to avoid r/all/.compact/ -> r/all//
1160 | rp.subredditUrl = rp.subredditUrl.replace(/\/{2,}/, "/");
1161 |
1162 | var subredditName;
1163 | if (rp.subredditUrl === "") {
1164 | rp.subredditUrl = "/";
1165 | subredditName = "reddit.com" + getVarsQuestionMark;
1166 | //var options = ["/r/aww/", "/r/earthporn/", "/r/foodporn", "/r/pics"];
1167 | //rp.subredditUrl = options[Math.floor(Math.random() * options.length)];
1168 | } else {
1169 | subredditName = rp.subredditUrl + getVarsQuestionMark;
1170 | }
1171 |
1172 |
1173 | var visitSubredditUrl = embedit.redditBaseUrl + rp.subredditUrl + getVarsQuestionMark;
1174 |
1175 | // truncate and display subreddit name in the control box
1176 | var displayedSubredditName = subredditName;
1177 | // empirically tested capsize, TODO: make css rules to verify this is enough.
1178 | // it would make the "nsfw" checkbox be on its own line :(
1179 | var capsize = 50;
1180 | if (displayedSubredditName.length > capsize) {
1181 | displayedSubredditName = displayedSubredditName.substr(0, capsize) + "…";
1182 | }
1183 | $('#subredditUrl').html("sub ");
1184 |
1185 | // This `document.title` happens on page load and will later be overwritten
1186 | // by every slide that loads.
1187 | document.title = "redditP - " + displayedSubredditName;
1188 | };
1189 |
1190 | var getVars;
1191 | var getVarsQuestionMark;
1192 |
1193 | initState();
1194 | setupUrls();
1195 |
1196 | // if ever found even 1 image, don't show the error
1197 | rp.session.foundOneImage = false;
1198 |
1199 | if (rp.subredditUrl.indexOf('/imgur') === 0) {
1200 | getImgurAlbum(rp.subredditUrl);
1201 | } else {
1202 | getRedditImages();
1203 | }
1204 | });
1205 |
1206 | /*rp.flattenRedditData = function(data) {
1207 | // Parse comments, get all links
1208 | // https://www.reddit.com/r/photoshopbattles/comments/7i5ipw/psbattle_this_hyped_up_mannequin/.json?jsonp=?&
1209 |
1210 | var queue = [];
1211 | var urls = [];
1212 | if (data && data.data && data.data.children) {
1213 | children = data.data.children;
1214 | } else {
1215 | // comments of e.g. a photoshopbattles post
1216 | if (data.length > 0) {
1217 | for (var i = 0; i < data.length; i++) {
1218 | children = flattenRedditData(data[i]);
1219 | Array.prototype.push.apply(children, newChildren);
1220 | }
1221 | }
1222 | }
1223 |
1224 | var urlChildren = [];
1225 | for (var i = 0; i < children.length; i++) {
1226 | var item = children[i];
1227 | if (item.data && (item.data.url || item.data.link_url)) {
1228 | // great
1229 | urlChildren.push(item);
1230 | continue;
1231 | }
1232 |
1233 | // keep digging for more urls, remove this one
1234 | if (item.data) {
1235 | var newChildren = rp.flattenRedditData(item.data.replies);
1236 | Array.prototype.push.apply(urlChildren, newChildren);
1237 | var newChildren = flattenRedditData(item.data.children);
1238 | Array.prototype.push.apply(urlChildren, newChildren);
1239 | if (item.data.body) {
1240 | // this is a comment
1241 | console.log('body', item.body);
1242 | }
1243 | continue;
1244 | }
1245 | }
1246 |
1247 | return urls;
1248 | }*/
1249 |
1250 |
1251 | function browserNodeExport(exported, name) {
1252 | // based off of http://www.matteoagosti.com/blog/2013/02/24/writing-javascript-modules-for-both-browser-and-node/
1253 | if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
1254 | /* global module */
1255 | module.exports = exported;
1256 | } else {
1257 | if (typeof define === 'function' && define.amd) {
1258 | /* global define */
1259 | define([], function () {
1260 | return exported;
1261 | });
1262 | } else {
1263 | window[name] = exported;
1264 | }
1265 | }
1266 | }
1267 |
1268 | browserNodeExport(rp, 'rp');
1269 |
--------------------------------------------------------------------------------
/nodejs/.eslintrc.js:
--------------------------------------------------------------------------------
1 | export default {
2 | "ecmaFeatures": {
3 | "modules": true,
4 | "spread" : true,
5 | "restParams" : true
6 | },
7 | "env" : {
8 | "browser" : true,
9 | "node" : true,
10 | "es6" : true
11 | },
12 | "rules" : {
13 | "no-unused-vars" : 2,
14 | "no-undef" : 2
15 | },
16 | "parserOptions": {
17 | "sourceType": "module"
18 | }
19 | }
--------------------------------------------------------------------------------
/nodejs/app.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var app = express();
3 |
4 |
5 | app.use('/css', express.static(__dirname + '/css'));
6 | app.use('/images', express.static(__dirname + '/images'));
7 | app.use('/js', express.static(__dirname + '/js'));
8 |
9 |
10 | // Always respond with the html, except for the above exceptions.
11 | // Because it's a single page app.
12 | app.get('/*', function(req, res) {
13 | res.sendFile(__dirname + '/index.html');
14 | });
15 |
16 | var port = process.env.PORT || 8080;
17 | //app.set('port', port);
18 |
19 |
20 | app.listen(port, function () {
21 | console.log('Listening on port: ' + port);
22 | });
23 |
--------------------------------------------------------------------------------
/nodejs/test-embedit.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const embedit = require('../js/EmbedIt');
3 |
4 | const imageTests = [
5 | {
6 | src: 'https://gfycat.com/gifs/detail/EntireForkedArachnid',
7 | dst: 'EntireForkedArachnid',
8 | },
9 | {
10 | src: 'https://gfycat.com/EntireForkedArachnid',
11 | dst: 'EntireForkedArachnid',
12 | },
13 | ];
14 |
15 | const redGifTests = [
16 | {
17 | src: 'https://www.redgifs.com/watch/gaseousoblongant',
18 | dst: 'gaseousoblongant',
19 | },
20 | {
21 | src: 'https://www.redgifs.com/watch/palatableflashybantamrooster-nature',
22 | dst: 'palatableflashybantamrooster-nature',
23 | },
24 | ];
25 |
26 | function getIdTests() {
27 | for (let tst of imageTests) {
28 | var result = embedit.gfyUrlToId(tst.src);
29 | if(result !== tst.dst) {
30 | console.warn('Mismatch expected', tst.dst, result);
31 | } else {
32 | console.log('.');
33 | }
34 | }
35 |
36 | for (let tst of redGifTests) {
37 | var result = embedit.redGifUrlToId(tst.src);
38 | if(result !== tst.dst) {
39 | console.warn('Mismatch expected', tst.dst, result);
40 | } else {
41 | console.log('.');
42 | }
43 | }
44 | }
45 |
46 | async function redditJsonTests() {
47 | // const redditJson = require('../test-data/reddit.com.json');
48 | const redditJson = require('../test-data/reddit-image-v2.json');
49 | const childrenAndAfter = embedit.processRedditJson(redditJson);
50 | const children = childrenAndAfter.children.map(embedit.redditItemToPic);
51 | await fs.promises.writeFile(__dirname + '/../build/result.json', JSON.stringify(childrenAndAfter, null, 4));
52 | await fs.promises.writeFile(__dirname + '/../build/children.json', JSON.stringify(children, null, 4));
53 | }
54 |
55 | getIdTests();
56 | redditJsonTests();
57 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redditp",
3 | "version": "1.0.0",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "redditp",
9 | "version": "1.0.0",
10 | "license": "MIT",
11 | "devDependencies": {
12 | "express": "^4.18.2"
13 | }
14 | },
15 | "node_modules/accepts": {
16 | "version": "1.3.8",
17 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
18 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
19 | "dev": true,
20 | "dependencies": {
21 | "mime-types": "~2.1.34",
22 | "negotiator": "0.6.3"
23 | },
24 | "engines": {
25 | "node": ">= 0.6"
26 | }
27 | },
28 | "node_modules/array-flatten": {
29 | "version": "1.1.1",
30 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
31 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
32 | "dev": true
33 | },
34 | "node_modules/body-parser": {
35 | "version": "1.20.1",
36 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
37 | "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
38 | "dev": true,
39 | "dependencies": {
40 | "bytes": "3.1.2",
41 | "content-type": "~1.0.4",
42 | "debug": "2.6.9",
43 | "depd": "2.0.0",
44 | "destroy": "1.2.0",
45 | "http-errors": "2.0.0",
46 | "iconv-lite": "0.4.24",
47 | "on-finished": "2.4.1",
48 | "qs": "6.11.0",
49 | "raw-body": "2.5.1",
50 | "type-is": "~1.6.18",
51 | "unpipe": "1.0.0"
52 | },
53 | "engines": {
54 | "node": ">= 0.8",
55 | "npm": "1.2.8000 || >= 1.4.16"
56 | }
57 | },
58 | "node_modules/bytes": {
59 | "version": "3.1.2",
60 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
61 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
62 | "dev": true,
63 | "engines": {
64 | "node": ">= 0.8"
65 | }
66 | },
67 | "node_modules/call-bind": {
68 | "version": "1.0.5",
69 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
70 | "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
71 | "dev": true,
72 | "dependencies": {
73 | "function-bind": "^1.1.2",
74 | "get-intrinsic": "^1.2.1",
75 | "set-function-length": "^1.1.1"
76 | },
77 | "funding": {
78 | "url": "https://github.com/sponsors/ljharb"
79 | }
80 | },
81 | "node_modules/content-disposition": {
82 | "version": "0.5.4",
83 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
84 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
85 | "dev": true,
86 | "dependencies": {
87 | "safe-buffer": "5.2.1"
88 | },
89 | "engines": {
90 | "node": ">= 0.6"
91 | }
92 | },
93 | "node_modules/content-type": {
94 | "version": "1.0.5",
95 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
96 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
97 | "dev": true,
98 | "engines": {
99 | "node": ">= 0.6"
100 | }
101 | },
102 | "node_modules/cookie": {
103 | "version": "0.5.0",
104 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
105 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
106 | "dev": true,
107 | "engines": {
108 | "node": ">= 0.6"
109 | }
110 | },
111 | "node_modules/cookie-signature": {
112 | "version": "1.0.6",
113 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
114 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
115 | "dev": true
116 | },
117 | "node_modules/debug": {
118 | "version": "2.6.9",
119 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
120 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
121 | "dev": true,
122 | "dependencies": {
123 | "ms": "2.0.0"
124 | }
125 | },
126 | "node_modules/define-data-property": {
127 | "version": "1.1.1",
128 | "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
129 | "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
130 | "dev": true,
131 | "dependencies": {
132 | "get-intrinsic": "^1.2.1",
133 | "gopd": "^1.0.1",
134 | "has-property-descriptors": "^1.0.0"
135 | },
136 | "engines": {
137 | "node": ">= 0.4"
138 | }
139 | },
140 | "node_modules/depd": {
141 | "version": "2.0.0",
142 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
143 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
144 | "dev": true,
145 | "engines": {
146 | "node": ">= 0.8"
147 | }
148 | },
149 | "node_modules/destroy": {
150 | "version": "1.2.0",
151 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
152 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
153 | "dev": true,
154 | "engines": {
155 | "node": ">= 0.8",
156 | "npm": "1.2.8000 || >= 1.4.16"
157 | }
158 | },
159 | "node_modules/ee-first": {
160 | "version": "1.1.1",
161 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
162 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
163 | "dev": true
164 | },
165 | "node_modules/encodeurl": {
166 | "version": "1.0.2",
167 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
168 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
169 | "dev": true,
170 | "engines": {
171 | "node": ">= 0.8"
172 | }
173 | },
174 | "node_modules/escape-html": {
175 | "version": "1.0.3",
176 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
177 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
178 | "dev": true
179 | },
180 | "node_modules/etag": {
181 | "version": "1.8.1",
182 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
183 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
184 | "dev": true,
185 | "engines": {
186 | "node": ">= 0.6"
187 | }
188 | },
189 | "node_modules/express": {
190 | "version": "4.18.2",
191 | "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
192 | "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
193 | "dev": true,
194 | "dependencies": {
195 | "accepts": "~1.3.8",
196 | "array-flatten": "1.1.1",
197 | "body-parser": "1.20.1",
198 | "content-disposition": "0.5.4",
199 | "content-type": "~1.0.4",
200 | "cookie": "0.5.0",
201 | "cookie-signature": "1.0.6",
202 | "debug": "2.6.9",
203 | "depd": "2.0.0",
204 | "encodeurl": "~1.0.2",
205 | "escape-html": "~1.0.3",
206 | "etag": "~1.8.1",
207 | "finalhandler": "1.2.0",
208 | "fresh": "0.5.2",
209 | "http-errors": "2.0.0",
210 | "merge-descriptors": "1.0.1",
211 | "methods": "~1.1.2",
212 | "on-finished": "2.4.1",
213 | "parseurl": "~1.3.3",
214 | "path-to-regexp": "0.1.7",
215 | "proxy-addr": "~2.0.7",
216 | "qs": "6.11.0",
217 | "range-parser": "~1.2.1",
218 | "safe-buffer": "5.2.1",
219 | "send": "0.18.0",
220 | "serve-static": "1.15.0",
221 | "setprototypeof": "1.2.0",
222 | "statuses": "2.0.1",
223 | "type-is": "~1.6.18",
224 | "utils-merge": "1.0.1",
225 | "vary": "~1.1.2"
226 | },
227 | "engines": {
228 | "node": ">= 0.10.0"
229 | }
230 | },
231 | "node_modules/finalhandler": {
232 | "version": "1.2.0",
233 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
234 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
235 | "dev": true,
236 | "dependencies": {
237 | "debug": "2.6.9",
238 | "encodeurl": "~1.0.2",
239 | "escape-html": "~1.0.3",
240 | "on-finished": "2.4.1",
241 | "parseurl": "~1.3.3",
242 | "statuses": "2.0.1",
243 | "unpipe": "~1.0.0"
244 | },
245 | "engines": {
246 | "node": ">= 0.8"
247 | }
248 | },
249 | "node_modules/forwarded": {
250 | "version": "0.2.0",
251 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
252 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
253 | "dev": true,
254 | "engines": {
255 | "node": ">= 0.6"
256 | }
257 | },
258 | "node_modules/fresh": {
259 | "version": "0.5.2",
260 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
261 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
262 | "dev": true,
263 | "engines": {
264 | "node": ">= 0.6"
265 | }
266 | },
267 | "node_modules/function-bind": {
268 | "version": "1.1.2",
269 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
270 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
271 | "dev": true,
272 | "funding": {
273 | "url": "https://github.com/sponsors/ljharb"
274 | }
275 | },
276 | "node_modules/get-intrinsic": {
277 | "version": "1.2.2",
278 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
279 | "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
280 | "dev": true,
281 | "dependencies": {
282 | "function-bind": "^1.1.2",
283 | "has-proto": "^1.0.1",
284 | "has-symbols": "^1.0.3",
285 | "hasown": "^2.0.0"
286 | },
287 | "funding": {
288 | "url": "https://github.com/sponsors/ljharb"
289 | }
290 | },
291 | "node_modules/gopd": {
292 | "version": "1.0.1",
293 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
294 | "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
295 | "dev": true,
296 | "dependencies": {
297 | "get-intrinsic": "^1.1.3"
298 | },
299 | "funding": {
300 | "url": "https://github.com/sponsors/ljharb"
301 | }
302 | },
303 | "node_modules/has-property-descriptors": {
304 | "version": "1.0.1",
305 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
306 | "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
307 | "dev": true,
308 | "dependencies": {
309 | "get-intrinsic": "^1.2.2"
310 | },
311 | "funding": {
312 | "url": "https://github.com/sponsors/ljharb"
313 | }
314 | },
315 | "node_modules/has-proto": {
316 | "version": "1.0.1",
317 | "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
318 | "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
319 | "dev": true,
320 | "engines": {
321 | "node": ">= 0.4"
322 | },
323 | "funding": {
324 | "url": "https://github.com/sponsors/ljharb"
325 | }
326 | },
327 | "node_modules/has-symbols": {
328 | "version": "1.0.3",
329 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
330 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
331 | "dev": true,
332 | "engines": {
333 | "node": ">= 0.4"
334 | },
335 | "funding": {
336 | "url": "https://github.com/sponsors/ljharb"
337 | }
338 | },
339 | "node_modules/hasown": {
340 | "version": "2.0.0",
341 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
342 | "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
343 | "dev": true,
344 | "dependencies": {
345 | "function-bind": "^1.1.2"
346 | },
347 | "engines": {
348 | "node": ">= 0.4"
349 | }
350 | },
351 | "node_modules/http-errors": {
352 | "version": "2.0.0",
353 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
354 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
355 | "dev": true,
356 | "dependencies": {
357 | "depd": "2.0.0",
358 | "inherits": "2.0.4",
359 | "setprototypeof": "1.2.0",
360 | "statuses": "2.0.1",
361 | "toidentifier": "1.0.1"
362 | },
363 | "engines": {
364 | "node": ">= 0.8"
365 | }
366 | },
367 | "node_modules/iconv-lite": {
368 | "version": "0.4.24",
369 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
370 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
371 | "dev": true,
372 | "dependencies": {
373 | "safer-buffer": ">= 2.1.2 < 3"
374 | },
375 | "engines": {
376 | "node": ">=0.10.0"
377 | }
378 | },
379 | "node_modules/inherits": {
380 | "version": "2.0.4",
381 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
382 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
383 | "dev": true
384 | },
385 | "node_modules/ipaddr.js": {
386 | "version": "1.9.1",
387 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
388 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
389 | "dev": true,
390 | "engines": {
391 | "node": ">= 0.10"
392 | }
393 | },
394 | "node_modules/media-typer": {
395 | "version": "0.3.0",
396 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
397 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
398 | "dev": true,
399 | "engines": {
400 | "node": ">= 0.6"
401 | }
402 | },
403 | "node_modules/merge-descriptors": {
404 | "version": "1.0.1",
405 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
406 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
407 | "dev": true
408 | },
409 | "node_modules/methods": {
410 | "version": "1.1.2",
411 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
412 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
413 | "dev": true,
414 | "engines": {
415 | "node": ">= 0.6"
416 | }
417 | },
418 | "node_modules/mime": {
419 | "version": "1.6.0",
420 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
421 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
422 | "dev": true,
423 | "bin": {
424 | "mime": "cli.js"
425 | },
426 | "engines": {
427 | "node": ">=4"
428 | }
429 | },
430 | "node_modules/mime-db": {
431 | "version": "1.52.0",
432 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
433 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
434 | "dev": true,
435 | "engines": {
436 | "node": ">= 0.6"
437 | }
438 | },
439 | "node_modules/mime-types": {
440 | "version": "2.1.35",
441 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
442 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
443 | "dev": true,
444 | "dependencies": {
445 | "mime-db": "1.52.0"
446 | },
447 | "engines": {
448 | "node": ">= 0.6"
449 | }
450 | },
451 | "node_modules/ms": {
452 | "version": "2.0.0",
453 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
454 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
455 | "dev": true
456 | },
457 | "node_modules/negotiator": {
458 | "version": "0.6.3",
459 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
460 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
461 | "dev": true,
462 | "engines": {
463 | "node": ">= 0.6"
464 | }
465 | },
466 | "node_modules/object-inspect": {
467 | "version": "1.13.1",
468 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
469 | "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
470 | "dev": true,
471 | "funding": {
472 | "url": "https://github.com/sponsors/ljharb"
473 | }
474 | },
475 | "node_modules/on-finished": {
476 | "version": "2.4.1",
477 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
478 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
479 | "dev": true,
480 | "dependencies": {
481 | "ee-first": "1.1.1"
482 | },
483 | "engines": {
484 | "node": ">= 0.8"
485 | }
486 | },
487 | "node_modules/parseurl": {
488 | "version": "1.3.3",
489 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
490 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
491 | "dev": true,
492 | "engines": {
493 | "node": ">= 0.8"
494 | }
495 | },
496 | "node_modules/path-to-regexp": {
497 | "version": "0.1.7",
498 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
499 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
500 | "dev": true
501 | },
502 | "node_modules/proxy-addr": {
503 | "version": "2.0.7",
504 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
505 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
506 | "dev": true,
507 | "dependencies": {
508 | "forwarded": "0.2.0",
509 | "ipaddr.js": "1.9.1"
510 | },
511 | "engines": {
512 | "node": ">= 0.10"
513 | }
514 | },
515 | "node_modules/qs": {
516 | "version": "6.11.0",
517 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
518 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
519 | "dev": true,
520 | "dependencies": {
521 | "side-channel": "^1.0.4"
522 | },
523 | "engines": {
524 | "node": ">=0.6"
525 | },
526 | "funding": {
527 | "url": "https://github.com/sponsors/ljharb"
528 | }
529 | },
530 | "node_modules/range-parser": {
531 | "version": "1.2.1",
532 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
533 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
534 | "dev": true,
535 | "engines": {
536 | "node": ">= 0.6"
537 | }
538 | },
539 | "node_modules/raw-body": {
540 | "version": "2.5.1",
541 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
542 | "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
543 | "dev": true,
544 | "dependencies": {
545 | "bytes": "3.1.2",
546 | "http-errors": "2.0.0",
547 | "iconv-lite": "0.4.24",
548 | "unpipe": "1.0.0"
549 | },
550 | "engines": {
551 | "node": ">= 0.8"
552 | }
553 | },
554 | "node_modules/safe-buffer": {
555 | "version": "5.2.1",
556 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
557 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
558 | "dev": true,
559 | "funding": [
560 | {
561 | "type": "github",
562 | "url": "https://github.com/sponsors/feross"
563 | },
564 | {
565 | "type": "patreon",
566 | "url": "https://www.patreon.com/feross"
567 | },
568 | {
569 | "type": "consulting",
570 | "url": "https://feross.org/support"
571 | }
572 | ]
573 | },
574 | "node_modules/safer-buffer": {
575 | "version": "2.1.2",
576 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
577 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
578 | "dev": true
579 | },
580 | "node_modules/send": {
581 | "version": "0.18.0",
582 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
583 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
584 | "dev": true,
585 | "dependencies": {
586 | "debug": "2.6.9",
587 | "depd": "2.0.0",
588 | "destroy": "1.2.0",
589 | "encodeurl": "~1.0.2",
590 | "escape-html": "~1.0.3",
591 | "etag": "~1.8.1",
592 | "fresh": "0.5.2",
593 | "http-errors": "2.0.0",
594 | "mime": "1.6.0",
595 | "ms": "2.1.3",
596 | "on-finished": "2.4.1",
597 | "range-parser": "~1.2.1",
598 | "statuses": "2.0.1"
599 | },
600 | "engines": {
601 | "node": ">= 0.8.0"
602 | }
603 | },
604 | "node_modules/send/node_modules/ms": {
605 | "version": "2.1.3",
606 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
607 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
608 | "dev": true
609 | },
610 | "node_modules/serve-static": {
611 | "version": "1.15.0",
612 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
613 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
614 | "dev": true,
615 | "dependencies": {
616 | "encodeurl": "~1.0.2",
617 | "escape-html": "~1.0.3",
618 | "parseurl": "~1.3.3",
619 | "send": "0.18.0"
620 | },
621 | "engines": {
622 | "node": ">= 0.8.0"
623 | }
624 | },
625 | "node_modules/set-function-length": {
626 | "version": "1.1.1",
627 | "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
628 | "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
629 | "dev": true,
630 | "dependencies": {
631 | "define-data-property": "^1.1.1",
632 | "get-intrinsic": "^1.2.1",
633 | "gopd": "^1.0.1",
634 | "has-property-descriptors": "^1.0.0"
635 | },
636 | "engines": {
637 | "node": ">= 0.4"
638 | }
639 | },
640 | "node_modules/setprototypeof": {
641 | "version": "1.2.0",
642 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
643 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
644 | "dev": true
645 | },
646 | "node_modules/side-channel": {
647 | "version": "1.0.4",
648 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
649 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
650 | "dev": true,
651 | "dependencies": {
652 | "call-bind": "^1.0.0",
653 | "get-intrinsic": "^1.0.2",
654 | "object-inspect": "^1.9.0"
655 | },
656 | "funding": {
657 | "url": "https://github.com/sponsors/ljharb"
658 | }
659 | },
660 | "node_modules/statuses": {
661 | "version": "2.0.1",
662 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
663 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
664 | "dev": true,
665 | "engines": {
666 | "node": ">= 0.8"
667 | }
668 | },
669 | "node_modules/toidentifier": {
670 | "version": "1.0.1",
671 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
672 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
673 | "dev": true,
674 | "engines": {
675 | "node": ">=0.6"
676 | }
677 | },
678 | "node_modules/type-is": {
679 | "version": "1.6.18",
680 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
681 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
682 | "dev": true,
683 | "dependencies": {
684 | "media-typer": "0.3.0",
685 | "mime-types": "~2.1.24"
686 | },
687 | "engines": {
688 | "node": ">= 0.6"
689 | }
690 | },
691 | "node_modules/unpipe": {
692 | "version": "1.0.0",
693 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
694 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
695 | "dev": true,
696 | "engines": {
697 | "node": ">= 0.8"
698 | }
699 | },
700 | "node_modules/utils-merge": {
701 | "version": "1.0.1",
702 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
703 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
704 | "dev": true,
705 | "engines": {
706 | "node": ">= 0.4.0"
707 | }
708 | },
709 | "node_modules/vary": {
710 | "version": "1.1.2",
711 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
712 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
713 | "dev": true,
714 | "engines": {
715 | "node": ">= 0.8"
716 | }
717 | }
718 | },
719 | "dependencies": {
720 | "accepts": {
721 | "version": "1.3.8",
722 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
723 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
724 | "dev": true,
725 | "requires": {
726 | "mime-types": "~2.1.34",
727 | "negotiator": "0.6.3"
728 | }
729 | },
730 | "array-flatten": {
731 | "version": "1.1.1",
732 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
733 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
734 | "dev": true
735 | },
736 | "body-parser": {
737 | "version": "1.20.1",
738 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
739 | "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
740 | "dev": true,
741 | "requires": {
742 | "bytes": "3.1.2",
743 | "content-type": "~1.0.4",
744 | "debug": "2.6.9",
745 | "depd": "2.0.0",
746 | "destroy": "1.2.0",
747 | "http-errors": "2.0.0",
748 | "iconv-lite": "0.4.24",
749 | "on-finished": "2.4.1",
750 | "qs": "6.11.0",
751 | "raw-body": "2.5.1",
752 | "type-is": "~1.6.18",
753 | "unpipe": "1.0.0"
754 | }
755 | },
756 | "bytes": {
757 | "version": "3.1.2",
758 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
759 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
760 | "dev": true
761 | },
762 | "call-bind": {
763 | "version": "1.0.5",
764 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
765 | "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
766 | "dev": true,
767 | "requires": {
768 | "function-bind": "^1.1.2",
769 | "get-intrinsic": "^1.2.1",
770 | "set-function-length": "^1.1.1"
771 | }
772 | },
773 | "content-disposition": {
774 | "version": "0.5.4",
775 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
776 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
777 | "dev": true,
778 | "requires": {
779 | "safe-buffer": "5.2.1"
780 | }
781 | },
782 | "content-type": {
783 | "version": "1.0.5",
784 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
785 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
786 | "dev": true
787 | },
788 | "cookie": {
789 | "version": "0.5.0",
790 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
791 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
792 | "dev": true
793 | },
794 | "cookie-signature": {
795 | "version": "1.0.6",
796 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
797 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
798 | "dev": true
799 | },
800 | "debug": {
801 | "version": "2.6.9",
802 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
803 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
804 | "dev": true,
805 | "requires": {
806 | "ms": "2.0.0"
807 | }
808 | },
809 | "define-data-property": {
810 | "version": "1.1.1",
811 | "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
812 | "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
813 | "dev": true,
814 | "requires": {
815 | "get-intrinsic": "^1.2.1",
816 | "gopd": "^1.0.1",
817 | "has-property-descriptors": "^1.0.0"
818 | }
819 | },
820 | "depd": {
821 | "version": "2.0.0",
822 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
823 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
824 | "dev": true
825 | },
826 | "destroy": {
827 | "version": "1.2.0",
828 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
829 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
830 | "dev": true
831 | },
832 | "ee-first": {
833 | "version": "1.1.1",
834 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
835 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
836 | "dev": true
837 | },
838 | "encodeurl": {
839 | "version": "1.0.2",
840 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
841 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
842 | "dev": true
843 | },
844 | "escape-html": {
845 | "version": "1.0.3",
846 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
847 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
848 | "dev": true
849 | },
850 | "etag": {
851 | "version": "1.8.1",
852 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
853 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
854 | "dev": true
855 | },
856 | "express": {
857 | "version": "4.18.2",
858 | "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
859 | "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
860 | "dev": true,
861 | "requires": {
862 | "accepts": "~1.3.8",
863 | "array-flatten": "1.1.1",
864 | "body-parser": "1.20.1",
865 | "content-disposition": "0.5.4",
866 | "content-type": "~1.0.4",
867 | "cookie": "0.5.0",
868 | "cookie-signature": "1.0.6",
869 | "debug": "2.6.9",
870 | "depd": "2.0.0",
871 | "encodeurl": "~1.0.2",
872 | "escape-html": "~1.0.3",
873 | "etag": "~1.8.1",
874 | "finalhandler": "1.2.0",
875 | "fresh": "0.5.2",
876 | "http-errors": "2.0.0",
877 | "merge-descriptors": "1.0.1",
878 | "methods": "~1.1.2",
879 | "on-finished": "2.4.1",
880 | "parseurl": "~1.3.3",
881 | "path-to-regexp": "0.1.7",
882 | "proxy-addr": "~2.0.7",
883 | "qs": "6.11.0",
884 | "range-parser": "~1.2.1",
885 | "safe-buffer": "5.2.1",
886 | "send": "0.18.0",
887 | "serve-static": "1.15.0",
888 | "setprototypeof": "1.2.0",
889 | "statuses": "2.0.1",
890 | "type-is": "~1.6.18",
891 | "utils-merge": "1.0.1",
892 | "vary": "~1.1.2"
893 | }
894 | },
895 | "finalhandler": {
896 | "version": "1.2.0",
897 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
898 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
899 | "dev": true,
900 | "requires": {
901 | "debug": "2.6.9",
902 | "encodeurl": "~1.0.2",
903 | "escape-html": "~1.0.3",
904 | "on-finished": "2.4.1",
905 | "parseurl": "~1.3.3",
906 | "statuses": "2.0.1",
907 | "unpipe": "~1.0.0"
908 | }
909 | },
910 | "forwarded": {
911 | "version": "0.2.0",
912 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
913 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
914 | "dev": true
915 | },
916 | "fresh": {
917 | "version": "0.5.2",
918 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
919 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
920 | "dev": true
921 | },
922 | "function-bind": {
923 | "version": "1.1.2",
924 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
925 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
926 | "dev": true
927 | },
928 | "get-intrinsic": {
929 | "version": "1.2.2",
930 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
931 | "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
932 | "dev": true,
933 | "requires": {
934 | "function-bind": "^1.1.2",
935 | "has-proto": "^1.0.1",
936 | "has-symbols": "^1.0.3",
937 | "hasown": "^2.0.0"
938 | }
939 | },
940 | "gopd": {
941 | "version": "1.0.1",
942 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
943 | "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
944 | "dev": true,
945 | "requires": {
946 | "get-intrinsic": "^1.1.3"
947 | }
948 | },
949 | "has-property-descriptors": {
950 | "version": "1.0.1",
951 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
952 | "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
953 | "dev": true,
954 | "requires": {
955 | "get-intrinsic": "^1.2.2"
956 | }
957 | },
958 | "has-proto": {
959 | "version": "1.0.1",
960 | "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
961 | "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
962 | "dev": true
963 | },
964 | "has-symbols": {
965 | "version": "1.0.3",
966 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
967 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
968 | "dev": true
969 | },
970 | "hasown": {
971 | "version": "2.0.0",
972 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
973 | "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
974 | "dev": true,
975 | "requires": {
976 | "function-bind": "^1.1.2"
977 | }
978 | },
979 | "http-errors": {
980 | "version": "2.0.0",
981 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
982 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
983 | "dev": true,
984 | "requires": {
985 | "depd": "2.0.0",
986 | "inherits": "2.0.4",
987 | "setprototypeof": "1.2.0",
988 | "statuses": "2.0.1",
989 | "toidentifier": "1.0.1"
990 | }
991 | },
992 | "iconv-lite": {
993 | "version": "0.4.24",
994 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
995 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
996 | "dev": true,
997 | "requires": {
998 | "safer-buffer": ">= 2.1.2 < 3"
999 | }
1000 | },
1001 | "inherits": {
1002 | "version": "2.0.4",
1003 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
1004 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
1005 | "dev": true
1006 | },
1007 | "ipaddr.js": {
1008 | "version": "1.9.1",
1009 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
1010 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
1011 | "dev": true
1012 | },
1013 | "media-typer": {
1014 | "version": "0.3.0",
1015 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
1016 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
1017 | "dev": true
1018 | },
1019 | "merge-descriptors": {
1020 | "version": "1.0.1",
1021 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
1022 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
1023 | "dev": true
1024 | },
1025 | "methods": {
1026 | "version": "1.1.2",
1027 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
1028 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
1029 | "dev": true
1030 | },
1031 | "mime": {
1032 | "version": "1.6.0",
1033 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
1034 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
1035 | "dev": true
1036 | },
1037 | "mime-db": {
1038 | "version": "1.52.0",
1039 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
1040 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
1041 | "dev": true
1042 | },
1043 | "mime-types": {
1044 | "version": "2.1.35",
1045 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
1046 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
1047 | "dev": true,
1048 | "requires": {
1049 | "mime-db": "1.52.0"
1050 | }
1051 | },
1052 | "ms": {
1053 | "version": "2.0.0",
1054 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1055 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
1056 | "dev": true
1057 | },
1058 | "negotiator": {
1059 | "version": "0.6.3",
1060 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
1061 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
1062 | "dev": true
1063 | },
1064 | "object-inspect": {
1065 | "version": "1.13.1",
1066 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
1067 | "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
1068 | "dev": true
1069 | },
1070 | "on-finished": {
1071 | "version": "2.4.1",
1072 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
1073 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
1074 | "dev": true,
1075 | "requires": {
1076 | "ee-first": "1.1.1"
1077 | }
1078 | },
1079 | "parseurl": {
1080 | "version": "1.3.3",
1081 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
1082 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
1083 | "dev": true
1084 | },
1085 | "path-to-regexp": {
1086 | "version": "0.1.7",
1087 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
1088 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
1089 | "dev": true
1090 | },
1091 | "proxy-addr": {
1092 | "version": "2.0.7",
1093 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
1094 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
1095 | "dev": true,
1096 | "requires": {
1097 | "forwarded": "0.2.0",
1098 | "ipaddr.js": "1.9.1"
1099 | }
1100 | },
1101 | "qs": {
1102 | "version": "6.11.0",
1103 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
1104 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
1105 | "dev": true,
1106 | "requires": {
1107 | "side-channel": "^1.0.4"
1108 | }
1109 | },
1110 | "range-parser": {
1111 | "version": "1.2.1",
1112 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
1113 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
1114 | "dev": true
1115 | },
1116 | "raw-body": {
1117 | "version": "2.5.1",
1118 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
1119 | "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
1120 | "dev": true,
1121 | "requires": {
1122 | "bytes": "3.1.2",
1123 | "http-errors": "2.0.0",
1124 | "iconv-lite": "0.4.24",
1125 | "unpipe": "1.0.0"
1126 | }
1127 | },
1128 | "safe-buffer": {
1129 | "version": "5.2.1",
1130 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
1131 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
1132 | "dev": true
1133 | },
1134 | "safer-buffer": {
1135 | "version": "2.1.2",
1136 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1137 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
1138 | "dev": true
1139 | },
1140 | "send": {
1141 | "version": "0.18.0",
1142 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
1143 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
1144 | "dev": true,
1145 | "requires": {
1146 | "debug": "2.6.9",
1147 | "depd": "2.0.0",
1148 | "destroy": "1.2.0",
1149 | "encodeurl": "~1.0.2",
1150 | "escape-html": "~1.0.3",
1151 | "etag": "~1.8.1",
1152 | "fresh": "0.5.2",
1153 | "http-errors": "2.0.0",
1154 | "mime": "1.6.0",
1155 | "ms": "2.1.3",
1156 | "on-finished": "2.4.1",
1157 | "range-parser": "~1.2.1",
1158 | "statuses": "2.0.1"
1159 | },
1160 | "dependencies": {
1161 | "ms": {
1162 | "version": "2.1.3",
1163 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1164 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1165 | "dev": true
1166 | }
1167 | }
1168 | },
1169 | "serve-static": {
1170 | "version": "1.15.0",
1171 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
1172 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
1173 | "dev": true,
1174 | "requires": {
1175 | "encodeurl": "~1.0.2",
1176 | "escape-html": "~1.0.3",
1177 | "parseurl": "~1.3.3",
1178 | "send": "0.18.0"
1179 | }
1180 | },
1181 | "set-function-length": {
1182 | "version": "1.1.1",
1183 | "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
1184 | "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
1185 | "dev": true,
1186 | "requires": {
1187 | "define-data-property": "^1.1.1",
1188 | "get-intrinsic": "^1.2.1",
1189 | "gopd": "^1.0.1",
1190 | "has-property-descriptors": "^1.0.0"
1191 | }
1192 | },
1193 | "setprototypeof": {
1194 | "version": "1.2.0",
1195 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
1196 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
1197 | "dev": true
1198 | },
1199 | "side-channel": {
1200 | "version": "1.0.4",
1201 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
1202 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
1203 | "dev": true,
1204 | "requires": {
1205 | "call-bind": "^1.0.0",
1206 | "get-intrinsic": "^1.0.2",
1207 | "object-inspect": "^1.9.0"
1208 | }
1209 | },
1210 | "statuses": {
1211 | "version": "2.0.1",
1212 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
1213 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
1214 | "dev": true
1215 | },
1216 | "toidentifier": {
1217 | "version": "1.0.1",
1218 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
1219 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
1220 | "dev": true
1221 | },
1222 | "type-is": {
1223 | "version": "1.6.18",
1224 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
1225 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
1226 | "dev": true,
1227 | "requires": {
1228 | "media-typer": "0.3.0",
1229 | "mime-types": "~2.1.24"
1230 | }
1231 | },
1232 | "unpipe": {
1233 | "version": "1.0.0",
1234 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
1235 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
1236 | "dev": true
1237 | },
1238 | "utils-merge": {
1239 | "version": "1.0.1",
1240 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
1241 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
1242 | "dev": true
1243 | },
1244 | "vary": {
1245 | "version": "1.1.2",
1246 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
1247 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
1248 | "dev": true
1249 | }
1250 | }
1251 | }
1252 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redditp",
3 | "version": "1.0.0",
4 | "description": "Convert reddit urls into slideshows",
5 | "main": "index.html",
6 | "devDependencies": {
7 | "express": "^4.18.2"
8 | },
9 | "scripts": {
10 | "start": "node server.js",
11 | "debug": "node --inspect server.js",
12 | "test": "node ./nodejs/test-embedit.js"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "git+https://github.com/ubershmekel/redditp.git"
17 | },
18 | "keywords": [
19 | "reddit",
20 | "redditp",
21 | "slideshow",
22 | "images"
23 | ],
24 | "author": "Yuval Greenfield",
25 | "license": "MIT",
26 | "bugs": {
27 | "url": "https://github.com/ubershmekel/redditp/issues"
28 | },
29 | "homepage": "https://github.com/ubershmekel/redditp#readme"
30 | }
31 |
--------------------------------------------------------------------------------
/redditp-redirect/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 | services:
3 | nginxredirect:
4 | image: schmunk42/nginx-redirect
5 | environment:
6 | - SERVER_REDIRECT=redditp.com
7 | labels:
8 | - "traefik.port=80"
9 | - "traefik.enable=true"
10 | - "traefik.frontend.rule=Host:www.redditp.com"
11 | - "traefik.docker.network=reverseproxy_default"
12 | networks:
13 | - "reverseproxy_default"
14 | restart: always
15 | logging:
16 | options:
17 | max-size: "50m"
18 |
19 | networks:
20 | reverseproxy_default:
21 | external:
22 | name: reverseproxy_default
23 |
24 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | // This node server is not required, you can use index.html directly off
2 | // the file system or a static host but you just have to use a question mark e.g.:
3 | // http://localhost:8080/index.html?/r/gifs
4 |
5 | var http = require('http');
6 | var path = require('path');
7 |
8 | var express = require('express');
9 |
10 | var app = express();
11 |
12 | app.set('port', process.env.PORT || 8080);
13 |
14 | const publicFolder = [
15 | '.well-known',
16 | 'css',
17 | 'images',
18 | 'js'
19 | ];
20 |
21 | for (let name of publicFolder) {
22 | app.use('/' + name, express.static(path.join(__dirname, name)));
23 | }
24 |
25 | var server = http.createServer(app);
26 |
27 | app.get('/*', function (req, res) {
28 | res.sendFile(path.join(__dirname, 'index.html'));
29 | });
30 |
31 | server.listen(app.get('port'), function () {
32 | console.log("Web server listening at: http://localhost:" + app.get('port'));
33 | });
--------------------------------------------------------------------------------
/start.cmd:
--------------------------------------------------------------------------------
1 | http-server
--------------------------------------------------------------------------------
/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "rewrites": [
3 | { "source": "/u/stellaraeee/submitted", "destination": "/dmca.html" },
4 | { "source": "/r/(.*)", "destination": "/index.html" },
5 | { "source": "/u/(.*)", "destination": "/index.html" },
6 | { "source": "/user/(.*)", "destination": "/index.html" },
7 | { "source": "/domain/(.*)", "destination": "/index.html" },
8 | { "source": "/search/(.*)", "destination": "/index.html" },
9 | { "source": "/search", "destination": "/index.html" }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------