Tree Style Tab の 一部を storage に 格納

// vim:fdm=marker
(function(){
  var useTreeStyleTab = "TreeStyleTabService" in window;
  if(!useTreeStyleTab) return;

  const key = "treeStyleTab_storage";
  const map = storage.newMap(key, true);
  const ttsCount = liberator.globalVariables["ttscount"] || 3;

  var T=//{{{
  {
    names:["tts"],
    desc: "tree tab stock manager",
    action:function(args){
      var cmd = T.commands[args[0]];
      if(cmd) cmd.action.apply(this,arguments);
    },
    commands://{{{
    {
      open://{{{
      {
        desc:"open session tree",
        action:function(args){
          if(args.length == 2){
            var info = map.get(args[1]);
            if(info){
              U.syncOpen(info, ttsCount);
            }
          }
        },
        completer:function() T.keyCompletr.apply(this,arguments),
      },//}}}
      once://{{{
      {
        desc: "open and remove storage",
        action:function(args){
          if(args.length == 2){
            var info = map.get(args[1]);
            if(info){
              U.syncOpen(info, ttsCount);
            }
            map.remove(args[1]);
          }
        },
        completer:function() T.keyCompletr.apply(this,arguments),
      },//}}}
      save://{{{
      {
        desc: "save and close",
        action:function(args){
          if(args.length == 2){
            map.set(args[1], U.getTreeTabJson(g.selectedTab));
            //storage.save(map.name);
          }
        },
        completer:function() T.keyCompletr.apply(this,arguments),
      },//}}}
      stock://{{{
      {
        desc: "save and close",
        action:function(args){
          if(args.length == 2){
            var t = g.selectedTab;
            map.set(args[1], U.getTreeTabJson(t));
            storage.save(map.name);
            TST.collapseExpandSubtree(t,true);
            g.selectedTab = TST.getPreviousSiblingTab(t) || TST.getNextSiblingTab(t) || TST.getParentTab(t);
            g.removeTab(t);
          }
        },
        completer:function() T.keyCompletr.apply(this,arguments),
      },//}}}
      remove://{{{
      {
        desc: "remove key from storage",
        action:function(args){
          if(args.length == 2){
            map.remove(args[1]);
          }
        },
        completer:function() T.keyCompletr.apply(this,arguments),
      },//}}}
    },//}}}
    keyCompletr:function(context, args){
      context.completions = [ [a,""] for([a] in map)];
    },
    options:{
			completer:function(context,args){
				if(args.length <= 1){
					context.completions = [ [a, b.desc] for([a,b] in Iterator(T.commands))];
				}else{
          let cmd = T.commands[args[0]];
          if(cmd && cmd.completer){
            cmd.completer.apply(this,arguments);
          }
        }
			}
    },
  },//}}}
  U=//{{{
  {
    getTreeTabJson:function(t){
      return {
        href: t.linkedBrowser.contentWindow.location.href,
        child: [U.getTreeTabJson(c) for(c in uai(TST.getChildTabs(t)))]
      }
    },
    syncOpen:function(aInfo, aCount){
      function openURI(aURI){
        var t,b=g.getBrowserForTab(t=g.addTab(aURI));
        b.addEventListener("load",function(evt){
          if(evt.originalTarget.defaultView.frameElement) return;
          b.removeEventListener("load", arguments.callee, true);

          try{
            syncIt.next();
          }
          catch(err if err instanceof StopIteration){ }
        }, true);
        return t;
      }

      function syncOpenIterator(aInfo){
        var stack=[],info;
        var itFunc=function(obj){
          for(let i=0,j=obj.length;i<j;++i) yield obj[i];
          yield null;
        };
        var stackClass=function(aInfo){
          this.tab = openURI(aInfo.href);
          this.it  = itFunc(aInfo.child);
        };
        var ex;

        try{
        stack.push(new stackClass(aInfo));
        yield;
        while(stack.length>0){
          cur = stack[stack.length-1];
          info = cur.it.next();
          if(info){
            TST.readyToOpenChildTab(cur.tab);
            if(info.child.length == 0){
              openURI(info.href);
            }
            else{
              stack.push(new stackClass(info));
            }
            yield;
          }
          else{
            cur.it.close();
            stack.pop();
          }
        }
        }catch(ex){
          liberator.echo(ex);
        }
      }
      let syncIt = syncOpenIterator(aInfo);

      for(let i=0,j=aCount||1;i<j;++i){
        try{
          syncIt.next();
        }
        catch(err if err instanceof StopIteration){ }
      }
    },
    log:function(){
      Firebug.Console.log.apply(Firebug.Console,arguments);
    },
  },//}}}
  g=gBrowser,
  TST=g.treeStyleTab,
  uai=util.Array.itervalues;
  plugins.syncOpen = U.syncOpen;

  commands.addUserCommand(T.names, T.desc, T.action,T.options, true);
})();