ブックマークをタグで絞りこんで開く

TagSifterは、高機能だけど結局絞り込みしか使ってないし、サイドバー開くことが面倒になってきた…orz
でも :bmarks が 使い辛かったので、作ってみた。

  • completion で タグとそのタグで含まれる ブックマークを表示される
    • /firefox/vimperator で firefox と vimperator のタグを持つブックマークの持つ 他のタグ一覧と ブックマーク一覧が出る
  • 絞り込んで結果に対して
    • 指定無しなら全て開く
    • 番号を記載でその番号のブックマークを開く
    • 複数ある場合は、コンマ区切り
    • 1,2,3,4と 1-4 は 等価
    • 0,1,2,3,4 と -4 は 等価
    • 4,5,6,7,... と 4- は 等価
    • 1,2,3,7 と 1-3,7 は 等価
  • 毎回全検やってるので、遅いorz

作ってて思ったけど、今は、オンラインブックマークのほうが多いのかなぁ

(function(){
  let mod=liberator.modules;
  let cmd=mod.commands;
  let n_style="display:inline-block;color:#20c;width:1em;text-align:right;font-weight:bold";
  let process=[
    function(item,text){ return <>
    <span style={n_style}>{item.item.index}</span>
    <span highlight="CompIcon">{item.icon ? <img src={item.icon}/> : <></>}</span>
    <span class="td-strut"/>{item.item.url ? item.item.desc : text}</>;
    },function(item,text){ return <>
      {item.item.url ? <a highlight="URL" href={item.item.url}>{item.item.url}</a> : <span>{text}</span>}
    </>;
    }
  ];
  let openURL = function(url){
    liberator.open(url, liberator.NEW_TAB);
  };
  let openURLs = ("TreeStyleTabService" in window) ?
    function(list){
      TreeStyleTabService.readyToOpenNewTabGroup(gBrowser);
      gBrowser.loadTabs(["about:blank"].concat(list));
    }:function(list){
      for(let url in mod.util.Array.itervalues(list))
        liberator.open(url, liberator.NEW_BACKGROUND_TAB);
  };
  var Check=function(){
    this.rule=[];
  }
  Check.prototype={
    addRule:function(msg){
      let m=msg.match(/^(\d+)?(-)?(\d+)?$/);
      if(m[1]){
        if(m[2]){
          if(m[3]) this.addMinMax(m[1],m[3]);
          else this.addMin(m[1]);
        }else this.addIndex(m[1]);
      }else if(m[3]) this.addMax(m[3]);
    }
    ,addIndex: function(v){this.rule.push(function(idx) idx==v);}
    ,addMin:function(v){this.rule.push(function(idx) idx>=v);}
    ,addMax:function(v){this.rule.push(function(idx) idx<=v);}
    ,addMinMax:function(v,w){this.rule.push(function(idx)(v<=idx)&&(idx<=w));}
    ,check:function(idx){
      for(var func in mod.util.Array.itervalues(this.rule)) 
        if(func(idx)) return true;
      return false;
    }
  };

	var map ={
		modes:[liberator.modules.modes.NORMAL]
		,keys:["<C-t>"]
		,desc:"open bookmark from tag"
		,action:function open_cmd(){
			commandline.open("",":tagopen /",liberator.modules.modes.EX);
		}
	};

	mappings.addUserMap(map.modes,map.keys, map.desc, map.action);
  cmd.addUserCommand(["tagopen"],"open bookmark from tag"
  ,function(args){
    let tags=args[0].match(/([^/]+)/g);
		if(!tags){
			liberator.echoerr("no tag list!");
			return;
		}
    let c = new Check();
    let all=true,m,cnt=0;
    let lastTag=tags[tags.length-1];
    if(/^[0-9-,]+$/.test(lastTag)){
      tags=tags.slice(0,-1);
      for(let r in mod.util.Array.itervalues(lastTag.split(","))){
        c.addRule(r);
      }
      all = false;
    }
    var list=[ u.url for(u in mod.util.Array.itervalues(bookmarks.get("",tags))) if(all || c.check(cnt++))];
    if(list.length === 0) liberator.echoerr("no match bookmark");
    else if(list.length === 1) openURL(list[0]);
    else openURLs(list);
  },{
    argCount: "1"
    ,completer:function(context,args){
      let ex;
      try{
        let q=context.filter.split("/");
        context.title = ["tag","description"];
        if(q.length <3){
          let p=[];

          let tags=PlacesUtils.tagging.allTags;

          for(let i=0,j=tags.length;i<j;++i){
            p.push(["/"+tags[i],i]);
          }
          context.completions=p;
        }else{
          q=q.slice(1,-1);
          let tags={};
          result=bookmarks.get("",q);
          let bm=[];
          let path="/"+q.join("/")+"/";
          let cnt=0;
          for(let r in mod.util.Array.itervalues(result)){
            let t=r.tags;
            for(let t1 in mod.util.Array.itervalues(t)){
              if(tags[t1]) ++tags[t1];
              else tags[t1] = 1;
            }
            bm.push({text:path+cnt,desc:r.title||"(Untitled)",ico:r.icon,index:cnt++,url:r.url});
          }
          for(let a in mod.util.Array.itervalues(q)) delete tags[a];

          context.process = process;
          context.keys={
            text:"text",description:"desc",icon:"ico"
          };
          context.completions=[{text:path,desc:"current path",index:""}]
            .concat([ {text:path+a,desc:b,index:""} for([a,b] in Iterator(tags))]
            ,bm);
        }
      }catch(ex){
        liberator.echoerr(ex);
      }
    }
  });
})();
</div>

さて次はどの アドオン を ダイエットしようかなっと

*1241001667*[javascript][vimperator]tree style tab を キーボード操作
概ねフィーリング通り動いたんで貼り

** 追記
いきなり 間違い発見して訂正orz
やっぱり、git とか hg で 管理しないと面倒かも…


<div class="code">
>|javascript|
(function(){
  if(!"TreeStyleTabService" in window) return;
  var mod=liberator.modules;
  var map=mod.mappings;
  var extra={flags:mod.Mappings.flags.COUNT};
	let caisui;

  var setup=function(k,t,f){
    map.remove(k,t);
    map.addUserMap([mod.modes.NORMAL],[k],t,function(){caisui.TreeStyleTab[f].apply(this,arguments)},extra);
  };

  setup("zn" , "tree style tab next parent tab" , "nextParentTab");
  setup("zN" , "tree style tab next root tab"   , "nextRootTab");
  setup("zj" , "tree style tab next tab"        , "nextTab");
  setup("zp" , "tree style tab prev parent tab" , "prevParentTab");
  setup("zP" , "tree style tab prev root tab"   , "prevRootTab");
  setup("zk" , "tree style tab prev tab"        , "prevTab");
  setup("zd" , "remove tree tab"                , "removeTabSubTree");
  setup("zD" , "remove tree tab from top tree"  , "removeTabTreeAll");
  setup("zh" , "tree style tab parent tab"      , "parentTab");
  setup("zH" , "tree style tab root tab"        , "rootTab");
  setup("zl" , "tree style tab first child tab" , "firstChildTab");
  setup("zu" , "move up"                        , "moveUp");
  setup("zU" , "move root"                      , "moveRoot");
  setup("d"  , "override remove tab"            , "removeTab");
  setup("zc" , "collapse subtree"               , "collapseSubtree");
  setup("zC" , "collapse all subtree"           , "collapseAllSubtree");
  setup("zo" , "expand subtree"                 , "expandSubtree");
  setup("z^" , "goto first tab"                 , "firstTab");
  setup("z$" , "goto last tab"                  , "lastTab");
	setup("z@" , "ready to open child tab once"   , "readyToOpenChildTabOnce");
	setup("z'" , "ready to open child tab once"   , "readyToOpenTabOnce");
	setup("z[" , "ready to open child tab"        , "readyToOpenChildTab");
	setup("z]" , "stop to open child tab"         , "stopToOpenChildTab");
  setup("zg" , "show tabbar"                    , "showTabbar");
  setup("zx" , "mark tab"                       , "setMarkTab");
  setup("zv" , "move child tab"                 , "moveChildTab");
  setup("zV" , "show next tab"                  , "moveNextTab");


	if(!liberator.plugins.caisui) liberator.plugins.caisui={};
	liberator.plugins.caisui.TreeStyleTab={
    getNextTab:function(aTab, aCount){
      let t1;

      if(aCount < 1) aCount=1;
      for(t1=aTab,i=0; (i<aCount);++i){
        let t2=TreeStyleTabService.getNextSiblingTab(t1);
        if(t2) t1=t2;
        else{
        t1 = (t2=TreeStyleTabService.getParentTab(t1))
          ? TreeStyleTabService.getFirstChildTab(t2)
          : TreeStyleTabService.getFirstTab(gBrowser);
        }
      }
			return t1;
    },nextTab:function(aCount){
			gBrowser.selectedTab = caisui.TreeStyleTab.getNextTab(gBrowser.selectedTab,aCount);
    },nextParentTab:function(aCount){
      var t=gBrowser.selectedTab;
			gBrowser.selectedTab = caisui.TreeStyleTab.getNextTab(
        TreeStyleTabService.getParentTab(t) || TreeStyleTabService.getRootTab(t)
        ,aCount);
    },nextRootTab:function(aCount){
			gBrowser.selectedTab = caisui.TreeStyleTab.getNextTab(TreeStyleTabService.getRootTab(gBrowser.selectedTab),aCount);
		},getPrevTab:function(aTab,aCount){
      let t1;

      if(aCount < 1) aCount=1;
      for(t1=aTab,i=0; (i<aCount);++i){
        let t2=TreeStyleTabService.getPreviousSiblingTab(t1);
        if(t2) t1=t2;
        else{
        t1 = (t2=TreeStyleTabService.getParentTab(t1))
          ? TreeStyleTabService.getLastChildTab(t2)
          : TreeStyleTabService.getRootTab(TreeStyleTabService.getLastTab(gBrowser));
        }
      }
			return t1;
		},prevTab:function(aCount){
			gBrowser.selectedTab = caisui.TreeStyleTab.getPrevTab(gBrowser.selectedTab,aCount);
		},prevParentTab:function(aCount){
      var t=gBrowser.selectedTab;
			gBrowser.selectedTab = caisui.TreeStyleTab.getPrevTab(
        TreeStyleTabService.getParentTab(t)||TreeStyleTabService.getRootTab(t)
        ,aCount);
		},prevRootTab:function(aCount){
			gBrowser.selectedTab = caisui.TreeStyleTab.getPrevTab(TreeStyleTabService.getRootTab(gBrowser.selectedTab),aCount);
    },removeTab:function(){
      var t=gBrowser.selectedTab;
      if(!TreeStyleTabService.getNextSiblingTab(t) && !TreeStyleTabService.hasChildTabs(t)){
				p = TreeStyleTabService.getPreviousSiblingTab(t)||TreeStyleTabService.getParentTab(t);
				if(p) gBrowser.selectedTab = p;
      }
      gBrowser.removeTab(t);
    },parentTab:function(){
      var t = TreeStyleTabService.getParentTab(gBrowser.selectedTab);
      if(t) gBrowser.selectedTab=t;
    },rootTab:function(){
			gBrowser.selectedTab = TreeStyleTabService.getRootTab(gBrowser.selectedTab);
    },firstChildTab:function(){
      var t = TreeStyleTabService.getFirstChildTab(gBrowser.selectedTab);
      if(t) gBrowser.selectedTab=t;
    },removeTabSubTree:function(){
      caisui.TreeStyleTab.collapseSubtree.apply(this,arguments);
			gBrowser.removeTab(gBrowser.selectedTab);
    },removeTabTreeAll:function(){
			gBrowser.selectedTab = TreeStyleTabService.getRootTab(gBrowser.selectedTab);
      caisui.TreeStyleTab.removeTabSubTree.apply(this,arguments);
    },collapseSubtree:function(){
      gBrowser.treeStyleTab.collapseExpandSubtree(gBrowser.selectedTab,true);
    },expandSubtree:function(){
      gBrowser.treeStyleTab.collapseExpandSubtree(gBrowser.selectedTab,false);
    },collapseAllSubtree:function(){
      TreeStyleTabService.collapseExpandAllSubtree(true);
    },firstTab:function(){
      let t1,t2;
      t1=gBrowser.selectedTab;
      gBrowser.selectedTab = (t2=TreeStyleTabService.getParentTab(t1))
        ? TreeStyleTabService.getFirstChildTab(t2)
        : TreeStyleTabService.getFirstTab(gBrowser);
    },lastTab:function(){
      let t1,t2;
      t1=gBrowser.selectedTab;
      gBrowser.selectedTab = (t2=TreeStyleTabService.getParentTab(t1))
        ? TreeStyleTabService.getLastChildTab(t2)
        : TreeStyleTabService.getRootTab(TreeStyleTabService.getLastTab(gBrowser));
    },readyToOpenChildTabCore:function(aTab,aFlag){
			TreeStyleTabService.readyToOpenChildTab(aTab,aFlag);
    },readyToOpenChildTabOnce:function(){
			caisui.TreeStyleTab.readyToOpenChildTabCore(gBrowser.selectedTab);
    },readyToOpenTabOnce:function(){
			caisui.TreeStyleTab.readyToOpenChildTabCore(TreeStyleTab.getParentTab(gBrowser.selectedTab)||gBrowser.selectedTab);
    },readyToOpenChildTab:function(){
			caisui.TreeStyleTab.readyToOpenChildTabCore(gBrowser.selectedTab,true);
		},stopToOpenChildTab:function(){
			TreeStyleTabService.stopToOpenChildTab(gBrowser.selectedTab);
		},showTabbar:function(){
      gBrowser.treeStyleTab.showTabbar();
    },setMarkTab:function(){
			markTab = gBrowser.selectedTab;
		},moveChildTab:function(){
			var t=gBrowser.selectedTab;
			caisui.TreeStyleTab.performDrop(markTab,t
			, caisui.TreeStyleTab.getInsertBeforeTab(t));
		},moveNextTab:function(){
			var t=gBrowser.selectedTab;
			caisui.TreeStyleTab.performDrop(markTab,TreeStyleTabService.getParentTab(t)
			, caisui.TreeStyleTab.getInsertBeforeTab(t));
		},performDrop:function(aTab,pTab,iTab){
      if(!aTab) return;
      else if(pTab && TreeStyleTabService.getChildTabs(aTab,[]).indexOf(pTab)>=0){
        liberator.echoerr("error:loop");
        return;
      }
			gBrowser.treeStyleTab.performDrop({
				action: (pTab ? TreeStyleTabService.kACTION_ATTACH:TreeStyleTabService.kACTION_PART)|TreeStyleTabService.kACTION_MOVE
				,parent: pTab
				,source: aTab
				,insertBefore: iTab
				,canDrop:true
				//,position: 0 //?
				//,target: null//?
			},aTab);
		},getInsertBeforeTab:function(aTab){
			var ret=null;
			for(let t = aTab;t;t=TreeStyleTabService.getParentTab(t)){
				if(ret=TreeStyleTabService.getNextSiblingTab(t)) break;
			}
			return ret;
		},moveUp:function(){
      var t=gBrowser.selectedTab,p=TreeStyleTabService.getParentTab(t);
      if(!p) return;
      caisui.TreeStyleTab.performDrop(t,TreeStyleTabService.getParentTab(p),caisui.TreeStyleTab.getInsertBeforeTab(p));
    },moveRoot:function(){
      var t=gBrowser.selectedTab,r=TreeStyleTabService.getRootTab(t);
      caisui.TreeStyleTab.performDrop(t,null,caisui.TreeStyleTab.getInsertBeforeTab(r));
    }
  };
	let markTab=null;
	caisui = liberator.plugins.caisui;
})();