Chrome拡張"OneTab"から、ブックマークにエクスポートする

# 経緯、タブをoneTabに貯め過ぎて、整理が不可能に。
気づいたら1000個ぐらい。縦に長過ぎて整理が不可能。折り畳めないしマージもできない。
じゃあ、ブックマークに戻そう。がきっかけ。

# oneTabに使えるエクスポート機能は無い
* まずChromeに取り込むには、特定形式の`bookmarks.html`にする必要ある。
    * Chromeからブックマークを書き出した時のファイル。
    * NETSCAPE-Bookmark-file-1形式？
* oneTabに簡易の書き出し機能はあるが、これは単純なURL改行のリスト。
    * そもそも日付情報が無い。
    * 日付は重要なので捨てたくない。
* まあ、便利なエクスポート機能あるとユーザー出て行きやすいから。
    * あえて付けなかったり（想像）、有料にしたりしてるのをよく見る。

# 表示部分からデータを解析する。
* 表示部分には日付データがちゃんと残ってる。
*  ターゲットはChrome拡張なので、Bookmarkletは使えない。
* `ctrl+shift+I` で開発者ツールのconsoleからやる。
    * 複数行のコードを弄るのは大変なので、ソース→スペニットを使って作る。
* 試行錯誤して・・・出来た。

## この記事投稿してから気づいた大失態
* oneTabのローカルストレージにJSONでデータが保存してあった！
* なので表示から解析する必要がなかった。
* でもまあ、書き出したものは同じだし、面倒くさいからそのままでいいか。

# スクリプトの使い方
* 下のコードをコピーして、ontab開いて、開発者ツールのコンソールに貼り付けて実行。
    * ただそういう攻撃もあるらしいので、軽くソースを読んでからのほうがいいらしい。
* 出来たhtmlファイルを、Chrome→ブックマークマネージャ→インポート
* スクリプトの主な機能
    * Chromeにインポートできる形式のhtmlを書き出す。
    * ブックマークにoneTabでの作成日を付与。
    * faviconも書き出す。

```js
/* eslint no-undef: "error" */
//便利関数
var log = console.log
  , qsa = (s, o = document) => [...o.querySelectorAll(s)]
  , qs = (s, o = document) => o.querySelector(s)

function saveTextToFile(fileName, text) {
  const blob = new Blob([text], {type: 'text/plan'})
  const link = document.createElement('a')
  link.href = URL.createObjectURL(blob)
  link.download = fileName
  link.click()
}
//dataURL化
function getBase64(img) {
  var cvs = document.createElement('canvas')
  cvs.width = img.naturalWidth
  cvs.height = img.naturalHeight
  var ctx = cvs.getContext('2d')
  ctx.drawImage(img, 0, 0)
  //枠つけ
  // ctx.lineWidth = cvs.width / 3
  // ctx.strokeStyle = '#0f0c'
  // ctx.strokeRect(0, 0, cvs.width, cvs.height)
  //canvas要素をBase64化する
  return cvs.toDataURL('image/png')
}

function oneTab2html() {
  //表示から解析してdataに入れる。
  var data = qsa('.tabGroup').map((v) => {
    let dateText = qsa('div', v).find(v => v.innerHTML.startsWith('作成日時')).textContent.slice(5)
    let unixtime = new Date(dateText).getTime() / 1000
    let obj = {
      tabs: null,
      title: (qs('.tabGroupTitleText', v).textContent.trim() + '_' || '') + dateText,
      time: unixtime,
    }
    obj.tabs = qsa('.tab', v).map(v => ({
      url: qs('a', v).href,
      title: qs('a', v).textContent,
      favicon: (() => {
        let el = qs('img:nth-child(1)', v)
        if (el && el.src.startsWith('chrome://'))
          return getBase64(el) //''//
        return ''
      })(),
    }))
    return obj
  })

  //dataからhtmlに書き出す
  var folders = ''
  data.forEach(v => {
    folders += `    <DT><H3 ADD_DATE="${v.time}" >${v.title}</H3>\n`
    folders += '    <DL>\n'
    v.tabs.forEach(a =>
      folders += `        <DT><A HREF="${a.url}" ADD_DATE="${v.time}" ICON="${a.favicon}">${a.title}</A>\n`
    )
    folders += '    </DL>\n'
  })

  var html = `<!DOCTYPE NETSCAPE-Bookmark-file-1>
<!-- This is an automatically generated file.
     It will be read and overwritten.
     DO NOT EDIT! -->
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks</H1>
<DL><p>
${folders}
</DL><p>
`
  //ファイルとして保存
  saveTextToFile('OneTab→bookmarks.html', html)
  return true
}
oneTab2html() && '完了'
```
#インポート後、日付が書き出せてるか、確認する方法
* 素のChromeでは、ブックマークの作成日を確認する方法は見つからなかった。
* なので以下の拡張機能を使いました。

https://chrome.google.com/webstore/detail/starmarks-bookmark-manage/ohhpijmpbgndokpodomgiclmflhbkihb

#自分用の雑記、試行錯誤のおおよそ時系列。
* chromeから書き出したhtmlファイルを色々試す
    * `a`の`add_date`属性にUNIX時間を入れるらしい。
    * その他ブックマークのほうがネストが浅くて分かりやすい。
    * `H3`をフォルダと認識
        * フォルダにも`add_date`時間を付けれる、`LAST_MODIFIED`も
    * `ICON="data:image/png;base64,`
        * base64のiconデータも渡せる。
    * インポート
        * インポート時はブックマークツールバーに入れられる
        * ブックマークツールバーが空の時以外は、`インポートしたブックマーク`フォルダが作られそこに収まる。
            * そのフォルダが存在する時は、同名のフォルダが作られる。
        * エクスポート時のフォルダ名が同じものは一つにマージされる、ブックマークは両方残る。
* oneTabの表示から解析
    * googleとかoverflowとか一部のサイトはfaviconのsrcが無く、よくわからない。放置。
    * `[{tabs:[{href,title,favicon},],time:UNIX時間,title:フォルダ名},]`
    * みたいな形式にまず変換。
* 出来上がったhtmlをctrl+s保存するために、window.open()を使う。
    * Chromeはブックマークのインポート形式がhtmlだから。
    * about:blankでは`ctrl+s`の保存できなかった。なぜぇ。
    * example.com開いて書き換えれば？→セキュリティー的に無理
    * もういいや、onetabの画面を直接書き換えてしまえ。
        * ctrl+sは可能だったが、
        * 保存した瞬間ファイルが削除される怪奇現象。chrome-ext://だからぽい。
    * about:blankにhtml表示させて、そのouterHTMLをコピーしよう
        * document.documentElement.outerHTMLをcopy()して、htmlに貼り付けた。
        * V1.html完成
* V1.htmlインポートしみてる
    * インポート失敗。まあそうか・・・。
    * インポートしてみるも数個の登録されるだけでおかしい。
    * Chromeが書き出したファイルと見比べると、Chromeはタグが大文字。！？
    * Chrome書き出しファイルを小文字にしてインポートテスト→反応なし。
        * タグは大文字必須？！
    * 大文字にしてからhtmlにparseしてからコピーすると、タグが小文字になってしまう。
        * タグと属性だけ大文字にするの・・・めんどくさいからparseするのやめよう。
* html→保存、でなく直接書き出すことにした。
    * Chrome書き出しファイルを雛形にして書き出してコピー→htmlファイルに貼り付け。
    * V2.html完成
    * インポートするも、反映されない。
    * href←これも大文字必須だった。疲れた。もう全部大文字にしよう。
    * 修正版を読み込ます→成功・・・日付がおかしい。
        * ブックマークの日付を調べるにはそれっぽい拡張が必要。
    * UNIX時間はミリ秒じゃなく秒だった。
    * インポート成功！！
* とりあえず機能的に完成。
* ここまで来たら、Chromeが書き出したファイルで最小化を調べてる
    * pタグを消してみる、問題なし、
    * フォルダ前のdl dt 削除、OK
    * `<h3>`のフォルダより前は全部不要。文字コードはutf8なら削除できる。
    * `<DL><DT>`は改行無いと読み込まない→まじかあ。HTMLのこと詳しくないけど。
    * タイトルやH1は必要無い。
    * なんでこんな不親切な仕様なんだろう、超かしこ集団が作ってるブラウザなのに。
    * ADD_DATEはミリ秒じゃないく秒。10桁ぐらい。
    * <!DOCTYPE NETSCAPE-Bookmark-file-1>て検索すると情報が少し出てくる。
* favicon取り込み（やらなくてもいい）
    * ついでにfaviconに色つけてみようかな。
    * そしたら、リンク開く→アイコン更新で、visited的なことできそう。
    * そもそも更新されるのかな？
    * とりあえず取り込もう
    * canvas作ったにimg貼り付ける、naturalWidthを初めて知る。
    * base64化で、toData(）→エラー
    * クロスドメイン問題にひっかかった・・・別オリジンのimg貼り付けがあかんらしい。
        * ダメ元で.setAttribute("crossorigin","anonymous")
        * 画像が壊れマーク。エラーは出なくなった。返り値は空に・・・。
        * DOMでスクリーンショット取れるとか聞いたことある、それはどうか？
    * そもそもクロスドメインで全部弾かれてないんじゃないか？
        * onetabオリジン→chrome-extension://burabura
        * icon1→chrome://burabrua
        * icon2→`https://s2.googleusercontent.com`
        * ちゃんと調べるとhttp系だけが弾かれてる。じゃあこっち捨てよう。
    * 完成、iconに色枠が付→見ると消えた。
        * この機能要らない。
    * 気づいたこと
        * ブックマークマネージャーだと同じfaviconは共通表示されてた。
        * ツールバーだと共通表示されない。初めて気づいた。
        * いままで後で読む、の区別にこの挙動を利用してた。
* 別のQiitaのBookmarkletの記事を読んでたら、偶然、ファイル保存の仕方を知る。
* Qiitaへ投稿
* onetabのローカルストレージにJSON発見。
    * 個別のタブデータに日付情報は無し。
    * faviconもChromeURLでfavicon返すのに突っ込めばよさそう。
    * favicon返しに使えない例外の判定は作る必要ありそう。


# bookmarks.htmlの最小構成を検証した結果
```html
<H3 ADD_DATE="1617199649" LAST_MODIFIED="0">フォルダ1</H3>
    <DL>
        <DT><A HREF="https://news.google.com/" ADD_DATE="1606126600" ICON="data:image/png;base64,burabura=">ニュース</A>
    </DL>

```

#手間取った点
* Chrome開発者ツールのスペニットで作ってみた。
    * サジェスト効かないし、コーフォドーマットが汚すぎて読みづらい。
* インポート形式の仕様がもっとゆるいと思い込み、試す→弾かれる、の繰り返しに疲れた。
    * 最初から完コピを目指すべきだった。
* 軽い気持ちでワンライナーや、アロー関数オブジェクト返しを多様。
    * アイテムが多い上に例外多くて、そこでエラーでとまると、どこでエラーが起きたか分かりにくい。
    * console.assrtを使うべきかな。
* テンプレートリテラルの中でforEachとかすると、凄く読みづらい。
    * join()ぐらいまでにすべき。
    
#参考にさせてもらったページ
* ブックマークレット・コンソールを使って Webページ内の複数の指定要素のテキストをファイルに保存 - Qiita
    * https://qiita.com/youtoy/items/7aaef1c44e0c77e0ced8