├── .gitattributes ├── LICENSE ├── README.md ├── legacy ├── README.md ├── index.html └── ytembed.js └── index.html /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, 2012 Jonathan Prusik 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Custom YouTube Embed Generator 2 | ============================== 3 | custom-yt-embed 4 | 5 | Purpose 6 | ------- 7 | YouTube embeds are surprisingly versatile; there are numerous URL parameters to allow customization, but they can become unwieldy fast. To solve this, I've put together this JavaScript tool which generates embed codes for all known, working, and relevant embed parameters. 8 | 9 | Usage 10 | ----- 11 | To see the tool in action, you can find a working example [here](https://jprusik.github.io/custom-yt-embed/index.html). (The legacy version of the tool is [here](https://jprusik.github.io/custom-yt-embed/legacy/index.html).) 12 | 13 | Upon option changes, the form value from each field will update the YouTube embed code and previews. An API request to [Google's YouTube data feed](https://developers.google.com/youtube/v3/) will also fire, and present additional information about the video upon a successful request. 14 | 15 | The "Search term playlist" value will generate a playlist based on the results of a search of the term you entered. 16 | 17 | The "On-the-fly playlist" value will generate a playlist based on comma-separated values of YouTube video ids. 18 | 19 | Both playlist options will begin playing videos in the playlist directly after the currently active video. You can also access the playlist menu in the video embed itself. 20 | 21 | The "Custom Parameter" field allows for parameters not supported by this tool to be easily included in the embed code output. 22 | 23 | To change thumbnails, you must already be logged into the active video's YouTube account. Clicking the thumbnails will only bring you the relevant Video Options page, and have no functionality on their own. 24 | 25 | Limitations/Known Issues 26 | ----------- 27 | The Custom YouTube Embed Generator currently only generates code for YouTube's newest, preferred methodology, which uses an HTML5-friendly iframe to automatically switch to the most appropriate embedding method for the viewer. The old (now officially deprecated) method used Flash only, and while it may still work in some cases, there are continually fewer reasons to use it as the iframe embeds becomes more robust. 28 | 29 | Author 30 | ------- 31 | Jonathan Prusik @jprusik [www.classynemesis.com] 32 | 33 | License 34 | ------- 35 | Custom YouTube Embed Generator is released under the MIT License. 36 | -------------------------------------------------------------------------------- /legacy/README.md: -------------------------------------------------------------------------------- 1 | Custom YouTube Embed Generator 2 | ============================== 3 | custom-yt-embed 4 | 5 | ## THIS VERSION OF THE TOOL IS NO LONGER MAINTAINED. THE LATEST VERSION CAN BE FOUND [HERE](https://jprusik.github.io/custom-yt-embed/index.html). 6 | 7 | Purpose 8 | ------- 9 | YouTube embeds are surprisingly versatile; there are numerous URL parameters to allow customization, but they can become unwieldy fast, and Google is constantly deprecating and adding tags without any kind of documentation. To solve this, I've put together this JavaScript tool which generates embed codes for any number of customization options, updated regularly with all known, working, and relevant embed parameters. 10 | 11 | Usage 12 | ----- 13 | To see the tool in action, you can find a working example [here](https://jprusik.github.io/custom-yt-embed/legacy/index.html). (The new version of the tool is [here](https://jprusik.github.io/custom-yt-embed/index.html).) 14 | 15 | The `generate()` function can be called on page load, button click, or however else you wish. This will take the values from each field and parse the YouTube embed syntax accordingly. 16 | 17 | The "Custom Parameter" field allows for parameters not supported by this tool to be easily included in the embed code output. 18 | 19 | To change thumbnails, you must already be logged into the active video's YouTube account. Clicking the thumbnails will only bring you the relevant Video Options page, and have no functionality on their own. 20 | 21 | Limitations/Known Issues 22 | ----------- 23 | The Custom YouTube Embed Generator currently only generates code for YouTube's newest, preferred methodology, which uses an HTML5-friendly iframe to automatically switch to the most appropriate embedding method for the viewer. The old method used Flash only, and while it will still work, there are continually fewer reasons to use it as the iframe approach becomes more robust. 24 | 25 | The watermark, custom preview image, Call-to-action, social media buttons, and custom frame functionalities are not officially supported by Google and are not embed parameters. As such, you may experience unexpected behavior when using these options. 26 | 27 | The preview image does not correctly scale for IE versions earlier than 9, and will be addressed in later revisions. 28 | 29 | Video info displays information pulled from the last valid video id, even if an invalid id is the present value. 30 | 31 | The "HD" thumbnail link results in a error image if the queried video does not have a vertical resolution of 720 or greater. 32 | 33 | The Pinterest button displays at a slightly lower position than other social media buttons. 34 | 35 | The JavaScript included has NOT been thoroughly cleaned - incomplete features and features-in-development are commented out, but may be present in the source. 36 | 37 | Author 38 | ------- 39 | Jonathan Prusik @jprusik [www.classynemesis.com] 40 | 41 | License 42 | ------- 43 | Custom YouTube Embed Generator is released under the MIT License. 44 | -------------------------------------------------------------------------------- /legacy/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | YouTube Custom Embed Generator 6 | 144 | 145 | 146 | 190 | 191 | 192 |
193 |
194 |

YouTube Logo Custom Video Embed Generator

195 |
196 |
Warning! This version of the tool is no longer updated (the last significant update was in 2012). The new, rebuilt version of this tool can be found here.
197 | 198 | 199 | 200 | 302 | 347 | 348 |
201 |
202 |
203 |
204 | 205 | 206 |
207 | YouTube Video Id or URL
208 |

209 | 210 |
211 |
212 |  Width (pixels)
213 |  Height (pixels)
214 |
215 |
216 | Start Video at:  seconds
217 | End Video at:  seconds
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 | 227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 | 235 |
236 |
237 |
238 |
239 |

240 | Custom Parameter
241 |

242 | Search term playlist (single-word search term)
243 |

244 | On-the-fly playlist (comma-seperated video IDs)
245 |
246 |
247 |
248 |

Social Media Share Buttons

249 |

250 | 258 |
259 |
260 |

Custom Watermark

261 | (Not supported by Internet Explorer 6 or earlier)
262 |

263 | 278 |
279 |
280 |

Custom Preview Image

281 | (Not supported by Internet Explorer 7 or earlier)
282 |

283 | 287 |
288 |
289 |

Call-to-Action

290 | (Not supported by Internet Explorer 7 or earlier)
291 |

292 | 298 |
299 |
300 |
301 |
303 | Embed Code
304 |

305 | 306 | 307 | 314 | 322 | 323 |
308 | Title: 

309 | Author: 

310 | Published: 

311 | Views: 

312 | Duration: 

313 |
315 | Direct Link
316 |

317 | Short Link
318 |

319 | Fullscreen Link
320 |


321 |
324 |
325 | Description: 326 |
327 |
328 |
329 | Click on the images below to open video settings page (must be logged into YouTube)

330 | 331 | 332 | 336 | 340 | 341 |
333 | Current Video Thumbnail

334 |
335 |
337 | Video Thumbnail Choices

338 |
339 |
342 |
343 | Preview:
344 | 345 | 346 |
349 |
350 |
351 |
352 |
353 | 354 | 355 | -------------------------------------------------------------------------------- /legacy/ytembed.js: -------------------------------------------------------------------------------- 1 | var apividtitle; 2 | var apividdec; 3 | var apividviews; 4 | var apividpubupdate; 5 | var apividauthor; 6 | var apividduration; 7 | 8 | /* Get video info and display */ 9 | function vidinfo(){ 10 | document.getElementById('ytdata').innerHTML = null; 11 | var scriptcontainer = document.getElementById('ytdata'); 12 | var script = document.createElement('script'); 13 | script.type = 'text/javascript'; 14 | script.src = 'https://gdata.youtube.com/feeds/api/videos/'+document.getElementById('ytid').value+'?v=2&alt=json-in-script&format=5&callback=getytinfo'; 15 | scriptcontainer.appendChild(script); 16 | getytinfo(); 17 | } 18 | var getytinfo = function (info){ 19 | apividtitle = info.entry.title.$t; 20 | apividdec = info.entry.media$group.media$description.$t; 21 | apividviews = info.entry.yt$statistics.viewCount; 22 | apividpubupdate = new Date(info.entry.published.$t).toLocaleDateString(); 23 | apividauthor = info.entry.author[0].name.$t; 24 | apividduration = info.entry.media$group.yt$duration.seconds; 25 | document.getElementById('vidtitle').innerHTML = ''+apividtitle+'<\/a>'; 26 | document.getElementById('viddesc').innerHTML = apividdec; 27 | document.getElementById('vidviews').innerHTML = numberWithCommas(apividviews); 28 | document.getElementById('vidpubdate').innerHTML = apividpubupdate; 29 | document.getElementById('vidauthor').innerHTML = ''+apividauthor+'<\/a>'; 30 | document.getElementById('vidduration').innerHTML = Math.floor(apividduration/60)+'m '+(apividduration%60)+'s'; 31 | }; 32 | 33 | /* Adds commas to passed value */ 34 | function numberWithCommas(x) { 35 | return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); 36 | } 37 | 38 | /* Parse the Youtube video id out of a pasted video URL */ 39 | function parseid(){ 40 | document.getElementById('ytidmess').innerHTML = ""; 41 | var url = document.getElementById('ytid').value; 42 | var regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=)([^#\&\?]*).*/; 43 | var match = url.match(regExp); 44 | if(document.getElementById('ytid').value.length !== 11){ 45 | if(match&&match[2].length===11){ 46 | document.getElementById('ytid').value = match[2]; 47 | } 48 | else{ 49 | document.getElementById('ytidmess').innerHTML = "The value you entered is not a valid YouTube URL"; 50 | } 51 | } 52 | imgcheck(); 53 | vidinfo(); 54 | } 55 | 56 | /* Load the image first so the size can be calculated properly */ 57 | function imgcheck() { 58 | /* Run check on field value first, not the image */ 59 | var img = new Image(); 60 | img.src = document.getElementById('wmlocation').value; 61 | if(img.src !== ""){ 62 | //If the img doesn't load properly (incorrect image address), kill the process, notify the user 63 | //img.onerror = alert("image fail!"); 64 | img.onload = generate(); 65 | } 66 | else{ 67 | alert('no image specified!'); 68 | } 69 | } 70 | 71 | /* After imgcheck function, check selected options and build embed code */ 72 | function generate() { 73 | /* If the embed size is set to less than 200x200, warn the user */ 74 | if((parseInt(document.getElementById('height').value,10) < 200) || (parseInt(document.getElementById('width').value,10) < 200)){ 75 | alert('Warning: YouTube does not support video embed sizes smaller than 200x200.'); 76 | } 77 | /* Set preview values to empty so the contents refresh rather than stack */ 78 | document.getElementById('embedcode').value = ""; 79 | document.getElementById('linkpreview').innerHTML = ""; 80 | document.getElementById('linkpreview2').innerHTML = ""; 81 | document.getElementById('linkpreview3').innerHTML = ""; 82 | document.getElementById('tbpreview').innerHTML = ""; 83 | document.getElementById('tbactive').innerHTML = ""; 84 | /*document.getElementById('vidwrap').innerHTML = "";*/ 85 | document.getElementById('wmcontainer').innerHTML = ""; 86 | var finalstring = ""; 87 | var width = 'width=\"'+document.getElementById('width').value+'\" '; 88 | var height = 'height=\"'+document.getElementById('height').value+'\" '; 89 | var ytid = document.getElementById('ytid').value; 90 | var custompara = document.getElementById('custompara').value; 91 | var playlist = document.getElementById('playlist').value; 92 | var searchplaylist = document.getElementById('searchplaylist').value; 93 | var start = document.getElementById('start').value; 94 | var end = document.getElementById('end').value; 95 | /* Social media buttons variables */ 96 | var socialurl='http://youtu.be/'+document.getElementById('ytid').value; 97 | var socialdescription=apividtitle; 98 | var socialstring = ""; 99 | var twittervia = document.getElementById('twittervia').value; 100 | var FacebookVal = ""; 101 | var TwitterVal = ""; 102 | var GoogleplusVal= ""; 103 | var LinkedInVal = ""; 104 | var PinterestVal = ""; 105 | var CommentsVal = ""; 106 | /*Watermark variables*/ 107 | var wmlocation = document.getElementById('wmlocation').value; 108 | var wmlink = document.getElementById('wmlink').value; 109 | var wmxcontrol = parseInt(document.getElementById('wmxcontrol').value,10); 110 | var wmycontrol = parseInt(document.getElementById('wmycontrol').value,10); 111 | /*Custom thumbnail variables*/ 112 | var prevthumblink = document.getElementById('prevthumblink').value; 113 | /*CTA variables*/ 114 | var ctaimg = document.getElementById('ctathumbimg').value; 115 | var ctalink = document.getElementById('ctathumblink').value; 116 | /*Set HTTP value based on set security*/ 117 | var protocol = ""; 118 | if(document.getElementById('security').checked){ 119 | protocol = "https://"; 120 | } 121 | else{ 122 | protocol = "http://"; 123 | } 124 | /*GETTING IMAGE SIZE START*/ 125 | document.getElementById('wmcontainer').innerHTML = ' 149 ? \'150px\' : \'auto\' );height: expression( this.scrollHeight > 74 ? \'75px\' : \'auto\' );max-width:150px;max-height:75px;\" src=\"'+wmlocation+'\"\/>'; 126 | var imgheight = document.querySelector("#wmprev").height; 127 | var imgwidth = document.querySelector("#wmprev").width; 128 | /*Position watermark with percentage of window values*/ 129 | var wmhpos = ((wmxcontrol/100)*(parseInt(document.getElementById('width').value,10)))-(imgwidth/2); 130 | var wmvpos = (wmycontrol/100)*(parseInt(document.getElementById('height').value,10))+(imgheight/2); 131 | /*Style Custom UI*/ 132 | var customuistyle = ''; 133 | if(document.getElementById('theme').checked === true){ 134 | customuistyle = 'border: 1px solid lightgray;padding:15px 15px 0px 15px;background:#cbcbcb;border-radius:15px;-moz-box-shadow:3px 3px 3px black;-webkit-box-shadow:3px 3px 3px black;box-shadow:3px 3px 3px black;'; 135 | } 136 | else{ 137 | customuistyle = 'padding:15px 15px 0px 15px;background:#1a1a1a;border-radius:15px;-moz-box-shadow:3px 3px 3px black;-webkit-box-shadow:3px 3px 3px black;box-shadow:3px 3px 3px black;'; 138 | } 139 | var ytstring = 'http:\/\/www.youtube.com\/watch?v='+document.getElementById('ytid').value; 140 | var ytstring2 = 'http:\/\/youtu.be\/'+document.getElementById('ytid').value; 141 | var ytstring3 = 'http:\/\/www.youtube.com\/v\/'+document.getElementById('ytid').value; 142 | var tbstring = '<\/a><\/a><\/a>'; 143 | var tbactive = '<\/a>Click for HD Version<\/a>'; 144 | if((wmlocation !== "") && (document.getElementById('watermark').checked)){ 145 | if(ytid === document.getElementById('ytid').value){ 146 | ytid += '?wmode=opaque'; 147 | } 148 | else{ 149 | ytid += '&wmode=opaque'; 150 | } 151 | } 152 | if(document.getElementById('720hd').checked){ 153 | if(ytid === document.getElementById('ytid').value){ 154 | ytid += '?vq=hd720'; 155 | } 156 | else{ 157 | ytid += '&vq=hd720'; 158 | } 159 | } 160 | if(document.getElementById('1080hd').checked){ 161 | if(ytid === document.getElementById('ytid').value){ 162 | ytid += '?vq=hd1080'; 163 | } 164 | else{ 165 | ytid += '&vq=hd1080'; 166 | } 167 | } 168 | if(document.getElementById('modestbranding').checked){ 169 | if(ytid === document.getElementById('ytid').value){ 170 | ytid += '?modestbranding=1'; 171 | } 172 | else{ 173 | ytid += '&modestbranding=1'; 174 | } 175 | } 176 | if(document.getElementById('autoplay').checked){ 177 | if(ytid === document.getElementById('ytid').value){ 178 | ytid += '?autoplay=1'; 179 | } 180 | else{ 181 | ytid += '&autoplay=1'; 182 | } 183 | } 184 | if(document.getElementById('ccload').checked){ 185 | if(ytid === document.getElementById('ytid').value){ 186 | ytid += '?cc_load_policy=1'; 187 | } 188 | else{ 189 | ytid += '&cc_load_policy=1'; 190 | } 191 | } 192 | if(document.getElementById('controls').checked){ 193 | if(ytid === document.getElementById('ytid').value){ 194 | ytid += '?controls=0'; 195 | } 196 | else{ 197 | ytid += '&controls=0'; 198 | } 199 | } 200 | if(document.getElementById('fullscreenbutton').checked){ 201 | if(ytid === document.getElementById('ytid').value){ 202 | ytid += '?fs=0'; 203 | } 204 | else{ 205 | ytid += '&fs=0'; 206 | } 207 | } 208 | if(document.getElementById('annotations').checked){ 209 | if(ytid === document.getElementById('ytid').value){ 210 | ytid += '?iv_load_policy=3'; 211 | } 212 | else{ 213 | ytid += '&iv_load_policy=3'; 214 | } 215 | } 216 | if(document.getElementById('loop').checked){ 217 | if(ytid === document.getElementById('ytid').value){ 218 | ytid += '?loop=1'; 219 | } 220 | else{ 221 | ytid += '&loop=1'; 222 | } 223 | } 224 | if(document.getElementById('related').checked){ 225 | if(ytid === document.getElementById('ytid').value){ 226 | ytid += '?rel=0'; 227 | } 228 | else{ 229 | ytid += '&rel=0'; 230 | } 231 | } 232 | if(document.getElementById('showinfo').checked){ 233 | if(ytid === document.getElementById('ytid').value){ 234 | ytid += '?showinfo=0'; 235 | } 236 | else{ 237 | ytid += '&showinfo=0'; 238 | } 239 | } 240 | if(document.getElementById('theme').checked){ 241 | if(ytid === document.getElementById('ytid').value){ 242 | ytid += '?theme=light'; 243 | } 244 | else{ 245 | ytid += '&theme=light'; 246 | } 247 | } 248 | if(document.getElementById('playbarcolor').checked){ 249 | if(ytid === document.getElementById('ytid').value){ 250 | ytid += '?color=white'; 251 | } 252 | else{ 253 | ytid += '&color=white'; 254 | } 255 | } 256 | if(document.getElementById('autohide').checked){ 257 | if(ytid === document.getElementById('ytid').value){ 258 | ytid += '?autohide=0'; 259 | } 260 | else{ 261 | ytid += '&autohide=0'; 262 | } 263 | } 264 | if(document.getElementById('kbcontrols').checked){ 265 | if(ytid === document.getElementById('ytid').value){ 266 | ytid += '?disablekb=1'; 267 | } 268 | else{ 269 | ytid += '&disablekb=1'; 270 | } 271 | } 272 | if(custompara !== ""){ 273 | if(ytid === document.getElementById('ytid').value){ 274 | ytid += '?'+custompara; 275 | } 276 | else{ 277 | ytid += '&'+custompara; 278 | } 279 | } 280 | if(searchplaylist !== ""){ 281 | if(ytid === document.getElementById('ytid').value){ 282 | ytid += '?q='+searchplaylist; 283 | } 284 | else{ 285 | ytid += '&q='+searchplaylist; 286 | } 287 | } 288 | if(playlist !== ""){ 289 | if(ytid === document.getElementById('ytid').value){ 290 | ytid += '?playlist='+playlist; 291 | } 292 | else{ 293 | ytid += '&playlist='+playlist; 294 | } 295 | } 296 | if(start !== ""){ 297 | if(ytid === document.getElementById('ytid').value){ 298 | ytid += '?start='+start; 299 | } 300 | else{ 301 | ytid += '&start='+start; 302 | } 303 | } 304 | if(end !== ""){ 305 | if(ytid === document.getElementById('ytid').value){ 306 | ytid += '?end='+end; 307 | } 308 | else{ 309 | ytid += '&end='+end; 310 | } 311 | } 312 | /* Social media buttons check */ 313 | if(twittervia === "Twitter Handle (optional)"){ 314 | twittervia = ""; 315 | } 316 | if(document.getElementById('Facebook').checked){ 317 | FacebookVal='`; 525 | 526 | var preview_output = embed_output; 527 | 528 | document.getElementById('embed-code').value = embed_output; 529 | document.getElementById('embed-preview').innerHTML = preview_output; 530 | } 531 | }; 532 | 533 | var updateVideoInfo = function(){ 534 | var video_id = document.getElementById('youtube-id').value; 535 | 536 | document.getElementById('direct-link').innerHTML = 'https://www.youtube.com/watch?v='+video_id; 537 | document.getElementById('direct-link').href = 'https://www.youtube.com/watch?v='+video_id; 538 | document.getElementById('short-link').innerHTML = 'https://youtu.be/'+video_id; 539 | document.getElementById('short-link').href = 'https://youtu.be/'+video_id; 540 | document.getElementById('fullscreen-link').innerHTML = 'https://www.youtube.com/v/'+video_id; 541 | document.getElementById('fullscreen-link').href = 'https://www.youtube.com/v/'+video_id; 542 | document.getElementById('video-edit-link').href= 'https://www.youtube.com/edit?video_id='+video_id; 543 | document.getElementById('video-endscreen-link').href= 'https://www.youtube.com/endscreen?v='+video_id; 544 | document.getElementById('video-title').value = 'A YouTube video'; 545 | 546 | ['current-thumbnail','first-thumbnail','second-thumbnail','third-thumbnail'].forEach(function(id,index){ 547 | if (index === 0){ 548 | index = 'default'; 549 | } 550 | 551 | document.getElementById(id).innerHTML = ''; 552 | }); 553 | 554 | $('#video-info, #video-links, #video-thumbnails').show(); 555 | 556 | return getVideoMetadata(video_id); 557 | }; 558 | 559 | var form_id = 'youtube-options'; 560 | var YOUTUBE_API_KEY = 'AIzaSyAKX2c_yQUOehC3bLDFA5NonWqmtp8cmRU'; 561 | window.errors = { 562 | 'video_id_invalid':{ 563 | 'active': false, 564 | 'message': 'The value you entered does not appear to be a valid video URL.' 565 | }, 566 | 'video_embed_too_small':{ 567 | 'active': false, 568 | 'message': 'Warning: YouTube does not support video embed sizes smaller than 200x200.' 569 | }, 570 | 'video_metadata_fetch_failed': { 571 | 'active': false, 572 | 'message': 'There was a problem fetching metadata for the specified video.' 573 | } 574 | }; 575 | 576 | // TODO: limit the rate at which the listeners fire 577 | 578 | var last_video_id = document.getElementById('youtube-id').value; 579 | 580 | // If any form values change 581 | document.forms[form_id].onchange = function(){ 582 | // If video ID changed (api requests happen) 583 | var current_video_id = document.getElementById('youtube-id').value; 584 | if (current_video_id !== last_video_id){ 585 | parseVideoId(current_video_id); 586 | updateVideoInfo(); 587 | 588 | last_video_id = current_video_id; 589 | } 590 | 591 | updateEmbedAndPreview(); 592 | checkVideoDimensions(); 593 | alertMessage(); 594 | }; 595 | 596 | // On page load 597 | updateVideoInfo().finally(updateEmbedAndPreview); 598 | }); 599 | 600 | // Polyfills 601 | // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys 602 | if (!Object.keys) { 603 | Object.keys = (function() { 604 | 'use strict'; 605 | var hasOwnProperty = Object.prototype.hasOwnProperty, 606 | hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'), 607 | dontEnums = [ 608 | 'toString', 609 | 'toLocaleString', 610 | 'valueOf', 611 | 'hasOwnProperty', 612 | 'isPrototypeOf', 613 | 'propertyIsEnumerable', 614 | 'constructor' 615 | ], 616 | dontEnumsLength = dontEnums.length; 617 | 618 | return function(obj) { 619 | if (typeof obj !== 'function' && (typeof obj !== 'object' || obj === null)) { 620 | throw new TypeError('Object.keys called on non-object'); 621 | } 622 | 623 | var result = [], prop, i; 624 | 625 | for (prop in obj) { 626 | if (hasOwnProperty.call(obj, prop)) { 627 | result.push(prop); 628 | } 629 | } 630 | 631 | if (hasDontEnumBug) { 632 | for (i = 0; i < dontEnumsLength; i++) { 633 | if (hasOwnProperty.call(obj, dontEnums[i])) { 634 | result.push(dontEnums[i]); 635 | } 636 | } 637 | } 638 | return result; 639 | }; 640 | }()); 641 | } 642 | 643 | 644 | 645 | --------------------------------------------------------------------------------