var PLUGIN_INFO =
  Mouse Gestures
  マウスジェスチャー
  mouse gestures
  マウスジェスチャー
  0.10
  pekepeke
  2.0pre
  2.0pre
  http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk/mouse_gestures.js
  R', 'Forward', '#Browser:Forward'],
    ['L, , ,  ]
    -- UDLR
      ジェスチャーを指定します。
      UDLRの文字列を指定してください。
      それぞれ、マウスジェスチャーの↑、↓、←, →に対応しています。
      一応、ロッカージェスチャー・ホイールジェスチャー等にも暫定で対応しています(別途、オプションを有効にする必要がある)。
      ロッカージェスチャはL>R(左→右クリック), L
liberator.plugins.MouseGestures = (function() {
  const Ci = Components.interfaces;
  var global = liberator.globalVariables;
  if (typeof global.mousegesture_list == 'undefined') return;
  if (liberator.plugins.MouseGestures) liberator.plugins.MouseGestures.registerEvents('remove');
  var Class = function() function() {this.initialize.apply(this, arguments);};
  var MouseGestures = new Class();
  var doCommandByID = function(id) {
    if (document.getElementById(id))
      document.getElementById(id).doCommand();
  };
  MouseGestures.prototype = {
    initialize: function() {
      this.parseSetting();
      var self = this;
      this.registerEvents('add');
      window.addEventListener('unload', function() { self.registerEvents('remove'); }, false);
    },
    parseSetting: function() {
      var gestures = {};
      var self = this;
      this._showstatus = global.mousegesture_showmsg || true;
      this._enableRocker = global.mousegesture_rocker || false;
      this._enableWheel = global.mousegesture_wheel || false;
      if (this._enableRocker) this.captureEvents.push('draggesture');
      if (this._enableWheel) this.captureEvents.push('DOMMouseScroll');
      global.mousegesture_list.forEach(function( [gesture, desc, action, noremap] ) {
        action = action || desc;
        noremap = noremap || false;
        if (typeof action == 'string') {
          let str = action;
          if (str.charAt(0) == ':') action = function() liberator.execute(str.substr(1));
          else if (str.charAt(0) == '#') action = function() doCommandByID(str.substr(1));
          else action = function() modules.events.feedkeys(str, noremap);
        }
        gestures[gesture] = [desc, action];
      });
      this.GESTURES = gestures;
    },
    captureEvents : ['mousedown', 'mousemove', 'mouseup', 'contextmenu'],
    registerEvents: function(action) {
      var self = this;
      this.captureEvents.forEach(
        function(type) { getBrowser().mPanelContainer[action + 'EventListener'](type, self, type == 'contextmenu' || type == 'draggesture');
      });
    },
    set status(msg) {
      if (this._showstatus) commandline.echo(msg, null, commandline.FORCE_SINGLELINE);
    },
    handleEvent: function(event) {
      switch(event.type) {
      case 'mousedown':
        if (event.button == 2) {
          this._isMouseDownR = true;
          this._suppressContext = false;
          this.startGesture(event);
          if (this._enableRocker && this._isMouseDownL) {
            this._isMouseDownR = false;
            this._suppressContext = true;
            this._gesture = 'L>R';
            this.stopGesture(event);
          }
        } else if (this._enableRocker && event.button == 0) {
          this._isMouseDownL = true;
          if (this._isMouseDownR) {
            this._isMouseDownL = false;
            this._suppressContext = true;
            this._gesture = 'L 0 ? '+' : '-');
          this.stopGesture( event, false );
        }
        break;
      case 'draggesture':
        this._isMouseDownL = false;
        break;
      }
    },
    startGesture: function(event) {
      this._gesture = '';
      this._x = event.screenX;
      this._y = event.screenY;
      this._origDoc = event.originalTarget.ownerDocument;
      this._links = [];
    },
    progressGesture: function(event) {
      if (!this._origDoc) return;
      for (let node = event.originalTarget; node; node = node.parentNode) {
        if (node instanceof Ci.nsIDOMHTMLAnchorElement) {
          if (this._links.indexOf(node.href) == -1) this._links.push(node.href);
          break;
        }
      }
      this.timerGesture();
      var x = event.screenX, y = event.screenY;
      var distX = Math.abs(x-this._x), distY = Math.abs(y-this._y);
      var threshold = 15/ (gBrowser.selectedBrowser.markupDocumentViewer.fullZoom || 1.0);
      if (distX < threshold && distY < threshold) return;
      var dir = distX > distY ? (x