├── README.md ├── embetter-builder.js ├── embetter.css ├── embetter.js ├── favicon.ico ├── index.html ├── playlist └── index.html ├── sample-data ├── bandcamp-album.html ├── bandcamp-track.html ├── imgur-gallery.html ├── imgur-gifv.html ├── imgur.html ├── mixcloud.json ├── slideshare.json ├── soundcloud-set-oembed.json ├── soundcloud-set.json ├── soundcloud-track-oembed.json ├── soundcloud-track.json ├── ustream-recorded.json ├── ustream.json ├── vimeo.json └── vine.json └── vendor ├── normalize.css ├── reqwest.min.js └── skeleton.css /README.md: -------------------------------------------------------------------------------- 1 | # Embetter 2 | 3 | #### Because iframes are janky 4 | 5 | Media embeds can quickly bog your site down, so let's lazy-load them! The basic Embetter player consists of a tiny template with a progressively-enhanced thumbnail image, a play button, and the essential data needed to construct the responsive iframe embed code. Add a dash of javascript & css to your web page, and you have a simple, lightweight media player. 6 | 7 | #### Mobile-happy 8 | 9 | Since media generally can't autoplay on mobile devices, we work around that by populating the embed's iframe when an Embetter player is at least partially visible in the mobile viewport. By doing this, we still lazy-load (and unload) as much as possible, but each embed only needs a single tap to start playing. 10 | 11 | #### Demo 12 | 13 | Check out the [demo](http://cacheflowe.github.io/embetter) with the embed builder, see an [auto-playthrough playlist](http://cacheflowe.github.io/embetter/playlist/#/2-step-chunes/oG_La5R9HQk|C4xDtB_9SZM|QFxdkHRpz7Q|0Yt_Ts26PLM|VIOMoOYOTQQ|IlNNrXzi60o|F73WxhZPvJ4|4_uggscguzs|5sDW7AMoHVs|gXCN1DhHTZA|xy3RjUX3UeQ|ewHYBOCypXc|uNWTlETtuGM|B1JK9FJf0cE|mIE92NdE4ww|qpa09-OuZek|IqrYbXNnGmc|yUrOSCkoJ6w|VDqBbSBjsuw|nves7T9ThZI|OhKypM3H0iY|Z7nvb8kTPl8|Shtc5vtjui0|Aw4I7-jHw7s|YDi9i5JT5Co|lZ3KM5E8wl8|E48rwBeHf-A|MFu-PSawX1k|SgaHZIobQms|oG_La5R9HQk|mapbdUAbcrY) or see it out [in the wild](http://plasticsoundsupply.com/video). 14 | 15 | #### Usage 16 | 17 | Add Embetter embed codes to your markup: 18 | 19 | ``` 20 |
21 | 22 |
23 | ``` 24 | 25 | Tell Embetter's JavaScript to activate any players on the page, or within a specific container. Be sure to pass in the 3rd-party services you'd like to enable: 26 | 27 | ``` 28 | var embedServices = [ 29 | window.embetter.services.youtube, 30 | window.embetter.services.vimeo, 31 | window.embetter.services.soundcloud 32 | ]; 33 | window.embetter.utils.initMediaPlayers(document.body, embedServices); 34 | ``` 35 | 36 | Dispose any existing players before you switch pages in your single-page app: 37 | 38 | ``` 39 | window.embetter.utils.disposePlayers(); 40 | ``` 41 | 42 | Stop all active embeds, in case you need to: 43 | 44 | ``` 45 | window.embetter.utils.unembedPlayers(document.body) 46 | ``` 47 | 48 | Get media complete callbacks from YouTube, Vimeo and Soundcloud embeds via their iframe .js APIs. This is useful to scroll your page to the next player contained in an element with `data-embetter-playlist="true"`. 49 | 50 | ``` 51 | window.embetter.apiEnabled = true; 52 | window.embetter.apiAutoplayCallback = function(playerEl) { 53 | window.scrollTo(0, window.scrollY - playerEl.offsetTop + 50); 54 | }; 55 | 56 | ``` 57 | 58 | #### How it works 59 | 60 | On it's own, an Embetter embed code is just a clickable thumbnail that takes you to the source 3rd-party media page. After activation via `initMediaPlayers`, each Embetter player becomes clickable and has all of the data it needs to construct an iframe, which stretches to match the size of the preview thumbnail. This information is stored as a data attribute on the `.embetter` wrapper div and is extracted via API calls, regex capturing, or metatag scraping. Additional operations on this data helps us handle special cases per service. The `embetter-builder.js` file contains the logic to properly extract the necessary data for each type of embed, and operates on URLs, CORS-enabled APIs (usually oembed services), or locally-`curl`ed demo files to explain and test the behavior. Most likely, you'd want to port this behavior to your backend if you need to do this on the fly. Some services have several types of embeds that require different data to be stored, and some have extra css that adds custom layout and state behavior. When it comes down to it, however, we only need a single string (an id of some kind) to interpolate into a predefined iframe src template per service. 61 | 62 | 63 | ## Supported services & URL formats: 64 | 65 | ##### YouTube 66 | * Formats: 67 | * `https://www.youtube.com/watch?v=Fb4bCgWkZRc` 68 | * `https://youtube.com/watch?v=Fb4bCgWkZRc` 69 | * `https://youtube.com/v/Fb4bCgWkZRc` 70 | * `http://youtu.be/Fb4bCgWkZRc` 71 | * Regex: 72 | * `/(?:.+?)?(?:youtube\.com\/v\/|watch\/|\?v=|\&v=|youtu\.be\/|\/v=|^youtu\.be\/)([a-zA-Z0-9_-]{11})+/` 73 | * Captures id: `Fb4bCgWkZRc` 74 | * API URL: 75 | * None needed 76 | * Thumbnail aspect ratio: 77 | * 4:3 78 | 79 | ##### Vimeo 80 | * Formats: 81 | * `https://vimeo.com/99276873` 82 | * Regex: 83 | * `/(?:https?:\/\/)?(?:w{3}\.)?vimeo.com\/(\S*)(?:\/?|$|\s|\?|#)/` 84 | * Captures id: `99276873` 85 | * API URL (CORS/jsonp enabled): 86 | * `https://vimeo.com/api/v2/video/` + videoId + `.json` 87 | * Thumbnail aspect ratio: 88 | * Variable (matches uploaded media) 89 | 90 | ##### Soundcloud 91 | * Formats: 92 | * `https://soundcloud.com/itemsandthings/chlo-live-items-things` 93 | * `https://snd.sc/cacheflowe/sets/automate-everything-2005` 94 | * `https://soundcloud.com/groups/berlin-minimal-techno` 95 | * Regex: 96 | * `/(?:https?:\/\/)?(?:w{3}\.)?(?:soundcloud.com|snd.sc)\/([a-zA-Z0-9_-]*(?:\/sets)?(?:\/groups)?\/[a-zA-Z0-9_-]*)(?:\/?|$|\s|\?|#)/` 97 | * Captures path: `itemsandthings/chlo-live-items-things` or `cacheflowe/sets/automate-everything-2005` 98 | * API URL (CORS/jsonp enabled): 99 | * `http://api.soundcloud.com/resolve.json?url=` + mediaUrl + `&client_id=` + YourClientID + `&callback=jsonpResponse` 100 | * Thumbnail aspect ratio: 101 | * 1:1 102 | 103 | ##### Instagram 104 | * Formats: 105 | * `https://instagram.com/p/xekoQiQY3-/` 106 | * `http://instagr.am/p/xekoQiQY3-/` 107 | * Regex: 108 | * `/(?:https?:\/\/)?(?:w{3}\.)?(?:instagram.com|instagr.am)\/p\/([a-zA-Z0-9-_]*)(?:\/?|$|\s|\?|#)/` 109 | * Captures id: `xekoQiQY3-` 110 | * API URL: 111 | * None needed 112 | * Thumbnail aspect ratio: 113 | * 1:1 114 | 115 | ##### Giphy 116 | * Formats: 117 | * `https://giphy.com/gifs/ken-lee-3ESp1RAn7PjOw` 118 | * `https://giphy.com/gifs/3ESp1RAn7PjOw` 119 | * Regex: 120 | * `/(?:https?:\/\/)?(?:w{3}\.)?giphy.com\/gifs\/([a-zA-Z0-9_\-%]*)(?:\/?|$|\s|\?|#)/` 121 | * Captures id: `3ESp1RAn7PjOw` 122 | * API URL: 123 | * None needed 124 | * Thumbnail aspect ratio: 125 | * Variable (matches original .gif) 126 | 127 | ##### Mixcloud 128 | * Formats: 129 | * `https://www.mixcloud.com/Davealex/davealex-30m-electro-2010/` 130 | * Regex: 131 | * `/(?:https?:\/\/)?(?:w{3}\.)?(?:mixcloud.com)\/(.*\/.*)(?:\/?|$|\s|\?|#)/` 132 | * Captures id: `Davealex/davealex-30m-electro-2010` 133 | * API URL (CORS/jsonp enabled): 134 | * `http://www.mixcloud.com/oembed/?url=` + mediaUrl + `&format=jsonp` 135 | * Thumbnail aspect ratio: 136 | * 1:1 137 | 138 | ##### Dailymotion 139 | * Formats: 140 | * `http://www.dailymotion.com/video/x2681lh_the-ultimate-fainting-fails-compilation_fun` 141 | * `http://www.dailymotion.com/video/x2681lh` 142 | * Regex: 143 | * `/(?:https?:\/\/)?(?:w{3}\.)?dailymotion.com\/video\/([a-zA-Z0-9-_]*)(?:\/?|$|\s|\?|#)/` 144 | * Captures id: `x2681lh` 145 | * API URL: 146 | * None needed 147 | * Thumbnail aspect ratio: 148 | * 4:3 149 | 150 | ##### CodePen 151 | * Formats: 152 | * `http://codepen.io/nicoptere/pen/mgpxB` 153 | * `http://codepen.io/nicoptere/embed/mgpxB` 154 | * Regex: 155 | * `/(?:https?:\/\/)?(?:w{3}\.)?(?:codepen.io)\/([a-zA…A-Z0-9_\-%]*\/[a-zA-Z0-9_\-%]*)(?:\/?|$|\s|\?|#)/` 156 | * Captures id: `nicoptere/embed/mgpxB` 157 | * API URL: 158 | * None needed 159 | * Thumbnail aspect ratio: 160 | * 1024:600 161 | 162 | ##### Shadertoy 163 | * Formats: 164 | * `https://www.shadertoy.com/view/4dfGzs` 165 | * Regex: 166 | * `/(?:https?:\/\/)?(?:w{3}\.)?shadertoy.com\/view\/([a-zA-Z0-9_\-%]*)(?:\/?|$|\s|\?|#)/` 167 | * Captures id: `4dfGzs` 168 | * API URL: 169 | * None needed 170 | * Thumbnail aspect ratio: 171 | * 16:9 172 | 173 | ##### Bandcamp 174 | * Formats: 175 | * `https://swindleuk.bandcamp.com/album/swindle-walters-call` 176 | * `https://swindleuk.bandcamp.com/track/summer-fruits` 177 | * Regex: 178 | * `/(?:https?:\/\/)?(?:w{3}\.)?([a-zA-Z0-9_\-]*.bandcamp.com\/(album|track)\/[a-zA-Z0-9_\-%]*)(?:\/?|$|\s|\?|#)/` 179 | * Captures id: `album=2659930103` and `track=1312622119` 180 | * API URL: 181 | * Must scrape metatags from the album/track page. 182 | * Thumbnail aspect ratio: 183 | * 1:1 184 | 185 | ##### Ustream 186 | * Formats: 187 | * `http://www.ustream.tv/channel/almost-home-adoptions-cat-cam` 188 | * `http://www.ustream.tv/recorded/69957739` 189 | * `http://www.ustream.tv/NASAHDTV` 190 | * Regex: 191 | * `/(?:https?:\/\/)?(?:w{3}\.)?(?:ustream.tv|ustre.am)\/((?:(recorded|channel)\/)?[a-zA-Z0-9_\-%]*)(?:\/?|$|\s|\?|#)/` 192 | * Captures id: `channel/almost-home-adoptions-cat-cam` or `recorded/69957739` or `NASAHDTV` 193 | * API URL: 194 | * `http://www.ustream.tv/oembed?url=` + mediaUrl 195 | * Thumbnail aspect ratio: 196 | * 4:3 most of the time... 197 | * 10:9 or possibly otherwise 198 | 199 | ##### Imgur 200 | * Formats: 201 | * `http://imgur.com/gallery/iKQET` 202 | * `http://imgur.com/USbuZSo` 203 | * Regex: 204 | * `/(?:https?:\/\/)?(?:w{3}\.)?(?:imgur.com)\/((?:gallery\/)?[a-zA-Z0-9_\-%]*)(?:\/?|$|\s|\?|#)/` 205 | * Captures id: `gallery/iKQET` and `USbuZSo` 206 | * API URL: 207 | * `http://api.imgur.com/oembed.json?url=` + mediaUrl 208 | * Must scrape metatags from the Imgur page, since the oembed service doesn't provide a thumbnail URL. 209 | * Thumbnail aspect ratio: 210 | * Variable (matches uploaded media) 211 | 212 | ##### Vine 213 | * Formats: 214 | * `https://vine.co/v/eWlADOIAEAd` 215 | * Regex: 216 | * `/(?:https?:\/\/)?(?:w{3}\.)?vine.co\/v\/([a-zA-Z0-9-]*)(?:\/?|$|\s|\?|#)/` 217 | * Captures id: `eWlADOIAEAd` 218 | * API URL: 219 | * `https://vine.co/oembed/` + vineId + `.json` 220 | * Thumbnail aspect ratio: 221 | * 1:1 222 | 223 | ##### Slideshare 224 | * Formats: 225 | * `http://www.slideshare.net/HunterLoftis1/forwardjs-we-will-all-be-game-developers` 226 | * Regex: 227 | * `/(?:https?:\/\/)?(?:w{3}\.)?slideshare.net\/([a-zA-Z0-9_\-%]*\/[a-zA-Z0-9_\-%]*)(?:\/?|$|\s|\?|#)/` 228 | * Captures id: `eWlADOIAEAd` 229 | * API URL: 230 | * `http://www.slideshare.net/api/oembed/2?url=https://www.slideshare.net/` + slideshowId + `&format=json'` 231 | * Thumbnail aspect ratio: 232 | * 170:96 233 | * 170:121 234 | * 170:128 235 | * Variable, but 170px width 236 | 237 | ##### Kuula 238 | * Formats: 239 | * `https://kuula.co/post/7fWCb` 240 | * Regex: 241 | * `/(?:https?:\/\/)?(?:w{3}\.)?kuula.co\/post\/([a-zA-Z0-9_\-%]*)(?:\/?|$|\s|\?|#)/` 242 | * Captures id: `7fWCb` 243 | * API URL: 244 | * None needed 245 | * Thumbnail aspect ratio: 246 | * 1:1 247 | 248 | 249 | 250 | ### TODO: 251 | 252 | * More documentation about how each service gathers IDs to create iframes - add iframe src construction example 253 | * Usage documentation - full API 254 | * Add info about special per-service handling 255 | * Make Slideshare iframe responsive to thumbnail aspect ratio 256 | -------------------------------------------------------------------------------- /embetter-builder.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | var embetter = window.embetter; 3 | 4 | ///////////////////////////////////////////////////////////// 5 | // BUILD PLAYER FROM PASTE 6 | ///////////////////////////////////////////////////////////// 7 | embetter.utils.buildPlayerFromServiceURL = function(el, string, services) { 8 | for (var i = 0; i < services.length; i++) { 9 | var service = services[i]; 10 | if(string.match(service.regex) != null) { 11 | service.buildFromText(string, el); 12 | } 13 | } 14 | }; 15 | 16 | embetter.utils.playerCode = function(htmlStr) { 17 | var entityMap = { 18 | "<": "<", 19 | ">": ">", 20 | }; 21 | function escapeHtml(string) { 22 | return String(string).replace(/[<>]/g, function (s) { 23 | return entityMap[s]; 24 | }); 25 | } 26 | htmlStr = htmlStr.replace(/\>\s+\<'); // remove whitespace between tags 27 | return '

Embed code:

'; 28 | }; 29 | 30 | embetter.utils.embedPlayerInContainer = function(containerEl, serviceObj, mediaUrl, thumbnail, id) { 31 | // create service title 32 | containerEl.appendChild(embetter.utils.stringToDomElement('

' + serviceObj.type.toUpperCase() + '

')); 33 | // create embed 34 | var newEmbedHTML = embetter.utils.playerHTML(serviceObj, mediaUrl, thumbnail, id); 35 | var newEmbedEl = embetter.utils.stringToDomElement(newEmbedHTML); 36 | embetter.utils.initPlayer(newEmbedEl, serviceObj, embetter.curEmbeds); 37 | containerEl.appendChild(newEmbedEl); 38 | // show embed code 39 | var newEmbedCode = embetter.utils.playerCode(newEmbedHTML); 40 | var newEmbedCodeEl = embetter.utils.stringToDomElement(newEmbedCode); 41 | containerEl.appendChild(newEmbedCodeEl); 42 | }; 43 | 44 | embetter.utils.copyPropsToObject = function(destObj, sourceObj) { 45 | for( var key in sourceObj ){ 46 | destObj[key] = sourceObj[key]; 47 | } 48 | }; 49 | 50 | embetter.utils.copyPropsToObject(embetter.services.video, { 51 | buildFromText: function(videoURL, containerEl) { 52 | var thumbnail = videoURL; 53 | thumbnail = thumbnail.replace('.mp4', '-poster.jpg'); 54 | thumbnail = thumbnail.replace('.mov', '-poster.jpg'); 55 | thumbnail = thumbnail.replace('.m4v', '-poster.jpg'); 56 | embetter.utils.embedPlayerInContainer(containerEl, this, videoURL, thumbnail, videoURL); 57 | } 58 | }); 59 | 60 | embetter.utils.copyPropsToObject(embetter.services.gif, { 61 | buildFromText: function(gifURL, containerEl) { 62 | var thumbnail = gifURL; 63 | thumbnail = thumbnail.replace('.gif', '-poster.jpg'); 64 | embetter.utils.embedPlayerInContainer(containerEl, this, gifURL, thumbnail, gifURL); 65 | } 66 | }); 67 | 68 | embetter.utils.copyPropsToObject(embetter.services.youtube, { 69 | getData: function(id) { 70 | return 'http://img.youtube.com/vi/'+ id +'/0.jpg'; 71 | }, 72 | buildFromText: function(text, containerEl) { 73 | var videoId = text.match(this.regex)[1]; 74 | if(videoId != null) { 75 | var videoURL = this.link(videoId); 76 | var videoThumbnail = this.getData(videoId); 77 | embetter.utils.embedPlayerInContainer(containerEl, this, videoURL, videoThumbnail, videoId); 78 | } 79 | } 80 | }); 81 | 82 | embetter.utils.copyPropsToObject(embetter.services.vimeo, { 83 | getData: function(mediaUrl, callback, sampleData) { 84 | var videoId = mediaUrl.split('vimeo.com/')[1]; 85 | window.reqwest({ 86 | url: sampleData || 'https://vimeo.com/api/v2/video/'+ videoId +'.json', 87 | type: (sampleData) ? 'json' : 'jsonp', 88 | error: function (err) {}, 89 | success: function (data) { 90 | callback(data[0].thumbnail_large); 91 | } 92 | }) 93 | 94 | return ''; 95 | }, 96 | buildFromText: function(text, containerEl, sampleData) { 97 | var self = this; 98 | var videoId = text.match(this.regex)[1]; 99 | if(videoId != null) { 100 | var videoURL = this.link(videoId); 101 | this.getData(videoURL, function(videoThumbnail) { 102 | embetter.utils.embedPlayerInContainer(containerEl, self, videoURL, videoThumbnail, videoId); 103 | }, sampleData); 104 | } 105 | } 106 | }); 107 | 108 | embetter.utils.copyPropsToObject(embetter.services.soundcloud, { 109 | getData: function(mediaUrl, callback, sampleData) { 110 | reqwest({ 111 | // url: 'http://soundcloud.com/oembed?url='+ mediaUrl +'&format=json', 112 | // http://soundcloud.com/oembed?url=https://soundcloud.com/cacheflowe/sets/automate-everything-2005&format=json 113 | url: sampleData || 'http://api.soundcloud.com/resolve.json?url='+ mediaUrl +'&client_id=YOUR_CLIENT_ID&callback=jsonpResponse', 114 | type: (sampleData) ? 'json' : 'jsonp', 115 | error: function (err) {}, 116 | success: function (data) { 117 | callback(data); 118 | } 119 | }) 120 | }, 121 | largerThumbnail: function(thumbnail) { 122 | return thumbnail.replace('large.jpg', 't500x500.jpg'); 123 | }, 124 | buildFromText: function(text, containerEl, sampleData) { 125 | var self = this; 126 | var soundURL = this.link(text.match(this.regex)[1]); 127 | if(soundURL != null) { 128 | this.getData(soundURL, function(data) { 129 | // progressively fall back from sound image to user image to group creator image. grab larger image where possible 130 | var thumbnail = data.artwork_url; 131 | if(thumbnail) thumbnail = self.largerThumbnail(thumbnail); 132 | 133 | if(thumbnail == null) { 134 | thumbnail = (data.user) ? data.user.avatar_url : null; 135 | if(thumbnail) thumbnail = self.largerThumbnail(thumbnail); 136 | } 137 | 138 | if(thumbnail == null) { 139 | thumbnail = (data.creator) ? data.creator.avatar_url : null; 140 | if(thumbnail) thumbnail = self.largerThumbnail(thumbnail); 141 | } 142 | 143 | if(thumbnail) { 144 | // handle special soundcloud ids 145 | var soundId = data.id; 146 | if(soundURL.indexOf('/sets/') != -1) soundId = 'playlists/' + soundId; 147 | else if(soundURL.indexOf('/groups/') != -1) soundId = 'groups/' + soundId; 148 | else soundId = 'tracks/' + soundId; 149 | 150 | // create embed 151 | embetter.utils.embedPlayerInContainer(containerEl, self, soundURL, thumbnail, soundId); 152 | } 153 | }, sampleData); 154 | } 155 | } 156 | }); 157 | 158 | embetter.utils.copyPropsToObject(embetter.services.instagram, { 159 | getData: function(id) { 160 | return 'https://instagram.com/p/' + id +'/media/?size=l'; 161 | }, 162 | buildFromText: function(text, containerEl) { 163 | var mediaId = text.match(this.regex)[1]; 164 | var mediaURL = this.link(mediaId); 165 | if(mediaURL != null) { 166 | var thumbnail = this.getData(mediaId); 167 | embetter.utils.embedPlayerInContainer(containerEl, this, mediaURL, thumbnail, mediaId); 168 | } 169 | } 170 | }); 171 | 172 | embetter.utils.copyPropsToObject(embetter.services.dailymotion, { 173 | getData: function(id) { 174 | return 'http://www.dailymotion.com/thumbnail/video/'+ id; 175 | }, 176 | buildFromText: function(text, containerEl) { 177 | text = text.split('_')[0]; 178 | var videoId = text.match(this.regex)[1]; 179 | if(videoId != null) { 180 | var videoURL = this.link(videoId); 181 | var videoThumbnail = this.getData(videoId); 182 | embetter.utils.embedPlayerInContainer(containerEl, this, videoURL, videoThumbnail, videoId); 183 | } 184 | } 185 | }); 186 | 187 | embetter.utils.copyPropsToObject(embetter.services.mixcloud, { 188 | getData: function(mediaUrl, callback, sampleData) { 189 | window.reqwest({ 190 | url: sampleData || 'http://www.mixcloud.com/oembed/?url='+ mediaUrl +'&format=jsonp', 191 | type: (sampleData) ? 'json' : 'jsonp', 192 | error: function (err) {}, 193 | success: function (data) { 194 | callback(data); 195 | } 196 | }); 197 | }, 198 | buildFromText: function(text, containerEl, sampleData) { 199 | var self = this; 200 | var soundId = text.match(this.regex)[1]; 201 | var soundURL = this.link(soundId); 202 | if(soundURL != null) { 203 | this.getData(soundURL, function(data) { 204 | if(data.image) { 205 | embetter.utils.embedPlayerInContainer(containerEl, self, soundURL, data.image, soundId); 206 | } 207 | }, sampleData); 208 | } 209 | } 210 | }); 211 | 212 | embetter.utils.copyPropsToObject(embetter.services.codepen, { 213 | getData: function(id) { 214 | return 'http://codepen.io/' + id + '/image/large.png'; 215 | }, 216 | buildFromText: function(text, containerEl) { 217 | var penId = text.match(this.regex)[1]; 218 | penId = penId.replace('/embed/', '/pen/'); 219 | if(penId != null) { 220 | var penURL = this.link(penId); 221 | var penThumbnail = this.getData(penId); 222 | embetter.utils.embedPlayerInContainer(containerEl, this, penURL, penThumbnail, penId); 223 | } 224 | } 225 | }); 226 | 227 | embetter.utils.copyPropsToObject(embetter.services.bandcamp, { 228 | getData: function(bandcampURL, callback, sampleData) { 229 | window.reqwest({ 230 | url: sampleData || bandcampURL, 231 | type: 'html', 232 | error: function (err) {}, 233 | success: function (data) { 234 | callback(data); 235 | } 236 | }) 237 | }, 238 | regexForId: /((?:album|track)=[0-9]*)/, 239 | regexForThumb: /https:\/\/(.)*_16.jpg/, 240 | buildFromText: function(text, containerEl, sampleData) { 241 | var self = this; 242 | var bandcampId = text.match(this.regex)[1]; 243 | if(bandcampId != null) { 244 | var bandcampURL = this.link(bandcampId); 245 | this.getData(bandcampURL, function(html) { 246 | if(html.match(self.regexForId) != null) { 247 | var streamId = html.match(self.regexForId)[0]; 248 | var thumbnailUrl = html.match(self.regexForThumb)[0]; 249 | embetter.utils.embedPlayerInContainer(containerEl, self, bandcampURL, thumbnailUrl, streamId); 250 | } 251 | }, sampleData); 252 | } 253 | } 254 | }); 255 | 256 | embetter.utils.copyPropsToObject(embetter.services.ustream, { 257 | getData: function(mediaUrl, callback, sampleData) { 258 | window.reqwest({ 259 | url: sampleData || 'https://www.ustream.tv/oembed?url='+ mediaUrl, 260 | type: 'json', 261 | error: function (err) {}, 262 | success: function (data) { 263 | callback(data); 264 | } 265 | }); 266 | }, 267 | buildFromText: function(text, containerEl, sampleData) { 268 | var self = this; 269 | var streamId = text.match(this.regex)[1]; 270 | var streamURL = this.link(streamId); 271 | if(streamURL != null) { 272 | this.getData(streamURL, function(data) { 273 | if(data.thumbnail_url) { 274 | var channelId = data.html.match(/http:\/\/www.ustream.tv\/embed\/([0-9]*)/); 275 | var recordedId = data.html.match(/http:\/\/www.ustream.tv\/embed\/recorded\/([0-9]*)/); 276 | streamId = (recordedId != null) ? 'recorded/' + recordedId[1] : channelId[1]; 277 | embetter.utils.embedPlayerInContainer(containerEl, self, streamURL, data.thumbnail_url, streamId); 278 | } 279 | }, sampleData); 280 | } 281 | } 282 | }); 283 | 284 | embetter.utils.copyPropsToObject(embetter.services.imgur, { 285 | getData: function(mediaUrl, callback, sampleData) { 286 | window.reqwest({ 287 | // url: 'http://api.imgur.com/oembed.json?url='+ mediaUrl, // oembed URL doesn't return a thumbnail for us, so it's useless here 288 | url: sampleData || bandcampURL, 289 | type: 'html', 290 | error: function (err) { 291 | console.log('imgur error', err); 292 | }, 293 | success: function (data) { 294 | callback(data); 295 | } 296 | }); 297 | }, 298 | buildFromText: function(text, containerEl, sampleData) { 299 | var self = this; 300 | var imgurId = text.match(this.regex)[1]; 301 | if(imgurId != null) { 302 | var mediaURL = this.link(imgurId); 303 | this.getData(mediaURL, function(data) { 304 | if(data.match('content="gallery"') != null) { 305 | var thumbMatch = data.match(/image_src(?:.)*(http(.)*jpg)/); 306 | var thumbnail = thumbMatch[1]; 307 | if(thumbMatch && thumbMatch.length) { 308 | // check for gallery, then prepend "/a/" before the image ID embed 309 | var embedId = imgurId.replace('gallery', 'a'); 310 | embetter.utils.embedPlayerInContainer(containerEl, self, mediaURL, thumbnail, embedId); 311 | } 312 | } else if(data.match('twitter:image:src') != null) { 313 | var thumbMatch = data.match(/twitter:image:src(?:.)*(http(.)*jpg)/); 314 | var thumbnail = thumbMatch[1]; 315 | if(thumbMatch && thumbMatch.length) { 316 | // if not an actual gallery, remove gallery/ from the url for a proper embed ID 317 | var embedId = imgurId.replace('gallery/', ''); 318 | embetter.utils.embedPlayerInContainer(containerEl, self, mediaURL, thumbnail, embedId); 319 | } 320 | } 321 | }, sampleData); 322 | } 323 | } 324 | }); 325 | 326 | embetter.utils.copyPropsToObject(embetter.services.vine, { 327 | getData: function(imgId, callback, sampleData) { 328 | window.reqwest({ 329 | url: sampleData || 'https://vine.co/oembed/' + imgId + '.json', 330 | type: 'json', 331 | error: function (err) { 332 | console.log('vine error', err); 333 | }, 334 | success: function (data) { 335 | callback(data); 336 | } 337 | }); 338 | }, 339 | buildFromText: function(text, containerEl, sampleData) { 340 | var videoId = text.match(this.regex)[1]; 341 | if(videoId != null) { 342 | var self = this; 343 | this.getData(videoId, function(data) { 344 | if(data.thumbnail_url) { 345 | var vineURL = self.link(videoId); 346 | embetter.utils.embedPlayerInContainer(containerEl, self, vineURL, data.thumbnail_url, videoId); 347 | } 348 | }, sampleData); 349 | } 350 | } 351 | }); 352 | 353 | embetter.utils.copyPropsToObject(embetter.services.slideshare, { 354 | getData: function(imgId, callback, sampleData) { 355 | window.reqwest({ 356 | url: sampleData || 'http://www.slideshare.net/api/oembed/2?url=https://www.slideshare.net/' + imgId + '&format=json', 357 | type: 'json', 358 | error: function (err) {}, 359 | success: function (data) { 360 | callback(data); 361 | } 362 | }); 363 | }, 364 | buildFromText: function(text, containerEl, sampleData) { 365 | var videoId = text.match(this.regex)[1]; 366 | if(videoId != null) { 367 | var self = this; 368 | this.getData(videoId, function(data) { 369 | if(data.thumbnail) { 370 | var imgId = data.html.match(/embed_code\/key\/([a-zA-Z0-9\-\/]*)/)[1]; 371 | var slideshareURL = self.link(videoId); 372 | embetter.utils.embedPlayerInContainer(containerEl, self, slideshareURL, data.thumbnail, imgId); 373 | } 374 | }, sampleData); 375 | } 376 | } 377 | }); 378 | 379 | embetter.utils.copyPropsToObject(embetter.services.giphy, { 380 | getData: function(id) { 381 | return 'https://media.giphy.com/media/' + id + '/giphy_s.gif'; 382 | }, 383 | buildFromText: function(text, containerEl, sampleData) { 384 | var self = this; 385 | var splitPath = text.split('/'); 386 | var longId = splitPath[splitPath.length - 1]; // get id, with extra dashed data 387 | var dashedId = longId.split('-'); 388 | var giphyId = dashedId[dashedId.length - 1]; // get id without extra dashed data 389 | if(giphyId != null) { 390 | var giphyURL = this.link(longId); 391 | var thumbnailUrl = this.getData(giphyId); 392 | var gifURL = 'https://media.giphy.com/media/' + giphyId + '/giphy.gif'; // not used for now 393 | embetter.utils.embedPlayerInContainer(containerEl, self, giphyURL, thumbnailUrl, giphyId); 394 | } 395 | } 396 | }); 397 | 398 | embetter.utils.copyPropsToObject(embetter.services.shadertoy, { 399 | getData: function(id) { 400 | return 'https://www.shadertoy.com/media/shaders/'+id+'.jpg'; 401 | }, 402 | buildFromText: function(text, containerEl) { 403 | var shaderId = text.match(this.regex)[1]; 404 | if(shaderId != null) { 405 | var shaderURL = this.link(shaderId); 406 | var shaderThumbnail = this.getData(shaderId); 407 | embetter.utils.embedPlayerInContainer(containerEl, this, shaderURL, shaderThumbnail, shaderId); 408 | } 409 | } 410 | }); 411 | 412 | embetter.utils.copyPropsToObject(embetter.services.kuula, { 413 | getData: function(id) { 414 | return 'https://kuula.co/cover/'+id; 415 | }, 416 | buildFromText: function(text, containerEl) { 417 | var postId = text.match(this.regex)[1]; 418 | if(postId != null) { 419 | var postURL = this.link(postId); 420 | var postThumbnail = this.getData(postId); 421 | embetter.utils.embedPlayerInContainer(containerEl, this, postURL, postThumbnail, postId); 422 | } 423 | } 424 | }); 425 | 426 | })(); 427 | -------------------------------------------------------------------------------- /embetter.css: -------------------------------------------------------------------------------- 1 | .embetter { 2 | -webkit-transition: background-color 0.25s linear, max-width 0.25s linear, max-height 0.25s linear; 3 | -moz-transition: background-color 0.25s linear, max-width 0.25s linear, max-height 0.25s linear; 4 | -ms-transition: background-color 0.25s linear, max-width 0.25s linear, max-height 0.25s linear; 5 | -o-transition: background-color 0.25s linear, max-width 0.25s linear, max-height 0.25s linear; 6 | transition: background-color 0.25s linear, max-width 0.25s linear, max-height 0.25s linear; 7 | 8 | background-color: transparent; 9 | position: relative; 10 | display: block; 11 | overflow: hidden; 12 | } 13 | 14 | .embetter:hover { 15 | background-color: #000; 16 | } 17 | 18 | .embetter a { 19 | display: block; 20 | line-height: 0; 21 | margin: 0; 22 | } 23 | 24 | .embetter img { 25 | -webkit-transition: opacity 0.25s linear, padding 0.25s linear, max-width 0.25s linear, -webkit-transform 0.25s linear; 26 | -moz-transition: opacity 0.25s linear, padding 0.25s linear, max-width 0.25s linear, -moz-transform 0.25s linear; 27 | -ms-transition: opacity 0.25s linear, padding 0.25s linear, max-width 0.25s linear, -ms-transform 0.25s linear; 28 | -o-transition: opacity 0.25s linear, padding 0.25s linear, max-width 0.25s linear, -o-transform 0.25s linear; 29 | transition: opacity 0.25s linear, padding 0.25s linear, max-width 0.25s linear, transform 0.25s linear; 30 | 31 | width: 100%; 32 | margin: 0; 33 | } 34 | 35 | .embetter:hover img { 36 | opacity: 0.9; 37 | -webkit-transform: scale(1.02); 38 | -moz-transform: scale(1.02); 39 | -ms-transform: scale(1.02); 40 | -o-transform: scale(1.02); 41 | transform: scale(1.02); 42 | } 43 | 44 | .embetter.embetter-static:hover img { 45 | opacity: 1; 46 | -webkit-transform: none; 47 | -moz-transform: none; 48 | -ms-transform: none; 49 | -o-transform: none; 50 | transform: none; 51 | } 52 | 53 | .embetter.embetter-playing img { 54 | opacity: 0; 55 | } 56 | 57 | 58 | .embetter .embetter-play-button, 59 | .embetter .embetter-loading { 60 | -webkit-transition: opacity 0.25s linear; 61 | -moz-transition: opacity 0.25s linear; 62 | -ms-transition: opacity 0.25s linear; 63 | -o-transition: opacity 0.25s linear; 64 | transition: opacity 0.25s linear; 65 | } 66 | 67 | .embetter .embetter-play-button, 68 | .embetter .embetter-loading { 69 | position: absolute; 70 | top: 0; 71 | left: 0; 72 | width: 100%; 73 | height: 100%; 74 | overflow: hidden; 75 | cursor: pointer; 76 | } 77 | 78 | .embetter.embetter-playing .embetter-play-button { 79 | opacity: 0; 80 | } 81 | 82 | .embetter .embetter-play-button:before { 83 | background-image: url('data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2286%22%20height%3D%2260%22%20viewBox%3D%220%200%2086%2060%22%3E%3Cpath%20fill%3D%22%23010101%22%20d%3D%22M0%200h86v60h-86z%22/%3E%3Cpath%20fill%3D%22%23fff%22%20d%3D%22M35.422%2017.6v24.8l22.263-12.048z%22/%3E%3C/svg%3E'); 84 | /* */ 85 | background-repeat: no-repeat; 86 | background-position: 50% 50%; 87 | background-size: 33.333% auto; 88 | width: 100%; 89 | max-width: 258px; 90 | height: 100%; 91 | min-height: 100%; 92 | content: " "; 93 | margin: 0 auto; 94 | display: block; 95 | } 96 | 97 | /* Audio services have a round play button */ 98 | .embetter[data-soundcloud-id] div:before, 99 | .embetter[data-mixcloud-id] div:before { 100 | background-image: url('data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2265%22%20height%3D%2265%22%20viewBox%3D%220%200%2065%2065%22%3E%3Ccircle%20fill%3D%22%23010101%22%20cx%3D%2232.5%22%20cy%3D%2232.5%22%20r%3D%2232.5%22/%3E%3Cpath%20fill%3D%22%23fff%22%20d%3D%22M25.095%2020.932v23.136l20.769-11.24z%22/%3E%3C/svg%3E'); 101 | /* */ 102 | max-width: 195px; 103 | } 104 | 105 | .embetter .embetter-loading { 106 | background-color: #000000; 107 | opacity: 0; 108 | } 109 | 110 | .embetter.embetter-playing .embetter-loading { 111 | opacity: 1; 112 | } 113 | 114 | .embetter .embetter-loading:before { 115 | background-repeat: no-repeat; 116 | background-position: 51.7% 50%; 117 | background-size: 9.0909% auto; /* 1/11th of the max width for a background-size of 23px 25px */ 118 | max-width: 253px; 119 | width: 100%; 120 | height: 100%; 121 | min-height: 100%; 122 | content: " "; 123 | margin: 0 auto; 124 | display: block; 125 | } 126 | 127 | .embetter.embetter-playing .embetter-loading:before { 128 | background-image: url("data:image/gif;base64,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"); 129 | } 130 | 131 | .embetter iframe { 132 | position: absolute; 133 | top: 0; 134 | left: 0; 135 | width: 100%; 136 | height: 100%; 137 | } 138 | 139 | 140 | 141 | /* Per-service overrides */ 142 | 143 | .embetter[data-youtube-id], 144 | .embetter[data-dailymotion-id] { 145 | padding-bottom: 56.25%; 146 | height: 0; 147 | } 148 | 149 | .embetter[data-youtube-id] img { 150 | margin: -9.4% 0; 151 | } 152 | 153 | .embetter[data-soundcloud-id], 154 | .embetter[data-bandcamp-id], 155 | .embetter[data-vine-id] { 156 | max-width: 600px; 157 | } 158 | 159 | .embetter[data-mixcloud-id] { 160 | max-width: 600px; 161 | max-height: 600px; 162 | } 163 | .embetter[data-mixcloud-id].embetter-playing { 164 | max-width: 660px; 165 | max-height: 180px; 166 | } 167 | 168 | .embetter[data-codepen-id] { 169 | max-width: 700px; 170 | } 171 | 172 | .embetter[data-ustream-id] { 173 | max-width: 640px; 174 | } 175 | 176 | .embetter[data-slideshare-id] { 177 | max-width: 1080px; 178 | } 179 | 180 | .embetter[data-imgur-id] { 181 | max-width: 540px; 182 | } 183 | 184 | .embetter[data-instagram-id] { 185 | max-width: 640px; 186 | } 187 | .embetter[data-instagram-id].embetter-playing { 188 | max-width: 658px; 189 | } 190 | .embetter[data-instagram-id].embetter-playing img { 191 | padding: 32px 0 48px 0; 192 | } 193 | 194 | 195 | -------------------------------------------------------------------------------- /embetter.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | 3 | window.embetter = {}; 4 | var embetter = window.embetter; 5 | 6 | ///////////////////////////////////////////////////////////// 7 | // COMMON UTIL HELPERS 8 | ///////////////////////////////////////////////////////////// 9 | 10 | embetter.debug = true; 11 | embetter.curEmbeds = []; 12 | embetter.mobileScrollTimeout = null; 13 | embetter.mobileScrollSetup = false; 14 | embetter.apiEnabled = false; 15 | embetter.apiAutoplayCallback = null; 16 | embetter.defaultThumbnail = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAArwAAAGcAQMAAAABMOGrAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAADUExURQAAAKd6PdoAAAA6SURBVHja7cGBAAAAAMOg+VPf4ARVAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAN488AAGP4e1mAAAAAElFTkSuQmCC'; 17 | 18 | embetter.utils = { 19 | ///////////////////////////////////////////////////////////// 20 | // REGEX HELPERS 21 | ///////////////////////////////////////////////////////////// 22 | buildRegex: function(regexStr) { 23 | var optionalPrefix = '(?:https?:\\/\\/)?(?:w{3}\\.)?'; 24 | var terminator = '(?:\\/?|$|\\s|\\?|#)'; 25 | return new RegExp(optionalPrefix + regexStr + terminator); 26 | }, 27 | ///////////////////////////////////////////////////////////// 28 | // BUILD HTML TEMPLATES 29 | ///////////////////////////////////////////////////////////// 30 | stringToDomElement: function(str) { 31 | var div = document.createElement('div'); 32 | div.innerHTML = str; 33 | return div.firstChild; 34 | }, 35 | playerHTML: function(service, mediaUrl, thumbnail, id) { 36 | return '
\ 37 | \ 38 |
'; 39 | }, 40 | isMobile: (function() { 41 | return navigator.userAgent.toLowerCase().match(/iphone|ipad|ipod|android/) ? true : false; 42 | })(), 43 | matches: (function() { 44 | var b = document.createElement('div'); 45 | return b.matches || b.webkitMatchesSelector || b.mozMatchesSelector || b.msMatchesSelector; 46 | })(), 47 | parentSelector: function(node, selector) { 48 | if (this.matches.bind(node)(selector)) { 49 | return node; 50 | } 51 | node = node.parentNode; 52 | while (node && node !== document) { 53 | if (this.matches.bind(node)(selector)) { 54 | return node; 55 | } else { 56 | node = node.parentNode; 57 | } 58 | } 59 | return false; 60 | }, 61 | 62 | ///////////////////////////////////////////////////////////// 63 | // MEDIA PLAYERS PAGE MANAGEMENT 64 | ///////////////////////////////////////////////////////////// 65 | initMediaPlayers: function(el, services) { 66 | for (var i = 0; i < services.length; i++) { 67 | var service = services[i]; 68 | var serviceEmbedContainers = el.querySelectorAll('div['+service.dataAttribute+']'); 69 | for(var j=0; j < serviceEmbedContainers.length; j++) { 70 | embetter.utils.initPlayer(serviceEmbedContainers[j], service); 71 | } 72 | } 73 | // handle mobile auto-embed on scroll 74 | if(embetter.utils.isMobile && embetter.mobileScrollSetup == false) { 75 | window.addEventListener('scroll', embetter.utils.scrollListener); 76 | embetter.mobileScrollSetup = true; 77 | // force scroll to trigger listener on page load 78 | window.scroll(window.scrollX, window.scrollY+1); 79 | window.scroll(window.scrollX, window.scrollY-1); 80 | }; 81 | }, 82 | scrollListener: function() { 83 | // throttled scroll listener 84 | if(embetter.mobileScrollTimeout != null) { 85 | window.clearTimeout(embetter.mobileScrollTimeout); 86 | } 87 | // check to see if embeds are on screen. if so, embed! otherwise, unembed 88 | // exclude codepen since we don't know what might execute 89 | embetter.mobileScrollTimeout = setTimeout(function() { 90 | for (var i = 0; i < embetter.curEmbeds.length; i++) { 91 | var player = embetter.curEmbeds[i]; 92 | var playerRect = player.el.getBoundingClientRect(); 93 | if(playerRect.top < window.innerHeight && playerRect.bottom > 0) { 94 | if(player.getType() !== 'codepen') { // && player.getType() != 'gif' 95 | player.embedMedia(false); 96 | } 97 | } else { 98 | player.unembedMedia(); 99 | } 100 | }; 101 | }, 500); 102 | }, 103 | initPlayer: function(embedEl, service) { 104 | if(embedEl.classList.contains('embetter-ready') == true) return; 105 | if(embedEl.classList.contains('embetter-static') == true) return; 106 | embetter.curEmbeds.push( new embetter.EmbetterPlayer(embedEl, service) ); 107 | }, 108 | unembedPlayers: function(containerEl) { 109 | for (var i = 0; i < embetter.curEmbeds.length; i++) { 110 | if(containerEl && containerEl.contains(embetter.curEmbeds[i].el)) { 111 | embetter.curEmbeds[i].unembedMedia(); 112 | } 113 | }; 114 | }, 115 | disposePlayers: function() { 116 | for (var i = 0; i < embetter.curEmbeds.length; i++) { 117 | embetter.curEmbeds[i].dispose(); 118 | } 119 | window.removeEventListener('scroll', embetter.utils.scrollListener); 120 | embetter.mobileScrollSetup = false; 121 | embetter.curEmbeds.splice(0, embetter.curEmbeds.length-1); 122 | }, 123 | mediaComplete: function() { 124 | if(embetter.curPlayer !== null) { 125 | var playerEl = embetter.curPlayer.el; 126 | var playlistContainer = this.parentSelector(playerEl, '[data-embetter-playlist]'); // check if we're in a playlist container 127 | if(playlistContainer) { 128 | var playlistPlayerEls = playlistContainer.querySelectorAll('.embetter'); 129 | for(var i=0; i < playlistPlayerEls.length - 1; i++) { // skip the last one, since there's nothing else to play 130 | if(playlistPlayerEls[i].classList.contains('embetter-playing')) { // find the active player and tell the next one to play 131 | var nextPlayerObj = embetter.utils.getPlayerFromEl(playlistPlayerEls[i+1]); 132 | if(nextPlayerObj) { 133 | nextPlayerObj.play(); 134 | if(embetter.apiAutoplayCallback) embetter.apiAutoplayCallback(nextPlayerObj.el); 135 | } 136 | break; 137 | } 138 | } 139 | } 140 | } 141 | }, 142 | getPlayerFromEl: function(el) { 143 | for (var i=0; i < embetter.curEmbeds.length; i++) { 144 | if(el === embetter.curEmbeds[i].el) { 145 | return embetter.curEmbeds[i]; 146 | } 147 | } 148 | return null; 149 | }, 150 | disposeDetachedPlayers: function() { 151 | // dispose any players no longer in the DOM 152 | for (var i = embetter.curEmbeds.length - 1; i >= 0; i--) { 153 | var embed = embetter.curEmbeds[i]; 154 | if(document.body.contains(embed.el) === false || embed.el === null) { 155 | embed.dispose(); 156 | delete embetter.curEmbeds.splice(i,1); 157 | } 158 | } 159 | }, 160 | loadRemoteScript: function(scriptURL) { 161 | var tag = document.createElement('script'); 162 | tag.src = scriptURL; 163 | var firstScriptTag = document.getElementsByTagName('script')[0]; 164 | firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); 165 | } 166 | }; 167 | 168 | 169 | ///////////////////////////////////////////////////////////// 170 | // 3RD-PARTY SERVICE SUPPORT 171 | ///////////////////////////////////////////////////////////// 172 | 173 | embetter.services = {}; 174 | 175 | ///////////////////////////////////////////////////////////// 176 | // NATIVE VIDEO 177 | ///////////////////////////////////////////////////////////// 178 | embetter.services.video = { 179 | type: 'video', 180 | dataAttribute: 'data-video-url', 181 | regex: embetter.utils.buildRegex('(.mp4|.mov|.m4v)'), 182 | embed: function (player) { // this.id, this.thumbnail.width, this.thumbnail.height, this.autoplay, this.thumbnail.src 183 | var autoplayAttr = (player.autoplay == true) ? ' autoplay="true" ' : ''; 184 | var loopsAttr = (player.loops == true) ? ' loop="true" ' : ''; 185 | return '