├── 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 | --------------------------------------------------------------------------------