home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 10218 / 10218.xpi / content / go.js < prev    next >
Encoding:
JavaScript  |  2010-02-10  |  37.3 KB  |  1,114 lines

  1. var API_CLIENT_ID = "ffext";
  2. var API_CLIENT_KEY = '34daab8d9da7f58420098ce7d7f740c8';
  3. var MAX_LINKS_TO_CHECK = 30;
  4. var VS_LOGGING_ACTIVE = null;
  5. var VS_AJAX_URL_ROOT = null;
  6. var VS_SUMMARY_TILE_WIDTH = 57;
  7. var URL_CHANGE_WATCH_TIMEOUT = 500;
  8. var CONTENT_CHANGE_WATCH_TIMEOUT = 500;
  9.  
  10. function enhanceGoogleSearch() {
  11.     vs_log("Enhancing google search/home page");
  12.     if($(VS_ACTIVE_SITE['link_selector']+':first').length > 0) {
  13.         //This is a regular search page, no need for the ajax checks
  14.         enhanceSearchPage(); 
  15.     } else {
  16.         //We're most likely on the xhr version of their page (so annoying!)
  17.         if(location.hash) {
  18.             watchForPageContentChange(enhanceSearchPage);
  19.         } else {
  20.             watchForUrlChange(enhanceSearchPage);    
  21.         }
  22.     }
  23. }
  24.  
  25. /**
  26.  * Twitter has a bunch of variations, ajax, no ajax, url change, no url change... This should handle them all
  27.  **/
  28. function enhanceTwitter() {
  29.     vs_log("Twitter enhancer initializing");
  30.     if($(VS_ACTIVE_SITE['link_selector']).length > 0) {
  31.         enhanceTwitterPage();
  32.     } else {
  33.         watchForPageContentChange(enhanceTwitterPage);
  34.     }
  35.     watchForUrlChange(enhanceTwitterPage);    
  36. }
  37.  
  38. function enhanceTwitterPage() {
  39.     vs_log("Actually enhancing a twitter page");    
  40.     var probablyShortUrls = getEnhanceableUrlsOnPage();
  41.     if(probablyShortUrls.length > MAX_LINKS_TO_CHECK) {
  42.         probablyShortUrls.length = MAX_LINKS_TO_CHECK;
  43.     }
  44.  
  45.     var ajaxUrl = VS_AJAX_URL_ROOT+"/api/ext/services/longUrlService.php?urls="+encodeURIComponent(probablyShortUrls.join(","))+"&client_id="+API_CLIENT_ID+"&client_key="+API_CLIENT_KEY;
  46.     ajaxCall(ajaxUrl, twitterUrlExpandAjaxCallback);
  47. }
  48.  
  49. function twitterUrlExpandAjaxCallback(json) {
  50.     var url_expansion_active = VS_getPref("expose_longer_twitter_urls");
  51.     $(VS_ACTIVE_SITE['link_selector']+":not(.vs_enhanced)").each(function() {
  52.         if(json[this.href]) {
  53.             var longVersion = json[this.href];
  54.             if(url_expansion_active && this.href != longVersion) {
  55.                 this.innerHTML = (longVersion.length > 25) ? longVersion.substr(0, 25)+"..." : longVersion;
  56.             }
  57.             this.href = longVersion;
  58.         }
  59.     });    
  60.     enhanceSearchPage(false, enhanceTwitterPage);
  61.  
  62.     //The Refresh and More actions do not change the url, we have to explicitly watch for them
  63.     $('#pagination .more:not(.vs_enhanced)').bind('click', function() { previousPageSignature = getPageSignature(); watchForPageContentChange(enhanceTwitterPage); }).addClass("vs_enhanced");
  64.     var refresh_link = $('#res-update:not(.vs_enhanced)');
  65.     if(refresh_link.length == 0) { refresh_link = $('#results_update:not(.vs_enhanced)'); }
  66.     refresh_link.bind('mouseout', function() { watchForPageContentChange(enhanceTwitterPage); });
  67.     refresh_link.addClass("vs_enhanced");
  68. }
  69.  
  70. /**
  71.  * Analyze the links on the page to generate a "signature" for it.  
  72.  * When this signature changes, we need to re-enhance the page
  73.  **/
  74. function getPageSignature() {
  75.     var urls = [];
  76.     var selector = VS_ACTIVE_SITE['link_selector'];
  77.     $(selector).each(function() {
  78.         urls[urls.length] = (this.original_href) ? this.original_href : this.href;        
  79.     });
  80.     return urls.join("");
  81. }
  82.  
  83. function getEnhanceableUrlsOnPage() {
  84.     var urls = [];
  85.     var selector = VS_ACTIVE_SITE['link_selector']+":not(.vs_enhanced)";
  86.     $(selector).each(function() {        
  87.         urls[urls.length] = (this.original_href) ? this.original_href : this.href;        
  88.     });
  89.     return urls;
  90. }
  91.  
  92. /**
  93.  * After the url changes, we periodically check to see if the videos on the page have changed.  Once they have,
  94.  * this means the xhr request has completed.  This is a relatively heavy check so we only do it after we detect 
  95.  * that the url has changed.  It should only persist for the duration of the site's xhr call and then we revert 
  96.  * to the light url check.   
  97.  **/
  98. function watchForPageContentChange(run_fn) {
  99.     var sig = getPageSignature();
  100.     if(sig && sig != previousPageSignature) {
  101.         vs_log("Urls on page changed!  Re-enhancing");
  102.         previousPageSignature = sig;
  103.         run_fn();
  104.     } else {
  105.         pccTimeout = setTimeout(function() { watchForPageContentChange(run_fn); }, CONTENT_CHANGE_WATCH_TIMEOUT);
  106.     }
  107. }
  108.  
  109. /**
  110.  * Watch to see if the url changes.  This indicates an xhr request has begun for a refinement.   
  111.  * This is an extremely light weight check, so we can do it fairly often
  112.  **/
  113. var previousHref;
  114. var pguTimeout;
  115. var pccTimeout;
  116. var previousPageSignature;
  117. var previousAutoPagerLength;
  118. function watchForUrlChange(run_fn) {
  119.     //ap = $('.autoPagerS');
  120.     if((previousHref != null && location.href != previousHref)) { // || (ap.length > 0 && ap.length != previousAutoPagerLength)) {
  121.         vs_log("URL changed!");
  122.         previousHref = location.href;
  123.         //previousAutoPagerLength = ap.length;
  124.         watchForPageContentChange(run_fn);    
  125.     } else {
  126.         previousHref = location.href;
  127.         pguTimeout = setTimeout(function() { watchForUrlChange(run_fn) }, URL_CHANGE_WATCH_TIMEOUT);
  128.     }
  129. }
  130.  
  131. function enhanceYoutubeVideoAjaxCallback(json) {
  132.     vs_log("Inside enhanceYoutubeVideoAjaxCallback ");
  133.  
  134.     if(!json.videos || json.videos.length == 0) { return; }
  135.  
  136.     var video = json.videos[0];
  137.     
  138.     if(video.visual_video_summary.frames.length) {
  139.         var n = getSummaryReadyToInsert(video, true, 0);
  140.         $('#watch-this-vid-info').prepend(n);
  141.         attachSummaryEventHandlers();
  142.         bindSeekClickHandlers($('#watch-this-vid-info'), unsafeWindow.document.getElementById('movie_player'));
  143.     }
  144.  
  145.     //Add related topics
  146.     var topics = video.related_entities;
  147.     
  148.     if(topics && topics.length > 0) {
  149.         var topicHtml = '<h2 id="videosurf-topics-heading" class="yt-uix-expander-head">' +
  150.                         '    <button class="yt-uix-expander-arrow master-sprite" title=""/>' +
  151.                         '    <span>Related VideoSurf Topics</span>' +
  152.                         '</h2>';
  153.  
  154.         topicHtml += "<div class='watch-discoverbox-body mini-list-view yt-uix-expander-body'>";
  155.         topicHtml += "<ul>";
  156.         var count = 0;
  157.         for(i = 0 ; i < topics.length ; i++) {
  158.             var topic = topics[i];
  159.             if(count >= 6) {
  160.                 break;
  161.             }
  162.             
  163.             if(topic['thumbnail']) {
  164.                 var title = topic['title'];
  165.                 var escapedTitle = title.replace("'", "\'");
  166.                 if(title.length > 12) { title = title.substring(0, 9).replace(" ", " ")+"..."; }
  167.                 topicHtml += "<li><a href='"+topic['url']+"' title='"+escapedTitle+"'><img src='"+topic['thumbnail']+"'></a><a class='topic-title' title='"+escapedTitle+"' href='"+topic['url']+"'>"+title+"</a></li>";
  168.                 count++;
  169.             }
  170.         }
  171.         topicHtml += "</ul></div>";
  172.         var expandedClass = VS_getPref('youtube_related_expanded') ? "" : "yt-uix-expander-collapsed";
  173.         n = $('<div>').attr('id', 'videosurf-topics').html(topicHtml).addClass('videosurf-topics watch-discoverbox-wrapper expand-panel yt-uix-expander '+expandedClass);
  174.         
  175.         if(count) 
  176.             n.insertBefore('#watch-channel-videos-panel');
  177.         
  178.         $('#videosurf-topics').click(function(event) {
  179.             event.stopPropagation();
  180.             
  181.             if($(this).hasClass('yt-uix-expander-collapsed')) {
  182.                 $(this).removeClass('yt-uix-expander-collapsed');
  183.                 VS_setPref("youtube_related_expanded", true);
  184.             }
  185.             else {
  186.                 $(this).addClass('yt-uix-expander-collapsed');
  187.                 VS_setPref("youtube_related_expanded", false);                
  188.             }
  189.         });
  190.     }
  191. }
  192.  
  193. function handleYoutubeSeekClick(e, embed_id) {
  194.     e.preventDefault();
  195.     var re = new RegExp("t=([0-9]+)", "i");
  196.     var matches = e.target.parentNode.href.match(re);
  197.     if(matches && matches.length > 0) {
  198.         vs_log("seeking in "+embed_id+" to "+matches[1]);
  199.         //var embed = unsafeWindow.document.getElementById('movie_player'); //Only the unsafeWindow document seems to have access to the api functions
  200.         var embed = unsafeWindow.document.getElementById(embed_id);
  201.         if(embed.getPlayerState() == 0) {
  202.             embed.playVideo(); //their player has a bug about seeking after it's "ended", so we restart it
  203.         }
  204.         embed.seekTo(parseInt(matches[1]), true);
  205.     }
  206.     return false;
  207. }
  208.  
  209. function bindSeekClickHandlers(summary, embed, source_id) {
  210.     if(VS_ACTIVE_SITE['slug'] == "youtube_video" || VS_ACTIVE_SITE['slug'] == "embedded_video_summaries") {
  211.         if(!embed.id) {
  212.             embed.id = "vs_embed_"+source_id;
  213.         }
  214.         $(summary).find('.summaryThumbnailFrame').attr('title', 'Click to jump to this moment in the video!').bind('click', function(e) { handleYoutubeSeekClick(e, embed.id); });
  215.     }
  216. }
  217.  
  218. /** 
  219.  * Gets info for just one video and inserts the summary
  220.  **/
  221. function enhanceYoutubeVideo() {
  222.     vs_log("Enhancing youtube video page - Summaries ("+VS_ACTIVE_SITE_SUMMARIES_ENABLED+") Related ("+VS_ACTIVE_SITE_RELATED_ENABLED+")");
  223.  
  224.     if(VS_ACTIVE_SITE_SUMMARIES_ENABLED) {
  225.         var url = document.location.href;
  226.         if(url.indexOf("#") == url.length - 1) { //strip off trailing #
  227.             url = url.substring(0, url.length - 1);
  228.         }
  229.         summarySize = 11;
  230.         ajaxUrl = VS_AJAX_URL_ROOT+"/video_lookup/v1.2/?urls="+encodeURIComponent(url)+"&visual_summary_size="+summarySize+"&client_id="+API_CLIENT_ID+"&client_key="+API_CLIENT_KEY+"&vss="+VS_ACTIVE_SITE['slug'];
  231.         ajaxCall(ajaxUrl, enhanceYoutubeVideoAjaxCallback);
  232.     }
  233.  
  234. }
  235.  
  236. /**
  237.  * Takes search results from videosurf's search api and inserts them into the page as related videos
  238.  **/
  239. function enhanceSearchPageRelatedAjaxCallback(json) {
  240.     vs_log("In Related Ajax Callback");
  241.     vs_log(json, true);
  242.     if(!json.videos || json.videos.length == 0) { return; }
  243.  
  244.     var container = null;
  245.     switch(VS_ACTIVE_SITE['slug']) {
  246.         case "google_search":
  247.             containerId = "mbEnd";
  248.             if(!document.getElementById(containerId)) {
  249.                 $('#res').before('<table id='+containerId+'></table>');
  250.                 vs_log("adding the related container");
  251.             }
  252.             container = $('#'+containerId);
  253.             container.prepend("<tr><td colspan='100' id='vs-related-container'></td></tr>");
  254.             container = $("#vs-related-container");
  255.             break;
  256.             
  257.         case "yahoo":
  258.             containerId = 'east';
  259.             if(!document.getElementById(containerId)) {
  260.                 $('#right').append($('<div>').attr('id', containerId));
  261.                 vs_log("adding the related container");
  262.             }
  263.             container = $('#'+containerId);
  264.             break;
  265.             
  266.         case "youtube":
  267.             container = $('#search-pva');
  268.             break;
  269.             
  270.         case "twitter_search":
  271.             containerId = "vs-twitter-related-container";
  272.             if(!document.getElementById("vs-twitter-related-container")) {
  273.                 $('#mainContent').append($('<div>').attr('id', containerId).addClass("module"));
  274.                 vs_log("adding the related container");
  275.             }
  276.             container = $('#'+containerId);
  277.             break;
  278.             
  279.         case "bing":
  280.             container = $('#sidebar');            
  281.             break;
  282.         
  283.         case "riot":
  284.             container = $('#secondaryContent');
  285.             
  286.             if(!container.length) {
  287.                 $('body .wrapper').animate({width:975});
  288.  
  289.                 container = $('<div id="videosurfContainer" style="float: right"></div>');                
  290.                 $('#OneRiotWebSearch').before(container);
  291.             }            
  292.             break;
  293.             
  294.         default:
  295.             return; 
  296.     }
  297.  
  298.     var related_container_template = VS_getUrlContents("chrome://videosurf_enhanced/content/templates/related_container.tmpl");
  299.     var related_video_template = VS_getUrlContents("chrome://videosurf_enhanced/content/templates/related_video.tmpl");
  300.     relatedHtml = "";
  301.     for(i = 0 ; i < json.videos.length; i++) {
  302.         v = json.videos[i];
  303.         v.img_width = 119; //needed for sprite hover animation
  304.         v.img_height = 73;
  305.         relatedHtml += getVideoHtml(v, false, i);
  306.     }    
  307.     var query = getCurrentSearchQuery();
  308.     
  309.     var more_url = "http://www.videosurf.com/videos/"+cleanQuery(query)+"?vlt=ffext";
  310.         
  311.     relatedHtml = fillTemplate(related_container_template, {
  312.         'videos'           : relatedHtml, 
  313.         'more_url'         : more_url, 
  314.         'matching_results' : addCommas(json.matching_results), 
  315.         'more_display'     : (json.matching_results > VS_getPref("related_search_num_results")) ? "block" : "none"
  316.     });
  317.  
  318.     if(document.getElementById('vs-related-uber-container')) { //because of ajax, only inject the related once
  319.         $('vs-related-uber-container').html(relatedHtml);    
  320.     }
  321.     else {
  322.         if(container && relatedHtml) {
  323.             var related_css = VS_getUrlContents("chrome://videosurf_enhanced/content/css/relatedVideos.css");
  324.             related_css += VS_getUrlContents("chrome://videosurf_enhanced/content/css/related_custom_"+VS_ACTIVE_SITE['slug']+".css");
  325.             VS_addStyle(related_css);
  326.  
  327.             vs_log("Injecting the related videos");
  328.             container.prepend("<span id='vs-related-uber-container'>"+relatedHtml+"</span>");            
  329.         }
  330.     }
  331.     
  332.     $('body').append($('<iframe style="position: absolute; top: -1000px; left: -1000px; height: 1px; width: 1px;" src="http://network.videosurf.com/beacon/serp/' + VS_ACTIVE_SITE['slug'] + '"></iframe>'));
  333.     
  334.     attachSummaryEventHandlers();
  335. }
  336.  
  337. /**
  338.  * Uses the link selector for the active type of page to get a list of video urls 
  339.  * It fetches the video info for these urls and then injects the summaries along with some custom css for this search engine
  340.  **/
  341. function enhanceSearchPageAjaxCallback(json, idsToCheck) {
  342.     vs_log("Running search page ajax callback");
  343.  
  344.     //dedupe videos to get rid of js conflicts later on
  345.     //There's gotta be a better way to do this
  346.     var seenVideos = "";
  347.     for(i = 0 ; i < json.videos.length; i++) {
  348.         if(seenVideos.indexOf(json.videos[i].url) != -1) {
  349.             Array.remove(json.videos, i);
  350.         } else {
  351.             seenVideos += json.videos[i].url;
  352.         }
  353.     }
  354.  
  355.     var parents = [];
  356.     var prev = null;
  357.  
  358.     var index = 0;    
  359.     $(VS_ACTIVE_SITE['link_selector']+":not(.vs_enhanced)").each(
  360.         function() {
  361.             $(this).addClass("vs_enhanced");
  362.             for(i = 0 ; i < json.videos.length; i++) {
  363.                 var item = json.videos[i];
  364.                 if(VS_ACTIVE_SITE['slug'] == "youtube" && VS_getPref("links_on_youtube_stay_there")) {
  365.                     item.url = item.source_url;
  366.                 }
  367.                 var searchStr = (idsToCheck && idsToCheck.indexOf(item.video_id) != -1) ? item.video_id : item.source_video_id;
  368.                 if(this.href.indexOf(searchStr) != -1) {
  369.  
  370.                     var n = getSummaryReadyToInsert(item, true, index);
  371.                     switch(VS_ACTIVE_SITE['slug']) {
  372.                         case "google_search": 
  373.                             var p = $(this).parents('li:first');
  374.                             p.addClass("vs");
  375.                             n.insertAfter(p);
  376.                             var pa = $(this).parents('table.ts:first');
  377.                             parents[parents.length] = (pa && pa.length > 0) ? pa : p; //If it's in the videos module (Table case), do something else
  378.                             break;
  379.                             
  380.                         case "yahoo":
  381.                             if($(this).parents('div.res.indent:first')[0]) {
  382.                                 n.addClass('indent');
  383.                             }
  384.                             var p = $(this).parents('li:first');
  385.                             parents[parents.length] = p;
  386.                             n.insertAfter(p);
  387.                             break;
  388.                             
  389.                         case "youtube":
  390.                             var p = $(this).parents('.video-entry:first');
  391.                             p.find('.hd-video-logo').parent().remove();
  392.                                                                                     
  393.                             n.find('.views').show().find('b').text(p.find('.video-view-count').text().split(' ')[0]);
  394.                             
  395.                             parents[parents.length] = p;                                                    
  396.                             n.insertAfter(p);                            
  397.                             break;
  398.                             
  399.                         case "friendfeed":
  400.                             $(this).parents('.title:first').append(n);
  401.                             break;
  402.                             
  403.                         case "twitter_pages":
  404.                             var p = $(this).parents(".entry-content:first");
  405.                             if(!p || p.length == 0) {
  406.                                 p = $(this).parents(".msgtxt:first");
  407.                             }
  408.  
  409.                             if($('#me_name').length == 1) {
  410.                                 $(n).find('.summaryThumbnails').css({top:56,left:175,height:60});
  411.                             }
  412.  
  413.                             p.append(n);    
  414.                             break;
  415.                             
  416.                         case "twitter_search":
  417.                             $(this).parents(".msgtxt:first").append(n);
  418.                             break;
  419.                             
  420.                         case "riot":
  421.                             $(this).parents('.searchResultBlock').html('').append(n);
  422.                             break;
  423.                             
  424.                         case "reddit":
  425.                             var sourceLink = $(this).attr('href');
  426.                             $(this).parent().html(n).find('a').attr('href', sourceLink).end().parents('.thing').find('.thumbnail').remove();                            
  427.                             break;
  428.                                 
  429.                         case "digg":
  430.                             $(this).parent().html(n).parents('.news-body').css({
  431.                                 'padding-left' : 60
  432.                             });
  433.                             break;                                                            
  434.                     }
  435.                     prev = n;
  436.                     index++;
  437.                 }
  438.             }
  439.         }
  440.     );
  441.     for(i = 0 ; i < parents.length ; i++) {
  442.         parents[i].hide();
  443.     }
  444.  
  445.     previousPageSignature = getPageSignature();
  446.  
  447.     attachSummaryEventHandlers();
  448. }
  449.  
  450. /**
  451.  * Take the json for a video from the server and generate the html for it
  452.  **/
  453. function getVideoHtml(item, needsSummary, index) {
  454.     var video_with_summary_template = VS_getUrlContents("chrome://videosurf_enhanced/content/templates/video_with_summary.tmpl");
  455.     var summary_tile_template = VS_getUrlContents("chrome://videosurf_enhanced/content/templates/video_with_summary_tile.tmpl");
  456.  
  457.     var title_length = 45;
  458.     var desc_length = 60;
  459.     switch(VS_ACTIVE_SITE['slug']) {
  460.         case "twitter_pages":
  461.             title_length = 30;
  462.             desc_length = 50;
  463.         break;
  464.         case "twitter_search":
  465.             title_length = 30;
  466.             desc_length = 50;
  467.         break;    
  468.     }
  469.  
  470.     context = {    
  471.         'video_id': item.video_id,
  472.         'url': item.url,
  473.         'source_url': item.source_url,
  474.         'title': (item.title.length > title_length) ? item.title.substr(0, title_length)+"..." : item.title,
  475.         'description': (item.description.length > desc_length) ? item.description.substr(0, desc_length)+"..." : item.description,
  476.         'thumbnail': item.thumbnail,
  477.         'source': item.source,
  478.         'date_added_display': item.date_added_display || "—",
  479.         'length_display': item.length_display || "—",
  480.         'img_width': item.img_width,
  481.            'img_height': item.img_height,
  482.         'small_sprite_url': item.visual_video_summary.sprites['64x48'],
  483.         'large_sprite_url': item.visual_video_summary.sprites['160x120'],
  484.         'type': (needsSummary) ? "full" : "related",
  485.         'index': index
  486.     }     
  487.  
  488.     var frames = item.visual_video_summary.frames;    
  489.     item.sprite_order = [];
  490.     item.summary_tiles = [];
  491.     for(j = 0 ; j < frames.length ; j++) { //DO NOT use i, trust me
  492.         item.sprite_order[item.sprite_order.length] = item.visual_video_summary.frames[j].sprite_position;
  493.         if(needsSummary) {
  494.             //Use a custom, smaller context to reduce number of regexp replacements in this inner loop
  495.             if(VS_ACTIVE_SITE['slug'] == "youtube_video" && frames[j].url.indexOf("?t=") == -1) {
  496.                 frames[j].url += "&t="+frames[j].time;
  497.             }
  498.             frame_context = {
  499.                 'video_id': item.video_id,
  500.                 'url': frames[j].url,
  501.                 'time_display': frames[j].time_display,
  502.                 'small_sprite_url': item.visual_video_summary.sprites['64x48'],
  503.                 'sprite_position': frames[j].sprite_position,
  504.                 'img_width': 119,
  505.                 'img_height': 73,
  506.                 'type': (needsSummary) ? "full" : "related",
  507.                 'index': index
  508.             }
  509.             item.summary_tiles[item.summary_tiles.length] = fillTemplate(summary_tile_template, frame_context);
  510.         }
  511.     }
  512.     context.sprite_order = item.sprite_order.join(",");
  513.     context.summary_tiles = item.summary_tiles.join(" ");
  514.  
  515.     context.content_type_class = "";
  516.     if(item.content_type && item.content_type.id == 4) {
  517.         context.content_type_class = "fullepisode";
  518.     }
  519.     if(item.content_type && item.content_type.id == 5) {
  520.         context.content_type_class = "fullmovie";
  521.     }
  522.  
  523.     item_html = fillTemplate(video_with_summary_template, context);
  524.     return item_html;
  525. }
  526.  
  527. function getSummaryReadyToInsert(item, needsSummary, index) {
  528.     item.img_width = 160;
  529.     item.img_height = 120;
  530.     item_html = getVideoHtml(item, needsSummary, index);
  531.     var el = $("<div>").addClass('top-results').addClass("index_"+index).html(item_html);
  532.     return el;
  533. }
  534.  
  535. /**
  536.  * Uses the link selector for the active type of page to get a list of video urls 
  537.  * It fetches the video info for these urls and then injects the summaries along with some custom css for this search engine
  538.  **/
  539. function enhanceSearchPage(relatedOnly, post_fn) {
  540.     vs_log("Enhancing search page: Summaries ("+VS_ACTIVE_SITE_SUMMARIES_ENABLED+") - Related ("+VS_ACTIVE_SITE_RELATED_ENABLED+")");
  541.  
  542.     //Inject the summaries
  543.     if(VS_ACTIVE_SITE_SUMMARIES_ENABLED && !relatedOnly) {
  544.  
  545.         linkCount = 0;
  546.         linksToCheck = "";
  547.         idsToCheck = "";
  548.  
  549.         $(VS_ACTIVE_SITE['link_selector']+":not(.vs_enhanced)").each(function() {                        
  550.             var videoSource = this.href;
  551.                         
  552.             //To patch some bad assumptions
  553.             if(VS_ACTIVE_SITE['slug'] == 'riot') {                
  554.                 videoSource = 'http://' + $(this).find('img').attr('alt')
  555.             }
  556.                                     
  557.             if(videoSource.indexOf("videosurf.com/video/") != -1) {
  558.                 var id = extractVideoSurfIdFromUrl(videoSource);
  559.                 if(idsToCheck.indexOf(id) == -1) {
  560.                     idsToCheck += id+",";
  561.                 }
  562.             }
  563.             else {
  564.                 if(linksToCheck.indexOf(videoSource) == -1 && linkCount <= MAX_LINKS_TO_CHECK && VS_isVideoPageUrl(videoSource)) { //dedupe
  565.                     linksToCheck += videoSource + "||";
  566.                     linkCount++;
  567.                 }
  568.             }            
  569.         });
  570.  
  571.         var summarySize = 8;
  572.         switch(VS_ACTIVE_SITE['slug']) {
  573.             case "youtube"        : summarySize = 7; break;
  574.             case "twitter_pages"  : summarySize = 5; break;
  575.             case "twitter_search" : summarySize = 5; break;
  576.             case "yahoo"          : summarySize = 5; break;
  577.             case "google_search"  : summarySize = 7; break;
  578.             case "riot"           : summarySize = 8; break;
  579.             case "reddit"         : summarySize = 12; break;
  580.             case "digg"           : summarySize = 10; break;            
  581.  
  582.         }
  583.  
  584.         if(linksToCheck || idsToCheck) {
  585.             ajaxCallPost({
  586.                 url : VS_AJAX_URL_ROOT + '/video_lookup/v1.2/',
  587.                 get : {
  588.                     client_id           : API_CLIENT_ID, 
  589.                     client_key          : API_CLIENT_KEY,
  590.                     visual_summary_size : summarySize,
  591.                     vss                 : VS_ACTIVE_SITE['slug']
  592.                 },
  593.                 post : {
  594.                     urls : linksToCheck,
  595.                     ids  : idsToCheck
  596.                 }
  597.             }, function(json) {
  598.                 enhanceSearchPageAjaxCallback(json, idsToCheck);
  599.             });
  600.         }
  601.     }
  602.  
  603.     //Do the related videos
  604.     if(VS_ACTIVE_SITE_RELATED_ENABLED) {        
  605.         var query = getCurrentSearchQuery();
  606.         if(query) {
  607.             var safe_level = VS_getPref("safe_search_enabled") ? "1" : "2";
  608.             var num_per_page = VS_getPref("related_search_num_results");
  609.             ajaxUrl = VS_AJAX_URL_ROOT+"/video_search/v1.2/?query="+encodeURIComponent(query)+"&num_per_page="+num_per_page+"&safe_level="+safe_level+"&client_id="+API_CLIENT_ID+"&client_key="+API_CLIENT_KEY+"&vss="+VS_ACTIVE_SITE['slug'];
  610.             if(VS_ACTIVE_SITE['slug'] == "youtube" && VS_getPref("exclude_youtube_results_on_youtube_search")) {
  611.                 ajaxUrl += "&refinement_providers=-2"; //exclude youtube results on youtube's search page
  612.             }
  613.             ajaxCall(ajaxUrl, enhanceSearchPageRelatedAjaxCallback);
  614.         }
  615.     }
  616.  
  617.     watchForUrlChange((post_fn) ? post_fn : enhanceSearchPage);
  618.  
  619. } //enhanceSearchPage
  620.  
  621. function attachSummaryEventHandlers() {
  622.     index_AttachEventHandlers();
  623. }
  624.  
  625. //Used to power related videos
  626. function getCurrentSearchQuery() {
  627.     var query = null;
  628.     switch(VS_ACTIVE_SITE['slug']) {
  629.         case "google_search": 
  630.             var input = $('input[name=q]:first');
  631.             if(input && input.length > 0) {
  632.                 query = input.get(0).value;
  633.             }
  634.             break;
  635.         
  636.         case "yahoo":
  637.             if($('#yschsp') && $('#yschsp').length > 0) {
  638.                 query = $('#yschsp').get(0).value;
  639.             }
  640.             break;
  641.         
  642.         case "youtube":
  643.             if(document.getElementById('masthead-search-term')) {
  644.                 query = $('#masthead-search-term').get(0).value;    
  645.             }
  646.             break;
  647.         
  648.         case "twitter_search":
  649.             if(document.getElementById('searchBox')) {
  650.                 query = document.getElementById('searchBox').value;
  651.             }
  652.             break;
  653.         
  654.         case "bing":
  655.             if(document.getElementById('sb_form_q')) {
  656.                 query = document.getElementById('sb_form_q').value;
  657.             }            
  658.             break;
  659.         
  660.         case "riot":
  661.             if(document.getElementById('searchQueryInput')) {
  662.                 query = document.getElementById('searchQueryInput').value;
  663.             }
  664.             break;
  665.     }
  666.  
  667.     vs_log("Current page search query is: "+query);
  668.     return query;
  669.  
  670. } //getCurrentSearchQuery
  671.  
  672. function vs_LoadCSS() {    
  673.     if(VS_IS_VIDEO_PAGE || VS_IS_SPECIAL_PAGE) {
  674.         VS_addStyle(
  675.             VS_getUrlContents("chrome://videosurf_enhanced/content/css/reset-min.css") +
  676.             
  677.             VS_getUrlContents("chrome://videosurf_enhanced/content/css/relatedTab.css") +
  678.             
  679.             VS_getUrlContents("chrome://videosurf_enhanced/content/css/genericVideoSummary.css") +
  680.             VS_getUrlContents("chrome://videosurf_enhanced/content/css/genericVideoSummary_NonVisuallyIndexed.css") +
  681.             VS_getUrlContents("chrome://videosurf_enhanced/content/css/genericVideoSummary_Sprite.css") +
  682.             VS_getUrlContents("chrome://videosurf_enhanced/content/css/genericVideoSummary_Tile.css") +
  683.             VS_getUrlContents("chrome://videosurf_enhanced/content/css/genericVideoSummary_SmallTile.css") +
  684.             VS_getUrlContents("chrome://videosurf_enhanced/content/css/genericVideoSummary_Vertical.css")
  685.         );
  686.  
  687.         vs_log('Loaded Generic CSS');
  688.     }
  689.     
  690.     if(VS_IS_SPECIAL_PAGE) {
  691.         VS_addStyle(        
  692.             VS_getUrlContents("chrome://videosurf_enhanced/content/css/"+VS_ACTIVE_SITE['slug']+"_custom.css") +
  693.             VS_getUrlContents("chrome://videosurf_enhanced/content/css/post.css")
  694.         );
  695.         
  696.         vs_log('Special CSS');        
  697.     }    
  698. }
  699.  
  700. function vs_init() {        
  701.     if(!VS_getPref('extension_enabled')) { 
  702.         //If the extension was turned off via the icon, don't do anything
  703.         return;
  704.     } 
  705.  
  706.     VS_LOGGING_ACTIVE = VS_getPref('logging_active');
  707.     VS_AJAX_URL_ROOT = VS_getPref('ajax_url_root');
  708.     
  709.     jQuery.each($('embed'), function(index, element) {
  710.         if($(element).attr('wmode') != 'transparent') {
  711.             var embedHTML = $(element).attr('wmode', 'transparent').parent().html();
  712.             vs_log(embedHTML, true);
  713.             
  714.             $(element).replaceWith($(embedHTML));
  715.         }
  716.     });
  717.     
  718.     if(VS_IS_VIDEO_PAGE) {
  719.         vs_log("Is video page: " + VS_IS_VIDEO_PAGE, true);        
  720.         vs_log("Page location: " + document.location.href, true);
  721.  
  722.         if(!VS_IS_SPECIAL_PAGE) {
  723.             VS_ACTIVE_SITE = [];            
  724.         }
  725.  
  726.         ajaxUrl = VS_AJAX_URL_ROOT + "/video_lookup/v1.2/?include_videos_without_summaries=true&visual_summary_size=11&client_id=" + API_CLIENT_ID + "&client_key=" + API_CLIENT_KEY + "&vss=generic_video&urls=" + encodeURIComponent(document.location.href);
  727.         ajaxCall(ajaxUrl, function(json) {
  728.             if(json.videos.length > 0 && json.videos[0].video_id) {
  729.                 vs_log(json.videos[0], true);
  730.                 vs_log('Found Videosurf ID: ' + json.videos[0].video_id);
  731.                 vs_log('Create tab with Videosurf ID and call getVideoPageRelatedVideos');
  732.                 
  733.                 videosurftab_InitializeTab(json);            
  734.                 if(VS_ACTIVE_SITE['slug'] == "youtube_video") {
  735.                     enhanceYoutubeVideoAjaxCallback(json);
  736.                 }
  737.             }
  738.         });            
  739.     }
  740.             
  741.     if(VS_IS_SPECIAL_PAGE) {
  742.         vs_log("Is special page - " + VS_ACTIVE_SITE + " - " + VS_AJAX_URL_ROOT);        
  743.     
  744.         VS_ACTIVE_SITE                  = VS_ACTIVE_SITE.split("||"); //This string is defined in compiler.js
  745.         VS_ACTIVE_SITE['slug']          = VS_ACTIVE_SITE[0];
  746.         VS_ACTIVE_SITE['title']         = VS_ACTIVE_SITE[1];
  747.         VS_ACTIVE_SITE['regexp']        = VS_ACTIVE_SITE[2];
  748.         VS_ACTIVE_SITE['link_selector'] = VS_ACTIVE_SITE[3];
  749.  
  750.         switch(VS_ACTIVE_SITE['slug']) {
  751.             case "google_search"  : enhanceGoogleSearch(); break;
  752.             case "twitter_pages"  : enhanceTwitter();      break;
  753.             case "twitter_search" : enhanceTwitter();      break;
  754.             case "youtube_video"  : /*enhanceYoutubeVideo();*/ break; //handled by the ajax call from the tab lookup now
  755.             default               : enhanceSearchPage();   break;
  756.         }
  757.     }
  758.     
  759.     vs_LoadCSS();    
  760. }
  761.  
  762. /**************************************************************************************************************************/
  763. var videosurf_Tab_TabHeight     = 156;
  764. var videosurf_Container_Width   = 338;
  765. var videosurf_Container_Padding = 11;
  766.  
  767. function videosurftab_InitializeTab(json) {        
  768.     var videoID = json.videos[0].video_id;
  769.         
  770.     //Create related tab, attach click handler, and append tab to the body of the current page
  771.     $('body').append($(
  772.         '<div id="videosurfRelated" class="videosurfRelated">' +
  773.         '    <div id="videosurfRelatedContainer">' +
  774.         '        <div id="videosurfRelatedContainerHeader">' +
  775.         '           <div id="videosurfRelatedContainerHeaderClose"></div>' +
  776.         '       </div>' +
  777.         '       <div id="videosurfRelatedContainerVideos">' +
  778.         '            <div id="videosurfRelatedContainerTextHeader">VideoSurf Related Videos</div>' +
  779.         '            <a id="videosurfRelatedContainerWatchOnVideosurf">Watch This Video on VideoSurf</a>' +        
  780.         '           <div id="videosurfRelatedVideosContainerPeople">' +
  781.         '                <div id="videosurfRelatedVideosContainerPeopleHeader">People Who Liked This Also Liked...</div>' +
  782.         '                <div id="videosurfRelatedVideosContainerPeopleVideos" class="vertical"></div>' +                        
  783.         '                <div id="videosurfRelatedVideosContainerPeopleMore">Show More</div>' +
  784.         '           </div>' +
  785.         '           <div id="videosurfRelatedVideosContainerRelated">' +
  786.         '                <div id="videosurfRelatedVideosContainerRelatedHeader">Related Videos</div>' +        
  787.         '                <div id="videosurfRelatedVideosContainerRelatedVideos" class="vertical"></div>' +                
  788.         '           </div>' +
  789.         '            <div id="videosurfRelatedVideosContainerRelatedNoneFoundError">' +
  790.         '            Sorry, no related videos were found.  <br>Please try again later. <br><br><img id="videosurfRelatedVideosContainerRelatedNoneFoundErrorImg"> ' +
  791.         '            </div>' +
  792.         '       </div>' +        
  793.         '    </div>' +
  794.         '</div>' +
  795.         '<div id="videosurfRelatedTab" class="videosurfRelatedTabClosed">' +
  796.         '    <div id="videosurfRelatedTabContainer">' +
  797.         '        <img id="videosurfRelatedContainerLoading" width="20" height="20" />' +
  798.         '    </div>' +
  799.         '</div>'
  800.     ));
  801.     
  802.     //Apply page specific styling to above template and load in cached images
  803.     $('#videosurfRelated').css({
  804.         background : 'url(' + VS_LOCAL_IMAGES['container-background'] + ')',
  805.          left       : -videosurf_Container_Width,
  806.          width      : videosurf_Container_Width
  807.     });
  808.  
  809.     $('#videosurfRelatedContainer').css({
  810.         background : 'url(' + VS_LOCAL_IMAGES['container-background'] + ')',
  811.          width      : videosurf_Container_Width - videosurf_Container_Padding
  812.     });
  813.     
  814.     $('#videosurfRelatedContainerHeader').css({
  815.         background : 'url(' + VS_LOCAL_IMAGES['container-header'] + ')',
  816.          width      : videosurf_Container_Width - videosurf_Container_Padding        
  817.     });
  818.  
  819.     $('#videosurfRelatedVideosContainerRelatedNoneFoundErrorImg').attr({
  820.         src : "http://rexee-00.vo.llnwd.net/d1/application/images/error_bear.gif"
  821.     });
  822.  
  823.     $('#videosurfRelatedContainerHeaderClose').click(function() {
  824.         videosurftab_HideRelated();
  825.     });
  826.  
  827.     $('#videosurfRelatedContainerVideos').css({
  828.          width      : videosurf_Container_Width - videosurf_Container_Padding,
  829.         height     : videosurftab_RelatedVideoContainerHeight()    
  830.     });
  831.  
  832.     if(!json.videos[0].should_open_externally) {
  833.         $('#videosurfRelatedContainerWatchOnVideosurf').attr(
  834.             'href', json.videos[0].url
  835.         ).css({
  836.             display    : 'block',
  837.              background : 'url(' + VS_LOCAL_IMAGES['arrow-right'] + ') no-repeat center right',
  838.         });
  839.     }
  840.  
  841.     $('#videosurfRelatedVideosContainerPeopleMore').css({
  842.          background : 'url(' + VS_LOCAL_IMAGES['arrow-down'] + ') no-repeat center right',
  843.     });
  844.  
  845.     $('#videosurfRelatedTab').css({
  846.         background : 'url(' + VS_LOCAL_IMAGES['tab-closed'] + ')',
  847.          height     : videosurf_Tab_TabHeight,
  848.          top        : videosurftab_GetTabLocation()
  849.     }).addClass('closed');
  850.         
  851.     $('#videosurfRelatedTabContainer').css({
  852.          height     : videosurf_Tab_TabHeight
  853.     });
  854.  
  855.     $('#videosurfRelatedContainerLoading').attr({
  856.         src        : VS_LOCAL_IMAGES['tab-loading-feedback']
  857.     });
  858.         
  859.     //Attach an event handler to adjust tab tab location on window resize
  860.     $(window).resize(function() {
  861.         $('#videosurfRelatedTab').css({
  862.             top    : videosurftab_GetTabLocation()
  863.         });
  864.         
  865.         $('#videosurfRelatedContainerVideos').css({
  866.             height : videosurftab_RelatedVideoContainerHeight()
  867.         });    
  868.     });
  869.     
  870.     //Show tab
  871.     $('#videosurfRelatedTab').click(function() {
  872.         if($('#videosurfRelatedTab').hasClass('videosurfRelatedTabClosed')) {
  873.             $('#videosurfRelatedTab').css({
  874.                 background : 'url(' + VS_LOCAL_IMAGES['tab-loading'] + ')'
  875.             });
  876.             
  877.             if(!VS_CACHED_RELATED_VIDEOS[videoID]) {
  878.                 videosurftab_GetRelatedVideos(videoID);                
  879.             }
  880.             else {    
  881.                 videosurftab_ProcessRelatedVideos(videoID);
  882.             }
  883.         }
  884.         else {
  885.             videosurftab_HideRelated();            
  886.         }            
  887.     }).fadeIn();
  888. }
  889.  
  890. function videosurftab_GetTabLocation() {
  891.     var newTop = ($(window).height() - videosurf_Tab_TabHeight) / 2;
  892.  
  893.     return newTop <= 0 ? 0 : newTop;    
  894. }
  895.  
  896. function videosurftab_RelatedVideoContainerHeight() {
  897.     return $(window).height() - $('#videosurfRelatedContainerHeader').height();
  898. }
  899.  
  900. function videosurftab_ShowRelated() {
  901.     $('#videosurfRelatedTab').removeClass('videosurfRelatedTabClosed').addClass('videosurfRelatedTabOpen');
  902.     
  903.     $('#videosurfRelatedTab').animate({
  904.         left : videosurf_Container_Width - videosurf_Container_Padding
  905.     });
  906.  
  907.     $('#videosurfRelated').animate({
  908.         left : 0
  909.     });
  910. }
  911.  
  912. function videosurftab_HideRelated() {
  913.     $('#videosurfRelatedTab').removeClass('videosurfRelatedTabOpen').addClass('videosurfRelatedTabClosed');
  914.     
  915.     $('#videosurfRelatedTab').animate({
  916.         left : 0
  917.     });   
  918.       
  919.     $('#videosurfRelated').animate(
  920.         { left : -videosurf_Container_Width },
  921.         { complete : function() { $('#videosurfRelatedTab').css({background : 'url(' + VS_LOCAL_IMAGES['tab-closed'] + ')'}) } }
  922.     );
  923. }
  924.  
  925. function videosurftab_GetRelatedVideos(videoID) {    
  926.     $('#videosurfRelatedContainerLoading').show();
  927.     
  928.     ajaxUrl = VS_AJAX_URL_ROOT + "/related_videos/v1.2/?visual_summary_size=10&client_id=" + API_CLIENT_ID + "&client_key=" + API_CLIENT_KEY + "&vss=generic_video&id=" + videoID;
  929.     ajaxCall(ajaxUrl, function(json) {                
  930.         vs_log(json, true);        
  931.         VS_CACHED_RELATED_VIDEOS[videoID] = json;
  932.         
  933.         $('body').append($('<iframe style="position: absolute; top: -1000px; left: -1000px; height: 1px; width: 1px;" src="http://network.videosurf.com/beacon/video"></iframe>'));
  934.         
  935.         videosurftab_ProcessRelatedVideos(videoID);        
  936.     });    
  937. }
  938.  
  939. function videosurftab_ProcessRelatedVideos(videoID) {
  940.     var behavioralRelated = VS_CACHED_RELATED_VIDEOS[videoID]['People who liked this video also liked'];
  941.     var regularRelated = VS_CACHED_RELATED_VIDEOS[videoID]['Related Videos'];
  942.     if(behavioralRelated) {
  943.         var relatedVideoHTML = jQuery.map(behavioralRelated, function(video, index) {
  944.             video.img_width  = 119;
  945.             video.img_height = 73;
  946.  
  947.             return getVideoHtml(video, false, index);
  948.         }).join('');
  949.     
  950.         if(relatedVideoHTML) {
  951.             $('#videosurfRelatedVideosContainerPeople').show();    
  952.  
  953.             var videosToHide = $('#videosurfRelatedVideosContainerPeopleVideos').html(
  954.                 relatedVideoHTML
  955.             ).find(
  956.                 '.summaryContainer:gt(3)'
  957.             );
  958.  
  959.             if(videosToHide.length > 0) {
  960.                 videosToHide.hide();
  961.                 $('#videosurfRelatedVideosContainerPeopleMore').show().toggle(
  962.                     function() {
  963.                         videosToHide.show();
  964.                         $(this).css({
  965.                              background : 'url(' + VS_LOCAL_IMAGES['arrow-up'] + ') no-repeat center right',
  966.                         }).text(
  967.                             'Show Less'
  968.                         );
  969.                     },
  970.                     function() {
  971.                         videosToHide.hide();
  972.                         $(this).css({
  973.                              background : 'url(' + VS_LOCAL_IMAGES['arrow-down'] + ') no-repeat center right',
  974.                         }).text(
  975.                             'Show More'
  976.                         );                        
  977.                     }
  978.                 );
  979.             }
  980.         }
  981.     }
  982.     
  983.     if(regularRelated) {
  984.         var relatedVideoHTML = jQuery.map(regularRelated, function(video, index) {
  985.             video.img_width  = 119;
  986.             video.img_height = 73;
  987.  
  988.             return getVideoHtml(video, false, index);
  989.         }).join('');
  990.     
  991.         if(relatedVideoHTML) {
  992.             $('#videosurfRelatedVideosContainerRelated').show();            
  993.             $('#videosurfRelatedVideosContainerRelatedVideos').html(relatedVideoHTML);
  994.         }
  995.     }
  996.  
  997.     if(!behavioralRelated && !regularRelated) {
  998.         $('#videosurfRelatedVideosContainerRelatedNoneFoundError').show();            
  999.     }
  1000.     
  1001.     attachSummaryEventHandlers();        
  1002.     
  1003.     $('#videosurfRelatedContainerLoading').hide();        
  1004.     videosurftab_ShowRelated();
  1005. }
  1006.  
  1007. /**************************************************************************************************************************/
  1008.  
  1009. /**
  1010.  * Take a template string that has variables inside with a {variable} format and substitutes all the items
  1011.  * in the context dictionary into the string, returning the result
  1012.  * s = '<a href="{url}">{title}</a>'; fillTemplate(s, {'url': 'http://whatever.com', 'title': 'this is the link title'});
  1013.  **/
  1014. function fillTemplate(templateString, context) {
  1015.     for(key in context) {
  1016.         s = "{"+key+"}";
  1017.         templateString = templateString.replace(new RegExp(s,"g"), context[key]);
  1018.     }
  1019.     return templateString;
  1020. }
  1021.  
  1022. /**
  1023.  * Utility function to make ajax calls.  Assumes json response.  
  1024.  **/
  1025. function ajaxCall(url, callback, additionalVar) {
  1026.     vs_log("Doing GET ajax call to: "+url);
  1027.  
  1028.     VS_xmlhttpRequest({
  1029.         method: 'GET',
  1030.         url: url,
  1031.         headers: {
  1032.             'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey - VSFFEXT',
  1033.             'Accept': 'application/atom+xml,application/xml,text/json,text/html,application/javascript'
  1034.         },
  1035.         onload: function(resp) { var json = evalJson(resp.responseText); json.original_ajax_url = url; callback(json, additionalVar); }
  1036.     });
  1037. }
  1038.  
  1039. function ajaxCallPost(parameters, callback, additionalVar) {
  1040.     vs_log("Doing POST ajax call to: "+ parameters.url + '?' + jQuery.param(parameters.get) + ' POST: ' + jQuery.param(parameters.post));
  1041.         
  1042.     VS_xmlhttpRequest({
  1043.         method  : 'POST',
  1044.         url     : parameters.url + '?' + jQuery.param(parameters.get),
  1045.         headers : {
  1046.             'User-Agent'   : 'Mozilla/4.0 (compatible) Greasemonkey - VSFFEXT',
  1047.             'Accept'       : 'application/atom+xml,application/xml,text/json,text/html,application/javascript',
  1048.             'Content-Type' : 'application/x-www-form-urlencoded'
  1049.         },
  1050.         data    : jQuery.param(parameters.post),
  1051.         onload  : function(resp) {             
  1052.             var json = evalJson(resp.responseText);
  1053.             json.original_ajax_url = parameters.url + '?' + jQuery.param(parameters.get);
  1054.             
  1055.             callback(json, additionalVar);
  1056.         }
  1057.     });
  1058. }
  1059.  
  1060.  
  1061. function evalJson(jsonString) {
  1062.     try {
  1063.         return JSON.parse(jsonString);
  1064.     } 
  1065.     catch(e) {
  1066.         vs_log('Whoops, there was an error evaluating the json from the server:'+txt)
  1067.     }
  1068. }
  1069.  
  1070. /**
  1071.  * Log something to the Firebug console if it's present and logging is active in preferences (off by default, hidden option)
  1072.  **/
  1073. function vs_log(item, clean) {
  1074.     if(VS_LOGGING_ACTIVE) {
  1075.         if(!clean) { item = "VS FFEXT:"+item; } //Pass true in for clean if you want to log an object or html element that needs further inspection and not a regular string
  1076.         try { unsafeWindow.console.log(item); } catch(e) { }
  1077.     }
  1078. }
  1079.  
  1080. // Array Remove - By John Resig (MIT Licensed)
  1081. Array.remove = function(array, from, to) {
  1082.     var rest = array.slice((to || from) + 1 || array.length);
  1083.     array.length = from < 0 ? array.length + from : from;
  1084.     return array.push.apply(array, rest);
  1085. };
  1086.  
  1087. function addCommas(nStr) {
  1088.     nStr += '';
  1089.     x = nStr.split('.');
  1090.     x1 = x[0];
  1091.     x2 = x.length > 1 ? '.' + x[1] : '';
  1092.     var rgx = /(\d+)(\d{3})/;
  1093.     while (rgx.test(x1)) {
  1094.         x1 = x1.replace(rgx, '$1' + ',' + '$2');
  1095.     }
  1096.     return x1 + x2;
  1097. }
  1098.  
  1099. function cleanQuery(str) {
  1100.         return encodeURIComponent(str.replace(/^ +/, '').replace(/ +$/, '')).replace(/%20/g, "+"); //special case spaces to be + instead of the super ugly %20
  1101. }
  1102.  
  1103. function extractVideoSurfIdFromUrl(url) {
  1104.     var re = new RegExp("videosurf.com\/video\/(.*)-([0-9]*)", "i"); 
  1105.     var matches = url.match(re);
  1106.     if(matches && matches.length > 1) {
  1107.         return matches[2];
  1108.     }
  1109.     return null;
  1110. }
  1111.  
  1112. //GO!
  1113. vs_init();
  1114.