// ==UserScript==
// @name        Block External (for Opera 9)
// @version     1.37
// @date        2008-04-19
// @author      Mike Samokhvalov <mikivanch@gmail.com>
// @download    http://www.puzzleclub.ru/files/block_external.js
// @exclude     file://*
// @exclude     ftp://*
// ==/UserScript==


(function(){

  ///////////////////////////////////////////////////////////////////
  // SETTINGS
  
  // Load images from the same top-level domain name.
  var checkTopLevelDomainName = true;
  
  // Don't block small images
  minImageWidth = 88;
  minImageHeight = 15;
  
  // Block big images
  bigImageWidth = 0;
  bigImageHeight = 0;
  
  // Limit the size of blocked element.
  maxBlockedElementWidth = 0;
  maxBlockedElementHeight = 0;
  limitSizeOfBlockedImages = true;
  
  var blockContextualAdvertising = false;
  
  // Automatically reload unblocked images.
  var autoReloadUnblockedImages = false;
  
  // Show header for img elements
  var bImgHeader = false;
  // Show header for embed and object elements
  var bEmbedHeader = false;
  // Show header for iframe elements
  var bFrameHeader = false;
  
  // Domain whitelist filter
  var whiteListFilter = {
    // example: ['flickr.com', 'yimg.com', 'images.google.com', 'yandex.net']
    image : [],
    ebmed : [],
    frame : []
  };
  
  ///////////////////////////////////////////////////////////////////
  // DO NOT EDIT

  if(!window.opera)
    return;
    
  if(typeof(opera.version) != 'function' || opera.version() < 9.0)
    return;
    
  var isBlockExternalScript = function(src)
  {
    if(src.indexOf('http://pagead2.googlesyndication.com') != -1)
        return true;
        
    if(src.indexOf('http://autocontext.begun.ru/') != -1)    
        return true;
        
    return false;    
  };
  
  var isBlockScript = function(text)
  {
    if(text.search(/(?:google_ad_client ?=)|(?:yandex_direct_print\x28\x29)|(?:http:\/\/an.yandex.ru\/code\/)/i) != -1)
      return true;
      
    return false;  
  };
  
  
  var bFrame = false;
  try
  {
    if(window.parent != window)
      bFrame = true;
  }
  catch(e)
  {
    bFrame = true;
  }  
  
  if(bFrame)
  {
    window.opera.addEventListener('BeforeScript', function(e){
      if(isBlockScript(e.element.text))
        e.preventDefault();
    }, false);    

    window.opera.addEventListener('BeforeExternalScript', function(e){
      if(isBlockExternalScript(e.element.src))       
        e.preventDefault();
    }, false);
    
    return;
  }
    
  var topLevelDomain = '';  
  var externalAttribute = 'ujs_external';
  var externalBlockAttribute = 'ujs_extblock'
  var externalUnblockedAttribute = 'ujs_external_unblocked';
  var oldTitleAttribute = 'ujs_oldTitle';
  var oldUsemapAttribute = 'ujs_oldUsemap';
  var contentAttribute = 'ujs_cnt';
  var tagAttribute = 'ujs_tag';
  var emptyAttribute = 'ujs_empty';  
  var styleId = 'block_external_images_style';
  var whiteListReFilter = {    
    image : null,
    ebmed : null,
    frame : null
  };
  
  var getCSSText = function(type)
  {  
    var externalStyle = 'display: inline-block !important; font-size:11px !important; font-family:verdana, arial, helvetica, sans-serif !important; font-weight:normal !important; text-align:center !important; vertical-align: middle !important; margin: auto !important; padding: 0 !important; opacity: 1 !important; ';
    var imgStyle = 'color: #000 !important; background: #cfdfff !important; border: 1px dashed #508aff !important; ';
    var embedStyle = 'color: #000 !important; background: #d3e7c7 !important; border: 1px dashed #5a9639 !important; ';
    var spanStyle = 'color: #000 !important; background: #ffcfcf !important; border: 1px dashed #e14040 !important; ';
    var parentStyle = 'color: #fff !important; border: none !important; text-align: left !important; width: auto !important; min-width: 50px !important; max-width: none !important; ';
    var blockAnchor = 'color: inherit !important; background: inherit !important; font-family: inherit !important; font-size: inherit !important; font-weight: inherit !important; text-decoration: none !important; border: none !important; display: block !important; padding: 0 !important; margin: 1px auto 0 1px !important; text-align: left !important;';
    var blockBtn = 'color: inherit !important; background: inherit !important; font-family: inherit !important; font-size: inherit !important; font-weight: inherit !important; text-decoration: none !important; border: 1px solid #fff !important; display: block !important; padding: 2px 3px !important; margin: 1px 1px 0 auto !important; float: right; width: 13px !important; height: 13px !important; text-align: center !important; vertical-align: middle !important;';

    var s = '';
    if(maxBlockedElementWidth > 0)    
    {
      s = 'max-width: ' + maxBlockedElementWidth + 'px !important; ';            
      parentStyle += 'max-width: ' + (maxBlockedElementWidth + 2).toString() + 'px !important; ';
    }
    if(maxBlockedElementHeight > 0)
      s += 'max-height: ' + maxBlockedElementHeight + 'px !important; ';

    if(s)
    {
      embedStyle += s;
      spanStyle += s;
    }    

    var bigImgStyle = 'img[src^="http://"], img[src^="https://"]';
    if(bigImageWidth > 0 || bigImageHeight > 0)
      bigImgStyle = 'img';

    var css = '';
    if(type == 0)
    {    
      css = bigImgStyle + ' {content: " " !important; ' + imgStyle + externalStyle + '}';
      css += ' embed[src^="http://"], embed[src^="https://"] {content: " " !important; ' + embedStyle + externalStyle + '}';
      css += ' object[src^="http://"], object[src^="https://"] {content: " " !important; ' + embedStyle + externalStyle + '}';
      css += ' iframe[src^="http://"], iframe[src^="https://"] {display: none !important;}';
      css += ' span[' + externalAttribute + '] {content: " " !important; ' + spanStyle + externalStyle + '}';
    }
    else if(type == 1 || type == 2)
    {
      if(s && limitSizeOfBlockedImages)
        imgStyle += s;
    
      css = 'img[' + externalAttribute + '] {' + imgStyle + externalStyle + '}';
      css += ' img[' + emptyAttribute + '] {width: 80px !important; height: 16px !important;}';
      css += ' embed[' + externalAttribute + '] {' + embedStyle + externalStyle + '}';
      css += ' object[' + externalAttribute + '] {' + embedStyle + externalStyle + '}';
      css += ' span[' + externalAttribute + '] {' + spanStyle + externalStyle + '}';
      css += ' span[' + emptyAttribute + '] {width: 80px !important; height: 16px !important;}';
      if(type == 2)
      {
        css += ' ' + bigImgStyle + ' {content: " " !important; ' + imgStyle + externalStyle + '}';
      }
    }    

    css += ' span[' + externalBlockAttribute + '] {' + externalStyle + parentStyle + '}';
    css += ' span.img[' + externalBlockAttribute + '] {background: #7da3ed !important;' + externalStyle + '}';
    css += ' span.embed[' + externalBlockAttribute + '] {background: #86b56a !important;' + externalStyle + '}';
    css += ' span.iframe[' + externalBlockAttribute + '] {background: #ff8c8c !important;' + externalStyle + '}';
    css += ' span[' + externalBlockAttribute + '] a {' + blockAnchor + '}';
    css += ' span[' + externalBlockAttribute + '] input {' + blockBtn + '}';
    
    return css;
  };
  
  var strToRegExp = function(str)
  {
    if(!str)
      return '';

    var nulls = new Array('00', '0', '');
    var reStr = '';
    for(var i = 0; i < str.length; i++)
    {          
      var code = str.charCodeAt(i);
      code = code.toString(16);          
      code = '\\u' + nulls[code.length - 2] + code;
      reStr += code;      
    }
    return reStr;
  };
  
  var getFilterRegExp = function(filter)
  {
    if(filter.length <= 0)
      return null;
      
    var re = '', sep = '';
    for(var i = 0; i < filter.length; i++)
    {
      var str = strToRegExp(filter[i]);
      if(str)
      {
        re += sep + str;
        sep = '|';
      }
    }
    
    if(re)
    {
      re = '(' + re + ')';
      return new RegExp(re);
    }
    return null;
  };
  
  var createWhiteListReFilter = function()
  {
    whiteListReFilter.image = getFilterRegExp(whiteListFilter.image);
    whiteListReFilter.ebmed = getFilterRegExp(whiteListFilter.ebmed);
    whiteListReFilter.frame = getFilterRegExp(whiteListFilter.frame);
  };
  createWhiteListReFilter();  
  
  getTopLevelDomainName = function(domain)
  {
    if(!domain.match(/^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}/))
      return domain;
    
    var a = domain.split('.');
    var l = a.length;
      
    if(l == 2)
      return domain;

    return (a[l - 2] + '.' + a[l - 1]);
  };
  topLevelDomain = getTopLevelDomainName(window.location.hostname);
  
  var addStyle = function(css, id) {
    if(!document || !document.documentElement)
      return;
      
    if(id && document.getElementById(id))  
    {
      var s = document.getElementById(id);
      s.innerHTML = '';
      s.appendChild(document.createTextNode(css));
    }
    else
    {
      var s = document.createElement('style');
      s.setAttribute('type', 'text/css');
      if(id)
      {
        s.id = id;
      }
      s.setAttribute('style', 'display:none !important;');
      s.appendChild(document.createTextNode(css));
      document.documentElement.appendChild(s);
    }
  };
  addStyle(getCSSText(0), styleId);

  var isInternal = function(src, filter)
  {
    if(src.indexOf('data:') == 0)
      return true;
      
    var a = document.createElement('a');
    a.href = src;

    if(a.hostname == window.location.hostname)
    {
      return true;
    }

    if(checkTopLevelDomainName)
    {
      var d = getTopLevelDomainName(a.hostname);      
      if(d == topLevelDomain)
      {
        return true;
      }
    }
    
    if(filter)
    {
      if(a.hostname.search(filter) != -1)
      {
        return true;
      }
    }

    return false;
  };
  
  var replaceExternalElement = function(obj, scr, tag, content)
  {
    obj.setAttribute(externalUnblockedAttribute, '1', false);
    var s = document.createElement('span');
    s.setAttribute(externalAttribute, '1', false);
    s.setAttribute('title', scr, false);
    s.setAttribute(tagAttribute, tag, false);
    s.setAttribute(contentAttribute, encodeURIComponent(obj.outerHTML), false);
    if(obj.hasAttribute(emptyAttribute))
    {
      obj.removeAttribute(emptyAttribute, false);
      s.setAttribute(emptyAttribute, '1', false);
    }
    
    var w = obj.width, h = obj.height;
    if(w)
      s.style.width = w + 'px';
    if(h)
      s.style.height = h + 'px';
    
    if(content)
      s.style.content = content;
    else
      s.style.content = '"' + tag + '"';
      
    obj.parentNode.replaceChild(s, obj);
    if(bFrameHeader)            
      createHeader(s, scr, 'iframe');
  };
  
  var getImageWidth = function(img)
  {
    var w = img.width;
    if(!w)
    {
      w = parseInt(img.getAttribute('width', false));
      if(isNaN(w))
        w = 0;
    }
    
    return w;
  };
  
  var getImageHeight = function(img)
  {
    var h = img.height;
    if(!h)
    {
      h = parseInt(img.getAttribute('height', false));
      if(isNaN(h))
        h = 0;
    }
    
    return h;
  };
  
  var modifyExternalImageAndFrame = function(obj, content)
  {
    obj.setAttribute(externalAttribute, '1', false);
    obj.setAttribute(oldTitleAttribute, obj.title, false);
    obj.setAttribute('title', obj.src, false);
    if(obj.hasAttribute('usemap'))
    {
      obj.setAttribute(oldUsemapAttribute, obj.getAttribute('usemap', false), false);
      obj.removeAttribute('usemap', false);
    }

    var w = getImageWidth(obj);
    var h = getImageHeight(obj);
    
    if(w > 0 && !obj.hasAttribute('width'))
      obj.setAttribute('width', w, false);
      
    if(h > 0 && !obj.hasAttribute('height'))
      obj.setAttribute('height', h, false);
    
    var cnt = '""';
    if(w == 0 || h == 0)
    {
      obj.setAttribute(emptyAttribute, '1', false);
      cnt = '"' + content + ' ?x?"';
    }
    else if(h < 16 || w < 64)
      cnt = '""';
    else if(w < 88)
      cnt = '"' + content + '"';
    else
      cnt = '"' + content + ' ' + w + 'x' + h + '"';
      
    return cnt;
  };
  
  var modifyExternalEmbedAndObject = function(obj, content, src)
  {
    obj.setAttribute(externalAttribute, '1', false);
    obj.setAttribute(oldTitleAttribute, obj.title, false);
    obj.setAttribute('title', src, false);
    
    var w = parseInt(obj.getAttribute('width', false));
    var h = parseInt(obj.getAttribute('height', false));
    if(isNaN(w))
      w = 31;
    if(isNaN(h))
      h = 128
      
    var cnt = '""';
    if(h < 16 || w < 64)
      cnt = '""';
    else if(w < 88)
      cnt = '"' + content + '"';
    else
      cnt = '"' + content + ' ' + w + 'x' + h + '"';
      
    return cnt;
  };  
  
  var createHeader = function(obj, src, class)
  {    
    obj.removeAttribute('align', false);
    
    var span = document.createElement('span');
    span.setAttribute(externalBlockAttribute, '1', false);
    span.setAttribute('class', class, false);
      
    var html = '<input type="button" value="&times;" onclick="var p=this.parentNode; p.parentNode.removeChild(p);">';              
    html += '<a href="' + src + '">link</a>';
    html += obj.outerHTML;
    span.innerHTML = html;
    obj.outerHTML = span.outerHTML;
  };
  
  var checkImage = function(tag, content, bUnblock)
  {
    var cache = new Array();
    var obj = document.getElementsByTagName(tag);
    for(var i = 0; i < obj.length; i++)
    {
      if(!obj[i].complete && obj[i].src && obj[i].src.toLowerCase().indexOf('data:') != 0 &&
        !obj[i].hasAttribute(externalAttribute) &&
        !obj[i].hasAttribute(externalUnblockedAttribute))
      { 
        var s = obj[i].getAttribute('src', false);
        if(s && (s.indexOf('http://') == 0 || s.indexOf('https://') == 0))
        {          
          var bInternal = false;
          if(!cache[obj[i].src])
          {
            bInternal = isInternal(obj[i].src, whiteListReFilter.image);
            if(bInternal)
            {
              cache[obj[i].src] = true;            
            }
          }          
          else
            bInternal = cache[obj[i].src];
          
          var w = getImageWidth(obj[i]), h = getImageHeight(obj[i]);
          if(!bInternal)
          {
            if((w > 0 && w < minImageWidth) || (h > 0 && h < minImageHeight))
              cache[obj[i].src] = true;
            else
            {
              cache[obj[i].src] = false;
              obj[i].style.content = modifyExternalImageAndFrame(obj[i], content);
              if(bImgHeader)
                createHeader(obj[i], obj[i].src, 'img');
            }
          }
          else if((bigImageWidth > 0 && w > bigImageWidth && h > 15) ||
            (bigImageHeight > 0 && h > bigImageHeight && w > 15))
          {
            cache[obj[i].src] = false;
            obj[i].style.content = modifyExternalImageAndFrame(obj[i], 'BIG IMG');            
            if(bImgHeader)
              createHeader(obj[i], obj[i].src, 'img');            
          }
        }
        else
        {
          var w = getImageWidth(obj[i]), h = getImageHeight(obj[i]);
          if((bigImageWidth > 0 && w > bigImageWidth && h > 15) ||
            (bigImageHeight > 0 && h > bigImageHeight && w > 15))
          {
            cache[obj[i].src] = false;
            obj[i].style.content = modifyExternalImageAndFrame(obj[i], 'BIG IMG');            
            if(bImgHeader)
              createHeader(obj[i], obj[i].src, 'img');
          }          
        }
      }      
    }    
  };
  
  var checkEmbedAndObject = function(tag, content)
  {
    var cache = new Array();
    var obj = document.getElementsByTagName(tag);
    for(var i = 0; i < obj.length; i++)
    {
      var s = obj[i].getAttribute('src', false);      
      if(s && s.toLowerCase().indexOf('data:') != 0 &&
        !obj[i].getAttribute(externalAttribute, false) &&
        !obj[i].hasAttribute(externalUnblockedAttribute))
      { 
        var bInternal = false;
        if(s.indexOf('http://') == 0 || s.indexOf('https://') == 0)
        {
          if(!cache[s])
          {
            bInternal = isInternal(s, whiteListReFilter.ebmed);
            if(bInternal)
              cache[s] = true;            
          }          
          else
            bInternal = cache[s];
            
          if(!bInternal)
          {
            cache[s] = false;
            obj[i].style.content = modifyExternalEmbedAndObject(obj[i], content, s); 
            if(bEmbedHeader)            
              createHeader(obj[i], s, 'embed');
          }
        }
      }
    }
  };
  
  var checkFrame = function(tag, content)
  {
    var obj = document.getElementsByTagName(tag);
    try
    {
      for(var i = 0; i < obj.length; i++)
      {
        if(obj[i].src && obj[i].src.toLowerCase().indexOf('data:') != 0 &&
          !obj[i].hasAttribute(externalUnblockedAttribute))
        { 
          var bInternal = false;
          var s = obj[i].getAttribute('src', false);
          if(s.indexOf('http://') == 0 || s.indexOf('https://') == 0)
          { 
            bInternal = isInternal(obj[i].src, whiteListReFilter.frame);              
            if(!bInternal)
            {
              var cnt = modifyExternalImageAndFrame(obj[i], content);
              replaceExternalElement(obj[i], obj[i].src, obj[i].tagName, cnt);
            }
          }
        }
      }
    }
    catch(e){}
  };
  
  var check = function()
  {    
    checkEmbedAndObject('embed', 'EMBED');
    checkEmbedAndObject('object', 'OBJECT');
    checkFrame('iframe', 'IFRAME');
  };
  
  var restoreImage = function(attrSrc){};
  if(typeof(opera.version) == 'function' && opera.version() >= 9.10)
  {
    var restoreImage = function(img)
    {      
      if(!img)
        return;
        
      var refreshImg = function(i, d)
      {
        i.style.display = d;
      };
      
      if(img.currentStyle.display != 'none')
      {
        var display = img.currentStyle.display;
        img.style.display = 'none';          
        setTimeout(refreshImg, 10, img, display);
      }      
    };
  }  
    
  var reloadImage = function(img)
  {
    var f = document.createElement('iframe');
    f.src = img.src;
    f.width = 0;
    f.height = 0;
    f.frameBorder = 'no';
    f.scrolling = 'no';
    f.onload = function(){
      this.parentNode.removeChild(this);      
      restoreImage(img);
    };
    document.documentElement.appendChild(f);
  };
  
  
  
  var unblockObject = function(obj)
  {
    try
    {
      if(obj.getAttribute(externalAttribute, false))
      {
        var title = obj.getAttribute(oldTitleAttribute, false);
        obj.setAttribute('title', title, false);
        obj.setAttribute(externalUnblockedAttribute, '1', false);
        obj.removeAttribute(oldTitleAttribute, false);
        obj.removeAttribute(externalAttribute, false);  
        obj.removeAttribute(emptyAttribute, false);          
        if(obj.hasAttribute(oldUsemapAttribute))
        {
          obj.setAttribute('usemap', obj.getAttribute(oldUsemapAttribute, false), false);
          obj.removeAttribute(oldUsemapAttribute, false);
        }        
        obj.style.content = "normal";
        
        var parent = obj.parentNode;
        if(parent.hasAttribute(externalBlockAttribute))
        {
          parent.parentNode.replaceChild(obj, parent);
          parent = obj.parentNode;
        }
        while(parent)
        {
          if(parent.tagName == 'BODY' || parent.tagName == 'HTML')
            break;            
            
          if(parent.tagName == 'A' || parent.hasAttribute('onclick'))
          {
            var html = parent.outerHTML;
            parent.innerHTML = '';
            parent.outerHTML = html;
            break;
          }

          parent = parent.parentNode;
        }

        if(obj.tagName == 'IMG' && autoReloadUnblockedImages)
        {      
          reloadImage(obj);
        }
      }
    }
    catch(e){}
  };
  
  var onMouseUp = function(obj)
  {
    try
    {
      if(obj.getAttribute(externalAttribute, false))
      {
        if(obj.tagName == 'IMG' || obj.tagName == 'EMBED' || obj.tagName == 'OBJECT')
        {
          unblockObject(obj);
          
          if(obj.tagName == 'IMG')
          {
            var imgs = document.selectNodes("//img[@src='" + obj.src + "']");
            for(var i = 0; i < imgs.length; i++)
            {
              if(imgs[i] != obj)
                unblockObject(imgs[i]);
            }
          }
        }
        else if(obj.tagName == 'SPAN')
        {
          var parent = obj.parentNode;
          if(parent.hasAttribute(externalBlockAttribute))
          {
            parent.parentNode.replaceChild(obj, parent);
          }
          
          var html = obj.getAttribute(contentAttribute, false);
          if(html)
          {
            html = decodeURIComponent(html);
            obj.outerHTML = html;
          }
        }
      }
    }
    catch(e){}
  };
 
  document.addEventListener('mouseup', function(e) {
    if(e.button == 0)
    {
      var obj = e.srcElement;
      onMouseUp(obj);
    }
  }, false);  

  if(blockContextualAdvertising)
  {
    window.opera.addEventListener('BeforeScript', function(e){
      if(isBlockScript(e.element.text))
      {
        e.preventDefault();
      }
      check();
    }, false);    

    window.opera.addEventListener('BeforeExternalScript', function(e){
      if(isBlockExternalScript(e.element.src))
      {
        e.preventDefault();
      }
      check();
    }, false);
  }
  else
  {
    window.opera.addEventListener('BeforeScript', check, false);
    window.opera.addEventListener('BeforeExternalScript', check, false);
  }
  
  window.addEventListener('load', function(){
    checkImage('img', 'IMG');
    check();
    addStyle(getCSSText(1), styleId);
  }, false);
    
  window.addEventListener('DOMContentLoaded', function(){
    check();
    addStyle(getCSSText(2), styleId);
  }, false);
})();
  