Flickrの埋め込み画像にExifデータを表示させる

Flickrの埋め込みURLをマウスオーバーすると画像の情報が表示されますが、そこにFlickrAPIを使用し取得したExif情報を表示させてみようという試みです。

以下の画像が素のままのコードです。
今回は右下のライセンス情報を表示している部分をExif情報に差し替えます。
![flickr1.png](https://qiita-image-store.s3.amazonaws.com/0/73378/6496fb6c-b6df-e9bb-f0bf-eddc4fa056cf.png)


Flickrから埋め込みコードを取得すると以下のような感じで取得できます。
(はてなブログのFlickr埋め込み機能でも同様のコードが取得できます)

```html:埋め込みコード
<a data-flickr-embed="true" href="https://www.flickr.com/photos/nayuneko/32528664881/in/dateposted-public/" title="東京ビッグサイト"><img src="https://c1.staticflickr.com/1/540/32528664881_97be6f337a.jpg" width="500" height="333"></a>
<script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"></script>
```

このコードはページ読み込み時に`client-code.js`によってiframeに展開されます。

```html:展開後のコード
<iframe frameborder="0" allowfullscreen="" class="flickr-embed-frame" webkitallowfullscreen="" mozallowfullscreen="" oallowfullscreen="" msallowfullscreen="" width="500" height="333" data-natural-width="500" data-natural-height="333" style="overflow: hidden; padding: 0px; margin: 0px; width: 500px; height: 333px; max-width: none;" data-loaded="true"></iframe>
```

```html:iframe内のドキュメント
<div class="flickr-embed" data-flickr-embed="true">
  <div class="flickr-embed-photo" style="width: 500px; height: 333px;">
    <a data-rapid="photo" data-slideshow-position="" target="_blank" href="https://www.flickr.com/photos/nayuneko/32528664881" class="slide">
      <img src="https://farm1.staticflickr.com/540/32528664881_97be6f337a.jpg" width="500" height="333" alt="" onload="addview(this.getAttribute('data-path'))" data-path="/photos/32528664881/addview" style="width: 500px; height: 333px;">
    </a>
    <div data-slideshow-position="" class="flickr-embed-photo-footer slide">
      <div class="sharing">(左上のシェアボタンSVG画像)</div>
      <div class="license">(右下のライセンス情報)</div> 【←今回はここを書き換える】
      <div class="title-and-attribution">(左下の画像タイトル、投稿者情報)</div>
    </div>
    <div class="branding">(右上のFlickrロゴSVG画像)</div>
  </div>
</div>
```

はじめは、jQueryでイベント拾ってDOM操作すれば楽勝やろーぐらいに思ってたのですが、iframe配下のHTMLを書き換える上、他のスクリプトで実行されたDOM操作は拾えないっぽい？しかも、`async`が指定されているのでどのタイミングで展開されるかもわからない！
ここは`setInterval`を使って監視を…とも考えましたがあまりスマートじゃないですね。

# DOMの変更を検知をするMutationObserver

[MutationObserver](https://developer.mozilla.org/ja/docs/Web/API/MutationObserver)
JavaScriptなんでもあるな！
というわけでDOM操作を監視して、Flickr用のiframeが展開されるのをキャッチします。

```js
let mo = new MutationObserver(function(records){
  records.forEach(function(record){
    for(let i = 0; i < record.addedNodes.length; i++){
      let el = record.addedNodes[i];
      // "iframe.flickr-embed-frame"以外は処理をしない
      if (!el.tagName || el.tagName.toUpperCase() != 'IFRAME' || !el.classList.contains('flickr-embed-frame')){
        continue;
      }
      replaceIframe(el); // iframeの置換処理
    }
  });
});
mo.observe(document.body, {childList: true, subtree: true});
```

```js:MutationObserver.observe()
void observe(
  Node target,
  MutationObserverInit options
);
```

`observe`メソッドの第1引数には`document.body`を指定、第2引数にはすべてのDOM操作をキャッチするために`{childList: true, subtree: true}`を指定します。
※ `{childList: true}`だけだとbody直下のDOM操作しか検知されない。

# あとは取得して加工するだけ

無事iframeの追加を検知できるようになったのであとは書き換えるのみです。

APIから取得できるJSONデータフォーマットはAPIドキュメントを参照ください。
→ [flickr.photos.getExif](https://www.flickr.com/services/api/flickr.photos.getExif.html)
あと当然ですが、画像にExif情報が埋まっていない場合は取得できません。

```js
function replaceIframe(el){
  // iframeのドキュメントを取得
  let ifr_doc = el.contentWindow.document;
  // iframeドキュメント内に設定されているimgタグを取得
  let img = ifr_doc.getElementsByTagName('img');
  // imgタグのファイル名からFlickrのphoto_idを取得
  let mc = img[0].src.match(/.+\/([^_]+)_.+$/);
  let photo_id = mc[1];
  // FlickrAPIにリクエストを投げる
  let url = `https://api.flickr.com/services/rest/?method=flickr.photos.getExif&api_key=<<<FLICKR_API_KEY>>>&format=json&nojsoncallback=1&photo_id=${photo_id}`;
  fetch(url)
  .then(function(data){
    return data.json();
  })
  .then(function(json){
    ...
    return `${json.photo.camera}, ${exif.LensModel}, ISO${exif.ISO} ${exif.FocalLength} f/${exif.FNumber} ${exif.ExposureTime}`;
  })
  .then(function(exif_text){
    // div.licenceのテキストをExif情報に書き換え
    let exif_div = ifr_doc.querySelector('div.license');
    exif_div.textContent = exif_text;
    exif_div.classList.add('exif-info');
    exif_div.style.fontStyle = 'italic';
  });
}
```

無事変更できるとこんな感じで表示されます。
![flickr1.png](https://qiita-image-store.s3.amazonaws.com/0/73378/2c72f6f9-bbc5-55a4-c48f-7e6a382c927d.png)

# 参考サイト

* [FlickrEx](https://github.com/drikin/FlickrEx)
* [【JavaScript】 MutationObserverの使い方 個人的まとめ - はりをきば](http://blog.gutyan.jp/entry/2014/09/06/MutationObserver)
* [Fexif](https://github.com/nayuneko/fexif) - とりあえず動くやつをつくりました