├── .gitignore ├── Gruntfile.js ├── LICENSE ├── README.md ├── changelog.md ├── demo ├── fixed.html ├── fluid.html ├── grid.html └── index.html ├── lazyYT.css ├── lazyYT.jquery.json ├── lazyYT.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | .project 4 | /demo/tmp* -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | grunt.initConfig({ 4 | pkg: grunt.file.readJSON('package.json'), 5 | uglify: { 6 | options: { 7 | banner: '/*!\n' + 8 | '* <%= pkg.name %>\n' + 9 | '* v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' + 10 | '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>; Licensed <%= pkg.license %> %>\n' + 11 | '*/' 12 | }, 13 | dist: { 14 | files: { 15 | '<%= pkg.name %>.min.js': '<%= pkg.name %>.js' 16 | } 17 | } 18 | }, 19 | jshint: { 20 | files: ['Gruntfile.js', '<%= pkg.name %>.js'], 21 | options: { 22 | globals: { 23 | jQuery: true, 24 | console: true, 25 | document: true 26 | } 27 | } 28 | }, 29 | cssmin: { 30 | minify: { 31 | expand: true, 32 | cwd: './', 33 | src: ['*.css', '!*.min.css'], 34 | dest: './', 35 | ext: '.min.css' 36 | } 37 | } 38 | }); 39 | 40 | grunt.loadNpmTasks('grunt-contrib-uglify'); 41 | grunt.loadNpmTasks('grunt-contrib-jshint'); 42 | grunt.loadNpmTasks('grunt-contrib-cssmin'); 43 | 44 | grunt.registerTask('default', ['jshint', 'uglify', 'cssmin']); 45 | 46 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (CC) Creative Commons 2 | Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) 3 | 4 | http://creativecommons.org/licenses/by-sa/4.0/ 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lazyYT.js 2 | 3 | ## Description 4 | 5 | This is a jQuery plugin to lazy load Youtube videos. On the initial load, the `div` will be appended by a preview `img` of the video. On click of the image, the preview `img` will be replaced by the autoplaying `iframe` Youtube video. 6 | 7 | Since 2015-07-08 it uses [Youtube API v3](https://developers.google.com/youtube/v3/). 8 | 9 | 10 | ## Demo 11 | 12 | 1. [Demo of v1.3.0](http://works.daugilas.com/lazyYT/demo/index.html) 13 | 14 | ## Setup 15 | 16 | ```html 17 |
loading...
18 | ``` 19 | 20 | 1. Get Your [Youtube API_KEY](https://developers.google.com/youtube/v3/getting-started#before-you-start). It should look something like this: `AIzaSyCawA87g_pgTbSNPhiWAemy-mFKszJGl4M`. 21 | 2. Include the lazyYT JS and CSS files. 22 | 3. Add a `div` where you want the video to be located. Add the id of the Youtube video to the data attribute `youtube-id`. 23 | 4. Either add the video width and height to `data-width` and `data-height`, or add an ascpent ratio like `16:9` to `data-ratio`, none are required. 24 | 5. Any [optional parameters you wanted passed to the iframe url](https://developers.google.com/youtube/player_parameters) should be added to `data-parameters`. 25 | 6. Get it started with `$('.lazyYT').lazyYT(YOUR_YOUTUBE_API_KEY);` 26 | 27 | Note: make sure to create your own API_KEY, the one used in demo will not work for your domain for long. 28 | 29 | ### Parameters / Settings 30 | 31 | Default parameters: 32 | 33 | ```javascript 34 | $('.js-lazyYT').lazyYT({ 35 | youtube_parameters: 'rel=0', // youtube URL parameters: https://developers.google.com/youtube/player_parameters#Parameters 36 | loading_text: 'Loading...', // displayed instead of video title while its loading 37 | display_title: true, // display title in video's info bar 38 | default_ratio: '16:9', 39 | display_duration: false, // display video duration in bottom right 40 | callback: function() { 41 | console.log(this); 42 | }, 43 | 44 | // Advanced settings 45 | video_loaded_class: 'lazyYT-video-loaded', // adds this class after video loads into container 46 | container_class: 'lazyYT-container' // default CSS depends on this class 47 | }); 48 | ``` 49 | 50 | ## License 51 | 52 | (CC) [Creative Commons](http://creativecommons.org/licenses/by-sa/4.0/) 53 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | Version 1.3.0 *(2016-03-06)* 5 | ---------------------------- 6 | 7 | * Fix: Thumbnail validation. Fallback to smaller images 8 | * Fix: minor fixes for FireFox and trivial updates 9 | 10 | Version 1.2.1 *(2015-09-04)* 11 | ---------------------------- 12 | 13 | * Fix: Play button CSS 14 | 15 | Version 1.2 *(2015-08-30)* 16 | ---------------------------- 17 | 18 | * Fix: updated YouTube UI 19 | * New: `callback` parameter 20 | * New: changelog.md 21 | 22 | Version 1.1.1 *(2015-07-09)* 23 | ---------------------------- 24 | 25 | *Fix: update to work with Youtube API v3 26 | 27 | ---------------------------- 28 | 29 | previous release. No changelog before :( 30 | -------------------------------------------------------------------------------- /demo/fixed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Fixed width - lazyYT jQuery plugin demo 11 | 12 | 13 | 14 | 15 | 30 | 31 | 32 | 33 | Fork me on GitHub 34 | 35 |
36 |

Fixed width video

37 |
38 |
39 | 40 | 41 | 42 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /demo/fluid.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Fluid width - lazyYT jQuery plugin demo 11 | 12 | 13 | 14 | 15 | 31 | 32 | 33 | 34 | Fork me on GitHub 35 | 36 |
37 |

Fluid width video

38 |
39 |
40 | 41 | 42 | 43 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /demo/grid.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Grid - lazyYT jQuery plugin demo 10 | 11 | 12 | 13 | 14 | 36 | 37 | 38 | 39 | Fork me on GitHub 40 | 41 |
42 |

Video grid

43 | 44 |
45 | 46 |
47 |
Loading video...
48 |
49 |
50 |
Loading video...
51 |
52 |
53 |
54 |
55 | 56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | 66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | 76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | 86 |
Just take a look how fast they all load!
87 | 88 |
89 | 90 |
91 | 92 | 93 | 94 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | lazyYT jQuery plugin demo 10 | 11 | 12 | 13 | 14 | 37 | 38 | 39 | 40 | Fork me on GitHub 41 | 42 |
43 |

lazyYT demo

44 |

v1.3.0 (2016-03-06)

45 | 46 | 51 | 52 |
53 | 54 | 55 | 56 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /lazyYT.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * lazyYT (lazy load YouTube videos) 3 | * v1.3.0 - 2016-03-06 4 | * (CC) This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. 5 | * http://creativecommons.org/licenses/by-sa/4.0/ 6 | * Contributors: https://github.com/tylerpearson/lazyYT/graphs/contributors || https://github.com/daugilas/lazyYT/graphs/contributors 7 | */ 8 | 9 | .lazyYT-container { 10 | position: relative; 11 | display: block; 12 | height: 0; 13 | padding: 0 0 56.25% 0; 14 | overflow: hidden; 15 | background-color: #000000; 16 | } 17 | 18 | .lazyYT-container iframe { 19 | position: absolute; 20 | top: 0; 21 | bottom: 0; 22 | left: 0; 23 | width: 100%; 24 | height: 100%; 25 | border: 0; 26 | } 27 | 28 | /* 29 | * Video Title (YouTube style) 30 | */ 31 | 32 | .ytp-gradient-top { 33 | top: 0; 34 | z-index: 21; 35 | width: 100%; 36 | height: 98px; 37 | position: absolute; 38 | pointer-events: none; 39 | background-repeat: repeat-x; 40 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAABiCAQAAAA7fHH2AAAAQUlEQVQYV22KQQoAIAzDyv7/Et/oVlGZVCfkkIYamsENXESyi8vc0u/PKZ6o8+7fZ0h8ShRYnD+BFKToRJkHFpkM2hZSxuyWpEYAAAAASUVORK5CYII=); 41 | -webkit-transition: opacity 0.25s cubic-bezier(0, 0, 0.2, 1); 42 | -moz-transition: opacity 0.25s cubic-bezier(0, 0, 0.2, 1); 43 | transition: opacity 0.25s cubic-bezier(0, 0, 0.2, 1); 44 | } 45 | 46 | .ytp-chrome-top { 47 | position: absolute; 48 | left: 12px; 49 | right: 10px; 50 | top: 0; 51 | font-family: Roboto,Arial,Helvetica,sans-serif; 52 | color: #eee; 53 | text-align: left; 54 | direction: ltr; 55 | font-size: 11px; 56 | line-height: 1.3; 57 | -webkit-font-smoothing: antialiased; 58 | text-shadow: 0 0 2px rgba(0,0,0,.5); 59 | z-index: 60; 60 | -moz-transition: opacity .25s cubic-bezier(0.0,0.0,0.2,1); 61 | -webkit-transition: opacity .25s cubic-bezier(0.0,0.0,0.2,1); 62 | transition: opacity .25s cubic-bezier(0.0,0.0,0.2,1); 63 | } 64 | 65 | .ytp-title { 66 | font-size: 150%; 67 | overflow: hidden; 68 | padding-right: 20px; 69 | white-space: nowrap; 70 | } 71 | 72 | .ytp-title-text { 73 | padding-top: 15px; 74 | display: inline-block; 75 | line-height: 1.1; 76 | vertical-align: top; 77 | max-width: 100%; 78 | margin-left: 4px; 79 | } 80 | 81 | .ytp-title-link { 82 | max-width: 100%; 83 | overflow: hidden; 84 | color: #eee; 85 | text-decoration: none; 86 | white-space: nowrap; 87 | word-wrap: normal; 88 | -o-text-overflow: ellipsis; 89 | text-overflow: ellipsis; 90 | float: left; 91 | } 92 | 93 | /* 94 | * Thumbnail 95 | */ 96 | 97 | .ytp-thumbnail { 98 | position: absolute; 99 | width: 100%; 100 | height: 100%; 101 | top: 0; 102 | left: 0; 103 | z-index: 12; 104 | cursor: pointer; 105 | background-position: 50% 50%; 106 | background-repeat: no-repeat; 107 | -moz-transition: opacity .5s cubic-bezier(0.0,0.0,0.2,1); 108 | -webkit-transition: opacity .5s cubic-bezier(0.0,0.0,0.2,1); 109 | transition: opacity .5s cubic-bezier(0.0,0.0,0.2,1); 110 | -webkit-background-size: cover; 111 | -moz-background-size: cover; 112 | -o-background-size: cover; 113 | background-size: cover; 114 | } 115 | 116 | 117 | .lazyYT-image-loaded .ytp-spinner { 118 | display: none; 119 | } 120 | .ytp-thumbnail button.ytp-button { 121 | visibility: hidden; 122 | } 123 | .ytp-thumbnail.lazyYT-image-loaded button.ytp-button { 124 | visibility: visible; 125 | } 126 | 127 | /* 128 | * Spinner pre-loader 129 | */ 130 | .ytp-spinner { 131 | position: absolute; 132 | left: 45%; 133 | top: 45%; 134 | width: 10%; 135 | height: 10%; 136 | z-index: 16; 137 | } 138 | 139 | .ytp-spinner-message { 140 | position: absolute; 141 | left: 50%; 142 | top: 100%; 143 | width: 300px; 144 | font-size: 127%; 145 | line-height: 182%; 146 | margin-left: -150px; 147 | display: none; 148 | text-align: center; 149 | background-color: black; 150 | opacity: .5 151 | }@keyframes ytp-spinner-dot-fade{0%{opacity:.5;-moz-transform:scale(1.2,1.2);-ms-transform:scale(1.2,1.2);-webkit-transform:scale(1.2,1.2);transform:scale(1.2,1.2)}50%{opacity:.15;-moz-transform:scale(.9,.9);-ms-transform:scale(.9,.9);-webkit-transform:scale(.9,.9);transform:scale(.9,.9)} 152 | 153 | to { 154 | opacity: .15; 155 | -moz-transform: scale(.85,.85); 156 | -ms-transform: scale(.85,.85); 157 | -webkit-transform: scale(.85,.85); 158 | transform: scale(.85,.85) 159 | }}@-moz-keyframes ytp-spinner-dot-fade{0%{opacity:.5;-moz-transform:scale(1.2,1.2);-ms-transform:scale(1.2,1.2);-webkit-transform:scale(1.2,1.2);transform:scale(1.2,1.2)}50%{opacity:.15;-moz-transform:scale(.9,.9);-ms-transform:scale(.9,.9);-webkit-transform:scale(.9,.9);transform:scale(.9,.9)} 160 | 161 | to { 162 | opacity: .15; 163 | -moz-transform: scale(.85,.85); 164 | -ms-transform: scale(.85,.85); 165 | -webkit-transform: scale(.85,.85); 166 | transform: scale(.85,.85) 167 | }}@-webkit-keyframes ytp-spinner-dot-fade{0%{opacity:.5;-moz-transform:scale(1.2,1.2);-ms-transform:scale(1.2,1.2);-webkit-transform:scale(1.2,1.2);transform:scale(1.2,1.2)}50%{opacity:.15;-moz-transform:scale(.9,.9);-ms-transform:scale(.9,.9);-webkit-transform:scale(.9,.9);transform:scale(.9,.9)} 168 | 169 | to { 170 | opacity: .15; 171 | -moz-transform: scale(.85,.85); 172 | -ms-transform: scale(.85,.85); 173 | -webkit-transform: scale(.85,.85); 174 | transform: scale(.85,.85) 175 | }} 176 | 177 | .ytp-spinner-dot { 178 | -moz-animation: ytp-spinner-dot-fade .8s ease infinite; 179 | -webkit-animation: ytp-spinner-dot-fade .8s ease infinite; 180 | animation: ytp-spinner-dot-fade .8s ease infinite; 181 | opacity: 0; 182 | fill: #ccc; 183 | -moz-transform-origin: 4px 4px; 184 | -ms-transform-origin: 4px 4px; 185 | -webkit-transform-origin: 4px 4px; 186 | transform-origin: 4px 4px 187 | } 188 | 189 | .ytp-spinner-dot-1 { 190 | -moz-animation-delay: .1s; 191 | -webkit-animation-delay: .1s; 192 | animation-delay: .1s 193 | } 194 | 195 | .ytp-spinner-dot-2 { 196 | -moz-animation-delay: .2s; 197 | -webkit-animation-delay: .2s; 198 | animation-delay: .2s 199 | } 200 | 201 | .ytp-spinner-dot-3 { 202 | -moz-animation-delay: .3s; 203 | -webkit-animation-delay: .3s; 204 | animation-delay: .3s 205 | } 206 | 207 | .ytp-spinner-dot-4 { 208 | -moz-animation-delay: .4s; 209 | -webkit-animation-delay: .4s; 210 | animation-delay: .4s 211 | } 212 | 213 | .ytp-spinner-dot-5 { 214 | -moz-animation-delay: .5s; 215 | -webkit-animation-delay: .5s; 216 | animation-delay: .5s 217 | } 218 | 219 | .ytp-spinner-dot-6 { 220 | -moz-animation-delay: .6s; 221 | -webkit-animation-delay: .6s; 222 | animation-delay: .6s 223 | } 224 | 225 | .ytp-spinner-dot-7 { 226 | -moz-animation-delay: .7s; 227 | -webkit-animation-delay: .7s; 228 | animation-delay: .7s 229 | } 230 | 231 | /* 232 | * Play button (YouTube style) 233 | */ 234 | .ytp-button:focus, 235 | .ytp-button { 236 | border: none; 237 | outline: 0; 238 | color: inherit; 239 | text-align: inherit; 240 | font-size: 100%; 241 | font-family: inherit; 242 | cursor: default; 243 | line-height: inherit; 244 | 245 | /* margin: 0; */ 246 | padding: 0; 247 | background: transparent; 248 | } 249 | 250 | .ytp-large-play-button { 251 | position: absolute; 252 | left: 50%; 253 | top: 50%; 254 | width: 68px; 255 | height: 48px; 256 | margin-left: -34px; 257 | margin-top: -24px; 258 | -moz-transition: opacity .25s cubic-bezier(0.0,0.0,0.2,1); 259 | -webkit-transition: opacity .25s cubic-bezier(0.0,0.0,0.2,1); 260 | transition: opacity .25s cubic-bezier(0.0,0.0,0.2,1); 261 | } 262 | 263 | .ytp-button:not([aria-disabled=true]):not([disabled]):not([aria-hidden=true]) { 264 | cursor: pointer; 265 | } 266 | 267 | .ytp-large-play-button-bg { 268 | -moz-transition: fill .1s cubic-bezier(0.4, 0.0, 1, 1), opacity .1s cubic-bezier(0.4, 0.0, 1, 1); 269 | -webkit-transition: fill .1s cubic-bezier(0.4, 0.0, 1, 1), opacity .1s cubic-bezier(0.4, 0.0, 1, 1); 270 | transition: fill .1s cubic-bezier(0.4, 0.0, 1, 1), opacity .1s cubic-bezier(0.4, 0.0, 1, 1); 271 | fill: #1f1f1f; 272 | opacity: .9 273 | } 274 | 275 | .ytp-thumbnail:hover .ytp-large-play-button-bg { 276 | -moz-transition: fill .1s cubic-bezier(0.0, 0.0, 0.2, 1), opacity .1s cubic-bezier(0.0, 0.0, 0.2, 1); 277 | -webkit-transition: fill .1s cubic-bezier(0.0, 0.0, 0.2, 1), opacity .1s cubic-bezier(0.0, 0.0, 0.2, 1); 278 | transition: fill .1s cubic-bezier(0.0, 0.0, 0.2, 1), opacity .1s cubic-bezier(0.0, 0.0, 0.2, 1); 279 | fill: #cc181e; 280 | opacity: 1 281 | } 282 | 283 | /* 284 | * Video time (YouTube style) 285 | */ 286 | 287 | .video-time { 288 | position: absolute; 289 | right: 2px; 290 | bottom: 2px; 291 | height: 14px; 292 | padding: 0 4px; 293 | font-family: Arial, Helvetica, Sans-serif; 294 | font-size: 11px; 295 | font-weight: bold; 296 | line-height: 14px; 297 | color: #fff !important; 298 | background-color: #000; 299 | opacity: .75; 300 | filter: alpha(opacity=75); 301 | zoom: 1; 302 | } 303 | -------------------------------------------------------------------------------- /lazyYT.jquery.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lazyYT", 3 | "title": "lazyYT", 4 | "description": "A jQuery plugin to lazy load those darn slow Youtube iframe videos", 5 | "keywords": [ 6 | "jQuery", 7 | "lazy", 8 | "load", 9 | "youtube" 10 | ], 11 | "version": "1.3.0", 12 | "author": { 13 | }, 14 | "maintainers": [ 15 | { 16 | "name": "Daugilas Kakaras", 17 | "email": "daugilas@gmail.com", 18 | "url": "http://daugilas.com" 19 | } 20 | ] 21 | "licenses": [ 22 | { 23 | "type": "CC", 24 | "url": "http://creativecommons.org/licenses/by-sa/4.0/" 25 | } 26 | ], 27 | "bugs": "https://github.com/tylerpearson/lazyYT/issues", 28 | "homepage": "https://github.com/tylerpearson/lazyYT", 29 | "docs": "https://github.com/tylerpearson/lazyYT", 30 | "download": "https://github.com/tylerpearson/lazyYT", 31 | "demo" : "http://tylerp.me/lazyYT/", 32 | "dependencies": { 33 | "jquery": ">=1.8" 34 | } 35 | } -------------------------------------------------------------------------------- /lazyYT.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * lazyYT (lazy load YouTube videos) 3 | * v1.3.0 - 2016-03-06 4 | * (CC) This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. 5 | * http://creativecommons.org/licenses/by-sa/4.0/ 6 | * Contributors: https://github.com/tylerpearson/lazyYT/graphs/contributors || https://github.com/daugilas/lazyYT/graphs/contributors 7 | * 8 | * Usage:
loading...
9 | */ 10 | 11 | ;(function ($) { 12 | 'use strict'; 13 | 14 | function setUp($el, settings) { 15 | var width = $el.data('width'), 16 | height = $el.data('height'), 17 | title = $el.attr('title') || $el.data('title'), 18 | display_title = $el.data('display-title'), 19 | ratio = ($el.data('ratio')) ? $el.data('ratio') : settings.default_ratio, 20 | display_duration = $el.data('display-duration'), 21 | id = $el.data('youtube-id'), 22 | padding_bottom, 23 | innerHtml = [], 24 | $thumb, 25 | thumb_img, 26 | loading_text = $el.text() ? $el.text() : settings.loading_text, 27 | youtube_data_url = ['https://www.googleapis.com/youtube/v3/videos?id=', id, '&key=', settings.yt_api_key, '&part=snippet'].join(''), 28 | youtube_parameters = $el.data('parameters') || ''; 29 | 30 | ratio = ratio.split(":"); 31 | 32 | youtube_parameters += '&' + settings.youtube_parameters; 33 | 34 | if (typeof display_title != "boolean") { 35 | display_title = settings.display_title; 36 | } 37 | 38 | if (typeof display_duration != "boolean") { 39 | display_duration = settings.display_duration; 40 | } 41 | 42 | // width and height might override default_ratio value 43 | if (typeof width === 'number' && typeof height === 'number') { 44 | $el.width(width); 45 | padding_bottom = height + 'px'; 46 | } else if (typeof width === 'number') { 47 | $el.width(width); 48 | padding_bottom = (width * ratio[1] / ratio[0]) + 'px'; 49 | } else { 50 | width = $el.width(); 51 | 52 | // no width means that container is fluid and will be the size of its parent 53 | if (width == 0) { 54 | width = $el.parent().width(); 55 | } 56 | 57 | padding_bottom = (ratio[1] / ratio[0] * 100) + '%'; 58 | } 59 | 60 | // 61 | // This HTML will be placed inside 'lazyYT' container 62 | 63 | innerHtml.push('
'); 64 | 65 | // Play button from YouTube (exactly as it is in YouTube) 66 | innerHtml.push(''); // end of .ytp-large-play-button 80 | 81 | innerHtml.push('
'); 82 | innerHtml.push(''); 83 | innerHtml.push(''); 84 | innerHtml.push(''); 85 | innerHtml.push(''); 86 | innerHtml.push(''); 87 | innerHtml.push(''); 88 | innerHtml.push(''); 89 | innerHtml.push(''); 90 | innerHtml.push(''); 91 | innerHtml.push(''); 92 | innerHtml.push(''); 93 | innerHtml.push(''); 94 | innerHtml.push(''); 95 | innerHtml.push('
'); // end of .ytp-spinner 96 | 97 | // video time from YouTube (exactly as it is in YouTube) 98 | if (display_duration) { 99 | innerHtml.push(''); 100 | } 101 | innerHtml.push('
'); // end of .ytp-thumbnail 102 | 103 | // Video title (info bar) 104 | if (display_title) { 105 | innerHtml.push('
'); 106 | innerHtml.push('
'); 107 | innerHtml.push('
'); 108 | innerHtml.push(''); // /.ytp-title-text 113 | innerHtml.push('
'); // /.ytp-title 114 | innerHtml.push('
'); // /.ytp-chrome-top 115 | } 116 | 117 | $el.css({ 118 | 'padding-bottom': padding_bottom 119 | }) 120 | .html(innerHtml.join('')); 121 | 122 | $thumb = $el.find('.ytp-thumbnail').on('click', function (e) { 123 | e.preventDefault(); 124 | if (!$el.hasClass(settings.video_loaded_class)) { 125 | $el.html('') 126 | .addClass(settings.video_loaded_class); 127 | 128 | // execute callback 129 | if (typeof settings.callback == 'function') { // make sure the callback is a function 130 | settings.callback.call($el); // brings the scope to the callback 131 | } 132 | } 133 | }); 134 | loadBackgroundImage(id, width, $thumb, youtube_data_url); 135 | 136 | if ((!title && display_title) || display_duration) { 137 | if (display_duration) youtube_data_url += ',contentDetails'; // this extra info now costs some quota points, so we retrieve it only when necessary. More on quota: https://developers.google.com/youtube/v3/getting-started#quota 138 | 139 | $.getJSON(youtube_data_url, function (data) { 140 | var item = data.items[0]; 141 | // console.log(item.snippet.title); 142 | 143 | $el.find('#lazyYT-title-' + id).text(item.snippet.title); 144 | 145 | if (display_duration) { 146 | $el.find('.video-time') 147 | .text(parseDuration(item.contentDetails.duration, settings)) 148 | .show(); 149 | } 150 | 151 | }); 152 | } 153 | 154 | }; 155 | 156 | function loadBackgroundImage(id, width, $thumb, youtube_data_url) { 157 | var thumb_img, 158 | thumb_url, 159 | downloadingImage = new Image(); 160 | 161 | if (width == 0) width = $thumb.width(); // sometimes (on fluid layout) it fails to get proper width immediately - so we try here again 162 | if (width > 640) { 163 | thumb_img = 'maxresdefault.jpg'; 164 | } else if (width > 480) { 165 | thumb_img = 'sddefault.jpg'; 166 | } else if (width > 320) { 167 | thumb_img = 'hqdefault.jpg'; 168 | } else if (width > 120) { 169 | thumb_img = 'mqdefault.jpg'; 170 | } else if (width == 0) { // sometimes it still might fail on fluid layout 171 | thumb_img = 'hqdefault.jpg'; 172 | } else { 173 | thumb_img = 'default.jpg'; 174 | } 175 | 176 | thumb_url = ['https://img.youtube.com/vi/', id, '/', thumb_img].join(''); 177 | 178 | downloadingImage.onload = function(data) { 179 | var naturalWidth = getOnloadDataParam(data, 'naturalWidth'); 180 | 181 | /* 182 | * Sometimes instead of an expected higher resolution image we get this 120x90 px 183 | * default img: https://img.youtube.com/vi/default.jpg 184 | * That sucks. So lets extract a proper thumbnail from YouTube API! 185 | */ 186 | if (naturalWidth < width) { 187 | $.getJSON(youtube_data_url, function (data) { 188 | var item = data.items[0], 189 | thumbs = item.snippet.thumbnails; 190 | if (width == 0) width = $thumb.width(); // just to make sure we have width 191 | if (width > 640 && typeof thumbs.maxres == 'object') { 192 | thumb_url = thumbs.maxres.url; 193 | } else if (width > 480 && typeof thumbs.standard == 'object') { 194 | thumb_url = thumbs.standard.url; 195 | } else if (width > 320 && typeof thumbs.high == 'object') { 196 | thumb_url = thumbs.high.url; 197 | } else if (width > 120 && typeof thumbs.medium == 'object') { 198 | thumb_url = thumbs.medium.url; 199 | } else { 200 | thumb_url = thumbs['default'].url; 201 | } 202 | setBackgroundImage($thumb, thumb_url); 203 | }); 204 | } else { 205 | setBackgroundImage($thumb, this.src); 206 | } 207 | 208 | }; 209 | 210 | downloadingImage.src = thumb_url; 211 | } 212 | 213 | function setBackgroundImage($thumb, url) { 214 | $thumb.css({ 215 | 'background-image': ['url(', url, ')'].join('') 216 | }).addClass('lazyYT-image-loaded'); 217 | } 218 | 219 | function getOnloadDataParam(data, key) { 220 | var img_data; 221 | if (typeof data.path == 'object') { 222 | img_data = data.path[0]; 223 | } else if(typeof data.target == 'object') { 224 | img_data = data.target; 225 | } else { 226 | img_data = data.originalTarget; 227 | } 228 | return img_data.naturalWidth; 229 | } 230 | 231 | function parseDuration(PT, settings) { 232 | var output = []; 233 | var durationInSec = 0; 234 | var matches = PT.match(/P(?:(\d*)Y)?(?:(\d*)M)?(?:(\d*)W)?(?:(\d*)D)?T(?:(\d*)H)?(?:(\d*)M)?(?:(\d*)S)?/i); 235 | var parts = [ 236 | { // years 237 | pos: 1, 238 | multiplier: 86400 * 365 239 | }, 240 | { // months 241 | pos: 2, 242 | multiplier: 86400 * 30 243 | }, 244 | { // weeks 245 | pos: 3, 246 | multiplier: 604800 247 | }, 248 | { // days 249 | pos: 4, 250 | multiplier: 86400 251 | }, 252 | { // hours 253 | pos: 5, 254 | multiplier: 3600 255 | }, 256 | { // minutes 257 | pos: 6, 258 | multiplier: 60 259 | }, 260 | { // seconds 261 | pos: 7, 262 | multiplier: 1 263 | } 264 | ]; 265 | 266 | for (var i = 0; i < parts.length; i++) { 267 | if (typeof matches[parts[i].pos] != 'undefined') { 268 | durationInSec += parseInt(matches[parts[i].pos]) * parts[i].multiplier; 269 | } 270 | } 271 | 272 | // Hours extraction 273 | if (durationInSec > 3599) { 274 | output.push(parseInt(durationInSec / 3600)); 275 | durationInSec %= 3600; 276 | } 277 | // Minutes extraction with leading zero 278 | output.push(('0' + parseInt(durationInSec / 60)).slice(-2)); 279 | // Seconds extraction with leading zero 280 | output.push(('0' + durationInSec % 60).slice(-2)); 281 | 282 | return output.join(':'); 283 | }; 284 | 285 | $.fn.lazyYT = function (yt_api_key, newSettings) { 286 | var defaultSettings = { 287 | yt_api_key: yt_api_key, 288 | 289 | youtube_parameters: 'rel=0', 290 | loading_text: 'Loading...', 291 | display_title: true, 292 | default_ratio: '16:9', 293 | display_duration: false, 294 | callback: null, 295 | 296 | // Advanced settings 297 | video_loaded_class: 'lazyYT-video-loaded', 298 | container_class: 'lazyYT-container' 299 | }; 300 | var settings = $.extend(defaultSettings, newSettings); 301 | 302 | return this.each(function () { 303 | var $el = $(this).addClass(settings.container_class); 304 | setUp($el, settings); 305 | }); 306 | }; 307 | 308 | }(jQuery)); 309 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lazyyt", 3 | "version": "1.3.0", 4 | "description": "A jQuery plugin to lazy load those darn slow Youtube iframe videos", 5 | "main": "lazyyt.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git://github.com/Daugilas/lazyYT.git" 9 | }, 10 | "keywords": [ 11 | "jQuery", 12 | "lazy load", 13 | "youtube" 14 | ], 15 | "authors": [ 16 | "Daugilas Kakaras (http://daugilas.com)", 17 | "Tyler Pearson (http://tylerp.me)" 18 | ], 19 | "license": "CC", 20 | "bugs": { 21 | "url": "https://github.com/Daugilas/lazyYT/issues" 22 | }, 23 | "devDependencies": { 24 | "grunt": "~0.4.2", 25 | "grunt-contrib-jshint": "~0.6.3", 26 | "grunt-contrib-uglify": "~0.2.2", 27 | "grunt-contrib-cssmin": "~0.9.0" 28 | } 29 | } 30 | --------------------------------------------------------------------------------