Home | History | Annotate | Download | only in videos
      1 videos=true
      2 page.title=Videos
      3 @jd:body
      4 
      5 <script src="http://swfobject.googlecode.com/svn/trunk/swfobject/swfobject.js" type="text/javascript"></script>
      6 <script src="{@docRoot}assets/jquery-history.js" type="text/javascript"></script>
      7 <script type="text/javascript">
      8 // for debugging in FF, so other browsers ignore the console commands.
      9 var console;
     10 if (!console) console = { 'log': function() {} };
     11 
     12 /* This 'playlist' object defines the playlist IDs for each tab.
     13  * Each name inside 'playlist' corresponds to class names for the tab that the playlists belong to (eg: "googleioTab" and "googleioBox" divs).
     14  * Each string in 'ids' is the ID of a YouTube playlist that belongs in the corresponding tab.
     15  */
     16 var playlists = {
     17   'googleio' : {
     18     'ids': ["734A052F802C96B9"]
     19   },
     20   'about' : {
     21     'ids': ["D7C64411AF40DEA5","611F8C5DBF49CEC6"]
     22   },
     23   'developertips' : {
     24     'ids': ["43E15866EF0033A2"]
     25   },
     26   'developersandbox' : {
     27     'ids': ["77426907BBAD558E"]
     28   }
     29 };
     30 
     31 /* Some playlists include the title in the description meta-data, so we need to account for this when building the thumbnail lists, so we don't show the title twice
     32  * This string is read via indexOf(), so multiple IDs need only be comma-separated in this string.
     33  */
     34 var playlistsWithTitleInDescription = "734A052F802C96B9";
     35 
     36 /* This 'featured' object defines the Feature Videos list.
     37  * Each playlist ID is paired with a custom video description.
     38  */
     39 var featured = {
     40 // Android Development Tools
     41   'Oq05KqjXTvs' : "The team behind the Android Development Tools demonstrate several powerful features for app development, including new capabilities in the Eclipse layout editor.",
     42 // Android UIs for phones and tablets
     43   'WGIU2JX1U5Y' : "This talk from the Android UI team explains several design patterns that the team recommends you use when designing your application for screens of all sizes.",
     44 // Android Protips
     45   'twmuBbC_oB8' : "In this talk, you'll learn how to create a well polished app that abides by several key virtues, using advanced development techniques and some lesser known APIs."
     46 };
     47 
     48 /* When an event on the browser history occurs (back, forward, load),
     49  * load the video found in the URL hash
     50  */
     51 $(window).history(function(e, hash) {
     52   if (location.href.indexOf("#v=") != -1) {
     53     videoId = location.href.split("#v=");
     54     clickVideo(videoId[1]); // click the link with a matching class
     55   }
     56 });
     57 
     58 /* Load a video into the player box.
     59  * @param id        The YouTube video ID
     60  * @param title     The video title to display in the player box (character escaped)
     61  * @param autoplay  Whether to automatically play the video
     62  */
     63 function loadVideo(id, title, autoplay) {
     64   if($("." + id).hasClass("noplay")) {
     65   	//console.log("noplay");
     66   	autoplay = false;
     67   	$("." + id).removeClass("noplay");
     68   }
     69   swfobject.embedSWF('http://www.youtube.com/v/' + id + '&rel=1&border=0&fs=1&autoplay=' +
     70       (autoplay?1:0), 'player', '500', '334', '9.0.0', false, false, {allowfullscreen: 'true'});
     71   $("#videoPlayerTitle").html("<h2>" + unescape(title) + "</h2>");
     72 
     73   $.history.add('v=' + id); // add the current video to the browser history
     74   document.getElementById("doc-content").scrollTop = 0; // scroll the window to the top
     75 }
     76 
     77 /* Draw all videos from a playlist into a 'videoPreviews' list
     78  * @param data  The feed data returned from the youtube request
     79  */
     80 function renderPlaylist(data) {
     81   var MAX_DESC_LENGTH = 390; // the length at which we will trim the description
     82   var feed = data.feed;
     83   var entries = feed.entry || [];
     84   var playlistId = feed.yt$playlistId.$t;
     85 
     86   var ul = $('<ul class="videoPreviews" />');
     87 
     88   // Loop through each entry (each video) and add it to the 'videoPreviews' list
     89   for (var i = 0; i < entries.length; i++) {
     90     var entry = entries[i];
     91 
     92     var title = entry.title.$t;
     93     var id = entry.media$group.yt$videoid.$t;
     94     var thumbUrl = entry.media$group.media$thumbnail[0].url;
     95     var fullDescription = entry.media$group.media$description.$t;
     96     var playerUrl = entry.media$group.media$content[0].url;
     97 
     98     // Check whether this playlist includes the video title inside the description meta-data, so we can remove it
     99     if (playlistsWithTitleInDescription.indexOf(playlistId) != -1) {
    100       var lines = fullDescription.split("\n");
    101       // If the first line includes the first 17 chars from the title, let's use the title from the description instead (because it's a more complete title)
    102       // This accounts for, literally, "Google I/O 2009 -", which is (so far) the min AND max for properly identifying a title in the only playlist with titles in the description
    103       if (lines[0].indexOf(title.slice(0,16)) != -1) {
    104 			h3Title = "<h3>" + lines[0] + "</h3>";
    105       	if (lines[2].length < 30) lines = lines.slice(3);  // also, if the second line is very short (the speaker name), slice it out too
    106       	else lines = lines.slice(1);  // otherwise, slice after the first line
    107       }
    108       fullDescription = lines.join("");
    109     }
    110 
    111     var shortDescription = fullDescription.substr(0, MAX_DESC_LENGTH);
    112     shortDescription += shortDescription.length == MAX_DESC_LENGTH ? "..." : ""; // add ellipsis if we've chopped the description
    113 
    114     var img = $('<img src="' + thumbUrl + '" width="120" height="90"/>');
    115     var a = $('<a class="' + id + '" href="#" onclick="loadVideo(\'' + id + '\',\'' + escape(title) + '\',true); return setSelected(this);" />');
    116     var pShortDescription = $('<p class="short">' + shortDescription + '</p>');
    117     var pFullDescription = $('<p class="full">' + fullDescription + '</p>');
    118     var h3Title = "<h3>" + title + "</h3>";
    119     var pToggle = "<p class='toggle'><a href='#' onclick='return toggleDescription(this)'><span class='more'>more</span><span class='less'>less</span></a></p>";
    120     var li = $('<li/>');
    121 
    122     li.append(a);
    123     a.append(img).append(h3Title).append(pShortDescription);
    124 
    125     // Add the full description and "more/less" toggle, if necessary
    126     if (fullDescription.length > MAX_DESC_LENGTH) {
    127     	a.append(pFullDescription);
    128     	li.append(pToggle);
    129     }
    130 
    131     ul.append(li);
    132   }
    133 
    134   // Now add the 'videoPreviews' list to the page, and be sure we put it in the right tab
    135   // This is the part that allows us to put multiple playlists in one tab
    136   for (var x in playlists) {
    137     var ids = playlists[x].ids;
    138     for (var i in ids) {
    139       if (ids[i] == playlistId) {
    140         $("#"+x+"Box").append(ul);
    141         break;
    142       }
    143     }
    144   }
    145 }
    146 
    147 /* Draw a featured video into the existing 'videoPreviews' list
    148  * @param data  The video data returned from the youtube request
    149  */
    150 function renderFeatured(data) {
    151   var MAX_TITLE_LENGTH = 48;
    152   var entry = data.entry || [];
    153   var id = entry.media$group.yt$videoid.$t;
    154   var description = featured[id];
    155   var title = entry.title.$t;
    156   var thumbUrl = entry.media$group.media$thumbnail[0].url;
    157   var playerUrl = entry.media$group.media$content[0].url;
    158 
    159   var ellipsis = title.length > MAX_TITLE_LENGTH ? "..." : "";
    160 
    161   var h3Title = "<h3>"+ title.substr(0,MAX_TITLE_LENGTH) + ellipsis + "</h3>";
    162   var img = $('<img src="' + thumbUrl + '" width="120" height="90"/>');
    163   var p = $('<p>' + description + '</p>');
    164   var a = $('<a class="' + id + '" href="#" onclick="loadVideo(\'' + id + '\',\'' + title + '\',true); return setSelected(this);" />');
    165   var li = $("<li/>");
    166 
    167   a.append(h3Title).append(img).append(p);
    168   li.append(a);
    169 
    170   $("#mainBodyRight .videoPreviews").append(li);
    171 }
    172 
    173 /* Request the playlist feeds from YouTube */
    174 function showPlaylists() {
    175   for (var x in playlists) {
    176     var ids = playlists[x].ids;
    177     for (var i in ids) {
    178       var script = "<script type='text/javascript' src='http://gdata.youtube.com/feeds/api/playlists/"
    179       				  + ids[i] +
    180       				  "?v=2&alt=json-in-script&max-results=50&callback=renderPlaylist'><\/script>";
    181     	$("body").append(script);
    182     }
    183   }
    184 }
    185 
    186 /* Request the featured videos from YouTube */
    187 function showFeatured() {
    188   for (var id in featured) {
    189     var script = "<script type='text/javascript' src='http://gdata.youtube.com/feeds/api/videos/"
    190     					+ id +
    191     					"?v=2&alt=json-in-script&callback=renderFeatured'><\/script>";
    192     $("body").append(script);
    193   }
    194 }
    195 
    196 /* Reveal a tab (playlist) box
    197  * @param name  The name of the tab
    198  */
    199 function showBox(name) {
    200   $("#"+name+"Box").addClass("selected").siblings().removeClass("selected");
    201   $("#"+name+"Tab").addClass("selected").siblings().removeClass("selected");
    202   return false;
    203 }
    204 
    205 /* Highlight a video thumbnail, including all duplicates that there may be
    206  * @param link  The link <a> object that was clicked
    207  */
    208 function setSelected(link) {
    209   var videoId = $(link).attr("class");
    210   if (videoId.indexOf("selected") != -1) {  // this means this video is already selected and playing, so bail out
    211     return false;
    212   }
    213   $(".videoPreviews .selected").removeClass("selected");
    214   $("a." + videoId).addClass("selected").each( function (i) {
    215   	 if ($(this).is(":hidden")) {
    216   	   var boxName = $(this).parent().parent().parent().attr("id").split("Box");
    217   	   $("#"+boxName[0]+"Tab a").click();
    218   	 }
    219   });
    220   return false;
    221 }
    222 
    223 /* Reveal and hide the long/short descriptions for a video in the playlist
    224  * @param link  The link <a> object that was clicked
    225  */
    226 function toggleDescription(link) {
    227 	var aToggle = $(link);
    228 	$("span", aToggle).toggle();
    229 	var aDescription = $(">a", aToggle.parent().parent());
    230 	$("p.short", aDescription).toggle();
    231 	$("p.full", aDescription).toggle();
    232 	if ($("span.less", aToggle).is(":visible")) {
    233 		aDescription.css("height", "auto");
    234 	} else {
    235 		aDescription.css("height", "90px");
    236 	}
    237 	return false;
    238 }
    239 
    240 /* Add actions to the page onload event so that we load a video right away */
    241 addLoadEvent(function () {
    242   // if there's a video url in the hash, click that video
    243   if (location.href.indexOf("#v=") != -1) {
    244     var videoId = location.href.split("#v=");
    245     clickVideo(videoId[1]);
    246   } else { // otherwise, click the default video
    247     clickDefaultVideo();
    248   }
    249 });
    250 
    251 
    252 var clickVideoAttempts = 0; // Used with clickVideo()
    253 
    254 /* Click a video in order to load it and select it
    255  * @param videoId  The ID of the video to click
    256  */
    257 function clickVideo(videoId) {
    258   if (!isAlphaNumeric(videoId)) {
    259     clickDefaultVideo();
    260     return;
    261   }
    262   
    263   if ($("." + videoId).length != 0) {  // if we find the video, click it and return
    264     $("." + videoId).addClass("noplay"); // add class to indicate we should NOT autoplay (class removed by loadVideo)
    265     $("." + videoId + ":first").click();
    266     return;
    267   } else { // if we don't find it, increment clickVideoAttempts
    268     console.log("video NOT found: " + videoId);
    269     clickVideoAttempts++;
    270   }
    271 
    272   // if we don't find it after 20 attempts (2 seconds), click the first feature video
    273   if (clickVideoAttempts > 10) {
    274     console.log("video never found, clicking default...");
    275     clickVideoAttempts = 0;
    276     clickDefaultVideo();
    277   } else { // try again after 100 milliseconds
    278     setTimeout('clickVideo("' + videoId + '")', 100);
    279   }
    280 }
    281 
    282 /* returns true if the provided text is alphanumeric, false otherwise 
    283    TODO: move this to the dev site js library */
    284 function isAlphaNumeric(text){
    285   var regex=/^[0-9A-Za-z]+$/; //^[a-zA-z]+$/
    286   if(regex.test(text)){
    287     return true;
    288   } else {
    289     console.log("Bogus video ID");
    290     return false;
    291   }
    292 }
    293 
    294 /* Click the default video that should be loaded on page load (the first video in the featured list) */
    295 function clickDefaultVideo() {
    296   if ($("#mainBodyRight .videoPreviews a:first").length != 0) {
    297     var videoId = $("#mainBodyRight .videoPreviews a:first").attr("class");
    298     $("." + videoId).addClass("noplay"); // add class to indicate we should NOT autoplay (class removed by loadVideo)
    299     $("." + videoId + ":first").click();
    300     return;
    301   } else { // if we don't find it, increment clickVideoAttempts
    302     console.log("default video NOT found");
    303     clickVideoAttempts++;
    304   }
    305 
    306   // if we don't find it after 50 attempts (5 seconds), just fail
    307   if (clickVideoAttempts > 50) {
    308     console.log("default video never found...");
    309   } else { // try again after 100 milliseconds
    310     setTimeout('clickDefaultVideo()', 100);
    311   }
    312 }
    313 </script>
    314 
    315   <div id="mainBodyFixed">
    316 
    317     <div id="mainBodyLeft" class="videoPlayer" >
    318       <div id="videoPlayerBox">
    319         <div id="videoBorder">
    320           <div id="videoPlayerTitle"></div>
    321           <div id="objectWrapper">
    322             <object id="player"></object>
    323           </div>
    324         </div>
    325       </div>
    326     </div><!-- end mainBodyLeft -->
    327 
    328     <div id="mainBodyRight" class="videoPlayer">
    329       <h2>Featured Videos</h2>
    330       <ul class="videoPreviews"></ul>
    331     </div><!-- end mainBodyRight -->
    332 
    333     <ul id="videoTabs">
    334       <li id="aboutTab" class="selected"><a onclick="return showBox('about');" href="#">About the Platform</a></li>
    335       <li id="developertipsTab"><a onclick="return showBox('developertips');" href="#">Developer Tips</a></li>
    336       <li id="googleioTab"><a onclick="return showBox('googleio');" href="#">Google I/O Sessions</a></li>
    337       <li id="developersandboxTab"><a onclick="return showBox('developersandbox');" href="#">Developer Sandbox</a></li>
    338     </ul>
    339 
    340     <div id="videos">
    341       <div id="aboutBox" class="selected"></div>
    342       <div id="developertipsBox"></div>
    343       <div id="googleioBox"></div>
    344       <div id="developersandboxBox"></div>
    345     </div>
    346 
    347   </div><!-- end mainBodyFixed -->
    348 
    349 <script type="text/javascript">
    350 // Initialization actions
    351 showFeatured();            // load featured videos
    352 showPlaylists();           // load playlists
    353 </script>
    354 
    355 
    356