├── LICENSE
├── README.md
├── css
├── facebook.png
├── link-btn.png
├── main.css
├── play-btn.png
└── whatsapp.png
├── favicon.png
├── index.html
├── js
├── aux.js
└── main.js
├── manifest.json
└── sw.js
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Victor Ribeiro
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Memes
2 |
3 | A PWA app with memes, videos and gifs in JavaScript. (Reddit parser)
4 |
5 | Live version [here](https://victorribeiro.com/memes)
6 |
7 | # About
8 |
9 | A PWA app that parses [Reddit](https://www.reddit.com) for content, without ads or comments.
10 | Since the app runs on the client side, there's virtually no costs with servers (trafic and storage).
11 |
12 | # How to add subs
13 |
14 | Modify the *subs* variable on the main.js file to add or remove any sub you want.
15 |
16 | ```javascript
17 | let subs = [
18 | "funny",
19 | "dankmemes",
20 | "videos",
21 | "gifs"
22 | ];
23 | ```
24 |
25 | # Disclaimer
26 |
27 | I do not own or host any of the content featured on the app and I do not share from the content creator's opinion.
28 | All of the content comes from Reddit and it's community.
29 | I'm not responsable for any offensive material that you'll eventually see on the app.
30 |
--------------------------------------------------------------------------------
/css/facebook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/victorqribeiro/memes/10990534cf209c01b3da02e8d17c48dfba41cf53/css/facebook.png
--------------------------------------------------------------------------------
/css/link-btn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/victorqribeiro/memes/10990534cf209c01b3da02e8d17c48dfba41cf53/css/link-btn.png
--------------------------------------------------------------------------------
/css/main.css:
--------------------------------------------------------------------------------
1 | #content{
2 | position: relative;
3 | }
4 | button{
5 | outline:none;
6 | }
7 |
8 | iframe{
9 | border:0;
10 | width: 100%;
11 | }
12 |
13 | .video{
14 | position: absolute;
15 | top: 50%;
16 | left: 50%;
17 | transform: translate(-50%, -50%);
18 | -ms-transform: translate(-50%, -50%);
19 | text-align: center;
20 | background: url('play-btn.png') no-repeat;
21 | background-size: cover;
22 | width: 64px;
23 | height: 64px;
24 | }
25 |
26 | .link{
27 | position: absolute;
28 | top: 50%;
29 | left: 50%;
30 | transform: translate(-50%, -50%);
31 | -ms-transform: translate(-50%, -50%);
32 | text-align: center;
33 | background: url('link-btn.png') no-repeat;
34 | background-size: cover;
35 | width: 64px;
36 | height: 64px;
37 | }
38 |
39 | .whatsapp{
40 | float: right;
41 | background: url('whatsapp.png') no-repeat;
42 | background-size: cover;
43 | max-width: 48px;
44 | width: 48px;
45 | height: 48px;
46 | }
47 |
48 | .facebook{
49 | float: right;
50 | background: url('facebook.png') no-repeat;
51 | background-size: cover;
52 | max-width: 48px;
53 | width: 48px;
54 | height: 48px;
55 | }
56 |
57 |
58 | @media (min-width: 480px) {
59 | body, html{
60 | margin: 0;
61 | padding: 0;
62 | font-family: Arial;
63 | font-size: 1.1em;
64 | }
65 |
66 | #main{
67 | display: flex;
68 | align-items: center;
69 | justify-content: center;
70 | flex-direction: column;
71 | }
72 |
73 | .post{
74 | width: 50vw;
75 | padding: 2em;
76 | margin: 2em;
77 | border: solid 1px rgb(200,200,200);
78 | border-radius: 1em;
79 | }
80 |
81 | .post #title{
82 | text-align: justify;
83 | border-bottom: solid 1px rgb(200,200,200);
84 | }
85 |
86 | .post #content img{
87 | width: 50vw;
88 | }
89 |
90 | .post #content video{
91 | width: 50vw;
92 | }
93 |
94 | #footer{
95 | display: flex;
96 | align-items: center;
97 | justify-content: space-around;
98 | margin: 2em;
99 | }
100 |
101 | .arrow{
102 | width: 20vw;
103 | text-align: center;
104 | font-family: verdana;
105 | font-weight: bold;
106 | font-size: 3em;
107 | border: solid 1px rgb(200,200,200);
108 | border-radius: 2em;
109 | line-height: 2em;
110 | cursor: pointer;
111 | }
112 |
113 | .whastlink{
114 | display: none;
115 | }
116 |
117 | .facelink{
118 | display: block;
119 | }
120 | }
121 |
122 | @media (max-width: 480px) {
123 | body, html{
124 | font-family: Arial;
125 | font-size: 1em;
126 | }
127 |
128 | #main{
129 | display: flex;
130 | align-items: center;
131 | justify-content: center;
132 | flex-direction: column;
133 | }
134 |
135 | .post{
136 | width: 100%;
137 | margin-bottom: 2em;
138 | border-bottom: solid 1px rgb(200,200,200);
139 | }
140 |
141 | .post #title{
142 | text-align: justify;
143 | background-color: rgb(250,250,250);
144 | padding: 0.3em;
145 | border-bottom: solid 1px rgb(230,230,230);
146 | }
147 |
148 | .post #content img{
149 | width: 100%;
150 | margin-bottom: 2em;
151 | }
152 |
153 | .post #content video{
154 | width: 100%;
155 | }
156 |
157 | #footer{
158 | display: flex;
159 | align-items: center;
160 | justify-content: space-around;
161 | }
162 |
163 | .arrow{
164 | width: 30vw;
165 | text-align: center;
166 | font-family: verdana;
167 | font-weight: bold;
168 | font-size: 3em;
169 | border: solid 1px rgba(0,0,0,0.5);
170 | border-radius: 2em;
171 | line-height: 2em;
172 | cursor: pointer;
173 | }
174 |
175 | .whastlink{
176 | display: block;
177 | }
178 |
179 | .facelink{
180 | display: block;
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/css/play-btn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/victorqribeiro/memes/10990534cf209c01b3da02e8d17c48dfba41cf53/css/play-btn.png
--------------------------------------------------------------------------------
/css/whatsapp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/victorqribeiro/memes/10990534cf209c01b3da02e8d17c48dfba41cf53/css/whatsapp.png
--------------------------------------------------------------------------------
/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/victorqribeiro/memes/10990534cf209c01b3da02e8d17c48dfba41cf53/favicon.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Memes
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | Loading...
29 |
30 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/js/aux.js:
--------------------------------------------------------------------------------
1 | if('serviceWorker' in navigator) {
2 | navigator.serviceWorker
3 | .register('/memes/sw.js', {scope: './'})
4 | .then(response => response)
5 | .catch(reason => reason);
6 | }
7 |
8 | let deferredPrompt;
9 | const addBtn = document.createElement('button');
10 |
11 | window.addEventListener('beforeinstallprompt', (e) => {
12 | e.preventDefault();
13 | deferredPrompt = e;
14 | addBtn.style.display = 'block';
15 | addBtn.addEventListener('click', (e) => {
16 | addBtn.style.display = 'none';
17 | deferredPrompt.prompt();
18 | deferredPrompt.userChoice.then((choiceResult) => {
19 | deferredPrompt = null;
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/js/main.js:
--------------------------------------------------------------------------------
1 | window.onload = function(){
2 | /*
3 | let subs = [
4 | "funny",
5 | "dankmemes",
6 | "videos",
7 | "gifs"
8 | ];
9 | */
10 | let subs = [
11 | "dankmemes",
12 | "funny"
13 | ];
14 |
15 | let state = {
16 | page: 25,
17 | baseUrl: "https://www.reddit.com/r/"+subs.join('+')+".json",
18 | currentUrl: "https://www.reddit.com/r/"+subs.join('+')+".json",
19 | afterUrl: "",
20 | beforeUrl: "",
21 | scroll: 0
22 | }
23 |
24 | getLink(state.currentUrl);
25 |
26 | function getLink(l){
27 | fetch(l).
28 | then( r => r.json() ).
29 | then( d => getData(d) ).
30 | catch( e => console.error('Error: '+e.message) );
31 | }
32 |
33 | function createPost(p){
34 | if( p.distinguished == "moderator" )
35 | return
36 | p.url = p.url.replace(/\&/g,'&').replace(/\?/g,'?');
37 | p.thumbnail = p.thumbnail.replace(/\&/g,'&').replace(/\?/g,'?');
38 | if( p.thumbnail.search(/\.jpg/i) != -1
39 | || p.thumbnail.search(/\.png/i) != -1
40 | || p.thumbnail.search(/\.gif/i) != -1 ){
41 | try{
42 | let main = document.getElementById('main');
43 | let post = document.createElement('div');
44 | post.id = p.id;
45 | post.className = 'post';
46 | let title = document.createElement('div');
47 | title.id = 'title';
48 | title.innerText = p.title.replace(/\&/g,'&').replace(/\?/g,'?');;
49 |
50 | let content = document.createElement('div');
51 | content.id = 'content';
52 |
53 | let whatslink = document.createElement('a');
54 | whatslink.classList.add('whastlink');
55 | whatslink.title = whatslink.alt = "Mandar via Whatsapp";
56 |
57 | let whatsapp = document.createElement('div');
58 | whatsapp.classList.add('whatsapp');
59 |
60 | let facelink = document.createElement('a');
61 | facelink.classList.add('facelink');
62 | facelink.target = "_tab";
63 | facelink.title = whatslink.alt = "Mandar via Facebook";
64 |
65 | let facebook = document.createElement('div');
66 | facebook.classList.add('facebook');
67 |
68 | if( p.url.search('.gifv') != -1 ){
69 |
70 | let url = p.url.replace('.gifv','.mp4');
71 | let video = document.createElement('video');
72 | let source = document.createElement('source');
73 | let videoclass = document.createElement('div');
74 | videoclass.classList.add('video');
75 | videoclass.title = "Video";
76 | source.src = url;
77 | whatslink.href = "whatsapp://send?text="+url;
78 | facelink.href = "http://www.facebook.com/sharer.php?u="+encodeURIComponent(url)+"&t=victorribeiro.com/memes";
79 | source.type = "video/mp4";
80 | video.preload="auto";
81 | video.loop = true;
82 | video.appendChild( source );
83 |
84 | content.addEventListener("click", e => play(content));
85 | content.appendChild( video );
86 | content.appendChild( videoclass );
87 |
88 | }else if( p.url.search(/\.webm/i) != -1 ){
89 |
90 | let video = document.createElement('video');
91 | let source = document.createElement('source');
92 | let videoclass = document.createElement('div');
93 | videoclass.classList.add('video');
94 | videoclass.title = "Video";
95 | source.src = p.url;
96 | whatslink.href = "whatsapp://send?text="+p.url;
97 | facelink.href = "http://www.facebook.com/sharer.php?u="+encodeURIComponent(p.url)+"&t=victorribeiro.com/memes";
98 | source.type = "video/webm";
99 | video.preload="auto";
100 | video.loop = true;
101 | video.appendChild( source );
102 |
103 | content.addEventListener("click", e => play(content));
104 | content.appendChild( video );
105 | content.appendChild( videoclass );
106 |
107 | }else if(p.url.search(/\.jpg/i) != -1 || p.url.search(/\.png/i) != -1 || p.url.search(/\.gif/i) != -1) {
108 |
109 | let img = new Image();
110 | img.src = p.url;
111 | whatslink.href = "whatsapp://send?text="+p.url;
112 | facelink.href = "http://www.facebook.com/sharer.php?u="+encodeURIComponent(p.url)+"&t=victorribeiro.com/memes";
113 | content.appendChild( img );
114 |
115 | }else if(p.url.search(/youtube\.com\/watch/i) != -1){ https://youtu.be/BskKbs_BHNg"
116 |
117 | p.url = p.url.replace('watch?v=','embed/').replace(/\&.*/, '');
118 |
119 | let iframe = document.createElement('iframe');
120 | iframe.width = 420;
121 | iframe.height = 315;
122 | iframe.src = p.url;
123 |
124 | whatslink.href = "whatsapp://send?text="+p.url;
125 | facelink.href = "http://www.facebook.com/sharer.php?u="+encodeURIComponent(p.url)+"&t=victorribeiro.com/memes";
126 |
127 | content.appendChild( iframe );
128 | }else if(p.url.search(/youtu\.be\//i) != -1){
129 |
130 | let url = p.url.replace('youtu.be/','youtube.com/embed/');
131 |
132 | let iframe = document.createElement('iframe');
133 | iframe.width = 420;
134 | iframe.height = 315;
135 | iframe.src = url;
136 |
137 | whatslink.href = "whatsapp://send?text="+p.url;
138 | facelink.href = "http://www.facebook.com/sharer.php?u="+encodeURIComponent(p.url)+"&t=victorribeiro.com/memes";
139 |
140 | content.appendChild( iframe );
141 | }else if( p.url.search(/gfycat\.com\//i) != -1) {
142 |
143 | let url = p.url.replace('gfycat.com/','gfycat.com/ifr/');
144 | let iframe = document.createElement('iframe');
145 | iframe.width = 420;
146 | iframe.height = 315;
147 | iframe.src = url;
148 | whatslink.href = "whatsapp://send?text="+p.url;
149 | facelink.href = "http://www.facebook.com/sharer.php?u="+encodeURIComponent(p.url)+"&t=victorribeiro.com/memes";
150 | content.appendChild( iframe );
151 |
152 | }else{
153 | let url = p.url.replace(/\&/g,'&').replace(/\?/g,'?');
154 | let link = document.createElement('a');
155 | link.href = url;
156 | whatslink.href = "whatsapp://send?text="+url;
157 | facelink.href = "http://www.facebook.com/sharer.php?u="+encodeURIComponent(url)+"&t=victorribeiro.com/memes";
158 | link.target = "_tab";
159 |
160 | let linkclass = document.createElement('div');
161 | linkclass.classList.add('link');
162 | linkclass.title = "Link";
163 |
164 | let img = new Image();
165 | if(p.preview.images[0].source.url){
166 | img.src = p.preview.images[0].source.url.replace(/\&/g,'&').replace(/\?/g,'?');
167 | }else{
168 | img.src = p.thumbnail;
169 | }
170 |
171 | link.appendChild( img );
172 | link.appendChild( linkclass );
173 |
174 | content.appendChild( link );
175 | }
176 |
177 | facelink.appendChild( facebook );
178 | whatslink.appendChild( whatsapp );
179 |
180 | post.appendChild( title );
181 | post.appendChild( content );
182 | post.appendChild( whatslink );
183 | post.appendChild( facelink );
184 | main.appendChild( post );
185 | }
186 | catch(e){
187 | console.error(p, e.message);
188 | }
189 | }
190 | }
191 |
192 | function getData(d){
193 | let main = document.getElementById('main');
194 | main.innerHTML = "";
195 | for(child of d.data.children){
196 | createPost(child.data);
197 | }
198 | state.beforeUrl = state.baseUrl+'?count='+state.page+'&before='+d.data.before;
199 | state.afterUrl = state.baseUrl+'?count='+state.page+'&after='+d.data.after;
200 | }
201 |
202 | function play(ele){
203 | if(ele.children[0].paused){
204 | ele.children[0].play();
205 | ele.children[1].style.visibility = "hidden";
206 | }else{
207 | ele.children[0].pause();
208 | ele.children[1].style.visibility = "visible";
209 | }
210 | }
211 |
212 | document.getElementById('prev').addEventListener('click',function(e){
213 | e.preventDefault();
214 | e.stopPropagation();
215 | if( state.page > 25 ){
216 | state.page -= 25;
217 | state.currentUrl = state.beforeUrl;
218 | getLink(state.beforeUrl);
219 | window.scrollTo(0,0);
220 | }
221 | });
222 |
223 | document.getElementById('next').addEventListener('click',function(e){
224 | e.preventDefault();
225 | e.stopPropagation();
226 | state.page += 25;
227 | state.currentUrl = state.afterUrl;
228 | getLink(state.afterUrl);
229 | window.scrollTo(0,0);
230 | });
231 |
232 | document.getElementById('home').addEventListener('click',function(e){
233 | getLink(state.baseUrl)
234 | window.scrollTo(0,0);
235 | });
236 |
237 | };
238 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Memes",
3 | "short_name": "Memes",
4 | "description": "Memes, videos and gifs",
5 | "start_url": "index.html",
6 | "display": "standalone",
7 | "orientation": "portrait",
8 | "background_color": "#FFF",
9 | "theme_color": "#FFF",
10 | "icons": [
11 | {
12 | "src": "favicon.png",
13 | "sizes": "256x256",
14 | "type": "image/png",
15 | "density": 1
16 | },
17 | {
18 | "src": "favicon.png",
19 | "sizes": "512x512",
20 | "type": "image/png",
21 | "density": 1
22 | }
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/sw.js:
--------------------------------------------------------------------------------
1 | const filesToCache = [
2 | './',
3 | './index.html',
4 | './js/main.js',
5 | './css/main.css',
6 | './css/facebook.png',
7 | './css/whatsapp.png',
8 | './css/link-btn.png',
9 | './css/play-btn.png',
10 | './favicon.png',
11 | './manifest.json'
12 | ];
13 |
14 | const staticCacheName = 'memes-v1';
15 |
16 | self.addEventListener('install', event => {
17 | event.waitUntil(
18 | caches.open(staticCacheName)
19 | .then(cache => {
20 | //return cache.addAll(filesToCache);
21 | return cache.addAll(urlsToPrefetch.map(function(filesToCache) {
22 | return new Request(filesToCache, { mode: 'no-cors' });
23 | }));
24 | })
25 | );
26 | });
27 |
28 | self.addEventListener('fetch', event => {
29 | event.respondWith(
30 | caches.match(event.request)
31 | .then(response => {
32 | if (response) {
33 | return response;
34 | }
35 |
36 | return fetch(event.request)
37 |
38 | .then(response => {
39 | return caches.open(staticCacheName).then(cache => {
40 | cache.put(event.request.url, response.clone());
41 | return response;
42 | });
43 | });
44 |
45 | }).catch(error => {})
46 | );
47 | });
48 |
49 | self.addEventListener('activate', event => {
50 |
51 | const cacheWhitelist = [staticCacheName];
52 |
53 | event.waitUntil(
54 | caches.keys().then(cacheNames => {
55 | return Promise.all(
56 | cacheNames.map(cacheName => {
57 | if (cacheWhitelist.indexOf(cacheName) === -1) {
58 | return caches.delete(cacheName);
59 | }
60 | })
61 | );
62 | })
63 | );
64 | });
65 |
--------------------------------------------------------------------------------