<overlay id="conqueryModoki"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!--
// ==UserScript==
// @name           conqueryModoki3
// @namespace      http://space.geocities.yahoo.co.jp/gl/alice0775
// @description    コンテキストメニューにQuery To...とサブメニューに検索エンジンリストを追加
// @description    選択したエンジンで検索,その後エンジンを元に戻す
// @include        main
// @compatibility  Firefox 2.0 3.0 3.1
// @author         Alice0775
// @version        2009/03/02 00:00 2009/02/18 14:30の串刺し検索バグ修正。
// ==/UserScript==
// @version        2009/02/25 00:00 userChrome.conqueryModoki.showInContext=true時缩短所需的时间
// @version        2009/02/24 20:55 userChrome.conqueryModoki.showInContext=true, promptNotSelected=false的时后是关闭菜单
// @version        2009/02/18 14:30 Full Screen でも動作するように
// @version        2009/01/12 00:00 コンテキストメニュー一番上を選択するといつもデフォルトエンジンで検索されるバグ修正
// @version        2008/12/27 22:00 コンテキストメニューのpopupshownのイベント伝搬を許可してみた
// @version        2008/06/21 22:00 検索エンジン編集後 SecondSearch0.4.2008052301のポップアップが変になるのを修正
// @version        2008/05/23 06:00 検索バーの検索エンジンの中右クリックでSearchOnEngineChange(Fx3)
// @version        2008/04/23 06:00 中クリック,右クリック時のバグFix
// @version        2008/04/21 12:00 文字数を制限した
// @version        2008/04/17 01:00 ポップアップが残るのを修正
// @version        2008/03/10 12:00 ルートメニューをダブルクリックでデフォルトエンジンで検索, 串刺し検索は, 親メニュー直下のエンジンに対してのみとした
// @version        2008/02/20 12:00 串刺し検索時ポップアップが残るのを修正
// @version        2007/12/06 01:00
// @version        2007/11/25 03:40 2007112405 Minefield/3.0b2pre にてサ－チバーのclass名が変わったので変更
// @version        2007/10/12  14:40 for Gecko/2007100405 Minefield/3.0a9pre
// @Note           userChrome.conqueryModoki.newTab を falseで現在タブで検索
// @Note           userChrome.conqueryModoki.clearAfter を trueで検索後,消去
// @Note           userChrome.conqueryModoki.promptNotSelected を trueで選択文無いときプロンプト表示
// @Note           userChrome.conqueryModoki.showInContext をtrueでコンテキストメニュー直下に検索エンジンを表示
// @Note           コンテキストでは右クリックまたはctrlキーで新規/現タブ反転
// @Note           コンテキストでは中クリックまたはshiftキーで選択文字列修正用プロンプト表示
// @Note           検索エンジンセパレータ : エンジン名に―――01等のダミー検索プラグイン
// @Note           検索エンジン階層化 : エンジン名に{01, }01等のダミー検索プラグイン
// @Note           階層化している場合, 親メニューを右クリック(コンテキストのみ)またはctrl+左クリックすることで串刺し検索
 -->
  <script type="application/x-javascript" xmlns="http://www.w3.org/1999/xhtml"><![CDATA[
  var conqueryModoki = {
    _currentEngine:null,
    
    searchBar: function(){
      var searchBar = null;
      try{
        searchBar = BrowserSearch.getSearchBar();
      }catch(e){
        searchBar = BrowserSearch.searchBar;  //fx3
      }
      return searchBar;
    },
    
    getEngineName: function(iEngine){
      const nsIBSS = Components.interfaces.nsIBrowserSearchService;
      const searchService =Components.classes["@mozilla.org/browser/search-service;1"].getService(nsIBSS);
      if(typeof iEngine == 'undefined'|| iEngine == null){
        var aEngine = searchService.defaultEngine;
      }else{
        var engines = searchService.getVisibleEngines({ });
        var aEngine = engines[iEngine];
      }
      return aEngine;
    },
    
    searchWord: function conqueryModokisearchWord(aEvent,iEngine){
      aEvent.stopPropagation();

      var aEngine = this.getEngineName(iEngine);

      var selected = this._getselection();
      //選択文字列の無いとき。 または,Shiftキ-押下時 はプロンプト表示
      if((selected==''
          && this.getPref("userChrome.conqueryModoki.promptNotSelected","bool",false))
        || (aEvent.shiftKey || aEvent.button == 1)){
        selected = window.prompt('搜索引擎: ' + aEngine.name ,selected);
        if(!selected) return;
      }
      selected = selected.substring(0,1024);
      var searchBar = this.searchBar();
      if (searchBar)
        searchBar.value = selected;
      this.doSearch(aEvent,iEngine, selected)
    },
    
    doSearch: function (aEvent,iEngine, word){
      if (word =="")  return;

      const nsIBSS = Components.interfaces.nsIBrowserSearchService;
      const searchService =Components.classes["@mozilla.org/browser/search-service;1"].getService(nsIBSS);
      var aEngine = this.getEngineName(iEngine);

      //新規タブに開くかどうか
      var flag = this.getPref("userChrome.conqueryModoki.newTab","bool",true);
      var anewTab = flag;
      if(aEvent.button == 2) {anewTab = !anewTab;} //右クリック 反転
      else if(aEvent.ctrlKey) {anewTab = !anewTab;} //ctrlキー 反転
      this.loadSearch(aEngine, word, anewTab, false, false)  //検索実行

      this.clearSearchBar();
    },
    
    clearSearchBar: function(){
      //検索ボックスを消す?
      var searchBar = this.searchBar();
      if ( searchBar && this.getPref("userChrome.conqueryModoki.clearAfter","bool",false)){
         searchBar.value = '' //検索ボックスをクリア;
      }
      this._dispatchEvent();
    },
    
    //Web検索
    loadSearch: function loadSearch(engine, searchText, useNewTab, backGround, useNewWin){
      var submission = engine.getSubmission(searchText, null); // HTML response
      if (!submission)
        return;
      if (useNewWin){
        openNewWindowWith(submission.uri.spec, null, submission.postData, false)
      }else if (useNewTab) {
        gBrowser.loadOneTab(submission.uri.spec, null, null, submission.postData, backGround, false);
      }else
        gBrowser.loadURI(submission.uri.spec, null, submission.postData, false);
    },
    
    
    kusizasi: function conqueryModokikusizasi(aEvent){
      this.hideAllPopup();
      if(aEvent.button != 2 && !aEvent.ctrlKey) return;

      const nsIBSS = Components.interfaces.nsIBrowserSearchService;
      const searchService =Components.classes["@mozilla.org/browser/search-service;1"].getService(nsIBSS);
      aEvent.preventDefault();
      aEvent.stopPropagation();
      var aMenu = aEvent.originalTarget;
      var selected = this._getselection();
      selected = selected.substring(0,1024);
      
      var pNode = aMenu.parentNode;
      while(pNode){
        if(pNode.id == 'searchbar'){
          selected = pNode.value;
          break;
        }
        pNode = pNode.parentNode;
      }
      //選択文字列の無いとき。 または,Shiftキ-押下時 はプロンプト表示
      if((selected==''
          && this.getPref("userChrome.conqueryModoki.promptNotSelected","bool",false))
        || aEvent.shiftKey){
        selected = window.prompt('搜索串: '+aMenu.getAttribute('label') ,selected);
        if(!selected) return;
      }

      var result = document.evaluate("*/*[@class = 'menuitem-iconic searchbar-engine-menuitem']",aMenu,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
      //alert(result.snapshotLength);
      var searchBar = this.searchBar();
      if (searchBar) 
        searchBar.value = selected;
      
      var anewTab = true;
      for(var i=0; i<result.snapshotLength; i++){
        try{
          setTimeout(function(self, aEngine,selected,anewTab,aEvent){
            self.loadSearch(aEngine, selected, anewTab, false, false)  //検索実行
          },100, this, result.snapshotItem(i).engine, selected, anewTab, aEvent);
        }catch(e){}
      }
      
      this.clearSearchBar();
    },

    _dispatchEvent: function(){
      var searchBar = this.searchBar();
      if (searchBar){ 
        var evt = document.createEvent("UIEvents");
        evt.initUIEvent("input", true, false, window, 0);
        searchBar.dispatchEvent(evt);
      }
    },

    activeBrowser: function() {
      return ('SplitBrowser' in window ? SplitBrowser.activeBrowser : null )
              ||  gBrowser;
    },

    _getFocusedWindow: function(){ //現在のウインドウを得る
        var focusedWindow = document.commandDispatcher.focusedWindow;
        if (!focusedWindow || focusedWindow == window)
            return window._content;
        else
            return focusedWindow;
    },

    _getselection: function() {  //選択されている文字列を得る
        var targetWindow = this._getFocusedWindow();
        var sel = Components.lookupMethod(targetWindow, 'getSelection').call(targetWindow);
        // for textfields
        if (sel && !sel.toString()) {
          var node = document.commandDispatcher.focusedElement;
          if (node &&
              (node.type == "text" || node.type == "textarea") &&
              'selectionStart' in node &&
              node.selectionStart != node.selectionEnd) {
              var offsetStart = Math.min(node.selectionStart, node.selectionEnd);
              var offsetEnd   = Math.max(node.selectionStart, node.selectionEnd);
              return node.value.substr(offsetStart, offsetEnd-offsetStart);
          }
        }
        return sel ? sel.toString().replace(/\s/g,' ').replace(/^[\ ]+|[\ ]+$/g,'').replace(/[\ ]+/g,' ') : "";
    },

    hideAllPopup: function(){
      var searchBar = this.searchBar();
      if (searchBar) 
        if (document.getAnonymousElementByAttribute(searchBar, "anonid", "searchbar-popup"))
          document.getAnonymousElementByAttribute(searchBar, "anonid", "searchbar-popup").hidePopup();
      if (document.getElementById("cq-hotmenu"))
        document.getElementById("cq-hotmenu").hidePopup();
      if (document.getElementById("contentAreaContextMenu"))
        document.getElementById("contentAreaContextMenu").hidePopup();
    },

    onpopupshowing: function(){
      var menu= document.getElementById('context-conqueryModoki');
      var selected = this._getselection();
      this.state = false;

      if( this.getPref("userChrome.conqueryModoki.showInContext","bool",false)){
        //コンテキストメニューに直接表示する
        menu.setAttribute('hidden',true);
        if (!!selected ||
            this.getPref("userChrome.conqueryModoki.promptNotSelected","bool",false) ){
          //コンテキストメニュー直下のを表示
          var child = menu.parentNode.childNodes;
          for (var i =0, len = child.length; i < len; i++){
            if (child[i].hasAttribute('Conquery')){
              child[i].removeAttribute('hidden');
            }
          }
        }
        return;
      }

      //コンテキストメニューにQuery to...を表示するかどうか
      if( !this.getPref("userChrome.conqueryModoki.showInContext","bool",false)){
        if(!!selected ||
            this.getPref("userChrome.conqueryModoki.promptNotSelected","bool",false) ){
          menu.setAttribute('hidden',false);
        }else{
          menu.setAttribute('hidden',true);
        }
      }
    },

    onpopuphidden: function(){
      var child = document.getElementById('context-conqueryModoki')
                  .parentNode.childNodes;
      for (var i = child.length - 1; i > -1; i--){
        if (child[i].hasAttribute('Conquery'))
          child[i].setAttribute('hidden',true);
      }
    },

    makePopupMenu: function(menupopup2){
      //ポップアップメニュー構築
      //既にあるなら消す
      while(menupopup2.firstChild){
        menupopup2.removeChild(menupopup2.firstChild);
      }
      //メニュー作成
      this.makemenu(menupopup2);

    },

    dupPopupMenu: function(){
      //コンテキストメニュー直下にメニュー構築
      //コンテキストメニュー用のを複製して利用する
      var menu= document.getElementById('context-conqueryModoki');
      //既にあるなら消す
      for(var i=menu.parentNode.childNodes.length - 1; i>-1; i--){
        if(menu.parentNode.childNodes[i].hasAttribute('Conquery'))
          menu.parentNode.removeChild(menu.parentNode.childNodes[i]);
      }
      //複製
      var menuepopup = document.getElementById("context-conqueryModoki-popup");
      var refItem = menu.nextSibling;
      for(var aMenu,i=0, len = menuepopup.childNodes.length; i<len; i++){
        aMenu = menu.parentNode.insertBefore(menuepopup.childNodes[i].cloneNode(true),refItem);
        aMenu.setAttribute('hidden',true);
        aMenu.setAttribute('Conquery',true);
      }

    },

    makemenu: function(popup,flg){
      // popupにメニューを構築, flg: trueの時検索バー用, falseの時, コンテキスト用
      const kXULNS =
           "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
      const nsIBSS = Components.interfaces.nsIBrowserSearchService;
      const searchService =Components.classes["@mozilla.org/browser/search-service;1"].getService(nsIBSS);

      var searchBar = conqueryModoki.searchBar();
      var engines = searchService.getVisibleEngines({ });
      var refNode=[];
      var nest = 0;
      refNode[nest] = popup
      for (var i =  0; i <engines.length; i++) {
        if(engines[i].name.match(/^{/) && !engines[i].name.match(/}/)){
          //エンジン名に "{"がある時 サブフォルダの開始
          var menu = document.createElementNS(kXULNS,"menu");
          menu.setAttribute("label", engines[i].name.replace(/^{/,''));
          menu.setAttribute("class", "menuitem-iconic");
          if (engines[i].iconURI)
            menu.setAttribute("src", engines[i].iconURI.spec);
          menu.setAttribute("hidden", false);
          menu.setAttribute("onclick", "event.stopPropagation();if(event.button == 2 || event.ctrlKey){ conqueryModoki.hideAllPopup();conqueryModoki.kusizasi(event);}");

          try{ //Fx3
            //var tooltip = document.getAnonymousElementByAttribute(searchBar,"anonid","searchbar-stringbundle").getFormattedString("searchtip", [engines[i].name]);
            var tooltip = "左键:子菜单, SHift+右键:提示框, Ctrl+左键 or 右键:关键字";
            menu.setAttribute("tooltiptext",tooltip);
          }catch(e){}
          var menupopup = document.createElementNS(kXULNS,"menupopup");
          menupopup.setAttribute("menugenerated","true");
          menupopup.setAttribute('onpopupshowing',"event.stopPropagation();"); //Fx3
          menu.appendChild(menupopup);
          refNode[nest].appendChild(menu);
          nest = nest+1;
          refNode[nest]=menupopup
        }else if(engines[i].name.match(/}/) && !engines[i].name.match(/{/)){
          //エンジン名に "}"がある時 サブフォルダの終了
          nest = nest-1;
          if(nest<0) nest = 0;
        }else if(engines[i].name.match(/^-{2,}|^―{2,}/)){
          //エンジン名に "――がある時 セパセータ
          var menusepa = document.createElementNS(kXULNS,"menuseparator");
          refNode[nest].appendChild(menusepa);
        }else{
          var menuitem = document.createElementNS(kXULNS,"menuitem");
          menuitem.setAttribute("label", engines[i].name);

          
          if(flg){
            try{ //Fx3
              //var tooltip = document.getAnonymousElementByAttribute(searchBar,"anonid","searchbar-stringbundle").getFormattedString("searchtip", [engines[i].name]);
              var tooltip = "左键点击:选择搜索引擎, 中键点击:搜索, 右键点击:打开搜索结果并返回默认搜索引擎";
              menuitem.setAttribute("tooltiptext",tooltip);
            }catch(e){}
            menuitem.setAttribute("id", engines[i].name);
            menuitem.setAttribute("onclick","if(event.button==1||event.button==2){event.stopPropagation();event.preventDefault();conqueryModoki.hideAllPopup();conqueryModoki.doSearch(event, "+i+");}");
          }else{
            try{ //Fx3
              var tooltip = "Shift+左键 or 中键:提示框, Ctrl+左键 or 右键:打开搜索结果并返回默认搜索引擎";
              menuitem.setAttribute("tooltiptext",tooltip);
            }catch(e){}
            menuitem.setAttribute('oncommand',"conqueryModoki.searchWord(event, "+i+");");
            menuitem.setAttribute('onclick','if(event.button==1||event.button==2){event.stopPropagation();event.preventDefault();conqueryModoki.hideAllPopup();var fn = new Function("event", this.getAttribute("oncommand"));fn.call(this, event);}');
          }

          menuitem.setAttribute("class", "menuitem-iconic searchbar-engine-menuitem");
          // Since this menu is rebuilt by the observer method whenever a new
          // engine is selected, the "selected" attribute does not need to be
          // explicitly cleared anywhere.
          if (engines[i] == searchService.currentEngine)
            menuitem.setAttribute("selected", "true");
          if (engines[i].iconURI)
            menuitem.setAttribute("src", engines[i].iconURI.spec);
          menuitem.engine = engines[i];
          refNode[nest].appendChild(menuitem);
        }
      }
      searchBar._needToBuildPopup = false;
    },

    getPref: function(aPrefString, aPrefType, aDefault){
      var xpPref = Components.classes["@mozilla.org/preferences-service;1"]
                    .getService(Components.interfaces.nsIPrefBranch2);
      try{
        switch (aPrefType){
          case "str":
            return xpPref.getCharPref(aPrefString).toString(); break;
          case "int":
            return xpPref.getIntPref(aPrefString); break;
          case "bool":
          default:
            return xpPref.getBoolPref(aPrefString); break;
        }
      }catch(e){
      }
      return aDefault;
    },

    getVer: function(){
      const Cc = Components.classes;
      const Ci = Components.interfaces;
      var info = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
      // このコードを実行しているアプリケーションの名前を取得する
      var ver = parseInt(info.version.substr(0,3) * 10,10) / 10;
      return ver;
    },

    debug: function(aMsg){
      const Cc = Components.classes;
      const Ci = Components.interfaces;
      Cc["@mozilla.org/consoleservice;1"]
        .getService(Ci.nsIConsoleService)
        .logStringMessage(aMsg);
    },

    handleEvent: function(aEvent){
      switch (aEvent.type){
        case "popupshowing":
          if (aEvent.originalTarget != document.getElementById('contentAreaContextMenu'))
            return;
          this.onpopupshowing();
          break;
        case "popuphidden":
          if (aEvent.originalTarget != document.getElementById('contentAreaContextMenu'))
            return;
          this.onpopuphidden();
          break;
      }
    }
  };

  //ConQueryのエミュレート for Drag de Go用
  function cqrPageSelection(){}
  function cqrShowHotmenu(aflg, x, y){
    const kXULNS =
           "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
    var hotmenu = document.getElementById("cq-hotmenu");
    if(!hotmenu){
      hotmenu = document.createElementNS(kXULNS,"menupopup");
      hotmenu.id = "cq-hotmenu";
      conqueryModoki.activeBrowser().appendChild(hotmenu);
    }
    //ポップアップメニュー構築
    conqueryModoki.makePopupMenu(hotmenu);
    document.popupNode = null; //THANKS http://www.xuldev.org/blog/?p=109
    //Drag de Goメニュー表示
    hotmenu.showPopup(conqueryModoki.activeBrowser(), x, y, "context","bottomleft","topleft");
  }

  (function(){
    var searchbar = document.getElementById('searchbar');
    if(!searchbar) return;
    var os =
         Components.classes["@mozilla.org/observer-service;1"]
                   .getService(Components.interfaces.nsIObserverService);
    //Observer登録解除
    os.removeObserver(searchbar, "browser-search-engine-modified", false);

    //search.xmlの置き換え
    var old_rebuildPopup = searchbar.rebuildPopup;
    searchbar.rebuildPopup=function(){
      var popup = searchbar._popup;

      // Clear the popup, down to the first separator
      var popup_org = []; //元のメニューをバックアップ
      var len= popup.childNodes.length;
      for(var i=len-1; i>-1;i--){
        if(popup.childNodes[i] && popup.childNodes[i].localName != "menuseparator") continue;
        var ref = i; // 検索バーの管理の直前のセパレータ位置
        for(var j=ref; j<len; j++){
          popup_org[j-ref] = popup.childNodes[j].cloneNode(true);
        }
        while(popup.firstChild){ //メニューを全部消す
          popup.removeChild(popup.firstChild);
        }
        break;
      }
      conqueryModoki.makemenu(popup,true); //検索バー用ポップアップメニュー作成
      //メニューに検索バーの管理と直前のセパレータを追加
      for(var i=0; i<popup_org.length; i++){
        popup.appendChild(popup_org[i]);
      }
      if(conqueryModoki.getVer()>2){ //trunc Gecko/2007070706 Minefield/3.0a7pre でこれがいる
        document.getAnonymousElementByAttribute(searchbar, "anonid", "open-engine-manager")
                .setAttribute("oncommand","document.getElementById('searchbar').openManager(event);");
      }

      var count = 0;
      var timer = setInterval(function(){
        if(++count > 100 || (function(){
          try{
            //コンテキストメニュー用ポップアップメニュー作成
            conqueryModoki.makePopupMenu(document.getElementById("context-conqueryModoki-popup"));
            conqueryModoki.dupPopupMenu();
            return true;
          }catch(e){
            return false
          }
        })())
          clearInterval(timer);
      },500);
      
      //second search -0.4.2008052301
      searchbar._engines = searchbar.searchService.getVisibleEngines({ });
    }


    //初回起動時, 検索バー用, コンテキストメニュー用ポップアップメニューを作成する
    try{ //search.xmlの書き替え
      var old_rebuildPopupDynamic = searchbar.rebuildPopupDynamic;
      var func = searchbar.rebuildPopupDynamic.toSource();
      func = func.replace(/popup\.firstChild/,'popup.lastChild');
      func = func.replace(/insertLocation\.nextSibling/g,'insertLocation.previousSibling');
      eval('searchbar.rebuildPopupDynamic = ' +  func);
    }catch(e){}
    
    //Observer登録し直し
    os.addObserver(searchbar, "browser-search-engine-modified", false);
    searchbar.rebuildPopup();
    
    //コンテキストメニューポップアップ時のイベント登録
    document.getElementById('contentAreaContextMenu').addEventListener('popupshowing', conqueryModoki, false);
    document.getElementById('contentAreaContextMenu').addEventListener('popuphidden', conqueryModoki, false);

  })();
  ]]></script>
  <popup id="contentAreaContextMenu">
    <menu id="context-conqueryModoki"
          label="搜索… "
          accesskey="q"
          insertbefore="context-searchselect"
          ondblclick="conqueryModoki.hideAllPopup();conqueryModoki.searchWord(event, null);"
          onclick="if(event.button == 2 || event.ctrlKey){ conqueryModoki.kusizasi(event);}"
     >
      <menupopup id="context-conqueryModoki-popup"
          onpopupshown="setTimeout(function(){conqueryModoki.state=true;},0);"
          />
    </menu>
  </popup>
</overlay>
