IEでしか見られないサイトをモダンブラウザで見られるようにする

# 化石を発見
どことは言わないが，とある有名企業で残骸を発見した．
ご丁寧に「Internet Explorerでしか開けません」と書いてある．
MacやLinuxでは見られないというのはなんとも悲しい．

中身を見たらVBScriptで書いてあるではないか！
残念，2年前（2017/07）にInternet Explorer 11からデフォで外されると発表されたので，もはやIEですらエミュレーションモードでしか開けないページとなっているのである．
良いサービスではあるので，なんとか自分用に使えるようにしてみた．
利用規約に引っかかるとは思いませんが，なにかあると嫌なので実際にどのWebサイトかへの言及は避けます．

# HTML5/JavaScriptで書き直す
といってもサーバに手を加えることはできない．
ローカルでhtml/jsのそれぞれを用意して，GETしてきてはbodyだけをすげかえて描画する，ということをしてなんとか表示できるようにした．


## （準備）CORSをすり抜ける
ローカルにあるhtmlファイルのoriginはnullなので，XMLHttpRequestのような関数で該当のWebページを取ってこようとするとCORSに引っかかる．

```
/path/to/chrome --disable-web-security --user-data-dir
```

のように，2つのオプションをつけてChromeを起動すれば，CORSを無効にすることができる．

## JavaScriptでフェッチして描画
該当のWebページは以下のような構成になっている．

```html
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=x-sjis" />
  <script language="vbscript">
    Sub hoge
      location.href = "fuga.html"
  </script>
</head>
<body>
  <button onclick="Call hoge">
</body>
</html>
```

ボタンアクションなどに合わせてVBScriptでページの遷移をしているから，IE以外では使えないということらしい．
最近は減ったはずだが，Shift_JIS(x-sjis)を使っているのでこの際UTF-8になおしてやろう．
文字エンコーディングを変更するためには，一度Blobで取得して，それをテキストとして解釈すればよい．
あとはDOMにパースして必要な部分を描画する．

こっちで用意したhtml/jsから読み込むにはこんな感じのコードを用意する．

```html
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <script type="text/javascript">
    window.onload = () => {
      getBlob("piyo.html").then(
        (blob) => translateFrom(blob, "x-sjis").then(
          (doc) => displayBody(doc)
        )
      )
    }

    var getBlob = (url) => {
      return new Promise((resolve, reject) => {
        var xhr = new XMLHttpRequest();
        xhr.onload = () => {
          resolve(xhr.response);
        }
        xhr.responseType = "blob";
        xhr.open("GET", url);
        xhr.send();
      });
    }

    var translateFrom = (blob, encoding) => {
      return new Promise((resolve, reject) => {
        var rdr = new FileReader();
        rdr.onload = (event) => {
          // text/html -> DOM
          var psr = new DOMParser();
          var doc = psr.parseFromString(event.target.result, "text/html")
          resolve(doc);
        }
        // x-sjis(encoding) -> utf-8
        rdr.readAsText(blob, encoding);
      });
    }

    var displayBody = (doc) => {
      // deeply copy the body
      var body = document.importNode(doc.body, true);

      // replace original body with fetched one
      var html = document.body.parentNode;
      html.removeChild(document.body);
      html.appendChild(body);
    }
  </script>
</head>
<body>
  <p>loading...</p>
</body>
</html>
```

ひとまずこれで，body以下を取り込むことができる．

## ボタンの中身を入れ替える
ボタンはいまだに```Call hoge```になっていて，これはVBScriptを呼び出している．
そこでこのボタンを見つけて，onclick時のイベントを書き換えてあげよう．
下は変更する部分のみを記述する．

```js
window.onload = () => {
  getBlob("piyo.html").then(
    (blob) => translateFrom(blob, "x-sjis").then(
      (doc) => useJSFunction(doc).then(
        (doc) => displayBody(doc)
      )
    )
  )
}

var useJSFunction = (doc) => {
  var buttons = doc.getElementsByTagName("button");
  for (var i = 0; i < buttons.length; i++) {
    var b = buttons[i];
    var oldFunc = b.getAttribute("onclick");
    var newFunc = oldFunc.replace("Call ", "javascript: ");
    b.setAttribute("onclick", newFunc);
  }
}

var hoge = () => {
  getBlob("fuga.html").then(
    (blob) => translateFrom(blob, "x-sjis").then(
      (doc) => displayBody(doc)
    )
  )
}
```

ほとんどのブラウザで，onclickなどの処理はJavaScriptで解釈されるのがデフォルトのはずだが，一応JavaScriptのコードですよーということを明記しておく．
実際にボタンが押されるとJavaScriptのhoge関数が呼ばれて，fuga.htmlのbodyを表示するようになる．
もちろんfuga.htmlでVBScriptが使われていて変更する必要があるなら，変更した上でbodyに描画する．
取ってきたhtmlのタグに含まれていないデザインは吹き飛ぶので，適宜`<head>`内にcssへの参照を入れておいたり，styleをJavaScriptからいじるなどしても良い．


## formタグへの対応
上の例ではボタンクリック時のページ遷移を，フェッチしてbodyに描画するというものに置き換えていた．
これは，あくまで自分のローカル内に用意したhtmlファイルから外に出ないためだ．
なので`<a>`タグなども同様に，hrefを`javascript: void(0)`などにしてリンクを無効にして，onclickで対象のページを取ってくる作業が必要だ．

しかし`<form>`タグなどでactionへの送信後にページにジャンプするものは，これでは対処できない．
タグの仕様で，ジャンプすることが原則になっているためだ．
ならば，`<form>`でやっていることをJavaScriptから実行してしまえばいい．

```js
var submitForm = (url, data) => {
  // data: e.g. [hoge: "hogehoge", fuga: "fugafuga"]
  var urlEncodedDataPairs = [];

  for(var name in data) {
    urlEncodedDataPairs.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name]));
  }
  var urlEncodedData = urlEncodedDataPairs.join('&').replace(/%20/g, '+');

  var xhr = new XMLHttpRequest();
  xhr.onload = (event) => {
    alert("done");
  }
  xhr.open("POST", url);
  xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  xhr.send(urlEncodedData);
}
```

# まとめ
おわかりいただけたとおり，エラーハンドリングはほとんどしていません．
Promiseでreject使ってるくせに何やってんだよ！
というお叱りもいただくかもしれません．
また私がJavaScriptの初心者なので，変な書き方やブラウザ互換性のないコードになっているかもしれません．
そのあたりのブラッシュアップのためにも，コメントはどんどん待っております．


# 参考

- [【メモ】XMLHttpRequestのイベントについて - Qiita](https://qiita.com/ShinyaKato/items/64b6726c361f5377b0f3)
- [UTF-8で作ってたら、サーバに置いてあるファイルがShift_JIS, EUC-JPだった - Qiita](https://qiita.com/ukyo/items/4a1d84f9cb59b98be60a)
- [JavaScript によるフォームの送信 - Web 開発を学ぶ | MDN](https://developer.mozilla.org/ja/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript)