/**
 * SL
 */
liberator.plugins.SL = (function(){
function xmlToDom(xml, xmlns){
  if (!xmlns) xmlns = xulNS;
  XML.ignoreWhitespace = true;
  XML.prettyPrinting = false;
  var doc = (new DOMParser).parseFromString('' + xml.toXMLString() + "", "application/xml")
  var imported = document.importNode(doc.documentElement, true);
  var range = document.createRange();
  range.selectNodeContents(imported);
  var fragment = range.extractContents();
  range.detach();
  return fragment.childNodes.length > 1 ? fragment : fragment.firstChild;
}
function getFullScreenAttr(){
  let s = window.screen;
  return {
    top:    s.top,
    left:   s.left,
    width:  s.availWidth,
    height: s.availHeight
  };
}
function SL(){ this.init.apply(this, arguments); }
SL.prototype = { // {{{
  init: function(elm, width, height, fontSize, frameRate, speed){
    this.canvas = elm;
    this.width = width + 10;
    this.height = height;
    this.fontSize = fontSize || 20;
    this.frameRate = frameRate || 60;
    this.speed = speed || 20;
    this.interval = null;
    this.slWidth = null;
    this.slPositionY = 0;
    this.count = 0;
    this.rotateRadian = 0;
    this.ctx = this.canvas.getContext("2d");
    this.ctx.clearRect(0,0, this.width, this.height);
    elm.setAttribute("width", this.width);
    elm.setAttribute("height", this.height);
  },
  start: function(isLogo, isAccident, isFly, isLuckyStar){
    this.isFly = isFly || false;
    this.ctx.clearRect(0,0, this.width, this.height);
    this.ctx.font = this.fontSize + 'px monospace';
    if (isFly){
      let len = Math.sqrt(Math.pow(this.width, 2) + Math.pow(this.height, 2));
      this.rotateRadian = Math.acos(this.width / len);
      this.width = len;
    }
    if (isLuckyStar){
      luckyStar.init(this.ctx, this.width, this.height);
    }
    //this.slWidth = isLogo ? this.ctx.mozMeasureText(this.slData.logo[0] + this.slData.lcoal[0] + this.slData.lcar[0]) :
    //                        this.ctx.mozMeasureText(this.slData.body[0] + this.slData.coal[0]);
    this.slWidth = isLogo ? this.ctx.measureText(this.slData.logo[0] + this.slData.lcoal[0] + this.slData.lcar[0]).width :
                            this.ctx.measureText(this.slData.body[0] + this.slData.coal[0]).width;
    this.slPositionY = this.getSLPositionY();
    let sl = this.slGenerator(isLogo);
    let self = this;
    this.interval = window.setInterval(function(){
      self.draw(sl, isLuckyStar);
    }, this.frameRate);
  },
  getSLPositionY: function(){
    return (this.height - (this.slData.steam[0].length + this.slData.body.length + this.slData.wheel[0].length) * this.fontSize) / 2;
  },
  draw: function(gene, isLuckyStar){
    this.count++;
    this.ctx.clearRect(0,0, this.width, this.height);
    this.ctx.save();
    this.ctx.fillStyle = "rgba(0,0,0,0.8)";
    this.ctx.fillRect(0, 0, this.width, this.height);
    if (this.isFly){
      this.ctx.rotate(this.rotateRadian);
      this.ctx.translate(0, - this.height/2);
    }
    this.ctx.fillStyle = "rgb(255,255,255)";
    let data = gene.next();
    let x = this.width - this.count * this.speed,
        fontSize = this.fontSize,
        baseHeight = this.slPositionY;
    data.forEach(function(str, i){
      this.ctx.fillText(str, x, baseHeight + (i+1)*fontSize);
    }, this);
    this.ctx.restore();
    if (isLuckyStar){
      luckyStar.draw();
    }
    if (this.count * this.speed > this.width + this.slWidth){
      window.clearInterval(this.interval);
      plugins.SL.close();
    }
  },
  merge: function(){
    let data = [];
    Array.slice(arguments).forEach(function($_){
      $_.forEach(function(str, i){
        if (!data[i]) data[i] = [];
        data[i].push(str);
      });
    });
    return data.map(function($_) $_.join(""));
  },
  slGenerator: function(isLogo){
    let steam = this.createGenerator(this.slData.steam);
    if (isLogo){
      let sl = this.slData.logo;
      let wheel = this.createGenerator(this.slData.logoWheel);
      let coal = this.slData.lcoal;
      let car = this.slData.lcar;
      while(true){
        yield [].concat(steam.next(), this.merge(sl.concat(wheel.next()), coal, car, car));
      }
    } else {
      let sl = this.slData.body;
      let wheel = this.createGenerator(this.slData.wheel);
      let coal = this.slData.coal;
      while(true){
        yield [].concat(steam.next(), this.merge(sl.concat(wheel.next()), coal));
      }
    }
  },
  createGenerator: function(array){
    var i = 0, len = array.length;
    while(true){
      yield array[i];
      yield array[i];
      i++;
      if (i == len){
        i = 0;
      }
    }
  },
  slData: { /// {{{
    steam: [
      [
        "                      (@@) (  ) (@)  ( )  @@    ()    @     O     @     O      @",
        "                 (   )",
        "             (@@@@)",
        "          (    )",
        "",
        "        (@@@)",
      ],[
        "                      (  ) (@@) ( )  (@)  ()    @@    O     @     O     @      O",
        "                 (@@@)",
        "             (    )",
        "          (@@@@)",
        "",
        "        (   )"
      ]
    ],
    body: [
      "      ====        ________                ___________ ",
      "  _D _|  |_______/        \\__I_I_____===__|_________| ",
      "   |(_)---  |   H\\________/ |   |        =|___ ___|   ",
      "   /     |  |   H  |  |     |   |         ||_| |_||   ",
      "  |      |  |   H  |__--------------------| [___] |   ",
      "  | ________|___H__/__|_____/[][]~\\_______|       |   ",
      "  |/ |   |-----------I_____I [][] []  D   |=======|__ "
    ],
    wheel: [
      [
        "__/ =| o |=-~~\\  /~~\\  /~~\\  /~~\\ ____Y___________|__ ",
        " |/-=|___|=    ||    ||    ||    |_____/~\\___/        ",
        "  \\_/      \\O=====O=====O=====O_/      \\_/            "
      ],[
        "__/ =| o |=-~~\\  /~~\\  /~~\\  /~~\\ ____Y___________|__ ",
        " |/-=|___|=    ||    ||    ||    |_____/~\\___/        ",
        "  \\_/      \\_O=====O=====O=====O/      \\_/            "
      ],[
        "__/ =| o |=-~~\\  /~~\\  /~~\\  /~~\\ ____Y___________|__ ",
        " |/-=|___|=   O=====O=====O=====O|_____/~\\___/        ",
        "  \\_/      \\__/  \\__/  \\__/  \\__/      \\_/            "
      ],[
        "__/ =| o |=-~O=====O=====O=====O\\ ____Y___________|__ ",
        " |/-=|___|=    ||    ||    ||    |_____/~\\___/        ",
        "  \\_/      \\__/  \\__/  \\__/  \\__/      \\_/            "
      ],[
        "__/ =| o |=-O=====O=====O=====O \\ ____Y___________|__ ",
        " |/-=|___|=    ||    ||    ||    |_____/~\\___/        ",
        "  \\_/      \\__/  \\__/  \\__/  \\__/      \\_/            "
      ],[
        "__/ =| o |=-~~\\  /~~\\  /~~\\  /~~\\ ____Y___________|__ ",
        " |/-=|___|=O=====O=====O=====O   |_____/~\\___/        ",
        "  \\_/      \\__/  \\__/  \\__/  \\__/      \\_/            "
      ]
    ],
    coal: [
      "                              ",
      "                              ",
      "    _________________         ",
      "   _|                \\_____A  ",
      " =|                        |  ",
      " -|                        |  ",
      "__|________________________|_ ",
      "|__________________________|_ ",
      "   |_D__D__D_|  |_D__D__D_|   ",
      "    \\_/   \\_/    \\_/   \\_/    "
    ],
    logo: [
      "     ++      +------ ",
      "     ||      |+-+ |  ",
      "   /---------|| | |  ",
      "  + ========  +-+ |  "
    ],
    logoWheel: [
      [
        " _|--/~O========O-+  ",
        "//// \\_/      \\_/    "
      ],[
        " _|--/O========O\\-+  ",
        "//// \\_/      \\_/   "
      ],[
        " _|--O========O~\\-+  ",
        "//// \\_/      \\_/    "
      ],[
        " _|--/~\\------/~\\-+  ",
        "//// O========O_/    "
      ],[
        " _|--/~\\------/~\\-+  ",
        "//// \\O========O/    "
      ],[
        " _|--/~\\------/~\\-+  ",
        "//// \\_O========O    "
      ]
    ],
    lcoal: [
      "____                 ",
      "|   \\@@@@@@@@@@@     ",
      "|    \\@@@@@@@@@@@@@_ ",
      "|                  | ",
      "|__________________| ",
      "   (O)       (O)     "
    ],
    lcar: [
      "____________________",
      "|  ___ ___ ___ ___ | ",
      "|  |_| |_| |_| |_| | ",
      "|__________________| ",
      "|__________________| ",
      "   (O)        (O)    "
    ]
  }, /// }}}
};
// }}}
let luckyStar = (function(){
function extend(class, obj){
  var flag;
  for (let i in obj){
    flag = false;
    if (obj.__lookupGetter__(i)){
      class.prototype.__defineGetter__(i, obj.__lookupGetter__(i));
      flag = true;
    }
    if (obj.__lookupSetter__(i)){
      class..prototype.__defineSetter__(i, obj.__lookupSetter__(i));
      flag = true;
    }
    if (!flag) class.prototype[i] = obj[i];
  }
}
let colors = [
  ["rgba(255,215,0,alpha)", "rgba(255,255,0,alpha)"], //gold, yellow
  ["rgba(255,20,147,alpha)","rgba(255,0,255, alpha)"], // deeppink, magenta
  ["rgba(34,139,34,alpha)", "rgba(0,128,0,alpha)"], // forestgreen,green
  ["rgba(0,255,255,alpha)", "rgba(0,191,255,alpha)"] // cyan, deepskyblue
];
function getColor(){
  let i = Math.round(Math.random()*10) % colors.length;
  return colors[i];
}
function Star(){ this.init.apply(this, arguments); }
Star.prototype = { // {{{
  init: function(x, y, size, ctx, color){
    this.x = x;
    this.y = y;
    this.r = size * 10;
    this.ctx = ctx;
    this.points = [];
    for (var i=0; i < 5; i++){
      var rad = 2 * i * Math.PI / 5 - Math.PI / 2;
      this.points.push([
        this.r * Math.cos(rad),
        this.r * Math.sin(rad)
      ]);
      this.points.push([
        this.r / 2 * Math.cos(rad + Math.PI / 5),
        this.r / 2 * Math.sin(rad + Math.PI / 5)
      ]);
    }
    if (!color) color = getColor();
    this.styles = {
      fillStyle: color[0],
      strokeStyle: color[1],
    };
    this.rotateSpeed = 2 * Math.PI / 10 * Math.random();
  },
  draw: function(x, y, alpha, doRotate){
    if (typeof alpha == "undefined") alpha = 1;
    if (alpha < 0.1) alpha = 0;
    if (this.x > width + this.r) this.x = -Math.random() * width;
    if (this.y + this.r < 0) this.y = height + Math.random() * height;
    this.x += x;
    this.y += y;
    ctx.save();
    ctx.fillStyle = this.styles.fillStyle.replace(/alpha/, alpha);
    ctx.beginPath();
    ctx.translate(this.x, this.y);
    if (doRotate){
      ctx.rotate(this.rotateSpeed*time);
    }
    ctx.moveTo(this.points[9][0], this.points[9][1]);
    this.points.forEach(function(p){
      ctx.lineTo(p[0],p[1])
    })
    ctx.fill();
    ctx.restore();
  }
}; // }}}
function BigStar(){ this.init.apply(this, arguments); }
BigStar.prototype = { // {{{
  init: function(x, y, size, ctx){
    this.x = x;
    this.y = y;
    this.r = size * 10;
    this.scale = 1;
    this.ctx = ctx;
    this.setPoints(0);
    this.styles = {
      fillStyle: "magenta",
      strokeStyle: "black",
      lineWidth: 100,
      lineCap: "round"
    };
  },
  setPoints: function(r){
    this.r += r;
    this.points = [];
    for (var i=0; i < 5; i++){
      var rad = 2 * i * Math.PI / 5 - Math.PI / 2;
      this.points.push([
        this.r * Math.cos(rad),
        this.r * Math.sin(rad)
      ]);
      this.points.push([
        this.r / 2 * Math.cos(rad + Math.PI / 5),
        this.r / 2 * Math.sin(rad + Math.PI / 5)
      ]);
    }
  },
  draw: function(x, y, scale, doStroke, doFill){
    this.x += x;
    this.y += y;
    this.scale += scale;
    ctx.save();
    ctx.fillStyle = this.styles.fillStyle;
    ctx.strokeStyle = this.styles.strokeStyle;
    ctx.lineWidth = this.styles.lineWidth;
    ctx.lineCap = this.styles.lineCap;
    ctx.beginPath();
    ctx.translate(this.x, this.y);
    ctx.scale(this.scale, this.scale);
    ctx.moveTo(this.points[9][0], this.points[9][1]);
    this.points.forEach(function(p){
      ctx.lineTo(p[0],p[1])
    })
    if (doStroke) ctx.stroke();
    if (doFill) ctx.fill();
    ctx.restore();
  },
  draw2: function(scale){
    var nowScale = this.scale + scale;
    this.draw(0, 0, scale, true, false);
    this.draw(0, 0, - 3 * this.scale / 5, false, true);
    this.scale = nowScale;
  },
}; // }}}
function Logo() { this.init.apply(this, arguments); }
Logo.prototype = { // {{{
  init: function(str, x, y){
    this.str = decodeURIComponent(escape(str));
    this.x = x;
    this.y = y;
    this.fontSize = 150;
    this.styles = {
      mozTextStyle: this.fontSize + "px Monospace",
      lineWidth: 50,
      strokeStyle: "black",
      fillStyle: "magenta",
      lineJoin: "round"
    };
    this.radian = 0;
  },
  draw: function(x, y, doRotate){
    this.x += x;
    this.y += y;
    ctx.save();
    ctx.fillStyle = this.styles.fillStyle;
    ctx.strokeStyle = this.styles.strokeStyle;
    ctx.lineWidth = this.styles.lineWidth;
    ctx.mozTextStyle = this.styles.mozTextStyle;
    ctx.lineJoin = this.styles.lineJoin;
    for (var i=0; i),
  open: function(attr){
    if (!attr) attr = {};
    let defAttr = getFullScreenAttr();
    let canvas = this.panel.getElementsByTagName("canvas")[0];
    let sl = new SL(canvas,
      attr.width || defAttr.width,
      attr.height || defAttr.height,
      attr.fontSize,
      attr.frameRate,
      attr.speed
    );
    sl.start(attr.logo, attr.accident, attr.fly, attr.luckystar);
    this.panel.openPopupAtScreen(attr.left || defAttr.left, attr.top || defAttr.top, false);
  },
  init: function(){
    let panel = document.getElementById("vimp-sl");
    if (panel){
      this.panel = panel;
    } else {
      document.documentElement.appendChild(this.panel);
      let canvas = document.createElementNS(xhtmlNS, "canvas");
      canvas.setAttribute("id", "vimp-sl-canvas");
      this.panel.appendChild(canvas);
    }
  },
  close: function(){
    this.panel.hidePopup();
  }
};
self.init();
// -----------------------------------------------------
// Commmand
// -----------------------------------------------------
commands.addUserCommand(['sl'],decodeURIComponent(escape('キータイプを矯正します。')),
  function(args){
    let opt = {};
    args.string.split(/\s+/).forEach(function(arg){
      if (arg && arg.charAt(0) == "-"){
        for (let i=1, len=arg.length; i