// ==UserScript==
// @name        Opera DHTML Menu Fixer
// @namespace   http://www.scss.com.au/family/andrew/opera/userjs/
// @version     1.8
// @date        2005-06-04
// @author      Andrew Gregory <andrew at scss dot com dot au>
// @description Attempts to automatically fix problem web menu scripts.
// @include     *
// ==/UserScript==
// License:     http://creativecommons.org/licenses/by-nc-sa/2.0/
// Notes:       Scans web page scripts looking for characteristics of
//              web menus that have known issues with Opera. Any found
//              scripts are reported. Fixes are attempted where possible.
//              Notification is via the JavaScript console and a pop-up
//              at the bottom-right of the window.

// NOTE: only partial support for popup display of multiple messages. currently, only the first is shown.

window.operaMenuFixer = {

  // == Configuration Start ==
  enableFixes:true,          // enable/disable fixing of menus, popup will still be shown even if the menu isn't fixed
  showAllMenus:false,        // show popup, even for menus that didn't need fixing
  disableNotification:false, // set true to never show popup
  // == Configuration End ==
  
  menu:'',
  desc:'',
  msgs:[],
  detect:function(ev) {
    var t, menu, desc, ver, omf = window.operaMenuFixer, name = ev.element.src, js = ev.element.text;
    if (name.length == 0) {
      // inline script
    } else {
      // external script

      /* * * * * * *
       * Coolmenus *
       * * * * * * */
      t = js.match(/Coolmenus/);
      if (t) {
        t = js.match(/\d/);
        ver = parseFloat(js.substring(t.index));
        menu = 'Coolmenus detected, v' + ver;
        desc = '';
        return {menu:menu,desc:desc};
      }

      /* * * * * *
       * Sothink *
       * * * * * */
      if (js.match(/stnav\(/)) {
        t = js.match(/\d/);
        ver = parseFloat(js.substring(t.index));
        menu = 'Sothink menu detected, v' + ver;
        desc = '';
        if (ver > 0 && ver <= 3.72) {
          desc = 'Old version';
          if (omf.enableFixes) {
            // UNTESTED - lots of variations

            // fix by forcing Opera to be identified as a Gecko browser
            js = js.replace(/navigator.appName/, '"Netscape"');
            js = js.replace(/navigator.appVersion/, '"5.0"');
            js = js.replace(/navigator.product/, '"Gecko"');
            js = js.replace(/navigator.productSub/, '"20041110"');
            ev.element.text = js;

          }
        }
        return {menu:menu,desc:desc};
      }

      /* * * * * *
       * Milonic *
       * * * * * */
      if (js.match(/Milonic/i) || js.match(/Wolley/i)) {
        t = js.match(/\d/);
        ver = parseFloat(js.substring(t.index));
        menu = 'Milonic menu detected, v' + ver;
        desc = '';
        if (ver > 0 && ver < 3.5) {
          desc = 'Old version';
          if (omf.enableFixes) {
            // NOTE: lots of variations here, not extensively tested

            // fix by preventing detection of Opera (and mis-detection of IE) and forcing detection as a Gecko browser
            js = js.replace(/konq=/, 'opera=ie4=ie55=false;ns6=ns61=true;konq=');
            ev.element.text = js;

          }
        }
        return {menu:menu,desc:desc};
      }

      /* * * * * * *
       * HierMenus *
       * * * * * * */
      if (js.match(/HM_IsMenu/)) {
        menu = 'HierMenus detected';
        desc = '';
        if (js.match(/!HM_Opera/)) {
          desc = 'Opera blocked!';
          if (omf.enableFixes) {

            // fix by changing Opera from an unsupported browser to a supported browser
            js = js.replace(/\!HM_Opera\s*&&\s*/, '');
            js = js.replace(/\(HM_DOM\s*\|\|/, '\(HM_Opera || HM_DOM ||');
            ev.element.text = js;

          }
        }
        return {menu:menu,desc:desc};
      }

      /* * * * * * * * * * *
       * Likno AllWebMenus *
       * * * * * * * * * * */
      if (js.match(/"\/awmlib"/)) {
        menu = 'Likno AllWebMenus detected';
        desc = '';
        if (omf.enableFixes) {

          // fix by forcing menu build #s before 498 to identify Opera as a Gecko browser
          js = js.replace(/"\+scriptNo\+/, '"+((awmBN<498)?1:scriptNo)+');
          ev.element.text = js;

        }
        return {menu:menu,desc:desc};
      }

      /* * * * * * * * * * * * * *
       * Menu/Pop Menu (HV Menu) *
       * * * * * * * * * * * * * */
      if (js.match(/burmees\.nl/) || js.match(/var PosStrt\=\(/)) {
        menu = 'Menu/Pop Menu (HV Menu) detected';
        desc = '';
        if (js.match(/PosStrt\=\(NavYes\|\|ExpYes\)\&\&\!Opr\?1\:0\;/)) {
          desc = 'Opera blocked!';
          if (omf.enableFixes) {

            // fix by changing Opera from an unsupported browser to a supported browser
            js = js.replace(/PosStrt\=\(NavYes\|\|ExpYes\)\&\&\!Opr\?1\:0\;/, 'NavYes=Opr=DomNav=Nav4=Exp4=0;ExpYes=DomExp=PosStrt=1;');
            ev.element.text = js;

          }
        }
        if (js.match(/"opera 7"/) && !js.match(/"opera 8"/)) {
          desc = 'No Opera 8 support';
          if (omf.enableFixes) {

            // fix by replacing Opera version 7 detection with version 8 detection
            js = js.replace(/"opera 7"/, '"opera 8"');
            js = js.replace(/"opera\/7"/, '"opera/8"');
            ev.element.text = js;

          }
        }
        return {menu:menu,desc:desc};
      }

      /* * * * * * * * * * *
       * Ultimate Dropdown *
       * * * * * * * * * * */
      if ( name.match(/udm\-/) || ( name.match(/sniffer\.js/) && js.indexOf('UDM') != -1 ) ) {
        menu = 'Ultimate Dropdown Menu detected';
        desc = '';
        if ( !name.match(/sniffer\.js/) && js.match(/opera\[\\\/ \]7\./) && !js.match(/opera\[\\\/ \]\/\)\[1\]\.match\(\/\[7-9\]/) ) {
        	//UDM 4
          desc = 'No Opera 8 support';
          if (omf.enableFixes) {

            // fix by replacing Opera version 7 detection with version 8 detection
            js = js.replace(/opera\[\\\/ \]7\./, 'opera[\\\/ ]8.');
            ev.element.text = js;

          }
        } else if ( name.match(/sniffer/) && !js.match(/opera\[\\\/ \]\[7-9\]/) ) {
        	//UDM 3
          desc = 'No Opera 8 support';
          if (omf.enableFixes) {

            // fix by setting opera 7 detection to always be 1
            opera.defineMagicVariable('op7',function () { return 1; },null);

          }
        }
        return {menu:menu,desc:desc};
      }

      /* * * * * * *
       * OpenCube  *
       * * * * * * */
      if (name.match(/dqm/) || js.match(/DQM_codebase/) || name.match(/cbrowser_opera/) || js.match(/cdd_menu/) ) {
        menu = 'OpenCube detected';
        desc = '';
        if (js.match(/msie/) && !js.match(/opera/)) {
          desc = 'No Opera support';
        }
        if (!js.match(/"7\."/)) {
          desc = 'No Opera 7 support';
          if (omf.enableFixes) {

            // fix by preventing detection of Opera and forcing detection as IE
            js = js.replace(/if\(\(ns5\)\|\|\(ie6\)\|\|\(ie4\)\)/, 'opera=false;ie=ie5=true;if((ns5)||(ie6)||(ie4))');
            ev.element.text = js;

          }
        }
        if (js.match(/"7\."/) && !js.match(/"8\."/)) {
          desc = 'No Opera 8 support';
          if (omf.enableFixes) {

            // fix by replacing Opera version 7 detection with version 8 detection
            js = js.replace(/"7\."/, '"8."');
            ev.element.text = js;

          }
        }
        if (js.match(/window\.vxml/)) {
          desc = 'vxml object detection';
          if (omf.enableFixes) {

            // fix by replacing bogus voice-xml (!) detection with a more sensible test
            js = js.replace(/window\.vxml/g, 'true');
            ev.element.text = js;

          }
        }
        return {menu:menu,desc:desc};
      }

      /* * * * *
       * Xara  *
       * * * * */
      if (name.match(/xara/)) {
        menu = 'Xara Menu detected';
        desc = '';
        return {menu:menu,desc:desc};
      }

      /* * * * * *
       * Unknown *
       * * * * * */
      // assume a script name with "menu" in it is some sort of menu script
      // can't do much, so far just check if an Opera 7 detection string is present without
      // an Opera 8 detection string, and if so replace the '7' string with an '8' string
      if (name.match(/menu/)) {
        menu = 'Unknown menu detected';
        desc = '';
        if (js.match(/[Oo]pera 7/) && !js.match(/[Oo]pera 8/)) {
          desc = 'No Opera 8 support';
          if (omf.enableFixes) {

            js = js.replace(/([Oo])pera 7/, '$1pera 8');
            ev.element.text = js;

          }
        }
        if (js.match(/[Oo]pera\/7/) && !js.match(/[Oo]pera\/8/)) {
          desc = 'No Opera 8 support';
          if (omf.enableFixes) {

            js = js.replace(/([Oo])pera\/7/, '$1pera\/8');
            ev.element.text = js;

          }
        }
        return {menu:menu,desc:desc};
      }
    }
    return null;
  },
  count:50,
  animate:function() {
    var b, div = document.getElementById('operaMenuFixMsg'), omf = window.operaMenuFixer;
    if (omf.count > 0) {
      omf.count--;
      div.style.bottom = (omf.count % 2) * 10 + 'px';
      div.style.right = (omf.count % 2) * 10 + 'px';
      window.setTimeout(arguments.callee, (omf.count == 0) ? 5000 : 20);
    } else {
      div.setAttribute('style', 'display:none');
      window.setTimeout(function() {
        document.body.removeChild(div);
      }, 0);
    }
  }
};
opera.addEventListener('BeforeScript', function(ev) {
  var omf = window.operaMenuFixer;
  var msg = omf.detect(ev);
  if (msg) {
    this.postError(msg.menu + ':' + msg.desc);
    omf.msgs[omf.msgs.length] = msg;
  }
}, false);
document.addEventListener('load', function() {
  var i, ns, div, msg, msgidx, node, smiley, omf = window.operaMenuFixer;
  if (omf && !omf.disableNotification && omf.msgs.length > 0) {
    msgidx = 0;
    for (i = 0; i < omf.msgs.length; i++) {
      if (omf.msgs[i].desc != '') {
        msgidx = i;
        break;
      }
    }
    if (omf.msgs[msgidx].desc != '' || omf.showAllMenus) {
      ns = document.getElementsByTagName('html')[0].namespaceURI;

      div = ns ? document.createElementNS(ns, 'div') : document.createElement('div');
      div.setAttribute('id', 'operaMenuFixMsg');
      div.setAttribute('style', 'background:#fff;position:fixed;width:200px;height:30px;bottom:0;right:0;border:2px inset #ccc;z-index:99999');

      msg = ns ? document.createElementNS(ns, 'div') : document.createElement('div');

      node = ns ? document.createElementNS(ns, 'div') : document.createElement('div');
      smiley = (omf.msgs[msgidx].desc != '') ? 'Angry' : 'Indifferent';
      node.setAttribute('style', 'background:#fff -o-skin(\'Smiley ' + smiley + '\');width:-o-skin;height:-o-skin;float:left;padding:0 2px 2px 0');
      msg.appendChild(node);

      node = ns ? document.createElementNS(ns, 'div') : document.createElement('div');
      node.setAttribute('style', 'font:message-box;color:#000;background:#fff;font-weight:bold');
      node.appendChild(document.createTextNode(omf.msgs[msgidx].menu));
      msg.appendChild(node);

      node = ns ? document.createElementNS(ns, 'div') : document.createElement('div');
      node.setAttribute('style', 'font:message-box;color:#000;background:#fff');
      node.appendChild(document.createTextNode(omf.msgs[msgidx].desc));
      msg.appendChild(node);

      div.appendChild(msg);

      document.body.appendChild(div);

      omf.animate();
    }
  }
}, false);
