// ==UserScript==
// @name        Download video from http://youtube.com/
// @version     2.02
// @date        2010-01-03
// @author      Mike Samokhvalov <mikivanch@gmail.com>
// @download    http://www.puzzleclub.ru/files/youtube_com.js
// @include     http://youtube.com/watch*
// @include     http://*.youtube.com/watch*
// ==/UserScript==

(function(){
  ///////////////////////////////////////////////////////////////////
  // SETTINGS
  
  var find_links = {
    'FLV HQ' : true,
    'MP4' : true,
    'MP4 HD' : true
  }
  
  // END OF SETTINGS
  ///////////////////////////////////////////////////////////////////
  
  var base_url = '';
  var link_id_prefix = 'userjs_link_';
  
  var fmt_params = new Array();
  
  if(find_links['FLV HQ'])
  {
    fmt_params['35'] = {
      'map' : '35/640000/',
      'name' : 'FLV HQ',
      'link' : ''
    };
  }  

  if(find_links['MP4'])
  {
    fmt_params['18'] = {
      'map' : '18/512000/',
      'name' : 'MP4',
      'link' : ''
    };
  }
  
  if(find_links['MP4 HD'])
  {
    fmt_params['22'] = {
      'map' : '22/2000000/',
      'name' : 'MP4 HD 720p',
      'link' : ''
    };
    
    fmt_params['37'] = {
      'map' : '37/4000000/',
      'name' : 'MP4 HD 1080p',
      'link' : ''
    };
  }  
  
  fmt_params['34'] = {
    'map' : '34/0/',
    'name' : 'FLV',
    'link' : ''
  };
  
  

  
  
  
  function getTitle()
  {
    if(window.yt && yt.config_ && yt.config_['VIDEO_TITLE'])
      return decodeURIComponent(yt.config_['VIDEO_TITLE']);
      
    var t = document.getElementById('watch-vid-title');    
    if(t)
    {
      var h1 = t.getElementsByTagName('h1');
      if(h1 && h1.length > 0)
        return h1[0].innerText;
    }

    return '';  
  }
  
  function modifyTitle(t)
  {
    t = t.replace(/[\x2F\x5C\x3A\x7C]/g, '-');
    t = t.replace(/[\x2A\x3F]/g, '');
    t = t.replace(/\x22/g, '\'');
    t = t.replace(/\x3C/g, '(');
    t = t.replace(/\x3E/g, ')');
    t = t.replace(/(?:^\s+)|(?:\s+$)/g, '');
    return t;
  }
  
  function getFmt()
  {
    if(!location.search)
      return '';

    var m = location.search.match(/[\?\&]fmt=(\d+)/i);
    if(m && m.length > 1)
      return m[1];
      
    return '';  
  }
  
  function getFmtMap(doc)
  {
    var swf_args = doc.match(/[\"\']swf_args[\"\']\s*:\s*\{([^\}]+)\}/i);
    if(swf_args && swf_args.length > 1)
    {
      var fmt_map =  swf_args[1].match(/[\"\']fmt_map[\"\']\s*:\s*[\"\']([^\s\"\']+)/i);
      if(fmt_map && fmt_map.length > 1)
        return decodeURIComponent(fmt_map[1]);
    }
    
    return '';
  }
  
  function show_link(fmt, link)
  {
    var e = document.getElementById(link_id_prefix + fmt);
    if(!e)
      return;
      
    if(e.tagName == 'A')
    {
      if(link)
      {
        e.href = link;
        e.style.fontWeight = 'bold';
        if(fmt_params[fmt])
          e.innerHTML = fmt_params[fmt]['name'];
      }
        
      return;
    }  
    
    if(!link)
    {
      e.innerHTML = fmt_params[fmt]['name'];
      return;
    }  
      
    var a = document.createElement('a');
    a.href = link;
    a.style.fontWeight = 'bold';
    if(fmt_params[fmt])
        a.innerHTML = fmt_params[fmt]['name'];
        
    e.parentNode.replaceChild(a, e);    
  }
  
  function check_links(url)
  {
    if(!url)
      return;
      
    var checkUrl = '', fmt = '';
    for(var i in fmt_params)
    {
      if(fmt_params[i] && !fmt_params[i]['link'])
      {
        fmt = i;
        checkUrl = url + '&fmt=' + fmt;
        break;
      }
    }
    
    if(!checkUrl)
      return;
      
    busy(link_id_prefix + fmt);
    
    sendRequest(checkUrl, function(r){
      if(r.responseText)
      {
        var fmt_map = getFmtMap(r.responseText);
        if(fmt_map)
        {
          fmt_map = fmt_map.split(',');
          if(check_fmt_map(fmt_map, fmt_params[fmt]['map']))
          {
            fmt_params[fmt]['link'] = base_url + '&fmt=' + fmt;
            show_link(fmt, fmt_params[fmt]['link']);
            delete fmt_params[fmt];
          }
          else
          {
            show_link(fmt, '');
            delete fmt_params[fmt];
          }

          for(var i in fmt_params)
          { 
            if(fmt_params[i] && !fmt_params[i]['link'])
            {
              if(check_fmt_map(fmt_map, fmt_params[i]['map']))
              {
                fmt_params[i]['link'] = base_url + '&fmt=' + i;
                show_link(i, fmt_params[i]['link']);
                delete fmt_params[i];
              }
            }
          }
        }
        else
        {
          show_link(fmt, '');
          delete fmt_params[fmt];
        }
          
        check_links(url);
      }
    }, '', location.href, '', '', '', '');
  }
  
  function busy(id)
  {
    var parent = document.getElementById(id);
    if(!parent)
    {
      return;
    }

    parent.innerHTML = '';
    var e = document.createElement('span');
    e = parent.appendChild(e);
    e.setAttribute('style', 'font-family: "courier new", "lucida console", monospace;', false);
      
    var i = 0;
    var text = ['&nbsp;&nbsp;&nbsp;','.&nbsp;&nbsp;','..&nbsp;','...','..&nbsp;','.&nbsp;&nbsp;'];
    
    var setText = function()
    {
      if(i >= text.length)
      {
        i = 0;
      }
      e.innerHTML = text[i];
      i++;
      setTimeout(setText, 200);
    }
    
    setText();
  }
  
  function check_fmt_map(fmt_map, map)
  {
    for(var i in fmt_map)
    {
      if(fmt_map[i].indexOf(map) == 0)
        return true;
    }
    
    return false;
  }
  
  function onLoad()
  {
    var video_id = '', t = '';
    
    var swfargs = null;
    if(window.yt && yt.config_ && yt.config_['SWF_ARGS'])
    {
      swfargs = yt.config_['SWF_ARGS'];
      
      video_id = (swfargs['video_id'] != undefined) ? swfargs['video_id'] : '';
      t = (swfargs['t'] != undefined) ? swfargs['t'] : '';
    }

    if(video_id && t)
      base_url = 'http://youtube.com/get_video?video_id=' + video_id + '&t=' + t;
    else
      return;

    var title = getTitle();  
    if(title)
      title = modifyTitle(title);    
    
    var size = title.length + 8;
    if(size > 64)
      size = 64;
      
    var inputAttr = (
      'type="text"  '
      +'onfocus="if(this.value && this.select){this.select()}" '
      +'style="border: 1px solid #c3d0ec; background-color: transparent;"'
    );

    var p = document.createElement('div');
    p.setAttribute('style', 'display:  block; color: #000; background-color: #e7eefa; border: 1px solid #c3d0ec; padding: 5px 0; text-align: center;', false);
    var html = '<table style="border-collapse: collapse; border: none; margin: 0 auto;">'    
      +'<tr><td align="left"><a href="' + base_url + '" style="font-weight:bold;">' + fmt_params[34]['name'] + '</a>';
      
    if(find_links['FLV HQ'])  
      html += '&nbsp|&nbsp;<span id="' + link_id_prefix + '35">' + fmt_params[35]['name'] + '</span>';
      
    html += '</td><td align="left">&nbsp;&nbsp;&nbsp;<input size="' + size + '" value="' + title + '.flv" ' + inputAttr + '></td></tr>';
    
    if(find_links['MP4'] || find_links['MP4 HD'])
    {
      var sep = '';
      
      html += '<tr><td align="left">';
      
      if(find_links['MP4'])
      {
        html += '<span id="' + link_id_prefix + '18">' + fmt_params[18]['name'] + '</span>';
        sep = '&nbsp|&nbsp;';
      }
      
      if(find_links['MP4 HD'])
      {
        html += sep + '<span id="' + link_id_prefix + '22">' + fmt_params[22]['name'] + '</span>';
        html += sep + '<span id="' + link_id_prefix + '37">' + fmt_params[37]['name'] + '</span>';
      }

      html += '</td><td align="left">&nbsp;&nbsp;&nbsp;<input size="' + size + '" value="' + title + '.mp4" ' + inputAttr + '></td></tr>';
    }      
      
    html += '</table>';
      
    p.innerHTML = html;
    document.body.insertBefore(p, document.body.firstChild);
    
    var fmt = getFmt();
    var fmt_map = swfargs['fmt_map'];
    if(fmt_map)
      fmt_map = decodeURIComponent(fmt_map);
    else
      fmt_map = getFmtMap(document.documentElement.innerHTML);

    if(fmt_map)
    {
      fmt_map = fmt_map.split(',');
      if(fmt && fmt_params[fmt])
      {
        if(check_fmt_map(fmt_map, fmt_params[fmt]['map']))
        {
          fmt_params[fmt]['link'] = base_url + '&fmt=' + fmt;
        }
        else
        {
          delete fmt_params[fmt];
          
          if(fmt != '34' && check_fmt_map(fmt_map, fmt_params['34']['map']))
            fmt_params['34']['link'] = base_url + '&fmt=34';
          else
            delete fmt_params['34'];
        }
      }
      else
      {
        for(var i in fmt_params)
        {
          if(check_fmt_map(fmt_map, fmt_params[i]['map']))
          {
            fmt_params[i]['link'] = base_url + '&fmt=' + i;
          }  
        }
      }
    }
    
    
    for(var i in fmt_params)
    {
      if(fmt_params[i] && fmt_params[i]['link'])
      {
        show_link(i, fmt_params[i]['link']);
        delete fmt_params[i];
      }
    }
    
    check_links('http://' + location.host + '/watch?v=' + video_id);
  }
  
  
  
  function sendRequest(url, callback, method, referer, post, cookie, user_agent, header)
  {
    var req = new XMLHttpRequest();
    if (!req)
      return;

    method = method ? method : ((post) ? 'POST' : 'GET');
    user_agent = user_agent ? user_agent : navigator.userAgent;
    
    req.open(method, url, true);
    
    req.setRequestHeader('User-Agent', user_agent);
    if(referer)
    {
      req.setRequestHeader('Referer', referer);
    }
    if(cookie)
    {
      req.setRequestHeader('Cookie', cookie);
    }
    if (post)
    {
      req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
      req.setRequestHeader("Content-Length", post.length);
    }
    if(header)
    {
      for(var i = 0; i < header.length; i++)
      {
        req.setRequestHeader(header[i][0], header[i][1]);
      }
    }
    
    req.onreadystatechange = function ()
    {
      if (req.readyState != 4)
        return;
      
      callback(req);
    };
    
    if (req.readyState == 4)
    {
      return;
    }  
    
    if(post)
      req.send(post);
    else
      req.send();
  }
  
  

  if(typeof(opera.version) == 'function' && opera.version() >= 9)
    document.addEventListener('DOMContentLoaded', onLoad, false);  
  else
    document.addEventListener('load', onLoad, false);

})();