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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 | };
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/js/script.js:
--------------------------------------------------------------------------------
1 | /*
2 | You can save the HTML file and use it locally btw like so:
3 | file:///wherever/index.html?/r/aww
4 |
5 | Check out the source at:
6 | https://github.com/ubershmekel/redditp
7 | */
8 |
9 | // TODO: refactor all the globals to use the rp object's namespace.
10 | var rp = {};
11 | var galleryOffset = 0
12 | rp.settings = {
13 | debug: true,
14 | // Speed of the animation
15 | animationSpeed: 100,
16 | shouldAutoNextSlide: true,
17 | timeToNextSlide: 6 * 1000,
18 | cookieDays: 300,
19 | nsfw: true,
20 | sound: false
21 | };
22 |
23 | rp.session = {
24 | // 0-based index to set which picture to show first
25 | // init to -1 until the first image is loaded
26 | activeIndex: -1,
27 |
28 | // Variable to store if the animation is playing or not
29 | isAnimating: false,
30 |
31 | // Id of timer
32 | nextSlideTimeoutId: null,
33 |
34 | // Reddit filter "After"
35 | after: "",
36 |
37 | foundOneImage: false,
38 |
39 | loadingNextImages: false
40 | };
41 |
42 | // Variable to store the images we need to set as background
43 | // which also includes some text and url's.
44 | rp.photos = [];
45 |
46 | // maybe checkout http://engineeredweb.com/blog/09/12/preloading-images-jquery-and-javascript/ for implementing the old precache
47 | rp.cache = {};
48 |
49 | function reportError(errMessage) {
50 | if (window.errorHandler && window.errorHandler.report) {
51 | window.errorHandler.report(new Error(errMessage));
52 | } else {
53 | console.log('No error handler yet: ' + errMessage);
54 | }
55 | toastr.error(errMessage + ', please alert ubershmekel on github ');
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 |
--------------------------------------------------------------------------------